diff --git a/lib/pangea/analytics_data/analytics_data_service.dart b/lib/pangea/analytics_data/analytics_data_service.dart index 50af5f3a5..1c10b1208 100644 --- a/lib/pangea/analytics_data/analytics_data_service.dart +++ b/lib/pangea/analytics_data/analytics_data_service.dart @@ -85,6 +85,7 @@ class AnalyticsDataService { void dispose() { _syncController?.dispose(); updateDispatcher.dispose(); + updateService.dispose(); _closeDatabase(); } diff --git a/lib/pangea/analytics_data/analytics_sync_controller.dart b/lib/pangea/analytics_data/analytics_sync_controller.dart index 53ccd760a..f895c5657 100644 --- a/lib/pangea/analytics_data/analytics_sync_controller.dart +++ b/lib/pangea/analytics_data/analytics_sync_controller.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/pangea/analytics_data/analytics_data_service.dart'; +import 'package:fluffychat/pangea/analytics_data/analytics_update_dispatcher.dart'; import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart'; import 'package:fluffychat/pangea/analytics_misc/constructs_event.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; @@ -52,6 +53,13 @@ class AnalyticsSyncController { if (constructEvents.isEmpty) return; await dataService.updateServerAnalytics(constructEvents); + + // Server updates do not usually need to update the UI, since usually they are only + // transfering local data to the server. However, if a user if using multiple devices, + // we do need to update the UI when new data comes from the server. + dataService.updateDispatcher.sendConstructAnalyticsUpdate( + AnalyticsUpdate([]), + ); } Future waitForSync(String analyticsRoomId) async { diff --git a/lib/pangea/analytics_data/analytics_update_service.dart b/lib/pangea/analytics_data/analytics_update_service.dart index cdb832650..d2bfd164e 100644 --- a/lib/pangea/analytics_data/analytics_update_service.dart +++ b/lib/pangea/analytics_data/analytics_update_service.dart @@ -23,9 +23,19 @@ class AnalyticsUpdateService { final AnalyticsDataService dataService; - AnalyticsUpdateService(this.dataService); + AnalyticsUpdateService(this.dataService) { + _periodicTimer = Timer.periodic( + const Duration(minutes: 5), + (_) => sendLocalAnalyticsToAnalyticsRoom(), + ); + } Completer? _updateCompleter; + Timer? _periodicTimer; + + void dispose() { + _periodicTimer?.cancel(); + } LanguageModel? get _l2 => MatrixState.pangeaController.userController.userL2; @@ -50,8 +60,9 @@ class AnalyticsUpdateService { Future addAnalytics( String? targetID, - List newConstructs, - ) async { + List newConstructs, { + bool forceUpdate = false, + }) async { await dataService.updateDispatcher.sendConstructAnalyticsUpdate( AnalyticsUpdate( newConstructs, @@ -63,7 +74,9 @@ class AnalyticsUpdateService { final lastUpdated = await dataService.getLastUpdatedAnalytics(); final difference = DateTime.now().difference(lastUpdated ?? DateTime.now()); - if (localConstructCount > _maxMessagesCached || difference.inMinutes > 10) { + if (forceUpdate || + localConstructCount > _maxMessagesCached || + difference.inMinutes > 10) { sendLocalAnalyticsToAnalyticsRoom(); } } diff --git a/lib/pangea/vocab_practice/vocab_practice_page.dart b/lib/pangea/vocab_practice/vocab_practice_page.dart index fe379e494..4bfbfc111 100644 --- a/lib/pangea/vocab_practice/vocab_practice_page.dart +++ b/lib/pangea/vocab_practice/vocab_practice_page.dart @@ -235,7 +235,11 @@ class VocabPracticeState extends State with AnalyticsUpdater { setState(() {}); final bonus = _sessionLoader.value!.state.allBonusUses; - await _analyticsService.updateService.addAnalytics(null, bonus); + await _analyticsService.updateService.addAnalytics( + null, + bonus, + forceUpdate: true, + ); await _saveSession(); } diff --git a/lib/pangea/vocab_practice/vocab_practice_session_repo.dart b/lib/pangea/vocab_practice/vocab_practice_session_repo.dart index 2ce376e37..bf780dda8 100644 --- a/lib/pangea/vocab_practice/vocab_practice_session_repo.dart +++ b/lib/pangea/vocab_practice/vocab_practice_session_repo.dart @@ -73,11 +73,17 @@ class VocabPracticeSessionRepo { return dateA.compareTo(dateB); }); - return constructs - .where((construct) => construct.lemma.isNotEmpty) - .take(VocabPracticeConstants.practiceGroupSize) - .map((construct) => construct.id) - .toList(); + final Set seemLemmas = {}; + final targets = []; + for (final construct in constructs) { + if (seemLemmas.contains(construct.lemma)) continue; + seemLemmas.add(construct.lemma); + targets.add(construct.id); + if (targets.length >= VocabPracticeConstants.practiceGroupSize) { + break; + } + } + return targets; } static VocabPracticeSessionModel? _getCached() {