chore: force update server analytics on complete practice, add timer to update server analytics every five minutes (#5206)

This commit is contained in:
ggurdin 2026-01-15 15:51:16 -05:00 committed by GitHub
parent 7fb8e44206
commit 9504b639fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 42 additions and 10 deletions

View file

@ -85,6 +85,7 @@ class AnalyticsDataService {
void dispose() {
_syncController?.dispose();
updateDispatcher.dispose();
updateService.dispose();
_closeDatabase();
}

View file

@ -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<void> waitForSync(String analyticsRoomId) async {

View file

@ -23,9 +23,19 @@ class AnalyticsUpdateService {
final AnalyticsDataService dataService;
AnalyticsUpdateService(this.dataService);
AnalyticsUpdateService(this.dataService) {
_periodicTimer = Timer.periodic(
const Duration(minutes: 5),
(_) => sendLocalAnalyticsToAnalyticsRoom(),
);
}
Completer<void>? _updateCompleter;
Timer? _periodicTimer;
void dispose() {
_periodicTimer?.cancel();
}
LanguageModel? get _l2 => MatrixState.pangeaController.userController.userL2;
@ -50,8 +60,9 @@ class AnalyticsUpdateService {
Future<void> addAnalytics(
String? targetID,
List<OneConstructUse> newConstructs,
) async {
List<OneConstructUse> 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();
}
}

View file

@ -235,7 +235,11 @@ class VocabPracticeState extends State<VocabPractice> 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();
}

View file

@ -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<String> seemLemmas = {};
final targets = <ConstructIdentifier>[];
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() {