Merge pull request #644 from pangeachat/merge-in-dev-changes
Merge in dev changes
This commit is contained in:
commit
5dbf127a30
13 changed files with 137 additions and 78 deletions
|
|
@ -4127,5 +4127,6 @@
|
|||
"createSpace": "Create space",
|
||||
"createChat": "Create chat",
|
||||
"error520Title": "Please try again.",
|
||||
"error520Desc": "Sorry, we could not understand your message..."
|
||||
"error520Desc": "Sorry, we could not understand your message...",
|
||||
"translationChoicesBody": "Click and hold an option for a hint."
|
||||
}
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
import 'package:animations/animations.dart';
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
|
||||
import 'package:fluffychat/pages/chat/input_bar.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/send_button.dart';
|
||||
import 'package:fluffychat/pangea/constants/language_constants.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/input_bar_wrapper.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
|
@ -34,7 +33,7 @@ class ChatInputRow extends StatelessWidget {
|
|||
controller.pangeaController.languageController.activeL2Model();
|
||||
|
||||
String hintText() {
|
||||
if (controller.choreographer.choreoMode == ChoreoMode.it) {
|
||||
if (controller.choreographer.itController.willOpen) {
|
||||
return L10n.of(context)!.buildTranslation;
|
||||
}
|
||||
return activel1 != null &&
|
||||
|
|
@ -322,10 +321,7 @@ class ChatInputRow extends StatelessWidget {
|
|||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 0.0),
|
||||
// #Pangea
|
||||
// child: InputBar(
|
||||
child: InputBarWrapper(
|
||||
// Pangea#
|
||||
child: InputBar(
|
||||
room: controller.room,
|
||||
minLines: 1,
|
||||
maxLines: 8,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import 'package:fluffychat/pages/chat/chat_app_bar_list_tile.dart';
|
|||
import 'package:fluffychat/pages/chat/chat_app_bar_title.dart';
|
||||
import 'package:fluffychat/pages/chat/chat_emoji_picker.dart';
|
||||
import 'package:fluffychat/pages/chat/chat_event_list.dart';
|
||||
import 'package:fluffychat/pages/chat/chat_input_row.dart';
|
||||
import 'package:fluffychat/pages/chat/pinned_events.dart';
|
||||
import 'package:fluffychat/pages/chat/reactions_picker.dart';
|
||||
import 'package:fluffychat/pages/chat/reply_display.dart';
|
||||
|
|
@ -13,6 +12,7 @@ import 'package:fluffychat/pangea/choreographer/widgets/it_bar.dart';
|
|||
import 'package:fluffychat/pangea/choreographer/widgets/start_igc_button.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/chat_floating_action_button.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/input_bar_wrapper.dart';
|
||||
import 'package:fluffychat/utils/account_config.dart';
|
||||
import 'package:fluffychat/widgets/chat_settings_popup_menu.dart';
|
||||
import 'package:fluffychat/widgets/connection_status_header.dart';
|
||||
|
|
@ -498,7 +498,9 @@ class ChatView extends StatelessWidget {
|
|||
),
|
||||
ReactionsPicker(controller),
|
||||
ReplyDisplay(controller),
|
||||
ChatInputRow(controller),
|
||||
ChatInputRowWrapper(
|
||||
controller: controller,
|
||||
),
|
||||
ChatEmojiPicker(controller),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ class IgcController {
|
|||
|
||||
final IGCRequestBody reqBody = IGCRequestBody(
|
||||
fullText: choreographer.currentText,
|
||||
userId: choreographer.pangeaController.userController.userId!,
|
||||
userL1: choreographer.l1LangCode!,
|
||||
userL2: choreographer.l2LangCode!,
|
||||
enableIGC: choreographer.igcEnabled && !onlyTokensAndLanguageDetection,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'dart:developer';
|
|||
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/error_service.dart';
|
||||
import 'package:fluffychat/pangea/constants/choreo_constants.dart';
|
||||
import 'package:fluffychat/pangea/enum/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -54,6 +55,23 @@ class ITController {
|
|||
choreographer.setState();
|
||||
}
|
||||
|
||||
bool _closingHint = false;
|
||||
Duration get animationSpeed => (_closingHint || !_willOpen)
|
||||
? const Duration(milliseconds: 500)
|
||||
: const Duration(milliseconds: 2000);
|
||||
|
||||
void closeHint() {
|
||||
_closingHint = true;
|
||||
final String hintKey = InlineInstructions.translationChoices.toString();
|
||||
final instructionsController = choreographer.pangeaController.instructions;
|
||||
instructionsController.turnOffInstruction(hintKey);
|
||||
instructionsController.updateEnableInstructions(hintKey, true);
|
||||
choreographer.setState();
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
_closingHint = false;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> initializeIT(ITStartData itStartData) async {
|
||||
_willOpen = true;
|
||||
Future.delayed(const Duration(microseconds: 100), () {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ import 'package:fluffychat/pangea/choreographer/widgets/it_bar_buttons.dart';
|
|||
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/enum/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/utils/inline_tooltip.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
|
@ -45,12 +47,16 @@ class ITBarState extends State<ITBar> {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
bool get instructionsTurnedOff =>
|
||||
widget.choreographer.pangeaController.instructions
|
||||
.wereInstructionsTurnedOff(
|
||||
InlineInstructions.translationChoices.toString(),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedSize(
|
||||
duration: itController.willOpen
|
||||
? const Duration(milliseconds: 2000)
|
||||
: const Duration(milliseconds: 500),
|
||||
duration: itController.animationSpeed,
|
||||
curve: Curves.fastOutSlowIn,
|
||||
clipBehavior: Clip.none,
|
||||
child: !itController.willOpen
|
||||
|
|
@ -58,9 +64,7 @@ class ITBarState extends State<ITBar> {
|
|||
: CompositedTransformTarget(
|
||||
link: widget.choreographer.itBarLinkAndKey.link,
|
||||
child: AnimatedOpacity(
|
||||
duration: itController.willOpen
|
||||
? const Duration(milliseconds: 2000)
|
||||
: const Duration(milliseconds: 500),
|
||||
duration: itController.animationSpeed,
|
||||
opacity: itController.willOpen ? 1.0 : 0.0,
|
||||
child: Container(
|
||||
key: widget.choreographer.itBarLinkAndKey.key,
|
||||
|
|
@ -109,6 +113,12 @@ class ITBarState extends State<ITBar> {
|
|||
// const SizedBox(height: 40.0),
|
||||
OriginalText(controller: itController),
|
||||
const SizedBox(height: 7.0),
|
||||
if (!instructionsTurnedOff)
|
||||
InlineTooltip(
|
||||
body: InlineInstructions.translationChoices
|
||||
.body(context),
|
||||
onClose: itController.closeHint,
|
||||
),
|
||||
IntrinsicHeight(
|
||||
child: Container(
|
||||
constraints:
|
||||
|
|
@ -151,6 +161,7 @@ class ITBarState extends State<ITBar> {
|
|||
),
|
||||
),
|
||||
),
|
||||
// ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -305,7 +316,11 @@ class ITChoices extends StatelessWidget {
|
|||
chosenContinuance:
|
||||
controller.currentITStep!.continuances[index].text,
|
||||
bestContinuance: controller.currentITStep!.best.text,
|
||||
feedbackLang: controller.targetLangCode,
|
||||
// TODO: we want this to eventually switch between target and source lang,
|
||||
// based on the learner's proficiency - maybe with the words involved in the translation
|
||||
// maybe overall. For now, we'll just use the source lang.
|
||||
feedbackLang: controller.choreographer.l1Lang?.langCode ??
|
||||
controller.sourceLangCode,
|
||||
sourceTextLang: controller.sourceLangCode,
|
||||
targetLang: controller.targetLangCode,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -146,8 +146,11 @@ class ITFeedbackCardView extends StatelessWidget {
|
|||
controller.res!.text,
|
||||
style: BotStyle.text(context),
|
||||
),
|
||||
// if res is not null, show a button to translate the text
|
||||
if (controller.res != null && controller.translatedFeedback == null)
|
||||
// if res is not null and feedback not in the userL1, show a button to translate the text
|
||||
if (controller.res != null &&
|
||||
controller.translatedFeedback == null &&
|
||||
controller.widget.req.feedbackLang !=
|
||||
controller.controller.languageController.userL1?.langCode)
|
||||
Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ extension InstructionsEnumExtension on InstructionsEnum {
|
|||
enum InlineInstructions {
|
||||
speechToText,
|
||||
l1Translation,
|
||||
translationChoices,
|
||||
}
|
||||
|
||||
extension InlineInstructionsExtension on InlineInstructions {
|
||||
|
|
@ -67,6 +68,21 @@ extension InlineInstructionsExtension on InlineInstructions {
|
|||
return L10n.of(context)!.speechToTextBody;
|
||||
case InlineInstructions.l1Translation:
|
||||
return L10n.of(context)!.l1TranslationBody;
|
||||
case InlineInstructions.translationChoices:
|
||||
return L10n.of(context)!.translationChoicesBody;
|
||||
}
|
||||
}
|
||||
|
||||
bool get toggledOff {
|
||||
final instructionSettings =
|
||||
MatrixState.pangeaController.userController.profile.instructionSettings;
|
||||
switch (this) {
|
||||
case InlineInstructions.speechToText:
|
||||
return instructionSettings.showedSpeechToTextTooltip;
|
||||
case InlineInstructions.l1Translation:
|
||||
return instructionSettings.showedL1TranslationTooltip;
|
||||
case InlineInstructions.translationChoices:
|
||||
return instructionSettings.showedTranslationChoicesTooltip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,11 +186,18 @@ class UserInstructions {
|
|||
bool showedBlurMeansTranslate;
|
||||
bool showedTooltipInstructions;
|
||||
|
||||
bool showedSpeechToTextTooltip;
|
||||
bool showedL1TranslationTooltip;
|
||||
bool showedTranslationChoicesTooltip;
|
||||
|
||||
UserInstructions({
|
||||
this.showedItInstructions = false,
|
||||
this.showedClickMessage = false,
|
||||
this.showedBlurMeansTranslate = false,
|
||||
this.showedTooltipInstructions = false,
|
||||
this.showedSpeechToTextTooltip = false,
|
||||
this.showedL1TranslationTooltip = false,
|
||||
this.showedTranslationChoicesTooltip = false,
|
||||
});
|
||||
|
||||
factory UserInstructions.fromJson(Map<String, dynamic> json) =>
|
||||
|
|
@ -203,6 +210,12 @@ class UserInstructions {
|
|||
json[InstructionsEnum.blurMeansTranslate.toString()] ?? false,
|
||||
showedTooltipInstructions:
|
||||
json[InstructionsEnum.tooltipInstructions.toString()] ?? false,
|
||||
showedL1TranslationTooltip:
|
||||
json[InlineInstructions.l1Translation.toString()] ?? false,
|
||||
showedTranslationChoicesTooltip:
|
||||
json[InlineInstructions.translationChoices.toString()] ?? false,
|
||||
showedSpeechToTextTooltip:
|
||||
json[InlineInstructions.speechToText.toString()] ?? false,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
|
|
@ -213,6 +226,12 @@ class UserInstructions {
|
|||
showedBlurMeansTranslate;
|
||||
data[InstructionsEnum.tooltipInstructions.toString()] =
|
||||
showedTooltipInstructions;
|
||||
data[InlineInstructions.l1Translation.toString()] =
|
||||
showedL1TranslationTooltip;
|
||||
data[InlineInstructions.translationChoices.toString()] =
|
||||
showedTranslationChoicesTooltip;
|
||||
data[InlineInstructions.speechToText.toString()] =
|
||||
showedSpeechToTextTooltip;
|
||||
return data;
|
||||
}
|
||||
|
||||
|
|
@ -238,6 +257,21 @@ class UserInstructions {
|
|||
?.content[InstructionsEnum.tooltipInstructions.toString()]
|
||||
as bool?) ??
|
||||
false,
|
||||
showedL1TranslationTooltip:
|
||||
(accountData[InlineInstructions.l1Translation.toString()]
|
||||
?.content[InlineInstructions.l1Translation.toString()]
|
||||
as bool?) ??
|
||||
false,
|
||||
showedTranslationChoicesTooltip: (accountData[
|
||||
InlineInstructions.translationChoices.toString()]
|
||||
?.content[InlineInstructions.translationChoices.toString()]
|
||||
as bool?) ??
|
||||
false,
|
||||
showedSpeechToTextTooltip:
|
||||
(accountData[InlineInstructions.speechToText.toString()]
|
||||
?.content[InlineInstructions.speechToText.toString()]
|
||||
as bool?) ??
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ class IGCRequestBody {
|
|||
String userL2;
|
||||
bool enableIT;
|
||||
bool enableIGC;
|
||||
String userId;
|
||||
List<PreviousMessage> prevMessages;
|
||||
|
||||
IGCRequestBody({
|
||||
|
|
@ -137,6 +138,7 @@ class IGCRequestBody {
|
|||
required this.userL2,
|
||||
required this.enableIGC,
|
||||
required this.enableIT,
|
||||
required this.userId,
|
||||
required this.prevMessages,
|
||||
});
|
||||
|
||||
|
|
@ -146,6 +148,7 @@ class IGCRequestBody {
|
|||
ModelKey.userL2: userL2,
|
||||
"enable_it": enableIT,
|
||||
"enable_igc": enableIGC,
|
||||
ModelKey.userId: userId,
|
||||
ModelKey.prevMessages:
|
||||
jsonEncode(prevMessages.map((x) => x.toJson()).toList()),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class InlineTooltip extends StatelessWidget {
|
|||
onPressed: onClose,
|
||||
),
|
||||
),
|
||||
child: Container(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: Theme.of(context).colorScheme.primary.withAlpha(20),
|
||||
|
|
|
|||
|
|
@ -25,9 +25,15 @@ class InstructionsController {
|
|||
final Map<String, bool> _instructionsShown = {};
|
||||
|
||||
/// Returns true if the user requested this popup not be shown again
|
||||
bool? toggledOff(String key) => InstructionsEnum.values
|
||||
.firstWhereOrNull((value) => value.toString() == key)
|
||||
?.toggledOff;
|
||||
bool? toggledOff(String key) {
|
||||
final bool? instruction = InstructionsEnum.values
|
||||
.firstWhereOrNull((value) => value.toString() == key)
|
||||
?.toggledOff;
|
||||
final bool? tooltip = InlineInstructions.values
|
||||
.firstWhereOrNull((value) => value.toString() == key)
|
||||
?.toggledOff;
|
||||
return instruction ?? tooltip;
|
||||
}
|
||||
|
||||
InstructionsController(PangeaController pangeaController) {
|
||||
_pangeaController = pangeaController;
|
||||
|
|
@ -58,6 +64,15 @@ class InstructionsController {
|
|||
if (key == InstructionsEnum.tooltipInstructions.toString()) {
|
||||
profile.instructionSettings.showedTooltipInstructions = value;
|
||||
}
|
||||
if (key == InlineInstructions.speechToText.toString()) {
|
||||
profile.instructionSettings.showedSpeechToTextTooltip = value;
|
||||
}
|
||||
if (key == InlineInstructions.l1Translation.toString()) {
|
||||
profile.instructionSettings.showedL1TranslationTooltip = value;
|
||||
}
|
||||
if (key == InlineInstructions.translationChoices.toString()) {
|
||||
profile.instructionSettings.showedTranslationChoicesTooltip = value;
|
||||
}
|
||||
return profile;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,48 +1,23 @@
|
|||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:fluffychat/pages/chat/input_bar.dart';
|
||||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
import 'package:fluffychat/pages/chat/chat_input_row.dart';
|
||||
import 'package:fluffychat/pangea/widgets/igc/pangea_text_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
class InputBarWrapper extends StatefulWidget {
|
||||
final Room room;
|
||||
final int? minLines;
|
||||
final int? maxLines;
|
||||
final TextInputType? keyboardType;
|
||||
final TextInputAction? textInputAction;
|
||||
final ValueChanged<String>? onSubmitted;
|
||||
final ValueChanged<Uint8List?>? onSubmitImage;
|
||||
final FocusNode? focusNode;
|
||||
final PangeaTextController? controller;
|
||||
final InputDecoration? decoration;
|
||||
final ValueChanged<String>? onChanged;
|
||||
final bool? autofocus;
|
||||
final bool readOnly;
|
||||
class ChatInputRowWrapper extends StatefulWidget {
|
||||
final ChatController controller;
|
||||
|
||||
const InputBarWrapper({
|
||||
required this.room,
|
||||
this.minLines,
|
||||
this.maxLines,
|
||||
this.keyboardType,
|
||||
this.onSubmitted,
|
||||
this.onSubmitImage,
|
||||
this.focusNode,
|
||||
this.controller,
|
||||
this.decoration,
|
||||
this.onChanged,
|
||||
this.autofocus,
|
||||
this.textInputAction,
|
||||
this.readOnly = false,
|
||||
const ChatInputRowWrapper({
|
||||
required this.controller,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<InputBarWrapper> createState() => InputBarWrapperState();
|
||||
State<ChatInputRowWrapper> createState() => ChatInputRowWrapperState();
|
||||
}
|
||||
|
||||
class InputBarWrapperState extends State<InputBarWrapper> {
|
||||
class ChatInputRowWrapperState extends State<ChatInputRowWrapper> {
|
||||
StreamSubscription? _choreoSub;
|
||||
String _currentText = '';
|
||||
|
||||
|
|
@ -50,7 +25,7 @@ class InputBarWrapperState extends State<InputBarWrapper> {
|
|||
void initState() {
|
||||
// Rebuild the widget each time there's an update from choreo
|
||||
_choreoSub =
|
||||
widget.controller?.choreographer.stateListener.stream.listen((_) {
|
||||
widget.controller.choreographer.stateListener.stream.listen((_) {
|
||||
setState(() {});
|
||||
});
|
||||
super.initState();
|
||||
|
|
@ -63,10 +38,6 @@ class InputBarWrapperState extends State<InputBarWrapper> {
|
|||
}
|
||||
|
||||
void refreshOnChange(String text) {
|
||||
if (widget.onChanged != null) {
|
||||
widget.onChanged!(text);
|
||||
}
|
||||
|
||||
final bool decreasedFromMaxLength =
|
||||
_currentText.length >= PangeaTextController.maxLength &&
|
||||
text.length < PangeaTextController.maxLength;
|
||||
|
|
@ -81,21 +52,5 @@ class InputBarWrapperState extends State<InputBarWrapper> {
|
|||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InputBar(
|
||||
room: widget.room,
|
||||
minLines: widget.minLines,
|
||||
maxLines: widget.maxLines,
|
||||
keyboardType: widget.keyboardType,
|
||||
onSubmitted: widget.onSubmitted,
|
||||
onSubmitImage: widget.onSubmitImage,
|
||||
focusNode: widget.focusNode,
|
||||
controller: widget.controller,
|
||||
decoration: widget.decoration,
|
||||
onChanged: refreshOnChange,
|
||||
autofocus: widget.autofocus,
|
||||
textInputAction: widget.textInputAction,
|
||||
readOnly: widget.readOnly,
|
||||
);
|
||||
}
|
||||
Widget build(BuildContext context) => ChatInputRow(widget.controller);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue