update UI on all analytics update types

This commit is contained in:
ggurdin 2026-02-11 16:04:13 -05:00
parent 811ba58c73
commit dfbc1ab0b9
No known key found for this signature in database
GPG key ID: A01CB41737CBB478
7 changed files with 138 additions and 38 deletions

View file

@ -156,7 +156,7 @@ class AnalyticsDataService {
} finally {
Logs().i("Analytics database initialized.");
initCompleter.complete();
updateDispatcher.sendLocalAnalyticsUpdate(AnalyticsUpdate([]));
updateDispatcher.sendEmptyAnalyticsUpdate();
updateDispatcher.sendActivityAnalyticsUpdate(null);
}
}
@ -408,7 +408,7 @@ class AnalyticsDataService {
final newConstructs = await getConstructUses(updateIds);
int points = 0;
if (update.blockedConstruct == null || updateIds.isNotEmpty) {
if (updateIds.isNotEmpty) {
for (final id in updateIds) {
final prevPoints = prevConstructs[id]?.points ?? 0;
final newPoints = newConstructs[id]?.points ?? 0;
@ -469,10 +469,6 @@ class AnalyticsDataService {
}
}
if (update.blockedConstruct != null) {
events.add(ConstructBlockedEvent(update.blockedConstruct!));
}
if (newUnusedConstructs.isNotEmpty) {
events.add(NewConstructsEvent(newUnusedConstructs));
}
@ -518,11 +514,7 @@ class AnalyticsDataService {
);
await _analyticsClientGetter.database.updateTotalXP(newXP);
_invalidateCaches();
updateDispatcher.sendLocalAnalyticsUpdate(
AnalyticsUpdate([], blockedConstruct: constructId),
);
}
Future<void> clearLocalAnalytics() async {

View file

@ -5,10 +5,34 @@ import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/analytics_data/analytics_data_service.dart';
import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_event.dart';
import 'package:fluffychat/pangea/analytics_settings/analytics_settings_model.dart';
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/lemmas/user_set_lemma_info.dart';
import 'package:fluffychat/widgets/matrix.dart';
enum _AnalyticsUpdateEvent {
constructAnalytics,
activityAnalytics,
lemmaInfo,
blockedConstruct;
String get eventType {
switch (this) {
case _AnalyticsUpdateEvent.constructAnalytics:
return PangeaEventTypes.construct;
case _AnalyticsUpdateEvent.activityAnalytics:
return PangeaEventTypes.activityRoomIds;
case _AnalyticsUpdateEvent.lemmaInfo:
return PangeaEventTypes.userSetLemmaInfo;
case _AnalyticsUpdateEvent.blockedConstruct:
return PangeaEventTypes.analyticsSettings;
}
}
}
class AnalyticsSyncController {
final Client client;
final AnalyticsDataService dataService;
@ -30,15 +54,43 @@ class AnalyticsSyncController {
final analyticsRoom = _getAnalyticsRoom();
if (analyticsRoom == null) return;
final events = update.rooms?.join?[analyticsRoom.id]?.timeline?.events
?.where(
(e) =>
e.type == PangeaEventTypes.construct &&
e.senderId == client.userID,
);
final roomUpdates = update.rooms?.join?[analyticsRoom.id]?.timeline?.events;
if (roomUpdates == null) return;
if (events == null || events.isEmpty) return;
for (final type in _AnalyticsUpdateEvent.values) {
await _dispatchSyncEvents(type, roomUpdates, analyticsRoom);
}
}
Future<void> _dispatchSyncEvents(
_AnalyticsUpdateEvent type,
List<MatrixEvent> events,
Room analyticsRoom,
) async {
final updates = events
.where((e) => e.type == type.eventType && e.senderId == client.userID)
.toList();
switch (type) {
case _AnalyticsUpdateEvent.constructAnalytics:
await _onConstructEvents(updates, analyticsRoom);
break;
case _AnalyticsUpdateEvent.activityAnalytics:
_onActivityEvents(updates);
break;
case _AnalyticsUpdateEvent.lemmaInfo:
_onLemmaInfoEvents(updates);
break;
case _AnalyticsUpdateEvent.blockedConstruct:
await _onBlockedConstructEvents(updates);
break;
}
}
Future<void> _onConstructEvents(
List<MatrixEvent> events,
Room analyticsRoom,
) async {
final constructEvents = events
.map(
(e) => ConstructAnalyticsEvent(
@ -54,6 +106,60 @@ class AnalyticsSyncController {
);
}
void _onActivityEvents(List<MatrixEvent> events) {
for (final event in events) {
if (event.content[ModelKey.roomIds] is! List) continue;
final roomIds = List<String>.from(
event.content[ModelKey.roomIds]! as List,
);
final prevContent =
event.unsigned?['prev_content'] as Map<String, Object?>?;
final prevRoomIds =
prevContent != null && prevContent[ModelKey.roomIds] is List
? List<String>.from(prevContent[ModelKey.roomIds] as List)
: [];
final newRoomIds = roomIds
.where((id) => !prevRoomIds.contains(id))
.toList();
for (final roomId in newRoomIds) {
dataService.updateDispatcher.sendActivityAnalyticsUpdate(roomId);
}
}
}
void _onLemmaInfoEvents(List<MatrixEvent> events) {
for (final event in events) {
if (event.stateKey == null) continue;
final cID = ConstructIdentifier.fromString(event.stateKey!);
if (cID == null) continue;
final update = UserSetLemmaInfo.fromJson(event.content);
dataService.updateDispatcher.sendLemmaInfoUpdate(cID, update);
}
}
Future<void> _onBlockedConstructEvents(List<MatrixEvent> events) async {
for (final event in events) {
final current = AnalyticsSettingsModel.fromJson(event.content);
final prevContent =
event.unsigned?['prev_content'] as Map<String, Object?>?;
final prev = prevContent != null
? AnalyticsSettingsModel.fromJson(prevContent)
: null;
final newBlocked = current.blockedConstructs;
final prevBlocked = prev?.blockedConstructs ?? {};
final newlyBlocked = newBlocked.where((c) => !prevBlocked.contains(c));
for (final constructId in newlyBlocked) {
await dataService.updateDispatcher.sendBlockedConstructUpdate(
constructId,
);
}
}
}
Future<void> waitForSync(String analyticsRoomId) async {
await client.onSync.stream.firstWhere((update) {
final roomUpdate = update.rooms?.join?[analyticsRoomId];

View file

@ -17,10 +17,9 @@ class LevelUpdate {
class AnalyticsUpdate {
final List<OneConstructUse> addedConstructs;
final ConstructIdentifier? blockedConstruct;
final String? targetID;
AnalyticsUpdate(this.addedConstructs, {this.blockedConstruct, this.targetID});
AnalyticsUpdate(this.addedConstructs, {this.targetID});
}
class ConstructLevelUpdate {
@ -86,6 +85,18 @@ class AnalyticsUpdateDispatcher {
UserSetLemmaInfo lemmaInfo,
) => _lemmaInfoUpdateStream.add(MapEntry(constructId, lemmaInfo));
Future<void> sendBlockedConstructUpdate(
ConstructIdentifier blockedConstruct,
) async {
await dataService.updateBlockedConstructs(blockedConstruct);
final update = AnalyticsStreamUpdate(blockedConstruct: blockedConstruct);
constructUpdateStream.add(update);
}
void sendEmptyAnalyticsUpdate() {
constructUpdateStream.add(AnalyticsStreamUpdate());
}
Future<void> sendServerAnalyticsUpdate(
List<ConstructAnalyticsEvent> events,
) async {
@ -113,9 +124,6 @@ class AnalyticsUpdateDispatcher {
case final XPGainedEvent e:
_onXPGained(e.points, e.targetID);
break;
case final ConstructBlockedEvent e:
_onBlockedConstruct(e.blockedConstruct);
break;
case final ConstructLevelUpEvent e:
_onConstructLevelUp(e.constructId, e.level, e.targetID);
break;
@ -163,11 +171,6 @@ class AnalyticsUpdateDispatcher {
);
}
void _onBlockedConstruct(ConstructIdentifier constructId) {
final update = AnalyticsStreamUpdate(blockedConstruct: constructId);
constructUpdateStream.add(update);
}
void _onNewConstruct(Set<ConstructIdentifier> constructIds) {
if (constructIds.isEmpty) return;
newConstructsStream.add(constructIds);

View file

@ -27,11 +27,6 @@ class XPGainedEvent extends AnalyticsUpdateEvent {
XPGainedEvent(this.points, this.targetID);
}
class ConstructBlockedEvent extends AnalyticsUpdateEvent {
final ConstructIdentifier blockedConstruct;
ConstructBlockedEvent(this.blockedConstruct);
}
class NewConstructsEvent extends AnalyticsUpdateEvent {
final Set<ConstructIdentifier> newConstructs;
NewConstructsEvent(this.newConstructs);

View file

@ -127,9 +127,6 @@ class AnalyticsUpdateService {
if (analyticsRoom == null) return;
await analyticsRoom.addActivityRoomId(roomId);
if (lang.langCodeShort == _l2?.langCodeShort) {
dataService.updateDispatcher.sendActivityAnalyticsUpdate(roomId);
}
}
Future<void> blockConstruct(ConstructIdentifier constructId) async {
@ -143,7 +140,6 @@ class AnalyticsUpdateService {
);
await analyticsRoom.setAnalyticsSettings(updated);
await dataService.updateBlockedConstructs(constructId);
}
Future<void> setLemmaInfo(
@ -160,7 +156,6 @@ class AnalyticsUpdateService {
meaning: meaning,
);
if (userLemmaInfo == updated) return;
dataService.updateDispatcher.sendLemmaInfoUpdate(constructId, updated);
try {
await analyticsRoom.setUserSetLemmaInfo(constructId, updated);

View file

@ -538,6 +538,13 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.3"
fcm_shared_isolate:
dependency: "direct overridden"
description:
path: "pangea_packages/fcm_shared_isolate"
relative: true
source: path
version: "0.2.0"
ffi:
dependency: transitive
description:

View file

@ -170,4 +170,6 @@ flutter:
# 1. Don't do it if you can avoid it or fix it upstream in a manageable time
# 2. Always link an (upstream?) issue
# 3. Explain how and when this can be removed (overrides must be temporarily)
dependency_overrides:
dependency_overrides:
fcm_shared_isolate:
path: pangea_packages/fcm_shared_isolate