Client-side implementation: - New service: order_polling_service.dart - Timer-based polling every 30s - Updated AppState: track active order ID and current status - Cart integration: start polling after order submission - In-app notifications: color-coded SnackBar for status changes - Notification widget: animated banner (created for future use) Status notification colors: - Blue = Submitted (1) - Orange = Preparing (2) - Green = Ready (3) - Purple = Completed (4) Polling workflow: 1. User submits order → polling starts with status=1 2. Every 30s: check backend for status changes 3. On update: show notification + update AppState 4. Continues until order completed or app closed Simple polling approach (Option 2) with planned migration to self-hosted WebSocket push for instant updates. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
119 lines
3.4 KiB
Dart
119 lines
3.4 KiB
Dart
import 'dart:async';
|
||
import 'package:flutter/material.dart';
|
||
import 'api.dart';
|
||
|
||
/// Service that polls the backend API for order status updates
|
||
/// Uses a simple polling approach (Option 2) with 30-second intervals
|
||
class OrderPollingService {
|
||
static Timer? _pollingTimer;
|
||
static int? _currentOrderId;
|
||
static int? _lastKnownStatusId;
|
||
static Function(OrderStatusUpdate)? _onStatusUpdate;
|
||
|
||
/// Start polling for order status updates
|
||
static void startPolling({
|
||
required int orderId,
|
||
required int initialStatusId,
|
||
required Function(OrderStatusUpdate) onStatusUpdate,
|
||
}) {
|
||
print('[OrderPolling] 🔄 Starting polling for OrderID=$orderId, Status=$initialStatusId');
|
||
|
||
// Stop any existing polling
|
||
stopPolling();
|
||
|
||
_currentOrderId = orderId;
|
||
_lastKnownStatusId = initialStatusId;
|
||
_onStatusUpdate = onStatusUpdate;
|
||
|
||
// Poll every 30 seconds
|
||
_pollingTimer = Timer.periodic(const Duration(seconds: 30), (_) {
|
||
_checkForUpdate();
|
||
});
|
||
|
||
// Also check immediately
|
||
_checkForUpdate();
|
||
}
|
||
|
||
/// Stop polling
|
||
static void stopPolling() {
|
||
print('[OrderPolling] ⏹️ Stopping polling');
|
||
_pollingTimer?.cancel();
|
||
_pollingTimer = null;
|
||
_currentOrderId = null;
|
||
_lastKnownStatusId = null;
|
||
_onStatusUpdate = null;
|
||
}
|
||
|
||
/// Check if currently polling
|
||
static bool get isPolling => _pollingTimer != null && _pollingTimer!.isActive;
|
||
|
||
/// Get current order ID being polled
|
||
static int? get currentOrderId => _currentOrderId;
|
||
|
||
/// Internal method to check for updates
|
||
static Future<void> _checkForUpdate() async {
|
||
if (_currentOrderId == null || _lastKnownStatusId == null) {
|
||
print('[OrderPolling] ⚠️ No order to poll, stopping');
|
||
stopPolling();
|
||
return;
|
||
}
|
||
|
||
try {
|
||
print('[OrderPolling] 🔍 Checking status for OrderID=$_currentOrderId');
|
||
|
||
final response = await Api.post(
|
||
'/orders/checkStatusUpdate.cfm',
|
||
body: {
|
||
'OrderID': _currentOrderId,
|
||
'LastKnownStatusID': _lastKnownStatusId,
|
||
},
|
||
);
|
||
|
||
final data = response.data;
|
||
|
||
if (data['OK'] == true && data['HAS_UPDATE'] == true) {
|
||
final orderStatus = data['ORDER_STATUS'];
|
||
final newStatusId = orderStatus['StatusID'] as int;
|
||
final statusName = orderStatus['StatusName'] as String;
|
||
final message = orderStatus['Message'] as String;
|
||
|
||
print('[OrderPolling] ✅ Status update detected: $statusName (ID=$newStatusId)');
|
||
|
||
// Update last known status
|
||
_lastKnownStatusId = newStatusId;
|
||
|
||
// Notify listener
|
||
if (_onStatusUpdate != null) {
|
||
_onStatusUpdate!(OrderStatusUpdate(
|
||
orderId: _currentOrderId!,
|
||
statusId: newStatusId,
|
||
statusName: statusName,
|
||
message: message,
|
||
));
|
||
}
|
||
} else {
|
||
print('[OrderPolling] ℹ️ No status update');
|
||
}
|
||
} catch (e) {
|
||
print('[OrderPolling] ❌ Error checking status: $e');
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Data class representing an order status update
|
||
class OrderStatusUpdate {
|
||
final int orderId;
|
||
final int statusId;
|
||
final String statusName;
|
||
final String message;
|
||
|
||
const OrderStatusUpdate({
|
||
required this.orderId,
|
||
required this.statusId,
|
||
required this.statusName,
|
||
required this.message,
|
||
});
|
||
|
||
@override
|
||
String toString() => 'OrderStatusUpdate(orderId: $orderId, statusId: $statusId, statusName: $statusName, message: $message)';
|
||
}
|