From 215686f4a4f88b22f287cebcf57699e1f0d681be Mon Sep 17 00:00:00 2001 From: Kelrap Date: Tue, 4 Jun 2024 14:59:17 -0400 Subject: [PATCH] Add capacity field, fix details page permissions --- assets/l10n/intl_en.arb | 6 +- lib/pages/chat_details/chat_details.dart | 10 +- lib/pages/chat_details/chat_details_view.dart | 20 +++- lib/pangea/constants/pangea_event_types.dart | 1 + lib/pangea/controllers/class_controller.dart | 11 ++- .../events_extension.dart | 14 +++ .../pangea_room_extension.dart | 12 +++ .../room_information_extension.dart | 11 +++ .../room_settings_extension.dart | 13 +++ .../class_description_button.dart | 84 ++++++++++++---- .../p_class_widgets/room_capacity_button.dart | 98 +++++++++++++++++++ lib/pangea/utils/set_class_topic.dart | 54 ---------- 12 files changed, 255 insertions(+), 79 deletions(-) create mode 100644 lib/pangea/pages/class_settings/p_class_widgets/room_capacity_button.dart delete mode 100644 lib/pangea/utils/set_class_topic.dart diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index bb9d15f0c..6f90f35d4 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -3963,5 +3963,9 @@ "studentAnalyticsNotAvailable": "Student data not currently available", "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" + "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." } \ No newline at end of file diff --git a/lib/pages/chat_details/chat_details.dart b/lib/pages/chat_details/chat_details.dart index 07a12876e..2089c9f79 100644 --- a/lib/pages/chat_details/chat_details.dart +++ b/lib/pages/chat_details/chat_details.dart @@ -3,8 +3,9 @@ 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/pages/class_settings/p_class_widgets/room_capacity_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'; @@ -232,6 +233,13 @@ class ChatDetailsController extends State { // Pangea# } + // #Pangea + void setCapacityAction() async { + final room = Matrix.of(context).client.getRoomById(roomId!)!; + setClassCapacity(room, context); + } + // Pangea# + void setGuestAccess() async { final room = Matrix.of(context).client.getRoomById(roomId!)!; final currentGuestAccess = room.guestAccess; diff --git a/lib/pages/chat_details/chat_details_view.dart b/lib/pages/chat_details/chat_details_view.dart index 6b7ce5616..f76a470a4 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/archive_space.dart'; import 'package:fluffychat/pangea/utils/lock_room.dart'; @@ -237,8 +238,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, @@ -248,6 +250,12 @@ class ChatDetailsView extends StatelessWidget { room: room, controller: controller, ), + // #Pangea + RoomCapacityButton( + room: room, + controller: controller, + ), + // Pangea# if ((room.isPangeaClass || room.isExchange) && room.isRoomAdmin) ListTile( @@ -436,7 +444,9 @@ class ChatDetailsView extends StatelessWidget { // : null, // ), // if (!room.isDirectChat) - if (!room.isDirectChat && !room.isSpace) + if (!room.isDirectChat && + !room.isSpace && + room.isRoomAdmin) // Pangea# ListTile( // #Pangea @@ -511,7 +521,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/pangea/constants/pangea_event_types.dart b/lib/pangea/constants/pangea_event_types.dart index cfdb7f0d7..9b5a520a1 100644 --- a/lib/pangea/constants/pangea_event_types.dart +++ b/lib/pangea/constants/pangea_event_types.dart @@ -18,6 +18,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 0820f769b..d2e711cc3 100644 --- a/lib/pangea/controllers/class_controller.dart +++ b/lib/pangea/controllers/class_controller.dart @@ -132,8 +132,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( @@ -142,6 +143,14 @@ class ClassController extends BaseController { ); } + final room = + _pangeaController.matrixState.client.getRoomById(classChunk.roomId); + if (room != null && (await room.leaveIfFull(context))) { + 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 ecf9ac941..3391fc13d 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(BuildContext context) async { + if (!isRoomAdmin && + (_capacity != null) && + (await _numNonAdmins) >= (int.parse(_capacity!))) { + ClassCodeUtil.messageSnack(context, L10n.of(context)!.roomFull); + if (!isSpace) { + markUnread(false); + } + await leave(); + return true; + } + return false; + } + Future _sendPangeaEvent({ required Map content, required String parentEventId, 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 13606fbc2..46ad76975 100644 --- a/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart @@ -10,9 +10,11 @@ import 'package:fluffychat/pangea/models/bot_options_model.dart'; import 'package:fluffychat/pangea/models/class_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/class_code.dart'; 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 markdown.dart import 'package:html_unescape/html_unescape.dart'; import 'package:matrix/matrix.dart'; @@ -138,6 +140,9 @@ extension PangeaRoom on Room { // events + Future leaveIfFull(BuildContext context) async => + await _leaveIfFull(context); + Future sendPangeaEvent({ required Map content, required String parentEventId, @@ -212,6 +217,8 @@ extension PangeaRoom on Room { // room_information + Future get numNonAdmins async => await _numNonAdmins; + DateTime? get creationTime => _creationTime; String? get creatorId => _creatorId; @@ -242,6 +249,11 @@ extension PangeaRoom on Room { // room_settings + Future updateRoomCapacity(String newCapacity) => + _updateRoomCapacity(newCapacity); + + String? 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 0c34a8aaf..b4f390ffb 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 6995659d3..ed2694051 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(String newCapacity) => + client.setRoomStateWithKey( + id, + PangeaEventTypes.capacity, + '', + {'capacity': newCapacity}, + ); + + String? get _capacity { + final t = getState(PangeaEventTypes.capacity)?.content['capacity']; + return t is String ? 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,9 +1,9 @@ -import 'package:flutter/material.dart'; - -import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:matrix/matrix.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 ClassDescriptionButton extends StatelessWidget { final Room room; @@ -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..03210aa3d --- /dev/null +++ b/lib/pangea/pages/class_settings/p_class_widgets/room_capacity_button.dart @@ -0,0 +1,98 @@ +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/services.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 StatelessWidget { + final Room room; + final ChatDetailsController? controller; + const RoomCapacityButton({ + super.key, + required this.room, + this.controller, + }); + + @override + Widget build(BuildContext context) { + final iconColor = Theme.of(context).textTheme.bodyLarge!.color; + // Edit - use FutureBuilder to allow async call + // String nonAdmins = (await room.numNonAdmins).toString; + return Column( + children: [ + ListTile( + onTap: room.isRoomAdmin ? controller!.setCapacityAction : null, + leading: CircleAvatar( + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + foregroundColor: iconColor, + child: const Icon(Icons.reduce_capacity), + ), + subtitle: Text( + // Edit + // '$nonAdmins/${room.capacity}', + (room.capacity ?? L10n.of(context)!.capacityNotSet), + ), + title: Text( + L10n.of(context)!.roomCapacity, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ); + } +} + +void setClassCapacity(Room room, BuildContext context) { + final TextEditingController myTextFieldController = + TextEditingController(text: (room.capacity ?? '')); + showDialog( + context: context, + useRootNavigator: false, + builder: (BuildContext context) => AlertDialog( + title: Text( + L10n.of(context)!.roomCapacity, + ), + content: TextFormField( + controller: myTextFieldController, + keyboardType: TextInputType.number, + maxLength: 2, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + ], + ), + actions: [ + TextButton( + child: Text(L10n.of(context)!.cancel), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: Text(L10n.of(context)!.ok), + onPressed: () async { + if (myTextFieldController.text == "") return; + final success = await showFutureLoadingDialog( + context: context, + future: () => room.updateRoomCapacity(myTextFieldController.text), + ); + if (success.error == null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + L10n.of(context)!.groupDescriptionHasBeenChanged, + ), // Edit + ), + ); + Navigator.of(context).pop(); + } + }, + ), + ], + ), + ); +} 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(); - } - }, - ), - ], - ), - ); -}