mirror of
https://github.com/kuhyx/diet-guard.git
synced 2026-11-11 17:13:58 +01:00
The phone/desktop half of the previous commit, so both devices now derive their slots the same way. Still no edit surface, so `MealScheduleService` returns the default and the derived hours are unchanged at 08/12/16/20. `slot.dart` takes the schedule as a required argument, mirroring `_slots.py`, and `slot_test.dart` gains the same every-hour x several-schedules sweep the Python suite runs. That sweep is the contract: the two must agree on which checkpoint a meal satisfies for every cell, not merely on the endpoints. Also fixes a real bug this feature would otherwise have shipped on top of. `syncToSlots` used the slot hour as the notification id but only cancelled ids in the *current* schedule, so changing 08/12/16/20 -> 08/11/14/17/20 would leave ids 12 and 16 posted with nothing that could ever cancel them: permanent reminders for checkpoints that no longer exist. It now sweeps the whole 0..23 id space. The regression test was confirmed to fail against the old loop (4 cancels vs 24) rather than passing vacuously. MealScheduleService.current degrades to the default when the singleton is absent, so `due_slot_check` deliberately does not init() it -- that would reach path_provider from widget tests and from the WorkManager isolate before main() runs. main.dart owns the real init, and background_sync_service.dart does its own, since a fresh isolate has its own static state and would otherwise nag against the default four meals. 609 Dart tests, 761 Python, both suites green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159b4E5hh512jYUeaJVNxfM
65 lines
2.5 KiB
Dart
65 lines
2.5 KiB
Dart
/// App entry point: initializes local storage services, registers the
|
|
/// background due-slot check, then shows the primary meal-logging screen.
|
|
library;
|
|
|
|
import 'dart:async';
|
|
import 'package:diet_guard_app/screens/log_meal_screen.dart';
|
|
import 'package:diet_guard_app/services/app_settings_service.dart';
|
|
import 'package:diet_guard_app/services/background_tasks.dart';
|
|
import 'package:diet_guard_app/services/budget_history_service.dart';
|
|
import 'package:diet_guard_app/services/foodbank_service.dart';
|
|
import 'package:diet_guard_app/services/frame_stats.dart';
|
|
import 'package:diet_guard_app/services/log_storage_service.dart';
|
|
import 'package:diet_guard_app/services/meal_schedule_service.dart';
|
|
import 'package:diet_guard_app/services/notification_service.dart';
|
|
import 'package:diet_guard_app/services/sync_device_id.dart';
|
|
import 'package:diet_guard_app/ui/theme.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
Future<void> main() async {
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
// Before anything that stamps an Hlc or syncs: until this resolves, the
|
|
// device id falls back to the pre-migration role constant.
|
|
await initSyncDeviceId();
|
|
await LogStorageService.init();
|
|
await AppSettingsService.init();
|
|
await BudgetHistoryService.init();
|
|
await MealScheduleService.init();
|
|
await FoodBankService.init();
|
|
final notifications = await NotificationService.init();
|
|
// Deliberately *not* awaited before the first frame: the browser's
|
|
// `Notification.requestPermission()` does not complete until the user
|
|
// answers the prompt, so awaiting it here left the desktop app as a blank
|
|
// white window behind the permission bubble. Scheduling is likewise
|
|
// platform-specific (WorkManager on Android, an in-page timer in the
|
|
// browser -- see background_tasks.dart) and equally not worth a delayed
|
|
// first frame.
|
|
// coverage:ignore-start
|
|
unawaited(
|
|
notifications.requestPermission().then((_) {
|
|
return initBackgroundTasks();
|
|
}),
|
|
);
|
|
// coverage:ignore-end
|
|
// Off unless armed with --dart-define=DIET_GUARD_FRAME_STATS=1.
|
|
// coverage:ignore-start
|
|
if (frameStatsEnabled) startFrameStats();
|
|
// coverage:ignore-end
|
|
runApp(const DietGuardApp());
|
|
}
|
|
|
|
/// Root widget for the Diet Guard companion app.
|
|
class DietGuardApp extends StatelessWidget {
|
|
/// Creates the [DietGuardApp] root widget.
|
|
const DietGuardApp({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return MaterialApp(
|
|
title: 'Diet Guard',
|
|
theme: buildAppTheme(),
|
|
home: const LogMealScreen(),
|
|
);
|
|
}
|
|
}
|