further toolbar tweaks

* remove print statement

* ending animation, savoring joy, properly adding xp in session

* forgot to switch env again...

* increment version number

* about to move toolbar buttons up to level of overlay controller

* added ability to give feedback and get new activity

* more practice tweaks and instructions too

* incrementing pubspec version
This commit is contained in:
wcjord 2024-10-04 17:52:56 -04:00 committed by GitHub
parent b8edf595ca
commit b7ab6038ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 292 additions and 319 deletions

View file

@ -3149,7 +3149,7 @@
"generateVocabulary": "Generate vocabulary from title and description",
"generatePrompts": "Generate prompts",
"subscribe": "Subscribe",
"getAccess": "Unlock learning tools",
"getAccess": "Subscribe now!",
"subscriptionDesc": "Messaging is free! Subscribe to unlock interactive translation, grammar checking and learning analytics.",
"subscriptionManagement": "Subscription Management",
"currentSubscription": "Current Subscription",
@ -3788,7 +3788,7 @@
}
},
"freeTrialDesc": "New users recieve a one week free trial of Pangea Chat",
"activateTrial": "Activate Free Trial",
"activateTrial": "Free 7-Day Trial",
"inNoSpaces": "You are not a member of any spaces",
"successfullySubscribed": "You have successfully subscribed!",
"clickToManageSubscription": "Click here to manage your subscription.",
@ -3968,11 +3968,11 @@
"seeOptions": "See options",
"continuedWithoutSubscription": "Continue without subscribing",
"trialPeriodExpired": "Your trial period has expired",
"selectToDefine": "Click a word to see its definition!",
"selectToDefine": "Click any word to see its definition!",
"translations": "translations",
"messageAudio": "message audio",
"definitions": "definitions",
"subscribedToUnlockTools": "Subscribe to unlock language tools, including",
"subscribedToUnlockTools": "Subscribe to unlock interactive translation and grammar checking, audio playback, personalized practice activities, and learning analytics!",
"more": "More",
"translationTooltip": "Translate",
"audioTooltip": "Play Audio",
@ -4162,7 +4162,7 @@
"placeholders": {}
},
"changeAnalyticsView": "Change Analytics View",
"l1TranslationBody": "Oops! It looks like this message wasn't sent in your target language. Messages not sent in your target language will not be translated.",
"l1TranslationBody": "Messages in your base language will not be translated.",
"continueText": "Continue",
"deleteSubscriptionWarningTitle": "You have an active subscription",
"deleteSubscriptionWarningBody": "Deleting your account will not automatically cancel your subscription.",
@ -4232,5 +4232,6 @@
"chatName": "Chat name",
"reportContentIssueTitle": "Report content issue",
"feedback": "Your feedback (optional)",
"reportContentIssueDescription": "Sorry! AI can make personalized experiences but also may have issues. Please provide any feedback you have and we'll generate a new activity."
"reportContentIssueDescription": "Sorry! AI can make personalized experiences but also may have issues. Please provide any feedback you have and we'll generate a new activity.",
"clickTheWordAgainToDeselect": "Click the selected word to deselect it."
}

View file

@ -4329,7 +4329,6 @@
"pleaseTryAgainLaterOrChooseDifferentServer": "",
"@pleaseTryAgainLaterOrChooseDifferentServer": {},
"createGroup": "",
"@createGroup": {},
"@noBackupWarning": {},
"kickUserDescription": "",
"@kickUserDescription": {},
@ -4490,7 +4489,6 @@
"translations": "traducciónes",
"messageAudio": "mensaje de audio",
"definitions": "definiciones",
"subscribedToUnlockTools": "Suscríbase para desbloquear herramientas lingüísticas, como",
"clickMessageTitle": "¿Necesitas ayuda?",
"clickMessageBody": "¡Lame un mensaje para obtener ayuda con el idioma! Haz clic y mantén presionado para reaccionar 😀",
"more": "Más",
@ -4686,7 +4684,6 @@
"fetchingVersion": "Obteniendo versión...",
"versionFetchError": "Error al obtener la versión",
"connectedToStaging": "Conectado al entorno de pruebas",
"connectedToStaging": "Conectado al entorno de pruebas",
"versionText": "Versión: {version}+{buildNumber}",
"@versionText": {
"description": "Texto que muestra la versión y el número de compilación de la aplicación.",
@ -4710,8 +4707,6 @@
},
"emojis": "Emojis",
"@emojis": {},
"createGroup": "Crear grupo",
"@createGroup": {},
"hydrateTorLong": "¿Exportó su sesión la última vez que estuvo en TOR? Impórtela rápidamente y continúe chateando.",
"@hydrateTorLong": {},
"custom": "Personalizado",
@ -4737,6 +4732,5 @@
}
},
"commandHint_googly": "Enviar unos ojos saltones",
"@commandHint_googly": {},
"reportContentIssue": "Problema de contenido"
}

