Implement Notifications and Pad Tracking Enhancements

This commit is contained in:
2026-01-08 15:46:28 -06:00
parent 9ae77e7ab0
commit 512577b092
19 changed files with 3059 additions and 1576 deletions

View File

@@ -27,6 +27,9 @@ enum MoodLevel {
/// Flow intensity for period days
@HiveType(typeId: 4)
enum FlowIntensity {
@HiveField(4)
none, // No flow / Precautionary
@HiveField(0)
spotting,
@@ -166,7 +169,8 @@ class CycleEntry extends HiveObject {
String? husbandNotes; // Separate notes for husband
@HiveField(29)
bool? intimacyProtected; // null = no selection, true = protected, false = unprotected
bool?
intimacyProtected; // null = no selection, true = protected, false = unprotected
@HiveField(30, defaultValue: false)
bool usedPantyliner;
@@ -174,6 +178,9 @@ class CycleEntry extends HiveObject {
@HiveField(31, defaultValue: 0)
int pantylinerCount;
@HiveField(32)
String? prayerRequest;
CycleEntry({
required this.id,
required this.date,
@@ -207,6 +214,7 @@ class CycleEntry extends HiveObject {
this.husbandNotes,
this.usedPantyliner = false,
this.pantylinerCount = 0,
this.prayerRequest,
});
List<bool> get _symptomsList => [
@@ -271,6 +279,7 @@ class CycleEntry extends HiveObject {
String? husbandNotes,
bool? usedPantyliner,
int? pantylinerCount,
String? prayerRequest,
}) {
return CycleEntry(
id: id ?? this.id,
@@ -292,7 +301,8 @@ class CycleEntry extends HiveObject {
hasInsomnia: hasInsomnia ?? this.hasInsomnia,
basalBodyTemperature: basalBodyTemperature ?? this.basalBodyTemperature,
cervicalMucus: cervicalMucus ?? this.cervicalMucus,
ovulationTestPositive: ovulationTestPositive ?? this.ovulationTestPositive,
ovulationTestPositive:
ovulationTestPositive ?? this.ovulationTestPositive,
notes: notes ?? this.notes,
cravings: cravings ?? this.cravings,
sleepHours: sleepHours ?? this.sleepHours,
@@ -305,6 +315,7 @@ class CycleEntry extends HiveObject {
husbandNotes: husbandNotes ?? this.husbandNotes,
usedPantyliner: usedPantyliner ?? this.usedPantyliner,
pantylinerCount: pantylinerCount ?? this.pantylinerCount,
prayerRequest: prayerRequest ?? this.prayerRequest,
);
}
}
@@ -345,6 +356,8 @@ extension MoodLevelExtension on MoodLevel {
extension FlowIntensityExtension on FlowIntensity {
String get label {
switch (this) {
case FlowIntensity.none:
return 'No Flow';
case FlowIntensity.spotting:
return 'Spotting';
case FlowIntensity.light:
@@ -410,11 +423,7 @@ extension CyclePhaseExtension on CyclePhase {
case CyclePhase.ovulation:
return [AppColors.lavender, AppColors.ovulationPhase, AppColors.rose];
case CyclePhase.luteal:
return [
AppColors.lutealPhase,
AppColors.lavender,
AppColors.blushPink
];
return [AppColors.lutealPhase, AppColors.lavender, AppColors.blushPink];
}
}

View File

@@ -49,13 +49,14 @@ class CycleEntryAdapter extends TypeAdapter<CycleEntry> {
husbandNotes: fields[28] as String?,
usedPantyliner: fields[30] == null ? false : fields[30] as bool,
pantylinerCount: fields[31] == null ? 0 : fields[31] as int,
prayerRequest: fields[32] as String?,
);
}
@override
void write(BinaryWriter writer, CycleEntry obj) {
writer
..writeByte(32)
..writeByte(33)
..writeByte(0)
..write(obj.id)
..writeByte(1)
@@ -119,7 +120,9 @@ class CycleEntryAdapter extends TypeAdapter<CycleEntry> {
..writeByte(30)
..write(obj.usedPantyliner)
..writeByte(31)
..write(obj.pantylinerCount);
..write(obj.pantylinerCount)
..writeByte(32)
..write(obj.prayerRequest);
}
@override
@@ -194,6 +197,8 @@ class FlowIntensityAdapter extends TypeAdapter<FlowIntensity> {
@override
FlowIntensity read(BinaryReader reader) {
switch (reader.readByte()) {
case 4:
return FlowIntensity.none;
case 0:
return FlowIntensity.spotting;
case 1:
@@ -203,13 +208,16 @@ class FlowIntensityAdapter extends TypeAdapter<FlowIntensity> {
case 3:
return FlowIntensity.heavy;
default:
return FlowIntensity.spotting;
return FlowIntensity.none;
}
}
@override
void write(BinaryWriter writer, FlowIntensity obj) {
switch (obj) {
case FlowIntensity.none:
writer.writeByte(4);
break;
case FlowIntensity.spotting:
writer.writeByte(0);
break;

View File

@@ -302,6 +302,17 @@ class UserProfile extends HiveObject {
@HiveField(53)
List<TeachingPlan>? teachingPlans;
// Husband-specific theme settings
@HiveField(54, defaultValue: AppThemeMode.system)
AppThemeMode husbandThemeMode;
@HiveField(55, defaultValue: '0xFF1A3A5C')
String husbandAccentColor;
// Whether to use example/demo data (for husband not connected to wife)
@HiveField(56, defaultValue: false)
bool useExampleData;
UserProfile({
required this.id,
required this.name,
@@ -356,6 +367,9 @@ class UserProfile extends HiveObject {
this.isCalendarProtected = false,
this.isSuppliesProtected = false,
this.teachingPlans,
this.husbandThemeMode = AppThemeMode.system,
this.husbandAccentColor = '0xFF1A3A5C',
this.useExampleData = false,
});
/// Check if user is married
@@ -435,6 +449,9 @@ class UserProfile extends HiveObject {
bool? isCalendarProtected,
bool? isSuppliesProtected,
List<TeachingPlan>? teachingPlans,
AppThemeMode? husbandThemeMode,
String? husbandAccentColor,
bool? useExampleData,
}) {
return UserProfile(
id: id ?? this.id,
@@ -470,8 +487,10 @@ class UserProfile extends HiveObject {
padBrand: padBrand ?? this.padBrand,
padAbsorbency: padAbsorbency ?? this.padAbsorbency,
padInventoryCount: padInventoryCount ?? this.padInventoryCount,
lowInventoryThreshold: lowInventoryThreshold ?? this.lowInventoryThreshold,
isAutoInventoryEnabled: isAutoInventoryEnabled ?? this.isAutoInventoryEnabled,
lowInventoryThreshold:
lowInventoryThreshold ?? this.lowInventoryThreshold,
isAutoInventoryEnabled:
isAutoInventoryEnabled ?? this.isAutoInventoryEnabled,
lastInventoryUpdate: lastInventoryUpdate ?? this.lastInventoryUpdate,
notifyPeriodEstimate: notifyPeriodEstimate ?? this.notifyPeriodEstimate,
notifyPeriodStart: notifyPeriodStart ?? this.notifyPeriodStart,
@@ -491,6 +510,9 @@ class UserProfile extends HiveObject {
isCalendarProtected: isCalendarProtected ?? this.isCalendarProtected,
isSuppliesProtected: isSuppliesProtected ?? this.isSuppliesProtected,
teachingPlans: teachingPlans ?? this.teachingPlans,
husbandThemeMode: husbandThemeMode ?? this.husbandThemeMode,
husbandAccentColor: husbandAccentColor ?? this.husbandAccentColor,
useExampleData: useExampleData ?? this.useExampleData,
);
}
}

View File

@@ -118,13 +118,18 @@ class UserProfileAdapter extends TypeAdapter<UserProfile> {
isCalendarProtected: fields[51] == null ? false : fields[51] as bool,
isSuppliesProtected: fields[52] == null ? false : fields[52] as bool,
teachingPlans: (fields[53] as List?)?.cast<TeachingPlan>(),
husbandThemeMode:
fields[54] == null ? AppThemeMode.system : fields[54] as AppThemeMode,
husbandAccentColor:
fields[55] == null ? '0xFF1A3A5C' : fields[55] as String,
useExampleData: fields[56] == null ? false : fields[56] as bool,
);
}
@override
void write(BinaryWriter writer, UserProfile obj) {
writer
..writeByte(53)
..writeByte(56)
..writeByte(0)
..write(obj.id)
..writeByte(1)
@@ -230,7 +235,13 @@ class UserProfileAdapter extends TypeAdapter<UserProfile> {
..writeByte(52)
..write(obj.isSuppliesProtected)
..writeByte(53)
..write(obj.teachingPlans);
..write(obj.teachingPlans)
..writeByte(54)
..write(obj.husbandThemeMode)
..writeByte(55)
..write(obj.husbandAccentColor)
..writeByte(56)
..write(obj.useExampleData);
}
@override