feat: allow users to edit lemmas (#1694)
This commit is contained in:
parent
b0149ecc26
commit
75a0d1e07b
19 changed files with 385 additions and 403 deletions
|
|
@ -4814,5 +4814,6 @@
|
|||
"appWantsToUseForLoginDescription": "You hereby allow the app and website to share information about you.",
|
||||
"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*."
|
||||
"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?"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1772,7 +1772,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
overlayEntry = MessageSelectionOverlay(
|
||||
chatController: this,
|
||||
event: event,
|
||||
pangeaMessageEvent: pangeaMessageEvent,
|
||||
timeline: timeline!,
|
||||
initialSelectedToken: selectedToken,
|
||||
nextEvent: nextEvent,
|
||||
prevEvent: prevEvent,
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_identifier.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_level_enum.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
|
||||
import 'package:fluffychat/pangea/morphs/default_morph_mapping.dart';
|
||||
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_icon.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_models.dart';
|
||||
import 'package:fluffychat/pangea/user/client_extension.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import '../morphs/morph_repo.dart';
|
||||
|
||||
class MorphAnalyticsView extends StatelessWidget {
|
||||
|
|
@ -19,33 +20,49 @@ class MorphAnalyticsView extends StatelessWidget {
|
|||
super.key,
|
||||
});
|
||||
|
||||
List<MorphFeature> get availableFeatures => MorphsRepo.get().displayFeatures;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||
child: ListView.builder(
|
||||
itemCount: availableFeatures.length,
|
||||
itemBuilder: (context, index) =>
|
||||
availableFeatures[index].displayTags.isNotEmpty
|
||||
? MorphFeatureBox(
|
||||
morphFeature: availableFeatures[index].feature,
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
child: FutureBuilder(
|
||||
future: MorphsRepo.get(),
|
||||
builder: (context, snapshot) {
|
||||
final morphs = snapshot.data ?? defaultMorphMapping;
|
||||
|
||||
return snapshot.connectionState == ConnectionState.done
|
||||
? ListView.builder(
|
||||
itemCount: morphs.displayFeatures.length,
|
||||
itemBuilder: (context, index) => morphs
|
||||
.displayFeatures[index].displayTags.isNotEmpty
|
||||
? MorphFeatureBox(
|
||||
morphFeature: morphs.displayFeatures[index].feature,
|
||||
allTags: snapshot.data
|
||||
?.getDisplayTags(
|
||||
morphs.displayFeatures[index].feature,
|
||||
)
|
||||
.map((tag) => tag.toLowerCase())
|
||||
.toSet() ??
|
||||
{},
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
)
|
||||
: const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class MorphFeatureBox extends StatelessWidget {
|
||||
final String morphFeature;
|
||||
final Set<String> allTags;
|
||||
|
||||
const MorphFeatureBox({
|
||||
super.key,
|
||||
required this.morphFeature,
|
||||
required this.allTags,
|
||||
});
|
||||
|
||||
// get constructData => MatrixState.pangeaController.
|
||||
|
||||
String _categoryCopy(
|
||||
String category,
|
||||
BuildContext context,
|
||||
|
|
@ -61,11 +78,6 @@ class MorphFeatureBox extends StatelessWidget {
|
|||
category;
|
||||
}
|
||||
|
||||
Set<String> get allTags => MorphsRepo.get()
|
||||
.getDisplayTags(morphFeature)
|
||||
.map((tag) => tag.toLowerCase())
|
||||
.toSet();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
|
@ -133,7 +145,9 @@ class MorphFeatureBox extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
)
|
||||
.sortedBy<num>((chip) => chip.constructAnalytics.points)
|
||||
.sortedBy<num>(
|
||||
(chip) => chip.constructAnalytics.points,
|
||||
)
|
||||
.reversed
|
||||
.toList(),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import 'package:matrix/matrix.dart';
|
|||
import 'package:fluffychat/pangea/analytics_misc/construct_identifier.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/morphs/default_morph_mapping.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_models.dart';
|
||||
import '../morphs/morph_repo.dart';
|
||||
import 'construct_type_enum.dart';
|
||||
|
||||
class ConstructAnalyticsModel {
|
||||
|
|
@ -155,7 +155,7 @@ class OneConstructUse {
|
|||
return category ?? "Other";
|
||||
}
|
||||
|
||||
final MorphFeatuuresAndTags morphs = MorphsRepo.get();
|
||||
final MorphFeaturesAndTags morphs = defaultMorphMapping;
|
||||
|
||||
if (categoryEntry == null) {
|
||||
return morphs.guessMorphCategory(json["lemma"]);
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ class ModelKey {
|
|||
/// something built in to matrix? should talk about this
|
||||
static const String messageTags = "p.tag";
|
||||
static const String messageTagMorphEdit = "morph_edit";
|
||||
static const String messageTagLemmaEdit = "lemma_edit";
|
||||
static const String messageTagActivityPlan = "activity_plan";
|
||||
|
||||
static const String baseDefinition = "base_definition";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/// Represents a lemma object
|
||||
class Lemma {
|
||||
/// [text] ex "ir" - text of the lemma of the word
|
||||
final String text;
|
||||
String text;
|
||||
|
||||
/// [form] ex "vamos" - conjugated form of the lemma and as it appeared in some original text
|
||||
final String form;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:fluffychat/pangea/morphs/morph_models.dart';
|
||||
|
||||
final MorphFeatuuresAndTags defaultMorphMapping =
|
||||
MorphFeatuuresAndTags.fromJson({
|
||||
final MorphFeaturesAndTags defaultMorphMapping = MorphFeaturesAndTags.fromJson({
|
||||
"language_code": "default",
|
||||
"features": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:fluffychat/pangea/morphs/morph_models.dart';
|
||||
|
||||
final MorphFeatuuresAndTags defaultUDMapping = MorphFeatuuresAndTags.fromJson({
|
||||
final MorphFeaturesAndTags defaultUDMapping = MorphFeaturesAndTags.fromJson({
|
||||
"language_code": "default",
|
||||
"features": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,232 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_identifier.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_level_enum.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
|
||||
import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/full_width_dialog.dart';
|
||||
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_icon.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_models.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../../morphs/morph_repo.dart';
|
||||
|
||||
class MorphAnalyticsPopup extends StatelessWidget {
|
||||
const MorphAnalyticsPopup({
|
||||
super.key,
|
||||
});
|
||||
|
||||
List<MorphFeature> get availableFeatures => MorphsRepo.get().displayFeatures;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => FullWidthDialog(
|
||||
dialogContent: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(ConstructTypeEnum.morph.indicator.tooltip(context)),
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: Navigator.of(context).pop,
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||
child: ListView.builder(
|
||||
itemCount: availableFeatures.length,
|
||||
itemBuilder: (context, index) =>
|
||||
availableFeatures[index].displayTags.isNotEmpty
|
||||
? MorphFeatureBox(
|
||||
morphFeature: availableFeatures[index].feature,
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
),
|
||||
maxWidth: 600,
|
||||
maxHeight: 800,
|
||||
);
|
||||
}
|
||||
|
||||
class MorphFeatureBox extends StatelessWidget {
|
||||
final String morphFeature;
|
||||
|
||||
const MorphFeatureBox({
|
||||
super.key,
|
||||
required this.morphFeature,
|
||||
});
|
||||
|
||||
// get constructData => MatrixState.pangeaController.
|
||||
|
||||
String _categoryCopy(
|
||||
String category,
|
||||
BuildContext context,
|
||||
) {
|
||||
if (category.toLowerCase() == "other") {
|
||||
return L10n.of(context).other;
|
||||
}
|
||||
|
||||
return ConstructTypeEnum.morph.getDisplayCopy(
|
||||
category,
|
||||
context,
|
||||
) ??
|
||||
category;
|
||||
}
|
||||
|
||||
Set<String> get allTags => MorphsRepo.get()
|
||||
.getDisplayTags(morphFeature)
|
||||
.map((tag) => tag.toLowerCase())
|
||||
.toSet();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16.0),
|
||||
border: Border.all(color: AppConfig.gold.withAlpha(100), width: 2),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 16.0,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 30.0,
|
||||
width: 30.0,
|
||||
child: MorphIcon(morphFeature: morphFeature, morphTag: null),
|
||||
),
|
||||
Text(
|
||||
_categoryCopy(morphFeature, context),
|
||||
style: theme.textTheme.bodyLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
spacing: 16.0,
|
||||
runSpacing: 16.0,
|
||||
children: allTags
|
||||
.map(
|
||||
(morphTag) => MorphTagChip(
|
||||
morphFeature: morphFeature,
|
||||
morphTag: morphTag,
|
||||
constructAnalytics: MatrixState.pangeaController
|
||||
.getAnalytics.constructListModel
|
||||
.getConstructUses(
|
||||
ConstructIdentifier(
|
||||
lemma: morphTag,
|
||||
type: ConstructTypeEnum.morph,
|
||||
category: morphFeature,
|
||||
),
|
||||
) ??
|
||||
ConstructUses(
|
||||
lemma: morphTag,
|
||||
constructType: ConstructTypeEnum.morph,
|
||||
category: morphFeature,
|
||||
uses: [],
|
||||
),
|
||||
),
|
||||
)
|
||||
.sortedBy<num>((chip) => chip.constructAnalytics.points)
|
||||
.reversed
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MorphTagChip extends StatelessWidget {
|
||||
final String morphFeature;
|
||||
final String morphTag;
|
||||
final ConstructUses constructAnalytics;
|
||||
|
||||
const MorphTagChip({
|
||||
super.key,
|
||||
required this.morphFeature,
|
||||
required this.morphTag,
|
||||
required this.constructAnalytics,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return Opacity(
|
||||
opacity: constructAnalytics.points > 0 ? 1.0 : 0.3,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(32.0),
|
||||
gradient: constructAnalytics.points > 0
|
||||
? LinearGradient(
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
colors: <Color>[
|
||||
Colors.transparent,
|
||||
constructAnalytics.lemmaCategory.color,
|
||||
],
|
||||
)
|
||||
: null,
|
||||
color: constructAnalytics.points > 0 ? null : theme.disabledColor,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 4.0,
|
||||
horizontal: 8.0,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 8.0,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 28.0,
|
||||
height: 28.0,
|
||||
child: constructAnalytics.points > 0
|
||||
? MorphIcon(
|
||||
morphFeature: morphFeature,
|
||||
morphTag: morphTag,
|
||||
)
|
||||
: const Icon(
|
||||
Icons.lock,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
getGrammarCopy(
|
||||
category: morphFeature,
|
||||
lemma: morphTag,
|
||||
context: context,
|
||||
) ??
|
||||
morphTag,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: theme.brightness == Brightness.dark
|
||||
? Colors.white
|
||||
: Colors.black,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -30,14 +30,14 @@ class MorphFeature {
|
|||
}
|
||||
}
|
||||
|
||||
class MorphFeatuuresAndTags {
|
||||
class MorphFeaturesAndTags {
|
||||
final String languageCode;
|
||||
final List<MorphFeature> features;
|
||||
|
||||
MorphFeatuuresAndTags({required this.languageCode, required this.features});
|
||||
MorphFeaturesAndTags({required this.languageCode, required this.features});
|
||||
|
||||
factory MorphFeatuuresAndTags.fromJson(Map<String, dynamic> json) {
|
||||
return MorphFeatuuresAndTags(
|
||||
factory MorphFeaturesAndTags.fromJson(Map<String, dynamic> json) {
|
||||
return MorphFeaturesAndTags(
|
||||
languageCode: json['language_code'],
|
||||
features: List<MorphFeature>.from(
|
||||
json['features'].map((x) => MorphFeature.fromJson(x)),
|
||||
|
|
@ -63,7 +63,9 @@ class MorphFeatuuresAndTags {
|
|||
/// i.e. minus punc, space, x, etc
|
||||
List<String> getDisplayTags(String feature) =>
|
||||
features
|
||||
.firstWhereOrNull((element) => element.feature == feature)
|
||||
.firstWhereOrNull(
|
||||
(element) => element.feature.toLowerCase() == feature.toLowerCase(),
|
||||
)
|
||||
?.displayTags ??
|
||||
[];
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import '../common/network/requests.dart';
|
|||
|
||||
class _APICallCacheItem {
|
||||
final DateTime time;
|
||||
final Future<MorphFeatuuresAndTags> future;
|
||||
final Future<MorphFeaturesAndTags> future;
|
||||
|
||||
_APICallCacheItem(this.time, this.future);
|
||||
}
|
||||
|
|
@ -30,18 +30,18 @@ class MorphsRepo {
|
|||
static final shortTermCache = <String, _APICallCacheItem>{};
|
||||
static const int _cacheDurationMinutes = 1;
|
||||
|
||||
static void set(String languageCode, MorphFeatuuresAndTags response) {
|
||||
static void set(String languageCode, MorphFeaturesAndTags response) {
|
||||
_morphsStorage.write(
|
||||
languageCode,
|
||||
response.toJson(),
|
||||
);
|
||||
}
|
||||
|
||||
static MorphFeatuuresAndTags fromJson(Map<String, dynamic> json) {
|
||||
return MorphFeatuuresAndTags.fromJson(json);
|
||||
static MorphFeaturesAndTags fromJson(Map<String, dynamic> json) {
|
||||
return MorphFeaturesAndTags.fromJson(json);
|
||||
}
|
||||
|
||||
static Future<MorphFeatuuresAndTags> _fetch(String languageCode) async {
|
||||
static Future<MorphFeaturesAndTags> _fetch(String languageCode) async {
|
||||
try {
|
||||
final Requests req = Requests(
|
||||
choreoApiKey: Environment.choreoApiKey,
|
||||
|
|
@ -76,7 +76,7 @@ class MorphsRepo {
|
|||
/// if the morphs are not yet fetched. we'll see if this works well
|
||||
/// if not, we can make it async and update uses of this function
|
||||
/// to be async as well
|
||||
static MorphFeatuuresAndTags get([String? languageCode]) {
|
||||
static Future<MorphFeaturesAndTags> get([String? languageCode]) async {
|
||||
languageCode ??=
|
||||
MatrixState.pangeaController.languageController.userL2?.langCode;
|
||||
|
||||
|
|
@ -95,7 +95,7 @@ class MorphsRepo {
|
|||
if (cachedCall != null) {
|
||||
if (DateTime.now().difference(cachedCall.time).inMinutes <
|
||||
_cacheDurationMinutes) {
|
||||
return defaultMorphMapping;
|
||||
return cachedCall.future;
|
||||
} else {
|
||||
shortTermCache.remove(languageCode);
|
||||
}
|
||||
|
|
@ -104,7 +104,6 @@ class MorphsRepo {
|
|||
// fetch the morphs but don't wait for it
|
||||
final future = _fetch(languageCode);
|
||||
shortTermCache[languageCode] = _APICallCacheItem(DateTime.now(), future);
|
||||
|
||||
return defaultMorphMapping;
|
||||
return future;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,22 +24,22 @@ class MessageSelectionOverlay extends StatefulWidget {
|
|||
final Event _event;
|
||||
final Event? _nextEvent;
|
||||
final Event? _prevEvent;
|
||||
final PangeaMessageEvent? _pangeaMessageEvent;
|
||||
final PangeaToken? _initialSelectedToken;
|
||||
final Timeline _timeline;
|
||||
|
||||
const MessageSelectionOverlay({
|
||||
required this.chatController,
|
||||
required Event event,
|
||||
required PangeaMessageEvent? pangeaMessageEvent,
|
||||
required PangeaToken? initialSelectedToken,
|
||||
required Event? nextEvent,
|
||||
required Event? prevEvent,
|
||||
required Timeline timeline,
|
||||
super.key,
|
||||
}) : _initialSelectedToken = initialSelectedToken,
|
||||
_pangeaMessageEvent = pangeaMessageEvent,
|
||||
_nextEvent = nextEvent,
|
||||
_prevEvent = prevEvent,
|
||||
_event = event;
|
||||
_event = event,
|
||||
_timeline = timeline;
|
||||
|
||||
@override
|
||||
MessageOverlayController createState() => MessageOverlayController();
|
||||
|
|
@ -54,7 +54,11 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
List<PangeaToken>? tokens;
|
||||
bool initialized = false;
|
||||
|
||||
PangeaMessageEvent? get pangeaMessageEvent => widget._pangeaMessageEvent;
|
||||
PangeaMessageEvent? get pangeaMessageEvent => PangeaMessageEvent(
|
||||
event: widget._event,
|
||||
timeline: widget._timeline,
|
||||
ownMessage: widget._event.room.client.userID == widget._event.senderId,
|
||||
);
|
||||
|
||||
bool isPlayingAudio = false;
|
||||
|
||||
|
|
@ -94,7 +98,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initializeTokensAndMode();
|
||||
initializeTokensAndMode();
|
||||
}
|
||||
|
||||
void _updateSelectedSpan(PangeaTokenText selectedSpan) {
|
||||
|
|
@ -104,7 +108,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
widget.chatController.choreographer.tts.tryToSpeak(
|
||||
selectedSpan.content,
|
||||
context,
|
||||
widget._pangeaMessageEvent?.eventId,
|
||||
pangeaMessageEvent?.eventId,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -144,7 +148,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
)
|
||||
: null;
|
||||
|
||||
Future<void> _initializeTokensAndMode() async {
|
||||
Future<void> initializeTokensAndMode() async {
|
||||
try {
|
||||
final repEvent = pangeaMessageEvent?.messageDisplayRepresentation;
|
||||
if (repEvent != null) {
|
||||
|
|
@ -174,7 +178,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
MatrixState.pangeaController.languageController.userL2?.langCode;
|
||||
|
||||
Future<void> _setInitialToolbarMode() async {
|
||||
if (widget._pangeaMessageEvent?.isAudioMessage ?? false) {
|
||||
if (pangeaMessageEvent?.isAudioMessage ?? false) {
|
||||
toolbarMode = MessageMode.speechToText;
|
||||
return setState(() {});
|
||||
}
|
||||
|
|
@ -269,11 +273,10 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
/// If there is a selectedSpan, then the target is the selected text
|
||||
String get targetText {
|
||||
if (_selectedSpan == null || pangeaMessageEvent == null) {
|
||||
return widget._pangeaMessageEvent?.messageDisplayText ??
|
||||
widget._event.body;
|
||||
return pangeaMessageEvent?.messageDisplayText ?? widget._event.body;
|
||||
}
|
||||
|
||||
return widget._pangeaMessageEvent!.messageDisplayText.substring(
|
||||
return pangeaMessageEvent!.messageDisplayText.substring(
|
||||
_selectedSpan!.offset,
|
||||
_selectedSpan!.offset + _selectedSpan!.length,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,42 +1,194 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_level_enum.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/customized_svg.dart';
|
||||
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
class LemmaWidget extends StatelessWidget {
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_level_enum.dart';
|
||||
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/customized_svg.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/events/models/tokens_event_content_model.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
|
||||
class LemmaWidget extends StatefulWidget {
|
||||
final PangeaToken token;
|
||||
final PangeaMessageEvent pangeaMessageEvent;
|
||||
final VoidCallback onEdit;
|
||||
final VoidCallback onEditDone;
|
||||
|
||||
const LemmaWidget({
|
||||
super.key,
|
||||
required this.token,
|
||||
required this.pangeaMessageEvent,
|
||||
required this.onEdit,
|
||||
required this.onEditDone,
|
||||
});
|
||||
|
||||
@override
|
||||
LemmaWidgetState createState() => LemmaWidgetState();
|
||||
}
|
||||
|
||||
class LemmaWidgetState extends State<LemmaWidget> {
|
||||
bool _editMode = false;
|
||||
late TextEditingController _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = TextEditingController();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _toggleEditMode(bool value) {
|
||||
value ? widget.onEdit() : widget.onEditDone();
|
||||
setState(() => _editMode = value);
|
||||
}
|
||||
|
||||
Future<void> _editLemma() async {
|
||||
try {
|
||||
final existingTokens = widget.pangeaMessageEvent.originalSent!.tokens!
|
||||
.map((token) => PangeaToken.fromJson(token.toJson()))
|
||||
.toList();
|
||||
|
||||
// change the morphological tag in the selected token
|
||||
final tokenIndex = existingTokens.indexWhere(
|
||||
(token) => token.text.offset == widget.token.text.offset,
|
||||
);
|
||||
|
||||
if (tokenIndex == -1) {
|
||||
throw Exception("Token not found in message");
|
||||
}
|
||||
|
||||
existingTokens[tokenIndex].lemma.text = _controller.text;
|
||||
await widget.pangeaMessageEvent.room.pangeaSendTextEvent(
|
||||
widget.pangeaMessageEvent.messageDisplayText,
|
||||
editEventId: widget.pangeaMessageEvent.eventId,
|
||||
originalSent: widget.pangeaMessageEvent.originalSent?.content,
|
||||
originalWritten: widget.pangeaMessageEvent.originalWritten?.content,
|
||||
tokensSent: PangeaMessageTokens(tokens: existingTokens),
|
||||
tokensWritten: widget.pangeaMessageEvent.originalWritten?.tokens != null
|
||||
? PangeaMessageTokens(
|
||||
tokens: widget.pangeaMessageEvent.originalWritten!.tokens!,
|
||||
)
|
||||
: null,
|
||||
choreo: widget.pangeaMessageEvent.originalSent?.choreo,
|
||||
messageTag: ModelKey.messageTagLemmaEdit,
|
||||
);
|
||||
|
||||
_toggleEditMode(false);
|
||||
} catch (e) {
|
||||
SnackBar(
|
||||
content: Text(L10n.of(context).oopsSomethingWentWrong),
|
||||
);
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
data: {
|
||||
"token": widget.token.toJson(),
|
||||
"pangeaMessageEvent": widget.pangeaMessageEvent.event.content,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 180),
|
||||
child: Text(
|
||||
token.lemma.text,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
if (_editMode) {
|
||||
_controller.text = widget.token.lemma.text;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
spacing: 10.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
"${L10n.of(context).pangeaBotIsFallible} ${L10n.of(context).whatIsLemma}",
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(fontStyle: FontStyle.italic),
|
||||
),
|
||||
TextField(
|
||||
minLines: 1,
|
||||
maxLines: 3,
|
||||
controller: _controller,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () => _toggleEditMode(false),
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
),
|
||||
child: Text(L10n.of(context).cancel),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
_controller.text != widget.token.lemma.text
|
||||
? showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () async => _editLemma(),
|
||||
)
|
||||
: null;
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
),
|
||||
child: Text(L10n.of(context).saveChanges),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Flexible(
|
||||
child: Tooltip(
|
||||
triggerMode: TooltipTriggerMode.tap,
|
||||
message: L10n.of(context).doubleClickToEdit,
|
||||
child: GestureDetector(
|
||||
onLongPress: () => _toggleEditMode(true),
|
||||
onDoubleTap: () => _toggleEditMode(true),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 180),
|
||||
child: Text(
|
||||
widget.token.lemma.text,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CustomizedSvg(
|
||||
svgUrl: widget.token.lemmaXPCategory.svgURL,
|
||||
colorReplacements: const {},
|
||||
errorIcon: Text(widget.token.xpEmoji),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CustomizedSvg(
|
||||
svgUrl: token.lemmaXPCategory.svgURL,
|
||||
colorReplacements: const {},
|
||||
errorIcon: Text(token.xpEmoji),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dar
|
|||
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
|
||||
import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/morphs/default_morph_mapping.dart';
|
||||
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_categories_enum.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_repo.dart';
|
||||
|
|
@ -23,11 +24,14 @@ class MorphologicalCenterWidget extends StatefulWidget {
|
|||
final PangeaMessageEvent pangeaMessageEvent;
|
||||
final MessageOverlayController overlayController;
|
||||
|
||||
final VoidCallback onEditDone;
|
||||
|
||||
const MorphologicalCenterWidget({
|
||||
required this.token,
|
||||
required this.morphFeature,
|
||||
required this.pangeaMessageEvent,
|
||||
required this.overlayController,
|
||||
required this.onEditDone,
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
|
@ -118,9 +122,8 @@ class MorphologicalCenterWidgetState extends State<MorphologicalCenterWidget> {
|
|||
messageTag: ModelKey.messageTagMorphEdit,
|
||||
);
|
||||
|
||||
setState(() {
|
||||
editMode = false;
|
||||
});
|
||||
setState(() => editMode = false);
|
||||
widget.onEditDone();
|
||||
} catch (e) {
|
||||
SnackBar(
|
||||
content: Text(L10n.of(context).oopsSomethingWentWrong),
|
||||
|
|
@ -137,11 +140,6 @@ class MorphologicalCenterWidgetState extends State<MorphologicalCenterWidget> {
|
|||
}
|
||||
}
|
||||
|
||||
/// all morphological tags for the selected morphological category
|
||||
/// that are eligible for setting as the morphological tag
|
||||
List<String> get allMorphTagsForEdit =>
|
||||
MorphsRepo.get().getDisplayTags(widget.morphFeature);
|
||||
|
||||
String get morphCopy =>
|
||||
getMorphologicalCategoryCopy(widget.morphFeature, context) ??
|
||||
widget.morphFeature;
|
||||
|
|
@ -194,57 +192,75 @@ class MorphologicalCenterWidgetState extends State<MorphologicalCenterWidget> {
|
|||
scrollDirection: Axis.vertical,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
children: allMorphTagsForEdit.map((tag) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.all(2),
|
||||
padding: EdgeInsets.zero,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
const BorderRadius.all(Radius.circular(10)),
|
||||
border: Border.all(
|
||||
color: selectedMorphTag == tag
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Colors.transparent,
|
||||
style: BorderStyle.solid,
|
||||
width: 2.0,
|
||||
),
|
||||
),
|
||||
child: TextButton(
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStateProperty.all(
|
||||
const EdgeInsets.symmetric(horizontal: 7),
|
||||
),
|
||||
backgroundColor: selectedMorphTag == tag
|
||||
? WidgetStateProperty.all<Color>(
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.primary
|
||||
.withAlpha(50),
|
||||
)
|
||||
: null,
|
||||
shape: WidgetStateProperty.all(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() => selectedMorphTag = tag);
|
||||
},
|
||||
child: Text(
|
||||
getGrammarCopy(
|
||||
category: widget.morphFeature,
|
||||
lemma: tag,
|
||||
context: context,
|
||||
) ??
|
||||
tag,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
child: FutureBuilder(
|
||||
future: MorphsRepo.get(),
|
||||
builder: (context, snapshot) {
|
||||
final allMorphTagsForEdit =
|
||||
snapshot.data?.getDisplayTags(widget.morphFeature) ??
|
||||
defaultMorphMapping
|
||||
.getDisplayTags(widget.morphFeature);
|
||||
|
||||
return snapshot.connectionState == ConnectionState.done
|
||||
? Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
children: allMorphTagsForEdit.map((tag) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.all(2),
|
||||
padding: EdgeInsets.zero,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(10),
|
||||
),
|
||||
border: Border.all(
|
||||
color: selectedMorphTag == tag
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.primary
|
||||
: Colors.transparent,
|
||||
style: BorderStyle.solid,
|
||||
width: 2.0,
|
||||
),
|
||||
),
|
||||
child: TextButton(
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStateProperty.all(
|
||||
const EdgeInsets.symmetric(
|
||||
horizontal: 7,
|
||||
),
|
||||
),
|
||||
backgroundColor: selectedMorphTag == tag
|
||||
? WidgetStateProperty.all<Color>(
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.primary
|
||||
.withAlpha(50),
|
||||
)
|
||||
: null,
|
||||
shape: WidgetStateProperty.all(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() => selectedMorphTag = tag);
|
||||
},
|
||||
child: Text(
|
||||
getGrammarCopy(
|
||||
category: widget.morphFeature,
|
||||
lemma: tag,
|
||||
context: context,
|
||||
) ??
|
||||
tag,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
)
|
||||
: const Center(child: CircularProgressIndicator());
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ class WordZoomCenterWidget extends StatelessWidget {
|
|||
morphFeature: selectedMorphFeature!,
|
||||
pangeaMessageEvent: wordDetailsController.widget.messageEvent,
|
||||
overlayController: overlayController,
|
||||
onEditDone: wordDetailsController.onEditDone,
|
||||
);
|
||||
case WordZoomSelection.lemma:
|
||||
return Text(token.lemma.text, textAlign: TextAlign.center);
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ class WordZoomWidgetState extends State<WordZoomWidget> {
|
|||
// is computationally expensive, so we only do it once
|
||||
bool _canGenerateLemmaActivity = false;
|
||||
|
||||
bool _hideCenterContent = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
|
@ -154,6 +156,10 @@ class WordZoomWidgetState extends State<WordZoomWidget> {
|
|||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
void _setHideCenterContent(bool value) {
|
||||
if (mounted) setState(() => _hideCenterContent = value);
|
||||
}
|
||||
|
||||
/// This function should be called before overlayController.onActivityFinish to
|
||||
/// prevent shouldDoActivity being set to false before _forceShowActivity is set to true.
|
||||
/// This keep the completed actvity visible to the user for a short time.
|
||||
|
|
@ -184,6 +190,8 @@ class WordZoomWidgetState extends State<WordZoomWidget> {
|
|||
: true;
|
||||
}
|
||||
|
||||
void onEditDone() => widget.overlayController.initializeTokensAndMode();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ConstrainedBox(
|
||||
|
|
@ -220,37 +228,49 @@ class WordZoomWidgetState extends State<WordZoomWidget> {
|
|||
_setSelectionType(WordZoomSelection.emoji),
|
||||
isSelected: _selectionType == WordZoomSelection.emoji,
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
WordTextWithAudioButton(
|
||||
text: widget.token.text.content,
|
||||
ttsController: widget.tts,
|
||||
eventID: widget.messageEvent.eventId,
|
||||
),
|
||||
// if _selectionType is null, we don't know if the lemma activity
|
||||
// can be shown yet, so we don't show the lemma definition
|
||||
if (!_shouldShowActivity(WordZoomSelection.lemma) &&
|
||||
_selectionType != null)
|
||||
LemmaWidget(
|
||||
token: widget.token,
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
WordTextWithAudioButton(
|
||||
text: widget.token.text.content,
|
||||
ttsController: widget.tts,
|
||||
eventID: widget.messageEvent.eventId,
|
||||
),
|
||||
],
|
||||
// if _selectionType is null, we don't know if the lemma activity
|
||||
// can be shown yet, so we don't show the lemma definition
|
||||
if (!_shouldShowActivity(
|
||||
WordZoomSelection.lemma,
|
||||
) &&
|
||||
_selectionType != null)
|
||||
LemmaWidget(
|
||||
token: widget.token,
|
||||
pangeaMessageEvent: widget.messageEvent,
|
||||
onEdit: () => _setHideCenterContent(true),
|
||||
onEditDone: () {
|
||||
_setHideCenterContent(false);
|
||||
onEditDone();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 30),
|
||||
],
|
||||
),
|
||||
),
|
||||
WordZoomCenterWidget(
|
||||
selectionType: _selectionType,
|
||||
selectedMorphFeature: _selectedMorphFeature,
|
||||
shouldDoActivity: _selectionType != null
|
||||
? _shouldShowActivity(_selectionType!)
|
||||
: false,
|
||||
locked:
|
||||
_activityLock != null && !_activityLock!.isCompleted,
|
||||
wordDetailsController: this,
|
||||
),
|
||||
if (!_hideCenterContent)
|
||||
WordZoomCenterWidget(
|
||||
selectionType: _selectionType,
|
||||
selectedMorphFeature: _selectedMorphFeature,
|
||||
shouldDoActivity: _selectionType != null
|
||||
? _shouldShowActivity(_selectionType!)
|
||||
: false,
|
||||
locked:
|
||||
_activityLock != null && !_activityLock!.isCompleted,
|
||||
wordDetailsController: this,
|
||||
),
|
||||
MorphologicalListWidget(
|
||||
token: widget.token,
|
||||
setMorphFeature: (feature) => _setSelectionType(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
|
||||
extension AccountIdentiferExt on Client {
|
||||
bool get isSupportAccount => userID == Environment.supportUserId;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:collection/collection.dart';
|
|||
import 'package:jwt_decode/jwt_decode.dart';
|
||||
import 'package:matrix/matrix.dart' as matrix;
|
||||
|
||||
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
|
||||
import 'package:fluffychat/pangea/common/controllers/base_controller.dart';
|
||||
import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
|
|
@ -32,7 +33,9 @@ class UserController extends BaseController {
|
|||
_profileListener ??= _pangeaController.matrixState.client.onSync.stream
|
||||
.where((sync) => sync.accountData != null)
|
||||
.listen((sync) {
|
||||
final Profile? fromAccountData = Profile.fromAccountData();
|
||||
final profileData = _pangeaController
|
||||
.matrixState.client.accountData[ModelKey.userProfile]?.content;
|
||||
final Profile? fromAccountData = Profile.fromAccountData(profileData);
|
||||
if (fromAccountData != null) {
|
||||
_cachedProfile = fromAccountData;
|
||||
}
|
||||
|
|
@ -52,7 +55,11 @@ class UserController extends BaseController {
|
|||
}
|
||||
|
||||
/// try to get the account data in the up-to-date format
|
||||
final Profile? fromAccountData = Profile.fromAccountData();
|
||||
final Profile? fromAccountData = Profile.fromAccountData(
|
||||
_pangeaController
|
||||
.matrixState.client.accountData[ModelKey.userProfile]?.content,
|
||||
);
|
||||
|
||||
if (fromAccountData != null) {
|
||||
_cachedProfile = fromAccountData;
|
||||
return fromAccountData;
|
||||
|
|
|
|||
|
|
@ -213,9 +213,7 @@ class Profile {
|
|||
}
|
||||
|
||||
/// Load an instance of profile from the client's account data.
|
||||
static Profile? fromAccountData() {
|
||||
final profileData = MatrixState.pangeaController.matrixState.client
|
||||
.accountData[ModelKey.userProfile]?.content;
|
||||
static Profile? fromAccountData(Map<String, Object?>? profileData) {
|
||||
if (profileData == null) return null;
|
||||
|
||||
final userSettingsContent = profileData[ModelKey.userSettings];
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue