device infos

This commit is contained in:
2026-08-28 15:07:30 +05:30
parent 5723d373b2
commit 69f4f3e909
84 changed files with 4337 additions and 2339 deletions

View File

@@ -228,23 +228,19 @@ class WorkRepository {
// it is the timeline contradicting itself in the rider's face. Anything the
// backend already reports as past his hands is skipped, and stays skipped
// forever because [stampOrderEvent] never overwrites.
unawaited(
stampOrderEvents(
mapped
.where((s) {
final st = stopStatusOf(s);
return !st.isWorkComplete &&
!st.isCancelled &&
!st.isRejected &&
!st.isSkipped &&
!st.isPicked &&
!st.isDeliveryLeg;
})
.map((s) => (s['orderid'] ?? '').toString())
.where((id) => id.isNotEmpty),
OrderEvent.assigned,
),
);
//
// ── And it is read back onto the row ──
//
// It used to be fired into the dark with `unawaited`: written for one
// screen's timeline, and joined by that screen alone. That left every other
// screen with no clock at all on a row the backend dates with nothing, and
// "does this belong to today" is a question three of them ask.
//
// So the stamp comes back as `assignedat` — a field on the stop, like any
// other. Awaited rather than fired off, because a row that reaches Home
// before its own clock does is a row Home cannot place, and the batch is
// one read and one write of a small blob. See [stampOrderEventsAndRead].
await _stampAssignment(mapped);
_loadedAt = DateTime.now();
return _publish(
@@ -286,6 +282,59 @@ class WorkRepository {
}
}
/// Writes each stop's assignment clock onto it, in place, as `assignedat`.
///
/// ── Which rows are stamped, and which only read ──
///
/// Only work still in front of the rider is *stamped* — see the note at the
/// call site: the ledger did not exist when a stop that is already delivered
/// arrived, so its first sighting is whenever the app happened to be opened,
/// and an `Assigned 3:34 PM` above a `Delivered 11:57 AM` is the timeline
/// contradicting itself.
///
/// Every row is **read**, though, finished ones included. A stop the rider
/// collected this morning was stamped this morning while it was still ahead
/// of him, and that clock is exactly what keeps it inside today's trip — drop
/// it and the progress ring loses its own denominator the moment the work is
/// done.
///
/// A backend clock always wins: this only fills a gap, never overwrites.
static Future<void> _stampAssignment(List<Map<String, dynamic>> day) async {
if (day.isEmpty) return;
try {
final ledger = await stampOrderEventsAndRead(
day
.where((s) {
final st = stopStatusOf(s);
return !st.isWorkComplete &&
!st.isCancelled &&
!st.isRejected &&
!st.isSkipped &&
!st.isPicked &&
!st.isDeliveryLeg;
})
.map((s) => (s['orderid'] ?? '').toString())
.where((id) => id.isNotEmpty),
OrderEvent.assigned,
);
if (ledger.isEmpty) return;
for (final stop in day) {
final id = (stop['orderid'] ?? '').toString().trim();
if (id.isEmpty) continue;
final at = ledger[id]?[OrderEvent.assigned];
if (at == null || at.isEmpty) continue;
final existing = (stop['assignedat'] ?? '').toString().trim();
if (existing.isNotEmpty) continue;
stop['assignedat'] = at;
}
} catch (e) {
// A day with no assignment clocks is a day the screens date by whatever
// the backend sent. Never worth failing the fetch over.
debugPrint('[WORK] could not read the assignment ledger: $e');
}
}
/// Turns a refused call into the thing the rider does about it.
static LoadFailureKind _classify(ApiResult res) => switch (res.status) {
0 => LoadFailureKind.offline,