diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index a7fb07449..5fbbb2f83 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -3948,6 +3948,15 @@ "roomDataMissing": "Some data may be missing from rooms in which you are not a member.", "updatePhoneOS": "You may need to update your device's OS version.", "wordsPerMinute": "Words per minute", + "roomCapacity": "Room Capacity", + "roomFull": "This room is already at capacity.", + "topicNotSet": "The topic has not been set.", + "capacityNotSet": "This room has no capacity limit.", + "roomCapacityHasBeenChanged": "Room capacity changed", + "roomExceedsCapacity": "Room exceeds capacity. Consider removing students from the room, or raising the capacity.", + "capacitySetTooLow": "Room capacity cannot be set below the current number of non-admins.", + "roomCapacityExplanation": "Room capacity limits the number of non-admins allowed in a room.", + "enterNumber": "Please enter a whole number value.", "autoIGCToolName": "Run Language Assistance Automatically", "autoIGCToolDescription": "Automatically run language assistance after typing messages", "runGrammarCorrection": "Run grammar correction", diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index e9724d606..69afab2aa 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -67,7 +67,10 @@ class ChatPage extends StatelessWidget { @override Widget build(BuildContext context) { final room = Matrix.of(context).client.getRoomById(roomId); - if (room == null) { + // #Pangea + if (room == null || room.membership == Membership.leave) { + // if (room == null) { + // Pangea# return Scaffold( appBar: AppBar(title: Text(L10n.of(context)!.oopsSomethingWentWrong)), body: Center( diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 317d81158..ff6da5099 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -21,6 +21,7 @@ import 'package:fluffychat/widgets/unread_rooms_badge.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; +import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; import '../../utils/stream_extension.dart'; @@ -152,6 +153,11 @@ class ChatView extends StatelessWidget { context: context, future: () => controller.room.join(), ); + // #Pangea + controller.room.leaveIfFull().then( + (full) => full ? context.go('/rooms') : null, + ); + // Pangea# } final bottomSheetPadding = FluffyThemes.isColumnMode(context) ? 16.0 : 8.0; final scrollUpBannerEventId = controller.scrollUpBannerEventId; diff --git a/lib/pages/chat_details/chat_details.dart b/lib/pages/chat_details/chat_details.dart index 07a12876e..e64b01bf0 100644 --- a/lib/pages/chat_details/chat_details.dart +++ b/lib/pages/chat_details/chat_details.dart @@ -3,8 +3,8 @@ import 'package:collection/collection.dart'; import 'package:file_picker/file_picker.dart'; import 'package:fluffychat/pages/chat_details/chat_details_view.dart'; import 'package:fluffychat/pages/settings/settings.dart'; +import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/class_description_button.dart'; import 'package:fluffychat/pangea/utils/set_class_name.dart'; -import 'package:fluffychat/pangea/utils/set_class_topic.dart'; import 'package:fluffychat/pangea/widgets/class/add_space_toggles.dart'; import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_settings.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; diff --git a/lib/pages/chat_details/chat_details_view.dart b/lib/pages/chat_details/chat_details_view.dart index 57e2a43e8..3159af329 100644 --- a/lib/pages/chat_details/chat_details_view.dart +++ b/lib/pages/chat_details/chat_details_view.dart @@ -8,6 +8,7 @@ import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/class_des import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/class_details_toggle_add_students_tile.dart'; import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/class_invitation_buttons.dart'; import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/class_name_button.dart'; +import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_capacity_button.dart'; import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_rules_editor.dart'; import 'package:fluffychat/pangea/utils/lock_room.dart'; import 'package:fluffychat/pangea/widgets/class/add_class_and_invite.dart'; @@ -34,7 +35,10 @@ class ChatDetailsView extends StatelessWidget { @override Widget build(BuildContext context) { final room = Matrix.of(context).client.getRoomById(controller.roomId!); - if (room == null) { + // #Pangea + if (room == null || room.membership == Membership.leave) { + // if (room == null) { + // Pangea# return Scaffold( appBar: AppBar( title: Text(L10n.of(context)!.oopsSomethingWentWrong), @@ -236,8 +240,9 @@ class ChatDetailsView extends StatelessWidget { height: 1, color: Theme.of(context).dividerColor, ), - // #Pangea - if (room.canSendEvent('m.room.name')) + // if (room.canSendEvent('m.room.name')) + if (room.isRoomAdmin) + // #Pangea ClassNameButton( room: room, controller: controller, @@ -247,6 +252,12 @@ class ChatDetailsView extends StatelessWidget { room: room, controller: controller, ), + // #Pangea + RoomCapacityButton( + room: room, + controller: controller, + ), + // Pangea# if ((room.isPangeaClass || room.isExchange) && room.isRoomAdmin) ListTile( @@ -435,7 +446,9 @@ class ChatDetailsView extends StatelessWidget { // : null, // ), // if (!room.isDirectChat) - if (!room.isDirectChat && !room.isSpace) + if (!room.isDirectChat && + !room.isSpace && + room.isRoomAdmin) // Pangea# ListTile( // #Pangea @@ -510,7 +523,9 @@ class ChatDetailsView extends StatelessWidget { room: room, ), const Divider(height: 1), - if (!room.isPangeaClass && !room.isDirectChat) + if (!room.isPangeaClass && + !room.isDirectChat && + room.isRoomAdmin) AddToSpaceToggles( roomId: room.id, key: controller.addToSpaceKey, diff --git a/lib/pages/chat_list/space_view.dart b/lib/pages/chat_list/space_view.dart index 0757354c5..2cdfed64a 100644 --- a/lib/pages/chat_list/space_view.dart +++ b/lib/pages/chat_list/space_view.dart @@ -179,6 +179,12 @@ class _SpaceViewState extends State { // Wait for room actually appears in sync await client.waitForRoomInSync(spaceChild.roomId, join: true); } + // #Pangea + final room = client.getRoomById(spaceChild.roomId); + if (room != null && (await room.leaveIfFull())) { + throw L10n.of(context)!.roomFull; + } + // Pangea# }, ); if (result.error != null) return; @@ -197,6 +203,9 @@ class _SpaceViewState extends State { ); await room.join(); await waitForRoom; + if (await room.leaveIfFull()) { + throw L10n.of(context)!.roomFull; + } }, ); if (joinResult.error != null) return; @@ -271,9 +280,8 @@ class _SpaceViewState extends State { icon: Icons.architecture_outlined, isDestructiveAction: true, ), - + // if (room != null) if (room != null && room.membership != Membership.leave) - // if (room != null) // Pangea# SheetAction( key: SpaceChildContextAction.leave, @@ -329,34 +337,14 @@ class _SpaceViewState extends State { case SpaceChildContextAction.archive: widget.controller.cancelAction(); // #Pangea - if (room == null) return; - // room.isSpace - // ? await showFutureLoadingDialog( - // context: context, - // future: () async { - // await room.archiveSpace( - // Matrix.of(context).client, - // ); - // widget.controller.selectedRoomIds.clear(); - // }, - // ) - // : await widget.controller.archiveAction(); - if (room.isSpace) { - await room.archiveSpace( - context, - Matrix.of(context).client, - ); - } else { - widget.controller.toggleSelection(room.id); - await widget.controller.archiveAction(); - } + if (room == null || room.membership == Membership.leave) return; // Pangea# _refresh(); break; case SpaceChildContextAction.addToSpace: widget.controller.cancelAction(); // #Pangea - if (room == null) return; + if (room == null || room.membership == Membership.leave) return; // Pangea# widget.controller.toggleSelection(room.id); await widget.controller.addToSpace(); @@ -614,7 +602,7 @@ class _SpaceViewState extends State { MatrixLocals(L10n.of(context)!), ); return Material( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: ListTile( leading: Avatar( mxContent: rootSpace.avatar, @@ -949,7 +937,7 @@ class _SpaceViewState extends State { : L10n.of(context)!.enterRoom), maxLines: 1, style: TextStyle( - color: Theme.of(context).colorScheme.onBackground, + color: Theme.of(context).colorScheme.onSurface, ), ), trailing: isSpace diff --git a/lib/pages/chat_list/utils/on_chat_tap.dart b/lib/pages/chat_list/utils/on_chat_tap.dart index b272d424e..f7daa248e 100644 --- a/lib/pages/chat_list/utils/on_chat_tap.dart +++ b/lib/pages/chat_list/utils/on_chat_tap.dart @@ -1,5 +1,6 @@ import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:fluffychat/pages/chat/send_file_dialog.dart'; +import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; @@ -65,6 +66,9 @@ void onChatTap(Room room, BuildContext context) async { room.id, join: true, ); + if (await room.leaveIfFull()) { + throw L10n.of(context)!.roomFull; + } await room.join(); await waitForRoom; }, diff --git a/lib/pages/new_group/new_group.dart b/lib/pages/new_group/new_group.dart index 2aec182fb..b3a1703af 100644 --- a/lib/pages/new_group/new_group.dart +++ b/lib/pages/new_group/new_group.dart @@ -3,8 +3,10 @@ import 'dart:typed_data'; import 'package:file_picker/file_picker.dart'; import 'package:fluffychat/pages/new_group/new_group_view.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; +import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/pangea/models/chat_topic_model.dart'; import 'package:fluffychat/pangea/models/lemma.dart'; +import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_capacity_button.dart'; import 'package:fluffychat/pangea/utils/bot_name.dart'; import 'package:fluffychat/pangea/utils/class_chat_power_levels.dart'; import 'package:fluffychat/pangea/utils/firebase_analytics.dart'; @@ -51,6 +53,8 @@ class NewGroupController extends State { final GlobalKey addToSpaceKey = GlobalKey(); final GlobalKey addConversationBotKey = GlobalKey(); + final GlobalKey addCapacityKey = + GlobalKey(); ChatTopic chatTopic = ChatTopic.empty; @@ -145,10 +149,16 @@ class NewGroupController extends State { visibility: sdk.Visibility.public, ); } - //#Pangea + // #Pangea GoogleAnalytics.createChat(roomId); await addToSpaceKey.currentState!.addSpaces(roomId); - //Pangea# + + final capacity = addCapacityKey.currentState?.capacity; + final room = client.getRoomById(roomId); + if (capacity != null && room != null) { + room.updateRoomCapacity(capacity); + } + // Pangea# context.go('/rooms/$roomId/invite'); } catch (e, s) { sdk.Logs().d('Unable to create group', e, s); diff --git a/lib/pages/new_group/new_group_view.dart b/lib/pages/new_group/new_group_view.dart index ced178038..ddf55dfbd 100644 --- a/lib/pages/new_group/new_group_view.dart +++ b/lib/pages/new_group/new_group_view.dart @@ -1,5 +1,6 @@ import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/new_group/new_group.dart'; +import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_capacity_button.dart'; import 'package:fluffychat/pangea/widgets/class/add_class_and_invite.dart'; import 'package:fluffychat/pangea/widgets/class/add_space_toggles.dart'; import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_settings.dart'; @@ -87,6 +88,9 @@ class NewGroupView extends StatelessWidget { // ), // ), // ), + RoomCapacityButton( + key: controller.addCapacityKey, + ), ConversationBotSettings( key: controller.addConversationBotKey, activeSpaceId: controller.activeSpaceId, diff --git a/lib/pages/new_space/new_space.dart b/lib/pages/new_space/new_space.dart index 10b5c0caf..fcb9f136b 100644 --- a/lib/pages/new_space/new_space.dart +++ b/lib/pages/new_space/new_space.dart @@ -4,6 +4,7 @@ import 'package:file_picker/file_picker.dart'; import 'package:fluffychat/pages/new_space/new_space_view.dart'; import 'package:fluffychat/pangea/constants/class_default_values.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; +import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_capacity_button.dart'; import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_rules_editor.dart'; import 'package:fluffychat/pangea/utils/bot_name.dart'; import 'package:fluffychat/pangea/utils/class_chat_power_levels.dart'; @@ -37,6 +38,8 @@ class NewSpaceController extends State { final GlobalKey addToSpaceKey = GlobalKey(); final GlobalKey classSettingsKey = GlobalKey(); + final GlobalKey addCapacityKey = + GlobalKey(); //Pangea# bool loading = false; @@ -196,6 +199,11 @@ class NewSpaceController extends State { } await Future.wait(futures); + final capacity = addCapacityKey.currentState?.capacity; + final space = client.getRoomById(spaceId); + if (capacity != null && space != null) { + space.updateRoomCapacity(capacity); + } final newChatRoomId = await Matrix.of(context).client.createGroupChat( enableEncryption: false, preset: sdk.CreateRoomPreset.publicChat, diff --git a/lib/pages/new_space/new_space_view.dart b/lib/pages/new_space/new_space_view.dart index a0a4d0927..09abb7066 100644 --- a/lib/pages/new_space/new_space_view.dart +++ b/lib/pages/new_space/new_space_view.dart @@ -1,6 +1,7 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/extensions/client_extension/client_extension.dart'; import 'package:fluffychat/pangea/models/class_model.dart'; +import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_capacity_button.dart'; import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_rules_editor.dart'; import 'package:fluffychat/pangea/widgets/class/add_class_and_invite.dart'; import 'package:fluffychat/pangea/widgets/class/add_space_toggles.dart'; @@ -130,6 +131,10 @@ class NewSpaceView extends StatelessWidget { // ), // ), // const SizedBox(height: 16), + + RoomCapacityButton( + key: controller.addCapacityKey, + ), if (controller.newClassMode) ClassSettings( key: controller.classSettingsKey, diff --git a/lib/pangea/constants/pangea_event_types.dart b/lib/pangea/constants/pangea_event_types.dart index 02334d7fe..a182728dd 100644 --- a/lib/pangea/constants/pangea_event_types.dart +++ b/lib/pangea/constants/pangea_event_types.dart @@ -20,6 +20,7 @@ class PangeaEventTypes { static const audio = "p.audio"; static const botOptions = "pangea.bot_options"; + static const capacity = "pangea.capacity"; static const userAge = "pangea.user_age"; diff --git a/lib/pangea/controllers/class_controller.dart b/lib/pangea/controllers/class_controller.dart index aed5574d2..1e2febf21 100644 --- a/lib/pangea/controllers/class_controller.dart +++ b/lib/pangea/controllers/class_controller.dart @@ -13,6 +13,7 @@ import 'package:fluffychat/pangea/utils/error_handler.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 'package:matrix/matrix.dart'; import '../../widgets/matrix.dart'; @@ -133,8 +134,9 @@ class ClassController extends BaseController { ClassCodeUtil.messageSnack(context, L10n.of(context)!.alreadyInClass); return; } + await _pangeaController.matrixState.client.joinRoom(classChunk.roomId); - setActiveSpaceIdInChatListController(classChunk.roomId); + if (_pangeaController.matrixState.client.getRoomById(classChunk.roomId) == null) { await _pangeaController.matrixState.client.waitForRoomInSync( @@ -143,6 +145,26 @@ class ClassController extends BaseController { ); } + // If the room is full, leave + final room = + _pangeaController.matrixState.client.getRoomById(classChunk.roomId); + if (room == null) { + return; + } + final joinResult = await showFutureLoadingDialog( + context: context, + future: () async { + if (await room.leaveIfFull()) { + throw L10n.of(context)!.roomFull; + } + }, + ); + if (joinResult.error != null) { + return; + } + + setActiveSpaceIdInChatListController(classChunk.roomId); + // add the user's analytics room to this joined space // so their teachers can join them via the space hierarchy final Room? joinedSpace = diff --git a/lib/pangea/extensions/pangea_room_extension/events_extension.dart b/lib/pangea/extensions/pangea_room_extension/events_extension.dart index eafb190e1..631d21478 100644 --- a/lib/pangea/extensions/pangea_room_extension/events_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/events_extension.dart @@ -1,6 +1,20 @@ part of "pangea_room_extension.dart"; extension EventsRoomExtension on Room { + Future _leaveIfFull() async { + await postLoad(); + if (!isRoomAdmin && + (_capacity != null) && + (await _numNonAdmins) > (_capacity!)) { + if (!isSpace) { + markUnread(false); + } + await leave(); + return true; + } + return false; + } + Future _archive() async { final students = (await requestParticipants()) .where( 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 bbadd6703..a8cc00a84 100644 --- a/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart @@ -137,6 +137,7 @@ extension PangeaRoom on Room { // events + Future leaveIfFull() async => await _leaveIfFull(); Future archive() async => await _archive(); Future archiveSpace( @@ -200,6 +201,8 @@ extension PangeaRoom on Room { // room_information + Future get numNonAdmins async => await _numNonAdmins; + DateTime? get creationTime => _creationTime; String? get creatorId => _creatorId; @@ -230,6 +233,11 @@ extension PangeaRoom on Room { // room_settings + Future updateRoomCapacity(int newCapacity) => + _updateRoomCapacity(newCapacity); + + int? get capacity => _capacity; + PangeaRoomRules? get pangeaRoomRules => _pangeaRoomRules; PangeaRoomRules? get firstRules => _firstRules; diff --git a/lib/pangea/extensions/pangea_room_extension/room_information_extension.dart b/lib/pangea/extensions/pangea_room_extension/room_information_extension.dart index 2a1ca83d0..213d3c4ce 100644 --- a/lib/pangea/extensions/pangea_room_extension/room_information_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/room_information_extension.dart @@ -1,6 +1,17 @@ part of "pangea_room_extension.dart"; extension RoomInformationRoomExtension on Room { + Future get _numNonAdmins async { + return (await requestParticipants()) + .where( + (e) => + e.powerLevel < ClassDefaultValues.powerLevelOfAdmin && + e.id != BotName.byEnvironment, + ) + .toList() + .length; + } + DateTime? get _creationTime => getState(EventTypes.RoomCreate)?.originServerTs; diff --git a/lib/pangea/extensions/pangea_room_extension/room_settings_extension.dart b/lib/pangea/extensions/pangea_room_extension/room_settings_extension.dart index 9746a5680..0eecb691d 100644 --- a/lib/pangea/extensions/pangea_room_extension/room_settings_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/room_settings_extension.dart @@ -1,6 +1,19 @@ part of "pangea_room_extension.dart"; extension RoomSettingsRoomExtension on Room { + Future _updateRoomCapacity(int newCapacity) => + client.setRoomStateWithKey( + id, + PangeaEventTypes.capacity, + '', + {'capacity': newCapacity}, + ); + + int? get _capacity { + final t = getState(PangeaEventTypes.capacity)?.content['capacity']; + return t is int ? t : null; + } + PangeaRoomRules? get _pangeaRoomRules { try { final Map? content = pangeaRoomRulesStateEvent?.content; diff --git a/lib/pangea/pages/class_settings/p_class_widgets/class_description_button.dart b/lib/pangea/pages/class_settings/p_class_widgets/class_description_button.dart index 0d6779e82..4fdb38604 100644 --- a/lib/pangea/pages/class_settings/p_class_widgets/class_description_button.dart +++ b/lib/pangea/pages/class_settings/p_class_widgets/class_description_button.dart @@ -1,10 +1,10 @@ +import 'package:fluffychat/pages/chat_details/chat_details.dart'; +import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:flutter/material.dart'; - import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:matrix/matrix.dart'; -import 'package:fluffychat/pages/chat_details/chat_details.dart'; - class ClassDescriptionButton extends StatelessWidget { final Room room; final ChatDetailsController controller; @@ -20,21 +20,19 @@ class ClassDescriptionButton extends StatelessWidget { return Column( children: [ ListTile( - onTap: room.canSendEvent(EventTypes.RoomTopic) - ? controller.setTopicAction - : null, - leading: room.canSendEvent(EventTypes.RoomTopic) - ? CircleAvatar( - backgroundColor: Theme.of(context).scaffoldBackgroundColor, - foregroundColor: iconColor, - child: const Icon(Icons.topic_outlined), - ) - : null, + onTap: room.isRoomAdmin ? controller.setTopicAction : null, + leading: CircleAvatar( + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + foregroundColor: iconColor, + child: const Icon(Icons.topic_outlined), + ), subtitle: Text( room.topic.isEmpty - ? (room.isSpace - ? L10n.of(context)!.classDescriptionDesc - : L10n.of(context)!.chatTopicDesc) + ? (room.isRoomAdmin + ? (room.isSpace + ? L10n.of(context)!.classDescriptionDesc + : L10n.of(context)!.chatTopicDesc) + : L10n.of(context)!.topicNotSet) : room.topic, ), title: Text( @@ -51,3 +49,53 @@ class ClassDescriptionButton extends StatelessWidget { ); } } + +void setClassTopic(Room room, BuildContext context) { + final TextEditingController textFieldController = + TextEditingController(text: room.topic); + showDialog( + context: context, + useRootNavigator: false, + builder: (BuildContext context) => AlertDialog( + title: Text( + room.isSpace + ? L10n.of(context)!.classDescription + : L10n.of(context)!.chatTopic, + ), + content: TextField( + controller: textFieldController, + keyboardType: TextInputType.multiline, + minLines: 1, + maxLines: 10, + maxLength: 2000, + ), + actions: [ + TextButton( + child: Text(L10n.of(context)!.cancel), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: Text(L10n.of(context)!.ok), + onPressed: () async { + if (textFieldController.text == "") return; + final success = await showFutureLoadingDialog( + context: context, + future: () => room.setDescription(textFieldController.text), + ); + if (success.error == null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: + Text(L10n.of(context)!.groupDescriptionHasBeenChanged), + ), + ); + Navigator.of(context).pop(); + } + }, + ), + ], + ), + ); +} diff --git a/lib/pangea/pages/class_settings/p_class_widgets/room_capacity_button.dart b/lib/pangea/pages/class_settings/p_class_widgets/room_capacity_button.dart new file mode 100644 index 000000000..ca876fae8 --- /dev/null +++ b/lib/pangea/pages/class_settings/p_class_widgets/room_capacity_button.dart @@ -0,0 +1,158 @@ +import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:fluffychat/pages/chat_details/chat_details.dart'; +import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:future_loading_dialog/future_loading_dialog.dart'; +import 'package:matrix/matrix.dart'; + +class RoomCapacityButton extends StatefulWidget { + final Room? room; + final ChatDetailsController? controller; + const RoomCapacityButton({ + super.key, + this.room, + this.controller, + }); + + @override + RoomCapacityButtonState createState() => RoomCapacityButtonState(); +} + +class RoomCapacityButtonState extends State { + int? capacity; + String? nonAdmins; + + RoomCapacityButtonState({Key? key}); + + @override + void initState() { + super.initState(); + capacity = widget.room?.capacity; + widget.room?.numNonAdmins.then( + (value) => setState(() { + nonAdmins = value.toString(); + overCapacity(); + }), + ); + } + + @override + void didUpdateWidget(RoomCapacityButton oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.room != widget.room) { + capacity = widget.room?.capacity; + widget.room?.numNonAdmins.then( + (value) => setState(() { + nonAdmins = value.toString(); + overCapacity(); + }), + ); + } + } + + Future overCapacity() async { + if ((widget.room?.isRoomAdmin ?? false) && + capacity != null && + nonAdmins != null && + int.parse(nonAdmins!) > capacity!) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + L10n.of(context)!.roomExceedsCapacity, + ), + ), + ); + } + } + + @override + Widget build(BuildContext context) { + final iconColor = Theme.of(context).textTheme.bodyLarge!.color; + return Column( + children: [ + ListTile( + onTap: () => + ((widget.room?.isRoomAdmin ?? true) ? (setRoomCapacity()) : null), + leading: CircleAvatar( + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + foregroundColor: iconColor, + child: const Icon(Icons.reduce_capacity), + ), + subtitle: Text( + (capacity == null) + ? L10n.of(context)!.capacityNotSet + : (nonAdmins != null) + ? '$nonAdmins/$capacity' + : '$capacity', + ), + title: Text( + L10n.of(context)!.roomCapacity, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ); + } + + Future setCapacity(int newCapacity) async { + capacity = newCapacity; + } + + Future setRoomCapacity() async { + final input = await showTextInputDialog( + context: context, + title: L10n.of(context)!.roomCapacity, + message: L10n.of(context)!.roomCapacityExplanation, + okLabel: L10n.of(context)!.ok, + cancelLabel: L10n.of(context)!.cancel, + textFields: [ + DialogTextField( + initialText: ((capacity != null) ? '$capacity' : ''), + keyboardType: TextInputType.number, + maxLength: 3, + validator: (value) { + if (value == null || + value.isEmpty || + int.tryParse(value) == null || + int.parse(value) < 0) { + return L10n.of(context)!.enterNumber; + } + if (nonAdmins != null && int.parse(value) < int.parse(nonAdmins!)) { + return L10n.of(context)!.capacitySetTooLow; + } + return null; + }, + ), + ], + ); + if (input == null || + input.first == "" || + int.tryParse(input.first) == null) { + return; + } + + final newCapacity = int.parse(input.first); + final success = await showFutureLoadingDialog( + context: context, + future: () => ((widget.room != null) + ? (widget.room!.updateRoomCapacity( + capacity = newCapacity, + )) + : setCapacity(newCapacity)), + ); + if (success.error == null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + L10n.of(context)!.roomCapacityHasBeenChanged, + ), + ), + ); + setState(() {}); + } + } +} diff --git a/lib/pangea/utils/chat_list_handle_space_tap.dart b/lib/pangea/utils/chat_list_handle_space_tap.dart index cb58a3ea4..4930f7ecc 100644 --- a/lib/pangea/utils/chat_list_handle_space_tap.dart +++ b/lib/pangea/utils/chat_list_handle_space_tap.dart @@ -33,6 +33,9 @@ void chatListHandleSpaceTap( context: context, future: () async { await space.join(); + if (await space.leaveIfFull()) { + throw L10n.of(context)!.roomFull; + } await space.postLoad(); setActiveSpaceAndCloseChat(); }, @@ -65,6 +68,9 @@ void chatListHandleSpaceTap( context: context, future: () async { await space.join(); + if (await space.leaveIfFull()) { + throw L10n.of(context)!.roomFull; + } if (space.isSpace) { await space.joinAnalyticsRoomsInSpace(); } diff --git a/lib/pangea/utils/set_class_topic.dart b/lib/pangea/utils/set_class_topic.dart deleted file mode 100644 index 91625af42..000000000 --- a/lib/pangea/utils/set_class_topic.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:future_loading_dialog/future_loading_dialog.dart'; -import 'package:matrix/matrix.dart'; - -void setClassTopic(Room room, BuildContext context) { - final TextEditingController textFieldController = - TextEditingController(text: room.topic); - showDialog( - context: context, - useRootNavigator: false, - builder: (BuildContext context) => AlertDialog( - title: Text( - room.isSpace - ? L10n.of(context)!.classDescription - : L10n.of(context)!.chatTopic, - ), - content: TextField( - controller: textFieldController, - keyboardType: TextInputType.multiline, - minLines: 1, - maxLines: 10, - maxLength: 2000, - ), - actions: [ - TextButton( - child: Text(L10n.of(context)!.cancel), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - TextButton( - child: Text(L10n.of(context)!.ok), - onPressed: () async { - if (textFieldController.text == "") return; - final success = await showFutureLoadingDialog( - context: context, - future: () => room.setDescription(textFieldController.text), - ); - if (success.error == null) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: - Text(L10n.of(context)!.groupDescriptionHasBeenChanged), - ), - ); - Navigator.of(context).pop(); - } - }, - ), - ], - ), - ); -} diff --git a/lib/utils/client_manager.dart b/lib/utils/client_manager.dart index 125307100..47805638e 100644 --- a/lib/utils/client_manager.dart +++ b/lib/utils/client_manager.dart @@ -116,6 +116,7 @@ abstract class ClientManager { PangeaEventTypes.botOptions, EventTypes.RoomTopic, EventTypes.RoomAvatar, + PangeaEventTypes.capacity, // Pangea# }, logLevel: kReleaseMode ? Level.warning : Level.verbose, diff --git a/lib/utils/url_launcher.dart b/lib/utils/url_launcher.dart index 53de9b138..bcdda51ea 100644 --- a/lib/utils/url_launcher.dart +++ b/lib/utils/url_launcher.dart @@ -1,7 +1,12 @@ -import 'package:flutter/material.dart'; - import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:collection/collection.dart' show IterableExtension; +import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/pages/user_bottom_sheet/user_bottom_sheet.dart'; +import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; +import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; +import 'package:fluffychat/widgets/matrix.dart'; +import 'package:fluffychat/widgets/public_room_bottom_sheet.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:go_router/go_router.dart'; @@ -9,11 +14,6 @@ import 'package:matrix/matrix.dart'; import 'package:punycode/punycode.dart'; import 'package:url_launcher/url_launcher_string.dart'; -import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/pages/user_bottom_sheet/user_bottom_sheet.dart'; -import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; -import 'package:fluffychat/widgets/matrix.dart'; -import 'package:fluffychat/widgets/public_room_bottom_sheet.dart'; import 'platform_infos.dart'; class UrlLauncher { @@ -159,7 +159,10 @@ class UrlLauncher { room = matrix.client.getRoomById(roomId!); } servers.addAll(identityParts.via); - if (room != null) { + // #Pangea + if (room != null && room.membership != Membership.leave) { + // if (room != null) { + // Pangea# if (room.isSpace) { // TODO: Implement navigate to space context.go('/rooms/${room.id}'); @@ -202,7 +205,19 @@ class UrlLauncher { serverName: servers.isNotEmpty ? servers.toList() : null, ), ); - if (response.error != null) return; + // #Pangea + // if (response.error != null) return; + if (response.error != null || + (room != null && (await room.leaveIfFull()))) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + duration: const Duration(seconds: 10), + content: Text(L10n.of(context)!.roomFull), + ), + ); + return; + } + // Pangea# // wait for two seconds so that it probably came down /sync await showFutureLoadingDialog( context: context, diff --git a/lib/widgets/public_room_bottom_sheet.dart b/lib/widgets/public_room_bottom_sheet.dart index 19932f5b9..086162b3f 100644 --- a/lib/widgets/public_room_bottom_sheet.dart +++ b/lib/widgets/public_room_bottom_sheet.dart @@ -1,15 +1,15 @@ +import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; +import 'package:fluffychat/utils/fluffy_share.dart'; +import 'package:fluffychat/utils/url_launcher.dart'; +import 'package:fluffychat/widgets/avatar.dart'; +import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; - import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; -import 'package:fluffychat/utils/fluffy_share.dart'; -import 'package:fluffychat/utils/url_launcher.dart'; -import 'package:fluffychat/widgets/avatar.dart'; -import 'package:fluffychat/widgets/matrix.dart'; import '../utils/localized_exception_extension.dart'; class PublicRoomBottomSheet extends StatelessWidget { @@ -44,6 +44,12 @@ class PublicRoomBottomSheet extends StatelessWidget { if (client.getRoomById(roomId) == null) { await client.waitForRoomInSync(roomId); } + // #Pangea + final room = client.getRoomById(roomId); + if (room != null && (await room.leaveIfFull())) { + throw L10n.of(context)!.roomFull; + } + // Pangea# return roomId; }, ); diff --git a/needed-translations.txt b/needed-translations.txt index e29d36ddd..fa0febfcd 100644 --- a/needed-translations.txt +++ b/needed-translations.txt @@ -839,6 +839,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -2288,6 +2297,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -3199,6 +3217,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -4110,6 +4137,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -5021,6 +5057,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -5932,6 +5977,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -6790,6 +6844,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -7701,6 +7764,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -8612,6 +8684,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -8629,6 +8710,15 @@ "es": [ "suggestToChat", "suggestToChatDesc", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -9483,6 +9573,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -10337,6 +10436,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -11248,6 +11356,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -12159,6 +12276,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -13070,6 +13196,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -13981,6 +14116,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -14835,6 +14979,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -15746,6 +15899,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -16657,6 +16819,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -17555,6 +17726,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -18466,6 +18646,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -19901,6 +20090,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -20812,6 +21010,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -21723,6 +21930,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -22619,6 +22835,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -23530,6 +23755,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -24441,6 +24675,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -25352,6 +25595,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -26263,6 +26515,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -27174,6 +27435,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -28085,6 +28355,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -28996,6 +29275,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -29907,6 +30195,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -30787,6 +31084,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -31698,6 +32004,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -32609,6 +32924,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -33463,6 +33787,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -34374,6 +34707,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -35285,6 +35627,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -36196,6 +36547,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -37072,6 +37432,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -37983,6 +38352,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -38894,6 +39272,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -39790,6 +40177,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -40644,6 +41040,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -41555,6 +41960,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -42409,6 +42823,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection", @@ -43320,6 +43743,15 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", + "roomCapacity", + "roomFull", + "topicNotSet", + "capacityNotSet", + "roomCapacityHasBeenChanged", + "roomExceedsCapacity", + "capacitySetTooLow", + "roomCapacityExplanation", + "enterNumber", "autoIGCToolName", "autoIGCToolDescription", "runGrammarCorrection",