Session expiry, arrival geofence guard, multi-destination stops
Three fixes found by running the app on a real handset against production. 1. An expired token left the app looking signed in and unable to work. MilerApi.onUnauthorized was declared and called on every 401 but never assigned, so the token was dropped and nothing else happened: the profile stayed on disk, logged_out stayed false, and the rider saw his own name over a dashboard whose every call returned 401. He reads that as "no work today". The teardown now lives in endSession() and both ways out of a session — the Log out button and the 401 path — use it. 2. Arrived was written locally even when the rider was not there. updateArrivedStatus answers false for three different things and the caller treated all of them as "the write did not land", which is only true of one. A geofence refusal and a server refusal now stop the rung and hand back the reason; a dead network still advances, as it should. 3. A multi-destination customer pickup collapsed onto one stop. GET /miler/bookings returns a row per destination once collected, all with the same bookingid and reference. Every local store keys on that id, so the accepted store deduped two of three drops away and their consignment ids were unrecoverable. orderid is now the stop key; bookingreference stays the booking's name. Cards show "Stop 2 of 3" and the receiver's own name and number rather than the sender's. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqVJPB9B4QuieZnBAAKgYQ
This commit is contained in:
@@ -68,6 +68,19 @@ import 'package:flutter/foundation.dart';
|
||||
/// flutter run --dart-define=MOCK_BACKEND=true # the canned day
|
||||
const bool kMockBackend = bool.fromEnvironment('MOCK_BACKEND');
|
||||
|
||||
/// One customer pickup with three doors, for looking at.
|
||||
///
|
||||
/// **Off unless asked for**, and separate from [kMockBackend] on purpose. The
|
||||
/// bookings route above is deliberately empty — "inventing one here would put
|
||||
/// fictional consignments in front of a parcel rider" — and that stays exactly
|
||||
/// true by default. This is the one shape that cannot be reached any other way:
|
||||
/// `GET /miler/bookings` only returns a row per destination once a
|
||||
/// multi-destination customer-app booking has been collected, so seeing the
|
||||
/// three-door card on a bench needs either a seeded backend or this.
|
||||
///
|
||||
/// flutter run --dart-define=MOCK_BACKEND=true --dart-define=MOCK_MULTIDROP=true
|
||||
const bool kMockMultiDrop = bool.fromEnvironment('MOCK_MULTIDROP');
|
||||
|
||||
/// Canned answers for the routes the rider's day passes through.
|
||||
///
|
||||
/// Everything else gets a generic success, on purpose: the alternative is a
|
||||
@@ -108,6 +121,103 @@ class MockBackend {
|
||||
/// of it and no log line makes it look like a real session.
|
||||
static const String token = 'MOCK-TOKEN-not-a-real-session';
|
||||
|
||||
/// One collected customer pickup, as three rows — the shape
|
||||
/// `milerStopsForBooking` returns once `pickup-complete` has fanned a
|
||||
/// multi-destination booking out into a consignment per door.
|
||||
///
|
||||
/// Written in the **raw wire shape**, not the app's stop shape, because that
|
||||
/// is what the mock intercepts: these go through `ApiConfig.pickupFromBooking`
|
||||
/// exactly as a real response would, so the adapter is being exercised rather
|
||||
/// than bypassed. Same `bookingid` and `bookingreference` on all three, which
|
||||
/// is the whole point — they are told apart only by `destinationseq`.
|
||||
///
|
||||
/// Ids are `MOCK-` per rule 3 so `purgeDemoRecords()` can find anything that
|
||||
/// leaks into a store.
|
||||
static const List<Map<String, dynamic>> multiDropVisit = [
|
||||
{
|
||||
'bookingid': 900482913,
|
||||
'bookingreference': 'MOCK-482913',
|
||||
'status': 'Converted_To_Consignment',
|
||||
'consignmentid': 900500,
|
||||
'consignmentstatus': 'Out_for_Delivery',
|
||||
'trackingno': 'MOCK-TRK-8841020',
|
||||
'destinationseq': 0,
|
||||
'destinationcount': 3,
|
||||
'next_action': 'deliver',
|
||||
'step': 1,
|
||||
'pickup_source_type': 'customer',
|
||||
'pickup_source_name': 'Ravi Kumar',
|
||||
'pickupaddress': '14, Cross Cut Road, Gandhipuram, Coimbatore 641012',
|
||||
'pickuplatitude': 11.0183,
|
||||
'pickuplongitude': 76.9725,
|
||||
'deliveryaddress': '22, Anna Nagar West, Chennai, Tamil Nadu',
|
||||
'deliverylatitude': 11.0210,
|
||||
'deliverylongitude': 76.9810,
|
||||
'recipientname': 'Anitha Raghavan',
|
||||
'recipientphone': '9840012201',
|
||||
'customername': 'Ravi Kumar',
|
||||
'customerphone': '9843311220',
|
||||
'parcels': [
|
||||
{'bookingparcelid': 900001, 'itemcategory': 'General'},
|
||||
{'bookingparcelid': 900002, 'itemcategory': 'General'},
|
||||
],
|
||||
},
|
||||
{
|
||||
'bookingid': 900482913,
|
||||
'bookingreference': 'MOCK-482913',
|
||||
'status': 'Converted_To_Consignment',
|
||||
'consignmentid': 900501,
|
||||
'consignmentstatus': 'Out_for_Delivery',
|
||||
'trackingno': 'MOCK-TRK-8841021',
|
||||
'destinationseq': 1,
|
||||
'destinationcount': 3,
|
||||
'next_action': 'deliver',
|
||||
'step': 2,
|
||||
'pickup_source_type': 'customer',
|
||||
'pickup_source_name': 'Ravi Kumar',
|
||||
'pickupaddress': '14, Cross Cut Road, Gandhipuram, Coimbatore 641012',
|
||||
'pickuplatitude': 11.0183,
|
||||
'pickuplongitude': 76.9725,
|
||||
'deliveryaddress': '7, Panampilly Nagar, Ernakulam, Kerala',
|
||||
'deliverylatitude': 11.0301,
|
||||
'deliverylongitude': 76.9902,
|
||||
'recipientname': 'Suresh Menon',
|
||||
'recipientphone': '9847712345',
|
||||
'customername': 'Ravi Kumar',
|
||||
'customerphone': '9843311220',
|
||||
'parcels': [
|
||||
{'bookingparcelid': 900003, 'itemcategory': 'General'},
|
||||
],
|
||||
},
|
||||
{
|
||||
'bookingid': 900482913,
|
||||
'bookingreference': 'MOCK-482913',
|
||||
'status': 'Converted_To_Consignment',
|
||||
'consignmentid': 900502,
|
||||
'consignmentstatus': 'Out_for_Delivery',
|
||||
'trackingno': 'MOCK-TRK-8841022',
|
||||
'destinationseq': 2,
|
||||
'destinationcount': 3,
|
||||
'next_action': 'deliver',
|
||||
'step': 3,
|
||||
'pickup_source_type': 'customer',
|
||||
'pickup_source_name': 'Ravi Kumar',
|
||||
'pickupaddress': '14, Cross Cut Road, Gandhipuram, Coimbatore 641012',
|
||||
'pickuplatitude': 11.0183,
|
||||
'pickuplongitude': 76.9725,
|
||||
'deliveryaddress': '90, Indiranagar 2nd Stage, Bengaluru, Karnataka',
|
||||
'deliverylatitude': 11.0405,
|
||||
'deliverylongitude': 77.0011,
|
||||
'recipientname': 'Meena Prakash',
|
||||
'recipientphone': '9880045512',
|
||||
'customername': 'Ravi Kumar',
|
||||
'customerphone': '9843311220',
|
||||
'parcels': [
|
||||
{'bookingparcelid': 900004, 'itemcategory': 'General'},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/// Answers [method] [path], or null when the caller should fall through to
|
||||
/// the network. Null is never returned while [enabled] — see the class note.
|
||||
static Map<String, dynamic>? respond(
|
||||
@@ -160,7 +270,7 @@ class MockBackend {
|
||||
// at all. A parcel booking list has no fixture and inventing one here would
|
||||
// put fictional consignments in front of a parcel rider.
|
||||
if (route.endsWith('/bookings') || route.endsWith('/assignments')) {
|
||||
return _ok(const <dynamic>[]);
|
||||
return _ok(kMockMultiDrop ? multiDropVisit : const <dynamic>[]);
|
||||
}
|
||||
|
||||
// ── Everything the rider writes ──
|
||||
|
||||
Reference in New Issue
Block a user