diff --git a/lib/pangea/activity_sessions/activity_room_extension.dart b/lib/pangea/activity_sessions/activity_room_extension.dart index f79603ee8..61a08ed35 100644 --- a/lib/pangea/activity_sessions/activity_room_extension.dart +++ b/lib/pangea/activity_sessions/activity_room_extension.dart @@ -8,6 +8,7 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_role_model.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_roles_model.dart'; +import 'package:fluffychat/pangea/activity_sessions/activity_session_analytics_repo.dart'; import 'package:fluffychat/pangea/activity_summary/activity_summary_analytics_model.dart'; import 'package:fluffychat/pangea/activity_summary/activity_summary_model.dart'; import 'package:fluffychat/pangea/activity_summary/activity_summary_request_model.dart'; @@ -19,6 +20,7 @@ import 'package:fluffychat/pangea/course_plans/course_plan_room_extension.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; +import 'package:fluffychat/widgets/matrix.dart'; import '../activity_summary/activity_summary_repo.dart'; extension ActivityRoomExtension on Room { @@ -326,4 +328,41 @@ extension ActivityRoomExtension on Room { roomType?.startsWith(PangeaRoomTypes.activitySession) == true; bool get isActivitySession => isActivityRoomType || activityPlan != null; + + Future getActivityAnalytics() async { + // wait for local storage box to init in getAnalytics initialization + if (!MatrixState.pangeaController.getAnalytics.initCompleter.isCompleted) { + await MatrixState.pangeaController.getAnalytics.initCompleter.future; + } + + final cached = ActivitySessionAnalyticsRepo.get(id); + final analytics = cached?.analytics ?? ActivitySummaryAnalyticsModel(); + + final eventsSince = await getAllEvents(since: cached?.lastEventId); + final timeline = this.timeline ?? await getTimeline(); + final messageEvents = getPangeaMessageEvents( + eventsSince, + timeline, + msgtypes: [ + MessageTypes.Text, + MessageTypes.Audio, + ], + ); + + if (messageEvents.isEmpty) { + return analytics; + } + + for (final pangeaMessage in messageEvents) { + analytics.addConstructs(pangeaMessage); + } + + await ActivitySessionAnalyticsRepo.set( + id, + messageEvents.last.eventId, + analytics, + ); + + return analytics; + } } diff --git a/lib/pangea/activity_sessions/activity_session_analytics_repo.dart b/lib/pangea/activity_sessions/activity_session_analytics_repo.dart new file mode 100644 index 000000000..fbb61b8df --- /dev/null +++ b/lib/pangea/activity_sessions/activity_session_analytics_repo.dart @@ -0,0 +1,56 @@ +import 'package:get_storage/get_storage.dart'; + +import 'package:fluffychat/pangea/activity_summary/activity_summary_analytics_model.dart'; + +class CachedActivityAnalytics { + final DateTime timestamp; + final String lastEventId; + final ActivitySummaryAnalyticsModel analytics; + + CachedActivityAnalytics( + this.timestamp, + this.lastEventId, + this.analytics, + ); +} + +class ActivitySessionAnalyticsRepo { + static final GetStorage _activityAnalyticsStorage = + GetStorage('activity_analytics_storage'); + + static Duration cacheDuration = const Duration(minutes: 30); + + static CachedActivityAnalytics? get(String roomId) { + final json = _activityAnalyticsStorage.read(roomId); + if (json == null) return null; + + try { + final timestamp = DateTime.parse(json['timestamp'] as String); + if (DateTime.now().difference(timestamp) > cacheDuration) { + _activityAnalyticsStorage.remove(roomId); + return null; + } + + final lastEventId = json['last_event_id'] as String; + final analyticsJson = json['analytics'] as Map; + final analytics = ActivitySummaryAnalyticsModel.fromJson(analyticsJson); + return CachedActivityAnalytics(timestamp, lastEventId, analytics); + } catch (e) { + _activityAnalyticsStorage.remove(roomId); + return null; + } + } + + static Future set( + String roomId, + String lastEventId, + ActivitySummaryAnalyticsModel analytics, + ) async { + final json = { + 'timestamp': DateTime.now().toIso8601String(), + 'last_event_id': lastEventId, + 'analytics': analytics.toJson(), + }; + await _activityAnalyticsStorage.write(roomId, json); + } +} diff --git a/lib/pangea/analytics_misc/get_analytics_controller.dart b/lib/pangea/analytics_misc/get_analytics_controller.dart index 04bab9704..f8963c88b 100644 --- a/lib/pangea/analytics_misc/get_analytics_controller.dart +++ b/lib/pangea/analytics_misc/get_analytics_controller.dart @@ -76,6 +76,7 @@ class GetAnalyticsController extends BaseController { try { await GetStorage.init("analytics_storage"); + await GetStorage.init("activity_analytics_storage"); _client.updateAnalyticsRoomJoinRules(); _client.addAnalyticsRoomsToSpaces(); diff --git a/lib/pangea/common/controllers/pangea_controller.dart b/lib/pangea/common/controllers/pangea_controller.dart index ddb5a9dc5..7382e12bb 100644 --- a/lib/pangea/common/controllers/pangea_controller.dart +++ b/lib/pangea/common/controllers/pangea_controller.dart @@ -129,6 +129,7 @@ class PangeaController { 'vocab_storage', 'onboarding_storage', 'analytics_request_storage', + 'activity_analytics_storage', ]; Future clearCache({List exclude = const []}) async { diff --git a/lib/pangea/extensions/room_events_extension.dart b/lib/pangea/extensions/room_events_extension.dart index 820a2ecd3..9de8a4c40 100644 --- a/lib/pangea/extensions/room_events_extension.dart +++ b/lib/pangea/extensions/room_events_extension.dart @@ -322,7 +322,7 @@ extension EventsRoomExtension on Room { return resp.chunk.map((e) => Event.fromMatrixEvent(e, this)).toList(); } - Future> getAllEvents() async { + Future> getAllEvents({String? since}) async { final GetRoomEventsResponse initalResp = await client.getRoomEvents(id, Direction.b); @@ -340,9 +340,20 @@ extension EventsRoomExtension on Room { resp.end != nextStartToken ? nextStartToken = resp.end : nextStartToken = null; + + if (since != null && chunkMessages.any((e) => e.eventId == since)) { + break; + } } allMatrixEvents = allMatrixEvents.reversed.toList(); + if (since != null) { + final index = allMatrixEvents.indexWhere((e) => e.eventId == since); + if (index != -1) { + allMatrixEvents = allMatrixEvents.sublist(index + 1); + } + } + final List allEvents = allMatrixEvents .map((MatrixEvent message) => Event.fromMatrixEvent(message, this)) .toList(); @@ -352,13 +363,14 @@ extension EventsRoomExtension on Room { List getPangeaMessageEvents( List events, - Timeline timeline, - ) { + Timeline timeline, { + List msgtypes = const [MessageTypes.Text], + }) { final List allPangeaMessages = events .where( (Event event) => event.type == EventTypes.Message && - event.content['msgtype'] == MessageTypes.Text, + msgtypes.contains(event.content['msgtype']), ) .map( (Event message) => PangeaMessageEvent(