Implement Notifications and Pad Tracking Enhancements
This commit is contained in:
@@ -12,14 +12,14 @@ import '../devotional/devotional_screen.dart';
|
||||
import '../settings/appearance_screen.dart';
|
||||
import '../settings/cycle_settings_screen.dart';
|
||||
import '../settings/relationship_settings_screen.dart';
|
||||
import '../settings/goal_settings_screen.dart';
|
||||
import '../settings/goal_settings_screen.dart';
|
||||
import '../settings/cycle_history_screen.dart';
|
||||
import '../settings/sharing_settings_screen.dart';
|
||||
import '../settings/notification_settings_screen.dart';
|
||||
import '../settings/privacy_settings_screen.dart';
|
||||
import '../settings/supplies_settings_screen.dart';
|
||||
import '../settings/export_data_screen.dart';
|
||||
import '../learn/wife_learn_screen.dart';
|
||||
import '../learn/wife_learn_screen.dart';
|
||||
import '../../widgets/tip_card.dart';
|
||||
import '../../widgets/cycle_ring.dart';
|
||||
import '../../widgets/scripture_card.dart';
|
||||
@@ -37,7 +37,8 @@ class HomeScreen extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final selectedIndex = ref.watch(navigationProvider);
|
||||
final isPadTrackingEnabled = ref.watch(userProfileProvider.select((u) => u?.isPadTrackingEnabled ?? false));
|
||||
final isPadTrackingEnabled = ref.watch(
|
||||
userProfileProvider.select((u) => u?.isPadTrackingEnabled ?? false));
|
||||
|
||||
final List<Widget> tabs;
|
||||
final List<BottomNavigationBarItem> navBarItems;
|
||||
@@ -50,16 +51,38 @@ class HomeScreen extends ConsumerWidget {
|
||||
const LogScreen(),
|
||||
const DevotionalScreen(),
|
||||
const WifeLearnScreen(),
|
||||
_SettingsTab(onReset: () => ref.read(navigationProvider.notifier).setIndex(0)),
|
||||
_SettingsTab(
|
||||
onReset: () => ref.read(navigationProvider.notifier).setIndex(0)),
|
||||
];
|
||||
navBarItems = [
|
||||
const BottomNavigationBarItem(icon: Icon(Icons.home_outlined), activeIcon: Icon(Icons.home), label: 'Home'),
|
||||
const BottomNavigationBarItem(icon: Icon(Icons.calendar_today_outlined), activeIcon: Icon(Icons.calendar_today), label: 'Calendar'),
|
||||
const BottomNavigationBarItem(icon: Icon(Icons.inventory_2_outlined), activeIcon: Icon(Icons.inventory_2), label: 'Supplies'),
|
||||
const BottomNavigationBarItem(icon: Icon(Icons.add_circle_outline), activeIcon: Icon(Icons.add_circle), label: 'Log'),
|
||||
const BottomNavigationBarItem(icon: Icon(Icons.menu_book_outlined), activeIcon: Icon(Icons.menu_book), label: 'Devotional'),
|
||||
const BottomNavigationBarItem(icon: Icon(Icons.school_outlined), activeIcon: Icon(Icons.school), label: 'Learn'),
|
||||
const BottomNavigationBarItem(icon: Icon(Icons.settings_outlined), activeIcon: Icon(Icons.settings), label: 'Settings'),
|
||||
const BottomNavigationBarItem(
|
||||
icon: Icon(Icons.home_outlined),
|
||||
activeIcon: Icon(Icons.home),
|
||||
label: 'Home'),
|
||||
const BottomNavigationBarItem(
|
||||
icon: Icon(Icons.calendar_today_outlined),
|
||||
activeIcon: Icon(Icons.calendar_today),
|
||||
label: 'Calendar'),
|
||||
const BottomNavigationBarItem(
|
||||
icon: Icon(Icons.inventory_2_outlined),
|
||||
activeIcon: Icon(Icons.inventory_2),
|
||||
label: 'Supplies'),
|
||||
const BottomNavigationBarItem(
|
||||
icon: Icon(Icons.add_circle_outline),
|
||||
activeIcon: Icon(Icons.add_circle),
|
||||
label: 'Log'),
|
||||
const BottomNavigationBarItem(
|
||||
icon: Icon(Icons.menu_book_outlined),
|
||||
activeIcon: Icon(Icons.menu_book),
|
||||
label: 'Devotional'),
|
||||
const BottomNavigationBarItem(
|
||||
icon: Icon(Icons.school_outlined),
|
||||
activeIcon: Icon(Icons.school),
|
||||
label: 'Learn'),
|
||||
const BottomNavigationBarItem(
|
||||
icon: Icon(Icons.settings_outlined),
|
||||
activeIcon: Icon(Icons.settings),
|
||||
label: 'Settings'),
|
||||
];
|
||||
} else {
|
||||
tabs = [
|
||||
@@ -68,15 +91,34 @@ class HomeScreen extends ConsumerWidget {
|
||||
const DevotionalScreen(),
|
||||
const LogScreen(),
|
||||
const WifeLearnScreen(),
|
||||
_SettingsTab(onReset: () => ref.read(navigationProvider.notifier).setIndex(0)),
|
||||
_SettingsTab(
|
||||
onReset: () => ref.read(navigationProvider.notifier).setIndex(0)),
|
||||
];
|
||||
navBarItems = [
|
||||
const BottomNavigationBarItem(icon: Icon(Icons.home_outlined), activeIcon: Icon(Icons.home), label: 'Home'),
|
||||
const BottomNavigationBarItem(icon: Icon(Icons.calendar_today_outlined), activeIcon: Icon(Icons.calendar_today), label: 'Calendar'),
|
||||
const BottomNavigationBarItem(icon: Icon(Icons.menu_book_outlined), activeIcon: Icon(Icons.menu_book), label: 'Devotional'),
|
||||
const BottomNavigationBarItem(icon: Icon(Icons.add_circle_outline), activeIcon: Icon(Icons.add_circle), label: 'Log'),
|
||||
const BottomNavigationBarItem(icon: Icon(Icons.school_outlined), activeIcon: Icon(Icons.school), label: 'Learn'),
|
||||
const BottomNavigationBarItem(icon: Icon(Icons.settings_outlined), activeIcon: Icon(Icons.settings), label: 'Settings'),
|
||||
const BottomNavigationBarItem(
|
||||
icon: Icon(Icons.home_outlined),
|
||||
activeIcon: Icon(Icons.home),
|
||||
label: 'Home'),
|
||||
const BottomNavigationBarItem(
|
||||
icon: Icon(Icons.calendar_today_outlined),
|
||||
activeIcon: Icon(Icons.calendar_today),
|
||||
label: 'Calendar'),
|
||||
const BottomNavigationBarItem(
|
||||
icon: Icon(Icons.menu_book_outlined),
|
||||
activeIcon: Icon(Icons.menu_book),
|
||||
label: 'Devotional'),
|
||||
const BottomNavigationBarItem(
|
||||
icon: Icon(Icons.add_circle_outline),
|
||||
activeIcon: Icon(Icons.add_circle),
|
||||
label: 'Log'),
|
||||
const BottomNavigationBarItem(
|
||||
icon: Icon(Icons.school_outlined),
|
||||
activeIcon: Icon(Icons.school),
|
||||
label: 'Learn'),
|
||||
const BottomNavigationBarItem(
|
||||
icon: Icon(Icons.settings_outlined),
|
||||
activeIcon: Icon(Icons.settings),
|
||||
label: 'Settings'),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -134,7 +176,8 @@ class _DashboardTabState extends ConsumerState<_DashboardTab> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Listen for changes in the cycle info to re-initialize scripture if needed
|
||||
ref.listen<CycleInfo>(currentCycleInfoProvider, (previousCycleInfo, newCycleInfo) {
|
||||
ref.listen<CycleInfo>(currentCycleInfoProvider,
|
||||
(previousCycleInfo, newCycleInfo) {
|
||||
if (previousCycleInfo?.phase != newCycleInfo.phase) {
|
||||
_initializeScripture();
|
||||
}
|
||||
@@ -145,8 +188,8 @@ class _DashboardTabState extends ConsumerState<_DashboardTab> {
|
||||
final translation =
|
||||
ref.watch(userProfileProvider.select((u) => u?.bibleTranslation)) ??
|
||||
BibleTranslation.esv;
|
||||
final role = ref.watch(userProfileProvider.select((u) => u?.role)) ??
|
||||
UserRole.wife;
|
||||
final role =
|
||||
ref.watch(userProfileProvider.select((u) => u?.role)) ?? UserRole.wife;
|
||||
final isMarried =
|
||||
ref.watch(userProfileProvider.select((u) => u?.isMarried)) ?? false;
|
||||
final averageCycleLength =
|
||||
@@ -163,7 +206,8 @@ class _DashboardTabState extends ConsumerState<_DashboardTab> {
|
||||
final maxIndex = scriptureState.maxIndex;
|
||||
|
||||
if (scripture == null) {
|
||||
return const Center(child: CircularProgressIndicator()); // Or some error message
|
||||
return const Center(
|
||||
child: CircularProgressIndicator()); // Or some error message
|
||||
}
|
||||
|
||||
return SafeArea(
|
||||
@@ -181,10 +225,8 @@ class _DashboardTabState extends ConsumerState<_DashboardTab> {
|
||||
phase: phase,
|
||||
),
|
||||
),
|
||||
if (phase == CyclePhase.menstrual) ...[
|
||||
const SizedBox(height: 24),
|
||||
const PadTrackerCard(),
|
||||
],
|
||||
const SizedBox(height: 24),
|
||||
const PadTrackerCard(),
|
||||
const SizedBox(height: 32),
|
||||
// Main Scripture Card with Navigation
|
||||
Stack(
|
||||
@@ -203,8 +245,9 @@ class _DashboardTabState extends ConsumerState<_DashboardTab> {
|
||||
left: 0,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.arrow_back_ios),
|
||||
onPressed: () =>
|
||||
ref.read(scriptureProvider.notifier).getPreviousScripture(),
|
||||
onPressed: () => ref
|
||||
.read(scriptureProvider.notifier)
|
||||
.getPreviousScripture(),
|
||||
color: AppColors.charcoal,
|
||||
),
|
||||
),
|
||||
@@ -212,8 +255,9 @@ class _DashboardTabState extends ConsumerState<_DashboardTab> {
|
||||
right: 0,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.arrow_forward_ios),
|
||||
onPressed: () =>
|
||||
ref.read(scriptureProvider.notifier).getNextScripture(),
|
||||
onPressed: () => ref
|
||||
.read(scriptureProvider.notifier)
|
||||
.getNextScripture(),
|
||||
color: AppColors.charcoal,
|
||||
),
|
||||
),
|
||||
@@ -222,16 +266,17 @@ class _DashboardTabState extends ConsumerState<_DashboardTab> {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (maxIndex != null && maxIndex > 1)
|
||||
Center(
|
||||
child: TextButton.icon(
|
||||
onPressed: () => ref.read(scriptureProvider.notifier).getRandomScripture(),
|
||||
icon: const Icon(Icons.shuffle),
|
||||
label: const Text('Random Verse'),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Theme.of(context).colorScheme.primary,
|
||||
Center(
|
||||
child: TextButton.icon(
|
||||
onPressed: () =>
|
||||
ref.read(scriptureProvider.notifier).getRandomScripture(),
|
||||
icon: const Icon(Icons.shuffle),
|
||||
label: const Text('Random Verse'),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
'Quick Log',
|
||||
@@ -243,8 +288,7 @@ class _DashboardTabState extends ConsumerState<_DashboardTab> {
|
||||
const SizedBox(height: 12),
|
||||
const QuickLogButtons(),
|
||||
const SizedBox(height: 24),
|
||||
if (role == UserRole.wife)
|
||||
_buildWifeTipsSection(context),
|
||||
if (role == UserRole.wife) _buildWifeTipsSection(context),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
@@ -364,18 +408,18 @@ class _SettingsTab extends ConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final name =
|
||||
ref.watch(userProfileProvider.select((u) => u?.name)) ?? 'Guest';
|
||||
final roleSymbol =
|
||||
ref.watch(userProfileProvider.select((u) => u?.role)) ==
|
||||
UserRole.husband
|
||||
? 'HUSBAND'
|
||||
: null;
|
||||
final roleSymbol = ref.watch(userProfileProvider.select((u) => u?.role)) ==
|
||||
UserRole.husband
|
||||
? 'HUSBAND'
|
||||
: null;
|
||||
final relationshipStatus = ref.watch(userProfileProvider
|
||||
.select((u) => u?.relationshipStatus.name.toUpperCase())) ??
|
||||
'SINGLE';
|
||||
final translationLabel =
|
||||
ref.watch(userProfileProvider.select((u) => u?.bibleTranslation.label)) ??
|
||||
'ESV';
|
||||
final isSingle = ref.watch(userProfileProvider.select((u) => u?.relationshipStatus == RelationshipStatus.single));
|
||||
final translationLabel = ref.watch(
|
||||
userProfileProvider.select((u) => u?.bibleTranslation.label)) ??
|
||||
'ESV';
|
||||
final isSingle = ref.watch(userProfileProvider
|
||||
.select((u) => u?.relationshipStatus == RelationshipStatus.single));
|
||||
|
||||
return SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
@@ -398,8 +442,10 @@ class _SettingsTab extends ConsumerWidget {
|
||||
color: Theme.of(context).cardTheme.color,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color:
|
||||
Theme.of(context).colorScheme.outline.withOpacity(0.05)),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.outline
|
||||
.withOpacity(0.05)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
@@ -409,8 +455,14 @@ class _SettingsTab extends ConsumerWidget {
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Theme.of(context).colorScheme.primary.withOpacity(0.7),
|
||||
Theme.of(context).colorScheme.secondary.withOpacity(0.7)
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.primary
|
||||
.withOpacity(0.7),
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.secondary
|
||||
.withOpacity(0.7)
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
@@ -459,14 +511,15 @@ class _SettingsTab extends ConsumerWidget {
|
||||
const SizedBox(height: 24),
|
||||
_buildSettingsGroup(context, 'Preferences', [
|
||||
_buildSettingsTile(
|
||||
context,
|
||||
Icons.notifications_outlined,
|
||||
context,
|
||||
Icons.notifications_outlined,
|
||||
'Notifications',
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const NotificationSettingsScreen()));
|
||||
builder: (context) =>
|
||||
const NotificationSettingsScreen()));
|
||||
},
|
||||
),
|
||||
_buildSettingsTile(
|
||||
@@ -477,7 +530,8 @@ class _SettingsTab extends ConsumerWidget {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const SuppliesSettingsScreen()));
|
||||
builder: (context) =>
|
||||
const SuppliesSettingsScreen()));
|
||||
},
|
||||
),
|
||||
_buildSettingsTile(
|
||||
@@ -488,12 +542,13 @@ class _SettingsTab extends ConsumerWidget {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const RelationshipSettingsScreen()));
|
||||
builder: (context) =>
|
||||
const RelationshipSettingsScreen()));
|
||||
},
|
||||
),
|
||||
_buildSettingsTile(
|
||||
context,
|
||||
Icons.flag_outlined,
|
||||
Icons.flag_outlined,
|
||||
'Cycle Goal',
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
@@ -521,8 +576,7 @@ class _SettingsTab extends ConsumerWidget {
|
||||
'My Favorites',
|
||||
onTap: () => _showFavoritesDialog(context, ref),
|
||||
),
|
||||
_buildSettingsTile(
|
||||
context, Icons.security, 'Privacy & Security',
|
||||
_buildSettingsTile(context, Icons.security, 'Privacy & Security',
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
@@ -562,8 +616,7 @@ class _SettingsTab extends ConsumerWidget {
|
||||
builder: (context) => CycleHistoryScreen()));
|
||||
}),
|
||||
_buildSettingsTile(
|
||||
context, Icons.download_outlined, 'Export Data',
|
||||
onTap: () {
|
||||
context, Icons.download_outlined, 'Export Data', onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
@@ -629,7 +682,9 @@ class _SettingsTab extends ConsumerWidget {
|
||||
autofocus: true,
|
||||
),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.pop(context), child: const Text('Cancel')),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Cancel')),
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.pop(context, controller.text),
|
||||
child: const Text('Unlock'),
|
||||
@@ -648,7 +703,8 @@ class _SettingsTab extends ConsumerWidget {
|
||||
final granted = await _authenticate(context, userProfile.privacyPin!);
|
||||
if (!granted) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Incorrect PIN')));
|
||||
ScaffoldMessenger.of(context)
|
||||
.showSnackBar(const SnackBar(content: Text('Incorrect PIN')));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -663,14 +719,16 @@ class _SettingsTab extends ConsumerWidget {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text('My Favorites', style: GoogleFonts.outfit(fontWeight: FontWeight.bold)),
|
||||
title: Text('My Favorites',
|
||||
style: GoogleFonts.outfit(fontWeight: FontWeight.bold)),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'List your favorite comfort foods, snacks, or flowers so your husband knows what to get you!',
|
||||
style: GoogleFonts.outfit(fontSize: 13, color: AppColors.warmGray),
|
||||
style:
|
||||
GoogleFonts.outfit(fontSize: 13, color: AppColors.warmGray),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextField(
|
||||
@@ -696,8 +754,11 @@ class _SettingsTab extends ConsumerWidget {
|
||||
.where((e) => e.isNotEmpty)
|
||||
.toList();
|
||||
|
||||
final updatedProfile = userProfile.copyWith(favoriteFoods: favorites);
|
||||
ref.read(userProfileProvider.notifier).updateProfile(updatedProfile);
|
||||
final updatedProfile =
|
||||
userProfile.copyWith(favoriteFoods: favorites);
|
||||
ref
|
||||
.read(userProfileProvider.notifier)
|
||||
.updateProfile(updatedProfile);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Save'),
|
||||
@@ -710,14 +771,16 @@ class _SettingsTab extends ConsumerWidget {
|
||||
void _showShareDialog(BuildContext context, WidgetRef ref) {
|
||||
// Generate a simple pairing code (in a real app, this would be stored/validated)
|
||||
final userProfile = ref.read(userProfileProvider);
|
||||
final pairingCode = userProfile?.id?.substring(0, 6).toUpperCase() ?? 'ABC123';
|
||||
final pairingCode =
|
||||
userProfile?.id?.substring(0, 6).toUpperCase() ?? 'ABC123';
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Row(
|
||||
children: [
|
||||
Icon(Icons.share_outlined, color: Theme.of(context).colorScheme.primary),
|
||||
Icon(Icons.share_outlined,
|
||||
color: Theme.of(context).colorScheme.primary),
|
||||
const SizedBox(width: 8),
|
||||
const Text('Share with Husband'),
|
||||
],
|
||||
@@ -727,7 +790,8 @@ class _SettingsTab extends ConsumerWidget {
|
||||
children: [
|
||||
Text(
|
||||
'Share this code with your husband so he can connect to your cycle data:',
|
||||
style: GoogleFonts.outfit(fontSize: 14, color: AppColors.warmGray),
|
||||
style:
|
||||
GoogleFonts.outfit(fontSize: 14, color: AppColors.warmGray),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Container(
|
||||
@@ -735,7 +799,9 @@ class _SettingsTab extends ConsumerWidget {
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: Theme.of(context).colorScheme.primary.withOpacity(0.3)),
|
||||
border: Border.all(
|
||||
color:
|
||||
Theme.of(context).colorScheme.primary.withOpacity(0.3)),
|
||||
),
|
||||
child: SelectableText(
|
||||
pairingCode,
|
||||
@@ -750,7 +816,8 @@ class _SettingsTab extends ConsumerWidget {
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'He can enter this in his app under Settings > Connect with Wife.',
|
||||
style: GoogleFonts.outfit(fontSize: 12, color: AppColors.warmGray),
|
||||
style:
|
||||
GoogleFonts.outfit(fontSize: 12, color: AppColors.warmGray),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
@@ -868,4 +935,4 @@ Widget _buildTipCard(
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user