View file

@ -552,6 +552,7 @@ class ChatController extends State<ChatPageWithRoom>
//#Pangea
choreographer.stateListener.close();
choreographer.dispose();
MatrixState.pAnyState.closeOverlay();
//Pangea#
super.dispose();
}

View file

@ -109,9 +109,14 @@ class Choreographer {
// if not, let's get the tokens again and log an error
if (igc.igcTextData?.tokens != null &&
PangeaToken.reconstructText(igc.igcTextData!.tokens) != currentText) {
debugger(when: kDebugMode);
if (kDebugMode) {
PangeaToken.reconstructText(
igc.igcTextData!.tokens,
debugWalkThrough: true,
);
}
ErrorHandler.logError(
m: "reconstructed text does not match current text",
m: "reconstructed text not working",
s: StackTrace.current,
data: {
"igcTextData": igc.igcTextData?.toJson(),

View file

@ -56,23 +56,10 @@ class ITController {
choreographer.setState();
}
bool _closingHint = false;
Duration get animationSpeed => (_closingHint || !_willOpen)
Duration get animationSpeed => (!_willOpen)
? const Duration(milliseconds: 500)
: const Duration(milliseconds: 2000);
void closeHint() {
_closingHint = true;
final String hintKey = InlineInstructions.translationChoices.toString();
final instructionsController = choreographer.pangeaController.instructions;
instructionsController.turnOffInstruction(hintKey);
instructionsController.updateEnableInstructions(hintKey, true);
choreographer.setState();
Future.delayed(const Duration(milliseconds: 500), () {
_closingHint = false;
});
}
Future<void> initializeIT(ITStartData itStartData) async {
_willOpen = true;
Future.delayed(const Duration(microseconds: 100), () {

View file

@ -49,12 +49,6 @@ class ITBarState extends State<ITBar> {
super.dispose();
}
bool get instructionsTurnedOff =>
widget.choreographer.pangeaController.instructions
.wereInstructionsTurnedOff(
InlineInstructions.translationChoices.toString(),
);
@override
Widget build(BuildContext context) {
return AnimatedSize(
@ -120,11 +114,12 @@ class ITBarState extends State<ITBar> {
// const SizedBox(height: 40.0),
OriginalText(controller: itController),
const SizedBox(height: 7.0),
if (!instructionsTurnedOff)
if (!InstructionsEnum.translationChoices
.toggledOff(context))
InlineTooltip(
body: InlineInstructions.translationChoices
.body(context),
onClose: itController.closeHint,
instructionsEnum:
InstructionsEnum.translationChoices,
onClose: () => setState(() {}),
),
IntrinsicHeight(
child: Container(

View file

@ -1,5 +1,9 @@
import 'dart:developer';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
@ -8,10 +12,22 @@ enum InstructionsEnum {
clickMessage,
blurMeansTranslate,
tooltipInstructions,
speechToText,
l1Translation,
translationChoices,
clickAgainToDeselect,
}
extension InstructionsEnumExtension on InstructionsEnum {
String title(BuildContext context) {
if (!context.mounted) {
ErrorHandler.logError(
e: Exception("Context not mounted"),
m: 'InstructionsEnumExtension.title for $this',
);
debugger(when: kDebugMode);
return '';
}
switch (this) {
case InstructionsEnum.itInstructions:
return L10n.of(context)!.itInstructionsTitle;
@ -21,10 +37,31 @@ extension InstructionsEnumExtension on InstructionsEnum {
return L10n.of(context)!.blurMeansTranslateTitle;
case InstructionsEnum.tooltipInstructions:
return L10n.of(context)!.tooltipInstructionsTitle;
case InstructionsEnum.clickAgainToDeselect:
case InstructionsEnum.speechToText:
case InstructionsEnum.l1Translation:
case InstructionsEnum.translationChoices:
ErrorHandler.logError(
e: Exception("No title for this instruction"),
m: 'InstructionsEnumExtension.title',
data: {
'this': this,
},
);
debugger(when: kDebugMode);
return "";
}
}
String body(BuildContext context) {
if (!context.mounted) {
ErrorHandler.logError(
e: Exception("Context not mounted"),
m: 'InstructionsEnumExtension.body for $this',
);
debugger(when: kDebugMode);
return "";
}
switch (this) {
case InstructionsEnum.itInstructions:
return L10n.of(context)!.itInstructionsBody;
@ -32,6 +69,14 @@ extension InstructionsEnumExtension on InstructionsEnum {
return L10n.of(context)!.clickMessageBody;
case InstructionsEnum.blurMeansTranslate:
return L10n.of(context)!.blurMeansTranslateBody;
case InstructionsEnum.speechToText:
return L10n.of(context)!.speechToTextBody;
case InstructionsEnum.l1Translation:
return L10n.of(context)!.l1TranslationBody;
case InstructionsEnum.translationChoices:
return L10n.of(context)!.translationChoicesBody;
case InstructionsEnum.clickAgainToDeselect:
return L10n.of(context)!.clickTheWordAgainToDeselect;
case InstructionsEnum.tooltipInstructions:
return PlatformInfos.isMobile
? L10n.of(context)!.tooltipInstructionsMobileBody
@ -39,7 +84,15 @@ extension InstructionsEnumExtension on InstructionsEnum {
}
}
bool get toggledOff {
bool toggledOff(BuildContext context) {
if (!context.mounted) {
ErrorHandler.logError(
e: Exception("Context not mounted"),
m: 'InstructionsEnumExtension.toggledOff for $this',
);
debugger(when: kDebugMode);
return false;
}
final instructionSettings =
MatrixState.pangeaController.userController.profile.instructionSettings;
switch (this) {
@ -51,38 +104,14 @@ extension InstructionsEnumExtension on InstructionsEnum {
return instructionSettings.showedBlurMeansTranslate;
case InstructionsEnum.tooltipInstructions:
return instructionSettings.showedTooltipInstructions;
}
}
}
enum InlineInstructions {
speechToText,
l1Translation,
translationChoices,
}
extension InlineInstructionsExtension on InlineInstructions {
String body(BuildContext context) {
switch (this) {
case InlineInstructions.speechToText:
return L10n.of(context)!.speechToTextBody;
case InlineInstructions.l1Translation:
return L10n.of(context)!.l1TranslationBody;
case InlineInstructions.translationChoices:
return L10n.of(context)!.translationChoicesBody;
}
}
bool get toggledOff {
final instructionSettings =
MatrixState.pangeaController.userController.profile.instructionSettings;
switch (this) {
case InlineInstructions.speechToText:
case InstructionsEnum.speechToText:
return instructionSettings.showedSpeechToTextTooltip;
case InlineInstructions.l1Translation:
case InstructionsEnum.l1Translation:
return instructionSettings.showedL1TranslationTooltip;
case InlineInstructions.translationChoices:
case InstructionsEnum.translationChoices:
return instructionSettings.showedTranslationChoicesTooltip;
case InstructionsEnum.clickAgainToDeselect:
return instructionSettings.showedClickAgainToDeselect;
}
}
}

View file

@ -117,6 +117,7 @@ class IGCTextData {
) async {
//should be already added to choreoRecord
//TODO - that should be done in the same function to avoid error potential
final PangeaMatch pangeaMatch = matches[matchIndex];
if (pangeaMatch.match.choices == null) {
@ -147,8 +148,8 @@ class IGCTextData {
// for all tokens after the replacement, update their offsets
for (int i = endIndex; i < tokens.length; i++) {
final PangeaToken token = tokens[i];
token.text.offset += replacement.value.length - pangeaMatch.match.length;
tokens[i].text.offset +=
replacement.value.length - pangeaMatch.match.length;
}
// clone the list for debugging purposes
@ -159,10 +160,16 @@ class IGCTextData {
final String newFullText = PangeaToken.reconstructText(newTokens);
if (newFullText != originalInput) {
debugger(when: kDebugMode);
if (newFullText != originalInput && kDebugMode) {
PangeaToken.reconstructText(newTokens, debugWalkThrough: true);
ErrorHandler.logError(
m: "reconstructed text does not match original input",
m: "reconstructed text not working",
s: StackTrace.current,
data: {
"originalInput": originalInput,
"newFullText": newFullText,
"match": pangeaMatch.match.toJson(),
},
);
}

View file

@ -28,11 +28,17 @@ class PangeaToken {
required this.morph,
});
/// reconstructs the text from the tokens
/// [tokens] - the tokens to reconstruct
/// [debugWalkThrough] - if true, will start the debugger
static String reconstructText(
List<PangeaToken> tokens, [
List<PangeaToken> tokens, {
bool debugWalkThrough = false,
int startTokenIndex = 0,
int endTokenIndex = -1,
]) {
}) {
debugger(when: kDebugMode && debugWalkThrough);
if (endTokenIndex == -1) {
endTokenIndex = tokens.length;
}
@ -55,7 +61,6 @@ class PangeaToken {
(i > 0 ? (subset[i - 1].text.offset + subset[i - 1].text.length) : 0);
if (whitespace < 0) {
debugger(when: kDebugMode);
whitespace = 0;
}
reconstruction += ' ' * whitespace + subset[i].text.content;

View file

@ -189,6 +189,7 @@ class UserInstructions {
bool showedSpeechToTextTooltip;
bool showedL1TranslationTooltip;
bool showedTranslationChoicesTooltip;
bool showedClickAgainToDeselect;
UserInstructions({
this.showedItInstructions = false,
@ -198,12 +199,12 @@ class UserInstructions {
this.showedSpeechToTextTooltip = false,
this.showedL1TranslationTooltip = false,
this.showedTranslationChoicesTooltip = false,
this.showedClickAgainToDeselect = false,
});
factory UserInstructions.fromJson(Map<String, dynamic> json) =>
UserInstructions(
showedItInstructions:
json[InstructionsEnum.itInstructions.toString()] ?? false,
showedItInstructions: json[InstructionsEnum.itInstructions.toString()],
showedClickMessage:
json[InstructionsEnum.clickMessage.toString()] ?? false,
showedBlurMeansTranslate:
@ -211,11 +212,13 @@ class UserInstructions {
showedTooltipInstructions:
json[InstructionsEnum.tooltipInstructions.toString()] ?? false,
showedL1TranslationTooltip:
json[InlineInstructions.l1Translation.toString()] ?? false,
json[InstructionsEnum.l1Translation.toString()] ?? false,
showedTranslationChoicesTooltip:
json[InlineInstructions.translationChoices.toString()] ?? false,
json[InstructionsEnum.translationChoices.toString()] ?? false,
showedSpeechToTextTooltip:
json[InlineInstructions.speechToText.toString()] ?? false,
json[InstructionsEnum.speechToText.toString()] ?? false,
showedClickAgainToDeselect:
json[InstructionsEnum.clickAgainToDeselect.toString()] ?? false,
);
Map<String, dynamic> toJson() {
@ -226,12 +229,13 @@ class UserInstructions {
showedBlurMeansTranslate;
data[InstructionsEnum.tooltipInstructions.toString()] =
showedTooltipInstructions;
data[InlineInstructions.l1Translation.toString()] =
data[InstructionsEnum.l1Translation.toString()] =
showedL1TranslationTooltip;
data[InlineInstructions.translationChoices.toString()] =
data[InstructionsEnum.translationChoices.toString()] =
showedTranslationChoicesTooltip;
data[InlineInstructions.speechToText.toString()] =
showedSpeechToTextTooltip;
data[InstructionsEnum.speechToText.toString()] = showedSpeechToTextTooltip;
data[InstructionsEnum.clickAgainToDeselect.toString()] =
showedClickAgainToDeselect;
return data;
}
@ -258,20 +262,25 @@ class UserInstructions {
as bool?) ??
false,
showedL1TranslationTooltip:
(accountData[InlineInstructions.l1Translation.toString()]
?.content[InlineInstructions.l1Translation.toString()]
(accountData[InstructionsEnum.l1Translation.toString()]
?.content[InstructionsEnum.l1Translation.toString()]
as bool?) ??
false,
showedTranslationChoicesTooltip: (accountData[
InlineInstructions.translationChoices.toString()]
?.content[InlineInstructions.translationChoices.toString()]
showedTranslationChoicesTooltip:
(accountData[InstructionsEnum.translationChoices.toString()]
?.content[InstructionsEnum.translationChoices.toString()]
as bool?) ??
false,
showedSpeechToTextTooltip:
(accountData[InstructionsEnum.speechToText.toString()]
?.content[InstructionsEnum.speechToText.toString()]
as bool?) ??
false,
showedClickAgainToDeselect: (accountData[
InstructionsEnum.clickAgainToDeselect.toString()]
?.content[InstructionsEnum.clickAgainToDeselect.toString()]
as bool?) ??
false,
showedSpeechToTextTooltip:
(accountData[InlineInstructions.speechToText.toString()]
?.content[InlineInstructions.speechToText.toString()]
as bool?) ??
false,
);
}
}

View file

@ -1,60 +1,71 @@
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/enum/instructions_enum.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
class InlineTooltip extends StatelessWidget {
final String body;
final InstructionsEnum instructionsEnum;
final VoidCallback onClose;
const InlineTooltip({
super.key,
required this.body,
required this.instructionsEnum,
required this.onClose,
});
@override
Widget build(BuildContext context) {
return Badge(
offset: const Offset(0, -7),
backgroundColor: Colors.transparent,
label: CircleAvatar(
radius: 10,
child: IconButton(
padding: EdgeInsets.zero,
icon: const Icon(
Icons.close_outlined,
size: 15,
),
onPressed: onClose,
),
),
if (instructionsEnum.toggledOff(context)) {
return const SizedBox();
}
return Padding(
padding: const EdgeInsets.all(8.0),
child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
color: Theme.of(context).colorScheme.primary.withAlpha(20),
),
child: Padding(
padding: const EdgeInsets.all(10),
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
children: [
const WidgetSpan(
child: Icon(
Icons.lightbulb,
size: 16,
),
),
const WidgetSpan(
child: SizedBox(width: 5),
),
TextSpan(
text: body,
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Lightbulb icon on the left
Icon(
Icons.lightbulb,
size: 20,
color: Theme.of(context).colorScheme.onSurface,
),
const SizedBox(width: 8),
// Text in the middle
Expanded(
child: Text(
instructionsEnum.body(context),
style: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
height: 1.5,
),
textAlign: TextAlign.left,
),
],
),
),
// Close button on the right
IconButton(
constraints: const BoxConstraints(),
icon: Icon(
Icons.close_outlined,
size: 20,
color: Theme.of(context).colorScheme.onSurface,
),
onPressed: () {
MatrixState.pangeaController.instructions.setToggledOff(
instructionsEnum,
true,
);
onClose();
},
),
],
),
),
),

View file

@ -1,6 +1,4 @@
import 'package:collection/collection.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';
@ -24,54 +22,40 @@ class InstructionsController {
/// Instruction popup has already been shown this session
final Map<String, bool> _instructionsShown = {};
/// Returns true if the user requested this popup not be shown again
bool? toggledOff(String key) {
final bool? instruction = InstructionsEnum.values
.firstWhereOrNull((value) => value.toString() == key)
?.toggledOff;
final bool? tooltip = InlineInstructions.values
.firstWhereOrNull((value) => value.toString() == key)
?.toggledOff;
return instruction ?? tooltip;
}
InstructionsController(PangeaController pangeaController) {
_pangeaController = pangeaController;
}
/// Returns true if the instructions were closed
/// or turned off by the user via the toggle switch
bool wereInstructionsTurnedOff(String key) {
return toggledOff(key) ?? _instructionsClosed[key] ?? false;
}
void turnOffInstruction(String key) => _instructionsClosed[key] = true;
void updateEnableInstructions(
String key,
void setToggledOff(
InstructionsEnum key,
bool value,
) {
_pangeaController.userController.updateProfile((profile) {
if (key == InstructionsEnum.itInstructions.toString()) {
profile.instructionSettings.showedItInstructions = value;
}
if (key == InstructionsEnum.clickMessage.toString()) {
profile.instructionSettings.showedClickMessage = value;
}
if (key == InstructionsEnum.blurMeansTranslate.toString()) {
profile.instructionSettings.showedBlurMeansTranslate = value;
}
if (key == InstructionsEnum.tooltipInstructions.toString()) {
profile.instructionSettings.showedTooltipInstructions = value;
}
if (key == InlineInstructions.speechToText.toString()) {
profile.instructionSettings.showedSpeechToTextTooltip = value;
}
if (key == InlineInstructions.l1Translation.toString()) {
profile.instructionSettings.showedL1TranslationTooltip = value;
}
if (key == InlineInstructions.translationChoices.toString()) {
profile.instructionSettings.showedTranslationChoicesTooltip = value;
switch (key) {
case InstructionsEnum.speechToText:
profile.instructionSettings.showedSpeechToTextTooltip = value;
break;
case InstructionsEnum.l1Translation:
profile.instructionSettings.showedL1TranslationTooltip = value;
break;
case InstructionsEnum.translationChoices:
profile.instructionSettings.showedTranslationChoicesTooltip = value;
break;
case InstructionsEnum.tooltipInstructions:
profile.instructionSettings.showedTooltipInstructions = value;
break;
case InstructionsEnum.itInstructions:
profile.instructionSettings.showedItInstructions = value;
break;
case InstructionsEnum.clickMessage:
profile.instructionSettings.showedClickMessage = value;
break;
case InstructionsEnum.blurMeansTranslate:
profile.instructionSettings.showedBlurMeansTranslate = value;
break;
case InstructionsEnum.clickAgainToDeselect:
profile.instructionSettings.showedClickAgainToDeselect = value;
break;
}
return profile;
});
@ -90,7 +74,7 @@ class InstructionsController {
}
_instructionsShown[key.toString()] = true;
if (wereInstructionsTurnedOff(key.toString())) {
if (key.toggledOff(context)) {
return;
}
if (L10n.of(context) == null) {
@ -142,31 +126,6 @@ class InstructionsController {
),
);
}
/// Returns a widget that will be added to existing widget
/// which displays hint text defined in the enum extension
Widget getInstructionInlineTooltip(
BuildContext context,
InlineInstructions key,
VoidCallback onClose,
) {
if (wereInstructionsTurnedOff(key.toString())) {
return const SizedBox();
}
if (L10n.of(context) == null) {
ErrorHandler.logError(
m: "null context in ITBotButton.showCard",
s: StackTrace.current,
);
return const SizedBox();
}
return InlineTooltip(
body: InlineInstructions.speechToText.body(context),
onClose: onClose,
);
}
}
/// User can toggle on to prevent Instruction Card
@ -196,12 +155,10 @@ class InstructionsToggleState extends State<InstructionsToggle> {
return SwitchListTile.adaptive(
activeColor: AppConfig.activeToggleColor,
title: Text(L10n.of(context)!.doNotShowAgain),
value: pangeaController.instructions.wereInstructionsTurnedOff(
widget.instructionsKey.toString(),
),
value: widget.instructionsKey.toggledOff(context),
onChanged: ((value) async {
pangeaController.instructions.updateEnableInstructions(
widget.instructionsKey.toString(),
pangeaController.instructions.setToggledOff(
widget.instructionsKey,
value,
);
setState(() {});

View file

@ -91,7 +91,7 @@ class MatchCopy {
}
final String afterColon = splits.join();
print("grammar rule ${match.match.rule!.id}");
debugPrint("grammar rule ${match.match.rule!.id}");
switch (afterColon) {
case MatchRuleIds.interactiveTranslation:

View file

@ -1,6 +1,7 @@
import 'package:fluffychat/pages/chat/events/audio_player.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.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/toolbar_content_loading_indicator.dart';
import 'package:fluffychat/pangea/widgets/igc/card_error_widget.dart';
import 'package:flutter/material.dart';
@ -9,10 +10,12 @@ import 'package:matrix/matrix.dart';
class MessageAudioCard extends StatefulWidget {
final PangeaMessageEvent messageEvent;
final MessageOverlayController overlayController;
const MessageAudioCard({
super.key,
required this.messageEvent,
required this.overlayController,
});
@override
@ -70,6 +73,12 @@ class MessageAudioCardState extends State<MessageAudioCard> {
@override
void initState() {
super.initState();
//once we have audio for words, we'll play that
if (widget.overlayController.isSelection) {
widget.overlayController.clearSelection();
}
fetchAudio();
}
@ -80,20 +89,15 @@ class MessageAudioCardState extends State<MessageAudioCard> {
child: _isLoading
? const ToolbarContentLoadingIndicator()
: localAudioEvent != null || audioFile != null
? Container(
constraints: const BoxConstraints(
maxWidth: 250,
),
child: Column(
children: [
AudioPlayerWidget(
localAudioEvent,
color: Theme.of(context).colorScheme.onPrimaryContainer,
matrixFile: audioFile,
autoplay: true,
),
],
),
? Column(
children: [
AudioPlayerWidget(
localAudioEvent,
color: Theme.of(context).colorScheme.onPrimaryContainer,
matrixFile: audioFile,
autoplay: true,
),
],
)
: const CardErrorWidget(),
);

View file

@ -87,7 +87,6 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
/// In some cases, we need to exit the practice flow and let the user
/// interact with the toolbar without completing activities
void exitPracticeFlow() {
debugPrint('Exiting practice flow');
clearSelection();
activitiesLeftToComplete = 0;
setState(() {});

View file

@ -4,6 +4,7 @@ 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/inline_tooltip.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';
@ -66,13 +67,6 @@ class MessageSpeechToTextCardState extends State<MessageSpeechToTextCard> {
}
void closeHint() {
MatrixState.pangeaController.instructions.turnOffInstruction(
InlineInstructions.speechToText.toString(),
);
MatrixState.pangeaController.instructions.updateEnableInstructions(
InlineInstructions.speechToText.toString(),
true,
);
setState(() {});
}
@ -183,33 +177,18 @@ class MessageSpeechToTextCardState extends State<MessageSpeechToTextCard> {
number:
"${selectedToken?.confidence ?? speechToTextResponse!.transcript.confidence}%",
toolTip: L10n.of(context)!.accuracy,
onPressed: () => MatrixState.pangeaController.instructions
.showInstructionsPopup(
context,
InstructionsEnum.tooltipInstructions,
widget.messageEvent.eventId,
true,
),
),
IconNumberWidget(
icon: Icons.speed,
number:
wordsPerMinuteString != null ? "$wordsPerMinuteString" : "??",
toolTip: L10n.of(context)!.wordsPerMinute,
onPressed: () => MatrixState.pangeaController.instructions
.showInstructionsPopup(
context,
InstructionsEnum.tooltipInstructions,
widget.messageEvent.eventId,
true,
),
),
],
),
MatrixState.pangeaController.instructions.getInstructionInlineTooltip(
context,
InlineInstructions.speechToText,
closeHint,
InlineTooltip(
instructionsEnum: InstructionsEnum.speechToText,
onClose: () => setState(() => {}),
),
],
);

View file

@ -43,9 +43,7 @@ class MessageToolbarState extends State<MessageToolbar> {
if (!subscribed) {
return MessageUnsubscribedCard(
languageTool: widget.overLayController.toolbarMode.title(context),
mode: widget.overLayController.toolbarMode,
controller: this,
controller: widget.overLayController,
);
}
@ -58,6 +56,7 @@ class MessageToolbarState extends State<MessageToolbar> {
case MessageMode.textToSpeech:
return MessageAudioCard(
messageEvent: widget.pangeaMessageEvent,
overlayController: widget.overLayController,
);
case MessageMode.speechToText:
return MessageSpeechToTextCard(

View file

@ -32,14 +32,13 @@ class MessageTranslationCardState extends State<MessageTranslationCard> {
@override
void initState() {
print('MessageTranslationCard initState');
debugPrint('MessageTranslationCard initState');
super.initState();
loadTranslation();
}
@override
void didUpdateWidget(covariant MessageTranslationCard oldWidget) {
// debugger(when: kDebugMode);
if (oldWidget.selection != widget.selection) {
debugPrint('selection changed');
loadTranslation();
@ -48,7 +47,6 @@ class MessageTranslationCardState extends State<MessageTranslationCard> {
}
Future<void> fetchRepresentationText() async {
// debugger(when: kDebugMode);
if (l1Code == null) return;
repEvent = widget.messageEvent
@ -114,36 +112,21 @@ class MessageTranslationCardState extends State<MessageTranslationCard> {
String? get l2Code =>
MatrixState.pangeaController.languageController.activeL2Code();
void closeHint() {
MatrixState.pangeaController.instructions.turnOffInstruction(
InlineInstructions.l1Translation.toString(),
);
MatrixState.pangeaController.instructions.updateEnableInstructions(
InlineInstructions.l1Translation.toString(),
true,
);
setState(() {});
}
/// Show warning if message's language code is user's L1
/// or if translated text is same as original text.
/// Warning does not show if was previously closed
bool get showWarning {
if (MatrixState.pangeaController.instructions.wereInstructionsTurnedOff(
InlineInstructions.l1Translation.toString(),
)) return false;
bool get notGoingToTranslate {
final bool isWrittenInL1 =
l1Code != null && widget.messageEvent.originalSent?.langCode == l1Code;
final bool isTextIdentical = selectionTranslation != null &&
widget.messageEvent.originalSent?.text == selectionTranslation;
return (isWrittenInL1 || isTextIdentical) && widget.messageEvent.ownMessage;
return (isWrittenInL1 || isTextIdentical);
}
@override
Widget build(BuildContext context) {
print('MessageTranslationCard build');
debugPrint('MessageTranslationCard build');
if (!_fetchingTranslation &&
repEvent == null &&
selectionTranslation == null) {
@ -165,11 +148,15 @@ class MessageTranslationCardState extends State<MessageTranslationCard> {
repEvent!.text,
style: BotStyle.text(context),
),
const SizedBox(height: 10),
if (showWarning)
if (notGoingToTranslate && widget.selection == null)
InlineTooltip(
body: InlineInstructions.l1Translation.body(context),
onClose: closeHint,
instructionsEnum: InstructionsEnum.l1Translation,
onClose: () => setState(() {}),
),
if (widget.selection != null)
InlineTooltip(
instructionsEnum: InstructionsEnum.clickAgainToDeselect,
onClose: () => setState(() {}),
),
// if (widget.selection != null)
],

View file

@ -1,21 +1,15 @@
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/utils/bot_style.dart';
import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart';
import 'package:fluffychat/pangea/widgets/chat/message_selection_overlay.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import '../../enum/message_mode_enum.dart';
class MessageUnsubscribedCard extends StatelessWidget {
final String languageTool;
final MessageMode mode;
final MessageToolbarState controller;
final MessageOverlayController controller;
const MessageUnsubscribedCard({
super.key,
required this.languageTool,
required this.mode,
required this.controller,
});
@ -24,42 +18,52 @@ class MessageUnsubscribedCard extends StatelessWidget {
final bool inTrialWindow =
MatrixState.pangeaController.userController.inTrialWindow;
void onButtonPress() {
if (inTrialWindow) {
MatrixState.pangeaController.subscriptionController
.activateNewUserTrial();
controller.widget.overLayController.updateToolbarMode(mode);
} else {
MatrixState.pangeaController.subscriptionController
.showPaywall(context);
}
}
return Column(
children: [
Text(
style: BotStyle.text(context),
"${L10n.of(context)!.subscribedToUnlockTools} $languageTool",
textAlign: TextAlign.center,
),
const SizedBox(height: 10),
SizedBox(
width: double.infinity,
child: TextButton(
onPressed: onButtonPress,
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(
(AppConfig.primaryColor).withOpacity(0.1),
return Container(
padding: const EdgeInsets.all(8),
child: Column(
children: [
Text(
style: BotStyle.text(context),
L10n.of(context)!.subscribedToUnlockTools,
textAlign: TextAlign.center,
),
if (inTrialWindow) ...[
const SizedBox(height: 10),
SizedBox(
width: double.infinity,
child: TextButton(
onPressed: () {
MatrixState.pangeaController.subscriptionController
.activateNewUserTrial();
controller.updateToolbarMode(controller.toolbarMode);
},
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(
(AppConfig.primaryColor).withOpacity(0.1),
),
),
child: Text(L10n.of(context)!.activateTrial),
),
),
child: Text(
inTrialWindow
? L10n.of(context)!.activateTrial
: L10n.of(context)!.getAccess,
],
const SizedBox(height: 10),
SizedBox(
width: double.infinity,
child: TextButton(
onPressed: () {
MatrixState.pangeaController.subscriptionController
.showPaywall(context);
},
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(
(AppConfig.primaryColor).withOpacity(0.1),
),
),
child: Text(L10n.of(context)!.getAccess),
),
),
),
],
],
),
);
}
}

View file

@ -98,7 +98,7 @@ class OverlayMessageTextState extends State<OverlayMessageText> {
return TextSpan(
recognizer: TapGestureRecognizer()
..onTap = () {
print(
debugPrint(
'tokenPosition.tokenIndex: ${tokenPosition.tokenIndex}',
);
widget.overlayController.onClickOverlayMessageToken(

View file

@ -6,7 +6,7 @@ class IconNumberWidget extends StatelessWidget {
final Color? iconColor;
final double? iconSize;
final String? toolTip;
final VoidCallback onPressed;
final VoidCallback? onPressed;
const IconNumberWidget({
super.key,
@ -15,7 +15,7 @@ class IconNumberWidget extends StatelessWidget {
this.toolTip,
this.iconColor,
this.iconSize,
required this.onPressed,
this.onPressed,
});
Widget _content(BuildContext context) {

View file

@ -411,7 +411,6 @@ class SelectToDefine extends StatelessWidget {
return Center(
child: Container(
height: 80,
width: 200,
padding: const EdgeInsets.all(8),
child: Center(
child: Text(

View file

@ -413,10 +413,11 @@ class MessagePracticeActivityCardState extends State<PracticeActivityCard> {
top: 0,
right: 0,
child: Opacity(
opacity: 0.8, // Slight opacity
opacity: 0.65, // Slight opacity
child: Tooltip(
message: L10n.of(context)!.reportContentIssueTitle,
child: IconButton(
padding: const EdgeInsets.all(2),
icon: const Icon(Icons.flag),
iconSize: 16,
onPressed: () =>

View file

@ -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.4+3537
version: 1.21.4+3538
environment:
sdk: ">=3.0.0 <4.0.0"