payfrit-app/lib/services/order_polling_service.dart
John Mizerek 9c91737b1a Fix modifier hierarchy and add order status polling
Flutter App Changes:
- Fixed modifier saving to maintain full hierarchy (containers + selections)
- Fixed cart display to show breadcrumb paths (e.g., "Customize Patties > Mustard grilled")
- Added order status polling service (30-second intervals)
- Updated AppState to track active order and status
- Fixed Android Bluetooth permissions for release builds
- Disabled minification to preserve beacon library in release builds
- Added ProGuard rules for beacon/Bluetooth classes
- Added foreground service permissions for beacon monitoring

Technical Details:
- menu_browse_screen.dart: Added _hasSelectedDescendants() to ensure container items are saved
- cart_view_screen.dart: Added _buildModifierPaths() for breadcrumb display
- order_polling_service.dart: Polls checkStatusUpdate.cfm every 30s
- api.dart: Added checkOrderStatus() method
- Android permissions: Removed neverForLocation flag, added FOREGROUND_SERVICE
- ProGuard: Disabled shrinking, added keep rules for beacon classes

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-31 21:37:55 -08:00

114 lines
3.3 KiB
Dart
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 data = await Api.checkOrderStatus(
orderId: _currentOrderId!,
lastKnownStatusId: _lastKnownStatusId!,
);
if (data['OK'] == true && data['HAS_UPDATE'] == true) {
final orderStatus = data['ORDER_STATUS'];
final newStatusId = (orderStatus['StatusID'] as num).toInt();
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)';
}