refactor: position points animation by keys instead of as a positioned widget in a stack (#2230)

This commit is contained in:
ggurdin 2025-03-26 10:51:16 -04:00 committed by GitHub
parent 027e13f32d
commit ba7a9ebf53
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 565 additions and 603 deletions

View file

@ -28,6 +28,7 @@ import 'package:fluffychat/pages/chat/event_info_dialog.dart';
import 'package:fluffychat/pages/chat/recording_dialog.dart';
import 'package:fluffychat/pages/chat_details/chat_details.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/analytics_misc/gain_points_animation.dart';
import 'package:fluffychat/pangea/analytics_misc/level_up.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
import 'package:fluffychat/pangea/chat/utils/unlocked_morphs_snackbar.dart';
@ -129,8 +130,10 @@ class ChatController extends State<ChatPageWithRoom>
// #Pangea
final PangeaController pangeaController = MatrixState.pangeaController;
late Choreographer choreographer = Choreographer(pangeaController, this);
StreamSubscription? _levelSubscription;
late GoRouter _router;
StreamSubscription? _levelSubscription;
StreamSubscription? _analyticsSubscription;
// Pangea#
Room get room => sendingClient.getRoomById(roomId) ?? widget.room;
@ -399,6 +402,23 @@ class ChatController extends State<ChatPageWithRoom>
}
},
);
_analyticsSubscription =
pangeaController.getAnalytics.analyticsStream.stream.listen((u) {
if (u.targetID == null) return;
OverlayUtil.showOverlay(
overlayKey: u.targetID,
followerAnchor: Alignment.bottomCenter,
targetAnchor: Alignment.bottomCenter,
context: context,
child: PointsGainedAnimation(
points: u.points,
targetID: u.targetID!,
),
transformTargetId: u.targetID ?? "",
closePrevOverlay: false,
);
});
// Pangea#
_tryLoadTimeline();
if (kIsWeb) {
@ -645,6 +665,7 @@ class ChatController extends State<ChatPageWithRoom>
stopAudioStream.close();
hideTextController.dispose();
_levelSubscription?.cancel();
_analyticsSubscription?.cancel();
_router.routeInformationProvider.removeListener(_onRouteChanged);
//Pangea#
super.dispose();
@ -798,6 +819,7 @@ class ChatController extends State<ChatPageWithRoom>
pangeaController.putAnalytics.setState(
AnalyticsStream(
eventId: msgEventId,
targetID: msgEventId,
roomId: room.id,
constructs: [
...originalSent.vocabAndMorphUses(
@ -806,7 +828,6 @@ class ChatController extends State<ChatPageWithRoom>
metadata: metadata,
),
],
origin: AnalyticsUpdateOrigin.sendMessage,
),
);
}

View file

