3396 collect on selection animation not line up (#3468)
* fix: switch to overlayUtil instead of manual overlay entry to fix alignment, and comment out second seed animation *still needs some work on the top right seed in word card * fix: fix construct xp widget bug and comment out previous animation also change message selection overlay to trigger update when animation is finished rather than on two conflicting timers * merge conflicts and code formatting * format --------- Co-authored-by: ggurdin <ggurdin@gmail.com> Co-authored-by: ggurdin <46800240+ggurdin@users.noreply.github.com>
This commit is contained in:
parent
593a0cc9c2
commit
8e5dc610f8
5 changed files with 336 additions and 314 deletions
|
|
@ -77,8 +77,8 @@ class ConstructXpWidgetState extends State<ConstructXpWidget>
|
|||
setState(() {
|
||||
constructLemmaCategory = constructUse?.lemmaCategory;
|
||||
didChange = true;
|
||||
_controller.reset();
|
||||
_controller.forward();
|
||||
//_controller.reset();
|
||||
//_controller.forward();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -113,18 +113,20 @@ class ConstructXpWidgetState extends State<ConstructXpWidget>
|
|||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 1000),
|
||||
child: svg,
|
||||
),
|
||||
if (didChange)
|
||||
SlideTransition(
|
||||
position: _offsetAnimation,
|
||||
child: FadeTransition(
|
||||
opacity: _fadeAnimation,
|
||||
child: svg,
|
||||
),
|
||||
),
|
||||
//replaces rise animation, remove 116 and uncomment everything to revert
|
||||
svg != null ? svg! : const SizedBox.shrink(),
|
||||
// AnimatedSwitcher(
|
||||
// duration: const Duration(milliseconds: 1000),
|
||||
// child: svg,
|
||||
// ),
|
||||
// if (didChange)
|
||||
// SlideTransition(
|
||||
// position: _offsetAnimation,
|
||||
// child: FadeTransition(
|
||||
// opacity: _fadeAnimation,
|
||||
// child: svg,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -279,9 +279,11 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
|
||||
_selectedSpan = selectedSpan;
|
||||
if (mounted) setState(() {});
|
||||
if (selectedToken != null && isNewToken(selectedToken!)) {
|
||||
_onSelectNewToken(selectedToken!);
|
||||
}
|
||||
|
||||
//Commented out so onSelectNewTokens can be manually called after animation is finished
|
||||
// if (selectedToken != null && isNewToken(selectedToken!)) {
|
||||
// _onSelectNewToken(selectedToken!);
|
||||
// }
|
||||
}
|
||||
|
||||
void _showReadingAssistanceContent() {
|
||||
|
|
@ -556,43 +558,41 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
updateSelectedSpan(token.text);
|
||||
}
|
||||
|
||||
void _onSelectNewToken(PangeaToken token) {
|
||||
void onSelectNewToken(PangeaToken token) {
|
||||
if (!isNewToken(token)) return;
|
||||
Future.delayed(const Duration(milliseconds: 1700), () {
|
||||
MatrixState.pangeaController.putAnalytics.setState(
|
||||
AnalyticsStream(
|
||||
eventId: event.eventId,
|
||||
roomId: event.room.id,
|
||||
constructs: [
|
||||
OneConstructUse(
|
||||
useType: ConstructUseTypeEnum.click,
|
||||
lemma: token.lemma.text,
|
||||
constructType: ConstructTypeEnum.vocab,
|
||||
metadata: ConstructUseMetaData(
|
||||
roomId: event.room.id,
|
||||
timeStamp: DateTime.now(),
|
||||
eventId: event.eventId,
|
||||
),
|
||||
category: token.pos,
|
||||
form: token.text.content,
|
||||
xp: ConstructUseTypeEnum.click.pointValue,
|
||||
MatrixState.pangeaController.putAnalytics.setState(
|
||||
AnalyticsStream(
|
||||
eventId: event.eventId,
|
||||
roomId: event.room.id,
|
||||
constructs: [
|
||||
OneConstructUse(
|
||||
useType: ConstructUseTypeEnum.click,
|
||||
lemma: token.lemma.text,
|
||||
constructType: ConstructTypeEnum.vocab,
|
||||
metadata: ConstructUseMetaData(
|
||||
roomId: event.room.id,
|
||||
timeStamp: DateTime.now(),
|
||||
eventId: event.eventId,
|
||||
),
|
||||
],
|
||||
targetID: token.text.uniqueKey,
|
||||
),
|
||||
);
|
||||
category: token.pos,
|
||||
form: token.text.content,
|
||||
xp: ConstructUseTypeEnum.click.pointValue,
|
||||
),
|
||||
],
|
||||
targetID: token.text.uniqueKey,
|
||||
),
|
||||
);
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
newTokens.removeWhere(
|
||||
(t) =>
|
||||
t.text.offset == token.text.offset &&
|
||||
t.text.length == token.text.length &&
|
||||
t.lemma.text.equals(token.lemma.text),
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
newTokens.removeWhere(
|
||||
(t) =>
|
||||
t.text.offset == token.text.offset &&
|
||||
t.text.length == token.text.length &&
|
||||
t.lemma.text.equals(token.lemma.text),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
PracticeTarget? practiceTargetForToken(PangeaToken token) {
|
||||
|
|
|
|||
|
|
@ -117,6 +117,11 @@ class ReadingAssistanceContentState extends State<ReadingAssistanceContent> {
|
|||
);
|
||||
}
|
||||
return WordZoomWidget(
|
||||
key: MatrixState.pAnyState
|
||||
.layerLinkAndKey(
|
||||
"word-zoom-card-${widget.overlayController.selectedToken!.text.uniqueKey}",
|
||||
)
|
||||
.key,
|
||||
token: widget.overlayController.selectedToken!,
|
||||
messageEvent: widget.overlayController.pangeaMessageEvent!,
|
||||
overlayController: widget.overlayController,
|
||||
|
|
|
|||
|
|
@ -3,16 +3,24 @@ import 'dart:math';
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/overlay.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_level_enum.dart';
|
||||
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class NewWordOverlay extends StatefulWidget {
|
||||
final Color overlayColor;
|
||||
final GlobalKey cardKey;
|
||||
final MessageOverlayController overlayController;
|
||||
final PangeaToken token;
|
||||
final String transformTargetId;
|
||||
|
||||
const NewWordOverlay({
|
||||
super.key,
|
||||
required this.overlayColor,
|
||||
required this.cardKey,
|
||||
required this.overlayController,
|
||||
required this.token,
|
||||
required this.transformTargetId,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -24,10 +32,9 @@ class _NewWordOverlayState extends State<NewWordOverlay>
|
|||
AnimationController? _controller;
|
||||
Animation<double>? _xpScaleAnim;
|
||||
Animation<double>? _fadeAnim;
|
||||
Size cardSize = const Size(0, 0);
|
||||
Offset cardPosition = const Offset(0, 0);
|
||||
OverlayEntry? _overlayEntry;
|
||||
Animation<double>? _moveAnim;
|
||||
bool columnMode = false;
|
||||
|
||||
Widget? get svg => ConstructLevelEnum.seeds.icon();
|
||||
|
||||
@override
|
||||
|
|
@ -35,20 +42,30 @@ class _NewWordOverlayState extends State<NewWordOverlay>
|
|||
super.initState();
|
||||
_controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 1700),
|
||||
);
|
||||
duration: const Duration(milliseconds: 1850),
|
||||
)..addStatusListener((AnimationStatus status) {
|
||||
if (status == AnimationStatus.completed) {
|
||||
dispose();
|
||||
}
|
||||
});
|
||||
|
||||
_xpScaleAnim = CurvedAnimation(
|
||||
parent: _controller!,
|
||||
curve: const Interval(0.0, 0.6, curve: Curves.easeInOut),
|
||||
curve: const Interval(0.0, 0.5, curve: Curves.easeInOut),
|
||||
);
|
||||
|
||||
_fadeAnim = CurvedAnimation(
|
||||
parent: _controller!,
|
||||
curve: const Interval(0.7, 1.0, curve: Curves.easeOut),
|
||||
curve: const Interval(0.5, 1.0, curve: Curves.easeOut),
|
||||
);
|
||||
|
||||
_moveAnim = CurvedAnimation(
|
||||
parent: _controller!,
|
||||
curve: const Interval(0.5, 1.0, curve: Curves.easeOut),
|
||||
);
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
columnMode = FluffyThemes.isColumnMode(context);
|
||||
calculateSizeAndPosition();
|
||||
_showFlyingWidget();
|
||||
_controller?.forward();
|
||||
});
|
||||
|
|
@ -56,63 +73,55 @@ class _NewWordOverlayState extends State<NewWordOverlay>
|
|||
|
||||
@override
|
||||
void dispose() {
|
||||
_overlayEntry?.remove();
|
||||
widget.overlayController.onSelectNewToken(widget.token);
|
||||
_controller?.dispose();
|
||||
MatrixState.pAnyState.closeOverlay(widget.transformTargetId);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void calculateSizeAndPosition() {
|
||||
//find position of word card and overlaybox(chat view) to figure out where seed should start
|
||||
final RenderBox? cardBox =
|
||||
widget.cardKey.currentContext?.findRenderObject() as RenderBox?;
|
||||
final RenderBox? overlayBox =
|
||||
Overlay.of(context).context.findRenderObject() as RenderBox?;
|
||||
if (cardBox != null && overlayBox != null) {
|
||||
final cardGlobal = cardBox.localToGlobal(Offset.zero);
|
||||
final overlayGlobal = overlayBox.localToGlobal(Offset.zero);
|
||||
setState(() {
|
||||
cardPosition = cardGlobal - overlayGlobal;
|
||||
cardSize = cardBox.size;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _showFlyingWidget() {
|
||||
_overlayEntry?.remove(); // Remove any existing overlay
|
||||
if (_controller == null || _xpScaleAnim == null || _fadeAnim == null) {
|
||||
if (_controller == null ||
|
||||
_xpScaleAnim == null ||
|
||||
_fadeAnim == null ||
|
||||
_moveAnim == null) {
|
||||
return;
|
||||
}
|
||||
_overlayEntry = OverlayEntry(
|
||||
builder: (context) => AnimatedBuilder(
|
||||
|
||||
OverlayUtil.showOverlay(
|
||||
context: context,
|
||||
closePrevOverlay: false,
|
||||
ignorePointer: true,
|
||||
// onDismiss: () {
|
||||
// MatrixState.pAnyState.closeOverlay(widget.transformTargetId);
|
||||
// },
|
||||
offset: const Offset(0, 65),
|
||||
targetAnchor: Alignment.center,
|
||||
overlayKey: widget.transformTargetId,
|
||||
transformTargetId: widget.transformTargetId,
|
||||
child: AnimatedBuilder(
|
||||
animation: _controller!,
|
||||
builder: (context, child) {
|
||||
final scale = _xpScaleAnim!.value;
|
||||
final fade = 1.0 - (_fadeAnim!.value);
|
||||
double t = 0.0;
|
||||
if ((_controller!.value) >= 0.7) {
|
||||
t = ((_controller!.value) - 0.7) / 0.3;
|
||||
t = t.clamp(0.0, 1.0);
|
||||
}
|
||||
//move starting position as seed grows so it stays centered
|
||||
final seedSize = 75 * scale * ((!columnMode) ? fade : 1);
|
||||
final startX = cardPosition.dx + cardSize.width / 2 - seedSize;
|
||||
final startY = cardPosition.dy + cardSize.height / 2 + 20 - seedSize;
|
||||
//end is top left if column mode (going towards vocab stats) or top right of card otherwise
|
||||
final endX = (columnMode) ? 0.0 : cardPosition.dx + cardSize.width;
|
||||
final endY = (columnMode) ? 0.0 : cardPosition.dy + 30;
|
||||
final currentX = startX * (1 - t) + endX * t;
|
||||
final currentY = startY * (1 - t) + endY * t;
|
||||
//Grows into frame, and then shrinks if going to top right so it matches card seed size
|
||||
final move = _moveAnim!.value;
|
||||
|
||||
return Positioned(
|
||||
left: currentX,
|
||||
top: currentY,
|
||||
final seedSize = 75 * scale * fade;
|
||||
|
||||
// Calculate movement to top left if fullscreen, or top right of word card if mobile
|
||||
final screenSize = MediaQuery.of(context).size;
|
||||
final moveX =
|
||||
columnMode ? -move * (screenSize.width / 2 - 50) : move * 130;
|
||||
|
||||
final moveY =
|
||||
columnMode ? -move * (screenSize.height / 2 - 50) : move * -120;
|
||||
|
||||
return Transform.translate(
|
||||
offset: Offset(moveX, moveY),
|
||||
child: Opacity(
|
||||
opacity: fade,
|
||||
child: Transform.rotate(
|
||||
angle: scale * 2 * pi,
|
||||
child: SizedBox(
|
||||
//if going to card top right, shrinks as it moves to match word card seed size
|
||||
width: seedSize,
|
||||
height: seedSize,
|
||||
child: svg ?? const SizedBox(),
|
||||
|
|
@ -123,36 +132,28 @@ class _NewWordOverlayState extends State<NewWordOverlay>
|
|||
},
|
||||
),
|
||||
);
|
||||
Overlay.of(context).insert(_overlayEntry!);
|
||||
_controller?.addStatusListener((status) {
|
||||
if (status == AnimationStatus.completed) {
|
||||
_overlayEntry?.remove();
|
||||
_overlayEntry = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
Container(
|
||||
height: cardSize.height,
|
||||
width: cardSize.width,
|
||||
color: Colors.transparent,
|
||||
),
|
||||
Positioned(
|
||||
return AnimatedBuilder(
|
||||
animation: _controller!,
|
||||
builder: (context, child) {
|
||||
return Positioned(
|
||||
left: 5,
|
||||
right: 5,
|
||||
top: 50,
|
||||
bottom: 5,
|
||||
child: Container(
|
||||
height: cardSize.height,
|
||||
width: cardSize.width,
|
||||
color: widget.overlayColor,
|
||||
child: Opacity(
|
||||
opacity: 1 - _fadeAnim!.value,
|
||||
child: Container(
|
||||
height: double.infinity,
|
||||
width: double.infinity,
|
||||
color: widget.overlayColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,222 +43,236 @@ class WordZoomWidget extends StatelessWidget {
|
|||
true &&
|
||||
overlayController.hideWordCardContent;
|
||||
|
||||
String get transformTargetId => "newer-word-overlay-${token.text.uniqueKey}";
|
||||
|
||||
LayerLink get layerLink =>
|
||||
MatrixState.pAnyState.layerLinkAndKey(transformTargetId).link;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final GlobalKey cardKey = MatrixState.pAnyState
|
||||
.layerLinkAndKey("word-zoom-card-${token.text.uniqueKey}")
|
||||
.key;
|
||||
// final GlobalKey cardKey = MatrixState.pAnyState
|
||||
// .layerLinkAndKey("word-zoom-card-${token.text.uniqueKey}")
|
||||
// .key;
|
||||
final overlayColor = Theme.of(context).scaffoldBackgroundColor;
|
||||
return Stack(
|
||||
children: [
|
||||
Container(
|
||||
key: cardKey,
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
constraints: const BoxConstraints(
|
||||
minHeight: AppConfig.toolbarMinHeight - 8,
|
||||
maxHeight: AppConfig.toolbarMaxHeight - 8,
|
||||
maxWidth: AppConfig.toolbarMinWidth,
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
spacing: 12.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 24.0,
|
||||
height: 24.0,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: GestureDetector(
|
||||
onTap: () =>
|
||||
overlayController.updateSelectedSpan(null),
|
||||
child: const Icon(
|
||||
Icons.close,
|
||||
size: 16.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
token.text.content,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 28.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.2,
|
||||
color:
|
||||
Theme.of(context).brightness == Brightness.light
|
||||
? AppConfig.yellowDark
|
||||
: AppConfig.yellowLight,
|
||||
),
|
||||
),
|
||||
),
|
||||
ConstructXpWidget(
|
||||
id: token.vocabConstructID,
|
||||
onTap: () => context.go(
|
||||
"/rooms/analytics?mode=vocab",
|
||||
extra: token.vocabConstructID,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
LemmaMeaningBuilder(
|
||||
langCode: messageEvent.messageDisplayLangCode,
|
||||
constructId: token.vocabConstructID,
|
||||
builder: (context, controller) {
|
||||
if (controller.editMode) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
"${L10n.of(context).pangeaBotIsFallible} ${L10n.of(context).whatIsMeaning(
|
||||
token.vocabConstructID.lemma,
|
||||
token.vocabConstructID.category,
|
||||
)}",
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(fontStyle: FontStyle.italic),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: TextField(
|
||||
minLines: 1,
|
||||
maxLines: 3,
|
||||
controller: controller.controller,
|
||||
decoration: InputDecoration(
|
||||
hintText: controller.lemmaInfo?.meaning,
|
||||
),
|
||||
child: CompositedTransformTarget(
|
||||
link: layerLink,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
spacing: 12.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 24.0,
|
||||
height: 24.0,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: GestureDetector(
|
||||
onTap: () =>
|
||||
overlayController.updateSelectedSpan(null),
|
||||
child: const Icon(
|
||||
Icons.close,
|
||||
size: 16.0,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () =>
|
||||
controller.toggleEditMode(false),
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
),
|
||||
),
|
||||
child: Text(L10n.of(context).cancel),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
ElevatedButton(
|
||||
onPressed: () => controller.controller.text !=
|
||||
controller.lemmaInfo?.meaning &&
|
||||
controller.controller.text.isNotEmpty
|
||||
? controller.editLemmaMeaning(
|
||||
controller.controller.text,
|
||||
)
|
||||
: null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
),
|
||||
),
|
||||
child: Text(L10n.of(context).saveChanges),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return Column(
|
||||
spacing: 12.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (MatrixState.pangeaController.languageController
|
||||
.showTrancription)
|
||||
PhoneticTranscriptionWidget(
|
||||
text: token.text.content,
|
||||
textLanguage: PLanguageStore.byLangCode(
|
||||
messageEvent.messageDisplayLangCode,
|
||||
) ??
|
||||
LanguageModel.unknown,
|
||||
style: const TextStyle(fontSize: 14.0),
|
||||
iconSize: 24.0,
|
||||
)
|
||||
else
|
||||
WordAudioButton(
|
||||
text: token.text.content,
|
||||
uniqueID: "lemma-content-${token.text.content}",
|
||||
langCode: messageEvent.messageDisplayLangCode,
|
||||
iconSize: 24.0,
|
||||
),
|
||||
LemmaReactionPicker(
|
||||
cId: _selectedToken.vocabConstructID,
|
||||
event: messageEvent.event,
|
||||
controller: overlayController.widget.chatController,
|
||||
),
|
||||
if (controller.error != null)
|
||||
ErrorIndicator(
|
||||
message: L10n.of(context).errorFetchingDefinition,
|
||||
style: const TextStyle(fontSize: 14.0),
|
||||
)
|
||||
else if (controller.isLoading ||
|
||||
controller.lemmaInfo == null)
|
||||
const CircularProgressIndicator.adaptive()
|
||||
else
|
||||
GestureDetector(
|
||||
onLongPress: () => controller.toggleEditMode(true),
|
||||
onDoubleTap: () => controller.toggleEditMode(true),
|
||||
child: token.lemma.text.toLowerCase() ==
|
||||
token.text.content.toLowerCase()
|
||||
? Text(
|
||||
controller.lemmaInfo!.meaning,
|
||||
style: const TextStyle(fontSize: 14.0),
|
||||
textAlign: TextAlign.center,
|
||||
)
|
||||
: RichText(
|
||||
text: TextSpan(
|
||||
style: DefaultTextStyle.of(context)
|
||||
.style
|
||||
.copyWith(
|
||||
fontSize: 14.0,
|
||||
),
|
||||
children: [
|
||||
TextSpan(text: token.lemma.text),
|
||||
const WidgetSpan(
|
||||
child: SizedBox(width: 8.0),
|
||||
),
|
||||
const TextSpan(text: ":"),
|
||||
const WidgetSpan(
|
||||
child: SizedBox(width: 8.0),
|
||||
),
|
||||
TextSpan(
|
||||
text: controller.lemmaInfo!.meaning,
|
||||
),
|
||||
],
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
token.text.content,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 28.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.2,
|
||||
color:
|
||||
Theme.of(context).brightness == Brightness.light
|
||||
? AppConfig.yellowDark
|
||||
: AppConfig.yellowLight,
|
||||
),
|
||||
),
|
||||
),
|
||||
ConstructXpWidget(
|
||||
id: token.vocabConstructID,
|
||||
onTap: () => context.go(
|
||||
"/rooms/analytics?mode=vocab",
|
||||
extra: token.vocabConstructID,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
LemmaMeaningBuilder(
|
||||
langCode: messageEvent.messageDisplayLangCode,
|
||||
constructId: token.vocabConstructID,
|
||||
builder: (context, controller) {
|
||||
if (controller.editMode) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
"${L10n.of(context).pangeaBotIsFallible} ${L10n.of(context).whatIsMeaning(
|
||||
token.vocabConstructID.lemma,
|
||||
token.vocabConstructID.category,
|
||||
)}",
|
||||
textAlign: TextAlign.center,
|
||||
style:
|
||||
const TextStyle(fontStyle: FontStyle.italic),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: TextField(
|
||||
minLines: 1,
|
||||
maxLines: 3,
|
||||
controller: controller.controller,
|
||||
decoration: InputDecoration(
|
||||
hintText: controller.lemmaInfo?.meaning,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () =>
|
||||
controller.toggleEditMode(false),
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
),
|
||||
),
|
||||
child: Text(L10n.of(context).cancel),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
ElevatedButton(
|
||||
onPressed: () => controller.controller.text !=
|
||||
controller.lemmaInfo?.meaning &&
|
||||
controller.controller.text.isNotEmpty
|
||||
? controller.editLemmaMeaning(
|
||||
controller.controller.text,
|
||||
)
|
||||
: null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
),
|
||||
),
|
||||
child: Text(L10n.of(context).saveChanges),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return Column(
|
||||
spacing: 12.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (MatrixState.pangeaController.languageController
|
||||
.showTrancription)
|
||||
PhoneticTranscriptionWidget(
|
||||
text: token.text.content,
|
||||
textLanguage: PLanguageStore.byLangCode(
|
||||
messageEvent.messageDisplayLangCode,
|
||||
) ??
|
||||
LanguageModel.unknown,
|
||||
style: const TextStyle(fontSize: 14.0),
|
||||
iconSize: 24.0,
|
||||
)
|
||||
else
|
||||
WordAudioButton(
|
||||
text: token.text.content,
|
||||
uniqueID: "lemma-content-${token.text.content}",
|
||||
langCode: messageEvent.messageDisplayLangCode,
|
||||
iconSize: 24.0,
|
||||
),
|
||||
LemmaReactionPicker(
|
||||
cId: _selectedToken.vocabConstructID,
|
||||
event: messageEvent.event,
|
||||
controller: overlayController.widget.chatController,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
if (controller.error != null)
|
||||
ErrorIndicator(
|
||||
message: L10n.of(context).errorFetchingDefinition,
|
||||
style: const TextStyle(fontSize: 14.0),
|
||||
)
|
||||
else if (controller.isLoading ||
|
||||
controller.lemmaInfo == null)
|
||||
const CircularProgressIndicator.adaptive()
|
||||
else
|
||||
GestureDetector(
|
||||
onLongPress: () =>
|
||||
controller.toggleEditMode(true),
|
||||
onDoubleTap: () =>
|
||||
controller.toggleEditMode(true),
|
||||
child: token.lemma.text.toLowerCase() ==
|
||||
token.text.content.toLowerCase()
|
||||
? Text(
|
||||
controller.lemmaInfo!.meaning,
|
||||
style: const TextStyle(fontSize: 14.0),
|
||||
textAlign: TextAlign.center,
|
||||
)
|
||||
: RichText(
|
||||
text: TextSpan(
|
||||
style: DefaultTextStyle.of(context)
|
||||
.style
|
||||
.copyWith(
|
||||
fontSize: 14.0,
|
||||
),
|
||||
children: [
|
||||
TextSpan(text: token.lemma.text),
|
||||
const WidgetSpan(
|
||||
child: SizedBox(width: 8.0),
|
||||
),
|
||||
const TextSpan(text: ":"),
|
||||
const WidgetSpan(
|
||||
child: SizedBox(width: 8.0),
|
||||
),
|
||||
TextSpan(
|
||||
text: controller.lemmaInfo!.meaning,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
wordIsNew
|
||||
? NewWordOverlay(
|
||||
key: ValueKey(transformTargetId),
|
||||
token: token,
|
||||
overlayColor: overlayColor,
|
||||
cardKey: cardKey,
|
||||
overlayController: overlayController,
|
||||
transformTargetId: transformTargetId,
|
||||
//cardKey: cardKey,
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
],
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue