-
-
Notifications
You must be signed in to change notification settings - Fork 336
Expand file tree
/
Copy pathconfig_dbo.dart
More file actions
220 lines (212 loc) · 9.28 KB
/
Copy pathconfig_dbo.dart
File metadata and controls
220 lines (212 loc) · 9.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
import 'package:hive_ce_flutter/hive_flutter.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:opennutritracker/core/data/dbo/app_theme_dbo.dart';
import 'package:opennutritracker/core/domain/entity/config_entity.dart';
part 'config_dbo.g.dart';
@HiveType(typeId: 13)
@JsonSerializable() // Used for exporting to JSON
class ConfigDBO extends HiveObject {
@HiveField(0)
bool hasAcceptedDisclaimer;
@HiveField(1)
bool hasAcceptedPolicy;
@HiveField(2)
bool hasAcceptedSendAnonymousData;
@HiveField(3)
AppThemeDBO selectedAppTheme;
@HiveField(4)
bool? usesImperialUnits;
@HiveField(5)
double? userKcalAdjustment;
@HiveField(6)
double? userCarbGoalPct;
@HiveField(7)
double? userProteinGoalPct;
@HiveField(8)
double? userFatGoalPct;
@HiveField(9)
bool? showActivityTracking;
@HiveField(10)
bool? notificationsEnabled;
@HiveField(11)
int? notificationHour;
@HiveField(12)
int? notificationMinute;
@HiveField(13)
String? selectedLocale;
@HiveField(14)
bool? showMealMacros;
@HiveField(15)
bool? showMicronutrients; // #237: null means default (false)
@HiveField(16)
bool? usesKilojoules; // #177: null means default (false → kcal)
// #150: per-meal kcal share, percent values keyed by meal type
// ("breakfast" / "lunch" / "dinner" / "snack"). Null means use defaults.
@HiveField(17)
Map<String, int>? mealKcalSharesPct;
@HiveField(18)
String? customMealFormMode; // #232: 'simple' or 'advanced'; null means default (simple)
@HiveField(19)
int? dayStartOffsetHours; // #139: 0-23, null means default (0 = wall-clock midnight)
// #160 follow-up: per-nutrient show/hide map for the daily nutrient panel.
// Keys are nutrient identifiers from [DailyNutrientPanel]; values are
// explicit user overrides. A nutrient not present in the map (or a null
// map altogether) means "use the default visibility", which is currently
// visible for every nutrient — see [ConfigEntity.isNutrientVisible].
@HiveField(22)
Map<String, bool>? nutrientPanelVisibility;
/// Per-meal sort preference for the diary day view. Keys are meal type
/// strings (`breakfast` / `lunch` / `dinner` / `snack`) and values are the
/// `DiarySortType` enum index. Nullable so existing configs without a
/// persisted preference keep falling back to the widget-state default
/// (`DiarySortType.timeAdded`) until the user picks a sort.
@HiveField(21)
Map<String, int>? diarySortPreferences;
// #139 follow-up: 0-59 minute companion. Composes additively with
// dayStartOffsetHours so existing users with hours=4 and a null minutes
// value see a 4:00 boundary, unchanged.
@HiveField(23)
int? dayStartOffsetMinutes;
// #32: configurable daily water goal in millilitres. Null means use the
// default (2000 ml), so existing configs without a stored value keep
// working without a migration.
@HiveField(24)
int? dailyWaterGoalMl;
// #84: persisted acknowledgement of the disordered-eating sensitivity
// warning shown the first time the fasting screen is opened. Once true,
// the warning dialog stays suppressed. Nullable so existing configs
// (and never-opened users) keep being treated as "not yet acknowledged".
@HiveField(25)
bool? fastingWarningAcknowledged;
// #415: opt-in Material You dynamic colour scheme. Null means the user
// has not made a deliberate choice yet, which the UI treats as "on" — the
// wallpaper-derived palette only actually appears on Android 12+, and on
// every other platform the static palette is what shows up regardless.
@HiveField(20)
bool? useMaterialYou;
// #415 follow-up: custom accent colour packed as a 32-bit ARGB value
// (e.g. 0xFFFF5733). Stores the full colour rather than just a hue so
// hex entry can round-trip without losing saturation or lightness.
// Null means "use the platform default" — Material You on Android 12+,
// the static palette elsewhere.
@HiveField(26)
int? accentColor;
// #165: whether the barcode scanner forces portrait. Null means the user
// has not made a deliberate choice, which the scanner treats as "follow the
// device" — locked on phone-sized screens (so the camera preview can't tip
// sideways) and free on tablets (so a landscape tablet isn't forced upright).
@HiveField(27)
bool? scannerPortraitLock;
// Three independent unit preferences that together replace the original
// single `usesImperialUnits` switch. Each is nullable so existing installs
// fall back to the legacy flag (see `ConfigEntity.fromConfigDBO`) and keep
// their current behaviour without a migration. `usesImperialUnits` is kept
// around as that fallback and as a coherent value for an older build.
@HiveField(28)
bool? usesImperialFoodUnits; // g/oz + ml/fl oz
@HiveField(29)
bool? usesImperialHeightUnits; // cm/ft
// Body weight is a three-way choice (kg / lb / st), persisted as the
// BodyWeightUnit enum index. Null means "derive from usesImperialUnits".
@HiveField(30)
int? bodyWeightUnitIndex;
// Food-source selection for the search screens. Keys are backend
// food_source codes (see SPConst.foodSourceDisplayNames); a source absent
// from the map — or a null map altogether — is enabled, so newly added
// sources default to on and existing installs need no migration. Open
// Food Facts is always enabled and deliberately has no entry here.
@HiveField(31)
Map<String, bool>? foodSourceToggles;
// Set true only by the demo-data seeder (see
// `lib/core/utils/demo/demo_seeder.dart`) when the active profile holds
// sample data seeded from the onboarding "try it with sample data" link,
// rather than a real user's own data. Null/false means real data — the
// Home screen's demo-mode banner only ever appears when this is true.
// `DeleteAllUserDataUsecase.deleteAll()` clears the whole config box, so
// leaving demo mode clears this for free.
@HiveField(32)
bool? isDemoData;
// Opt-in workout import from Health Connect (Android) / Apple Health (iOS).
// Null means the user has never enabled it, which reads as off — no health
// data is touched until they say so.
@HiveField(33)
bool? healthImportEnabled;
// Share of an imported workout's device-reported energy that counts toward
// the daily goal, stored as a fraction in 0.50–1.00. Null means "not chosen
// yet"; the settings flow writes a body-composition-derived suggestion the
// moment import is switched on (see WorkoutCompensationCalc).
@HiveField(34)
double? healthWorkoutKcalMultiplier;
// Watermark: the end of the window the last successful import read. Doubles
// as the debounce timestamp so a resume storm can't hammer the platform.
@HiveField(35)
DateTime? healthLastImportAt;
// External record ids of imported workouts the user has deleted. The
// importer's dedupe set is built from the activities actually on file, so
// without this the next overlapping read would file a deleted workout all
// over again. Kept indefinitely — it only grows by one entry per deletion,
// and forgetting an entry resurrects the workout it stands for. Null means
// nothing has been deleted.
@HiveField(36)
List<String>? healthDeletedExternalIds;
// One-shot marker for [reconcileAppLocale]'s migration push: set once the
// saved language has been offered to Android's per-app language picker (or
// the picker was seen holding a value). Null means the push has not
// happened yet. Without it, an override the user cleared in the OS picker
// is indistinguishable from one that was never seeded, and gets silently
// pushed back on the next launch.
@HiveField(37)
bool? localeSyncSeeded;
ConfigDBO(
this.hasAcceptedDisclaimer,
this.hasAcceptedPolicy,
this.hasAcceptedSendAnonymousData,
this.selectedAppTheme, {
this.usesImperialUnits = false,
this.userKcalAdjustment,
this.showActivityTracking,
this.showMealMacros,
this.notificationsEnabled,
this.notificationHour,
this.notificationMinute,
this.selectedLocale,
this.showMicronutrients,
this.usesKilojoules,
this.mealKcalSharesPct,
this.customMealFormMode,
this.dayStartOffsetHours,
this.diarySortPreferences,
this.nutrientPanelVisibility,
this.dayStartOffsetMinutes,
this.dailyWaterGoalMl,
this.fastingWarningAcknowledged,
this.useMaterialYou,
this.accentColor,
this.scannerPortraitLock,
this.usesImperialFoodUnits,
this.usesImperialHeightUnits,
this.bodyWeightUnitIndex,
this.foodSourceToggles,
this.isDemoData,
this.healthImportEnabled,
this.healthWorkoutKcalMultiplier,
this.healthLastImportAt,
this.healthDeletedExternalIds,
this.localeSyncSeeded,
});
factory ConfigDBO.empty() =>
ConfigDBO(false, false, false, AppThemeDBO.system);
factory ConfigDBO.fromConfigEntity(ConfigEntity entity) => ConfigDBO(
entity.hasAcceptedDisclaimer,
entity.hasAcceptedPolicy,
entity.hasAcceptedSendAnonymousData,
AppThemeDBO.fromAppThemeEntity(entity.appTheme),
usesImperialUnits: entity.usesImperialUnits,
usesImperialFoodUnits: entity.usesImperialFoodUnits,
usesImperialHeightUnits: entity.usesImperialHeightUnits,
bodyWeightUnitIndex: entity.bodyWeightUnit.index,
);
factory ConfigDBO.fromJson(Map<String, dynamic> json) =>
_$ConfigDBOFromJson(json);
Map<String, dynamic> toJson() => _$ConfigDBOToJson(this);
}