diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index bb9d15f0c..ef3042700 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -3128,7 +3128,7 @@ "maybeLater": "Maybe Later", "mainMenu": "Main Menu", "toggleImmersionMode": "Immersion Mode", - "toggleImmersionModeDesc": "When enabled, all messages are displayed in your target language and you can click the message to access definitions and translations.", + "toggleImmersionModeDesc": "When enabled, all messages are displayed in your target language. This setting is most useful in language exchanges.", "itToggleDescription": "This language learning tool will identify words in your base language and help you translate them to your target language. Though rare, the AI can make translation errors.", "igcToggleDescription": "This language learning tool will identify common spelling, grammar and punctuation errors in your message and suggest corrections. Though rare, the AI can make correction errors.", "sendOnEnterDescription": "Turn this off to be able to add line spaces in messages. When the toggle is off on the browser app, you can press Shift + Enter to start a new line. When the toggle is off on mobile apps, just Enter will start a new line.", @@ -3609,7 +3609,7 @@ "zmCountryDisplayName": "Zambia", "zwCountryDisplayName": "Zimbabwe", "pay": "Pay", - "allPrivateChats": "All private chats in space (including with Pangea Bot)", + "allPrivateChats": "Direct chats", "unknownPrivateChat": "Unknown private chat", "copyClassCodeDesc": "Students who are already in the app can 'Join class or exchange' via the main menu.", "addToClass": "Add exchange to class", @@ -3681,24 +3681,8 @@ "lockSpace": "Lock Space", "lockChat": "Lock Chat", "archiveSpace": "Archive Space", - "suggestTo": "Suggest to {spaceName}", - "@suggestTo": { - "placeholders": { - "spaceName": {} - } - }, - "suggestChatDesc": "Suggested chats will appear in the chat list for {spaceName}", - "@suggestToDesc": { - "placeholders": { - "spaceName": {} - } - }, - "suggestExchangeDesc": "Suggested exchanges will appear in the chat list for {spaceName}", - "@suggestToExchangeDesc": { - "placeholders": { - "spaceName": {} - } - }, + "suggestToChat": "Suggest this chat", + "suggestToChatDesc": "Suggested chats will appear in chat lists", "acceptSelection": "Accept Correction", "acceptSelectionAnyway": "Use this anyway", "makingActivity": "Making activity", @@ -3724,7 +3708,7 @@ }, "noTeachersFound": "No teachers found to report to", "pleaseEnterANumber": "Please enter a number greater than 0", - "archiveRoomDescription": "The chat will be moved to the archive. Other users will be able to see that you have left the chat.", + "archiveRoomDescription": "The chat will be moved to the archive for yourself and other non-admin users.", "roomUpgradeDescription": "The chat will then be recreated with the new room version. All participants will be notified that they need to switch to the new chat. You can find out more about room versions at https://spec.matrix.org/latest/rooms/", "removeDevicesDescription": "You will be logged out of this device and will no longer be able to receive messages.", "banUserDescription": "The user will be banned from the chat and will not be able to enter the chat again until they are unbanned.", @@ -3963,5 +3947,17 @@ "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", + "autoIGCToolName": "Run Language Assistance Automatically", + "autoIGCToolDescription": "Automatically run language assistance after typing messages", + "runGrammarCorrection": "Run grammar correction", + "grammarCorrectionFailed": "Grammar correction failed", + "grammarCorrectionComplete": "Grammar correction complete", + "leaveRoomDescription": "The chat will be moved to the archive. Other users will be able to see that you have left the chat.", + "archiveSpaceDescription": "All chats within this space will be moved to the archive for yourself and other non-admin users.", + "leaveSpaceDescription": "All chats within this space will be moved to the archive. Other users will be able to see that you have left the space.", + "onlyAdminDescription": "Since there are no other admins, all other participants will also be removed.", + "tooltipInstructionsTitle": "Not sure what that does?", + "tooltipInstructionsMobileBody": "Press and hold items to view tooltips.", + "tooltipInstructionsBrowserBody": "Hover over items to view tooltips." } \ No newline at end of file diff --git a/assets/l10n/intl_es.arb b/assets/l10n/intl_es.arb index d8f54304a..fa097d393 100644 --- a/assets/l10n/intl_es.arb +++ b/assets/l10n/intl_es.arb @@ -3281,7 +3281,7 @@ "generateVocabulary": "Generar vocabulario basado en el título y la descripción", "generatePrompts": "Generar preguntas basado en el título y la descripción", "toggleImmersionMode": "Modo de inmersión", - "toggleImmersionModeDesc": "Cuando está habilitado, todos los mensajes se muestran en su idioma de destino y puede hacer clic en el mensaje para acceder a definiciones y traducciones.", + "toggleImmersionModeDesc": "Cuando está habilitado, todos los mensajes se muestran en su idioma de destino. Esta configuración es más útil en intercambios de idiomas.", "subscribe": "Subscríbase", "getAccess": "Activar herramientas", "subscriptionDesc": "¡Enviar y recibir mensajes es gratis! Suscríbase para aceder a la traducción interactiva, la revisión gramatical y el análisis de aprendizaje.", @@ -3698,7 +3698,7 @@ "@optionalRedactReason": {}, "dehydrate": "Exportar sesión y borrar dispositivo", "@dehydrate": {}, - "archiveRoomDescription": "", + "archiveRoomDescription": "El chat se moverá al archivo para ti y para otros usuarios que no sean administradores", "@archiveRoomDescription": {}, "pleaseEnterRecoveryKeyDescription": "Para desbloquear sus mensajes antiguos, ingrese su clave de recuperación que se generó en una sesión anterior. Su clave de recuperación NO es su contraseña.", "@pleaseEnterRecoveryKeyDescription": {}, @@ -4274,7 +4274,7 @@ "zwCountryDisplayName": "Zimbabue", "downloadXLSXFile": "Descargar archivo Excel", "unknownPrivateChat": "Chat Privado Desconocido", - "allPrivateChats": "Todos los chats privados (incluso con bots) en clase", + "allPrivateChats": "Chats privado", "chatHasBeenAddedToThisSpace": "Se ha añadido el chat a este espacio", "classes": "Clases", "spaceIsPublic": "El espacio es público", diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index c5068a62a..81d6c411a 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -227,6 +227,17 @@ class ChatController extends State context.go('/rooms'); } + // #Pangea + void archiveChat() async { + final success = await showFutureLoadingDialog( + context: context, + future: room.archive, + ); + if (success.error != null) return; + context.go('/rooms'); + } + // Pangea# + EmojiPickerType emojiPickerType = EmojiPickerType.keyboard; // #Pangea diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 0f2d1860d..317d81158 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -9,6 +9,7 @@ import 'package:fluffychat/pages/chat/reactions_picker.dart'; import 'package:fluffychat/pages/chat/reply_display.dart'; import 'package:fluffychat/pangea/choreographer/widgets/has_error_button.dart'; import 'package:fluffychat/pangea/choreographer/widgets/language_permissions_warning_buttons.dart'; +import 'package:fluffychat/pangea/choreographer/widgets/start_igc_button.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/pangea/pages/class_analytics/measure_able.dart'; import 'package:fluffychat/utils/account_config.dart'; @@ -367,6 +368,29 @@ class ChatView extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ + // #Pangea + if (controller.room.isRoomAdmin) + TextButton.icon( + style: TextButton.styleFrom( + padding: + const EdgeInsets.all( + 16, + ), + foregroundColor: + Theme.of(context) + .colorScheme + .error, + ), + icon: const Icon( + Icons.archive_outlined, + ), + onPressed: + controller.archiveChat, + label: Text( + L10n.of(context)!.archive, + ), + ), + // Pangea# TextButton.icon( style: TextButton.styleFrom( padding: const EdgeInsets.all( @@ -378,7 +402,10 @@ class ChatView extends StatelessWidget { .error, ), icon: const Icon( - Icons.archive_outlined, + // #Pangea + // Icons.archive_outlined, + Icons.arrow_forward, + // Pangea# ), onPressed: controller.leaveChat, label: Text( @@ -423,8 +450,8 @@ class ChatView extends StatelessWidget { // #Pangea // if (controller.dragging) // Container( - // color: Theme.of(context) - // .scaffoldBackgroundColor + // color: Theme.of(context) + // .scaffoldBackgroundColor // .withOpacity(0.9), // alignment: Alignment.center, // child: const Icon( @@ -432,6 +459,11 @@ class ChatView extends StatelessWidget { // size: 100, // ), // ), + Positioned( + left: 20, + bottom: 75, + child: StartIGCButton(controller: controller), + ), // Pangea# ], ), diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 41997afea..2d27807b8 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -199,18 +199,8 @@ class MessageContent extends StatelessWidget { case MessageTypes.Notice: case MessageTypes.Emote: if (AppConfig.renderHtml && - !event.redacted && - event.isRichMessage - // #Pangea - && - !(pangeaMessageEvent?.showRichText( - selected, - isOverlay: isOverlay, - highlighted: toolbarController?.highlighted ?? false, - ) ?? - false) - // Pangea# - ) { + !event.redacted && + event.isRichMessage) { var html = event.formattedText; if (event.messageType == MessageTypes.Emote) { html = '* $html'; @@ -306,18 +296,17 @@ class MessageContent extends StatelessWidget { decoration: event.redacted ? TextDecoration.lineThrough : null, height: 1.3, ); - if (pangeaMessageEvent?.showRichText( - selected, - isOverlay: isOverlay, - highlighted: toolbarController?.highlighted ?? false, - ) ?? - false) { + if (immersionMode && pangeaMessageEvent != null) { return PangeaRichText( style: messageTextStyle, pangeaMessageEvent: pangeaMessageEvent!, immersionMode: immersionMode, toolbarController: toolbarController, ); + } else if (pangeaMessageEvent != null) { + toolbarController?.toolbar?.textSelection.setMessageText( + pangeaMessageEvent!.body, + ); } // Pangea# return FutureBuilder( diff --git a/lib/pages/chat_details/chat_details_view.dart b/lib/pages/chat_details/chat_details_view.dart index 6b7ce5616..57e2a43e8 100644 --- a/lib/pages/chat_details/chat_details_view.dart +++ b/lib/pages/chat_details/chat_details_view.dart @@ -9,7 +9,6 @@ import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/class_det 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_rules_editor.dart'; -import 'package:fluffychat/pangea/utils/archive_space.dart'; import 'package:fluffychat/pangea/utils/lock_room.dart'; import 'package:fluffychat/pangea/widgets/class/add_class_and_invite.dart'; import 'package:fluffychat/pangea/widgets/class/add_space_toggles.dart'; @@ -522,52 +521,126 @@ class ChatDetailsView extends StatelessWidget { ), const Divider(height: 1), if (!room.isDirectChat) - ListTile( - title: Text( - room.isSpace - ? L10n.of(context)!.archiveSpace - : L10n.of(context)!.archive, - style: TextStyle( - color: Theme.of(context).colorScheme.secondary, - fontWeight: FontWeight.bold, + if (room.isRoomAdmin) + ListTile( + title: Text( + room.isSpace + ? L10n.of(context)!.archiveSpace + : L10n.of(context)!.archive, + style: TextStyle( + color: + Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.bold, + ), ), - ), - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: iconColor, - child: const Icon( - Icons.archive_outlined, + leading: CircleAvatar( + backgroundColor: + Theme.of(context).scaffoldBackgroundColor, + foregroundColor: iconColor, + child: const Icon( + Icons.archive_outlined, + ), ), + onTap: () async { + OkCancelResult confirmed = OkCancelResult.ok; + bool shouldGo = false; + // archiveSpace has its own popup; only show if not space + if (!room.isSpace) { + confirmed = await showOkCancelAlertDialog( + useRootNavigator: false, + context: context, + title: L10n.of(context)!.areYouSure, + okLabel: L10n.of(context)!.ok, + cancelLabel: L10n.of(context)!.cancel, + message: L10n.of(context)! + .archiveRoomDescription, + ); + } + if (confirmed == OkCancelResult.ok) { + if (room.isSpace) { + shouldGo = await room.archiveSpace( + context, + Matrix.of(context).client, + ); + } else { + final success = + await showFutureLoadingDialog( + context: context, + future: () async { + await room.archive(); + }, + ); + shouldGo = (success.error == null); + } + if (shouldGo) { + context.go('/rooms'); + } + } + }, ), - onTap: () async { - final confirmed = await showOkCancelAlertDialog( + ListTile( + title: Text( + L10n.of(context)!.leave, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.bold, + ), + ), + leading: CircleAvatar( + backgroundColor: + Theme.of(context).scaffoldBackgroundColor, + foregroundColor: iconColor, + child: const Icon( + Icons.arrow_forward, + ), + ), + onTap: () async { + OkCancelResult confirmed = OkCancelResult.ok; + bool shouldGo = false; + // If user is only admin, room will be archived + final bool onlyAdmin = await room.isOnlyAdmin(); + // archiveSpace has its own popup; only show if not space + if (!room.isSpace) { + confirmed = await showOkCancelAlertDialog( useRootNavigator: false, context: context, title: L10n.of(context)!.areYouSure, okLabel: L10n.of(context)!.ok, cancelLabel: L10n.of(context)!.cancel, - message: - L10n.of(context)!.archiveRoomDescription, + message: onlyAdmin + ? L10n.of(context)!.onlyAdminDescription + : L10n.of(context)!.leaveRoomDescription, ); - if (confirmed == OkCancelResult.ok) { + } + if (confirmed == OkCancelResult.ok) { + if (room.isSpace) { + shouldGo = onlyAdmin + ? await room.archiveSpace( + context, + Matrix.of(context).client, + onlyAdmin: true, + ) + : await room.leaveSpace( + context, + Matrix.of(context).client, + ); + } else { final success = await showFutureLoadingDialog( context: context, future: () async { - room.isSpace - ? await archiveSpace( - room, - Matrix.of(context).client, - ) + onlyAdmin + ? await room.archive() : await room.leave(); }, ); - if (success.error == null) { - context.go('/rooms'); - } + shouldGo = (success.error == null); } - }, - ), + if (shouldGo) { + context.go('/rooms'); + } + } + }, + ), if (room.isRoomAdmin && !room.isDirectChat) SwitchListTile.adaptive( activeColor: AppConfig.activeToggleColor, diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index 5d6286404..d852e17e0 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -515,7 +515,8 @@ class ChatListController extends State //#Pangea classStream = pangeaController.classController.stateStream.listen((event) { - if (event["activeSpaceId"] != null && mounted) { + // if (event["activeSpaceId"] != null && mounted) { + if (mounted) { setActiveSpace(event["activeSpaceId"]); } }); @@ -679,6 +680,38 @@ class ChatListController extends State // Pangea# } + // #Pangea + Future leaveAction() async { + final bool onlyAdmin = await Matrix.of(context) + .client + .getRoomById(selectedRoomIds.first) + ?.isOnlyAdmin() ?? + false; + final confirmed = await showOkCancelAlertDialog( + useRootNavigator: false, + context: context, + title: L10n.of(context)!.areYouSure, + okLabel: L10n.of(context)!.yes, + cancelLabel: L10n.of(context)!.cancel, + message: onlyAdmin + ? L10n.of(context)!.onlyAdminDescription + : L10n.of(context)!.leaveRoomDescription, + ) == + OkCancelResult.ok; + if (!confirmed) return; + final bool leftActiveRoom = + selectedRoomIds.contains(Matrix.of(context).activeRoomId); + await showFutureLoadingDialog( + context: context, + future: () => _leaveSelectedRooms(onlyAdmin), + ); + setState(() {}); + if (leftActiveRoom) { + context.go('/rooms'); + } + } + // Pangea# + void dismissStatusList() async { final result = await showOkCancelAlertDialog( title: L10n.of(context)!.hidePresences, @@ -729,17 +762,35 @@ class ChatListController extends State final roomId = selectedRoomIds.first; try { // #Pangea - if (client.getRoomById(roomId)!.isUnread) { - await client.getRoomById(roomId)!.markUnread(false); - } + // await client.getRoomById(roomId)!.leave(); + await client.getRoomById(roomId)!.archive(); // Pangea# - await client.getRoomById(roomId)!.leave(); } finally { toggleSelection(roomId); } } } + // #Pangea + Future _leaveSelectedRooms(bool onlyAdmin) async { + final client = Matrix.of(context).client; + while (selectedRoomIds.isNotEmpty) { + final roomId = selectedRoomIds.first; + try { + final room = client.getRoomById(roomId); + if (!room!.isSpace && + room.membership == Membership.join && + room.isUnread) { + await room.markUnread(false); + } + onlyAdmin ? await room.archive() : await room.leave(); + } finally { + toggleSelection(roomId); + } + } + } + // Pangea# + Future addToSpace() async { final selectedSpace = await showConfirmationDialog( context: context, diff --git a/lib/pages/chat_list/chat_list_header.dart b/lib/pages/chat_list/chat_list_header.dart index c5973d8bb..12f2f6b44 100644 --- a/lib/pages/chat_list/chat_list_header.dart +++ b/lib/pages/chat_list/chat_list_header.dart @@ -1,6 +1,8 @@ import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat_list/chat_list.dart'; import 'package:fluffychat/pages/chat_list/client_chooser_button.dart'; +import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; +import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -168,14 +170,33 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget { tooltip: L10n.of(context)!.toggleMuted, onPressed: controller.toggleMuted, ), - IconButton( - // #Pangea - // icon: const Icon(Icons.delete_outlined), - icon: const Icon(Icons.archive_outlined), + // #Pangea + if (controller.selectedRoomIds.length == 1 && + !(Matrix.of(context) + .client + .getRoomById(controller.selectedRoomIds.single) + ?.isRoomAdmin ?? + false)) + IconButton( + icon: const Icon(Icons.arrow_forward), + tooltip: L10n.of(context)!.leave, + onPressed: controller.leaveAction, + ), + if (controller.selectedRoomIds.length == 1 && + (Matrix.of(context) + .client + .getRoomById(controller.selectedRoomIds.single) + ?.isRoomAdmin ?? + false)) // Pangea# - tooltip: L10n.of(context)!.archive, - onPressed: controller.archiveAction, - ), + IconButton( + // #Pangea + // icon: const Icon(Icons.delete_outlined), + icon: const Icon(Icons.archive_outlined), + // Pangea# + tooltip: L10n.of(context)!.archive, + onPressed: controller.archiveAction, + ), ] : null, ); diff --git a/lib/pages/chat_list/chat_list_item.dart b/lib/pages/chat_list/chat_list_item.dart index c917c8eff..927b97a79 100644 --- a/lib/pages/chat_list/chat_list_item.dart +++ b/lib/pages/chat_list/chat_list_item.dart @@ -53,14 +53,12 @@ class ChatListItem extends StatelessWidget { message: L10n.of(context)!.archiveRoomDescription, ); if (confirmed == OkCancelResult.cancel) return; - // #Pangea - if (room.isUnread) { - await room.markUnread(false); - } - // Pangea# await showFutureLoadingDialog( context: context, - future: () => room.leave(), + // #Pangea + // future: () => room.leave(), + future: () => room.archive(), + // Pangea# ); return; } diff --git a/lib/pages/chat_list/space_view.dart b/lib/pages/chat_list/space_view.dart index 74bea0910..1fde5f0f8 100644 --- a/lib/pages/chat_list/space_view.dart +++ b/lib/pages/chat_list/space_view.dart @@ -11,7 +11,6 @@ import 'package:fluffychat/pangea/constants/class_default_values.dart'; import 'package:fluffychat/pangea/constants/pangea_room_types.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/pangea/extensions/sync_update_extension.dart'; -import 'package:fluffychat/pangea/utils/archive_space.dart'; import 'package:fluffychat/pangea/utils/chat_list_handle_space_tap.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/widgets/avatar.dart'; @@ -230,7 +229,10 @@ class _SpaceViewState extends State { ), message: spaceChild?.topic ?? room?.topic, actions: [ - if (room == null) + // #Pangea + // if (room == null) + if (room == null || room.membership == Membership.leave) + // Pangea# SheetAction( key: SpaceChildContextAction.join, label: L10n.of(context)!.joinRoom, @@ -255,16 +257,21 @@ class _SpaceViewState extends State { label: L10n.of(context)!.addToSpace, icon: Icons.workspaces_outlined, ), - if (room != null && room.isRoomAdmin) + if (room != null && + room.isRoomAdmin && + room.membership != Membership.leave) SheetAction( key: SpaceChildContextAction.archive, label: room.isSpace ? L10n.of(context)!.archiveSpace : L10n.of(context)!.archive, icon: Icons.architecture_outlined, + isDestructiveAction: true, ), - // Pangea# - if (room != null) + + if (room != null && room.membership != Membership.leave) + // if (room != null) + // Pangea# SheetAction( key: SpaceChildContextAction.leave, label: L10n.of(context)!.leave, @@ -283,22 +290,32 @@ class _SpaceViewState extends State { _onJoinSpaceChild(spaceChild!); break; case SpaceChildContextAction.leave: - await showFutureLoadingDialog( - context: context, - // #Pangea - // future: room!.leave, - future: () async { - if (room!.isUnread) { - await room.markUnread(false); - } - await room.leave(); - if (Matrix.of(context).activeRoomId == room.id) { - context.go('/rooms'); - } - }, - // Pangea# - ); + // #Pangea + widget.controller.cancelAction(); + if (room == null) return; + if (room.isSpace) { + await room.isOnlyAdmin() + ? await room.archiveSpace( + context, + Matrix.of(context).client, + onlyAdmin: true, + ) + : await room.leaveSpace( + context, + Matrix.of(context).client, + ); + } else { + widget.controller.toggleSelection(room.id); + await widget.controller.leaveAction(); + } + _refresh(); break; + // await showFutureLoadingDialog( + // context: context, + // future: room!.leave, + // ); + // break; + // Pangea# case SpaceChildContextAction.removeFromSpace: await showFutureLoadingDialog( context: context, @@ -310,20 +327,27 @@ class _SpaceViewState extends State { 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(); + } // Pangea# - widget.controller.toggleSelection(room.id); - room.isSpace - ? await showFutureLoadingDialog( - context: context, - future: () async { - await archiveSpace( - room, - Matrix.of(context).client, - ); - widget.controller.selectedRoomIds.clear(); - }, - ) - : await widget.controller.archiveAction(); _refresh(); break; case SpaceChildContextAction.addToSpace: @@ -333,8 +357,10 @@ class _SpaceViewState extends State { // Pangea# widget.controller.toggleSelection(room.id); await widget.controller.addToSpace(); + // #Pangea + setState(() => widget.controller.selectedRoomIds.clear()); + // Pangea# break; - // Pangea# } } diff --git a/lib/pages/chat_list/utils/on_chat_tap.dart b/lib/pages/chat_list/utils/on_chat_tap.dart index d9f1c8191..b272d424e 100644 --- a/lib/pages/chat_list/utils/on_chat_tap.dart +++ b/lib/pages/chat_list/utils/on_chat_tap.dart @@ -46,7 +46,9 @@ void onChatTap(Room room, BuildContext context) async { } if (inviteAction == InviteActions.decline) { // #Pangea - if (room.isUnread) { + if (!room.isSpace && + room.membership == Membership.join && + room.isUnread) { await room.markUnread(false); } // Pangea# diff --git a/lib/pages/invitation_selection/invitation_selection.dart b/lib/pages/invitation_selection/invitation_selection.dart index 368bf0584..5b69e7049 100644 --- a/lib/pages/invitation_selection/invitation_selection.dart +++ b/lib/pages/invitation_selection/invitation_selection.dart @@ -159,6 +159,8 @@ class InvitationSelectionController extends State { future: () async { if (mode == InvitationSelectionMode.admin) { await inviteTeacherAction(room, id); + } else { + await room.invite(id); } }, // Pangea# diff --git a/lib/pages/new_group/new_group.dart b/lib/pages/new_group/new_group.dart index 2ada80dfa..2aec182fb 100644 --- a/lib/pages/new_group/new_group.dart +++ b/lib/pages/new_group/new_group.dart @@ -130,9 +130,7 @@ class NewGroupController extends State { powerLevelContentOverride: await ClassChatPowerLevels.powerLevelOverrideForClassChat( context, - addToSpaceKey.currentState!.parents - .map((suggestionStatus) => suggestionStatus.room) - .toList(), + addToSpaceKey.currentState!.parents, ), invite: [ if (addConversationBotKey.currentState?.addBot ?? false) diff --git a/lib/pages/new_space/new_space.dart b/lib/pages/new_space/new_space.dart index 2eb8f778e..10b5c0caf 100644 --- a/lib/pages/new_space/new_space.dart +++ b/lib/pages/new_space/new_space.dart @@ -174,9 +174,7 @@ class NewSpaceController extends State { powerLevelContentOverride: addToSpaceKey.currentState != null ? await ClassChatPowerLevels.powerLevelOverrideForClassChat( context, - addToSpaceKey.currentState!.parents - .map((suggestionStatus) => suggestionStatus.room) - .toList(), + addToSpaceKey.currentState!.parents, ) : null, // initialState: [ diff --git a/lib/pangea/choreographer/controllers/choreographer.dart b/lib/pangea/choreographer/controllers/choreographer.dart index 973ca6984..3d84b0e1d 100644 --- a/lib/pangea/choreographer/controllers/choreographer.dart +++ b/lib/pangea/choreographer/controllers/choreographer.dart @@ -13,6 +13,7 @@ import 'package:fluffychat/pangea/enum/edit_type.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/pangea/models/class_model.dart'; import 'package:fluffychat/pangea/models/it_step.dart'; +import 'package:fluffychat/pangea/models/language_detection_model.dart'; import 'package:fluffychat/pangea/models/representation_content_model.dart'; import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/utils/any_state_holder.dart'; @@ -51,7 +52,7 @@ class Choreographer { // last checked by IGC or translation String? _lastChecked; ChoreoMode choreoMode = ChoreoMode.igc; - final StreamController stateListener = StreamController(); + final StreamController stateListener = StreamController.broadcast(); StreamSubscription? trialStream; Choreographer(this.pangeaController, this.chatController) { @@ -93,7 +94,7 @@ class Choreographer { } } - void _sendWithIGC(BuildContext context) { + Future _sendWithIGC(BuildContext context) async { if (igc.canSendMessage) { final PangeaRepresentation? originalWritten = choreoRecord.includedIT && itController.sourceText != null @@ -105,7 +106,6 @@ class Choreographer { ) : null; - // PTODO - just put this in original message event final PangeaRepresentation originalSent = PangeaRepresentation( langCode: langCodeOfCurrentText ?? LanguageKeys.unknownLanguage, text: currentText, @@ -115,6 +115,22 @@ class Choreographer { final ChoreoRecord? applicableChoreo = isITandIGCEnabled && igc.igcTextData != null ? choreoRecord : null; + // if the message has not been processed to determine its language + // then run it through the language detection endpoint. If the detection + // confidence is high enough, use that language code as the message's language + // to save that pangea representation + if (applicableChoreo == null) { + final resp = await pangeaController.languageDetection.detectLanguage( + currentText, + pangeaController.languageController.userL2?.langCode, + pangeaController.languageController.userL1?.langCode, + ); + final LanguageDetection? bestDetection = resp.bestDetection(); + if (bestDetection != null) { + originalSent.langCode = bestDetection.langCode; + } + } + final UseType useType = useTypeCalculator(applicableChoreo); debugPrint("use type in choreographer $useType"); @@ -205,14 +221,18 @@ class Choreographer { textController.editType = EditType.keyboard; } - Future getLanguageHelp([bool tokensOnly = false]) async { + Future getLanguageHelp([ + bool tokensOnly = false, + bool manual = false, + ]) async { try { if (errorService.isError) return; final CanSendStatus canSendStatus = pangeaController.subscriptionController.canSendStatus; if (canSendStatus != CanSendStatus.subscribed || - (!igcEnabled && !itEnabled)) { + (!igcEnabled && !itEnabled) || + (!isAutoIGCEnabled && !manual && choreoMode != ChoreoMode.it)) { return; } @@ -525,14 +545,50 @@ class Choreographer { chatController.room, ); - bool get translationEnabled => - pangeaController.permissionsController.isToolEnabled( - ToolSetting.translations, - chatController.room, - ); + // bool get translationEnabled => + // pangeaController.permissionsController.isToolEnabled( + // ToolSetting.translations, + // chatController.room, + // ); bool get isITandIGCEnabled => pangeaController.permissionsController.isWritingAssistanceEnabled( chatController.room, ); + + bool get isAutoIGCEnabled => + pangeaController.permissionsController.isToolEnabled( + ToolSetting.autoIGC, + chatController.room, + ); + + AssistanceState get assistanceState { + if (currentText.isEmpty && itController.sourceText == null) { + return AssistanceState.noMessage; + } + + if (igc.igcTextData?.matches.isNotEmpty ?? false) { + return AssistanceState.fetched; + } + + if (isFetching) { + return AssistanceState.fetching; + } + + if (igc.igcTextData == null) { + return AssistanceState.notFetched; + } + + return AssistanceState.complete; + } +} + +// assistance state is, user has not typed a message, user has typed a message and IGC has not run, +// IGC is running, IGC has run and there are remaining steps (either IT or IGC), or all steps are done +enum AssistanceState { + noMessage, + notFetched, + fetching, + fetched, + complete, } diff --git a/lib/pangea/choreographer/controllers/it_controller.dart b/lib/pangea/choreographer/controllers/it_controller.dart index 6d5f8e347..61c8c2312 100644 --- a/lib/pangea/choreographer/controllers/it_controller.dart +++ b/lib/pangea/choreographer/controllers/it_controller.dart @@ -183,7 +183,7 @@ class ITController { } } - FuturegetNextTranslationData() async { + Future getNextTranslationData() async { try { if (completedITSteps.length < goldRouteTracker.continuances.length) { final String currentText = choreographer.currentText; @@ -478,5 +478,5 @@ class CurrentITStep { // get continuance with highest level Continuance get best => - continuances.reduce((a, b) => a.level > b.level ? a : b); + continuances.reduce((a, b) => a.level < b.level ? a : b); } diff --git a/lib/pangea/choreographer/widgets/language_display_toggle.dart b/lib/pangea/choreographer/widgets/language_display_toggle.dart index 9478b4220..bc0efd492 100644 --- a/lib/pangea/choreographer/widgets/language_display_toggle.dart +++ b/lib/pangea/choreographer/widgets/language_display_toggle.dart @@ -17,9 +17,9 @@ class LanguageDisplayToggle extends StatelessWidget { @override Widget build(BuildContext context) { - if (!controller.choreographer.translationEnabled) { - return const SizedBox(); - } + // if (!controller.choreographer.translationEnabled) { + // return const SizedBox(); + // } return Container( decoration: BoxDecoration( shape: BoxShape.circle, diff --git a/lib/pangea/choreographer/widgets/send_button.dart b/lib/pangea/choreographer/widgets/send_button.dart index 045f0c516..fa3189df5 100644 --- a/lib/pangea/choreographer/widgets/send_button.dart +++ b/lib/pangea/choreographer/widgets/send_button.dart @@ -1,8 +1,7 @@ +import 'package:fluffychat/pangea/constants/colors.dart'; import 'package:flutter/material.dart'; - import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:fluffychat/pangea/constants/colors.dart'; import '../../../pages/chat/chat.dart'; class ChoreographerSendButton extends StatelessWidget { @@ -16,7 +15,8 @@ class ChoreographerSendButton extends StatelessWidget { @override Widget build(BuildContext context) { // commit for cicd - return controller.choreographer.isFetching + return controller.choreographer.isFetching && + controller.choreographer.isAutoIGCEnabled ? Container( height: 56, width: 56, @@ -28,7 +28,8 @@ class ChoreographerSendButton extends StatelessWidget { alignment: Alignment.center, child: IconButton( icon: const Icon(Icons.send_outlined), - color: controller.choreographer.igc.canSendMessage + color: controller.choreographer.igc.canSendMessage || + !controller.choreographer.isAutoIGCEnabled ? null : PangeaColors.igcError, onPressed: () { diff --git a/lib/pangea/choreographer/widgets/start_igc_button.dart b/lib/pangea/choreographer/widgets/start_igc_button.dart new file mode 100644 index 000000000..183ac690d --- /dev/null +++ b/lib/pangea/choreographer/widgets/start_igc_button.dart @@ -0,0 +1,150 @@ +import 'dart:async'; +import 'dart:math' as math; + +import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart'; +import 'package:fluffychat/pangea/constants/colors.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +import '../../../pages/chat/chat.dart'; + +class StartIGCButton extends StatefulWidget { + const StartIGCButton({ + super.key, + required this.controller, + }); + + final ChatController controller; + + @override + State createState() => StartIGCButtonState(); +} + +class StartIGCButtonState extends State + with SingleTickerProviderStateMixin { + AssistanceState get assistanceState => + widget.controller.choreographer.assistanceState; + AnimationController? _controller; + StreamSubscription? choreoListener; + AssistanceState? prevState; + + @override + void initState() { + _controller = AnimationController( + vsync: this, + duration: const Duration(seconds: 1), + ); + choreoListener = widget.controller.choreographer.stateListener.stream + .listen(updateSpinnerState); + super.initState(); + } + + void updateSpinnerState(_) { + if (prevState != AssistanceState.fetching && + assistanceState == AssistanceState.fetching) { + _controller?.repeat(); + } else if (prevState == AssistanceState.fetching && + assistanceState != AssistanceState.fetching) { + _controller?.stop(); + _controller?.reverse(); + } + setState(() => prevState = assistanceState); + } + + @override + Widget build(BuildContext context) { + if (widget.controller.choreographer.isAutoIGCEnabled) { + return const SizedBox.shrink(); + } + + final Widget icon = Icon( + Icons.autorenew_rounded, + size: 46, + color: assistanceState.stateColor, + ); + + return SizedBox( + height: 50, + width: 50, + child: FloatingActionButton( + tooltip: assistanceState.tooltip( + L10n.of(context)!, + ), + backgroundColor: Colors.white, + disabledElevation: 0, + shape: const CircleBorder(), + onPressed: () { + if (assistanceState != AssistanceState.complete) { + widget.controller.choreographer.getLanguageHelp( + false, + true, + ); + } + }, + child: Stack( + alignment: Alignment.center, + children: [ + _controller != null + ? RotationTransition( + turns: Tween(begin: 0.0, end: math.pi * 2) + .animate(_controller!), + child: icon, + ) + : icon, + Container( + width: 26, + height: 26, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: Colors.white, + ), + ), + Container( + width: 20, + height: 20, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: assistanceState.stateColor, + ), + ), + const Icon( + size: 16, + Icons.check, + color: Colors.white, + ), + ], + ), + ), + ); + } +} + +extension AssistanceStateExtension on AssistanceState { + Color get stateColor { + switch (this) { + case AssistanceState.noMessage: + case AssistanceState.notFetched: + case AssistanceState.fetching: + return AppConfig.primaryColor; + case AssistanceState.fetched: + return PangeaColors.igcError; + case AssistanceState.complete: + return AppConfig.success; + } + } + + String tooltip(L10n l10n) { + switch (this) { + case AssistanceState.noMessage: + case AssistanceState.notFetched: + return l10n.runGrammarCorrection; + case AssistanceState.fetching: + return ""; + case AssistanceState.fetched: + return l10n.grammarCorrectionFailed; + case AssistanceState.complete: + return l10n.grammarCorrectionComplete; + } + } +} diff --git a/lib/pangea/constants/model_keys.dart b/lib/pangea/constants/model_keys.dart index 6787e7b20..0cd14e5a4 100644 --- a/lib/pangea/constants/model_keys.dart +++ b/lib/pangea/constants/model_keys.dart @@ -54,6 +54,7 @@ class ModelKey { static const String offset = "offset"; static const String length = "length"; static const String langCode = 'lang_code'; + static const String confidence = 'confidence'; // some old analytics rooms have langCode instead of lang_code in the room creation content static const String oldLangCode = 'langCode'; static const String wordLang = "word_lang"; diff --git a/lib/pangea/controllers/class_controller.dart b/lib/pangea/controllers/class_controller.dart index 0820f769b..aed5574d2 100644 --- a/lib/pangea/controllers/class_controller.dart +++ b/lib/pangea/controllers/class_controller.dart @@ -27,15 +27,16 @@ class ClassController extends BaseController { _pangeaController = pangeaController; } - setActiveSpaceIdInChatListController(String classId) { + setActiveSpaceIdInChatListController(String? classId) { setState(data: {"activeSpaceId": classId}); } Future fixClassPowerLevels() async { try { final List> classFixes = []; - for (final room in (await _pangeaController - .matrixState.client.classesAndExchangesImTeaching)) { + final teacherSpaces = await _pangeaController + .matrixState.client.classesAndExchangesImTeaching; + for (final room in teacherSpaces) { classFixes.add(room.setClassPowerLevels()); } await Future.wait(classFixes); diff --git a/lib/pangea/controllers/language_detection_controller.dart b/lib/pangea/controllers/language_detection_controller.dart index 0ff18b556..4ddfabc88 100644 --- a/lib/pangea/controllers/language_detection_controller.dart +++ b/lib/pangea/controllers/language_detection_controller.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:fluffychat/pangea/config/environment.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; +import 'package:fluffychat/pangea/models/language_detection_model.dart'; import 'package:fluffychat/pangea/network/urls.dart'; import 'package:http/http.dart' as http; @@ -48,7 +49,7 @@ class LanguageDetectionRequest { } class LanguageDetectionResponse { - List> detections; + List detections; String fullText; LanguageDetectionResponse({ @@ -58,7 +59,11 @@ class LanguageDetectionResponse { factory LanguageDetectionResponse.fromJson(Map json) { return LanguageDetectionResponse( - detections: List>.from(json['detections']), + detections: List.from( + json['detections'].map( + (e) => LanguageDetection.fromJson(e), + ), + ), fullText: json['full_text'], ); } @@ -69,6 +74,20 @@ class LanguageDetectionResponse { 'full_text': fullText, }; } + + LanguageDetection? get _bestDetection { + detections.sort((a, b) => b.confidence.compareTo(a.confidence)); + return detections.isNotEmpty ? detections.first : null; + } + + final double _confidenceThreshold = 0.95; + + LanguageDetection? bestDetection({double? threshold}) { + threshold ??= _confidenceThreshold; + return (_bestDetection?.confidence ?? 0) >= _confidenceThreshold + ? _bestDetection! + : null; + } } class _LanguageDetectionCacheItem { @@ -103,6 +122,19 @@ class LanguageDetectionController { _cacheClearTimer?.cancel(); } + Future detectLanguage( + String fullText, + String? userL2, + String? userL1, + ) async { + final LanguageDetectionRequest params = LanguageDetectionRequest( + fullText: fullText, + userL1: userL1, + userL2: userL2, + ); + return get(params); + } + Future get( LanguageDetectionRequest params, ) async { diff --git a/lib/pangea/controllers/local_settings.dart b/lib/pangea/controllers/local_settings.dart index 5984a7bf5..2dc30cfe7 100644 --- a/lib/pangea/controllers/local_settings.dart +++ b/lib/pangea/controllers/local_settings.dart @@ -8,8 +8,14 @@ class LocalSettings { _pangeaController = pangeaController; } - bool userLanguageToolSetting(ToolSetting setting) => - _pangeaController.pStoreService.read(setting.toString()) ?? true; + bool userLanguageToolSetting(ToolSetting setting) { + final profileSetting = + _pangeaController.pStoreService.read(setting.toString()); + if (profileSetting != null) { + return profileSetting; + } + return setting == ToolSetting.immersionMode ? false : true; + } // bool get userEnableIT => // _pangeaController.pStoreService.read(ToolSetting.interactiveTranslator.toString()) ?? true; diff --git a/lib/pangea/controllers/user_controller.dart b/lib/pangea/controllers/user_controller.dart index d3a17d365..c7a35b3e8 100644 --- a/lib/pangea/controllers/user_controller.dart +++ b/lib/pangea/controllers/user_controller.dart @@ -131,7 +131,7 @@ class UserController extends BaseController { final bool? immersionMode = migratedProfileInfo(MatrixProfile.immersionMode); final bool? definitions = migratedProfileInfo(MatrixProfile.definitions); - final bool? translations = migratedProfileInfo(MatrixProfile.translations); + // final bool? translations = migratedProfileInfo(MatrixProfile.translations); final bool? showItInstructions = migratedProfileInfo(MatrixProfile.showedItInstructions); final bool? showClickMessage = @@ -147,7 +147,7 @@ class UserController extends BaseController { interactiveGrammar: interactiveGrammar, immersionMode: immersionMode, definitions: definitions, - translations: translations, + // translations: translations, showedItInstructions: showItInstructions, showedClickMessage: showClickMessage, showedBlurMeansTranslate: showBlurMeansTranslate, @@ -228,10 +228,11 @@ class UserController extends BaseController { bool? interactiveGrammar, bool? immersionMode, bool? definitions, - bool? translations, + // bool? translations, bool? showedItInstructions, bool? showedClickMessage, bool? showedBlurMeansTranslate, + bool? showedTooltipInstructions, String? createdAt, String? targetLanguage, String? sourceLanguage, @@ -280,12 +281,12 @@ class UserController extends BaseController { definitions, ); } - if (translations != null) { - await _pangeaController.pStoreService.save( - MatrixProfile.translations.title, - translations, - ); - } + // if (translations != null) { + // await _pangeaController.pStoreService.save( + // MatrixProfile.translations.title, + // translations, + // ); + // } if (showedItInstructions != null) { await _pangeaController.pStoreService.save( MatrixProfile.showedItInstructions.title, @@ -304,6 +305,12 @@ class UserController extends BaseController { showedBlurMeansTranslate, ); } + if (showedTooltipInstructions != null) { + await _pangeaController.pStoreService.save( + MatrixProfile.showedTooltipInstructions.title, + showedTooltipInstructions, + ); + } if (createdAt != null) { await _pangeaController.pStoreService.save( MatrixProfile.createdAt.title, diff --git a/lib/pangea/extensions/pangea_room_extension/events_extension.dart b/lib/pangea/extensions/pangea_room_extension/events_extension.dart index feb71aa4a..eafb190e1 100644 --- a/lib/pangea/extensions/pangea_room_extension/events_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/events_extension.dart @@ -1,6 +1,102 @@ part of "pangea_room_extension.dart"; extension EventsRoomExtension on Room { + Future _archive() async { + final students = (await requestParticipants()) + .where( + (e) => + e.id != client.userID && + e.powerLevel < ClassDefaultValues.powerLevelOfAdmin && + e.id != BotName.byEnvironment, + ) + .toList(); + try { + for (final student in students) { + await kick(student.id); + } + if (!isSpace && membership == Membership.join && isUnread) { + await markUnread(false); + } + await leave(); + } catch (err, s) { + debugger(when: kDebugMode); + ErrorHandler.logError(e: err, s: s, data: toJson()); + } + } + + Future _archiveSpace( + BuildContext context, + Client client, { + bool onlyAdmin = false, + }) async { + final confirmed = await showOkCancelAlertDialog( + useRootNavigator: false, + context: context, + title: L10n.of(context)!.areYouSure, + okLabel: L10n.of(context)!.yes, + cancelLabel: L10n.of(context)!.cancel, + message: onlyAdmin + ? L10n.of(context)!.onlyAdminDescription + : L10n.of(context)!.archiveSpaceDescription, + ) == + OkCancelResult.ok; + if (!confirmed) return false; + final success = await showFutureLoadingDialog( + context: context, + future: () async { + final List children = await getChildRooms(); + for (final Room child in children) { + await child.archive(); + } + await archive(); + }, + ); + MatrixState.pangeaController.classController + .setActiveSpaceIdInChatListController( + null, + ); + return success.error == null; + } + + Future _leaveSpace(BuildContext context, Client client) async { + final confirmed = await showOkCancelAlertDialog( + useRootNavigator: false, + context: context, + title: L10n.of(context)!.areYouSure, + okLabel: L10n.of(context)!.yes, + cancelLabel: L10n.of(context)!.cancel, + message: L10n.of(context)!.leaveSpaceDescription, + ) == + OkCancelResult.ok; + if (!confirmed) return false; + final success = await showFutureLoadingDialog( + context: context, + future: () async { + try { + final List children = await getChildRooms(); + for (final Room child in children) { + if (!child.isSpace && + child.membership == Membership.join && + child.isUnread) { + await child.markUnread(false); + } + await child.leave(); + } + await leave(); + } catch (err, stack) { + debugger(when: kDebugMode); + ErrorHandler.logError(e: err, s: stack, data: powerLevels); + rethrow; + } + }, + ); + MatrixState.pangeaController.classController + .setActiveSpaceIdInChatListController( + null, + ); + return success.error == null; + } + 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 166ab179c..e3efc7eca 100644 --- a/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:developer'; +import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/constants/class_default_values.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart'; @@ -15,8 +16,11 @@ 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/error_handler.dart'; +import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:future_loading_dialog/future_loading_dialog.dart'; // import markdown.dart import 'package:html_unescape/html_unescape.dart'; import 'package:matrix/matrix.dart'; @@ -137,6 +141,18 @@ extension PangeaRoom on Room { // events + Future archive() async => await _archive(); + + Future archiveSpace( + BuildContext context, + Client client, { + bool onlyAdmin = false, + }) async => + await _archiveSpace(context, client, onlyAdmin: onlyAdmin); + + Future leaveSpace(BuildContext context, Client client) async => + await _leaveSpace(context, client); + Future sendPangeaEvent({ required Map content, required String parentEventId, @@ -229,14 +245,15 @@ extension PangeaRoom on Room { BotOptionsModel? get botOptions => _botOptions; - Future suggestedInSpace(Room space) async => - await _suggestedInSpace(space); + Future setSuggested(bool suggested) async => + await _setSuggested(suggested); - Future setSuggestedInSpace(bool suggest, Room space) async => - await _setSuggestedInSpace(suggest, space); + Future isSuggested() async => await _isSuggested(); // user_permissions + Future isOnlyAdmin() async => await _isOnlyAdmin(); + bool isMadeByUser(String userId) => _isMadeByUser(userId); bool get isSpaceAdmin => _isSpaceAdmin; 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..9746a5680 100644 --- a/lib/pangea/extensions/pangea_room_extension/room_settings_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/room_settings_extension.dart @@ -55,7 +55,40 @@ extension RoomSettingsRoomExtension on Room { ); } - Future _suggestedInSpace(Room space) async { + Future _isSuggested() async { + final List spaceParents = client.rooms + .where( + (room) => + room.isSpace && + room.spaceChildren.any( + (sc) => sc.roomId == id, + ), + ) + .toList(); + + for (final parent in spaceParents) { + final suggested = await _isSuggestedInSpace(parent); + if (!suggested) return false; + } + return true; + } + + Future _setSuggested(bool suggested) async { + final List spaceParents = client.rooms + .where( + (room) => + room.isSpace && + room.spaceChildren.any( + (sc) => sc.roomId == id, + ), + ) + .toList(); + for (final parent in spaceParents) { + await _setSuggestedInSpace(suggested, parent); + } + } + + Future _isSuggestedInSpace(Room space) async { try { final Map resp = await client.getRoomStateWithKey(space.id, EventTypes.spaceChild, id); diff --git a/lib/pangea/extensions/pangea_room_extension/user_permissions_extension.dart b/lib/pangea/extensions/pangea_room_extension/user_permissions_extension.dart index 929a74e66..17d6aead2 100644 --- a/lib/pangea/extensions/pangea_room_extension/user_permissions_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/user_permissions_extension.dart @@ -1,6 +1,32 @@ part of "pangea_room_extension.dart"; extension UserPermissionsRoomExtension on Room { +// If there are no other admins, and at least one non-admin, return true + Future _isOnlyAdmin() async { + if (!isRoomAdmin) { + return false; + } + final List participants = await requestParticipants(); + + return ((participants + .where( + (e) => + e.powerLevel == ClassDefaultValues.powerLevelOfAdmin && + e.id != BotName.byEnvironment, + ) + .toList() + .length) == + 1) && + (participants + .where( + (e) => + e.powerLevel < ClassDefaultValues.powerLevelOfAdmin && + e.id != BotName.byEnvironment, + ) + .toList()) + .isNotEmpty; + } + bool _isMadeByUser(String userId) => getState(EventTypes.RoomCreate)?.senderId == userId; diff --git a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart index 5fa2e2659..ff8696989 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart @@ -80,29 +80,6 @@ class PangeaMessageEvent { return _latestEdit; } - bool showRichText( - bool selected, { - bool highlighted = false, - bool isOverlay = false, - }) { - if (!_isValidPangeaMessageEvent) { - return false; - } - - if ([EventStatus.error, EventStatus.sending].contains(_event.status)) { - return false; - } - - if (isOverlay) return true; - - // if ownMessage, don't show rich text if not selected or highlighted - // and don't show is the message is not an overlay - if (ownMessage && ((!selected && !highlighted) || !isOverlay)) { - return false; - } - return true; - } - Future getMatrixAudioFile( String langCode, BuildContext context, diff --git a/lib/pangea/models/class_model.dart b/lib/pangea/models/class_model.dart index 1f588980c..7e95c9d26 100644 --- a/lib/pangea/models/class_model.dart +++ b/lib/pangea/models/class_model.dart @@ -1,13 +1,12 @@ import 'dart:developer'; +import 'package:fluffychat/pangea/constants/model_keys.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 'package:matrix/matrix.dart'; -import 'package:fluffychat/pangea/constants/model_keys.dart'; -import 'package:fluffychat/pangea/utils/error_handler.dart'; import '../constants/class_default_values.dart'; import '../constants/language_keys.dart'; import '../constants/pangea_event_types.dart'; @@ -124,6 +123,7 @@ class PangeaRoomRules { int immersionMode; int definitions; int translations; + int autoIGC; PangeaRoomRules({ this.isPublic = false, @@ -142,6 +142,7 @@ class PangeaRoomRules { this.immersionMode = ClassDefaultValues.languageToolPermissions, this.definitions = ClassDefaultValues.languageToolPermissions, this.translations = ClassDefaultValues.languageToolPermissions, + this.autoIGC = ClassDefaultValues.languageToolPermissions, }); updatePermission(String key, bool value) { @@ -198,8 +199,11 @@ class PangeaRoomRules { case ToolSetting.definitions: definitions = value; break; - case ToolSetting.translations: - translations = value; + // case ToolSetting.translations: + // translations = value; + // break; + case ToolSetting.autoIGC: + autoIGC = value; break; default: throw Exception('Invalid key for setting permissions - $setting'); @@ -235,6 +239,7 @@ class PangeaRoomRules { json['definitions'] ?? ClassDefaultValues.languageToolPermissions, translations: json['translations'] ?? ClassDefaultValues.languageToolPermissions, + autoIGC: json['auto_igc'] ?? ClassDefaultValues.languageToolPermissions, ); Map toJson() { @@ -256,6 +261,7 @@ class PangeaRoomRules { data['immersion_mode'] = immersionMode; data['definitions'] = definitions; data['translations'] = translations; + data['auto_igc'] = autoIGC; return data; } @@ -269,8 +275,10 @@ class PangeaRoomRules { return immersionMode; case ToolSetting.definitions: return definitions; - case ToolSetting.translations: - return translations; + // case ToolSetting.translations: + // return translations; + case ToolSetting.autoIGC: + return autoIGC; default: throw Exception('Invalid key for setting permissions - $setting'); } @@ -298,7 +306,8 @@ enum ToolSetting { interactiveGrammar, immersionMode, definitions, - translations, + // translations, + autoIGC, } extension SettingCopy on ToolSetting { @@ -312,8 +321,10 @@ extension SettingCopy on ToolSetting { return L10n.of(context)!.toggleImmersionMode; case ToolSetting.definitions: return L10n.of(context)!.definitionsToolName; - case ToolSetting.translations: - return L10n.of(context)!.messageTranslationsToolName; + // case ToolSetting.translations: + // return L10n.of(context)!.messageTranslationsToolName; + case ToolSetting.autoIGC: + return L10n.of(context)!.autoIGCToolName; } } @@ -328,8 +339,10 @@ extension SettingCopy on ToolSetting { return L10n.of(context)!.toggleImmersionModeDesc; case ToolSetting.definitions: return L10n.of(context)!.definitionsToolDescription; - case ToolSetting.translations: - return L10n.of(context)!.translationsToolDescrption; + // case ToolSetting.translations: + // return L10n.of(context)!.translationsToolDescrption; + case ToolSetting.autoIGC: + return L10n.of(context)!.autoIGCToolDescription; } } } diff --git a/lib/pangea/models/language_detection_model.dart b/lib/pangea/models/language_detection_model.dart index 6fa3d7299..7ed44868c 100644 --- a/lib/pangea/models/language_detection_model.dart +++ b/lib/pangea/models/language_detection_model.dart @@ -1,19 +1,23 @@ +import 'package:fluffychat/pangea/constants/model_keys.dart'; + class LanguageDetection { String langCode; + double confidence; LanguageDetection({ required this.langCode, + required this.confidence, }); factory LanguageDetection.fromJson(Map json) { return LanguageDetection( - langCode: json[_langCodeKey], + langCode: json[ModelKey.langCode], + confidence: json[ModelKey.confidence], ); } - static const _langCodeKey = "lang_code"; - Map toJson() => { - _langCodeKey: langCode, + ModelKey.langCode: langCode, + ModelKey.confidence: confidence, }; } diff --git a/lib/pangea/models/user_model.dart b/lib/pangea/models/user_model.dart index 2169c6f70..a6ae5730f 100644 --- a/lib/pangea/models/user_model.dart +++ b/lib/pangea/models/user_model.dart @@ -59,15 +59,17 @@ enum MatrixProfile { interactiveGrammar, immersionMode, definitions, - translations, + // translations, showedItInstructions, showedClickMessage, showedBlurMeansTranslate, + showedTooltipInstructions, createdAt, targetLanguage, sourceLanguage, country, publicProfile, + autoIGC, } extension MatrixProfileExtension on MatrixProfile { @@ -87,14 +89,18 @@ extension MatrixProfileExtension on MatrixProfile { return ToolSetting.immersionMode.toString(); case MatrixProfile.definitions: return ToolSetting.definitions.toString(); - case MatrixProfile.translations: - return ToolSetting.translations.toString(); + // case MatrixProfile.translations: + // return ToolSetting.translations.toString(); + case MatrixProfile.autoIGC: + return ToolSetting.autoIGC.toString(); case MatrixProfile.showedItInstructions: return InstructionsEnum.itInstructions.toString(); case MatrixProfile.showedClickMessage: return InstructionsEnum.clickMessage.toString(); case MatrixProfile.showedBlurMeansTranslate: return InstructionsEnum.blurMeansTranslate.toString(); + case MatrixProfile.showedTooltipInstructions: + return InstructionsEnum.tooltipInstructions.toString(); case MatrixProfile.createdAt: return ModelKey.userCreatedAt; case MatrixProfile.targetLanguage: diff --git a/lib/pangea/repo/igc_repo.dart b/lib/pangea/repo/igc_repo.dart index 068a009e8..9517515d0 100644 --- a/lib/pangea/repo/igc_repo.dart +++ b/lib/pangea/repo/igc_repo.dart @@ -1,13 +1,13 @@ import 'dart:convert'; -import 'package:http/http.dart'; - import 'package:fluffychat/pangea/config/environment.dart'; import 'package:fluffychat/pangea/models/language_detection_model.dart'; import 'package:fluffychat/pangea/models/lemma.dart'; import 'package:fluffychat/pangea/models/pangea_match_model.dart'; import 'package:fluffychat/pangea/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/repo/span_data_repo.dart'; +import 'package:http/http.dart'; + import '../constants/model_keys.dart'; import '../models/igc_text_data_model.dart'; import '../network/requests.dart'; @@ -39,7 +39,7 @@ class IgcRepo { await Future.delayed(const Duration(seconds: 2)); final IGCTextData igcTextData = IGCTextData( - detections: [LanguageDetection(langCode: "en")], + detections: [LanguageDetection(langCode: "en", confidence: 0.99)], tokens: [ PangeaToken( text: PangeaTokenText(content: "This", offset: 0, length: 4), diff --git a/lib/pangea/utils/archive_space.dart b/lib/pangea/utils/archive_space.dart deleted file mode 100644 index 68b9f82fd..000000000 --- a/lib/pangea/utils/archive_space.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/utils/error_handler.dart'; -import 'package:matrix/matrix.dart'; - -Future archiveSpace(Room? space, Client client) async { - if (space == null) { - ErrorHandler.logError( - e: 'Tried to archive a space that is null. This should not happen.', - s: StackTrace.current, - ); - return; - } - - final List children = await space.getChildRooms(); - for (final Room child in children) { - if (child.isUnread) { - await child.markUnread(false); - } - await child.leave(); - } - await space.leave(); -} diff --git a/lib/pangea/utils/get_chat_list_item_subtitle.dart b/lib/pangea/utils/get_chat_list_item_subtitle.dart index 2fee578bf..5ccb623d7 100644 --- a/lib/pangea/utils/get_chat_list_item_subtitle.dart +++ b/lib/pangea/utils/get_chat_list_item_subtitle.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/constants/language_keys.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; @@ -10,6 +11,17 @@ import 'package:matrix/matrix.dart'; import '../../utils/matrix_sdk_extensions/matrix_locals.dart'; class GetChatListItemSubtitle { + final List hideContentKeys = [ + ModelKey.transcription, + ]; + + bool moveBackInTimeline(Event event) => + hideContentKeys.any( + (key) => event.content.tryGet(key) != null, + ) || + event.type.startsWith("p.") || + event.type.startsWith("pangea."); + Future getSubtitle( L10n l10n, Event? event, @@ -22,23 +34,14 @@ class GetChatListItemSubtitle { eventContextId = null; } - final Timeline timeline = - await event.room.getTimeline(eventContextId: eventContextId); - - if (event.content.tryGet(ModelKey.transcription) != null) { - int index = timeline.events.indexWhere( - (e) => e.eventId == event!.eventId, - ); - - while (index < timeline.events.length && - (timeline.events[index].content.tryGet(ModelKey.transcription) != - null || - timeline.events[index].type != EventTypes.Message)) { - index++; - } + final Timeline timeline = await event.room.getTimeline( + eventContextId: eventContextId, + ); - if (timeline.events.length > index + 1) { - event = timeline.events[index]; + if (moveBackInTimeline(event)) { + event = timeline.events.firstWhereOrNull((e) => !moveBackInTimeline(e)); + if (event == null) { + return l10n.emptyChat; } } diff --git a/lib/pangea/utils/instructions.dart b/lib/pangea/utils/instructions.dart index f1fa8b59f..43350f518 100644 --- a/lib/pangea/utils/instructions.dart +++ b/lib/pangea/utils/instructions.dart @@ -1,3 +1,4 @@ +import 'package:fluffychat/utils/platform_infos.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -102,6 +103,7 @@ enum InstructionsEnum { itInstructions, clickMessage, blurMeansTranslate, + tooltipInstructions, } extension Copy on InstructionsEnum { @@ -113,6 +115,8 @@ extension Copy on InstructionsEnum { return L10n.of(context)!.clickMessageTitle; case InstructionsEnum.blurMeansTranslate: return L10n.of(context)!.blurMeansTranslateTitle; + case InstructionsEnum.tooltipInstructions: + return L10n.of(context)!.tooltipInstructionsTitle; } } @@ -124,6 +128,10 @@ extension Copy on InstructionsEnum { return L10n.of(context)!.clickMessageBody; case InstructionsEnum.blurMeansTranslate: return L10n.of(context)!.blurMeansTranslateBody; + case InstructionsEnum.tooltipInstructions: + return PlatformInfos.isMobile + ? L10n.of(context)!.tooltipInstructionsMobileBody + : L10n.of(context)!.tooltipInstructionsBrowserBody; } } } diff --git a/lib/pangea/widgets/chat/message_speech_to_text_card.dart b/lib/pangea/widgets/chat/message_speech_to_text_card.dart index 37b013b53..663e39ffe 100644 --- a/lib/pangea/widgets/chat/message_speech_to_text_card.dart +++ b/lib/pangea/widgets/chat/message_speech_to_text_card.dart @@ -3,6 +3,7 @@ import 'dart:developer'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/speech_to_text_models.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; +import 'package:fluffychat/pangea/utils/instructions.dart'; import 'package:fluffychat/pangea/widgets/chat/toolbar_content_loading_indicator.dart'; import 'package:fluffychat/pangea/widgets/common/icon_number_widget.dart'; import 'package:fluffychat/pangea/widgets/igc/card_error_widget.dart'; @@ -175,12 +176,24 @@ class MessageSpeechToTextCardState extends State { number: "${selectedToken?.confidence ?? speechToTextResponse!.transcript.confidence}%", toolTip: L10n.of(context)!.accuracy, + onPressed: () => MatrixState.pangeaController.instructions.show( + context, + InstructionsEnum.tooltipInstructions, + widget.messageEvent.eventId, + true, + ), ), IconNumberWidget( icon: Icons.speed, number: wordsPerMinuteString != null ? "$wordsPerMinuteString" : "??", toolTip: L10n.of(context)!.wordsPerMinute, + onPressed: () => MatrixState.pangeaController.instructions.show( + context, + InstructionsEnum.tooltipInstructions, + widget.messageEvent.eventId, + true, + ), ), ], ), diff --git a/lib/pangea/widgets/class/add_space_toggles.dart b/lib/pangea/widgets/class/add_space_toggles.dart index d3bfdbd3d..cfea4dd7b 100644 --- a/lib/pangea/widgets/class/add_space_toggles.dart +++ b/lib/pangea/widgets/class/add_space_toggles.dart @@ -33,9 +33,10 @@ class AddToSpaceToggles extends StatefulWidget { class AddToSpaceState extends State { late Room? room; - late List parents; + late List parents; late List possibleParents; late bool isOpen; + late bool isSuggested; AddToSpaceState({Key? key}); @@ -46,6 +47,9 @@ class AddToSpaceState extends State { ? Matrix.of(context).client.getRoomById(widget.roomId!) : null; + isSuggested = true; + room?.isSuggested().then((value) => isSuggested = value); + possibleParents = Matrix.of(context) .client .rooms @@ -63,8 +67,6 @@ class AddToSpaceState extends State { (r) => r.spaceChildren.any((room) => room.roomId == widget.roomId), ) - .map((r) => SuggestionStatus(false, r)) - .cast() .toList() : []; @@ -72,7 +74,7 @@ class AddToSpaceState extends State { final activeSpace = Matrix.of(context).client.getRoomById(widget.activeSpaceId!); if (activeSpace != null && activeSpace.canIAddSpaceChild(null)) { - parents.add(SuggestionStatus(true, activeSpace)); + parents.add(activeSpace); } else { ErrorHandler.logError( e: Exception('activeSpaceId ${widget.activeSpaceId} not found'), @@ -84,10 +86,9 @@ class AddToSpaceState extends State { //if possibleParent in parents, put first //use sort but use any instead of contains because contains uses == and we want to compare by id possibleParents.sort((a, b) { - if (parents.any((suggestionStatus) => suggestionStatus.room.id == a.id)) { + if (parents.any((parent) => parent.id == a.id)) { return -1; - } else if (parents - .any((suggestionStatus) => suggestionStatus.room.id == b.id)) { + } else if (parents.any((parent) => parent.id == b.id)) { return 1; } else { return a.name.compareTo(b.name); @@ -95,35 +96,21 @@ class AddToSpaceState extends State { }); isOpen = widget.startOpen; - initSuggestedParents(); super.initState(); } - Future initSuggestedParents() async { - if (room != null) { - for (var i = 0; i < parents.length; i++) { - final parent = parents[i]; - final bool suggested = - await room?.suggestedInSpace(parent.room) ?? false; - parents[i].suggested = suggested; - } - setState(() {}); - } - } - Future _addSingleSpace(String roomToAddId, Room newParent) async { GoogleAnalytics.addParent(roomToAddId, newParent.classCode); await newParent.setSpaceChild( roomToAddId, - suggested: isSuggestedInSpace(newParent), + suggested: isSuggested, ); - await setSuggested(true, newParent); } Future addSpaces(String roomToAddId) async { final List> addFutures = []; - for (final SuggestionStatus newParent in parents) { - addFutures.add(_addSingleSpace(roomToAddId, newParent.room)); + for (final Room parent in parents) { + addFutures.add(_addSingleSpace(roomToAddId, parent)); } await addFutures.wait; } @@ -148,39 +135,18 @@ class AddToSpaceState extends State { setState( () => add - ? parents.add(SuggestionStatus(true, possibleParent)) + ? parents.add(possibleParent) : parents.removeWhere( - (suggestionStatus) => - suggestionStatus.room.id == possibleParent.id, + (parent) => parent.id == possibleParent.id, ), ); } - Future setSuggested(bool suggest, Room possibleParent) async { - if (room != null) { - await showFutureLoadingDialog( - context: context, - future: () => room!.setSuggestedInSpace(suggest, possibleParent), - ); - } - - for (final SuggestionStatus suggestionStatus in parents) { - if (suggestionStatus.room.id == possibleParent.id) { - suggestionStatus.suggested = suggest; - } - } - - setState(() {}); - } - - bool isSuggestedInSpace(Room parent) => - parents.firstWhereOrNull((r) => r.room.id == parent.id)?.suggested ?? - false; - Widget getAddToSpaceToggleItem(int index) { final Room possibleParent = possibleParents[index]; - final String possibleParentName = possibleParent.getLocalizedDisplayname(); - final bool canAdd = possibleParent.canIAddSpaceChild(room); + final bool canAdd = !(!possibleParent.isRoomAdmin && + widget.mode == AddToClassMode.exchange) && + possibleParent.canIAddSpaceChild(room); return Opacity( opacity: canAdd ? 1 : 0.5, @@ -189,7 +155,7 @@ class AddToSpaceState extends State { SwitchListTile.adaptive( title: possibleParent.nameAndRoomTypeIcon(), activeColor: AppConfig.activeToggleColor, - value: parents.any((r) => r.room.id == possibleParent.id), + value: parents.any((r) => r.id == possibleParent.id), onChanged: (bool add) => canAdd ? handleAdd(add, possibleParent) : ScaffoldMessenger.of(context).showSnackBar( @@ -198,53 +164,6 @@ class AddToSpaceState extends State { ), ), ), - AnimatedSize( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - child: parents.any((r) => r.room.id == possibleParent.id) - ? SwitchListTile.adaptive( - title: Row( - children: [ - const SizedBox(width: 32), - Expanded( - child: Text( - L10n.of(context)!.suggestTo(possibleParentName), - style: TextStyle( - color: Theme.of(context).colorScheme.secondary, - ), - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - subtitle: Row( - children: [ - const SizedBox(width: 32), - Expanded( - child: Text( - widget.mode == AddToClassMode.chat - ? L10n.of(context)! - .suggestChatDesc(possibleParentName) - : L10n.of(context)!.suggestExchangeDesc( - possibleParentName, - ), - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - activeColor: AppConfig.activeToggleColor, - value: isSuggestedInSpace(possibleParent), - onChanged: (bool suggest) => canAdd - ? setSuggested(suggest, possibleParent) - : ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(L10n.of(context)!.noPermission), - ), - ), - ) - : Container(), - ), Divider( height: 0.5, color: Theme.of(context).colorScheme.secondary.withAlpha(25), @@ -254,6 +173,16 @@ class AddToSpaceState extends State { ); } + Future setSuggested(bool suggested) async { + setState(() => isSuggested = suggested); + if (room != null) { + await showFutureLoadingDialog( + context: context, + future: () async => await room?.setSuggested(suggested), + ); + } + } + @override Widget build(BuildContext context) { final String title = widget.mode == AddToClassMode.exchange @@ -292,9 +221,28 @@ class AddToSpaceState extends State { const Divider(height: 1), possibleParents.isNotEmpty ? Column( - children: possibleParents - .mapIndexed((index, _) => getAddToSpaceToggleItem(index)) - .toList(), + children: [ + SwitchListTile.adaptive( + title: Text(L10n.of(context)!.suggestToChat), + secondary: Icon( + isSuggested + ? Icons.visibility_outlined + : Icons.visibility_off_outlined, + ), + subtitle: Text(L10n.of(context)!.suggestToChatDesc), + activeColor: AppConfig.activeToggleColor, + value: isSuggested, + onChanged: (bool add) => setSuggested(add), + ), + Divider( + height: 0.5, + color: + Theme.of(context).colorScheme.secondary.withAlpha(25), + ), + ...possibleParents.mapIndexed( + (index, _) => getAddToSpaceToggleItem(index), + ), + ], ) : Center( child: Padding( @@ -312,10 +260,3 @@ class AddToSpaceState extends State { ); } } - -class SuggestionStatus { - bool suggested; - final Room room; - - SuggestionStatus(this.suggested, this.room); -} diff --git a/lib/pangea/widgets/common/icon_number_widget.dart b/lib/pangea/widgets/common/icon_number_widget.dart index b42777f91..f677ea579 100644 --- a/lib/pangea/widgets/common/icon_number_widget.dart +++ b/lib/pangea/widgets/common/icon_number_widget.dart @@ -6,6 +6,7 @@ class IconNumberWidget extends StatelessWidget { final Color? iconColor; final double? iconSize; final String? toolTip; + final VoidCallback onPressed; const IconNumberWidget({ super.key, @@ -14,16 +15,20 @@ class IconNumberWidget extends StatelessWidget { this.toolTip, this.iconColor, this.iconSize, + required this.onPressed, }); Widget _content(BuildContext context) { return Row( mainAxisSize: MainAxisSize.min, children: [ - Icon( - icon, - color: iconColor ?? Theme.of(context).iconTheme.color, - size: iconSize ?? Theme.of(context).iconTheme.size, + IconButton( + icon: Icon( + icon, + color: iconColor ?? Theme.of(context).iconTheme.color, + size: iconSize ?? Theme.of(context).iconTheme.size, + ), + onPressed: onPressed, ), const SizedBox(width: 8), Text( diff --git a/lib/pangea/widgets/igc/span_card.dart b/lib/pangea/widgets/igc/span_card.dart index fd44383a0..4f0b22dfe 100644 --- a/lib/pangea/widgets/igc/span_card.dart +++ b/lib/pangea/widgets/igc/span_card.dart @@ -61,7 +61,7 @@ class SpanCardState extends State { SpanChoice? get selectedChoice { if (selectedChoiceIndex == null || widget.scm.pangeaMatch?.match.choices == null || - widget.scm.pangeaMatch!.match.choices!.length >= selectedChoiceIndex!) { + widget.scm.pangeaMatch!.match.choices!.length <= selectedChoiceIndex!) { return null; } return widget.scm.pangeaMatch?.match.choices?[selectedChoiceIndex!]; diff --git a/lib/widgets/chat_settings_popup_menu.dart b/lib/widgets/chat_settings_popup_menu.dart index fb90aa8db..9b6e83270 100644 --- a/lib/widgets/chat_settings_popup_menu.dart +++ b/lib/widgets/chat_settings_popup_menu.dart @@ -85,20 +85,31 @@ class ChatSettingsPopupMenuState extends State { ), // #Pangea if (!widget.room.isArchived) - // Pangea# - PopupMenuItem( - value: 'leave', - child: Row( - children: [ - // #Pangea - // const Icon(Icons.delete_outlined), - const Icon(Icons.arrow_forward), - // Pangea# - const SizedBox(width: 12), - Text(L10n.of(context)!.leave), - ], + if (widget.room.isRoomAdmin) + PopupMenuItem( + value: 'archive', + child: Row( + children: [ + const Icon(Icons.archive_outlined), + const SizedBox(width: 12), + Text(L10n.of(context)!.archive), + ], + ), ), + // Pangea# + PopupMenuItem( + value: 'leave', + child: Row( + children: [ + // #Pangea + // const Icon(Icons.delete_outlined), + const Icon(Icons.arrow_forward), + // Pangea# + const SizedBox(width: 12), + Text(L10n.of(context)!.leave), + ], ), + ), // #Pangea if (classSettings != null) PopupMenuItem( @@ -167,7 +178,8 @@ class ChatSettingsPopupMenuState extends State { PopupMenuButton( onSelected: (String choice) async { switch (choice) { - case 'leave': + // #Pangea + case 'archive': final confirmed = await showOkCancelAlertDialog( useRootNavigator: false, context: context, @@ -179,7 +191,31 @@ class ChatSettingsPopupMenuState extends State { if (confirmed == OkCancelResult.ok) { final success = await showFutureLoadingDialog( context: context, - future: () => widget.room.leave(), + future: () => widget.room.archive(), + ); + if (success.error == null) { + context.go('/rooms'); + } + } + break; + // Pangea# + case 'leave': + final bool onlyAdmin = await widget.room.isOnlyAdmin(); + final confirmed = await showOkCancelAlertDialog( + useRootNavigator: false, + context: context, + title: L10n.of(context)!.areYouSure, + okLabel: L10n.of(context)!.ok, + cancelLabel: L10n.of(context)!.cancel, + message: onlyAdmin + ? L10n.of(context)!.onlyAdminDescription + : L10n.of(context)!.leaveRoomDescription, + ); + if (confirmed == OkCancelResult.ok) { + final success = await showFutureLoadingDialog( + context: context, + future: () => + onlyAdmin ? widget.room.archive() : widget.room.leave(), ); if (success.error == null) { context.go('/rooms'); diff --git a/needed-translations.txt b/needed-translations.txt index bb967d011..e29d36ddd 100644 --- a/needed-translations.txt +++ b/needed-translations.txt @@ -745,9 +745,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -839,7 +838,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "be": [ @@ -2124,9 +2135,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -2277,7 +2287,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "bn": [ @@ -3034,9 +3056,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -3177,7 +3198,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "bo": [ @@ -3934,9 +3967,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -4077,7 +4109,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "ca": [ @@ -4834,9 +4878,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -4977,7 +5020,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "cs": [ @@ -5734,9 +5789,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -5877,7 +5931,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "de": [ @@ -6629,9 +6695,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -6724,7 +6789,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "el": [ @@ -7481,9 +7558,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -7624,7 +7700,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "eo": [ @@ -8381,9 +8469,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -8524,7 +8611,36 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" + ], + + "es": [ + "suggestToChat", + "suggestToChatDesc", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "et": [ @@ -9273,9 +9389,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -9367,7 +9482,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "eu": [ @@ -10116,9 +10243,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -10210,7 +10336,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "fa": [ @@ -10967,9 +11105,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -11110,7 +11247,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "fi": [ @@ -11867,9 +12016,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -12010,7 +12158,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "fr": [ @@ -12767,9 +12927,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -12910,7 +13069,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "ga": [ @@ -13667,9 +13838,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -13810,7 +13980,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "gl": [ @@ -14559,9 +14741,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -14653,7 +14834,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "he": [ @@ -15410,9 +15603,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -15553,7 +15745,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "hi": [ @@ -16310,9 +16514,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -16453,7 +16656,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "hr": [ @@ -17207,9 +17422,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -17340,7 +17554,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "hu": [ @@ -18097,9 +18323,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -18240,7 +18465,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "ia": [ @@ -19511,9 +19748,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -19664,7 +19900,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "id": [ @@ -20421,9 +20669,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -20564,7 +20811,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "ie": [ @@ -21321,9 +21580,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -21464,7 +21722,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "it": [ @@ -22217,9 +22487,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -22349,7 +22618,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "ja": [ @@ -23106,9 +23387,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -23249,7 +23529,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "ko": [ @@ -24006,9 +24298,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -24149,7 +24440,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "lt": [ @@ -24906,9 +25209,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -25049,7 +25351,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "lv": [ @@ -25806,9 +26120,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -25949,7 +26262,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "nb": [ @@ -26706,9 +27031,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -26849,7 +27173,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "nl": [ @@ -27606,9 +27942,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -27749,7 +28084,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "pl": [ @@ -28506,9 +28853,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -28649,7 +28995,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "pt": [ @@ -29406,9 +29764,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -29549,7 +29906,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "pt_BR": [ @@ -30302,9 +30671,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -30418,7 +30786,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "pt_PT": [ @@ -31175,9 +31555,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -31318,7 +31697,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "ro": [ @@ -32075,9 +32466,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -32218,7 +32608,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "ru": [ @@ -32967,9 +33369,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -33061,7 +33462,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "sk": [ @@ -33818,9 +34231,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -33961,7 +34373,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "sl": [ @@ -34718,9 +35142,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -34861,7 +35284,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "sr": [ @@ -35618,9 +36053,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -35761,7 +36195,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "sv": [ @@ -36514,9 +36960,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -36626,7 +37071,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "ta": [ @@ -37383,9 +37840,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -37526,7 +37982,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "th": [ @@ -38283,9 +38751,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -38426,7 +38893,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "tr": [ @@ -39179,9 +39658,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -39311,7 +39789,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "uk": [ @@ -40060,9 +40550,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -40154,7 +40643,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "vi": [ @@ -40911,9 +41412,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -41054,7 +41554,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "zh": [ @@ -41803,9 +42315,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -41897,7 +42408,19 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ], "zh_Hant": [ @@ -42654,9 +43177,8 @@ "lockSpace", "lockChat", "archiveSpace", - "suggestTo", - "suggestChatDesc", - "suggestExchangeDesc", + "suggestToChat", + "suggestToChatDesc", "acceptSelection", "acceptSelectionAnyway", "makingActivity", @@ -42797,6 +43319,18 @@ "studentAnalyticsNotAvailable", "roomDataMissing", "updatePhoneOS", - "wordsPerMinute" + "wordsPerMinute", + "autoIGCToolName", + "autoIGCToolDescription", + "runGrammarCorrection", + "grammarCorrectionFailed", + "grammarCorrectionComplete", + "leaveRoomDescription", + "archiveSpaceDescription", + "leaveSpaceDescription", + "onlyAdminDescription", + "tooltipInstructionsTitle", + "tooltipInstructionsMobileBody", + "tooltipInstructionsBrowserBody" ] }