import 'dart:async'; import 'dart:convert'; import 'dart:developer'; import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/constants/class_default_values.dart'; import 'package:fluffychat/pangea/constants/language_constants.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart'; import 'package:fluffychat/pangea/constants/pangea_room_types.dart'; import 'package:fluffychat/pangea/controllers/language_list_controller.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/analytics/analytics_event.dart'; import 'package:fluffychat/pangea/models/analytics/constructs_event.dart'; import 'package:fluffychat/pangea/models/analytics/constructs_model.dart'; import 'package:fluffychat/pangea/models/analytics/summary_analytics_event.dart'; import 'package:fluffychat/pangea/models/analytics/summary_analytics_model.dart'; import 'package:fluffychat/pangea/models/bot_options_model.dart'; import 'package:fluffychat/pangea/models/language_model.dart'; import 'package:fluffychat/pangea/models/space_model.dart'; import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/utils/bot_name.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; // import markdown.dart import 'package:html_unescape/html_unescape.dart'; import 'package:matrix/matrix.dart'; import 'package:matrix/src/utils/markdown.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import '../../../config/app_config.dart'; import '../../constants/pangea_event_types.dart'; import '../../models/choreo_record.dart'; import '../../models/representation_content_model.dart'; import '../client_extension/client_extension.dart'; part "children_and_parents_extension.dart"; part "events_extension.dart"; part "room_analytics_extension.dart"; part "room_information_extension.dart"; part "room_settings_extension.dart"; part "space_settings_extension.dart"; part "user_permissions_extension.dart"; extension PangeaRoom on Room { // analytics /// Join analytics rooms in space. /// Allows teachers to join analytics rooms without being invited. Future joinAnalyticsRoomsInSpace() async => await _joinAnalyticsRoomsInSpace(); Future addAnalyticsRoomToSpace(Room analyticsRoom) async => await _addAnalyticsRoomToSpace(analyticsRoom); /// Add analytics room to all spaces the user is a student in (1 analytics room to all spaces). /// Enables teachers to join student analytics rooms via space hierarchy. /// Will not always work, as there may be spaces where students don't have permission to add chats, /// but allows teachers to join analytics rooms without being invited. void addAnalyticsRoomToSpaces() => _addAnalyticsRoomToSpaces(); /// Add all the user's analytics rooms to 1 space. void addAnalyticsRoomsToSpace() => _addAnalyticsRoomsToSpace(); /// Invite teachers of 1 space to 1 analytics room Future inviteSpaceTeachersToAnalyticsRoom(Room analyticsRoom) async => await _inviteSpaceTeachersToAnalyticsRoom(analyticsRoom); /// Invite all the user's teachers to 1 analytics room. /// Handles case when students cannot add analytics room to space /// so teacher is still able to get analytics data for this student. void inviteTeachersToAnalyticsRoom() => _inviteTeachersToAnalyticsRoom(); /// Invite teachers of 1 space to all users' analytics rooms void inviteSpaceTeachersToAnalyticsRooms() => _inviteSpaceTeachersToAnalyticsRooms(); Future getLastAnalyticsEvent( String type, String userId, ) async => await _getLastAnalyticsEvent(type, userId); Future analyticsLastUpdated(String type, String userId) async { return await _analyticsLastUpdated(type, userId); } Future?> getAnalyticsEvents({ required String type, required String userId, DateTime? since, }) async => await _getAnalyticsEvents(type: type, since: since, userId: userId); String? get madeForLang => _madeForLang; bool isMadeForLang(String langCode) => _isMadeForLang(langCode); // children_and_parents List get joinedChildren => _joinedChildren; List get joinedChildrenRoomIds => _joinedChildrenRoomIds; Future> getChildRooms() async => await _getChildRooms(); Future joinSpaceChild(String roomID) async => await _joinSpaceChild(roomID); Room? firstParentWithState(String stateType) => _firstParentWithState(stateType); List get pangeaSpaceParents => _pangeaSpaceParents; String nameIncludingParents(BuildContext context) => _nameIncludingParents(context); List get allSpaceChildRoomIds => _allSpaceChildRoomIds; bool canAddAsParentOf(Room? child, {bool spaceMode = false}) { return _canAddAsParentOf(child, spaceMode: spaceMode); } Future pangeaSetSpaceChild( String roomId, { bool? suggested, }) async => await _pangeaSetSpaceChild(roomId, suggested: suggested); /// Returns a map of child suggestion status for a space. /// /// If the current object is not a space, an empty map is returned. /// Otherwise, it iterates through each child in the `spaceChildren` list /// and adds their suggestion status to the `suggestionStatus` map. /// The suggestion status is determined by the `suggested` property of each child. /// If the `suggested` property is `null`, it defaults to `true`. Map get spaceChildSuggestionStatus => _spaceChildSuggestionStatus; /// Checks if this space has a parent space bool get isSubspace => _isSubspace; // class_and_exchange_settings DateTime? get rulesUpdatedAt => _rulesUpdatedAt; String get classCode => _classCode; void checkClass() => _checkClass(); List get students => _students; Future> get teachers async => await _teachers; /// Synchronous version of teachers getter. Does not request /// participants, so this list may not be complete. List get teachersLocal => _teachersLocal; /// If the user is an admin of this space, and the space's /// m.space.child power level hasn't yet been set, so it to 0 Future setClassPowerLevels() async => await _setClassPowerLevels(); Event? get pangeaRoomRulesStateEvent => _pangeaRoomRulesStateEvent; Future> targetLanguages() async => await _targetLanguages(); // events Future leaveIfFull() async => await _leaveIfFull(); Future archive() async => await _archive(); Future archiveSpace( BuildContext context, Client client, { bool onlyAdmin = false, }) async => await _archiveSpace(context, client, onlyAdmin: onlyAdmin); Future archiveSubspace() async => await _archiveSubspace(); Future leaveSpace(BuildContext context, Client client) async => await _leaveSpace(context, client); Future leaveSubspace() async => await _leaveSubspace(); Future sendPangeaEvent({ required Map content, required String parentEventId, required String type, }) async => await _sendPangeaEvent( content: content, parentEventId: parentEventId, type: type, ); Future pangeaSendTextEvent( String message, { String? txid, Event? inReplyTo, String? editEventId, bool parseMarkdown = true, bool parseCommands = false, String msgtype = MessageTypes.Text, String? threadRootEventId, String? threadLastEventId, PangeaRepresentation? originalSent, PangeaRepresentation? originalWritten, PangeaMessageTokens? tokensSent, PangeaMessageTokens? tokensWritten, ChoreoRecord? choreo, }) => _pangeaSendTextEvent( message, txid: txid, inReplyTo: inReplyTo, editEventId: editEventId, parseMarkdown: parseMarkdown, parseCommands: parseCommands, msgtype: msgtype, threadRootEventId: threadRootEventId, threadLastEventId: threadLastEventId, originalSent: originalSent, originalWritten: originalWritten, tokensSent: tokensSent, tokensWritten: tokensWritten, choreo: choreo, ); Future updateStateEvent(Event stateEvent) => _updateStateEvent(stateEvent); // room_information Future get numNonAdmins async => await _numNonAdmins; DateTime? get creationTime => _creationTime; String? get creatorId => _creatorId; String get domainString => _domainString; bool isChild(String roomId) => _isChild(roomId); bool isFirstOrSecondChild(String roomId) => _isFirstOrSecondChild(roomId); bool get isDirectChatWithoutMe => _isDirectChatWithoutMe; // bool isMadeForLang(String langCode) => _isMadeForLang(langCode); Future get isBotRoom async => await _isBotRoom; Future get isBotDM async => await _isBotDM; bool get isLocked => _isLocked; bool isAnalyticsRoomOfUser(String userId) => _isAnalyticsRoomOfUser(userId); bool get isAnalyticsRoom => _isAnalyticsRoom; // room_settings Future updateRoomCapacity(int newCapacity) => _updateRoomCapacity(newCapacity); int? get capacity => _capacity; PangeaRoomRules? get pangeaRoomRules => _pangeaRoomRules; PangeaRoomRules? get firstRules => _firstRules; IconData? get roomTypeIcon => _roomTypeIcon; Text nameAndRoomTypeIcon([TextStyle? textStyle]) => _nameAndRoomTypeIcon(textStyle); BotOptionsModel? get botOptions => _botOptions; Future setSuggested(bool suggested) async => await _setSuggested(suggested); Future isSuggested() async => await _isSuggested(); // user_permissions Future isOnlyAdmin() async => await _isOnlyAdmin(); bool isMadeByUser(String userId) => _isMadeByUser(userId); bool get isSpaceAdmin => _isSpaceAdmin; bool isUserRoomAdmin(String userId) => _isUserRoomAdmin(userId); bool isUserSpaceAdmin(String userId) => _isUserSpaceAdmin(userId); bool get isRoomOwner => _isRoomOwner; bool get isRoomAdmin => _isRoomAdmin; bool get showClassEditOptions => _showClassEditOptions; bool get canDelete => _canDelete; bool get canIAddSpaceParents => _canIAddSpaceParents; bool pangeaCanSendEvent(String eventType) => _pangeaCanSendEvent(eventType); int? get eventsDefaultPowerLevel => _eventsDefaultPowerLevel; }