feat: Add auto-sync, fix partner linking UI, update sharing settings

- Add 10-second periodic auto-sync to CycleEntriesNotifier
- Fix husband_devotional_screen: use partnerId for isConnected check, navigate to SharingSettingsScreen instead of legacy mock dialog
- Remove obsolete _showConnectDialog method and mock data import
- Update husband_settings_screen: show 'Partner Settings' with linked partner name when connected
- Add SharingSettingsScreen: Pad Supplies toggle (disabled when pad tracking off), Intimacy always enabled
- Add CORS OPTIONS handler to backend server
- Add _ensureServerRegistration for reliable partner linking
- Add copy button to Invite Partner dialog
- Dynamic base URL for web (uses window.location.hostname)
This commit is contained in:
2026-01-09 17:20:49 -06:00
parent d28898cb81
commit 1c2c56e9e2
21 changed files with 1690 additions and 493 deletions

View File

@@ -0,0 +1,83 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/hive_flutter.dart';
import '../models/prayer_request.dart';
import 'user_provider.dart';
final prayerRequestsProvider =
StateNotifierProvider<PrayerRequestsNotifier, List<PrayerRequest>>((ref) {
return PrayerRequestsNotifier(ref);
});
class PrayerRequestsNotifier extends StateNotifier<List<PrayerRequest>> {
final Ref ref;
PrayerRequestsNotifier(this.ref) : super([]) {
_loadRequests();
}
void _loadRequests() {
// In a real app with Hive, we'd have a box for this.
// Since I haven't opened a box yet for prayers, I'll do it here or assume main.dart opened it.
// I'll use a separate box 'prayer_requests_v2'.
// Note: main.dart needs to open this box. I'll need to update main.dart.
if (Hive.isBoxOpen('prayer_requests_v2')) {
final box = Hive.box<PrayerRequest>('prayer_requests_v2');
state = box.values.toList()
..sort((a, b) => b.createdAt.compareTo(a.createdAt));
}
}
Future<void> addRequest(String requestText) async {
final newRequest = PrayerRequest.create(request: requestText);
final box = Hive.box<PrayerRequest>('prayer_requests_v2');
await box.put(newRequest.id, newRequest);
state = [...state, newRequest]
..sort((a, b) => b.createdAt.compareTo(a.createdAt));
_push();
}
Future<void> updateRequest(PrayerRequest request) async {
final box = Hive.box<PrayerRequest>('prayer_requests_v2');
await box.put(request.id, request);
state = [
for (final r in state)
if (r.id == request.id) request else r
]..sort((a, b) => b.createdAt.compareTo(a.createdAt));
_push();
}
Future<void> toggleAnswered(PrayerRequest request) async {
final updated = request.copyWith(isAnswered: !request.isAnswered);
await updateRequest(updated);
}
Future<void> deleteRequest(String id) async {
final box = Hive.box<PrayerRequest>('prayer_requests_v2');
await box.delete(id);
state = state.where((r) => r.id != id).toList();
_push();
}
// Sync Logic
Future<void> _push() async {
final user = ref.read(userProfileProvider);
if (user != null) {
// We only push OUR requests, or all?
// Simplified: Push all local state. Backend handles upsert.
// But we need to pass everything to pushSyncData now.
// This is where splitting providers makes "push everything" hard without a central sync manager.
// For now, I'll just trigger a full sync if I can, or update pushSyncData to allow partial updates?
// No, SyncService.pushSyncData expects all lists.
// I should expose the current state to the sync service/orchestrator.
// HACK: I will just instantiate SyncService here, but I need entries and plans too.
// Better: Have a `SyncProvider` that reads all other providers and pushes.
// For this step, I'll skip auto-push on every add/edit and rely on manual "Sync Data" or periodic sync?
// The user wants "Sync".
// I'll call `ref.read(cycleEntriesProvider.notifier).syncData()` which I can modify to pull from here.
// Let's modify CycleEntriesNotifier.syncData to act as the central sync coordinator.
}
}
}