updated the sheet
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:miler/xpress/views/helpers/constants/apiconstants.dart';
|
||||
import 'package:miler/xpress/Models/riders/riders_models.dart';
|
||||
import 'package:miler/xpress/providers/Riderlog/riderlog_provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
@@ -15,6 +14,7 @@ import 'package:miler/xpress/controllers/logcontroller.dart';
|
||||
import 'package:miler/xpress/background/foreground_service.dart' as fg;
|
||||
import 'package:miler/xpress/utils/kalman_filter.dart';
|
||||
import 'package:miler/xpress/utils/mqtt_service.dart';
|
||||
import 'package:miler/data/heartbeat.dart';
|
||||
import 'package:miler/xpress/views/helpers/constants/mqtt_constants.dart';
|
||||
import 'package:battery_plus/battery_plus.dart';
|
||||
|
||||
@@ -40,11 +40,9 @@ class RiderLogController extends GetxController {
|
||||
Future<void> fetchRiderLogs({required String userId}) async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
final base = ApiConstants.mainRoute == 'live'
|
||||
? ApiConstants.getRiderLogLive
|
||||
: ApiConstants.getRiderLogDev;
|
||||
final url = "$base?userid=$userId";
|
||||
final response = await _getProvider.getRiderLog(url);
|
||||
// v1 answers duty for the *token's* rider — `userid` is neither sent
|
||||
// nor needed. See [GetRiderLogProvider].
|
||||
final response = await _getProvider.getRiderLog();
|
||||
|
||||
// Handle either {details: {...}} or {data: [...]} shapes
|
||||
if (response == null) {
|
||||
@@ -122,11 +120,7 @@ class RiderLogController extends GetxController {
|
||||
}
|
||||
|
||||
Future<void> fetchRiderCount({required String userId}) async {
|
||||
final base = ApiConstants.mainRoute == 'live'
|
||||
? ApiConstants.getRiderCountLive
|
||||
: ApiConstants.getRiderCountDev;
|
||||
final url = "$base?userid=$userId";
|
||||
final response = await _getProvider.getRiderCount(url);
|
||||
final response = await _getProvider.getRiderCount();
|
||||
final dynamic cnt = response != null ? response['count'] : null;
|
||||
riderCount.value = cnt is num
|
||||
? cnt.toInt()
|
||||
@@ -134,46 +128,30 @@ class RiderLogController extends GetxController {
|
||||
}
|
||||
|
||||
Future<bool> createLogin(RiderLogin login) async {
|
||||
final base = ApiConstants.mainRoute == 'live'
|
||||
? ApiConstants.createRiderLogLive
|
||||
: ApiConstants.createRiderLogDev;
|
||||
final ok = await _createProvider.createRiderLog(
|
||||
base,
|
||||
_CreateRiderLogRequestCompat.fromRiderLogin(login),
|
||||
);
|
||||
return ok != null && ok.isNotEmpty;
|
||||
}
|
||||
|
||||
Future<bool> updateLog(RiderUpdate update) async {
|
||||
final base = ApiConstants.mainRoute == 'live'
|
||||
? ApiConstants.updateRiderLogLive
|
||||
: ApiConstants.updateRiderLogDev;
|
||||
final ok = await _updateProvider.updateRiderLog(
|
||||
base,
|
||||
_UpdateRiderLogRequestCompat.fromRiderUpdate(update),
|
||||
);
|
||||
return ok != null && ok.isNotEmpty;
|
||||
}
|
||||
|
||||
Future<bool> createBreak(RiderBreak brk) async {
|
||||
final base = ApiConstants.mainRoute == 'live'
|
||||
? ApiConstants.createBreakRiderLogLive
|
||||
: ApiConstants.createBreakRiderLogDev;
|
||||
final ok = await _breakProvider.createBreakRiderLog(
|
||||
base,
|
||||
_BreakLogRequestCompat.fromRiderBreak(brk),
|
||||
);
|
||||
return ok != null && ok.isNotEmpty;
|
||||
}
|
||||
|
||||
Future<bool> updateBreak(RiderBreak brk) async {
|
||||
final base = ApiConstants.mainRoute == 'live'
|
||||
? ApiConstants.updateBreakRiderLogLive
|
||||
: ApiConstants.updateBreakRiderLogDev;
|
||||
final ok = await _breakProvider.updateBreakRiderLog(
|
||||
base,
|
||||
_BreakLogRequestCompat.fromRiderBreak(brk),
|
||||
);
|
||||
// `PUT /miler/breaks/end` takes no body and closes whichever break is
|
||||
// open, which the server knows and the app was only guessing at.
|
||||
final ok = await _breakProvider.updateBreakRiderLog();
|
||||
return ok != null && ok.isNotEmpty;
|
||||
}
|
||||
|
||||
@@ -190,10 +168,6 @@ class RiderLogController extends GetxController {
|
||||
required String latitude,
|
||||
required String longitude,
|
||||
}) async {
|
||||
final url = ApiConstants.mainRoute == 'live'
|
||||
? ApiConstants.updateBreakRiderLogLive
|
||||
: ApiConstants.updateBreakRiderLogDev;
|
||||
|
||||
final payload = {
|
||||
"breakid": breakid,
|
||||
"logid": logid,
|
||||
@@ -209,11 +183,9 @@ class RiderLogController extends GetxController {
|
||||
};
|
||||
|
||||
try {
|
||||
// Debug: show exact URL and JSON body
|
||||
debugPrint('[BREAK][UPDATE] URL: $url');
|
||||
debugPrint('[BREAK][UPDATE] Body: $payload');
|
||||
|
||||
final response = await _breakProvider.updateBreakRiderLog(url, payload);
|
||||
final response = await _breakProvider.updateBreakRiderLog();
|
||||
|
||||
// Debug: show raw response
|
||||
debugPrint(
|
||||
@@ -375,13 +347,9 @@ class RiderLogController extends GetxController {
|
||||
payload.remove('lastname');
|
||||
}
|
||||
|
||||
final base = ApiConstants.mainRoute == 'live'
|
||||
? ApiConstants.createRiderLogLive
|
||||
: ApiConstants.createRiderLogDev;
|
||||
debugPrint('[RIDERLOG][CREATE LOGIN NOW] URL: $base');
|
||||
debugPrint('[RIDERLOG][CREATE LOGIN NOW] Payload: $payload');
|
||||
|
||||
final resp = await _createProvider.createRiderLog(base, payload);
|
||||
final resp = await _createProvider.createRiderLog(payload);
|
||||
debugPrint(
|
||||
'[RIDERLOG][CREATE LOGIN NOW] Response: ${resp == null ? 'null' : resp.toString()}',
|
||||
);
|
||||
@@ -391,7 +359,7 @@ class RiderLogController extends GetxController {
|
||||
debugPrint(
|
||||
'[RIDERLOG][CREATE LOGIN NOW] Failed, saving to offline queue',
|
||||
);
|
||||
await _saveToOfflineQueue(base, payload);
|
||||
await _saveToOfflineQueue('/miler/logs', payload);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -500,11 +468,7 @@ class RiderLogController extends GetxController {
|
||||
payload.remove('lastname');
|
||||
}
|
||||
|
||||
final base = ApiConstants.mainRoute == 'live'
|
||||
? ApiConstants.createRiderLogLive
|
||||
: ApiConstants.createRiderLogDev;
|
||||
|
||||
await _saveToOfflineQueue(base, payload);
|
||||
await _saveToOfflineQueue('/miler/logs', payload);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('[RIDERLOG] Error saving offline log: $e');
|
||||
@@ -532,10 +496,15 @@ class RiderLogController extends GetxController {
|
||||
await prefs.setInt('onduty', on ? 1 : 0);
|
||||
if (on) {
|
||||
await createLoginNowV2();
|
||||
final int interval = prefs.getInt('logseconds') ?? 0;
|
||||
if (interval > 0) {
|
||||
startAutoCreateLoginLoop(seconds: interval);
|
||||
}
|
||||
// ── Going on duty always starts the heartbeat ──
|
||||
//
|
||||
// Gated on `interval > 0`, this read a stored zero as a decision not
|
||||
// to beat. The v1 login has no `logseconds` to store, so the gate was
|
||||
// shut on every rider: duty went on, one log was posted by the call
|
||||
// above, and nothing followed it. Mirrors the logistics controller —
|
||||
// see [resolveLogSeconds].
|
||||
final int interval = resolveLogSeconds(prefs.getInt('logseconds'));
|
||||
startAutoCreateLoginLoop(seconds: interval);
|
||||
|
||||
// Ensure foreground logging notification is started when going on-duty
|
||||
try {
|
||||
@@ -604,16 +573,43 @@ class RiderLogController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the periodic heartbeat is currently running on the stream path.
|
||||
///
|
||||
/// ── Why a test needs to be able to see this ──
|
||||
///
|
||||
/// The `logseconds` bug was invisible from outside the class. Duty went on,
|
||||
/// the API accepted it, one log was posted by the immediate call — and the
|
||||
/// loop that was supposed to follow it simply never started. Nothing threw,
|
||||
/// nothing logged an error, and the only symptom was a console panel that had
|
||||
/// been blank for so long it read as a backend problem.
|
||||
///
|
||||
/// Android normally runs the heartbeat as a foreground service instead, and
|
||||
/// leaves this null; the test host is never Android, so the stream path is
|
||||
/// the one under test. See [startAutoCreateLoginLoop].
|
||||
@visibleForTesting
|
||||
bool get autoLoopRunning => _autoLoginSubscription != null;
|
||||
|
||||
// Start periodic createRiderLog calls based on seconds (or prefs 'logseconds')
|
||||
// ✅ CRITICAL: When there are active deliveries, use 30 seconds (same as delivery logs)
|
||||
// Otherwise, use the configured logseconds interval
|
||||
void startAutoCreateLoginLoop({int? seconds}) async {
|
||||
/// `Future<void>`, not `void`. It was fire-and-forget, which meant no caller
|
||||
/// could wait for the loop to actually be up — and the one place that most
|
||||
/// wanted to, [setOnDuty], had no way to tell whether the heartbeat it had
|
||||
/// just asked for existed. Every existing call site ignores the future and is
|
||||
/// unchanged by this.
|
||||
Future<void> startAutoCreateLoginLoop({int? seconds}) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
|
||||
// ✅ Check if there are active deliveries - if yes, use 30 seconds (same as delivery logs)
|
||||
final bool hasActiveDeliveries =
|
||||
prefs.getBool('has_live_deliveries') ?? false;
|
||||
final int baseInterval = seconds ?? (prefs.getInt('logseconds') ?? 0);
|
||||
// The configured cadence, or the floor under it. `?? 0` here was the
|
||||
// second half of the same bug as the one in [setOnDuty]: a caller passing
|
||||
// no `seconds` fell through to a stored zero and returned at the
|
||||
// `interval <= 0` guard below. See [resolveLogSeconds].
|
||||
final int baseInterval = resolveLogSeconds(
|
||||
seconds ?? prefs.getInt('logseconds'),
|
||||
);
|
||||
|
||||
// When there are active deliveries, post rider logs every 30 seconds (matching delivery logs)
|
||||
// Otherwise, use the configured interval
|
||||
@@ -625,12 +621,18 @@ class RiderLogController extends GetxController {
|
||||
);
|
||||
}
|
||||
|
||||
await stopAutoCreateLoginLoop(); // Cancel existing subscription
|
||||
// Stop before start, which is what keeps this method safe to call from
|
||||
// every place that does. Whichever call arrives last wins, and there is
|
||||
// never a second loop: the subscription is cancelled and the foreground
|
||||
// service stopped before either is created again.
|
||||
await stopAutoCreateLoginLoop();
|
||||
|
||||
// Attempt to flush offline logs on loop start
|
||||
flushOfflineLogs();
|
||||
|
||||
if (interval <= 0) return;
|
||||
// No `interval <= 0` guard. [resolveLogSeconds] cannot return one, and the
|
||||
// guard that used to be here is what turned a missing `logseconds` into a
|
||||
// rider who never reported.
|
||||
final int onduty = prefs.getInt('onduty') ?? 0;
|
||||
if (onduty != 1) {
|
||||
debugPrint('[RIDERLOG][AUTO LOOP] Not starting - onduty=$onduty');
|
||||
@@ -921,10 +923,6 @@ class RiderLogController extends GetxController {
|
||||
final breakdate = _formatDateTimeFull(now); // e.g. 2025-10-16 16:36:16
|
||||
final breakstart = _formatTime(now); // e.g. 16:36:16
|
||||
|
||||
final url = ApiConstants.mainRoute == 'live'
|
||||
? ApiConstants.createBreakRiderLogLive
|
||||
: ApiConstants.createBreakRiderLogDev;
|
||||
|
||||
// Build payload with all required fields in the exact format the API expects
|
||||
final payload = <String, dynamic>{
|
||||
"breakid": localBreakId,
|
||||
@@ -941,13 +939,12 @@ class RiderLogController extends GetxController {
|
||||
};
|
||||
|
||||
// Debug logs for terminal visibility
|
||||
debugPrint('[BREAK][CREATE] URL: $url');
|
||||
debugPrint('[BREAK][CREATE] Payload: $payload');
|
||||
|
||||
// Persist local break id immediately to ensure update can reference it
|
||||
await prefs.setInt('breakId', localBreakId);
|
||||
|
||||
final resp = await _breakProvider.createBreakRiderLog(url, payload);
|
||||
final resp = await _breakProvider.createBreakRiderLog(payload);
|
||||
|
||||
debugPrint(
|
||||
'[BREAK][CREATE] Response: ${resp == null ? 'null' : resp.toString()}',
|
||||
@@ -1045,9 +1042,6 @@ class RiderLogController extends GetxController {
|
||||
final breakhoursDouble = duration.inSeconds / 3600.0;
|
||||
|
||||
// Debug logs for terminal visibility
|
||||
debugPrint(
|
||||
'[BREAK][UPDATE] URL: ${ApiConstants.mainRoute == 'live' ? ApiConstants.updateBreakRiderLogLive : ApiConstants.updateBreakRiderLogDev}',
|
||||
);
|
||||
debugPrint(
|
||||
'[BREAK][UPDATE] Fields: breakid=$breakid, logid=$logid, userid=$userid, partnerid=$partnerid, shiftid=$shiftid, breakend=$breakend, breakhours=$breakhoursDouble, lat=$latitude, lng=$longitude',
|
||||
);
|
||||
@@ -1101,10 +1095,6 @@ class RiderLogController extends GetxController {
|
||||
final iso = _formatDateTimeFull(now);
|
||||
final loginTime = _formatTime(now);
|
||||
|
||||
final url = ApiConstants.mainRoute == 'live'
|
||||
? ApiConstants.createRiderLogLive
|
||||
: ApiConstants.createRiderLogDev;
|
||||
|
||||
// ✅ Check if there are active deliveries to set status
|
||||
final bool hasActiveDeliveries =
|
||||
prefs.getBool('has_live_deliveries') ?? false;
|
||||
@@ -1141,10 +1131,9 @@ class RiderLogController extends GetxController {
|
||||
userfcmtoken: userfcmtoken ?? '',
|
||||
).toJson();
|
||||
|
||||
debugPrint('[RIDERLOG][CREATE LOGIN] URL: $url');
|
||||
debugPrint('[RIDERLOG][CREATE LOGIN] Payload: $payload');
|
||||
|
||||
final resp = await _createProvider.createRiderLog(url, payload);
|
||||
final resp = await _createProvider.createRiderLog(payload);
|
||||
debugPrint(
|
||||
'[RIDERLOG][CREATE LOGIN] Response: ${resp == null ? 'null' : resp.toString()}',
|
||||
);
|
||||
@@ -1205,7 +1194,7 @@ class RiderLogController extends GetxController {
|
||||
// We can use _createProvider generic call
|
||||
|
||||
debugPrint('[RIDERLOG][OFFLINE] Retrying: $payload');
|
||||
final resp = await _createProvider.createRiderLog(url, payload);
|
||||
final resp = await _createProvider.createRiderLog(payload);
|
||||
|
||||
if (resp != null && resp.isNotEmpty) {
|
||||
debugPrint('[RIDERLOG][OFFLINE] Success!');
|
||||
|
||||
Reference in New Issue
Block a user