Tour and introduce

This commit is contained in:
2025-05-20 01:51:24 +08:00
parent 9e609b8fe4
commit 1f2a5c107d
8 changed files with 410 additions and 67 deletions

68
lib/services/tour.dart Normal file
View File

@ -0,0 +1,68 @@
import 'dart:convert';
import 'package:flutter/widgets.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:island/pods/config.dart';
import 'package:island/widgets/tour/techincal_review_intro.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'tour.g.dart';
part 'tour.freezed.dart';
const kAppTourStatusKey = "app_tour_statuses";
const List<Tour> kAllTours = [
Tour(id: 'technical_review_intro', isStartup: true),
];
@freezed
abstract class Tour with _$Tour {
const Tour._();
const factory Tour({required String id, required bool isStartup}) = _Tour;
Widget get widget => switch (id) {
'technical_review_intro' => const TechicalReviewIntroWidget(),
_ => throw UnimplementedError(),
};
}
@riverpod
class TourStatusNotifier extends _$TourStatusNotifier {
@override
Map<String, bool> build() {
final prefs = ref.watch(sharedPreferencesProvider);
final storedJson = prefs.getString(kAppTourStatusKey);
if (storedJson != null) {
try {
final Map<String, dynamic> stored = jsonDecode(storedJson);
return Map<String, bool>.from(stored);
} catch (_) {
return {for (final id in kAllTours.map((e) => e.id)) id: false};
}
}
return {for (final id in kAllTours.map((e) => e.id)) id: false};
}
bool isTourShown(String tourId) => state[tourId] ?? false;
Future<void> _saveState(Map<String, bool> newState) async {
state = newState;
final prefs = ref.read(sharedPreferencesProvider);
await prefs.setString(kAppTourStatusKey, jsonEncode(newState));
}
Future<Widget?> showTour(String tourId) async {
if (!isTourShown(tourId) || true) {
final newState = {...state, tourId: true};
await _saveState(newState);
return kAllTours.firstWhere((e) => e.id == tourId).widget;
}
return null;
}
Future<void> resetTours() async {
final newState = {for (final id in kAllTours.map((e) => e.id)) id: false};
await _saveState(newState);
}
}

View File

@ -0,0 +1,145 @@
// dart format width=80
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'tour.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$Tour {
String get id; bool get isStartup;
/// Create a copy of Tour
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$TourCopyWith<Tour> get copyWith => _$TourCopyWithImpl<Tour>(this as Tour, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is Tour&&(identical(other.id, id) || other.id == id)&&(identical(other.isStartup, isStartup) || other.isStartup == isStartup));
}
@override
int get hashCode => Object.hash(runtimeType,id,isStartup);
@override
String toString() {
return 'Tour(id: $id, isStartup: $isStartup)';
}
}
/// @nodoc
abstract mixin class $TourCopyWith<$Res> {
factory $TourCopyWith(Tour value, $Res Function(Tour) _then) = _$TourCopyWithImpl;
@useResult
$Res call({
String id, bool isStartup
});
}
/// @nodoc
class _$TourCopyWithImpl<$Res>
implements $TourCopyWith<$Res> {
_$TourCopyWithImpl(this._self, this._then);
final Tour _self;
final $Res Function(Tour) _then;
/// Create a copy of Tour
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? isStartup = null,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,isStartup: null == isStartup ? _self.isStartup : isStartup // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// @nodoc
class _Tour extends Tour {
const _Tour({required this.id, required this.isStartup}): super._();
@override final String id;
@override final bool isStartup;
/// Create a copy of Tour
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$TourCopyWith<_Tour> get copyWith => __$TourCopyWithImpl<_Tour>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Tour&&(identical(other.id, id) || other.id == id)&&(identical(other.isStartup, isStartup) || other.isStartup == isStartup));
}
@override
int get hashCode => Object.hash(runtimeType,id,isStartup);
@override
String toString() {
return 'Tour(id: $id, isStartup: $isStartup)';
}
}
/// @nodoc
abstract mixin class _$TourCopyWith<$Res> implements $TourCopyWith<$Res> {
factory _$TourCopyWith(_Tour value, $Res Function(_Tour) _then) = __$TourCopyWithImpl;
@override @useResult
$Res call({
String id, bool isStartup
});
}
/// @nodoc
class __$TourCopyWithImpl<$Res>
implements _$TourCopyWith<$Res> {
__$TourCopyWithImpl(this._self, this._then);
final _Tour _self;
final $Res Function(_Tour) _then;
/// Create a copy of Tour
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? isStartup = null,}) {
return _then(_Tour(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,isStartup: null == isStartup ? _self.isStartup : isStartup // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
// dart format on

28
lib/services/tour.g.dart Normal file
View File

@ -0,0 +1,28 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'tour.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$tourStatusNotifierHash() =>
r'040aac2d7cf6d14e539c1b04cf311421ee133ed3';
/// See also [TourStatusNotifier].
@ProviderFor(TourStatusNotifier)
final tourStatusNotifierProvider =
AutoDisposeNotifierProvider<TourStatusNotifier, Map<String, bool>>.internal(
TourStatusNotifier.new,
name: r'tourStatusNotifierProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$tourStatusNotifierHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$TourStatusNotifier = AutoDisposeNotifier<Map<String, bool>>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package