@ -1,24 +1,19 @@
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics_misc/get_analytics_controller.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
import 'package:fluffychat/pangea/bot/utils/bot_style.dart';
import 'package:fluffychat/widgets/matrix.dart';
class PointsGainedAnimation extends StatefulWidget {
final Color? gainColor;
final Color? loseColor;
final AnalyticsUpdateOrigin origin;
final int points;
final String targetID;
const PointsGainedAnimation({
super.key,
required this.origin,
this.gainColor = AppConfig.gold,
this.loseColor = Colors.red,
required this.points,
required this.targetID,
});
@override
@ -27,25 +22,22 @@ class PointsGainedAnimation extends StatefulWidget {
class PointsGainedAnimationState extends State<PointsGainedAnimation>
with SingleTickerProviderStateMixin {
final Color? gainColor = AppConfig.gold;
final Color? loseColor = Colors.red;
late AnimationController _controller;
late Animation<Offset> _offsetAnimation;
late Animation<double> _fadeAnimation;
final List<Animation<double>> _swayAnimation = [];
final List<double> _randomSwayOffset = [];
final List<Offset> _particleTrajectories = [];
StreamSubscription? _pointsSubscription;
int? get _prevXP =>
MatrixState.pangeaController.getAnalytics.constructListModel.prevXP;
int? get _currentXP =>
MatrixState.pangeaController.getAnalytics.constructListModel.totalXP;
int? _addedPoints;
final Random _random = Random();
@override
void initState() {
super.initState();
if (widget.points == 0) return;
_controller = AnimationController(
duration: const Duration(milliseconds: 2000),
vsync: this,
@ -71,14 +63,12 @@ class PointsGainedAnimationState extends State<PointsGainedAnimation>
),
);
_pointsSubscription = MatrixState
.pangeaController.getAnalytics.analyticsStream.stream
.listen(_showPointsGained);
_showPointsGained();
}
void initParticleTrajectories() {
_particleTrajectories.clear();
for (int i = 0; i < (_addedPoints?.abs() ?? 0); i++) {
for (int i = 0; i < widget.points.abs(); i++) {
final angle = _random.nextDouble() * (pi / 2) +
pi / 4; // Random angle in the V-shaped range.
const baseSpeed = 20; // Initial base speed.
@ -93,10 +83,9 @@ class PointsGainedAnimationState extends State<PointsGainedAnimation>
void initSwayAnimations() {
_swayAnimation.clear();
_randomSwayOffset.clear();
initParticleTrajectories();
for (int i = 0; i < (_addedPoints ?? 0); i++) {
for (int i = 0; i < widget.points; i++) {
_swayAnimation.add(
Tween<double>(
begin: 0.0,
@ -108,41 +97,41 @@ class PointsGainedAnimationState extends State<PointsGainedAnimation>
),
),
);
_randomSwayOffset.add(_random.nextDouble() * 2 * pi);
}
}
@override
void dispose() {
_controller.dispose();
_pointsSubscription?.cancel();
super.dispose();
}
void _showPointsGained(AnalyticsStreamUpdate update) {
if (update.origin != widget.origin) return;
setState(() => _addedPoints = (_currentXP ?? 0) - (_prevXP ?? 0));
if (_prevXP != _currentXP) {
initSwayAnimations();
_controller.reset();
_controller.forward();
}
void _showPointsGained() {
initSwayAnimations();
_controller.reset();
_controller.forward().then(
(_) {
if (!mounted) return;
MatrixState.pAnyState.closeOverlay(widget.targetID);
},
);
}
bool get animate =>
_currentXP != null &&
_prevXP != null &&
_addedPoints != null &&
_prevXP! != _currentXP!;
@override
Widget build(BuildContext context) {
if (!animate) return const SizedBox();
if (widget.points == 0) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
MatrixState.pAnyState.closeOverlay(widget.targetID);
}
});
return const SizedBox();
}
final textColor = _addedPoints! > 0 ? widget.gainColor : widget.loseColor;
final textColor = widget.points > 0 ? gainColor : loseColor;
final plusWidget = Text(
_addedPoints! > 0 ? "+" : "-",
widget.points > 0 ? "+" : "-",
style: BotStyle.text(
context,
big: true,
@ -153,29 +142,32 @@ class PointsGainedAnimationState extends State<PointsGainedAnimation>
),
);
return SlideTransition(
position: _offsetAnimation,
child: FadeTransition(
opacity: _fadeAnimation,
child: IgnorePointer(
ignoring: _controller.isAnimating,
child: Stack(
children: List.generate(_addedPoints!.abs(), (index) {
return AnimatedBuilder(
animation: _controller,
builder: (context, child) {
final progress = _controller.value;
final trajectory = _particleTrajectories[index];
return Transform.translate(
offset: Offset(
trajectory.dx * pow(progress, 2),
trajectory.dy * pow(progress, 2),
),
child: plusWidget,
);
},
);
}),
return Material(
type: MaterialType.transparency,
child: SlideTransition(
position: _offsetAnimation,
child: FadeTransition(
opacity: _fadeAnimation,
child: IgnorePointer(
ignoring: _controller.isAnimating,
child: Stack(
children: List.generate(widget.points.abs(), (index) {
return AnimatedBuilder(
animation: _controller,
builder: (context, child) {
final progress = _controller.value;
final trajectory = _particleTrajectories[index];
return Transform.translate(
offset: Offset(
trajectory.dx * pow(progress, 2),
trajectory.dy * pow(progress, 2),
),
child: plusWidget,
);
},
);
}),
),
),
),
),

View file

@ -109,7 +109,7 @@ class GetAnalyticsController extends BaseController {
data: {},
);
} finally {
_updateAnalyticsStream();
_updateAnalyticsStream(points: 0);
if (!initCompleter.isCompleted) initCompleter.complete();
_initializing = false;
}
@ -154,7 +154,13 @@ class GetAnalyticsController extends BaseController {
if (newUnlockedMorphs.isNotEmpty) {
_onUnlockMorphLemmas(newUnlockedMorphs);
}
_updateAnalyticsStream(origin: analyticsUpdate.origin);
_updateAnalyticsStream(
points: analyticsUpdate.newConstructs.fold<int>(
0,
(previousValue, element) => previousValue + element.pointValue,
),
targetID: analyticsUpdate.targetID,
);
// Update public profile each time that new analytics are added.
// If the level hasn't changed, this will not send an update to the server.
// Do this on all updates (not just on level updates) to account for cases
@ -165,9 +171,15 @@ class GetAnalyticsController extends BaseController {
}
void _updateAnalyticsStream({
AnalyticsUpdateOrigin? origin,
required int points,
String? targetID,
}) =>
analyticsStream.add(AnalyticsStreamUpdate(origin: origin));
analyticsStream.add(
AnalyticsStreamUpdate(
points: points,
targetID: targetID,
),
);
Future<void> _onLevelUp(final int lowerLevel, final int upperLevel) async {
final result = await _generateLevelUpAnalyticsAndSaveToStateEvent(
@ -484,9 +496,11 @@ class AnalyticsCacheEntry {
}
class AnalyticsStreamUpdate {
final AnalyticsUpdateOrigin? origin;
final int points;
final String? targetID;
AnalyticsStreamUpdate({
this.origin,
required this.points,
this.targetID,
});
}

View file

@ -144,7 +144,7 @@ class PutAnalyticsController extends BaseController<AnalyticsStream> {
if (roomID != null) _clearDraftUses(roomID);
_decideWhetherToUpdateAnalyticsRoom(
level,
data.origin,
data.targetID,
data.constructs,
);
},
@ -164,9 +164,9 @@ class PutAnalyticsController extends BaseController<AnalyticsStream> {
void addDraftUses(
List<PangeaToken> tokens,
String roomID,
ConstructUseTypeEnum useType,
AnalyticsUpdateOrigin origin,
) {
ConstructUseTypeEnum useType, {
String? targetID,
}) {
final metadata = ConstructUseMetaData(
roomId: roomID,
timeStamp: DateTime.now(),
@ -230,7 +230,11 @@ class PutAnalyticsController extends BaseController<AnalyticsStream> {
// so copy it here to that the list of new uses is accurate
final List<OneConstructUse> newUses = List.from(uses);
_addLocalMessage('draft$roomID', uses).then(
(_) => _decideWhetherToUpdateAnalyticsRoom(level, origin, newUses),
(_) => _decideWhetherToUpdateAnalyticsRoom(
level,
targetID,
newUses,
),
);
}
@ -287,7 +291,7 @@ class PutAnalyticsController extends BaseController<AnalyticsStream> {
/// Otherwise, add a local update to the alert stream.
void _decideWhetherToUpdateAnalyticsRoom(
int prevLevel,
AnalyticsUpdateOrigin? origin,
String? targetID,
List<OneConstructUse> newConstructs,
) {
// cancel the last timer that was set on message event and
@ -308,7 +312,7 @@ class PutAnalyticsController extends BaseController<AnalyticsStream> {
AnalyticsUpdate(
AnalyticsUpdateType.local,
newConstructs,
origin: origin,
targetID: targetID,
),
);
}
@ -426,7 +430,7 @@ class PutAnalyticsController extends BaseController<AnalyticsStream> {
class AnalyticsStream {
final String? eventId;
final String? roomId;
final AnalyticsUpdateOrigin? origin;
final String? targetID;
final List<OneConstructUse> constructs;
@ -434,29 +438,20 @@ class AnalyticsStream {
required this.eventId,
required this.roomId,
required this.constructs,
this.origin,
this.targetID,
});
}
enum AnalyticsUpdateOrigin {
it,
igc,
sendMessage,
practiceActivity,
inputBar,
wordZoom,
}
class AnalyticsUpdate {
final AnalyticsUpdateType type;
final AnalyticsUpdateOrigin? origin;
final List<OneConstructUse> newConstructs;
final bool isLogout;
final String? targetID;
AnalyticsUpdate(
this.type,
this.newConstructs, {
this.isLogout = false,
this.origin,
this.targetID,
});
}

View file

@ -1,10 +1,7 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pangea/analytics_misc/gain_points_animation.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
import 'package:fluffychat/pangea/chat/widgets/chat_floating_action_button.dart';
class ChatInputBarHeader extends StatelessWidget {
@ -35,11 +32,6 @@ class ChatInputBarHeader extends StatelessWidget {
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const PointsGainedAnimation(
gainColor: AppConfig.gold,
origin: AnalyticsUpdateOrigin.sendMessage,
),
const SizedBox(width: 100),
ChatFloatingActionButton(
controller: controller,
),

View file

@ -8,7 +8,6 @@ import 'package:http/http.dart' as http;
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
import 'package:fluffychat/pangea/choreographer/constants/choreo_constants.dart';
import 'package:fluffychat/pangea/choreographer/controllers/error_service.dart';
import 'package:fluffychat/pangea/choreographer/enums/edit_type.dart';
@ -333,7 +332,6 @@ class ITController {
ignoredTokens ?? [],
choreographer.roomId,
ConstructUseTypeEnum.ignIt,
AnalyticsUpdateOrigin.it,
);
Future.delayed(

View file

@ -9,6 +9,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../../bot/utils/bot_style.dart';
import 'it_shimmer.dart';
@ -29,7 +30,6 @@ class ChoicesArray extends StatefulWidget {
final ChoiceCallback? onLongPress;
final int? selectedChoiceIndex;
final String originalSpan;
final String Function(int) uniqueKeyForLayerLink;
/// If null then should not be used
/// We don't want tts in the case of L1 options
@ -60,7 +60,6 @@ class ChoicesArray extends StatefulWidget {
required this.choices,
required this.onPressed,
required this.originalSpan,
required this.uniqueKeyForLayerLink,
required this.selectedChoiceIndex,
required this.tts,
this.enableAudio = true,
@ -214,60 +213,68 @@ class ChoiceItem extends StatelessWidget {
waitDuration: onLongPress != null
? const Duration(milliseconds: 500)
: const Duration(days: 1),
child: ChoiceAnimationWidget(
key: ValueKey("${entry.value.text}$id"),
selected: entry.value.color != null,
isGold: entry.value.isGold,
enableInteraction: enableInteraction,
disableInteraction: disableInteraction,
child: Container(
margin: const EdgeInsets.all(2),
padding: EdgeInsets.zero,
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(
Radius.circular(AppConfig.borderRadius),
child: CompositedTransformTarget(
link: MatrixState.pAnyState
.layerLinkAndKey("${entry.value.text}$id")
.link,
child: ChoiceAnimationWidget(
key: MatrixState.pAnyState
.layerLinkAndKey("${entry.value.text}$id")
.key,
selected: entry.value.color != null,
isGold: entry.value.isGold,
enableInteraction: enableInteraction,
disableInteraction: disableInteraction,
child: Container(
margin: const EdgeInsets.all(2),
padding: EdgeInsets.zero,
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(
Radius.circular(AppConfig.borderRadius),
),
border: Border.all(
color: isSelected
? entry.value.color ?? theme.colorScheme.primary
: Colors.transparent,
style: BorderStyle.solid,
width: 2.0,
),
),
border: Border.all(
color: isSelected
? entry.value.color ?? theme.colorScheme.primary
: Colors.transparent,
style: BorderStyle.solid,
width: 2.0,
),
),
child: TextButton(
style: ButtonStyle(
padding: WidgetStateProperty.all(
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
),
//if index is selected, then give the background a slight primary color
backgroundColor: WidgetStateProperty.all<Color>(
entry.value.color?.withAlpha(50) ??
theme.colorScheme.primary.withAlpha(10),
),
textStyle: WidgetStateProperty.all(
BotStyle.text(context),
),
shape: WidgetStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
child: TextButton(
style: ButtonStyle(
padding: WidgetStateProperty.all(
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
),
//if index is selected, then give the background a slight primary color
backgroundColor: WidgetStateProperty.all<Color>(
entry.value.color?.withAlpha(50) ??
theme.colorScheme.primary.withAlpha(10),
),
textStyle: WidgetStateProperty.all(
BotStyle.text(context),
),
shape: WidgetStateProperty.all(
RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(AppConfig.borderRadius),
),
),
),
),
onLongPress: onLongPress != null && !interactionDisabled
? () => onLongPress!(entry.value.text, entry.key)
: null,
onPressed: interactionDisabled
? null
: () => onPressed(entry.value.text, entry.key),
child: Text(
getDisplayCopy != null
? getDisplayCopy!(entry.value.text)
: entry.value.text,
style: BotStyle.text(context).copyWith(
fontSize: fontSize,
onLongPress: onLongPress != null && !interactionDisabled
? () => onLongPress!(entry.value.text, entry.key)
: null,
onPressed: interactionDisabled
? null
: () => onPressed(entry.value.text, entry.key),
child: Text(
getDisplayCopy != null
? getDisplayCopy!(entry.value.text)
: entry.value.text,
style: BotStyle.text(context).copyWith(
fontSize: fontSize,
),
textAlign: TextAlign.center,
),
textAlign: TextAlign.center,
),
),
),

View file

@ -7,8 +7,6 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/gain_points_animation.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
import 'package:fluffychat/pangea/bot/utils/bot_style.dart';
import 'package:fluffychat/pangea/choreographer/enums/span_data_type.dart';
import 'package:fluffychat/pangea/choreographer/models/span_data.dart';
@ -150,7 +148,7 @@ class SpanCardState extends State<SpanCard> {
}
}
Future<void> onChoiceSelect(String value, int index) async {
Future<void> onChoiceSelect(int index) async {
selectedChoiceIndex = index;
if (selectedChoice != null) {
if (!selectedChoice!.selected) {
@ -160,7 +158,8 @@ class SpanCardState extends State<SpanCard> {
selectedChoice!.isBestCorrection
? ConstructUseTypeEnum.corIGC
: ConstructUseTypeEnum.incIGC,
AnalyticsUpdateOrigin.igc,
targetID:
"${selectedChoice!.value}${widget.scm.pangeaMatch?.hashCode.toString()}",
);
}
@ -191,7 +190,6 @@ class SpanCardState extends State<SpanCard> {
ignoredTokens ?? [],
widget.roomId,
ConstructUseTypeEnum.ignIGC,
AnalyticsUpdateOrigin.igc,
);
}
@ -261,156 +259,143 @@ class WordMatchContent extends StatelessWidget {
final ScrollController scrollController = ScrollController();
try {
return Stack(
alignment: Alignment.topCenter,
return Column(
children: [
const Positioned(
top: 40,
child: PointsGainedAnimation(
origin: AnalyticsUpdateOrigin.igc,
// if (!controller.widget.scm.pangeaMatch!.isITStart)
CardHeader(
text: controller.error?.toString() ?? matchCopy.title,
botExpression: controller.error == null
? controller.currentExpression
: BotExpression.addled,
),
Scrollbar(
controller: scrollController,
thumbVisibility: true,
child: SingleChildScrollView(
controller: scrollController,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// const SizedBox(height: 10.0),
// if (matchCopy.description != null)
// Padding(
// padding: const EdgeInsets.only(),
// child: Text(
// matchCopy.description!,
// style: BotStyle.text(context),
// ),
// ),
const SizedBox(height: 8),
if (!controller.widget.scm.pangeaMatch!.isITStart)
ChoicesArray(
originalSpan:
controller.widget.scm.pangeaMatch!.matchContent,
isLoading: controller.fetchingData,
choices: controller.widget.scm.pangeaMatch!.match.choices
?.map(
(e) => Choice(
text: e.value,
color: e.selected ? e.type.color : null,
isGold: e.type.name == 'bestCorrection',
),
)
.toList(),
onPressed: (value, index) =>
controller.onChoiceSelect(index),
selectedChoiceIndex: controller.selectedChoiceIndex,
tts: controller.tts,
id: controller.widget.scm.pangeaMatch!.hashCode
.toString(),
),
const SizedBox(height: 12),
PromptAndFeedback(controller: controller),
],
),
),
),
Column(
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// if (!controller.widget.scm.pangeaMatch!.isITStart)
CardHeader(
text: controller.error?.toString() ?? matchCopy.title,
botExpression: controller.error == null
? controller.currentExpression
: BotExpression.addled,
),
Scrollbar(
controller: scrollController,
thumbVisibility: true,
child: SingleChildScrollView(
controller: scrollController,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// const SizedBox(height: 10.0),
// if (matchCopy.description != null)
// Padding(
// padding: const EdgeInsets.only(),
// child: Text(
// matchCopy.description!,
// style: BotStyle.text(context),
// ),
// ),
const SizedBox(height: 8),
if (!controller.widget.scm.pangeaMatch!.isITStart)
ChoicesArray(
originalSpan:
controller.widget.scm.pangeaMatch!.matchContent,
isLoading: controller.fetchingData,
choices:
controller.widget.scm.pangeaMatch!.match.choices
?.map(
(e) => Choice(
text: e.value,
color: e.selected ? e.type.color : null,
isGold: e.type.name == 'bestCorrection',
),
)
.toList(),
onPressed: controller.onChoiceSelect,
uniqueKeyForLayerLink: (int index) =>
"wordMatch$index",
selectedChoiceIndex: controller.selectedChoiceIndex,
tts: controller.tts,
),
const SizedBox(height: 12),
PromptAndFeedback(controller: controller),
],
const SizedBox(width: 10),
Expanded(
child: Opacity(
opacity: 0.8,
child: TextButton(
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(
Theme.of(context).colorScheme.primary.withAlpha(25),
),
),
onPressed: controller.onIgnoreMatch,
child: Center(
child: Text(L10n.of(context).ignoreInThisText),
),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const SizedBox(width: 10),
Expanded(
child: Opacity(
opacity: 0.8,
child: TextButton(
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(
Theme.of(context).colorScheme.primary.withAlpha(25),
),
),
onPressed: controller.onIgnoreMatch,
child: Center(
child: Text(L10n.of(context).ignoreInThisText),
const SizedBox(width: 10),
if (!controller.widget.scm.pangeaMatch!.isITStart)
Expanded(
child: Opacity(
opacity: controller.selectedChoiceIndex != null ? 1.0 : 0.5,
child: TextButton(
onPressed: controller.selectedChoiceIndex != null
? controller.onReplaceSelected
: null,
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(
(controller.selectedChoice != null
? controller.selectedChoice!.color
: Theme.of(context).colorScheme.primary)
.withAlpha(50),
),
// Outline if Replace button enabled
side: controller.selectedChoice != null
? WidgetStateProperty.all(
BorderSide(
color: controller.selectedChoice!.color,
style: BorderStyle.solid,
width: 2.0,
),
)
: null,
),
child: Text(L10n.of(context).replace),
),
),
const SizedBox(width: 10),
if (!controller.widget.scm.pangeaMatch!.isITStart)
Expanded(
child: Opacity(
opacity:
controller.selectedChoiceIndex != null ? 1.0 : 0.5,
child: TextButton(
onPressed: controller.selectedChoiceIndex != null
? controller.onReplaceSelected
: null,
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(
(controller.selectedChoice != null
? controller.selectedChoice!.color
: Theme.of(context).colorScheme.primary)
.withAlpha(50),
),
// Outline if Replace button enabled
side: controller.selectedChoice != null
? WidgetStateProperty.all(
BorderSide(
color: controller.selectedChoice!.color,
style: BorderStyle.solid,
width: 2.0,
),
)
: null,
),
child: Text(L10n.of(context).replace),
),
),
const SizedBox(width: 10),
if (controller.widget.scm.pangeaMatch!.isITStart)
Expanded(
child: TextButton(
onPressed: () {
MatrixState.pAnyState.closeOverlay();
Future.delayed(
Duration.zero,
() => controller.widget.scm.onITStart(),
);
},
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(
(Theme.of(context).colorScheme.primary).withAlpha(25),
),
),
const SizedBox(width: 10),
if (controller.widget.scm.pangeaMatch!.isITStart)
Expanded(
child: TextButton(
onPressed: () {
MatrixState.pAnyState.closeOverlay();
Future.delayed(
Duration.zero,
() => controller.widget.scm.onITStart(),
);
},
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(
(Theme.of(context).colorScheme.primary)
.withAlpha(25),
),
),
child: Text(L10n.of(context).helpMeTranslate),
),
),
],
),
// if (controller.widget.scm.pangeaMatch!.isITStart)
// DontShowSwitchListTile(
// controller: pangeaController,
// onSwitch: (bool value) {
// pangeaController.userController.updateProfile((profile) {
// profile.userSettings.itAutoPlay = value;
// return profile;
// });
// },
// ),
child: Text(L10n.of(context).helpMeTranslate),
),
),
],
),
// if (controller.widget.scm.pangeaMatch!.isITStart)
// DontShowSwitchListTile(
// controller: pangeaController,
// onSwitch: (bool value) {
// pangeaController.userController.updateProfile((profile) {
// profile.userSettings.itAutoPlay = value;
// return profile;
// });
// },
// ),
],
);
} on Exception catch (e) {

View file

@ -5,8 +5,6 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/gain_points_animation.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
import 'package:fluffychat/pangea/choreographer/constants/choreo_constants.dart';
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
import 'package:fluffychat/pangea/choreographer/controllers/it_controller.dart';
@ -111,142 +109,130 @@ class ITBarState extends State<ITBar> with SingleTickerProviderStateMixin {
: Colors.black,
),
padding: const EdgeInsets.fromLTRB(0, 3, 3, 3),
child: Stack(
alignment: Alignment.topCenter,
children: [
SingleChildScrollView(
child: Column(
child: SingleChildScrollView(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (itController.isEditingSourceText)
Expanded(
child: Padding(
padding: const EdgeInsets.only(
left: 20,
right: 10,
top: 10,
),
child: TextField(
controller: TextEditingController(
text: itController.sourceText,
),
autofocus: true,
enableSuggestions: false,
maxLines: null,
textInputAction: TextInputAction.send,
onSubmitted:
itController.onEditSourceTextSubmit,
obscureText: false,
decoration: const InputDecoration(
border: OutlineInputBorder(),
),
),
),
if (itController.isEditingSourceText)
Expanded(
child: Padding(
padding: const EdgeInsets.only(
left: 20,
right: 10,
top: 10,
),
if (!itController.isEditingSourceText &&
itController.sourceText != null)
SizedBox(
width: iconDimension,
height: iconDimension,
child: IconButton(
iconSize: iconSize,
color: Theme.of(context).colorScheme.primary,
onPressed: () {
if (itController.nextITStep != null) {
itController.setIsEditingSourceText(true);
}
},
icon: const Icon(Icons.edit_outlined),
// iconSize: 20,
child: TextField(
controller: TextEditingController(
text: itController.sourceText,
),
),
if (!itController.isEditingSourceText)
SizedBox(
width: iconDimension,
height: iconDimension,
child: IconButton(
iconSize: iconSize,
color: Theme.of(context).colorScheme.primary,
icon: const Icon(Icons.settings_outlined),
onPressed: () => showDialog(
context: context,
builder: (c) => const SettingsLearning(),
barrierDismissible: false,
),
autofocus: true,
enableSuggestions: false,
maxLines: null,
textInputAction: TextInputAction.send,
onSubmitted: itController.onEditSourceTextSubmit,
obscureText: false,
decoration: const InputDecoration(
border: OutlineInputBorder(),
),
),
SizedBox(
width: iconDimension,
height: iconDimension,
child: IconButton(
iconSize: iconSize,
color: Theme.of(context).colorScheme.primary,
icon: const Icon(Icons.close_outlined),
onPressed: () {
itController.isEditingSourceText
? itController.setIsEditingSourceText(false)
: itController.closeIT();
},
),
),
],
),
),
if (!itController.isEditingSourceText &&
itController.sourceText != null)
SizedBox(
width: iconDimension,
height: iconDimension,
child: IconButton(
iconSize: iconSize,
color: Theme.of(context).colorScheme.primary,
onPressed: () {
if (itController.nextITStep != null) {
itController.setIsEditingSourceText(true);
}
},
icon: const Icon(Icons.edit_outlined),
// iconSize: 20,
),
),
if (!itController.isEditingSourceText)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: itController.sourceText != null
? Text(
itController.sourceText!,
textAlign: TextAlign.center,
)
: const LinearProgressIndicator(),
),
const SizedBox(height: 8.0),
if (showITInstructionsTooltip)
const InstructionsInlineTooltip(
instructionsEnum: InstructionsEnum.clickBestOption,
),
if (showTranslationsChoicesTooltip)
const InstructionsInlineTooltip(
instructionsEnum: InstructionsEnum.translationChoices,
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
constraints: const BoxConstraints(minHeight: 80),
child: AnimatedSize(
duration: itController.animationSpeed,
child: Center(
child: itController.choreographer.errorService.isError
? ITError(
error: itController
.choreographer.errorService.error!,
controller: itController,
)
: itController.showChoiceFeedback
? ChoiceFeedbackText(
controller: itController,
)
: itController.isTranslationDone
? TranslationFeedback(
controller: itController,
)
: ITChoices(controller: itController),
SizedBox(
width: iconDimension,
height: iconDimension,
child: IconButton(
iconSize: iconSize,
color: Theme.of(context).colorScheme.primary,
icon: const Icon(Icons.settings_outlined),
onPressed: () => showDialog(
context: context,
builder: (c) => const SettingsLearning(),
barrierDismissible: false,
),
),
),
SizedBox(
width: iconDimension,
height: iconDimension,
child: IconButton(
iconSize: iconSize,
color: Theme.of(context).colorScheme.primary,
icon: const Icon(Icons.close_outlined),
onPressed: () {
itController.isEditingSourceText
? itController.setIsEditingSourceText(false)
: itController.closeIT();
},
),
),
],
),
),
const Positioned(
top: 60,
child: PointsGainedAnimation(
origin: AnalyticsUpdateOrigin.it,
if (!itController.isEditingSourceText)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: itController.sourceText != null
? Text(
itController.sourceText!,
textAlign: TextAlign.center,
)
: const LinearProgressIndicator(),
),
const SizedBox(height: 8.0),
if (showITInstructionsTooltip)
const InstructionsInlineTooltip(
instructionsEnum: InstructionsEnum.clickBestOption,
),
if (showTranslationsChoicesTooltip)
const InstructionsInlineTooltip(
instructionsEnum: InstructionsEnum.translationChoices,
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
constraints: const BoxConstraints(minHeight: 80),
child: AnimatedSize(
duration: itController.animationSpeed,
child: Center(
child: itController.choreographer.errorService.isError
? ITError(
error: itController
.choreographer.errorService.error!,
controller: itController,
)
: itController.showChoiceFeedback
? ChoiceFeedbackText(
controller: itController,
)
: itController.isTranslationDone
? TranslationFeedback(
controller: itController,
)
: ITChoices(controller: itController),
),
),
),
),
],
],
),
),
),
),
@ -390,7 +376,8 @@ class ITChoices extends StatelessWidget {
continuance.level > 1
? ConstructUseTypeEnum.incIt
: ConstructUseTypeEnum.corIt,
AnalyticsUpdateOrigin.it,
targetID:
"${continuance.text.trim()}${controller.currentITStep.hashCode.toString()}",
);
}
controller.currentITStep!.continuances[index].wasClicked = true;
@ -430,7 +417,6 @@ class ITChoices extends StatelessWidget {
}).toList(),
onPressed: (value, index) => selectContinuance(index, context),
onLongPress: (value, index) => showCard(context, index),
uniqueKeyForLayerLink: (int index) => "itChoices$index",
selectedChoiceIndex: null,
tts: controller.choreographer.tts,
);

View file

@ -87,7 +87,6 @@ class AlternativeTranslations extends StatelessWidget {
controller.choreographer.altTranslator.translations[index],
);
},
uniqueKeyForLayerLink: (int index) => "altTranslation$index",
selectedChoiceIndex: null,
tts: null,
);

View file

@ -51,7 +51,6 @@ class PangeaAnyState {
void openOverlay(
OverlayEntry entry,
BuildContext context, {
bool closePrevOverlay = true,
String? overlayKey,
}) {
if (overlayKey != null &&
@ -59,9 +58,6 @@ class PangeaAnyState {
return;
}
if (closePrevOverlay) {
closeOverlay();
}
entries.add(OverlayListEntry(entry, key: overlayKey));
Overlay.of(context).insert(entry);
}

View file

@ -77,7 +77,6 @@ class OverlayUtil {
MatrixState.pAnyState.openOverlay(
entry,
context,
closePrevOverlay: closePrevOverlay,
overlayKey: overlayKey,
);
} catch (err, stack) {

View file

@ -69,12 +69,15 @@ class MessageMatchActivity extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (overlayController.messageAnalyticsEntry == null ||
overlayController.messageLemmaInfos == null ||
activityType == null) {
debugger(when: kDebugMode);
return const SizedBox();
}
if (overlayController.messageLemmaInfos == null) {
return const CircularProgressIndicator.adaptive();
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,

View file

@ -142,7 +142,10 @@ class MessageMatchActivityItemState extends State<MessageMatchActivityItem> {
Widget build(BuildContext context) {
return LongPressDraggable<ConstructForm>(
data: widget.constructForm,
feedback: content(context),
feedback: Material(
type: MaterialType.transparency,
child: content(context),
),
delay: const Duration(milliseconds: 100),
onDragStarted: () {
widget.overlayController.onChoiceSelect(widget.constructForm, true);

View file

@ -143,7 +143,7 @@ class MessageMorphInputBarContentState
form: token!.text.content,
),
],
origin: AnalyticsUpdateOrigin.wordZoom,
targetID: token!.text.uniqueKey,
),
);
@ -183,14 +183,17 @@ class MessageMorphInputBarContentState
size: const Size(30, 30),
showTooltip: false,
),
Text(
L10n.of(context).whatIsTheMorphTag(
morph!.getDisplayCopy(context),
token!.text.content,
Flexible(
child: Text(
L10n.of(context).whatIsTheMorphTag(
morph!.getDisplayCopy(context),
token!.text.content,
),
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),

View file

@ -5,7 +5,6 @@ import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart';
@ -53,7 +52,6 @@ class ReadingAssistanceInputBar extends StatelessWidget {
),
overlayController: overlayController,
morphFeature: morphFeature,
location: AnalyticsUpdateOrigin.inputBar,
);
}

View file

@ -167,6 +167,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
} finally {
_initializeSelectedToken();
_setInitialToolbarMode();
messageLemmaInfos ??= {};
initialized = true;
if (mounted) setState(() {});
}
@ -290,7 +291,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
pangeaMessageEvent: pangeaMessageEvent!,
overlayController: this,
);
if (context.mounted) {
if (mounted) {
OverlayUtil.showPositionedCard(
context: context,
cardToShow: entry,
@ -363,7 +364,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
form: token.text.content,
),
],
origin: AnalyticsUpdateOrigin.wordZoom,
targetID: token.text.uniqueKey,
),
);
}

View file

@ -115,7 +115,6 @@ class MultipleChoiceActivityState extends State<MultipleChoiceActivity> {
widget.practiceCardController.currentActivity!,
widget.practiceCardController.metadata,
),
origin: AnalyticsUpdateOrigin.practiceActivity,
),
);
@ -226,7 +225,6 @@ class MultipleChoiceActivityState extends State<MultipleChoiceActivity> {
),
ChoicesArray(
isLoading: false,
uniqueKeyForLayerLink: (index) => "multiple_choice_$index",
originalSpan: "placeholder",
onPressed: updateChoice,
selectedChoiceIndex: selectedChoiceIndex,

View file

@ -8,8 +8,6 @@ import 'package:collection/collection.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/analytics_misc/gain_points_animation.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
import 'package:fluffychat/pangea/choreographer/widgets/igc/card_error_widget.dart';
import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
@ -36,7 +34,6 @@ class PracticeActivityCard extends StatefulWidget {
final TargetTokensAndActivityType targetTokensAndActivityType;
final MessageOverlayController overlayController;
final WordZoomWidget? wordDetailsController;
final AnalyticsUpdateOrigin location;
final String? morphFeature;
@ -47,7 +44,6 @@ class PracticeActivityCard extends StatefulWidget {
required this.overlayController,
this.morphFeature,
this.wordDetailsController,
required this.location,
});
@override
@ -341,12 +337,6 @@ class PracticeActivityCardState extends State<PracticeActivityCard> {
return Stack(
alignment: Alignment.center,
children: [
// Main content
Positioned(
child: PointsGainedAnimation(
origin: widget.location,
),
),
if (activityWidget != null) activityWidget!,
// Conditionally show the darkening and progress indicator based on the loading state
if (!savoringTheJoy && fetchingActivity) ...[

View file

@ -5,7 +5,6 @@ import 'package:matrix/matrix_api_lite/model/message_types.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart';
import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart';
@ -58,7 +57,6 @@ class ReadingAssistanceContentState extends State<ReadingAssistanceContent> {
targetTokensAndActivityType: widget
.overlayController.messageAnalyticsEntry!
.nextActivity(ActivityTypeEnum.hiddenWordListening)!,
location: AnalyticsUpdateOrigin.practiceActivity,
);
}
@ -71,7 +69,6 @@ class ReadingAssistanceContentState extends State<ReadingAssistanceContent> {
targetTokensAndActivityType: widget
.overlayController.messageAnalyticsEntry!
.nextActivity(ActivityTypeEnum.messageMeaning)!,
location: AnalyticsUpdateOrigin.practiceActivity,
);
}

View file

@ -3,8 +3,6 @@ import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics_details_popup/analytics_details_popup.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/gain_points_animation.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.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/learning_settings/constants/language_constants.dart';
@ -54,136 +52,126 @@ class WordZoomWidget extends StatelessWidget {
maxHeight: AppConfig.toolbarMaxHeight,
maxWidth: AppConfig.toolbarMinWidth,
),
child: Stack(
alignment: Alignment.topCenter,
children: [
const Positioned(
child: PointsGainedAnimation(
origin: AnalyticsUpdateOrigin.wordZoom,
child: SingleChildScrollView(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.min,
children: [
Container(
constraints: const BoxConstraints(
minHeight: 40,
),
color: Theme.of(context).colorScheme.surface,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
//@ggurdin - might need to play with size to properly center
IconButton(
onPressed: () =>
overlayController.onClickOverlayMessageToken(token),
icon: const Icon(Icons.close),
),
LemmaWidget(
token: _selectedToken,
pangeaMessageEvent: messageEvent,
// onEdit: () => _setHideCenterContent(true),
onEdit: () {
debugPrint("what are we doing edits with?");
},
onEditDone: () {
debugPrint("what are we doing edits with?");
onEditDone();
},
tts: tts,
overlayController: overlayController,
),
ConstructXpWidget(
id: token.vocabConstructID,
onTap: () => showDialog<AnalyticsPopupWrapper>(
context: context,
builder: (context) => AnalyticsPopupWrapper(
constructZoom: token.vocabConstructID,
view: ConstructTypeEnum.vocab,
),
),
),
],
),
),
),
SingleChildScrollView(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.min,
const SizedBox(
height: 8.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
constraints: const BoxConstraints(
minHeight: 40,
),
color: Theme.of(context).colorScheme.surface,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
//@ggurdin - might need to play with size to properly center
IconButton(
onPressed: () =>
overlayController.onClickOverlayMessageToken(token),
icon: const Icon(Icons.close),
),
LemmaWidget(
token: _selectedToken,
pangeaMessageEvent: messageEvent,
// onEdit: () => _setHideCenterContent(true),
onEdit: () {
debugPrint("what are we doing edits with?");
},
onEditDone: () {
debugPrint("what are we doing edits with?");
onEditDone();
},
tts: tts,
overlayController: overlayController,
),
ConstructXpWidget(
id: token.vocabConstructID,
onTap: () => showDialog<AnalyticsPopupWrapper>(
context: context,
builder: (context) => AnalyticsPopupWrapper(
constructZoom: token.vocabConstructID,
view: ConstructTypeEnum.vocab,
),
),
),
],
),
),
const SizedBox(
height: 8.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
constraints: const BoxConstraints(
minHeight: 40,
),
alignment: Alignment.center,
child: LemmaEmojiRow(
cId: _selectedToken.vocabConstructID,
onTapOverride: hasEmojiActivity
? () => overlayController.updateToolbarMode(
MessageMode.wordEmoji,
)
: null,
isSelected: overlayController.toolbarMode ==
MessageMode.wordEmoji,
emojiSetCallback: () =>
overlayController.setState(() {}),
shouldShowEmojis: !hasEmojiActivity,
),
),
],
),
const SizedBox(
height: 8.0,
),
Container(
constraints: const BoxConstraints(
minHeight: 40,
),
alignment: Alignment.center,
child: Wrap(
alignment: WrapAlignment.center,
runAlignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
spacing: 8,
children: [
LemmaMeaningWidget(
constructUse: token.vocabConstructID.constructUses,
langCode: MatrixState.pangeaController
.languageController.userL2?.langCodeShort ??
LanguageKeys.defaultLanguage,
token: overlayController.selectedToken!,
controller: overlayController,
style: Theme.of(context).textTheme.bodyLarge,
),
],
child: LemmaEmojiRow(
cId: _selectedToken.vocabConstructID,
onTapOverride: hasEmojiActivity
? () => overlayController.updateToolbarMode(
MessageMode.wordEmoji,
)
: null,
isSelected:
overlayController.toolbarMode == MessageMode.wordEmoji,
emojiSetCallback: () => overlayController.setState(() {}),
shouldShowEmojis: !hasEmojiActivity,
),
),
const SizedBox(
height: 8.0,
),
Wrap(
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
if (!_selectedToken.doesLemmaTextMatchTokenText) ...[
Text(
_selectedToken.text.content,
style: Theme.of(context).textTheme.bodyLarge,
overflow: TextOverflow.ellipsis,
),
WordAudioButton(
text: _selectedToken.text.content,
isSelected: MessageMode.listening ==
overlayController.toolbarMode,
baseOpacity: 0.4,
callbackOverride: overlayController
.messageAnalyticsEntry
?.hasActivity(
],
),
const SizedBox(
height: 8.0,
),
Container(
constraints: const BoxConstraints(
minHeight: 40,
),
alignment: Alignment.center,
child: Wrap(
alignment: WrapAlignment.center,
runAlignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
spacing: 8,
children: [
LemmaMeaningWidget(
constructUse: token.vocabConstructID.constructUses,
langCode: MatrixState.pangeaController.languageController
.userL2?.langCodeShort ??
LanguageKeys.defaultLanguage,
token: overlayController.selectedToken!,
controller: overlayController,
style: Theme.of(context).textTheme.bodyLarge,
),
],
),
),
const SizedBox(
height: 8.0,
),
Wrap(
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
if (!_selectedToken.doesLemmaTextMatchTokenText) ...[
Text(
_selectedToken.text.content,
style: Theme.of(context).textTheme.bodyLarge,
overflow: TextOverflow.ellipsis,
),
WordAudioButton(
text: _selectedToken.text.content,
isSelected:
MessageMode.listening == overlayController.toolbarMode,
baseOpacity: 0.4,
callbackOverride:
overlayController.messageAnalyticsEntry?.hasActivity(
MessageMode.listening.associatedActivityType!,
_selectedToken,
) ==
@ -191,33 +179,30 @@ class WordZoomWidget extends StatelessWidget {
? () => overlayController
.updateToolbarMode(MessageMode.listening)
: null,
),
],
..._selectedToken
.morphsBasicallyEligibleForPracticeByPriority
.map(
(cId) => MorphologicalListItem(
morphFeature: MorphFeaturesEnumExtension.fromString(
cId.category,
),
token: _selectedToken,
overlayController: overlayController,
),
),
],
..._selectedToken.morphsBasicallyEligibleForPracticeByPriority
.map(
(cId) => MorphologicalListItem(
morphFeature: MorphFeaturesEnumExtension.fromString(
cId.category,
),
],
token: _selectedToken,
overlayController: overlayController,
),
),
// if (_selectedMorphFeature != null)
// MorphologicalCenterWidget(
// token: token,
// morphFeature: _selectedMorphFeature!,
// pangeaMessageEvent: messageEvent,
// overlayController: overlayController,
// onEditDone: onEditDone,
// ),
],
),
),
],
// if (_selectedMorphFeature != null)
// MorphologicalCenterWidget(
// token: token,
// morphFeature: _selectedMorphFeature!,
// pangeaMessageEvent: messageEvent,
// overlayController: overlayController,
// onEditDone: onEditDone,
// ),
],
),
),
);
}