chore(reading_assistance): persistent distractor selection and fixes to analytics saving
This commit is contained in:
parent
ac7d5f3938
commit
b6e27d739a
20 changed files with 473 additions and 423 deletions
|
|
@ -1,7 +1,4 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:collection/collection.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';
|
||||
|
|
@ -15,6 +12,7 @@ import 'package:fluffychat/pangea/morphs/morph_features_enum.dart';
|
|||
import 'package:fluffychat/pangea/morphs/morph_icon.dart';
|
||||
import 'package:fluffychat/pangea/user/client_extension.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MorphAnalyticsListView extends StatelessWidget {
|
||||
final void Function(ConstructIdentifier) onConstructZoom;
|
||||
|
|
@ -29,10 +27,10 @@ class MorphAnalyticsListView extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// spacing: 16.0,
|
||||
spacing: 16.0,
|
||||
children: [
|
||||
// Add your text widget here
|
||||
const InstructionsInlineTooltip(
|
||||
|
|
@ -84,7 +82,6 @@ class MorphFeatureBox extends StatelessWidget {
|
|||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
border: Border.all(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/analytics_details_popup/vocab_analytics_list_tile.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
|
||||
|
|
@ -10,6 +7,7 @@ import 'package:fluffychat/pangea/constructs/construct_level_enum.dart';
|
|||
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Displays vocab analytics, sorted into categories
|
||||
/// (flowers, greens, and seeds) by points
|
||||
|
|
@ -113,14 +111,14 @@ class VocabAnalyticsListViewState extends State<VocabAnalyticsListView> {
|
|||
),
|
||||
);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
const InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.analyticsVocabList,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(32.0),
|
||||
child: AnimatedContainer(
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
const InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.analyticsVocabList,
|
||||
),
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
padding:
|
||||
|
|
@ -175,62 +173,29 @@ class VocabAnalyticsListViewState extends State<VocabAnalyticsListView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(32.0),
|
||||
// child: Row(
|
||||
// spacing: _isSearching ? 8.0 : 24.0,
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: _isSearching
|
||||
// ? [
|
||||
// ConstrainedBox(
|
||||
// constraints: const BoxConstraints(maxWidth: 200),
|
||||
// child: TextField(
|
||||
// autofocus: true,
|
||||
// controller: _searchController,
|
||||
// decoration: const InputDecoration(
|
||||
// contentPadding: EdgeInsets.symmetric(
|
||||
// vertical: 6.0,
|
||||
// horizontal: 12.0,
|
||||
// ),
|
||||
// isDense: true,
|
||||
// border: OutlineInputBorder(),
|
||||
// ),
|
||||
// onChanged: (value) {
|
||||
// if (mounted) setState(() {});
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: const Icon(Icons.close),
|
||||
// onPressed: _toggleSearching,
|
||||
// ),
|
||||
// ]
|
||||
// : filters,
|
||||
// ),
|
||||
// ),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: GridView.builder(
|
||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 100.0,
|
||||
mainAxisExtent: 100.0,
|
||||
crossAxisSpacing: 8.0,
|
||||
mainAxisSpacing: 8.0,
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: GridView.builder(
|
||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 100.0,
|
||||
mainAxisExtent: 100.0,
|
||||
crossAxisSpacing: 8.0,
|
||||
mainAxisSpacing: 8.0,
|
||||
),
|
||||
itemCount: _filteredVocab.length,
|
||||
itemBuilder: (context, index) {
|
||||
final vocabItem = _filteredVocab[index];
|
||||
return VocabAnalyticsListTile(
|
||||
onTap: () => widget.onConstructZoom(vocabItem.id),
|
||||
constructUse: vocabItem,
|
||||
);
|
||||
},
|
||||
),
|
||||
itemCount: _filteredVocab.length,
|
||||
itemBuilder: (context, index) {
|
||||
final vocabItem = _filteredVocab[index];
|
||||
return VocabAnalyticsListTile(
|
||||
onTap: () => widget.onConstructZoom(vocabItem.id),
|
||||
constructUse: vocabItem,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
|
||||
import 'package:fluffychat/pangea/morphs/default_morph_mapping.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_features_enum.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_models.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'construct_type_enum.dart';
|
||||
|
||||
class ConstructAnalyticsModel {
|
||||
|
|
@ -76,7 +76,7 @@ class OneConstructUse {
|
|||
|
||||
/// For vocab constructs, this is the POS. For morph
|
||||
/// constructs, this is the morphological category.
|
||||
String _category;
|
||||
late String _category;
|
||||
|
||||
ConstructTypeEnum constructType;
|
||||
ConstructUseTypeEnum useType;
|
||||
|
|
@ -95,7 +95,13 @@ class OneConstructUse {
|
|||
required category,
|
||||
required this.form,
|
||||
this.id,
|
||||
}) : _category = category ?? "other";
|
||||
}) {
|
||||
if (category is MorphFeaturesEnum) {
|
||||
_category = category.name;
|
||||
} else {
|
||||
_category = category ?? "other";
|
||||
}
|
||||
}
|
||||
|
||||
String? get chatId => metadata.roomId;
|
||||
String get msgId => metadata.eventId!;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
|
||||
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/choreographer/constants/choreo_constants.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
|
||||
|
|
@ -15,6 +12,9 @@ import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
|||
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/pages/settings_learning.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../common/utils/overlay.dart';
|
||||
import '../controllers/it_feedback_controller.dart';
|
||||
import '../models/it_response_model.dart';
|
||||
|
|
@ -101,139 +101,145 @@ class ITBarState extends State<ITBar> with SingleTickerProviderStateMixin {
|
|||
axisAlignment: -1.0,
|
||||
child: CompositedTransformTarget(
|
||||
link: widget.choreographer.itBarLinkAndKey.link,
|
||||
child: Container(
|
||||
key: widget.choreographer.itBarLinkAndKey.key,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).brightness == Brightness.light
|
||||
? Colors.white
|
||||
: Colors.black,
|
||||
),
|
||||
padding: const EdgeInsets.fromLTRB(0, 3, 3, 3),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
children: [
|
||||
if (showITInstructionsTooltip)
|
||||
const InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.clickBestOption,
|
||||
animate: false,
|
||||
),
|
||||
if (showTranslationsChoicesTooltip)
|
||||
const InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.translationChoices,
|
||||
animate: false,
|
||||
),
|
||||
Container(
|
||||
key: widget.choreographer.itBarLinkAndKey.key,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
),
|
||||
padding: const EdgeInsets.fromLTRB(0, 3, 3, 3),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
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(),
|
||||
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 &&
|
||||
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)
|
||||
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();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
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)
|
||||
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,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: itController.sourceText != null
|
||||
? Text(
|
||||
itController.sourceText!,
|
||||
textAlign: TextAlign.center,
|
||||
)
|
||||
: const LinearProgressIndicator(),
|
||||
),
|
||||
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 SizedBox(height: 8.0),
|
||||
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),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
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),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
|
||||
|
||||
class ConstructForm {
|
||||
String form;
|
||||
ConstructIdentifier cId;
|
||||
/// Form of the construct
|
||||
final String form;
|
||||
|
||||
ConstructForm(
|
||||
this.form,
|
||||
this.cId,
|
||||
);
|
||||
/// The constructIdenfifier
|
||||
final ConstructIdentifier cId;
|
||||
|
||||
ConstructForm({
|
||||
required this.form,
|
||||
required this.cId,
|
||||
});
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
|
|
@ -18,4 +21,18 @@ class ConstructForm {
|
|||
|
||||
@override
|
||||
int get hashCode => form.hashCode ^ cId.hashCode;
|
||||
|
||||
factory ConstructForm.fromJson(Map<String, dynamic> json) {
|
||||
return ConstructForm(
|
||||
form: json['form'],
|
||||
cId: ConstructIdentifier.fromJson(json['cId']),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'form': form,
|
||||
'cId': cId.toJson(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
|
|||
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_form.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_level_enum.dart';
|
||||
import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart';
|
||||
|
|
@ -493,6 +494,9 @@ class PangeaToken {
|
|||
category: pos,
|
||||
);
|
||||
|
||||
ConstructForm get vocabForm =>
|
||||
ConstructForm(form: text.content, cId: vocabConstructID);
|
||||
|
||||
/// [setEmoji] sets the emoji for the lemma
|
||||
/// NOTE: assumes that the language of the lemma is the same as the user's current l2
|
||||
Future<void> setEmoji(List<String> emojis) =>
|
||||
|
|
|
|||
|
|
@ -9,11 +9,15 @@ import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
|
|||
class InstructionsInlineTooltip extends StatefulWidget {
|
||||
final InstructionsEnum instructionsEnum;
|
||||
final bool bold;
|
||||
final bool animate;
|
||||
final double padding;
|
||||
|
||||
const InstructionsInlineTooltip({
|
||||
super.key,
|
||||
required this.instructionsEnum,
|
||||
this.bold = false,
|
||||
this.animate = true,
|
||||
this.padding = 0.0,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -24,8 +28,8 @@ class InstructionsInlineTooltip extends StatefulWidget {
|
|||
class InstructionsInlineTooltipState extends State<InstructionsInlineTooltip>
|
||||
with TickerProviderStateMixin {
|
||||
bool _isToggledOff = true;
|
||||
late AnimationController _controller;
|
||||
late Animation<double> _animation;
|
||||
AnimationController? _controller;
|
||||
Animation<double>? _animation;
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant InstructionsInlineTooltip oldWidget) {
|
||||
|
|
@ -44,26 +48,28 @@ class InstructionsInlineTooltipState extends State<InstructionsInlineTooltip>
|
|||
void setToggled() {
|
||||
_isToggledOff = widget.instructionsEnum.isToggledOff;
|
||||
|
||||
// Initialize AnimationController and Animation
|
||||
_controller = AnimationController(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
vsync: this,
|
||||
);
|
||||
if (widget.animate) {
|
||||
// Initialize AnimationController and Animation only if animate is true
|
||||
_controller = AnimationController(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
vsync: this,
|
||||
);
|
||||
|
||||
_animation = CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
_animation = CurvedAnimation(
|
||||
parent: _controller!,
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
|
||||
// Start in correct state
|
||||
if (!_isToggledOff) _controller.forward();
|
||||
// Start in correct state
|
||||
if (!_isToggledOff) _controller!.forward();
|
||||
}
|
||||
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
_controller?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
@ -71,15 +77,28 @@ class InstructionsInlineTooltipState extends State<InstructionsInlineTooltip>
|
|||
widget.instructionsEnum.setToggledOff(true);
|
||||
setState(() {
|
||||
_isToggledOff = true;
|
||||
_controller.reverse();
|
||||
if (widget.animate) {
|
||||
_controller?.reverse();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizeTransition(
|
||||
sizeFactor: _animation,
|
||||
axisAlignment: -1.0,
|
||||
return widget.animate
|
||||
? SizeTransition(
|
||||
sizeFactor: _animation!,
|
||||
axisAlignment: -1.0,
|
||||
child: _buildTooltipContent(context),
|
||||
)
|
||||
: (_isToggledOff
|
||||
? const SizedBox.shrink()
|
||||
: _buildTooltipContent(context));
|
||||
}
|
||||
|
||||
Widget _buildTooltipContent(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.all(widget.padding),
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ import 'package:material_symbols_icons/symbols.dart';
|
|||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_form.dart';
|
||||
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
|
||||
import 'package:fluffychat/pangea/message_token_text/dotted_border_painter.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/practice_choice.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/practice_target.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/reading_assistance_input_row/morph_selection.dart';
|
||||
|
|
@ -129,7 +129,7 @@ class MessageTokenButtonState extends State<MessageTokenButton>
|
|||
|
||||
PracticeTarget? get activity => widget.practiceTarget;
|
||||
|
||||
onMatch(ConstructForm form) {
|
||||
onMatch(PracticeChoice form) {
|
||||
if (widget.overlayController?.activity == null) {
|
||||
debugger(when: kDebugMode);
|
||||
ErrorHandler.logError(
|
||||
|
|
@ -256,7 +256,7 @@ class MessageTokenButtonState extends State<MessageTokenButton>
|
|||
);
|
||||
}
|
||||
|
||||
return DragTarget<ConstructForm>(
|
||||
return DragTarget<PracticeChoice>(
|
||||
builder: (BuildContext context, accepted, rejected) {
|
||||
final double colorAlpha = 0.3 +
|
||||
(widget.overlayController?.selectedChoice != null ? 0.4 : 0.0) +
|
||||
|
|
|
|||
|
|
@ -1,8 +1,4 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_form.dart';
|
||||
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
|
||||
import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart';
|
||||
|
|
@ -10,6 +6,8 @@ import 'package:fluffychat/pangea/practice_activities/message_activity_request.d
|
|||
import 'package:fluffychat/pangea/practice_activities/multiple_choice_activity_model.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/practice_match.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
class EmojiActivityGenerator {
|
||||
Future<MessageActivityResponse> get(
|
||||
|
|
@ -57,8 +55,8 @@ class EmojiActivityGenerator {
|
|||
final List<LemmaInfoResponse> lemmaInfos =
|
||||
await Future.wait(lemmaInfoFutures);
|
||||
|
||||
final Map<ConstructIdentifier, List<String>> matchInfo = Map.fromIterables(
|
||||
req.targetTokens.map((token) => token.vocabConstructID),
|
||||
final Map<ConstructForm, List<String>> matchInfo = Map.fromIterables(
|
||||
req.targetTokens.map((token) => token.vocabForm),
|
||||
lemmaInfos.map((e) => e.emoji),
|
||||
);
|
||||
|
||||
|
|
@ -67,7 +65,7 @@ class EmojiActivityGenerator {
|
|||
activityType: ActivityTypeEnum.emoji,
|
||||
targetTokens: req.targetTokens,
|
||||
langCode: req.userL2,
|
||||
matchContent: PracticeMatch(
|
||||
matchContent: PracticeMatchActivity(
|
||||
matchInfo: matchInfo,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'dart:async';
|
|||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_form.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
|
||||
import 'package:fluffychat/pangea/lemmas/lemma_info_repo.dart';
|
||||
import 'package:fluffychat/pangea/lemmas/lemma_info_request.dart';
|
||||
|
|
@ -80,8 +81,8 @@ class LemmaMeaningActivityGenerator {
|
|||
final List<LemmaInfoResponse> lemmaInfos =
|
||||
await Future.wait(lemmaInfoFutures);
|
||||
|
||||
final Map<ConstructIdentifier, List<String>> matchInfo = Map.fromIterables(
|
||||
req.targetTokens.map((token) => token.vocabConstructID),
|
||||
final Map<ConstructForm, List<String>> matchInfo = Map.fromIterables(
|
||||
req.targetTokens.map((token) => token.vocabForm),
|
||||
lemmaInfos.map((lemmaInfo) => [lemmaInfo.meaning]),
|
||||
);
|
||||
|
||||
|
|
@ -90,7 +91,7 @@ class LemmaMeaningActivityGenerator {
|
|||
activityType: ActivityTypeEnum.wordMeaning,
|
||||
targetTokens: req.targetTokens,
|
||||
langCode: req.userL2,
|
||||
matchContent: PracticeMatch(
|
||||
matchContent: PracticeMatchActivity(
|
||||
matchInfo: matchInfo,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -11,13 +11,13 @@ import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
|
|||
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_form.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/lemmas/user_set_lemma_info.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_features_enum.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/multiple_choice_activity_model.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/practice_choice.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/practice_match.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/practice_record.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/practice_target.dart';
|
||||
|
|
@ -32,7 +32,7 @@ class PracticeActivityModel {
|
|||
final String langCode;
|
||||
|
||||
final MultipleChoiceActivity? multipleChoiceContent;
|
||||
final PracticeMatch? matchContent;
|
||||
final PracticeMatchActivity? matchContent;
|
||||
|
||||
PracticeActivityModel({
|
||||
required this.targetTokens,
|
||||
|
|
@ -60,7 +60,7 @@ class PracticeActivityModel {
|
|||
|
||||
void onMultipleChoiceSelect(
|
||||
PangeaToken token,
|
||||
ConstructForm choice,
|
||||
PracticeChoice choice,
|
||||
PangeaMessageEvent? event,
|
||||
void Function() callback,
|
||||
) {
|
||||
|
|
@ -74,23 +74,24 @@ class PracticeActivityModel {
|
|||
return;
|
||||
}
|
||||
|
||||
if (practiceTarget.record.hasTextResponse(choice.form) || isComplete) {
|
||||
if (practiceTarget.record.hasTextResponse(choice.choiceContent) ||
|
||||
isComplete) {
|
||||
// the user has already selected this choice
|
||||
// so we don't want to record it again
|
||||
return;
|
||||
}
|
||||
|
||||
final bool isCorrect = multipleChoiceContent!.answers.any(
|
||||
(answer) => answer.toLowerCase() == choice.form.toLowerCase(),
|
||||
(answer) => answer.toLowerCase() == choice.choiceContent.toLowerCase(),
|
||||
);
|
||||
|
||||
// NOTE: the response is associated with the contructId of the choice, not the selected token
|
||||
// example: the user selects the word "cat" to match with the emoji 🐶
|
||||
// the response is associated with correct word "dog", not the word "cat"
|
||||
practiceTarget.record.addResponse(
|
||||
cId: choice.cId,
|
||||
cId: choice.form.cId,
|
||||
target: practiceTarget,
|
||||
text: choice.form,
|
||||
text: choice.choiceContent,
|
||||
score: isCorrect ? 1 : 0,
|
||||
);
|
||||
|
||||
|
|
@ -105,15 +106,15 @@ class PracticeActivityModel {
|
|||
constructs: [
|
||||
OneConstructUse(
|
||||
useType: responseUseType(choice)!,
|
||||
lemma: choice.cId.lemma,
|
||||
constructType: choice.cId.type,
|
||||
lemma: choice.form.cId.lemma,
|
||||
constructType: choice.form.cId.type,
|
||||
metadata: ConstructUseMetaData(
|
||||
roomId: event?.room.id,
|
||||
timeStamp: DateTime.now(),
|
||||
eventId: event?.eventId,
|
||||
),
|
||||
category: choice.cId.category,
|
||||
form: choice.form,
|
||||
category: choice.form.cId.category,
|
||||
form: choice.form.form,
|
||||
),
|
||||
],
|
||||
targetID: targetTokens.first.text.uniqueKey,
|
||||
|
|
@ -126,24 +127,25 @@ class PracticeActivityModel {
|
|||
/// only set up for vocab constructs atm
|
||||
void onMatch(
|
||||
PangeaToken token,
|
||||
ConstructForm choice,
|
||||
PracticeChoice choice,
|
||||
PangeaMessageEvent? event,
|
||||
void Function() callback,
|
||||
) {
|
||||
// the user has already selected this choice
|
||||
// so we don't want to record it again
|
||||
if (practiceTarget.record.hasTextResponse(choice.form) || isComplete) {
|
||||
if (practiceTarget.record.hasTextResponse(choice.choiceContent) ||
|
||||
isComplete) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool isCorrect = false;
|
||||
if (multipleChoiceContent != null) {
|
||||
isCorrect = multipleChoiceContent!.answers.any(
|
||||
(answer) => answer.toLowerCase() == choice.form.toLowerCase(),
|
||||
(answer) => answer.toLowerCase() == choice.choiceContent.toLowerCase(),
|
||||
);
|
||||
} else if (matchContent != null) {
|
||||
isCorrect = matchContent!.matchInfo[token.vocabConstructID]!
|
||||
.contains(choice.form);
|
||||
isCorrect = matchContent!.matchInfo[token.vocabForm]!
|
||||
.contains(choice.choiceContent);
|
||||
} else {
|
||||
debugger(when: kDebugMode);
|
||||
ErrorHandler.logError(
|
||||
|
|
@ -158,8 +160,8 @@ class PracticeActivityModel {
|
|||
// example: the user selects the word "cat" to match with the emoji 🐶
|
||||
// the response is associated with correct word "dog", not the word "cat"
|
||||
practiceTarget.record.addResponse(
|
||||
cId: choice.cId,
|
||||
text: choice.form,
|
||||
cId: choice.form.cId,
|
||||
text: choice.choiceContent,
|
||||
target: practiceTarget,
|
||||
score: isCorrect ? 1 : 0,
|
||||
);
|
||||
|
|
@ -173,14 +175,14 @@ class PracticeActivityModel {
|
|||
constructs: [
|
||||
OneConstructUse(
|
||||
useType: responseUseType(choice)!,
|
||||
lemma: choice.cId.lemma,
|
||||
constructType: choice.cId.type,
|
||||
lemma: choice.form.cId.lemma,
|
||||
constructType: choice.form.cId.type,
|
||||
metadata: ConstructUseMetaData(
|
||||
roomId: event?.room.id,
|
||||
timeStamp: DateTime.now(),
|
||||
eventId: event?.eventId,
|
||||
),
|
||||
category: choice.cId.category,
|
||||
category: choice.form.cId.category,
|
||||
// in the case of a wrong answer, the cId doesn't match the token
|
||||
form: token.text.content,
|
||||
),
|
||||
|
|
@ -193,16 +195,16 @@ class PracticeActivityModel {
|
|||
if (activityType == ActivityTypeEnum.emoji) {
|
||||
// final allEmojis = ;
|
||||
|
||||
choice.cId
|
||||
.setUserLemmaInfo(UserSetLemmaInfo(emojis: [choice.form]))
|
||||
choice.form.cId
|
||||
.setUserLemmaInfo(UserSetLemmaInfo(emojis: [choice.choiceContent]))
|
||||
.then((value) {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
if (activityType == ActivityTypeEnum.wordMeaning) {
|
||||
choice.cId
|
||||
.setUserLemmaInfo(UserSetLemmaInfo(meaning: choice.form))
|
||||
choice.form.cId
|
||||
.setUserLemmaInfo(UserSetLemmaInfo(meaning: choice.choiceContent))
|
||||
.then((value) {
|
||||
callback();
|
||||
});
|
||||
|
|
@ -222,23 +224,23 @@ class PracticeActivityModel {
|
|||
}
|
||||
|
||||
/// if null, it means the user has not yet responded with that choice
|
||||
bool? wasCorrectMatch(ConstructForm choice) {
|
||||
bool? wasCorrectMatch(PracticeChoice choice) {
|
||||
for (final response in practiceTarget.record.responses) {
|
||||
if (response.cId == choice.cId && response.isCorrect) {
|
||||
if (response.cId == choice.form.cId && response.isCorrect) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (final response in practiceTarget.record.responses) {
|
||||
if (response.cId == choice.cId) {
|
||||
if (response.cId == choice.form.cId) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
ConstructUseTypeEnum? responseUseType(ConstructForm choice) {
|
||||
ConstructUseTypeEnum? responseUseType(PracticeChoice choice) {
|
||||
for (final response in practiceTarget.record.responses) {
|
||||
if (response.cId == choice.cId) {
|
||||
if (response.cId == choice.form.cId) {
|
||||
return response.useType(activityType);
|
||||
}
|
||||
}
|
||||
|
|
@ -324,7 +326,7 @@ class PracticeActivityModel {
|
|||
.map((e) => PangeaToken.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
matchContent: json['match_content'] != null
|
||||
? PracticeMatch.fromJson(contentMap)
|
||||
? PracticeMatchActivity.fromJson(contentMap)
|
||||
: null,
|
||||
morphFeature: json['morph_feature'] != null
|
||||
? MorphFeaturesEnumExtension.fromString(
|
||||
|
|
|
|||
26
lib/pangea/practice_activities/practice_choice.dart
Normal file
26
lib/pangea/practice_activities/practice_choice.dart
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import 'package:fluffychat/pangea/constructs/construct_form.dart';
|
||||
|
||||
class PracticeChoice {
|
||||
/// choiceContent
|
||||
final String choiceContent;
|
||||
|
||||
/// Form of the associated token
|
||||
final ConstructForm form;
|
||||
|
||||
PracticeChoice({
|
||||
required this.choiceContent,
|
||||
required this.form,
|
||||
});
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is PracticeChoice &&
|
||||
other.form == form &&
|
||||
other.choiceContent == choiceContent;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => form.hashCode ^ choiceContent.hashCode;
|
||||
}
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
import 'package:collection/collection.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/constructs/construct_form.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/practice_choice.dart';
|
||||
|
||||
class PracticeMatch {
|
||||
class PracticeMatchActivity {
|
||||
/// The constructIdenfifiers involved in the activity
|
||||
/// and the forms that are acceptable answers
|
||||
final Map<ConstructIdentifier, List<String>> matchInfo;
|
||||
final Map<ConstructForm, List<String>> matchInfo;
|
||||
|
||||
List<ConstructForm> displayForms = List.empty(growable: true);
|
||||
List<PracticeChoice> choices = List.empty(growable: true);
|
||||
|
||||
PracticeMatch({
|
||||
PracticeMatchActivity({
|
||||
required this.matchInfo,
|
||||
}) {
|
||||
// if there are multiple forms for a construct, pick one to display
|
||||
|
|
@ -21,38 +21,40 @@ class PracticeMatch {
|
|||
// either from that construct's options, or returning to the previous construct
|
||||
// and picking a different form from there
|
||||
for (final ith in matchInfo.entries) {
|
||||
for (final form in ith.value) {
|
||||
if (!displayForms.any((element) => element.form == form)) {
|
||||
displayForms.add(ConstructForm(form, ith.key));
|
||||
for (final acceptableAnswer in ith.value) {
|
||||
if (!choices
|
||||
.any((element) => element.choiceContent == acceptableAnswer)) {
|
||||
choices.add(
|
||||
PracticeChoice(choiceContent: acceptableAnswer, form: ith.key),
|
||||
);
|
||||
break;
|
||||
}
|
||||
// TODO: if none found, we can probably pick a different form for the other one
|
||||
}
|
||||
}
|
||||
|
||||
// remove any items from matchInfo that don't have an item in displayForms
|
||||
// remove any items from matchInfo that don't have an item in choices
|
||||
for (final ith in matchInfo.keys) {
|
||||
if (!displayForms.any((element) => element.cId == ith)) {
|
||||
if (!choices.any((choice) => choice.form == ith)) {
|
||||
matchInfo.remove(ith);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isCorrect(ConstructIdentifier cId, String value) {
|
||||
return matchInfo[cId]!.contains(value);
|
||||
bool isCorrect(ConstructForm form, String value) {
|
||||
return matchInfo[form]!.contains(value);
|
||||
}
|
||||
|
||||
factory PracticeMatch.fromJson(Map<String, dynamic> json) {
|
||||
final Map<ConstructIdentifier, List<String>> matchInfo = {};
|
||||
factory PracticeMatchActivity.fromJson(Map<String, dynamic> json) {
|
||||
final Map<ConstructForm, List<String>> matchInfo = {};
|
||||
for (final constructJson in json['match_info']) {
|
||||
final ConstructIdentifier cId =
|
||||
ConstructIdentifier.fromJson(constructJson['cId']);
|
||||
final ConstructForm cId = ConstructForm.fromJson(constructJson['cId']);
|
||||
final List<String> surfaceForms =
|
||||
List<String>.from(constructJson['forms']);
|
||||
matchInfo[cId] = surfaceForms;
|
||||
}
|
||||
|
||||
return PracticeMatch(
|
||||
return PracticeMatchActivity(
|
||||
matchInfo: matchInfo,
|
||||
);
|
||||
}
|
||||
|
|
@ -75,7 +77,7 @@ class PracticeMatch {
|
|||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is PracticeMatch &&
|
||||
return other is PracticeMatchActivity &&
|
||||
const MapEquality().equals(other.matchInfo, matchInfo);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_form.dart';
|
||||
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/message_activity_request.dart';
|
||||
|
|
@ -55,10 +56,16 @@ class WordFocusListeningGenerator {
|
|||
activityType: ActivityTypeEnum.wordFocusListening,
|
||||
targetTokens: req.targetTokens,
|
||||
langCode: req.userL2,
|
||||
matchContent: PracticeMatch(
|
||||
matchContent: PracticeMatchActivity(
|
||||
matchInfo: Map.fromEntries(
|
||||
req.targetTokens.map(
|
||||
(token) => MapEntry(token.vocabConstructID, [token.text.content]),
|
||||
(token) => MapEntry(
|
||||
ConstructForm(
|
||||
cId: token.vocabConstructID,
|
||||
form: token.text.content,
|
||||
),
|
||||
[token.text.content],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
|
|||
import 'package:fluffychat/pangea/morphs/morph_features_enum.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_icon.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/practice_choice.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/reading_assistance_input_row/message_morph_choice_item.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
|
||||
|
||||
|
|
@ -126,11 +127,15 @@ class MessageMorphInputBarContentState
|
|||
|
||||
widget.activity.onMultipleChoiceSelect(
|
||||
token,
|
||||
ConstructForm(
|
||||
choice,
|
||||
widget.activity.targetTokens.first.morphIdByFeature(
|
||||
widget.activity.morphFeature!,
|
||||
)!,
|
||||
PracticeChoice(
|
||||
choiceContent: choice,
|
||||
form: ConstructForm(
|
||||
cId: widget.activity.targetTokens.first
|
||||
.morphIdByFeature(
|
||||
widget.activity.morphFeature!,
|
||||
)!,
|
||||
form: token.text.content,
|
||||
),
|
||||
),
|
||||
widget.pangeaMessageEvent,
|
||||
() => overlay.setState(() {}),
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/choice_animation.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_form.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/practice_choice.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/reading_assistance_input_row/practice_match_item.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_audio_card.dart';
|
||||
|
|
@ -81,8 +81,8 @@ class MatchActivityCard extends StatelessWidget {
|
|||
alignment: WrapAlignment.center,
|
||||
spacing: 4.0,
|
||||
runSpacing: 4.0,
|
||||
children: activity.matchContent!.displayForms.map(
|
||||
(ConstructForm cf) {
|
||||
children: activity.matchContent!.choices.map(
|
||||
(PracticeChoice cf) {
|
||||
return ChoiceAnimationWidget(
|
||||
isSelected: overlayController.selectedChoice == cf,
|
||||
isCorrect: currentActivity.wasCorrectMatch(cf) ?? false,
|
||||
|
|
@ -90,10 +90,10 @@ class MatchActivityCard extends StatelessWidget {
|
|||
isSelected: overlayController.selectedChoice == cf,
|
||||
isCorrect: currentActivity.wasCorrectMatch(cf),
|
||||
constructForm: cf,
|
||||
content: choiceDisplayContent(cf.form, fontSize),
|
||||
content: choiceDisplayContent(cf.choiceContent, fontSize),
|
||||
audioContent:
|
||||
activityType == ActivityTypeEnum.wordFocusListening
|
||||
? cf.form
|
||||
? cf.choiceContent
|
||||
: null,
|
||||
overlayController: overlayController,
|
||||
fixedSize: activityType == ActivityTypeEnum.wordMeaning
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_form.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/practice_choice.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ class PracticeMatchItem extends StatefulWidget {
|
|||
});
|
||||
|
||||
final Widget content;
|
||||
final ConstructForm constructForm;
|
||||
final PracticeChoice constructForm;
|
||||
final String? audioContent;
|
||||
final MessageOverlayController overlayController;
|
||||
final double? fixedSize;
|
||||
|
|
@ -129,9 +129,14 @@ class PracticeMatchItemState extends State<PracticeMatchItem> {
|
|||
);
|
||||
}
|
||||
|
||||
void onTap() {
|
||||
play();
|
||||
widget.overlayController.onChoiceSelect(widget.constructForm);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LongPressDraggable<ConstructForm>(
|
||||
return LongPressDraggable<PracticeChoice>(
|
||||
data: widget.constructForm,
|
||||
feedback: Material(
|
||||
type: MaterialType.transparency,
|
||||
|
|
@ -144,10 +149,7 @@ class PracticeMatchItemState extends State<PracticeMatchItem> {
|
|||
child: InkWell(
|
||||
onHover: (isHovered) => setState(() => _isHovered = isHovered),
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
onTap: () {
|
||||
play();
|
||||
widget.overlayController.onChoiceSelect(widget.constructForm);
|
||||
},
|
||||
onTap: onTap,
|
||||
child: content(context),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import 'package:fluffychat/pages/chat/chat.dart';
|
|||
import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_mode_locked_card.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_speech_to_text_card.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_translation_card.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/practice_activity/practice_activity_card.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/word_zoom/morph_focus_widget.dart';
|
||||
|
|
@ -35,63 +36,65 @@ class ReadingAssistanceInputBar extends StatelessWidget {
|
|||
)
|
||||
: null;
|
||||
|
||||
switch (overlayController.toolbarMode) {
|
||||
case MessageMode.messageSpeechToText:
|
||||
case MessageMode.practiceActivity:
|
||||
case MessageMode.wordZoom:
|
||||
case MessageMode.noneSelected:
|
||||
case MessageMode.messageMeaning:
|
||||
//TODO: show all emojis for the lemmas and allow sending normal reactions
|
||||
break;
|
||||
if (overlayController.pangeaMessageEvent?.isAudioMessage == true) {
|
||||
return MessageSpeechToTextCard(
|
||||
messageEvent: overlayController.pangeaMessageEvent!,
|
||||
);
|
||||
} else {
|
||||
switch (overlayController.toolbarMode) {
|
||||
case MessageMode.messageSpeechToText:
|
||||
case MessageMode.practiceActivity:
|
||||
case MessageMode.wordZoom:
|
||||
case MessageMode.noneSelected:
|
||||
case MessageMode.messageMeaning:
|
||||
//TODO: show all emojis for the lemmas and allow sending normal reactions
|
||||
break;
|
||||
|
||||
case MessageMode.messageTranslation:
|
||||
if (overlayController.isTranslationUnlocked) {
|
||||
content = MessageTranslationCard(
|
||||
messageEvent: overlayController.pangeaMessageEvent!,
|
||||
);
|
||||
} else {
|
||||
content = MessageModeLockedCard(controller: overlayController);
|
||||
}
|
||||
case MessageMode.messageTranslation:
|
||||
if (overlayController.isTranslationUnlocked) {
|
||||
content = MessageTranslationCard(
|
||||
messageEvent: overlayController.pangeaMessageEvent!,
|
||||
);
|
||||
} else {
|
||||
content = MessageModeLockedCard(controller: overlayController);
|
||||
}
|
||||
|
||||
case MessageMode.wordEmoji:
|
||||
case MessageMode.wordMeaning:
|
||||
case MessageMode.listening:
|
||||
debugPrint(
|
||||
"reading_assistance_input_bar: wordEmoji or wordMeaning with target: $target",
|
||||
);
|
||||
|
||||
if (target != null) {
|
||||
content = PracticeActivityCard(
|
||||
pangeaMessageEvent: overlayController.pangeaMessageEvent!,
|
||||
targetTokensAndActivityType: target,
|
||||
overlayController: overlayController,
|
||||
);
|
||||
} else {
|
||||
content = const Text("All done!");
|
||||
}
|
||||
case MessageMode.wordMorph:
|
||||
if (target != null) {
|
||||
content = PracticeActivityCard(
|
||||
pangeaMessageEvent: overlayController.pangeaMessageEvent!,
|
||||
targetTokensAndActivityType: target,
|
||||
overlayController: overlayController,
|
||||
);
|
||||
} else if (overlayController.selectedMorph != null) {
|
||||
content = MorphFocusWidget(
|
||||
token: overlayController.selectedMorph!.token,
|
||||
morphFeature: overlayController.selectedMorph!.morph,
|
||||
pangeaMessageEvent: overlayController.pangeaMessageEvent!,
|
||||
overlayController: overlayController,
|
||||
onEditDone: () => overlayController.setState(() {}),
|
||||
);
|
||||
} else {
|
||||
content = Center(
|
||||
child: Text(
|
||||
L10n.of(context).selectForGrammar,
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
);
|
||||
}
|
||||
case MessageMode.wordEmoji:
|
||||
case MessageMode.wordMeaning:
|
||||
case MessageMode.listening:
|
||||
if (target != null) {
|
||||
content = PracticeActivityCard(
|
||||
pangeaMessageEvent: overlayController.pangeaMessageEvent!,
|
||||
targetTokensAndActivityType: target,
|
||||
overlayController: overlayController,
|
||||
);
|
||||
} else {
|
||||
content = const Text("All done!");
|
||||
}
|
||||
case MessageMode.wordMorph:
|
||||
if (target != null) {
|
||||
content = PracticeActivityCard(
|
||||
pangeaMessageEvent: overlayController.pangeaMessageEvent!,
|
||||
targetTokensAndActivityType: target,
|
||||
overlayController: overlayController,
|
||||
);
|
||||
} else if (overlayController.selectedMorph != null) {
|
||||
content = MorphFocusWidget(
|
||||
token: overlayController.selectedMorph!.token,
|
||||
morphFeature: overlayController.selectedMorph!.morph,
|
||||
pangeaMessageEvent: overlayController.pangeaMessageEvent!,
|
||||
overlayController: overlayController,
|
||||
onEditDone: () => overlayController.setState(() {}),
|
||||
);
|
||||
} else {
|
||||
content = Center(
|
||||
child: Text(
|
||||
L10n.of(context).selectForGrammar,
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (content == null) {
|
||||
|
|
@ -108,17 +111,6 @@ class ReadingAssistanceInputBar extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (controller.showEmojiPicker) return const SizedBox.shrink();
|
||||
|
||||
final display = controller.editEvent == null &&
|
||||
controller.replyEvent == null &&
|
||||
controller.room.canSendDefaultMessages &&
|
||||
controller.selectedEvents.isNotEmpty;
|
||||
|
||||
if (!display) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return Expanded(
|
||||
child: Container(
|
||||
width: overlayController.maxWidth,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import 'package:fluffychat/config/app_config.dart';
|
|||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/overlay.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_form.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/event_wrappers/pangea_representation_event.dart';
|
||||
|
|
@ -23,6 +22,7 @@ import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
|||
import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/practice_choice.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/practice_selection.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/practice_selection_repo.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/controllers/text_to_speech_controller.dart';
|
||||
|
|
@ -77,7 +77,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
MorphSelection? selectedMorph;
|
||||
|
||||
/// tracks selected choice
|
||||
ConstructForm? selectedChoice;
|
||||
PracticeChoice? selectedChoice;
|
||||
|
||||
PangeaTokenText? _selectedSpan;
|
||||
|
||||
|
|
@ -336,7 +336,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
}
|
||||
}
|
||||
|
||||
void onChoiceSelect(ConstructForm choice, [bool force = false]) {
|
||||
void onChoiceSelect(PracticeChoice choice, [bool force = false]) {
|
||||
if (selectedChoice == choice && !force) {
|
||||
selectedChoice = null;
|
||||
} else {
|
||||
|
|
@ -399,6 +399,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
|
||||
/// you have to complete one of the mode mini-games to unlock translation
|
||||
bool get isTranslationUnlocked =>
|
||||
pangeaMessageEvent?.ownMessage == true ||
|
||||
!messageInUserL2 ||
|
||||
(messageLemmaInfos?.isEmpty ?? false) ||
|
||||
isEmojiDone ||
|
||||
|
|
|
|||
94
pubspec.lock
94
pubspec.lock
|
|
@ -114,10 +114,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: async
|
||||
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
version: "2.12.0"
|
||||
audio_session:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -218,10 +218,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.2"
|
||||
cached_network_image:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -266,10 +266,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: characters
|
||||
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
version: "1.4.0"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -306,18 +306,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
version: "1.1.2"
|
||||
collection:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: collection
|
||||
sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.0"
|
||||
version: "1.19.1"
|
||||
colorize:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -506,10 +506,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
version: "1.3.2"
|
||||
fcm_shared_isolate:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -529,10 +529,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
version: "7.0.1"
|
||||
file_picker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -1389,18 +1389,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06"
|
||||
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.7"
|
||||
version: "10.0.8"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379"
|
||||
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.8"
|
||||
version: "3.0.9"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1477,10 +1477,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.16+1"
|
||||
version: "0.12.17"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1510,10 +1510,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.15.0"
|
||||
version: "1.16.0"
|
||||
mgrs_dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1694,10 +1694,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: path
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.0"
|
||||
version: "1.9.1"
|
||||
path_parsing:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1822,10 +1822,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
|
||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.5"
|
||||
version: "3.1.6"
|
||||
platform_detect:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1902,10 +1902,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: process
|
||||
sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32"
|
||||
sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.2"
|
||||
version: "5.0.3"
|
||||
proj4dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -2283,10 +2283,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
version: "1.10.1"
|
||||
sprintf:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -2363,26 +2363,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377"
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.0"
|
||||
version: "1.12.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
version: "2.1.4"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3"
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
version: "1.4.1"
|
||||
string_validator:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -2435,34 +2435,34 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
version: "1.2.2"
|
||||
test:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test
|
||||
sha256: "713a8789d62f3233c46b4a90b174737b2c04cb6ae4500f2aa8b1be8f03f5e67f"
|
||||
sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.25.8"
|
||||
version: "1.25.15"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c"
|
||||
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.3"
|
||||
version: "0.7.4"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
sha256: "12391302411737c176b0b5d6491f466b0dd56d4763e347b6714efbaa74d7953d"
|
||||
sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.5"
|
||||
version: "0.6.8"
|
||||
text_to_speech:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -2755,10 +2755,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b
|
||||
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.3.0"
|
||||
version: "14.3.1"
|
||||
wakelock_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -2888,5 +2888,5 @@ packages:
|
|||
source: hosted
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.6.0 <4.0.0"
|
||||
dart: ">=3.7.0-0 <4.0.0"
|
||||
flutter: ">=3.27.0"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue