feat: add historical data

This commit is contained in:
2026-10-19 23:31:54 +02:00
parent b0074aeb3f
commit 5754ae742d
10 changed files with 1211 additions and 4 deletions
+45
View File
@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:drift/drift.dart';
@@ -45,6 +46,9 @@ class RemoteSyncService {
static const _venueIdKey = 'remote_venue_id';
static const _syncStateId = 'default';
/// Most outbox events sent per sync; a larger backlog drains over syncs.
static const maxEventsPerSync = 500;
static const _pollingInterval = Duration(
seconds: kSyncPollingIntervalSeconds,
);
@@ -227,6 +231,8 @@ class RemoteSyncService {
Future<void> _doSync(_Credentials credentials) async {
final state = await _getSyncState();
// Read before the snapshot, so the snapshot is never older than the events
final outbox = await _pendingEvents();
final openTabs = await barTabService.getOpenTabs();
final response = await apiClient.sync(
@@ -235,9 +241,15 @@ class RemoteSyncService {
deviceId: credentials.deviceId,
cursor: state?.serverCursor,
openTabs: openTabs.map(_toSnapshot).toList(),
events: [
for (final row in outbox)
jsonDecode(row.payload) as Map<String, dynamic>,
],
),
);
await _clearSentEvents(response);
for (final command in response.commands) {
await _processCommand(command, credentials);
}
@@ -251,6 +263,38 @@ class RemoteSyncService {
await _updateSyncState(success: true, cursor: response.cursor);
}
Future<List<TabEventRow>> _pendingEvents() {
return (database.select(database.tabEvents)
..orderBy([
(row) => OrderingTerm.asc(row.createdAt),
// Same-second events keep their insertion order
(row) => OrderingTerm.asc(const CustomExpression<int>('rowid')),
])
..limit(maxEventsPerSync))
.get();
}
/// Events the server stored or refused leave the outbox. Events created
/// while the sync was in flight are in neither list and go next time.
Future<void> _clearSentEvents(SyncResponse response) async {
for (final rejected in response.rejectedEvents) {
debugPrint(
'RemoteSyncService: event ${rejected.id} rejected: ${rejected.reason}',
);
}
final ids = {
...response.acknowledgedEventIds,
for (final rejected in response.rejectedEvents)
if (rejected.id != null) rejected.id!,
};
if (ids.isEmpty) return;
await (database.delete(
database.tabEvents,
)..where((row) => row.id.isIn(ids))).go();
}
SnapshotTab _toSnapshot(BarTab tab) {
return SnapshotTab(
id: tab.id,
@@ -309,6 +353,7 @@ class RemoteSyncService {
command.tabId,
paymentMethod: PaymentMethod.fromValue(command.paymentMethod),
expectedVersion: command.expectedVersion,
commandId: command.id,
);
_eventController.add(