Add capacity field, fix details page permissions
This commit is contained in:
parent
7a43706b79
commit
215686f4a4
12 changed files with 255 additions and 79 deletions
|
|
@ -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."
|
||||
}
|
||||
|
|
@ -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<ChatDetails> {
|
|||
// 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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -1,6 +1,20 @@
|
|||
part of "pangea_room_extension.dart";
|
||||
|
||||
extension EventsRoomExtension on Room {
|
||||
Future<bool> _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<Event?> _sendPangeaEvent({
|
||||
required Map<String, dynamic> content,
|
||||
required String parentEventId,
|
||||
|
|
|
|||
|
|
@ -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<bool> leaveIfFull(BuildContext context) async =>
|
||||
await _leaveIfFull(context);
|
||||
|
||||
Future<Event?> sendPangeaEvent({
|
||||
required Map<String, dynamic> content,
|
||||
required String parentEventId,
|
||||
|
|
@ -212,6 +217,8 @@ extension PangeaRoom on Room {
|
|||
|
||||
// room_information
|
||||
|
||||
Future<int> get numNonAdmins async => await _numNonAdmins;
|
||||
|
||||
DateTime? get creationTime => _creationTime;
|
||||
|
||||
String? get creatorId => _creatorId;
|
||||
|
|
@ -242,6 +249,11 @@ extension PangeaRoom on Room {
|
|||
|
||||
// room_settings
|
||||
|
||||
Future<String> updateRoomCapacity(String newCapacity) =>
|
||||
_updateRoomCapacity(newCapacity);
|
||||
|
||||
String? get capacity => _capacity;
|
||||
|
||||
PangeaRoomRules? get pangeaRoomRules => _pangeaRoomRules;
|
||||
|
||||
PangeaRoomRules? get firstRules => _firstRules;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,17 @@
|
|||
part of "pangea_room_extension.dart";
|
||||
|
||||
extension RoomInformationRoomExtension on Room {
|
||||
Future<int> 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;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,19 @@
|
|||
part of "pangea_room_extension.dart";
|
||||
|
||||
extension RoomSettingsRoomExtension on Room {
|
||||
Future<String> _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<String, dynamic>? content = pangeaRoomRulesStateEvent?.content;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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: <TextInputFormatter>[
|
||||
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();
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue