From b4f2110ebe29bdef6a663962e4d7dcffd8082883 Mon Sep 17 00:00:00 2001 From: Fries_I23 Date: Sun, 7 Apr 2024 07:20:44 +0800 Subject: [PATCH 1/6] refactor: cache to implement an interface --- lib/model/cache.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/model/cache.dart b/lib/model/cache.dart index e1d60f66..422ac5a9 100644 --- a/lib/model/cache.dart +++ b/lib/model/cache.dart @@ -16,7 +16,7 @@ import '../common/types.dart'; import '../logging/helper.dart'; import '../persistent/profile/profile_helper.dart'; -abstract class Cache { +abstract interface class Cache { T? getCache(K key); Future updateCache(K key, T? value, {void Function(bool, T?)? onUpdated}); From 33362a8182f9f997af3e11094883c34c08811af9 Mon Sep 17 00:00:00 2001 From: Fries_I23 Date: Sun, 7 Apr 2024 08:27:15 +0800 Subject: [PATCH 2/6] refactor: organize models --- lib/common/enums.dart | 6 +- lib/common/rules.dart | 1 - lib/model/about_info.dart | 65 +++++++--------- lib/model/about_info.g.dart | 20 +++++ lib/model/app_reminder_config.dart | 5 +- lib/model/custom_date_format.dart | 4 +- lib/model/global.dart | 14 ---- lib/model/habit_date.dart | 4 +- lib/model/habit_detail_chart.dart | 18 ++--- lib/model/habit_display.dart | 45 +++-------- lib/model/habit_display.g.dart | 15 ++++ lib/model/habit_form.dart | 24 +++--- lib/model/habit_freq.dart | 5 +- lib/model/habit_reminder.dart | 6 +- lib/model/habit_summary.dart | 78 ++++++++++--------- lib/persistent/local/db_helper.dart | 2 +- .../handler/display_habits_filter.dart | 4 +- lib/provider/app_developer.dart | 1 + .../global.dart} | 27 +++---- lib/provider/habit_detail.dart | 14 ++++ lib/provider/habit_form.dart | 4 +- lib/theme/color.dart | 2 +- lib/view/_debug.dart | 2 +- lib/view/for_app_about/page_providers.dart | 2 +- lib/view/page_habit_detail.dart | 1 - lib/view/page_habit_edit.dart | 1 - lib/view/page_habits_display.dart | 2 +- test/viewmodel_test/habit_form_test.dart | 16 ++-- 28 files changed, 194 insertions(+), 194 deletions(-) create mode 100644 lib/model/about_info.g.dart rename lib/{model/habit_detail_page.dart => provider/global.dart} (55%) diff --git a/lib/common/enums.dart b/lib/common/enums.dart index b7cc5b77..7d6382ab 100644 --- a/lib/common/enums.dart +++ b/lib/common/enums.dart @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -abstract class EnumWithDBCodeABC { +abstract interface class EnumWithDBCode { int get dbCode; } -enum HabitsRecordScrollBehavior implements EnumWithDBCodeABC { +enum HabitsRecordScrollBehavior implements EnumWithDBCode { unknown(code: 0), scrollable(code: 1), page(code: 2); @@ -54,7 +54,7 @@ enum DonateWay { } } -enum UserAction implements EnumWithDBCodeABC { +enum UserAction implements EnumWithDBCode { nothing(code: 0), tap(code: 1), doubleTap(code: 2), diff --git a/lib/common/rules.dart b/lib/common/rules.dart index f10ff0d5..7cc9b948 100644 --- a/lib/common/rules.dart +++ b/lib/common/rules.dart @@ -20,7 +20,6 @@ import 'types.dart'; HabitDailyGoal onDailyGoalTextInputChanged( num newDailyGoal, { required TextEditingController controller, - num defaultValue = defaultHabitDailyGoal, num maxValue = maxHabitdailyGoal, bool allowInputZero = false, }) { diff --git a/lib/model/about_info.dart b/lib/model/about_info.dart index f44b7012..5f81f40e 100644 --- a/lib/model/about_info.dart +++ b/lib/model/about_info.dart @@ -12,19 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -enum _AboutInfoKey { - sourceCodeUrl, - issueTrackerUrl, - contactEmail, - donateBuyMeACoffeeToken, - donatePaypalToken, - donateCryptoBTCAddr, - donateCryptoETHAddr, - donateCryptoBNBAddr, - donateCryptoAVAXAddr, - donateCryptoFTMAddr -} +import 'package:json_annotation/json_annotation.dart'; + +part 'about_info.g.dart'; +@JsonSerializable(createToJson: false) class AboutInfo { final String sourceCodeUrl; final String issueTrackerUrl; @@ -38,31 +30,30 @@ class AboutInfo { final String donateCryptoFTMAddr; const AboutInfo({ - this.sourceCodeUrl = '', - this.issueTrackerUrl = '', - this.contactEmail = '', - this.donateBuyMeACoffeeToken = '', - this.donatePaypalToken = '', - this.donateCryptoBTCAddr = '', - this.donateCryptoETHAddr = '', - this.donateCryptoBNBAddr = '', - this.donateCryptoAVAXAddr = '', - this.donateCryptoFTMAddr = '', + required this.sourceCodeUrl, + required this.issueTrackerUrl, + required this.contactEmail, + required this.donateBuyMeACoffeeToken, + required this.donatePaypalToken, + required this.donateCryptoBTCAddr, + required this.donateCryptoETHAddr, + required this.donateCryptoBNBAddr, + required this.donateCryptoAVAXAddr, + required this.donateCryptoFTMAddr, }); - factory AboutInfo.fromJson(Map json) { - return AboutInfo( - sourceCodeUrl: json[_AboutInfoKey.sourceCodeUrl.name] ?? '', - issueTrackerUrl: json[_AboutInfoKey.issueTrackerUrl.name] ?? '', - contactEmail: json[_AboutInfoKey.contactEmail.name] ?? '', - donateBuyMeACoffeeToken: - json[_AboutInfoKey.donateBuyMeACoffeeToken.name] ?? '', - donatePaypalToken: json[_AboutInfoKey.donatePaypalToken.name] ?? '', - donateCryptoBTCAddr: json[_AboutInfoKey.donateCryptoBTCAddr.name] ?? '', - donateCryptoETHAddr: json[_AboutInfoKey.donateCryptoETHAddr.name] ?? '', - donateCryptoBNBAddr: json[_AboutInfoKey.donateCryptoBNBAddr.name] ?? '', - donateCryptoAVAXAddr: json[_AboutInfoKey.donateCryptoAVAXAddr.name] ?? '', - donateCryptoFTMAddr: json[_AboutInfoKey.donateCryptoFTMAddr.name] ?? '', - ); - } + const AboutInfo.empty() + : sourceCodeUrl = '', + issueTrackerUrl = '', + contactEmail = '', + donateBuyMeACoffeeToken = '', + donatePaypalToken = '', + donateCryptoBTCAddr = '', + donateCryptoETHAddr = '', + donateCryptoBNBAddr = '', + donateCryptoAVAXAddr = '', + donateCryptoFTMAddr = ''; + + factory AboutInfo.fromJson(Map json) => + _$AboutInfoFromJson(json); } diff --git a/lib/model/about_info.g.dart b/lib/model/about_info.g.dart new file mode 100644 index 00000000..18610ae8 --- /dev/null +++ b/lib/model/about_info.g.dart @@ -0,0 +1,20 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'about_info.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +AboutInfo _$AboutInfoFromJson(Map json) => AboutInfo( + sourceCodeUrl: json['sourceCodeUrl'] as String, + issueTrackerUrl: json['issueTrackerUrl'] as String, + contactEmail: json['contactEmail'] as String, + donateBuyMeACoffeeToken: json['donateBuyMeACoffeeToken'] as String, + donatePaypalToken: json['donatePaypalToken'] as String, + donateCryptoBTCAddr: json['donateCryptoBTCAddr'] as String, + donateCryptoETHAddr: json['donateCryptoETHAddr'] as String, + donateCryptoBNBAddr: json['donateCryptoBNBAddr'] as String, + donateCryptoAVAXAddr: json['donateCryptoAVAXAddr'] as String, + donateCryptoFTMAddr: json['donateCryptoFTMAddr'] as String, + ); diff --git a/lib/model/app_reminder_config.dart b/lib/model/app_reminder_config.dart index 80ab8550..10106d8b 100644 --- a/lib/model/app_reminder_config.dart +++ b/lib/model/app_reminder_config.dart @@ -24,7 +24,7 @@ import 'common.dart'; part 'app_reminder_config.g.dart'; @JsonEnum(valueField: "code") -enum AppReminderConfigType implements EnumWithDBCodeABC { +enum AppReminderConfigType implements EnumWithDBCode { daily(code: 1); final int code; @@ -80,8 +80,7 @@ class AppReminderConfig implements JsonAdaptor { } @override - int get hashCode => - hash3(enabled.hashCode, type.hashCode, timeOfDay.hashCode); + int get hashCode => hash3(enabled, type, timeOfDay); @override String toString() { diff --git a/lib/model/custom_date_format.dart b/lib/model/custom_date_format.dart index 2988d718..f47a667f 100644 --- a/lib/model/custom_date_format.dart +++ b/lib/model/custom_date_format.dart @@ -23,7 +23,7 @@ import 'common.dart'; part 'custom_date_format.g.dart'; @JsonEnum(valueField: 'code') -enum YearMonthDayFormtEnum implements EnumWithDBCodeABC { +enum YearMonthDayFormtEnum implements EnumWithDBCode { yearMonthDay(code: 1), monthDayYear(code: 2), dayMonthYear(code: 3); @@ -37,7 +37,7 @@ enum YearMonthDayFormtEnum implements EnumWithDBCodeABC { } @JsonEnum(valueField: 'code') -enum DateSplitCharEnum implements EnumWithDBCodeABC { +enum DateSplitCharEnum implements EnumWithDBCode { dash(code: 1, char: '-'), slash(code: 2, char: '/'), space(code: 3, char: ' '), diff --git a/lib/model/global.dart b/lib/model/global.dart index 3c03eb0c..18ffb54f 100644 --- a/lib/model/global.dart +++ b/lib/model/global.dart @@ -14,8 +14,6 @@ import 'package:flutter/foundation.dart'; -import '../logging/helper.dart'; - class Global { Global(); @@ -28,15 +26,3 @@ class Global { bool get displayDebugMenu => _displayDebugMenu ?? kDebugMode ? true : false; void switchDisplayDebugMenu(bool value) => _displayDebugMenu = value; } - -abstract mixin class GlobalLoadedMixin { - late Global _g; - - Global get g => _g; - - @mustCallSuper - void updateGlobal(Global newGloal) { - appLog.load.info("$runtimeType.updateGlobal", ex: [newGloal]); - _g = newGloal; - } -} diff --git a/lib/model/habit_date.dart b/lib/model/habit_date.dart index e6bbcc89..348510d4 100644 --- a/lib/model/habit_date.dart +++ b/lib/model/habit_date.dart @@ -146,9 +146,7 @@ class HabitDate implements DateTime, DateTimeExtensionsABC { int get hashCode => _date.hashCode; @override - String toString() { - return _date.toString(); - } + String toString() => "${_date.year}-${_date.month}-${_date.day}"; @override bool operator <(DateTime other) => _date < other; diff --git a/lib/model/habit_detail_chart.dart b/lib/model/habit_detail_chart.dart index 33db1bbc..373b0dea 100644 --- a/lib/model/habit_detail_chart.dart +++ b/lib/model/habit_detail_chart.dart @@ -187,12 +187,12 @@ class HabitDetailFreqChartData { } @override - String toString() { - return 'HabitDetailFreqChartData(p=$_partiallyCompleted,a=$_autoComplate,' - 'c=$_complate,o=$_overfulfil || pv=$_partiallyCompletedTotalValue,' - 'av=$_autoComplateTotalValue,cv=$_complateTotalValue,' - 'ov=$_overfulfilTotalValue}'; - } + String toString() => 'HabitDetailFreqChartData(' + 'pavo=$_partiallyCompleted|$_autoComplate|$_complate|$_overfulfil,' + 'pv=$_partiallyCompletedTotalValue,' + 'av=$_autoComplateTotalValue,' + 'cv=$_complateTotalValue,' + 'ov=$_overfulfilTotalValue)'; } class HabitDetailScoreChartDate { @@ -212,8 +212,6 @@ class HabitDetailScoreChartDate { } @override - String toString() { - return 'HabitDetailScoreChartDate{totalScore: ' - '$totalScore, count: $count, avgScore: $avgScore}'; - } + String toString() => + 'HabitDetailScoreChartDate(ts=$totalScore,c=$count,avs=$avgScore)'; } diff --git a/lib/model/habit_display.dart b/lib/model/habit_display.dart index 1fedf35a..4345637e 100644 --- a/lib/model/habit_display.dart +++ b/lib/model/habit_display.dart @@ -26,7 +26,7 @@ import 'habit_summary.dart'; part 'habit_display.g.dart'; -enum HabitDisplaySortType implements EnumWithDBCodeABC { +enum HabitDisplaySortType implements EnumWithDBCode { manual(code: 1), name(code: 2), colorType(code: 3), @@ -59,7 +59,7 @@ enum HabitDisplaySortType implements EnumWithDBCodeABC { ]; } -enum HabitDisplaySortDirection implements EnumWithDBCodeABC { +enum HabitDisplaySortDirection implements EnumWithDBCode { asc(code: 1), desc(code: 2); @@ -82,19 +82,6 @@ enum HabitDisplaySortDirection implements EnumWithDBCodeABC { enum HabitDisplayEditMode { create, edit } -enum HabitsDisplayFilterKey { - // Compatibility with old version configuration options - allowInProgressHabits(key: "allowActivedHabits"), - allowArchivedHabits(), - allowCompleteHabits(); - - final String? _key; - - const HabitsDisplayFilterKey({String? key}) : _key = key; - - String get key => _key ?? name; -} - class HabitDisplayEditParams { final HabitUUID uuid; final DateTime createT; @@ -115,16 +102,18 @@ class HabitDisplayEditParams { @override String toString() { - return "HabitEditParams($uuid, $createT, $modifyT)"; + return "HabitEditParams($uuid,$createT,$modifyT)"; } } @CopyWith() +@JsonSerializable() class HabitsDisplayFilter { static const _defaultAllowInProgressHabits = true; static const _defualtAllowArchivedHabits = false; static const _defaultAllowCompleteHabits = true; + @JsonKey(name: "allowActivedHabits") final bool allowInProgressHabits; final bool allowArchivedHabits; final bool allowCompleteHabits; @@ -146,24 +135,10 @@ class HabitsDisplayFilter { allowArchivedHabits = _defualtAllowArchivedHabits, allowCompleteHabits = _defaultAllowCompleteHabits; - HabitsDisplayFilter.fromMap(Map data) - : allowInProgressHabits = - (data[HabitsDisplayFilterKey.allowInProgressHabits.key] ?? - _defaultAllowInProgressHabits) as bool, - allowArchivedHabits = - (data[HabitsDisplayFilterKey.allowArchivedHabits.key] ?? - _defualtAllowArchivedHabits) as bool, - allowCompleteHabits = - (data[HabitsDisplayFilterKey.allowCompleteHabits.key] ?? - _defaultAllowCompleteHabits) as bool; - - Map toMap() { - return { - HabitsDisplayFilterKey.allowInProgressHabits.key: allowInProgressHabits, - HabitsDisplayFilterKey.allowArchivedHabits.key: allowArchivedHabits, - HabitsDisplayFilterKey.allowCompleteHabits.key: allowCompleteHabits, - }; - } + factory HabitsDisplayFilter.fromJson(JsonMap data) => + _$HabitsDisplayFilterFromJson(data); + + JsonMap toJson() => _$HabitsDisplayFilterToJson(this); bool Function(HabitSummaryData) getDisplayFilterFunction() { bool func(HabitSummaryData data) { @@ -195,7 +170,7 @@ class HabitsDisplayFilter { hash3(allowInProgressHabits, allowArchivedHabits, allowCompleteHabits); @override - String toString() => "$runtimeType(${toMap()})"; + String toString() => "$runtimeType(${toJson()})"; } @JsonSerializable( diff --git a/lib/model/habit_display.g.dart b/lib/model/habit_display.g.dart index c4c3ef78..92a6fddf 100644 --- a/lib/model/habit_display.g.dart +++ b/lib/model/habit_display.g.dart @@ -146,6 +146,21 @@ extension $HabitDisplayOpConfigCopyWith on HabitDisplayOpConfig { // JsonSerializableGenerator // ************************************************************************** +HabitsDisplayFilter _$HabitsDisplayFilterFromJson(Map json) => + HabitsDisplayFilter( + allowInProgressHabits: json['allowActivedHabits'] as bool, + allowArchivedHabits: json['allowArchivedHabits'] as bool, + allowCompleteHabits: json['allowCompleteHabits'] as bool, + ); + +Map _$HabitsDisplayFilterToJson( + HabitsDisplayFilter instance) => + { + 'allowActivedHabits': instance.allowInProgressHabits, + 'allowArchivedHabits': instance.allowArchivedHabits, + 'allowCompleteHabits': instance.allowCompleteHabits, + }; + HabitDisplayOpConfig _$HabitDisplayOpConfigFromJson( Map json) => HabitDisplayOpConfig( diff --git a/lib/model/habit_form.dart b/lib/model/habit_form.dart index 703401f4..f2dedde8 100644 --- a/lib/model/habit_form.dart +++ b/lib/model/habit_form.dart @@ -27,7 +27,7 @@ import 'habit_freq.dart'; import 'habit_reminder.dart'; @JsonEnum(valueField: 'code') -enum HabitType implements EnumWithDBCodeABC { +enum HabitType implements EnumWithDBCode { unknown(code: 0), normal(code: 1), negative(code: 2); @@ -74,7 +74,7 @@ enum HabitType implements EnumWithDBCodeABC { } @JsonEnum(valueField: 'code') -enum HabitStatus implements EnumWithDBCodeABC { +enum HabitStatus implements EnumWithDBCode { unknown(code: 0), activated(code: 1), deleted(code: 2), @@ -97,7 +97,7 @@ enum HabitStatus implements EnumWithDBCodeABC { } @JsonEnum(valueField: 'code') -enum HabitColorType implements EnumWithDBCodeABC { +enum HabitColorType implements EnumWithDBCode { cc1(code: 1), cc2(code: 2), cc3(code: 3), @@ -126,7 +126,7 @@ enum HabitColorType implements EnumWithDBCodeABC { } @JsonEnum(valueField: 'code') -enum HabitFrequencyType implements EnumWithDBCodeABC { +enum HabitFrequencyType implements EnumWithDBCode { unknown(code: 0), weekly(code: 1), monthly(code: 2), @@ -149,7 +149,7 @@ enum HabitFrequencyType implements EnumWithDBCodeABC { } @JsonEnum(valueField: 'code') -enum HabitRecordStatus implements EnumWithDBCodeABC { +enum HabitRecordStatus implements EnumWithDBCode { unknown(code: 0), done(code: 1), skip(code: 2); @@ -219,7 +219,7 @@ class HabitForm { dailyGoal = cell.dailyGoal, dailyGoalUnit = cell.dailyGoalUnit, dailyGoalExtra = cell.dailyGoalExtra, - frequency = HabitFrequency.fromMap( + frequency = HabitFrequency.fromJson( {"type": cell.freqType, "args": jsonDecode(cell.freqCustom!)}), startDate = HabitStartDate.fromEpochDay(cell.startDate!), targetDays = cell.targetDays, @@ -231,11 +231,11 @@ class HabitForm { @override String toString() { - return 'HabitForm(name=$name, type=$type, ' - 'colorType=$colorType, dailyGoal=$dailyGoal, ' - 'dailyGoalUnit=$dailyGoalUnit, dailyGoalExtra=$dailyGoalExtra, ' - 'frequency=$frequency, startDate=$startDate, targetDays=$targetDays, ' - 'desc=$desc, reminder=$reminder, reminderQuest=$reminderQuest, ' - 'editMode=$editMode, editParams=$editParams)'; + return 'HabitForm(name=$name,type=$type,colorType=$colorType,' + 'dailyGoal=$dailyGoal,dailyGoalUnit=$dailyGoalUnit,' + 'dailyGoalExtra=$dailyGoalExtra,frequency=$frequency,' + 'startDate=$startDate,targetDays=$targetDays,desc=$desc,' + 'reminder=$reminder,reminderQuest=$reminderQuest,editMode=$editMode,' + 'editParams=$editParams)'; } } diff --git a/lib/model/habit_freq.dart b/lib/model/habit_freq.dart index 5e615c52..036a85ab 100644 --- a/lib/model/habit_freq.dart +++ b/lib/model/habit_freq.dart @@ -17,6 +17,7 @@ import 'dart:math' as math; import 'package:quiver/core.dart'; import '../common/exceptions.dart'; +import '../common/types.dart'; import '../l10n/localizations.dart'; import 'habit_form.dart'; @@ -58,7 +59,7 @@ class HabitFrequency { } } - static HabitFrequency fromMap(Map data) { + static HabitFrequency fromJson(JsonMap data) { final type = HabitFrequencyType.getFromDBCode(data["type"])!; switch (type) { case HabitFrequencyType.weekly: @@ -73,7 +74,7 @@ class HabitFrequency { } } - Map toMap() { + JsonMap toJson() { Map result = {"type": type.dbCode}; switch (type) { case HabitFrequencyType.weekly: diff --git a/lib/model/habit_reminder.dart b/lib/model/habit_reminder.dart index d87a4d38..53d44f63 100644 --- a/lib/model/habit_reminder.dart +++ b/lib/model/habit_reminder.dart @@ -29,7 +29,7 @@ import 'habit_date.dart'; part 'habit_reminder.g.dart'; @JsonEnum(valueField: 'code') -enum HabitReminderType implements EnumWithDBCodeABC { +enum HabitReminderType implements EnumWithDBCode { unknown(code: 0), whenNeeded(code: 1), day(code: 2), @@ -287,7 +287,5 @@ class HabitReminder implements JsonAdaptor { } @override - String toString() { - return toJson().toString(); - } + String toString() => "$runtimeType($toJson())"; } diff --git a/lib/model/habit_summary.dart b/lib/model/habit_summary.dart index a87db3ef..d2823f97 100644 --- a/lib/model/habit_summary.dart +++ b/lib/model/habit_summary.dart @@ -175,7 +175,6 @@ class HabitSummaryData with _HabitSummaryDataRecordsMixin, DirtyMarkMixin { num _progress = 0.0; final SplayTreeSet _autoMarkedRecords = SplayTreeSet((a, b) => a.compareTo(b)); - // final Set _autoMarkedRecords = {}; HabitSummaryData({ required this.id, @@ -206,7 +205,7 @@ class HabitSummaryData with _HabitSummaryDataRecordsMixin, DirtyMarkMixin { dailyGoal = cell.dailyGoal!, dailyGoalExtra = cell.dailyGoalExtra, targetDays = cell.targetDays!, - frequency = HabitFrequency.fromMap( + frequency = HabitFrequency.fromJson( {"type": cell.freqType, "args": jsonDecode(cell.freqCustom!)}), startDate = HabitStartDate.fromEpochDay(cell.startDate!), status = HabitStatus.getFromDBCode(cell.status!)!, @@ -252,7 +251,7 @@ class HabitSummaryData with _HabitSummaryDataRecordsMixin, DirtyMarkMixin { num debugCalcTotalScore({HabitRecordDate? endDate}) { assert(kDebugMode); num result = 0.0; - var calculator = getCalculator(); + final calculator = getCalculator(); calculator.calculate( onTotalScoreCalculated: (score) { result = math.min(math.max(score, 0), 100); @@ -330,9 +329,9 @@ class HabitSummaryData with _HabitSummaryDataRecordsMixin, DirtyMarkMixin { Iterable _calculateAutoComplateRecordsCustom() { assert(frequency.type == HabitFrequencyType.custom); - var markedDateSet = {}; - var window = Queue(); - var dateNow = HabitDate.now(); + final markedDateSet = {}; + final window = Queue(); + final dateNow = HabitDate.now(); HabitRecordDate crtDate; HabitSummaryRecord crtRecord; @@ -360,7 +359,7 @@ class HabitSummaryData with _HabitSummaryDataRecordsMixin, DirtyMarkMixin { // debugPrint("window: $window"); if (window.length < frequency.freq) continue; - int insideDays = window.last.epochDay - window.first.epochDay + 1; + final insideDays = window.last.epochDay - window.first.epochDay + 1; if (insideDays > frequency.days) { window.removeFirst(); continue; @@ -374,7 +373,7 @@ class HabitSummaryData with _HabitSummaryDataRecordsMixin, DirtyMarkMixin { int lastDays = frequency.days - insideDays; - var leftLastDays = lastDays; + final leftLastDays = lastDays; for (var i = 1; i <= leftLastDays; i++) { var leftMarkDate = window.first.subtractDays(i); if (markedDateSet.contains(leftMarkDate) || leftMarkDate < startDate) { @@ -386,7 +385,7 @@ class HabitSummaryData with _HabitSummaryDataRecordsMixin, DirtyMarkMixin { } for (var i = 1; i <= lastDays; i++) { - var rightMarkDate = window.last.addDays(i); + final rightMarkDate = window.last.addDays(i); // debugPrint("right add[$lastDays]: $rightMarkDate"); // calculate full automarks // if (rightMarkDate > dateNow) break; @@ -403,12 +402,12 @@ class HabitSummaryData with _HabitSummaryDataRecordsMixin, DirtyMarkMixin { int firstDay) sync* { assert(frequency.type == HabitFrequencyType.weekly); - var yearWeekRecordMap = {}; - var dateNow = HabitDate.now(); + final yearWeekRecordMap = {}; + final dateNow = HabitDate.now(); for (var e in _recordDateCacheMap.entries) { - var crtDate = e.key; - var crtRecord = e.value; + final crtDate = e.key; + final crtRecord = e.value; if (crtDate > dateNow) break; @@ -437,7 +436,7 @@ class HabitSummaryData with _HabitSummaryDataRecordsMixin, DirtyMarkMixin { crtCount = e.value; if (crtCount < frequency.freq) continue; for (var i = DateTime.daysPerWeek - 1; i >= 0; i--) { - var result = crtFirstDate.add(Duration(days: i)); + final result = crtFirstDate.add(Duration(days: i)); // calculate full automarks // if (result > dateNow) continue; if (result < startDate) break; @@ -449,12 +448,12 @@ class HabitSummaryData with _HabitSummaryDataRecordsMixin, DirtyMarkMixin { Iterable _calculateAutoComplateRecordsMonthly() sync* { assert(frequency.type == HabitFrequencyType.monthly); - var yearMonthRecordMap = {}; - var dateNow = HabitDate.now(); + final yearMonthRecordMap = {}; + final dateNow = HabitDate.now(); for (var e in _recordDateCacheMap.entries) { - var crtDate = e.key; - var crtRecord = e.value; + final crtDate = e.key; + final crtRecord = e.value; if (crtDate > dateNow) break; @@ -482,7 +481,7 @@ class HabitSummaryData with _HabitSummaryDataRecordsMixin, DirtyMarkMixin { crtCount = e.value; if (crtCount < frequency.freq) continue; for (var i = 0; i < crtLastDate.day; i++) { - var result = crtLastDate.subtract(Duration(days: i)); + final result = crtLastDate.subtract(Duration(days: i)); // calculate full automarks // if (result > dateNow) continue; if (result < startDate) break; @@ -508,11 +507,21 @@ class HabitSummaryData with _HabitSummaryDataRecordsMixin, DirtyMarkMixin { @override String toString() { - return "HabitAboutData(id=$id, uuid=$uuid, type=${type.dbCode}, " - "name=$name, color=$colorType, " - "dailyGoal=$dailyGoal, freq=$frequency, startDate=$startDate, " - "status=$status, sort=$sortPostion score=$progress, version=$diryMark, " - "records={${getAllRecord().toList()}})"; + String getRecordsString() { + final iterable = getAllRecord(); + return iterable.length > 10 + ? [ + ...iterable.take(5), + '... [ignore ${iterable.length - 10} records] ...', + ...iterable.skip(iterable.length - 5) + ].join("|") + : iterable.join("|"); + } + + return "HabitAboutData(id=$id,uuid=$uuid,type=${type.dbCode},name=$name," + "color=$colorType,dailyGoal=$dailyGoal,freq=$frequency," + "startDate=$startDate,status=$status,sort=$sortPostion,score=$progress," + "version=$diryMark,records={${getRecordsString()}})"; } } @@ -534,12 +543,12 @@ class HabitSummaryDataCollection { _dataMap[data.uuid] = data; } for (final cell in recordResult) { - var habitCell = getHabitByUUID(cell.parentUUID!); + final habitCell = getHabitByUUID(cell.parentUUID!); if (habitCell == null) continue; // Memory optimization: Don't cache records before start date. // When the StartDate changes, it is necessary to reload the data from // database to ensure that the cache is complete. - var record = HabitSummaryRecord.fromDBQueryCell(cell); + final record = HabitSummaryRecord.fromDBQueryCell(cell); if (record.date.isBefore(habitCell.startDate)) continue; habitCell.addRecord(record); } @@ -584,7 +593,7 @@ class HabitSummaryDataCollection { List sortDataByName( HabitDisplaySortDirection sortDirecton) { int compareble(HabitSummaryData a, HabitSummaryData b) { - var r1 = a.name.compareTo(b.name); + final r1 = a.name.compareTo(b.name); if (r1 != 0) { return r1; } else { @@ -598,7 +607,7 @@ class HabitSummaryDataCollection { List sortDataByColorType( HabitDisplaySortDirection sortDirecton) { int compareble(HabitSummaryData a, HabitSummaryData b) { - var r1 = a.colorType.dbCode.compareTo(b.colorType.dbCode); + final r1 = a.colorType.dbCode.compareTo(b.colorType.dbCode); if (r1 != 0) { return r1; } else { @@ -612,7 +621,7 @@ class HabitSummaryDataCollection { List sortDataByProgress( HabitDisplaySortDirection sortDirecton) { int compareble(HabitSummaryData a, HabitSummaryData b) { - var r1 = b.progress.compareTo(a.progress); + final r1 = b.progress.compareTo(a.progress); if (r1 != 0) { return r1; } else { @@ -735,7 +744,7 @@ class HabitSummaryDataSortCache @override String toString() { - return "HabitSummaryDataSortCache(uuid=$uuid, weakrefData=$data)"; + return "HabitSummaryDataSortCache(uuid=$uuid,weakrefData=$data)"; } } @@ -756,16 +765,13 @@ class HabitSummaryStatusCache { @override String toString() { - return "HabitSummaryStatusCache($isAppbarPinned, $reloadDBToggleSwich, " - "$reloadUIToggleSwitch, $isClandarExpanded, $isInEditMode)"; + return "HabitSummaryStatusCache($isAppbarPinned|$reloadDBToggleSwich|" + "$reloadUIToggleSwitch|$isClandarExpanded|$isInEditMode)"; } @override bool operator ==(Object other) { - if (identical(this, other)) { - return true; - } - + if (identical(this, other)) return true; return other is HabitSummaryStatusCache && isAppbarPinned == other.isAppbarPinned && reloadDBToggleSwich == other.reloadDBToggleSwich && diff --git a/lib/persistent/local/db_helper.dart b/lib/persistent/local/db_helper.dart index 43041542..a961233b 100644 --- a/lib/persistent/local/db_helper.dart +++ b/lib/persistent/local/db_helper.dart @@ -115,7 +115,7 @@ class _DBHelper implements DBHelper { appLog.db.info("local.$runtimeType.init", ex: ["processing"]); if (kDebugMode && debugClearDBWhenStart) await _deleteBD(dbPath); _db = await _openDB(dbPath); - appLog.db.info("local.$runtimeType.init", ex: ["done"]); + appLog.db.info("local.$runtimeType.init", ex: ["done", _db]); } Future initOld() async { diff --git a/lib/persistent/profile/handler/display_habits_filter.dart b/lib/persistent/profile/handler/display_habits_filter.dart index 11c8b1f2..af4f292c 100644 --- a/lib/persistent/profile/handler/display_habits_filter.dart +++ b/lib/persistent/profile/handler/display_habits_filter.dart @@ -43,12 +43,12 @@ final class _Decoder extends Converter { @override HabitsDisplayFilter convert(JsonMap input) => - HabitsDisplayFilter.fromMap(input); + HabitsDisplayFilter.fromJson(input); } final class _Encoder extends Converter { const _Encoder(); @override - JsonMap convert(HabitsDisplayFilter input) => input.toMap(); + JsonMap convert(HabitsDisplayFilter input) => input.toJson(); } diff --git a/lib/provider/app_developer.dart b/lib/provider/app_developer.dart index 56292dc2..95584f2b 100644 --- a/lib/provider/app_developer.dart +++ b/lib/provider/app_developer.dart @@ -17,6 +17,7 @@ import 'package:flutter/material.dart'; import '../common/global.dart'; import '../logging/level.dart'; import '../model/global.dart'; +import 'global.dart'; class AppDeveloperViewModel extends ChangeNotifier with GlobalLoadedMixin { AppDeveloperViewModel({required Global global}) { diff --git a/lib/model/habit_detail_page.dart b/lib/provider/global.dart similarity index 55% rename from lib/model/habit_detail_page.dart rename to lib/provider/global.dart index 238f37d7..f0326c8b 100644 --- a/lib/model/habit_detail_page.dart +++ b/lib/provider/global.dart @@ -1,10 +1,10 @@ -// Copyright 2023 Fries_I23 +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 +// 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, @@ -12,18 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'habit_status.dart'; +import 'package:flutter/material.dart'; -enum DetailPageReturnOpr { unknown, deleted } +import '../logging/helper.dart'; +import '../model/global.dart'; -class DetailPageReturn { - final DetailPageReturnOpr op; - final String? habitName; - final List? recordList; +abstract mixin class GlobalLoadedMixin { + late Global _g; - const DetailPageReturn({ - this.op = DetailPageReturnOpr.unknown, - this.habitName, - this.recordList, - }); + Global get g => _g; + + @mustCallSuper + void updateGlobal(Global newGloal) { + appLog.load.info("$runtimeType.updateGlobal", ex: [newGloal]); + _g = newGloal; + } } diff --git a/lib/provider/habit_detail.dart b/lib/provider/habit_detail.dart index e603e11c..72c7d0d2 100644 --- a/lib/provider/habit_detail.dart +++ b/lib/provider/habit_detail.dart @@ -654,3 +654,17 @@ class ScoreChartCalculator { return result; } } + +enum DetailPageReturnOpr { unknown, deleted } + +class DetailPageReturn { + final DetailPageReturnOpr op; + final String? habitName; + final List? recordList; + + const DetailPageReturn({ + this.op = DetailPageReturnOpr.unknown, + this.habitName, + this.recordList, + }); +} diff --git a/lib/provider/habit_form.dart b/lib/provider/habit_form.dart index c935a029..0a3682b6 100644 --- a/lib/provider/habit_form.dart +++ b/lib/provider/habit_form.dart @@ -261,7 +261,7 @@ class HabitFormViewModel extends ChangeNotifier } Future _saveNewHabit({bool returnResult = false}) async { - final freq = frequency.toMap(); + final freq = frequency.toJson(); final now = DateTime.now().millisecondsSinceEpoch ~/ onSecondMS; final dbCell = HabitDBCell( type: habitType.dbCode, @@ -293,7 +293,7 @@ class HabitFormViewModel extends ChangeNotifier } Future _saveExistHabit({bool returnResult = false}) async { - final freq = frequency.toMap(); + final freq = frequency.toJson(); final dbCell = HabitDBCell( type: habitType.dbCode, uuid: _form.editParams!.uuid, diff --git a/lib/theme/color.dart b/lib/theme/color.dart index b92c9ee2..bbadf857 100644 --- a/lib/theme/color.dart +++ b/lib/theme/color.dart @@ -19,7 +19,7 @@ export '_colors/crypto_colors.dart'; export '_colors/custom_color.g.dart'; export '_colors/userdefined_color.dart'; -enum AppThemeType implements EnumWithDBCodeABC { +enum AppThemeType implements EnumWithDBCode { unknown(code: 0), light(code: 1), dark(code: 2), diff --git a/lib/view/_debug.dart b/lib/view/_debug.dart index 2845e452..c6d6739d 100644 --- a/lib/view/_debug.dart +++ b/lib/view/_debug.dart @@ -55,7 +55,7 @@ mixin HabitsDisplayViewDebug { final tasks = []; final now = DateTime.now().millisecondsSinceEpoch ~/ onSecondMS; final rnd = Random(); - final freq = HabitFrequency.custom().toMap(); + final freq = HabitFrequency.custom().toJson(); for (var i = 0; i < count; i++) { final uuid = genHabitUUID(); final meta = debugGetRandomHabitMeta(rnd); diff --git a/lib/view/for_app_about/page_providers.dart b/lib/view/for_app_about/page_providers.dart index e9ba74a4..de11d37e 100644 --- a/lib/view/for_app_about/page_providers.dart +++ b/lib/view/for_app_about/page_providers.dart @@ -36,7 +36,7 @@ class PageProviders extends SingleChildStatelessWidget { providers: [ FutureProvider( create: (_) async => loadAboutInfoData(), - initialData: const AboutInfo(), + initialData: const AboutInfo.empty(), ), ], child: child, diff --git a/lib/view/page_habit_detail.dart b/lib/view/page_habit_detail.dart index 174e82ee..9377cf36 100644 --- a/lib/view/page_habit_detail.dart +++ b/lib/view/page_habit_detail.dart @@ -35,7 +35,6 @@ import '../logging/helper.dart'; import '../model/custom_date_format.dart'; import '../model/habit_date.dart'; import '../model/habit_detail_chart.dart'; -import '../model/habit_detail_page.dart'; import '../model/habit_display.dart'; import '../model/habit_form.dart'; import '../persistent/local/handler/habit.dart'; diff --git a/lib/view/page_habit_edit.dart b/lib/view/page_habit_edit.dart index 22e20c05..b4335caf 100644 --- a/lib/view/page_habit_edit.dart +++ b/lib/view/page_habit_edit.dart @@ -451,7 +451,6 @@ class _HabitEditView extends State { ? onDailyGoalTextInputChanged( newDailyGoalExtra, controller: formvm.dailyGoalExtraFieldInpuController, - defaultValue: formvm.dailyGoal, maxValue: maxHabitdailyGoalExtra, allowInputZero: true, ) diff --git a/lib/view/page_habits_display.dart b/lib/view/page_habits_display.dart index d3dcc583..20c802b6 100644 --- a/lib/view/page_habits_display.dart +++ b/lib/view/page_habits_display.dart @@ -33,7 +33,6 @@ import '../l10n/localizations.dart'; import '../logging/helper.dart'; import '../model/habit_daily_record_form.dart'; import '../model/habit_date.dart'; -import '../model/habit_detail_page.dart'; import '../model/habit_display.dart'; import '../model/habit_form.dart'; import '../model/habit_stat.dart'; @@ -43,6 +42,7 @@ import '../persistent/local/handler/habit.dart'; import '../provider/app_compact_ui_switcher.dart'; import '../provider/app_developer.dart'; import '../provider/app_theme.dart'; +import '../provider/habit_detail.dart'; import '../provider/habit_op_config.dart'; import '../provider/habit_summary.dart'; import '../provider/habits_file_exporter.dart'; diff --git a/test/viewmodel_test/habit_form_test.dart b/test/viewmodel_test/habit_form_test.dart index 878f89f9..cc95d983 100644 --- a/test/viewmodel_test/habit_form_test.dart +++ b/test/viewmodel_test/habit_form_test.dart @@ -179,21 +179,21 @@ void main() { }); test('toMap:monthly', () { var obj2 = const HabitFrequency.monthly(freq: 3); - var obj2map = obj2.toMap(); + var obj2map = obj2.toJson(); expect(obj2map['type'], HabitFrequencyType.monthly.dbCode); expect((obj2map['args'] as List).length, 1); expect(obj2map['args'][0], 3); }); test('toMap:weekly', () { var obj3 = const HabitFrequency.weekly(freq: 1); - var obj3map = obj3.toMap(); + var obj3map = obj3.toJson(); expect(obj3map['type'], HabitFrequencyType.weekly.dbCode); expect((obj3map['args'] as List).length, 1); expect(obj3map['args'][0], 1); }); test('toMap:daily', () { var obj1 = HabitFrequency.custom(days: 5, freq: 2); - var obj1map = obj1.toMap(); + var obj1map = obj1.toJson(); expect(obj1map['type'], HabitFrequencyType.custom.dbCode); expect((obj1map['args'] as List).length, 2); expect(obj1map['args'][0], 2); @@ -201,14 +201,14 @@ void main() { }); test('toMapError', () { var obj = const HabitFrequency(type: HabitFrequencyType.unknown, freq: 0); - expect(() => obj.toMap(), throwsA(isA())); + expect(() => obj.toJson(), throwsA(isA())); }); test('fromMap:monthly', () { var data1 = { "type": HabitFrequencyType.monthly.dbCode, "args": [10], }; - var obj1 = HabitFrequency.fromMap(data1); + var obj1 = HabitFrequency.fromJson(data1); expect(obj1.type, HabitFrequencyType.monthly); expect(obj1.freq, 10); }); @@ -217,7 +217,7 @@ void main() { "type": HabitFrequencyType.weekly.dbCode, "args": [5], }; - var obj1 = HabitFrequency.fromMap(data1); + var obj1 = HabitFrequency.fromJson(data1); expect(obj1.type, HabitFrequencyType.weekly); expect(obj1.freq, 5); }); @@ -226,7 +226,7 @@ void main() { "type": HabitFrequencyType.custom.dbCode, "args": [2, 10], }; - var obj1 = HabitFrequency.fromMap(data1); + var obj1 = HabitFrequency.fromJson(data1); expect(obj1.type, HabitFrequencyType.custom); expect(obj1.freq, 2); expect(obj1.days, 10); @@ -236,7 +236,7 @@ void main() { "type": HabitFrequencyType.unknown.dbCode, "args": [1], }; - expect(() => HabitFrequency.fromMap(data1), + expect(() => HabitFrequency.fromJson(data1), throwsA(isA())); }); }); From 9e3942ecfb570ff39517ab8f056594e085658283 Mon Sep 17 00:00:00 2001 From: Fries_I23 Date: Sun, 7 Apr 2024 14:41:39 +0800 Subject: [PATCH 3/6] fix: loading completer --- lib/persistent/db_helper_provider.dart | 19 ++++--- lib/persistent/local/db_helper.dart | 8 ++- lib/persistent/local/handler/record.dart | 2 +- lib/persistent/profile_provider.dart | 18 +++---- lib/provider/habit_detail.dart | 68 ++++++++++++++---------- lib/provider/habit_summary.dart | 65 +++++++++++++++------- lib/view/page_habit_detail.dart | 15 +++--- lib/view/page_habits_display.dart | 8 +-- 8 files changed, 125 insertions(+), 78 deletions(-) diff --git a/lib/persistent/db_helper_provider.dart b/lib/persistent/db_helper_provider.dart index 18d7c3a3..808e569f 100644 --- a/lib/persistent/db_helper_provider.dart +++ b/lib/persistent/db_helper_provider.dart @@ -16,6 +16,7 @@ import 'dart:async'; import 'package:async/async.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import '../common/async.dart'; import '../common/consts.dart'; @@ -31,19 +32,23 @@ class DBHelperViewModel extends ChangeNotifier implements ProviderMounted, AsyncInitialization { final DBHelper local; - Completer? _completer; + CancelableCompleter? _completer; bool _mounted = true; DBHelperViewModel() : local = DBHelper(); @override Future init() async { - if (_completer == null) { - _completer = Completer(); + Future doInit() async { await local.init(); - _completer!.complete(); + _completer?.complete(); + } + + if (_completer == null) { + _completer = CancelableCompleter(); + doInit(); } - return _completer!.future; + return _completer?.operation.value; } @override @@ -51,7 +56,7 @@ class DBHelperViewModel extends ChangeNotifier _mounted = false; if (inited) local.dispose(); if (_completer?.isCompleted != true) { - CancelableOperation.fromFuture(_completer!.future).cancel(); + _completer?.operation.cancel(); _completer = null; } super.dispose(); @@ -59,7 +64,7 @@ class DBHelperViewModel extends ChangeNotifier Future reload() async { if (_completer?.isCompleted != true) { - CancelableOperation.fromFuture(_completer!.future).cancel(); + _completer?.operation.cancel(); _completer = null; } await local.init(reinit: true); diff --git a/lib/persistent/local/db_helper.dart b/lib/persistent/local/db_helper.dart index a961233b..a78ec0d0 100644 --- a/lib/persistent/local/db_helper.dart +++ b/lib/persistent/local/db_helper.dart @@ -94,6 +94,7 @@ class _DBHelper implements DBHelper { onConfigure: (db) async { db.execute("PRAGMA foreign_keys = ON"); }, + singleInstance: false, ); } @@ -131,7 +132,12 @@ class _DBHelper implements DBHelper { @override void dispose() { - db.close(); + appLog.db.info("local.$runtimeType.dispose", ex: [db]); + final orginDB = db; + Future.delayed(const Duration(seconds: 1)).then((_) { + appLog.db.info("local.$runtimeType.dispose", ex: ["close db", orginDB]); + if (orginDB.isOpen) orginDB.close(); + }); } @override diff --git a/lib/persistent/local/handler/record.dart b/lib/persistent/local/handler/record.dart index 3e2f211d..ced0a5e2 100644 --- a/lib/persistent/local/handler/record.dart +++ b/lib/persistent/local/handler/record.dart @@ -81,7 +81,7 @@ class RecordDBCell with DBCell { } class RecordDBHelper extends DBHelperHandler { - RecordDBHelper(super.helper); + const RecordDBHelper(super.helper); @override String get table => TableName.records; diff --git a/lib/persistent/profile_provider.dart b/lib/persistent/profile_provider.dart index 200b31e2..a63c225c 100644 --- a/lib/persistent/profile_provider.dart +++ b/lib/persistent/profile_provider.dart @@ -34,7 +34,7 @@ class ProfileViewModel extends ChangeNotifier final Iterable _handlerBuilders; late final Map _handlers; - Completer? _completer; + CancelableCompleter? _completer; bool _mounted = true; ProfileViewModel(Iterable builders) @@ -60,21 +60,21 @@ class ProfileViewModel extends ChangeNotifier handlerKeyColl[e.key] = e.runtimeType; return true; }).map((e) => MapEntry(e.runtimeType, e))); + _completer?.complete(); } if (_completer == null) { - _completer = Completer(); - await doInit(); - _completer!.complete(); + _completer = CancelableCompleter(); + doInit(); } - return _completer!.future; + return _completer?.operation.value; } @override void dispose() { _mounted = false; if (_completer?.isCompleted != true) { - CancelableOperation.fromFuture(_completer!.future).cancel(); + _completer?.operation.cancel(); _completer = null; } super.dispose(); @@ -82,7 +82,7 @@ class ProfileViewModel extends ChangeNotifier Future reload() async { if (_completer?.isCompleted != true) { - CancelableOperation.fromFuture(_completer!.future).cancel(); + _completer?.operation.cancel(); _completer = null; } await _pref.reload(); @@ -101,8 +101,8 @@ class ProfileViewModel extends ChangeNotifier @override String toString() { - return "$runtimeType[$hashCode](pref=$_pref,mounted=$mounted," - "inited=$inited,handlers=$_handlers)"; + return "$runtimeType[$hashCode](pref=${inited ? _pref : null}," + "mounted=$mounted,inited=$inited,handlers=$_handlers)"; } } diff --git a/lib/provider/habit_detail.dart b/lib/provider/habit_detail.dart index 72c7d0d2..3bdd894c 100644 --- a/lib/provider/habit_detail.dart +++ b/lib/provider/habit_detail.dart @@ -38,11 +38,6 @@ import 'utils.dart'; const defaultHabitDetailFreqChardCombine = HabitDetailFreqChartCombine.monthly; const defaultHabitDetailScoreChartCombine = HabitDetailScoreChartCombine.daily; -enum HabitDetailLoadDataResult { - done, - habitMissing, -} - class HabitDetailViewModel extends ChangeNotifier with NotificationChannelDataMixin, DBHelperLoadedMixin, DBOperationsMixin implements ProviderMounted, HabitSummaryDirtyMarker { @@ -52,7 +47,7 @@ class HabitDetailViewModel extends ChangeNotifier final _habitScoreChangedDateColl = {}; // status bool _reloadDBToggleSwich = false; - Completer? _loading; + CancelableCompleter? _loading; HabitDetailFreqChartCombine _freqChartCombine = defaultHabitDetailFreqChardCombine; HabitDetailScoreChartCombine _scoreChartCombine = @@ -152,49 +147,64 @@ class HabitDetailViewModel extends ChangeNotifier } void _cancelLoading() { - if (_loading != null && !_loading!.isCompleted) { - CancelableOperation.fromFuture(_loading!.future).cancel(); + if (_loading?.isCompleted != true) { + appLog.load.info("$runtimeType._cancelLoading", ex: [_loading]); + _loading?.operation.cancel(); } _loading = null; } - Future loadData(HabitUUID uuid, + Future loadData(HabitUUID uuid, {bool listen = true, bool inFutureBuilder = false}) async { if (_loading != null) { appLog.load.warn("$runtimeType.load", ex: ["data already loaded", uuid]); - return _loading!.future; + return _loading!.operation.value; } - Future loadingData() async { + void loadingFailed(List errmsg) { + appLog.load.warn("$runtimeType.load", ex: errmsg); + _loading?.completeError(errmsg); + } + + Future loadingData() async { + appLog.load.debug("$runtimeType.load", + ex: ["loading data", listen, inFutureBuilder]); + if (!mounted) { + loadingFailed(["viewmodel disposed"]); + return; + } + // init habit final dataLoadTask = habitDBHelper.loadHabitDetail(uuid); final recordLoadTask = recordDBHelper.loadRecords(uuid); final cell = await dataLoadTask; final records = await recordLoadTask; if (cell == null) { - appLog.load.warn("$runtimeType.load", ex: ["data load failed", uuid]); - return null; + loadingFailed(["data load failed", uuid]); + return; + } + if (!mounted) { + loadingFailed(["viewmodel disposed", uuid]); + return; } final data = HabitDetailData.fromDBQueryCell(cell); data.data.initRecords( records.map((e) => HabitSummaryRecord.fromDBQueryCell(e))); - return data; + _habitDetailData = data; + _calcHabitInfo(); + appLog.load.debug("$runtimeType.load", + ex: ["loaded", listen, inFutureBuilder, data]); + // complete + _loading?.complete(); + // reload + if (listen) { + if (!inFutureBuilder) _reloadDBToggleSwich = !_reloadDBToggleSwich; + notifyListeners(); + } } - final completer = _loading = Completer(); - appLog.load.debug("$runtimeType.load", - ex: ["loading data", listen, inFutureBuilder]); - _habitDetailData = await loadingData(); - if (_habitDetailData != null) _calcHabitInfo(); - if (listen) { - if (!inFutureBuilder) _reloadDBToggleSwich = !_reloadDBToggleSwich; - notifyListeners(); - } - appLog.load.debug("$runtimeType.load", - ex: ["loaded", listen, inFutureBuilder, _habitDetailData]); - completer.complete(_habitDetailData != null - ? HabitDetailLoadDataResult.done - : HabitDetailLoadDataResult.habitMissing); - return completer.future; + _loading = CancelableCompleter(); + loadingData(); + return _loading?.operation.value; } void _calcHabitInfo() { diff --git a/lib/provider/habit_summary.dart b/lib/provider/habit_summary.dart index 0842d9fb..f5f5ad5b 100644 --- a/lib/provider/habit_summary.dart +++ b/lib/provider/habit_summary.dart @@ -62,7 +62,7 @@ class HabitSummaryViewModel extends ChangeNotifier final _selectorData = _SelectedHabitsData(); final _last30daysProgressChangeData = HabitLast30DaysProgressChangeData(); // status - Completer? _loading; + CancelableCompleter? _loading; bool _reloadDBToggleSwich = false; bool _nextRefreshClearSnackBar = false; bool _reloadUIToggleSwitch = false; @@ -247,8 +247,9 @@ class HabitSummaryViewModel extends ChangeNotifier } void _cancelLoading() { - if (_loading != null && !_loading!.isCompleted) { - CancelableOperation.fromFuture(_loading!.future).cancel(); + if (_loading?.isCompleted != true) { + appLog.load.info("$runtimeType._cancelLoading", ex: [_loading]); + _loading?.operation.cancel(); } _loading = null; } @@ -256,24 +257,50 @@ class HabitSummaryViewModel extends ChangeNotifier Future loadData({bool listen = true, bool inFutureBuilder = false}) async { if (_loading != null) { appLog.load.warn("$runtimeType.loadData", ex: ["data already loaded"]); - return _loading!.future; + return _loading?.operation.value; } - final completer = _loading = Completer(); - final habitLoadTask = habitDBHelper.loadHabitAboutDataCollection(); - final recordLoadTask = recordDBHelper.loadAllRecords(); - _data.initDataFromDBQueuryResult(await habitLoadTask, await recordLoadTask); - _data.forEach((_, habit) => _calcHabitAutoComplateRecords(habit)); - _resortData(); - completer.complete(); - if (listen) { - if (!inFutureBuilder) _reloadDBToggleSwich = !_reloadDBToggleSwich; - notifyListeners(); + + void loadingFailed(List errmsg) { + appLog.load.warn("$runtimeType.load", ex: errmsg); + _loading?.completeError(errmsg); } - // init reminders - final futureList = []; - _data.forEach((_, habit) => futureList.add(_regrHabitReminder(habit))); - await Future.wait(futureList); - return completer.future; + + Future loadingData() async { + appLog.load.debug("$runtimeType.load", + ex: ["loading data", _loading.hashCode, listen, inFutureBuilder]); + if (!mounted) { + loadingFailed(["viewmodel disposed"]); + return; + } + // init habits + final habitLoadTask = habitDBHelper.loadHabitAboutDataCollection(); + final recordLoadTask = recordDBHelper.loadAllRecords(); + _data.initDataFromDBQueuryResult( + await habitLoadTask, await recordLoadTask); + if (!mounted) { + loadingFailed(["viewmodel disposed"]); + return; + } + _data.forEach((_, habit) => _calcHabitAutoComplateRecords(habit)); + _resortData(); + // complete + _loading?.complete(); + // init reminders + final futureList = []; + _data.forEach((_, habit) => futureList.add(_regrHabitReminder(habit))); + await Future.wait(futureList); + // reload + if (listen) { + if (!inFutureBuilder) _reloadDBToggleSwich = !_reloadDBToggleSwich; + notifyListeners(); + } + appLog.load.debug("$runtimeType.load", + ex: ["loaded", _loading.hashCode, listen, inFutureBuilder]); + } + + _loading = CancelableCompleter(); + loadingData(); + return _loading?.operation.value; } HabitSummaryData? getHabit(HabitUUID habitUUID) { diff --git a/lib/view/page_habit_detail.dart b/lib/view/page_habit_detail.dart index 9377cf36..e70f12b3 100644 --- a/lib/view/page_habit_detail.dart +++ b/lib/view/page_habit_detail.dart @@ -881,15 +881,15 @@ class _HabitDetailView extends State selector: (context, viewmodel) => viewmodel.reloadDBToggleSwich, shouldRebuild: (previous, next) => previous != next, builder: (context, _, child) { - var viewmodel = context.read(); - - Future loadData() async { - final loading = - viewmodel.loadData(widget.habitUUID, inFutureBuilder: true); + Future loadData() async { + final loading = context + .read() + .loadData(widget.habitUUID, inFutureBuilder: true); await Future.delayed(_kHabitDetailFutureLoadDuration); - return await loading; + await loading; } + final viewmodel = context.read(); return FutureBuilder( future: loadData(), builder: (context, snapshot) { @@ -913,8 +913,7 @@ class _HabitDetailView extends State colorType: viewmodel.habitColorType, ), ); - } else if (snapshot.isDone && - snapshot.data == HabitDetailLoadDataResult.habitMissing) { + } else if (snapshot.hasError) { switcherWidget = SliverFillRemaining( hasScrollBody: false, child: L10nBuilder( diff --git a/lib/view/page_habits_display.dart b/lib/view/page_habits_display.dart index 20c802b6..bcdfab3e 100644 --- a/lib/view/page_habits_display.dart +++ b/lib/view/page_habits_display.dart @@ -96,8 +96,8 @@ class _HabitsDisplayView extends State void initState() { appLog.build.debug(context, ex: ["init"]); super.initState(); - var viewmodel = context.read(); - var dispatcher = AnimatedListDiffListDispatcher( + final viewmodel = context.read(); + final dispatcher = AnimatedListDiffListDispatcher( controller: AnimatedListController(), itemBuilder: (context, element, data) { if (data.measuring) { @@ -974,8 +974,8 @@ class _HabitsDisplayView extends State selector: (context, viewmodel) => viewmodel.reloadDBToggleSwich, shouldRebuild: (previous, next) => previous != next, builder: (context, value, child) { - Future loadData() async { - var loadedFuture = context + Future loadData() async { + final loadedFuture = context .read() .loadData(inFutureBuilder: true); await Future.delayed(_kHabitListFutureLoadDuration); From 6ce2ade2b51aa7ccf15133f9585641017e9a34f9 Mon Sep 17 00:00:00 2001 From: Fries_I23 Date: Sun, 7 Apr 2024 14:48:28 +0800 Subject: [PATCH 4/6] refactor: move about info to provider folder --- lib/{model => provider}/about_info.dart | 0 lib/{model => provider}/about_info.g.dart | 0 lib/view/for_app_about/app_about_contact_email_tile.dart | 2 +- lib/view/for_app_about/app_about_issue_tracker_tile.dart | 2 +- lib/view/for_app_about/app_about_source_code_tile.dart | 2 +- lib/view/for_app_about/page_providers.dart | 2 +- lib/view/page_app_about.dart | 2 +- 7 files changed, 5 insertions(+), 5 deletions(-) rename lib/{model => provider}/about_info.dart (100%) rename lib/{model => provider}/about_info.g.dart (100%) diff --git a/lib/model/about_info.dart b/lib/provider/about_info.dart similarity index 100% rename from lib/model/about_info.dart rename to lib/provider/about_info.dart diff --git a/lib/model/about_info.g.dart b/lib/provider/about_info.g.dart similarity index 100% rename from lib/model/about_info.g.dart rename to lib/provider/about_info.g.dart diff --git a/lib/view/for_app_about/app_about_contact_email_tile.dart b/lib/view/for_app_about/app_about_contact_email_tile.dart index be4a6bcd..67ca42bb 100644 --- a/lib/view/for_app_about/app_about_contact_email_tile.dart +++ b/lib/view/for_app_about/app_about_contact_email_tile.dart @@ -21,7 +21,7 @@ import '../../common/utils.dart'; import '../../l10n/localizations.dart'; import '../../logging/helper.dart'; import '../../logging/logger_stack.dart'; -import '../../model/about_info.dart'; +import '../../provider/about_info.dart'; import '_widget.dart'; class AppAboutContactEmailTile extends StatefulWidget { diff --git a/lib/view/for_app_about/app_about_issue_tracker_tile.dart b/lib/view/for_app_about/app_about_issue_tracker_tile.dart index 1e325521..ebc32539 100644 --- a/lib/view/for_app_about/app_about_issue_tracker_tile.dart +++ b/lib/view/for_app_about/app_about_issue_tracker_tile.dart @@ -21,7 +21,7 @@ import '../../common/utils.dart'; import '../../l10n/localizations.dart'; import '../../logging/helper.dart'; import '../../logging/logger_stack.dart'; -import '../../model/about_info.dart'; +import '../../provider/about_info.dart'; import '_widget.dart'; class AppAboutIssueTrackerTile extends StatefulWidget { diff --git a/lib/view/for_app_about/app_about_source_code_tile.dart b/lib/view/for_app_about/app_about_source_code_tile.dart index 2d858d58..9b8d63c8 100644 --- a/lib/view/for_app_about/app_about_source_code_tile.dart +++ b/lib/view/for_app_about/app_about_source_code_tile.dart @@ -21,7 +21,7 @@ import '../../common/utils.dart'; import '../../l10n/localizations.dart'; import '../../logging/helper.dart'; import '../../logging/logger_stack.dart'; -import '../../model/about_info.dart'; +import '../../provider/about_info.dart'; import '_widget.dart'; class AppAboutSourceCodeTile extends StatefulWidget { diff --git a/lib/view/for_app_about/page_providers.dart b/lib/view/for_app_about/page_providers.dart index de11d37e..88b3c508 100644 --- a/lib/view/for_app_about/page_providers.dart +++ b/lib/view/for_app_about/page_providers.dart @@ -20,7 +20,7 @@ import 'package:nested/nested.dart'; import 'package:provider/provider.dart'; import '../../common/consts.dart'; -import '../../model/about_info.dart'; +import '../../provider/about_info.dart'; class PageProviders extends SingleChildStatelessWidget { const PageProviders({super.key, super.child}); diff --git a/lib/view/page_app_about.dart b/lib/view/page_app_about.dart index 763500df..3cfcf667 100644 --- a/lib/view/page_app_about.dart +++ b/lib/view/page_app_about.dart @@ -21,8 +21,8 @@ import 'package:provider/provider.dart'; import '../assets/assets.dart'; import '../component/widget.dart'; import '../logging/helper.dart'; -import '../model/about_info.dart'; import '../model/contributor.dart'; +import '../provider/about_info.dart'; import 'common/_dialog.dart'; import 'common/contributor_tile.dart'; import 'for_app_about/_widget.dart'; From 7def520ee8290e1e8acecdcc2a879ee35a5bdd20 Mon Sep 17 00:00:00 2001 From: Fries_I23 Date: Sun, 7 Apr 2024 15:06:32 +0800 Subject: [PATCH 5/6] refactor: move global to provider folder --- lib/model/global.dart | 28 ---------------------------- lib/provider/app_developer.dart | 1 - lib/provider/global.dart | 16 ++++++++++++++-- lib/view/for_app/app_providers.dart | 2 +- 4 files changed, 15 insertions(+), 32 deletions(-) delete mode 100644 lib/model/global.dart diff --git a/lib/model/global.dart b/lib/model/global.dart deleted file mode 100644 index 18ffb54f..00000000 --- a/lib/model/global.dart +++ /dev/null @@ -1,28 +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/foundation.dart'; - -class Global { - Global(); - - bool _isInDevelopMode = kDebugMode ? true : false; - bool? _displayDebugMenu; - - bool get isInDevelopMode => _isInDevelopMode; - void switchDevelopMode(bool value) => _isInDevelopMode = value; - - bool get displayDebugMenu => _displayDebugMenu ?? kDebugMode ? true : false; - void switchDisplayDebugMenu(bool value) => _displayDebugMenu = value; -} diff --git a/lib/provider/app_developer.dart b/lib/provider/app_developer.dart index 95584f2b..b98f272b 100644 --- a/lib/provider/app_developer.dart +++ b/lib/provider/app_developer.dart @@ -16,7 +16,6 @@ import 'package:flutter/material.dart'; import '../common/global.dart'; import '../logging/level.dart'; -import '../model/global.dart'; import 'global.dart'; class AppDeveloperViewModel extends ChangeNotifier with GlobalLoadedMixin { diff --git a/lib/provider/global.dart b/lib/provider/global.dart index f0326c8b..94b5853d 100644 --- a/lib/provider/global.dart +++ b/lib/provider/global.dart @@ -12,10 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; import '../logging/helper.dart'; -import '../model/global.dart'; + +class Global { + Global(); + + bool _isInDevelopMode = kDebugMode ? true : false; + bool? _displayDebugMenu; + + bool get isInDevelopMode => _isInDevelopMode; + void switchDevelopMode(bool value) => _isInDevelopMode = value; + + bool get displayDebugMenu => _displayDebugMenu ?? kDebugMode ? true : false; + void switchDisplayDebugMenu(bool value) => _displayDebugMenu = value; +} abstract mixin class GlobalLoadedMixin { late Global _g; diff --git a/lib/view/for_app/app_providers.dart b/lib/view/for_app/app_providers.dart index 4486d03c..de41af40 100644 --- a/lib/view/for_app/app_providers.dart +++ b/lib/view/for_app/app_providers.dart @@ -17,7 +17,6 @@ import 'package:nested/nested.dart'; 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_caches.dart'; @@ -27,6 +26,7 @@ import '../../provider/app_developer.dart'; import '../../provider/app_first_day.dart'; import '../../provider/app_reminder.dart'; import '../../provider/app_theme.dart'; +import '../../provider/global.dart'; import '../../provider/habit_op_config.dart'; import '../../provider/habits_file_exporter.dart'; import '../../provider/habits_file_importer.dart'; From 6b2c4928e698232d3446753ec910f10cba5f783c Mon Sep 17 00:00:00 2001 From: Fries_I23 Date: Sun, 7 Apr 2024 16:00:32 +0800 Subject: [PATCH 6/6] feat: raise flutter error --- lib/provider/habit_detail.dart | 7 +++++-- lib/provider/habit_summary.dart | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/provider/habit_detail.dart b/lib/provider/habit_detail.dart index 3bdd894c..f5cba642 100644 --- a/lib/provider/habit_detail.dart +++ b/lib/provider/habit_detail.dart @@ -22,6 +22,7 @@ import '../common/exceptions.dart'; import '../common/types.dart'; import '../common/utils.dart'; import '../logging/helper.dart'; +import '../logging/logger_stack.dart'; import '../model/habit_daily_record_form.dart'; import '../model/habit_date.dart'; import '../model/habit_detail.dart'; @@ -162,8 +163,10 @@ class HabitDetailViewModel extends ChangeNotifier } void loadingFailed(List errmsg) { - appLog.load.warn("$runtimeType.load", ex: errmsg); - _loading?.completeError(errmsg); + appLog.load.error("$runtimeType.load", + ex: errmsg, stackTrace: LoggerStackTrace.from(StackTrace.current)); + _loading?.completeError( + FlutterError(errmsg.join(" ")), StackTrace.current); } Future loadingData() async { diff --git a/lib/provider/habit_summary.dart b/lib/provider/habit_summary.dart index f5f5ad5b..81d35beb 100644 --- a/lib/provider/habit_summary.dart +++ b/lib/provider/habit_summary.dart @@ -26,6 +26,7 @@ import '../common/exceptions.dart'; import '../common/types.dart'; import '../common/utils.dart'; import '../logging/helper.dart'; +import '../logging/logger_stack.dart'; import '../model/habit_date.dart'; import '../model/habit_display.dart'; import '../model/habit_form.dart'; @@ -261,8 +262,10 @@ class HabitSummaryViewModel extends ChangeNotifier } void loadingFailed(List errmsg) { - appLog.load.warn("$runtimeType.load", ex: errmsg); - _loading?.completeError(errmsg); + appLog.load.error("$runtimeType.load", + ex: errmsg, stackTrace: LoggerStackTrace.from(StackTrace.current)); + _loading?.completeError( + FlutterError(errmsg.join(" ")), StackTrace.current); } Future loadingData() async {