feat: add remote products and qr code for payconic

This commit is contained in:
2026-10-22 00:10:26 +02:00
parent b15d94a805
commit 71af6f9ffe
31 changed files with 2053 additions and 179 deletions
+29
View File
@@ -17,6 +17,7 @@ import '../models/remote_command.dart';
import '../utils/app_config.dart';
import 'bar_tab_service.dart';
import 'kooltap_api_client.dart';
import 'product_image_sync.dart';
typedef _Credentials = ({String deviceId, String token});
@@ -38,6 +39,10 @@ class RemoteSyncService {
final KoolTapApiClient apiClient;
final FlutterSecureStorage secureStorage;
/// Uploads pictures taken here before a sync and downloads new ones after;
/// null leaves pictures alone.
final ProductImageSync? imageSync;
/// How long tab changes must be quiet before they are synced.
final Duration changeDebounce;
@@ -49,6 +54,7 @@ class RemoteSyncService {
required this.barTabService,
required this.apiClient,
FlutterSecureStorage? secureStorage,
this.imageSync,
this.changeDebounce = const Duration(milliseconds: kSyncChangeDebounceMs),
this.changeMaxDelay = const Duration(milliseconds: kSyncChangeMaxDelayMs),
}) : secureStorage = secureStorage ?? const FlutterSecureStorage();
@@ -222,6 +228,14 @@ class RemoteSyncService {
try {
final appliedStock = await _doSync(credentials);
_consecutiveFailures = 0;
// In the background: a slow download must not hold up the next sync
if (imageSync case final images?) {
unawaited(
images.downloadMissing().catchError(
(Object e) => debugPrint('RemoteSyncService: pictures: $e'),
),
);
}
// Show the new stock on the dashboard within seconds. The follow-up
// skips the adjustments it already applied, so this doesn't loop.
nextDelay = appliedStock ? Duration.zero : _pollingInterval;
@@ -278,6 +292,21 @@ class RemoteSyncService {
/// Returns whether any stock adjustment was applied, which only happens
/// with a server that doesn't own the stock.
Future<bool> _doSync(_Credentials credentials) async {
// Pictures taken here first, so the events linking them go in this sync.
// One that doesn't upload (offline) waits; the sync itself still goes.
if (imageSync case final images?) {
try {
await images.uploadPending(
token: credentials.token,
deviceId: credentials.deviceId,
);
} on UnauthorizedException {
rethrow;
} catch (e) {
debugPrint('RemoteSyncService: picture upload failed: $e');
}
}
final state = await _getSyncState();
// One read, so the snapshot and the stock match the events that are sent
final (outbox, openTabs, products, stockResults) = await database