|
1 | 1 | import 'dart:async'; |
2 | 2 |
|
| 3 | +import 'package:collection/collection.dart'; |
3 | 4 | import 'package:dio/dio.dart'; |
4 | 5 | import 'package:logging/logging.dart'; |
5 | 6 | import 'package:meta/meta.dart'; |
@@ -186,9 +187,6 @@ class StreamChatClient { |
186 | 187 |
|
187 | 188 | late final RetryPolicy _retryPolicy; |
188 | 189 |
|
189 | | - /// the last dateTime at the which all the channels were synced |
190 | | - DateTime? _lastSyncedAt; |
191 | | - |
192 | 190 | /// The retry policy options getter |
193 | 191 | RetryPolicy get retryPolicy => _retryPolicy; |
194 | 192 |
|
@@ -519,25 +517,17 @@ class StreamChatClient { |
519 | 517 |
|
520 | 518 | if (connectionRecovered) { |
521 | 519 | // connection recovered |
522 | | - final cids = state.channels.keys.toList(growable: false); |
| 520 | + final cids = [...state.channels.keys.toSet()]; |
523 | 521 | if (cids.isNotEmpty) { |
524 | 522 | await queryChannelsOnline( |
525 | 523 | filter: Filter.in_('cid', cids), |
526 | 524 | paginationParams: const PaginationParams(limit: 30), |
527 | 525 | ); |
528 | | - if (persistenceEnabled) { |
529 | | - await sync(cids: cids, lastSyncAt: _lastSyncedAt); |
530 | | - } |
531 | | - } else { |
532 | | - // channels are empty, assuming it's a fresh start |
533 | | - // and making sure `lastSyncAt` is initialized |
534 | | - if (persistenceEnabled) { |
535 | | - final lastSyncAt = await chatPersistenceClient?.getLastSyncAt(); |
536 | | - if (lastSyncAt == null) { |
537 | | - await chatPersistenceClient?.updateLastSyncAt(DateTime.now()); |
538 | | - } |
539 | | - } |
| 526 | + |
| 527 | + // Sync the persistence client if available |
| 528 | + if (persistenceEnabled) await sync(cids: cids); |
540 | 529 | } |
| 530 | + |
541 | 531 | handleEvent(Event( |
542 | 532 | type: EventType.connectionRecovered, |
543 | 533 | online: true, |
@@ -569,34 +559,45 @@ class StreamChatClient { |
569 | 559 | Future<void> sync({List<String>? cids, DateTime? lastSyncAt}) { |
570 | 560 | return _syncLock.synchronized(() async { |
571 | 561 | final channels = cids ?? await chatPersistenceClient?.getChannelCids(); |
572 | | - if (channels == null || channels.isEmpty) { |
573 | | - return; |
574 | | - } |
| 562 | + if (channels == null || channels.isEmpty) return; |
575 | 563 |
|
576 | 564 | final syncAt = lastSyncAt ?? await chatPersistenceClient?.getLastSyncAt(); |
577 | 565 | if (syncAt == null) { |
578 | | - return; |
| 566 | + logger.info('Fresh sync start: lastSyncAt initialized to now.'); |
| 567 | + return chatPersistenceClient?.updateLastSyncAt(DateTime.now()); |
579 | 568 | } |
580 | 569 |
|
581 | 570 | try { |
| 571 | + logger.info('Syncing events since $syncAt for channels: $channels'); |
| 572 | + |
582 | 573 | final res = await _chatApi.general.sync(channels, syncAt); |
583 | | - final events = res.events |
584 | | - ..sort((a, b) => a.createdAt.compareTo(b.createdAt)); |
| 574 | + final events = res.events.sorted( |
| 575 | + (a, b) => a.createdAt.compareTo(b.createdAt), |
| 576 | + ); |
585 | 577 |
|
586 | 578 | for (final event in events) { |
587 | | - logger.fine('event.type: ${event.type}'); |
588 | | - final messageText = event.message?.text; |
589 | | - if (messageText != null) { |
590 | | - logger.fine('event.message.text: $messageText'); |
591 | | - } |
| 579 | + logger.fine('Syncing event: ${event.type}'); |
592 | 580 | handleEvent(event); |
593 | 581 | } |
594 | 582 |
|
595 | | - final now = DateTime.now(); |
596 | | - _lastSyncedAt = now; |
597 | | - chatPersistenceClient?.updateLastSyncAt(now); |
598 | | - } catch (e, stk) { |
599 | | - logger.severe('Error during sync', e, stk); |
| 583 | + final updatedSyncAt = events.lastOrNull?.createdAt ?? DateTime.now(); |
| 584 | + return chatPersistenceClient?.updateLastSyncAt(updatedSyncAt); |
| 585 | + } catch (error, stk) { |
| 586 | + // If we got a 400 error, it means that either the sync time is too |
| 587 | + // old or the channel list is too long or too many events need to be |
| 588 | + // synced. In this case, we should just flush the persistence client |
| 589 | + // and start over. |
| 590 | + if (error is StreamChatNetworkError && error.statusCode == 400) { |
| 591 | + logger.warning( |
| 592 | + 'Failed to sync events due to stale or oversized state. ' |
| 593 | + 'Resetting the persistence client to enable a fresh start.', |
| 594 | + ); |
| 595 | + |
| 596 | + await chatPersistenceClient?.flush(); |
| 597 | + return chatPersistenceClient?.updateLastSyncAt(DateTime.now()); |
| 598 | + } |
| 599 | + |
| 600 | + logger.warning('Error syncing events', error, stk); |
600 | 601 | } |
601 | 602 | }); |
602 | 603 | } |
@@ -2102,7 +2103,6 @@ class StreamChatClient { |
2102 | 2103 | // resetting state. |
2103 | 2104 | state.dispose(); |
2104 | 2105 | state = ClientState(this); |
2105 | | - _lastSyncedAt = null; |
2106 | 2106 |
|
2107 | 2107 | // resetting credentials. |
2108 | 2108 | _tokenManager.reset(); |
|
0 commit comments