Design system - MilerSurface ladder (canvas → working → raised → floating) with MilerPanel as layer 1; canvas moved to #DEE3EA so white separates at 1.290:1. - Visible vocabulary applied across Home, Deliveries, Activity, Account and the sheets: hero heads (tabular numeral + small caption, clamped at 1.3x), canvas wells for anything that opens, small filled tags for shelf labels, demoted placeholders. Recorded in DESIGN_SYSTEM.md §6. - One icon family: 222 Material glyphs migrated to Lucide; none left outside lib/xpress. - Colour semantics corrected: amber only for what is genuinely owed, brand red reserved for the live stop, disabled primaries go neutral rather than pale. Data and lifecycle - lib/data/lifecycle.dart reads mutations for what they prove; route_order.dart makes admin sequence the single ordering authority; service_day.dart, and stop_area.dart rewritten against live Coimbatore addresses (digit-token stripping, city stoplist, street suffixes, stammer collapse). - countLabel states the load once, in bags. Testing - 1440 tests passing; golden shot harnesses for Home, Deliveries, Activity, sheets and verify, with test/failures/ now gitignored (diff debris). - New pins: home_gutter_test, stop_area_test, plus updated structural bounds. Note: this commit also carries pre-existing working-tree deletions that were present before this work (API_SPEC.md, README.md, demo test fixtures). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
104 lines
4.9 KiB
Dart
104 lines
4.9 KiB
Dart
import 'package:get/get.dart';
|
|
|
|
import 'package:miler/xpress/controllers/delivery.dart';
|
|
import 'package:miler/xpress/controllers/logcontroller.dart';
|
|
import 'package:miler/xpress/controllers/profile_controller.dart';
|
|
import 'package:miler/xpress/controllers/riderlog.dart';
|
|
|
|
/// ─────────────────────────────────────────────────────────────────────────
|
|
/// THE DELIVERY LINE'S CONTROLLERS
|
|
///
|
|
/// ── The problem this solves ──
|
|
///
|
|
/// `main()` registers the parcel line's controllers — `RiderLogController`,
|
|
/// `PickupController`, `LogController`, `ProfileController` — and the ported
|
|
/// delivery screens ask for controllers with three of those *same names*.
|
|
///
|
|
/// They are not the same classes. `miler/controllers/riderlog.dart` and
|
|
/// `miler/xpress/controllers/riderlog.dart` both declare a `RiderLogController`,
|
|
/// and GetX keys its container on the **type**, not the name. So a delivery
|
|
/// screen calling `Get.find<RiderLogController>()` — resolving to the xpress
|
|
/// type — finds nothing, no matter that a controller by that name is registered.
|
|
///
|
|
/// That failure is not graceful. The delivery home page resolves its controller
|
|
/// in a *field initialiser*:
|
|
///
|
|
/// final DeliveryController _deliveryController = Get.find<DeliveryController>();
|
|
///
|
|
/// which runs while the widget is being constructed, so an unregistered
|
|
/// controller throws before the first frame of the delivery dashboard and the
|
|
/// rider gets a red screen rather than a degraded one.
|
|
///
|
|
/// ── Why it is not simply added to `main()` ──
|
|
///
|
|
/// It is called from `main()`, but only for a rider on the delivery line. A
|
|
/// parcel rider must not pay for four controllers he will never look at — two of
|
|
/// which (`RiderLogController`, `LogController`) start timers and location work
|
|
/// on construction. Registering both lines' logging controllers at once would
|
|
/// have a single rider filing two sets of duty logs against two different
|
|
/// endpoints.
|
|
///
|
|
/// So: one line's controllers, chosen once, at boot.
|
|
///
|
|
/// ── Idempotent on purpose ──
|
|
///
|
|
/// Guarded by `isRegistered` rather than assuming a clean container, because
|
|
/// this is also called defensively from [riderShell] — a rider who signs out of
|
|
/// a parcel account and into a delivery one switches lines without the process
|
|
/// restarting, so boot is not the only moment this can be needed.
|
|
/// ─────────────────────────────────────────────────────────────────────────
|
|
/// Puts every delivery-line controller in the container. **Synchronous, and it
|
|
/// must stay that way.**
|
|
///
|
|
/// ── The bug this shape exists to prevent ──
|
|
///
|
|
/// This was once a single `async` function that awaited `loadFromPrefs()` before
|
|
/// registering `DeliveryController`. Called from [riderShell] without an await —
|
|
/// correctly, since a widget builder cannot await — the first `await` handed
|
|
/// control back to the framework, which built the delivery shell *during the
|
|
/// suspension*, while three of the four controllers were still unregistered:
|
|
///
|
|
/// "DeliveryController" not found. You need to call
|
|
/// "Get.put(DeliveryController())"
|
|
///
|
|
/// thrown from the home page's field initialiser, i.e. a red screen instead of a
|
|
/// dashboard. A registration that a synchronous caller depends on cannot sit
|
|
/// behind an await.
|
|
///
|
|
/// So registration is synchronous and complete when it returns, and anything
|
|
/// that needs IO is [warmDeliveryLine]'s job.
|
|
void registerDeliveryLine() {
|
|
if (!Get.isRegistered<ProfileController>()) {
|
|
Get.put(ProfileController(), permanent: true);
|
|
}
|
|
if (!Get.isRegistered<DeliveryController>()) {
|
|
Get.put(DeliveryController(), permanent: true);
|
|
}
|
|
if (!Get.isRegistered<RiderLogController>()) {
|
|
Get.put(RiderLogController(), permanent: true);
|
|
}
|
|
if (!Get.isRegistered<LogController>()) {
|
|
Get.put(LogController(), permanent: true);
|
|
}
|
|
}
|
|
|
|
/// Registers the line, then does the IO that wants to be finished before the
|
|
/// first frame. Awaited from `main()`; see [registerDeliveryLine] for why the
|
|
/// two halves are separate.
|
|
Future<void> bootstrapDeliveryLine() async {
|
|
final firstRun = !Get.isRegistered<LogController>();
|
|
|
|
registerDeliveryLine();
|
|
|
|
// The rider's photo and name: the delivery home page's header draws the
|
|
// avatar, so populating this late means the letter fallback flashes on every
|
|
// launch. Mirrors what `main()` does for the parcel line.
|
|
await Get.find<ProfileController>().loadFromPrefs();
|
|
|
|
if (firstRun) {
|
|
// Same call `main()` makes for parcel. It no-ops unless the rider is
|
|
// already on duty — see the `onduty` guard inside `startLogging`.
|
|
await Get.find<LogController>().startLogging();
|
|
}
|
|
}
|