feat: add local cache for activity session analytics (#3878)
This commit is contained in:
parent
85ac0317b8
commit
6bf699c190
5 changed files with 113 additions and 4 deletions
|
|
@ -8,6 +8,7 @@ import 'package:matrix/matrix.dart';
|
||||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.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_role_model.dart';
|
||||||
import 'package:fluffychat/pangea/activity_sessions/activity_roles_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_analytics_model.dart';
|
||||||
import 'package:fluffychat/pangea/activity_summary/activity_summary_model.dart';
|
import 'package:fluffychat/pangea/activity_summary/activity_summary_model.dart';
|
||||||
import 'package:fluffychat/pangea/activity_summary/activity_summary_request_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/constants/pangea_event_types.dart';
|
||||||
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
|
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
|
||||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||||
|
import 'package:fluffychat/widgets/matrix.dart';
|
||||||
import '../activity_summary/activity_summary_repo.dart';
|
import '../activity_summary/activity_summary_repo.dart';
|
||||||
|
|
||||||
extension ActivityRoomExtension on Room {
|
extension ActivityRoomExtension on Room {
|
||||||
|
|
@ -326,4 +328,41 @@ extension ActivityRoomExtension on Room {
|
||||||
roomType?.startsWith(PangeaRoomTypes.activitySession) == true;
|
roomType?.startsWith(PangeaRoomTypes.activitySession) == true;
|
||||||
|
|
||||||
bool get isActivitySession => isActivityRoomType || activityPlan != null;
|
bool get isActivitySession => isActivityRoomType || activityPlan != null;
|
||||||
|
|
||||||
|
Future<ActivitySummaryAnalyticsModel> 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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<String, dynamic>;
|
||||||
|
final analytics = ActivitySummaryAnalyticsModel.fromJson(analyticsJson);
|
||||||
|
return CachedActivityAnalytics(timestamp, lastEventId, analytics);
|
||||||
|
} catch (e) {
|
||||||
|
_activityAnalyticsStorage.remove(roomId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -76,6 +76,7 @@ class GetAnalyticsController extends BaseController {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await GetStorage.init("analytics_storage");
|
await GetStorage.init("analytics_storage");
|
||||||
|
await GetStorage.init("activity_analytics_storage");
|
||||||
_client.updateAnalyticsRoomJoinRules();
|
_client.updateAnalyticsRoomJoinRules();
|
||||||
_client.addAnalyticsRoomsToSpaces();
|
_client.addAnalyticsRoomsToSpaces();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,7 @@ class PangeaController {
|
||||||
'vocab_storage',
|
'vocab_storage',
|
||||||
'onboarding_storage',
|
'onboarding_storage',
|
||||||
'analytics_request_storage',
|
'analytics_request_storage',
|
||||||
|
'activity_analytics_storage',
|
||||||
];
|
];
|
||||||
|
|
||||||
Future<void> clearCache({List<String> exclude = const []}) async {
|
Future<void> clearCache({List<String> exclude = const []}) async {
|
||||||
|
|
|
||||||
|
|
@ -322,7 +322,7 @@ extension EventsRoomExtension on Room {
|
||||||
return resp.chunk.map((e) => Event.fromMatrixEvent(e, this)).toList();
|
return resp.chunk.map((e) => Event.fromMatrixEvent(e, this)).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Event>> getAllEvents() async {
|
Future<List<Event>> getAllEvents({String? since}) async {
|
||||||
final GetRoomEventsResponse initalResp =
|
final GetRoomEventsResponse initalResp =
|
||||||
await client.getRoomEvents(id, Direction.b);
|
await client.getRoomEvents(id, Direction.b);
|
||||||
|
|
||||||
|
|
@ -340,9 +340,20 @@ extension EventsRoomExtension on Room {
|
||||||
resp.end != nextStartToken
|
resp.end != nextStartToken
|
||||||
? nextStartToken = resp.end
|
? nextStartToken = resp.end
|
||||||
: nextStartToken = null;
|
: nextStartToken = null;
|
||||||
|
|
||||||
|
if (since != null && chunkMessages.any((e) => e.eventId == since)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allMatrixEvents = allMatrixEvents.reversed.toList();
|
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<Event> allEvents = allMatrixEvents
|
final List<Event> allEvents = allMatrixEvents
|
||||||
.map((MatrixEvent message) => Event.fromMatrixEvent(message, this))
|
.map((MatrixEvent message) => Event.fromMatrixEvent(message, this))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
@ -352,13 +363,14 @@ extension EventsRoomExtension on Room {
|
||||||
|
|
||||||
List<PangeaMessageEvent> getPangeaMessageEvents(
|
List<PangeaMessageEvent> getPangeaMessageEvents(
|
||||||
List<Event> events,
|
List<Event> events,
|
||||||
Timeline timeline,
|
Timeline timeline, {
|
||||||
) {
|
List<String> msgtypes = const [MessageTypes.Text],
|
||||||
|
}) {
|
||||||
final List<PangeaMessageEvent> allPangeaMessages = events
|
final List<PangeaMessageEvent> allPangeaMessages = events
|
||||||
.where(
|
.where(
|
||||||
(Event event) =>
|
(Event event) =>
|
||||||
event.type == EventTypes.Message &&
|
event.type == EventTypes.Message &&
|
||||||
event.content['msgtype'] == MessageTypes.Text,
|
msgtypes.contains(event.content['msgtype']),
|
||||||
)
|
)
|
||||||
.map(
|
.map(
|
||||||
(Event message) => PangeaMessageEvent(
|
(Event message) => PangeaMessageEvent(
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue