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:
parent
b8edf595ca
commit
b7ab6038ac
24 changed files with 292 additions and 319 deletions
|
|
@ -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."
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -552,6 +552,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
//#Pangea
|
||||
choreographer.stateListener.close();
|
||||
choreographer.dispose();
|
||||
MatrixState.pAnyState.closeOverlay();
|
||||
//Pangea#
|
||||
super.dispose();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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), () {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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(() {});
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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(() {});
|
||||
|
|
|
|||
|
|
@ -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(() => {}),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
],
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ class OverlayMessageTextState extends State<OverlayMessageText> {
|
|||
return TextSpan(
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
print(
|
||||
debugPrint(
|
||||
'tokenPosition.tokenIndex: ${tokenPosition.tokenIndex}',
|
||||
);
|
||||
widget.overlayController.onClickOverlayMessageToken(
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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: () =>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue