diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 93d4fc0c7..964107cf8 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -4065,6 +4065,8 @@ "practice": "Practice", "noLanguagesSet": "No languages set", "noActivitiesFound": "No practice activities found for this message", + "hintTitle": "Hint:", + "speechToTextBody": "See how well you did by looking at your Accuracy and Words Per Minute scores", "previous": "Previous", "languageButtonLabel": "Language: {currentLanguage}", "@languageButtonLabel": { diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index 048b54443..24508bb62 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -4,8 +4,8 @@ import 'package:fluffychat/pages/chat/events/message.dart'; import 'package:fluffychat/pages/chat/seen_by_row.dart'; import 'package:fluffychat/pages/chat/typing_indicators.dart'; import 'package:fluffychat/pages/user_bottom_sheet/user_bottom_sheet.dart'; +import 'package:fluffychat/pangea/enum/instructions_enum.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/utils/instructions.dart'; import 'package:fluffychat/pangea/widgets/chat/locked_chat_message.dart'; import 'package:fluffychat/utils/account_config.dart'; import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; @@ -46,7 +46,7 @@ class ChatEventList extends StatelessWidget { // card, attach it on top of the first shown message WidgetsBinding.instance.addPostFrameCallback((_) { if (events.isEmpty) return; - controller.pangeaController.instructions.show( + controller.pangeaController.instructions.showInstructionsPopup( context, InstructionsEnum.clickMessage, events[0].eventId, diff --git a/lib/pangea/choreographer/widgets/it_bar_buttons.dart b/lib/pangea/choreographer/widgets/it_bar_buttons.dart index 786c17c07..9fcaa927e 100644 --- a/lib/pangea/choreographer/widgets/it_bar_buttons.dart +++ b/lib/pangea/choreographer/widgets/it_bar_buttons.dart @@ -1,8 +1,8 @@ +import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; +import 'package:fluffychat/pangea/enum/instructions_enum.dart'; +import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; -import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; -import 'package:fluffychat/pangea/utils/instructions.dart'; -import 'package:fluffychat/widgets/matrix.dart'; import '../../widgets/common/bot_face_svg.dart'; import '../controllers/choreographer.dart'; import '../controllers/it_controller.dart'; @@ -37,7 +37,7 @@ class ITBotButton extends StatelessWidget { @override Widget build(BuildContext context) { - choreographer.pangeaController.instructions.show( + choreographer.pangeaController.instructions.showInstructionsPopup( context, InstructionsEnum.itInstructions, choreographer.itBotTransformTargetKey, @@ -46,7 +46,8 @@ class ITBotButton extends StatelessWidget { return IconButton( icon: const BotFace(width: 40.0, expression: BotExpression.idle), - onPressed: () => choreographer.pangeaController.instructions.show( + onPressed: () => + choreographer.pangeaController.instructions.showInstructionsPopup( context, InstructionsEnum.itInstructions, choreographer.itBotTransformTargetKey, diff --git a/lib/pangea/enum/instructions_enum.dart b/lib/pangea/enum/instructions_enum.dart new file mode 100644 index 000000000..c684d5b14 --- /dev/null +++ b/lib/pangea/enum/instructions_enum.dart @@ -0,0 +1,45 @@ +import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +enum InstructionsEnum { + itInstructions, + clickMessage, + blurMeansTranslate, + tooltipInstructions, + speechToText, +} + +extension Copy on InstructionsEnum { + String title(BuildContext context) { + switch (this) { + case InstructionsEnum.itInstructions: + return L10n.of(context)!.itInstructionsTitle; + case InstructionsEnum.clickMessage: + return L10n.of(context)!.clickMessageTitle; + case InstructionsEnum.blurMeansTranslate: + return L10n.of(context)!.blurMeansTranslateTitle; + case InstructionsEnum.tooltipInstructions: + return L10n.of(context)!.tooltipInstructionsTitle; + case InstructionsEnum.speechToText: + return L10n.of(context)!.hintTitle; + } + } + + String body(BuildContext context) { + switch (this) { + case InstructionsEnum.itInstructions: + return L10n.of(context)!.itInstructionsBody; + case InstructionsEnum.clickMessage: + return L10n.of(context)!.clickMessageBody; + case InstructionsEnum.blurMeansTranslate: + return L10n.of(context)!.blurMeansTranslateBody; + case InstructionsEnum.speechToText: + return L10n.of(context)!.speechToTextBody; + case InstructionsEnum.tooltipInstructions: + return PlatformInfos.isMobile + ? L10n.of(context)!.tooltipInstructionsMobileBody + : L10n.of(context)!.tooltipInstructionsBrowserBody; + } + } +} diff --git a/lib/pangea/models/user_model.dart b/lib/pangea/models/user_model.dart index dd5ec0fa5..3e3a508cd 100644 --- a/lib/pangea/models/user_model.dart +++ b/lib/pangea/models/user_model.dart @@ -4,8 +4,8 @@ import 'package:country_picker/country_picker.dart'; import 'package:fluffychat/pangea/constants/local.key.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; +import 'package:fluffychat/pangea/enum/instructions_enum.dart'; import 'package:fluffychat/pangea/models/space_model.dart'; -import 'package:fluffychat/pangea/utils/instructions.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; diff --git a/lib/pangea/utils/instructions.dart b/lib/pangea/utils/instructions.dart index ff0fea8f6..382268082 100644 --- a/lib/pangea/utils/instructions.dart +++ b/lib/pangea/utils/instructions.dart @@ -1,4 +1,5 @@ -import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:fluffychat/pangea/enum/instructions_enum.dart'; +import 'package:fluffychat/pangea/utils/inline_tooltip.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -14,17 +15,29 @@ import 'overlay.dart'; class InstructionsController { late PangeaController _pangeaController; + // We have these three methods to make sure that the instructions are not shown too much + + /// Instruction popup was closed by the user final Map _instructionsClosed = {}; + + /// Instruction popup has already been shown this session final Map _instructionsShown = {}; + /// Returns true if the user requested this popup not be shown again + bool? toggledOff(InstructionsEnum key) => + _pangeaController.pStoreService.read(key.toString()); + InstructionsController(PangeaController pangeaController) { _pangeaController = pangeaController; } + /// Returns true if the instructions were closed + /// or turned off by the user via the toggle switch bool wereInstructionsTurnedOff(InstructionsEnum key) => - _pangeaController.pStoreService.read(key.toString()) ?? - _instructionsClosed[key] ?? - false; + toggledOff(key) ?? _instructionsClosed[key] ?? false; + + void turnOffInstruction(InstructionsEnum key) => + _instructionsClosed[key] = true; Future updateEnableInstructions( InstructionsEnum key, @@ -35,7 +48,9 @@ class InstructionsController { value, ); - Future show( + /// Instruction Card gives users tips on + /// how to use Pangea Chat's features + Future showInstructionsPopup( BuildContext context, InstructionsEnum key, String transformTargetKey, [ @@ -98,45 +113,35 @@ class InstructionsController { ), ); } -} -enum InstructionsEnum { - itInstructions, - clickMessage, - blurMeansTranslate, - tooltipInstructions, -} - -extension Copy on InstructionsEnum { - String title(BuildContext context) { - switch (this) { - case InstructionsEnum.itInstructions: - return L10n.of(context)!.itInstructionsTitle; - case InstructionsEnum.clickMessage: - return L10n.of(context)!.clickMessageTitle; - case InstructionsEnum.blurMeansTranslate: - return L10n.of(context)!.blurMeansTranslateTitle; - case InstructionsEnum.tooltipInstructions: - return L10n.of(context)!.tooltipInstructionsTitle; + /// Returns a widget that will be added to existing widget + /// which displays hint text defined in the enum extension + Widget getInstructionInlineTooltip( + BuildContext context, + InstructionsEnum key, + VoidCallback onClose, + ) { + if (wereInstructionsTurnedOff(key)) { + return const SizedBox(); } - } - String body(BuildContext context) { - switch (this) { - case InstructionsEnum.itInstructions: - return L10n.of(context)!.itInstructionsBody; - case InstructionsEnum.clickMessage: - return L10n.of(context)!.clickMessageBody; - case InstructionsEnum.blurMeansTranslate: - return L10n.of(context)!.blurMeansTranslateBody; - case InstructionsEnum.tooltipInstructions: - return PlatformInfos.isMobile - ? L10n.of(context)!.tooltipInstructionsMobileBody - : L10n.of(context)!.tooltipInstructionsBrowserBody; + if (L10n.of(context) == null) { + ErrorHandler.logError( + m: "null context in ITBotButton.showCard", + s: StackTrace.current, + ); + return const SizedBox(); } + + return InlineTooltip( + body: InstructionsEnum.speechToText.body(context), + onClose: onClose, + ); } } +/// User can toggle on to prevent Instruction Card +/// from appearing in future sessions class InstructionsToggle extends StatefulWidget { const InstructionsToggle({ super.key, 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 9099b9b95..a26a36658 100644 --- a/lib/pangea/widgets/chat/message_speech_to_text_card.dart +++ b/lib/pangea/widgets/chat/message_speech_to_text_card.dart @@ -1,9 +1,9 @@ import 'dart:developer'; +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'; import 'package:fluffychat/pangea/utils/error_handler.dart'; -import 'package:fluffychat/pangea/utils/instructions.dart'; import 'package:fluffychat/pangea/widgets/chat/toolbar_content_loading_indicator.dart'; import 'package:fluffychat/pangea/widgets/common/icon_number_widget.dart'; import 'package:fluffychat/pangea/widgets/igc/card_error_widget.dart'; @@ -65,6 +65,13 @@ class MessageSpeechToTextCardState extends State { } } + void closeHint() { + MatrixState.pangeaController.instructions.turnOffInstruction( + InstructionsEnum.speechToText, + ); + setState(() {}); + } + TextSpan _buildTranscriptText(BuildContext context) { final Transcript transcript = speechToTextResponse!.transcript; final List spans = []; @@ -172,7 +179,8 @@ class MessageSpeechToTextCardState extends State { number: "${selectedToken?.confidence ?? speechToTextResponse!.transcript.confidence}%", toolTip: L10n.of(context)!.accuracy, - onPressed: () => MatrixState.pangeaController.instructions.show( + onPressed: () => MatrixState.pangeaController.instructions + .showInstructionsPopup( context, InstructionsEnum.tooltipInstructions, widget.messageEvent.eventId, @@ -184,7 +192,8 @@ class MessageSpeechToTextCardState extends State { number: wordsPerMinuteString != null ? "$wordsPerMinuteString" : "??", toolTip: L10n.of(context)!.wordsPerMinute, - onPressed: () => MatrixState.pangeaController.instructions.show( + onPressed: () => MatrixState.pangeaController.instructions + .showInstructionsPopup( context, InstructionsEnum.tooltipInstructions, widget.messageEvent.eventId, @@ -193,6 +202,11 @@ class MessageSpeechToTextCardState extends State { ), ], ), + MatrixState.pangeaController.instructions.getInstructionInlineTooltip( + context, + InstructionsEnum.speechToText, + closeHint, + ), ], ); } diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index 57b5845a4..8d2d66b7d 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -137,6 +137,8 @@ class ToolbarDisplayController { ? Alignment.bottomLeft : Alignment.topLeft, backgroundColor: const Color.fromRGBO(0, 0, 0, 1).withAlpha(100), + closePrevOverlay: + MatrixState.pangeaController.subscriptionController.isSubscribed, ); if (MatrixState.pAnyState.entries.isNotEmpty) { diff --git a/lib/pangea/widgets/igc/pangea_rich_text.dart b/lib/pangea/widgets/igc/pangea_rich_text.dart index bbd6868bf..1dd1cd16e 100644 --- a/lib/pangea/widgets/igc/pangea_rich_text.dart +++ b/lib/pangea/widgets/igc/pangea_rich_text.dart @@ -3,10 +3,10 @@ import 'dart:ui'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.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/representation_content_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; -import 'package:fluffychat/pangea/utils/instructions.dart'; import 'package:fluffychat/pangea/widgets/chat/message_context_menu.dart'; import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -115,7 +115,7 @@ class PangeaRichTextState extends State { @override Widget build(BuildContext context) { if (blur > 0) { - pangeaController.instructions.show( + pangeaController.instructions.showInstructionsPopup( context, InstructionsEnum.blurMeansTranslate, widget.pangeaMessageEvent.eventId,