diff --git a/lib/alarm/types/alarm.dart b/lib/alarm/types/alarm.dart index bd23d5df..d4f004e3 100644 --- a/lib/alarm/types/alarm.dart +++ b/lib/alarm/types/alarm.dart @@ -450,4 +450,8 @@ class Alarm extends CustomizableListItem { 'settings': _settings.valueToJson(), 'skippedTime': _skippedTime?.millisecondsSinceEpoch, }; + + bool isEqualTo(Alarm other) { + return _time == other._time && _settings.isEqualTo(other._settings); + } } diff --git a/lib/common/types/tag.dart b/lib/common/types/tag.dart index d665fbfe..0e1fae0e 100644 --- a/lib/common/types/tag.dart +++ b/lib/common/types/tag.dart @@ -49,4 +49,10 @@ class Tag extends ListItem { description = other.description; color = other.color; } + + bool isEqualTo(Tag other) { + return name == other.name && + description == other.description && + color == other.color; + } } diff --git a/lib/common/types/time.dart b/lib/common/types/time.dart index 4cd27f5c..74f29bce 100644 --- a/lib/common/types/time.dart +++ b/lib/common/types/time.dart @@ -69,4 +69,18 @@ class Time extends JsonSerializable { return time >= startTime || time <= endTime; } } + + @override + bool operator ==(Object other) { + if (other is Time) { + return hour == other.hour && + minute == other.minute && + second == other.second; + } + return false; + } + + @override + int get hashCode => Object.hash(hour, minute, second); + } diff --git a/lib/settings/data/backup_options.dart b/lib/settings/data/backup_options.dart index c6fc30f3..01c76d5a 100644 --- a/lib/settings/data/backup_options.dart +++ b/lib/settings/data/backup_options.dart @@ -4,6 +4,7 @@ import 'package:clock_app/alarm/logic/update_alarms.dart'; import 'package:clock_app/alarm/types/alarm.dart'; import 'package:clock_app/app.dart'; import 'package:clock_app/clock/types/city.dart'; +import 'package:clock_app/common/types/tag.dart'; import 'package:clock_app/common/utils/json_serialize.dart'; import 'package:clock_app/common/utils/list_storage.dart'; import 'package:clock_app/settings/data/settings_schema.dart'; @@ -27,11 +28,13 @@ final backupOptions = [ return await loadTextFile("tags"); }, decode: (context, value) async { - await saveList("tags", [ - ...listFromString(value) - .map((tag) => TimerPreset.from(tag)), - ...await loadList("tags") - ]); + final existingItems = await loadList("tags"); + final itemsToAdd = listFromString(value) + .where((tag) => + !existingItems.any((existingTag) => existingTag.isEqualTo(tag))) + .map((tag) => Tag.from(tag)); + + await saveList("tags", [...itemsToAdd, ...existingItems]); }, ), BackupOption( @@ -45,10 +48,15 @@ final backupOptions = [ return listToString(customColorSchemes); }, decode: (context, value) async { + final existingItems = await loadList("color_schemes"); + final itemsToAdd = listFromString(value) + .where((colorScheme) => !existingItems.any((existingColorScheme) => + existingColorScheme.isEqualTo(colorScheme))) + .map((scheme) => ColorSchemeData.from(scheme)); + await saveList("color_schemes", [ - ...listFromString(value) - .map((scheme) => ColorSchemeData.from(scheme)), - ...await loadList("color_schemes") + ...itemsToAdd, + ...existingItems, ]); if (context.mounted) App.refreshTheme(context); }, @@ -63,11 +71,13 @@ final backupOptions = [ return listToString(customThemes); }, decode: (context, value) async { - await saveList("style_themes", [ - ...listFromString(value) - .map((theme) => StyleTheme.from(theme)), - ...await loadList("style_themes") - ]); + final existingItems = await loadList("style_themes"); + final itemsToAdd = listFromString(value) + .where((theme) => !existingItems + .any((existingTheme) => existingTheme.isEqualTo(theme))) + .map((theme) => StyleTheme.from(theme)); + await saveList( + "style_themes", [...itemsToAdd, ...existingItems]); if (context.mounted) App.refreshTheme(context); }, ), @@ -94,10 +104,12 @@ final backupOptions = [ return await loadTextFile("alarms"); }, decode: (context, value) async { - await saveList("alarms", [ - ...listFromString(value).map((alarm) => Alarm.fromAlarm(alarm)), - ...await loadList("alarms") - ]); + final existingItems = await loadList("alarms"); + final itemsToAdd = listFromString(value) + .where((alarm) => !existingItems + .any((existingAlarm) => existingAlarm.isEqualTo(alarm))) + .map((alarm) => Alarm.fromAlarm(alarm)); + await saveList("alarms", [...itemsToAdd, ...existingItems]); await updateAlarms("Updated alarms on importing backup"); }, ), @@ -108,11 +120,12 @@ final backupOptions = [ return await loadTextFile("timers"); }, decode: (context, value) async { - await saveList("timers", [ - ...listFromString(value) - .map((timer) => ClockTimer.from(timer)), - ...await loadList("timers") - ]); + final existingItems = await loadList("timers"); + final itemsToAdd = listFromString(value) + .where((timer) => !existingItems + .any((existingTimer) => existingTimer.isEqualTo(timer))) + .map((timer) => ClockTimer.from(timer)); + await saveList("timers", [...itemsToAdd, ...existingItems]); await updateTimers("Updated timers on importing backup"); }, ), @@ -151,11 +164,14 @@ final backupOptions = [ return await loadTextFile("timer_presets"); }, decode: (context, value) async { - await saveList("timer_presets", [ - ...listFromString(value) - .map((preset) => TimerPreset.from(preset)), - ...await loadList("timer_presets") - ]); + final existingItems = await loadList("timer_presets"); + final itemsToAdd = listFromString(value) + .where((preset) => !existingItems + .any((existingPreset) => existingPreset.isEqualTo(preset))) + .map((preset) => TimerPreset.from(preset)); + + await saveList( + "timer_presets", [...itemsToAdd, ...existingItems]); }, ), ]; diff --git a/lib/settings/types/setting.dart b/lib/settings/types/setting.dart index af975507..1acbdae7 100644 --- a/lib/settings/types/setting.dart +++ b/lib/settings/types/setting.dart @@ -68,7 +68,8 @@ abstract class Setting extends SettingItem { } } -class CustomizableListSetting extends Setting> { +class CustomizableListSetting + extends Setting> { List possibleItems; Widget Function(T item, [VoidCallback?, VoidCallback?]) cardBuilder; Widget Function(T item) addCardBuilder; @@ -198,8 +199,6 @@ class ListSetting extends Setting> { ); } - - Widget getItemAddCard(T item) { return addCardBuilder(item); } @@ -209,7 +208,6 @@ class ListSetting extends Setting> { return cardBuilder(item, onDelete, onDuplicate); } - @override dynamic valueToJson() { return _value.map((e) => e.toJson()).toList(); @@ -222,7 +220,6 @@ class ListSetting extends Setting> { } } - class CustomSetting extends Setting { // The screen that will be navigated to when this setting is tapped. Widget Function(BuildContext, CustomSetting) screenBuilder; diff --git a/lib/settings/types/setting_group.dart b/lib/settings/types/setting_group.dart index f72263f7..c2a7cc41 100644 --- a/lib/settings/types/setting_group.dart +++ b/lib/settings/types/setting_group.dart @@ -171,6 +171,10 @@ class SettingGroup extends SettingItem { } } + bool isEqualTo(SettingGroup other) { + return json.encode(valueToJson()) == json.encode(other.valueToJson()); + } + @override dynamic valueToJson() { Json json = {}; diff --git a/lib/settings/types/setting_item.dart b/lib/settings/types/setting_item.dart index 49d162c3..606c8587 100644 --- a/lib/settings/types/setting_item.dart +++ b/lib/settings/types/setting_item.dart @@ -16,11 +16,9 @@ abstract class SettingItem { // Settings which influence whether this setting is enabled List enableSettings; - String displayName(BuildContext context) => - getLocalizedName(context); + String displayName(BuildContext context) => getLocalizedName(context); - String displayDescription(BuildContext context) => - getDescription(context); + String displayDescription(BuildContext context) => getDescription(context); bool get isEnabled { for (var enableSetting in enableSettings) { @@ -48,8 +46,8 @@ abstract class SettingItem { return path.reversed.toList(); } - SettingItem( - this.name, this.getLocalizedName, this.getDescription, this.searchTags, this.enableConditions ) + SettingItem(this.name, this.getLocalizedName, this.getDescription, + this.searchTags, this.enableConditions) : id = name, _settingListeners = [], enableSettings = []; diff --git a/lib/theme/types/color_scheme.dart b/lib/theme/types/color_scheme.dart index 24be3a5c..ece52d7e 100644 --- a/lib/theme/types/color_scheme.dart +++ b/lib/theme/types/color_scheme.dart @@ -97,6 +97,22 @@ class ColorSchemeData extends ThemeItem { ColorSchemeData.fromJson(Json json) : super.fromJson(json, colorSchemeSettingsSchema.copy()); + + bool isEqualTo(ColorSchemeData other) { + return background == other.background && + error == other.error && + accent == other.accent && + onError == other.onError && + card == other.card && + onCard == other.onCard && + onAccent == other.onAccent && + onBackground == other.onBackground && + shadow == other.shadow && + outline == other.outline && + useAccentAsShadow == other.useAccentAsShadow && + useAccentAsOutline == other.useAccentAsOutline && + name == other.name; + } } ColorScheme getColorScheme(ColorSchemeData colorSchemeData) { diff --git a/lib/theme/types/style_theme.dart b/lib/theme/types/style_theme.dart index c4775334..f0be1244 100644 --- a/lib/theme/types/style_theme.dart +++ b/lib/theme/types/style_theme.dart @@ -40,7 +40,7 @@ class StyleTheme extends ThemeItem { .setValueWithoutNotify(borderWidth); } - StyleTheme.from(StyleTheme colorSchemeData) : super.from(colorSchemeData); + StyleTheme.from(StyleTheme super.colorSchemeData) : super.from(); @override String get name => settings.getSetting("Name").value; @@ -65,4 +65,14 @@ class StyleTheme extends ThemeItem { StyleTheme.fromJson(Json json) : super.fromJson(json, styleThemeSettingsSchema.copy()); + + bool isEqualTo(StyleTheme other) { + return name == other.name && + shadowElevation == other.shadowElevation && + shadowOpacity == other.shadowOpacity && + shadowBlurRadius == other.shadowBlurRadius && + shadowSpreadRadius == other.shadowSpreadRadius && + borderRadius == other.borderRadius && + borderWidth == other.borderWidth; + } } diff --git a/lib/timer/types/time_duration.dart b/lib/timer/types/time_duration.dart index fd7bc61f..7deeecf1 100644 --- a/lib/timer/types/time_duration.dart +++ b/lib/timer/types/time_duration.dart @@ -112,4 +112,18 @@ class TimeDuration extends JsonSerializable { minutes = json != null ? json['minutes'] ?? 0 : 0, seconds = json != null ? json['seconds'] ?? 0 : 0, milliseconds = json != null ? json['milliseconds'] ?? 0 : 0; + + @override + bool operator ==(Object other) { + if (other is TimeDuration) { + return hours == other.hours && + minutes == other.minutes && + seconds == other.seconds && + milliseconds == other.milliseconds; + } + return false; + } + + @override + int get hashCode => Object.hash(hours, minutes, seconds, milliseconds); } diff --git a/lib/timer/types/timer.dart b/lib/timer/types/timer.dart index 87f8ca2f..6cd2b9e7 100644 --- a/lib/timer/types/timer.dart +++ b/lib/timer/types/timer.dart @@ -153,7 +153,7 @@ class ClockTimer extends CustomizableListItem { } Future snooze() async { - TimeDuration addedDuration = TimeDuration(minutes: addLength.floor()); + TimeDuration addedDuration = TimeDuration(minutes: addLength.floor()); _currentDuration = addedDuration; _milliSecondsRemainingOnPause = addedDuration.inSeconds * 1000; await start(); @@ -262,4 +262,8 @@ class ClockTimer extends CustomizableListItem { copy() { return ClockTimer.from(this); } + + bool isEqualTo(ClockTimer other) { + return _duration == other._duration && _settings.isEqualTo(other._settings); + } } diff --git a/lib/timer/types/timer_preset.dart b/lib/timer/types/timer_preset.dart index 4a15b3a6..aef325c5 100644 --- a/lib/timer/types/timer_preset.dart +++ b/lib/timer/types/timer_preset.dart @@ -1,6 +1,7 @@ import 'package:clock_app/common/types/json.dart'; import 'package:clock_app/common/types/list_item.dart'; import 'package:clock_app/common/utils/id.dart'; +import 'package:clock_app/developer/logic/logger.dart'; import 'package:clock_app/timer/types/time_duration.dart'; class TimerPreset extends ListItem { @@ -45,6 +46,10 @@ class TimerPreset extends ListItem { } } + bool isEqualTo(TimerPreset other) { + return name == other.name && duration == other.duration; + } + @override copy() { return TimerPreset(name, duration);