Add message meaning challenge activity (#1706)
* Add message meaning mode to toolbar --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: ggurdin <46800240+ggurdin@users.noreply.github.com> Co-authored-by: ggurdin <ggurdin@gmail.com>
This commit is contained in:
parent
b98f2d3283
commit
e2ca788f81
24 changed files with 421 additions and 79 deletions
|
|
@ -4815,5 +4815,10 @@
|
|||
"open": "Open",
|
||||
"waitingForServer": "Waiting for server...",
|
||||
"appIntroduction": "FluffyChat lets you chat with your friends across different messengers. Learn more at https://matrix.org or just tap *Continue*.",
|
||||
"whatIsLemma": "What is the lemma?"
|
||||
"whatIsLemma": "What is the lemma?",
|
||||
"constructUseCorMmDesc": "Correct message meaning",
|
||||
"constructUseIncMmDesc": "Incorrect message meaning",
|
||||
"constructUseIgnMmDesc": "Ignored message meaning",
|
||||
"clickForMeaningActivity": "Click here for a Meaning Challenge",
|
||||
"meaning": "Meaning"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,11 @@ enum ConstructUseTypeEnum {
|
|||
/// User can select any emoji
|
||||
em,
|
||||
|
||||
/// message meaning activity
|
||||
corMM,
|
||||
incMM,
|
||||
ignMM,
|
||||
|
||||
/// not defined, likely a new construct introduced by choreo and not yet classified by an old version of the client
|
||||
nan
|
||||
}
|
||||
|
|
@ -121,6 +126,12 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
|
|||
return L10n.of(context).constructUseEmojiDesc;
|
||||
case ConstructUseTypeEnum.pvm:
|
||||
return L10n.of(context).constructUsePvmDesc;
|
||||
case ConstructUseTypeEnum.corMM:
|
||||
return L10n.of(context).constructUseCorMmDesc;
|
||||
case ConstructUseTypeEnum.incMM:
|
||||
return L10n.of(context).constructUseIncMmDesc;
|
||||
case ConstructUseTypeEnum.ignMM:
|
||||
return L10n.of(context).constructUseIgnMmDesc;
|
||||
case ConstructUseTypeEnum.nan:
|
||||
return L10n.of(context).constructUseNanDesc;
|
||||
}
|
||||
|
|
@ -161,6 +172,10 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
|
|||
return ActivityTypeEnum.morphId.icon;
|
||||
case ConstructUseTypeEnum.em:
|
||||
return ActivityTypeEnum.emoji.icon;
|
||||
case ConstructUseTypeEnum.corMM:
|
||||
case ConstructUseTypeEnum.incMM:
|
||||
case ConstructUseTypeEnum.ignMM:
|
||||
return ActivityTypeEnum.messageMeaning.icon;
|
||||
case ConstructUseTypeEnum.pvm:
|
||||
return Icons.mic;
|
||||
case ConstructUseTypeEnum.unk:
|
||||
|
|
@ -195,6 +210,7 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
|
|||
|
||||
case ConstructUseTypeEnum.corIt:
|
||||
case ConstructUseTypeEnum.em:
|
||||
case ConstructUseTypeEnum.corMM:
|
||||
return 1;
|
||||
|
||||
case ConstructUseTypeEnum.ignIt:
|
||||
|
|
@ -204,11 +220,13 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
|
|||
case ConstructUseTypeEnum.ignHWL:
|
||||
case ConstructUseTypeEnum.ignL:
|
||||
case ConstructUseTypeEnum.ignM:
|
||||
case ConstructUseTypeEnum.ignMM:
|
||||
case ConstructUseTypeEnum.unk:
|
||||
case ConstructUseTypeEnum.nan:
|
||||
return 0;
|
||||
|
||||
case ConstructUseTypeEnum.ga:
|
||||
case ConstructUseTypeEnum.incMM:
|
||||
return -1;
|
||||
|
||||
case ConstructUseTypeEnum.incIt:
|
||||
|
|
@ -253,6 +271,9 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
|
|||
case ConstructUseTypeEnum.corM:
|
||||
case ConstructUseTypeEnum.incM:
|
||||
case ConstructUseTypeEnum.ignM:
|
||||
case ConstructUseTypeEnum.corMM:
|
||||
case ConstructUseTypeEnum.incMM:
|
||||
case ConstructUseTypeEnum.ignMM:
|
||||
case ConstructUseTypeEnum.em:
|
||||
case ConstructUseTypeEnum.nan:
|
||||
return false;
|
||||
|
|
@ -288,6 +309,9 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
|
|||
case ConstructUseTypeEnum.corM:
|
||||
case ConstructUseTypeEnum.ignM:
|
||||
case ConstructUseTypeEnum.incM:
|
||||
case ConstructUseTypeEnum.corMM:
|
||||
case ConstructUseTypeEnum.incMM:
|
||||
case ConstructUseTypeEnum.ignMM:
|
||||
case ConstructUseTypeEnum.em:
|
||||
return LearningSkillsEnum.reading;
|
||||
case ConstructUseTypeEnum.pvm:
|
||||
|
|
@ -313,6 +337,7 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
|
|||
case ConstructUseTypeEnum.corL:
|
||||
case ConstructUseTypeEnum.corM:
|
||||
case ConstructUseTypeEnum.em:
|
||||
case ConstructUseTypeEnum.corMM:
|
||||
return AnalyticsSummaryEnum.numChoicesCorrect;
|
||||
|
||||
case ConstructUseTypeEnum.incIt:
|
||||
|
|
@ -322,6 +347,7 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
|
|||
case ConstructUseTypeEnum.incHWL:
|
||||
case ConstructUseTypeEnum.incL:
|
||||
case ConstructUseTypeEnum.incM:
|
||||
case ConstructUseTypeEnum.incMM:
|
||||
return AnalyticsSummaryEnum.numChoicesIncorrect;
|
||||
|
||||
case ConstructUseTypeEnum.ignIt:
|
||||
|
|
@ -331,6 +357,7 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
|
|||
case ConstructUseTypeEnum.ignHWL:
|
||||
case ConstructUseTypeEnum.ignL:
|
||||
case ConstructUseTypeEnum.ignM:
|
||||
case ConstructUseTypeEnum.ignMM:
|
||||
case ConstructUseTypeEnum.nan:
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,9 +104,7 @@ class MessageAnalyticsEntry {
|
|||
}
|
||||
|
||||
void _popQueue() {
|
||||
if (hasHiddenWordActivity) {
|
||||
_activityQueue.removeAt(0);
|
||||
}
|
||||
if (_activityQueue.isNotEmpty) _activityQueue.removeAt(0);
|
||||
}
|
||||
|
||||
void _filterQueue(ActivityTypeEnum activityType) {
|
||||
|
|
@ -123,6 +121,9 @@ class MessageAnalyticsEntry {
|
|||
bool get hasHiddenWordActivity =>
|
||||
nextActivity?.activityType.hiddenType ?? false;
|
||||
|
||||
bool get hasMessageMeaningActivity => _activityQueue
|
||||
.any((a) => a.activityType == ActivityTypeEnum.messageMeaning);
|
||||
|
||||
int get numActivities => _activityQueue.length;
|
||||
|
||||
// /// If there are more than 4 tokens that can be heard, we don't want to do word focus listening
|
||||
|
|
@ -141,15 +142,12 @@ class MessageAnalyticsEntry {
|
|||
}
|
||||
}
|
||||
|
||||
/// Adds a word focus listening activity to the front of the queue
|
||||
/// Add a message meaning activity to the front of the queue
|
||||
/// And limits to _maxQueueLength activities
|
||||
void addTokenToActivityQueue(
|
||||
PangeaToken token, {
|
||||
ActivityTypeEnum type = ActivityTypeEnum.wordMeaning,
|
||||
}) {
|
||||
void addMessageMeaningActivity() {
|
||||
final entry = TargetTokensAndActivityType(
|
||||
tokens: [token],
|
||||
activityType: ActivityTypeEnum.wordMeaning,
|
||||
tokens: _tokens,
|
||||
activityType: ActivityTypeEnum.messageMeaning,
|
||||
);
|
||||
_pushQueue(entry);
|
||||
}
|
||||
|
|
@ -204,9 +202,7 @@ class MessageAnalyticsEntry {
|
|||
);
|
||||
}
|
||||
|
||||
void onActivityComplete() {
|
||||
_popQueue();
|
||||
}
|
||||
void onActivityComplete() => _popQueue();
|
||||
|
||||
void exitPracticeFlow() => _clearQueue();
|
||||
|
||||
|
|
|
|||
|
|
@ -461,11 +461,11 @@ class PangeaMessageEvent {
|
|||
|
||||
RepresentationEvent? representationByLanguage(String langCode) =>
|
||||
representations.firstWhereOrNull(
|
||||
(element) => element.langCode == langCode,
|
||||
(element) => element.langCode.split("-")[0] == langCode.split("-")[0],
|
||||
);
|
||||
|
||||
int translationIndex(String langCode) => representations.indexWhere(
|
||||
(element) => element.langCode == langCode,
|
||||
(element) => element.langCode.split("-")[0] == langCode.split("-")[0],
|
||||
);
|
||||
|
||||
String translationTextSafe(String langCode) {
|
||||
|
|
@ -596,7 +596,8 @@ class PangeaMessageEvent {
|
|||
|
||||
/// Should almost always be true. Useful in the case that the message
|
||||
/// display rep has the langCode "unk"
|
||||
bool get messageDisplayLangIsL2 => messageDisplayLangCode == l2Code;
|
||||
bool get messageDisplayLangIsL2 =>
|
||||
messageDisplayLangCode.split("-")[0] == l2Code?.split("-")[0];
|
||||
|
||||
String get messageDisplayLangCode {
|
||||
final bool immersionMode = MatrixState
|
||||
|
|
@ -684,7 +685,11 @@ class PangeaMessageEvent {
|
|||
bool debug = false,
|
||||
}) =>
|
||||
_practiceActivityEvents
|
||||
.where((event) => event.practiceActivity.langCode == langCode)
|
||||
.where(
|
||||
(event) =>
|
||||
event.practiceActivity.langCode.split("-")[0] ==
|
||||
langCode.split("")[0],
|
||||
)
|
||||
.toList();
|
||||
|
||||
/// Returns a list of [PracticeActivityEvent] for the user's active l2.
|
||||
|
|
|
|||
|
|
@ -213,6 +213,30 @@ class PangeaToken {
|
|||
);
|
||||
}
|
||||
|
||||
List<OneConstructUse> allUses(
|
||||
ConstructUseTypeEnum type,
|
||||
ConstructUseMetaData metadata,
|
||||
) {
|
||||
final List<OneConstructUse> uses = [];
|
||||
if (!lemma.saveVocab) return uses;
|
||||
|
||||
uses.add(toVocabUse(type, metadata));
|
||||
for (final morphFeature in morph.keys) {
|
||||
uses.add(
|
||||
OneConstructUse(
|
||||
useType: type,
|
||||
lemma: morph[morphFeature],
|
||||
form: text.content,
|
||||
constructType: ConstructTypeEnum.morph,
|
||||
metadata: metadata,
|
||||
category: morphFeature,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return uses;
|
||||
}
|
||||
|
||||
bool isActivityBasicallyEligible(
|
||||
ActivityTypeEnum a, [
|
||||
String? morphFeature,
|
||||
|
|
@ -238,6 +262,7 @@ class PangeaToken {
|
|||
return lemma.saveVocab &&
|
||||
text.content.toLowerCase() != lemma.text.toLowerCase();
|
||||
case ActivityTypeEnum.emoji:
|
||||
case ActivityTypeEnum.messageMeaning:
|
||||
return true;
|
||||
case ActivityTypeEnum.morphId:
|
||||
return morph.isNotEmpty && canGenerate;
|
||||
|
|
@ -299,6 +324,7 @@ class PangeaToken {
|
|||
.any((u) => u == a.correctUse);
|
||||
// Note that it matters less if they did morphId in general, than if they did it with the particular feature
|
||||
case ActivityTypeEnum.morphId:
|
||||
// TODO: investigate if we take out condition "|| morphTag == null", will we get the expected number of morph activities?
|
||||
if (morphFeature == null || morphTag == null) {
|
||||
debugger(when: kDebugMode);
|
||||
return false;
|
||||
|
|
@ -306,6 +332,13 @@ class PangeaToken {
|
|||
return morphConstruct(morphFeature, morphTag)
|
||||
.uses
|
||||
.any((u) => u.useType == a.correctUse && u.form == text.content);
|
||||
case ActivityTypeEnum.messageMeaning:
|
||||
debugger(when: kDebugMode);
|
||||
ErrorHandler.logError(
|
||||
m: "should not call didActivitySuccessfully for ActivityTypeEnum.messageMeaning",
|
||||
data: toJson(),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -316,14 +349,15 @@ class PangeaToken {
|
|||
]) {
|
||||
switch (a) {
|
||||
case ActivityTypeEnum.wordMeaning:
|
||||
if (daysSinceLastUseByType(ActivityTypeEnum.wordMeaning) < 7) {
|
||||
if (daysSinceLastUseByType(ActivityTypeEnum.wordMeaning) < 7 ||
|
||||
daysSinceLastUseByType(ActivityTypeEnum.messageMeaning) < 7) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isContentWord) {
|
||||
return vocabConstruct.points < 3;
|
||||
return vocabConstruct.points < 1;
|
||||
} else if (canBeDefined) {
|
||||
return vocabConstruct.points < 2;
|
||||
return vocabConstruct.points < 1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -342,6 +376,7 @@ class PangeaToken {
|
|||
// return _didActivitySuccessfully(ActivityTypeEnum.wordMeaning) &&
|
||||
// daysSinceLastUseByType(a) > 7;
|
||||
case ActivityTypeEnum.emoji:
|
||||
case ActivityTypeEnum.messageMeaning:
|
||||
return true;
|
||||
case ActivityTypeEnum.morphId:
|
||||
if (morphFeature == null || morphTag == null) {
|
||||
|
|
@ -407,6 +442,7 @@ class PangeaToken {
|
|||
case ActivityTypeEnum.emoji:
|
||||
case ActivityTypeEnum.wordFocusListening:
|
||||
case ActivityTypeEnum.hiddenWordListening:
|
||||
case ActivityTypeEnum.messageMeaning:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ extension LanguageLevelTypeEnumExtension on LanguageLevelTypeEnum {
|
|||
String get string {
|
||||
switch (this) {
|
||||
case LanguageLevelTypeEnum.preA1:
|
||||
return 'Pre-A1';
|
||||
return 'PREA1';
|
||||
case LanguageLevelTypeEnum.a1:
|
||||
return 'A1';
|
||||
case LanguageLevelTypeEnum.a2:
|
||||
|
|
@ -65,6 +65,7 @@ extension LanguageLevelTypeEnumExtension on LanguageLevelTypeEnum {
|
|||
static LanguageLevelTypeEnum fromString(String? value) {
|
||||
switch (value) {
|
||||
case 'PREA1':
|
||||
case 'PRE-A1':
|
||||
case 'Pre-A1':
|
||||
return LanguageLevelTypeEnum.preA1;
|
||||
case 'A1':
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ enum ActivityTypeEnum {
|
|||
lemmaId,
|
||||
emoji,
|
||||
morphId,
|
||||
// correctionPuzzle,
|
||||
messageMeaning, // TODO: Add to L10n
|
||||
}
|
||||
|
||||
extension ActivityTypeExtension on ActivityTypeEnum {
|
||||
|
|
@ -31,6 +31,8 @@ extension ActivityTypeExtension on ActivityTypeEnum {
|
|||
return 'emoji';
|
||||
case ActivityTypeEnum.morphId:
|
||||
return 'morph_id';
|
||||
case ActivityTypeEnum.messageMeaning:
|
||||
return 'message_meaning'; // TODO: Add to L10n
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -41,6 +43,7 @@ extension ActivityTypeExtension on ActivityTypeEnum {
|
|||
case ActivityTypeEnum.lemmaId:
|
||||
case ActivityTypeEnum.emoji:
|
||||
case ActivityTypeEnum.morphId:
|
||||
case ActivityTypeEnum.messageMeaning:
|
||||
return false;
|
||||
case ActivityTypeEnum.hiddenWordListening:
|
||||
return true;
|
||||
|
|
@ -53,6 +56,7 @@ extension ActivityTypeExtension on ActivityTypeEnum {
|
|||
case ActivityTypeEnum.lemmaId:
|
||||
case ActivityTypeEnum.emoji:
|
||||
case ActivityTypeEnum.morphId:
|
||||
case ActivityTypeEnum.messageMeaning:
|
||||
return false;
|
||||
case ActivityTypeEnum.wordFocusListening:
|
||||
case ActivityTypeEnum.hiddenWordListening:
|
||||
|
|
@ -83,6 +87,8 @@ extension ActivityTypeExtension on ActivityTypeEnum {
|
|||
return ActivityTypeEnum.emoji;
|
||||
case 'morph_id':
|
||||
return ActivityTypeEnum.morphId;
|
||||
case 'message_meaning':
|
||||
return ActivityTypeEnum.messageMeaning; // TODO: Add to L10n
|
||||
default:
|
||||
throw Exception('Unknown activity type: $split');
|
||||
}
|
||||
|
|
@ -122,6 +128,12 @@ extension ActivityTypeExtension on ActivityTypeEnum {
|
|||
ConstructUseTypeEnum.incM,
|
||||
ConstructUseTypeEnum.ignM,
|
||||
];
|
||||
case ActivityTypeEnum.messageMeaning:
|
||||
return [
|
||||
ConstructUseTypeEnum.corMM,
|
||||
ConstructUseTypeEnum.incMM,
|
||||
ConstructUseTypeEnum.ignMM,
|
||||
]; // TODO: Add to L10n
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -139,6 +151,8 @@ extension ActivityTypeExtension on ActivityTypeEnum {
|
|||
return ConstructUseTypeEnum.em;
|
||||
case ActivityTypeEnum.morphId:
|
||||
return ConstructUseTypeEnum.corM;
|
||||
case ActivityTypeEnum.messageMeaning:
|
||||
return ConstructUseTypeEnum.corMM;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -150,6 +164,7 @@ extension ActivityTypeExtension on ActivityTypeEnum {
|
|||
case ActivityTypeEnum.hiddenWordListening:
|
||||
case ActivityTypeEnum.lemmaId:
|
||||
case ActivityTypeEnum.emoji:
|
||||
case ActivityTypeEnum.messageMeaning:
|
||||
return (id) => id.type == ConstructTypeEnum.vocab;
|
||||
case ActivityTypeEnum.morphId:
|
||||
return (id) => id.type == ConstructTypeEnum.morph;
|
||||
|
|
@ -169,6 +184,8 @@ extension ActivityTypeExtension on ActivityTypeEnum {
|
|||
return Icons.emoji_emotions;
|
||||
case ActivityTypeEnum.morphId:
|
||||
return Icons.format_shapes;
|
||||
case ActivityTypeEnum.messageMeaning:
|
||||
return Icons.star; // TODO: Add to L10n
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
enum MessageMode {
|
||||
practiceActivity,
|
||||
|
|
@ -11,6 +10,7 @@ enum MessageMode {
|
|||
speechToText,
|
||||
wordZoom,
|
||||
noneSelected,
|
||||
messageMeaning,
|
||||
}
|
||||
|
||||
extension MessageModeExtension on MessageMode {
|
||||
|
|
@ -28,6 +28,8 @@ extension MessageModeExtension on MessageMode {
|
|||
return Symbols.dictionary;
|
||||
case MessageMode.noneSelected:
|
||||
return Icons.error;
|
||||
case MessageMode.messageMeaning:
|
||||
return Icons.star;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -45,6 +47,8 @@ extension MessageModeExtension on MessageMode {
|
|||
return L10n.of(context).vocab;
|
||||
case MessageMode.noneSelected:
|
||||
return '';
|
||||
case MessageMode.messageMeaning:
|
||||
return L10n.of(context).meaning;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -62,21 +66,8 @@ extension MessageModeExtension on MessageMode {
|
|||
return L10n.of(context).vocab;
|
||||
case MessageMode.noneSelected:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldShowAsToolbarButton(Event event) {
|
||||
switch (this) {
|
||||
case MessageMode.translation:
|
||||
case MessageMode.textToSpeech:
|
||||
return event.messageType == MessageTypes.Text;
|
||||
case MessageMode.speechToText:
|
||||
return event.messageType == MessageTypes.Audio;
|
||||
case MessageMode.practiceActivity:
|
||||
return true;
|
||||
case MessageMode.wordZoom:
|
||||
case MessageMode.noneSelected:
|
||||
return false;
|
||||
case MessageMode.messageMeaning:
|
||||
return L10n.of(context).meaning;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -91,6 +82,7 @@ extension MessageModeExtension on MessageMode {
|
|||
case MessageMode.speechToText:
|
||||
case MessageMode.wordZoom:
|
||||
case MessageMode.noneSelected:
|
||||
case MessageMode.messageMeaning:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -109,6 +101,7 @@ extension MessageModeExtension on MessageMode {
|
|||
case MessageMode.practiceActivity:
|
||||
case MessageMode.wordZoom:
|
||||
case MessageMode.noneSelected:
|
||||
case MessageMode.messageMeaning:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
|
|
@ -75,6 +79,7 @@ class MessageActivityRequest {
|
|||
if ([ActivityTypeEnum.wordFocusListening, ActivityTypeEnum.wordMeaning]
|
||||
.contains(targetType) &&
|
||||
targetTokens.length > 1) {
|
||||
debugger(when: kDebugMode);
|
||||
throw Exception(
|
||||
'Target tokens must be a single token for this activity type',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -174,6 +174,7 @@ class PracticeActivityModel {
|
|||
case ActivityTypeEnum.hiddenWordListening:
|
||||
case ActivityTypeEnum.wordFocusListening:
|
||||
case ActivityTypeEnum.lemmaId:
|
||||
case ActivityTypeEnum.messageMeaning:
|
||||
return content.question;
|
||||
case ActivityTypeEnum.emoji:
|
||||
return L10n.of(context).pickAnEmoji(targetLemma, partOfSpeech);
|
||||
|
|
|
|||
|
|
@ -163,6 +163,10 @@ class ActivityRecordResponse {
|
|||
return score > 0
|
||||
? ConstructUseTypeEnum.corHWL
|
||||
: ConstructUseTypeEnum.incHWL;
|
||||
case ActivityTypeEnum.messageMeaning:
|
||||
return score > 0
|
||||
? ConstructUseTypeEnum.corMM
|
||||
: ConstructUseTypeEnum.incMM;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -199,6 +203,15 @@ class ActivityRecordResponse {
|
|||
category: token.pos,
|
||||
),
|
||||
];
|
||||
case ActivityTypeEnum.messageMeaning:
|
||||
return practiceActivity.targetTokens!
|
||||
.expand(
|
||||
(t) => t.allUses(
|
||||
useType(practiceActivity.activityType),
|
||||
metadata,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
case ActivityTypeEnum.hiddenWordListening:
|
||||
return practiceActivity.targetTokens!
|
||||
.map(
|
||||
|
|
|
|||
|
|
@ -133,7 +133,9 @@ class PracticeGenerationController {
|
|||
case ActivityTypeEnum.morphId:
|
||||
return _morph.get(req);
|
||||
case ActivityTypeEnum.wordMeaning:
|
||||
debugger(when: kDebugMode);
|
||||
return _wordMeaning.get(req);
|
||||
case ActivityTypeEnum.messageMeaning:
|
||||
case ActivityTypeEnum.wordFocusListening:
|
||||
case ActivityTypeEnum.hiddenWordListening:
|
||||
return _fetchFromServer(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
import 'package:fluffychat/pangea/analytics_misc/construct_identifier.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
|
||||
import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart';
|
||||
import 'package:fluffychat/pangea/lemmas/lemma.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/enums/activity_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/models/message_activity_request.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/models/multiple_choice_activity_model.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/models/practice_activity_model.dart';
|
||||
|
||||
final wordMeaningStaticPracticeActivityModel = MessageActivityResponse(
|
||||
activity: PracticeActivityModel(
|
||||
tgtConstructs: [
|
||||
ConstructIdentifier(
|
||||
type: ConstructTypeEnum.vocab,
|
||||
lemma: 'example',
|
||||
category: 'noun',
|
||||
),
|
||||
],
|
||||
targetTokens: [
|
||||
PangeaToken(
|
||||
text: PangeaTokenText(content: 'Cómo', offset: 0, length: 4),
|
||||
lemma: Lemma(text: 'cómo', saveVocab: true, form: 'cómo'),
|
||||
pos: 'ADV',
|
||||
morph: {
|
||||
'PronType': 'Int',
|
||||
},
|
||||
),
|
||||
PangeaToken(
|
||||
text: PangeaTokenText(content: 'estás', offset: 5, length: 5),
|
||||
lemma: Lemma(text: 'estar', saveVocab: true, form: 'estás'),
|
||||
pos: 'VERB',
|
||||
morph: {
|
||||
'Mood': 'Ind',
|
||||
'Tense': 'Pres',
|
||||
'VerbForm': 'Fin',
|
||||
'Number': 'Sing',
|
||||
'Person': '2',
|
||||
},
|
||||
),
|
||||
PangeaToken(
|
||||
text: PangeaTokenText(content: '?', offset: 10, length: 1),
|
||||
lemma: Lemma(text: '?', saveVocab: false, form: '?'),
|
||||
pos: 'PUNCT',
|
||||
morph: {
|
||||
'PunctType': 'Peri',
|
||||
},
|
||||
),
|
||||
],
|
||||
langCode: 'en',
|
||||
activityType: ActivityTypeEnum.messageMeaning,
|
||||
content: ActivityContent(
|
||||
question: 'What is the meaning of the message?',
|
||||
choices: ['How are you?', 'What is your name?'],
|
||||
answers: ['How are you?'],
|
||||
spanDisplayDetails: null,
|
||||
),
|
||||
),
|
||||
);
|
||||
43
lib/pangea/toolbar/widgets/message_meaning_button.dart
Normal file
43
lib/pangea/toolbar/widgets/message_meaning_button.dart
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/toolbar_button.dart';
|
||||
|
||||
class MessageMeaningButton extends StatelessWidget {
|
||||
final MessageOverlayController overlayController;
|
||||
final double buttonSize;
|
||||
|
||||
const MessageMeaningButton({
|
||||
super.key,
|
||||
required this.overlayController,
|
||||
required this.buttonSize,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedCrossFade(
|
||||
crossFadeState: overlayController.isPracticeComplete
|
||||
? CrossFadeState.showSecond
|
||||
: CrossFadeState.showFirst,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
firstChild: ToolbarButton(
|
||||
mode: MessageMode.messageMeaning,
|
||||
overlayController: overlayController,
|
||||
buttonSize: buttonSize,
|
||||
),
|
||||
secondChild: Container(
|
||||
width: buttonSize,
|
||||
height: buttonSize,
|
||||
alignment: Alignment.center,
|
||||
child: Icon(
|
||||
MessageMode.messageMeaning.icon,
|
||||
color: AppConfig.gold,
|
||||
size: buttonSize,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
41
lib/pangea/toolbar/widgets/message_meaning_card.dart
Normal file
41
lib/pangea/toolbar/widgets/message_meaning_card.dart
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
|
||||
|
||||
class MessageMeaningCard extends StatelessWidget {
|
||||
final MessageOverlayController controller;
|
||||
|
||||
const MessageMeaningCard({super.key, required this.controller});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: AppConfig.toolbarMinWidth,
|
||||
maxHeight: AppConfig.toolbarMaxHeight,
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 20, 16, 16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.sports_martial_arts,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
TextButton(
|
||||
onPressed: () => controller.onRequestForMeaningChallenge(),
|
||||
child: Text(L10n.of(context).clickForMeaningActivity),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,9 +3,12 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
|
||||
|
||||
class MessageModeLockedCard extends StatelessWidget {
|
||||
const MessageModeLockedCard({super.key});
|
||||
final MessageOverlayController controller;
|
||||
|
||||
const MessageModeLockedCard({super.key, required this.controller});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
|
||||
|
|
@ -173,6 +175,22 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
}
|
||||
}
|
||||
|
||||
void onRequestForMeaningChallenge() {
|
||||
if (messageAnalyticsEntry == null) {
|
||||
debugger(when: kDebugMode);
|
||||
ErrorHandler.logError(
|
||||
e: "MessageAnalyticsEntry is null in onRequestForMeaningChallenge",
|
||||
data: {},
|
||||
);
|
||||
return;
|
||||
}
|
||||
messageAnalyticsEntry!.addMessageMeaningActivity();
|
||||
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
bool get messageInUserL2 =>
|
||||
pangeaMessageEvent?.messageDisplayLangCode ==
|
||||
MatrixState.pangeaController.languageController.userL2?.langCode;
|
||||
|
|
@ -240,8 +258,12 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
|
||||
/// When an activity is completed, we need to update the state
|
||||
/// and check if the toolbar should be unlocked
|
||||
void onActivityFinish() {
|
||||
void onActivityFinish(ActivityTypeEnum activityType) {
|
||||
messageAnalyticsEntry!.onActivityComplete();
|
||||
if (activityType == ActivityTypeEnum.messageMeaning) {
|
||||
updateToolbarMode(MessageMode.wordZoom);
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
setState(() {});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -172,7 +172,9 @@ class MessageTextWidget extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
|
||||
final hasHiddenTokens = tokenPositions.any((t) => t.hideContent);
|
||||
final hideTokenHighlights = messageAnalyticsEntry != null &&
|
||||
(messageAnalyticsEntry!.hasHiddenWordActivity ||
|
||||
messageAnalyticsEntry!.hasMessageMeaningActivity);
|
||||
|
||||
return RichText(
|
||||
softWrap: softWrap ?? true,
|
||||
|
|
@ -200,7 +202,7 @@ class MessageTextWidget extends StatelessWidget {
|
|||
.toString();
|
||||
|
||||
Color backgroundColor = Colors.transparent;
|
||||
if (!hasHiddenTokens) {
|
||||
if (!hideTokenHighlights) {
|
||||
if (tokenPosition.selected) {
|
||||
backgroundColor = AppConfig.primaryColor.withAlpha(80);
|
||||
} else if (isSelected != null && shouldDo) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dar
|
|||
import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_audio_card.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_meaning_card.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';
|
||||
|
|
@ -44,8 +45,10 @@ class MessageToolbar extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
|
||||
if (overlayController.messageAnalyticsEntry?.hasHiddenWordActivity ??
|
||||
false) {
|
||||
if ((overlayController.messageAnalyticsEntry?.hasHiddenWordActivity ??
|
||||
false) ||
|
||||
(overlayController.messageAnalyticsEntry?.hasMessageMeaningActivity ??
|
||||
false)) {
|
||||
return PracticeActivityCard(
|
||||
pangeaMessageEvent: pangeaMessageEvent,
|
||||
overlayController: overlayController,
|
||||
|
|
@ -64,7 +67,7 @@ class MessageToolbar extends StatelessWidget {
|
|||
);
|
||||
|
||||
if (!unlocked) {
|
||||
return const MessageModeLockedCard();
|
||||
return MessageModeLockedCard(controller: overlayController);
|
||||
}
|
||||
|
||||
switch (overlayController.toolbarMode) {
|
||||
|
|
@ -93,6 +96,8 @@ class MessageToolbar extends StatelessWidget {
|
|||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
case MessageMode.messageMeaning:
|
||||
return MessageMeaningCard(controller: overlayController);
|
||||
case MessageMode.practiceActivity:
|
||||
case MessageMode.wordZoom:
|
||||
if (overlayController.selectedToken == null) {
|
||||
|
|
|
|||
|
|
@ -256,7 +256,9 @@ class MultipleChoiceActivityState extends State<MultipleChoiceActivity> {
|
|||
],
|
||||
);
|
||||
|
||||
return practiceActivity.activityType == ActivityTypeEnum.hiddenWordListening
|
||||
return practiceActivity.activityType ==
|
||||
ActivityTypeEnum.hiddenWordListening ||
|
||||
practiceActivity.activityType == ActivityTypeEnum.messageMeaning
|
||||
? ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
// see https://github.com/pangeachat/client/issues/1422
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ class PracticeActivityCardState extends State<PracticeActivityCard> {
|
|||
ActivityQualityFeedback? activityFeedback,
|
||||
}) async {
|
||||
_error = null;
|
||||
debugger(when: kDebugMode);
|
||||
if (!mounted ||
|
||||
!pangeaController.languageController.languagesSet ||
|
||||
widget.overlayController.messageAnalyticsEntry == null) {
|
||||
|
|
@ -152,6 +153,7 @@ class PracticeActivityCardState extends State<PracticeActivityCard> {
|
|||
Future<PracticeActivityModel?> _fetchActivityModel({
|
||||
ActivityQualityFeedback? activityFeedback,
|
||||
}) async {
|
||||
debugger(when: kDebugMode);
|
||||
debugPrint(
|
||||
"fetching activity model of type: ${widget.targetTokensAndActivityType.activityType}",
|
||||
);
|
||||
|
|
@ -215,6 +217,8 @@ class PracticeActivityCardState extends State<PracticeActivityCard> {
|
|||
context,
|
||||
);
|
||||
|
||||
debugger(when: kDebugMode);
|
||||
|
||||
if (activityResponse.activity == null) return null;
|
||||
|
||||
currentActivityCompleter = activityResponse.eventCompleter;
|
||||
|
|
@ -274,7 +278,7 @@ class PracticeActivityCardState extends State<PracticeActivityCard> {
|
|||
|
||||
// wait for savor the joy before popping from the activity queue
|
||||
// to keep the completed activity on screen for a moment
|
||||
widget.overlayController.onActivityFinish();
|
||||
widget.overlayController.onActivityFinish(currentActivity!.activityType);
|
||||
} catch (e, s) {
|
||||
_onError();
|
||||
debugger(when: kDebugMode);
|
||||
|
|
@ -309,6 +313,7 @@ class PracticeActivityCardState extends State<PracticeActivityCard> {
|
|||
case ActivityTypeEnum.lemmaId:
|
||||
case ActivityTypeEnum.emoji:
|
||||
case ActivityTypeEnum.morphId:
|
||||
case ActivityTypeEnum.messageMeaning:
|
||||
final selectedChoice =
|
||||
currentActivity?.activityType == ActivityTypeEnum.emoji &&
|
||||
(currentActivity?.targetTokens?.isNotEmpty ?? false)
|
||||
|
|
@ -330,6 +335,7 @@ class PracticeActivityCardState extends State<PracticeActivityCard> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_error != null) {
|
||||
debugger(when: kDebugMode);
|
||||
return CardErrorWidget(
|
||||
error: _error!,
|
||||
maxWidth: 500,
|
||||
|
|
|
|||
|
|
@ -33,32 +33,26 @@ class ToolbarButton extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return Tooltip(
|
||||
message: mode.tooltip(context),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
PressableButton(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
depressed: mode == overlayController.toolbarMode,
|
||||
child: PressableButton(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
depressed: mode == overlayController.toolbarMode,
|
||||
color: color(context),
|
||||
onPressed: () => overlayController.updateToolbarMode(mode),
|
||||
playSound: true,
|
||||
child: AnimatedContainer(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
height: buttonSize,
|
||||
width: buttonSize,
|
||||
decoration: BoxDecoration(
|
||||
color: color(context),
|
||||
onPressed: () => overlayController.updateToolbarMode(mode),
|
||||
playSound: true,
|
||||
child: AnimatedContainer(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
height: buttonSize,
|
||||
width: buttonSize,
|
||||
decoration: BoxDecoration(
|
||||
color: color(context),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
mode.icon,
|
||||
size: 20,
|
||||
color:
|
||||
mode == overlayController.toolbarMode ? Colors.white : null,
|
||||
),
|
||||
),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
],
|
||||
child: Icon(
|
||||
mode.icon,
|
||||
size: 20,
|
||||
color: mode == overlayController.toolbarMode ? Colors.white : null,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import 'package:matrix/matrix.dart';
|
|||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_meaning_button.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/toolbar_button.dart';
|
||||
|
||||
|
|
@ -23,10 +24,6 @@ class ToolbarButtonAndProgressRow extends StatelessWidget {
|
|||
double? get proportionOfActivitiesCompleted =>
|
||||
overlayController.pangeaMessageEvent?.proportionOfActivitiesCompleted;
|
||||
|
||||
List<MessageMode> get modes => MessageMode.values
|
||||
.where((mode) => mode.shouldShowAsToolbarButton(event))
|
||||
.toList();
|
||||
|
||||
static const double iconWidth = 36.0;
|
||||
static const double buttonSize = 40.0;
|
||||
static const double totalRowWidth = 250.0;
|
||||
|
|
@ -77,11 +74,14 @@ class ToolbarButtonAndProgressRow extends StatelessWidget {
|
|||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
MessageMeaningButton(
|
||||
buttonSize: buttonSize,
|
||||
overlayController: overlayController,
|
||||
),
|
||||
SizedBox(
|
||||
width: MessageMode.textToSpeech.pointOnBar * totalRowWidth -
|
||||
buttonSize / 2,
|
||||
buttonSize,
|
||||
),
|
||||
ToolbarButton(
|
||||
mode: MessageMode.textToSpeech,
|
||||
|
|
@ -91,7 +91,7 @@ class ToolbarButtonAndProgressRow extends StatelessWidget {
|
|||
SizedBox(
|
||||
width: MessageMode.translation.pointOnBar * totalRowWidth -
|
||||
MessageMode.textToSpeech.pointOnBar * totalRowWidth -
|
||||
buttonSize / 2 -
|
||||
buttonSize -
|
||||
buttonSize,
|
||||
),
|
||||
ToolbarButton(
|
||||
|
|
|
|||
64
pubspec.lock
64
pubspec.lock
|
|
@ -153,6 +153,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.0"
|
||||
base58check:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: base58check
|
||||
sha256: "6c300dfc33e598d2fe26319e13f6243fea81eaf8204cb4c6b69ef20a625319a5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
blurhash_dart:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -185,6 +193,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.3"
|
||||
canonical_json:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: canonical_json
|
||||
sha256: d6be1dd66b420c6ac9f42e3693e09edf4ff6edfee26cb4c28c1c019fdb8c0c15
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
characters:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -417,6 +433,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.9"
|
||||
enhanced_enum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: enhanced_enum
|
||||
sha256: "074c5a8b9664799ca91e1e8b68003b8694cb19998671cbafd9c7779c13fcdecf"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.4"
|
||||
equatable:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1095,6 +1119,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.4"
|
||||
html_unescape:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: html_unescape
|
||||
sha256: "15362d7a18f19d7b742ef8dcb811f5fd2a2df98db9f80ea393c075189e0b61e3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -1493,6 +1525,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
olm:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: olm
|
||||
sha256: "3306bf534ceb914fd148b3b4a3d603fb5e067b2e6da8304025b47c24cfdf6b46"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.4"
|
||||
open_file:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -1845,6 +1885,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
random_string:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: random_string
|
||||
sha256: "03b52435aae8cbdd1056cf91bfc5bf845e9706724dd35ae2e99fa14a1ef79d02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
receive_sharing_intent:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -1957,6 +2005,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
sdp_transform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sdp_transform
|
||||
sha256: "73e412a5279a5c2de74001535208e20fff88f225c9a4571af0f7146202755e45"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.2"
|
||||
sentry:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -2434,6 +2490,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
unorm_dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: unorm_dart
|
||||
sha256: "5b35bff83fce4d76467641438f9e867dc9bcfdb8c1694854f230579d68cd8f4b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
url_launcher:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue