From 985ccfe30d0e0c613408681fcffd4c69aa6d845c Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 22 Oct 2024 14:54:42 -0400 Subject: [PATCH 01/50] better error handling if originalSent is null in _fetchActivity --- .../practice_activity_card.dart | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index 277a8bed9..82d3c0a17 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -4,7 +4,6 @@ import 'dart:developer'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/enum/activity_type_enum.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; -import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_representation_event.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/practice_activity_event.dart'; import 'package:fluffychat/pangea/models/analytics/constructs_model.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/message_activity_request.dart'; @@ -119,13 +118,25 @@ class MessagePracticeActivityCardState extends State { return null; } + if (widget.pangeaMessageEvent.originalSent == null) { + debugger(when: kDebugMode); + _updateFetchingActivity(false); + ErrorHandler.logError( + e: Exception('No original message found in _fetchNewActivity'), + data: { + 'event': widget.pangeaMessageEvent.event.toJson(), + }, + ); + return null; + } + final PracticeActivityModel? ourNewActivity = await pangeaController .practiceGenerationController .getPracticeActivity( MessageActivityRequest( userL1: pangeaController.languageController.userL1!.langCode, userL2: pangeaController.languageController.userL2!.langCode, - messageText: representation!.text, + messageText: widget.pangeaMessageEvent.originalSent!.text, tokensWithXP: await targetTokensController.targetTokens( context, widget.pangeaMessageEvent, @@ -256,11 +267,6 @@ class MessagePracticeActivityCardState extends State { }); } - RepresentationEvent? get representation => - widget.pangeaMessageEvent.originalSent; - - String get messsageText => representation!.text; - PangeaController get pangeaController => MatrixState.pangeaController; /// The widget that displays the current activity. From c028f64f606c46d6a99edfe60b64ebf0ad17770f Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 22 Oct 2024 15:01:07 -0400 Subject: [PATCH 02/50] tts web version always return null on stop(), don't log error --- lib/pangea/widgets/chat/tts_controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pangea/widgets/chat/tts_controller.dart b/lib/pangea/widgets/chat/tts_controller.dart index 7e7381c53..225d7a04e 100644 --- a/lib/pangea/widgets/chat/tts_controller.dart +++ b/lib/pangea/widgets/chat/tts_controller.dart @@ -96,7 +96,7 @@ class TtsController { // return type is dynamic but apparent its supposed to be 1 // https://pub.dev/packages/flutter_tts - if (result != 1) { + if (result != 1 && !kIsWeb) { ErrorHandler.logError( m: 'Unexpected result from tts.speak', data: { From b499ef2f14d1eabb7ec5ce93ecc66c4ceb60ee8c Mon Sep 17 00:00:00 2001 From: choreo development Date: Tue, 22 Oct 2024 15:05:38 -0400 Subject: [PATCH 03/50] finishedd copying to intl_es.arb --- assets/l10n/intl_es.arb | 142 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 141 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_es.arb b/assets/l10n/intl_es.arb index dd8312d8f..acfcdee46 100644 --- a/assets/l10n/intl_es.arb +++ b/assets/l10n/intl_es.arb @@ -4731,5 +4731,145 @@ } }, "commandHint_googly": "Enviar unos ojos saltones", - "reportContentIssue": "Problema de contenido" + "reportContentIssue": "Problema de contenido", + "alwaysUse24HourFormat": "falso", + "countChatsAndCountParticipants": "{chats} chats y {participants} participantes", + "@countChatsAndCountParticipants": { + "type": "text", + "placeholders": { + "chats": {}, + "participants": {} + } + }, + "noMoreChatsFound": "No se encontraron más chats...", + "noChatsFoundHere": "Aún no se encontraron chats aquí. Inicia un nuevo chat con alguien usando el botón de abajo. ⤵️", + "joinedChats": "Chats unidos", + "unread": "No leído", + "space": "Espacio", + "spaces": "Espacios", + "enterASpacepName": "Ingresa un nombre", + "invitedBy": "📩 Invitado por {user}", + "@invitedBy": { + "placeholders": { + "user": {} + } + }, + "clickMessageBody": "Haz clic en un mensaje para herramientas de idioma como traducción, reproducción y más!", + "searchIn": "Buscar en el chat \"{chat}\"...", + "@searchIn": { + "type": "text", + "placeholders": { + "chat": {} + } + }, + "subscribedToUnlockTools": "¡Suscríbete para desbloquear la traducción interactiva y la verificación gramatical, la reproducción de audio, las actividades de práctica personalizadas y la analítica de aprendizaje!", + "conversationBotModeSelectOption_storyGame": "Juego de Historia", + "conversationBotCustomZone_title": "Configuraciones Personalizadas", + "conversationBotCustomZone_customSystemPromptLabel": "Mensaje del sistema", + "conversationBotCustomZone_customSystemPromptPlaceholder": "Establecer mensaje del sistema personalizado", + "conversationBotCustomZone_customSystemPromptEmptyError": "Falta mensaje del sistema personalizado", + "conversationBotCustomZone_customTriggerReactionEnabledLabel": "Responde a la reacción ⏩", + "botConfig": "Configuraciones del Bot de Conversación", + "addConversationBotDialogTitleInvite": "Confirmar la invitación del bot de conversación", + "addConversationBotButtonInvite": "Invitar", + "addConversationBotDialogInviteConfirmation": "Invitar", + "addConversationBotButtonTitleRemove": "Confirmar la eliminación del bot de conversación", + "addConversationBotButtonRemove": "Eliminar", + "addConversationBotDialogRemoveConfirmation": "Eliminar", + "conversationBotConfigConfirmChange": "Confirmar", + "conversationBotStatus": "Estado del Bot", + "conversationBotTextAdventureZone_title": "Aventura de Texto", + "conversationBotTextAdventureZone_instructionLabel": "Instrucciones del Maestro del Juego", + "conversationBotTextAdventureZone_instructionPlaceholder": "Establecer instrucciones del maestro del juego", + "conversationBotCustomZone_instructionSystemPromptEmptyError": "Faltan instrucciones del maestro del juego", + "suggestToSpace": "Sugerir este espacio", + "suggestToSpaceDesc": "Los subespacios sugeridos aparecerán en la lista de chats de su espacio principal", + "practice": "Práctica", + "noLanguagesSet": "No hay idiomas configurados", + "hintTitle": "Sugerencia:", + "speechToTextBody": "Ve qué tan bien lo hiciste al mirar tus puntajes de Precisión y Palabras Por Minuto.", + "previous": "Anterior", + "languageButtonLabel": "Idioma: {currentLanguage}", + "@languageButtonLabel": { + "type": "text", + "placeholders": { + "currentLanguage": {} + } + }, + "changeAnalyticsView": "Cambiar Vista de Análisis", + "l1TranslationBody": "Los mensajes en tu idioma base no serán traducidos.", + "continueText": "Continuar", + "deleteSubscriptionWarningTitle": "YTienes una suscripción activa", + "deleteSubscriptionWarningBody": "Eliminar tu cuenta no cancelará automáticamente tu suscripción.", + "manageSubscription": "Gestionar Suscripción", + "createSpace": "Crear espacio", + "createChat": "Crear chat", + "error520Title": "Por favor, intenta de nuevo.", + "error520Desc": "Lo sentimos, no pudimos entender tu mensaje...", + "wordsUsed": "Palabras Usadas", + "errorTypes": "Tipos de Error", + "level": "Nivel", + "canceledSend": "Envío cancelado", + "morphsUsed": "Morphs Usados", + "translationChoicesBody": "Haz clic y mantén presionada una opción para una pista.", + "sendCanceled": "Envío cancelado", + "goToSpace": "Ir al espacio: {space}", + "@goToSpace": { + "type": "text", + "space": {} + }, + "markAsUnread": "Marcar como no leído", + "userLevel": "{level} - Usuario", + "@userLevel": { + "type": "text", + "placeholders": { + "level": {} + } + }, + "moderatorLevel": "{level} - Moderador", + "@moderatorLevel": { + "type": "text", + "placeholders": { + "level": {} + } + }, + "adminLevel": "{level} - Administrador", + "@adminLevel": { + "type": "text", + "placeholders": { + "level": {} + } + }, + "changeGeneralChatSettings": "Cambiar la configuración general del chat.", + "inviteOtherUsers": "Invitar a otros usuarios a este chat", + "changeTheChatPermissions": "Cambiar los permisos del chat", + "changeTheVisibilityOfChatHistory": "Cambiar la visibilidad del historial de chat", + "changeTheCanonicalRoomAlias": "Cambiar la dirección del chat público principal.", + "sendRoomNotifications": "Enviar una notificación a @room", + "changeTheDescriptionOfTheGroup": "Cambiar la descripción del chat", + "chatPermissionsDescription": "Define qué nivel de poder es necesario para ciertas acciones en este chat. Los niveles de poder 0, 50 y 100 suelen representar a usuarios, moderadores y administradores, pero cualquier graduación es posible.", + "updateInstalled": "🎉 ¡Actualización {version} instalada!", + "@updateInstalled": { + "type": "text", + "placeholders": { + "version": {} + } + }, + "loginWithMatrixId": "Iniciar sesión con Matrix-ID.", + "discoverHomeservers": "Descubrir homeservers", + "whatIsAHomeserver": "¿Qué es un homeserver?", + "homeserverDescription": "Todos tus datos se almacenan en el homeserver, al igual que un proveedor de correo electrónico. Puedes elegir qué homeserver deseas utilizar, mientras que aún puedes comunicarte con todos. Aprende más en https://matrix.org.", + "doesNotSeemToBeAValidHomeserver": "No parece ser un homeserver compatible. ¿URL incorrecta?", + "grammar": "Gramática", + "contactHasBeenInvitedToTheChat": "El contacto ha sido invitado al chat", + "inviteChat": "📨 Invitar al chat", + "chatName": "Nombre del chat", + "reportContentIssueTitle": "Informar sobre un problema de contenido", + "feedback": "Comentarios opcionales", + "reportContentIssueDescription": "¡Ups! La IA puede facilitar experiencias de aprendizaje personalizadas, pero... también alucina. Por favor, proporciona cualquier comentario que tengas y lo intentaremos de nuevo.", + "clickTheWordAgainToDeselect": "Click the selected word to deselect it.", + "l2SupportNa": "Haz clic en la palabra seleccionada para deseleccionarla", + "l2SupportAlpha": "Alfa", + "l2SupportBeta": "Beta", + "l2SupportFull": "Lleno" } \ No newline at end of file From 2a7fd9a9620b27e3f4161601e19e7025368ce49a Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 22 Oct 2024 15:27:01 -0400 Subject: [PATCH 04/50] always provice error message to card error widget --- .../widgets/chat/message_audio_card.dart | 4 +- .../chat/message_translation_card.dart | 2 +- lib/pangea/widgets/igc/card_error_widget.dart | 39 ++++++++----------- lib/pangea/widgets/igc/card_header.dart | 17 ++++---- 4 files changed, 28 insertions(+), 34 deletions(-) diff --git a/lib/pangea/widgets/chat/message_audio_card.dart b/lib/pangea/widgets/chat/message_audio_card.dart index 64bb449aa..82375bacf 100644 --- a/lib/pangea/widgets/chat/message_audio_card.dart +++ b/lib/pangea/widgets/chat/message_audio_card.dart @@ -210,7 +210,9 @@ class MessageAudioCardState extends State { tts.missingVoiceButton, ], ) - : const CardErrorWidget(), + : const CardErrorWidget( + error: "Null audio file in message_audio_card", + ), ), ], ); diff --git a/lib/pangea/widgets/chat/message_translation_card.dart b/lib/pangea/widgets/chat/message_translation_card.dart index c209e7b8f..81ed7a2e2 100644 --- a/lib/pangea/widgets/chat/message_translation_card.dart +++ b/lib/pangea/widgets/chat/message_translation_card.dart @@ -130,7 +130,7 @@ class MessageTranslationCardState extends State { if (!_fetchingTranslation && repEvent == null && selectionTranslation == null) { - return const CardErrorWidget(); + return const CardErrorWidget(error: "No translation found"); } return Padding( diff --git a/lib/pangea/widgets/igc/card_error_widget.dart b/lib/pangea/widgets/igc/card_error_widget.dart index 3f08f6277..708fdd88f 100644 --- a/lib/pangea/widgets/igc/card_error_widget.dart +++ b/lib/pangea/widgets/igc/card_error_widget.dart @@ -1,7 +1,6 @@ import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart'; import 'package:fluffychat/pangea/utils/bot_style.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; -import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart'; import 'package:fluffychat/pangea/widgets/common/bot_face_svg.dart'; import 'package:fluffychat/pangea/widgets/igc/card_header.dart'; import 'package:flutter/material.dart'; @@ -21,30 +20,26 @@ class CardErrorWidget extends StatelessWidget { Widget build(BuildContext context) { final ErrorCopy errorCopy = ErrorCopy(context, error); - return Container( + return Padding( padding: const EdgeInsets.all(8), - constraints: const BoxConstraints(minHeight: minCardHeight), - alignment: Alignment.center, - child: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - CardHeader( - text: errorCopy.title, - botExpression: BotExpression.addled, - onClose: () => choreographer?.onMatchError( - cursorOffset: offset, - ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + CardHeader( + text: errorCopy.title, + botExpression: BotExpression.addled, + onClose: () => choreographer?.onMatchError( + cursorOffset: offset, ), - const SizedBox(height: 10.0), - Center( - child: Text( - errorCopy.body, - style: BotStyle.text(context), - ), + ), + const SizedBox(height: 10.0), + Center( + child: Text( + errorCopy.body, + style: BotStyle.text(context), ), - ], - ), + ), + ], ), ); } diff --git a/lib/pangea/widgets/igc/card_header.dart b/lib/pangea/widgets/igc/card_header.dart index 5ee6b98f2..671e58492 100644 --- a/lib/pangea/widgets/igc/card_header.dart +++ b/lib/pangea/widgets/igc/card_header.dart @@ -1,8 +1,8 @@ +import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/pangea/utils/bot_style.dart'; import 'package:flutter/material.dart'; -import 'package:fluffychat/config/app_config.dart'; import '../../../widgets/matrix.dart'; -import '../../utils/bot_style.dart'; import '../common/bot_face_svg.dart'; class CardHeader extends StatelessWidget { @@ -22,8 +22,6 @@ class CardHeader extends StatelessWidget { return Padding( padding: const EdgeInsets.only(bottom: 5.0), child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, children: [ Padding( padding: const EdgeInsets.only(top: 3.0), @@ -33,13 +31,12 @@ class CardHeader extends StatelessWidget { ), ), const SizedBox(width: 5.0), - Expanded( - child: Text( - text, - style: BotStyle.text(context), - textAlign: TextAlign.left, - ), + Text( + text, + style: BotStyle.text(context), + textAlign: TextAlign.left, ), + const SizedBox(width: 5.0), CircleAvatar( backgroundColor: AppConfig.primaryColor.withOpacity(0.1), child: IconButton( From f99b48af980fe35bb263f48f31e1e5656f1cb6f2 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 22 Oct 2024 15:32:41 -0400 Subject: [PATCH 05/50] if eventID is invalid, don't try to set read marker --- lib/pages/chat/chat.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index d5cb30747..cb1c86f5e 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -485,6 +485,14 @@ class ChatController extends State Future? setReadMarkerFuture; void setReadMarker({String? eventId}) { + // #Pangea + if (eventId != null && + (eventId.contains("web") || + eventId.contains("android") || + eventId.contains("ios"))) { + return; + } + // Pangea# if (setReadMarkerFuture != null) return; if (_scrolledUp) return; if (scrollUpBannerEventId != null) return; From 2d9cb5f8d989f4066fe6a03cf4218adce2585b52 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 22 Oct 2024 15:38:56 -0400 Subject: [PATCH 06/50] clearer error messages on fail to invite bot to space --- lib/pages/new_space/new_space.dart | 3 ++- lib/pangea/controllers/pangea_controller.dart | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/pages/new_space/new_space.dart b/lib/pages/new_space/new_space.dart index e89159900..9264d0657 100644 --- a/lib/pages/new_space/new_space.dart +++ b/lib/pages/new_space/new_space.dart @@ -136,7 +136,8 @@ class NewSpaceController extends State { await room.invite(BotName.byEnvironment); } catch (err) { ErrorHandler.logError( - e: "Failed to invite pangea bot to space ${room.id}", + e: "Failed to invite pangea bot to new space", + data: {"spaceId": spaceId, "error": err}, ); } MatrixState.pangeaController.classController diff --git a/lib/pangea/controllers/pangea_controller.dart b/lib/pangea/controllers/pangea_controller.dart index 95a8ef57a..3f0938052 100644 --- a/lib/pangea/controllers/pangea_controller.dart +++ b/lib/pangea/controllers/pangea_controller.dart @@ -298,7 +298,8 @@ class PangeaController { await space.invite(BotName.byEnvironment); } catch (err) { ErrorHandler.logError( - e: "Failed to invite pangea bot to space ${space.id}", + e: "Failed to invite pangea bot to existing space", + data: {"spaceId": space.id, "error": err}, ); } } From d26e71123e84904246eedb06b67905bfe623ecef Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Tue, 22 Oct 2024 16:40:44 -0400 Subject: [PATCH 07/50] adding user l1 and l2 to text to speech req --- lib/pangea/controllers/text_to_speech_controller.dart | 6 ++++++ lib/pangea/matrix_event_wrappers/pangea_message_event.dart | 2 ++ 2 files changed, 8 insertions(+) diff --git a/lib/pangea/controllers/text_to_speech_controller.dart b/lib/pangea/controllers/text_to_speech_controller.dart index e032c4045..9d409c515 100644 --- a/lib/pangea/controllers/text_to_speech_controller.dart +++ b/lib/pangea/controllers/text_to_speech_controller.dart @@ -80,17 +80,23 @@ class TTSToken { class TextToSpeechRequest { String text; String langCode; + String userL1; + String userL2; List tokens; TextToSpeechRequest({ required this.text, required this.langCode, + required this.userL1, + required this.userL2, required this.tokens, }); Map toJson() => { ModelKey.text: text, ModelKey.langCode: langCode, + ModelKey.userL1: userL1, + ModelKey.userL2: userL2, ModelKey.tokens: tokens.map((token) => token.toJson()).toList(), }; diff --git a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart index 324c4a018..85dfb8760 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart @@ -93,6 +93,8 @@ class PangeaMessageEvent { text: rep.content.text, tokens: (await rep.tokensGlobal(context)).map((t) => t.text).toList(), langCode: langCode, + userL1: l1Code ?? LanguageKeys.unknownLanguage, + userL2: l2Code ?? LanguageKeys.unknownLanguage, ); final TextToSpeechResponse response = From 9f485ccb0549f1770f893716e5d024fce845c3da Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 22 Oct 2024 16:42:05 -0400 Subject: [PATCH 08/50] dynamic sizing for practice activity toolbar content --- .../multiple_choice_activity.dart | 2 +- .../no_more_practice_card.dart | 22 ++---- .../practice_activity_card.dart | 76 ++++++++----------- 3 files changed, 41 insertions(+), 59 deletions(-) diff --git a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart index 5a1f50497..f0ad5b80a 100644 --- a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart +++ b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart @@ -94,7 +94,7 @@ class MultipleChoiceActivityState extends State { Widget build(BuildContext context) { final PracticeActivityModel practiceActivity = widget.currentActivity; - return Container( + return Padding( padding: const EdgeInsets.all(8), child: Column( children: [ diff --git a/lib/pangea/widgets/practice_activity/no_more_practice_card.dart b/lib/pangea/widgets/practice_activity/no_more_practice_card.dart index 1cef6c174..d4844ac21 100644 --- a/lib/pangea/widgets/practice_activity/no_more_practice_card.dart +++ b/lib/pangea/widgets/practice_activity/no_more_practice_card.dart @@ -71,24 +71,16 @@ class GamifiedTextWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return Center( + return Padding( + padding: const EdgeInsets.all(8), child: Column( - mainAxisSize: MainAxisSize.min, // Adjusts the size to fit children children: [ - const SizedBox(height: 10), // Spacing between the star and text - // Star animation above the text const StarAnimationWidget(), - const SizedBox(height: 10), // Spacing between the star and text - Container( - constraints: const BoxConstraints( - minHeight: 80, - ), - padding: const EdgeInsets.all(8), - child: Text( - userMessage, - style: BotStyle.text(context), - textAlign: TextAlign.center, // Center-align the text - ), + const SizedBox(height: 10), + Text( + userMessage, + style: BotStyle.text(context), + textAlign: TextAlign.center, ), ], ), diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index 82d3c0a17..113c7573e 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -306,54 +306,44 @@ class MessagePracticeActivityCardState extends State { } } - String? get userMessage { - if (!fetchingActivity && currentActivity == null) { - return L10n.of(context)!.noActivitiesFound; - } - return null; - } - @override Widget build(BuildContext context) { - if (userMessage != null) { - return GamifiedTextWidget(userMessage: userMessage!); + if (!fetchingActivity && currentActivity == null) { + return GamifiedTextWidget( + userMessage: L10n.of(context)!.noActivitiesFound, + ); } - return Row( - mainAxisSize: MainAxisSize.min, + return Stack( + alignment: Alignment.center, children: [ - Stack( - alignment: Alignment.center, - children: [ - // Main content - const Positioned( - child: PointsGainedAnimation(), - ), - Container( - padding: const EdgeInsets.all(8), - child: activityWidget, - ), - // Conditionally show the darkening and progress indicator based on the loading state - if (!savoringTheJoy && fetchingActivity) ...[ - // Semi-transparent overlay - Container( - color: Colors.black.withOpacity(0.5), // Darkening effect - ), - // Circular progress indicator in the center - const Center( - child: CircularProgressIndicator(), - ), - ], - // Flag button in the top right corner - Positioned( - top: 0, - right: 0, - child: ContentIssueButton( - isActive: currentActivity != null, - submitFeedback: submitFeedback, - ), - ), - ], + // Main content + const Positioned( + child: PointsGainedAnimation(), + ), + Padding( + padding: const EdgeInsets.fromLTRB(8, 20, 8, 8), + child: activityWidget, + ), + // Conditionally show the darkening and progress indicator based on the loading state + if (!savoringTheJoy && fetchingActivity) ...[ + // Semi-transparent overlay + Container( + color: Colors.black.withOpacity(0.5), // Darkening effect + ), + // Circular progress indicator in the center + const Center( + child: CircularProgressIndicator(), + ), + ], + // Flag button in the top right corner + Positioned( + top: 0, + right: 0, + child: ContentIssueButton( + isActive: currentActivity != null, + submitFeedback: submitFeedback, + ), ), ], ); From d5eee79f4cfd14c5182b2f00e2f645261afaccc7 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 23 Oct 2024 09:08:32 -0400 Subject: [PATCH 09/50] log actual error in message_audio_card logging statements instead of empty exception --- lib/pangea/widgets/chat/message_audio_card.dart | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/pangea/widgets/chat/message_audio_card.dart b/lib/pangea/widgets/chat/message_audio_card.dart index 82375bacf..7a003a01c 100644 --- a/lib/pangea/widgets/chat/message_audio_card.dart +++ b/lib/pangea/widgets/chat/message_audio_card.dart @@ -89,8 +89,7 @@ class MessageAudioCardState extends State { // should never happen but just in case debugger(when: kDebugMode); ErrorHandler.logError( - e: Exception(), - m: 'audioFile duration is null in MessageAudioCardState', + e: 'audioFile duration is null in MessageAudioCardState', data: { 'audioFile': audioFile, }, @@ -124,8 +123,7 @@ class MessageAudioCardState extends State { // if we didn't find the token, we should pause if debug and log an error debugger(when: kDebugMode); ErrorHandler.logError( - e: Exception(), - m: 'could not find token for selection in MessageAudioCardState', + e: 'could not find token for selection in MessageAudioCardState', data: { 'selection': selection, 'tokens': tokens, @@ -174,7 +172,7 @@ class MessageAudioCardState extends State { ), ); ErrorHandler.logError( - e: Exception(), + e: e, s: s, m: 'something wrong getting audio in MessageAudioCardState', data: { From 0b2c32904a031693270d0804c21ab25d938b7345 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 23 Oct 2024 09:21:02 -0400 Subject: [PATCH 10/50] only call setState in message_selection_overlay if mounted --- lib/pangea/widgets/chat/message_selection_overlay.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pangea/widgets/chat/message_selection_overlay.dart b/lib/pangea/widgets/chat/message_selection_overlay.dart index db421dd15..21c875681 100644 --- a/lib/pangea/widgets/chat/message_selection_overlay.dart +++ b/lib/pangea/widgets/chat/message_selection_overlay.dart @@ -106,7 +106,8 @@ class MessageOverlayController extends State void setState(VoidCallback fn) { if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.idle || SchedulerBinding.instance.schedulerPhase == - SchedulerPhase.postFrameCallbacks) { + SchedulerPhase.postFrameCallbacks && + mounted) { // It's safe to call setState immediately super.setState(fn); } else { From bc1dfc1e0e8f017c9c4032afcd4b18112042d35b Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 23 Oct 2024 09:21:44 -0400 Subject: [PATCH 11/50] when inviting tachers to analytics room, request all particpants to ensure teacher isn't already a member --- .../pangea_room_extension/room_analytics_extension.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart b/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart index 73371b080..b44c40ece 100644 --- a/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart @@ -99,7 +99,7 @@ extension AnalyticsRoomExtension on Room { await analyticsRoom.requestParticipants(); } - final List participants = analyticsRoom.getParticipants(); + final List participants = await analyticsRoom.requestParticipants(); final List uninvitedTeachers = teachersLocal .where((teacher) => !participants.contains(teacher)) .toList(); @@ -110,8 +110,12 @@ extension AnalyticsRoomExtension on Room { (teacher) => analyticsRoom.invite(teacher.id).catchError((err, s) { ErrorHandler.logError( e: err, - m: "Failed to invite teacher ${teacher.id} to analytics room ${analyticsRoom.id}", + m: "Failed to invite teacher to analytics room", s: s, + data: { + "teacherId": teacher.id, + "analyticsRoomId": analyticsRoom.id, + }, ); }), ), From 7e9855dcc1eae311a65f8f00a36bfcf87376e2bd Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 23 Oct 2024 09:34:03 -0400 Subject: [PATCH 12/50] better error logging if sourceText is null in getNextTranslationData --- .../choreographer/controllers/it_controller.dart | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/pangea/choreographer/controllers/it_controller.dart b/lib/pangea/choreographer/controllers/it_controller.dart index 636415b8e..b618386f8 100644 --- a/lib/pangea/choreographer/controllers/it_controller.dart +++ b/lib/pangea/choreographer/controllers/it_controller.dart @@ -4,7 +4,6 @@ import 'dart:developer'; import 'package:fluffychat/pangea/choreographer/controllers/error_service.dart'; import 'package:fluffychat/pangea/constants/choreo_constants.dart'; import 'package:fluffychat/pangea/enum/construct_use_type_enum.dart'; -import 'package:fluffychat/pangea/enum/instructions_enum.dart'; import 'package:fluffychat/pangea/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:flutter/foundation.dart'; @@ -180,6 +179,18 @@ class ITController { } Future getNextTranslationData() async { + if (sourceText == null) { + ErrorHandler.logError( + e: Exception("sourceText is null in getNextTranslationData"), + data: { + "sourceText": sourceText, + "currentITStep": currentITStep, + "nextITStep": nextITStep, + }, + ); + return; + } + try { if (completedITSteps.length < goldRouteTracker.continuances.length) { final String currentText = choreographer.currentText; From 7c0078694b9567e4ccc4a97a4152a877899bccba Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Wed, 23 Oct 2024 10:06:16 -0400 Subject: [PATCH 13/50] adding mounted check --- lib/pages/chat/events/message_content.dart | 2 -- .../widgets/practice_activity/practice_activity_card.dart | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 2cddb6f67..4e21f00ac 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -123,7 +123,6 @@ class MessageContent extends StatelessWidget { @override Widget build(BuildContext context) { - // debugger(when: overlayController != null); final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor; final buttonTextColor = textColor; switch (event.type) { @@ -307,7 +306,6 @@ class MessageContent extends StatelessWidget { height: 1.3, ); - // debugger(when: overlayController != null); if (overlayController != null && pangeaMessageEvent != null) { return OverlayMessageText( pangeaMessageEvent: pangeaMessageEvent!, diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index 113c7573e..fd4428b59 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -177,7 +177,7 @@ class MessagePracticeActivityCardState extends State { Future _savorTheJoy() async { debugger(when: savoringTheJoy && kDebugMode); - setState(() => savoringTheJoy = true); + if (mounted) setState(() => savoringTheJoy = true); await Future.delayed(appropriateTimeForJoy); From 5d190cc51e1d0d96e32e232e93ffa0305e060d33 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 23 Oct 2024 10:10:49 -0400 Subject: [PATCH 14/50] check for null content in message translation card --- .../chat/message_translation_card.dart | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/pangea/widgets/chat/message_translation_card.dart b/lib/pangea/widgets/chat/message_translation_card.dart index 81ed7a2e2..4d8bad28d 100644 --- a/lib/pangea/widgets/chat/message_translation_card.dart +++ b/lib/pangea/widgets/chat/message_translation_card.dart @@ -144,14 +144,18 @@ class MessageTranslationCardState extends State { child: Column( children: [ widget.selection != null - ? Text( - selectionTranslation!, - style: BotStyle.text(context), - ) - : Text( - repEvent!.text, - style: BotStyle.text(context), - ), + ? selectionTranslation != null + ? Text( + selectionTranslation!, + style: BotStyle.text(context), + ) + : const ToolbarContentLoadingIndicator() + : repEvent != null + ? Text( + repEvent!.text, + style: BotStyle.text(context), + ) + : const ToolbarContentLoadingIndicator(), if (notGoingToTranslate && widget.selection == null) InlineTooltip( instructionsEnum: InstructionsEnum.l1Translation, From 3efe3743023c996049caba48fe8b30c074edebd9 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 23 Oct 2024 10:12:40 -0400 Subject: [PATCH 15/50] added mounted check before setting state in choice_array --- lib/pangea/choreographer/widgets/choice_array.dart | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/pangea/choreographer/widgets/choice_array.dart b/lib/pangea/choreographer/widgets/choice_array.dart index ff13da78f..32395099e 100644 --- a/lib/pangea/choreographer/widgets/choice_array.dart +++ b/lib/pangea/choreographer/widgets/choice_array.dart @@ -44,17 +44,13 @@ class ChoicesArrayState extends State { void disableInteraction() { WidgetsBinding.instance.addPostFrameCallback((_) { - setState(() { - interactionDisabled = true; - }); + if (mounted) setState(() => interactionDisabled = true); }); } void enableInteractions() { WidgetsBinding.instance.addPostFrameCallback((_) { - setState(() { - interactionDisabled = false; - }); + if (mounted) setState(() => interactionDisabled = false); }); } From 696bd0f1299a8805520f476b61c5f811c9327b98 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 23 Oct 2024 10:57:05 -0400 Subject: [PATCH 16/50] in message overlay, wrap any calls to get renderbox or media query in a try catch block to get better error handling --- .../chat/message_selection_overlay.dart | 121 ++++++++++++------ 1 file changed, 80 insertions(+), 41 deletions(-) diff --git a/lib/pangea/widgets/chat/message_selection_overlay.dart b/lib/pangea/widgets/chat/message_selection_overlay.dart index 21c875681..7c91a87bc 100644 --- a/lib/pangea/widgets/chat/message_selection_overlay.dart +++ b/lib/pangea/widgets/chat/message_selection_overlay.dart @@ -11,6 +11,7 @@ import 'package:fluffychat/pangea/enum/message_mode_enum.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart'; +import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart'; import 'package:fluffychat/pangea/widgets/chat/message_toolbar_buttons.dart'; import 'package:fluffychat/pangea/widgets/chat/overlay_footer.dart'; @@ -261,14 +262,14 @@ class MessageOverlayController extends State @override void didChangeDependencies() { super.didChangeDependencies(); - if (messageSize == null || messageOffset == null) { + if (messageSize == null || messageOffset == null || screenHeight == null) { return; } // position the overlay directly over the underlying message - final headerBottomOffset = screenHeight - headerHeight; + final headerBottomOffset = screenHeight! - headerHeight; final footerBottomOffset = footerHeight; - final currentBottomOffset = screenHeight - + final currentBottomOffset = screenHeight! - messageOffset!.dy - messageSize!.height - belowMessageHeight; @@ -296,7 +297,7 @@ class MessageOverlayController extends State animationEndOffset = midpoint - messageSize!.height - belowMessageHeight; final totalTopOffset = animationEndOffset + messageSize!.height + AppConfig.toolbarMaxHeight; - final remainingSpace = screenHeight - totalTopOffset; + final remainingSpace = screenHeight! - totalTopOffset; if (remainingSpace < headerHeight) { // the overlay could run over the header, so it needs to be shifted down animationEndOffset -= (headerHeight - remainingSpace); @@ -311,7 +312,7 @@ class MessageOverlayController extends State // update the message height to fit the screen. The message is scrollable, so // this will make the both the toolbar box and the toolbar buttons visible. if (animationEndOffset < footerHeight + belowMessageHeight) { - final double remainingSpace = screenHeight - + final double remainingSpace = screenHeight! - AppConfig.toolbarMaxHeight - headerHeight - footerHeight - @@ -349,25 +350,57 @@ class MessageOverlayController extends State super.dispose(); } - RenderBox? get messageRenderBox => MatrixState.pAnyState.getRenderBox( + RenderBox? get messageRenderBox { + try { + return MatrixState.pAnyState.getRenderBox( widget._event.eventId, ); + } catch (e, s) { + ErrorHandler.logError(e: "Error getting message render box: $e", s: s); + return null; + } + } + + Size? get messageSize { + try { + return messageRenderBox?.size; + } catch (e, s) { + ErrorHandler.logError(e: "Error getting message size: $e", s: s); + return null; + } + } + + Offset? get messageOffset { + try { + return messageRenderBox?.localToGlobal(Offset.zero); + } catch (e, s) { + ErrorHandler.logError(e: "Error getting message offset: $e", s: s); + return null; + } + } - Size? get messageSize => messageRenderBox?.size; - Offset? get messageOffset => messageRenderBox?.localToGlobal(Offset.zero); double? adjustedMessageHeight; // height of the reply/forward bar + the reaction picker + contextual padding double get footerHeight => 48 + 56 + (FluffyThemes.isColumnMode(context) ? 16.0 : 8.0); + MediaQueryData? get mediaQuery { + try { + return MediaQuery.of(context); + } catch (e, s) { + ErrorHandler.logError(e: "Error getting media query: $e", s: s); + return null; + } + } + double get headerHeight => (Theme.of(context).appBarTheme.toolbarHeight ?? 56) + - MediaQuery.of(context).padding.top; + (mediaQuery?.padding.top ?? 0); - double get screenHeight => MediaQuery.of(context).size.height; + double? get screenHeight => mediaQuery?.size.height; - double get screenWidth => MediaQuery.of(context).size.width; + double? get screenWidth => mediaQuery?.size.width; @override Widget build(BuildContext context) { @@ -381,13 +414,17 @@ class MessageOverlayController extends State // the default spacing between the side of the screen and the message bubble const double messageMargin = Avatar.defaultSize + 16 + 8; final horizontalPadding = FluffyThemes.isColumnMode(context) ? 8.0 : 0.0; - final chatViewWidth = screenWidth - - (FluffyThemes.isColumnMode(context) - ? (FluffyThemes.columnWidth + FluffyThemes.navRailWidth) - : 0); + const totalMaxWidth = (FluffyThemes.columnWidth * 2.5) - messageMargin; - double maxWidth = chatViewWidth - (2 * horizontalPadding) - messageMargin; - if (maxWidth > totalMaxWidth) { + double? maxWidth; + if (screenWidth != null) { + final chatViewWidth = screenWidth! - + (FluffyThemes.isColumnMode(context) + ? (FluffyThemes.columnWidth + FluffyThemes.navRailWidth) + : 0); + maxWidth = chatViewWidth - (2 * horizontalPadding) - messageMargin; + } + if (maxWidth == null || maxWidth > totalMaxWidth) { maxWidth = totalMaxWidth; } @@ -450,34 +487,36 @@ class MessageOverlayController extends State ? null : messageOffset!.dx - horizontalPadding - columnOffset; - final double? rightPadding = widget._pangeaMessageEvent.ownMessage - ? screenWidth - - messageOffset!.dx - - messageSize!.width - - horizontalPadding - : null; - - final positionedOverlayMessage = _overlayPositionAnimation == null - ? Positioned( - left: leftPadding, - right: rightPadding, - bottom: screenHeight - - messageOffset!.dy - - messageSize!.height - - belowMessageHeight, - child: overlayMessage, - ) - : AnimatedBuilder( - animation: _overlayPositionAnimation!, - builder: (context, child) { - return Positioned( + final double? rightPadding = + (widget._pangeaMessageEvent.ownMessage && screenWidth != null) + ? screenWidth! - + messageOffset!.dx - + messageSize!.width - + horizontalPadding + : null; + + final positionedOverlayMessage = + (_overlayPositionAnimation == null || screenHeight == null) + ? Positioned( left: leftPadding, right: rightPadding, - bottom: _overlayPositionAnimation!.value, + bottom: screenHeight! - + messageOffset!.dy - + messageSize!.height - + belowMessageHeight, child: overlayMessage, + ) + : AnimatedBuilder( + animation: _overlayPositionAnimation!, + builder: (context, child) { + return Positioned( + left: leftPadding, + right: rightPadding, + bottom: _overlayPositionAnimation!.value, + child: overlayMessage, + ); + }, ); - }, - ); return Padding( padding: EdgeInsets.only( From f6bab9273340fabaeaaf5766fa6b338ab517c05e Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 23 Oct 2024 11:19:30 -0400 Subject: [PATCH 17/50] better error handling for renderbox errors --- .../chat/message_selection_overlay.dart | 82 +++++++++++-------- 1 file changed, 50 insertions(+), 32 deletions(-) diff --git a/lib/pangea/widgets/chat/message_selection_overlay.dart b/lib/pangea/widgets/chat/message_selection_overlay.dart index 7c91a87bc..48cdfbd47 100644 --- a/lib/pangea/widgets/chat/message_selection_overlay.dart +++ b/lib/pangea/widgets/chat/message_selection_overlay.dart @@ -105,17 +105,29 @@ class MessageOverlayController extends State /// This is a workaround to prevent that error @override void setState(VoidCallback fn) { - if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.idle || - SchedulerBinding.instance.schedulerPhase == - SchedulerPhase.postFrameCallbacks && - mounted) { + final phase = SchedulerBinding.instance.schedulerPhase; + if (mounted && + (phase == SchedulerPhase.idle || + phase == SchedulerPhase.postFrameCallbacks)) { // It's safe to call setState immediately - super.setState(fn); + try { + super.setState(fn); + } catch (e, s) { + ErrorHandler.logError( + e: "Error calling setState in MessageSelectionOverlay: $e", + s: s, + ); + } } else { // Defer the setState call to after the current frame WidgetsBinding.instance.addPostFrameCallback((_) { - if (mounted) { - super.setState(fn); + try { + if (mounted) super.setState(fn); + } catch (e, s) { + ErrorHandler.logError( + e: "Error calling setState in MessageSelectionOverlay after postframeCallback: $e", + s: s, + ); } }); } @@ -404,6 +416,8 @@ class MessageOverlayController extends State @override Widget build(BuildContext context) { + if (messageSize == null) return const SizedBox.shrink(); + final bool showDetails = (Matrix.of(context) .store .getBool(SettingKeys.displayChatDetailsColumn) ?? @@ -483,21 +497,25 @@ class MessageOverlayController extends State ? FluffyThemes.columnWidth + FluffyThemes.navRailWidth : 0; - final double? leftPadding = widget._pangeaMessageEvent.ownMessage - ? null - : messageOffset!.dx - horizontalPadding - columnOffset; - - final double? rightPadding = - (widget._pangeaMessageEvent.ownMessage && screenWidth != null) - ? screenWidth! - - messageOffset!.dx - - messageSize!.width - - horizontalPadding - : null; - - final positionedOverlayMessage = - (_overlayPositionAnimation == null || screenHeight == null) - ? Positioned( + final double? leftPadding = + (widget._pangeaMessageEvent.ownMessage || messageOffset == null) + ? null + : messageOffset!.dx - horizontalPadding - columnOffset; + + final double? rightPadding = (widget._pangeaMessageEvent.ownMessage && + screenWidth != null && + messageOffset != null && + messageSize != null) + ? screenWidth! - + messageOffset!.dx - + messageSize!.width - + horizontalPadding + : null; + + final positionedOverlayMessage = (_overlayPositionAnimation == null) + ? (screenHeight == null || messageSize == null || messageOffset == null) + ? const SizedBox.shrink() + : Positioned( left: leftPadding, right: rightPadding, bottom: screenHeight! - @@ -506,17 +524,17 @@ class MessageOverlayController extends State belowMessageHeight, child: overlayMessage, ) - : AnimatedBuilder( - animation: _overlayPositionAnimation!, - builder: (context, child) { - return Positioned( - left: leftPadding, - right: rightPadding, - bottom: _overlayPositionAnimation!.value, - child: overlayMessage, - ); - }, + : AnimatedBuilder( + animation: _overlayPositionAnimation!, + builder: (context, child) { + return Positioned( + left: leftPadding, + right: rightPadding, + bottom: _overlayPositionAnimation!.value, + child: overlayMessage, ); + }, + ); return Padding( padding: EdgeInsets.only( From dfe1ca6653cd21c6b32bcc4a82e439ffb0559549 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Wed, 23 Oct 2024 11:48:45 -0400 Subject: [PATCH 18/50] commenting out audio button in debug attempt --- .../widgets/chat/missing_voice_button.dart | 8 ++++--- .../multiple_choice_activity.dart | 17 ++++++------- .../practice_activity_card.dart | 24 +++++++++++++++---- .../practice_activity/word_audio_button.dart | 6 ++++- pubspec.yaml | 2 +- 5 files changed, 39 insertions(+), 18 deletions(-) diff --git a/lib/pangea/widgets/chat/missing_voice_button.dart b/lib/pangea/widgets/chat/missing_voice_button.dart index e1f8b74fb..3baa8422e 100644 --- a/lib/pangea/widgets/chat/missing_voice_button.dart +++ b/lib/pangea/widgets/chat/missing_voice_button.dart @@ -49,9 +49,11 @@ class MissingVoiceButton extends StatelessWidget { ), TextButton( onPressed: () => launchTTSSettings, - style: const ButtonStyle( - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), + // commenting out as suspecting this is causing an issue + // #freeze-activity + // style: const ButtonStyle( + // tapTargetSize: MaterialTapTargetSize.shrinkWrap, + // ), child: Text(L10n.of(context)!.openVoiceSettings), ), ], diff --git a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart index f0ad5b80a..8a34dfc6d 100644 --- a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart +++ b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart @@ -3,11 +3,9 @@ import 'dart:developer'; import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/choreographer/widgets/choice_array.dart'; import 'package:fluffychat/pangea/controllers/my_analytics_controller.dart'; -import 'package:fluffychat/pangea/enum/activity_type_enum.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_record_model.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart'; -import 'package:fluffychat/pangea/widgets/practice_activity/word_audio_button.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -85,9 +83,11 @@ class MultipleChoiceActivityState extends State { widget.practiceCardController.onActivityFinish(); } - setState( - () => selectedChoiceIndex = index, - ); + if (mounted) { + setState( + () => selectedChoiceIndex = index, + ); + } } @override @@ -106,9 +106,10 @@ class MultipleChoiceActivityState extends State { ), ), const SizedBox(height: 8), - if (practiceActivity.activityType == - ActivityTypeEnum.wordFocusListening) - WordAudioButton(text: practiceActivity.content.answer), + // #freeze-activity + // if (practiceActivity.activityType == + // ActivityTypeEnum.wordFocusListening) + // WordAudioButton(text: practiceActivity.content.answer), ChoicesArray( isLoading: false, uniqueKeyForLayerLink: (index) => "multiple_choice_$index", diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index fd4428b59..62dadc78b 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -175,13 +175,26 @@ class MessagePracticeActivityCardState extends State { ); Future _savorTheJoy() async { - debugger(when: savoringTheJoy && kDebugMode); + try { + debugger(when: savoringTheJoy && kDebugMode); - if (mounted) setState(() => savoringTheJoy = true); + if (mounted) setState(() => savoringTheJoy = true); - await Future.delayed(appropriateTimeForJoy); + await Future.delayed(appropriateTimeForJoy); - if (mounted) setState(() => savoringTheJoy = false); + if (mounted) setState(() => savoringTheJoy = false); + } catch (e, s) { + debugger(when: kDebugMode); + ErrorHandler.logError( + e: e, + s: s, + m: 'Failed to savor the joy', + data: { + 'activity': currentActivity, + 'record': currentCompletionRecord, + }, + ); + } } /// Called when the user finishes an activity. @@ -211,7 +224,8 @@ class MessagePracticeActivityCardState extends State { widget.pangeaMessageEvent.eventId, ); - // + // wait for the joy to be savored before resolving the activity + // and setting it to replace the previous activity final Iterable result = await Future.wait([ _savorTheJoy(), _fetchNewActivity(), diff --git a/lib/pangea/widgets/practice_activity/word_audio_button.dart b/lib/pangea/widgets/practice_activity/word_audio_button.dart index 226328804..78c0efb7d 100644 --- a/lib/pangea/widgets/practice_activity/word_audio_button.dart +++ b/lib/pangea/widgets/practice_activity/word_audio_button.dart @@ -22,6 +22,7 @@ class WordAudioButtonState extends State { @override void initState() { // TODO: implement initState + debugPrint('initState WordAudioButton'); super.initState(); ttsController.setupTTS().then((value) => setState(() {})); } @@ -34,6 +35,7 @@ class WordAudioButtonState extends State { @override Widget build(BuildContext context) { + debugPrint('build WordAudioButton'); return Column( children: [ IconButton( @@ -67,7 +69,9 @@ class WordAudioButtonState extends State { } }, // Disable button if language isn't supported ), - ttsController.missingVoiceButton, + // #freeze-activity + //commenting out to see if it's causing an issue + // ttsController.missingVoiceButton, ], ); } diff --git a/pubspec.yaml b/pubspec.yaml index 1af58cbb5..2d2c27543 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ description: Learn a language while texting your friends. # Pangea# publish_to: none # On version bump also increase the build number for F-Droid -version: 1.21.5+3541 +version: 1.21.5+3542 environment: sdk: ">=3.0.0 <4.0.0" From a7a7f4c252c1257fbf4ae1aae4d52b2fa963b3fe Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 23 Oct 2024 12:40:38 -0400 Subject: [PATCH 19/50] switch form dropdowns to new wigget type to make options appear below the dropdown --- assets/l10n/intl_en.arb | 7 ++++--- .../conversation_bot_mode_dynamic_zone.dart | 4 ++++ .../conversation_bot_mode_select.dart | 20 +++---------------- .../conversation_bot_settings_form.dart | 12 +++++------ .../space/language_level_dropdown.dart | 8 ++------ pubspec.lock | 18 ++++++++--------- pubspec.yaml | 1 + 7 files changed, 29 insertions(+), 41 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 51ab9d1d7..d7eb4b149 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -4016,9 +4016,9 @@ "conversationBotModeSelectOption_storyGame": "Story Game", "conversationBotDiscussionZone_title": "Discussion Settings", "conversationBotDiscussionZone_discussionTopicLabel": "Discussion Topic", - "conversationBotDiscussionZone_discussionTopicPlaceholder": "Set Discussion Topic", + "conversationBotDiscussionZone_discussionTopicPlaceholder": "Set discussion topic", "conversationBotDiscussionZone_discussionKeywordsLabel": "Discussion Keywords", - "conversationBotDiscussionZone_discussionKeywordsPlaceholder": "Set Discussion Keywords", + "conversationBotDiscussionZone_discussionKeywordsPlaceholder": "Set discussion keywords", "conversationBotDiscussionZone_discussionKeywordsHintText": "Comma separated list of keywords to guide the discussion", "conversationBotDiscussionZone_discussionTriggerScheduleEnabledLabel": "Send discussion prompt on a schedule", "conversationBotDiscussionZone_discussionTriggerScheduleHourIntervalLabel": "Hours between discussion prompts", @@ -4364,5 +4364,6 @@ "selectBotLanguage": "Select bot language", "chooseVoice": "Choose a voice", "enterLanguageLevel": "Please enter a language level", - "enterDiscussionTopic": "Please enter a discussion topic" + "enterDiscussionTopic": "Please enter a discussion topic", + "selectBotChatMode": "Select chat mode" } \ No newline at end of file diff --git a/lib/pangea/widgets/conversation_bot/conversation_bot_mode_dynamic_zone.dart b/lib/pangea/widgets/conversation_bot/conversation_bot_mode_dynamic_zone.dart index e8f33fe3d..5a3082610 100644 --- a/lib/pangea/widgets/conversation_bot/conversation_bot_mode_dynamic_zone.dart +++ b/lib/pangea/widgets/conversation_bot/conversation_bot_mode_dynamic_zone.dart @@ -27,6 +27,8 @@ class ConversationBotModeDynamicZone extends StatelessWidget { decoration: InputDecoration( hintText: L10n.of(context)! .conversationBotDiscussionZone_discussionTopicPlaceholder, + contentPadding: + const EdgeInsets.symmetric(horizontal: 28.0, vertical: 12.0), ), controller: discussionTopicController, validator: (value) => enabled && @@ -44,6 +46,7 @@ class ConversationBotModeDynamicZone extends StatelessWidget { decoration: InputDecoration( hintText: L10n.of(context)! .conversationBotDiscussionZone_discussionKeywordsPlaceholder, + contentPadding: const EdgeInsets.symmetric(horizontal: 28.0), ), controller: discussionKeywordsController, enabled: enabled, @@ -58,6 +61,7 @@ class ConversationBotModeDynamicZone extends StatelessWidget { decoration: InputDecoration( hintText: L10n.of(context)! .conversationBotCustomZone_customSystemPromptPlaceholder, + contentPadding: const EdgeInsets.symmetric(horizontal: 28.0), ), validator: (value) => enabled && botOptions.mode == BotMode.custom && diff --git a/lib/pangea/widgets/conversation_bot/conversation_bot_mode_select.dart b/lib/pangea/widgets/conversation_bot/conversation_bot_mode_select.dart index 3d893f078..408e6560e 100644 --- a/lib/pangea/widgets/conversation_bot/conversation_bot_mode_select.dart +++ b/lib/pangea/widgets/conversation_bot/conversation_bot_mode_select.dart @@ -1,3 +1,4 @@ +import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:fluffychat/pangea/constants/bot_mode.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -26,23 +27,8 @@ class ConversationBotModeSelect extends StatelessWidget { // L10n.of(context)!.conversationBotModeSelectOption_storyGame, }; - String? mode = initialMode; - if (!options.containsKey(initialMode)) { - mode = null; - } - - return DropdownButtonFormField( - // Initial Value - hint: Text( - options[mode ?? BotMode.discussion]!, - overflow: TextOverflow.clip, - textAlign: TextAlign.center, - ), - // ), - isExpanded: true, - // Down Arrow Icon - icon: const Icon(Icons.keyboard_arrow_down), - // Array list of items + return DropdownButtonFormField2( + hint: Text(L10n.of(context)!.selectBotChatMode), items: [ for (final entry in options.entries) DropdownMenuItem( diff --git a/lib/pangea/widgets/conversation_bot/conversation_bot_settings_form.dart b/lib/pangea/widgets/conversation_bot/conversation_bot_settings_form.dart index 195d35801..6b5535bc4 100644 --- a/lib/pangea/widgets/conversation_bot/conversation_bot_settings_form.dart +++ b/lib/pangea/widgets/conversation_bot/conversation_bot_settings_form.dart @@ -1,3 +1,4 @@ +import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:fluffychat/pangea/models/bot_options_model.dart'; import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_mode_dynamic_zone.dart'; import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_mode_select.dart'; @@ -36,8 +37,10 @@ class ConversationBotSettingsForm extends StatelessWidget { Widget build(BuildContext context) { return Column( children: [ - DropdownButtonFormField( - // Initial Value + DropdownButtonFormField2( + dropdownStyleData: const DropdownStyleData( + padding: EdgeInsets.zero, + ), hint: Text( L10n.of(context)!.selectBotLanguage, overflow: TextOverflow.clip, @@ -45,7 +48,6 @@ class ConversationBotSettingsForm extends StatelessWidget { ), value: botOptions.targetLanguage, isExpanded: true, - icon: const Icon(Icons.keyboard_arrow_down), items: MatrixState.pangeaController.pLanguageStore.targetOptions .map((language) { return DropdownMenuItem( @@ -60,8 +62,7 @@ class ConversationBotSettingsForm extends StatelessWidget { onChanged: enabled ? onUpdateBotLanguage : null, ), const SizedBox(height: 12), - DropdownButtonFormField( - // Initial Value + DropdownButtonFormField2( hint: Text( L10n.of(context)!.chooseVoice, overflow: TextOverflow.clip, @@ -69,7 +70,6 @@ class ConversationBotSettingsForm extends StatelessWidget { ), value: botOptions.targetVoice, isExpanded: true, - icon: const Icon(Icons.keyboard_arrow_down), items: const [], onChanged: enabled ? onUpdateBotVoice : null, ), diff --git a/lib/pangea/widgets/space/language_level_dropdown.dart b/lib/pangea/widgets/space/language_level_dropdown.dart index 0b238485a..a8c618bc1 100644 --- a/lib/pangea/widgets/space/language_level_dropdown.dart +++ b/lib/pangea/widgets/space/language_level_dropdown.dart @@ -1,3 +1,4 @@ +import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:fluffychat/pangea/constants/language_constants.dart'; import 'package:fluffychat/pangea/utils/language_level_copy.dart'; import 'package:flutter/material.dart'; @@ -19,18 +20,13 @@ class LanguageLevelDropdown extends StatelessWidget { @override Widget build(BuildContext context) { - return DropdownButtonFormField( - // Initial Value + return DropdownButtonFormField2( hint: Text( L10n.of(context)!.selectLanguageLevel, overflow: TextOverflow.clip, textAlign: TextAlign.center, ), value: initialLevel, - isExpanded: true, - // Down Arrow Icon - icon: const Icon(Icons.keyboard_arrow_down), - // Array list of items items: LanguageLevelType.allInts.map((int levelOption) { return DropdownMenuItem( value: levelOption, diff --git a/pubspec.lock b/pubspec.lock index 0a0e1ed23..99543db43 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -345,6 +345,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" + dropdown_button2: + dependency: "direct main" + description: + name: dropdown_button2 + sha256: b0fe8d49a030315e9eef6c7ac84ca964250155a6224d491c1365061bc974a9e1 + url: "https://pub.dev" + source: hosted + version: "2.3.9" dynamic_color: dependency: "direct main" description: @@ -2642,14 +2650,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.1" - visibility_detector: - dependency: transitive - description: - name: visibility_detector - sha256: "15c54a459ec2c17b4705450483f3d5a2858e733aee893dcee9d75fd04814940d" - url: "https://pub.dev" - source: hosted - version: "0.3.3" vm_service: dependency: transitive description: @@ -2723,7 +2723,7 @@ packages: source: hosted version: "1.2.0" win32: - dependency: "direct overridden" + dependency: transitive description: name: win32 sha256: "015002c060f1ae9f41a818f2d5640389cc05283e368be19dc8d77cecb43c40c9" diff --git a/pubspec.yaml b/pubspec.yaml index 1af58cbb5..1dc0fcc8f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -113,6 +113,7 @@ dependencies: android_intent_plus: ^5.2.0 country_picker: ^2.0.25 csv: ^6.0.0 + dropdown_button2: ^2.3.9 fl_chart: ^0.67.0 firebase_analytics: ^11.0.1 firebase_core: ^3.1.0 From 058f876146a4792d9eb7a49806b8a94515beabf3 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 23 Oct 2024 12:46:16 -0400 Subject: [PATCH 20/50] copyeditting and updated order of main menu options --- assets/l10n/intl_en.arb | 2 +- .../chat_list/client_chooser_button.dart | 51 ++++++++++--------- lib/pages/new_group/new_group_view.dart | 5 +- 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 51ab9d1d7..87e134d03 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -3095,7 +3095,7 @@ "type": "text", "placeholders": {} }, - "learningSettings": "My Learning Settings", + "learningSettings": "Learning settings", "classNameRequired": "Please enter a space name", "@classNameRequired": { "type": "text", diff --git a/lib/pages/chat_list/client_chooser_button.dart b/lib/pages/chat_list/client_chooser_button.dart index b22512bbe..6073034bd 100644 --- a/lib/pages/chat_list/client_chooser_button.dart +++ b/lib/pages/chat_list/client_chooser_button.dart @@ -41,44 +41,41 @@ class ClientChooserButton extends StatelessWidget { ], ), ), - PopupMenuItem( - value: SettingsAction.learning, - child: Row( - children: [ - const Icon(Icons.psychology_outlined), - const SizedBox(width: 18), - Expanded(child: Text(L10n.of(context)!.learningSettings)), - ], - ), - ), + // PopupMenuItem( + // value: SettingsAction.newGroup, + // child: Row( + // children: [ + // const Icon(Icons.group_add_outlined), + // const SizedBox(width: 18), + // Text(L10n.of(context)!.createGroup), + // ], + // ), + // ), // Pangea# PopupMenuItem( - value: SettingsAction.newGroup, + value: SettingsAction.newSpace, child: Row( children: [ - const Icon(Icons.group_add_outlined), + const Icon(Icons.workspaces_outlined), const SizedBox(width: 18), // #Pangea - Expanded(child: Text(L10n.of(context)!.createGroup)), - // Text(L10n.of(context)!.createGroup), + Text(L10n.of(context)!.createNewSpace), + // Text(L10n.of(context)!.createNewSpace), // Pangea# ], ), ), + // #Pangea PopupMenuItem( - value: SettingsAction.newSpace, + value: SettingsAction.learning, child: Row( children: [ - const Icon(Icons.workspaces_outlined), + const Icon(Icons.psychology_outlined), const SizedBox(width: 18), - // #Pangea - Text(L10n.of(context)!.createNewSpace), - // Text(L10n.of(context)!.createNewSpace), - // Pangea# + Expanded(child: Text(L10n.of(context)!.learningSettings)), ], ), ), - // #Pangea // PopupMenuItem( // value: SettingsAction.setStatus, // child: Row( @@ -306,9 +303,11 @@ class ClientChooserButton extends StatelessWidget { if (consent != OkCancelResult.ok) return; context.go('/rooms/settings/addaccount'); break; - case SettingsAction.newGroup: - context.go('/rooms/newgroup'); - break; + // #Pangea + // case SettingsAction.newGroup: + // context.go('/rooms/newgroup'); + // break; + // Pangea# case SettingsAction.newSpace: controller.createNewSpace(); break; @@ -416,7 +415,9 @@ class ClientChooserButton extends StatelessWidget { enum SettingsAction { addAccount, - newGroup, + // #Pangea + // newGroup, + // Pangea# newSpace, // #Pangea // setStatus, diff --git a/lib/pages/new_group/new_group_view.dart b/lib/pages/new_group/new_group_view.dart index addf7b5f7..7ea7f9de5 100644 --- a/lib/pages/new_group/new_group_view.dart +++ b/lib/pages/new_group/new_group_view.dart @@ -24,7 +24,10 @@ class NewGroupView extends StatelessWidget { onPressed: controller.loading ? null : Navigator.of(context).pop, ), ), - title: Text(L10n.of(context)!.createGroup), + // #Pangea + // title: Text(L10n.of(context)!.createGroup), + title: Text(L10n.of(context)!.newChat), + // Pangea# ), body: MaxWidthBody( child: Column( From d570b772bc4bfae775a91f211f2347b68cc987f3 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 23 Oct 2024 12:47:46 -0400 Subject: [PATCH 21/50] switch from 'my learning settings' -> 'learning settings' --- assets/l10n/intl_en.arb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 87e134d03..5c994c841 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2563,7 +2563,7 @@ "type": "text", "placeholders": {} }, - "interactiveTranslatorAllowedDesc": "Students can choose whether to use translation assistance in space group chats in Main Menu > My Learning Settings.", + "interactiveTranslatorAllowedDesc": "Students can choose whether to use translation assistance in space group chats in Main Menu > Learning Settings.", "@interactiveTranslatorAllowedDesc": { "type": "text", "placeholders": {} @@ -3030,9 +3030,9 @@ "errorDisableLanguageAssistanceClassDesc": "Translation assistance and grammar assistance are turned off for the space that this chat is in.", "itIsDisabled": "Interactive Translation is disabled", "igcIsDisabled": "Interactive Grammar Checking is disabled", - "goToLearningSettings": "Go to My Learning Settings", + "goToLearningSettings": "Go to Learning Settings", "error405Title": "Languages not set", - "error405Desc": "Please set your languages in Main Menu > My Learning Settings.", + "error405Desc": "Please set your languages in Main Menu > Learning Settings.", "loginOrSignup": "Sign in with", "@loginOrSignup": { "type": "text", From 7b2defab02c71437333ea96e1b72612bfa95d0cf Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 23 Oct 2024 14:00:16 -0400 Subject: [PATCH 22/50] adjust oerlay offset on overflow --- lib/pages/chat_list/space_view.dart | 4 +-- lib/pangea/utils/overlay.dart | 52 ++++++++++++++++++++--------- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/lib/pages/chat_list/space_view.dart b/lib/pages/chat_list/space_view.dart index a45d28f43..f0f2eda05 100644 --- a/lib/pages/chat_list/space_view.dart +++ b/lib/pages/chat_list/space_view.dart @@ -317,14 +317,14 @@ class _SpaceViewState extends State { key: AddRoomType.subspace, // #Pangea // label: L10n.of(context)!.createNewSpace, - label: L10n.of(context)!.newChat, + label: L10n.of(context)!.newSpace, // Pangea# ), AlertDialogAction( key: AddRoomType.chat, // #Pangea // label: L10n.of(context)!.createGroup, - label: L10n.of(context)!.createChat, + label: L10n.of(context)!.newChat, // Pangea# ), ], diff --git a/lib/pangea/utils/overlay.dart b/lib/pangea/utils/overlay.dart index 5bf8a5823..ef7a16b80 100644 --- a/lib/pangea/utils/overlay.dart +++ b/lib/pangea/utils/overlay.dart @@ -20,17 +20,14 @@ class OverlayUtil { required BuildContext context, required Widget child, required String transformTargetId, - double? width, - double? height, backDropToDismiss = true, blurBackground = false, Color? borderColor, Color? backgroundColor, - Alignment? targetAnchor, - Alignment? followerAnchor, bool closePrevOverlay = true, Function? onDismiss, OverlayPositionEnum position = OverlayPositionEnum.transform, + Offset? offset, }) { try { if (closePrevOverlay) { @@ -54,18 +51,16 @@ class OverlayUtil { right: (position == OverlayPositionEnum.centered) ? 0 : null, left: (position == OverlayPositionEnum.centered) ? 0 : null, bottom: (position == OverlayPositionEnum.centered) ? 0 : null, - width: width, - height: height, child: (position != OverlayPositionEnum.transform) ? child : CompositedTransformFollower( - targetAnchor: targetAnchor ?? Alignment.topCenter, - followerAnchor: - followerAnchor ?? Alignment.bottomCenter, + targetAnchor: Alignment.topCenter, + followerAnchor: Alignment.bottomCenter, link: MatrixState.pAnyState .layerLinkAndKey(transformTargetId) .link, showWhenUnlinked: false, + offset: offset ?? Offset.zero, child: child, ), ), @@ -100,6 +95,32 @@ class OverlayUtil { return; } + Offset offset = Offset.zero; + final RenderBox? targetRenderBox = + layerLinkAndKey.key.currentContext!.findRenderObject() as RenderBox?; + if (targetRenderBox != null && targetRenderBox.hasSize) { + final Offset transformTargetOffset = + (targetRenderBox).localToGlobal(Offset.zero); + final Size transformTargetSize = targetRenderBox.size; + final horizontalMidpoint = + transformTargetOffset.dx + (transformTargetSize.width / 2); + + final halfMaxWidth = maxWidth / 2; + final hasLeftOverflow = (horizontalMidpoint - halfMaxWidth) < 0; + final hasRightOverflow = (horizontalMidpoint + halfMaxWidth) > + MediaQuery.of(context).size.width; + double xOffset = 0; + + MediaQuery.of(context).size.width - (horizontalMidpoint + halfMaxWidth); + if (hasLeftOverflow) { + xOffset = (transformTargetOffset.dx - halfMaxWidth) * -1; + } else if (hasRightOverflow) { + xOffset = MediaQuery.of(context).size.width - + (horizontalMidpoint + halfMaxWidth); + } + offset = Offset(xOffset, 0); + } + final Widget child = Material( borderOnForeground: false, color: Colors.transparent, @@ -119,6 +140,7 @@ class OverlayUtil { backDropToDismiss: backDropToDismiss, borderColor: borderColor, closePrevOverlay: closePrevOverlay, + offset: offset, ); } catch (err, stack) { debugger(when: kDebugMode); @@ -138,12 +160,12 @@ class OverlayUtil { // final OverlayConstraints constraints = // ChatViewConstraints(transformTargetContext); - // final RenderObject? targetRenderBox = - // transformTargetContext.findRenderObject(); - // if (targetRenderBox == null) return Offset.zero; - // final Offset transformTargetOffset = - // (targetRenderBox as RenderBox).localToGlobal(Offset.zero); - // final Size transformTargetSize = targetRenderBox.size; + // final RenderObject? targetRenderBox = + // transformTargetContext.findRenderObject(); + // if (targetRenderBox == null) return Offset.zero; + // final Offset transformTargetOffset = + // (targetRenderBox as RenderBox).localToGlobal(Offset.zero); + // final Size transformTargetSize = targetRenderBox.size; // // ideally horizontally centered on target // double dx = transformTargetSize.width / 2 - cardSize.width / 2; From 0fa4202f7dffada5f84a7fd9431f96505c403e5e Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 23 Oct 2024 14:23:11 -0400 Subject: [PATCH 23/50] always show learning settings in popup --- lib/config/routes.dart | 10 ------ .../chat_list/client_chooser_button.dart | 6 +++- lib/pages/settings/settings_view.dart | 5 --- .../language_permissions_warning_buttons.dart | 9 +++-- .../settings_learning/settings_learning.dart | 6 +--- .../settings_learning_view.dart | 33 +++++++++++++++---- .../find_conversation_partner_dialog.dart | 8 ----- lib/widgets/chat_settings_popup_menu.dart | 23 +------------ 8 files changed, 40 insertions(+), 60 deletions(-) diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 515b25fd9..86a0ca75e 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -29,7 +29,6 @@ import 'package:fluffychat/pages/settings_style/settings_style.dart'; import 'package:fluffychat/pangea/guard/p_vguard.dart'; import 'package:fluffychat/pangea/pages/find_partner/find_partner.dart'; import 'package:fluffychat/pangea/pages/p_user_age/p_user_age.dart'; -import 'package:fluffychat/pangea/pages/settings_learning/settings_learning.dart'; import 'package:fluffychat/pangea/pages/settings_subscription/settings_subscription.dart'; import 'package:fluffychat/pangea/pages/sign_up/signup.dart'; import 'package:fluffychat/pangea/widgets/class/join_with_link.dart'; @@ -406,15 +405,6 @@ abstract class AppRoutes { ], ), // #Pangea - GoRoute( - path: 'learning', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const SettingsLearning(), - ), - redirect: loggedOutRedirect, - ), GoRoute( path: 'subscription', pageBuilder: (context, state) => defaultPageBuilder( diff --git a/lib/pages/chat_list/client_chooser_button.dart b/lib/pages/chat_list/client_chooser_button.dart index 6073034bd..34fda564b 100644 --- a/lib/pages/chat_list/client_chooser_button.dart +++ b/lib/pages/chat_list/client_chooser_button.dart @@ -1,4 +1,5 @@ import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:fluffychat/pangea/pages/settings_learning/settings_learning.dart'; import 'package:fluffychat/pangea/utils/logout.dart'; import 'package:fluffychat/pangea/utils/space_code.dart'; import 'package:fluffychat/widgets/avatar.dart'; @@ -327,7 +328,10 @@ class ClientChooserButton extends StatelessWidget { // controller.setStatus(); // break; case SettingsAction.learning: - context.go('/rooms/settings/learning'); + showDialog( + context: context, + builder: (c) => const SettingsLearning(), + ); break; case SettingsAction.joinWithClassCode: SpaceCodeUtil.joinWithSpaceCodeDialog( diff --git a/lib/pages/settings/settings_view.dart b/lib/pages/settings/settings_view.dart index d63776272..cf89cd326 100644 --- a/lib/pages/settings/settings_view.dart +++ b/lib/pages/settings/settings_view.dart @@ -162,11 +162,6 @@ class SettingsView extends StatelessWidget { title: Text(L10n.of(context)!.subscriptionManagement), onTap: () => context.go('/rooms/settings/subscription'), ), - ListTile( - leading: const Icon(Icons.psychology_outlined), - title: Text(L10n.of(context)!.learningSettings), - onTap: () => context.go('/rooms/settings/learning'), - ), // Pangea# ListTile( leading: const Icon(Icons.shield_outlined), diff --git a/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart b/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart index fcf3b57be..f595e1316 100644 --- a/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart +++ b/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart @@ -3,12 +3,12 @@ import 'dart:developer'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart'; import 'package:fluffychat/pangea/models/space_model.dart'; +import 'package:fluffychat/pangea/pages/settings_learning/settings_learning.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; import '../../../widgets/matrix.dart'; @@ -52,7 +52,12 @@ class LanguagePermissionsButtons extends StatelessWidget { text: copy.description, style: const TextStyle(color: AppConfig.primaryColor), recognizer: TapGestureRecognizer() - ..onTap = () => context.go('/rooms/settings/learning'), + ..onTap = () { + showDialog( + context: context, + builder: (c) => const SettingsLearning(), + ); + }, ), ], ), diff --git a/lib/pangea/pages/settings_learning/settings_learning.dart b/lib/pangea/pages/settings_learning/settings_learning.dart index 368d0a7c3..3e0a11e4c 100644 --- a/lib/pangea/pages/settings_learning/settings_learning.dart +++ b/lib/pangea/pages/settings_learning/settings_learning.dart @@ -8,11 +8,7 @@ import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; class SettingsLearning extends StatefulWidget { - final bool isPopup; - const SettingsLearning({ - this.isPopup = false, - super.key, - }); + const SettingsLearning({super.key}); @override SettingsLearningController createState() => SettingsLearningController(); diff --git a/lib/pangea/pages/settings_learning/settings_learning_view.dart b/lib/pangea/pages/settings_learning/settings_learning_view.dart index 835dbb774..943d81ca2 100644 --- a/lib/pangea/pages/settings_learning/settings_learning_view.dart +++ b/lib/pangea/pages/settings_learning/settings_learning_view.dart @@ -4,6 +4,7 @@ import 'package:fluffychat/pangea/widgets/user_settings/country_picker_tile.dart import 'package:fluffychat/pangea/widgets/user_settings/language_tile.dart'; import 'package:fluffychat/pangea/widgets/user_settings/p_settings_switch_list_tile.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -13,18 +14,16 @@ class SettingsLearningView extends StatelessWidget { @override Widget build(BuildContext context) { - return Scaffold( + final dialogContent = Scaffold( appBar: AppBar( centerTitle: true, title: Text( L10n.of(context)!.learningSettings, ), - leading: controller.widget.isPopup - ? IconButton( - icon: const Icon(Icons.close), - onPressed: Navigator.of(context).pop, - ) - : null, + leading: IconButton( + icon: const Icon(Icons.close), + onPressed: Navigator.of(context).pop, + ), ), body: ListTileTheme( iconColor: Theme.of(context).textTheme.bodyLarge!.color, @@ -79,5 +78,25 @@ class SettingsLearningView extends StatelessWidget { ), ), ); + + return kIsWeb + ? Dialog( + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 600, + maxHeight: 600, + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(20.0), + child: dialogContent, + ), + ), + ) + : Dialog.fullscreen( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 600), + child: dialogContent, + ), + ); } } diff --git a/lib/pangea/utils/find_conversation_partner_dialog.dart b/lib/pangea/utils/find_conversation_partner_dialog.dart index 035f091d8..149e20c74 100644 --- a/lib/pangea/utils/find_conversation_partner_dialog.dart +++ b/lib/pangea/utils/find_conversation_partner_dialog.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; - import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:go_router/go_router.dart'; @@ -27,13 +26,6 @@ void findConversationPartnerDialog( onPressed: Navigator.of(context).pop, child: Text(L10n.of(context)!.cancel), ), - TextButton( - onPressed: () { - context.go('/rooms/settings/learning'); - Navigator.of(context).pop(); - }, - child: Text(L10n.of(context)!.accountSettings), - ), ], ), ); diff --git a/lib/widgets/chat_settings_popup_menu.dart b/lib/widgets/chat_settings_popup_menu.dart index b4271213f..6e216a38a 100644 --- a/lib/widgets/chat_settings_popup_menu.dart +++ b/lib/widgets/chat_settings_popup_menu.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:fluffychat/pangea/pages/settings_learning/settings_learning.dart'; import 'package:fluffychat/pangea/utils/download_chat.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'; @@ -154,27 +153,7 @@ class ChatSettingsPopupMenuState extends State { case ChatPopupMenuActions.learningSettings: showDialog( context: context, - builder: (c) { - return kIsWeb - ? Dialog( - child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 600, - maxHeight: 600, - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(20.0), - child: const SettingsLearning(isPopup: true), - ), - ), - ) - : Dialog.fullscreen( - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 600), - child: const SettingsLearning(isPopup: true), - ), - ); - }, + builder: (c) => const SettingsLearning(), ); break; // Pangea# From e30267bf8e72bfde8976680414557702905f4102 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Wed, 23 Oct 2024 14:34:42 -0400 Subject: [PATCH 24/50] turn off analytics setting in multiple choice --- .../practice_activity_event.dart | 9 ++---- .../widgets/chat/missing_voice_button.dart | 6 ++-- .../multiple_choice_activity.dart | 29 +++++++++---------- .../practice_activity_card.dart | 5 ++-- .../word_focus_listening_activity.dart | 2 +- 5 files changed, 22 insertions(+), 29 deletions(-) diff --git a/lib/pangea/matrix_event_wrappers/practice_activity_event.dart b/lib/pangea/matrix_event_wrappers/practice_activity_event.dart index 5ab1cce31..3de2e2ffc 100644 --- a/lib/pangea/matrix_event_wrappers/practice_activity_event.dart +++ b/lib/pangea/matrix_event_wrappers/practice_activity_event.dart @@ -36,13 +36,8 @@ class PracticeActivityEvent { } PracticeActivityModel get practiceActivity { - try { - _content ??= event.getPangeaContent(); - return _content!; - } catch (e, s) { - final contentMap = event.content; - rethrow; - } + _content ??= event.getPangeaContent(); + return _content!; } /// All completion records assosiated with this activity diff --git a/lib/pangea/widgets/chat/missing_voice_button.dart b/lib/pangea/widgets/chat/missing_voice_button.dart index 3baa8422e..b1f12c626 100644 --- a/lib/pangea/widgets/chat/missing_voice_button.dart +++ b/lib/pangea/widgets/chat/missing_voice_button.dart @@ -51,9 +51,9 @@ class MissingVoiceButton extends StatelessWidget { onPressed: () => launchTTSSettings, // commenting out as suspecting this is causing an issue // #freeze-activity - // style: const ButtonStyle( - // tapTargetSize: MaterialTapTargetSize.shrinkWrap, - // ), + style: const ButtonStyle( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), child: Text(L10n.of(context)!.openVoiceSettings), ), ], diff --git a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart index 8a34dfc6d..16b8e44a6 100644 --- a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart +++ b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart @@ -2,17 +2,15 @@ import 'dart:developer'; import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/choreographer/widgets/choice_array.dart'; -import 'package:fluffychat/pangea/controllers/my_analytics_controller.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_record_model.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart'; -import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; /// The multiple choice activity view class MultipleChoiceActivity extends StatefulWidget { - final MessagePracticeActivityCardState practiceCardController; + final PracticeActivityCardState practiceCardController; final PracticeActivityModel currentActivity; const MultipleChoiceActivity({ @@ -65,18 +63,19 @@ class MultipleChoiceActivityState extends State { return; } - MatrixState.pangeaController.myAnalytics.setState( - AnalyticsStream( - // note - this maybe should be the activity event id - eventId: - widget.practiceCardController.widget.pangeaMessageEvent.eventId, - roomId: widget.practiceCardController.widget.pangeaMessageEvent.room.id, - constructs: currentRecordModel!.latestResponse!.toUses( - widget.practiceCardController.currentActivity!, - widget.practiceCardController.metadata, - ), - ), - ); + // #freeze-activity + // MatrixState.pangeaController.myAnalytics.setState( + // AnalyticsStream( + // // note - this maybe should be the activity event id + // eventId: + // widget.practiceCardController.widget.pangeaMessageEvent.eventId, + // roomId: widget.practiceCardController.widget.pangeaMessageEvent.room.id, + // constructs: currentRecordModel!.latestResponse!.toUses( + // widget.practiceCardController.currentActivity!, + // widget.practiceCardController.metadata, + // ), + // ), + // ); // If the selected choice is correct, send the record and get the next activity if (widget.currentActivity.content.isCorrect(value, index)) { diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index 62dadc78b..7a2af7fc9 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -36,11 +36,10 @@ class PracticeActivityCard extends StatefulWidget { }); @override - MessagePracticeActivityCardState createState() => - MessagePracticeActivityCardState(); + PracticeActivityCardState createState() => PracticeActivityCardState(); } -class MessagePracticeActivityCardState extends State { +class PracticeActivityCardState extends State { PracticeActivityModel? currentActivity; PracticeActivityRecordModel? currentCompletionRecord; bool fetchingActivity = false; diff --git a/lib/pangea/widgets/practice_activity/word_focus_listening_activity.dart b/lib/pangea/widgets/practice_activity/word_focus_listening_activity.dart index 720f784ba..8e22aced8 100644 --- a/lib/pangea/widgets/practice_activity/word_focus_listening_activity.dart +++ b/lib/pangea/widgets/practice_activity/word_focus_listening_activity.dart @@ -13,7 +13,7 @@ import 'package:flutter/material.dart'; class WordFocusListeningActivity extends StatefulWidget { final PracticeActivityModel activity; - final MessagePracticeActivityCardState practiceCardController; + final PracticeActivityCardState practiceCardController; const WordFocusListeningActivity({ super.key, From 4bfc70300ae1e1893e90fc810e2f9c407424e474 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Wed, 23 Oct 2024 14:39:03 -0400 Subject: [PATCH 25/50] bumping version number --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 5b2f1b287..d4327d77b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ description: Learn a language while texting your friends. # Pangea# publish_to: none # On version bump also increase the build number for F-Droid -version: 1.21.5+3542 +version: 1.21.5+3543 environment: sdk: ">=3.0.0 <4.0.0" From 6265d6636d3d50071b1062ae470222b1b49ef882 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Wed, 23 Oct 2024 15:13:08 -0400 Subject: [PATCH 26/50] bringing word audio card --- .../practice_activity/multiple_choice_activity.dart | 8 +++++--- .../widgets/practice_activity/word_audio_button.dart | 3 +-- pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart index 16b8e44a6..3e8a9f126 100644 --- a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart +++ b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart @@ -2,9 +2,11 @@ import 'dart:developer'; import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/choreographer/widgets/choice_array.dart'; +import 'package:fluffychat/pangea/enum/activity_type_enum.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_record_model.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart'; +import 'package:fluffychat/pangea/widgets/practice_activity/word_audio_button.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -106,9 +108,9 @@ class MultipleChoiceActivityState extends State { ), const SizedBox(height: 8), // #freeze-activity - // if (practiceActivity.activityType == - // ActivityTypeEnum.wordFocusListening) - // WordAudioButton(text: practiceActivity.content.answer), + if (practiceActivity.activityType == + ActivityTypeEnum.wordFocusListening) + WordAudioButton(text: practiceActivity.content.answer), ChoicesArray( isLoading: false, uniqueKeyForLayerLink: (index) => "multiple_choice_$index", diff --git a/lib/pangea/widgets/practice_activity/word_audio_button.dart b/lib/pangea/widgets/practice_activity/word_audio_button.dart index 78c0efb7d..24835fcb2 100644 --- a/lib/pangea/widgets/practice_activity/word_audio_button.dart +++ b/lib/pangea/widgets/practice_activity/word_audio_button.dart @@ -70,8 +70,7 @@ class WordAudioButtonState extends State { }, // Disable button if language isn't supported ), // #freeze-activity - //commenting out to see if it's causing an issue - // ttsController.missingVoiceButton, + ttsController.missingVoiceButton, ], ); } diff --git a/pubspec.yaml b/pubspec.yaml index d4327d77b..7708fc277 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ description: Learn a language while texting your friends. # Pangea# publish_to: none # On version bump also increase the build number for F-Droid -version: 1.21.5+3543 +version: 1.21.5+3544 environment: sdk: ">=3.0.0 <4.0.0" From 6d7ef49d91dba637a8408bb9af0e5d991dfa1b74 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Wed, 23 Oct 2024 15:35:50 -0400 Subject: [PATCH 27/50] bring back set analytics in updateChoice --- .../practice_activity_model.dart | 8 +++--- .../multiple_choice_activity.dart | 26 ++++++++++--------- pubspec.yaml | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/pangea/models/practice_activities.dart/practice_activity_model.dart b/lib/pangea/models/practice_activities.dart/practice_activity_model.dart index 644031e47..65f02c4f7 100644 --- a/lib/pangea/models/practice_activities.dart/practice_activity_model.dart +++ b/lib/pangea/models/practice_activities.dart/practice_activity_model.dart @@ -187,10 +187,10 @@ class PracticeActivityModel { // moving from multiple_choice to content as the key // this is to make the model more generic // here for backward compatibility - final Map? content = + final Map? contentMap = (json['content'] ?? json["multiple_choice"]) as Map?; - if (content == null) { + if (contentMap == null) { Sentry.addBreadcrumb( Breadcrumb(data: {"json": json}), ); @@ -211,9 +211,7 @@ class PracticeActivityModel { e.string == json['activity_type'] as String || e.string.split('.').last == json['activity_type'] as String, ), - content: ActivityContent.fromJson( - content, - ), + content: ActivityContent.fromJson(contentMap), ); } diff --git a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart index 3e8a9f126..ee892837b 100644 --- a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart +++ b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart @@ -2,11 +2,13 @@ import 'dart:developer'; import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/choreographer/widgets/choice_array.dart'; +import 'package:fluffychat/pangea/controllers/my_analytics_controller.dart'; import 'package:fluffychat/pangea/enum/activity_type_enum.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_record_model.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/word_audio_button.dart'; +import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -66,18 +68,18 @@ class MultipleChoiceActivityState extends State { } // #freeze-activity - // MatrixState.pangeaController.myAnalytics.setState( - // AnalyticsStream( - // // note - this maybe should be the activity event id - // eventId: - // widget.practiceCardController.widget.pangeaMessageEvent.eventId, - // roomId: widget.practiceCardController.widget.pangeaMessageEvent.room.id, - // constructs: currentRecordModel!.latestResponse!.toUses( - // widget.practiceCardController.currentActivity!, - // widget.practiceCardController.metadata, - // ), - // ), - // ); + MatrixState.pangeaController.myAnalytics.setState( + AnalyticsStream( + // note - this maybe should be the activity event id + eventId: + widget.practiceCardController.widget.pangeaMessageEvent.eventId, + roomId: widget.practiceCardController.widget.pangeaMessageEvent.room.id, + constructs: currentRecordModel!.latestResponse!.toUses( + widget.practiceCardController.currentActivity!, + widget.practiceCardController.metadata, + ), + ), + ); // If the selected choice is correct, send the record and get the next activity if (widget.currentActivity.content.isCorrect(value, index)) { diff --git a/pubspec.yaml b/pubspec.yaml index 7708fc277..43430c50c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ description: Learn a language while texting your friends. # Pangea# publish_to: none # On version bump also increase the build number for F-Droid -version: 1.21.5+3544 +version: 1.21.5+3545 environment: sdk: ">=3.0.0 <4.0.0" From 31b77c6d99093620b5e294d97acb8b5452d5154b Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Wed, 23 Oct 2024 16:36:24 -0400 Subject: [PATCH 28/50] some name cleanup, error handling and dont open overlay if click outside text --- .../controllers/choreographer.dart | 10 +++---- .../widgets/start_igc_button.dart | 9 ++++-- .../controllers/subscription_controller.dart | 13 +++++---- .../widgets/igc/pangea_text_controller.dart | 21 ++++++++++---- lib/utils/error_reporter.dart | 28 +++++++++++-------- 5 files changed, 51 insertions(+), 30 deletions(-) diff --git a/lib/pangea/choreographer/controllers/choreographer.dart b/lib/pangea/choreographer/controllers/choreographer.dart index cc016b1ca..df254d49c 100644 --- a/lib/pangea/choreographer/controllers/choreographer.dart +++ b/lib/pangea/choreographer/controllers/choreographer.dart @@ -70,8 +70,8 @@ class Choreographer { void send(BuildContext context) { if (isFetching) return; - if (pangeaController.subscriptionController.canSendStatus == - CanSendStatus.showPaywall) { + if (pangeaController.subscriptionController.subscriptionStatus == + SubscriptionStatus.showPaywall) { OverlayUtil.showPositionedCard( context: context, cardToShow: PaywallCard( @@ -245,10 +245,10 @@ class Choreographer { }) async { try { if (errorService.isError) return; - final CanSendStatus canSendStatus = - pangeaController.subscriptionController.canSendStatus; + final SubscriptionStatus canSendStatus = + pangeaController.subscriptionController.subscriptionStatus; - if (canSendStatus != CanSendStatus.subscribed || + if (canSendStatus != SubscriptionStatus.subscribed || (!igcEnabled && !itEnabled) || (!isAutoIGCEnabled && !manual && choreoMode != ChoreoMode.it)) { return; diff --git a/lib/pangea/choreographer/widgets/start_igc_button.dart b/lib/pangea/choreographer/widgets/start_igc_button.dart index 39d35a5a9..f9782e763 100644 --- a/lib/pangea/choreographer/widgets/start_igc_button.dart +++ b/lib/pangea/choreographer/widgets/start_igc_button.dart @@ -63,10 +63,13 @@ class StartIGCButtonState extends State bool get itEnabled => widget.controller.choreographer.itEnabled; bool get igcEnabled => widget.controller.choreographer.igcEnabled; - CanSendStatus get canSendStatus => - widget.controller.pangeaController.subscriptionController.canSendStatus; + + SubscriptionStatus get subscriptionStatus => widget + .controller.pangeaController.subscriptionController.subscriptionStatus; + bool get grammarCorrectionEnabled => - (itEnabled || igcEnabled) && canSendStatus == CanSendStatus.subscribed; + (itEnabled || igcEnabled) && + subscriptionStatus == SubscriptionStatus.subscribed; @override Widget build(BuildContext context) { diff --git a/lib/pangea/controllers/subscription_controller.dart b/lib/pangea/controllers/subscription_controller.dart index 73cf77dff..94c396cab 100644 --- a/lib/pangea/controllers/subscription_controller.dart +++ b/lib/pangea/controllers/subscription_controller.dart @@ -23,7 +23,7 @@ import 'package:http/http.dart'; import 'package:purchases_flutter/purchases_flutter.dart'; import 'package:url_launcher/url_launcher_string.dart'; -enum CanSendStatus { +enum SubscriptionStatus { subscribed, dimissedPaywall, showPaywall, @@ -227,11 +227,13 @@ class SubscriptionController extends BaseController { setState(null); } - CanSendStatus get canSendStatus => isSubscribed - ? CanSendStatus.subscribed + /// if the user is subscribed, returns subscribed + /// if the user has dismissed the paywall, returns dismissed + SubscriptionStatus get subscriptionStatus => isSubscribed + ? SubscriptionStatus.subscribed : _shouldShowPaywall - ? CanSendStatus.showPaywall - : CanSendStatus.dimissedPaywall; + ? SubscriptionStatus.showPaywall + : SubscriptionStatus.dimissedPaywall; DateTime? get _lastDismissedPaywall { final lastDismissed = _pangeaController.pStoreService.read( @@ -249,6 +251,7 @@ class SubscriptionController extends BaseController { return backoff; } + /// whether or not the paywall should be shown bool get _shouldShowPaywall { return initialized.isCompleted && !isSubscribed && diff --git a/lib/pangea/widgets/igc/pangea_text_controller.dart b/lib/pangea/widgets/igc/pangea_text_controller.dart index 378c33ad7..b7bbc1af8 100644 --- a/lib/pangea/widgets/igc/pangea_text_controller.dart +++ b/lib/pangea/widgets/igc/pangea_text_controller.dart @@ -47,9 +47,11 @@ class PangeaTextController extends TextEditingController { debugger(when: kDebugMode); return; } - final CanSendStatus canSendStatus = - choreographer.pangeaController.subscriptionController.canSendStatus; - if (canSendStatus == CanSendStatus.showPaywall && + + // show the paywall if appropriate + if (choreographer + .pangeaController.subscriptionController.subscriptionStatus == + SubscriptionStatus.showPaywall && !choreographer.isFetching && text.isNotEmpty) { OverlayUtil.showPositionedCard( @@ -63,11 +65,18 @@ class PangeaTextController extends TextEditingController { ); } + // if there is no igc text data, then don't do anything if (choreographer.igc.igcTextData == null) return; // debugPrint( // "onInputTap matches are ${choreographer.igc.igcTextData?.matches.map((e) => e.match.rule.id).toList().toString()}"); + // if user is just trying to get their cursor into the text input field to add soemthing, + // then don't interrupt them + if (selection.baseOffset >= text.length) { + return; + } + final int tokenIndex = choreographer.igc.igcTextData!.tokenIndexByOffset( selection.baseOffset, ); @@ -147,9 +156,9 @@ class PangeaTextController extends TextEditingController { // debugPrint("composing after ${value.composing.textAfter(value.text)}"); // } - final CanSendStatus canSendStatus = - choreographer.pangeaController.subscriptionController.canSendStatus; - if (canSendStatus == CanSendStatus.showPaywall && + final SubscriptionStatus canSendStatus = choreographer + .pangeaController.subscriptionController.subscriptionStatus; + if (canSendStatus == SubscriptionStatus.showPaywall && !choreographer.isFetching && text.isNotEmpty) { return TextSpan( diff --git a/lib/utils/error_reporter.dart b/lib/utils/error_reporter.dart index 650277bf1..2e7d41122 100644 --- a/lib/utils/error_reporter.dart +++ b/lib/utils/error_reporter.dart @@ -1,3 +1,4 @@ +import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; @@ -11,18 +12,17 @@ class ErrorReporter { void onErrorCallback(Object error, [StackTrace? stackTrace]) async { Logs().e(message ?? 'Error caught', error, stackTrace); // #Pangea - // Attempt to retrieve the L10n instance using the current context - final L10n? l10n = L10n.of(context); - - // Check if the L10n instance is null - if (l10n == null) { - // Log an error message saying that the localization object is null - Logs().e('Localization object is null, cannot show error message.'); - // Exits early to prevent further execution - return; - } - try { + // Attempt to retrieve the L10n instance using the current context + final L10n? l10n = L10n.of(context); + + // Check if the L10n instance is null + if (l10n == null) { + // Log an error message saying that the localization object is null + Logs().e('Localization object is null, cannot show error message.'); + // Exits early to prevent further execution + return; + } ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( @@ -32,6 +32,12 @@ class ErrorReporter { ); } catch (err) { debugPrint("Failed to show error snackbar."); + } finally { + ErrorHandler.logError( + e: error, + s: stackTrace, + m: message ?? 'Error caught', + ); } } // final text = '$error\n${stackTrace ?? ''}'; From 0c7042b51cee9c9c1d46ef9e92a3463685396acf Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 23 Oct 2024 16:36:40 -0400 Subject: [PATCH 29/50] store num completed activities in memory instead of local cache --- .../practice_activity_record_controller.dart | 105 ++++++++---------- .../pangea_message_event.dart | 3 +- pubspec.yaml | 2 +- 3 files changed, 51 insertions(+), 59 deletions(-) diff --git a/lib/pangea/controllers/practice_activity_record_controller.dart b/lib/pangea/controllers/practice_activity_record_controller.dart index 45b036611..8ee52e696 100644 --- a/lib/pangea/controllers/practice_activity_record_controller.dart +++ b/lib/pangea/controllers/practice_activity_record_controller.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:collection'; import 'dart:developer'; -import 'package:fluffychat/pangea/constants/local.key.dart'; import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; @@ -26,66 +25,60 @@ class PracticeActivityRecordController { static const int maxStoredEvents = 100; static final Map _cache = {}; late final PangeaController _pangeaController; - Timer? _cacheClearTimer; - PracticeActivityRecordController(this._pangeaController) { - _initializeCacheClearing(); - } + PracticeActivityRecordController(this._pangeaController); - LinkedHashMap get completedActivities { - try { - final dynamic locallySaved = _pangeaController.pStoreService.read( - PLocalKey.completedActivities, - ); - if (locallySaved == null) return LinkedHashMap(); - try { - final LinkedHashMap cache = - LinkedHashMap.from(locallySaved); - return cache; - } catch (err) { - _pangeaController.pStoreService.delete( - PLocalKey.completedActivities, - ); - return LinkedHashMap(); - } - } catch (exception, stackTrace) { - ErrorHandler.logError( - e: PangeaWarningError( - "Failed to get completed activities from cache: $exception", - ), - s: stackTrace, - m: 'Failed to get completed activities from cache', - ); - return LinkedHashMap(); - } + int getCompletedActivityCount(String messageID) { + return _completedActivities[messageID] ?? 0; } - Future completeActivity(String messageID) async { - final LinkedHashMap currentCache = completedActivities; - final numCompleted = currentCache[messageID] ?? 0; - currentCache[messageID] = numCompleted + 1; + final LinkedHashMap _completedActivities = + LinkedHashMap(); + + // LinkedHashMap get _completedActivities { + // try { + // final dynamic locallySaved = _pangeaController.pStoreService.read( + // PLocalKey.completedActivities, + // ); + // if (locallySaved == null) return LinkedHashMap(); + // try { + // final LinkedHashMap cache = + // LinkedHashMap.from(locallySaved); + // return cache; + // } catch (err) { + // _pangeaController.pStoreService.delete( + // PLocalKey.completedActivities, + // ); + // return LinkedHashMap(); + // } + // } catch (exception, stackTrace) { + // ErrorHandler.logError( + // e: PangeaWarningError( + // "Failed to get completed activities from cache: $exception", + // ), + // s: stackTrace, + // m: 'Failed to get completed activities from cache', + // ); + // return LinkedHashMap(); + // } + // } - if (currentCache.length > maxStoredEvents) { - currentCache.remove(currentCache.keys.first); - } - - await _pangeaController.pStoreService.save( - PLocalKey.completedActivities, - currentCache, - ); - } - - void _initializeCacheClearing() { - const duration = Duration(minutes: 2); - _cacheClearTimer = Timer.periodic(duration, (Timer t) => _clearCache()); - } - - void _clearCache() { - _cache.clear(); - } - - void dispose() { - _cacheClearTimer?.cancel(); + Future completeActivity(String messageID) async { + final numCompleted = _completedActivities[messageID] ?? 0; + _completedActivities[messageID] = numCompleted + 1; + // final LinkedHashMap currentCache = _completedActivities; + // final numCompleted = currentCache[messageID] ?? 0; + // currentCache[messageID] = numCompleted + 1; + + // if (currentCache.length > maxStoredEvents) { + // currentCache.remove(currentCache.keys.first); + // } + + // await _pangeaController.pStoreService.save( + // PLocalKey.completedActivities, + // currentCache, + // ); + debugPrint("completed activities is now: $_completedActivities"); } /// Sends a practice activity record to the server and returns the corresponding event. diff --git a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart index 85dfb8760..39d1bd314 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart @@ -540,8 +540,7 @@ class PangeaMessageEvent { int get numberOfActivitiesCompleted { return MatrixState.pangeaController.activityRecordController - .completedActivities[eventId] ?? - 0; + .getCompletedActivityCount(eventId); } String? get l2Code => diff --git a/pubspec.yaml b/pubspec.yaml index 43430c50c..cdb13d908 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ description: Learn a language while texting your friends. # Pangea# publish_to: none # On version bump also increase the build number for F-Droid -version: 1.21.5+3545 +version: 1.21.6+3546 environment: sdk: ">=3.0.0 <4.0.0" From 9b97895a9d02e8b8471a70e893191e722aa56d0e Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 08:50:41 -0400 Subject: [PATCH 30/50] merge main into toolbar-min-dimensions --- lib/config/app_config.dart | 2 ++ .../widgets/chat/message_audio_card.dart | 2 -- .../chat/message_selection_overlay.dart | 4 +-- lib/pangea/widgets/chat/message_toolbar.dart | 3 ++ .../chat/message_translation_card.dart | 7 +++- .../chat/message_unsubscribed_card.dart | 4 +-- .../toolbar_content_loading_indicator.dart | 32 +++++++++++-------- .../practice_activity_card.dart | 18 +++++------ 8 files changed, 40 insertions(+), 32 deletions(-) diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index 5a0706fe8..b044da3ee 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -23,6 +23,8 @@ abstract class AppConfig { static const bool allowOtherHomeservers = true; static const bool enableRegistration = true; static const double toolbarMaxHeight = 300.0; + static const double toolbarMinHeight = 70.0; + static const double toolbarMinWidth = 350.0; // #Pangea // static const Color primaryColor = Color(0xFF5625BA); // static const Color primaryColorLight = Color(0xFFCCBDEA); diff --git a/lib/pangea/widgets/chat/message_audio_card.dart b/lib/pangea/widgets/chat/message_audio_card.dart index 7a003a01c..133e16b1e 100644 --- a/lib/pangea/widgets/chat/message_audio_card.dart +++ b/lib/pangea/widgets/chat/message_audio_card.dart @@ -8,7 +8,6 @@ import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dar import 'package:fluffychat/pangea/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/widgets/chat/message_selection_overlay.dart'; -import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart'; import 'package:fluffychat/pangea/widgets/chat/toolbar_content_loading_indicator.dart'; import 'package:fluffychat/pangea/widgets/chat/tts_controller.dart'; import 'package:fluffychat/pangea/widgets/igc/card_error_widget.dart'; @@ -190,7 +189,6 @@ class MessageAudioCardState extends State { children: [ Container( padding: const EdgeInsets.all(8), - constraints: const BoxConstraints(minHeight: minCardHeight), alignment: Alignment.center, child: _isLoading ? const ToolbarContentLoadingIndicator() diff --git a/lib/pangea/widgets/chat/message_selection_overlay.dart b/lib/pangea/widgets/chat/message_selection_overlay.dart index 48cdfbd47..5cf9e70b5 100644 --- a/lib/pangea/widgets/chat/message_selection_overlay.dart +++ b/lib/pangea/widgets/chat/message_selection_overlay.dart @@ -443,9 +443,7 @@ class MessageOverlayController extends State } final overlayMessage = Container( - constraints: BoxConstraints( - maxWidth: maxWidth, - ), + constraints: BoxConstraints(maxWidth: maxWidth), child: Material( type: MaterialType.transparency, child: Column( diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index bcffbdf2a..cf4192691 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -114,6 +114,9 @@ class MessageToolbar extends StatelessWidget { ), constraints: const BoxConstraints( maxHeight: AppConfig.toolbarMaxHeight, + minWidth: AppConfig.toolbarMinWidth, + minHeight: AppConfig.toolbarMinHeight, + // maxWidth is set by MessageSelectionOverlay ), child: SingleChildScrollView( child: AnimatedSize( diff --git a/lib/pangea/widgets/chat/message_translation_card.dart b/lib/pangea/widgets/chat/message_translation_card.dart index 4d8bad28d..f37238a6b 100644 --- a/lib/pangea/widgets/chat/message_translation_card.dart +++ b/lib/pangea/widgets/chat/message_translation_card.dart @@ -134,26 +134,31 @@ class MessageTranslationCardState extends State { } return Padding( - padding: const EdgeInsets.all(8), + padding: const EdgeInsets.fromLTRB(16, 20, 16, 16), child: Row( mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, children: [ _fetchingTranslation ? const ToolbarContentLoadingIndicator() : Flexible( child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, children: [ widget.selection != null ? selectionTranslation != null ? Text( selectionTranslation!, style: BotStyle.text(context), + textAlign: TextAlign.center, ) : const ToolbarContentLoadingIndicator() : repEvent != null ? Text( repEvent!.text, style: BotStyle.text(context), + textAlign: TextAlign.center, ) : const ToolbarContentLoadingIndicator(), if (notGoingToTranslate && widget.selection == null) diff --git a/lib/pangea/widgets/chat/message_unsubscribed_card.dart b/lib/pangea/widgets/chat/message_unsubscribed_card.dart index 5d91099b1..99a08456e 100644 --- a/lib/pangea/widgets/chat/message_unsubscribed_card.dart +++ b/lib/pangea/widgets/chat/message_unsubscribed_card.dart @@ -18,8 +18,8 @@ class MessageUnsubscribedCard extends StatelessWidget { final bool inTrialWindow = MatrixState.pangeaController.userController.inTrialWindow; - return Container( - padding: const EdgeInsets.all(8), + return Padding( + padding: const EdgeInsets.all(16), child: Column( children: [ Text( diff --git a/lib/pangea/widgets/chat/toolbar_content_loading_indicator.dart b/lib/pangea/widgets/chat/toolbar_content_loading_indicator.dart index f61496013..28f15c1a9 100644 --- a/lib/pangea/widgets/chat/toolbar_content_loading_indicator.dart +++ b/lib/pangea/widgets/chat/toolbar_content_loading_indicator.dart @@ -1,4 +1,3 @@ -import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart'; import 'package:flutter/material.dart'; class ToolbarContentLoadingIndicator extends StatelessWidget { @@ -8,20 +7,25 @@ class ToolbarContentLoadingIndicator extends StatelessWidget { @override Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.all(8), - constraints: const BoxConstraints(minHeight: minCardHeight), - alignment: Alignment.center, - child: Center( - child: SizedBox( - height: 14, - width: 14, - child: CircularProgressIndicator( - strokeWidth: 2.0, - color: Theme.of(context).colorScheme.primary, - ), + return Column( + // mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + height: 14, + width: 14, + child: CircularProgressIndicator( + strokeWidth: 2.0, + color: Theme.of(context).colorScheme.primary, + ), + ), + ], ), - ), + ], ); } } diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index 7a2af7fc9..13d22890f 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -13,9 +13,9 @@ import 'package:fluffychat/pangea/utils/bot_style.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/widgets/animations/gain_points.dart'; import 'package:fluffychat/pangea/widgets/chat/message_selection_overlay.dart'; +import 'package:fluffychat/pangea/widgets/chat/toolbar_content_loading_indicator.dart'; import 'package:fluffychat/pangea/widgets/content_issue_button.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/multiple_choice_activity.dart'; -import 'package:fluffychat/pangea/widgets/practice_activity/no_more_practice_card.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/target_tokens_controller.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; @@ -321,11 +321,13 @@ class PracticeActivityCardState extends State { @override Widget build(BuildContext context) { - if (!fetchingActivity && currentActivity == null) { - return GamifiedTextWidget( - userMessage: L10n.of(context)!.noActivitiesFound, - ); - } + // if (!fetchingActivity && currentActivity == null) { + // return GamifiedTextWidget( + // userMessage: L10n.of(context)!.noActivitiesFound, + // ); + // } + + return const ToolbarContentLoadingIndicator(); return Stack( alignment: Alignment.center, @@ -340,10 +342,6 @@ class PracticeActivityCardState extends State { ), // Conditionally show the darkening and progress indicator based on the loading state if (!savoringTheJoy && fetchingActivity) ...[ - // Semi-transparent overlay - Container( - color: Colors.black.withOpacity(0.5), // Darkening effect - ), // Circular progress indicator in the center const Center( child: CircularProgressIndicator(), From 59682599a5a6a3b5d3109cd44d1c10a512cae369 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 10:47:02 -0400 Subject: [PATCH 31/50] turn off choice array animation --- .../choreographer/widgets/choice_array.dart | 150 +++++++++--------- 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/lib/pangea/choreographer/widgets/choice_array.dart b/lib/pangea/choreographer/widgets/choice_array.dart index 32395099e..549665064 100644 --- a/lib/pangea/choreographer/widgets/choice_array.dart +++ b/lib/pangea/choreographer/widgets/choice_array.dart @@ -1,5 +1,4 @@ import 'dart:developer'; -import 'dart:math'; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; @@ -213,98 +212,99 @@ class ChoiceAnimationWidgetState extends State with SingleTickerProviderStateMixin { late final AnimationController _controller; late final Animation _animation; - AnimationState animationState = AnimationState.ready; + // AnimationState animationState = AnimationState.ready; @override void initState() { super.initState(); - _controller = AnimationController( - duration: const Duration(milliseconds: 300), - vsync: this, - ); + // _controller = AnimationController( + // duration: const Duration(milliseconds: 300), + // vsync: this, + // ); - _animation = widget.isGold - ? Tween(begin: 1.0, end: 1.2).animate(_controller) - : TweenSequence([ - TweenSequenceItem( - tween: Tween(begin: 0, end: -8 * pi / 180), - weight: 1.0, - ), - TweenSequenceItem( - tween: Tween(begin: -8 * pi / 180, end: 16 * pi / 180), - weight: 2.0, - ), - TweenSequenceItem( - tween: Tween(begin: 16 * pi / 180, end: 0), - weight: 1.0, - ), - ]).animate(_controller); + // _animation = widget.isGold + // ? Tween(begin: 1.0, end: 1.2).animate(_controller) + // : TweenSequence([ + // TweenSequenceItem( + // tween: Tween(begin: 0, end: -8 * pi / 180), + // weight: 1.0, + // ), + // TweenSequenceItem( + // tween: Tween(begin: -8 * pi / 180, end: 16 * pi / 180), + // weight: 2.0, + // ), + // TweenSequenceItem( + // tween: Tween(begin: 16 * pi / 180, end: 0), + // weight: 1.0, + // ), + // ]).animate(_controller); widget.enableInteraction(); - if (widget.selected && animationState == AnimationState.ready) { - widget.disableInteraction(); - _controller.forward(); - setState(() { - animationState = AnimationState.forward; - }); - } - _controller.addStatusListener((status) { - if (status == AnimationStatus.completed && - animationState == AnimationState.forward) { - _controller.reverse(); - setState(() { - animationState = AnimationState.reverse; - }); - } - if (status == AnimationStatus.dismissed && - animationState == AnimationState.reverse) { - widget.enableInteraction(); - setState(() { - animationState = AnimationState.finished; - }); - } - }); + // if (widget.selected && animationState == AnimationState.ready) { + // widget.disableInteraction(); + // _controller.forward(); + // setState(() { + // animationState = AnimationState.forward; + // }); + // } + // _controller.addStatusListener((status) { + // if (status == AnimationStatus.completed && + // animationState == AnimationState.forward) { + // _controller.reverse(); + // setState(() { + // animationState = AnimationState.reverse; + // }); + // } + // if (status == AnimationStatus.dismissed && + // animationState == AnimationState.reverse) { + // widget.enableInteraction(); + // setState(() { + // animationState = AnimationState.finished; + // }); + // } + // }); } @override void didUpdateWidget(ChoiceAnimationWidget oldWidget) { super.didUpdateWidget(oldWidget); - if (widget.selected && animationState == AnimationState.ready) { - widget.disableInteraction(); - _controller.forward(); - setState(() { - animationState = AnimationState.forward; - }); - } + // if (widget.selected && animationState == AnimationState.ready) { + // widget.disableInteraction(); + // _controller.forward(); + // setState(() { + // animationState = AnimationState.forward; + // }); + // } } @override Widget build(BuildContext context) { - return widget.isGold - ? AnimatedBuilder( - key: UniqueKey(), - animation: _animation, - builder: (context, child) { - return Transform.scale( - scale: _animation.value, - child: child, - ); - }, - child: widget.child, - ) - : AnimatedBuilder( - key: UniqueKey(), - animation: _animation, - builder: (context, child) { - return Transform.rotate( - angle: _animation.value, - child: child, - ); - }, - child: widget.child, - ); + return widget.child; + // widget.isGold + // ? AnimatedBuilder( + // key: UniqueKey(), + // animation: _animation, + // builder: (context, child) { + // return Transform.scale( + // scale: _animation.value, + // child: child, + // ); + // }, + // child: widget.child, + // ) + // : AnimatedBuilder( + // key: UniqueKey(), + // animation: _animation, + // builder: (context, child) { + // return Transform.rotate( + // angle: _animation.value, + // child: child, + // ); + // }, + // child: widget.child, + // ); } @override From 612296d341ef01c7bcd2e0e2e091a4aaa9a3570a Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 10:47:31 -0400 Subject: [PATCH 32/50] bump version number --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index cdb13d908..98b846ae7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ description: Learn a language while texting your friends. # Pangea# publish_to: none # On version bump also increase the build number for F-Droid -version: 1.21.6+3546 +version: 1.21.7+3547 environment: sdk: ">=3.0.0 <4.0.0" From f141680cc73ca672a08c94fb6bc29242a81db93b Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 11:07:05 -0400 Subject: [PATCH 33/50] comment out some references to myAnalytics stream --- lib/pangea/utils/logout.dart | 4 +-- .../multiple_choice_activity.dart | 26 +++++++++---------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/lib/pangea/utils/logout.dart b/lib/pangea/utils/logout.dart index e2bdce074..c94cb8909 100644 --- a/lib/pangea/utils/logout.dart +++ b/lib/pangea/utils/logout.dart @@ -20,8 +20,8 @@ void pLogoutAction(BuildContext context, {bool? isDestructiveAction}) async { final matrix = Matrix.of(context); // before wiping out locally cached construct data, save it to the server - await MatrixState.pangeaController.myAnalytics - .sendLocalAnalyticsToAnalyticsRoom(); + // await MatrixState.pangeaController.myAnalytics + // .sendLocalAnalyticsToAnalyticsRoom(); await showFutureLoadingDialog( context: context, diff --git a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart index ee892837b..3e8a9f126 100644 --- a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart +++ b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart @@ -2,13 +2,11 @@ import 'dart:developer'; import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/choreographer/widgets/choice_array.dart'; -import 'package:fluffychat/pangea/controllers/my_analytics_controller.dart'; import 'package:fluffychat/pangea/enum/activity_type_enum.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_record_model.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/word_audio_button.dart'; -import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -68,18 +66,18 @@ class MultipleChoiceActivityState extends State { } // #freeze-activity - MatrixState.pangeaController.myAnalytics.setState( - AnalyticsStream( - // note - this maybe should be the activity event id - eventId: - widget.practiceCardController.widget.pangeaMessageEvent.eventId, - roomId: widget.practiceCardController.widget.pangeaMessageEvent.room.id, - constructs: currentRecordModel!.latestResponse!.toUses( - widget.practiceCardController.currentActivity!, - widget.practiceCardController.metadata, - ), - ), - ); + // MatrixState.pangeaController.myAnalytics.setState( + // AnalyticsStream( + // // note - this maybe should be the activity event id + // eventId: + // widget.practiceCardController.widget.pangeaMessageEvent.eventId, + // roomId: widget.practiceCardController.widget.pangeaMessageEvent.room.id, + // constructs: currentRecordModel!.latestResponse!.toUses( + // widget.practiceCardController.currentActivity!, + // widget.practiceCardController.metadata, + // ), + // ), + // ); // If the selected choice is correct, send the record and get the next activity if (widget.currentActivity.content.isCorrect(value, index)) { From 70d598c7b2cadfbab1f334b6c388c37f3182cf4f Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 11:07:25 -0400 Subject: [PATCH 34/50] bump version --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 98b846ae7..444472501 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ description: Learn a language while texting your friends. # Pangea# publish_to: none # On version bump also increase the build number for F-Droid -version: 1.21.7+3547 +version: 1.21.8+3548 environment: sdk: ">=3.0.0 <4.0.0" From 4c51bb15f63313f424bf9e9366535b6d540a144e Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 11:22:04 -0400 Subject: [PATCH 35/50] turn back on analytics stream call in logout --- lib/pangea/utils/logout.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pangea/utils/logout.dart b/lib/pangea/utils/logout.dart index c94cb8909..e2bdce074 100644 --- a/lib/pangea/utils/logout.dart +++ b/lib/pangea/utils/logout.dart @@ -20,8 +20,8 @@ void pLogoutAction(BuildContext context, {bool? isDestructiveAction}) async { final matrix = Matrix.of(context); // before wiping out locally cached construct data, save it to the server - // await MatrixState.pangeaController.myAnalytics - // .sendLocalAnalyticsToAnalyticsRoom(); + await MatrixState.pangeaController.myAnalytics + .sendLocalAnalyticsToAnalyticsRoom(); await showFutureLoadingDialog( context: context, From 636fb150e8bac8e7b9f19b553a9432808a600fec Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 11:22:21 -0400 Subject: [PATCH 36/50] bump version --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 444472501..0a45647dc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ description: Learn a language while texting your friends. # Pangea# publish_to: none # On version bump also increase the build number for F-Droid -version: 1.21.8+3548 +version: 1.21.9+3549 environment: sdk: ">=3.0.0 <4.0.0" From 1944c19c50f1f6356a0f71db48f81051e98eaca6 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 11:28:20 -0400 Subject: [PATCH 37/50] Revert "when inviting tachers to analytics room, request all particpants to ensure teacher isn't already a member" This reverts commit bc1dfc1e0e8f017c9c4032afcd4b18112042d35b. --- .../pangea_room_extension/room_analytics_extension.dart | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart b/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart index b44c40ece..73371b080 100644 --- a/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart @@ -99,7 +99,7 @@ extension AnalyticsRoomExtension on Room { await analyticsRoom.requestParticipants(); } - final List participants = await analyticsRoom.requestParticipants(); + final List participants = analyticsRoom.getParticipants(); final List uninvitedTeachers = teachersLocal .where((teacher) => !participants.contains(teacher)) .toList(); @@ -110,12 +110,8 @@ extension AnalyticsRoomExtension on Room { (teacher) => analyticsRoom.invite(teacher.id).catchError((err, s) { ErrorHandler.logError( e: err, - m: "Failed to invite teacher to analytics room", + m: "Failed to invite teacher ${teacher.id} to analytics room ${analyticsRoom.id}", s: s, - data: { - "teacherId": teacher.id, - "analyticsRoomId": analyticsRoom.id, - }, ); }), ), From f6bd07400098c4a0fd9205d6ae41926af9c0ccb2 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 11:29:02 -0400 Subject: [PATCH 38/50] revert change to getting room participants --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 0a45647dc..dba848cf1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ description: Learn a language while texting your friends. # Pangea# publish_to: none # On version bump also increase the build number for F-Droid -version: 1.21.9+3549 +version: 1.22.0+3550 environment: sdk: ">=3.0.0 <4.0.0" From 5b232cea69df588ca3dbec732aad4378bb8d7bc4 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 11:37:24 -0400 Subject: [PATCH 39/50] turn back on setState on analytics data --- .../multiple_choice_activity.dart | 26 ++++++++++--------- pubspec.yaml | 2 +- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart index 3e8a9f126..ee892837b 100644 --- a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart +++ b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart @@ -2,11 +2,13 @@ import 'dart:developer'; import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/choreographer/widgets/choice_array.dart'; +import 'package:fluffychat/pangea/controllers/my_analytics_controller.dart'; import 'package:fluffychat/pangea/enum/activity_type_enum.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_record_model.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/word_audio_button.dart'; +import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -66,18 +68,18 @@ class MultipleChoiceActivityState extends State { } // #freeze-activity - // MatrixState.pangeaController.myAnalytics.setState( - // AnalyticsStream( - // // note - this maybe should be the activity event id - // eventId: - // widget.practiceCardController.widget.pangeaMessageEvent.eventId, - // roomId: widget.practiceCardController.widget.pangeaMessageEvent.room.id, - // constructs: currentRecordModel!.latestResponse!.toUses( - // widget.practiceCardController.currentActivity!, - // widget.practiceCardController.metadata, - // ), - // ), - // ); + MatrixState.pangeaController.myAnalytics.setState( + AnalyticsStream( + // note - this maybe should be the activity event id + eventId: + widget.practiceCardController.widget.pangeaMessageEvent.eventId, + roomId: widget.practiceCardController.widget.pangeaMessageEvent.room.id, + constructs: currentRecordModel!.latestResponse!.toUses( + widget.practiceCardController.currentActivity!, + widget.practiceCardController.metadata, + ), + ), + ); // If the selected choice is correct, send the record and get the next activity if (widget.currentActivity.content.isCorrect(value, index)) { diff --git a/pubspec.yaml b/pubspec.yaml index dba848cf1..f1b17f029 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ description: Learn a language while texting your friends. # Pangea# publish_to: none # On version bump also increase the build number for F-Droid -version: 1.22.0+3550 +version: 1.22.1+3551 environment: sdk: ">=3.0.0 <4.0.0" From 63cd77baf49819322ade33aa646bfd9665a192bd Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 11:58:11 -0400 Subject: [PATCH 40/50] Revert "turn off choice array animation" This reverts commit 59682599a5a6a3b5d3109cd44d1c10a512cae369. --- .../choreographer/widgets/choice_array.dart | 150 +++++++++--------- 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/lib/pangea/choreographer/widgets/choice_array.dart b/lib/pangea/choreographer/widgets/choice_array.dart index 549665064..32395099e 100644 --- a/lib/pangea/choreographer/widgets/choice_array.dart +++ b/lib/pangea/choreographer/widgets/choice_array.dart @@ -1,4 +1,5 @@ import 'dart:developer'; +import 'dart:math'; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; @@ -212,99 +213,98 @@ class ChoiceAnimationWidgetState extends State with SingleTickerProviderStateMixin { late final AnimationController _controller; late final Animation _animation; - // AnimationState animationState = AnimationState.ready; + AnimationState animationState = AnimationState.ready; @override void initState() { super.initState(); - // _controller = AnimationController( - // duration: const Duration(milliseconds: 300), - // vsync: this, - // ); + _controller = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); - // _animation = widget.isGold - // ? Tween(begin: 1.0, end: 1.2).animate(_controller) - // : TweenSequence([ - // TweenSequenceItem( - // tween: Tween(begin: 0, end: -8 * pi / 180), - // weight: 1.0, - // ), - // TweenSequenceItem( - // tween: Tween(begin: -8 * pi / 180, end: 16 * pi / 180), - // weight: 2.0, - // ), - // TweenSequenceItem( - // tween: Tween(begin: 16 * pi / 180, end: 0), - // weight: 1.0, - // ), - // ]).animate(_controller); + _animation = widget.isGold + ? Tween(begin: 1.0, end: 1.2).animate(_controller) + : TweenSequence([ + TweenSequenceItem( + tween: Tween(begin: 0, end: -8 * pi / 180), + weight: 1.0, + ), + TweenSequenceItem( + tween: Tween(begin: -8 * pi / 180, end: 16 * pi / 180), + weight: 2.0, + ), + TweenSequenceItem( + tween: Tween(begin: 16 * pi / 180, end: 0), + weight: 1.0, + ), + ]).animate(_controller); widget.enableInteraction(); - // if (widget.selected && animationState == AnimationState.ready) { - // widget.disableInteraction(); - // _controller.forward(); - // setState(() { - // animationState = AnimationState.forward; - // }); - // } - // _controller.addStatusListener((status) { - // if (status == AnimationStatus.completed && - // animationState == AnimationState.forward) { - // _controller.reverse(); - // setState(() { - // animationState = AnimationState.reverse; - // }); - // } - // if (status == AnimationStatus.dismissed && - // animationState == AnimationState.reverse) { - // widget.enableInteraction(); - // setState(() { - // animationState = AnimationState.finished; - // }); - // } - // }); + if (widget.selected && animationState == AnimationState.ready) { + widget.disableInteraction(); + _controller.forward(); + setState(() { + animationState = AnimationState.forward; + }); + } + _controller.addStatusListener((status) { + if (status == AnimationStatus.completed && + animationState == AnimationState.forward) { + _controller.reverse(); + setState(() { + animationState = AnimationState.reverse; + }); + } + if (status == AnimationStatus.dismissed && + animationState == AnimationState.reverse) { + widget.enableInteraction(); + setState(() { + animationState = AnimationState.finished; + }); + } + }); } @override void didUpdateWidget(ChoiceAnimationWidget oldWidget) { super.didUpdateWidget(oldWidget); - // if (widget.selected && animationState == AnimationState.ready) { - // widget.disableInteraction(); - // _controller.forward(); - // setState(() { - // animationState = AnimationState.forward; - // }); - // } + if (widget.selected && animationState == AnimationState.ready) { + widget.disableInteraction(); + _controller.forward(); + setState(() { + animationState = AnimationState.forward; + }); + } } @override Widget build(BuildContext context) { - return widget.child; - // widget.isGold - // ? AnimatedBuilder( - // key: UniqueKey(), - // animation: _animation, - // builder: (context, child) { - // return Transform.scale( - // scale: _animation.value, - // child: child, - // ); - // }, - // child: widget.child, - // ) - // : AnimatedBuilder( - // key: UniqueKey(), - // animation: _animation, - // builder: (context, child) { - // return Transform.rotate( - // angle: _animation.value, - // child: child, - // ); - // }, - // child: widget.child, - // ); + return widget.isGold + ? AnimatedBuilder( + key: UniqueKey(), + animation: _animation, + builder: (context, child) { + return Transform.scale( + scale: _animation.value, + child: child, + ); + }, + child: widget.child, + ) + : AnimatedBuilder( + key: UniqueKey(), + animation: _animation, + builder: (context, child) { + return Transform.rotate( + angle: _animation.value, + child: child, + ); + }, + child: widget.child, + ); } @override From 6052e1618981b7fcc0f87f2412331df36fcbe75c Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 12:01:05 -0400 Subject: [PATCH 41/50] bump version --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index f1b17f029..10b124acf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ description: Learn a language while texting your friends. # Pangea# publish_to: none # On version bump also increase the build number for F-Droid -version: 1.22.1+3551 +version: 1.22.2+3552 environment: sdk: ">=3.0.0 <4.0.0" From 23b6dd08b55f3409b94066b250c0195f620d39ce Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 12:19:00 -0400 Subject: [PATCH 42/50] when sending analytics to the server at logout, don't update the getAnalytics stream afterwards --- .../controllers/get_analytics_controller.dart | 7 ++--- .../controllers/my_analytics_controller.dart | 26 +++++++++++++++---- lib/pangea/utils/logout.dart | 2 +- pubspec.yaml | 2 +- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/lib/pangea/controllers/get_analytics_controller.dart b/lib/pangea/controllers/get_analytics_controller.dart index 03cb2d60b..66cf39501 100644 --- a/lib/pangea/controllers/get_analytics_controller.dart +++ b/lib/pangea/controllers/get_analytics_controller.dart @@ -22,7 +22,7 @@ import 'package:sentry_flutter/sentry_flutter.dart'; class GetAnalyticsController { late PangeaController _pangeaController; final List _cache = []; - StreamSubscription? _analyticsUpdateSubscription; + StreamSubscription? _analyticsUpdateSubscription; CachedStreamController> analyticsStream = CachedStreamController>(); @@ -87,8 +87,9 @@ class GetAnalyticsController { prevXP = null; } - Future onAnalyticsUpdate(AnalyticsUpdateType type) async { - if (type == AnalyticsUpdateType.server) { + Future onAnalyticsUpdate(AnalyticsUpdate analyticsUpdate) async { + if (analyticsUpdate.isLogout) return; + if (analyticsUpdate.type == AnalyticsUpdateType.server) { await getConstructs(forceUpdate: true); } updateAnalyticsStream(); diff --git a/lib/pangea/controllers/my_analytics_controller.dart b/lib/pangea/controllers/my_analytics_controller.dart index 77e6caf27..6ffa1ef31 100644 --- a/lib/pangea/controllers/my_analytics_controller.dart +++ b/lib/pangea/controllers/my_analytics_controller.dart @@ -21,8 +21,8 @@ enum AnalyticsUpdateType { server, local } /// 2) constructs used by the user, both in sending messages and doing practice activities class MyAnalyticsController extends BaseController { late PangeaController _pangeaController; - CachedStreamController analyticsUpdateStream = - CachedStreamController(); + CachedStreamController analyticsUpdateStream = + CachedStreamController(); StreamSubscription? _analyticsStream; Timer? _updateTimer; @@ -237,7 +237,9 @@ class MyAnalyticsController extends BaseController { final int newLevel = _pangeaController.analytics.level; newLevel > prevLevel ? sendLocalAnalyticsToAnalyticsRoom() - : analyticsUpdateStream.add(AnalyticsUpdateType.local); + : analyticsUpdateStream.add( + AnalyticsUpdate(AnalyticsUpdateType.local), + ); } /// Clears the local cache of recently sent constructs. Called before updating analytics @@ -281,7 +283,9 @@ class MyAnalyticsController extends BaseController { /// for the completion of the previous update and returns. Otherwise, it creates a new [_updateCompleter] and /// proceeds with the update process. If the update is successful, it clears any messages that were received /// since the last update and notifies the [analyticsUpdateStream]. - Future sendLocalAnalyticsToAnalyticsRoom() async { + Future sendLocalAnalyticsToAnalyticsRoom({ + onLogout = false, + }) async { if (_pangeaController.matrixState.client.userID == null) return; if (!(_updateCompleter?.isCompleted ?? true)) { await _updateCompleter!.future; @@ -293,7 +297,12 @@ class MyAnalyticsController extends BaseController { clearMessagesSinceUpdate(); lastUpdated = DateTime.now(); - analyticsUpdateStream.add(AnalyticsUpdateType.server); + analyticsUpdateStream.add( + AnalyticsUpdate( + AnalyticsUpdateType.server, + isLogout: onLogout, + ), + ); } catch (err, s) { ErrorHandler.logError( e: err, @@ -340,3 +349,10 @@ class AnalyticsStream { required this.constructs, }); } + +class AnalyticsUpdate { + final AnalyticsUpdateType type; + final bool isLogout; + + AnalyticsUpdate(this.type, {this.isLogout = false}); +} diff --git a/lib/pangea/utils/logout.dart b/lib/pangea/utils/logout.dart index e2bdce074..def632828 100644 --- a/lib/pangea/utils/logout.dart +++ b/lib/pangea/utils/logout.dart @@ -21,7 +21,7 @@ void pLogoutAction(BuildContext context, {bool? isDestructiveAction}) async { // before wiping out locally cached construct data, save it to the server await MatrixState.pangeaController.myAnalytics - .sendLocalAnalyticsToAnalyticsRoom(); + .sendLocalAnalyticsToAnalyticsRoom(onLogout: true); await showFutureLoadingDialog( context: context, diff --git a/pubspec.yaml b/pubspec.yaml index 98b846ae7..076a0ed49 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ description: Learn a language while texting your friends. # Pangea# publish_to: none # On version bump also increase the build number for F-Droid -version: 1.21.7+3547 +version: 1.22.3+3553 environment: sdk: ">=3.0.0 <4.0.0" From 00d6277bc6cc1cbf4eb7d357f53d5a6ecdf3f3e1 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Thu, 24 Oct 2024 12:48:51 -0400 Subject: [PATCH 43/50] some code cleanup and comments --- lib/pangea/controllers/base_controller.dart | 8 ++++---- lib/pangea/controllers/user_controller.dart | 7 +++++++ lib/pangea/enum/construct_use_type_enum.dart | 9 +++++++++ lib/pangea/models/analytics/constructs_model.dart | 5 +---- .../message_activity_request.dart | 7 +------ 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/lib/pangea/controllers/base_controller.dart b/lib/pangea/controllers/base_controller.dart index 69939e50a..51a66dd00 100644 --- a/lib/pangea/controllers/base_controller.dart +++ b/lib/pangea/controllers/base_controller.dart @@ -1,18 +1,18 @@ import 'dart:async'; class BaseController { - final StreamController stateListener = StreamController(); + final StreamController _stateListener = StreamController(); late Stream stateStream; BaseController() { - stateStream = stateListener.stream.asBroadcastStream(); + stateStream = _stateListener.stream.asBroadcastStream(); } dispose() { - stateListener.close(); + _stateListener.close(); } setState(T data) { - stateListener.add(data); + _stateListener.add(data); } } diff --git a/lib/pangea/controllers/user_controller.dart b/lib/pangea/controllers/user_controller.dart index 5ee8672ff..ca7e2869a 100644 --- a/lib/pangea/controllers/user_controller.dart +++ b/lib/pangea/controllers/user_controller.dart @@ -121,19 +121,26 @@ class UserController extends BaseController { /// Initializes the user's profile by waiting for account data to load, reading in account /// data to profile, and migrating from the pangea profile if the account data is not present. Future _initialize() async { + // wait for account data to load + // as long as it's not null, then this we've already migrated the profile await _pangeaController.matrixState.client.waitForAccountData(); if (profile.userSettings.dateOfBirth != null) { return; } + // we used to store the user's profile in the pangea server + // we now store it in the matrix account data final PangeaProfileResponse? resp = await PUserRepo.fetchPangeaUserInfo( userID: userId!, matrixAccessToken: _matrixAccessToken!, ); + + // if it's null, we don't have a profile in the pangea server if (resp?.profile == null) { return; } + // if we have a profile in the pangea server, we need to migrate it to the matrix account data final userSetting = UserSettings.fromJson(resp!.profile.toJson()); final newProfile = Profile(userSettings: userSetting); await newProfile.saveProfileData(waitForDataInSync: true); diff --git a/lib/pangea/enum/construct_use_type_enum.dart b/lib/pangea/enum/construct_use_type_enum.dart index 1f1d37dfe..6ced270b7 100644 --- a/lib/pangea/enum/construct_use_type_enum.dart +++ b/lib/pangea/enum/construct_use_type_enum.dart @@ -125,3 +125,12 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum { } } } + +class ConstructUseTypeUtil { + static ConstructUseTypeEnum fromString(String value) { + return ConstructUseTypeEnum.values.firstWhere( + (e) => e.string == value, + orElse: () => ConstructUseTypeEnum.nan, + ); + } +} diff --git a/lib/pangea/models/analytics/constructs_model.dart b/lib/pangea/models/analytics/constructs_model.dart index 10a47516a..a9781f9ae 100644 --- a/lib/pangea/models/analytics/constructs_model.dart +++ b/lib/pangea/models/analytics/constructs_model.dart @@ -1,6 +1,5 @@ import 'dart:developer'; -import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/enum/construct_use_type_enum.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:flutter/foundation.dart'; @@ -106,9 +105,7 @@ class OneConstructUse { debugger(when: kDebugMode && constructType == null); return OneConstructUse( - useType: ConstructUseTypeEnum.values - .firstWhereOrNull((e) => e.string == json['useType']) ?? - ConstructUseTypeEnum.unk, + useType: ConstructUseTypeUtil.fromString(json['useType']), lemma: json['lemma'], form: json['form'], categories: json['categories'] != null diff --git a/lib/pangea/models/practice_activities.dart/message_activity_request.dart b/lib/pangea/models/practice_activities.dart/message_activity_request.dart index 0740fb8c3..9101a78ce 100644 --- a/lib/pangea/models/practice_activities.dart/message_activity_request.dart +++ b/lib/pangea/models/practice_activities.dart/message_activity_request.dart @@ -27,12 +27,7 @@ class ConstructWithXP { ? DateTime.parse(json['last_used'] as String) : null, condensedConstructUses: (json['uses'] as List).map((e) { - return ConstructUseTypeEnum.values.firstWhereOrNull( - (element) => - element.string == e || - element.toString().split('.').last == e, - ) ?? - ConstructUseTypeEnum.nan; + return ConstructUseTypeUtil.fromString(e); }).toList(), ); } From cb566d06bced6a26dd3f57d653a995378b1b71e9 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 13:24:41 -0400 Subject: [PATCH 44/50] add minimum dimensions to toolbar contents --- lib/config/app_config.dart | 2 +- lib/pages/chat/events/audio_player.dart | 2 +- lib/pangea/utils/inline_tooltip.dart | 17 +- .../widgets/chat/message_audio_card.dart | 2 + .../chat/message_speech_to_text_card.dart | 8 +- .../chat/message_translation_card.dart | 73 ++--- .../toolbar_content_loading_indicator.dart | 31 +- .../common_widgets/overlay_container.dart | 2 + lib/pangea/widgets/igc/card_error_widget.dart | 39 ++- lib/pangea/widgets/igc/card_header.dart | 37 ++- lib/pangea/widgets/igc/word_data_card.dart | 289 ++++++++---------- .../no_more_practice_card.dart | 28 +- .../practice_activity_card.dart | 61 ++-- lib/pangea/widgets/select_to_define.dart | 17 +- pubspec.yaml | 2 +- 15 files changed, 284 insertions(+), 326 deletions(-) diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index b044da3ee..418244fb9 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -24,7 +24,7 @@ abstract class AppConfig { static const bool enableRegistration = true; static const double toolbarMaxHeight = 300.0; static const double toolbarMinHeight = 70.0; - static const double toolbarMinWidth = 350.0; + static const double toolbarMinWidth = 250.0; // #Pangea // static const Color primaryColor = Color(0xFF5625BA); // static const Color primaryColorLight = Color(0xFFCCBDEA); diff --git a/lib/pages/chat/events/audio_player.dart b/lib/pages/chat/events/audio_player.dart index 41e0dc388..4a4f6fac9 100644 --- a/lib/pages/chat/events/audio_player.dart +++ b/lib/pages/chat/events/audio_player.dart @@ -467,7 +467,7 @@ class AudioPlayerState extends State { borderRadius: BorderRadius.circular(2), ), height: 32 * (waveform[i] / 1024), - width: 1.5, + width: 3, ), ], ), diff --git a/lib/pangea/utils/inline_tooltip.dart b/lib/pangea/utils/inline_tooltip.dart index bef617b98..26ec05426 100644 --- a/lib/pangea/utils/inline_tooltip.dart +++ b/lib/pangea/utils/inline_tooltip.dart @@ -30,6 +30,7 @@ class InlineTooltip extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), child: Row( crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, children: [ // Lightbulb icon on the left Icon( @@ -39,16 +40,14 @@ class InlineTooltip extends StatelessWidget { ), const SizedBox(width: 8), // Text in the middle - Expanded( - child: Center( - child: Text( - instructionsEnum.body(context), - style: TextStyle( - color: Theme.of(context).colorScheme.onSurface, - height: 1.5, - ), - textAlign: TextAlign.left, + Center( + child: Text( + instructionsEnum.body(context), + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface, + height: 1.5, ), + textAlign: TextAlign.left, ), ), // Close button on the right diff --git a/lib/pangea/widgets/chat/message_audio_card.dart b/lib/pangea/widgets/chat/message_audio_card.dart index 133e16b1e..97ffe36d5 100644 --- a/lib/pangea/widgets/chat/message_audio_card.dart +++ b/lib/pangea/widgets/chat/message_audio_card.dart @@ -1,6 +1,7 @@ import 'dart:developer'; import 'dart:math'; +import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/chat/events/audio_player.dart'; import 'package:fluffychat/pangea/controllers/text_to_speech_controller.dart'; import 'package:fluffychat/pangea/extensions/pangea_event_extension.dart'; @@ -208,6 +209,7 @@ class MessageAudioCardState extends State { ) : const CardErrorWidget( error: "Null audio file in message_audio_card", + maxWidth: AppConfig.toolbarMinWidth, ), ), ], 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 92489ce29..aae3b3eeb 100644 --- a/lib/pangea/widgets/chat/message_speech_to_text_card.dart +++ b/lib/pangea/widgets/chat/message_speech_to_text_card.dart @@ -1,5 +1,6 @@ import 'dart:developer'; +import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/enum/instructions_enum.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/speech_to_text_models.dart'; @@ -148,9 +149,12 @@ class MessageSpeechToTextCardState extends State { return const ToolbarContentLoadingIndicator(); } - //done fetchig but not results means some kind of error + // done fetchig but not results means some kind of error if (speechToTextResponse == null) { - return CardErrorWidget(error: error); + return CardErrorWidget( + error: error, + maxWidth: AppConfig.toolbarMinWidth, + ); } //TODO: find better icons diff --git a/lib/pangea/widgets/chat/message_translation_card.dart b/lib/pangea/widgets/chat/message_translation_card.dart index f37238a6b..3c0d750a3 100644 --- a/lib/pangea/widgets/chat/message_translation_card.dart +++ b/lib/pangea/widgets/chat/message_translation_card.dart @@ -1,3 +1,4 @@ +import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/enum/instructions_enum.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/pangea_token_model.dart'; @@ -130,7 +131,18 @@ class MessageTranslationCardState extends State { if (!_fetchingTranslation && repEvent == null && selectionTranslation == null) { - return const CardErrorWidget(error: "No translation found"); + return const CardErrorWidget( + error: "No translation found", + maxWidth: AppConfig.toolbarMinWidth, + ); + } + + final loadingTranslation = + (widget.selection != null && selectionTranslation == null) || + (widget.selection == null && repEvent == null); + + if (_fetchingTranslation || loadingTranslation) { + return const ToolbarContentLoadingIndicator(); } return Padding( @@ -139,42 +151,31 @@ class MessageTranslationCardState extends State { mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ - _fetchingTranslation - ? const ToolbarContentLoadingIndicator() - : Flexible( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - widget.selection != null - ? selectionTranslation != null - ? Text( - selectionTranslation!, - style: BotStyle.text(context), - textAlign: TextAlign.center, - ) - : const ToolbarContentLoadingIndicator() - : repEvent != null - ? Text( - repEvent!.text, - style: BotStyle.text(context), - textAlign: TextAlign.center, - ) - : const ToolbarContentLoadingIndicator(), - if (notGoingToTranslate && widget.selection == null) - InlineTooltip( - instructionsEnum: InstructionsEnum.l1Translation, - onClose: () => setState(() {}), - ), - if (widget.selection != null) - InlineTooltip( - instructionsEnum: - InstructionsEnum.clickAgainToDeselect, - onClose: () => setState(() {}), - ), - ], - ), + Flexible( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + widget.selection != null + ? selectionTranslation! + : repEvent!.text, + style: BotStyle.text(context), + textAlign: TextAlign.center, ), + if (notGoingToTranslate && widget.selection == null) + InlineTooltip( + instructionsEnum: InstructionsEnum.l1Translation, + onClose: () => setState(() {}), + ), + if (widget.selection != null) + InlineTooltip( + instructionsEnum: InstructionsEnum.clickAgainToDeselect, + onClose: () => setState(() {}), + ), + ], + ), + ), ], ), ); diff --git a/lib/pangea/widgets/chat/toolbar_content_loading_indicator.dart b/lib/pangea/widgets/chat/toolbar_content_loading_indicator.dart index 28f15c1a9..a497e121d 100644 --- a/lib/pangea/widgets/chat/toolbar_content_loading_indicator.dart +++ b/lib/pangea/widgets/chat/toolbar_content_loading_indicator.dart @@ -1,3 +1,4 @@ +import 'package:fluffychat/config/app_config.dart'; import 'package:flutter/material.dart'; class ToolbarContentLoadingIndicator extends StatelessWidget { @@ -7,25 +8,19 @@ class ToolbarContentLoadingIndicator extends StatelessWidget { @override Widget build(BuildContext context) { - return Column( - // mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox( - height: 14, - width: 14, - child: CircularProgressIndicator( - strokeWidth: 2.0, - color: Theme.of(context).colorScheme.primary, - ), - ), - ], + return SizedBox( + width: AppConfig.toolbarMinWidth, + height: AppConfig.toolbarMinHeight, + child: Center( + child: SizedBox( + height: 14, + width: 14, + child: CircularProgressIndicator( + strokeWidth: 2.0, + color: Theme.of(context).colorScheme.primary, + ), ), - ], + ), ); } } diff --git a/lib/pangea/widgets/common_widgets/overlay_container.dart b/lib/pangea/widgets/common_widgets/overlay_container.dart index 5e2991be7..eae6c935f 100644 --- a/lib/pangea/widgets/common_widgets/overlay_container.dart +++ b/lib/pangea/widgets/common_widgets/overlay_container.dart @@ -32,6 +32,8 @@ class OverlayContainer extends StatelessWidget { constraints: BoxConstraints( maxWidth: maxWidth, maxHeight: maxHeight, + minHeight: 100, + minWidth: 100, ), //PTODO - position card above input/message // margin: const EdgeInsets.all(10), diff --git a/lib/pangea/widgets/igc/card_error_widget.dart b/lib/pangea/widgets/igc/card_error_widget.dart index 708fdd88f..52f66f5a0 100644 --- a/lib/pangea/widgets/igc/card_error_widget.dart +++ b/lib/pangea/widgets/igc/card_error_widget.dart @@ -9,37 +9,44 @@ class CardErrorWidget extends StatelessWidget { final Object? error; final Choreographer? choreographer; final int? offset; + final double? maxWidth; + const CardErrorWidget({ super.key, this.error, this.choreographer, this.offset, + this.maxWidth, }); @override Widget build(BuildContext context) { final ErrorCopy errorCopy = ErrorCopy(context, error); - return Padding( - padding: const EdgeInsets.all(8), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - CardHeader( - text: errorCopy.title, - botExpression: BotExpression.addled, - onClose: () => choreographer?.onMatchError( - cursorOffset: offset, + return ConstrainedBox( + constraints: maxWidth != null + ? BoxConstraints(maxWidth: maxWidth!) + : const BoxConstraints(), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CardHeader( + text: errorCopy.title, + botExpression: BotExpression.addled, + onClose: () => choreographer?.onMatchError( + cursorOffset: offset, + ), ), - ), - const SizedBox(height: 10.0), - Center( - child: Text( + const SizedBox(height: 12.0), + Text( errorCopy.body, style: BotStyle.text(context), + textAlign: TextAlign.center, ), - ), - ], + ], + ), ), ); } diff --git a/lib/pangea/widgets/igc/card_header.dart b/lib/pangea/widgets/igc/card_header.dart index 671e58492..270314f72 100644 --- a/lib/pangea/widgets/igc/card_header.dart +++ b/lib/pangea/widgets/igc/card_header.dart @@ -1,8 +1,6 @@ -import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/utils/bot_style.dart'; import 'package:flutter/material.dart'; -import '../../../widgets/matrix.dart'; import '../common/bot_face_svg.dart'; class CardHeader extends StatelessWidget { @@ -30,26 +28,25 @@ class CardHeader extends StatelessWidget { expression: botExpression, ), ), - const SizedBox(width: 5.0), - Text( - text, - style: BotStyle.text(context), - textAlign: TextAlign.left, - ), - const SizedBox(width: 5.0), - CircleAvatar( - backgroundColor: AppConfig.primaryColor.withOpacity(0.1), - child: IconButton( - icon: const Icon(Icons.close_outlined), - onPressed: () { - if (onClose != null) onClose!(); - MatrixState.pAnyState.closeOverlay(); - }, - color: Theme.of(context).brightness == Brightness.dark - ? AppConfig.primaryColorLight - : AppConfig.primaryColor, + const SizedBox(width: 12.0), + Flexible( + child: Text( + text, + style: BotStyle.text(context), + softWrap: true, ), ), + // const SizedBox(width: 5.0), + // IconButton( + // icon: const Icon(Icons.close_outlined), + // onPressed: () { + // if (onClose != null) onClose!(); + // MatrixState.pAnyState.closeOverlay(); + // }, + // color: Theme.of(context).brightness == Brightness.dark + // ? AppConfig.primaryColorLight + // : AppConfig.primaryColor, + // ), ], ), ); diff --git a/lib/pangea/widgets/igc/word_data_card.dart b/lib/pangea/widgets/igc/word_data_card.dart index 6f1492a75..ff494bb38 100644 --- a/lib/pangea/widgets/igc/word_data_card.dart +++ b/lib/pangea/widgets/igc/word_data_card.dart @@ -1,5 +1,6 @@ import 'dart:developer'; +import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/constants/language_constants.dart'; import 'package:fluffychat/pangea/controllers/contextual_definition_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; @@ -7,8 +8,7 @@ import 'package:fluffychat/pangea/models/language_model.dart'; import 'package:fluffychat/pangea/utils/bot_style.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/utils/firebase_analytics.dart'; -import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart'; -import 'package:fluffychat/pangea/widgets/common/p_circular_loader.dart'; +import 'package:fluffychat/pangea/widgets/chat/toolbar_content_loading_indicator.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -166,71 +166,68 @@ class WordDataCardView extends StatelessWidget { @override Widget build(BuildContext context) { if (controller.wordNetError != null) { - return CardErrorWidget(error: controller.wordNetError); + return CardErrorWidget( + error: controller.wordNetError, + maxWidth: AppConfig.toolbarMinWidth, + ); } if (controller.activeL1 == null || controller.activeL2 == null) { ErrorHandler.logError(m: "should not be here"); - return CardErrorWidget(error: controller.noLanguages); + return CardErrorWidget( + error: controller.noLanguages, + maxWidth: AppConfig.toolbarMinWidth, + ); } - final ScrollController scrollController = ScrollController(); - - return Container( - padding: const EdgeInsets.all(8), - constraints: const BoxConstraints(minHeight: minCardHeight), - alignment: Alignment.center, - child: Scrollbar( - thumbVisibility: true, - controller: scrollController, - child: SingleChildScrollView( - controller: scrollController, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - if (controller.widget.choiceFeedback != null) - Text( - controller.widget.choiceFeedback!, - style: BotStyle.text(context), - ), - const SizedBox(height: 5.0), - if (controller.wordData != null && - controller.wordNetError == null && - controller.activeL1 != null && - controller.activeL2 != null) - WordNetInfo( - wordData: controller.wordData!, - activeL1: controller.activeL1!, - activeL2: controller.activeL2!, - ), - if (controller.isLoadingWordNet) const PCircular(), - const SizedBox(height: 5.0), - // if (controller.widget.hasInfo && - // !controller.isLoadingContextualDefinition && - // controller.contextualDefinitionRes == null) - // Material( - // type: MaterialType.transparency, - // child: ListTile( - // leading: const BotFace( - // width: 40, expression: BotExpression.surprised), - // title: Text(L10n.of(context)!.askPangeaBot), - // onTap: controller.handleGetDefinitionButtonPress, - // ), - // ), - if (controller.isLoadingContextualDefinition) const PCircular(), - if (controller.contextualDefinitionRes != null) - Text( - controller.contextualDefinitionRes!.text, - style: BotStyle.text(context), - ), - if (controller.definitionError != null) - Text( - L10n.of(context)!.sorryNoResults, - style: BotStyle.text(context), - ), - ], - ), - ), + return Padding( + padding: const EdgeInsets.fromLTRB(16, 20, 16, 16), + child: Column( + children: [ + if (controller.widget.choiceFeedback != null) + Text( + controller.widget.choiceFeedback!, + style: BotStyle.text(context), + ), + const SizedBox(height: 5.0), + if (controller.wordData != null && + controller.wordNetError == null && + controller.activeL1 != null && + controller.activeL2 != null) + WordNetInfo( + wordData: controller.wordData!, + activeL1: controller.activeL1!, + activeL2: controller.activeL2!, + ), + if (controller.isLoadingWordNet) + const ToolbarContentLoadingIndicator(), + const SizedBox(height: 5.0), + // if (controller.widget.hasInfo && + // !controller.isLoadingContextualDefinition && + // controller.contextualDefinitionRes == null) + // Material( + // type: MaterialType.transparency, + // child: ListTile( + // leading: const BotFace( + // width: 40, expression: BotExpression.surprised), + // title: Text(L10n.of(context)!.askPangeaBot), + // onTap: controller.handleGetDefinitionButtonPress, + // ), + // ), + if (controller.isLoadingContextualDefinition) + const ToolbarContentLoadingIndicator(), + if (controller.contextualDefinitionRes != null) + Text( + controller.contextualDefinitionRes!.text, + style: BotStyle.text(context), + textAlign: TextAlign.center, + ), + if (controller.definitionError != null) + Text( + L10n.of(context)!.sorryNoResults, + style: BotStyle.text(context), + textAlign: TextAlign.center, + ), + ], ), ); } @@ -251,12 +248,14 @@ class WordNetInfo extends StatelessWidget { @override Widget build(BuildContext context) { return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ SensesForLanguage( wordData: wordData, languageType: LanguageType.target, language: activeL2, ), + const SizedBox(height: 10), SensesForLanguage( wordData: wordData, languageType: LanguageType.base, @@ -273,52 +272,6 @@ enum LanguageType { } class SensesForLanguage extends StatelessWidget { - const SensesForLanguage({ - super.key, - required this.wordData, - required this.languageType, - required this.language, - }); - - final LanguageModel language; - final LanguageType languageType; - final WordData wordData; - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 5), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(7, 0, 0, 0), - child: LanguageFlag( - language: language, - ), - ), - Expanded( - child: PartOfSpeechBlock( - wordData: wordData, - languageType: languageType, - ), - ), - ], - ), - ); - } -} - -class PartOfSpeechBlock extends StatelessWidget { - final WordData wordData; - final LanguageType languageType; - - const PartOfSpeechBlock({ - super.key, - required this.wordData, - required this.languageType, - }); - String get exampleSentence => languageType == LanguageType.target ? wordData.targetExampleSentence : wordData.baseExampleSentence; @@ -336,70 +289,76 @@ class PartOfSpeechBlock extends StatelessWidget { return "$word (${wordData.formattedPartOfSpeech(languageType)})"; } + const SensesForLanguage({ + super.key, + required this.wordData, + required this.languageType, + required this.language, + }); + + final LanguageModel language; + final LanguageType languageType; + final WordData wordData; + @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Align( - alignment: Alignment.centerLeft, - child: Text( - formattedTitle(context), - style: BotStyle.text(context, italics: true, bold: false), - ), - ), - const SizedBox(height: 10), - Padding( - padding: const EdgeInsets.only(left: 14.0, bottom: 10.0), - child: Align( - alignment: Alignment.centerLeft, - child: Column( - children: [ - if (definition.isNotEmpty) - RichText( - text: TextSpan( - style: BotStyle.text( - context, - italics: false, - bold: false, - ), - children: [ - TextSpan( - text: "${L10n.of(context)!.definition}: ", - style: const TextStyle(fontWeight: FontWeight.bold), - ), - TextSpan(text: definition), - ], + return Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LanguageFlag(language: language), + const SizedBox(width: 10), + Flexible( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + formattedTitle(context), + style: BotStyle.text(context, italics: true, bold: false), + ), + const SizedBox(height: 4), + if (definition.isNotEmpty) + RichText( + text: TextSpan( + style: BotStyle.text( + context, + italics: false, + bold: false, + ), + children: [ + TextSpan( + text: "${L10n.of(context)!.definition}: ", + style: const TextStyle(fontWeight: FontWeight.bold), ), + TextSpan(text: definition), + ], + ), + ), + const SizedBox(height: 4), + if (exampleSentence.isNotEmpty) + RichText( + text: TextSpan( + style: BotStyle.text( + context, + italics: false, + bold: false, ), - const SizedBox(height: 10), - if (exampleSentence.isNotEmpty) - RichText( - text: TextSpan( - style: BotStyle.text( - context, - italics: false, - bold: false, + children: [ + TextSpan( + text: "${L10n.of(context)!.exampleSentence}: ", + style: const TextStyle( + fontWeight: FontWeight.bold, ), - children: [ - TextSpan( - text: "${L10n.of(context)!.exampleSentence}: ", - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - ), - TextSpan(text: exampleSentence), - ], ), - ), - ], - ), - ), + TextSpan(text: exampleSentence), + ], + ), + ), + ], ), - ], - ), + ), + ], ); } } diff --git a/lib/pangea/widgets/practice_activity/no_more_practice_card.dart b/lib/pangea/widgets/practice_activity/no_more_practice_card.dart index d4844ac21..12a087d97 100644 --- a/lib/pangea/widgets/practice_activity/no_more_practice_card.dart +++ b/lib/pangea/widgets/practice_activity/no_more_practice_card.dart @@ -1,3 +1,4 @@ +import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/utils/bot_style.dart'; import 'package:flutter/material.dart'; @@ -71,18 +72,21 @@ class GamifiedTextWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8), - child: Column( - children: [ - const StarAnimationWidget(), - const SizedBox(height: 10), - Text( - userMessage, - style: BotStyle.text(context), - textAlign: TextAlign.center, - ), - ], + return SizedBox( + width: AppConfig.toolbarMinWidth, + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 20, 16, 16), + child: Column( + children: [ + const StarAnimationWidget(), + const SizedBox(height: 10), + Text( + userMessage, + style: BotStyle.text(context), + textAlign: TextAlign.center, + ), + ], + ), ), ); } diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index 13d22890f..c9c42b85b 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -9,13 +9,13 @@ import 'package:fluffychat/pangea/models/analytics/constructs_model.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/message_activity_request.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_record_model.dart'; -import 'package:fluffychat/pangea/utils/bot_style.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/widgets/animations/gain_points.dart'; import 'package:fluffychat/pangea/widgets/chat/message_selection_overlay.dart'; import 'package:fluffychat/pangea/widgets/chat/toolbar_content_loading_indicator.dart'; import 'package:fluffychat/pangea/widgets/content_issue_button.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/multiple_choice_activity.dart'; +import 'package:fluffychat/pangea/widgets/practice_activity/no_more_practice_card.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/target_tokens_controller.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; @@ -286,12 +286,10 @@ class PracticeActivityCardState extends State { /// If there is no current activity, the widget returns a sizedbox with a height of 80. /// If the activity type is multiple choice, the widget returns a MultipleChoiceActivity. /// If the activity type is unknown, the widget logs an error and returns a text widget with an error message. - Widget get activityWidget { - if (currentActivity == null) { - // return sizedbox with height of 80 - return const SizedBox(height: 80); - } - switch (currentActivity!.activityType) { + Widget? get activityWidget { + switch (currentActivity?.activityType) { + case null: + return null; case ActivityTypeEnum.multipleChoice: return MultipleChoiceActivity( practiceCardController: this, @@ -304,30 +302,28 @@ class PracticeActivityCardState extends State { practiceCardController: this, currentActivity: currentActivity!, ); - default: - ErrorHandler.logError( - e: Exception('Unknown activity type'), - m: 'Unknown activity type', - data: { - 'activityType': currentActivity!.activityType, - }, - ); - return Text( - L10n.of(context)!.oopsSomethingWentWrong, - style: BotStyle.text(context), - ); + // default: + // ErrorHandler.logError( + // e: Exception('Unknown activity type'), + // m: 'Unknown activity type', + // data: { + // 'activityType': currentActivity!.activityType, + // }, + // ); + // return Text( + // L10n.of(context)!.oopsSomethingWentWrong, + // style: BotStyle.text(context), + // ); } } @override Widget build(BuildContext context) { - // if (!fetchingActivity && currentActivity == null) { - // return GamifiedTextWidget( - // userMessage: L10n.of(context)!.noActivitiesFound, - // ); - // } - - return const ToolbarContentLoadingIndicator(); + if (!fetchingActivity && currentActivity == null) { + return GamifiedTextWidget( + userMessage: L10n.of(context)!.noActivitiesFound, + ); + } return Stack( alignment: Alignment.center, @@ -336,16 +332,15 @@ class PracticeActivityCardState extends State { const Positioned( child: PointsGainedAnimation(), ), - Padding( - padding: const EdgeInsets.fromLTRB(8, 20, 8, 8), - child: activityWidget, - ), + if (activityWidget != null) + Padding( + padding: const EdgeInsets.fromLTRB(16, 20, 16, 16), + child: activityWidget, + ), // Conditionally show the darkening and progress indicator based on the loading state if (!savoringTheJoy && fetchingActivity) ...[ // Circular progress indicator in the center - const Center( - child: CircularProgressIndicator(), - ), + const ToolbarContentLoadingIndicator(), ], // Flag button in the top right corner Positioned( diff --git a/lib/pangea/widgets/select_to_define.dart b/lib/pangea/widgets/select_to_define.dart index 556cc7f94..968eaa147 100644 --- a/lib/pangea/widgets/select_to_define.dart +++ b/lib/pangea/widgets/select_to_define.dart @@ -10,18 +10,11 @@ class SelectToDefine extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.all(8), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Flexible( - child: Text( - L10n.of(context)!.selectToDefine, - style: BotStyle.text(context), - textAlign: TextAlign.center, - ), - ), - ], + padding: const EdgeInsets.fromLTRB(16, 20, 16, 16), + child: Text( + L10n.of(context)!.selectToDefine, + style: BotStyle.text(context), + textAlign: TextAlign.center, ), ); } diff --git a/pubspec.yaml b/pubspec.yaml index 076a0ed49..f8f00ee8f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ description: Learn a language while texting your friends. # Pangea# publish_to: none # On version bump also increase the build number for F-Droid -version: 1.22.3+3553 +version: 1.22.4+3554 environment: sdk: ">=3.0.0 <4.0.0" From 4b5602b2374ad360a9e2f87e4189c5867b91ed5e Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 13:42:22 -0400 Subject: [PATCH 45/50] added x button back to card error header --- lib/config/app_config.dart | 2 +- lib/pangea/widgets/igc/card_error_widget.dart | 30 ++++++++-------- lib/pangea/widgets/igc/card_header.dart | 35 ++++++++++--------- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index 418244fb9..ab05ffd99 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -24,7 +24,7 @@ abstract class AppConfig { static const bool enableRegistration = true; static const double toolbarMaxHeight = 300.0; static const double toolbarMinHeight = 70.0; - static const double toolbarMinWidth = 250.0; + static const double toolbarMinWidth = 270.0; // #Pangea // static const Color primaryColor = Color(0xFF5625BA); // static const Color primaryColorLight = Color(0xFFCCBDEA); diff --git a/lib/pangea/widgets/igc/card_error_widget.dart b/lib/pangea/widgets/igc/card_error_widget.dart index 52f66f5a0..8c810391b 100644 --- a/lib/pangea/widgets/igc/card_error_widget.dart +++ b/lib/pangea/widgets/igc/card_error_widget.dart @@ -27,26 +27,26 @@ class CardErrorWidget extends StatelessWidget { constraints: maxWidth != null ? BoxConstraints(maxWidth: maxWidth!) : const BoxConstraints(), - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - CardHeader( - text: errorCopy.title, - botExpression: BotExpression.addled, - onClose: () => choreographer?.onMatchError( - cursorOffset: offset, - ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CardHeader( + text: errorCopy.title, + botExpression: BotExpression.addled, + onClose: () => choreographer?.onMatchError( + cursorOffset: offset, ), - const SizedBox(height: 12.0), - Text( + ), + const SizedBox(height: 12.0), + Padding( + padding: const EdgeInsets.all(12), + child: Text( errorCopy.body, style: BotStyle.text(context), textAlign: TextAlign.center, ), - ], - ), + ), + ], ), ); } diff --git a/lib/pangea/widgets/igc/card_header.dart b/lib/pangea/widgets/igc/card_header.dart index 270314f72..1816cc214 100644 --- a/lib/pangea/widgets/igc/card_header.dart +++ b/lib/pangea/widgets/igc/card_header.dart @@ -1,4 +1,6 @@ +import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/utils/bot_style.dart'; +import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; import '../common/bot_face_svg.dart'; @@ -20,13 +22,12 @@ class CardHeader extends StatelessWidget { return Padding( padding: const EdgeInsets.only(bottom: 5.0), child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Padding( - padding: const EdgeInsets.only(top: 3.0), - child: BotFace( - width: 50.0, - expression: botExpression, - ), + BotFace( + width: 50.0, + expression: botExpression, ), const SizedBox(width: 12.0), Flexible( @@ -36,17 +37,17 @@ class CardHeader extends StatelessWidget { softWrap: true, ), ), - // const SizedBox(width: 5.0), - // IconButton( - // icon: const Icon(Icons.close_outlined), - // onPressed: () { - // if (onClose != null) onClose!(); - // MatrixState.pAnyState.closeOverlay(); - // }, - // color: Theme.of(context).brightness == Brightness.dark - // ? AppConfig.primaryColorLight - // : AppConfig.primaryColor, - // ), + const SizedBox(width: 5.0), + IconButton( + icon: const Icon(Icons.close_outlined), + onPressed: () { + if (onClose != null) onClose!(); + MatrixState.pAnyState.closeOverlay(); + }, + color: Theme.of(context).brightness == Brightness.dark + ? AppConfig.primaryColorLight + : AppConfig.primaryColor, + ), ], ), ); From dc79a50fda7606140a485bcf67b29794c27c8ab0 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 13:44:32 -0400 Subject: [PATCH 46/50] only init one instance of ttscontroller, don't stop tts twice --- .../widgets/chat/message_audio_card.dart | 8 +++---- .../chat/message_selection_overlay.dart | 8 +++++-- lib/pangea/widgets/chat/message_toolbar.dart | 5 ++++ lib/pangea/widgets/chat/tts_controller.dart | 5 ++-- .../multiple_choice_activity.dart | 8 ++++++- .../practice_activity_card.dart | 5 ++++ .../practice_activity/word_audio_button.dart | 24 ++++--------------- pubspec.yaml | 6 ++--- 8 files changed, 37 insertions(+), 32 deletions(-) diff --git a/lib/pangea/widgets/chat/message_audio_card.dart b/lib/pangea/widgets/chat/message_audio_card.dart index 97ffe36d5..e66e69477 100644 --- a/lib/pangea/widgets/chat/message_audio_card.dart +++ b/lib/pangea/widgets/chat/message_audio_card.dart @@ -21,11 +21,13 @@ class MessageAudioCard extends StatefulWidget { final PangeaMessageEvent messageEvent; final MessageOverlayController overlayController; final PangeaTokenText? selection; + final TtsController tts; const MessageAudioCard({ super.key, required this.messageEvent, required this.overlayController, + required this.tts, this.selection, }); @@ -40,8 +42,6 @@ class MessageAudioCardState extends State { int? sectionStartMS; int? sectionEndMS; - TtsController tts = TtsController(); - @override void initState() { super.initState(); @@ -68,7 +68,7 @@ class MessageAudioCardState extends State { final PangeaTokenText selection = widget.selection!; final tokenText = selection.content; - await tts.speak(tokenText); + await widget.tts.speak(tokenText); } void setSectionStartAndEnd(int? start, int? end) => mounted @@ -204,7 +204,7 @@ class MessageAudioCardState extends State { color: Theme.of(context).colorScheme.onPrimaryContainer, ), - tts.missingVoiceButton, + widget.tts.missingVoiceButton, ], ) : const CardErrorWidget( diff --git a/lib/pangea/widgets/chat/message_selection_overlay.dart b/lib/pangea/widgets/chat/message_selection_overlay.dart index 5cf9e70b5..dbce6f2b8 100644 --- a/lib/pangea/widgets/chat/message_selection_overlay.dart +++ b/lib/pangea/widgets/chat/message_selection_overlay.dart @@ -17,6 +17,7 @@ import 'package:fluffychat/pangea/widgets/chat/message_toolbar_buttons.dart'; import 'package:fluffychat/pangea/widgets/chat/overlay_footer.dart'; import 'package:fluffychat/pangea/widgets/chat/overlay_header.dart'; import 'package:fluffychat/pangea/widgets/chat/overlay_message.dart'; +import 'package:fluffychat/pangea/widgets/chat/tts_controller.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; @@ -61,11 +62,11 @@ class MessageOverlayController extends State /// The number of activities that need to be completed before the toolbar is unlocked /// If we don't have any good activities for them, we'll decrease this number static const int neededActivities = 3; - int activitiesLeftToComplete = neededActivities; - PangeaMessageEvent get pangeaMessageEvent => widget._pangeaMessageEvent; + final TtsController tts = TtsController(); + @override void initState() { super.initState(); @@ -98,6 +99,7 @@ class MessageOverlayController extends State ).listen((_) => setState(() {})); setInitialToolbarMode(); + tts.setupTTS(); } /// We need to check if the setState call is safe to call immediately @@ -359,6 +361,7 @@ class MessageOverlayController extends State void dispose() { _animationController.dispose(); _reactionSubscription?.cancel(); + tts.dispose(); super.dispose(); } @@ -455,6 +458,7 @@ class MessageOverlayController extends State MessageToolbar( pangeaMessageEvent: widget._pangeaMessageEvent, overLayController: this, + tts: tts, ), SizedBox( height: adjustedMessageHeight, diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index cf4192691..2f2f2b736 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -10,6 +10,7 @@ import 'package:fluffychat/pangea/widgets/chat/message_selection_overlay.dart'; import 'package:fluffychat/pangea/widgets/chat/message_speech_to_text_card.dart'; import 'package:fluffychat/pangea/widgets/chat/message_translation_card.dart'; import 'package:fluffychat/pangea/widgets/chat/message_unsubscribed_card.dart'; +import 'package:fluffychat/pangea/widgets/chat/tts_controller.dart'; import 'package:fluffychat/pangea/widgets/igc/word_data_card.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart'; import 'package:fluffychat/pangea/widgets/select_to_define.dart'; @@ -22,11 +23,13 @@ const double minCardHeight = 70; class MessageToolbar extends StatelessWidget { final PangeaMessageEvent pangeaMessageEvent; final MessageOverlayController overLayController; + final TtsController tts; const MessageToolbar({ super.key, required this.pangeaMessageEvent, required this.overLayController, + required this.tts, }); Widget get toolbarContent { @@ -50,6 +53,7 @@ class MessageToolbar extends StatelessWidget { messageEvent: pangeaMessageEvent, overlayController: overLayController, selection: overLayController.selectedSpan, + tts: tts, ); case MessageMode.speechToText: return MessageSpeechToTextCard( @@ -87,6 +91,7 @@ class MessageToolbar extends StatelessWidget { return PracticeActivityCard( pangeaMessageEvent: pangeaMessageEvent, overlayController: overLayController, + tts: tts, ); default: debugger(when: kDebugMode); diff --git a/lib/pangea/widgets/chat/tts_controller.dart b/lib/pangea/widgets/chat/tts_controller.dart index 225d7a04e..c98cb6220 100644 --- a/lib/pangea/widgets/chat/tts_controller.dart +++ b/lib/pangea/widgets/chat/tts_controller.dart @@ -23,7 +23,8 @@ class TtsController { } onError(dynamic message) => ErrorHandler.logError( - m: 'TTS error', + e: message, + m: (message.toString().isNotEmpty) ? message.toString() : 'TTS error', data: { 'message': message, }, @@ -82,13 +83,11 @@ class TtsController { debugger(when: kDebugMode); ErrorHandler.logError(e: e, s: s); } - await tts.stop(); } Future speak(String text) async { try { stop(); - targetLanguage ??= MatrixState.pangeaController.languageController.userL2?.langCode; diff --git a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart index ee892837b..e76021000 100644 --- a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart +++ b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart @@ -6,6 +6,7 @@ import 'package:fluffychat/pangea/controllers/my_analytics_controller.dart'; import 'package:fluffychat/pangea/enum/activity_type_enum.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_record_model.dart'; +import 'package:fluffychat/pangea/widgets/chat/tts_controller.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/word_audio_button.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -16,11 +17,13 @@ import 'package:flutter/material.dart'; class MultipleChoiceActivity extends StatefulWidget { final PracticeActivityCardState practiceCardController; final PracticeActivityModel currentActivity; + final TtsController tts; const MultipleChoiceActivity({ super.key, required this.practiceCardController, required this.currentActivity, + required this.tts, }); @override @@ -112,7 +115,10 @@ class MultipleChoiceActivityState extends State { // #freeze-activity if (practiceActivity.activityType == ActivityTypeEnum.wordFocusListening) - WordAudioButton(text: practiceActivity.content.answer), + WordAudioButton( + text: practiceActivity.content.answer, + ttsController: widget.tts, + ), ChoicesArray( isLoading: false, uniqueKeyForLayerLink: (index) => "multiple_choice_$index", diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index c9c42b85b..dbb98de73 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -13,6 +13,7 @@ import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/widgets/animations/gain_points.dart'; import 'package:fluffychat/pangea/widgets/chat/message_selection_overlay.dart'; import 'package:fluffychat/pangea/widgets/chat/toolbar_content_loading_indicator.dart'; +import 'package:fluffychat/pangea/widgets/chat/tts_controller.dart'; import 'package:fluffychat/pangea/widgets/content_issue_button.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/multiple_choice_activity.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/no_more_practice_card.dart'; @@ -28,11 +29,13 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; class PracticeActivityCard extends StatefulWidget { final PangeaMessageEvent pangeaMessageEvent; final MessageOverlayController overlayController; + final TtsController tts; const PracticeActivityCard({ super.key, required this.pangeaMessageEvent, required this.overlayController, + required this.tts, }); @override @@ -294,6 +297,7 @@ class PracticeActivityCardState extends State { return MultipleChoiceActivity( practiceCardController: this, currentActivity: currentActivity!, + tts: widget.tts, ); case ActivityTypeEnum.wordFocusListening: // return WordFocusListeningActivity( @@ -301,6 +305,7 @@ class PracticeActivityCardState extends State { return MultipleChoiceActivity( practiceCardController: this, currentActivity: currentActivity!, + tts: widget.tts, ); // default: // ErrorHandler.logError( diff --git a/lib/pangea/widgets/practice_activity/word_audio_button.dart b/lib/pangea/widgets/practice_activity/word_audio_button.dart index 24835fcb2..2f56299c8 100644 --- a/lib/pangea/widgets/practice_activity/word_audio_button.dart +++ b/lib/pangea/widgets/practice_activity/word_audio_button.dart @@ -4,10 +4,12 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; class WordAudioButton extends StatefulWidget { final String text; + final TtsController ttsController; const WordAudioButton({ super.key, required this.text, + required this.ttsController, }); @override @@ -17,22 +19,6 @@ class WordAudioButton extends StatefulWidget { class WordAudioButtonState extends State { bool _isPlaying = false; - TtsController ttsController = TtsController(); - - @override - void initState() { - // TODO: implement initState - debugPrint('initState WordAudioButton'); - super.initState(); - ttsController.setupTTS().then((value) => setState(() {})); - } - - @override - void dispose() { - ttsController.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { debugPrint('build WordAudioButton'); @@ -54,7 +40,7 @@ class WordAudioButtonState extends State { _isPlaying ? L10n.of(context)!.stop : L10n.of(context)!.playAudio, onPressed: () async { if (_isPlaying) { - await ttsController.tts.stop(); + await widget.ttsController.tts.stop(); if (mounted) { setState(() => _isPlaying = false); } @@ -62,7 +48,7 @@ class WordAudioButtonState extends State { if (mounted) { setState(() => _isPlaying = true); } - await ttsController.speak(widget.text); + await widget.ttsController.speak(widget.text); if (mounted) { setState(() => _isPlaying = false); } @@ -70,7 +56,7 @@ class WordAudioButtonState extends State { }, // Disable button if language isn't supported ), // #freeze-activity - ttsController.missingVoiceButton, + widget.ttsController.missingVoiceButton, ], ); } diff --git a/pubspec.yaml b/pubspec.yaml index f8f00ee8f..25403c0ec 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ description: Learn a language while texting your friends. # Pangea# publish_to: none # On version bump also increase the build number for F-Droid -version: 1.22.4+3554 +version: 1.22.5+3555 environment: sdk: ">=3.0.0 <4.0.0" @@ -162,8 +162,8 @@ flutter: # #Pangea # uncomment this to enable mobile builds # causes error with github actions - # - .env - # - assets/.env + - .env + - assets/.env - assets/pangea/ - assets/pangea/bot_faces/ # Pangea# From a3845eb13ab33ba085e50041d25ddfb6da66db10 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 13:46:20 -0400 Subject: [PATCH 47/50] comment out env file paths in pubspec --- pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 25403c0ec..377087387 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -162,8 +162,8 @@ flutter: # #Pangea # uncomment this to enable mobile builds # causes error with github actions - - .env - - assets/.env + # - .env + # - assets/.env - assets/pangea/ - assets/pangea/bot_faces/ # Pangea# From 9e3111f97c68c347cc5aa67cf6df197e3a63bb0a Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 14:16:39 -0400 Subject: [PATCH 48/50] don't play token text if null message audio is playing --- lib/pages/chat/events/audio_player.dart | 11 +++++++++-- lib/pangea/widgets/chat/message_audio_card.dart | 6 +++++- .../widgets/chat/message_selection_overlay.dart | 15 ++++++++++++--- lib/pangea/widgets/chat/message_toolbar.dart | 1 + pubspec.yaml | 2 +- 5 files changed, 28 insertions(+), 7 deletions(-) diff --git a/lib/pages/chat/events/audio_player.dart b/lib/pages/chat/events/audio_player.dart index 4a4f6fac9..c79fc9cb3 100644 --- a/lib/pages/chat/events/audio_player.dart +++ b/lib/pages/chat/events/audio_player.dart @@ -21,6 +21,7 @@ class AudioPlayerWidget extends StatefulWidget { final Event? event; final PangeaAudioFile? matrixFile; final bool autoplay; + final Function(bool)? setIsPlayingAudio; // Pangea# static String? currentId; @@ -41,6 +42,7 @@ class AudioPlayerWidget extends StatefulWidget { this.autoplay = false, this.sectionStartMS, this.sectionEndMS, + this.setIsPlayingAudio, // Pangea# super.key, }); @@ -204,8 +206,13 @@ class AudioPlayerState extends State { if (max == null || max == Duration.zero) return; setState(() => maxPosition = max.inMilliseconds.toDouble()); }); - onPlayerStateChanged ??= - audioPlayer.playingStream.listen((_) => setState(() {})); + onPlayerStateChanged ??= audioPlayer.playingStream.listen( + (isPlaying) => setState(() { + // #Pangea + widget.setIsPlayingAudio?.call(isPlaying); + // Pangea# + }), + ); final audioFile = this.audioFile; if (audioFile != null) { audioPlayer.setFilePath(audioFile.path); diff --git a/lib/pangea/widgets/chat/message_audio_card.dart b/lib/pangea/widgets/chat/message_audio_card.dart index e66e69477..47cb41af8 100644 --- a/lib/pangea/widgets/chat/message_audio_card.dart +++ b/lib/pangea/widgets/chat/message_audio_card.dart @@ -22,12 +22,14 @@ class MessageAudioCard extends StatefulWidget { final MessageOverlayController overlayController; final PangeaTokenText? selection; final TtsController tts; + final Function(bool) setIsPlayingAudio; const MessageAudioCard({ super.key, required this.messageEvent, required this.overlayController, required this.tts, + required this.setIsPlayingAudio, this.selection, }); @@ -56,7 +58,7 @@ class MessageAudioCardState extends State { @override void didUpdateWidget(covariant oldWidget) { - if (oldWidget.selection != widget.selection) { + if (oldWidget.selection != widget.selection && widget.selection != null) { debugPrint('selection changed'); setSectionStartAndEndFromSelection(); playSelectionAudio(); @@ -65,6 +67,7 @@ class MessageAudioCardState extends State { } Future playSelectionAudio() async { + if (widget.selection == null) return; final PangeaTokenText selection = widget.selection!; final tokenText = selection.content; @@ -203,6 +206,7 @@ class MessageAudioCardState extends State { sectionEndMS: sectionEndMS, color: Theme.of(context).colorScheme.onPrimaryContainer, + setIsPlayingAudio: widget.setIsPlayingAudio, ), widget.tts.missingVoiceButton, ], diff --git a/lib/pangea/widgets/chat/message_selection_overlay.dart b/lib/pangea/widgets/chat/message_selection_overlay.dart index dbce6f2b8..a8022b1cc 100644 --- a/lib/pangea/widgets/chat/message_selection_overlay.dart +++ b/lib/pangea/widgets/chat/message_selection_overlay.dart @@ -66,6 +66,7 @@ class MessageOverlayController extends State PangeaMessageEvent get pangeaMessageEvent => widget._pangeaMessageEvent; final TtsController tts = TtsController(); + bool isPlayingAudio = false; @override void initState() { @@ -200,9 +201,10 @@ class MessageOverlayController extends State PangeaToken token, ) { if ([ - MessageMode.practiceActivity, - // MessageMode.textToSpeech - ].contains(toolbarMode)) { + MessageMode.practiceActivity, + // MessageMode.textToSpeech + ].contains(toolbarMode) || + isPlayingAudio) { return; } @@ -273,6 +275,13 @@ class MessageOverlayController extends State double get reactionsHeight => hasReactions ? 28 : 0; double get belowMessageHeight => toolbarButtonsHeight + reactionsHeight; + void setIsPlayingAudio(bool isPlaying) { + if (mounted) { + setState(() => isPlayingAudio = isPlaying); + debugPrint("IS PLAYING AUDIO: $isPlaying"); + } + } + @override void didChangeDependencies() { super.didChangeDependencies(); diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index 2f2f2b736..071a99f69 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -54,6 +54,7 @@ class MessageToolbar extends StatelessWidget { overlayController: overLayController, selection: overLayController.selectedSpan, tts: tts, + setIsPlayingAudio: overLayController.setIsPlayingAudio, ); case MessageMode.speechToText: return MessageSpeechToTextCard( diff --git a/pubspec.yaml b/pubspec.yaml index 377087387..80906d77c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ description: Learn a language while texting your friends. # Pangea# publish_to: none # On version bump also increase the build number for F-Droid -version: 1.22.5+3555 +version: 1.22.6+3556 environment: sdk: ">=3.0.0 <4.0.0" From 1a151e90b7ec7d82f012c3b10a43fe2a98be7aac Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 14:33:25 -0400 Subject: [PATCH 49/50] removed print statement --- lib/pangea/widgets/chat/message_selection_overlay.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/pangea/widgets/chat/message_selection_overlay.dart b/lib/pangea/widgets/chat/message_selection_overlay.dart index a8022b1cc..bd5b3da00 100644 --- a/lib/pangea/widgets/chat/message_selection_overlay.dart +++ b/lib/pangea/widgets/chat/message_selection_overlay.dart @@ -278,7 +278,6 @@ class MessageOverlayController extends State void setIsPlayingAudio(bool isPlaying) { if (mounted) { setState(() => isPlayingAudio = isPlaying); - debugPrint("IS PLAYING AUDIO: $isPlaying"); } } From fbccd3a7f87a4a2a593042205f7c33e35e32c7b3 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 24 Oct 2024 14:55:40 -0400 Subject: [PATCH 50/50] some tweaks to the card header --- lib/pangea/widgets/igc/card_header.dart | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/pangea/widgets/igc/card_header.dart b/lib/pangea/widgets/igc/card_header.dart index 1816cc214..0063c9638 100644 --- a/lib/pangea/widgets/igc/card_header.dart +++ b/lib/pangea/widgets/igc/card_header.dart @@ -22,19 +22,24 @@ class CardHeader extends StatelessWidget { return Padding( padding: const EdgeInsets.only(bottom: 5.0), child: Row( - crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - BotFace( - width: 50.0, - expression: botExpression, - ), - const SizedBox(width: 12.0), Flexible( - child: Text( - text, - style: BotStyle.text(context), - softWrap: true, + child: Row( + children: [ + BotFace( + width: 50.0, + expression: botExpression, + ), + const SizedBox(width: 12.0), + Flexible( + child: Text( + text, + style: BotStyle.text(context), + softWrap: true, + ), + ), + ], ), ), const SizedBox(width: 5.0),