4428 word zoom card changes positionsize when mobile keyboard opens (#4491)
* fix: on flag token info, close word card * chore: update icon and tooltip for emoji mode * refactor: create base feedback dialog * fix: start drag imeadiatly on tap practice match item
This commit is contained in:
parent
1b3ae5f537
commit
6cbec5abca
11 changed files with 257 additions and 370 deletions
|
|
@ -5313,5 +5313,7 @@
|
|||
"saveActivityDesc": "Good job! Save this activity for later review and practice",
|
||||
"levelInfoTooltip": "Here you can see all the points you’ve earned and how!",
|
||||
"alreadyInCourseWithID": "You are already in a course with this plan. Do you want to create a course with the same plan, or go to the existing course?",
|
||||
"goToExistingCourse": "Go to existing course"
|
||||
"goToExistingCourse": "Go to existing course",
|
||||
"emojiView": "Emoji view",
|
||||
"feedbackDialogDesc": "I make mistakes too! Anything to help me improve?"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,9 @@ import 'package:fluffychat/pangea/learning_settings/constants/language_constants
|
|||
import 'package:fluffychat/pangea/learning_settings/widgets/p_language_dialog.dart';
|
||||
import 'package:fluffychat/pangea/message_token_text/tokens_util.dart';
|
||||
import 'package:fluffychat/pangea/spaces/utils/load_participants_util.dart';
|
||||
import 'package:fluffychat/pangea/token_info_feedback/token_info_feedback_dialog.dart';
|
||||
import 'package:fluffychat/pangea/token_info_feedback/token_info_feedback_notification.dart';
|
||||
import 'package:fluffychat/pangea/token_info_feedback/token_info_feedback_request.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
|
||||
import 'package:fluffychat/utils/error_reporter.dart';
|
||||
|
|
@ -2232,6 +2235,39 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> showTokenFeedbackDialog(
|
||||
TokenInfoFeedbackRequestData requestData,
|
||||
String langCode,
|
||||
PangeaMessageEvent event,
|
||||
) async {
|
||||
clearSelectedEvents();
|
||||
final resp = await showDialog(
|
||||
context: context,
|
||||
builder: (context) => TokenInfoFeedbackDialog(
|
||||
requestData: requestData,
|
||||
langCode: langCode,
|
||||
event: event,
|
||||
),
|
||||
);
|
||||
|
||||
if (resp != null && resp is String) {
|
||||
OverlayUtil.showOverlay(
|
||||
overlayKey: "token_feedback_snackbar",
|
||||
context: context,
|
||||
child: TokenFeedbackNotification(message: resp),
|
||||
transformTargetId: '',
|
||||
position: OverlayPositionEnum.top,
|
||||
backDropToDismiss: false,
|
||||
closePrevOverlay: false,
|
||||
canPop: false,
|
||||
);
|
||||
|
||||
Future.delayed(const Duration(seconds: 10), () {
|
||||
MatrixState.pAnyState.closeOverlay("token_feedback_snackbar");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
final ScrollController carouselController = ScrollController();
|
||||
|
||||
ActivityRoleModel? highlightedRole;
|
||||
|
|
|
|||
|
|
@ -1,121 +0,0 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
|
||||
class ActivityFeedbackRequestDialog extends StatefulWidget {
|
||||
const ActivityFeedbackRequestDialog({super.key});
|
||||
|
||||
@override
|
||||
State<ActivityFeedbackRequestDialog> createState() =>
|
||||
ActivityFeedbackRequestDialogState();
|
||||
}
|
||||
|
||||
class ActivityFeedbackRequestDialogState
|
||||
extends State<ActivityFeedbackRequestDialog> {
|
||||
final TextEditingController _feedbackController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_feedbackController.addListener(() {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_feedbackController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 2.5, sigmaY: 2.5),
|
||||
child: Dialog(
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
),
|
||||
child: SizedBox(
|
||||
width: 325.0,
|
||||
child: Column(
|
||||
spacing: 20.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
L10n.of(context).feedbackTitle,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 40.0,
|
||||
height: 40.0,
|
||||
child: Center(
|
||||
child: Icon(Icons.flag_outlined),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20.0,
|
||||
),
|
||||
child: Column(
|
||||
spacing: 20.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context).feedbackDesc,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(
|
||||
child: TextField(
|
||||
controller: _feedbackController,
|
||||
decoration: InputDecoration(
|
||||
hintText: L10n.of(context).feedbackHint,
|
||||
),
|
||||
keyboardType: TextInputType.multiline,
|
||||
minLines: 1,
|
||||
maxLines: 5,
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: _feedbackController.text.isNotEmpty
|
||||
? () {
|
||||
Navigator.of(context).pop(
|
||||
_feedbackController.text,
|
||||
);
|
||||
}
|
||||
: null,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(L10n.of(context).feedbackButton),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,11 +6,11 @@ import 'package:fluffychat/config/themes.dart';
|
|||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/activity_feedback/activity_feedback_repo.dart';
|
||||
import 'package:fluffychat/pangea/activity_feedback/activity_feedback_request.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_session_start/activity_feedback_request_dialog.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_session_start/activity_feedback_response_dialog.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_summary_widget.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/feedback_dialog.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_activities/course_activity_repo.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/utils/stream_extension.dart';
|
||||
|
|
@ -65,7 +65,13 @@ class ActivitySessionStartView extends StatelessWidget {
|
|||
final feedback = await showDialog<String?>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return const ActivityFeedbackRequestDialog();
|
||||
return FeedbackDialog(
|
||||
title: L10n.of(context).feedbackTitle,
|
||||
onSubmit: (feedback) {
|
||||
Navigator.of(context).pop(feedback);
|
||||
},
|
||||
scrollable: false,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
142
lib/pangea/common/widgets/feedback_dialog.dart
Normal file
142
lib/pangea/common/widgets/feedback_dialog.dart
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/bot/widgets/bot_face_svg.dart';
|
||||
|
||||
class FeedbackDialog extends StatefulWidget {
|
||||
final String title;
|
||||
final Function(String) onSubmit;
|
||||
|
||||
final bool scrollable;
|
||||
final Widget? extraContent;
|
||||
|
||||
const FeedbackDialog({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.onSubmit,
|
||||
this.scrollable = true,
|
||||
this.extraContent,
|
||||
});
|
||||
|
||||
@override
|
||||
State<FeedbackDialog> createState() => _FeedbackDialogState();
|
||||
}
|
||||
|
||||
class _FeedbackDialogState extends State<FeedbackDialog> {
|
||||
final TextEditingController _feedbackController = TextEditingController();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_feedbackController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final content = Column(
|
||||
spacing: 20.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Center(
|
||||
child: BotFace(
|
||||
width: 50.0,
|
||||
expression: BotExpression.addled,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
L10n.of(context).feedbackDialogDesc,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
if (widget.extraContent != null) widget.extraContent!,
|
||||
TextFormField(
|
||||
controller: _feedbackController,
|
||||
decoration: InputDecoration(
|
||||
hintText: L10n.of(context).feedbackHint,
|
||||
),
|
||||
keyboardType: TextInputType.multiline,
|
||||
onFieldSubmitted: _feedbackController.text.isNotEmpty
|
||||
? (value) => widget.onSubmit(value)
|
||||
: null,
|
||||
maxLines: null,
|
||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 2.5, sigmaY: 2.5),
|
||||
child: Dialog(
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
),
|
||||
child: Container(
|
||||
width: 325.0,
|
||||
constraints: const BoxConstraints(
|
||||
maxHeight: 600.0,
|
||||
),
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
spacing: 20.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(
|
||||
minHeight: 40.0,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
widget.title,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 40.0,
|
||||
height: 40.0,
|
||||
child: Center(
|
||||
child: Icon(Icons.flag_outlined),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
widget.scrollable
|
||||
? Expanded(child: SingleChildScrollView(child: content))
|
||||
: content,
|
||||
ValueListenableBuilder<TextEditingValue>(
|
||||
valueListenable: _feedbackController,
|
||||
builder: (context, value, _) {
|
||||
final isNotEmpty = value.text.isNotEmpty;
|
||||
return ElevatedButton(
|
||||
onPressed:
|
||||
isNotEmpty ? () => widget.onSubmit(value.text) : null,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(L10n.of(context).feedbackButton),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/feedback_dialog.dart';
|
||||
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
|
||||
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
|
||||
import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart';
|
||||
|
|
@ -21,45 +20,28 @@ import 'package:fluffychat/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dar
|
|||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class TokenInfoFeedbackDialog extends StatefulWidget {
|
||||
class TokenInfoFeedbackDialog extends StatelessWidget {
|
||||
final TokenInfoFeedbackRequestData requestData;
|
||||
final String langCode;
|
||||
final PangeaMessageEvent event;
|
||||
final VoidCallback onUpdate;
|
||||
|
||||
const TokenInfoFeedbackDialog({
|
||||
super.key,
|
||||
required this.requestData,
|
||||
required this.langCode,
|
||||
required this.event,
|
||||
required this.onUpdate,
|
||||
});
|
||||
|
||||
@override
|
||||
State<TokenInfoFeedbackDialog> createState() =>
|
||||
_TokenInfoFeedbackDialogState();
|
||||
}
|
||||
|
||||
class _TokenInfoFeedbackDialogState extends State<TokenInfoFeedbackDialog> {
|
||||
final TextEditingController _feedbackController = TextEditingController();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_feedbackController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<String> _submitFeedback() async {
|
||||
Future<String> _submitFeedback(String feedback) async {
|
||||
final request = TokenInfoFeedbackRequest(
|
||||
userFeedback: _feedbackController.text,
|
||||
data: widget.requestData,
|
||||
userFeedback: feedback,
|
||||
data: requestData,
|
||||
);
|
||||
|
||||
final TokenInfoFeedbackResponse response =
|
||||
await TokenInfoFeedbackRepo.submitFeedback(request);
|
||||
|
||||
final originalToken =
|
||||
widget.requestData.tokens[widget.requestData.selectedToken];
|
||||
final originalToken = requestData.tokens[requestData.selectedToken];
|
||||
final token = response.updatedToken ?? originalToken;
|
||||
|
||||
// first, update lemma info if changed
|
||||
|
|
@ -77,7 +59,7 @@ class _TokenInfoFeedbackDialogState extends State<TokenInfoFeedbackDialog> {
|
|||
);
|
||||
}
|
||||
|
||||
final originalSent = widget.event.originalSent;
|
||||
final originalSent = event.originalSent;
|
||||
|
||||
// if no other changes, just return the message
|
||||
final hasTokenUpdate = response.updatedToken != null;
|
||||
|
|
@ -86,45 +68,43 @@ class _TokenInfoFeedbackDialogState extends State<TokenInfoFeedbackDialog> {
|
|||
response.updatedLanguage != originalSent.langCode;
|
||||
|
||||
if (!hasTokenUpdate && !hasLangUpdate) {
|
||||
widget.onUpdate();
|
||||
return response.userFriendlyMessage;
|
||||
}
|
||||
|
||||
// update the tokens to be sent in the message edit
|
||||
final tokens = List<PangeaToken>.from(widget.requestData.tokens);
|
||||
final tokens = List<PangeaToken>.from(requestData.tokens);
|
||||
if (hasTokenUpdate) {
|
||||
tokens[widget.requestData.selectedToken] = response.updatedToken!;
|
||||
tokens[requestData.selectedToken] = response.updatedToken!;
|
||||
}
|
||||
|
||||
if (hasLangUpdate) {
|
||||
originalSent.content.langCode = response.updatedLanguage!;
|
||||
}
|
||||
|
||||
await widget.event.room.pangeaSendTextEvent(
|
||||
widget.requestData.fullText,
|
||||
editEventId: widget.event.eventId,
|
||||
await event.room.pangeaSendTextEvent(
|
||||
requestData.fullText,
|
||||
editEventId: event.eventId,
|
||||
originalSent: originalSent?.content,
|
||||
originalWritten: widget.event.originalWritten?.content,
|
||||
originalWritten: event.originalWritten?.content,
|
||||
tokensSent: PangeaMessageTokens(
|
||||
tokens: tokens,
|
||||
),
|
||||
tokensWritten: widget.event.originalWritten?.tokens != null
|
||||
tokensWritten: event.originalWritten?.tokens != null
|
||||
? PangeaMessageTokens(
|
||||
tokens: widget.event.originalWritten!.tokens!,
|
||||
detections: widget.event.originalWritten?.detections,
|
||||
tokens: event.originalWritten!.tokens!,
|
||||
detections: event.originalWritten?.detections,
|
||||
)
|
||||
: null,
|
||||
choreo: originalSent?.choreo,
|
||||
);
|
||||
|
||||
widget.onUpdate();
|
||||
return response.userFriendlyMessage;
|
||||
}
|
||||
|
||||
Future<void> _submit() async {
|
||||
Future<void> _submit(String feedback, BuildContext context) async {
|
||||
final resp = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => _submitFeedback(),
|
||||
future: () => _submitFeedback(feedback),
|
||||
);
|
||||
|
||||
if (!resp.isError) {
|
||||
|
|
@ -154,9 +134,9 @@ class _TokenInfoFeedbackDialogState extends State<TokenInfoFeedbackDialog> {
|
|||
) async {
|
||||
final req = PhoneticTranscriptionRequest(
|
||||
arc: LanguageArc(
|
||||
l1: PLanguageStore.byLangCode(widget.requestData.wordCardL1) ??
|
||||
l1: PLanguageStore.byLangCode(requestData.wordCardL1) ??
|
||||
MatrixState.pangeaController.languageController.userL1!,
|
||||
l2: PLanguageStore.byLangCode(widget.langCode) ??
|
||||
l2: PLanguageStore.byLangCode(langCode) ??
|
||||
MatrixState.pangeaController.languageController.userL2!,
|
||||
),
|
||||
content: response.content,
|
||||
|
|
@ -166,109 +146,14 @@ class _TokenInfoFeedbackDialogState extends State<TokenInfoFeedbackDialog> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final selectedToken =
|
||||
widget.requestData.tokens[widget.requestData.selectedToken];
|
||||
return BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 2.5, sigmaY: 2.5),
|
||||
child: Dialog(
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
),
|
||||
child: Container(
|
||||
width: 325.0,
|
||||
constraints: const BoxConstraints(
|
||||
maxHeight: 600.0,
|
||||
),
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
spacing: 20.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Header with title and close button
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
L10n.of(context).tokenInfoFeedbackDialogTitle,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 40.0,
|
||||
height: 40.0,
|
||||
child: Center(
|
||||
child: Icon(Icons.flag_outlined),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// Content
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
spacing: 20.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Placeholder for word card
|
||||
Center(
|
||||
child: WordZoomWidget(
|
||||
token: selectedToken.text,
|
||||
construct: selectedToken.vocabConstructID,
|
||||
langCode: widget.langCode,
|
||||
),
|
||||
),
|
||||
// Description text
|
||||
Text(
|
||||
L10n.of(context).tokenInfoFeedbackDialogDesc,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
// Feedback text field
|
||||
TextFormField(
|
||||
controller: _feedbackController,
|
||||
decoration: InputDecoration(
|
||||
hintText: L10n.of(context).feedbackHint,
|
||||
),
|
||||
keyboardType: TextInputType.multiline,
|
||||
minLines: 1,
|
||||
maxLines: 5,
|
||||
onFieldSubmitted: _feedbackController.text.isNotEmpty
|
||||
? (_) => _submit()
|
||||
: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
ValueListenableBuilder<TextEditingValue>(
|
||||
valueListenable: _feedbackController,
|
||||
builder: (context, value, _) {
|
||||
final isNotEmpty = value.text.isNotEmpty;
|
||||
return ElevatedButton(
|
||||
onPressed: isNotEmpty ? _submit : null,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(L10n.of(context).feedbackButton),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
final selectedToken = requestData.tokens[requestData.selectedToken];
|
||||
return FeedbackDialog(
|
||||
title: L10n.of(context).tokenInfoFeedbackDialogTitle,
|
||||
onSubmit: (feedback) => _submit(feedback, context),
|
||||
extraContent: WordZoomWidget(
|
||||
token: selectedToken.text,
|
||||
construct: selectedToken.vocabConstructID,
|
||||
langCode: langCode,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,82 +3,22 @@ import 'package:flutter/material.dart';
|
|||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/bot/widgets/bot_face_svg.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/overlay.dart';
|
||||
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
|
||||
import 'package:fluffychat/pangea/token_info_feedback/token_info_feedback_dialog.dart';
|
||||
import 'package:fluffychat/pangea/token_info_feedback/token_info_feedback_request.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class TokenInfoFeedbackButton extends StatelessWidget {
|
||||
final TokenInfoFeedbackRequestData requestData;
|
||||
final String langCode;
|
||||
final PangeaMessageEvent event;
|
||||
final VoidCallback onUpdate;
|
||||
|
||||
const TokenInfoFeedbackButton({
|
||||
super.key,
|
||||
required this.requestData,
|
||||
required this.langCode,
|
||||
required this.event,
|
||||
required this.onUpdate,
|
||||
});
|
||||
|
||||
Future<void> _submitFeedback(BuildContext context) async {
|
||||
final resp = await showDialog(
|
||||
context: context,
|
||||
builder: (context) => TokenInfoFeedbackDialog(
|
||||
requestData: requestData,
|
||||
langCode: langCode,
|
||||
event: event,
|
||||
onUpdate: onUpdate,
|
||||
),
|
||||
);
|
||||
|
||||
if (resp != null && resp is String) {
|
||||
_showSuccessSnackBar(resp, context);
|
||||
}
|
||||
}
|
||||
|
||||
void _showSuccessSnackBar(String message, BuildContext context) {
|
||||
OverlayUtil.showOverlay(
|
||||
overlayKey: "token_feedback_snackbar",
|
||||
context: context,
|
||||
child: _TokenFeedbackNotification(message: message),
|
||||
transformTargetId: '',
|
||||
position: OverlayPositionEnum.top,
|
||||
backDropToDismiss: false,
|
||||
closePrevOverlay: false,
|
||||
canPop: false,
|
||||
);
|
||||
|
||||
Future.delayed(const Duration(seconds: 10), () {
|
||||
MatrixState.pAnyState.closeOverlay("token_feedback_snackbar");
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IconButton(
|
||||
icon: const Icon(Icons.flag_outlined),
|
||||
onPressed: () => _submitFeedback(context),
|
||||
tooltip: L10n.of(context).reportWordIssueTooltip,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TokenFeedbackNotification extends StatefulWidget {
|
||||
class TokenFeedbackNotification extends StatefulWidget {
|
||||
final String message;
|
||||
|
||||
const _TokenFeedbackNotification({
|
||||
const TokenFeedbackNotification({
|
||||
super.key,
|
||||
required this.message,
|
||||
});
|
||||
|
||||
@override
|
||||
State<_TokenFeedbackNotification> createState() =>
|
||||
State<TokenFeedbackNotification> createState() =>
|
||||
_TokenFeedbackNotificationState();
|
||||
}
|
||||
|
||||
class _TokenFeedbackNotificationState extends State<_TokenFeedbackNotification>
|
||||
class _TokenFeedbackNotificationState extends State<TokenFeedbackNotification>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _slideController;
|
||||
late Animation<Offset> _slideAnimation;
|
||||
|
|
@ -142,13 +142,12 @@ class PracticeMatchItemState extends State<PracticeMatchItem> {
|
|||
],
|
||||
);
|
||||
|
||||
return LongPressDraggable<PracticeChoice>(
|
||||
return Draggable<PracticeChoice>(
|
||||
data: widget.constructForm,
|
||||
feedback: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: content,
|
||||
),
|
||||
delay: const Duration(milliseconds: 50),
|
||||
onDragStarted: onTap,
|
||||
child: InkWell(
|
||||
onHover: (isHovered) => setState(() => _isHovered = isHovered),
|
||||
|
|
|
|||
|
|
@ -50,20 +50,25 @@ class ReadingAssistanceContent extends StatelessWidget {
|
|||
onClose: () => overlayController.updateSelectedSpan(null),
|
||||
langCode: overlayController.pangeaMessageEvent.messageDisplayLangCode,
|
||||
onDismissNewWordOverlay: () => overlayController.setState(() {}),
|
||||
requestData: selectedTokenIndex >= 0
|
||||
? TokenInfoFeedbackRequestData(
|
||||
userId: Matrix.of(context).client.userID!,
|
||||
roomId: overlayController.event.room.id,
|
||||
fullText: overlayController.pangeaMessageEvent.messageDisplayText,
|
||||
detectedLanguage:
|
||||
overlayController.pangeaMessageEvent.messageDisplayLangCode,
|
||||
tokens: tokens ?? [],
|
||||
selectedToken: selectedTokenIndex,
|
||||
wordCardL1: MatrixState.pangeaController.languageController
|
||||
.activeL1Code()!,
|
||||
)
|
||||
: null,
|
||||
pangeaMessageEvent: overlayController.pangeaMessageEvent,
|
||||
onFlagTokenInfo: () {
|
||||
if (selectedTokenIndex < 0) return;
|
||||
final requestData = TokenInfoFeedbackRequestData(
|
||||
userId: Matrix.of(context).client.userID!,
|
||||
roomId: overlayController.event.room.id,
|
||||
fullText: overlayController.pangeaMessageEvent.messageDisplayText,
|
||||
detectedLanguage:
|
||||
overlayController.pangeaMessageEvent.messageDisplayLangCode,
|
||||
tokens: tokens ?? [],
|
||||
selectedToken: selectedTokenIndex,
|
||||
wordCardL1:
|
||||
MatrixState.pangeaController.languageController.activeL1Code()!,
|
||||
);
|
||||
overlayController.widget.chatController.showTokenFeedbackDialog(
|
||||
requestData,
|
||||
overlayController.pangeaMessageEvent.messageDisplayLangCode,
|
||||
overlayController.pangeaMessageEvent,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ enum SelectMode {
|
|||
audio(Icons.volume_up),
|
||||
translate(Icons.translate),
|
||||
practice(Symbols.fitness_center),
|
||||
emoji(Symbols.imagesmode),
|
||||
emoji(Icons.visibility_outlined),
|
||||
speechTranslation(Icons.translate);
|
||||
|
||||
final IconData icon;
|
||||
|
|
@ -45,7 +45,7 @@ enum SelectMode {
|
|||
case SelectMode.practice:
|
||||
return l10n.practice;
|
||||
case SelectMode.emoji:
|
||||
return l10n.image;
|
||||
return l10n.emojiView;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,14 +6,11 @@ import 'package:fluffychat/config/app_config.dart';
|
|||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
|
||||
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
|
||||
import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/models/language_model.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart';
|
||||
import 'package:fluffychat/pangea/lemmas/lemma_reaction_picker.dart';
|
||||
import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_widget.dart';
|
||||
import 'package:fluffychat/pangea/token_info_feedback/token_info_feedback_button.dart';
|
||||
import 'package:fluffychat/pangea/token_info_feedback/token_info_feedback_request.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_unsubscribed_card.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/practice_activity/word_audio_button.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/word_zoom/lemma_meaning_builder.dart';
|
||||
|
|
@ -28,11 +25,13 @@ class WordZoomWidget extends StatelessWidget {
|
|||
final VoidCallback? onClose;
|
||||
|
||||
final bool wordIsNew;
|
||||
final VoidCallback? onDismissNewWordOverlay;
|
||||
final Event? event;
|
||||
|
||||
final TokenInfoFeedbackRequestData? requestData;
|
||||
final PangeaMessageEvent? pangeaMessageEvent;
|
||||
final VoidCallback? onDismissNewWordOverlay;
|
||||
final VoidCallback? onFlagTokenInfo;
|
||||
|
||||
// final TokenInfoFeedbackRequestData? requestData;
|
||||
// final PangeaMessageEvent? pangeaMessageEvent;
|
||||
|
||||
const WordZoomWidget({
|
||||
super.key,
|
||||
|
|
@ -41,10 +40,9 @@ class WordZoomWidget extends StatelessWidget {
|
|||
required this.langCode,
|
||||
this.onClose,
|
||||
this.wordIsNew = false,
|
||||
this.onDismissNewWordOverlay,
|
||||
this.event,
|
||||
this.requestData,
|
||||
this.pangeaMessageEvent,
|
||||
this.onDismissNewWordOverlay,
|
||||
this.onFlagTokenInfo,
|
||||
});
|
||||
|
||||
String get transformTargetId => "word-zoom-card-${token.uniqueKey}";
|
||||
|
|
@ -107,17 +105,12 @@ class WordZoomWidget extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
requestData != null && pangeaMessageEvent != null
|
||||
? TokenInfoFeedbackButton(
|
||||
requestData: requestData!,
|
||||
langCode: langCode,
|
||||
event: pangeaMessageEvent!,
|
||||
onUpdate: () {
|
||||
// close the zoom when updating
|
||||
if (onClose != null) {
|
||||
onClose!();
|
||||
}
|
||||
},
|
||||
onFlagTokenInfo != null
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.flag_outlined),
|
||||
onPressed: onFlagTokenInfo,
|
||||
tooltip:
|
||||
L10n.of(context).reportWordIssueTooltip,
|
||||
)
|
||||
: const SizedBox(
|
||||
width: 40.0,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue