Toolbar practice (#707)
* remove print statement * ending animation, savoring joy, properly adding xp in session * forgot to switch env again... * increment version number * about to move toolbar buttons up to level of overlay controller * added ability to give feedback and get new activitypull/1398/head
parent
371d4f06d4
commit
b8edf595ca
@ -0,0 +1,122 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:fluffychat/config/app_config.dart';
|
||||||
|
import 'package:fluffychat/config/themes.dart';
|
||||||
|
import 'package:fluffychat/pangea/enum/message_mode_enum.dart';
|
||||||
|
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
|
||||||
|
import 'package:fluffychat/pangea/widgets/chat/message_selection_overlay.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ToolbarButtons extends StatefulWidget {
|
||||||
|
final MessageOverlayController overlayController;
|
||||||
|
final double width;
|
||||||
|
|
||||||
|
const ToolbarButtons({
|
||||||
|
required this.overlayController,
|
||||||
|
required this.width,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
ToolbarButtonsState createState() => ToolbarButtonsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ToolbarButtonsState extends State<ToolbarButtons> {
|
||||||
|
PangeaMessageEvent get pangeaMessageEvent =>
|
||||||
|
widget.overlayController.pangeaMessageEvent;
|
||||||
|
|
||||||
|
List<MessageMode> get modes => MessageMode.values
|
||||||
|
.where((mode) => mode.isValidMode(pangeaMessageEvent.event))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
static const double iconWidth = 36.0;
|
||||||
|
|
||||||
|
MessageOverlayController get overlayController => widget.overlayController;
|
||||||
|
|
||||||
|
// @ggurdin - maybe this can be stateless now?
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final double barWidth = widget.width - iconWidth;
|
||||||
|
|
||||||
|
if (widget.overlayController.pangeaMessageEvent.isAudioMessage) {
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
width: widget.width,
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: widget.width,
|
||||||
|
height: 12,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: MessageModeExtension.barAndLockedButtonColor(context),
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: iconWidth / 2),
|
||||||
|
),
|
||||||
|
AnimatedContainer(
|
||||||
|
duration: FluffyThemes.animationDuration,
|
||||||
|
height: 12,
|
||||||
|
width: overlayController.isPracticeComplete
|
||||||
|
? barWidth
|
||||||
|
: min(
|
||||||
|
barWidth,
|
||||||
|
(barWidth / 3) *
|
||||||
|
pangeaMessageEvent.numberOfActivitiesCompleted,
|
||||||
|
),
|
||||||
|
color: AppConfig.success,
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: iconWidth / 2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: modes
|
||||||
|
.mapIndexed(
|
||||||
|
(index, mode) => Tooltip(
|
||||||
|
message: mode.tooltip(context),
|
||||||
|
child: IconButton(
|
||||||
|
iconSize: 20,
|
||||||
|
icon: Icon(mode.icon),
|
||||||
|
color: mode == widget.overlayController.toolbarMode
|
||||||
|
? Colors.white
|
||||||
|
: null,
|
||||||
|
isSelected: mode == widget.overlayController.toolbarMode,
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: WidgetStateProperty.all(
|
||||||
|
mode.iconButtonColor(
|
||||||
|
context,
|
||||||
|
index,
|
||||||
|
widget.overlayController.toolbarMode,
|
||||||
|
pangeaMessageEvent.numberOfActivitiesCompleted,
|
||||||
|
widget.overlayController.isPracticeComplete,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: mode.isUnlocked(
|
||||||
|
index,
|
||||||
|
pangeaMessageEvent.numberOfActivitiesCompleted,
|
||||||
|
overlayController.isPracticeComplete,
|
||||||
|
)
|
||||||
|
? () =>
|
||||||
|
widget.overlayController.updateToolbarMode(mode)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
import 'package:fluffychat/pages/chat/chat.dart';
|
||||||
|
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
|
class ToolbarSelectionArea extends StatelessWidget {
|
||||||
|
final ChatController controller;
|
||||||
|
final PangeaMessageEvent? pangeaMessageEvent;
|
||||||
|
final bool isOverlay;
|
||||||
|
final Widget child;
|
||||||
|
final Event? nextEvent;
|
||||||
|
final Event? prevEvent;
|
||||||
|
|
||||||
|
const ToolbarSelectionArea({
|
||||||
|
required this.controller,
|
||||||
|
this.pangeaMessageEvent,
|
||||||
|
this.isOverlay = false,
|
||||||
|
required this.child,
|
||||||
|
this.nextEvent,
|
||||||
|
this.prevEvent,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
if (pangeaMessageEvent != null && !isOverlay) {
|
||||||
|
controller.showToolbar(
|
||||||
|
pangeaMessageEvent!,
|
||||||
|
nextEvent: nextEvent,
|
||||||
|
prevEvent: prevEvent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLongPress: () {
|
||||||
|
if (pangeaMessageEvent != null && !isOverlay) {
|
||||||
|
controller.showToolbar(
|
||||||
|
pangeaMessageEvent!,
|
||||||
|
nextEvent: nextEvent,
|
||||||
|
prevEvent: prevEvent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,99 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
|
||||||
|
import 'package:fluffychat/pangea/models/analytics/construct_list_model.dart';
|
||||||
|
import 'package:fluffychat/pangea/models/analytics/constructs_model.dart';
|
||||||
|
import 'package:fluffychat/pangea/models/practice_activities.dart/message_activity_request.dart';
|
||||||
|
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||||
|
import 'package:fluffychat/widgets/matrix.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
/// Seperated out the target tokens from the practice activity card
|
||||||
|
/// in order to control the state of the target tokens
|
||||||
|
class TargetTokensController {
|
||||||
|
List<TokenWithXP>? _targetTokens;
|
||||||
|
|
||||||
|
TargetTokensController();
|
||||||
|
|
||||||
|
/// From the tokens in the message, do a preliminary filtering of which to target
|
||||||
|
/// Then get the construct uses for those tokens
|
||||||
|
Future<List<TokenWithXP>> targetTokens(
|
||||||
|
BuildContext context,
|
||||||
|
PangeaMessageEvent pangeaMessageEvent,
|
||||||
|
) async {
|
||||||
|
if (_targetTokens != null) {
|
||||||
|
return _targetTokens!;
|
||||||
|
}
|
||||||
|
|
||||||
|
_targetTokens = await _initialize(context, pangeaMessageEvent);
|
||||||
|
|
||||||
|
await updateTokensWithConstructs(
|
||||||
|
MatrixState.pangeaController.analytics.analyticsStream.value ?? [],
|
||||||
|
context,
|
||||||
|
pangeaMessageEvent,
|
||||||
|
);
|
||||||
|
|
||||||
|
return _targetTokens!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<TokenWithXP>> _initialize(
|
||||||
|
BuildContext context,
|
||||||
|
PangeaMessageEvent pangeaMessageEvent,
|
||||||
|
) async {
|
||||||
|
if (!context.mounted) {
|
||||||
|
ErrorHandler.logError(
|
||||||
|
m: 'getTargetTokens called when not mounted',
|
||||||
|
s: StackTrace.current,
|
||||||
|
);
|
||||||
|
return _targetTokens = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
final tokens = await pangeaMessageEvent
|
||||||
|
.representationByLanguage(pangeaMessageEvent.messageDisplayLangCode)
|
||||||
|
?.tokensGlobal(context);
|
||||||
|
|
||||||
|
if (tokens == null || tokens.isEmpty) {
|
||||||
|
debugger(when: kDebugMode);
|
||||||
|
return _targetTokens = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
_targetTokens = [];
|
||||||
|
for (int i = 0; i < tokens.length; i++) {
|
||||||
|
//don't bother with tokens that we don't save to vocab
|
||||||
|
if (!tokens[i].lemma.saveVocab) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_targetTokens!.add(tokens[i].emptyTokenWithXP);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _targetTokens!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateTokensWithConstructs(
|
||||||
|
List<OneConstructUse> constructUses,
|
||||||
|
context,
|
||||||
|
pangeaMessageEvent,
|
||||||
|
) async {
|
||||||
|
final ConstructListModel constructList = ConstructListModel(
|
||||||
|
uses: constructUses,
|
||||||
|
type: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
_targetTokens ??= await _initialize(context, pangeaMessageEvent);
|
||||||
|
|
||||||
|
for (final token in _targetTokens!) {
|
||||||
|
for (final construct in token.constructs) {
|
||||||
|
final constructUseModel = constructList.getConstructUses(
|
||||||
|
construct.id.lemma,
|
||||||
|
construct.id.type,
|
||||||
|
);
|
||||||
|
if (constructUseModel != null) {
|
||||||
|
construct.xp += constructUseModel.points;
|
||||||
|
construct.lastUsed = constructUseModel.lastUsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue