3921 display unsubscribed errors for users (#3991)
* url cleanup * chore: display unsubscribed errors differently
This commit is contained in:
parent
d166f40849
commit
d951d5eee9
29 changed files with 175 additions and 1319 deletions
|
|
@ -5243,5 +5243,8 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"inviteFriendsToCourse": "Invite friends to my course"
|
||||
"inviteFriendsToCourse": "Invite friends to my course",
|
||||
"subscribeToUnlockActivitySummaries": "Subscribe to unlock activity summaries",
|
||||
"subscribeToUnlockDefinitions": "Subscribe to unlock definitions",
|
||||
"subscribeToUnlockTranscriptions": "Subscribe to unlock transcriptions"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import 'package:fluffychat/pangea/activity_summary/activity_summary_request_mode
|
|||
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/constants/pangea_room_types.dart';
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/common/network/requests.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_plan_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
|
||||
|
|
@ -190,15 +191,17 @@ extension ActivityRoomExtension on Room {
|
|||
|
||||
ActivitySummaryRepo.delete(id, activityPlan!);
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"roomID": id,
|
||||
"activityPlan": activityPlan?.toJson(),
|
||||
"activityResults": messages.map((m) => m.toJson()).toList(),
|
||||
},
|
||||
);
|
||||
if (e is! UnsubscribedException) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"roomID": id,
|
||||
"activityPlan": activityPlan?.toJson(),
|
||||
"activityResults": messages.map((m) => m.toJson()).toList(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (activitySummary?.summary == null) {
|
||||
await setActivitySummary(
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import 'package:fluffychat/pages/chat/chat.dart';
|
|||
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/saved_activity_analytics_dialog.dart';
|
||||
import 'package:fluffychat/pangea/activity_summary/activity_summary_model.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_plan_room_extension.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
|
@ -70,6 +71,9 @@ class ActivityFinishedStatusMessage extends StatelessWidget {
|
|||
}
|
||||
|
||||
final theme = Theme.of(context);
|
||||
final isSubscribed =
|
||||
MatrixState.pangeaController.subscriptionController.isSubscribed;
|
||||
|
||||
return AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
child: Container(
|
||||
|
|
@ -103,7 +107,16 @@ class ActivityFinishedStatusMessage extends StatelessWidget {
|
|||
width: 36.0,
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
] else if (summary?.hasError ?? false) ...[
|
||||
] else if (isSubscribed == false)
|
||||
ErrorIndicator(
|
||||
message: L10n.of(context)
|
||||
.subscribeToUnlockActivitySummaries,
|
||||
onTap: () {
|
||||
MatrixState.pangeaController.subscriptionController
|
||||
.showPaywall(context);
|
||||
},
|
||||
)
|
||||
else if (summary?.hasError ?? false) ...[
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import 'dart:developer';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.dart';
|
||||
import 'package:fluffychat/pangea/common/network/requests.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_features_enum.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_meaning/morph_info_repo.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class MorphMeaningWidget extends StatefulWidget {
|
||||
final MorphFeaturesEnum feature;
|
||||
|
|
@ -63,7 +63,6 @@ class MorphMeaningWidgetState extends State<MorphMeaningWidget> {
|
|||
final response = await _morphMeaning();
|
||||
_setMeaningText(response);
|
||||
} catch (e) {
|
||||
debugger(when: kDebugMode);
|
||||
_error = e;
|
||||
} finally {
|
||||
if (mounted) setState(() => _isLoading = false);
|
||||
|
|
@ -117,9 +116,17 @@ class MorphMeaningWidgetState extends State<MorphMeaningWidget> {
|
|||
|
||||
if (_error != null) {
|
||||
return Center(
|
||||
child: ErrorIndicator(
|
||||
message: L10n.of(context).errorFetchingDefinition,
|
||||
),
|
||||
child: _error is UnsubscribedException
|
||||
? ErrorIndicator(
|
||||
message: L10n.of(context).subscribeToUnlockDefinitions,
|
||||
onTap: () {
|
||||
MatrixState.pangeaController.subscriptionController
|
||||
.showPaywall(context);
|
||||
},
|
||||
)
|
||||
: ErrorIndicator(
|
||||
message: L10n.of(context).errorFetchingDefinition,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ class VocabDetailsView extends StatelessWidget {
|
|||
},
|
||||
),
|
||||
if (MatrixState
|
||||
.pangeaController.languageController.showTrancription)
|
||||
.pangeaController.languageController.showTranscription)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4.0),
|
||||
child: PhoneticTranscriptionWidget(
|
||||
|
|
|
|||
|
|
@ -1,120 +1,4 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import '../../common/constants/model_keys.dart';
|
||||
import '../../common/controllers/pangea_controller.dart';
|
||||
import '../../common/network/requests.dart';
|
||||
import '../../common/network/urls.dart';
|
||||
|
||||
class ITFeedbackController {
|
||||
late PangeaController _pangeaController;
|
||||
|
||||
final List<_ITFeedbackCacheItem> _feedback = [];
|
||||
|
||||
ITFeedbackController(PangeaController pangeaController) {
|
||||
_pangeaController = pangeaController;
|
||||
}
|
||||
|
||||
_ITFeedbackCacheItem? _getLocal(
|
||||
ITFeedbackRequestModel req,
|
||||
) =>
|
||||
_feedback.firstWhereOrNull(
|
||||
(e) =>
|
||||
e.chosenContinuance == req.chosenContinuance &&
|
||||
e.sourceText == req.sourceText,
|
||||
);
|
||||
|
||||
Future<ITFeedbackResponseModel?> get(
|
||||
ITFeedbackRequestModel req,
|
||||
) {
|
||||
final _ITFeedbackCacheItem? localItem = _getLocal(req);
|
||||
|
||||
if (localItem != null) return localItem.data;
|
||||
|
||||
_feedback.add(
|
||||
_ITFeedbackCacheItem(
|
||||
chosenContinuance: req.chosenContinuance,
|
||||
sourceText: req.sourceText,
|
||||
data: _get(req),
|
||||
),
|
||||
);
|
||||
|
||||
return _feedback.last.data;
|
||||
}
|
||||
|
||||
Future<ITFeedbackResponseModel?> _get(
|
||||
ITFeedbackRequestModel request,
|
||||
) async {
|
||||
try {
|
||||
final ITFeedbackResponseModel res = await _ITFeedbackRepo.get(
|
||||
_pangeaController.userController.accessToken,
|
||||
request,
|
||||
);
|
||||
return res;
|
||||
} catch (err, stack) {
|
||||
debugPrint(
|
||||
"error getting contextual definition for ${request.chosenContinuance} in '${request.sourceText}'",
|
||||
);
|
||||
ErrorHandler.logError(e: err, s: stack, data: request.toJson());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _ITFeedbackCacheItem {
|
||||
String chosenContinuance;
|
||||
String sourceText;
|
||||
Future<ITFeedbackResponseModel?> data;
|
||||
|
||||
_ITFeedbackCacheItem({
|
||||
required this.chosenContinuance,
|
||||
required this.sourceText,
|
||||
required this.data,
|
||||
});
|
||||
}
|
||||
|
||||
class _ITFeedbackRepo {
|
||||
static Future<ITFeedbackResponseModel> get(
|
||||
String accessToken,
|
||||
ITFeedbackRequestModel request,
|
||||
) async {
|
||||
final Requests req = Requests(
|
||||
choreoApiKey: Environment.choreoApiKey,
|
||||
accessToken: accessToken,
|
||||
);
|
||||
|
||||
final Response res = await req.post(
|
||||
url: PApiUrls.itFeedback,
|
||||
body: request.toJson(),
|
||||
);
|
||||
|
||||
final ITFeedbackResponseModel response = ITFeedbackResponseModel.fromJson(
|
||||
jsonDecode(
|
||||
utf8.decode(res.bodyBytes).toString(),
|
||||
),
|
||||
);
|
||||
|
||||
if (response.text.isEmpty) {
|
||||
ErrorHandler.logError(
|
||||
e: Exception(
|
||||
"empty text in contextual definition response",
|
||||
),
|
||||
data: {
|
||||
"request": request.toJson(),
|
||||
"accessToken": accessToken,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
|
||||
|
||||
class ITFeedbackRequestModel {
|
||||
final String sourceText;
|
||||
|
|
|
|||
|
|
@ -1,83 +0,0 @@
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import 'package:fluffychat/pangea/choreographer/repo/word_repo.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart';
|
||||
import '../../common/controllers/base_controller.dart';
|
||||
import '../../common/controllers/pangea_controller.dart';
|
||||
import '../models/word_data_model.dart';
|
||||
|
||||
class WordController extends BaseController {
|
||||
late PangeaController _pangeaController;
|
||||
|
||||
final List<WordData> _wordData = [];
|
||||
|
||||
WordController(PangeaController pangeaController) : super() {
|
||||
_pangeaController = pangeaController;
|
||||
}
|
||||
|
||||
WordData? getWordDataLocal({
|
||||
required String word,
|
||||
required String fullText,
|
||||
required String? userL1,
|
||||
required String? userL2,
|
||||
}) =>
|
||||
_wordData.firstWhereOrNull(
|
||||
(e) => e.isMatch(
|
||||
w: word,
|
||||
f: fullText,
|
||||
l1: userL1,
|
||||
l2: userL2,
|
||||
),
|
||||
);
|
||||
|
||||
Future<WordData> getWordDataGlobal({
|
||||
required String word,
|
||||
required String fullText,
|
||||
required String? userL1,
|
||||
required String? userL2,
|
||||
}) async {
|
||||
if (userL1 == null ||
|
||||
userL2 == null ||
|
||||
userL1 == LanguageKeys.unknownLanguage ||
|
||||
userL2 == LanguageKeys.unknownLanguage) {
|
||||
throw http.Response("", 405);
|
||||
}
|
||||
|
||||
final WordData? local = getWordDataLocal(
|
||||
word: word,
|
||||
fullText: fullText,
|
||||
userL1: userL1,
|
||||
userL2: userL2,
|
||||
);
|
||||
|
||||
if (local != null) return local;
|
||||
|
||||
final WordData remote = await WordRepo.getWordNetData(
|
||||
accessToken: _pangeaController.userController.accessToken,
|
||||
fullText: fullText,
|
||||
word: word,
|
||||
userL1: userL1,
|
||||
userL2: userL2,
|
||||
);
|
||||
|
||||
_addWordData(remote);
|
||||
|
||||
return remote;
|
||||
}
|
||||
|
||||
_addWordData(WordData w) {
|
||||
final WordData? local = getWordDataLocal(
|
||||
word: w.word,
|
||||
fullText: w.fullText,
|
||||
userL1: w.userL1,
|
||||
userL2: w.userL2,
|
||||
);
|
||||
|
||||
if (local == null) {
|
||||
if (_wordData.length > 100) _wordData.clear();
|
||||
_wordData.add(w);
|
||||
setState(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import '../../common/network/requests.dart';
|
||||
import '../../common/network/urls.dart';
|
||||
|
||||
class SimilarityRepo {
|
||||
static Future<SimilartyResponseModel> get({
|
||||
required String accessToken,
|
||||
required SimilarityRequestModel request,
|
||||
}) async {
|
||||
final Requests req = Requests(
|
||||
choreoApiKey: Environment.choreoApiKey,
|
||||
accessToken: accessToken,
|
||||
);
|
||||
|
||||
final Response res = await req.post(
|
||||
url: PApiUrls.similarity,
|
||||
body: request.toJson(),
|
||||
);
|
||||
|
||||
final SimilartyResponseModel response = SimilartyResponseModel.fromJson(
|
||||
jsonDecode(
|
||||
utf8.decode(res.bodyBytes).toString(),
|
||||
),
|
||||
);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
class SimilarityRequestModel {
|
||||
String benchmark;
|
||||
List<String> toCompare;
|
||||
|
||||
SimilarityRequestModel({required this.benchmark, required this.toCompare});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"original": benchmark,
|
||||
"to_compare": toCompare,
|
||||
};
|
||||
}
|
||||
|
||||
class SimilartyResponseModel {
|
||||
String benchmark;
|
||||
List<SimilarityScore> scores;
|
||||
|
||||
SimilartyResponseModel({required this.benchmark, required this.scores});
|
||||
|
||||
factory SimilartyResponseModel.fromJson(
|
||||
Map<String, dynamic> json,
|
||||
) =>
|
||||
SimilartyResponseModel(
|
||||
benchmark: json["original"],
|
||||
scores: List<SimilarityScore>.from(
|
||||
json["scores"].map(
|
||||
(x) => SimilarityScore.fromJson(x),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
SimilarityScore get highestScore {
|
||||
SimilarityScore highest = scores.first;
|
||||
for (final SimilarityScore score in scores) {
|
||||
if (score.score > highest.score) {
|
||||
highest = score;
|
||||
}
|
||||
}
|
||||
return highest;
|
||||
}
|
||||
|
||||
bool userTranslationIsDifferentButBetter(String userTranslation) {
|
||||
return highestScore.text == userTranslation;
|
||||
}
|
||||
|
||||
bool userTranslationIsSameAsBotTranslation(String userTranslation) {
|
||||
return highestScore.text == userTranslation &&
|
||||
scores.where((e) => e.text == userTranslation).length == 2;
|
||||
}
|
||||
|
||||
num userScore(String userTranslation) {
|
||||
return scores.firstWhere((e) => e.text == userTranslation).score;
|
||||
}
|
||||
}
|
||||
|
||||
class SimilarityScore {
|
||||
String text;
|
||||
double score;
|
||||
int index;
|
||||
|
||||
SimilarityScore({
|
||||
required this.text,
|
||||
required this.score,
|
||||
required this.index,
|
||||
});
|
||||
|
||||
factory SimilarityScore.fromJson(Map<String, dynamic> json) {
|
||||
return SimilarityScore(
|
||||
text: json["text"],
|
||||
score: json["score"],
|
||||
index: json["index"],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import '../../common/constants/model_keys.dart';
|
||||
import '../../common/network/requests.dart';
|
||||
import '../../common/network/urls.dart';
|
||||
import '../models/word_data_model.dart';
|
||||
|
||||
class WordRepo {
|
||||
static Future<WordData> getWordNetData({
|
||||
required String accessToken,
|
||||
required String fullText,
|
||||
required String word,
|
||||
required String userL1,
|
||||
required String userL2,
|
||||
}) async {
|
||||
final Requests req = Requests(
|
||||
choreoApiKey: Environment.choreoApiKey,
|
||||
accessToken: accessToken,
|
||||
);
|
||||
final Response res = await req.post(
|
||||
url: PApiUrls.wordNet,
|
||||
body: {
|
||||
ModelKey.word: word,
|
||||
ModelKey.fullText: fullText,
|
||||
ModelKey.userL1: userL1,
|
||||
ModelKey.userL2: userL2,
|
||||
},
|
||||
);
|
||||
|
||||
final json = jsonDecode(utf8.decode(res.bodyBytes));
|
||||
|
||||
final WordData wordData = WordData.fromJson(
|
||||
json,
|
||||
fullText: fullText,
|
||||
word: word,
|
||||
userL1: userL1,
|
||||
userL2: userL2,
|
||||
);
|
||||
|
||||
return wordData;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,203 +0,0 @@
|
|||
import 'dart:developer';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
|
||||
import 'package:fluffychat/pangea/events/models/representation_content_model.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_show_popup.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_toolbar_selection_area.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../../models/pangea_match_model.dart';
|
||||
|
||||
class PangeaRichText extends StatefulWidget {
|
||||
final PangeaMessageEvent pangeaMessageEvent;
|
||||
final bool immersionMode;
|
||||
final TextStyle? style;
|
||||
final bool isOverlay;
|
||||
final ChatController controller;
|
||||
final Event? nextEvent;
|
||||
final Event? prevEvent;
|
||||
|
||||
const PangeaRichText({
|
||||
super.key,
|
||||
required this.pangeaMessageEvent,
|
||||
required this.immersionMode,
|
||||
required this.isOverlay,
|
||||
required this.controller,
|
||||
this.nextEvent,
|
||||
this.prevEvent,
|
||||
this.style,
|
||||
});
|
||||
|
||||
@override
|
||||
PangeaRichTextState createState() => PangeaRichTextState();
|
||||
}
|
||||
|
||||
class PangeaRichTextState extends State<PangeaRichText> {
|
||||
final PangeaController pangeaController = MatrixState.pangeaController;
|
||||
bool _fetchingRepresentation = false;
|
||||
double get blur => (_fetchingRepresentation && widget.immersionMode) ||
|
||||
!pangeaController.languageController.languagesSet
|
||||
? 5
|
||||
: 0;
|
||||
String textSpan = "";
|
||||
PangeaRepresentation? repEvent;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
setTextSpan();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(PangeaRichText oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
setTextSpan();
|
||||
}
|
||||
|
||||
void _setTextSpan(String newTextSpan) {
|
||||
try {
|
||||
if (!mounted) return; // Early exit if the widget is no longer in the tree
|
||||
setState(() {
|
||||
textSpan = newTextSpan;
|
||||
});
|
||||
} catch (error, stackTrace) {
|
||||
ErrorHandler.logError(
|
||||
e: PangeaWarningError(error),
|
||||
s: stackTrace,
|
||||
m: "Error setting text span in PangeaRichText",
|
||||
data: {
|
||||
"newTextSpan": newTextSpan,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void setTextSpan() {
|
||||
if (_fetchingRepresentation) {
|
||||
_setTextSpan(
|
||||
widget.pangeaMessageEvent.event
|
||||
.getDisplayEvent(widget.pangeaMessageEvent.timeline)
|
||||
.body,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (widget.pangeaMessageEvent.eventId.contains("webdebug")) {
|
||||
debugger(when: kDebugMode);
|
||||
}
|
||||
|
||||
repEvent = widget.pangeaMessageEvent.messageDisplayRepresentation?.content;
|
||||
if (repEvent == null) {
|
||||
setState(() => _fetchingRepresentation = true);
|
||||
widget.pangeaMessageEvent
|
||||
.representationByLanguageGlobal(
|
||||
langCode: widget.pangeaMessageEvent.messageDisplayLangCode,
|
||||
)
|
||||
.onError((error, stackTrace) {
|
||||
ErrorHandler.logError(
|
||||
e: PangeaWarningError(error),
|
||||
s: stackTrace,
|
||||
m: "Error fetching representation",
|
||||
data: {
|
||||
"langCode": widget.pangeaMessageEvent.messageDisplayLangCode,
|
||||
},
|
||||
);
|
||||
return null;
|
||||
}).then((event) {
|
||||
if (!mounted) return;
|
||||
repEvent = event;
|
||||
_setTextSpan(repEvent?.text ?? widget.pangeaMessageEvent.body);
|
||||
}).whenComplete(() {
|
||||
if (mounted) {
|
||||
setState(() => _fetchingRepresentation = false);
|
||||
}
|
||||
});
|
||||
|
||||
_setTextSpan(widget.pangeaMessageEvent.body);
|
||||
} else {
|
||||
_setTextSpan(repEvent!.text);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (blur > 0) {
|
||||
instructionsShowPopup(
|
||||
context,
|
||||
InstructionsEnum.blurMeansTranslate,
|
||||
widget.pangeaMessageEvent.eventId,
|
||||
);
|
||||
}
|
||||
|
||||
//TODO - take out of build function of every message
|
||||
final Widget richText = ToolbarSelectionArea(
|
||||
event: widget.pangeaMessageEvent.event,
|
||||
isOverlay: widget.isOverlay,
|
||||
pangeaMessageEvent: widget.pangeaMessageEvent,
|
||||
controller: widget.controller,
|
||||
nextEvent: widget.nextEvent,
|
||||
prevEvent: widget.prevEvent,
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
text: textSpan,
|
||||
style: widget.style,
|
||||
children: [
|
||||
if (_fetchingRepresentation)
|
||||
const WidgetSpan(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 5.0),
|
||||
child: SizedBox(
|
||||
height: 14,
|
||||
width: 14,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2.0,
|
||||
color: AppConfig.secondaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return blur > 0
|
||||
? ImageFiltered(
|
||||
imageFilter: ImageFilter.blur(
|
||||
sigmaX: blur,
|
||||
sigmaY: blur,
|
||||
),
|
||||
child: richText,
|
||||
)
|
||||
: richText;
|
||||
}
|
||||
|
||||
Future<void> onIgnore() async {
|
||||
debugPrint("PTODO implement onIgnore");
|
||||
}
|
||||
|
||||
Future<void> onITStart() async {
|
||||
debugPrint("PTODO implement onITStart");
|
||||
}
|
||||
|
||||
Future<void> onReplacementSelect(
|
||||
PangeaMatch pangeaMatch,
|
||||
String replacement,
|
||||
) async {
|
||||
debugPrint("PTODO implement onReplacementSelect");
|
||||
}
|
||||
|
||||
Future<void> onSentenceRewrite(String sentenceRewrite) async {
|
||||
debugPrint("PTODO implement onSentenceRewrite");
|
||||
}
|
||||
}
|
||||
|
|
@ -48,11 +48,8 @@ class WordDataCard extends StatefulWidget {
|
|||
class WordDataCardController extends State<WordDataCard> {
|
||||
final PangeaController controller = MatrixState.pangeaController;
|
||||
|
||||
bool isLoadingWordNet = false;
|
||||
bool isLoadingContextualDefinition = false;
|
||||
ContextualDefinitionResponseModel? contextualDefinitionRes;
|
||||
WordData? wordData;
|
||||
Object? wordNetError;
|
||||
|
||||
Object? definitionError;
|
||||
LanguageModel? activeL1;
|
||||
|
|
@ -66,12 +63,9 @@ class WordDataCardController extends State<WordDataCard> {
|
|||
activeL1 = controller.languageController.activeL1Model()!;
|
||||
activeL2 = controller.languageController.activeL2Model()!;
|
||||
if (activeL1 == null || activeL2 == null) {
|
||||
wordNetError = noLanguages;
|
||||
definitionError = noLanguages;
|
||||
} else if (!widget.hasInfo) {
|
||||
getContextualDefinition();
|
||||
} else {
|
||||
getWordNet();
|
||||
getContextualDefinition();
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
|
@ -80,11 +74,7 @@ class WordDataCardController extends State<WordDataCard> {
|
|||
void didUpdateWidget(covariant WordDataCard oldWidget) {
|
||||
// debugger(when: kDebugMode);
|
||||
if (oldWidget.word != widget.word) {
|
||||
if (!widget.hasInfo) {
|
||||
getContextualDefinition();
|
||||
} else {
|
||||
getWordNet();
|
||||
}
|
||||
getContextualDefinition();
|
||||
}
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
|
@ -127,32 +117,6 @@ class WordDataCardController extends State<WordDataCard> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> getWordNet() async {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
wordData = null;
|
||||
isLoadingWordNet = true;
|
||||
});
|
||||
}
|
||||
try {
|
||||
wordData = await controller.wordNet.getWordDataGlobal(
|
||||
word: widget.word,
|
||||
fullText: widget.fullText,
|
||||
userL1: activeL1?.langCode,
|
||||
userL2: activeL2?.langCode,
|
||||
);
|
||||
} catch (err) {
|
||||
ErrorHandler.logError(
|
||||
e: err,
|
||||
s: StackTrace.current,
|
||||
data: {"word": widget.word, "hasInfo": widget.hasInfo},
|
||||
);
|
||||
wordNetError = err;
|
||||
} finally {
|
||||
if (mounted) setState(() => isLoadingWordNet = false);
|
||||
}
|
||||
}
|
||||
|
||||
void handleGetDefinitionButtonPress() {
|
||||
if (isLoadingContextualDefinition) return;
|
||||
getContextualDefinition();
|
||||
|
|
@ -172,12 +136,6 @@ class WordDataCardView extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (controller.wordNetError != null) {
|
||||
return CardErrorWidget(
|
||||
error: controller.wordNetError!.toString(),
|
||||
maxWidth: AppConfig.toolbarMinWidth,
|
||||
);
|
||||
}
|
||||
if (controller.activeL1 == null || controller.activeL2 == null) {
|
||||
ErrorHandler.logError(
|
||||
m: "should not be here",
|
||||
|
|
@ -210,30 +168,6 @@ class WordDataCardView extends StatelessWidget {
|
|||
style: BotStyle.text(context),
|
||||
),
|
||||
const SizedBox(height: 5.0),
|
||||
if (controller.wordData != null &&
|
||||
controller.wordNetError == null &&
|
||||
controller.activeL1 != null &&
|
||||
controller.activeL2 != null)
|
||||
WordNetInfo(
|
||||
wordData: controller.wordData!,
|
||||
activeL1: controller.activeL1!,
|
||||
activeL2: controller.activeL2!,
|
||||
),
|
||||
if (controller.isLoadingWordNet)
|
||||
const ToolbarContentLoadingIndicator(),
|
||||
const SizedBox(height: 5.0),
|
||||
// if (controller.widget.hasInfo &&
|
||||
// !controller.isLoadingContextualDefinition &&
|
||||
// controller.contextualDefinitionRes == null)
|
||||
// Material(
|
||||
// type: MaterialType.transparency,
|
||||
// child: ListTile(
|
||||
// leading: const BotFace(
|
||||
// width: 40, expression: BotExpression.surprised),
|
||||
// title: Text(L10n.of(context).askPangeaBot),
|
||||
// onTap: controller.handleGetDefinitionButtonPress,
|
||||
// ),
|
||||
// ),
|
||||
if (controller.isLoadingContextualDefinition)
|
||||
const ToolbarContentLoadingIndicator(),
|
||||
if (controller.contextualDefinitionRes != null)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import 'package:fluffychat/l10n/l10n.dart';
|
|||
import 'package:fluffychat/pangea/choreographer/constants/choreo_constants.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/it_controller.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/it_feedback_controller.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/igc/word_data_card.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/it_feedback_card.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
|
|
@ -17,7 +18,6 @@ import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart'
|
|||
import 'package:fluffychat/pangea/learning_settings/pages/settings_learning.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../../common/utils/overlay.dart';
|
||||
import '../controllers/it_feedback_controller.dart';
|
||||
import '../models/it_response_model.dart';
|
||||
import 'choice_array.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import 'package:fluffychat/config/app_config.dart';
|
|||
import 'package:fluffychat/pangea/analytics_misc/get_analytics_controller.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/contextual_definition_controller.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/word_net_controller.dart';
|
||||
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
|
||||
import 'package:fluffychat/pangea/events/controllers/message_data_controller.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
|
|
@ -28,7 +27,6 @@ import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart';
|
|||
import 'package:fluffychat/pangea/user/controllers/permissions_controller.dart';
|
||||
import 'package:fluffychat/pangea/user/controllers/user_controller.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../../choreographer/controllers/it_feedback_controller.dart';
|
||||
import '../utils/firebase_analytics.dart';
|
||||
|
||||
class PangeaController {
|
||||
|
|
@ -39,12 +37,10 @@ class PangeaController {
|
|||
late PermissionsController permissionsController;
|
||||
late GetAnalyticsController getAnalytics;
|
||||
late PutAnalyticsController putAnalytics;
|
||||
late WordController wordNet;
|
||||
late MessageDataController messageData;
|
||||
|
||||
// TODO: make these static so we can remove from here
|
||||
late ContextualDefinitionController definitions;
|
||||
late ITFeedbackController itFeedback;
|
||||
late SubscriptionController subscriptionController;
|
||||
late TextToSpeechController textToSpeech;
|
||||
late SpeechToTextController speechToText;
|
||||
|
|
@ -91,10 +87,8 @@ class PangeaController {
|
|||
getAnalytics = GetAnalyticsController(this);
|
||||
putAnalytics = PutAnalyticsController(this);
|
||||
messageData = MessageDataController(this);
|
||||
wordNet = WordController(this);
|
||||
definitions = ContextualDefinitionController(this);
|
||||
subscriptionController = SubscriptionController(this);
|
||||
itFeedback = ITFeedbackController(this);
|
||||
textToSpeech = TextToSpeechController(this);
|
||||
speechToText = SpeechToTextController(this);
|
||||
PAuthGaurd.pController = this;
|
||||
|
|
|
|||
|
|
@ -10,14 +10,11 @@ import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_en
|
|||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class Requests {
|
||||
late String? baseUrl;
|
||||
// Matrix access token
|
||||
late String? accessToken;
|
||||
late String? choreoApiKey;
|
||||
//Question: How can we make baseUrl optional?
|
||||
|
||||
Requests({
|
||||
this.accessToken,
|
||||
this.baseUrl = '',
|
||||
this.choreoApiKey,
|
||||
});
|
||||
|
||||
|
|
@ -31,84 +28,31 @@ class Requests {
|
|||
dynamic encoded;
|
||||
encoded = jsonEncode(body);
|
||||
|
||||
debugPrint(baseUrl! + url);
|
||||
|
||||
final http.Response response = await http.post(
|
||||
_uriBuilder(url),
|
||||
body: encoded,
|
||||
headers: _headers,
|
||||
);
|
||||
handleError(response, body: body);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<http.Response> put({
|
||||
required String url,
|
||||
required Map<dynamic, dynamic> body,
|
||||
}) async {
|
||||
body[ModelKey.cefrLevel] = MatrixState
|
||||
.pangeaController.userController.profile.userSettings.cefrLevel.string;
|
||||
|
||||
dynamic encoded;
|
||||
encoded = jsonEncode(body);
|
||||
|
||||
debugPrint(baseUrl! + url);
|
||||
|
||||
final http.Response response = await http.put(
|
||||
_uriBuilder(url),
|
||||
Uri.parse(url),
|
||||
body: encoded,
|
||||
headers: _headers,
|
||||
);
|
||||
|
||||
handleError(response, body: body);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<http.Response> patch({
|
||||
required String url,
|
||||
required Map<dynamic, dynamic> body,
|
||||
}) async {
|
||||
body[ModelKey.cefrLevel] = MatrixState
|
||||
.pangeaController.userController.profile.userSettings.cefrLevel.string;
|
||||
|
||||
dynamic encoded;
|
||||
encoded = jsonEncode(body);
|
||||
|
||||
debugPrint(baseUrl! + url);
|
||||
|
||||
final http.Response response = await http.patch(
|
||||
_uriBuilder(url),
|
||||
body: encoded,
|
||||
headers: _headers,
|
||||
);
|
||||
|
||||
handleError(response, body: body);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<http.Response> get({required String url, String objectId = ""}) async {
|
||||
Future<http.Response> get({required String url}) async {
|
||||
final http.Response response =
|
||||
await http.get(_uriBuilder(url + objectId), headers: _headers);
|
||||
|
||||
handleError(response, objectId: objectId);
|
||||
await http.get(Uri.parse(url), headers: _headers);
|
||||
|
||||
handleError(response);
|
||||
return response;
|
||||
}
|
||||
|
||||
Uri _uriBuilder(url) =>
|
||||
baseUrl != null ? Uri.parse(baseUrl! + url) : Uri.parse(url);
|
||||
|
||||
void addBreadcrumb(
|
||||
http.Response response, {
|
||||
Map<dynamic, dynamic>? body,
|
||||
String? objectId,
|
||||
}) {
|
||||
debugPrint("Error - code: ${response.statusCode}");
|
||||
debugPrint("api: ${response.request?.url}");
|
||||
debugPrint("request body: ${body ?? objectId}");
|
||||
debugPrint("request body: $body");
|
||||
Sentry.addBreadcrumb(
|
||||
Breadcrumb.http(
|
||||
url: response.request?.url ?? Uri(path: "not available"),
|
||||
|
|
@ -117,20 +61,23 @@ class Requests {
|
|||
),
|
||||
);
|
||||
Sentry.addBreadcrumb(
|
||||
Breadcrumb(data: {"body": body, "objectId": objectId}),
|
||||
Breadcrumb(data: {"body": body}),
|
||||
);
|
||||
}
|
||||
|
||||
void handleError(
|
||||
http.Response response, {
|
||||
Map<dynamic, dynamic>? body,
|
||||
String? objectId,
|
||||
}) {
|
||||
//PTODO - handle 401 error - unauthorized call
|
||||
//kick them back to login?
|
||||
if (response.statusCode == 401) {
|
||||
final responseBody = jsonDecode(utf8.decode(response.bodyBytes));
|
||||
if (responseBody['detail'] == 'No active subscription found') {
|
||||
throw UnsubscribedException();
|
||||
}
|
||||
}
|
||||
|
||||
if (response.statusCode >= 400) {
|
||||
addBreadcrumb(response, body: body, objectId: objectId);
|
||||
addBreadcrumb(response, body: body);
|
||||
throw response;
|
||||
}
|
||||
}
|
||||
|
|
@ -142,7 +89,6 @@ class Requests {
|
|||
};
|
||||
if (accessToken != null) {
|
||||
headers["Authorization"] = 'Bearer ${accessToken!}';
|
||||
//headers["Matrix-Access-Token"] = accessToken!;
|
||||
}
|
||||
if (choreoApiKey != null) {
|
||||
headers['api_key'] = choreoApiKey!;
|
||||
|
|
@ -150,3 +96,5 @@ class Requests {
|
|||
return headers;
|
||||
}
|
||||
}
|
||||
|
||||
class UnsubscribedException implements Exception {}
|
||||
|
|
|
|||
|
|
@ -9,79 +9,73 @@ import 'package:fluffychat/pangea/common/config/environment.dart';
|
|||
///
|
||||
/// https://api.staging.pangea.chat/api/v1/
|
||||
class PApiUrls {
|
||||
static String choreoPrefix = "/choreo";
|
||||
static String subscriptionPrefix = "/subscription";
|
||||
static String accountPrefix = "/account";
|
||||
static const String _choreoPrefix = "/choreo";
|
||||
static const String _subscriptionPrefix = "/subscription";
|
||||
|
||||
static String get choreoEndpoint =>
|
||||
"${Environment.choreoApi}${PApiUrls.choreoPrefix}";
|
||||
static String get subscriptionEndpoint =>
|
||||
"${Environment.choreoApi}${PApiUrls.subscriptionPrefix}";
|
||||
static String get accountEndpoint =>
|
||||
"${Environment.choreoApi}${PApiUrls.accountPrefix}";
|
||||
static String get _choreoEndpoint =>
|
||||
"${Environment.choreoApi}${PApiUrls._choreoPrefix}";
|
||||
static String get _subscriptionEndpoint =>
|
||||
"${Environment.choreoApi}${PApiUrls._subscriptionPrefix}";
|
||||
|
||||
/// ---------------------- Util --------------------------------------
|
||||
static String appVersion = "${PApiUrls.choreoEndpoint}/version";
|
||||
static String appVersion = "${PApiUrls._choreoEndpoint}/version";
|
||||
|
||||
/// ---------------------- Languages --------------------------------------
|
||||
static String getLanguages = "${PApiUrls.choreoEndpoint}/languages_v2";
|
||||
static String getLanguages = "${PApiUrls._choreoEndpoint}/languages_v2";
|
||||
|
||||
/// ---------------------- Users --------------------------------------
|
||||
static String paymentLink = "${PApiUrls.subscriptionEndpoint}/payment_link";
|
||||
static String paymentLink = "${PApiUrls._subscriptionEndpoint}/payment_link";
|
||||
|
||||
static String languageDetection =
|
||||
"${PApiUrls.choreoEndpoint}/language_detection";
|
||||
"${PApiUrls._choreoEndpoint}/language_detection";
|
||||
|
||||
static String igcLite = "${PApiUrls.choreoEndpoint}/grammar_lite";
|
||||
static String spanDetails = "${PApiUrls.choreoEndpoint}/span_details";
|
||||
static String igcLite = "${PApiUrls._choreoEndpoint}/grammar_lite";
|
||||
static String spanDetails = "${PApiUrls._choreoEndpoint}/span_details";
|
||||
|
||||
static String wordNet = "${PApiUrls.choreoEndpoint}/wordnet";
|
||||
static String simpleTranslation =
|
||||
"${PApiUrls.choreoEndpoint}/translation/direct";
|
||||
static String tokenize = "${PApiUrls.choreoEndpoint}/tokenize";
|
||||
"${PApiUrls._choreoEndpoint}/translation/direct";
|
||||
static String tokenize = "${PApiUrls._choreoEndpoint}/tokenize";
|
||||
static String contextualDefinition =
|
||||
"${PApiUrls.choreoEndpoint}/contextual_definition";
|
||||
static String similarity = "${PApiUrls.choreoEndpoint}/similarity";
|
||||
"${PApiUrls._choreoEndpoint}/contextual_definition";
|
||||
|
||||
static String itFeedback = "${PApiUrls.choreoEndpoint}/translation/feedback";
|
||||
static String firstStep = "${PApiUrls._choreoEndpoint}/it_initialstep";
|
||||
|
||||
static String firstStep = "${PApiUrls.choreoEndpoint}/it_initialstep";
|
||||
|
||||
static String textToSpeech = "${PApiUrls.choreoEndpoint}/text_to_speech";
|
||||
static String speechToText = "${PApiUrls.choreoEndpoint}/speech_to_text";
|
||||
static String textToSpeech = "${PApiUrls._choreoEndpoint}/text_to_speech";
|
||||
static String speechToText = "${PApiUrls._choreoEndpoint}/speech_to_text";
|
||||
static String phoneticTranscription =
|
||||
"${PApiUrls._choreoEndpoint}/phonetic_transcription";
|
||||
|
||||
static String messageActivityGeneration =
|
||||
"${PApiUrls.choreoEndpoint}/practice";
|
||||
"${PApiUrls._choreoEndpoint}/practice";
|
||||
|
||||
static String lemmaDictionary = "${PApiUrls.choreoEndpoint}/lemma_definition";
|
||||
static String lemmaDictionary =
|
||||
"${PApiUrls._choreoEndpoint}/lemma_definition";
|
||||
static String lemmaDictionaryEdit =
|
||||
"${PApiUrls.choreoEndpoint}/lemma_definition/edit";
|
||||
static String morphDictionary = "${PApiUrls.choreoEndpoint}/morph_meaning";
|
||||
"${PApiUrls._choreoEndpoint}/lemma_definition/edit";
|
||||
static String morphDictionary = "${PApiUrls._choreoEndpoint}/morph_meaning";
|
||||
|
||||
static String activityPlan = "${PApiUrls.choreoEndpoint}/activity_plan";
|
||||
static String activityPlanGeneration =
|
||||
"${PApiUrls.choreoEndpoint}/activity_plan/generate";
|
||||
static String activityPlanSearch =
|
||||
"${PApiUrls.choreoEndpoint}/activity_plan/search";
|
||||
// static String activityPlan = "${PApiUrls._choreoEndpoint}/activity_plan";
|
||||
// static String activityPlanGeneration =
|
||||
// "${PApiUrls._choreoEndpoint}/activity_plan/generate";
|
||||
// static String activityPlanSearch =
|
||||
// "${PApiUrls._choreoEndpoint}/activity_plan/search";
|
||||
// static String activityModeList = "${PApiUrls._choreoEndpoint}/modes";
|
||||
// static String objectiveList = "${PApiUrls._choreoEndpoint}/objectives";
|
||||
// static String topicList = "${PApiUrls._choreoEndpoint}/topics";
|
||||
|
||||
static String activityModeList = "${PApiUrls.choreoEndpoint}/modes";
|
||||
static String objectiveList = "${PApiUrls.choreoEndpoint}/objectives";
|
||||
static String topicList = "${PApiUrls.choreoEndpoint}/topics";
|
||||
static String activitySummary =
|
||||
"${PApiUrls._choreoEndpoint}/activity_summary";
|
||||
|
||||
static String activitySummary = "${PApiUrls.choreoEndpoint}/activity_summary";
|
||||
|
||||
static String morphFeaturesAndTags = "${PApiUrls.choreoEndpoint}/morphs";
|
||||
static String morphFeaturesAndTags = "${PApiUrls._choreoEndpoint}/morphs";
|
||||
static String constructSummary =
|
||||
"${PApiUrls.choreoEndpoint}/construct_summary";
|
||||
"${PApiUrls._choreoEndpoint}/construct_summary";
|
||||
|
||||
///-------------------------------- revenue cat --------------------------
|
||||
static String rcAppsChoreo = "${PApiUrls.subscriptionEndpoint}/app_ids";
|
||||
static String rcAppsChoreo = "${PApiUrls._subscriptionEndpoint}/app_ids";
|
||||
static String rcProductsChoreo =
|
||||
"${PApiUrls.subscriptionEndpoint}/all_products";
|
||||
static String rcProductsTrial = "${PApiUrls.subscriptionEndpoint}/free_trial";
|
||||
"${PApiUrls._subscriptionEndpoint}/all_products";
|
||||
static String rcProductsTrial =
|
||||
"${PApiUrls._subscriptionEndpoint}/free_trial";
|
||||
|
||||
static String rcSubscription = PApiUrls.subscriptionEndpoint;
|
||||
|
||||
static String phoneticTranscription =
|
||||
"${PApiUrls.choreoEndpoint}/phonetic_transcription";
|
||||
static String rcSubscription = PApiUrls._subscriptionEndpoint;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,17 +4,19 @@ class ErrorIndicator extends StatelessWidget {
|
|||
final String message;
|
||||
final double? iconSize;
|
||||
final TextStyle? style;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
const ErrorIndicator({
|
||||
super.key,
|
||||
required this.message,
|
||||
this.iconSize,
|
||||
this.style,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
final content = Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
|
|
@ -32,5 +34,14 @@ class ErrorIndicator extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
);
|
||||
|
||||
if (onTap != null) {
|
||||
return TextButton(
|
||||
onPressed: onTap,
|
||||
child: content,
|
||||
);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -506,40 +506,6 @@ class PangeaMessageEvent {
|
|||
(filter?.call(element) ?? true),
|
||||
);
|
||||
|
||||
Future<PangeaRepresentation?> representationByLanguageGlobal({
|
||||
required String langCode,
|
||||
}) async {
|
||||
final RepresentationEvent? repLocal = representationByLanguage(langCode);
|
||||
|
||||
if (repLocal != null ||
|
||||
langCode == LanguageKeys.unknownLanguage ||
|
||||
langCode == LanguageKeys.mixedLanguage ||
|
||||
langCode == LanguageKeys.multiLanguage) {
|
||||
return repLocal?.content;
|
||||
}
|
||||
|
||||
if (eventId.contains("Pangea Chat")) return null;
|
||||
|
||||
// should this just be the original event body?
|
||||
// worth a conversation with the team
|
||||
final PangeaRepresentation? basis = originalSent?.content;
|
||||
|
||||
// clear representations cache so the new representation event can be added
|
||||
// when next requested
|
||||
_representations = null;
|
||||
|
||||
return MatrixState.pangeaController.messageData.getPangeaRepresentation(
|
||||
req: FullTextTranslationRequestModel(
|
||||
text: basis?.text ?? _latestEdit.body,
|
||||
srcLang: basis?.langCode,
|
||||
tgtLang: langCode,
|
||||
userL2: l2Code ?? LanguageKeys.unknownLanguage,
|
||||
userL1: l1Code ?? LanguageKeys.unknownLanguage,
|
||||
),
|
||||
messageEvent: _event,
|
||||
);
|
||||
}
|
||||
|
||||
Future<String?> representationByDetectedLanguage() async {
|
||||
LanguageDetectionResponse? resp;
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -14,9 +14,6 @@ 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';
|
||||
import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart';
|
||||
import 'package:fluffychat/pangea/lemmas/lemma_info_repo.dart';
|
||||
import 'package:fluffychat/pangea/lemmas/lemma_info_request.dart';
|
||||
import 'package:fluffychat/pangea/lemmas/user_set_lemma_info.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_features_enum.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_repo.dart';
|
||||
|
|
@ -438,19 +435,6 @@ class PangeaToken {
|
|||
.cast<ConstructUses>()
|
||||
.toList();
|
||||
|
||||
Future<List<String>> getEmojiChoices() => LemmaInfoRepo.get(
|
||||
LemmaInfoRequest(
|
||||
lemma: lemma.text,
|
||||
partOfSpeech: pos,
|
||||
lemmaLang: MatrixState
|
||||
.pangeaController.languageController.userL2?.langCode ??
|
||||
LanguageKeys.unknownLanguage,
|
||||
userL1: MatrixState
|
||||
.pangeaController.languageController.userL1?.langCode ??
|
||||
LanguageKeys.defaultLanguage,
|
||||
),
|
||||
).then((onValue) => onValue.emoji);
|
||||
|
||||
ConstructIdentifier get vocabConstructID => ConstructIdentifier(
|
||||
lemma: lemma.text,
|
||||
type: ConstructTypeEnum.vocab,
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ class LanguageController {
|
|||
// return model;
|
||||
}
|
||||
|
||||
bool get showTrancription =>
|
||||
bool get showTranscription =>
|
||||
(_pangeaController.languageController.userL1 != null &&
|
||||
_pangeaController.languageController.userL2 != null &&
|
||||
_pangeaController.languageController.userL1?.script !=
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
|
@ -9,7 +6,6 @@ import 'package:http/http.dart';
|
|||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/common/network/requests.dart';
|
||||
import 'package:fluffychat/pangea/common/network/urls.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/lemmas/lemma_edit_request.dart';
|
||||
import 'package:fluffychat/pangea/lemmas/lemma_info_request.dart';
|
||||
import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart';
|
||||
|
|
@ -40,7 +36,8 @@ class LemmaInfoRepo {
|
|||
return null;
|
||||
}
|
||||
|
||||
static Future<LemmaInfoResponse> _fetch(LemmaInfoRequest request) async {
|
||||
/// Get lemma info, prefering user set data over fetched data
|
||||
static Future<LemmaInfoResponse> get(LemmaInfoRequest request) async {
|
||||
final cached = getCached(request);
|
||||
if (cached != null) return cached;
|
||||
|
||||
|
|
@ -58,52 +55,9 @@ class LemmaInfoRepo {
|
|||
final response = LemmaInfoResponse.fromJson(decodedBody);
|
||||
|
||||
set(request, response);
|
||||
|
||||
// debugPrint(
|
||||
// 'fetched data for ${request.lemma} ${response.toJson()}',
|
||||
// );
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/// Get lemma info, prefering user set data over fetched data
|
||||
static Future<LemmaInfoResponse> get(LemmaInfoRequest request) async {
|
||||
try {
|
||||
return await _fetch(request);
|
||||
// if the user has either emojis or meaning in the past, use those first
|
||||
// final UserSetLemmaInfo? userSetLemmaInfo = request.cId.userLemmaInfo;
|
||||
|
||||
// final List<String> emojis = userSetLemmaInfo?.emojis ?? [];
|
||||
// String? meaning = userSetLemmaInfo?.meaning;
|
||||
|
||||
// if the user has not set these, fetch from the server
|
||||
// if (emojis.length < maxEmojisPerLemma || meaning == null) {
|
||||
// final LemmaInfoResponse fetched = await _fetch(request);
|
||||
|
||||
// while (emojis.length < maxEmojisPerLemma && fetched.emoji.isNotEmpty) {
|
||||
// final maybeToAdd = fetched.emoji.removeAt(0);
|
||||
// if (!emojis.contains(maybeToAdd)) {
|
||||
// emojis.add(maybeToAdd);
|
||||
// }
|
||||
// }
|
||||
// meaning ??= fetched.meaning;
|
||||
// } else {
|
||||
// // debugPrint(
|
||||
// // 'using user set data for ${request.lemma} ${userSetLemmaInfo?.toJson()}',
|
||||
// // );
|
||||
// }
|
||||
|
||||
// return LemmaInfoResponse(
|
||||
// emoji: emojis,
|
||||
// meaning: meaning,
|
||||
// );
|
||||
} catch (e) {
|
||||
debugger(when: kDebugMode);
|
||||
ErrorHandler.logError(e: e, data: request.toJson());
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> edit(LemmaEditRequest request) async {
|
||||
final Requests req = Requests(
|
||||
choreoApiKey: Environment.choreoApiKey,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'dart:async';
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/common/network/requests.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart';
|
||||
|
|
@ -98,14 +99,16 @@ class _PhoneticTranscriptionWidgetState
|
|||
.first.phoneticL1Transcription.content;
|
||||
} catch (e, s) {
|
||||
_error = e;
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
'text': widget.text,
|
||||
'textLanguageCode': widget.textLanguage.langCode,
|
||||
},
|
||||
);
|
||||
if (e is! UnsubscribedException) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
'text': widget.text,
|
||||
'textLanguageCode': widget.textLanguage.langCode,
|
||||
},
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
|
|
@ -157,9 +160,20 @@ class _PhoneticTranscriptionWidgetState
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (_error != null)
|
||||
ErrorIndicator(
|
||||
message: L10n.of(context).failedToFetchTranscription,
|
||||
)
|
||||
_error is UnsubscribedException
|
||||
? ErrorIndicator(
|
||||
message: L10n.of(context)
|
||||
.subscribeToUnlockTranscriptions,
|
||||
onTap: () {
|
||||
MatrixState
|
||||
.pangeaController.subscriptionController
|
||||
.showPaywall(context);
|
||||
},
|
||||
)
|
||||
: ErrorIndicator(
|
||||
message:
|
||||
L10n.of(context).failedToFetchTranscription,
|
||||
)
|
||||
else if (_isLoading || _transcription == null)
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:http/http.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../../common/config/environment.dart';
|
||||
import '../../common/network/requests.dart';
|
||||
|
||||
class GenerateImageeResponse {
|
||||
final String imageUrl;
|
||||
final String prompt;
|
||||
|
||||
GenerateImageeResponse({
|
||||
required this.imageUrl,
|
||||
required this.prompt,
|
||||
});
|
||||
|
||||
factory GenerateImageeResponse.fromJson(Map<String, dynamic> json) {
|
||||
return GenerateImageeResponse(
|
||||
imageUrl: json['image_url'],
|
||||
prompt: json['prompt'],
|
||||
);
|
||||
}
|
||||
|
||||
factory GenerateImageeResponse.error() {
|
||||
// TODO: Implement better error handling
|
||||
return GenerateImageeResponse(
|
||||
imageUrl: 'https://i.imgur.com/2L2JYqk.png',
|
||||
prompt: 'Error',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GenerateImageRequest {
|
||||
String prompt;
|
||||
|
||||
GenerateImageRequest({required this.prompt});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'prompt': prompt,
|
||||
};
|
||||
}
|
||||
|
||||
class ImageRepo {
|
||||
static Future<GenerateImageeResponse> fetchImage(
|
||||
GenerateImageRequest request,
|
||||
) async {
|
||||
final Requests req = Requests(
|
||||
choreoApiKey: Environment.choreoApiKey,
|
||||
accessToken: MatrixState.pangeaController.userController.accessToken,
|
||||
); // Set your API base URL
|
||||
final requestBody = request.toJson();
|
||||
|
||||
try {
|
||||
final Response res = await req.post(
|
||||
url: '/generate-image/', // Endpoint in your FastAPI server
|
||||
body: requestBody,
|
||||
);
|
||||
|
||||
if (res.statusCode == 200) {
|
||||
final decodedBody = jsonDecode(utf8.decode(res.bodyBytes));
|
||||
return GenerateImageeResponse.fromJson(
|
||||
decodedBody,
|
||||
); // Convert response to ImageModel
|
||||
} else {
|
||||
throw Exception('Failed to load image');
|
||||
}
|
||||
} catch (err, stack) {
|
||||
debugger(when: kDebugMode);
|
||||
ErrorHandler.logError(e: err, s: stack, data: requestBody);
|
||||
return GenerateImageeResponse
|
||||
.error(); // Return an error model or handle accordingly
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,22 +3,13 @@ import 'package:flutter/material.dart';
|
|||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/bot/utils/bot_style.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class MessageUnsubscribedCard extends StatelessWidget {
|
||||
final MessageOverlayController controller;
|
||||
|
||||
const MessageUnsubscribedCard({
|
||||
super.key,
|
||||
required this.controller,
|
||||
});
|
||||
const MessageUnsubscribedCard({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool inTrialWindow =
|
||||
MatrixState.pangeaController.userController.inTrialWindow();
|
||||
|
||||
return Container(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: AppConfig.toolbarMinWidth,
|
||||
|
|
@ -31,31 +22,11 @@ class MessageUnsubscribedCard extends StatelessWidget {
|
|||
L10n.of(context).subscribedToUnlockTools,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
if (inTrialWindow) ...[
|
||||
const SizedBox(height: 10),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
MatrixState.pangeaController.subscriptionController
|
||||
.activateNewUserTrial();
|
||||
controller.updateToolbarMode(controller.toolbarMode);
|
||||
},
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.all<Color>(
|
||||
(Theme.of(context).colorScheme.primary).withAlpha(25),
|
||||
),
|
||||
),
|
||||
child: Text(L10n.of(context).activateTrial),
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 10),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
controller.widget.chatController.clearSelectedEvents();
|
||||
MatrixState.pangeaController.subscriptionController
|
||||
.showPaywall(context);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -137,14 +137,20 @@ class OverlayMessage extends StatelessWidget {
|
|||
event.numberEmotes > 0 &&
|
||||
event.numberEmotes <= 3);
|
||||
|
||||
final isSubscribed =
|
||||
MatrixState.pangeaController.subscriptionController.isSubscribed;
|
||||
|
||||
final showTranslation = overlayController.showTranslation &&
|
||||
overlayController.translation != null;
|
||||
overlayController.translation != null &&
|
||||
isSubscribed != false;
|
||||
|
||||
final showTranscription =
|
||||
overlayController.pangeaMessageEvent?.isAudioMessage == true;
|
||||
overlayController.pangeaMessageEvent?.isAudioMessage == true &&
|
||||
isSubscribed != false;
|
||||
|
||||
final showSpeechTranslation = overlayController.showSpeechTranslation &&
|
||||
overlayController.speechTranslation != null;
|
||||
overlayController.speechTranslation != null &&
|
||||
isSubscribed != false;
|
||||
|
||||
final transcription = showTranscription
|
||||
? Container(
|
||||
|
|
@ -200,7 +206,7 @@ class OverlayMessage extends StatelessWidget {
|
|||
isSelected: overlayController.isTokenSelected,
|
||||
),
|
||||
if (MatrixState.pangeaController
|
||||
.languageController.showTrancription)
|
||||
.languageController.showTranscription)
|
||||
PhoneticTranscriptionWidget(
|
||||
text: overlayController
|
||||
.transcription!.transcript.text,
|
||||
|
|
|
|||
|
|
@ -42,9 +42,7 @@ class ReadingAssistanceContentState extends State<ReadingAssistanceContent> {
|
|||
MatrixState.pangeaController.subscriptionController.isSubscribed;
|
||||
|
||||
if (subscribed != null && !subscribed) {
|
||||
return MessageUnsubscribedCard(
|
||||
controller: widget.overlayController,
|
||||
);
|
||||
return const MessageUnsubscribedCard();
|
||||
}
|
||||
|
||||
if (widget.overlayController.practiceSelection?.hasHiddenWordActivity ??
|
||||
|
|
|
|||
|
|
@ -549,12 +549,15 @@ class SelectModeButtonsState extends State<SelectModeButtons> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final modes = widget.overlayController.showLanguageAssistance
|
||||
final isSubscribed =
|
||||
MatrixState.pangeaController.subscriptionController.isSubscribed;
|
||||
List<SelectMode> modes = widget.overlayController.showLanguageAssistance
|
||||
? messageEvent?.isAudioMessage == true
|
||||
? audioModes
|
||||
: textModes
|
||||
: [];
|
||||
|
||||
if (isSubscribed == false) modes = [];
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: SizedBox(
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.dart';
|
||||
import 'package:fluffychat/pangea/common/network/requests.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/word_zoom/lemma_meaning_builder.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class LemmaMeaningWidget extends StatelessWidget {
|
||||
final LemmaMeaningBuilderState controller;
|
||||
|
|
@ -31,7 +30,16 @@ class LemmaMeaningWidget extends StatelessWidget {
|
|||
}
|
||||
|
||||
if (controller.error != null) {
|
||||
debugger(when: kDebugMode);
|
||||
if (controller.error is UnsubscribedException) {
|
||||
return ErrorIndicator(
|
||||
message: L10n.of(context).subscribeToUnlockDefinitions,
|
||||
style: style,
|
||||
onTap: () {
|
||||
MatrixState.pangeaController.subscriptionController
|
||||
.showPaywall(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
return ErrorIndicator(
|
||||
message: L10n.of(context).errorFetchingDefinition,
|
||||
style: style,
|
||||
|
|
|
|||
|
|
@ -1,323 +0,0 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/overlay.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/models/pangea_token_model.dart';
|
||||
import 'package:fluffychat/pangea/lemmas/construct_xp_widget.dart';
|
||||
import 'package:fluffychat/pangea/morphs/edit_morph_widget.dart';
|
||||
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_features_enum.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_icon.dart';
|
||||
import 'package:fluffychat/pangea/morphs/morph_meaning/morph_info_repo.dart';
|
||||
import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/reading_assistance_input_row/morph_selection.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/practice_activity/word_zoom_activity_button.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class MorphologicalListItem extends StatelessWidget {
|
||||
final MorphFeaturesEnum morphFeature;
|
||||
final PangeaToken token;
|
||||
final MessageOverlayController overlayController;
|
||||
// final VoidCallback editMorph;
|
||||
|
||||
const MorphologicalListItem({
|
||||
required this.morphFeature,
|
||||
required this.token,
|
||||
required this.overlayController,
|
||||
// required this.editMorph,
|
||||
super.key,
|
||||
});
|
||||
|
||||
bool get shouldDoActivity =>
|
||||
overlayController.hideWordCardContent &&
|
||||
overlayController.practiceSelection?.hasActiveActivityByToken(
|
||||
ActivityTypeEnum.morphId,
|
||||
token,
|
||||
morphFeature,
|
||||
) ==
|
||||
true;
|
||||
|
||||
bool get isSelected =>
|
||||
overlayController.toolbarMode == MessageMode.wordMorph &&
|
||||
overlayController.selectedMorph?.morph == morphFeature;
|
||||
|
||||
String get morphTag => token.getMorphTag(morphFeature) ?? "X";
|
||||
|
||||
ConstructIdentifier get cId =>
|
||||
token.morphIdByFeature(morphFeature) ??
|
||||
ConstructIdentifier(
|
||||
type: ConstructTypeEnum.morph,
|
||||
category: morphFeature.name,
|
||||
lemma: morphTag,
|
||||
);
|
||||
|
||||
void _openDefintionPopup(BuildContext context) async {
|
||||
const width = 300.0;
|
||||
const height = 200.0;
|
||||
|
||||
try {
|
||||
if (overlayController.pangeaMessageEvent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
OverlayUtil.showPositionedCard(
|
||||
context: context,
|
||||
cardToShow: MorphMeaningPopup(
|
||||
token: token,
|
||||
pangeaMessageEvent: overlayController.pangeaMessageEvent!,
|
||||
cId: cId,
|
||||
width: width,
|
||||
height: height,
|
||||
refresh: () {
|
||||
overlayController.onMorphActivitySelect(
|
||||
MorphSelection(token, morphFeature),
|
||||
);
|
||||
},
|
||||
),
|
||||
transformTargetId: cId.string,
|
||||
backDropToDismiss: true,
|
||||
borderColor: Theme.of(context).colorScheme.primary,
|
||||
closePrevOverlay: false,
|
||||
addBorder: false,
|
||||
maxHeight: height,
|
||||
maxWidth: width,
|
||||
);
|
||||
} catch (e, s) {
|
||||
debugger(when: kDebugMode);
|
||||
ErrorHandler.logError(
|
||||
data: cId.toJson(),
|
||||
e: e,
|
||||
s: s,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CompositedTransformTarget(
|
||||
link: MatrixState.pAnyState.layerLinkAndKey(cId.string).link,
|
||||
child: SizedBox(
|
||||
key: MatrixState.pAnyState.layerLinkAndKey(cId.string).key,
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: WordZoomActivityButton(
|
||||
icon: shouldDoActivity
|
||||
? const Icon(Symbols.toys_and_games)
|
||||
: MorphIcon(
|
||||
morphFeature: morphFeature,
|
||||
morphTag: token.getMorphTag(morphFeature),
|
||||
size: const Size(24, 24),
|
||||
),
|
||||
isSelected: isSelected,
|
||||
onPressed: () {
|
||||
overlayController
|
||||
.onMorphActivitySelect(MorphSelection(token, morphFeature));
|
||||
_openDefintionPopup(context);
|
||||
},
|
||||
tooltip: shouldDoActivity
|
||||
? morphFeature.getDisplayCopy(context)
|
||||
: getGrammarCopy(
|
||||
category: morphFeature.name,
|
||||
lemma: morphTag,
|
||||
context: context,
|
||||
),
|
||||
opacity: isSelected ? 1 : 0.7,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MorphMeaningPopup extends StatefulWidget {
|
||||
final PangeaToken token;
|
||||
final PangeaMessageEvent pangeaMessageEvent;
|
||||
final ConstructIdentifier cId;
|
||||
final double width;
|
||||
final double height;
|
||||
final VoidCallback refresh;
|
||||
|
||||
const MorphMeaningPopup({
|
||||
super.key,
|
||||
required this.token,
|
||||
required this.pangeaMessageEvent,
|
||||
required this.cId,
|
||||
required this.width,
|
||||
required this.height,
|
||||
required this.refresh,
|
||||
});
|
||||
|
||||
@override
|
||||
State<MorphMeaningPopup> createState() => MorphMeaningPopupState();
|
||||
}
|
||||
|
||||
class MorphMeaningPopupState extends State<MorphMeaningPopup> {
|
||||
MorphFeaturesEnum get _morphFeature =>
|
||||
MorphFeaturesEnumExtension.fromString(widget.cId.category);
|
||||
|
||||
String get _morphTag => widget.cId.lemma;
|
||||
String? _defintion;
|
||||
|
||||
bool _isEditMode = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_fetchDefinition();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant MorphMeaningPopup oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.cId != widget.cId) {
|
||||
_fetchDefinition();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _fetchDefinition() async {
|
||||
try {
|
||||
final response = await MorphInfoRepo.get(
|
||||
feature: _morphFeature,
|
||||
tag: _morphTag,
|
||||
);
|
||||
|
||||
if (mounted) {
|
||||
setState(
|
||||
() => _defintion = response ?? L10n.of(context).meaningNotFound,
|
||||
);
|
||||
}
|
||||
} catch (e, s) {
|
||||
debugger(when: kDebugMode);
|
||||
ErrorHandler.logError(
|
||||
data: widget.cId.toJson(),
|
||||
e: e,
|
||||
s: s,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _setEditMode(bool editing) {
|
||||
setState(() => _isEditMode = editing);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
height: widget.height,
|
||||
width: widget.width,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Theme.of(context).colorScheme.onSurface.withAlpha(50),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: _isEditMode
|
||||
? EditMorphWidget(
|
||||
token: widget.token,
|
||||
pangeaMessageEvent: widget.pangeaMessageEvent,
|
||||
morphFeature: _morphFeature,
|
||||
onClose: () {
|
||||
_setEditMode(false);
|
||||
_fetchDefinition();
|
||||
widget.refresh();
|
||||
},
|
||||
)
|
||||
: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 16.0,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 24.0,
|
||||
height: 24.0,
|
||||
child: MorphIcon(
|
||||
morphFeature: _morphFeature,
|
||||
morphTag: _morphTag,
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
textAlign: TextAlign.center,
|
||||
getGrammarCopy(
|
||||
category: _morphFeature.name,
|
||||
lemma: _morphTag,
|
||||
context: context,
|
||||
) ??
|
||||
_morphTag,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
if (MatrixState.pangeaController.getAnalytics
|
||||
.constructListModel
|
||||
.getConstructUses(widget.cId) !=
|
||||
null)
|
||||
ConstructXpWidget(
|
||||
id: widget.cId,
|
||||
onTap: () => context.go(
|
||||
"/rooms/analytics/${ConstructTypeEnum.morph.string}/${widget.cId.string}",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16.0),
|
||||
child: _defintion != null
|
||||
? Text(
|
||||
_defintion!,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
)
|
||||
: const LinearProgressIndicator(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!_isEditMode)
|
||||
Positioned(
|
||||
top: 12.0,
|
||||
right: 12.0,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: GestureDetector(
|
||||
child: Icon(
|
||||
Icons.edit_outlined,
|
||||
size: 20.0,
|
||||
color: Theme.of(context).disabledColor,
|
||||
),
|
||||
onTap: () {
|
||||
_setEditMode(true);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -190,7 +190,7 @@ class WordZoomWidget extends StatelessWidget {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (MatrixState.pangeaController.languageController
|
||||
.showTrancription)
|
||||
.showTranscription)
|
||||
PhoneticTranscriptionWidget(
|
||||
text: token.text.content,
|
||||
textLanguage: PLanguageStore.byLangCode(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue