Files
doormile_milderapp/lib/xpress/delivery_bootstrap.dart
Thiru-tenext d7348e253f Miler rider app: surface system, visible design language, backend lifecycle
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>
2026-08-22 05:40:35 +05:30

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();
}
}