diff --git a/lib/pangea/controllers/message_analytics_controller.dart b/lib/pangea/controllers/message_analytics_controller.dart index 43b119087..092d01c2a 100644 --- a/lib/pangea/controllers/message_analytics_controller.dart +++ b/lib/pangea/controllers/message_analytics_controller.dart @@ -129,6 +129,15 @@ class AnalyticsController extends BaseController { return null; } + // Map of space ids to the last fetched hierarchy. Used when filtering + // private chat analytics to determine which children are already visible + // in the chat list + final Map> _lastFetchedHierarchies = {}; + void setLatestHierarchy(String spaceId, GetSpaceHierarchyResponse resp) { + final List roomIds = resp.rooms.map((room) => room.roomId).toList(); + _lastFetchedHierarchies[spaceId] = roomIds; + } + //////////////////////////// MESSAGE SUMMARY ANALYTICS //////////////////////////// Future> mySummaryAnalytics() async { @@ -188,10 +197,7 @@ class AnalyticsController extends BaseController { } } - // get a list of all the space's children, including sub-space children - final resp = await space.client.getSpaceHierarchy(space.id); - final List spaceChildrenIds = - resp.rooms.map((room) => room.roomId).toList(); + final List spaceChildrenIds = space.allSpaceChildRoomIds; // filter out the analyics events that don't belong to the space's children final List allAnalyticsEvents = []; @@ -288,22 +294,32 @@ class AnalyticsController extends BaseController { return filtered; } - List filterPrivateChatAnalytics( + Future> filterPrivateChatAnalytics( List unfiltered, Room? space, - ) { - final List directChatIds = - space?.childrenAndGrandChildrenDirectChatIds ?? []; + ) async { + if (space != null && !_lastFetchedHierarchies.containsKey(space.id)) { + final resp = await _pangeaController.matrixState.client + .getSpaceHierarchy(space.id); + setLatestHierarchy(space.id, resp); + } + + final List privateChatIds = space?.allSpaceChildRoomIds ?? []; + final List lastFetched = _lastFetchedHierarchies[space!.id] ?? []; + for (final id in lastFetched) { + privateChatIds.removeWhere((e) => e == id); + } + List filtered = List.from(unfiltered); filtered = filtered.where((e) { return (e.content).messages.any( - (u) => directChatIds.contains(u.chatId), + (u) => privateChatIds.contains(u.chatId), ); }).toList(); filtered.forEachIndexed( (i, _) => (filtered[i].content).messages.removeWhere( - (u) => !directChatIds.contains(u.chatId), + (u) => !privateChatIds.contains(u.chatId), ), ); return filtered; @@ -369,7 +385,10 @@ class AnalyticsController extends BaseController { if (defaultSelected.type == AnalyticsEntryType.student) { throw "private chat filtering not available for my analytics"; } - return filterPrivateChatAnalytics(unfilteredAnalytics, space); + return await filterPrivateChatAnalytics( + unfilteredAnalytics, + space, + ); case AnalyticsEntryType.space: return filterSpaceAnalytics(unfilteredAnalytics, selected!.id); default: @@ -573,10 +592,7 @@ class AnalyticsController extends BaseController { } } - final resp = await space.client.getSpaceHierarchy(space.id); - final List spaceChildrenIds = - resp.rooms.map((room) => room.roomId).toList(); - + final List spaceChildrenIds = space.allSpaceChildRoomIds; final List allConstructs = []; for (final constructEvent in constructEvents) { final lemmaUses = constructEvent.content.uses; @@ -622,8 +638,7 @@ class AnalyticsController extends BaseController { List unfilteredConstructs, Room parentSpace, ) { - final List directChatIds = - parentSpace.childrenAndGrandChildrenDirectChatIds; + final List directChatIds = []; final List filtered = List.from(unfilteredConstructs); for (final construct in filtered) { diff --git a/lib/pangea/extensions/client_extension/general_info_extension.dart b/lib/pangea/extensions/client_extension/general_info_extension.dart index af9700cf6..058b6f695 100644 --- a/lib/pangea/extensions/client_extension/general_info_extension.dart +++ b/lib/pangea/extensions/client_extension/general_info_extension.dart @@ -5,11 +5,7 @@ extension GeneralInfoClientExtension on Client { final List adminRoomIds = []; for (final Room adminSpace in (await _classesAndExchangesImTeaching)) { adminRoomIds.add(adminSpace.id); - final children = adminSpace.childrenAndGrandChildren; - final List adminSpaceRooms = children - .where((e) => e.roomId != null) - .map((e) => e.roomId!) - .toList(); + final List adminSpaceRooms = adminSpace.allSpaceChildRoomIds; adminRoomIds.addAll(adminSpaceRooms); } return adminRoomIds; diff --git a/lib/pangea/extensions/pangea_room_extension/children_and_parents_extension.dart b/lib/pangea/extensions/pangea_room_extension/children_and_parents_extension.dart index 4362c17d8..79b923683 100644 --- a/lib/pangea/extensions/pangea_room_extension/children_and_parents_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/children_and_parents_extension.dart @@ -20,57 +20,6 @@ extension ChildrenAndParentsRoomExtension on Room { List get _joinedChildrenRoomIds => joinedChildren.map((child) => child.id).toList(); - List get _childrenAndGrandChildren { - if (!isSpace) return []; - final List kids = []; - for (final child in spaceChildren) { - kids.add(child); - if (child.roomId != null) { - final Room? childRoom = client.getRoomById(child.roomId!); - if (childRoom != null && childRoom.isSpace) { - kids.addAll(childRoom.spaceChildren); - } - } - } - return kids.where((element) => element.roomId != null).toList(); - } - - //this assumes that a user has been invited to all group chats in a space - //it is a janky workaround for determining whether a spacechild is a direct chat - //since the spaceChild object doesn't contain this info. this info is only accessible - //when the user has joined or been invited to the room. direct chats included in - //a space show up in spaceChildren but the user has not been invited to them. - List get _childrenAndGrandChildrenDirectChatIds { - final List nonDirectChatRoomIds = childrenAndGrandChildren - .where((child) => child.roomId != null) - .map((e) => client.getRoomById(e.roomId!)) - .where((r) => r != null && !r.isDirectChat) - .map((e) => e!.id) - .toList(); - - return childrenAndGrandChildren - .where( - (child) => - child.roomId != null && - !nonDirectChatRoomIds.contains(child.roomId), - ) - .map((e) => e.roomId) - .cast() - .toList(); - - // return childrenAndGrandChildren - // .where((element) => element.roomId != null) - // .where( - // (child) { - // final room = client.getRoomById(child.roomId!); - // return room == null || room.isDirectChat; - // }, - // ) - // .map((e) => e.roomId) - // .cast() - // .toList(); - } - Future> _getChildRooms() async { final List children = []; for (final child in spaceChildren) { @@ -145,4 +94,19 @@ extension ChildrenAndParentsRoomExtension on Room { ), ) .toList(); + + // gets all space children of a given space, down the + // space tree. + List get _allSpaceChildRoomIds { + final List childIds = []; + for (final child in spaceChildren) { + if (child.roomId == null) continue; + childIds.add(child.roomId!); + final Room? room = client.getRoomById(child.roomId!); + if (room != null && room.isSpace) { + childIds.addAll(room._allSpaceChildRoomIds); + } + } + return childIds; + } } diff --git a/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart b/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart index e3efc7eca..bbadd6703 100644 --- a/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart @@ -25,7 +25,6 @@ import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:html_unescape/html_unescape.dart'; import 'package:matrix/matrix.dart'; import 'package:matrix/src/utils/markdown.dart'; -import 'package:matrix/src/utils/space_child.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import '../../../config/app_config.dart'; @@ -98,11 +97,6 @@ extension PangeaRoom on Room { List get joinedChildrenRoomIds => _joinedChildrenRoomIds; - List get childrenAndGrandChildren => _childrenAndGrandChildren; - - List get childrenAndGrandChildrenDirectChatIds => - _childrenAndGrandChildrenDirectChatIds; - Future> getChildRooms() async => await _getChildRooms(); Future joinSpaceChild(String roomID) async => @@ -115,6 +109,8 @@ extension PangeaRoom on Room { List get pangeaSpaceParents => _pangeaSpaceParents; + List get allSpaceChildRoomIds => _allSpaceChildRoomIds; + // class_and_exchange_settings DateTime? get rulesUpdatedAt => _rulesUpdatedAt; diff --git a/lib/pangea/pages/analytics/base_analytics.dart b/lib/pangea/pages/analytics/base_analytics.dart index fde016ee2..fb5295dfe 100644 --- a/lib/pangea/pages/analytics/base_analytics.dart +++ b/lib/pangea/pages/analytics/base_analytics.dart @@ -94,15 +94,18 @@ class BaseAnalyticsController extends State { } Future onRefresh() async { - await showFutureLoadingDialog( - context: context, - future: () async { - debugPrint("updating analytics"); - await pangeaController.myAnalytics.updateAnalytics(); - await setChartData(forceUpdate: true); - refreshStream.add(true); - }, - ); + // postframe callback to avoid calling this function during build + WidgetsBinding.instance.addPostFrameCallback((_) async { + await showFutureLoadingDialog( + context: context, + future: () async { + debugPrint("updating analytics"); + await pangeaController.myAnalytics.updateAnalytics(); + await setChartData(forceUpdate: true); + refreshStream.add(true); + }, + ); + }); } Future fetchChartData( diff --git a/lib/pangea/pages/analytics/class_analytics/class_analytics.dart b/lib/pangea/pages/analytics/class_analytics/class_analytics.dart index f271ba96d..7219e7113 100644 --- a/lib/pangea/pages/analytics/class_analytics/class_analytics.dart +++ b/lib/pangea/pages/analytics/class_analytics/class_analytics.dart @@ -70,6 +70,13 @@ class ClassAnalyticsV2Controller extends State { final response = await Matrix.of(context).client.getSpaceHierarchy( classRoom!.id, ); + + // set the latest fetched full hierarchy in message analytics controller + // we want to avoid calling this endpoint again and again, so whenever the + // data is made available, set it in the controller + MatrixState.pangeaController.analytics + .setLatestHierarchy(_classRoom!.id, response); + students = classRoom!.students; chats = response.rooms .where(