merge conflicts
This commit is contained in:
commit
8b1cda2e94
51 changed files with 1894 additions and 978 deletions
|
|
@ -2894,21 +2894,23 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"helpMeTranslate": "Help me translate!",
|
||||
"helpMeTranslate": "Yes!",
|
||||
"@helpMeTranslate": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"needsItShortMessage": "Try interactive translation!",
|
||||
"needsItShortMessage": "Out of target",
|
||||
"needsIGCShortMessage": "Try interactive grammar assistance!",
|
||||
"@needsItShortMessage": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"needsItMessage": "This message has too many words in your base language.",
|
||||
"needsItMessage": "Wait, that's not {targetLanguage}! Do you need help translating?",
|
||||
"@needsItMessage": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
"placeholders": {
|
||||
"targetLanguage": {}
|
||||
}
|
||||
},
|
||||
"needsIgcMessage": "This message has a grammar error.",
|
||||
"tokenTranslationTitle": "A word is in your base language.",
|
||||
|
|
@ -3998,6 +4000,10 @@
|
|||
"conversationBotDiscussionZone_discussionTriggerScheduleHourIntervalLabel": "Hours between discussion prompts",
|
||||
"conversationBotDiscussionZone_discussionTriggerReactionEnabledLabel": "Responds on ⏩ reaction",
|
||||
"conversationBotDiscussionZone_discussionTriggerReactionKeyLabel": "Reaction to send discussion prompt",
|
||||
"conversationBotCustomZone_title": "Custom Settings",
|
||||
"conversationBotCustomZone_customSystemPromptLabel": "System prompt",
|
||||
"conversationBotCustomZone_customSystemPromptPlaceholder": "Set custom system prompt",
|
||||
"conversationBotCustomZone_customTriggerReactionEnabledLabel": "Responds on ⏩ reaction",
|
||||
"addConversationBotDialogTitleInvite": "Confirm inviting conversation bot",
|
||||
"addConversationBotButtonInvite": "Invite",
|
||||
"addConversationBotDialogInviteConfirmation": "Invite",
|
||||
|
|
@ -4060,5 +4066,22 @@
|
|||
"noLanguagesSet": "No languages set",
|
||||
"noActivitiesFound": "No practice activities found for this message",
|
||||
"hintTitle": "Hint:",
|
||||
"speechToTextBody": "See how well you did by looking at your Accuracy and Words Per Minute scores"
|
||||
"speechToTextBody": "See how well you did by looking at your Accuracy and Words Per Minute scores",
|
||||
"languageButtonLabel": "Language: {currentLanguage}",
|
||||
"@languageButtonLabel": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"currentLanguage": {}
|
||||
}
|
||||
},
|
||||
"interactiveTranslatorAutoPlaySliderHeader": "Autoplay translation",
|
||||
"@interactiveTranslatorAutoPlaySliderHeader": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"interactiveTranslatorAutoPlayDesc": "Launches the interactive translator without asking.",
|
||||
"@interactiveTranslatorAutoPlayDesc": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
}
|
||||
}
|
||||
|
|
@ -3064,6 +3064,17 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"interactiveTranslatorAutoPlaySliderHeader": "Traducción de reproducción automática",
|
||||
"interactiveTranslatorAutoPlay": "Traductora interactiva de reproducción automática",
|
||||
"@interactiveTranslatorAutoPlay": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"interactiveTranslatorAutoPlayDesc": "Inicia el traductor interactivo sin preguntar.",
|
||||
"@interactiveTranslatorAutoPlayDesc": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"grammarAssistance": "Asistencia gramatical",
|
||||
"@grammarAssistance": {
|
||||
"type": "text",
|
||||
|
|
@ -3126,10 +3137,16 @@
|
|||
"translationSeemsFinished": "La traducción parece estar terminada.",
|
||||
"needsItShortMessage": "¡Pruebe la traducción interactiva!",
|
||||
"needsIGCShortMessage": "¡Pruebe el corrector gramatical interactivo!",
|
||||
"needsItMessage": "Este mensaje tiene demasiadas palabras en su idioma base.",
|
||||
"needsItMessage": "Espera, ¡ese no es {targetLanguage}! ¿Necesitas ayuda para traducir?",
|
||||
"@needsItMessage": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"targetLanguage": {}
|
||||
}
|
||||
},
|
||||
"needsIgcMessage": "Este mensaje tiene un error gramatical.",
|
||||
"tokenTranslationTitle": "Una palabra está en su idioma base.",
|
||||
"helpMeTranslate": "¡Ayúdeme a traducir!",
|
||||
"helpMeTranslate": "¡Sí!",
|
||||
"setToPublicSettingsTitle": "¿Quiere encontrar un compañero de conversación?",
|
||||
"setToPublicSettingsDesc": "Antes de que pueda buscar un compañero de conversación, debe configurar la visibilidad de su perfil como pública.",
|
||||
"publicProfileTitle": "Perfil público",
|
||||
|
|
|
|||
BIN
assets/pangea/bot_faces/pangea_bot.riv
Normal file
BIN
assets/pangea/bot_faces/pangea_bot.riv
Normal file
Binary file not shown.
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:animations/animations.dart';
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/it_bar.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/send_button.dart';
|
||||
import 'package:fluffychat/pangea/constants/language_keys.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
|
|
@ -55,9 +54,6 @@ class ChatInputRow extends StatelessWidget {
|
|||
|
||||
return Column(
|
||||
children: [
|
||||
ITBar(
|
||||
choreographer: controller.choreographer,
|
||||
),
|
||||
Row(
|
||||
// crossAxisAlignment: CrossAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import 'package:future_loading_dialog/future_loading_dialog.dart';
|
|||
import 'package:go_router/go_router.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import '../../pangea/choreographer/widgets/it_bar.dart';
|
||||
import '../../utils/stream_extension.dart';
|
||||
import 'chat_emoji_picker.dart';
|
||||
import 'chat_input_row.dart';
|
||||
|
|
@ -440,6 +441,9 @@ class ChatView extends StatelessWidget {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const ConnectionStatusHeader(),
|
||||
ITBar(
|
||||
choreographer: controller.choreographer,
|
||||
),
|
||||
ReactionsPicker(controller),
|
||||
ReplyDisplay(controller),
|
||||
ChatInputRow(controller),
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -15,6 +15,7 @@ import 'package:fluffychat/pangea/models/language_detection_model.dart';
|
|||
import 'package:fluffychat/pangea/models/representation_content_model.dart';
|
||||
import 'package:fluffychat/pangea/models/space_model.dart';
|
||||
import 'package:fluffychat/pangea/models/tokens_event_content_model.dart';
|
||||
import 'package:fluffychat/pangea/models/user_model.dart';
|
||||
import 'package:fluffychat/pangea/utils/any_state_holder.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/utils/overlay.dart';
|
||||
|
|
@ -522,6 +523,10 @@ class Choreographer {
|
|||
chatController.room,
|
||||
);
|
||||
|
||||
bool get itAutoPlayEnabled => pangeaController.pStoreService.read(
|
||||
MatrixProfile.itAutoPlay.title,
|
||||
) ?? false;
|
||||
|
||||
bool get definitionsEnabled =>
|
||||
pangeaController.permissionsController.isToolEnabled(
|
||||
ToolSetting.definitions,
|
||||
|
|
|
|||
|
|
@ -170,6 +170,15 @@ class IgcController {
|
|||
const int firstMatchIndex = 0;
|
||||
final PangeaMatch match = igcTextData!.matches[firstMatchIndex];
|
||||
|
||||
if (
|
||||
match.isITStart &&
|
||||
choreographer.itAutoPlayEnabled &&
|
||||
igcTextData != null
|
||||
) {
|
||||
choreographer.onITStart(igcTextData!.matches[firstMatchIndex]);
|
||||
return;
|
||||
}
|
||||
|
||||
OverlayUtil.showPositionedCard(
|
||||
context: context,
|
||||
cardToShow: SpanCard(
|
||||
|
|
@ -189,7 +198,7 @@ class IgcController {
|
|||
),
|
||||
roomId: choreographer.roomId,
|
||||
),
|
||||
cardSize: match.isITStart ? const Size(350, 220) : const Size(350, 400),
|
||||
cardSize: match.isITStart ? const Size(350, 260) : const Size(350, 400),
|
||||
transformTargetId: choreographer.inputTransformTargetKey,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ class ITController {
|
|||
Choreographer choreographer;
|
||||
|
||||
bool _isOpen = false;
|
||||
bool _willOpen = false;
|
||||
bool _isEditingSourceText = false;
|
||||
bool showChoiceFeedback = false;
|
||||
|
||||
|
|
@ -36,6 +37,7 @@ class ITController {
|
|||
|
||||
void clear() {
|
||||
_isOpen = false;
|
||||
_willOpen = false;
|
||||
showChoiceFeedback = false;
|
||||
_isEditingSourceText = false;
|
||||
|
||||
|
|
@ -54,6 +56,7 @@ class ITController {
|
|||
}
|
||||
|
||||
Future<void> initializeIT(ITStartData itStartData) async {
|
||||
_willOpen = true;
|
||||
Future.delayed(const Duration(microseconds: 100), () {
|
||||
_isOpen = true;
|
||||
});
|
||||
|
|
@ -61,11 +64,9 @@ class ITController {
|
|||
}
|
||||
|
||||
void closeIT() {
|
||||
//if they close it before choosing anything, just put their text back
|
||||
//if they close it before completing, just put their text back
|
||||
//PTODO - explore using last itStep
|
||||
if (choreographer.currentText.isEmpty) {
|
||||
choreographer.textController.text = sourceText ?? "";
|
||||
}
|
||||
choreographer.textController.text = sourceText ?? "";
|
||||
clear();
|
||||
}
|
||||
|
||||
|
|
@ -217,31 +218,19 @@ class ITController {
|
|||
|
||||
Future<void> onEditSourceTextSubmit(String newSourceText) async {
|
||||
try {
|
||||
sourceText = newSourceText;
|
||||
|
||||
_isOpen = true;
|
||||
_isEditingSourceText = false;
|
||||
final String currentText = choreographer.currentText;
|
||||
_itStartData = ITStartData(newSourceText, choreographer.l1LangCode);
|
||||
completedITSteps = [];
|
||||
currentITStep = null;
|
||||
nextITStep = null;
|
||||
goldRouteTracker = GoldRouteTracker.defaultTracker;
|
||||
payLoadIds = [];
|
||||
|
||||
choreographer.startLoading();
|
||||
_setSourceText();
|
||||
getTranslationData(false);
|
||||
|
||||
final List<ITResponseModel> responses = await Future.wait([
|
||||
_customInputTranslation(""),
|
||||
_customInputTranslation(choreographer.currentText),
|
||||
]);
|
||||
if (responses[0].goldContinuances != null &&
|
||||
responses[0].goldContinuances!.isNotEmpty) {
|
||||
goldRouteTracker = GoldRouteTracker(
|
||||
responses[0].goldContinuances!,
|
||||
sourceText!,
|
||||
);
|
||||
}
|
||||
currentITStep = CurrentITStep(
|
||||
sourceText: sourceText!,
|
||||
currentText: currentText,
|
||||
responseModel: responses[1],
|
||||
storedGoldContinuances: goldRouteTracker.continuances,
|
||||
);
|
||||
|
||||
_addPayloadId(responses[1]);
|
||||
} catch (err, stack) {
|
||||
debugger(when: kDebugMode);
|
||||
if (err is! http.Response) {
|
||||
|
|
@ -252,6 +241,7 @@ class ITController {
|
|||
);
|
||||
} finally {
|
||||
choreographer.stopLoading();
|
||||
choreographer.textController.text = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -334,6 +324,8 @@ class ITController {
|
|||
|
||||
bool get isOpen => _isOpen;
|
||||
|
||||
bool get willOpen => _willOpen;
|
||||
|
||||
String get targetLangCode => choreographer.l2LangCode!;
|
||||
|
||||
String get sourceLangCode => choreographer.l1LangCode!;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|||
import '../../utils/bot_style.dart';
|
||||
import 'it_shimmer.dart';
|
||||
|
||||
class ChoicesArray extends StatelessWidget {
|
||||
class ChoicesArray extends StatefulWidget {
|
||||
final bool isLoading;
|
||||
final List<Choice>? choices;
|
||||
final void Function(int) onPressed;
|
||||
|
|
@ -32,23 +32,51 @@ class ChoicesArray extends StatelessWidget {
|
|||
this.onLongPress,
|
||||
});
|
||||
|
||||
@override
|
||||
ChoicesArrayState createState() => ChoicesArrayState();
|
||||
}
|
||||
|
||||
class ChoicesArrayState extends State<ChoicesArray> {
|
||||
bool interactionDisabled = false;
|
||||
|
||||
void disableInteraction() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
setState(() {
|
||||
interactionDisabled = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void enableInteractions() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
setState(() {
|
||||
interactionDisabled = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
return isLoading && (choices == null || choices!.length <= 1)
|
||||
? ItShimmer(originalSpan: originalSpan)
|
||||
return widget.isLoading &&
|
||||
(widget.choices == null || widget.choices!.length <= 1)
|
||||
? ItShimmer(originalSpan: widget.originalSpan)
|
||||
: Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
children: choices
|
||||
children: widget.choices
|
||||
?.asMap()
|
||||
.entries
|
||||
.map(
|
||||
(entry) => ChoiceItem(
|
||||
theme: theme,
|
||||
onLongPress: isActive ? onLongPress : null,
|
||||
onPressed: isActive ? onPressed : (_) {},
|
||||
onLongPress:
|
||||
widget.isActive ? widget.onLongPress : null,
|
||||
onPressed: widget.isActive ? widget.onPressed : (_) {},
|
||||
entry: entry,
|
||||
isSelected: selectedChoiceIndex == entry.key,
|
||||
interactionDisabled: interactionDisabled,
|
||||
enableInteraction: enableInteractions,
|
||||
disableInteraction: disableInteraction,
|
||||
isSelected: widget.selectedChoiceIndex == entry.key,
|
||||
),
|
||||
)
|
||||
.toList() ??
|
||||
|
|
@ -77,6 +105,9 @@ class ChoiceItem extends StatelessWidget {
|
|||
required this.onPressed,
|
||||
required this.entry,
|
||||
required this.isSelected,
|
||||
required this.interactionDisabled,
|
||||
required this.enableInteraction,
|
||||
required this.disableInteraction,
|
||||
});
|
||||
|
||||
final MapEntry<int, Choice> entry;
|
||||
|
|
@ -84,6 +115,9 @@ class ChoiceItem extends StatelessWidget {
|
|||
final void Function(int p1)? onLongPress;
|
||||
final void Function(int p1) onPressed;
|
||||
final bool isSelected;
|
||||
final bool interactionDisabled;
|
||||
final VoidCallback enableInteraction;
|
||||
final VoidCallback disableInteraction;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -97,6 +131,8 @@ class ChoiceItem extends StatelessWidget {
|
|||
key: ValueKey(entry.value.text),
|
||||
selected: entry.value.color != null,
|
||||
isGold: entry.value.isGold,
|
||||
enableInteraction: enableInteraction,
|
||||
disableInteraction: disableInteraction,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(2),
|
||||
padding: EdgeInsets.zero,
|
||||
|
|
@ -130,9 +166,11 @@ class ChoiceItem extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
onLongPress:
|
||||
onLongPress != null ? () => onLongPress!(entry.key) : null,
|
||||
onPressed: () => onPressed(entry.key),
|
||||
onLongPress: onLongPress != null && !interactionDisabled
|
||||
? () => onLongPress!(entry.key)
|
||||
: null,
|
||||
onPressed:
|
||||
interactionDisabled ? null : () => onPressed(entry.key),
|
||||
child: Text(
|
||||
entry.value.text,
|
||||
style: BotStyle.text(context),
|
||||
|
|
@ -152,11 +190,15 @@ class ChoiceAnimationWidget extends StatefulWidget {
|
|||
final Widget child;
|
||||
final bool selected;
|
||||
final bool isGold;
|
||||
final VoidCallback enableInteraction;
|
||||
final VoidCallback disableInteraction;
|
||||
|
||||
const ChoiceAnimationWidget({
|
||||
super.key,
|
||||
required this.child,
|
||||
required this.selected,
|
||||
required this.enableInteraction,
|
||||
required this.disableInteraction,
|
||||
this.isGold = false,
|
||||
});
|
||||
|
||||
|
|
@ -164,11 +206,13 @@ class ChoiceAnimationWidget extends StatefulWidget {
|
|||
ChoiceAnimationWidgetState createState() => ChoiceAnimationWidgetState();
|
||||
}
|
||||
|
||||
enum AnimationState { ready, forward, reverse, finished }
|
||||
|
||||
class ChoiceAnimationWidgetState extends State<ChoiceAnimationWidget>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late final AnimationController _controller;
|
||||
late final Animation<double> _animation;
|
||||
bool animationPlayed = false;
|
||||
AnimationState animationState = AnimationState.ready;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -196,17 +240,29 @@ class ChoiceAnimationWidgetState extends State<ChoiceAnimationWidget>
|
|||
),
|
||||
]).animate(_controller);
|
||||
|
||||
if (widget.selected && !animationPlayed) {
|
||||
widget.enableInteraction();
|
||||
|
||||
if (widget.selected && animationState == AnimationState.ready) {
|
||||
widget.disableInteraction();
|
||||
_controller.forward();
|
||||
animationPlayed = true;
|
||||
setState(() {});
|
||||
setState(() {
|
||||
animationState = AnimationState.forward;
|
||||
});
|
||||
}
|
||||
_controller.addStatusListener((status) {
|
||||
if (status == AnimationStatus.completed) {
|
||||
if (status == AnimationStatus.completed &&
|
||||
animationState == AnimationState.forward) {
|
||||
_controller.reverse();
|
||||
} else if (status == AnimationStatus.dismissed) {
|
||||
_controller.stop();
|
||||
_controller.reset();
|
||||
setState(() {
|
||||
animationState = AnimationState.reverse;
|
||||
});
|
||||
}
|
||||
if (status == AnimationStatus.dismissed &&
|
||||
animationState == AnimationState.reverse) {
|
||||
widget.enableInteraction();
|
||||
setState(() {
|
||||
animationState = AnimationState.finished;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -214,10 +270,12 @@ class ChoiceAnimationWidgetState extends State<ChoiceAnimationWidget>
|
|||
@override
|
||||
void didUpdateWidget(ChoiceAnimationWidget oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.selected && !animationPlayed) {
|
||||
if (widget.selected && animationState == AnimationState.ready) {
|
||||
widget.disableInteraction();
|
||||
_controller.forward();
|
||||
animationPlayed = true;
|
||||
setState(() {});
|
||||
setState(() {
|
||||
animationState = AnimationState.forward;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import 'package:fluffychat/pangea/choreographer/widgets/it_feedback_card.dart';
|
|||
import 'package:fluffychat/pangea/choreographer/widgets/translation_finished_flow.dart';
|
||||
import 'package:fluffychat/pangea/constants/choreo_constants.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
|
@ -21,93 +22,107 @@ class ITBar extends StatelessWidget {
|
|||
final Choreographer choreographer;
|
||||
const ITBar({super.key, required this.choreographer});
|
||||
|
||||
ITController get controller => choreographer.itController;
|
||||
ITController get itController => choreographer.itController;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!controller.isOpen) return const SizedBox();
|
||||
|
||||
return CompositedTransformTarget(
|
||||
link: choreographer.itBarLinkAndKey.link,
|
||||
child: Container(
|
||||
key: choreographer.itBarLinkAndKey.key,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).brightness == Brightness.light
|
||||
? Colors.white
|
||||
: Colors.black,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(AppConfig.borderRadius),
|
||||
topRight: Radius.circular(AppConfig.borderRadius),
|
||||
),
|
||||
),
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.fromLTRB(0, 3, 3, 3),
|
||||
child: Stack(
|
||||
children: [
|
||||
SingleChildScrollView(
|
||||
child: Column(
|
||||
return AnimatedSize(
|
||||
duration: itController.willOpen
|
||||
? const Duration(milliseconds: 2000)
|
||||
: const Duration(milliseconds: 500),
|
||||
curve: Curves.fastOutSlowIn,
|
||||
clipBehavior: Clip.none,
|
||||
child: !itController.willOpen
|
||||
? const SizedBox()
|
||||
: CompositedTransformTarget(
|
||||
link: choreographer.itBarLinkAndKey.link,
|
||||
child: AnimatedOpacity(
|
||||
duration: itController.willOpen
|
||||
? const Duration(milliseconds: 2000)
|
||||
: const Duration(milliseconds: 500),
|
||||
opacity: itController.willOpen ? 1.0 : 0.0,
|
||||
child: Container(
|
||||
key: choreographer.itBarLinkAndKey.key,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).brightness == Brightness.light
|
||||
? Colors.white
|
||||
: Colors.black,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(AppConfig.borderRadius),
|
||||
topRight: Radius.circular(AppConfig.borderRadius),
|
||||
),
|
||||
),
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.fromLTRB(0, 3, 3, 3),
|
||||
child: Stack(
|
||||
children: [
|
||||
// Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// // Row(
|
||||
// // mainAxisAlignment: MainAxisAlignment.start,
|
||||
// // crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// // children: [
|
||||
// // CounterDisplay(
|
||||
// // correct: controller.correctChoices,
|
||||
// // custom: controller.customChoices,
|
||||
// // incorrect: controller.incorrectChoices,
|
||||
// // yellow: controller.wildcardChoices,
|
||||
// // ),
|
||||
// // CompositedTransformTarget(
|
||||
// // link: choreographer.itBotLayerLinkAndKey.link,
|
||||
// // child: ITBotButton(
|
||||
// // key: choreographer.itBotLayerLinkAndKey.key,
|
||||
// // choreographer: choreographer,
|
||||
// // ),
|
||||
// // ),
|
||||
// // ],
|
||||
// // ),
|
||||
// ITCloseButton(choreographer: choreographer),
|
||||
// ],
|
||||
// ),
|
||||
// const SizedBox(height: 40.0),
|
||||
OriginalText(controller: controller),
|
||||
const SizedBox(height: 7.0),
|
||||
IntrinsicHeight(
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(minHeight: 80),
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: Center(
|
||||
child: controller.choreographer.errorService.isError
|
||||
? ITError(
|
||||
error: controller
|
||||
SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
// Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// // Row(
|
||||
// // mainAxisAlignment: MainAxisAlignment.start,
|
||||
// // crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// // children: [
|
||||
// // CounterDisplay(
|
||||
// // correct: controller.correctChoices,
|
||||
// // custom: controller.customChoices,
|
||||
// // incorrect: controller.incorrectChoices,
|
||||
// // yellow: controller.wildcardChoices,
|
||||
// // ),
|
||||
// // CompositedTransformTarget(
|
||||
// // link: choreographer.itBotLayerLinkAndKey.link,
|
||||
// // child: ITBotButton(
|
||||
// // key: choreographer.itBotLayerLinkAndKey.key,
|
||||
// // choreographer: choreographer,
|
||||
// // ),
|
||||
// // ),
|
||||
// // ],
|
||||
// // ),
|
||||
// ITCloseButton(choreographer: choreographer),
|
||||
// ],
|
||||
// ),
|
||||
// const SizedBox(height: 40.0),
|
||||
OriginalText(controller: itController),
|
||||
const SizedBox(height: 7.0),
|
||||
IntrinsicHeight(
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(minHeight: 80),
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: Center(
|
||||
child: itController.choreographer.errorService.isError
|
||||
? ITError(
|
||||
error: itController
|
||||
.choreographer.errorService.error!,
|
||||
controller: controller,
|
||||
controller: itController,
|
||||
)
|
||||
: controller.showChoiceFeedback
|
||||
? ChoiceFeedbackText(controller: controller)
|
||||
: controller.isTranslationDone
|
||||
? TranslationFeedback(
|
||||
controller: controller,
|
||||
)
|
||||
: ITChoices(controller: controller),
|
||||
),
|
||||
: itController.showChoiceFeedback
|
||||
? ChoiceFeedbackText(controller: itController)
|
||||
: itController.isTranslationDone
|
||||
? TranslationFeedback(
|
||||
controller: itController,
|
||||
)
|
||||
: ITChoices(controller: itController),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0.0,
|
||||
right: 0.0,
|
||||
child: ITCloseButton(choreographer: choreographer),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0.0,
|
||||
right: 0.0,
|
||||
child: ITCloseButton(choreographer: choreographer),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -184,10 +199,23 @@ class OriginalText extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
if (!controller.isEditingSourceText && controller.sourceText != null)
|
||||
IconButton(
|
||||
onPressed: () => controller.setIsEditingSourceText(true),
|
||||
icon: const Icon(Icons.edit_outlined),
|
||||
if (
|
||||
!controller.isEditingSourceText
|
||||
&& controller.sourceText != null
|
||||
)
|
||||
AnimatedOpacity(
|
||||
duration: const Duration(milliseconds: 500),
|
||||
opacity: controller.nextITStep != null
|
||||
? 1.0
|
||||
: 0.0,
|
||||
child: IconButton(
|
||||
onPressed: () => {
|
||||
if (controller.nextITStep != null) {
|
||||
controller.setIsEditingSourceText(true),
|
||||
},
|
||||
},
|
||||
icon: const Icon(Icons.edit_outlined),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class ITBotButton extends StatelessWidget {
|
|||
);
|
||||
|
||||
return IconButton(
|
||||
icon: const BotFace(width: 40.0, expression: BotExpression.right),
|
||||
icon: const BotFace(width: 40.0, expression: BotExpression.idle),
|
||||
onPressed: () =>
|
||||
choreographer.pangeaController.instructions.showInstructionsPopup(
|
||||
context,
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ class ITFeedbackCardView extends StatelessWidget {
|
|||
children: [
|
||||
CardHeader(
|
||||
text: controller.widget.req.chosenContinuance,
|
||||
botExpression: BotExpression.down,
|
||||
botExpression: BotExpression.nonGold,
|
||||
),
|
||||
Text(
|
||||
controller.widget.choiceFeedback,
|
||||
|
|
|
|||
|
|
@ -11,5 +11,6 @@ class PLocalKey {
|
|||
static const String dismissedPaywall = 'dismissedPaywall';
|
||||
static const String paywallBackoff = 'paywallBackoff';
|
||||
static const String autoPlayMessages = 'autoPlayMessages';
|
||||
static const String itAutoPlay = 'itAutoPlay';
|
||||
static const String messagesSinceUpdate = 'messagesSinceLastUpdate';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,17 +95,16 @@ class ModelKey {
|
|||
static const String languageLevel = "difficulty";
|
||||
static const String safetyModeration = "safety_moderation";
|
||||
static const String mode = "mode";
|
||||
static const String custom = "custom";
|
||||
static const String discussionTopic = "discussion_topic";
|
||||
static const String discussionKeywords = "discussion_keywords";
|
||||
static const String discussionTriggerScheduleEnabled =
|
||||
"discussion_trigger_schedule_enabled";
|
||||
static const String discussionTriggerScheduleHourInterval =
|
||||
"discussion_trigger_schedule_hour_interval";
|
||||
static const String discussionTriggerReactionEnabled =
|
||||
"discussion_trigger_reaction_enabled";
|
||||
static const String discussionTriggerReactionKey =
|
||||
"discussion_trigger_reaction_key";
|
||||
static const String customSystemPrompt = "custom_system_prompt";
|
||||
static const String customTriggerReactionEnabled =
|
||||
"custom_trigger_reaction_enabled";
|
||||
static const String customTriggerReactionKey = "custom_trigger_reaction_key";
|
||||
|
||||
static const String prevEventId = "prev_event_id";
|
||||
static const String prevLastUpdated = "prev_last_updated";
|
||||
|
|
|
|||
|
|
@ -62,12 +62,13 @@ class AnalyticsController extends BaseController {
|
|||
timeSpan.toString(),
|
||||
local: true,
|
||||
);
|
||||
setState();
|
||||
}
|
||||
|
||||
///////// SPACE ANALYTICS LANGUAGES //////////
|
||||
String get _analyticsSpaceLangKey => "ANALYTICS_SPACE_LANG_KEY";
|
||||
|
||||
LanguageModel get currentAnalyticsSpaceLang {
|
||||
LanguageModel get currentAnalyticsLang {
|
||||
try {
|
||||
final String? str = _pangeaController.pStoreService.read(
|
||||
_analyticsSpaceLangKey,
|
||||
|
|
@ -83,41 +84,43 @@ class AnalyticsController extends BaseController {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> setCurrentAnalyticsSpaceLang(LanguageModel lang) async {
|
||||
Future<void> setCurrentAnalyticsLang(LanguageModel lang) async {
|
||||
await _pangeaController.pStoreService.save(
|
||||
_analyticsSpaceLangKey,
|
||||
lang.langCode,
|
||||
local: true,
|
||||
);
|
||||
setState();
|
||||
}
|
||||
|
||||
/// given an analytics event type and the current analytics language,
|
||||
/// get the last time the user updated their analytics
|
||||
Future<DateTime?> myAnalyticsLastUpdated(String type) async {
|
||||
// given an analytics event type, get the last updated times
|
||||
// for each of the user's analytics rooms and return the most recent
|
||||
// Most Recent instead of the oldest because, for instance:
|
||||
// My last Spanish event was sent 3 days ago.
|
||||
// My last English event was sent 1 day ago.
|
||||
// When I go to check if the cached data is out of date, the cached item was set 2 days ago.
|
||||
// I know there’s new data available because the English update data (the most recent) is after the cache’s creation time.
|
||||
// So, I should update the cache.
|
||||
final List<Room> analyticsRooms = _pangeaController
|
||||
.matrixState.client.allMyAnalyticsRooms
|
||||
.where((room) => room.isAnalyticsRoom)
|
||||
.toList();
|
||||
|
||||
final List<DateTime> lastUpdates = [];
|
||||
final Map<String, DateTime> langCodeLastUpdates = {};
|
||||
for (final Room analyticsRoom in analyticsRooms) {
|
||||
final String? roomLang = analyticsRoom.madeForLang;
|
||||
if (roomLang == null) continue;
|
||||
final DateTime? lastUpdated = await analyticsRoom.analyticsLastUpdated(
|
||||
type,
|
||||
_pangeaController.matrixState.client.userID!,
|
||||
);
|
||||
if (lastUpdated != null) {
|
||||
lastUpdates.add(lastUpdated);
|
||||
langCodeLastUpdates[roomLang] = lastUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastUpdates.isEmpty) return null;
|
||||
return lastUpdates.reduce(
|
||||
if (langCodeLastUpdates.isEmpty) return null;
|
||||
final String? l2Code =
|
||||
_pangeaController.languageController.userL2?.langCode;
|
||||
if (l2Code != null && langCodeLastUpdates.containsKey(l2Code)) {
|
||||
return langCodeLastUpdates[l2Code];
|
||||
}
|
||||
return langCodeLastUpdates.values.reduce(
|
||||
(check, mostRecent) => check.isAfter(mostRecent) ? check : mostRecent,
|
||||
);
|
||||
}
|
||||
|
|
@ -134,7 +137,7 @@ class AnalyticsController extends BaseController {
|
|||
final List<Future<DateTime?>> lastUpdatedFutures = [];
|
||||
for (final student in space.students) {
|
||||
final Room? analyticsRoom = _pangeaController.matrixState.client
|
||||
.analyticsRoomLocal(currentAnalyticsSpaceLang.langCode, student.id);
|
||||
.analyticsRoomLocal(currentAnalyticsLang.langCode, student.id);
|
||||
if (analyticsRoom == null) continue;
|
||||
lastUpdatedFutures.add(
|
||||
analyticsRoom.analyticsLastUpdated(
|
||||
|
|
@ -177,28 +180,20 @@ class AnalyticsController extends BaseController {
|
|||
|
||||
//////////////////////////// MESSAGE SUMMARY ANALYTICS ////////////////////////////
|
||||
|
||||
/// get all the summary analytics events for the current user
|
||||
/// in the current language's analytics room
|
||||
Future<List<SummaryAnalyticsEvent>> mySummaryAnalytics() async {
|
||||
// gets all the summary analytics events for the user
|
||||
// since the current timespace's cut off date
|
||||
final analyticsRooms =
|
||||
_pangeaController.matrixState.client.allMyAnalyticsRooms;
|
||||
final Room? analyticsRoom = _pangeaController.matrixState.client
|
||||
.analyticsRoomLocal(currentAnalyticsLang.langCode);
|
||||
if (analyticsRoom == null) return [];
|
||||
|
||||
final List<SummaryAnalyticsEvent> allEvents = [];
|
||||
|
||||
// TODO switch to using list of futures
|
||||
for (final Room analyticsRoom in analyticsRooms) {
|
||||
final List<AnalyticsEvent>? roomEvents =
|
||||
await analyticsRoom.getAnalyticsEvents(
|
||||
type: PangeaEventTypes.summaryAnalytics,
|
||||
since: currentAnalyticsTimeSpan.cutOffDate,
|
||||
userId: _pangeaController.matrixState.client.userID!,
|
||||
);
|
||||
|
||||
allEvents.addAll(
|
||||
roomEvents?.cast<SummaryAnalyticsEvent>() ?? [],
|
||||
);
|
||||
}
|
||||
return allEvents;
|
||||
final List<AnalyticsEvent>? roomEvents =
|
||||
await analyticsRoom.getAnalyticsEvents(
|
||||
type: PangeaEventTypes.summaryAnalytics,
|
||||
since: currentAnalyticsTimeSpan.cutOffDate,
|
||||
userId: _pangeaController.matrixState.client.userID!,
|
||||
);
|
||||
return roomEvents?.cast<SummaryAnalyticsEvent>() ?? [];
|
||||
}
|
||||
|
||||
Future<List<SummaryAnalyticsEvent>> spaceMemberAnalytics(
|
||||
|
|
@ -216,7 +211,7 @@ class AnalyticsController extends BaseController {
|
|||
final List<SummaryAnalyticsEvent> analyticsEvents = [];
|
||||
for (final student in space.students) {
|
||||
final Room? analyticsRoom = _pangeaController.matrixState.client
|
||||
.analyticsRoomLocal(currentAnalyticsSpaceLang.langCode, student.id);
|
||||
.analyticsRoomLocal(currentAnalyticsLang.langCode, student.id);
|
||||
|
||||
if (analyticsRoom != null) {
|
||||
final List<AnalyticsEvent>? roomEvents =
|
||||
|
|
@ -261,7 +256,7 @@ class AnalyticsController extends BaseController {
|
|||
(e.defaultSelected.type == defaultSelected.type) &&
|
||||
(e.selected?.id == selected?.id) &&
|
||||
(e.selected?.type == selected?.type) &&
|
||||
(e.langCode == currentAnalyticsSpaceLang.langCode),
|
||||
(e.langCode == currentAnalyticsLang.langCode),
|
||||
);
|
||||
|
||||
if (index != -1) {
|
||||
|
|
@ -289,7 +284,7 @@ class AnalyticsController extends BaseController {
|
|||
chartAnalyticsModel: chartAnalyticsModel,
|
||||
defaultSelected: defaultSelected,
|
||||
selected: selected,
|
||||
langCode: currentAnalyticsSpaceLang.langCode,
|
||||
langCode: currentAnalyticsLang.langCode,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -525,20 +520,18 @@ class AnalyticsController extends BaseController {
|
|||
//////////////////////////// CONSTRUCTS ////////////////////////////
|
||||
|
||||
Future<List<ConstructAnalyticsEvent>> allMyConstructs() async {
|
||||
final List<Room> analyticsRooms =
|
||||
_pangeaController.matrixState.client.allMyAnalyticsRooms;
|
||||
final Room? analyticsRoom = _pangeaController.matrixState.client
|
||||
.analyticsRoomLocal(currentAnalyticsLang.langCode);
|
||||
if (analyticsRoom == null) return [];
|
||||
|
||||
final List<ConstructAnalyticsEvent> allConstructs = [];
|
||||
for (final Room analyticsRoom in analyticsRooms) {
|
||||
final List<ConstructAnalyticsEvent>? roomEvents =
|
||||
(await analyticsRoom.getAnalyticsEvents(
|
||||
type: PangeaEventTypes.construct,
|
||||
since: currentAnalyticsTimeSpan.cutOffDate,
|
||||
userId: _pangeaController.matrixState.client.userID!,
|
||||
))
|
||||
?.cast<ConstructAnalyticsEvent>();
|
||||
allConstructs.addAll(roomEvents ?? []);
|
||||
}
|
||||
final List<ConstructAnalyticsEvent>? roomEvents =
|
||||
(await analyticsRoom.getAnalyticsEvents(
|
||||
type: PangeaEventTypes.construct,
|
||||
since: currentAnalyticsTimeSpan.cutOffDate,
|
||||
userId: _pangeaController.matrixState.client.userID!,
|
||||
))
|
||||
?.cast<ConstructAnalyticsEvent>();
|
||||
final List<ConstructAnalyticsEvent> allConstructs = roomEvents ?? [];
|
||||
|
||||
final List<String> adminSpaceRooms =
|
||||
await _pangeaController.matrixState.client.teacherRoomIds;
|
||||
|
|
@ -561,7 +554,7 @@ class AnalyticsController extends BaseController {
|
|||
final List<ConstructAnalyticsEvent> constructEvents = [];
|
||||
for (final student in space.students) {
|
||||
final Room? analyticsRoom = _pangeaController.matrixState.client
|
||||
.analyticsRoomLocal(currentAnalyticsSpaceLang.langCode, student.id);
|
||||
.analyticsRoomLocal(currentAnalyticsLang.langCode, student.id);
|
||||
if (analyticsRoom != null) {
|
||||
final List<ConstructAnalyticsEvent>? roomEvents =
|
||||
(await analyticsRoom.getAnalyticsEvents(
|
||||
|
|
@ -661,7 +654,7 @@ class AnalyticsController extends BaseController {
|
|||
e.defaultSelected.type == defaultSelected.type &&
|
||||
e.selected?.id == selected?.id &&
|
||||
e.selected?.type == selected?.type &&
|
||||
e.langCode == currentAnalyticsSpaceLang.langCode,
|
||||
e.langCode == currentAnalyticsLang.langCode,
|
||||
);
|
||||
|
||||
if (index > -1) {
|
||||
|
|
@ -687,7 +680,7 @@ class AnalyticsController extends BaseController {
|
|||
events: List.from(events),
|
||||
defaultSelected: defaultSelected,
|
||||
selected: selected,
|
||||
langCode: currentAnalyticsSpaceLang.langCode,
|
||||
langCode: currentAnalyticsLang.langCode,
|
||||
);
|
||||
_cachedConstructs.add(entry);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ class UserController extends BaseController {
|
|||
: null;
|
||||
|
||||
final bool? autoPlay = migratedProfileInfo(MatrixProfile.autoPlayMessages);
|
||||
final bool? itAutoPlay = migratedProfileInfo(MatrixProfile.itAutoPlay);
|
||||
final bool? trial = migratedProfileInfo(MatrixProfile.activatedFreeTrial);
|
||||
final bool? interactiveTranslator =
|
||||
migratedProfileInfo(MatrixProfile.interactiveTranslator);
|
||||
|
|
@ -142,6 +143,7 @@ class UserController extends BaseController {
|
|||
await updateMatrixProfile(
|
||||
dateOfBirth: dob,
|
||||
autoPlayMessages: autoPlay,
|
||||
itAutoPlay: itAutoPlay,
|
||||
activatedFreeTrial: trial,
|
||||
interactiveTranslator: interactiveTranslator,
|
||||
interactiveGrammar: interactiveGrammar,
|
||||
|
|
@ -223,6 +225,7 @@ class UserController extends BaseController {
|
|||
Future<void> updateMatrixProfile({
|
||||
String? dateOfBirth,
|
||||
bool? autoPlayMessages,
|
||||
bool? itAutoPlay,
|
||||
bool? activatedFreeTrial,
|
||||
bool? interactiveTranslator,
|
||||
bool? interactiveGrammar,
|
||||
|
|
@ -251,6 +254,12 @@ class UserController extends BaseController {
|
|||
autoPlayMessages,
|
||||
);
|
||||
}
|
||||
if (itAutoPlay != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.itAutoPlay.title,
|
||||
itAutoPlay,
|
||||
);
|
||||
}
|
||||
if (activatedFreeTrial != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.activatedFreeTrial.title,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
enum SpanDataTypeEnum {
|
||||
|
|
@ -32,7 +32,10 @@ extension SpanDataTypeEnumExt on SpanDataTypeEnum {
|
|||
case SpanDataTypeEnum.correction:
|
||||
return L10n.of(context)!.correctionDefaultPrompt;
|
||||
case SpanDataTypeEnum.itStart:
|
||||
return L10n.of(context)!.needsItMessage;
|
||||
return L10n.of(context)!.needsItMessage(
|
||||
MatrixState.pangeaController.languageController.userL2?.displayName ??
|
||||
L10n.of(context)!.targetLanguage,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,44 +13,66 @@ class BotOptionsModel {
|
|||
List<String> keywords;
|
||||
bool safetyModeration;
|
||||
String mode;
|
||||
String? custom;
|
||||
String? discussionTopic;
|
||||
String? discussionKeywords;
|
||||
bool? discussionTriggerScheduleEnabled;
|
||||
int? discussionTriggerScheduleHourInterval;
|
||||
bool? discussionTriggerReactionEnabled;
|
||||
String? discussionTriggerReactionKey;
|
||||
String? customSystemPrompt;
|
||||
bool? customTriggerReactionEnabled;
|
||||
String? customTriggerReactionKey;
|
||||
|
||||
BotOptionsModel({
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// General Bot Options
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
this.languageLevel,
|
||||
this.topic = "General Conversation",
|
||||
this.keywords = const [],
|
||||
this.safetyModeration = true,
|
||||
this.mode = "discussion",
|
||||
this.custom = "",
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Discussion Mode Options
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
this.discussionTopic,
|
||||
this.discussionKeywords,
|
||||
this.discussionTriggerScheduleEnabled,
|
||||
this.discussionTriggerScheduleHourInterval,
|
||||
this.discussionTriggerReactionEnabled = true,
|
||||
this.discussionTriggerReactionKey,
|
||||
this.discussionTriggerReactionKey = "⏩",
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Custom Mode Options
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
this.customSystemPrompt,
|
||||
this.customTriggerReactionEnabled = true,
|
||||
this.customTriggerReactionKey = "⏩",
|
||||
});
|
||||
|
||||
factory BotOptionsModel.fromJson(json) {
|
||||
return BotOptionsModel(
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// General Bot Options
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
languageLevel: json[ModelKey.languageLevel],
|
||||
safetyModeration: json[ModelKey.safetyModeration] ?? true,
|
||||
mode: json[ModelKey.mode] ?? "discussion",
|
||||
custom: json[ModelKey.custom],
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Discussion Mode Options
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
discussionTopic: json[ModelKey.discussionTopic],
|
||||
discussionKeywords: json[ModelKey.discussionKeywords],
|
||||
discussionTriggerScheduleEnabled:
|
||||
json[ModelKey.discussionTriggerScheduleEnabled],
|
||||
discussionTriggerScheduleHourInterval:
|
||||
json[ModelKey.discussionTriggerScheduleHourInterval],
|
||||
discussionTriggerReactionEnabled:
|
||||
json[ModelKey.discussionTriggerReactionEnabled],
|
||||
discussionTriggerReactionKey: json[ModelKey.discussionTriggerReactionKey],
|
||||
json[ModelKey.discussionTriggerReactionEnabled] ?? true,
|
||||
discussionTriggerReactionKey:
|
||||
json[ModelKey.discussionTriggerReactionKey] ?? "⏩",
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Custom Mode Options
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
customSystemPrompt: json[ModelKey.customSystemPrompt],
|
||||
customTriggerReactionEnabled:
|
||||
json[ModelKey.customTriggerReactionEnabled] ?? true,
|
||||
customTriggerReactionKey: json[ModelKey.customTriggerReactionKey] ?? "⏩",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -61,17 +83,16 @@ class BotOptionsModel {
|
|||
data[ModelKey.languageLevel] = languageLevel;
|
||||
data[ModelKey.safetyModeration] = safetyModeration;
|
||||
data[ModelKey.mode] = mode;
|
||||
data[ModelKey.custom] = custom;
|
||||
data[ModelKey.discussionTopic] = discussionTopic;
|
||||
data[ModelKey.discussionKeywords] = discussionKeywords;
|
||||
data[ModelKey.discussionTriggerScheduleEnabled] =
|
||||
discussionTriggerScheduleEnabled;
|
||||
data[ModelKey.discussionTriggerScheduleHourInterval] =
|
||||
discussionTriggerScheduleHourInterval;
|
||||
data[ModelKey.discussionTriggerReactionEnabled] =
|
||||
discussionTriggerReactionEnabled;
|
||||
discussionTriggerReactionEnabled ?? true;
|
||||
data[ModelKey.discussionTriggerReactionKey] =
|
||||
discussionTriggerReactionKey;
|
||||
discussionTriggerReactionKey ?? "⏩";
|
||||
data[ModelKey.customSystemPrompt] = customSystemPrompt;
|
||||
data[ModelKey.customTriggerReactionEnabled] =
|
||||
customTriggerReactionEnabled ?? true;
|
||||
data[ModelKey.customTriggerReactionKey] = customTriggerReactionKey ?? "⏩";
|
||||
return data;
|
||||
} catch (e, s) {
|
||||
debugger(when: kDebugMode);
|
||||
|
|
@ -92,27 +113,27 @@ class BotOptionsModel {
|
|||
case ModelKey.mode:
|
||||
mode = value;
|
||||
break;
|
||||
case ModelKey.custom:
|
||||
custom = value;
|
||||
break;
|
||||
case ModelKey.discussionTopic:
|
||||
discussionTopic = value;
|
||||
break;
|
||||
case ModelKey.discussionKeywords:
|
||||
discussionKeywords = value;
|
||||
break;
|
||||
case ModelKey.discussionTriggerScheduleEnabled:
|
||||
discussionTriggerScheduleEnabled = value;
|
||||
break;
|
||||
case ModelKey.discussionTriggerScheduleHourInterval:
|
||||
discussionTriggerScheduleHourInterval = value;
|
||||
break;
|
||||
case ModelKey.discussionTriggerReactionEnabled:
|
||||
discussionTriggerReactionEnabled = value;
|
||||
break;
|
||||
case ModelKey.discussionTriggerReactionKey:
|
||||
discussionTriggerReactionKey = value;
|
||||
break;
|
||||
case ModelKey.customSystemPrompt:
|
||||
customSystemPrompt = value;
|
||||
break;
|
||||
case ModelKey.customTriggerReactionEnabled:
|
||||
customTriggerReactionEnabled = value;
|
||||
break;
|
||||
case ModelKey.customTriggerReactionKey:
|
||||
customTriggerReactionKey = value;
|
||||
break;
|
||||
default:
|
||||
throw Exception('Invalid key for bot options - $key');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ class PUserModel {
|
|||
enum MatrixProfile {
|
||||
dateOfBirth,
|
||||
autoPlayMessages,
|
||||
itAutoPlay,
|
||||
activatedFreeTrial,
|
||||
interactiveTranslator,
|
||||
interactiveGrammar,
|
||||
|
|
@ -79,6 +80,8 @@ extension MatrixProfileExtension on MatrixProfile {
|
|||
return ModelKey.userDateOfBirth;
|
||||
case MatrixProfile.autoPlayMessages:
|
||||
return PLocalKey.autoPlayMessages;
|
||||
case MatrixProfile.itAutoPlay:
|
||||
return PLocalKey.itAutoPlay;
|
||||
case MatrixProfile.activatedFreeTrial:
|
||||
return PLocalKey.activatedTrialKey;
|
||||
case MatrixProfile.interactiveTranslator:
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ class AnalyticsLanguageButton extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PopupMenuButton<LanguageModel>(
|
||||
icon: const Icon(Icons.language_outlined),
|
||||
tooltip: L10n.of(context)!.changeAnalyticsLanguage,
|
||||
initialValue: value,
|
||||
onSelected: (LanguageModel? lang) {
|
||||
|
|
@ -33,6 +32,21 @@ class AnalyticsLanguageButton extends StatelessWidget {
|
|||
child: Text(lang.getDisplayName(context) ?? lang.langCode),
|
||||
);
|
||||
}).toList(),
|
||||
child: TextButton.icon(
|
||||
label: Text(
|
||||
L10n.of(context)!.languageButtonLabel(
|
||||
value.getDisplayName(context) ?? value.langCode,
|
||||
),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
icon: Icon(
|
||||
Icons.language_outlined,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
onPressed: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ class BaseAnalyticsController extends State<BaseAnalyticsPage> {
|
|||
}
|
||||
|
||||
Future<void> toggleSpaceLang(LanguageModel lang) async {
|
||||
await pangeaController.analytics.setCurrentAnalyticsSpaceLang(lang);
|
||||
await pangeaController.analytics.setCurrentAnalyticsLang(lang);
|
||||
await setChartData();
|
||||
refreshStream.add(false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,28 +108,26 @@ class BaseAnalyticsView extends StatelessWidget {
|
|||
? Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
if (controller.widget.defaultSelected.type ==
|
||||
AnalyticsEntryType.student)
|
||||
IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: controller.onRefresh,
|
||||
tooltip: L10n.of(context)!.refresh,
|
||||
),
|
||||
// if (controller.widget.defaultSelected.type ==
|
||||
// AnalyticsEntryType.student)
|
||||
// IconButton(
|
||||
// icon: const Icon(Icons.refresh),
|
||||
// onPressed: controller.onRefresh,
|
||||
// tooltip: L10n.of(context)!.refresh,
|
||||
// ),
|
||||
TimeSpanMenuButton(
|
||||
value: controller.currentTimeSpan,
|
||||
onChange: (TimeSpan value) =>
|
||||
controller.toggleTimeSpan(context, value),
|
||||
),
|
||||
if (controller.widget.defaultSelected.type ==
|
||||
AnalyticsEntryType.space)
|
||||
AnalyticsLanguageButton(
|
||||
value: controller.pangeaController.analytics
|
||||
.currentAnalyticsSpaceLang,
|
||||
onChange: (lang) => controller.toggleSpaceLang(lang),
|
||||
languages: controller.widget.targetLanguages,
|
||||
),
|
||||
AnalyticsLanguageButton(
|
||||
value: controller
|
||||
.pangeaController.analytics.currentAnalyticsLang,
|
||||
onChange: (lang) => controller.toggleSpaceLang(lang),
|
||||
languages: controller.widget.targetLanguages,
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
|
|
|
|||
|
|
@ -355,15 +355,17 @@ class ConstructMessagesDialog extends StatelessWidget {
|
|||
|
||||
final msgEventMatches = controller.getMessageEventMatches();
|
||||
|
||||
final noData = controller.constructs![controller.lemmaIndex].uses.length >
|
||||
controller._msgEvents.length;
|
||||
|
||||
return AlertDialog(
|
||||
title: Center(child: Text(controller.widget.controller.currentLemma!)),
|
||||
content: SizedBox(
|
||||
height: 350,
|
||||
width: 500,
|
||||
height: noData ? 90 : 250,
|
||||
width: noData ? 200 : 400,
|
||||
child: Column(
|
||||
children: [
|
||||
if (controller.constructs![controller.lemmaIndex].uses.length >
|
||||
controller._msgEvents.length)
|
||||
if (noData)
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
|
|
@ -398,8 +400,8 @@ class ConstructMessagesDialog extends StatelessWidget {
|
|||
child: Text(
|
||||
L10n.of(context)!.close.toUpperCase(),
|
||||
style: TextStyle(
|
||||
color:
|
||||
Theme.of(context).textTheme.bodyMedium?.color?.withAlpha(150),
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -23,13 +23,24 @@ class AnalyticsSpaceList extends StatefulWidget {
|
|||
class AnalyticsSpaceListController extends State<AnalyticsSpaceList> {
|
||||
PangeaController pangeaController = MatrixState.pangeaController;
|
||||
List<Room> spaces = [];
|
||||
StreamSubscription? stateSub;
|
||||
List<LanguageModel> targetLanguages = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
setSpaceList().then((_) => setTargetLanguages());
|
||||
|
||||
// reload dropdowns when their values change in analytics page
|
||||
stateSub = pangeaController.analytics.stateStream.listen(
|
||||
(_) => setState(() {}),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
stateSub?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
StreamController refreshStream = StreamController.broadcast();
|
||||
|
|
@ -71,7 +82,7 @@ class AnalyticsSpaceListController extends State<AnalyticsSpaceList> {
|
|||
}
|
||||
|
||||
Future<void> toggleSpaceLang(LanguageModel lang) async {
|
||||
await pangeaController.analytics.setCurrentAnalyticsSpaceLang(lang);
|
||||
await pangeaController.analytics.setCurrentAnalyticsLang(lang);
|
||||
refreshStream.add(false);
|
||||
setState(() {});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:fluffychat/pangea/enum/time_span.dart';
|
||||
import 'package:fluffychat/pangea/pages/analytics/analytics_language_button.dart';
|
||||
import 'package:fluffychat/pangea/pages/analytics/analytics_list_tile.dart';
|
||||
import 'package:fluffychat/pangea/pages/analytics/time_span_menu_button.dart';
|
||||
|
|
@ -5,7 +6,6 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../enum/time_span.dart';
|
||||
import '../base_analytics.dart';
|
||||
import 'space_list.dart';
|
||||
|
||||
|
|
@ -32,27 +32,29 @@ class AnalyticsSpaceListView extends StatelessWidget {
|
|||
icon: const Icon(Icons.close_outlined),
|
||||
onPressed: () => context.pop(),
|
||||
),
|
||||
actions: [
|
||||
TimeSpanMenuButton(
|
||||
value:
|
||||
controller.pangeaController.analytics.currentAnalyticsTimeSpan,
|
||||
onChange: (TimeSpan value) => controller.toggleTimeSpan(
|
||||
context,
|
||||
value,
|
||||
),
|
||||
),
|
||||
AnalyticsLanguageButton(
|
||||
value:
|
||||
controller.pangeaController.analytics.currentAnalyticsSpaceLang,
|
||||
onChange: (lang) => controller.toggleSpaceLang(lang),
|
||||
languages: controller.targetLanguages.isEmpty
|
||||
? controller.pangeaController.pLanguageStore.targetOptions
|
||||
: controller.targetLanguages,
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
TimeSpanMenuButton(
|
||||
value: controller
|
||||
.pangeaController.analytics.currentAnalyticsTimeSpan,
|
||||
onChange: (TimeSpan value) => controller.toggleTimeSpan(
|
||||
context,
|
||||
value,
|
||||
),
|
||||
),
|
||||
AnalyticsLanguageButton(
|
||||
value:
|
||||
controller.pangeaController.analytics.currentAnalyticsLang,
|
||||
onChange: (lang) => controller.toggleSpaceLang(lang),
|
||||
languages:
|
||||
controller.pangeaController.pLanguageStore.targetOptions,
|
||||
),
|
||||
],
|
||||
),
|
||||
Flexible(
|
||||
child: ListView.builder(
|
||||
itemCount: controller.spaces.length,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:fluffychat/pangea/constants/language_keys.dart';
|
||||
import 'package:fluffychat/pangea/controllers/language_list_controller.dart';
|
||||
import 'package:fluffychat/pangea/enum/bar_chart_view_enum.dart';
|
||||
import 'package:fluffychat/pangea/extensions/client_extension/client_extension.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/models/language_model.dart';
|
||||
import 'package:fluffychat/pangea/widgets/common/list_placeholder.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -75,6 +80,24 @@ class StudentAnalyticsController extends State<StudentAnalyticsPage> {
|
|||
return id;
|
||||
}
|
||||
|
||||
List<LanguageModel> get targetLanguages {
|
||||
final LanguageModel? l2 =
|
||||
_pangeaController.languageController.activeL2Model();
|
||||
final List<LanguageModel> analyticsRoomLangs =
|
||||
_pangeaController.matrixState.client.allMyAnalyticsRooms
|
||||
.map((analyticsRoom) => analyticsRoom.madeForLang)
|
||||
.where((langCode) => langCode != null)
|
||||
.map((langCode) => PangeaLanguage.byLangCode(langCode!))
|
||||
.where(
|
||||
(langModel) => langModel.langCode != LanguageKeys.unknownLanguage,
|
||||
)
|
||||
.toList();
|
||||
if (l2 != null) {
|
||||
analyticsRoomLangs.add(l2);
|
||||
}
|
||||
return analyticsRoomLangs.toSet().toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PLoadingStatusV2(
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ class StudentAnalyticsView extends StatelessWidget {
|
|||
AnalyticsEntryType.student,
|
||||
L10n.of(context)!.allChatsAndClasses,
|
||||
),
|
||||
targetLanguages: controller.targetLanguages,
|
||||
)
|
||||
: const SizedBox();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ class TimeSpanMenuButton extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PopupMenuButton<TimeSpan>(
|
||||
icon: const Icon(Icons.calendar_month_outlined),
|
||||
tooltip: L10n.of(context)!.changeDateRange,
|
||||
initialValue: value,
|
||||
onSelected: (TimeSpan? timeSpan) {
|
||||
|
|
@ -32,6 +31,19 @@ class TimeSpanMenuButton extends StatelessWidget {
|
|||
child: Text(timeSpan.string(context)),
|
||||
);
|
||||
}).toList(),
|
||||
child: TextButton.icon(
|
||||
label: Text(
|
||||
value.string(context),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
icon: Icon(
|
||||
Icons.calendar_month_outlined,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
onPressed: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ class ClassDescriptionButton extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final iconColor = Theme.of(context).textTheme.bodyLarge!.color;
|
||||
final ScrollController scrollController = ScrollController();
|
||||
return Column(
|
||||
children: [
|
||||
ListTile(
|
||||
|
|
@ -26,14 +27,27 @@ class ClassDescriptionButton extends StatelessWidget {
|
|||
foregroundColor: iconColor,
|
||||
child: const Icon(Icons.topic_outlined),
|
||||
),
|
||||
subtitle: Text(
|
||||
room.topic.isEmpty
|
||||
? (room.isRoomAdmin
|
||||
? (room.isSpace
|
||||
? L10n.of(context)!.classDescriptionDesc
|
||||
: L10n.of(context)!.chatTopicDesc)
|
||||
: L10n.of(context)!.topicNotSet)
|
||||
: room.topic,
|
||||
subtitle: ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxHeight: 190,
|
||||
),
|
||||
child: Scrollbar(
|
||||
controller: scrollController,
|
||||
interactive: true,
|
||||
child: SingleChildScrollView(
|
||||
controller: scrollController,
|
||||
primary: false,
|
||||
child: Text(
|
||||
room.topic.isEmpty
|
||||
? (room.isRoomAdmin
|
||||
? (room.isSpace
|
||||
? L10n.of(context)!.classDescriptionDesc
|
||||
: L10n.of(context)!.chatTopicDesc)
|
||||
: L10n.of(context)!.topicNotSet)
|
||||
: room.topic,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
room.isSpace
|
||||
|
|
|
|||
|
|
@ -61,6 +61,16 @@ class SettingsLearningView extends StatelessWidget {
|
|||
pStoreKey: setting.toString(),
|
||||
local: false,
|
||||
),
|
||||
PSettingsSwitchListTile.adaptive(
|
||||
defaultValue: controller.pangeaController.pStoreService.read(
|
||||
PLocalKey.itAutoPlay,
|
||||
) ??
|
||||
false,
|
||||
title: L10n.of(context)!.interactiveTranslatorAutoPlaySliderHeader,
|
||||
subtitle: L10n.of(context)!.interactiveTranslatorAutoPlayDesc,
|
||||
pStoreKey: PLocalKey.itAutoPlay,
|
||||
local: false,
|
||||
),
|
||||
PSettingsSwitchListTile.adaptive(
|
||||
defaultValue: controller.pangeaController.pStoreService.read(
|
||||
PLocalKey.autoPlayMessages,
|
||||
|
|
|
|||
|
|
@ -52,6 +52,11 @@ class InstructionsController {
|
|||
String transformTargetKey, [
|
||||
bool showToggle = true,
|
||||
]) async {
|
||||
if (_instructionsShown[key] ?? false) {
|
||||
return;
|
||||
}
|
||||
_instructionsShown[key] = true;
|
||||
|
||||
if (wereInstructionsTurnedOff(key)) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -62,9 +67,6 @@ class InstructionsController {
|
|||
);
|
||||
return;
|
||||
}
|
||||
if (_instructionsShown[key] ?? false) {
|
||||
return;
|
||||
}
|
||||
|
||||
final bool userLangsSet =
|
||||
await _pangeaController.userController.areUserLanguagesSet;
|
||||
|
|
@ -72,8 +74,6 @@ class InstructionsController {
|
|||
return;
|
||||
}
|
||||
|
||||
_instructionsShown[key] = true;
|
||||
|
||||
final botStyle = BotStyle.text(context);
|
||||
Future.delayed(
|
||||
const Duration(seconds: 1),
|
||||
|
|
@ -85,7 +85,7 @@ class InstructionsController {
|
|||
children: [
|
||||
CardHeader(
|
||||
text: key.title(context),
|
||||
botExpression: BotExpression.surprised,
|
||||
botExpression: BotExpression.idle,
|
||||
onClose: () => {_instructionsClosed[key] = true},
|
||||
),
|
||||
const SizedBox(height: 10.0),
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import 'dart:developer';
|
||||
|
||||
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';
|
||||
|
||||
import 'package:fluffychat/pangea/enum/span_data_type.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.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';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
import '../constants/match_rule_ids.dart';
|
||||
import '../models/pangea_match_model.dart';
|
||||
|
||||
|
|
@ -96,7 +96,11 @@ class MatchCopy {
|
|||
switch (afterColon) {
|
||||
case MatchRuleIds.interactiveTranslation:
|
||||
title = l10n.needsItShortMessage;
|
||||
description = l10n.needsItMessage;
|
||||
description = l10n.needsItMessage(
|
||||
MatrixState
|
||||
.pangeaController.languageController.userL2?.displayName ??
|
||||
"target language",
|
||||
);
|
||||
break;
|
||||
case MatchRuleIds.tokenNeedsTranslation:
|
||||
title = l10n.tokenTranslationTitle;
|
||||
|
|
|
|||
|
|
@ -94,61 +94,64 @@ class ToolbarDisplayController {
|
|||
previousEvent: previousEvent,
|
||||
);
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
Widget overlayEntry;
|
||||
if (toolbar == null) return;
|
||||
try {
|
||||
overlayEntry = Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: pangeaMessageEvent.ownMessage
|
||||
? CrossAxisAlignment.end
|
||||
: CrossAxisAlignment.start,
|
||||
children: [
|
||||
toolbarUp ? toolbar! : overlayMessage,
|
||||
const SizedBox(height: 6),
|
||||
toolbarUp ? overlayMessage : toolbar!,
|
||||
],
|
||||
);
|
||||
} catch (err) {
|
||||
debugger(when: kDebugMode);
|
||||
ErrorHandler.logError(e: err, s: StackTrace.current);
|
||||
return;
|
||||
}
|
||||
|
||||
OverlayUtil.showOverlay(
|
||||
context: context,
|
||||
child: overlayEntry,
|
||||
transformTargetId: targetId,
|
||||
targetAnchor: pangeaMessageEvent.ownMessage
|
||||
? toolbarUp
|
||||
? Alignment.bottomRight
|
||||
: Alignment.topRight
|
||||
: toolbarUp
|
||||
? Alignment.bottomLeft
|
||||
: Alignment.topLeft,
|
||||
followerAnchor: pangeaMessageEvent.ownMessage
|
||||
? toolbarUp
|
||||
? Alignment.bottomRight
|
||||
: Alignment.topRight
|
||||
: toolbarUp
|
||||
? Alignment.bottomLeft
|
||||
: Alignment.topLeft,
|
||||
backgroundColor: const Color.fromRGBO(0, 0, 0, 1).withAlpha(100),
|
||||
closePrevOverlay:
|
||||
MatrixState.pangeaController.subscriptionController.isSubscribed,
|
||||
// I'm not sure why I put this here, but it causes the toolbar
|
||||
// not to open immediately after clicking (user has to scroll or move their cursor)
|
||||
// so I'm commenting it out for now
|
||||
// WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
Widget overlayEntry;
|
||||
if (toolbar == null) return;
|
||||
try {
|
||||
overlayEntry = Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: pangeaMessageEvent.ownMessage
|
||||
? CrossAxisAlignment.end
|
||||
: CrossAxisAlignment.start,
|
||||
children: [
|
||||
toolbarUp ? toolbar! : overlayMessage,
|
||||
const SizedBox(height: 6),
|
||||
toolbarUp ? overlayMessage : toolbar!,
|
||||
],
|
||||
);
|
||||
} catch (err) {
|
||||
debugger(when: kDebugMode);
|
||||
ErrorHandler.logError(e: err, s: StackTrace.current);
|
||||
return;
|
||||
}
|
||||
|
||||
if (MatrixState.pAnyState.entries.isNotEmpty) {
|
||||
overlayId = MatrixState.pAnyState.entries.last.hashCode.toString();
|
||||
}
|
||||
OverlayUtil.showOverlay(
|
||||
context: context,
|
||||
child: overlayEntry,
|
||||
transformTargetId: targetId,
|
||||
targetAnchor: pangeaMessageEvent.ownMessage
|
||||
? toolbarUp
|
||||
? Alignment.bottomRight
|
||||
: Alignment.topRight
|
||||
: toolbarUp
|
||||
? Alignment.bottomLeft
|
||||
: Alignment.topLeft,
|
||||
followerAnchor: pangeaMessageEvent.ownMessage
|
||||
? toolbarUp
|
||||
? Alignment.bottomRight
|
||||
: Alignment.topRight
|
||||
: toolbarUp
|
||||
? Alignment.bottomLeft
|
||||
: Alignment.topLeft,
|
||||
backgroundColor: const Color.fromRGBO(0, 0, 0, 1).withAlpha(100),
|
||||
closePrevOverlay:
|
||||
MatrixState.pangeaController.subscriptionController.isSubscribed,
|
||||
);
|
||||
|
||||
if (mode != null) {
|
||||
Future.delayed(
|
||||
const Duration(milliseconds: 100),
|
||||
() => toolbarModeStream.add(mode),
|
||||
);
|
||||
}
|
||||
});
|
||||
if (MatrixState.pAnyState.entries.isNotEmpty) {
|
||||
overlayId = MatrixState.pAnyState.entries.last.hashCode.toString();
|
||||
}
|
||||
|
||||
if (mode != null) {
|
||||
Future.delayed(
|
||||
const Duration(milliseconds: 100),
|
||||
() => toolbarModeStream.add(mode),
|
||||
);
|
||||
}
|
||||
// });
|
||||
}
|
||||
|
||||
bool get highlighted {
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ class MessageTranslationCardState extends State<MessageTranslationCard> {
|
|||
|
||||
String? translationLangCode() {
|
||||
if (widget.immersionMode) return l1Code;
|
||||
final String? originalWrittenCode =
|
||||
widget.messageEvent.originalWritten?.content.langCode;
|
||||
return (l1Code == originalWrittenCode || originalWrittenCode == null)
|
||||
final String? originalSentCode =
|
||||
widget.messageEvent.originalSent?.content.langCode;
|
||||
return (l1Code == originalSentCode || originalSentCode == null)
|
||||
? l2Code
|
||||
: l1Code;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:rive/rive.dart';
|
||||
|
||||
enum BotExpression { surprised, right, addled, left, down, shocked }
|
||||
enum BotExpression { gold, nonGold, addled, idle, surprised }
|
||||
|
||||
class BotFace extends StatefulWidget {
|
||||
final double width;
|
||||
final Color? forceColor;
|
||||
final BotExpression expression;
|
||||
|
||||
class BotFace extends StatelessWidget {
|
||||
const BotFace({
|
||||
super.key,
|
||||
required this.width,
|
||||
|
|
@ -10,23 +15,80 @@ class BotFace extends StatelessWidget {
|
|||
this.forceColor,
|
||||
});
|
||||
|
||||
final double width;
|
||||
final Color? forceColor;
|
||||
final BotExpression expression;
|
||||
@override
|
||||
BotFaceState createState() => BotFaceState();
|
||||
}
|
||||
|
||||
class BotFaceState extends State<BotFace> {
|
||||
Artboard? _artboard;
|
||||
StateMachineController? _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadRiveFile();
|
||||
}
|
||||
|
||||
double mapExpressionToInput(BotExpression expression) {
|
||||
switch (expression) {
|
||||
case BotExpression.gold:
|
||||
return 1.0;
|
||||
case BotExpression.nonGold:
|
||||
return 2.0;
|
||||
case BotExpression.surprised:
|
||||
return 3.0;
|
||||
case BotExpression.addled:
|
||||
return 4.0;
|
||||
default:
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadRiveFile() async {
|
||||
final riveFile = await RiveFile.asset('assets/pangea/bot_faces/pangea_bot.riv');
|
||||
|
||||
final artboard = riveFile.mainArtboard;
|
||||
_controller = StateMachineController
|
||||
.fromArtboard(artboard, 'BotIconStateMachine');
|
||||
|
||||
if (_controller != null) {
|
||||
artboard.addController(_controller!);
|
||||
_controller!.setInputValue(
|
||||
_controller!.stateMachine.inputs[0].id,
|
||||
mapExpressionToInput(widget.expression),
|
||||
);
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_artboard = artboard;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Image.asset(
|
||||
'assets/pangea/bot_faces/${expression.toString().split('.').last}.png',
|
||||
// 'assets/pangea/bot_faces/surprised.png',
|
||||
width: width,
|
||||
height: width,
|
||||
// color: forceColor ??
|
||||
// (Theme.of(context).brightness == Brightness.light
|
||||
// ? Theme.of(context).colorScheme.primary
|
||||
// : Theme.of(context).colorScheme.primary),
|
||||
|
||||
return SizedBox(
|
||||
width: widget.width,
|
||||
height: widget.width,
|
||||
child: _artboard != null
|
||||
? Rive(
|
||||
artboard: _artboard!,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: Container(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(BotFace oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.expression != widget.expression) {
|
||||
_controller!.setInputValue(
|
||||
_controller!.stateMachine.inputs[0].id,
|
||||
mapExpressionToInput(widget.expression),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// extension ParseToString on BotExpressions {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
import 'package:fluffychat/pangea/models/bot_options_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
class ConversationBotCustomSystemPromptInput extends StatelessWidget {
|
||||
final BotOptionsModel initialBotOptions;
|
||||
// call this to update propagate changes to parents
|
||||
final void Function(BotOptionsModel) onChanged;
|
||||
|
||||
const ConversationBotCustomSystemPromptInput({
|
||||
super.key,
|
||||
required this.initialBotOptions,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String customSystemPrompt = initialBotOptions.customSystemPrompt ?? "";
|
||||
|
||||
final TextEditingController textFieldController =
|
||||
TextEditingController(text: customSystemPrompt);
|
||||
|
||||
void setBotCustomSystemPromptAction() async {
|
||||
showDialog(
|
||||
context: context,
|
||||
useRootNavigator: false,
|
||||
builder: (BuildContext context) => AlertDialog(
|
||||
title: Text(
|
||||
L10n.of(context)!.conversationBotCustomZone_customSystemPromptLabel,
|
||||
),
|
||||
content: TextField(
|
||||
minLines: 1,
|
||||
maxLines: 10,
|
||||
maxLength: 1000,
|
||||
controller: textFieldController,
|
||||
onChanged: (value) {
|
||||
customSystemPrompt = value;
|
||||
},
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text(L10n.of(context)!.cancel),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: Text(L10n.of(context)!.ok),
|
||||
onPressed: () {
|
||||
if (customSystemPrompt == "") return;
|
||||
if (customSystemPrompt !=
|
||||
initialBotOptions.customSystemPrompt) {
|
||||
initialBotOptions.customSystemPrompt = customSystemPrompt;
|
||||
onChanged.call(initialBotOptions);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListTile(
|
||||
onTap: setBotCustomSystemPromptAction,
|
||||
title: Text(
|
||||
initialBotOptions.customSystemPrompt ??
|
||||
L10n.of(context)!
|
||||
.conversationBotCustomZone_customSystemPromptPlaceholder,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,75 @@
|
|||
import 'package:fluffychat/pangea/models/bot_options_model.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_custom_system_prompt_input.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
class ConversationBotCustomZone extends StatelessWidget {
|
||||
final BotOptionsModel initialBotOptions;
|
||||
// call this to update propagate changes to parents
|
||||
final void Function(BotOptionsModel) onChanged;
|
||||
|
||||
const ConversationBotCustomZone({
|
||||
super.key,
|
||||
required this.initialBotOptions,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Column(
|
||||
print(initialBotOptions.toJson());
|
||||
return Column(
|
||||
children: [
|
||||
Text('Custom Zone'),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
L10n.of(context)!.conversationBotCustomZone_title,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const Divider(
|
||||
color: Colors.grey,
|
||||
thickness: 1,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 0, 0, 0),
|
||||
child: Text(
|
||||
L10n.of(context)!
|
||||
.conversationBotCustomZone_customSystemPromptLabel,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: ConversationBotCustomSystemPromptInput(
|
||||
initialBotOptions: initialBotOptions,
|
||||
onChanged: onChanged,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
CheckboxListTile(
|
||||
title: Text(
|
||||
L10n.of(context)!
|
||||
.conversationBotCustomZone_customTriggerReactionEnabledLabel,
|
||||
),
|
||||
enabled: false,
|
||||
value: initialBotOptions.customTriggerReactionEnabled ?? true,
|
||||
onChanged: (value) {
|
||||
initialBotOptions.customTriggerReactionEnabled = value ?? true;
|
||||
initialBotOptions.customTriggerReactionKey =
|
||||
"⏩"; // hard code this for now
|
||||
onChanged.call(initialBotOptions);
|
||||
},
|
||||
// make this input disabled always
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,9 +82,9 @@ class ConversationBotDiscussionZone extends StatelessWidget {
|
|||
.conversationBotDiscussionZone_discussionTriggerReactionEnabledLabel,
|
||||
),
|
||||
enabled: false,
|
||||
value: initialBotOptions.discussionTriggerReactionEnabled ?? false,
|
||||
value: initialBotOptions.discussionTriggerReactionEnabled ?? true,
|
||||
onChanged: (value) {
|
||||
initialBotOptions.discussionTriggerReactionEnabled = value ?? false;
|
||||
initialBotOptions.discussionTriggerReactionEnabled = value ?? true;
|
||||
initialBotOptions.discussionTriggerReactionKey =
|
||||
"⏩"; // hard code this for now
|
||||
onChanged.call(initialBotOptions);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import 'package:fluffychat/pangea/models/bot_options_model.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_conversation_zone.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_custom_zone.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_text_adventure_zone.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'conversation_bot_discussion_zone.dart';
|
||||
|
|
@ -23,9 +21,12 @@ class ConversationBotModeDynamicZone extends StatelessWidget {
|
|||
initialBotOptions: initialBotOptions,
|
||||
onChanged: onChanged,
|
||||
),
|
||||
"custom": const ConversationBotCustomZone(),
|
||||
"conversation": const ConversationBotConversationZone(),
|
||||
"text_adventure": const ConversationBotTextAdventureZone(),
|
||||
"custom": ConversationBotCustomZone(
|
||||
initialBotOptions: initialBotOptions,
|
||||
onChanged: onChanged,
|
||||
),
|
||||
// "conversation": const ConversationBotConversationZone(),
|
||||
// "text_adventure": const ConversationBotTextAdventureZone(),
|
||||
};
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class ConversationBotModeSelect extends StatelessWidget {
|
|||
final Map<String, String> options = {
|
||||
"discussion":
|
||||
L10n.of(context)!.conversationBotModeSelectOption_discussion,
|
||||
// "custom": L10n.of(context)!.conversationBotModeSelectOption_custom,
|
||||
"custom": L10n.of(context)!.conversationBotModeSelectOption_custom,
|
||||
// "conversation":
|
||||
// L10n.of(context)!.conversationBotModeSelectOption_conversation,
|
||||
// "text_adventure":
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:fluffychat/pangea/models/bot_options_model.dart';
|
|||
import 'package:fluffychat/pangea/utils/bot_name.dart';
|
||||
import 'package:fluffychat/pangea/widgets/common/bot_face_svg.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_mode_dynamic_zone.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_mode_select.dart';
|
||||
import 'package:fluffychat/pangea/widgets/space/language_level_dropdown.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -44,7 +45,9 @@ class ConversationBotSettingsState extends State<ConversationBotSettings> {
|
|||
void initState() {
|
||||
super.initState();
|
||||
isOpen = widget.startOpen;
|
||||
botOptions = widget.room?.botOptions ?? BotOptionsModel();
|
||||
botOptions = widget.room?.botOptions != null
|
||||
? BotOptionsModel.fromJson(widget.room?.botOptions?.toJson())
|
||||
: BotOptionsModel();
|
||||
widget.room?.isBotRoom.then((bool isBotRoom) {
|
||||
setState(() {
|
||||
addBot = isBotRoom;
|
||||
|
|
@ -136,7 +139,7 @@ class ConversationBotSettingsState extends State<ConversationBotSettings> {
|
|||
Theme.of(context).textTheme.bodyLarge!.color,
|
||||
child: const BotFace(
|
||||
width: 30.0,
|
||||
expression: BotExpression.right,
|
||||
expression: BotExpression.idle,
|
||||
),
|
||||
),
|
||||
trailing: ElevatedButton(
|
||||
|
|
@ -221,28 +224,28 @@ class ConversationBotSettingsState extends State<ConversationBotSettings> {
|
|||
}),
|
||||
),
|
||||
),
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.fromLTRB(32, 16, 0, 0),
|
||||
// child: Text(
|
||||
// L10n.of(context)!.conversationBotModeSelectDescription,
|
||||
// style: TextStyle(
|
||||
// color: Theme.of(context).colorScheme.secondary,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// fontSize: 16,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.only(left: 16),
|
||||
// child: ConversationBotModeSelect(
|
||||
// initialMode: botOptions.mode,
|
||||
// onChanged: (String? mode) => updateBotOption(
|
||||
// () {
|
||||
// botOptions.mode = mode ?? "discussion";
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(32, 16, 0, 0),
|
||||
child: Text(
|
||||
L10n.of(context)!.conversationBotModeSelectDescription,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: ConversationBotModeSelect(
|
||||
initialMode: botOptions.mode,
|
||||
onChanged: (String? mode) => updateBotOption(
|
||||
() {
|
||||
botOptions.mode = mode ?? "discussion";
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(28, 0, 12, 0),
|
||||
child: ConversationBotModeDynamicZone(
|
||||
|
|
|
|||
|
|
@ -71,6 +71,16 @@ class PangeaTextController extends TextEditingController {
|
|||
choreographer.igc.igcTextData!.getTopMatchIndexForOffset(
|
||||
selection.baseOffset,
|
||||
);
|
||||
|
||||
// if autoplay on and it start then just start it
|
||||
if (matchIndex != -1 &&
|
||||
choreographer.itAutoPlayEnabled &&
|
||||
choreographer.igc.igcTextData!.matches[matchIndex].isITStart) {
|
||||
return choreographer.onITStart(
|
||||
choreographer.igc.igcTextData!.matches[matchIndex],
|
||||
);
|
||||
}
|
||||
|
||||
final Widget? cardToShow = matchIndex != -1
|
||||
? SpanCard(
|
||||
scm: SpanCardModel(
|
||||
|
|
@ -100,7 +110,7 @@ class PangeaTextController extends TextEditingController {
|
|||
context: context,
|
||||
cardSize: matchIndex != -1 &&
|
||||
choreographer.igc.igcTextData!.matches[matchIndex].isITStart
|
||||
? const Size(350, 220)
|
||||
? const Size(350, 260)
|
||||
: const Size(350, 400),
|
||||
cardToShow: cardToShow,
|
||||
transformTargetId: choreographer.inputTransformTargetKey,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/constants/local.key.dart';
|
||||
import 'package:fluffychat/pangea/enum/span_data_type.dart';
|
||||
import 'package:fluffychat/pangea/models/span_data.dart';
|
||||
import 'package:fluffychat/pangea/utils/bot_style.dart';
|
||||
|
|
@ -49,6 +50,8 @@ class SpanCardState extends State<SpanCard> {
|
|||
bool fetchingData = false;
|
||||
int? selectedChoiceIndex;
|
||||
|
||||
BotExpression currentExpression = BotExpression.nonGold;
|
||||
|
||||
//on initState, get SpanDetails
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -150,7 +153,23 @@ class WordMatchContent extends StatelessWidget {
|
|||
.choices?[index]
|
||||
.selected = true;
|
||||
|
||||
controller.setState(() => ());
|
||||
controller.setState(
|
||||
() => (
|
||||
controller.currentExpression =
|
||||
controller
|
||||
.widget
|
||||
.scm
|
||||
.choreographer
|
||||
.igc
|
||||
.igcTextData
|
||||
!.matches[controller.widget.scm.matchIndex]
|
||||
.match
|
||||
.choices![index]
|
||||
.isBestCorrection
|
||||
? BotExpression.gold
|
||||
: BotExpression.surprised
|
||||
),
|
||||
);
|
||||
// if (controller.widget.scm.pangeaMatch.match.choices![index].type ==
|
||||
// SpanChoiceType.distractor) {
|
||||
// await controller.getSpanDetails();
|
||||
|
|
@ -192,10 +211,11 @@ class WordMatchContent extends StatelessWidget {
|
|||
try {
|
||||
return Column(
|
||||
children: [
|
||||
// if (!controller.widget.scm.pangeaMatch!.isITStart)
|
||||
CardHeader(
|
||||
text: controller.error?.toString() ?? matchCopy.title,
|
||||
botExpression: controller.error == null
|
||||
? BotExpression.right
|
||||
? controller.currentExpression
|
||||
: BotExpression.addled,
|
||||
),
|
||||
Expanded(
|
||||
|
|
@ -321,6 +341,10 @@ class WordMatchContent extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
if (controller.widget.scm.pangeaMatch!.isITStart)
|
||||
DontShowSwitchListTile(
|
||||
controller: pangeaController,
|
||||
),
|
||||
],
|
||||
);
|
||||
} on Exception catch (e) {
|
||||
|
|
@ -459,3 +483,40 @@ class StartITButton extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DontShowSwitchListTile extends StatefulWidget {
|
||||
final PangeaController controller;
|
||||
|
||||
const DontShowSwitchListTile({
|
||||
super.key,
|
||||
required this.controller,
|
||||
});
|
||||
|
||||
@override
|
||||
DontShowSwitchListTileState createState() => DontShowSwitchListTileState();
|
||||
}
|
||||
|
||||
class DontShowSwitchListTileState extends State<DontShowSwitchListTile> {
|
||||
bool switchValue = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SwitchListTile.adaptive(
|
||||
activeColor: AppConfig.activeToggleColor,
|
||||
title: Text(L10n.of(context)!.interactiveTranslatorAutoPlaySliderHeader),
|
||||
value: switchValue,
|
||||
onChanged: (value) => {
|
||||
widget.controller.pStoreService.save(
|
||||
PLocalKey.itAutoPlay.toString(),
|
||||
value,
|
||||
),
|
||||
setState(() => switchValue = value),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -271,7 +271,7 @@ class GenerateVocabButtonState extends State<GenerateVocabButton> {
|
|||
ElevatedButton.icon(
|
||||
icon: const BotFace(
|
||||
width: 50.0,
|
||||
expression: BotExpression.right,
|
||||
expression: BotExpression.idle,
|
||||
),
|
||||
label: Text(L10n.of(context)!.generateVocabulary),
|
||||
onPressed: () async {
|
||||
|
|
@ -464,9 +464,9 @@ class PromptsFieldState extends State<PromptsField> {
|
|||
|
||||
// button to call API
|
||||
ElevatedButton.icon(
|
||||
icon: const BotFace(
|
||||
icon: BotFace(
|
||||
width: 50.0,
|
||||
expression: BotExpression.right,
|
||||
expression: BotExpression.idle,
|
||||
),
|
||||
label: Text(L10n.of(context)!.generatePrompts),
|
||||
onPressed: () async {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
24
pubspec.lock
24
pubspec.lock
|
|
@ -999,6 +999,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: graphs
|
||||
sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
highlighter:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1925,6 +1933,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
rive:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: rive
|
||||
sha256: "166019487e8bfa6525d4537bfd494deb4f306e32f97a7f50ff452b44ab6b72ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.11"
|
||||
rive_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rive_common
|
||||
sha256: "83af8b500ad4d23930397229c7ed520e266eb851690a8621afa6cbd782aeac87"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.3"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ dependencies:
|
|||
sentry_flutter: ^8.2.0
|
||||
shimmer: ^3.0.0
|
||||
syncfusion_flutter_xlsio: ^25.1.40
|
||||
rive: 0.11.11
|
||||
# Pangea#
|
||||
|
||||
dev_dependencies:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue