From 550c3ab6999274382577574c36dc025ac96fff5f Mon Sep 17 00:00:00 2001 From: WilsonLe Date: Mon, 14 Oct 2024 23:43:19 +1100 Subject: [PATCH 1/8] ideal case of private class code --- lib/pages/new_space/new_space.dart | 20 +++++-- lib/pangea/constants/model_keys.dart | 4 ++ lib/pangea/controllers/class_controller.dart | 59 +++++++++++-------- .../space_settings_extension.dart | 10 +++- lib/pangea/utils/space_code.dart | 30 +++++++--- 5 files changed, 83 insertions(+), 40 deletions(-) diff --git a/lib/pages/new_space/new_space.dart b/lib/pages/new_space/new_space.dart index 0b1faf674..9f4465c6e 100644 --- a/lib/pages/new_space/new_space.dart +++ b/lib/pages/new_space/new_space.dart @@ -150,7 +150,10 @@ class NewSpaceController extends State { try { final avatar = this.avatar; avatarUrl ??= avatar == null ? null : await client.uploadContent(avatar); - + final classCode = await SpaceCodeUtil.generateSpaceCode(client); + if (classCode == null) { + return; + } final spaceId = await client.createRoom( // #Pangea // preset: publicGroup @@ -164,7 +167,7 @@ class NewSpaceController extends State { // roomAliasName: publicGroup // ? nameController.text.trim().toLowerCase().replaceAll(' ', '_') // : null, - roomAliasName: SpaceCodeUtil.generateSpaceCode(), + roomAliasName: classCode, // Pangea# name: nameController.text.trim(), topic: topicController.text.isEmpty ? null : topicController.text, @@ -178,14 +181,21 @@ class NewSpaceController extends State { : null, // Pangea# initialState: [ + // #Pangea + ...initialState, + // Pangea# if (avatar != null) sdk.StateEvent( type: sdk.EventTypes.RoomAvatar, content: {'url': avatarUrl.toString()}, ), - // #Pangea - ...initialState, - // Pangea# + sdk.StateEvent( + type: sdk.EventTypes.RoomJoinRules, + content: { + 'join_rule': 'knock', + 'access_code': classCode, + }, + ), ], ); // #Pangea diff --git a/lib/pangea/constants/model_keys.dart b/lib/pangea/constants/model_keys.dart index 195e919b4..962532c9f 100644 --- a/lib/pangea/constants/model_keys.dart +++ b/lib/pangea/constants/model_keys.dart @@ -123,4 +123,8 @@ class ModelKey { static const String prevEventId = "prev_event_id"; static const String prevLastUpdated = "prev_last_updated"; + + // room code + static const String joinRule = "join_rule"; + static const String accessCode = "access_code"; } diff --git a/lib/pangea/controllers/class_controller.dart b/lib/pangea/controllers/class_controller.dart index 157a11b59..48623fe9d 100644 --- a/lib/pangea/controllers/class_controller.dart +++ b/lib/pangea/controllers/class_controller.dart @@ -1,7 +1,7 @@ import 'dart:async'; +import 'dart:convert'; import 'dart:developer'; -import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/constants/local.key.dart'; import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; @@ -9,15 +9,15 @@ import 'package:fluffychat/pangea/extensions/client_extension/client_extension.d import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/pangea/models/space_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; +import 'package:fluffychat/pangea/utils/firebase_analytics.dart'; import 'package:fluffychat/pangea/utils/space_code.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 'package:matrix/matrix.dart'; -import '../../widgets/matrix.dart'; -import '../utils/firebase_analytics.dart'; import 'base_controller.dart'; class ClassController extends BaseController { @@ -65,47 +65,56 @@ class ClassController extends BaseController { } Future joinClasswithCode(BuildContext context, String classCode) async { + final client = Matrix.of(context).client; try { - final QueryPublicRoomsResponse queryPublicRoomsResponse = - await Matrix.of(context).client.queryPublicRooms( - limit: 1, - filter: PublicRoomQueryFilter(genericSearchTerm: classCode), - ); - - final PublicRoomsChunk? classChunk = - queryPublicRoomsResponse.chunk.firstWhereOrNull((element) { - return element.canonicalAlias?.replaceAll("#", "").split(":")[0] == - classCode; - }); - - if (classChunk == null) { + final knockResponse = await client.httpClient.post( + Uri.parse( + '${client.homeserver}/_synapse/client/pangea/v1/knock_with_code', + ), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ${client.accessToken}', + }, + body: jsonEncode({'access_code': classCode}), + ); + if (knockResponse.statusCode != 200) { SpaceCodeUtil.messageSnack( context, L10n.of(context)!.unableToFindClass, ); return; } - + final knockResult = jsonDecode(knockResponse.body); + final foundClasses = knockResult['rooms']; + if (!(foundClasses is List && foundClasses.isNotEmpty)) { + SpaceCodeUtil.messageSnack( + context, + L10n.of(context)!.unableToFindClass, + ); + return; + } + final chosenClassId = foundClasses.first; + await client.joinRoomById(chosenClassId); if (_pangeaController.matrixState.client.rooms - .any((room) => room.id == classChunk.roomId)) { - setActiveSpaceIdInChatListController(classChunk.roomId); + .any((room) => room.id == chosenClassId)) { + setActiveSpaceIdInChatListController(chosenClassId); SpaceCodeUtil.messageSnack(context, L10n.of(context)!.alreadyInClass); return; } - await _pangeaController.matrixState.client.joinRoom(classChunk.roomId); + await _pangeaController.matrixState.client.joinRoom(chosenClassId); - if (_pangeaController.matrixState.client.getRoomById(classChunk.roomId) == + if (_pangeaController.matrixState.client.getRoomById(chosenClassId) == null) { await _pangeaController.matrixState.client.waitForRoomInSync( - classChunk.roomId, + chosenClassId, join: true, ); } // If the room is full, leave final room = - _pangeaController.matrixState.client.getRoomById(classChunk.roomId); + _pangeaController.matrixState.client.getRoomById(chosenClassId); if (room == null) { return; } @@ -121,12 +130,12 @@ class ClassController extends BaseController { return; } - setActiveSpaceIdInChatListController(classChunk.roomId); + setActiveSpaceIdInChatListController(chosenClassId); // add the user's analytics room to this joined space // so their teachers can join them via the space hierarchy final Room? joinedSpace = - _pangeaController.matrixState.client.getRoomById(classChunk.roomId); + _pangeaController.matrixState.client.getRoomById(chosenClassId); // when possible, add user's analytics room the to space they joined joinedSpace?.addAnalyticsRoomsToSpace(); diff --git a/lib/pangea/extensions/pangea_room_extension/space_settings_extension.dart b/lib/pangea/extensions/pangea_room_extension/space_settings_extension.dart index 6354de96e..fce45f17a 100644 --- a/lib/pangea/extensions/pangea_room_extension/space_settings_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/space_settings_extension.dart @@ -15,8 +15,14 @@ extension SpaceRoomExtension on Room { } return "Not in a class!"; } - - return canonicalAlias.replaceAll(":$domainString", "").replaceAll("#", ""); + final roomJoinRules = getState(EventTypes.RoomJoinRules, ""); + if (roomJoinRules != null) { + final accessCode = roomJoinRules.content.tryGet(ModelKey.accessCode); + if (accessCode is String) { + return accessCode; + } + } + return "No class code!"; } void _checkClass() { diff --git a/lib/pangea/utils/space_code.dart b/lib/pangea/utils/space_code.dart index 9b1a44525..376450101 100644 --- a/lib/pangea/utils/space_code.dart +++ b/lib/pangea/utils/space_code.dart @@ -1,10 +1,10 @@ -import 'dart:math'; +import 'dart:convert'; import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; - -import '../controllers/pangea_controller.dart'; +import 'package:matrix/matrix.dart'; class SpaceCodeUtil { static const codeLength = 6; @@ -13,11 +13,25 @@ class SpaceCodeUtil { return spacecode == null || spacecode.length > 4; } - static String generateSpaceCode() { - final r = Random(); - const chars = 'AaBbCcDdEeFfGgHhiJjKkLMmNnoPpQqRrSsTtUuVvWwXxYyZz1234567890'; - return List.generate(codeLength, (index) => chars[r.nextInt(chars.length)]) - .join(); + static Future generateSpaceCode(Client client) async { + final response = await client.httpClient.get( + Uri.parse( + '${client.homeserver}/_synapse/client/pangea/v1/request_room_code', + ), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ${client.accessToken}', + }, + ); + if (response.statusCode != 200) { + return null; + } + final roomCodeResult = jsonDecode(response.body); + if (roomCodeResult['access_code'] is String) { + return roomCodeResult['access_code'] as String; + } else { + throw Exception('Invalid response, access_code not found $response'); + } } static Future joinWithSpaceCodeDialog( From f780f450ee8324b18a3827de2f1742ac6f3a05d1 Mon Sep 17 00:00:00 2001 From: WilsonLe Date: Tue, 15 Oct 2024 17:10:34 +1100 Subject: [PATCH 2/8] Student enter invalid code, shows class room not found error --- lib/pages/new_space/new_space.dart | 16 +++------------- lib/pangea/constants/class_code_constants.dart | 3 +++ lib/pangea/controllers/class_controller.dart | 11 ++++++----- .../pangea_room_extension.dart | 1 + .../space_settings_extension.dart | 2 +- lib/pangea/utils/chat_list_handle_space_tap.dart | 4 ++++ lib/pangea/utils/space_code.dart | 5 +++-- 7 files changed, 21 insertions(+), 21 deletions(-) create mode 100644 lib/pangea/constants/class_code_constants.dart diff --git a/lib/pages/new_space/new_space.dart b/lib/pages/new_space/new_space.dart index 9f4465c6e..029da0202 100644 --- a/lib/pages/new_space/new_space.dart +++ b/lib/pages/new_space/new_space.dart @@ -1,6 +1,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/constants/model_keys.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/utils/bot_name.dart'; @@ -156,23 +157,12 @@ class NewSpaceController extends State { } final spaceId = await client.createRoom( // #Pangea - // preset: publicGroup - // ? sdk.CreateRoomPreset.publicChat - // : sdk.CreateRoomPreset.privateChat, preset: sdk.CreateRoomPreset.publicChat, - // Pangea# creationContent: {'type': RoomCreationTypes.mSpace}, visibility: publicGroup ? sdk.Visibility.public : null, - // #Pangea - // roomAliasName: publicGroup - // ? nameController.text.trim().toLowerCase().replaceAll(' ', '_') - // : null, - roomAliasName: classCode, - // Pangea# name: nameController.text.trim(), topic: topicController.text.isEmpty ? null : topicController.text, // #Pangea - // powerLevelContentOverride: {'events_default': 100}, powerLevelContentOverride: addToSpaceKey.currentState != null ? await ClassChatPowerLevels.powerLevelOverrideForClassChat( context, @@ -192,8 +182,8 @@ class NewSpaceController extends State { sdk.StateEvent( type: sdk.EventTypes.RoomJoinRules, content: { - 'join_rule': 'knock', - 'access_code': classCode, + ModelKey.joinRule: sdk.JoinRules.knock, + ModelKey.accessCode: classCode, }, ), ], diff --git a/lib/pangea/constants/class_code_constants.dart b/lib/pangea/constants/class_code_constants.dart new file mode 100644 index 000000000..491834a0f --- /dev/null +++ b/lib/pangea/constants/class_code_constants.dart @@ -0,0 +1,3 @@ +const String noClassCode = 'No class code!'; + +String? justInputtedCode; diff --git a/lib/pangea/controllers/class_controller.dart b/lib/pangea/controllers/class_controller.dart index 48623fe9d..5f290a8e8 100644 --- a/lib/pangea/controllers/class_controller.dart +++ b/lib/pangea/controllers/class_controller.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:developer'; +import 'package:fluffychat/pangea/constants/class_code_constants.dart'; import 'package:fluffychat/pangea/constants/local.key.dart'; import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; @@ -85,8 +86,8 @@ class ClassController extends BaseController { return; } final knockResult = jsonDecode(knockResponse.body); - final foundClasses = knockResult['rooms']; - if (!(foundClasses is List && foundClasses.isNotEmpty)) { + final foundClasses = List.from(knockResult['rooms']); + if (foundClasses.isEmpty) { SpaceCodeUtil.messageSnack( context, L10n.of(context)!.unableToFindClass, @@ -94,16 +95,16 @@ class ClassController extends BaseController { return; } final chosenClassId = foundClasses.first; - await client.joinRoomById(chosenClassId); if (_pangeaController.matrixState.client.rooms .any((room) => room.id == chosenClassId)) { setActiveSpaceIdInChatListController(chosenClassId); SpaceCodeUtil.messageSnack(context, L10n.of(context)!.alreadyInClass); return; + } else { + justInputtedCode = classCode; + await client.joinRoomById(chosenClassId); } - await _pangeaController.matrixState.client.joinRoom(chosenClassId); - if (_pangeaController.matrixState.client.getRoomById(chosenClassId) == null) { await _pangeaController.matrixState.client.waitForRoomInSync( 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 813fb7f1c..162b4f238 100644 --- a/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart @@ -5,6 +5,7 @@ import 'dart:developer'; import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/constants/bot_mode.dart'; +import 'package:fluffychat/pangea/constants/class_code_constants.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'; diff --git a/lib/pangea/extensions/pangea_room_extension/space_settings_extension.dart b/lib/pangea/extensions/pangea_room_extension/space_settings_extension.dart index fce45f17a..60af461a2 100644 --- a/lib/pangea/extensions/pangea_room_extension/space_settings_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/space_settings_extension.dart @@ -22,7 +22,7 @@ extension SpaceRoomExtension on Room { return accessCode; } } - return "No class code!"; + return noClassCode; } void _checkClass() { diff --git a/lib/pangea/utils/chat_list_handle_space_tap.dart b/lib/pangea/utils/chat_list_handle_space_tap.dart index f6605d03f..02f0fab81 100644 --- a/lib/pangea/utils/chat_list_handle_space_tap.dart +++ b/lib/pangea/utils/chat_list_handle_space_tap.dart @@ -1,6 +1,7 @@ import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat_list/chat_list.dart'; +import 'package:fluffychat/pangea/constants/class_code_constants.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; @@ -107,6 +108,9 @@ void chatListHandleSpaceTap( ); if (rooms.any((s) => s.spaceChildren.any((c) => c.roomId == space.id))) { autoJoin(space); + } else if (justInputtedCode != null && + space.classCode == justInputtedCode) { + // do nothing } else { showAlertDialog(context); } diff --git a/lib/pangea/utils/space_code.dart b/lib/pangea/utils/space_code.dart index 376450101..946e4b1e3 100644 --- a/lib/pangea/utils/space_code.dart +++ b/lib/pangea/utils/space_code.dart @@ -7,10 +7,11 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; class SpaceCodeUtil { - static const codeLength = 6; + static const codeLength = 7; static bool isValidCode(String? spacecode) { - return spacecode == null || spacecode.length > 4; + if (spacecode == null) return false; + return spacecode.length == codeLength && spacecode.contains(r'[0-9]'); } static Future generateSpaceCode(Client client) async { From d2f1340f75b88e61b5c21da97451c1afb78048dc Mon Sep 17 00:00:00 2001 From: WilsonLe Date: Tue, 15 Oct 2024 17:47:18 +1100 Subject: [PATCH 3/8] add rate limit, add already joined --- assets/l10n/intl_en.arb | 1 + lib/pangea/controllers/class_controller.dart | 15 +++++++++++++++ lib/pangea/utils/space_code.dart | 1 + 3 files changed, 17 insertions(+) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 437b6d075..88e80bb44 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -4086,6 +4086,7 @@ } }, "roomCapacityExplanation": "{roomType} capacity limits the number of non-admins allowed in a room.", + "tooManyRequest": "Too many request, please try again later.", "@roomCapacityExplanation": { "type": "text", "placeholders": { diff --git a/lib/pangea/controllers/class_controller.dart b/lib/pangea/controllers/class_controller.dart index 5f290a8e8..afb60d8aa 100644 --- a/lib/pangea/controllers/class_controller.dart +++ b/lib/pangea/controllers/class_controller.dart @@ -78,6 +78,13 @@ class ClassController extends BaseController { }, body: jsonEncode({'access_code': classCode}), ); + if (knockResponse.statusCode == 429) { + SpaceCodeUtil.messageSnack( + context, + L10n.of(context)!.tooManyRequest, + ); + return; + } if (knockResponse.statusCode != 200) { SpaceCodeUtil.messageSnack( context, @@ -87,6 +94,14 @@ class ClassController extends BaseController { } final knockResult = jsonDecode(knockResponse.body); final foundClasses = List.from(knockResult['rooms']); + final alreadyJoined = List.from(knockResult['already_joined']); + if (alreadyJoined.isNotEmpty) { + SpaceCodeUtil.messageSnack( + context, + L10n.of(context)!.alreadyInClass, + ); + return; + } if (foundClasses.isEmpty) { SpaceCodeUtil.messageSnack( context, diff --git a/lib/pangea/utils/space_code.dart b/lib/pangea/utils/space_code.dart index 946e4b1e3..0eaef5751 100644 --- a/lib/pangea/utils/space_code.dart +++ b/lib/pangea/utils/space_code.dart @@ -79,6 +79,7 @@ class SpaceCodeUtil { SnackBar( duration: const Duration(seconds: 10), content: Text(message), + showCloseIcon: true, ), ); } From c9a4733fb6bf9f94c5f32ed1c9113367bf1c37df Mon Sep 17 00:00:00 2001 From: WilsonLe Date: Tue, 15 Oct 2024 18:04:19 +1100 Subject: [PATCH 4/8] add join with link --- .../constants/class_code_constants.dart | 2 -- lib/pangea/constants/local.key.dart | 1 + lib/pangea/controllers/class_controller.dart | 19 ++++++++++--------- .../utils/chat_list_handle_space_tap.dart | 8 ++++++-- lib/pangea/widgets/class/join_with_link.dart | 10 ++++------ 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/lib/pangea/constants/class_code_constants.dart b/lib/pangea/constants/class_code_constants.dart index 491834a0f..6dd35c2d4 100644 --- a/lib/pangea/constants/class_code_constants.dart +++ b/lib/pangea/constants/class_code_constants.dart @@ -1,3 +1 @@ const String noClassCode = 'No class code!'; - -String? justInputtedCode; diff --git a/lib/pangea/constants/local.key.dart b/lib/pangea/constants/local.key.dart index 1c6f1f37b..2e2dadc9d 100644 --- a/lib/pangea/constants/local.key.dart +++ b/lib/pangea/constants/local.key.dart @@ -6,4 +6,5 @@ class PLocalKey { static const String paywallBackoff = 'paywallBackoff'; static const String messagesSinceUpdate = 'messagesSinceLastUpdate'; static const String completedActivities = 'completedActivities'; + static const String justInputtedCode = 'justInputtedCode'; } diff --git a/lib/pangea/controllers/class_controller.dart b/lib/pangea/controllers/class_controller.dart index afb60d8aa..186cac389 100644 --- a/lib/pangea/controllers/class_controller.dart +++ b/lib/pangea/controllers/class_controller.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:developer'; -import 'package:fluffychat/pangea/constants/class_code_constants.dart'; import 'package:fluffychat/pangea/constants/local.key.dart'; import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; @@ -51,16 +50,13 @@ class ClassController extends BaseController { ); if (classCode != null) { - await _pangeaController.pStoreService.delete( - PLocalKey.cachedClassCodeToJoin, - isAccountData: false, - ); await joinClasswithCode( context, classCode, - ).onError( - (error, stackTrace) => - SpaceCodeUtil.messageSnack(context, ErrorCopy(context, error).body), + ); + await _pangeaController.pStoreService.delete( + PLocalKey.cachedClassCodeToJoin, + isAccountData: false, ); } } @@ -116,8 +112,13 @@ class ClassController extends BaseController { SpaceCodeUtil.messageSnack(context, L10n.of(context)!.alreadyInClass); return; } else { - justInputtedCode = classCode; + await _pangeaController.pStoreService.save( + PLocalKey.justInputtedCode, + classCode, + isAccountData: false, + ); await client.joinRoomById(chosenClassId); + _pangeaController.pStoreService.delete(PLocalKey.justInputtedCode); } if (_pangeaController.matrixState.client.getRoomById(chosenClassId) == diff --git a/lib/pangea/utils/chat_list_handle_space_tap.dart b/lib/pangea/utils/chat_list_handle_space_tap.dart index 02f0fab81..2c38f9eb1 100644 --- a/lib/pangea/utils/chat_list_handle_space_tap.dart +++ b/lib/pangea/utils/chat_list_handle_space_tap.dart @@ -1,7 +1,8 @@ import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat_list/chat_list.dart'; -import 'package:fluffychat/pangea/constants/class_code_constants.dart'; +import 'package:fluffychat/pangea/constants/local.key.dart'; +import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; @@ -18,6 +19,7 @@ void chatListHandleSpaceTap( ChatListController controller, Room space, ) { + final PangeaController pangeaController = MatrixState.pangeaController; void setActiveSpaceAndCloseChat() { controller.setActiveSpace(space.id); @@ -106,10 +108,12 @@ void chatListHandleSpaceTap( (element) => element.isSpace && element.membership == Membership.join, ); + final justInputtedCode = pangeaController.pStoreService + .read(PLocalKey.justInputtedCode, isAccountData: false); if (rooms.any((s) => s.spaceChildren.any((c) => c.roomId == space.id))) { autoJoin(space); } else if (justInputtedCode != null && - space.classCode == justInputtedCode) { + justInputtedCode == space.classCode) { // do nothing } else { showAlertDialog(context); diff --git a/lib/pangea/widgets/class/join_with_link.dart b/lib/pangea/widgets/class/join_with_link.dart index a20f7f1e1..fbfbf331d 100644 --- a/lib/pangea/widgets/class/join_with_link.dart +++ b/lib/pangea/widgets/class/join_with_link.dart @@ -1,12 +1,11 @@ +import 'package:fluffychat/pangea/constants/local.key.dart'; import 'package:fluffychat/pangea/constants/url_query_parameter_keys.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; +import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import '../../../widgets/matrix.dart'; -import '../../constants/local.key.dart'; - //if on home with classcode in url and not logged in, then save it soemhow and after llogin, join class automatically //if on home with classcode in url and logged in, then join class automatically class JoinClassWithLink extends StatefulWidget { @@ -19,7 +18,7 @@ class JoinClassWithLink extends StatefulWidget { //PTODO - show class info in field so they know they're joining the right class class _JoinClassWithLinkState extends State { String? classCode; - final PangeaController _pangeaController = MatrixState.pangeaController; + final PangeaController pangeaController = MatrixState.pangeaController; @override void initState() { @@ -39,8 +38,7 @@ class _JoinClassWithLinkState extends State { ); return; } - - await _pangeaController.pStoreService.save( + await pangeaController.pStoreService.save( PLocalKey.cachedClassCodeToJoin, classCode, isAccountData: false, From 450e19f4817676ef7cbdce8a6fe5938368f00a21 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 15 Oct 2024 14:22:31 -0400 Subject: [PATCH 5/8] make join rule in initial state JSON encodable --- lib/pages/new_space/new_space.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pages/new_space/new_space.dart b/lib/pages/new_space/new_space.dart index 029da0202..1bf6ae9b5 100644 --- a/lib/pages/new_space/new_space.dart +++ b/lib/pages/new_space/new_space.dart @@ -182,7 +182,8 @@ class NewSpaceController extends State { sdk.StateEvent( type: sdk.EventTypes.RoomJoinRules, content: { - ModelKey.joinRule: sdk.JoinRules.knock, + ModelKey.joinRule: + sdk.JoinRules.knock.toString().replaceAll('JoinRules.', ''), ModelKey.accessCode: classCode, }, ), From a806f736c069f232a29e03128276fc634036f26a Mon Sep 17 00:00:00 2001 From: WilsonLe Date: Wed, 16 Oct 2024 18:57:12 +1100 Subject: [PATCH 6/8] make class code non null, throw errors on non-200 responses --- lib/pages/new_space/new_space.dart | 21 ++++++++++++++------- lib/pangea/utils/space_code.dart | 4 ++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/pages/new_space/new_space.dart b/lib/pages/new_space/new_space.dart index 1bf6ae9b5..9fb8d787d 100644 --- a/lib/pages/new_space/new_space.dart +++ b/lib/pages/new_space/new_space.dart @@ -28,8 +28,8 @@ class NewSpaceController extends State { TextEditingController nameController = TextEditingController(); TextEditingController topicController = TextEditingController(); // #Pangea - // bool publicGroup = false; - bool publicGroup = true; + bool publicGroup = false; + // bool publicGroup = true; // final GlobalKey rulesEditorKey = GlobalKey(); final GlobalKey addToSpaceKey = GlobalKey(); // commenting out language settings in spaces for now @@ -152,14 +152,20 @@ class NewSpaceController extends State { final avatar = this.avatar; avatarUrl ??= avatar == null ? null : await client.uploadContent(avatar); final classCode = await SpaceCodeUtil.generateSpaceCode(client); - if (classCode == null) { - return; - } final spaceId = await client.createRoom( // #Pangea - preset: sdk.CreateRoomPreset.publicChat, + preset: publicGroup + ? sdk.CreateRoomPreset.publicChat + : sdk.CreateRoomPreset.privateChat, + // #Pangea creationContent: {'type': RoomCreationTypes.mSpace}, visibility: publicGroup ? sdk.Visibility.public : null, + // #Pangea + // roomAliasName: publicGroup + // ? nameController.text.trim().toLowerCase().replaceAll(' ', '_') + // : null, + // roomAliasName: SpaceCodeUtil.generateSpaceCode(), + // Pangea# name: nameController.text.trim(), topic: topicController.text.isEmpty ? null : topicController.text, // #Pangea @@ -173,7 +179,6 @@ class NewSpaceController extends State { initialState: [ // #Pangea ...initialState, - // Pangea# if (avatar != null) sdk.StateEvent( type: sdk.EventTypes.RoomAvatar, @@ -187,7 +192,9 @@ class NewSpaceController extends State { ModelKey.accessCode: classCode, }, ), + // Pangea# ], + // Pangea# ); // #Pangea final List> futures = [ diff --git a/lib/pangea/utils/space_code.dart b/lib/pangea/utils/space_code.dart index 0eaef5751..957e04ca8 100644 --- a/lib/pangea/utils/space_code.dart +++ b/lib/pangea/utils/space_code.dart @@ -14,7 +14,7 @@ class SpaceCodeUtil { return spacecode.length == codeLength && spacecode.contains(r'[0-9]'); } - static Future generateSpaceCode(Client client) async { + static Future generateSpaceCode(Client client) async { final response = await client.httpClient.get( Uri.parse( '${client.homeserver}/_synapse/client/pangea/v1/request_room_code', @@ -25,7 +25,7 @@ class SpaceCodeUtil { }, ); if (response.statusCode != 200) { - return null; + throw Exception('Failed to generate room code: $response'); } final roomCodeResult = jsonDecode(response.body); if (roomCodeResult['access_code'] is String) { From 4f8bb9e4ba7ea65fe4c565eb8d2072f261918a12 Mon Sep 17 00:00:00 2001 From: WilsonLe Date: Wed, 16 Oct 2024 19:02:33 +1100 Subject: [PATCH 7/8] added back comments --- lib/pages/new_space/new_space.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pages/new_space/new_space.dart b/lib/pages/new_space/new_space.dart index 9fb8d787d..d5f8b37c6 100644 --- a/lib/pages/new_space/new_space.dart +++ b/lib/pages/new_space/new_space.dart @@ -169,6 +169,7 @@ class NewSpaceController extends State { name: nameController.text.trim(), topic: topicController.text.isEmpty ? null : topicController.text, // #Pangea + // powerLevelContentOverride: {'events_default': 100}, powerLevelContentOverride: addToSpaceKey.currentState != null ? await ClassChatPowerLevels.powerLevelOverrideForClassChat( context, From ce07c9cb72fcb63dc1fc78389908fb648d2f3a6a Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 16 Oct 2024 09:51:18 -0400 Subject: [PATCH 8/8] wrap new space submit function in futureLoadingDialog to display to the user if there's an error in the space creation flow --- lib/pages/new_space/new_space.dart | 205 +++++++++++++++-------------- 1 file changed, 109 insertions(+), 96 deletions(-) diff --git a/lib/pages/new_space/new_space.dart b/lib/pages/new_space/new_space.dart index d5f8b37c6..0a7b809d8 100644 --- a/lib/pages/new_space/new_space.dart +++ b/lib/pages/new_space/new_space.dart @@ -14,6 +14,7 @@ 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 'package:matrix/matrix.dart' as sdk; import 'package:matrix/matrix.dart'; @@ -148,106 +149,118 @@ class NewSpaceController extends State { setState(() { loading = true; }); - try { - final avatar = this.avatar; - avatarUrl ??= avatar == null ? null : await client.uploadContent(avatar); - final classCode = await SpaceCodeUtil.generateSpaceCode(client); - final spaceId = await client.createRoom( - // #Pangea - preset: publicGroup - ? sdk.CreateRoomPreset.publicChat - : sdk.CreateRoomPreset.privateChat, - // #Pangea - creationContent: {'type': RoomCreationTypes.mSpace}, - visibility: publicGroup ? sdk.Visibility.public : null, - // #Pangea - // roomAliasName: publicGroup - // ? nameController.text.trim().toLowerCase().replaceAll(' ', '_') - // : null, - // roomAliasName: SpaceCodeUtil.generateSpaceCode(), - // Pangea# - name: nameController.text.trim(), - topic: topicController.text.isEmpty ? null : topicController.text, - // #Pangea - // powerLevelContentOverride: {'events_default': 100}, - powerLevelContentOverride: addToSpaceKey.currentState != null - ? await ClassChatPowerLevels.powerLevelOverrideForClassChat( - context, - addToSpaceKey.currentState!.parent, - ) - : null, - // Pangea# - initialState: [ - // #Pangea - ...initialState, - if (avatar != null) - sdk.StateEvent( - type: sdk.EventTypes.RoomAvatar, - content: {'url': avatarUrl.toString()}, - ), - sdk.StateEvent( - type: sdk.EventTypes.RoomJoinRules, - content: { - ModelKey.joinRule: - sdk.JoinRules.knock.toString().replaceAll('JoinRules.', ''), - ModelKey.accessCode: classCode, - }, - ), + // #Pangea + // try { + await showFutureLoadingDialog( + context: context, + future: () async { + try { // Pangea# - ], - // Pangea# - ); - // #Pangea - final List> futures = [ - Matrix.of(context).client.waitForRoomInSync(spaceId, join: true), - ]; - if (addToSpaceKey.currentState != null) { - futures.add(addToSpaceKey.currentState!.addSpaces(spaceId)); - } - await Future.wait(futures); + final avatar = this.avatar; + avatarUrl ??= + avatar == null ? null : await client.uploadContent(avatar); + final classCode = await SpaceCodeUtil.generateSpaceCode(client); + final spaceId = await client.createRoom( + // #Pangea + preset: publicGroup + ? sdk.CreateRoomPreset.publicChat + : sdk.CreateRoomPreset.privateChat, + // #Pangea + creationContent: {'type': RoomCreationTypes.mSpace}, + visibility: publicGroup ? sdk.Visibility.public : null, + // #Pangea + // roomAliasName: publicGroup + // ? nameController.text.trim().toLowerCase().replaceAll(' ', '_') + // : null, + // roomAliasName: SpaceCodeUtil.generateSpaceCode(), + // Pangea# + name: nameController.text.trim(), + topic: topicController.text.isEmpty ? null : topicController.text, + // #Pangea + // powerLevelContentOverride: {'events_default': 100}, + powerLevelContentOverride: addToSpaceKey.currentState != null + ? await ClassChatPowerLevels.powerLevelOverrideForClassChat( + context, + addToSpaceKey.currentState!.parent, + ) + : null, + // Pangea# + initialState: [ + // #Pangea + ...initialState, + if (avatar != null) + sdk.StateEvent( + type: sdk.EventTypes.RoomAvatar, + content: {'url': avatarUrl.toString()}, + ), + sdk.StateEvent( + type: sdk.EventTypes.RoomJoinRules, + content: { + ModelKey.joinRule: sdk.JoinRules.knock + .toString() + .replaceAll('JoinRules.', ''), + ModelKey.accessCode: classCode, + }, + ), + // Pangea# + ], + // Pangea# + ); + // #Pangea + final List> futures = [ + Matrix.of(context).client.waitForRoomInSync(spaceId, join: true), + ]; + if (addToSpaceKey.currentState != null) { + futures.add(addToSpaceKey.currentState!.addSpaces(spaceId)); + } + await Future.wait(futures); - final capacity = addCapacityKey.currentState?.capacity; - final space = client.getRoomById(spaceId); - if (capacity != null && space != null) { - space.updateRoomCapacity(capacity); - } + final capacity = addCapacityKey.currentState?.capacity; + final space = client.getRoomById(spaceId); + if (capacity != null && space != null) { + space.updateRoomCapacity(capacity); + } - final Room? room = Matrix.of(context).client.getRoomById(spaceId); - if (room == null) { - ErrorHandler.logError( - e: 'Failed to get new space by id $spaceId', - ); - MatrixState.pangeaController.classController - .setActiveSpaceIdInChatListController(spaceId); - return; - } + final Room? room = Matrix.of(context).client.getRoomById(spaceId); + if (room == null) { + ErrorHandler.logError( + e: 'Failed to get new space by id $spaceId', + ); + MatrixState.pangeaController.classController + .setActiveSpaceIdInChatListController(spaceId); + return; + } - GoogleAnalytics.createClass(room.name, room.classCode); - try { - await room.invite(BotName.byEnvironment); - } catch (err) { - ErrorHandler.logError( - e: "Failed to invite pangea bot to space ${room.id}", - ); - } - // Pangea# - if (!mounted) return; - // #Pangea - // context.pop(spaceId); - MatrixState.pangeaController.classController - .setActiveSpaceIdInChatListController(spaceId); - // Pangea# - } catch (e) { - setState(() { - // #Pangea - // topicError = e.toLocalizedString(context); - // Pangea# - }); - } finally { - setState(() { - loading = false; - }); - } + GoogleAnalytics.createClass(room.name, room.classCode); + try { + await room.invite(BotName.byEnvironment); + } catch (err) { + ErrorHandler.logError( + e: "Failed to invite pangea bot to space ${room.id}", + ); + } + // Pangea# + if (!mounted) return; + // #Pangea + // context.pop(spaceId); + MatrixState.pangeaController.classController + .setActiveSpaceIdInChatListController(spaceId); + // Pangea# + } catch (e, s) { + // #Pangea + ErrorHandler.logError(e: e, s: s); + rethrow; + // setState(() { + // topicError = e.toLocalizedString(context); + // }); + // Pangea# + } finally { + setState(() { + loading = false; + }); + } + }, + ); // TODO: Go to spaces }