dev/feat: refactor of instructions, tweaking of morph/lemma edits
This commit is contained in:
parent
7054d92532
commit
bbc791b314
23 changed files with 434 additions and 482 deletions
|
|
@ -4660,7 +4660,6 @@
|
|||
"publicProfileTitle": "Allow my profile to be found in search",
|
||||
"publicProfileDesc": "By enabling this option, I confirm that I am of legal age in my country of residence",
|
||||
"clickWordsInstructions": "Click on individual words for more activities.",
|
||||
"chooseBestDefinition": "What does this word mean?",
|
||||
"chooseBaseForm": "Choose the base form",
|
||||
"notTheCodeError": "Sorry, that's not the code!",
|
||||
"totalXP": "Total XP",
|
||||
|
|
@ -4700,11 +4699,43 @@
|
|||
"downloading": "Downloading...",
|
||||
"failedFetchUserAnalytics": "Failed to download user analytics",
|
||||
"downloadComplete": "Download complete!",
|
||||
"editMorphologicalLabel": "Pangea Bot makes mistakes too! What should this label be?",
|
||||
"whatIsTheMorphTag": "What is the {morphologicalFeature} of '{wordForm}'?",
|
||||
"@whatIsTheMorphTag": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"morphologicalFeature": {},
|
||||
"wordForm": {}
|
||||
}
|
||||
},
|
||||
"dataAvailable": "Data availability",
|
||||
"lemmasNeverUsedCorrectly": "Number of lemmas used correctly 0 times",
|
||||
"available": "Available",
|
||||
"unavailable": "Unavailable",
|
||||
"accessingMemberAnalytics": "Accessing member analytics...",
|
||||
"editLemmaMeaning": "Pangea Bot makes mistakes too! What should be the definition of this lemma?"
|
||||
"pangeaBotIsFallible": "Pangea Bot makes mistakes too!",
|
||||
"whatIsMeaning": "What does '{lemma}' mean?",
|
||||
"@whatIsMeaning": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"lemma": {},
|
||||
"partOfSpeech": {}
|
||||
}
|
||||
},
|
||||
"pickAnEmoji": "What's your favorite emoji for '{lemma}'?",
|
||||
"@pickAnEmoji": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"lemma": {},
|
||||
"partOfSpeech": {}
|
||||
}
|
||||
},
|
||||
"lemmaMeaningInstructionsBody": "Above is the meaning of the lemma. Double-click to edit.",
|
||||
"doubleClickToEdit": "Double-click to edit.",
|
||||
"removeFeature": "Remove {feature}",
|
||||
"@removeFeature": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"feature": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,3 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:scroll_to_index/scroll_to_index.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
|
|
@ -10,11 +5,15 @@ 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/instructions/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_show_popup.dart';
|
||||
import 'package:fluffychat/utils/account_config.dart';
|
||||
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
|
||||
import 'package:fluffychat/utils/matrix_sdk_extensions/filtered_timeline_extension.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:scroll_to_index/scroll_to_index.dart';
|
||||
|
||||
class ChatEventList extends StatelessWidget {
|
||||
final ChatController controller;
|
||||
|
|
@ -59,7 +58,7 @@ class ChatEventList extends StatelessWidget {
|
|||
)
|
||||
.toList();
|
||||
if (msgEvents.isEmpty) return;
|
||||
controller.pangeaController.instructions.showInstructionsPopup(
|
||||
instructionsShowPopup(
|
||||
context,
|
||||
InstructionsEnum.clickMessage,
|
||||
msgEvents[0].eventId,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/it_controller.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/it_bar_buttons.dart';
|
||||
|
|
@ -12,11 +9,14 @@ import 'package:fluffychat/pangea/choreographer/widgets/translation_finished_flo
|
|||
import 'package:fluffychat/pangea/constants/choreo_constants.dart';
|
||||
import 'package:fluffychat/pangea/controllers/put_analytics_controller.dart';
|
||||
import 'package:fluffychat/pangea/enum/construct_use_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/enum/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart';
|
||||
import 'package:fluffychat/pangea/pages/settings_learning/settings_learning.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/utils/inline_tooltip.dart';
|
||||
import 'package:fluffychat/pangea/widgets/animations/gain_points.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../controllers/it_feedback_controller.dart';
|
||||
import '../../models/it_response_model.dart';
|
||||
import '../../utils/overlay.dart';
|
||||
|
|
@ -67,7 +67,7 @@ class ITBarState extends State<ITBar> with SingleTickerProviderStateMixin {
|
|||
}
|
||||
|
||||
bool get showITInstructionsTooltip {
|
||||
final toggledOff = InstructionsEnum.clickBestOption.toggledOff();
|
||||
final toggledOff = InstructionsEnum.clickBestOption.isToggledOff;
|
||||
if (!toggledOff) {
|
||||
setState(() => showedClickInstruction = true);
|
||||
}
|
||||
|
|
@ -205,11 +205,11 @@ class ITBarState extends State<ITBar> with SingleTickerProviderStateMixin {
|
|||
),
|
||||
const SizedBox(height: 8.0),
|
||||
if (showITInstructionsTooltip)
|
||||
const InlineTooltip(
|
||||
const InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.clickBestOption,
|
||||
),
|
||||
if (showTranslationsChoicesTooltip)
|
||||
const InlineTooltip(
|
||||
const InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.translationChoices,
|
||||
),
|
||||
Container(
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_show_popup.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/enum/instructions_enum.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 +38,7 @@ class ITBotButton extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
choreographer.pangeaController.instructions.showInstructionsPopup(
|
||||
instructionsShowPopup(
|
||||
context,
|
||||
InstructionsEnum.itInstructions,
|
||||
choreographer.itBotTransformTargetKey,
|
||||
|
|
@ -45,8 +46,7 @@ class ITBotButton extends StatelessWidget {
|
|||
|
||||
return IconButton(
|
||||
icon: const BotFace(width: 40.0, expression: BotExpression.idle),
|
||||
onPressed: () =>
|
||||
choreographer.pangeaController.instructions.showInstructionsPopup(
|
||||
onPressed: () => instructionsShowPopup(
|
||||
context,
|
||||
InstructionsEnum.itInstructions,
|
||||
choreographer.itBotTransformTargetKey,
|
||||
|
|
|
|||
|
|
@ -2,11 +2,6 @@ import 'dart:async';
|
|||
import 'dart:developer';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/constants/bot_mode.dart';
|
||||
import 'package:fluffychat/pangea/constants/class_default_values.dart';
|
||||
import 'package:fluffychat/pangea/constants/pangea_event_types.dart';
|
||||
|
|
@ -30,8 +25,11 @@ import 'package:fluffychat/pangea/guard/p_vguard.dart';
|
|||
import 'package:fluffychat/pangea/models/bot_options_model.dart';
|
||||
import 'package:fluffychat/pangea/utils/bot_name.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/utils/instructions.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
import '../../config/app_config.dart';
|
||||
import '../utils/firebase_analytics.dart';
|
||||
import '../utils/p_store.dart';
|
||||
|
|
@ -51,7 +49,6 @@ class PangeaController {
|
|||
// TODO: make these static so we can remove from here
|
||||
late ContextualDefinitionController definitions;
|
||||
late ITFeedbackController itFeedback;
|
||||
late InstructionsController instructions;
|
||||
late SubscriptionController subscriptionController;
|
||||
late TextToSpeechController textToSpeech;
|
||||
late SpeechToTextController speechToText;
|
||||
|
|
@ -106,7 +103,6 @@ class PangeaController {
|
|||
messageData = MessageDataController(this);
|
||||
wordNet = WordController(this);
|
||||
definitions = ContextualDefinitionController(this);
|
||||
instructions = InstructionsController(this);
|
||||
subscriptionController = SubscriptionController(this);
|
||||
itFeedback = ITFeedbackController(this);
|
||||
textToSpeech = TextToSpeechController(this);
|
||||
|
|
|
|||
53
lib/pangea/instructions/instruction_settings.dart
Normal file
53
lib/pangea/instructions/instruction_settings.dart
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
/// The user's settings for whether or not to show instuction messages.
|
||||
class InstructionSettings {
|
||||
Map<String, bool> _instructions = {};
|
||||
|
||||
InstructionSettings([Map<String, bool>? instructions]) {
|
||||
if (instructions != null) {
|
||||
_instructions = instructions;
|
||||
} else {
|
||||
for (final key in InstructionsEnum.values) {
|
||||
_instructions[key.toString()] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
factory InstructionSettings.fromJson(Map<String, dynamic> json) {
|
||||
final Map<String, bool> instructions = {};
|
||||
for (final key in InstructionsEnum.values) {
|
||||
instructions[key.toString()] = json[key.toString()] ?? false;
|
||||
}
|
||||
return InstructionSettings(instructions);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
for (final key in InstructionsEnum.values) {
|
||||
data[key.toString()] = _instructions[key.toString()];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
factory InstructionSettings.migrateFromAccountData() {
|
||||
final accountData =
|
||||
MatrixState.pangeaController.matrixState.client.accountData;
|
||||
final Map<String, bool> instructions = {};
|
||||
for (final key in InstructionsEnum.values) {
|
||||
instructions[key.toString()] =
|
||||
(accountData[key.toString()]?.content[key.toString()] as bool?) ??
|
||||
false;
|
||||
}
|
||||
return InstructionSettings(instructions);
|
||||
}
|
||||
|
||||
bool getStatus(InstructionsEnum instruction) {
|
||||
return _instructions[instruction.toString()] ?? false;
|
||||
}
|
||||
|
||||
void setStatus(InstructionsEnum instruction, bool status) {
|
||||
_instructions[instruction.toString()] = status;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,10 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
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_gen/gen_l10n/l10n.dart';
|
||||
|
||||
enum InstructionsEnum {
|
||||
itInstructions,
|
||||
|
|
@ -20,6 +18,7 @@ enum InstructionsEnum {
|
|||
missingVoice,
|
||||
clickBestOption,
|
||||
unlockedLanguageTools,
|
||||
lemmaMeaning,
|
||||
}
|
||||
|
||||
extension InstructionsEnumExtension on InstructionsEnum {
|
||||
|
|
@ -41,6 +40,7 @@ extension InstructionsEnumExtension on InstructionsEnum {
|
|||
case InstructionsEnum.translationChoices:
|
||||
case InstructionsEnum.clickBestOption:
|
||||
case InstructionsEnum.unlockedLanguageTools:
|
||||
case InstructionsEnum.lemmaMeaning:
|
||||
ErrorHandler.logError(
|
||||
e: Exception("No title for this instruction"),
|
||||
m: 'InstructionsEnumExtension.title',
|
||||
|
|
@ -79,35 +79,18 @@ extension InstructionsEnumExtension on InstructionsEnum {
|
|||
return l10n.clickBestOption;
|
||||
case InstructionsEnum.unlockedLanguageTools:
|
||||
return l10n.unlockedLanguageTools;
|
||||
case InstructionsEnum.lemmaMeaning:
|
||||
return l10n.lemmaMeaningInstructionsBody;
|
||||
}
|
||||
}
|
||||
|
||||
bool toggledOff() {
|
||||
final instructionSettings =
|
||||
MatrixState.pangeaController.userController.profile.instructionSettings;
|
||||
switch (this) {
|
||||
case InstructionsEnum.itInstructions:
|
||||
return instructionSettings.showedItInstructions;
|
||||
case InstructionsEnum.clickMessage:
|
||||
return instructionSettings.showedClickMessage;
|
||||
case InstructionsEnum.blurMeansTranslate:
|
||||
return instructionSettings.showedBlurMeansTranslate;
|
||||
case InstructionsEnum.tooltipInstructions:
|
||||
return instructionSettings.showedTooltipInstructions;
|
||||
case InstructionsEnum.speechToText:
|
||||
return instructionSettings.showedSpeechToTextTooltip;
|
||||
case InstructionsEnum.l1Translation:
|
||||
return instructionSettings.showedL1TranslationTooltip;
|
||||
case InstructionsEnum.translationChoices:
|
||||
return instructionSettings.showedTranslationChoicesTooltip;
|
||||
case InstructionsEnum.clickAgainToDeselect:
|
||||
return instructionSettings.showedClickAgainToDeselect;
|
||||
case InstructionsEnum.missingVoice:
|
||||
return instructionSettings.showedMissingVoice;
|
||||
case InstructionsEnum.clickBestOption:
|
||||
return instructionSettings.showedClickBestOption;
|
||||
case InstructionsEnum.unlockedLanguageTools:
|
||||
return instructionSettings.showedUnlockedLanguageTools;
|
||||
}
|
||||
}
|
||||
bool get isToggledOff =>
|
||||
MatrixState.pangeaController.userController.profile.instructionSettings
|
||||
.getStatus(this);
|
||||
|
||||
void setToggledOff(bool value) =>
|
||||
MatrixState.pangeaController.userController.updateProfile((profile) {
|
||||
profile.instructionSettings.setStatus(this, value);
|
||||
return profile;
|
||||
});
|
||||
}
|
||||
|
|
@ -1,25 +1,23 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pangea/enum/instructions_enum.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
class InlineTooltip extends StatefulWidget {
|
||||
class InstructionsInlineTooltip extends StatefulWidget {
|
||||
final InstructionsEnum instructionsEnum;
|
||||
|
||||
const InlineTooltip({
|
||||
const InstructionsInlineTooltip({
|
||||
super.key,
|
||||
required this.instructionsEnum,
|
||||
});
|
||||
|
||||
@override
|
||||
InlineTooltipState createState() => InlineTooltipState();
|
||||
InstructionsInlineTooltipState createState() =>
|
||||
InstructionsInlineTooltipState();
|
||||
}
|
||||
|
||||
class InlineTooltipState extends State<InlineTooltip>
|
||||
class InstructionsInlineTooltipState extends State<InstructionsInlineTooltip>
|
||||
with SingleTickerProviderStateMixin {
|
||||
bool _isToggledOff = true;
|
||||
late AnimationController _controller;
|
||||
|
|
@ -28,7 +26,7 @@ class InlineTooltipState extends State<InlineTooltip>
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_isToggledOff = widget.instructionsEnum.toggledOff();
|
||||
_isToggledOff = widget.instructionsEnum.isToggledOff;
|
||||
|
||||
// Initialize AnimationController and Animation
|
||||
_controller = AnimationController(
|
||||
|
|
@ -52,10 +50,7 @@ class InlineTooltipState extends State<InlineTooltip>
|
|||
}
|
||||
|
||||
void _closeTooltip() {
|
||||
MatrixState.pangeaController.instructions.setToggledOff(
|
||||
widget.instructionsEnum,
|
||||
true,
|
||||
);
|
||||
widget.instructionsEnum.setToggledOff(true);
|
||||
setState(() {
|
||||
_isToggledOff = true;
|
||||
_controller.reverse();
|
||||
71
lib/pangea/instructions/instructions_show_popup.dart
Normal file
71
lib/pangea/instructions/instructions_show_popup.dart
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_toggle.dart';
|
||||
import 'package:fluffychat/pangea/utils/bot_style.dart';
|
||||
import 'package:fluffychat/pangea/utils/overlay.dart';
|
||||
import 'package:fluffychat/pangea/widgets/common/bot_face_svg.dart';
|
||||
import 'package:fluffychat/pangea/widgets/igc/card_header.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
/// Instruction Card gives users tips on
|
||||
/// how to use Pangea Chat's features
|
||||
Future<void> instructionsShowPopup(
|
||||
BuildContext context,
|
||||
InstructionsEnum key,
|
||||
String transformTargetKey, {
|
||||
bool showToggle = true,
|
||||
Widget? customContent,
|
||||
bool forceShow = false,
|
||||
}) async {
|
||||
final bool userLangsSet =
|
||||
await MatrixState.pangeaController.userController.areUserLanguagesSet;
|
||||
if (!userLangsSet) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if ((_instructionsShown[key.toString()] ?? false) && !forceShow) {
|
||||
// return;
|
||||
// }
|
||||
// _instructionsShown[key.toString()] = true;
|
||||
|
||||
if (key.isToggledOff && !forceShow) {
|
||||
return;
|
||||
}
|
||||
|
||||
final botStyle = BotStyle.text(context);
|
||||
Future.delayed(
|
||||
const Duration(seconds: 1),
|
||||
() {
|
||||
if (!context.mounted) return;
|
||||
OverlayUtil.showPositionedCard(
|
||||
context: context,
|
||||
backDropToDismiss: false,
|
||||
cardToShow: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
CardHeader(
|
||||
text: key.title(L10n.of(context)),
|
||||
botExpression: BotExpression.idle,
|
||||
// onClose: () => {_instructionsClosed[key.toString()] = true},
|
||||
),
|
||||
const SizedBox(height: 10.0),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(6.0),
|
||||
child: Text(
|
||||
key.body(L10n.of(context)),
|
||||
style: botStyle,
|
||||
),
|
||||
),
|
||||
if (customContent != null) customContent,
|
||||
if (showToggle) InstructionsToggle(instructionsKey: key),
|
||||
],
|
||||
),
|
||||
maxHeight: 300,
|
||||
maxWidth: 300,
|
||||
transformTargetId: transformTargetKey,
|
||||
closePrevOverlay: false,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
42
lib/pangea/instructions/instructions_toggle.dart
Normal file
42
lib/pangea/instructions/instructions_toggle.dart
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
/// User can toggle on to prevent Instruction Card
|
||||
/// from appearing in future sessions
|
||||
class InstructionsToggle extends StatefulWidget {
|
||||
const InstructionsToggle({
|
||||
super.key,
|
||||
required this.instructionsKey,
|
||||
});
|
||||
|
||||
final InstructionsEnum instructionsKey;
|
||||
|
||||
@override
|
||||
InstructionsToggleState createState() => InstructionsToggleState();
|
||||
}
|
||||
|
||||
class InstructionsToggleState extends State<InstructionsToggle> {
|
||||
PangeaController pangeaController = MatrixState.pangeaController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SwitchListTile.adaptive(
|
||||
activeColor: AppConfig.activeToggleColor,
|
||||
title: Text(L10n.of(context).doNotShowAgain),
|
||||
value: widget.instructionsKey.isToggledOff,
|
||||
onChanged: ((value) async {
|
||||
widget.instructionsKey.setToggledOff(value);
|
||||
setState(() {});
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,7 @@
|
|||
import 'dart:developer';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/constants/language_constants.dart';
|
||||
import 'package:fluffychat/pangea/constants/pangea_event_types.dart';
|
||||
import 'package:fluffychat/pangea/enum/activity_type_enum.dart';
|
||||
|
|
@ -20,6 +16,9 @@ import 'package:fluffychat/pangea/repo/lemma_info/lemma_info_repo.dart';
|
|||
import 'package:fluffychat/pangea/repo/lemma_info/lemma_info_request.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import '../constants/model_keys.dart';
|
||||
import 'lemma.dart';
|
||||
|
||||
|
|
@ -313,8 +312,15 @@ class PangeaToken {
|
|||
case ActivityTypeEnum.hiddenWordListening:
|
||||
return daysSinceLastUseByType(a) > 7;
|
||||
case ActivityTypeEnum.lemmaId:
|
||||
return _didActivitySuccessfully(ActivityTypeEnum.wordMeaning) &&
|
||||
daysSinceLastUseByType(a) > 7;
|
||||
return false;
|
||||
// disabling lemma activities for now
|
||||
// It has 2 purposes:• learning value• triangulating our determination of the lemma with
|
||||
// AI plus user verification.However, displaying the lemma during the meaning activity helps
|
||||
// disambiguate what the meaning activity is about. This is probably more valuable than the
|
||||
// lemma activity itself. The piping for the lemma activity will stay there if we want to turn
|
||||
//it back on, maybe in select instances.
|
||||
// return _didActivitySuccessfully(ActivityTypeEnum.wordMeaning) &&
|
||||
// daysSinceLastUseByType(a) > 7;
|
||||
case ActivityTypeEnum.emoji:
|
||||
return true;
|
||||
case ActivityTypeEnum.morphId:
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/enum/activity_display_instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/enum/activity_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/enum/analytics/morph_categories_enum.dart';
|
||||
import 'package:fluffychat/pangea/enum/construct_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/models/pangea_token_model.dart';
|
||||
import 'package:fluffychat/pangea/models/practice_activities.dart/multiple_choice_activity_model.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
class ConstructIdentifier {
|
||||
final String lemma;
|
||||
|
|
@ -228,7 +229,47 @@ class PracticeActivityModel {
|
|||
required this.content,
|
||||
});
|
||||
|
||||
String get question => content.question;
|
||||
String get targetLemma =>
|
||||
targetTokens?.first.lemma.text ??
|
||||
tgtConstructs
|
||||
.firstWhereOrNull(
|
||||
(element) => element.type == ConstructTypeEnum.vocab,
|
||||
)
|
||||
?.lemma ??
|
||||
"___";
|
||||
|
||||
String get partOfSpeech =>
|
||||
targetTokens?.first.pos ??
|
||||
tgtConstructs
|
||||
.firstWhereOrNull(
|
||||
(element) => element.type == ConstructTypeEnum.vocab,
|
||||
)
|
||||
?.category ??
|
||||
"___";
|
||||
|
||||
String get targetWordForm => targetTokens?.first.text.content ?? "___";
|
||||
|
||||
/// we were setting the question copy on creation of the activity
|
||||
/// but, in order to localize the question using the same system
|
||||
/// as other copy, we should do it with context, when it is built
|
||||
/// some types are doing this now, others should be migrated
|
||||
String question(BuildContext context, String? morphFeature) {
|
||||
switch (activityType) {
|
||||
case ActivityTypeEnum.hiddenWordListening:
|
||||
case ActivityTypeEnum.wordFocusListening:
|
||||
case ActivityTypeEnum.lemmaId:
|
||||
return content.question;
|
||||
case ActivityTypeEnum.emoji:
|
||||
return L10n.of(context).pickAnEmoji(targetLemma, partOfSpeech);
|
||||
case ActivityTypeEnum.wordMeaning:
|
||||
return L10n.of(context).whatIsMeaning(targetLemma, partOfSpeech);
|
||||
case ActivityTypeEnum.morphId:
|
||||
return L10n.of(context).whatIsTheMorphTag(
|
||||
getMorphologicalCategoryCopy(morphFeature!, context) ?? morphFeature,
|
||||
targetWordForm,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
factory PracticeActivityModel.fromJson(Map<String, dynamic> json) {
|
||||
// moving from multiple_choice to content as the key
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import 'package:matrix/matrix.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/instructions/instruction_settings.dart';
|
||||
import 'package:fluffychat/pangea/models/space_model.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'language_model.dart';
|
||||
|
||||
/// The user's settings learning settings.
|
||||
|
|
@ -187,142 +187,20 @@ class UserToolSettings {
|
|||
}
|
||||
}
|
||||
|
||||
/// The user's settings for whether or not to show instuction messages.
|
||||
class UserInstructions {
|
||||
bool showedItInstructions;
|
||||
bool showedClickMessage;
|
||||
bool showedBlurMeansTranslate;
|
||||
bool showedTooltipInstructions;
|
||||
bool showedMissingVoice;
|
||||
bool showedClickBestOption;
|
||||
bool showedUnlockedLanguageTools;
|
||||
|
||||
bool showedSpeechToTextTooltip;
|
||||
bool showedL1TranslationTooltip;
|
||||
bool showedTranslationChoicesTooltip;
|
||||
bool showedClickAgainToDeselect;
|
||||
|
||||
UserInstructions({
|
||||
this.showedItInstructions = false,
|
||||
this.showedClickMessage = false,
|
||||
this.showedBlurMeansTranslate = false,
|
||||
this.showedTooltipInstructions = false,
|
||||
this.showedSpeechToTextTooltip = false,
|
||||
this.showedL1TranslationTooltip = false,
|
||||
this.showedTranslationChoicesTooltip = false,
|
||||
this.showedClickAgainToDeselect = false,
|
||||
this.showedMissingVoice = false,
|
||||
this.showedClickBestOption = false,
|
||||
this.showedUnlockedLanguageTools = false,
|
||||
});
|
||||
|
||||
factory UserInstructions.fromJson(Map<String, dynamic> json) =>
|
||||
UserInstructions(
|
||||
showedItInstructions: json[InstructionsEnum.itInstructions.toString()],
|
||||
showedClickMessage:
|
||||
json[InstructionsEnum.clickMessage.toString()] ?? false,
|
||||
showedBlurMeansTranslate:
|
||||
json[InstructionsEnum.blurMeansTranslate.toString()] ?? false,
|
||||
showedTooltipInstructions:
|
||||
json[InstructionsEnum.tooltipInstructions.toString()] ?? false,
|
||||
showedL1TranslationTooltip:
|
||||
json[InstructionsEnum.l1Translation.toString()] ?? false,
|
||||
showedTranslationChoicesTooltip:
|
||||
json[InstructionsEnum.translationChoices.toString()] ?? false,
|
||||
showedSpeechToTextTooltip:
|
||||
json[InstructionsEnum.speechToText.toString()] ?? false,
|
||||
showedClickAgainToDeselect:
|
||||
json[InstructionsEnum.clickAgainToDeselect.toString()] ?? false,
|
||||
showedMissingVoice:
|
||||
json[InstructionsEnum.missingVoice.toString()] ?? false,
|
||||
showedClickBestOption:
|
||||
json[InstructionsEnum.clickBestOption.toString()] ?? false,
|
||||
showedUnlockedLanguageTools:
|
||||
json[InstructionsEnum.unlockedLanguageTools.toString()] ?? false,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data[InstructionsEnum.itInstructions.toString()] = showedItInstructions;
|
||||
data[InstructionsEnum.clickMessage.toString()] = showedClickMessage;
|
||||
data[InstructionsEnum.blurMeansTranslate.toString()] =
|
||||
showedBlurMeansTranslate;
|
||||
data[InstructionsEnum.tooltipInstructions.toString()] =
|
||||
showedTooltipInstructions;
|
||||
data[InstructionsEnum.l1Translation.toString()] =
|
||||
showedL1TranslationTooltip;
|
||||
data[InstructionsEnum.translationChoices.toString()] =
|
||||
showedTranslationChoicesTooltip;
|
||||
data[InstructionsEnum.speechToText.toString()] = showedSpeechToTextTooltip;
|
||||
data[InstructionsEnum.clickAgainToDeselect.toString()] =
|
||||
showedClickAgainToDeselect;
|
||||
data[InstructionsEnum.missingVoice.toString()] = showedMissingVoice;
|
||||
data[InstructionsEnum.clickBestOption.toString()] = showedClickBestOption;
|
||||
data[InstructionsEnum.unlockedLanguageTools.toString()] =
|
||||
showedUnlockedLanguageTools;
|
||||
return data;
|
||||
}
|
||||
|
||||
factory UserInstructions.migrateFromAccountData() {
|
||||
final accountData =
|
||||
MatrixState.pangeaController.matrixState.client.accountData;
|
||||
return UserInstructions(
|
||||
showedItInstructions:
|
||||
(accountData[InstructionsEnum.itInstructions.toString()]
|
||||
?.content[InstructionsEnum.itInstructions.toString()]
|
||||
as bool?) ??
|
||||
false,
|
||||
showedClickMessage: (accountData[InstructionsEnum.clickMessage.toString()]
|
||||
?.content[InstructionsEnum.clickMessage.toString()] as bool?) ??
|
||||
false,
|
||||
showedBlurMeansTranslate:
|
||||
(accountData[InstructionsEnum.blurMeansTranslate.toString()]
|
||||
?.content[InstructionsEnum.blurMeansTranslate.toString()]
|
||||
as bool?) ??
|
||||
false,
|
||||
showedTooltipInstructions:
|
||||
(accountData[InstructionsEnum.tooltipInstructions.toString()]
|
||||
?.content[InstructionsEnum.tooltipInstructions.toString()]
|
||||
as bool?) ??
|
||||
false,
|
||||
showedL1TranslationTooltip:
|
||||
(accountData[InstructionsEnum.l1Translation.toString()]
|
||||
?.content[InstructionsEnum.l1Translation.toString()]
|
||||
as bool?) ??
|
||||
false,
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper around the matrix account data for the user profile.
|
||||
/// Enables easy access to the profile data and saving new data.
|
||||
class Profile {
|
||||
late UserSettings userSettings;
|
||||
late UserToolSettings toolSettings;
|
||||
late UserInstructions instructionSettings;
|
||||
late InstructionSettings instructionSettings;
|
||||
|
||||
Profile({
|
||||
required this.userSettings,
|
||||
UserToolSettings? toolSettings,
|
||||
UserInstructions? instructionSettings,
|
||||
InstructionSettings? instructionSettings,
|
||||
}) {
|
||||
this.toolSettings = toolSettings ?? UserToolSettings();
|
||||
this.instructionSettings = instructionSettings ?? UserInstructions();
|
||||
this.instructionSettings = instructionSettings ?? InstructionSettings();
|
||||
}
|
||||
|
||||
/// Load an instance of profile from the client's account data.
|
||||
|
|
@ -347,10 +225,10 @@ class Profile {
|
|||
)
|
||||
: UserToolSettings(),
|
||||
instructionSettings: instructionSettingsContent != null
|
||||
? UserInstructions.fromJson(
|
||||
? InstructionSettings.fromJson(
|
||||
instructionSettingsContent as Map<String, dynamic>,
|
||||
)
|
||||
: UserInstructions(),
|
||||
: InstructionSettings(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -370,7 +248,7 @@ class Profile {
|
|||
if (userSettings == null) return null;
|
||||
|
||||
final toolSettings = UserToolSettings.migrateFromAccountData();
|
||||
final instructionSettings = UserInstructions.migrateFromAccountData();
|
||||
final instructionSettings = InstructionSettings.migrateFromAccountData();
|
||||
return Profile(
|
||||
userSettings: userSettings,
|
||||
toolSettings: toolSettings,
|
||||
|
|
@ -417,7 +295,7 @@ class Profile {
|
|||
return Profile(
|
||||
userSettings: UserSettings(),
|
||||
toolSettings: UserToolSettings(),
|
||||
instructionSettings: UserInstructions(),
|
||||
instructionSettings: InstructionSettings(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/enum/activity_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/enum/construct_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/models/practice_activities.dart/message_activity_request.dart';
|
||||
|
|
@ -9,6 +5,8 @@ import 'package:fluffychat/pangea/models/practice_activities.dart/multiple_choic
|
|||
import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart';
|
||||
import 'package:fluffychat/pangea/repo/lemma_info/lemma_info_repo.dart';
|
||||
import 'package:fluffychat/pangea/repo/lemma_info/lemma_info_request.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
class WordMeaningActivityGenerator {
|
||||
Future<MessageActivityResponse> get(
|
||||
|
|
@ -48,7 +46,8 @@ class WordMeaningActivityGenerator {
|
|||
langCode: req.userL2,
|
||||
activityType: ActivityTypeEnum.wordMeaning,
|
||||
content: ActivityContent(
|
||||
question: L10n.of(context).chooseBestDefinition,
|
||||
question:
|
||||
L10n.of(context).whatIsMeaning(lemmaId.lemma, lemmaId.category),
|
||||
choices: choices,
|
||||
answers: [res.meaning],
|
||||
spanDisplayDetails: null,
|
||||
|
|
|
|||
|
|
@ -1,173 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/enum/instructions_enum.dart';
|
||||
import '../../config/app_config.dart';
|
||||
import '../../widgets/matrix.dart';
|
||||
import '../controllers/pangea_controller.dart';
|
||||
import '../widgets/common/bot_face_svg.dart';
|
||||
import '../widgets/igc/card_header.dart';
|
||||
import 'bot_style.dart';
|
||||
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<String, bool> _instructionsClosed = {};
|
||||
|
||||
/// Instruction popup has already been shown this session
|
||||
final Map<String, bool> _instructionsShown = {};
|
||||
|
||||
InstructionsController(PangeaController pangeaController) {
|
||||
_pangeaController = pangeaController;
|
||||
}
|
||||
|
||||
void setToggledOff(
|
||||
InstructionsEnum key,
|
||||
bool value,
|
||||
) {
|
||||
_pangeaController.userController.updateProfile((profile) {
|
||||
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;
|
||||
case InstructionsEnum.missingVoice:
|
||||
profile.instructionSettings.showedMissingVoice = value;
|
||||
break;
|
||||
case InstructionsEnum.clickBestOption:
|
||||
profile.instructionSettings.showedClickBestOption = value;
|
||||
break;
|
||||
case InstructionsEnum.unlockedLanguageTools:
|
||||
profile.instructionSettings.showedUnlockedLanguageTools = value;
|
||||
break;
|
||||
}
|
||||
return profile;
|
||||
});
|
||||
}
|
||||
|
||||
/// Instruction Card gives users tips on
|
||||
/// how to use Pangea Chat's features
|
||||
Future<void> showInstructionsPopup(
|
||||
BuildContext context,
|
||||
InstructionsEnum key,
|
||||
String transformTargetKey, {
|
||||
bool showToggle = true,
|
||||
Widget? customContent,
|
||||
bool forceShow = false,
|
||||
}) async {
|
||||
final bool userLangsSet =
|
||||
await _pangeaController.userController.areUserLanguagesSet;
|
||||
if (!userLangsSet) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((_instructionsShown[key.toString()] ?? false) && !forceShow) {
|
||||
return;
|
||||
}
|
||||
_instructionsShown[key.toString()] = true;
|
||||
|
||||
if (key.toggledOff() && !forceShow) {
|
||||
return;
|
||||
}
|
||||
|
||||
final botStyle = BotStyle.text(context);
|
||||
Future.delayed(
|
||||
const Duration(seconds: 1),
|
||||
() {
|
||||
if (!context.mounted) return;
|
||||
OverlayUtil.showPositionedCard(
|
||||
context: context,
|
||||
backDropToDismiss: false,
|
||||
cardToShow: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
CardHeader(
|
||||
text: key.title(L10n.of(context)),
|
||||
botExpression: BotExpression.idle,
|
||||
onClose: () => {_instructionsClosed[key.toString()] = true},
|
||||
),
|
||||
const SizedBox(height: 10.0),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(6.0),
|
||||
child: Text(
|
||||
key.body(L10n.of(context)),
|
||||
style: botStyle,
|
||||
),
|
||||
),
|
||||
if (customContent != null) customContent,
|
||||
if (showToggle) InstructionsToggle(instructionsKey: key),
|
||||
],
|
||||
),
|
||||
maxHeight: 300,
|
||||
maxWidth: 300,
|
||||
transformTargetId: transformTargetKey,
|
||||
closePrevOverlay: false,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// User can toggle on to prevent Instruction Card
|
||||
/// from appearing in future sessions
|
||||
class InstructionsToggle extends StatefulWidget {
|
||||
const InstructionsToggle({
|
||||
super.key,
|
||||
required this.instructionsKey,
|
||||
});
|
||||
|
||||
final InstructionsEnum instructionsKey;
|
||||
|
||||
@override
|
||||
InstructionsToggleState createState() => InstructionsToggleState();
|
||||
}
|
||||
|
||||
class InstructionsToggleState extends State<InstructionsToggle> {
|
||||
PangeaController pangeaController = MatrixState.pangeaController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SwitchListTile.adaptive(
|
||||
activeColor: AppConfig.activeToggleColor,
|
||||
title: Text(L10n.of(context).doNotShowAgain),
|
||||
value: widget.instructionsKey.toggledOff(),
|
||||
onChanged: ((value) async {
|
||||
pangeaController.instructions.setToggledOff(
|
||||
widget.instructionsKey,
|
||||
value,
|
||||
);
|
||||
setState(() {});
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +1,21 @@
|
|||
import 'dart:developer';
|
||||
|
||||
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:material_symbols_icons/symbols.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/enum/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.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';
|
||||
import 'package:fluffychat/widgets/matrix.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:material_symbols_icons/symbols.dart';
|
||||
|
||||
import '../../utils/bot_style.dart';
|
||||
|
||||
class MessageSpeechToTextCard extends StatefulWidget {
|
||||
|
|
@ -214,7 +213,7 @@ class MessageSpeechToTextCardState extends State<MessageSpeechToTextCard> {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: InlineTooltip(
|
||||
child: InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.speechToText,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/enum/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart';
|
||||
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
|
||||
import 'package:fluffychat/pangea/models/pangea_token_text_model.dart';
|
||||
import 'package:fluffychat/pangea/models/representation_content_model.dart';
|
||||
import 'package:fluffychat/pangea/repo/full_text_translation_repo.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/igc/card_error_widget.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MessageTranslationCard extends StatefulWidget {
|
||||
final PangeaMessageEvent messageEvent;
|
||||
|
|
@ -172,24 +171,24 @@ class MessageTranslationCardState extends State<MessageTranslationCard> {
|
|||
),
|
||||
if (notGoingToTranslate &&
|
||||
widget.selection == null &&
|
||||
!InstructionsEnum.l1Translation.toggledOff())
|
||||
!InstructionsEnum.l1Translation.isToggledOff)
|
||||
const Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: InlineTooltip(
|
||||
child: InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.l1Translation,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (widget.selection != null &&
|
||||
!InstructionsEnum.clickAgainToDeselect.toggledOff())
|
||||
!InstructionsEnum.clickAgainToDeselect.isToggledOff)
|
||||
const Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: InlineTooltip(
|
||||
child: InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.clickAgainToDeselect,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,19 +1,18 @@
|
|||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_tts/flutter_tts.dart' as flutter_tts;
|
||||
import 'package:matrix/matrix_api_lite/utils/logs.dart';
|
||||
import 'package:text_to_speech/text_to_speech.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/controllers/user_controller.dart';
|
||||
import 'package:fluffychat/pangea/enum/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_show_popup.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/missing_voice_button.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_tts/flutter_tts.dart' as flutter_tts;
|
||||
import 'package:matrix/matrix_api_lite/utils/logs.dart';
|
||||
import 'package:text_to_speech/text_to_speech.dart';
|
||||
|
||||
class TtsController {
|
||||
String? get targetLanguage =>
|
||||
|
|
@ -163,7 +162,7 @@ class TtsController {
|
|||
BuildContext context,
|
||||
String eventID,
|
||||
) async {
|
||||
await MatrixState.pangeaController.instructions.showInstructionsPopup(
|
||||
await instructionsShowPopup(
|
||||
context,
|
||||
InstructionsEnum.missingVoice,
|
||||
eventID,
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
import 'dart:developer';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/enum/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_show_popup.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/widgets/chat/message_toolbar_selection_area.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import '../../models/pangea_match_model.dart';
|
||||
|
||||
class PangeaRichText extends StatefulWidget {
|
||||
|
|
@ -131,7 +131,7 @@ class PangeaRichTextState extends State<PangeaRichText> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (blur > 0) {
|
||||
pangeaController.instructions.showInstructionsPopup(
|
||||
instructionsShowPopup(
|
||||
context,
|
||||
InstructionsEnum.blurMeansTranslate,
|
||||
widget.pangeaMessageEvent.eventId,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/enum/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/utils/inline_tooltip.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class StarAnimationWidget extends StatefulWidget {
|
||||
const StarAnimationWidget({super.key});
|
||||
|
|
@ -79,7 +78,7 @@ class GamifiedTextWidget extends StatelessWidget {
|
|||
child: Column(
|
||||
children: [
|
||||
StarAnimationWidget(),
|
||||
InlineTooltip(
|
||||
InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.unlockedLanguageTools,
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/controllers/message_analytics_controller.dart';
|
||||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/controllers/put_analytics_controller.dart';
|
||||
|
|
@ -26,6 +22,8 @@ import 'package:fluffychat/pangea/widgets/igc/card_error_widget.dart';
|
|||
import 'package:fluffychat/pangea/widgets/practice_activity/multiple_choice_activity.dart';
|
||||
import 'package:fluffychat/pangea/widgets/word_zoom/word_zoom_widget.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// The wrapper for practice activity content.
|
||||
/// Handles the activities associated with a message,
|
||||
|
|
@ -70,6 +68,8 @@ class PracticeActivityCardState extends State<PracticeActivityCard> {
|
|||
PangeaController get pangeaController => MatrixState.pangeaController;
|
||||
String? _error;
|
||||
|
||||
String? activityQuestion;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
|
@ -122,7 +122,9 @@ class PracticeActivityCardState extends State<PracticeActivityCard> {
|
|||
}
|
||||
|
||||
currentCompletionRecord = PracticeActivityRecordModel(
|
||||
question: activity.question,
|
||||
question: mounted
|
||||
? currentActivity?.question(context, widget.morphFeature)
|
||||
: currentActivity?.content.question,
|
||||
);
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ class LemmaMeaningWidgetState extends State<LemmaMeaningWidget> {
|
|||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context).editLemmaMeaning,
|
||||
"${L10n.of(context).pangeaBotIsFallible} ${L10n.of(context).whatIsMeaning(widget.lemma, widget.pos)}",
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(fontStyle: FontStyle.italic),
|
||||
),
|
||||
|
|
@ -138,9 +138,13 @@ class LemmaMeaningWidgetState extends State<LemmaMeaningWidget> {
|
|||
return GestureDetector(
|
||||
onLongPress: () => _toggleEditMode(true),
|
||||
onDoubleTap: () => _toggleEditMode(true),
|
||||
child: Text(
|
||||
snapshot.data!.meaning,
|
||||
textAlign: TextAlign.center,
|
||||
child: Tooltip(
|
||||
message: L10n.of(context).doubleClickToEdit,
|
||||
waitDuration: const Duration(milliseconds: 2000),
|
||||
child: Text(
|
||||
snapshot.data!.meaning,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import 'package:fluffychat/pangea/constants/model_keys.dart';
|
||||
import 'package:fluffychat/pangea/constants/morph_categories_and_labels.dart';
|
||||
import 'package:fluffychat/pangea/enum/analytics/morph_categories_enum.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
|
||||
import 'package:fluffychat/pangea/models/pangea_token_model.dart';
|
||||
|
|
@ -67,7 +68,12 @@ class MorphologicalCenterWidgetState extends State<MorphologicalCenterWidget> {
|
|||
|
||||
PangeaMessageEvent get pm => widget.pangeaMessageEvent;
|
||||
|
||||
Future<void> confirmChanges() async {
|
||||
/// confirm the changes made by the user
|
||||
/// this will send a new message to the server
|
||||
/// with the new morphological tag
|
||||
Future<void> saveChanges(
|
||||
PangeaToken Function(PangeaToken token) changeCallback,
|
||||
) async {
|
||||
try {
|
||||
// NOTE: it is not clear how this would work if the user was not editing the originalSent tokens
|
||||
// this case would only happen in immersion mode which is disabled until further notice
|
||||
|
|
@ -85,7 +91,7 @@ class MorphologicalCenterWidgetState extends State<MorphologicalCenterWidget> {
|
|||
if (tokenIndex == -1) {
|
||||
throw Exception("Token not found in message");
|
||||
}
|
||||
existingTokens[tokenIndex].morph[widget.morphFeature] = selectedMorphTag;
|
||||
existingTokens[tokenIndex] = changeCallback(existingTokens[tokenIndex]);
|
||||
|
||||
// send a new message as an edit to original message to the server
|
||||
// including the new tokens
|
||||
|
|
@ -123,22 +129,27 @@ class MorphologicalCenterWidgetState extends State<MorphologicalCenterWidget> {
|
|||
}
|
||||
}
|
||||
|
||||
List<String> get allMorphTagsForEdit {
|
||||
final List<String> tags = getLabelsForMorphCategory(widget.morphFeature)
|
||||
.where(
|
||||
(tag) => !["punct", "space", "sym", "x", "other"]
|
||||
.contains(tag.toLowerCase()),
|
||||
)
|
||||
.toList();
|
||||
/// all morphological tags for the selected morphological category
|
||||
/// that are eligible for setting as the morphological tag
|
||||
List<String> get allMorphTagsForEdit =>
|
||||
getLabelsForMorphCategory(widget.morphFeature)
|
||||
.where(
|
||||
(tag) => !["punct", "space", "sym", "x", "other"]
|
||||
.contains(tag.toLowerCase()),
|
||||
)
|
||||
.toList();
|
||||
|
||||
// as long as the feature is not POS, add a nan tag
|
||||
// this will allow the user to remove the feature from the tags
|
||||
if (widget.morphFeature.toLowerCase() != "pos") {
|
||||
tags.add(L10n.of(context).constructUseNanDesc);
|
||||
}
|
||||
String get morphCopy =>
|
||||
getMorphologicalCategoryCopy(widget.morphFeature, context) ??
|
||||
widget.morphFeature;
|
||||
|
||||
return tags;
|
||||
}
|
||||
String get tagCopy =>
|
||||
getGrammarCopy(
|
||||
category: widget.morphFeature,
|
||||
lemma: selectedMorphTag,
|
||||
context: context,
|
||||
) ??
|
||||
selectedMorphTag;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -146,14 +157,13 @@ class MorphologicalCenterWidgetState extends State<MorphologicalCenterWidget> {
|
|||
return GestureDetector(
|
||||
onLongPress: enterEditMode,
|
||||
onDoubleTap: enterEditMode,
|
||||
child: Text(
|
||||
getGrammarCopy(
|
||||
category: widget.morphFeature,
|
||||
lemma: widget.token.morph[widget.morphFeature],
|
||||
context: context,
|
||||
) ??
|
||||
widget.token.morph[widget.morphFeature]!,
|
||||
textAlign: TextAlign.center,
|
||||
child: Tooltip(
|
||||
message: L10n.of(context).doubleClickToEdit,
|
||||
waitDuration: const Duration(milliseconds: 2000),
|
||||
child: Text(
|
||||
"$morphCopy: $tagCopy",
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -161,7 +171,10 @@ class MorphologicalCenterWidgetState extends State<MorphologicalCenterWidget> {
|
|||
return Column(
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context).editMorphologicalLabel,
|
||||
"${L10n.of(context).pangeaBotIsFallible} ${L10n.of(context).whatIsTheMorphTag(
|
||||
morphCopy,
|
||||
widget.token.text.content,
|
||||
)}",
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(fontStyle: FontStyle.italic),
|
||||
),
|
||||
|
|
@ -228,8 +241,20 @@ class MorphologicalCenterWidgetState extends State<MorphologicalCenterWidget> {
|
|||
),
|
||||
const SizedBox(height: 10),
|
||||
Row(
|
||||
spacing: 10,
|
||||
children: [
|
||||
//cancel button
|
||||
// this would allow the user to totally remove the morphological feature from the token
|
||||
// disabled and let's see if someone asks for it
|
||||
// if (widget.morphFeature.toLowerCase() != "pos")
|
||||
// TextButton(
|
||||
// onPressed: () => saveChanges(
|
||||
// (token) {
|
||||
// token.morph.remove(widget.morphFeature);
|
||||
// return token;
|
||||
// },
|
||||
// ),
|
||||
// child: Text(L10n.of(context).removeFeature(morphCopy)),
|
||||
// ),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
|
|
@ -238,15 +263,19 @@ class MorphologicalCenterWidgetState extends State<MorphologicalCenterWidget> {
|
|||
},
|
||||
child: Text(L10n.of(context).cancel),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
ElevatedButton(
|
||||
onPressed:
|
||||
selectedMorphTag == widget.token.morph[widget.morphFeature]
|
||||
? null
|
||||
: () => showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: confirmChanges,
|
||||
),
|
||||
onPressed: selectedMorphTag ==
|
||||
widget.token.morph[widget.morphFeature]
|
||||
? null
|
||||
: () => showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => saveChanges(
|
||||
(token) {
|
||||
token.morph[widget.morphFeature] = selectedMorphTag;
|
||||
return token;
|
||||
},
|
||||
),
|
||||
),
|
||||
child: Text(L10n.of(context).saveChanges),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue