From de8096161fe48d68031ff588eae6fb20fab53c38 Mon Sep 17 00:00:00 2001 From: Fries_I23 Date: Sun, 31 Mar 2024 09:26:40 +0800 Subject: [PATCH 01/13] feat: add new profile framework --- lib/persistent/db_helper_builder.dart | 2 +- lib/persistent/db_helper_provider.dart | 35 +++++---- lib/persistent/profile/profile_helper.dart | 45 +++++++++++ lib/persistent/profile_builder.dart | 72 +++++++++++++++++ lib/persistent/profile_provider.dart | 91 ++++++++++++++++++++++ lib/view/app.dart | 9 ++- pubspec.lock | 2 +- pubspec.yaml | 1 + 8 files changed, 237 insertions(+), 20 deletions(-) create mode 100644 lib/persistent/profile/profile_helper.dart create mode 100644 lib/persistent/profile_builder.dart create mode 100644 lib/persistent/profile_provider.dart diff --git a/lib/persistent/db_helper_builder.dart b/lib/persistent/db_helper_builder.dart index c56e1807..55aa4ed5 100644 --- a/lib/persistent/db_helper_builder.dart +++ b/lib/persistent/db_helper_builder.dart @@ -34,7 +34,7 @@ class DBHelperBuilder extends SingleChildStatelessWidget { Future _loadingHelper(BuildContext context) async { final helper = context.read(); - if (helper.waitingInit != null) await helper.waitingInit; + if (helper.mounted && !helper.inited) await helper.init(); } @override diff --git a/lib/persistent/db_helper_provider.dart b/lib/persistent/db_helper_provider.dart index c6361c96..3139dc43 100644 --- a/lib/persistent/db_helper_provider.dart +++ b/lib/persistent/db_helper_provider.dart @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +import 'dart:async'; + +import 'package:async/async.dart'; import 'package:flutter/foundation.dart'; import '../common/abc.dart'; @@ -26,37 +29,39 @@ import 'local/handler/record.dart'; class DBHelperViewModel extends ChangeNotifier with FutureInitializationABC, ProviderMounted { - DBHelper local; - Future? waitingInit; - bool _inited = false; + final DBHelper local; + + Completer? _completer; bool _mounted = true; DBHelperViewModel() : local = DBHelper(); @override Future init() async { - Future initAll() async { + if (_completer == null) { + _completer = Completer(); await local.init(); + _completer!.complete(); } - - if (inited) { - appLog.load.error("$runtimeType.init", - ex: ["already inited", local, _inited, _mounted]); - return; - } - - waitingInit = initAll()..whenComplete(() => _inited = true); - await waitingInit; + return _completer!.future; } @override void dispose() { _mounted = false; - local.dispose(); + if (inited) local.dispose(); + if (_completer?.isCompleted != true) { + CancelableOperation.fromFuture(_completer!.future).cancel(); + _completer = null; + } super.dispose(); } Future reload() async { + if (_completer?.isCompleted != true) { + CancelableOperation.fromFuture(_completer!.future).cancel(); + _completer = null; + } await local.init(reinit: true); notifyListeners(); } @@ -64,7 +69,7 @@ class DBHelperViewModel extends ChangeNotifier @override bool get mounted => _mounted; - bool get inited => _inited; + bool get inited => _completer?.isCompleted ?? false; @override String toString() { diff --git a/lib/persistent/profile/profile_helper.dart b/lib/persistent/profile/profile_helper.dart new file mode 100644 index 00000000..4e769961 --- /dev/null +++ b/lib/persistent/profile/profile_helper.dart @@ -0,0 +1,45 @@ +// Copyright 2024 Fries_I23 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:convert'; + +abstract interface class ProfileHelperHandler { + String get key; + Future set(T value); + T? get(); +} + +abstract class ProfileHelperConvertHandler + implements ProfileHelperHandler { + @override + final String key; + + final Codec _codec; + + const ProfileHelperConvertHandler( + {required this.key, required Codec codec}) + : _codec = codec; + + @override + Future set(S value) => setMethod.call(key, _codec.encode(value)); + + @override + S? get() { + final rawValue = getMethod.call(key); + return rawValue != null ? _codec.decode(rawValue) : null; + } + + Future Function(String, T) get setMethod; + T? Function(String) get getMethod; +} diff --git a/lib/persistent/profile_builder.dart b/lib/persistent/profile_builder.dart new file mode 100644 index 00000000..1e5c871c --- /dev/null +++ b/lib/persistent/profile_builder.dart @@ -0,0 +1,72 @@ +// Copyright 2024 Fries_I23 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'package:flutter/widgets.dart'; +import 'package:nested/nested.dart'; +import 'package:provider/provider.dart'; + +import '../extension/async_extensions.dart'; +import '../logging/helper.dart'; +import 'profile_provider.dart'; + +class ProfileBuilder extends SingleChildStatelessWidget { + final TransitionBuilder builder; + final TransitionBuilder? loadingBuilder; + final ErrorWidgetBuilder? errorBuilder; + final Iterable handlers; + + const ProfileBuilder({ + super.key, + super.child, + required this.builder, + this.loadingBuilder, + this.errorBuilder, + this.handlers = const [], + }); + + Future _loadingHelper(BuildContext context) async { + appLog.db.info("$runtimeType._loadingHelper", ex: ["processing"]); + final helper = context.read(); + if (helper.mounted && !helper.inited) await helper.init(); + appLog.db.info("$runtimeType._loadingHelper", ex: ["done"]); + } + + @override + Widget buildWithChild(BuildContext context, Widget? child) => + ChangeNotifierProvider( + create: (context) => + ProfileViewModel(Map.fromIterable(handlers))..init(), + lazy: false, + child: child, + builder: (context, child) => FutureBuilder( + future: _loadingHelper(context), + builder: (context, snapshot) { + if (snapshot.hasError) { + if (errorBuilder != null) { + return errorBuilder!(FlutterErrorDetails( + exception: snapshot.error!, + stack: snapshot.stackTrace, + library: "profile_builder")); + } else { + throw FlutterError("profile build failed"); + } + } else if (snapshot.isDone) { + return builder(context, child); + } else { + return loadingBuilder?.call(context, child) ?? const SizedBox(); + } + }, + ), + ); +} diff --git a/lib/persistent/profile_provider.dart b/lib/persistent/profile_provider.dart new file mode 100644 index 00000000..671fb0c9 --- /dev/null +++ b/lib/persistent/profile_provider.dart @@ -0,0 +1,91 @@ +// Copyright 2024 Fries_I23 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:async'; + +import 'package:async/async.dart'; +import 'package:flutter/foundation.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../common/abc.dart'; +import '../provider/commons.dart'; +import 'profile/profile_helper.dart'; + +typedef ProfileHandlerBuilder = T Function( + SharedPreferences pref); + +class ProfileViewModel extends ChangeNotifier + with FutureInitializationABC, ProviderMounted { + late final SharedPreferences _pref; + + final Map _handlers; + + Completer? _completer; + bool _mounted = true; + + ProfileViewModel(Map handlers) + : _handlers = handlers; + + @override + Future init() async { + if (_completer == null) { + _completer = Completer(); + _pref = await SharedPreferences.getInstance(); + _completer!.complete(); + } + return _completer!.future; + } + + @override + void dispose() { + _mounted = false; + if (_completer?.isCompleted != true) { + CancelableOperation.fromFuture(_completer!.future).cancel(); + _completer = null; + } + super.dispose(); + } + + Future reload() async { + if (_completer?.isCompleted != true) { + CancelableOperation.fromFuture(_completer!.future).cancel(); + _completer = null; + } + await _pref.reload(); + notifyListeners(); + } + + Future clear() => + (_mounted && inited) ? _pref.clear() : Future.value(false); + + @override + bool get mounted => _mounted; + + bool get inited => _completer?.isCompleted ?? false; + + T? getHandler() => + _handlers[T.runtimeType.toString()]?.call(_pref) as T?; + + bool addHandler( + ProfileHandlerBuilder builder) { + _handlers[builder.runtimeType.toString()] = builder; + return true; + } + + @override + String toString() { + return "$runtimeType[$hashCode](pref=$_pref,mounted=$mounted," + "inited=$inited)"; + } +} diff --git a/lib/view/app.dart b/lib/view/app.dart index 5fb5f0bc..bb72129a 100644 --- a/lib/view/app.dart +++ b/lib/view/app.dart @@ -22,6 +22,7 @@ import '../common/global.dart'; import '../l10n/localizations.dart'; import '../model/global.dart'; import '../persistent/db_helper_builder.dart'; +import '../persistent/profile_builder.dart'; import '../provider/app_theme.dart'; import '../theme/color.dart'; import 'common/_widget.dart'; @@ -34,9 +35,11 @@ class App extends StatelessWidget { @override Widget build(BuildContext context) { debugPrint('------ App start ------'); - return DBHelperBuilder( - child: const AppView(), - builder: (context, child) => AppProviders(child: child), + return ProfileBuilder( + builder: (context, child) => DBHelperBuilder( + builder: (context, child) => AppProviders(child: child), + child: const AppView(), + ), ); } } diff --git a/pubspec.lock b/pubspec.lock index a080b4f2..cf146052 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -50,7 +50,7 @@ packages: source: hosted version: "2.4.2" async: - dependency: transitive + dependency: "direct main" description: name: async sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" diff --git a/pubspec.yaml b/pubspec.yaml index 4f20bdf7..2545547a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -81,6 +81,7 @@ dependencies: crypto: ^3.0.2 timezone: ^0.9.1 nested: ^1.0.0 + async: ^2.11.0 # Data Persistence shared_preferences: ^2.0.15 From dab987f8ddc1dd1f3ff2c433a0ac3c51d3d1dab9 Mon Sep 17 00:00:00 2001 From: Fries_I23 Date: Sun, 31 Mar 2024 14:43:14 +0800 Subject: [PATCH 02/13] refactor: app theme type --- lib/db/profile.dart | 17 ------ lib/model/global.dart | 3 -- lib/persistent/profile/converter.dart | 32 ++++++++++++ .../profile/handler/app_theme_type.dart | 52 +++++++++++++++++++ .../display_calendar_bar_occupy_prt.dart | 25 +++++++++ lib/persistent/profile/handlers.dart | 16 ++++++ lib/persistent/profile/profile_helper.dart | 27 +++++++--- lib/persistent/profile_builder.dart | 3 +- lib/persistent/profile_provider.dart | 30 +++++++---- lib/provider/app_theme.dart | 40 +++++++------- lib/view/app.dart | 8 +++ lib/view/for_app/app_providers.dart | 10 ++-- test/model_test/profiles_test.dart | 28 ---------- test/viewmodel_test/app_theme_test.dart | 14 ++--- 14 files changed, 208 insertions(+), 97 deletions(-) create mode 100644 lib/persistent/profile/converter.dart create mode 100644 lib/persistent/profile/handler/app_theme_type.dart create mode 100644 lib/persistent/profile/handler/display_calendar_bar_occupy_prt.dart create mode 100644 lib/persistent/profile/handlers.dart diff --git a/lib/db/profile.dart b/lib/db/profile.dart index bac675ce..236eba1e 100644 --- a/lib/db/profile.dart +++ b/lib/db/profile.dart @@ -28,7 +28,6 @@ import '../logging/helper.dart'; import '../model/app_reminder_config.dart'; import '../model/custom_date_format.dart'; import '../model/habit_display.dart'; -import '../theme/color.dart'; mixin CacheInterface { Map getInputFillCache(); @@ -37,8 +36,6 @@ mixin CacheInterface { abstract class ProfileInterface { Future clearAll(); - int getThemeType(); - Future setThemeType(AppThemeType newThemeType); int getSysThemeMainColor(); Future setSysThemeMainColor(Color newColor); @@ -115,20 +112,6 @@ class Profile return _pref.clear(); } - @override - int getThemeType() { - return _pref.getInt(ProfileKey.themeType.name) ?? - appDefaultThemeType.dbCode; - } - - @override - Future setThemeType(AppThemeType newThemeType) async { - if (newThemeType == AppThemeType.unknown) { - throw TypeError(); - } - return _pref.setInt(ProfileKey.themeType.name, newThemeType.dbCode); - } - @override int getSysThemeMainColor() { return _pref.getInt(ProfileKey.sysThemeMainColor.name) ?? diff --git a/lib/model/global.dart b/lib/model/global.dart index 7f43f308..7261d3ce 100644 --- a/lib/model/global.dart +++ b/lib/model/global.dart @@ -18,7 +18,6 @@ import 'package:flutter/material.dart'; import '../common/consts.dart'; import '../common/enums.dart'; import '../db/profile.dart'; -import '../theme/color.dart'; import 'app_reminder_config.dart'; import 'cache.dart'; import 'custom_date_format.dart'; @@ -75,8 +74,6 @@ class Global @override Profile get profile => Profile(); - AppThemeType get themeType => - AppThemeType.getFromDBCode(profile.getThemeType())!; Color get themeMainColor => Color(profile.getSysThemeMainColor()); HabitDisplaySortType get sortType => diff --git a/lib/persistent/profile/converter.dart b/lib/persistent/profile/converter.dart new file mode 100644 index 00000000..3e7fc429 --- /dev/null +++ b/lib/persistent/profile/converter.dart @@ -0,0 +1,32 @@ +// Copyright 2024 Fries_I23 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:convert'; + +class SameTypeCodec extends Codec { + const SameTypeCodec(); + + @override + Converter get decoder => const _Converter(); + + @override + Converter get encoder => const _Converter(); +} + +class _Converter extends Converter { + const _Converter(); + + @override + T convert(T input) => input; +} diff --git a/lib/persistent/profile/handler/app_theme_type.dart b/lib/persistent/profile/handler/app_theme_type.dart new file mode 100644 index 00000000..161fa321 --- /dev/null +++ b/lib/persistent/profile/handler/app_theme_type.dart @@ -0,0 +1,52 @@ +// Copyright 2024 Fries_I23 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:convert'; + +import '../../../theme/color.dart'; +import '../profile_helper.dart'; + +final class AppThemeTypeProfileHandler + extends ProfileHelperCovertToIntHandler { + @override + String get key => "themeType"; + + const AppThemeTypeProfileHandler(super.pref, + {super.codec = const AppThemeTypeProfileCodec()}); +} + +final class AppThemeTypeProfileCodec extends Codec { + const AppThemeTypeProfileCodec(); + + @override + Converter get decoder => const _Decoder(); + + @override + Converter get encoder => const _Encoder(); +} + +final class _Decoder extends Converter { + const _Decoder(); + + @override + AppThemeType convert(int input) => + AppThemeType.getFromDBCode(input, withDefault: AppThemeType.unknown)!; +} + +final class _Encoder extends Converter { + const _Encoder(); + + @override + int convert(AppThemeType input) => input.dbCode; +} diff --git a/lib/persistent/profile/handler/display_calendar_bar_occupy_prt.dart b/lib/persistent/profile/handler/display_calendar_bar_occupy_prt.dart new file mode 100644 index 00000000..60ce10a5 --- /dev/null +++ b/lib/persistent/profile/handler/display_calendar_bar_occupy_prt.dart @@ -0,0 +1,25 @@ +// Copyright 2024 Fries_I23 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import '../converter.dart'; +import '../profile_helper.dart'; + +final class DisplayCalendartBarOccupyPrtProfileHandler + extends ProfileHelperCovertToIntHandler { + DisplayCalendartBarOccupyPrtProfileHandler(super.pref) + : super(codec: const SameTypeCodec()); + + @override + String get key => "displayCalendarBarOccupyPrt"; +} diff --git a/lib/persistent/profile/handlers.dart b/lib/persistent/profile/handlers.dart new file mode 100644 index 00000000..23eca8de --- /dev/null +++ b/lib/persistent/profile/handlers.dart @@ -0,0 +1,16 @@ +// Copyright 2024 Fries_I23 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +export './handler/app_theme_type.dart'; +export './handler/display_calendar_bar_occupy_prt.dart'; diff --git a/lib/persistent/profile/profile_helper.dart b/lib/persistent/profile/profile_helper.dart index 4e769961..56e7e85f 100644 --- a/lib/persistent/profile/profile_helper.dart +++ b/lib/persistent/profile/profile_helper.dart @@ -14,6 +14,8 @@ import 'dart:convert'; +import 'package:shared_preferences/shared_preferences.dart'; + abstract interface class ProfileHelperHandler { String get key; Future set(T value); @@ -22,13 +24,9 @@ abstract interface class ProfileHelperHandler { abstract class ProfileHelperConvertHandler implements ProfileHelperHandler { - @override - final String key; - final Codec _codec; - const ProfileHelperConvertHandler( - {required this.key, required Codec codec}) + const ProfileHelperConvertHandler({required Codec codec}) : _codec = codec; @override @@ -40,6 +38,21 @@ abstract class ProfileHelperConvertHandler return rawValue != null ? _codec.decode(rawValue) : null; } - Future Function(String, T) get setMethod; - T? Function(String) get getMethod; + Future Function(String key, T value) get setMethod; + T? Function(String key) get getMethod; +} + +abstract class ProfileHelperCovertToIntHandler + extends ProfileHelperConvertHandler { + final SharedPreferences _pref; + + const ProfileHelperCovertToIntHandler(SharedPreferences pref, + {required super.codec}) + : _pref = pref; + + @override + int? Function(String key) get getMethod => _pref.getInt; + + @override + Future Function(String key, int value) get setMethod => _pref.setInt; } diff --git a/lib/persistent/profile_builder.dart b/lib/persistent/profile_builder.dart index 1e5c871c..a9cebb8f 100644 --- a/lib/persistent/profile_builder.dart +++ b/lib/persistent/profile_builder.dart @@ -45,8 +45,7 @@ class ProfileBuilder extends SingleChildStatelessWidget { @override Widget buildWithChild(BuildContext context, Widget? child) => ChangeNotifierProvider( - create: (context) => - ProfileViewModel(Map.fromIterable(handlers))..init(), + create: (context) => ProfileViewModel(handlers)..init(), lazy: false, child: child, builder: (context, child) => FutureBuilder( diff --git a/lib/persistent/profile_provider.dart b/lib/persistent/profile_provider.dart index 671fb0c9..92c0fc11 100644 --- a/lib/persistent/profile_provider.dart +++ b/lib/persistent/profile_provider.dart @@ -19,6 +19,7 @@ import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../common/abc.dart'; +import '../logging/helper.dart'; import '../provider/commons.dart'; import 'profile/profile_helper.dart'; @@ -29,19 +30,23 @@ class ProfileViewModel extends ChangeNotifier with FutureInitializationABC, ProviderMounted { late final SharedPreferences _pref; - final Map _handlers; + final Iterable _handlerBuilders; + late final Map _handlers; Completer? _completer; bool _mounted = true; - ProfileViewModel(Map handlers) - : _handlers = handlers; + ProfileViewModel(Iterable builders) + : _handlerBuilders = builders; @override Future init() async { if (_completer == null) { _completer = Completer(); _pref = await SharedPreferences.getInstance(); + _handlers = Map.fromEntries(_handlerBuilders + .map((e) => e.call(_pref)) + .map((e) => MapEntry(e.runtimeType, e))); _completer!.complete(); } return _completer!.future; @@ -74,14 +79,7 @@ class ProfileViewModel extends ChangeNotifier bool get inited => _completer?.isCompleted ?? false; - T? getHandler() => - _handlers[T.runtimeType.toString()]?.call(_pref) as T?; - - bool addHandler( - ProfileHandlerBuilder builder) { - _handlers[builder.runtimeType.toString()] = builder; - return true; - } + T? getHandler() => _handlers[T] as T?; @override String toString() { @@ -89,3 +87,13 @@ class ProfileViewModel extends ChangeNotifier "inited=$inited)"; } } + +abstract mixin class ProfileHandlerLoadedMixin { + late ProfileViewModel profile; + + @mustCallSuper + void updateProfile(ProfileViewModel newProfile) { + appLog.load.info("$runtimeType.updateDBHelper", ex: [newProfile]); + profile = newProfile; + } +} diff --git a/lib/provider/app_theme.dart b/lib/provider/app_theme.dart index 312875bb..38d5fedb 100644 --- a/lib/provider/app_theme.dart +++ b/lib/provider/app_theme.dart @@ -14,39 +14,42 @@ import 'package:flutter/material.dart'; +import '../common/consts.dart'; import '../common/utils.dart'; -import '../model/global.dart'; +import '../persistent/profile/handlers.dart'; +import '../persistent/profile_provider.dart'; import '../theme/color.dart'; -class AppThemeViewModel extends ChangeNotifier - implements GlobalProxyProviderInterface { - Global _g; +class AppThemeViewModel extends ChangeNotifier with ProfileHandlerLoadedMixin { + AppThemeTypeProfileHandler? _theme; + DisplayCalendartBarOccupyPrtProfileHandler? _calOccupy; - AppThemeViewModel({required Global global}) : _g = global; + AppThemeViewModel(); @override - Global get g => _g; - - @override - void updateGlobal(Global newGloal) => _g = newGloal; + void updateProfile(ProfileViewModel newProfile) { + super.updateProfile(newProfile); + _theme = newProfile.getHandler(); + _calOccupy = + newProfile.getHandler(); + } //#region app theme - AppThemeType get themeType => _g.themeType; + AppThemeType get themeType => _theme?.get() ?? appDefaultThemeType; ThemeMode get matertialThemeType => transToMaterialThemeType(themeType); Future setNewthemeType(AppThemeType newThemeType) async { - if (_g.themeType != newThemeType) { - await _g.profile.setThemeType(newThemeType); + if (_theme?.get() != newThemeType) { + await _theme?.set(newThemeType); notifyListeners(); } } Future onTapChangeThemeType(Brightness brightness) async { - var crtThemeType = _g.themeType; - AppThemeType nextThemeType; + final AppThemeType nextThemeType; // in lightmode: followSystem -> light -> dark -> followSystem -> ... // in darkmode: followSystem -> dart -> light -> followSystem -> ... - switch (crtThemeType) { + switch (themeType) { case AppThemeType.light: nextThemeType = brightness == Brightness.light ? AppThemeType.dark @@ -69,11 +72,12 @@ class AppThemeViewModel extends ChangeNotifier //#endregion //#region display page occupy percentage - int get displayPageOccupyPrt => _g.displayPageCalendarBarOccupyPrt; + int get displayPageOccupyPrt => + _calOccupy?.get() ?? appCalendarBarDefualtOccupyPrt; Future setNewDisplayPageOccupyPrt(int newPrt) async { - if (_g.displayPageCalendarBarOccupyPrt != newPrt) { - await _g.profile.setDisplayCalendarBarOccupyPrt(newPrt); + if (_calOccupy?.get() != newPrt) { + await _calOccupy?.set(newPrt); notifyListeners(); } } diff --git a/lib/view/app.dart b/lib/view/app.dart index bb72129a..9899ce47 100644 --- a/lib/view/app.dart +++ b/lib/view/app.dart @@ -22,7 +22,9 @@ import '../common/global.dart'; import '../l10n/localizations.dart'; import '../model/global.dart'; import '../persistent/db_helper_builder.dart'; +import '../persistent/profile/handlers.dart'; import '../persistent/profile_builder.dart'; +import '../persistent/profile_provider.dart'; import '../provider/app_theme.dart'; import '../theme/color.dart'; import 'common/_widget.dart'; @@ -32,10 +34,16 @@ import 'page_habits_display.dart' show PageHabitsDisplay; class App extends StatelessWidget { const App({super.key}); + Iterable _buildProfileHanlder() sync* { + yield (pref) => AppThemeTypeProfileHandler(pref); + yield (pref) => DisplayCalendartBarOccupyPrtProfileHandler(pref); + } + @override Widget build(BuildContext context) { debugPrint('------ App start ------'); return ProfileBuilder( + handlers: _buildProfileHanlder(), builder: (context, child) => DBHelperBuilder( builder: (context, child) => AppProviders(child: child), child: const AppView(), diff --git a/lib/view/for_app/app_providers.dart b/lib/view/for_app/app_providers.dart index 3dcf4328..9d62b7b1 100644 --- a/lib/view/for_app/app_providers.dart +++ b/lib/view/for_app/app_providers.dart @@ -19,6 +19,7 @@ import 'package:provider/provider.dart'; import '../../l10n/localizations.dart'; import '../../model/global.dart'; import '../../persistent/db_helper_provider.dart'; +import '../../persistent/profile_provider.dart'; import '../../provider/app_compact_ui_switcher.dart'; import '../../provider/app_custom_date_format.dart'; import '../../provider/app_developer.dart'; @@ -64,11 +65,10 @@ class AppProviders extends SingleChildStatelessWidget { ChangeNotifierProvider( create: (context) => NotificationChannelData(), ), - ChangeNotifierProxyProvider( - create: (context) => - AppThemeViewModel(global: context.read()), - update: (context, value, previous) => - previous!..updateGlobal(value), + ChangeNotifierProxyProvider( + create: (context) => AppThemeViewModel(), + update: (context, profile, previous) => + previous!..updateProfile(profile), ), ChangeNotifierProxyProvider( create: (context) => diff --git a/test/model_test/profiles_test.dart b/test/model_test/profiles_test.dart index 9fc246c2..c67ecac2 100644 --- a/test/model_test/profiles_test.dart +++ b/test/model_test/profiles_test.dart @@ -16,40 +16,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mhabit/common/consts.dart'; import 'package:mhabit/db/profile.dart'; -import 'package:mhabit/theme/color.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() async { SharedPreferences.setMockInitialValues({}); final setting = Profile(); await setting.init(); - group("AppSettingSharedPrefModel:themeType", () { - test('getThemeType', () async { - await setting.clearAll(); - final int thisAppThemeType = setting.getThemeType(); - expect(thisAppThemeType, AppThemeType.followSystem.dbCode); - }); - test('setThemeType To Dark', () async { - await setting.clearAll(); - final result = await setting.setThemeType(AppThemeType.dark); - expect(result, true); - expect(setting.getThemeType(), AppThemeType.dark.dbCode); - }); - test('setThemeType To Light', () async { - await setting.clearAll(); - final result = await setting.setThemeType(AppThemeType.light); - expect(result, true); - expect(setting.getThemeType(), AppThemeType.light.dbCode); - }); - test('setThemeType Error', () async { - await setting.clearAll(); - try { - await setting.setThemeType(AppThemeType.unknown); - } catch (err) { - expect(err, isInstanceOf()); - } - }); - }); group("AppSettingSharedPrefModel:sysThemeMainColor", () { test('getSysThemeMainColor', () async { await setting.clearAll(); diff --git a/test/viewmodel_test/app_theme_test.dart b/test/viewmodel_test/app_theme_test.dart index b5a48731..05a65bbf 100644 --- a/test/viewmodel_test/app_theme_test.dart +++ b/test/viewmodel_test/app_theme_test.dart @@ -15,22 +15,24 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mhabit/common/consts.dart'; -import 'package:mhabit/db/profile.dart'; -import 'package:mhabit/model/global.dart'; +import 'package:mhabit/persistent/profile/handlers.dart'; +import 'package:mhabit/persistent/profile_provider.dart'; import 'package:mhabit/provider/app_theme.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() async { SharedPreferences.setMockInitialValues({}); - await Profile().init(); - var g = Global(); + final profile = ProfileViewModel([ + (pref) => AppThemeTypeProfileHandler(pref), + ]); + await profile.init(); group("AppThemeViewModel", () { test("getThemeType", () async { - final obj = AppThemeViewModel(global: g); + final obj = AppThemeViewModel()..updateProfile(profile); expect(obj.themeType, appDefaultThemeType); }); test("getMatertialThemeType", () async { - final obj = AppThemeViewModel(global: g); + final obj = AppThemeViewModel()..updateProfile(profile); expect(obj.matertialThemeType, ThemeMode.system); }); }); From 1ec2fa62fa4aeb551896a51e588e8ae19879b7bb Mon Sep 17 00:00:00 2001 From: Fries_I23 Date: Sun, 31 Mar 2024 15:21:21 +0800 Subject: [PATCH 03/13] refactor: app theme main color --- lib/common/consts.dart | 4 +- lib/db/profile.dart | 33 -------- lib/model/global.dart | 6 -- .../profile/handler/app_theme_main_color.dart | 52 ++++++++++++ lib/persistent/profile/handlers.dart | 1 + lib/persistent/profile_builder.dart | 2 +- lib/persistent/profile_provider.dart | 2 +- lib/provider/app_theme.dart | 6 ++ lib/view/app.dart | 83 ++++++++++--------- test/model_test/profiles_test.dart | 38 --------- 10 files changed, 110 insertions(+), 117 deletions(-) create mode 100644 lib/persistent/profile/handler/app_theme_main_color.dart delete mode 100644 test/model_test/profiles_test.dart diff --git a/lib/common/consts.dart b/lib/common/consts.dart index a214a3c4..7a641236 100644 --- a/lib/common/consts.dart +++ b/lib/common/consts.dart @@ -13,6 +13,8 @@ // limitations under the License. // coverage:ignore-file +import 'dart:ui'; + import '../model/app_reminder_config.dart'; import '../model/habit_display.dart'; import '../model/habit_form.dart'; @@ -35,7 +37,7 @@ const int appDBVersion = 3; //#endregion //#region app-theme -const int appDefaultThemeMainColor = 0xFF006493; +const Color appDefaultThemeMainColor = Color(0xFF006493); const AppThemeType appDefaultThemeType = AppThemeType.followSystem; const int appCalendarBarMaxOccupyPrt = 70; const int appCalendarBarMinOccupyPrt = 20; diff --git a/lib/db/profile.dart b/lib/db/profile.dart index 236eba1e..a36be29a 100644 --- a/lib/db/profile.dart +++ b/lib/db/profile.dart @@ -15,7 +15,6 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:tuple/tuple.dart'; @@ -37,9 +36,6 @@ mixin CacheInterface { abstract class ProfileInterface { Future clearAll(); - int getSysThemeMainColor(); - Future setSysThemeMainColor(Color newColor); - Tuple2 getSortMode(); Future setSortMode(HabitDisplaySortType t, HabitDisplaySortDirection d); @@ -59,9 +55,6 @@ abstract class ProfileInterface { CustomDateYmdHmsConfig? getCustomDateYmdHmsConfig(); Future setCustomDateYmdHmsConfig(CustomDateYmdHmsConfig newConfig); - int getDisplayCalendarBarOccupyPrt(); - Future setDisplayCalendarBarOccupyPrt(int newPrt); - bool getCompactUISwticher(); Future setCompactUISwitcher(bool newStatus); @@ -70,15 +63,12 @@ abstract class ProfileInterface { } enum ProfileKey { - themeType, - sysThemeMainColor, habitSortMode, habitDisplayFilter, habitsRecordScrollBehavior, firstDay, appReminder, customDateYmdHmsConfig, - displayCalendarBarOccupyPrt, compactUISwitcher, displayOpConfig, // cache @@ -112,17 +102,6 @@ class Profile return _pref.clear(); } - @override - int getSysThemeMainColor() { - return _pref.getInt(ProfileKey.sysThemeMainColor.name) ?? - appDefaultThemeMainColor; - } - - @override - Future setSysThemeMainColor(Color newColor) async { - return _pref.setInt(ProfileKey.sysThemeMainColor.name, newColor.value); - } - @override Tuple2 getSortMode() { var raw = _pref.getString(ProfileKey.habitSortMode.name); @@ -211,18 +190,6 @@ class Profile ProfileKey.customDateYmdHmsConfig.name, jsonEncode(newConfig.toJson())); } - @override - int getDisplayCalendarBarOccupyPrt() { - return _pref.getInt(ProfileKey.displayCalendarBarOccupyPrt.name) ?? - appCalendarBarDefualtOccupyPrt; - } - - @override - Future setDisplayCalendarBarOccupyPrt(int newPrt) { - return _pref.setInt(ProfileKey.displayCalendarBarOccupyPrt.name, - normalizeAppCalendarBarOccupyPrt(newPrt)); - } - @override bool getCompactUISwticher() { return _pref.getBool(ProfileKey.compactUISwitcher.name) ?? false; diff --git a/lib/model/global.dart b/lib/model/global.dart index 7261d3ce..81f6b2c9 100644 --- a/lib/model/global.dart +++ b/lib/model/global.dart @@ -13,7 +13,6 @@ // limitations under the License. import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; import '../common/consts.dart'; import '../common/enums.dart'; @@ -74,8 +73,6 @@ class Global @override Profile get profile => Profile(); - Color get themeMainColor => Color(profile.getSysThemeMainColor()); - HabitDisplaySortType get sortType => HabitDisplaySortType.getFromDBCode(profile.getSortMode().item1)!; HabitDisplaySortDirection get sortDirection => @@ -98,9 +95,6 @@ class Global CustomDateYmdHmsConfig get customDateYmdHmsConfig => profile.getCustomDateYmdHmsConfig(); - int get displayPageCalendarBarOccupyPrt => - profile.getDisplayCalendarBarOccupyPrt(); - bool get compactUISwitcher => profile.getCompactUISwticher(); HabitDisplayOpConfig get displayOpConfig => profile.getDisplayOpConfig(); diff --git a/lib/persistent/profile/handler/app_theme_main_color.dart b/lib/persistent/profile/handler/app_theme_main_color.dart new file mode 100644 index 00000000..e16691bc --- /dev/null +++ b/lib/persistent/profile/handler/app_theme_main_color.dart @@ -0,0 +1,52 @@ +// Copyright 2024 Fries_I23 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:convert'; + +import 'package:flutter/material.dart'; + +import '../profile_helper.dart'; + +final class AppThemeMainColorProfileHandler + extends ProfileHelperCovertToIntHandler { + AppThemeMainColorProfileHandler(super.pref) + : super(codec: const AppThemeMainColorProfileCodes()); + + @override + String get key => "sysThemeMainColor"; +} + +final class AppThemeMainColorProfileCodes extends Codec { + const AppThemeMainColorProfileCodes(); + + @override + Converter get decoder => const _Decoder(); + + @override + Converter get encoder => const _Encoder(); +} + +final class _Decoder extends Converter { + const _Decoder(); + + @override + Color convert(int input) => Color(input); +} + +final class _Encoder extends Converter { + const _Encoder(); + + @override + int convert(Color input) => input.value; +} diff --git a/lib/persistent/profile/handlers.dart b/lib/persistent/profile/handlers.dart index 23eca8de..8635a0f4 100644 --- a/lib/persistent/profile/handlers.dart +++ b/lib/persistent/profile/handlers.dart @@ -12,5 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. +export './handler/app_theme_main_color.dart'; export './handler/app_theme_type.dart'; export './handler/display_calendar_bar_occupy_prt.dart'; diff --git a/lib/persistent/profile_builder.dart b/lib/persistent/profile_builder.dart index a9cebb8f..d65c1fa7 100644 --- a/lib/persistent/profile_builder.dart +++ b/lib/persistent/profile_builder.dart @@ -39,7 +39,7 @@ class ProfileBuilder extends SingleChildStatelessWidget { appLog.db.info("$runtimeType._loadingHelper", ex: ["processing"]); final helper = context.read(); if (helper.mounted && !helper.inited) await helper.init(); - appLog.db.info("$runtimeType._loadingHelper", ex: ["done"]); + appLog.db.info("$runtimeType._loadingHelper", ex: ["done", helper]); } @override diff --git a/lib/persistent/profile_provider.dart b/lib/persistent/profile_provider.dart index 92c0fc11..875526a8 100644 --- a/lib/persistent/profile_provider.dart +++ b/lib/persistent/profile_provider.dart @@ -84,7 +84,7 @@ class ProfileViewModel extends ChangeNotifier @override String toString() { return "$runtimeType[$hashCode](pref=$_pref,mounted=$mounted," - "inited=$inited)"; + "inited=$inited,handlers=$_handlers)"; } } diff --git a/lib/provider/app_theme.dart b/lib/provider/app_theme.dart index 38d5fedb..a6317a1a 100644 --- a/lib/provider/app_theme.dart +++ b/lib/provider/app_theme.dart @@ -22,6 +22,7 @@ import '../theme/color.dart'; class AppThemeViewModel extends ChangeNotifier with ProfileHandlerLoadedMixin { AppThemeTypeProfileHandler? _theme; + AppThemeMainColorProfileHandler? _mainColor; DisplayCalendartBarOccupyPrtProfileHandler? _calOccupy; AppThemeViewModel(); @@ -30,6 +31,7 @@ class AppThemeViewModel extends ChangeNotifier with ProfileHandlerLoadedMixin { void updateProfile(ProfileViewModel newProfile) { super.updateProfile(newProfile); _theme = newProfile.getHandler(); + _mainColor = newProfile.getHandler(); _calOccupy = newProfile.getHandler(); } @@ -71,6 +73,10 @@ class AppThemeViewModel extends ChangeNotifier with ProfileHandlerLoadedMixin { } //#endregion + //#region app theme main color + Color get mainColor => _mainColor?.get() ?? appDefaultThemeMainColor; + //#endregion + //#region display page occupy percentage int get displayPageOccupyPrt => _calOccupy?.get() ?? appCalendarBarDefualtOccupyPrt; diff --git a/lib/view/app.dart b/lib/view/app.dart index 9899ce47..7ac4a1ae 100644 --- a/lib/view/app.dart +++ b/lib/view/app.dart @@ -16,11 +16,11 @@ import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:tuple/tuple.dart'; import '../common/consts.dart'; import '../common/global.dart'; import '../l10n/localizations.dart'; -import '../model/global.dart'; import '../persistent/db_helper_builder.dart'; import '../persistent/profile/handlers.dart'; import '../persistent/profile_builder.dart'; @@ -36,6 +36,7 @@ class App extends StatelessWidget { Iterable _buildProfileHanlder() sync* { yield (pref) => AppThemeTypeProfileHandler(pref); + yield (pref) => AppThemeMainColorProfileHandler(pref); yield (pref) => DisplayCalendartBarOccupyPrtProfileHandler(pref); } @@ -61,9 +62,9 @@ class AppView extends StatefulWidget { class _AppView extends State { ThemeData _getLightThemeData(BuildContext context, - {ColorScheme? dynamicColor}) { + {ColorScheme? dynamicColor, required Color mainColor}) { ColorScheme appColorLight = ColorScheme.fromSeed( - seedColor: context.read().themeMainColor, + seedColor: mainColor, brightness: Brightness.light, ); return ThemeData( @@ -74,9 +75,9 @@ class _AppView extends State { } ThemeData _getDartThemeData(BuildContext context, - {ColorScheme? dynamicColor}) { + {ColorScheme? dynamicColor, required Color mainColor}) { ColorScheme appColorDark = ColorScheme.fromSeed( - seedColor: context.read().themeMainColor, + seedColor: mainColor, brightness: Brightness.dark, ); return ThemeData( @@ -90,40 +91,48 @@ class _AppView extends State { Widget build(BuildContext context) { var homePage = const PageHabitsDisplay(); - return Selector( - selector: (context, viewmodel) => viewmodel.matertialThemeType, + return Selector>( + selector: (context, viewmodel) => + Tuple2(viewmodel.matertialThemeType, viewmodel.mainColor), shouldRebuild: (previous, next) => previous != next, - builder: (context, themeMode, child) => DynamicColorBuilder( - builder: (lightDynamic, darkDynamic) => DateChanger( - interval: const Duration(seconds: 10), - builder: (context) => MaterialApp( - onGenerateTitle: (context) => L10n.of(context)?.appName ?? appName, - scaffoldMessengerKey: snackbarKey, - theme: _getLightThemeData(context, dynamicColor: lightDynamic), - darkTheme: _getDartThemeData(context, dynamicColor: darkDynamic), - themeMode: themeMode, - home: child, - localizationsDelegates: const [ - L10n.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: const [ - // Fixed #133 - // en must be the first item in the list (default language) - Locale.fromSubtags(languageCode: 'en'), - Locale.fromSubtags(languageCode: 'ar'), - Locale.fromSubtags(languageCode: 'de'), - Locale.fromSubtags(languageCode: 'fa'), - Locale.fromSubtags(languageCode: 'fr'), - Locale.fromSubtags(languageCode: 'vi'), - Locale.fromSubtags(languageCode: 'zh'), - ], - debugShowCheckedModeBanner: false, + builder: (context, appThemeArgs, child) { + final themeMode = appThemeArgs.item1; + final themeMainColor = appThemeArgs.item2; + return DynamicColorBuilder( + builder: (lightDynamic, darkDynamic) => DateChanger( + interval: const Duration(seconds: 10), + builder: (context) => MaterialApp( + onGenerateTitle: (context) => + L10n.of(context)?.appName ?? appName, + scaffoldMessengerKey: snackbarKey, + theme: _getLightThemeData(context, + dynamicColor: lightDynamic, mainColor: themeMainColor), + darkTheme: _getDartThemeData(context, + dynamicColor: darkDynamic, mainColor: themeMainColor), + themeMode: themeMode, + home: child, + localizationsDelegates: const [ + L10n.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: const [ + // Fixed #133 + // en must be the first item in the list (default language) + Locale.fromSubtags(languageCode: 'en'), + Locale.fromSubtags(languageCode: 'ar'), + Locale.fromSubtags(languageCode: 'de'), + Locale.fromSubtags(languageCode: 'fa'), + Locale.fromSubtags(languageCode: 'fr'), + Locale.fromSubtags(languageCode: 'vi'), + Locale.fromSubtags(languageCode: 'zh'), + ], + debugShowCheckedModeBanner: false, + ), ), - ), - ), + ); + }, child: homePage, ); } diff --git a/test/model_test/profiles_test.dart b/test/model_test/profiles_test.dart deleted file mode 100644 index c67ecac2..00000000 --- a/test/model_test/profiles_test.dart +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2023 Fries_I23 -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mhabit/common/consts.dart'; -import 'package:mhabit/db/profile.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -void main() async { - SharedPreferences.setMockInitialValues({}); - final setting = Profile(); - await setting.init(); - group("AppSettingSharedPrefModel:sysThemeMainColor", () { - test('getSysThemeMainColor', () async { - await setting.clearAll(); - expect(setting.getSysThemeMainColor(), appDefaultThemeMainColor); - }); - test('setSysThemeMainColor', () async { - await setting.clearAll(); - const newColor = 0xFF8BC34A; - final result = await setting.setSysThemeMainColor(const Color(newColor)); - expect(result, true); - expect(setting.getSysThemeMainColor(), newColor); - }); - }); -} From 87d5da942978a38b13e58090717fc1e6dd7bc0a7 Mon Sep 17 00:00:00 2001 From: Fries_I23 Date: Sun, 31 Mar 2024 15:52:57 +0800 Subject: [PATCH 04/13] refactor: app compact ui switcher --- lib/db/profile.dart | 14 ---------- lib/model/global.dart | 2 -- lib/persistent/profile/converter.dart | 4 +-- .../profile/handler/app_theme_main_color.dart | 2 +- .../profile/handler/compact_ui_switcher.dart | 25 ++++++++++++++++++ .../display_calendar_bar_occupy_prt.dart | 2 +- lib/persistent/profile/handlers.dart | 1 + lib/persistent/profile/profile_helper.dart | 15 +++++++++++ lib/provider/app_compact_ui_switcher.dart | 26 +++++++++++-------- lib/view/app.dart | 1 + lib/view/for_app/app_providers.dart | 10 +++---- 11 files changed, 66 insertions(+), 36 deletions(-) create mode 100644 lib/persistent/profile/handler/compact_ui_switcher.dart diff --git a/lib/db/profile.dart b/lib/db/profile.dart index a36be29a..f90288bc 100644 --- a/lib/db/profile.dart +++ b/lib/db/profile.dart @@ -55,9 +55,6 @@ abstract class ProfileInterface { CustomDateYmdHmsConfig? getCustomDateYmdHmsConfig(); Future setCustomDateYmdHmsConfig(CustomDateYmdHmsConfig newConfig); - bool getCompactUISwticher(); - Future setCompactUISwitcher(bool newStatus); - HabitDisplayOpConfig? getDisplayOpConfig(); Future setDisplayOpConfig(HabitDisplayOpConfig opConfig); } @@ -69,7 +66,6 @@ enum ProfileKey { firstDay, appReminder, customDateYmdHmsConfig, - compactUISwitcher, displayOpConfig, // cache inputFillCache, @@ -190,16 +186,6 @@ class Profile ProfileKey.customDateYmdHmsConfig.name, jsonEncode(newConfig.toJson())); } - @override - bool getCompactUISwticher() { - return _pref.getBool(ProfileKey.compactUISwitcher.name) ?? false; - } - - @override - Future setCompactUISwitcher(bool newStatus) { - return _pref.setBool(ProfileKey.compactUISwitcher.name, newStatus); - } - @override Map getInputFillCache() { final raw = _pref.getString(ProfileKey.inputFillCache.name); diff --git a/lib/model/global.dart b/lib/model/global.dart index 81f6b2c9..bc682c74 100644 --- a/lib/model/global.dart +++ b/lib/model/global.dart @@ -95,7 +95,5 @@ class Global CustomDateYmdHmsConfig get customDateYmdHmsConfig => profile.getCustomDateYmdHmsConfig(); - bool get compactUISwitcher => profile.getCompactUISwticher(); - HabitDisplayOpConfig get displayOpConfig => profile.getDisplayOpConfig(); } diff --git a/lib/persistent/profile/converter.dart b/lib/persistent/profile/converter.dart index 3e7fc429..3141b0ec 100644 --- a/lib/persistent/profile/converter.dart +++ b/lib/persistent/profile/converter.dart @@ -18,10 +18,10 @@ class SameTypeCodec extends Codec { const SameTypeCodec(); @override - Converter get decoder => const _Converter(); + Converter get decoder => _Converter(); @override - Converter get encoder => const _Converter(); + Converter get encoder => _Converter(); } class _Converter extends Converter { diff --git a/lib/persistent/profile/handler/app_theme_main_color.dart b/lib/persistent/profile/handler/app_theme_main_color.dart index e16691bc..4ce3831c 100644 --- a/lib/persistent/profile/handler/app_theme_main_color.dart +++ b/lib/persistent/profile/handler/app_theme_main_color.dart @@ -20,7 +20,7 @@ import '../profile_helper.dart'; final class AppThemeMainColorProfileHandler extends ProfileHelperCovertToIntHandler { - AppThemeMainColorProfileHandler(super.pref) + const AppThemeMainColorProfileHandler(super.pref) : super(codec: const AppThemeMainColorProfileCodes()); @override diff --git a/lib/persistent/profile/handler/compact_ui_switcher.dart b/lib/persistent/profile/handler/compact_ui_switcher.dart new file mode 100644 index 00000000..1e4972c6 --- /dev/null +++ b/lib/persistent/profile/handler/compact_ui_switcher.dart @@ -0,0 +1,25 @@ +// Copyright 2024 Fries_I23 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import '../converter.dart'; +import '../profile_helper.dart'; + +class CompactUISwitcherProfileHandler + extends ProfileHelperCovertToBoolHandler { + const CompactUISwitcherProfileHandler(super.pref) + : super(codec: const SameTypeCodec()); + + @override + String get key => "compactUISwitcher"; +} diff --git a/lib/persistent/profile/handler/display_calendar_bar_occupy_prt.dart b/lib/persistent/profile/handler/display_calendar_bar_occupy_prt.dart index 60ce10a5..f657c41c 100644 --- a/lib/persistent/profile/handler/display_calendar_bar_occupy_prt.dart +++ b/lib/persistent/profile/handler/display_calendar_bar_occupy_prt.dart @@ -17,7 +17,7 @@ import '../profile_helper.dart'; final class DisplayCalendartBarOccupyPrtProfileHandler extends ProfileHelperCovertToIntHandler { - DisplayCalendartBarOccupyPrtProfileHandler(super.pref) + const DisplayCalendartBarOccupyPrtProfileHandler(super.pref) : super(codec: const SameTypeCodec()); @override diff --git a/lib/persistent/profile/handlers.dart b/lib/persistent/profile/handlers.dart index 8635a0f4..ee14f4c6 100644 --- a/lib/persistent/profile/handlers.dart +++ b/lib/persistent/profile/handlers.dart @@ -14,4 +14,5 @@ export './handler/app_theme_main_color.dart'; export './handler/app_theme_type.dart'; +export './handler/compact_ui_switcher.dart'; export './handler/display_calendar_bar_occupy_prt.dart'; diff --git a/lib/persistent/profile/profile_helper.dart b/lib/persistent/profile/profile_helper.dart index 56e7e85f..8daf27c6 100644 --- a/lib/persistent/profile/profile_helper.dart +++ b/lib/persistent/profile/profile_helper.dart @@ -56,3 +56,18 @@ abstract class ProfileHelperCovertToIntHandler @override Future Function(String key, int value) get setMethod => _pref.setInt; } + +abstract class ProfileHelperCovertToBoolHandler + extends ProfileHelperConvertHandler { + final SharedPreferences _pref; + + const ProfileHelperCovertToBoolHandler(SharedPreferences pref, + {required super.codec}) + : _pref = pref; + + @override + bool? Function(String key) get getMethod => _pref.getBool; + + @override + Future Function(String key, bool value) get setMethod => _pref.setBool; +} diff --git a/lib/provider/app_compact_ui_switcher.dart b/lib/provider/app_compact_ui_switcher.dart index 4ba1789c..1454b148 100644 --- a/lib/provider/app_compact_ui_switcher.dart +++ b/lib/provider/app_compact_ui_switcher.dart @@ -14,25 +14,29 @@ import 'package:flutter/material.dart'; -import '../model/global.dart'; +import '../logging/helper.dart'; +import '../persistent/profile/handler/compact_ui_switcher.dart'; +import '../persistent/profile_provider.dart'; class AppCompactUISwitcherViewModel extends ChangeNotifier - implements GlobalProxyProviderInterface { - Global _g; + with ProfileHandlerLoadedMixin { + CompactUISwitcherProfileHandler? _compactUI; - AppCompactUISwitcherViewModel({required Global global}) : _g = global; + AppCompactUISwitcherViewModel(); @override - Global get g => _g; - - @override - void updateGlobal(Global newGloal) => _g = newGloal; + void updateProfile(ProfileViewModel newProfile) { + super.updateProfile(newProfile); + _compactUI = newProfile.getHandler(); + } - bool get flag => g.compactUISwitcher; + bool get flag => _compactUI?.get() ?? false; Future setFlag(bool newFlag) async { - if (g.compactUISwitcher != newFlag) { - await g.profile.setCompactUISwitcher(newFlag); + if (_compactUI?.get() != newFlag) { + appLog.value + .info("$runtimeType.flag", beforeVal: flag, afterVal: newFlag); + await _compactUI?.set(newFlag); notifyListeners(); } } diff --git a/lib/view/app.dart b/lib/view/app.dart index 7ac4a1ae..968e55ca 100644 --- a/lib/view/app.dart +++ b/lib/view/app.dart @@ -37,6 +37,7 @@ class App extends StatelessWidget { Iterable _buildProfileHanlder() sync* { yield (pref) => AppThemeTypeProfileHandler(pref); yield (pref) => AppThemeMainColorProfileHandler(pref); + yield (pref) => CompactUISwitcherProfileHandler(pref); yield (pref) => DisplayCalendartBarOccupyPrtProfileHandler(pref); } diff --git a/lib/view/for_app/app_providers.dart b/lib/view/for_app/app_providers.dart index 9d62b7b1..b5341721 100644 --- a/lib/view/for_app/app_providers.dart +++ b/lib/view/for_app/app_providers.dart @@ -70,11 +70,11 @@ class AppProviders extends SingleChildStatelessWidget { update: (context, profile, previous) => previous!..updateProfile(profile), ), - ChangeNotifierProxyProvider( - create: (context) => - AppCompactUISwitcherViewModel(global: context.read()), - update: (context, value, previous) => - previous!..updateGlobal(value), + ChangeNotifierProxyProvider( + create: (context) => AppCompactUISwitcherViewModel(), + update: (context, profile, previous) => + previous!..updateProfile(profile), ), ChangeNotifierProxyProvider( create: (context) => From dc188810ba5d74caef6e0dd8b27747c0204ef1f7 Mon Sep 17 00:00:00 2001 From: Fries_I23 Date: Sun, 31 Mar 2024 16:39:27 +0800 Subject: [PATCH 05/13] refactor: displayOpConfig setting --- lib/common/types.dart | 2 + lib/db/profile.dart | 18 ------- lib/model/global.dart | 2 - lib/model/habit_display.dart | 3 ++ .../handler/habit_cell_gesture_mode.dart | 54 +++++++++++++++++++ lib/persistent/profile/handlers.dart | 1 + lib/persistent/profile/profile_helper.dart | 25 +++++++++ lib/persistent/profile_provider.dart | 15 ++++-- lib/provider/habit_op_config.dart | 54 +++++++++---------- lib/view/app.dart | 1 + lib/view/for_app/app_providers.dart | 10 ++-- 11 files changed, 130 insertions(+), 55 deletions(-) create mode 100644 lib/persistent/profile/handler/habit_cell_gesture_mode.dart diff --git a/lib/common/types.dart b/lib/common/types.dart index 94cb748c..dfc25dcf 100644 --- a/lib/common/types.dart +++ b/lib/common/types.dart @@ -21,6 +21,8 @@ import '../model/habit_form.dart'; typedef DBID = int; +typedef JsonMap = Map; + typedef HabitUUID = String; typedef HabitRecordUUID = String; typedef HabitDailyGoal = num; diff --git a/lib/db/profile.dart b/lib/db/profile.dart index f90288bc..ccb8ba98 100644 --- a/lib/db/profile.dart +++ b/lib/db/profile.dart @@ -54,9 +54,6 @@ abstract class ProfileInterface { CustomDateYmdHmsConfig? getCustomDateYmdHmsConfig(); Future setCustomDateYmdHmsConfig(CustomDateYmdHmsConfig newConfig); - - HabitDisplayOpConfig? getDisplayOpConfig(); - Future setDisplayOpConfig(HabitDisplayOpConfig opConfig); } enum ProfileKey { @@ -66,7 +63,6 @@ enum ProfileKey { firstDay, appReminder, customDateYmdHmsConfig, - displayOpConfig, // cache inputFillCache, } @@ -204,18 +200,4 @@ class Profile return _pref.setString( ProfileKey.inputFillCache.name, jsonEncode(newCache)); } - - @override - HabitDisplayOpConfig getDisplayOpConfig() { - final raw = _pref.getString(ProfileKey.displayOpConfig.name); - return raw != null - ? HabitDisplayOpConfig.fromJson(jsonDecode(raw)) - : const HabitDisplayOpConfig.withDefault(); - } - - @override - Future setDisplayOpConfig(HabitDisplayOpConfig opConfig) { - return _pref.setString( - ProfileKey.displayOpConfig.name, jsonEncode(opConfig.toJson())); - } } diff --git a/lib/model/global.dart b/lib/model/global.dart index bc682c74..bcab3ea9 100644 --- a/lib/model/global.dart +++ b/lib/model/global.dart @@ -94,6 +94,4 @@ class Global CustomDateYmdHmsConfig get customDateYmdHmsConfig => profile.getCustomDateYmdHmsConfig(); - - HabitDisplayOpConfig get displayOpConfig => profile.getDisplayOpConfig(); } diff --git a/lib/model/habit_display.dart b/lib/model/habit_display.dart index 9519f159..68be8012 100644 --- a/lib/model/habit_display.dart +++ b/lib/model/habit_display.dart @@ -219,4 +219,7 @@ class HabitDisplayOpConfig implements JsonAdaptor { @override Map toJson() => _$HabitDisplayOpConfigToJson(this); + + @override + String toString() => "$runtimeType(${toJson()})"; } diff --git a/lib/persistent/profile/handler/habit_cell_gesture_mode.dart b/lib/persistent/profile/handler/habit_cell_gesture_mode.dart new file mode 100644 index 00000000..1d3c3d70 --- /dev/null +++ b/lib/persistent/profile/handler/habit_cell_gesture_mode.dart @@ -0,0 +1,54 @@ +// Copyright 2024 Fries_I23 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:convert'; + +import '../../../common/types.dart'; +import '../../../model/habit_display.dart'; +import '../profile_helper.dart'; + +final class HabitCellGestureModeProfileHandler + extends ProfileHelperCovertToJsonHandler { + HabitCellGestureModeProfileHandler(super.pref) + : super(codec: const HabitCellGestureModeCodec()); + + @override + String get key => "displayOpConfig"; +} + +final class HabitCellGestureModeCodec + extends Codec { + const HabitCellGestureModeCodec(); + + @override + Converter get decoder => const _Decoder(); + + @override + Converter get encoder => const _Encoder(); +} + +final class _Decoder extends Converter { + const _Decoder(); + + @override + HabitDisplayOpConfig convert(JsonMap input) => + HabitDisplayOpConfig.fromJson(input); +} + +final class _Encoder extends Converter { + const _Encoder(); + + @override + JsonMap convert(HabitDisplayOpConfig input) => input.toJson(); +} diff --git a/lib/persistent/profile/handlers.dart b/lib/persistent/profile/handlers.dart index ee14f4c6..cc57885e 100644 --- a/lib/persistent/profile/handlers.dart +++ b/lib/persistent/profile/handlers.dart @@ -16,3 +16,4 @@ export './handler/app_theme_main_color.dart'; export './handler/app_theme_type.dart'; export './handler/compact_ui_switcher.dart'; export './handler/display_calendar_bar_occupy_prt.dart'; +export './handler/habit_cell_gesture_mode.dart'; diff --git a/lib/persistent/profile/profile_helper.dart b/lib/persistent/profile/profile_helper.dart index 8daf27c6..e1d91cc0 100644 --- a/lib/persistent/profile/profile_helper.dart +++ b/lib/persistent/profile/profile_helper.dart @@ -16,6 +16,8 @@ import 'dart:convert'; import 'package:shared_preferences/shared_preferences.dart'; +import '../../common/types.dart'; + abstract interface class ProfileHelperHandler { String get key; Future set(T value); @@ -71,3 +73,26 @@ abstract class ProfileHelperCovertToBoolHandler @override Future Function(String key, bool value) get setMethod => _pref.setBool; } + +abstract class ProfileHelperCovertToJsonHandler + extends ProfileHelperConvertHandler { + final SharedPreferences _pref; + + const ProfileHelperCovertToJsonHandler(SharedPreferences pref, + {required super.codec}) + : _pref = pref; + + JsonMap? _getMethod(String key) { + final source = _pref.getString(key); + return source != null ? jsonDecode(source) : null; + } + + @override + JsonMap? Function(String key) get getMethod => _getMethod; + + Future _setMethod(String key, JsonMap value) => + _pref.setString(key, jsonEncode(value)); + + @override + Future Function(String key, JsonMap value) get setMethod => _setMethod; +} diff --git a/lib/persistent/profile_provider.dart b/lib/persistent/profile_provider.dart index 875526a8..59cd34e9 100644 --- a/lib/persistent/profile_provider.dart +++ b/lib/persistent/profile_provider.dart @@ -43,10 +43,19 @@ class ProfileViewModel extends ChangeNotifier Future init() async { if (_completer == null) { _completer = Completer(); + final handlerKeyColl = {}; _pref = await SharedPreferences.getInstance(); - _handlers = Map.fromEntries(_handlerBuilders - .map((e) => e.call(_pref)) - .map((e) => MapEntry(e.runtimeType, e))); + _handlers = + Map.fromEntries(_handlerBuilders.map((e) => e.call(_pref)).where((e) { + if (handlerKeyColl.containsKey(e.key)) { + appLog.load.error("$runtimeType.init", + ex: ["load handler failed", e, e.key, handlerKeyColl[e.key]]); + if (kDebugMode) throw FlutterError("load handler failed: $e"); + return false; + } + handlerKeyColl[e.key] = e.runtimeType; + return true; + }).map((e) => MapEntry(e.runtimeType, e))); _completer!.complete(); } return _completer!.future; diff --git a/lib/provider/habit_op_config.dart b/lib/provider/habit_op_config.dart index 684ecdf9..36c04223 100644 --- a/lib/provider/habit_op_config.dart +++ b/lib/provider/habit_op_config.dart @@ -15,51 +15,51 @@ import 'package:flutter/material.dart'; import '../common/enums.dart'; -import '../model/global.dart'; +import '../logging/helper.dart'; import '../model/habit_display.dart'; +import '../persistent/profile/handlers.dart'; +import '../persistent/profile_provider.dart'; class HabitRecordOpConfigViewModel extends ChangeNotifier - implements GlobalProxyProviderInterface { - Global _g; + with ProfileHandlerLoadedMixin { + HabitCellGestureModeProfileHandler? _op; - HabitRecordOpConfigViewModel({required Global global}) : _g = global; + HabitRecordOpConfigViewModel(); @override - Global get g => _g; + void updateProfile(ProfileViewModel newProfile) { + super.updateProfile(newProfile); + _op = newProfile.getHandler(); + } - @override - void updateGlobal(Global newGloal) => _g = newGloal; + HabitDisplayOpConfig get _opData => + _op?.get() ?? const HabitDisplayOpConfig.withDefault(); - UserAction get changeRecordStatus => _g.displayOpConfig.changeRecordStatus; - UserAction get openRecordStatusDialog => - _g.displayOpConfig.openRecordStatusDialog; + UserAction get changeRecordStatus => _opData.changeRecordStatus; + UserAction get openRecordStatusDialog => _opData.openRecordStatusDialog; - HabitDisplayOpConfig _generateExchangeActionsConfig() => - _g.displayOpConfig.copyWith( + HabitDisplayOpConfig _generateExchangeActionsConfig() => _opData.copyWith( changeRecordStatus: openRecordStatusDialog, openRecordStatusDialog: changeRecordStatus, ); void setChangeRecordStatusAction(UserAction newAction) async { - late final HabitDisplayOpConfig newOpConfig; - if (newAction == openRecordStatusDialog) { - newOpConfig = _generateExchangeActionsConfig(); - } else { - newOpConfig = _g.displayOpConfig.copyWith(changeRecordStatus: newAction); - } - await _g.profile.setDisplayOpConfig(newOpConfig); + final newOpConfig = newAction == openRecordStatusDialog + ? _generateExchangeActionsConfig() + : _opData.copyWith(changeRecordStatus: newAction); + appLog.value.info("$runtimeType.setChangeRecordStatusAction", + beforeVal: _opData, afterVal: newOpConfig); + await _op?.set(newOpConfig); notifyListeners(); } void setOpenRecordStatusDialogAction(UserAction newAction) async { - late final HabitDisplayOpConfig newOpConfig; - if (newAction == changeRecordStatus) { - newOpConfig = _generateExchangeActionsConfig(); - } else { - newOpConfig = - _g.displayOpConfig.copyWith(openRecordStatusDialog: newAction); - } - await _g.profile.setDisplayOpConfig(newOpConfig); + final newOpConfig = newAction == changeRecordStatus + ? _generateExchangeActionsConfig() + : _opData.copyWith(openRecordStatusDialog: newAction); + appLog.value.info("$runtimeType.setOpenRecordStatusDialogAction", + beforeVal: _opData, afterVal: newOpConfig); + await _op?.set(newOpConfig); notifyListeners(); } } diff --git a/lib/view/app.dart b/lib/view/app.dart index 968e55ca..87f25525 100644 --- a/lib/view/app.dart +++ b/lib/view/app.dart @@ -39,6 +39,7 @@ class App extends StatelessWidget { yield (pref) => AppThemeMainColorProfileHandler(pref); yield (pref) => CompactUISwitcherProfileHandler(pref); yield (pref) => DisplayCalendartBarOccupyPrtProfileHandler(pref); + yield (pref) => HabitCellGestureModeProfileHandler(pref); } @override diff --git a/lib/view/for_app/app_providers.dart b/lib/view/for_app/app_providers.dart index b5341721..523bf1f9 100644 --- a/lib/view/for_app/app_providers.dart +++ b/lib/view/for_app/app_providers.dart @@ -89,11 +89,11 @@ class AppProviders extends SingleChildStatelessWidget { update: (context, value, previous) => previous!..updateGlobal(value), ), - ChangeNotifierProxyProvider( - create: (context) => - HabitRecordOpConfigViewModel(global: context.read()), - update: (context, value, previous) => - previous!..updateGlobal(value), + ChangeNotifierProxyProvider( + create: (context) => HabitRecordOpConfigViewModel(), + update: (context, profile, previous) => + previous!..updateProfile(profile), ), ChangeNotifierProxyProvider2( From f8704bd5421141c30285bb5c7e3cf2a47aa83a5a Mon Sep 17 00:00:00 2001 From: Fries_I23 Date: Sun, 31 Mar 2024 17:21:21 +0800 Subject: [PATCH 06/13] refactor: sort mode --- lib/db/profile.dart | 23 ----- lib/model/global.dart | 5 -- .../profile/handler/display_sort_mode.dart | 90 +++++++++++++++++++ lib/persistent/profile/handlers.dart | 1 + lib/provider/habits_sort.dart | 35 +++++--- lib/view/app.dart | 1 + .../for_habits_display/page_providers.dart | 10 +-- 7 files changed, 118 insertions(+), 47 deletions(-) create mode 100644 lib/persistent/profile/handler/display_sort_mode.dart diff --git a/lib/db/profile.dart b/lib/db/profile.dart index ccb8ba98..71876612 100644 --- a/lib/db/profile.dart +++ b/lib/db/profile.dart @@ -16,7 +16,6 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:tuple/tuple.dart'; import '../common/abc.dart'; import '../common/consts.dart'; @@ -26,7 +25,6 @@ import '../common/utils.dart'; import '../logging/helper.dart'; import '../model/app_reminder_config.dart'; import '../model/custom_date_format.dart'; -import '../model/habit_display.dart'; mixin CacheInterface { Map getInputFillCache(); @@ -36,9 +34,6 @@ mixin CacheInterface { abstract class ProfileInterface { Future clearAll(); - Tuple2 getSortMode(); - Future setSortMode(HabitDisplaySortType t, HabitDisplaySortDirection d); - Map getHabitDisplayFilter(); Future setHabitDisplayFilter(Map filterMap); @@ -57,7 +52,6 @@ abstract class ProfileInterface { } enum ProfileKey { - habitSortMode, habitDisplayFilter, habitsRecordScrollBehavior, firstDay, @@ -94,23 +88,6 @@ class Profile return _pref.clear(); } - @override - Tuple2 getSortMode() { - var raw = _pref.getString(ProfileKey.habitSortMode.name); - if (raw == null) { - return Tuple2(HabitDisplaySortType.manual.dbCode, - HabitDisplaySortDirection.asc.dbCode); - } - return Tuple2.fromList(jsonDecode(raw)); - } - - @override - Future setSortMode( - HabitDisplaySortType t, HabitDisplaySortDirection d) { - return _pref.setString( - ProfileKey.habitSortMode.name, jsonEncode([t.dbCode, d.dbCode])); - } - @override Map getHabitDisplayFilter() { var raw = _pref.getString(ProfileKey.habitDisplayFilter.name); diff --git a/lib/model/global.dart b/lib/model/global.dart index bcab3ea9..c1eff03b 100644 --- a/lib/model/global.dart +++ b/lib/model/global.dart @@ -73,11 +73,6 @@ class Global @override Profile get profile => Profile(); - HabitDisplaySortType get sortType => - HabitDisplaySortType.getFromDBCode(profile.getSortMode().item1)!; - HabitDisplaySortDirection get sortDirection => - HabitDisplaySortDirection.getFromDBCode(profile.getSortMode().item2)!; - HabitsDisplayFilter get habitsDisplayFilter { var raw = profile.getHabitDisplayFilter(); return HabitsDisplayFilter.fromMap(raw); diff --git a/lib/persistent/profile/handler/display_sort_mode.dart b/lib/persistent/profile/handler/display_sort_mode.dart new file mode 100644 index 00000000..e60d7395 --- /dev/null +++ b/lib/persistent/profile/handler/display_sort_mode.dart @@ -0,0 +1,90 @@ +// Copyright 2024 Fries_I23 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:convert'; + +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:tuple/tuple.dart'; + +import '../../../model/habit_display.dart'; +import '../profile_helper.dart'; + +final class DisplaySortModeProfileHandler extends ProfileHelperConvertHandler< + Tuple2, List> { + final SharedPreferences _pref; + + const DisplaySortModeProfileHandler(SharedPreferences pref) + : _pref = pref, + super(codec: const DisplaySortModeCodec()); + + @override + String get key => "habitSortMode"; + + HabitDisplaySortType? get sortType => get()?.item1; + HabitDisplaySortDirection? get sortDirection => get()?.item2; + + List? _getMethod(String key) { + final source = _pref.getString(key); + return source != null ? jsonDecode(source) : null; + } + + @override + List? Function(String key) get getMethod => _getMethod; + + Future _setMethod(String key, List value) => + _pref.setString(key, jsonEncode(value)); + + @override + Future Function(String key, List value) get setMethod => _setMethod; +} + +final class DisplaySortModeCodec extends Codec< + Tuple2, List> { + const DisplaySortModeCodec(); + + @override + Converter> + get decoder => const _Decoder(); + + @override + Converter, List> + get encoder => const _Encoder(); +} + +final class _Decoder extends Converter> { + const _Decoder(); + + @override + Tuple2 convert( + List input) => + Tuple2( + ((input[0] as int?) != null) + ? HabitDisplaySortType.getFromDBCode(input[0]) + : null, + ((input[1] as int?) != null) + ? HabitDisplaySortDirection.getFromDBCode(input[1]) + : null, + ); +} + +final class _Encoder extends Converter< + Tuple2, List> { + const _Encoder(); + + @override + List convert( + Tuple2 input) => + [input.item1?.dbCode, input.item2?.dbCode]; +} diff --git a/lib/persistent/profile/handlers.dart b/lib/persistent/profile/handlers.dart index cc57885e..8251db94 100644 --- a/lib/persistent/profile/handlers.dart +++ b/lib/persistent/profile/handlers.dart @@ -17,3 +17,4 @@ export './handler/app_theme_type.dart'; export './handler/compact_ui_switcher.dart'; export './handler/display_calendar_bar_occupy_prt.dart'; export './handler/habit_cell_gesture_mode.dart'; +export './handler/display_sort_mode.dart'; diff --git a/lib/provider/habits_sort.dart b/lib/provider/habits_sort.dart index 0498c315..7f4b5208 100644 --- a/lib/provider/habits_sort.dart +++ b/lib/provider/habits_sort.dart @@ -13,34 +13,41 @@ // limitations under the License. import 'package:flutter/material.dart'; +import 'package:tuple/tuple.dart'; import '../l10n/localizations.dart'; -import '../model/global.dart'; +import '../logging/helper.dart'; import '../model/habit_display.dart'; +import '../persistent/profile/handlers.dart'; +import '../persistent/profile_provider.dart'; import '../theme/icon.dart'; class HabitsSortViewModel extends ChangeNotifier - implements GlobalProxyProviderInterface { - Global _g; + with ProfileHandlerLoadedMixin { + DisplaySortModeProfileHandler? _sortMode; - HabitsSortViewModel({required Global global}) : _g = global; + HabitsSortViewModel(); @override - Global get g => _g; - - @override - void updateGlobal(Global newGloal) => _g = newGloal; + void updateProfile(ProfileViewModel newProfile) { + super.updateProfile(newProfile); + _sortMode = newProfile.getHandler(); + } - HabitDisplaySortType get sortType => _g.sortType; - HabitDisplaySortDirection get sortDirection => _g.sortDirection; + HabitDisplaySortType get sortType => + _sortMode?.sortType ?? HabitDisplaySortType.manual; + HabitDisplaySortDirection get sortDirection => + _sortMode?.sortDirection ?? HabitDisplaySortDirection.asc; Future setNewSortMode( {HabitDisplaySortType? sortType, HabitDisplaySortDirection? sortDirection}) async { - var newSortType = (sortType != null) ? sortType : this.sortType; - var newSortDirection = - (sortDirection != null) ? sortDirection : this.sortDirection; - await _g.profile.setSortMode(newSortType, newSortDirection); + final Tuple2 newSortMode = + Tuple2((sortType != null) ? sortType : this.sortType, + (sortDirection != null) ? sortDirection : this.sortDirection); + appLog.value.info("$runtimeType.setNewSortMode", + beforeVal: [this.sortType, this.sortDirection], afterVal: newSortMode); + await _sortMode?.set(newSortMode); notifyListeners(); } diff --git a/lib/view/app.dart b/lib/view/app.dart index 87f25525..4483ce7c 100644 --- a/lib/view/app.dart +++ b/lib/view/app.dart @@ -38,6 +38,7 @@ class App extends StatelessWidget { yield (pref) => AppThemeTypeProfileHandler(pref); yield (pref) => AppThemeMainColorProfileHandler(pref); yield (pref) => CompactUISwitcherProfileHandler(pref); + yield (pref) => DisplaySortModeProfileHandler(pref); yield (pref) => DisplayCalendartBarOccupyPrtProfileHandler(pref); yield (pref) => HabitCellGestureModeProfileHandler(pref); } diff --git a/lib/view/for_habits_display/page_providers.dart b/lib/view/for_habits_display/page_providers.dart index edbe1223..a6bb0cff 100644 --- a/lib/view/for_habits_display/page_providers.dart +++ b/lib/view/for_habits_display/page_providers.dart @@ -19,6 +19,7 @@ import 'package:provider/provider.dart'; import '../../model/global.dart'; import '../../persistent/db_helper_provider.dart'; +import '../../persistent/profile_provider.dart'; import '../../provider/app_first_day.dart'; import '../../provider/habit_summary.dart'; import '../../provider/habits_file_importer.dart'; @@ -96,11 +97,10 @@ class PageProviders extends SingleChildStatelessWidget { @override Widget buildWithChild(BuildContext context, Widget? child) => MultiProvider( providers: [ - ChangeNotifierProxyProvider( - create: (context) => - HabitsSortViewModel(global: context.read()), - update: (context, value, previous) => - previous!..updateGlobal(value), + ChangeNotifierProxyProvider( + create: (context) => HabitsSortViewModel(), + update: (context, profile, previous) => + previous!..updateProfile(profile), ), ChangeNotifierProxyProvider( create: (context) => From db413f324656ea35761e792835ca9d056343db12 Mon Sep 17 00:00:00 2001 From: Fries_I23 Date: Sun, 31 Mar 2024 17:48:10 +0800 Subject: [PATCH 07/13] refactor: custom date format --- lib/db/profile.dart | 26 -------- lib/model/custom_date_format.dart | 3 + lib/model/global.dart | 4 -- .../profile/handler/show_data_format.dart | 61 +++++++++++++++++++ lib/persistent/profile/handlers.dart | 3 +- lib/provider/app_custom_date_format.dart | 25 +++++--- lib/view/app.dart | 1 + lib/view/for_app/app_providers.dart | 9 ++- 8 files changed, 86 insertions(+), 46 deletions(-) create mode 100644 lib/persistent/profile/handler/show_data_format.dart diff --git a/lib/db/profile.dart b/lib/db/profile.dart index 71876612..1e99de5d 100644 --- a/lib/db/profile.dart +++ b/lib/db/profile.dart @@ -24,7 +24,6 @@ import '../common/global.dart'; import '../common/utils.dart'; import '../logging/helper.dart'; import '../model/app_reminder_config.dart'; -import '../model/custom_date_format.dart'; mixin CacheInterface { Map getInputFillCache(); @@ -46,9 +45,6 @@ abstract class ProfileInterface { AppReminderConfig? getAppReminder(); Future setAppReminder(AppReminderConfig newTimeOfDay); - - CustomDateYmdHmsConfig? getCustomDateYmdHmsConfig(); - Future setCustomDateYmdHmsConfig(CustomDateYmdHmsConfig newConfig); } enum ProfileKey { @@ -56,7 +52,6 @@ enum ProfileKey { habitsRecordScrollBehavior, firstDay, appReminder, - customDateYmdHmsConfig, // cache inputFillCache, } @@ -138,27 +133,6 @@ class Profile ProfileKey.appReminder.name, jsonEncode(newReminder.toJson())); } - @override - CustomDateYmdHmsConfig getCustomDateYmdHmsConfig() { - final raw = _pref.getString(ProfileKey.customDateYmdHmsConfig.name); - if (raw == null) { - return const CustomDateYmdHmsConfig.withDefault(); - } - try { - return CustomDateYmdHmsConfig.fromJson(jsonDecode(raw)); - } catch (e) { - appLog.json.warn("$runtimeType.getCustomDateYmdHmsConfig", - ex: ["profile decode err"], error: e); - return const CustomDateYmdHmsConfig.withDefault(); - } - } - - @override - Future setCustomDateYmdHmsConfig(CustomDateYmdHmsConfig newConfig) { - return _pref.setString( - ProfileKey.customDateYmdHmsConfig.name, jsonEncode(newConfig.toJson())); - } - @override Map getInputFillCache() { final raw = _pref.getString(ProfileKey.inputFillCache.name); diff --git a/lib/model/custom_date_format.dart b/lib/model/custom_date_format.dart index a344933b..2988d718 100644 --- a/lib/model/custom_date_format.dart +++ b/lib/model/custom_date_format.dart @@ -248,4 +248,7 @@ class CustomDateYmdHmsConfig implements JsonAdaptor { : splitChar.char; } } + + @override + String toString() => "$runtimeType(${toJson()})"; } diff --git a/lib/model/global.dart b/lib/model/global.dart index c1eff03b..6adfc6a5 100644 --- a/lib/model/global.dart +++ b/lib/model/global.dart @@ -19,7 +19,6 @@ import '../common/enums.dart'; import '../db/profile.dart'; import 'app_reminder_config.dart'; import 'cache.dart'; -import 'custom_date_format.dart'; import 'habit_display.dart'; abstract class GlobalProxyProviderInterface { @@ -86,7 +85,4 @@ class Global int get firstDay => profile.getFirstDay(); AppReminderConfig get appReminderConfig => profile.getAppReminder(); - - CustomDateYmdHmsConfig get customDateYmdHmsConfig => - profile.getCustomDateYmdHmsConfig(); } diff --git a/lib/persistent/profile/handler/show_data_format.dart b/lib/persistent/profile/handler/show_data_format.dart new file mode 100644 index 00000000..93d7f293 --- /dev/null +++ b/lib/persistent/profile/handler/show_data_format.dart @@ -0,0 +1,61 @@ +// Copyright 2024 Fries_I23 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:convert'; + +import '../../../common/types.dart'; +import '../../../logging/helper.dart'; +import '../../../model/custom_date_format.dart'; +import '../profile_helper.dart'; + +final class ShowDateFormatProfileHandler + extends ProfileHelperCovertToJsonHandler { + const ShowDateFormatProfileHandler(super.pref) + : super(codec: const ShowDateFormatCodec()); + + @override + String get key => "customDateYmdHmsConfig"; +} + +final class ShowDateFormatCodec extends Codec { + const ShowDateFormatCodec(); + + @override + Converter get decoder => const _Decoder(); + + @override + Converter get encoder => const _Encoder(); +} + +final class _Decoder extends Converter { + const _Decoder(); + + @override + CustomDateYmdHmsConfig convert(JsonMap input) { + try { + return CustomDateYmdHmsConfig.fromJson(input); + } catch (e) { + appLog.load.warn("ShowDateFormatCodec.$runtimeType", + ex: ["format err"], error: e); + return const CustomDateYmdHmsConfig.withDefault(); + } + } +} + +final class _Encoder extends Converter { + const _Encoder(); + + @override + JsonMap convert(CustomDateYmdHmsConfig input) => input.toJson(); +} diff --git a/lib/persistent/profile/handlers.dart b/lib/persistent/profile/handlers.dart index 8251db94..dfe1d8b5 100644 --- a/lib/persistent/profile/handlers.dart +++ b/lib/persistent/profile/handlers.dart @@ -16,5 +16,6 @@ export './handler/app_theme_main_color.dart'; export './handler/app_theme_type.dart'; export './handler/compact_ui_switcher.dart'; export './handler/display_calendar_bar_occupy_prt.dart'; -export './handler/habit_cell_gesture_mode.dart'; export './handler/display_sort_mode.dart'; +export './handler/habit_cell_gesture_mode.dart'; +export './handler/show_data_format.dart'; diff --git a/lib/provider/app_custom_date_format.dart b/lib/provider/app_custom_date_format.dart index 697591e1..89af7f22 100644 --- a/lib/provider/app_custom_date_format.dart +++ b/lib/provider/app_custom_date_format.dart @@ -14,25 +14,30 @@ import 'package:flutter/material.dart'; +import '../logging/helper.dart'; import '../model/custom_date_format.dart'; -import '../model/global.dart'; +import '../persistent/profile/handlers.dart'; +import '../persistent/profile_provider.dart'; class AppCustomDateYmdHmsConfigViewModel extends ChangeNotifier - implements GlobalProxyProviderInterface { - Global _g; + with ProfileHandlerLoadedMixin { + ShowDateFormatProfileHandler? _dataFmt; - AppCustomDateYmdHmsConfigViewModel({required Global global}) : _g = global; + AppCustomDateYmdHmsConfigViewModel(); @override - Global get g => _g; - - @override - void updateGlobal(Global newGloal) => _g = newGloal; + void updateProfile(ProfileViewModel newProfile) { + super.updateProfile(newProfile); + _dataFmt = newProfile.getHandler(); + } - CustomDateYmdHmsConfig get config => g.customDateYmdHmsConfig; + CustomDateYmdHmsConfig get config => + _dataFmt?.get() ?? const CustomDateYmdHmsConfig.withDefault(); Future setNewConfig(CustomDateYmdHmsConfig newConfig) async { - await g.profile.setCustomDateYmdHmsConfig(newConfig); + appLog.value.info("$runtimeType.setNewConfig", + beforeVal: config, afterVal: newConfig); + await _dataFmt?.set(newConfig); notifyListeners(); } } diff --git a/lib/view/app.dart b/lib/view/app.dart index 4483ce7c..935e3dcc 100644 --- a/lib/view/app.dart +++ b/lib/view/app.dart @@ -40,6 +40,7 @@ class App extends StatelessWidget { yield (pref) => CompactUISwitcherProfileHandler(pref); yield (pref) => DisplaySortModeProfileHandler(pref); yield (pref) => DisplayCalendartBarOccupyPrtProfileHandler(pref); + yield (pref) => ShowDateFormatProfileHandler(pref); yield (pref) => HabitCellGestureModeProfileHandler(pref); } diff --git a/lib/view/for_app/app_providers.dart b/lib/view/for_app/app_providers.dart index 523bf1f9..553e874d 100644 --- a/lib/view/for_app/app_providers.dart +++ b/lib/view/for_app/app_providers.dart @@ -82,12 +82,11 @@ class AppProviders extends SingleChildStatelessWidget { update: (context, value, previous) => previous!..updateGlobal(value), ), - ChangeNotifierProxyProvider( - create: (context) => AppCustomDateYmdHmsConfigViewModel( - global: context.read()), - update: (context, value, previous) => - previous!..updateGlobal(value), + create: (context) => AppCustomDateYmdHmsConfigViewModel(), + update: (context, profile, previous) => + previous!..updateProfile(profile), ), ChangeNotifierProxyProvider( From 075c9b4df36b41ea5e94cadb75be8ebc571e2e4b Mon Sep 17 00:00:00 2001 From: Fries_I23 Date: Sun, 31 Mar 2024 18:29:32 +0800 Subject: [PATCH 08/13] refactor: app reminder --- lib/db/profile.dart | 19 ------- lib/model/app_reminder_config.dart | 5 ++ lib/model/global.dart | 3 - .../profile/handler/app_reminder.dart | 52 +++++++++++++++++ lib/persistent/profile/handlers.dart | 1 + lib/provider/app_reminder.dart | 57 +++++++++++-------- lib/view/app.dart | 1 + lib/view/for_app/app_providers.dart | 11 ++-- 8 files changed, 97 insertions(+), 52 deletions(-) create mode 100644 lib/persistent/profile/handler/app_reminder.dart diff --git a/lib/db/profile.dart b/lib/db/profile.dart index 1e99de5d..12e5b3cc 100644 --- a/lib/db/profile.dart +++ b/lib/db/profile.dart @@ -23,7 +23,6 @@ import '../common/enums.dart'; import '../common/global.dart'; import '../common/utils.dart'; import '../logging/helper.dart'; -import '../model/app_reminder_config.dart'; mixin CacheInterface { Map getInputFillCache(); @@ -42,16 +41,12 @@ abstract class ProfileInterface { int getFirstDay(); Future setFirstDay(int newFirstDay); - - AppReminderConfig? getAppReminder(); - Future setAppReminder(AppReminderConfig newTimeOfDay); } enum ProfileKey { habitDisplayFilter, habitsRecordScrollBehavior, firstDay, - appReminder, // cache inputFillCache, } @@ -119,20 +114,6 @@ class Profile return _pref.setInt(ProfileKey.firstDay.name, newFirstDay); } - @override - AppReminderConfig getAppReminder() { - final raw = _pref.getString(ProfileKey.appReminder.name); - return raw != null - ? AppReminderConfig.fromJson(jsonDecode(raw)) - : defaultAppReminder; - } - - @override - Future setAppReminder(AppReminderConfig newReminder) { - return _pref.setString( - ProfileKey.appReminder.name, jsonEncode(newReminder.toJson())); - } - @override Map getInputFillCache() { final raw = _pref.getString(ProfileKey.inputFillCache.name); diff --git a/lib/model/app_reminder_config.dart b/lib/model/app_reminder_config.dart index 2440baea..80ab8550 100644 --- a/lib/model/app_reminder_config.dart +++ b/lib/model/app_reminder_config.dart @@ -82,4 +82,9 @@ class AppReminderConfig implements JsonAdaptor { @override int get hashCode => hash3(enabled.hashCode, type.hashCode, timeOfDay.hashCode); + + @override + String toString() { + return "$runtimeType(${toJson()})"; + } } diff --git a/lib/model/global.dart b/lib/model/global.dart index 6adfc6a5..df4c5306 100644 --- a/lib/model/global.dart +++ b/lib/model/global.dart @@ -17,7 +17,6 @@ import 'package:flutter/foundation.dart'; import '../common/consts.dart'; import '../common/enums.dart'; import '../db/profile.dart'; -import 'app_reminder_config.dart'; import 'cache.dart'; import 'habit_display.dart'; @@ -83,6 +82,4 @@ class Global withDefault: defaultHabitsRecordScrollBehavior)!; int get firstDay => profile.getFirstDay(); - - AppReminderConfig get appReminderConfig => profile.getAppReminder(); } diff --git a/lib/persistent/profile/handler/app_reminder.dart b/lib/persistent/profile/handler/app_reminder.dart new file mode 100644 index 00000000..6458e330 --- /dev/null +++ b/lib/persistent/profile/handler/app_reminder.dart @@ -0,0 +1,52 @@ +// Copyright 2024 Fries_I23 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:convert'; + +import '../../../common/types.dart'; +import '../../../model/app_reminder_config.dart'; +import '../profile_helper.dart'; + +final class AppReminderProfileHandler + extends ProfileHelperCovertToJsonHandler { + const AppReminderProfileHandler(super.pref) + : super(codec: const AppReminderCodec()); + + @override + String get key => "appReminder"; +} + +final class AppReminderCodec extends Codec { + const AppReminderCodec(); + + @override + Converter get decoder => const _Decoder(); + + @override + Converter get encoder => const _Encoder(); +} + +final class _Decoder extends Converter { + const _Decoder(); + + @override + AppReminderConfig convert(JsonMap input) => AppReminderConfig.fromJson(input); +} + +final class _Encoder extends Converter { + const _Encoder(); + + @override + JsonMap convert(AppReminderConfig input) => input.toJson(); +} diff --git a/lib/persistent/profile/handlers.dart b/lib/persistent/profile/handlers.dart index dfe1d8b5..ee3d597b 100644 --- a/lib/persistent/profile/handlers.dart +++ b/lib/persistent/profile/handlers.dart @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +export './handler/app_reminder.dart'; export './handler/app_theme_main_color.dart'; export './handler/app_theme_type.dart'; export './handler/compact_ui_switcher.dart'; diff --git a/lib/provider/app_reminder.dart b/lib/provider/app_reminder.dart index c5b5afc6..58221a93 100644 --- a/lib/provider/app_reminder.dart +++ b/lib/provider/app_reminder.dart @@ -14,25 +14,27 @@ import 'package:flutter/material.dart'; +import '../common/consts.dart'; import '../l10n/localizations.dart'; +import '../logging/helper.dart'; import '../model/app_reminder_config.dart'; -import '../model/global.dart'; +import '../persistent/profile/handlers.dart'; +import '../persistent/profile_provider.dart'; import '../reminders/notification_channel.dart'; import '../reminders/notification_service.dart'; import 'commons.dart'; class AppReminderViewModel extends ChangeNotifier - with NotificationChannelDataMixin - implements GlobalProxyProviderInterface { - Global _g; + with NotificationChannelDataMixin, ProfileHandlerLoadedMixin { + AppReminderProfileHandler? _rmd; - AppReminderViewModel({required Global global}) : _g = global; + AppReminderViewModel(); @override - Global get g => _g; - - @override - void updateGlobal(Global newGloal) => _g = newGloal; + void updateProfile(ProfileViewModel newProfile) { + super.updateProfile(newProfile); + _rmd = newProfile.getHandler(); + } @override void setNotificationChannelData(NotificationChannelData newData, @@ -41,23 +43,25 @@ class AppReminderViewModel extends ChangeNotifier _handleChangeReminder(l10n); } - AppReminderConfig get reminder => _g.appReminderConfig; + AppReminderConfig get reminder => _rmd?.get() ?? defaultAppReminder; Future switchOff({L10n? l10n}) async { final reminder = this.reminder; if (reminder.enabled) { - await _g.profile.setAppReminder(reminder.copyWith(enabled: false)); - notifyListeners(); - await _handleChangeReminder(l10n); + appLog.value.info("$runtimeType.switchOff", + beforeVal: reminder.enabled, afterVal: false, ex: [l10n]); + await _rmd?.set(reminder.copyWith(enabled: false)); + if (!await _handleChangeReminder(l10n)) notifyListeners(); } } Future switchOn({L10n? l10n}) async { final reminder = this.reminder; if (!reminder.enabled) { - await _g.profile.setAppReminder(reminder.copyWith(enabled: true)); - notifyListeners(); - await _handleChangeReminder(l10n); + appLog.value.info("$runtimeType.switchOn", + beforeVal: reminder.enabled, afterVal: true, ex: [l10n]); + await _rmd?.set(reminder.copyWith(enabled: true)); + if (!await _handleChangeReminder(l10n)) notifyListeners(); } } @@ -66,22 +70,27 @@ class AppReminderViewModel extends ChangeNotifier final newReminder = AppReminderConfig.dailyNight .copyWith(timeOfDay: timeOfDay, enabled: reminder.enabled); if (newReminder != reminder) { - await _g.profile.setAppReminder(newReminder); - notifyListeners(); - await _handleChangeReminder(l10n); + appLog.value.info("$runtimeType.switchToDaily", + beforeVal: reminder, afterVal: newReminder, ex: [timeOfDay, l10n]); + await _rmd?.set(newReminder); + if (!await _handleChangeReminder(l10n)) notifyListeners(); } } - Future _handleChangeReminder(L10n? l10n) async { - if ((l10n != null ? await processAppReminder(l10n) : true) != true) { - await _g.profile.setAppReminder(reminder.copyWith(enabled: false)); + Future _handleChangeReminder(L10n? l10n) async { + if ((await processAppReminder(l10n)) != true) { + appLog.value.info("$runtimeType._handleChangeReminder", + beforeVal: reminder.enabled, afterVal: false, ex: [l10n]); + await _rmd?.set(reminder.copyWith(enabled: false)); notifyListeners(); + return true; } + return false; } - Future processAppReminder(L10n l10n) async { + Future processAppReminder(L10n? l10n) async { final reminder = this.reminder; - if (reminder.enabled) { + if (reminder.enabled && l10n != null) { if (await NotificationService().requestPermissions() != true) { return false; } diff --git a/lib/view/app.dart b/lib/view/app.dart index 935e3dcc..ef0ef09f 100644 --- a/lib/view/app.dart +++ b/lib/view/app.dart @@ -35,6 +35,7 @@ class App extends StatelessWidget { const App({super.key}); Iterable _buildProfileHanlder() sync* { + yield (pref) => AppReminderProfileHandler(pref); yield (pref) => AppThemeTypeProfileHandler(pref); yield (pref) => AppThemeMainColorProfileHandler(pref); yield (pref) => CompactUISwitcherProfileHandler(pref); diff --git a/lib/view/for_app/app_providers.dart b/lib/view/for_app/app_providers.dart index 553e874d..6fd1c4aa 100644 --- a/lib/view/for_app/app_providers.dart +++ b/lib/view/for_app/app_providers.dart @@ -94,13 +94,12 @@ class AppProviders extends SingleChildStatelessWidget { update: (context, profile, previous) => previous!..updateProfile(profile), ), - ChangeNotifierProxyProvider2( + ChangeNotifierProxyProvider2( lazy: false, - create: (context) => - AppReminderViewModel(global: context.read()), - update: (context, global, channel, previous) => previous! - ..updateGlobal(global) + create: (context) => AppReminderViewModel(), + update: (context, profile, channel, previous) => previous! + ..updateProfile(profile) ..setNotificationChannelData(channel, l10n: L10n.of(context)), ), ChangeNotifierProxyProvider( From 44a468bbca300e6185ace56e0034bef9ebf68023 Mon Sep 17 00:00:00 2001 From: Fries_I23 Date: Sun, 31 Mar 2024 18:42:20 +0800 Subject: [PATCH 09/13] refactor: app first day --- lib/db/profile.dart | 16 ---------- lib/model/global.dart | 2 -- lib/persistent/profile/handler/first_day.dart | 25 ++++++++++++++++ lib/persistent/profile/handlers.dart | 1 + lib/provider/app_first_day.dart | 29 ++++++++++++------- lib/view/app.dart | 1 + lib/view/for_app/app_providers.dart | 9 +++--- 7 files changed, 49 insertions(+), 34 deletions(-) create mode 100644 lib/persistent/profile/handler/first_day.dart diff --git a/lib/db/profile.dart b/lib/db/profile.dart index 12e5b3cc..4bfbd408 100644 --- a/lib/db/profile.dart +++ b/lib/db/profile.dart @@ -21,7 +21,6 @@ import '../common/abc.dart'; import '../common/consts.dart'; import '../common/enums.dart'; import '../common/global.dart'; -import '../common/utils.dart'; import '../logging/helper.dart'; mixin CacheInterface { @@ -38,15 +37,11 @@ abstract class ProfileInterface { int getHabitsRecordScrollBehavior(); Future setHabitsRecordScrollBehavior( HabitsRecordScrollBehavior behavior); - - int getFirstDay(); - Future setFirstDay(int newFirstDay); } enum ProfileKey { habitDisplayFilter, habitsRecordScrollBehavior, - firstDay, // cache inputFillCache, } @@ -103,17 +98,6 @@ class Profile ProfileKey.habitsRecordScrollBehavior.name, behavior.dbCode); } - @override - int getFirstDay() { - return _pref.getInt(ProfileKey.firstDay.name) ?? defaultFirstDay; - } - - @override - Future setFirstDay(int newFirstDay) { - newFirstDay = standardizeFirstDay(newFirstDay); - return _pref.setInt(ProfileKey.firstDay.name, newFirstDay); - } - @override Map getInputFillCache() { final raw = _pref.getString(ProfileKey.inputFillCache.name); diff --git a/lib/model/global.dart b/lib/model/global.dart index df4c5306..9eb315a6 100644 --- a/lib/model/global.dart +++ b/lib/model/global.dart @@ -80,6 +80,4 @@ class Global HabitsRecordScrollBehavior.getFromDBCode( profile.getHabitsRecordScrollBehavior(), withDefault: defaultHabitsRecordScrollBehavior)!; - - int get firstDay => profile.getFirstDay(); } diff --git a/lib/persistent/profile/handler/first_day.dart b/lib/persistent/profile/handler/first_day.dart new file mode 100644 index 00000000..ba4b9dc2 --- /dev/null +++ b/lib/persistent/profile/handler/first_day.dart @@ -0,0 +1,25 @@ +// Copyright 2024 Fries_I23 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import '../converter.dart'; +import '../profile_helper.dart'; + +final class FirstDayProfileHandler + extends ProfileHelperCovertToIntHandler { + const FirstDayProfileHandler(super.pref) + : super(codec: const SameTypeCodec()); + + @override + String get key => "firstDay"; +} diff --git a/lib/persistent/profile/handlers.dart b/lib/persistent/profile/handlers.dart index ee3d597b..0d974da2 100644 --- a/lib/persistent/profile/handlers.dart +++ b/lib/persistent/profile/handlers.dart @@ -18,5 +18,6 @@ export './handler/app_theme_type.dart'; export './handler/compact_ui_switcher.dart'; export './handler/display_calendar_bar_occupy_prt.dart'; export './handler/display_sort_mode.dart'; +export './handler/first_day.dart'; export './handler/habit_cell_gesture_mode.dart'; export './handler/show_data_format.dart'; diff --git a/lib/provider/app_first_day.dart b/lib/provider/app_first_day.dart index 08d35308..fcaecc0e 100644 --- a/lib/provider/app_first_day.dart +++ b/lib/provider/app_first_day.dart @@ -14,25 +14,32 @@ import 'package:flutter/material.dart'; -import '../model/global.dart'; +import '../common/consts.dart'; +import '../common/utils.dart'; +import '../logging/helper.dart'; +import '../persistent/profile/handlers.dart'; +import '../persistent/profile_provider.dart'; class AppFirstDayViewModel extends ChangeNotifier - implements GlobalProxyProviderInterface { - Global _g; + with ProfileHandlerLoadedMixin { + FirstDayProfileHandler? _firstDay; - AppFirstDayViewModel({required Global global}) : _g = global; + AppFirstDayViewModel(); @override - Global get g => _g; - - @override - void updateGlobal(Global newGloal) => _g = newGloal; + void updateProfile(ProfileViewModel newProfile) { + super.updateProfile(newProfile); + _firstDay = newProfile.getHandler(); + } - int get firstDay => g.firstDay; + int get firstDay => _firstDay?.get() ?? defaultFirstDay; Future setNewFirstDay(int newFirstDay) async { - if (g.firstDay != newFirstDay) { - await g.profile.setFirstDay(newFirstDay); + if (_firstDay?.get() != newFirstDay) { + final stdNewFirstDay = standardizeFirstDay(newFirstDay); + appLog.value.info("$runtimeType.setNewFirstDay", + beforeVal: firstDay, afterVal: newFirstDay, ex: [stdNewFirstDay]); + await _firstDay?.set(stdNewFirstDay); notifyListeners(); } } diff --git a/lib/view/app.dart b/lib/view/app.dart index ef0ef09f..009a1d47 100644 --- a/lib/view/app.dart +++ b/lib/view/app.dart @@ -42,6 +42,7 @@ class App extends StatelessWidget { yield (pref) => DisplaySortModeProfileHandler(pref); yield (pref) => DisplayCalendartBarOccupyPrtProfileHandler(pref); yield (pref) => ShowDateFormatProfileHandler(pref); + yield (pref) => FirstDayProfileHandler(pref); yield (pref) => HabitCellGestureModeProfileHandler(pref); } diff --git a/lib/view/for_app/app_providers.dart b/lib/view/for_app/app_providers.dart index 6fd1c4aa..ec390e28 100644 --- a/lib/view/for_app/app_providers.dart +++ b/lib/view/for_app/app_providers.dart @@ -76,11 +76,10 @@ class AppProviders extends SingleChildStatelessWidget { update: (context, profile, previous) => previous!..updateProfile(profile), ), - ChangeNotifierProxyProvider( - create: (context) => - AppFirstDayViewModel(global: context.read()), - update: (context, value, previous) => - previous!..updateGlobal(value), + ChangeNotifierProxyProvider( + create: (context) => AppFirstDayViewModel(), + update: (context, profile, previous) => + previous!..updateProfile(profile), ), ChangeNotifierProxyProvider( From 1269fa1a17e9ef8b477674a282a815f4badd5b02 Mon Sep 17 00:00:00 2001 From: Fries_I23 Date: Sun, 31 Mar 2024 18:58:07 +0800 Subject: [PATCH 10/13] refactor: display calendar scroll behavior --- lib/db/profile.dart | 20 ------- lib/model/global.dart | 7 --- .../handler/display_calendar_scroll_mode.dart | 54 +++++++++++++++++++ lib/persistent/profile/handlers.dart | 1 + .../habits_record_scroll_behavior.dart | 27 ++++++---- lib/view/app.dart | 1 + .../for_habits_display/page_providers.dart | 9 ++-- 7 files changed, 76 insertions(+), 43 deletions(-) create mode 100644 lib/persistent/profile/handler/display_calendar_scroll_mode.dart diff --git a/lib/db/profile.dart b/lib/db/profile.dart index 4bfbd408..f74c16ed 100644 --- a/lib/db/profile.dart +++ b/lib/db/profile.dart @@ -18,8 +18,6 @@ import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../common/abc.dart'; -import '../common/consts.dart'; -import '../common/enums.dart'; import '../common/global.dart'; import '../logging/helper.dart'; @@ -33,15 +31,10 @@ abstract class ProfileInterface { Map getHabitDisplayFilter(); Future setHabitDisplayFilter(Map filterMap); - - int getHabitsRecordScrollBehavior(); - Future setHabitsRecordScrollBehavior( - HabitsRecordScrollBehavior behavior); } enum ProfileKey { habitDisplayFilter, - habitsRecordScrollBehavior, // cache inputFillCache, } @@ -85,19 +78,6 @@ class Profile ProfileKey.habitDisplayFilter.name, jsonEncode(filterMap)); } - @override - int getHabitsRecordScrollBehavior() { - return _pref.getInt(ProfileKey.habitsRecordScrollBehavior.name) ?? - defaultHabitsRecordScrollBehavior.dbCode; - } - - @override - Future setHabitsRecordScrollBehavior( - HabitsRecordScrollBehavior behavior) { - return _pref.setInt( - ProfileKey.habitsRecordScrollBehavior.name, behavior.dbCode); - } - @override Map getInputFillCache() { final raw = _pref.getString(ProfileKey.inputFillCache.name); diff --git a/lib/model/global.dart b/lib/model/global.dart index 9eb315a6..0e769da8 100644 --- a/lib/model/global.dart +++ b/lib/model/global.dart @@ -14,8 +14,6 @@ import 'package:flutter/foundation.dart'; -import '../common/consts.dart'; -import '../common/enums.dart'; import '../db/profile.dart'; import 'cache.dart'; import 'habit_display.dart'; @@ -75,9 +73,4 @@ class Global var raw = profile.getHabitDisplayFilter(); return HabitsDisplayFilter.fromMap(raw); } - - HabitsRecordScrollBehavior get habitsRecordScrollBehavior => - HabitsRecordScrollBehavior.getFromDBCode( - profile.getHabitsRecordScrollBehavior(), - withDefault: defaultHabitsRecordScrollBehavior)!; } diff --git a/lib/persistent/profile/handler/display_calendar_scroll_mode.dart b/lib/persistent/profile/handler/display_calendar_scroll_mode.dart new file mode 100644 index 00000000..fbe974d2 --- /dev/null +++ b/lib/persistent/profile/handler/display_calendar_scroll_mode.dart @@ -0,0 +1,54 @@ +// Copyright 2024 Fries_I23 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:convert'; + +import '../../../common/enums.dart'; +import '../profile_helper.dart'; + +final class DisplayCalendarScrollModeProfileHandler + extends ProfileHelperCovertToIntHandler { + const DisplayCalendarScrollModeProfileHandler(super.pref) + : super(codec: const DisplayCalendarScrollModeCodec()); + + @override + String get key => "habitsRecordScrollBehavior"; +} + +final class DisplayCalendarScrollModeCodec + extends Codec { + const DisplayCalendarScrollModeCodec(); + + @override + Converter get decoder => const _Decoder(); + + @override + Converter get encoder => const _Encoder(); +} + +final class _Decoder extends Converter { + const _Decoder(); + + @override + HabitsRecordScrollBehavior convert(int input) => + HabitsRecordScrollBehavior.getFromDBCode(input, + withDefault: HabitsRecordScrollBehavior.unknown)!; +} + +final class _Encoder extends Converter { + const _Encoder(); + + @override + int convert(HabitsRecordScrollBehavior input) => input.dbCode; +} diff --git a/lib/persistent/profile/handlers.dart b/lib/persistent/profile/handlers.dart index 0d974da2..58d796c8 100644 --- a/lib/persistent/profile/handlers.dart +++ b/lib/persistent/profile/handlers.dart @@ -17,6 +17,7 @@ export './handler/app_theme_main_color.dart'; export './handler/app_theme_type.dart'; export './handler/compact_ui_switcher.dart'; export './handler/display_calendar_bar_occupy_prt.dart'; +export './handler/display_calendar_scroll_mode.dart'; export './handler/display_sort_mode.dart'; export './handler/first_day.dart'; export './handler/habit_cell_gesture_mode.dart'; diff --git a/lib/provider/habits_record_scroll_behavior.dart b/lib/provider/habits_record_scroll_behavior.dart index 8b017ce8..bed7a76d 100644 --- a/lib/provider/habits_record_scroll_behavior.dart +++ b/lib/provider/habits_record_scroll_behavior.dart @@ -14,28 +14,33 @@ import 'package:flutter/material.dart'; +import '../common/consts.dart'; import '../common/enums.dart'; import '../component/widget.dart'; -import '../model/global.dart'; +import '../logging/helper.dart'; +import '../persistent/profile/handlers.dart'; +import '../persistent/profile_provider.dart'; class HabitsRecordScrollBehaviorViewModel extends ChangeNotifier - implements GlobalProxyProviderInterface { - Global _g; + with ProfileHandlerLoadedMixin { + DisplayCalendarScrollModeProfileHandler? _scroll; - HabitsRecordScrollBehaviorViewModel({required Global global}) : _g = global; + HabitsRecordScrollBehaviorViewModel(); @override - Global get g => _g; - - @override - void updateGlobal(Global newGloal) => _g = newGloal; + void updateProfile(ProfileViewModel newProfile) { + super.updateProfile(newProfile); + _scroll = newProfile.getHandler(); + } HabitsRecordScrollBehavior get scrollBehavior => - _g.habitsRecordScrollBehavior; + _scroll?.get() ?? defaultHabitsRecordScrollBehavior; Future setScrollBehavior(HabitsRecordScrollBehavior newBehavior) async { - if (_g.habitsRecordScrollBehavior != newBehavior) { - await _g.profile.setHabitsRecordScrollBehavior(newBehavior); + if (_scroll?.get() != newBehavior) { + appLog.value.info("$runtimeType.setScrollBehavior", + beforeVal: scrollBehavior, afterVal: newBehavior); + await _scroll?.set(newBehavior); notifyListeners(); } } diff --git a/lib/view/app.dart b/lib/view/app.dart index 009a1d47..3c028f29 100644 --- a/lib/view/app.dart +++ b/lib/view/app.dart @@ -40,6 +40,7 @@ class App extends StatelessWidget { yield (pref) => AppThemeMainColorProfileHandler(pref); yield (pref) => CompactUISwitcherProfileHandler(pref); yield (pref) => DisplaySortModeProfileHandler(pref); + yield (pref) => DisplayCalendarScrollModeProfileHandler(pref); yield (pref) => DisplayCalendartBarOccupyPrtProfileHandler(pref); yield (pref) => ShowDateFormatProfileHandler(pref); yield (pref) => FirstDayProfileHandler(pref); diff --git a/lib/view/for_habits_display/page_providers.dart b/lib/view/for_habits_display/page_providers.dart index a6bb0cff..b5c768a0 100644 --- a/lib/view/for_habits_display/page_providers.dart +++ b/lib/view/for_habits_display/page_providers.dart @@ -108,12 +108,11 @@ class PageProviders extends SingleChildStatelessWidget { update: (context, value, previous) => previous!..updateGlobal(value), ), - ChangeNotifierProxyProvider( - create: (context) => HabitsRecordScrollBehaviorViewModel( - global: context.read()), - update: (context, value, previous) => - previous!..updateGlobal(value), + create: (context) => HabitsRecordScrollBehaviorViewModel(), + update: (context, profile, previous) => + previous!..updateProfile(profile), ), ..._buildPageViewModel(), ], From cbe0370043c53fcb85647d0bdaaaea5302c3d433 Mon Sep 17 00:00:00 2001 From: Fries_I23 Date: Sun, 31 Mar 2024 19:30:27 +0800 Subject: [PATCH 11/13] refactor: habits filter --- lib/db/profile.dart | 16 ------ lib/model/global.dart | 6 --- lib/model/habit_display.dart | 3 ++ .../handler/display_habits_filter.dart | 54 +++++++++++++++++++ lib/persistent/profile/handlers.dart | 1 + lib/provider/habits_filter.dart | 30 ++++++----- lib/view/app.dart | 1 + .../for_habits_display/page_providers.dart | 10 ++-- 8 files changed, 81 insertions(+), 40 deletions(-) create mode 100644 lib/persistent/profile/handler/display_habits_filter.dart diff --git a/lib/db/profile.dart b/lib/db/profile.dart index f74c16ed..ebdc0297 100644 --- a/lib/db/profile.dart +++ b/lib/db/profile.dart @@ -28,13 +28,9 @@ mixin CacheInterface { abstract class ProfileInterface { Future clearAll(); - - Map getHabitDisplayFilter(); - Future setHabitDisplayFilter(Map filterMap); } enum ProfileKey { - habitDisplayFilter, // cache inputFillCache, } @@ -66,18 +62,6 @@ class Profile return _pref.clear(); } - @override - Map getHabitDisplayFilter() { - var raw = _pref.getString(ProfileKey.habitDisplayFilter.name); - return raw != null ? jsonDecode(raw) : {}; - } - - @override - Future setHabitDisplayFilter(Map filterMap) { - return _pref.setString( - ProfileKey.habitDisplayFilter.name, jsonEncode(filterMap)); - } - @override Map getInputFillCache() { final raw = _pref.getString(ProfileKey.inputFillCache.name); diff --git a/lib/model/global.dart b/lib/model/global.dart index 0e769da8..b077558e 100644 --- a/lib/model/global.dart +++ b/lib/model/global.dart @@ -16,7 +16,6 @@ import 'package:flutter/foundation.dart'; import '../db/profile.dart'; import 'cache.dart'; -import 'habit_display.dart'; abstract class GlobalProxyProviderInterface { Global get g; @@ -68,9 +67,4 @@ class Global @override Profile get profile => Profile(); - - HabitsDisplayFilter get habitsDisplayFilter { - var raw = profile.getHabitDisplayFilter(); - return HabitsDisplayFilter.fromMap(raw); - } } diff --git a/lib/model/habit_display.dart b/lib/model/habit_display.dart index 68be8012..1fedf35a 100644 --- a/lib/model/habit_display.dart +++ b/lib/model/habit_display.dart @@ -193,6 +193,9 @@ class HabitsDisplayFilter { @override int get hashCode => hash3(allowInProgressHabits, allowArchivedHabits, allowCompleteHabits); + + @override + String toString() => "$runtimeType(${toMap()})"; } @JsonSerializable( diff --git a/lib/persistent/profile/handler/display_habits_filter.dart b/lib/persistent/profile/handler/display_habits_filter.dart new file mode 100644 index 00000000..11c8b1f2 --- /dev/null +++ b/lib/persistent/profile/handler/display_habits_filter.dart @@ -0,0 +1,54 @@ +// Copyright 2024 Fries_I23 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:convert'; + +import '../../../common/types.dart'; +import '../../../model/habit_display.dart'; +import '../profile_helper.dart'; + +final class DisplayHabitsFilterProfileHandler + extends ProfileHelperCovertToJsonHandler { + const DisplayHabitsFilterProfileHandler(super.pref) + : super(codec: const DisplayHabitsFilterCodec()); + + @override + String get key => "habitDisplayFilter"; +} + +final class DisplayHabitsFilterCodec + extends Codec { + const DisplayHabitsFilterCodec(); + + @override + Converter get decoder => const _Decoder(); + + @override + Converter get encoder => const _Encoder(); +} + +final class _Decoder extends Converter { + const _Decoder(); + + @override + HabitsDisplayFilter convert(JsonMap input) => + HabitsDisplayFilter.fromMap(input); +} + +final class _Encoder extends Converter { + const _Encoder(); + + @override + JsonMap convert(HabitsDisplayFilter input) => input.toMap(); +} diff --git a/lib/persistent/profile/handlers.dart b/lib/persistent/profile/handlers.dart index 58d796c8..755c5ffd 100644 --- a/lib/persistent/profile/handlers.dart +++ b/lib/persistent/profile/handlers.dart @@ -18,6 +18,7 @@ export './handler/app_theme_type.dart'; export './handler/compact_ui_switcher.dart'; export './handler/display_calendar_bar_occupy_prt.dart'; export './handler/display_calendar_scroll_mode.dart'; +export './handler/display_habits_filter.dart'; export './handler/display_sort_mode.dart'; export './handler/first_day.dart'; export './handler/habit_cell_gesture_mode.dart'; diff --git a/lib/provider/habits_filter.dart b/lib/provider/habits_filter.dart index 221d68d9..0d3851ee 100644 --- a/lib/provider/habits_filter.dart +++ b/lib/provider/habits_filter.dart @@ -14,28 +14,34 @@ import 'package:flutter/material.dart'; -import '../model/global.dart'; +import '../logging/helper.dart'; import '../model/habit_display.dart'; +import '../persistent/profile/handlers.dart'; +import '../persistent/profile_provider.dart'; class HabitsFilterViewModel extends ChangeNotifier - implements GlobalProxyProviderInterface { - Global _g; + with ProfileHandlerLoadedMixin { + DisplayHabitsFilterProfileHandler? _filter; - HabitsFilterViewModel({required Global global}) : _g = global; + HabitsFilterViewModel(); @override - Global get g => _g; - - @override - void updateGlobal(Global newGloal) => _g = newGloal; + void updateProfile(ProfileViewModel newProfile) { + super.updateProfile(newProfile); + _filter = newProfile.getHandler(); + } - HabitsDisplayFilter get habitsDisplayFilter => _g.habitsDisplayFilter; + HabitsDisplayFilter get habitsDisplayFilter => + _filter?.get() ?? const HabitsDisplayFilter.withDefault(); Future setNewHabitsDisplayFilter(HabitsDisplayFilter? newFilter, {bool listen = true}) async { - var filter = newFilter ?? const HabitsDisplayFilter.withDefault(); - await _g.profile.setHabitDisplayFilter(filter.toMap()); - if (listen) notifyListeners(); + if (_filter?.get() != newFilter) { + appLog.value.info("$runtimeType.setNewHabitsDisplayFilter", + beforeVal: habitsDisplayFilter, afterVal: newFilter); + await _filter?.set(newFilter ?? const HabitsDisplayFilter.withDefault()); + if (listen) notifyListeners(); + } } int getHabitDisplayFilterAllowedNumber() { diff --git a/lib/view/app.dart b/lib/view/app.dart index 3c028f29..d50f1e56 100644 --- a/lib/view/app.dart +++ b/lib/view/app.dart @@ -40,6 +40,7 @@ class App extends StatelessWidget { yield (pref) => AppThemeMainColorProfileHandler(pref); yield (pref) => CompactUISwitcherProfileHandler(pref); yield (pref) => DisplaySortModeProfileHandler(pref); + yield (pref) => DisplayHabitsFilterProfileHandler(pref); yield (pref) => DisplayCalendarScrollModeProfileHandler(pref); yield (pref) => DisplayCalendartBarOccupyPrtProfileHandler(pref); yield (pref) => ShowDateFormatProfileHandler(pref); diff --git a/lib/view/for_habits_display/page_providers.dart b/lib/view/for_habits_display/page_providers.dart index b5c768a0..f027f120 100644 --- a/lib/view/for_habits_display/page_providers.dart +++ b/lib/view/for_habits_display/page_providers.dart @@ -17,7 +17,6 @@ import 'package:linked_scroll_controller/linked_scroll_controller.dart'; import 'package:nested/nested.dart'; import 'package:provider/provider.dart'; -import '../../model/global.dart'; import '../../persistent/db_helper_provider.dart'; import '../../persistent/profile_provider.dart'; import '../../provider/app_first_day.dart'; @@ -102,11 +101,10 @@ class PageProviders extends SingleChildStatelessWidget { update: (context, profile, previous) => previous!..updateProfile(profile), ), - ChangeNotifierProxyProvider( - create: (context) => - HabitsFilterViewModel(global: context.read()), - update: (context, value, previous) => - previous!..updateGlobal(value), + ChangeNotifierProxyProvider( + create: (context) => HabitsFilterViewModel(), + update: (context, profile, previous) => + previous!..updateProfile(profile), ), ChangeNotifierProxyProvider( From 1d8ac0510b2c3360b6ec91a2c4a67d4703961f9a Mon Sep 17 00:00:00 2001 From: Fries_I23 Date: Mon, 1 Apr 2024 08:07:51 +0800 Subject: [PATCH 12/13] refactor: app caches --- lib/db/profile.dart | 83 ------------------- lib/logging/logger_manager.dart | 4 + lib/logging/logger_type.dart | 3 + lib/main.dart | 2 - lib/model/cache.dart | 62 +++++++------- lib/model/global.dart | 52 +++--------- .../profile/handler/input_fill_cache.dart | 39 +++++++++ lib/persistent/profile/handlers.dart | 1 + lib/persistent/profile_provider.dart | 23 +++-- lib/provider/app_caches.dart | 57 +++++++++++++ lib/provider/app_developer.dart | 19 ++--- lib/view/app.dart | 1 + lib/view/for_app/app_providers.dart | 6 ++ lib/view/page_app_setting.dart | 13 ++- lib/view/page_habit_edit.dart | 5 +- 15 files changed, 192 insertions(+), 178 deletions(-) delete mode 100644 lib/db/profile.dart create mode 100644 lib/persistent/profile/handler/input_fill_cache.dart create mode 100644 lib/provider/app_caches.dart diff --git a/lib/db/profile.dart b/lib/db/profile.dart deleted file mode 100644 index ebdc0297..00000000 --- a/lib/db/profile.dart +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2023 Fries_I23 -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'dart:convert'; - -import 'package:flutter/foundation.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -import '../common/abc.dart'; -import '../common/global.dart'; -import '../logging/helper.dart'; - -mixin CacheInterface { - Map getInputFillCache(); - Future setInputFillCache(Map newCache); -} - -abstract class ProfileInterface { - Future clearAll(); -} - -enum ProfileKey { - // cache - inputFillCache, -} - -class Profile - implements ProfileInterface, CacheInterface, FutureInitializationABC { - late final SharedPreferences _pref; - static Profile? _instance; - - Profile._internal() { - _instance = this; - } - - factory Profile() => _instance ?? Profile._internal(); - - @override - Future init() async { - appLog.profile.info('Initializing profiles ...'); - _pref = await SharedPreferences.getInstance(); - if (kDebugMode && debugClearSharedPrefWhenStart) { - appLog.db.info("Clear shared preferences"); - await clearAll(); - } - appLog.profile.info("Initialized profiles", ex: [_pref]); - } - - @override - Future clearAll() { - return _pref.clear(); - } - - @override - Map getInputFillCache() { - final raw = _pref.getString(ProfileKey.inputFillCache.name); - if (raw == null) return {}; - try { - return jsonDecode(raw); - } catch (e) { - appLog.json.warn("$runtimeType.getInputFillCache", - ex: ["profile decode err"], error: e); - return {}; - } - } - - @override - Future setInputFillCache(Map newCache) { - return _pref.setString( - ProfileKey.inputFillCache.name, jsonEncode(newCache)); - } -} diff --git a/lib/logging/logger_manager.dart b/lib/logging/logger_manager.dart index c59771ad..b28bab0d 100644 --- a/lib/logging/logger_manager.dart +++ b/lib/logging/logger_manager.dart @@ -42,6 +42,7 @@ abstract interface class AppLoggerMananger with FutureInitializationABC { AppTextLogger get network; AppTextLogger get json; AppWidgetLogger get l10n; + AppTextLogger get cache; Future changeLogger(l.Logger newLogger); @@ -138,6 +139,9 @@ class _AppLoggerManager implements AppLoggerMananger { buildNewLogger: (t) => AppWidgetLogger(this, t), ); + @override + AppTextLogger get cache => _tryGetAppTextLogger(LoggerType.cache); + @override Future changeLogger(l.Logger newLogger) async { if (newLogger.isClosed()) return false; diff --git a/lib/logging/logger_type.dart b/lib/logging/logger_type.dart index 018f6aff..dd1a7c8f 100644 --- a/lib/logging/logger_type.dart +++ b/lib/logging/logger_type.dart @@ -51,4 +51,7 @@ enum LoggerType { /// Localization l10n, + + /// App cache + cache, } diff --git a/lib/main.dart b/lib/main.dart index 051403d8..550228da 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -17,7 +17,6 @@ import 'package:timezone/data/latest_all.dart' as tz; import 'common/app_info.dart'; import 'common/utils.dart'; -import 'db/profile.dart'; import 'logging/logger_manager.dart'; import 'reminders/notification_service.dart'; import 'view/app.dart'; @@ -26,7 +25,6 @@ Future main() async { WidgetsFlutterBinding.ensureInitialized(); await AppLoggerMananger().init(); - await Profile().init(); await AppInfo().init(); await NotificationService().init(); diff --git a/lib/model/cache.dart b/lib/model/cache.dart index b042a191..e1d60f66 100644 --- a/lib/model/cache.dart +++ b/lib/model/cache.dart @@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import '../db/profile.dart'; +import '../common/types.dart'; +import '../logging/helper.dart'; +import '../persistent/profile/profile_helper.dart'; abstract class Cache { T? getCache(K key); @@ -24,24 +26,20 @@ abstract class Cache { bool isDirty(); } -enum InputFillCacheKey { - habitEditTargetDays, -} - -class InputFillCache implements Cache { - final Map _cache; - final WeakReference _profile; +class AppCacheDelegate> + implements Cache { + final JsonMap _cache = {}; + final WeakReference _handler; bool _dirty = false; - InputFillCache({required Profile profile}) - : _profile = WeakReference(profile), - _cache = {} { - reload(); - } + AppCacheDelegate({ + required T handler, + }) : _handler = WeakReference(handler); @override Future clear({void Function(bool)? onClear}) async { - if (_profile.target == null) onClear?.call(false); + appLog.cache.debug("$runtimeType.clear", ex: [this]); + if (_handler.target == null) onClear?.call(false); final oldCache = {..._cache}; _cache.clear(); _markDirty(); @@ -52,23 +50,25 @@ class InputFillCache implements Cache { @override Future reload({void Function(bool)? onReload}) async { - if (_profile.target == null) onReload?.call(false); - final raw = _profile.target!.getInputFillCache(); + appLog.cache.debug("$runtimeType.reload", ex: [this]); + if (_handler.target == null) onReload?.call(false); + final raw = _handler.target!.get(); _cache.clear(); - _cache.addAll(raw); + if (raw != null) _cache.addAll(raw); onReload?.call(true); } @override - T? getCache(key) { + V? getCache(String key) { return _cache[key]; } @override - Future removeCache(key, - {void Function(bool result, T? removeValue)? onRemoved}) async { - if (_profile.target == null) onRemoved?.call(false, null); - final oldValue = _cache.remove(key); + Future removeCache(String key, + {void Function(bool result, V? removeValue)? onRemoved}) async { + appLog.cache.debug("$runtimeType.removeCache", ex: [key, this]); + if (_handler.target == null) onRemoved?.call(false, null); + final V? oldValue = _cache.remove(key); _markDirty(); final result = await writeCache(); if (!result) _cache[key] = oldValue; @@ -76,10 +76,11 @@ class InputFillCache implements Cache { } @override - Future updateCache(key, T? value, - {void Function(bool result, T? oldValue)? onUpdated}) async { - if (_profile.target == null) onUpdated?.call(false, null); - final oldValue = _cache[key]; + Future updateCache(String key, V? value, + {void Function(bool result, V? oldValue)? onUpdated}) async { + appLog.cache.debug("$runtimeType.updateCache", ex: [key, value, this]); + if (_handler.target == null) onUpdated?.call(false, null); + final V? oldValue = _cache[key]; _cache[key] = value; _markDirty(); final result = await writeCache(); @@ -97,9 +98,14 @@ class InputFillCache implements Cache { void _markClean() => _dirty = false; Future writeCache() async { - if (_profile.target == null) return false; - final result = await _profile.target!.setInputFillCache(_cache); + appLog.cache.debug("$runtimeType.writeCache", ex: [this]); + if (_handler.target == null) return false; + final result = await _handler.target!.set(_cache); if (result) _markClean(); return result; } + + @override + String toString() => + "$runtimeType(handler=$_handler,dirty=$_dirty,cache=$_cache)"; } diff --git a/lib/model/global.dart b/lib/model/global.dart index b077558e..3c03eb0c 100644 --- a/lib/model/global.dart +++ b/lib/model/global.dart @@ -14,40 +14,11 @@ import 'package:flutter/foundation.dart'; -import '../db/profile.dart'; -import 'cache.dart'; +import '../logging/helper.dart'; -abstract class GlobalProxyProviderInterface { - Global get g; - void updateGlobal(Global newGloal); -} - -abstract class GlobalInterface {} -// coverage:ignore-end - -mixin CacheMixin { - late final Cache inputFillCache; - - int? get habitEditTargetDaysInputFill => - inputFillCache.getCache(InputFillCacheKey.habitEditTargetDays.name); - - void updateHabitEditTargetDaysInputFill(int? newTargetDays) { - inputFillCache.updateCache( - InputFillCacheKey.habitEditTargetDays.name, newTargetDays); - } +class Global { + Global(); - Future> clearAllCache() async { - List clearResultList = []; - List futures = [ - inputFillCache.clear(onClear: (r) => clearResultList.add(r)), - ]; - await Future.wait(futures); - return clearResultList; - } -} - -mixin GlobalDevelopModeMixin { - Profile get profile; bool _isInDevelopMode = kDebugMode ? true : false; bool? _displayDebugMenu; @@ -58,13 +29,14 @@ mixin GlobalDevelopModeMixin { void switchDisplayDebugMenu(bool value) => _displayDebugMenu = value; } -class Global - with GlobalDevelopModeMixin, CacheMixin - implements GlobalInterface { - Global() { - inputFillCache = InputFillCache(profile: profile); - } +abstract mixin class GlobalLoadedMixin { + late Global _g; + + Global get g => _g; - @override - Profile get profile => Profile(); + @mustCallSuper + void updateGlobal(Global newGloal) { + appLog.load.info("$runtimeType.updateGlobal", ex: [newGloal]); + _g = newGloal; + } } diff --git a/lib/persistent/profile/handler/input_fill_cache.dart b/lib/persistent/profile/handler/input_fill_cache.dart new file mode 100644 index 00000000..984f3cd6 --- /dev/null +++ b/lib/persistent/profile/handler/input_fill_cache.dart @@ -0,0 +1,39 @@ +// Copyright 2024 Fries_I23 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:convert'; + +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../../common/types.dart'; +import '../profile_helper.dart'; + +final class InputFillCacheProfileHandler + implements ProfileHelperHandler { + final SharedPreferences _pref; + + const InputFillCacheProfileHandler(SharedPreferences pref) : _pref = pref; + + @override + String get key => "inputFillCache"; + + @override + JsonMap? get() { + final source = _pref.getString(key); + return source != null ? jsonDecode(source) : null; + } + + @override + Future set(JsonMap value) => _pref.setString(key, jsonEncode(value)); +} diff --git a/lib/persistent/profile/handlers.dart b/lib/persistent/profile/handlers.dart index 755c5ffd..eed14113 100644 --- a/lib/persistent/profile/handlers.dart +++ b/lib/persistent/profile/handlers.dart @@ -22,4 +22,5 @@ export './handler/display_habits_filter.dart'; export './handler/display_sort_mode.dart'; export './handler/first_day.dart'; export './handler/habit_cell_gesture_mode.dart'; +export './handler/input_fill_cache.dart'; export './handler/show_data_format.dart'; diff --git a/lib/persistent/profile_provider.dart b/lib/persistent/profile_provider.dart index 59cd34e9..d92dfb22 100644 --- a/lib/persistent/profile_provider.dart +++ b/lib/persistent/profile_provider.dart @@ -19,6 +19,7 @@ import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../common/abc.dart'; +import '../common/global.dart'; import '../logging/helper.dart'; import '../provider/commons.dart'; import 'profile/profile_helper.dart'; @@ -41,14 +42,17 @@ class ProfileViewModel extends ChangeNotifier @override Future init() async { - if (_completer == null) { - _completer = Completer(); + Future doInit() async { final handlerKeyColl = {}; _pref = await SharedPreferences.getInstance(); + if (kDebugMode && debugClearSharedPrefWhenStart) { + appLog.profile.info("$runtimeType.init", ex: ["clear preferences"]); + await clear(); + } _handlers = Map.fromEntries(_handlerBuilders.map((e) => e.call(_pref)).where((e) { if (handlerKeyColl.containsKey(e.key)) { - appLog.load.error("$runtimeType.init", + appLog.profile.error("$runtimeType.init", ex: ["load handler failed", e, e.key, handlerKeyColl[e.key]]); if (kDebugMode) throw FlutterError("load handler failed: $e"); return false; @@ -56,6 +60,11 @@ class ProfileViewModel extends ChangeNotifier handlerKeyColl[e.key] = e.runtimeType; return true; }).map((e) => MapEntry(e.runtimeType, e))); + } + + if (_completer == null) { + _completer = Completer(); + await doInit(); _completer!.complete(); } return _completer!.future; @@ -98,11 +107,13 @@ class ProfileViewModel extends ChangeNotifier } abstract mixin class ProfileHandlerLoadedMixin { - late ProfileViewModel profile; + late ProfileViewModel _profile; + + ProfileViewModel get profile => _profile; @mustCallSuper void updateProfile(ProfileViewModel newProfile) { - appLog.load.info("$runtimeType.updateDBHelper", ex: [newProfile]); - profile = newProfile; + appLog.profile.info("$runtimeType.updateProfile", ex: [newProfile]); + _profile = newProfile; } } diff --git a/lib/provider/app_caches.dart b/lib/provider/app_caches.dart new file mode 100644 index 00000000..4cbc1b57 --- /dev/null +++ b/lib/provider/app_caches.dart @@ -0,0 +1,57 @@ +// Copyright 2024 Fries_I23 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import '../model/cache.dart'; +import '../persistent/profile/handlers.dart'; +import '../persistent/profile_provider.dart'; + +enum _InputFillCacheKey { + habitEditTargetDays, +} + +class AppCachesViewModel with ProfileHandlerLoadedMixin { + AppCacheDelegate? _inputFill; + + void _updateInputFile(ProfileViewModel newProfile) { + final handler = newProfile.getHandler(); + _inputFill = handler != null ? AppCacheDelegate(handler: handler) : null; + } + + @override + void updateProfile(ProfileViewModel newProfile) { + super.updateProfile(newProfile); + _updateInputFile(newProfile); + } + + int? get habitEditTargetDaysInputFill => + _inputFill?.getCache(_InputFillCacheKey.habitEditTargetDays.name); + + Future updateHabitEditTargetDaysInputFill(int? newTargetDays) async { + bool rst = false; + await _inputFill?.updateCache( + _InputFillCacheKey.habitEditTargetDays.name, newTargetDays, + onUpdated: (result, oldValue) => rst = result); + return rst; + } + + Future> clearAllCache() async { + List clearResultList = []; + List futures = [ + if (_inputFill != null) + _inputFill!.clear(onClear: (r) => clearResultList.add(r)), + ]; + await Future.wait(futures); + return clearResultList; + } +} diff --git a/lib/provider/app_developer.dart b/lib/provider/app_developer.dart index 935717f8..dda04c68 100644 --- a/lib/provider/app_developer.dart +++ b/lib/provider/app_developer.dart @@ -18,19 +18,12 @@ import '../common/global.dart'; import '../logging/level.dart'; import '../model/global.dart'; -class AppDeveloperViewModel extends ChangeNotifier - implements GlobalProxyProviderInterface { - Global _g; - - AppDeveloperViewModel({required Global global}) : _g = global; - - @override - Global get g => _g; - - @override - void updateGlobal(Global newGloal) => _g = newGloal; +class AppDeveloperViewModel extends ChangeNotifier with GlobalLoadedMixin { + AppDeveloperViewModel({required Global global}) { + updateGlobal(global); + } - bool get isInDevelopMode => _g.isInDevelopMode; + bool get isInDevelopMode => g.isInDevelopMode; void switchDevelopMode(bool value) { if (g.isInDevelopMode != value) { @@ -39,7 +32,7 @@ class AppDeveloperViewModel extends ChangeNotifier } } - bool get displayDebugMenu => _g.displayDebugMenu; + bool get displayDebugMenu => g.displayDebugMenu; void switchDisplayDebugMenu(bool value) { if (g.displayDebugMenu != value) { diff --git a/lib/view/app.dart b/lib/view/app.dart index d50f1e56..f37760ec 100644 --- a/lib/view/app.dart +++ b/lib/view/app.dart @@ -46,6 +46,7 @@ class App extends StatelessWidget { yield (pref) => ShowDateFormatProfileHandler(pref); yield (pref) => FirstDayProfileHandler(pref); yield (pref) => HabitCellGestureModeProfileHandler(pref); + yield (pref) => InputFillCacheProfileHandler(pref); } @override diff --git a/lib/view/for_app/app_providers.dart b/lib/view/for_app/app_providers.dart index ec390e28..4486d03c 100644 --- a/lib/view/for_app/app_providers.dart +++ b/lib/view/for_app/app_providers.dart @@ -20,6 +20,7 @@ import '../../l10n/localizations.dart'; import '../../model/global.dart'; import '../../persistent/db_helper_provider.dart'; import '../../persistent/profile_provider.dart'; +import '../../provider/app_caches.dart'; import '../../provider/app_compact_ui_switcher.dart'; import '../../provider/app_custom_date_format.dart'; import '../../provider/app_developer.dart'; @@ -62,6 +63,11 @@ class AppProviders extends SingleChildStatelessWidget { Provider( create: (context) => Global(), ), + ProxyProvider( + create: (context) => AppCachesViewModel(), + update: (context, profile, previous) => + previous!..updateProfile(profile), + ), ChangeNotifierProvider( create: (context) => NotificationChannelData(), ), diff --git a/lib/view/page_app_setting.dart b/lib/view/page_app_setting.dart index 33d23a94..87171301 100644 --- a/lib/view/page_app_setting.dart +++ b/lib/view/page_app_setting.dart @@ -29,7 +29,6 @@ import '../common/enums.dart'; import '../common/utils.dart'; import '../component/helper.dart'; import '../component/widget.dart'; -import '../db/profile.dart'; import '../extension/context_extensions.dart'; import '../l10n/localizations.dart'; import '../logging/helper.dart'; @@ -39,6 +38,8 @@ import '../model/app_reminder_config.dart'; import '../model/custom_date_format.dart'; import '../model/global.dart'; import '../persistent/db_helper_provider.dart'; +import '../persistent/profile_provider.dart'; +import '../provider/app_caches.dart'; import '../provider/app_compact_ui_switcher.dart'; import '../provider/app_custom_date_format.dart'; import '../provider/app_developer.dart'; @@ -88,6 +89,7 @@ class PageAppSetting extends StatelessWidget { assert(context.maybeRead() != null); assert(context.maybeRead() != null); assert(context.maybeRead() != null); + assert(context.maybeRead() != null); assert(context.maybeRead() != null); return const AppSettingView(); } @@ -140,7 +142,7 @@ class _AppSettingView extends State final result = await showAppSettingClearCacheDialog(context: context); if (!mounted || result != true) return; - final resultList = await context.read().clearAllCache(); + final resultList = await context.read().clearAllCache(); if (!mounted) return; var hasSuss = false, hasFail = false; @@ -290,8 +292,11 @@ class _AppSettingView extends State ); if (result == null || !result) return; - await Profile().clearAll(); - await NotificationService().cancelAppReminder(); + await Future.wait([ + context.read().clear(), + NotificationService().cancelAppReminder() + ]); + await context.read().reload(); if (!mounted) return; final snackBar = BuildWidgetHelper().buildSnackBarWithDismiss( diff --git a/lib/view/page_habit_edit.dart b/lib/view/page_habit_edit.dart index ad5fd28f..2854701d 100644 --- a/lib/view/page_habit_edit.dart +++ b/lib/view/page_habit_edit.dart @@ -31,6 +31,7 @@ import '../model/habit_form.dart'; import '../model/habit_freq.dart'; import '../model/habit_reminder.dart'; import '../persistent/local/handler/habit.dart'; +import '../provider/app_caches.dart'; import '../provider/app_developer.dart'; import '../provider/app_first_day.dart'; import '../provider/habit_form.dart'; @@ -153,13 +154,13 @@ class _HabitEditView extends State { context: context, targetDays: targetDays, initialCustomTargetDays: - context.read().habitEditTargetDaysInputFill, + context.read().habitEditTargetDaysInputFill, ); if (result == null || !mounted) return; context.read().targetDays = result.targetDays; if (result.isCustomDaysType) { context - .read() + .read() .updateHabitEditTargetDaysInputFill(result.targetDays); } } From d2e26fac28d72865d331bdf53d43decef72a4c71 Mon Sep 17 00:00:00 2001 From: Fries_I23 Date: Mon, 1 Apr 2024 09:21:20 +0800 Subject: [PATCH 13/13] feat: change based on review --- lib/persistent/local/handler/record.dart | 2 +- lib/provider/app_compact_ui_switcher.dart | 2 +- lib/view/common/_widget.dart | 2 +- lib/view/page_habit_edit.dart | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/persistent/local/handler/record.dart b/lib/persistent/local/handler/record.dart index e6e4d9fb..3e2f211d 100644 --- a/lib/persistent/local/handler/record.dart +++ b/lib/persistent/local/handler/record.dart @@ -17,9 +17,9 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:sqflite/sqflite.dart'; import '../../../common/types.dart'; -import '../table.dart'; import '../db_cell.dart'; import '../db_helper.dart'; +import '../table.dart'; import 'habit.dart'; part 'record.g.dart'; diff --git a/lib/provider/app_compact_ui_switcher.dart b/lib/provider/app_compact_ui_switcher.dart index 1454b148..d39d4905 100644 --- a/lib/provider/app_compact_ui_switcher.dart +++ b/lib/provider/app_compact_ui_switcher.dart @@ -15,7 +15,7 @@ import 'package:flutter/material.dart'; import '../logging/helper.dart'; -import '../persistent/profile/handler/compact_ui_switcher.dart'; +import '../persistent/profile/handlers.dart'; import '../persistent/profile_provider.dart'; class AppCompactUISwitcherViewModel extends ChangeNotifier diff --git a/lib/view/common/_widget.dart b/lib/view/common/_widget.dart index 628d0c24..628c8866 100644 --- a/lib/view/common/_widget.dart +++ b/lib/view/common/_widget.dart @@ -16,7 +16,7 @@ export './appbar_combined_action.dart' show AppbarActionShowStatus, AppBarActions, AppbarActionItemConfig; export './colorful_navibar.dart' show ColorfulNavibar; export './contributor_tile.dart'; -export 'date_changer.dart'; +export './date_changer.dart'; export './donate_dialog.dart' show DonateDialogResult; export './fixed_page_place_holder.dart'; export './habit_divider.dart'; diff --git a/lib/view/page_habit_edit.dart b/lib/view/page_habit_edit.dart index 2854701d..ec6042ec 100644 --- a/lib/view/page_habit_edit.dart +++ b/lib/view/page_habit_edit.dart @@ -24,7 +24,6 @@ import '../component/widget.dart'; import '../extension/context_extensions.dart'; import '../l10n/localizations.dart'; import '../logging/helper.dart'; -import '../model/global.dart'; import '../model/habit_daily_goal.dart'; import '../model/habit_display.dart'; import '../model/habit_form.dart'; @@ -77,7 +76,7 @@ class PageHabitEdit extends StatelessWidget { assert(context.maybeRead() != null); assert(context.maybeRead() != null); assert(context.maybeRead() != null); - assert(context.maybeRead() != null); + assert(context.maybeRead() != null); return PageProviders( initForm: initForm, child: HabitEditView(