Merge pull request #4874 from pangeachat/more-practice-tweaks

More practice tweaks
This commit is contained in:
ggurdin 2025-12-17 15:41:38 -05:00 committed by GitHub
commit 6ec7e9ff59
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 93 additions and 46 deletions

View file

@ -14,6 +14,7 @@ import 'package:fluffychat/pages/chat/chat.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/toolbar/layout/reading_assistance_mode_enum.dart';
import 'package:fluffychat/pangea/toolbar/message_practice/message_practice_mode_enum.dart';
import 'package:fluffychat/pangea/toolbar/message_practice/token_practice_button.dart';
import 'package:fluffychat/pangea/toolbar/message_selection_overlay.dart';
import 'package:fluffychat/pangea/toolbar/reading_assistance/token_emoji_button.dart';
@ -513,6 +514,26 @@ class HtmlMessage extends StatelessWidget {
),
),
),
if (renderer.showCenterStyling &&
token != null &&
overlayController != null)
ListenableBuilder(
listenable: overlayController!.practiceController,
builder: (context, _) => AnimatedSize(
duration: const Duration(
milliseconds: AppConfig.overlayAnimationDuration,
),
curve: Curves.easeOut,
child: SizedBox(
height: overlayController!
.practiceController.practiceMode !=
MessagePracticeMode.noneSelected
? 16.0
: 0.0,
width: tokenWidth,
),
),
),
],
// ),
),

View file

@ -111,7 +111,7 @@ class PutAnalyticsController {
String? targetId,
}) {
final level = _pangeaController.getAnalytics.constructListModel.level;
_addLocalMessage(eventId, constructs).then(
_addLocalMessage(eventId, List.from(constructs)).then(
(_) => _sendAnalytics(level, targetId, constructs),
);
}

View file

@ -395,28 +395,6 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
alignment:
ownMessage ? Alignment.centerRight : Alignment.centerLeft,
children: [
Positioned(
top: 0,
left: 0,
right: 0,
child: ListenableBuilder(
listenable: widget.overlayController.practiceController,
builder: (context, _) {
final instruction = widget.overlayController
.practiceController.practiceMode.instruction;
if (instruction != null) {
return InstructionsInlineTooltip(
instructionsEnum: widget.overlayController
.practiceController.practiceMode.instruction!,
padding: const EdgeInsets.all(16.0),
animate: false,
);
}
return const SizedBox();
},
),
),
ValueListenableBuilder(
valueListenable: _startedTransition,
builder: (context, started, __) {
@ -458,6 +436,36 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
selectedToken: widget.overlayController.selectedToken,
),
),
Positioned(
top: switch (MediaQuery.heightOf(context)) {
< 700 => 0,
> 900 => 160,
_ => 80,
},
left: 0,
right: 0,
child: ListenableBuilder(
listenable:
widget.overlayController.practiceController,
builder: (context, _) {
final instruction = widget.overlayController
.practiceController.practiceMode.instruction;
if (instruction != null) {
return InstructionsInlineTooltip(
instructionsEnum: widget
.overlayController
.practiceController
.practiceMode
.instruction!,
padding: const EdgeInsets.all(16.0),
animate: false,
);
}
return const SizedBox();
},
),
),
],
],
),

View file

@ -54,6 +54,30 @@ class PracticeController with ChangeNotifier {
practiceSelection?.activities(activityType).every((a) => a.isComplete) ==
true;
bool isPracticeButtonEmpty(PangeaToken token) {
final target = practiceTargetForToken(token);
if (MessagePracticeMode.wordEmoji == practiceMode) {
if (token.vocabConstructID.userSetEmoji.firstOrNull != null) {
return false;
}
// Keep open even when completed to show emoji
return target == null;
}
if (MessagePracticeMode.wordMorph == practiceMode) {
// Keep open even when completed to show morph icon
return target == null;
}
return target == null ||
target.isCompleteByToken(
token,
_activity?.morphFeature,
) ==
true;
}
Future<Result<PracticeActivityModel>> fetchActivityModel(
PracticeTarget target,
) async {

View file

@ -176,11 +176,10 @@ class _ReadingAssistanceBarContent extends StatelessWidget {
}
if (target == null) {
return Center(
child: Text(
L10n.of(context).selectForGrammar,
style: Theme.of(context).textTheme.bodyLarge,
textAlign: TextAlign.center,
return const Center(
child: Icon(
Symbols.fitness_center,
size: 60.0,
),
);
}

View file

@ -55,20 +55,7 @@ class TokenPracticeButton extends StatelessWidget {
true;
}
bool get _isEmpty {
final mode = controller.practiceMode;
if (MessagePracticeMode.wordEmoji == mode &&
token.vocabConstructID.userSetEmoji.firstOrNull != null) {
return false;
}
return _activity == null ||
(isActivityCompleteOrNullForToken &&
![MessagePracticeMode.wordEmoji, MessagePracticeMode.wordMorph]
.contains(mode)) ||
(MessagePracticeMode.wordMorph == mode &&
_activity?.morphFeature == null);
}
bool get _isEmpty => controller.isPracticeButtonEmpty(token);
bool get _isSelected =>
controller.selectedMorph?.token == token &&
@ -98,6 +85,7 @@ class TokenPracticeButton extends StatelessWidget {
child = _MorphMatchButton(
active: _isSelected,
textColor: textColor,
width: tokenButtonHeight,
onTap: () => controller.onSelectMorph(
MorphSelection(
token,
@ -123,8 +111,14 @@ class TokenPracticeButton extends StatelessWidget {
curve: Curves.easeOut,
alignment: Alignment.bottomCenter,
child: _isEmpty
? const SizedBox(height: 0)
: SizedBox(height: tokenButtonHeight, child: child),
? const SizedBox()
: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 16.0),
SizedBox(height: tokenButtonHeight, child: child),
],
),
);
},
);
@ -198,10 +192,12 @@ class _MorphMatchButton extends StatelessWidget {
final bool active;
final Color textColor;
final bool shimmer;
final double width;
const _MorphMatchButton({
required this.active,
required this.textColor,
required this.width,
this.shimmer = false,
this.onTap,
});
@ -218,7 +214,7 @@ class _MorphMatchButton extends StatelessWidget {
child: ShimmerBackground(
enabled: shimmer,
child: SizedBox(
width: 24.0,
width: width,
child: Center(
child: Opacity(
opacity: active ? 1.0 : 0.6,
@ -282,7 +278,6 @@ class _NoActivityContentButton extends StatelessWidget {
context: context,
),
child: SizedBox(
width: 24.0,
child: Center(
child: MorphIcon(
morphFeature: morphFeature,