made matrix profile save one json value instead of one value per field, added documentation to user controller and user model
This commit is contained in:
parent
87b1b98f0e
commit
f69ab79c9d
34 changed files with 740 additions and 663 deletions
|
|
@ -50,10 +50,7 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
|
||||
final String _chatCountsKey = 'chatCounts';
|
||||
Map<String, int> get chatCounts => Map.from(
|
||||
widget.controller.pangeaController.pStoreService.read(
|
||||
_chatCountsKey,
|
||||
local: true,
|
||||
) ??
|
||||
widget.controller.pangeaController.pStoreService.read(_chatCountsKey) ??
|
||||
{},
|
||||
);
|
||||
// Pangea#
|
||||
|
|
@ -550,7 +547,6 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
await widget.controller.pangeaController.pStoreService.save(
|
||||
_chatCountsKey,
|
||||
updatedChatCounts,
|
||||
local: true,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/error_service.dart';
|
||||
import 'package:fluffychat/pangea/repo/full_text_translation_repo.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../../repo/similarity_repo.dart';
|
||||
|
||||
class AlternativeTranslator {
|
||||
|
|
@ -90,9 +89,19 @@ class AlternativeTranslator {
|
|||
final String? goldRouteTranslation =
|
||||
choreographer.itController.goldRouteTracker.fullTranslation;
|
||||
|
||||
final accessToken = await choreographer.accessToken;
|
||||
if (accessToken == null) {
|
||||
ErrorHandler.logError(
|
||||
m: "accessToken null in setTranslationFeedback",
|
||||
s: StackTrace.current,
|
||||
);
|
||||
translationFeedbackKey = FeedbackKey.loadingPleaseWait;
|
||||
return;
|
||||
}
|
||||
|
||||
final FullTextTranslationResponseModel results =
|
||||
await FullTextTranslationRepo.translate(
|
||||
accessToken: await choreographer.accessToken,
|
||||
accessToken: accessToken,
|
||||
request: FullTextTranslationRequestModel(
|
||||
text: choreographer.itController.sourceText!,
|
||||
tgtLang: choreographer.l2LangCode!,
|
||||
|
|
@ -118,7 +127,7 @@ class AlternativeTranslator {
|
|||
}
|
||||
|
||||
similarityResponse = await SimilarityRepo.get(
|
||||
accessToken: await choreographer.accessToken,
|
||||
accessToken: accessToken,
|
||||
request: SimilarityRequestModel(
|
||||
benchmark: results.bestTranslation,
|
||||
toCompare: [userTranslation!],
|
||||
|
|
|
|||
|
|
@ -407,7 +407,8 @@ class Choreographer {
|
|||
|
||||
PangeaTextController get textController => _textController;
|
||||
|
||||
Future<String> get accessToken => pangeaController.userController.accessToken;
|
||||
Future<String?> get accessToken =>
|
||||
pangeaController.userController.accessToken;
|
||||
|
||||
clear() {
|
||||
choreoMode = ChoreoMode.igc;
|
||||
|
|
@ -514,11 +515,7 @@ class Choreographer {
|
|||
chatController.room,
|
||||
);
|
||||
|
||||
bool get itAutoPlayEnabled =>
|
||||
pangeaController.pStoreService.read(
|
||||
MatrixProfile.itAutoPlay.title,
|
||||
) ??
|
||||
false;
|
||||
bool get itAutoPlayEnabled => MatrixProfile.itAutoPlay;
|
||||
|
||||
bool get definitionsEnabled =>
|
||||
pangeaController.permissionsController.isToolEnabled(
|
||||
|
|
|
|||
|
|
@ -72,8 +72,18 @@ class ITFeedbackCardController extends State<ITFeedbackCard> {
|
|||
setState(() {
|
||||
isTranslating = true;
|
||||
});
|
||||
|
||||
final String? accessToken = await controller.userController.accessToken;
|
||||
if (accessToken == null) {
|
||||
ErrorHandler.logError(
|
||||
m: "Cannot translate feedback because accessToken is null",
|
||||
);
|
||||
error = "Cannot translate feedback because accessToken is null";
|
||||
return;
|
||||
}
|
||||
|
||||
FullTextTranslationRepo.translate(
|
||||
accessToken: await controller.userController.accessToken,
|
||||
accessToken: accessToken,
|
||||
request: FullTextTranslationRequestModel(
|
||||
text: res!.text,
|
||||
tgtLang: controller.languageController.userL1?.langCode ??
|
||||
|
|
@ -197,7 +207,7 @@ class TranslateButton extends StatelessWidget {
|
|||
return TextButton(
|
||||
onPressed: loading ? null : onPress,
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all<Color>(
|
||||
backgroundColor: WidgetStateProperty.all<Color>(
|
||||
AppConfig.primaryColor.withOpacity(0.1),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,16 +1,8 @@
|
|||
class PLocalKey {
|
||||
static const String user = 'user';
|
||||
|
||||
static const String classes = 'classes';
|
||||
|
||||
static const String cachedClassCodeToJoin = "cachedclasscodetojoin";
|
||||
static const String beganWebPayment = "beganWebPayment";
|
||||
|
||||
// making this a random string so that it's harder to guess
|
||||
static const String activatedTrialKey = '7C4EuKIsph';
|
||||
static const String dismissedPaywall = 'dismissedPaywall';
|
||||
static const String paywallBackoff = 'paywallBackoff';
|
||||
static const String autoPlayMessages = 'autoPlayMessages';
|
||||
static const String itAutoPlay = 'itAutoPlay';
|
||||
static const String messagesSinceUpdate = 'messagesSinceLastUpdate';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,12 @@ class ModelKey {
|
|||
static const String publicProfile = 'public';
|
||||
static const String userId = 'user_id';
|
||||
|
||||
// matrix profile keys
|
||||
// making this a random string so that it's harder to guess
|
||||
static const String activatedTrialKey = '7C4EuKIsph';
|
||||
static const String autoPlayMessages = 'autoPlayMessages';
|
||||
static const String itAutoPlay = 'itAutoPlay';
|
||||
|
||||
static const String clientClassCity = "city";
|
||||
static const String clientClassCountry = "country";
|
||||
static const String clientClassDominantLanguage = "dominantLanguage";
|
||||
|
|
|
|||
|
|
@ -48,15 +48,13 @@ class ClassController extends BaseController {
|
|||
Future<void> checkForClassCodeAndSubscription(BuildContext context) async {
|
||||
final String? classCode = _pangeaController.pStoreService.read(
|
||||
PLocalKey.cachedClassCodeToJoin,
|
||||
addClientIdToKey: false,
|
||||
local: true,
|
||||
isAccountData: false,
|
||||
);
|
||||
|
||||
if (classCode != null) {
|
||||
await _pangeaController.pStoreService.delete(
|
||||
PLocalKey.cachedClassCodeToJoin,
|
||||
addClientIdToKey: false,
|
||||
local: true,
|
||||
isAccountData: false,
|
||||
);
|
||||
await joinClasswithCode(
|
||||
context,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
||||
import '../constants/model_keys.dart';
|
||||
import '../network/requests.dart';
|
||||
import '../network/urls.dart';
|
||||
|
|
@ -50,9 +49,17 @@ class ContextualDefinitionController {
|
|||
ContextualDefinitionRequestModel request,
|
||||
) async {
|
||||
try {
|
||||
final accessToken = await _pangeaController.userController.accessToken;
|
||||
if (accessToken == null) {
|
||||
ErrorHandler.logError(
|
||||
e: "null accessToken in contextual definition controller",
|
||||
s: StackTrace.current,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
final ContextualDefinitionResponseModel res =
|
||||
await _ContextualDefinitionRepo.define(
|
||||
await _pangeaController.userController.accessToken,
|
||||
accessToken,
|
||||
request,
|
||||
);
|
||||
return res;
|
||||
|
|
|
|||
|
|
@ -51,8 +51,17 @@ class ITFeedbackController {
|
|||
ITFeedbackRequestModel request,
|
||||
) async {
|
||||
try {
|
||||
final String? accessToken =
|
||||
await _pangeaController.userController.accessToken;
|
||||
if (accessToken == null) {
|
||||
ErrorHandler.logError(
|
||||
e: "null accessToken in it feedback controller",
|
||||
s: StackTrace.current,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
final ITFeedbackResponseModel res = await _ITFeedbackRepo.get(
|
||||
await _pangeaController.userController.accessToken,
|
||||
accessToken,
|
||||
request,
|
||||
);
|
||||
return res;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import 'package:fluffychat/pangea/constants/language_constants.dart';
|
|||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/models/language_detection_model.dart';
|
||||
import 'package:fluffychat/pangea/network/urls.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../network/requests.dart';
|
||||
|
|
@ -125,7 +126,7 @@ class LanguageDetectionController {
|
|||
_cacheClearTimer?.cancel();
|
||||
}
|
||||
|
||||
Future<LanguageDetectionResponse> detectLanguage(
|
||||
Future<LanguageDetectionResponse?> detectLanguage(
|
||||
String fullText,
|
||||
String? userL2,
|
||||
String? userL1,
|
||||
|
|
@ -138,14 +139,23 @@ class LanguageDetectionController {
|
|||
return get(params);
|
||||
}
|
||||
|
||||
Future<LanguageDetectionResponse> get(
|
||||
Future<LanguageDetectionResponse?> get(
|
||||
LanguageDetectionRequest params,
|
||||
) async {
|
||||
if (_cache.containsKey(params)) {
|
||||
return _cache[params]!.data;
|
||||
} else {
|
||||
final String? accessToken =
|
||||
await _pangeaController.userController.accessToken;
|
||||
if (accessToken == null) {
|
||||
ErrorHandler.logError(
|
||||
e: "null accessToken in language detection controller",
|
||||
s: StackTrace.current,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
final Future<LanguageDetectionResponse> response = _fetchResponse(
|
||||
await _pangeaController.userController.accessToken,
|
||||
accessToken,
|
||||
params,
|
||||
);
|
||||
_cache[params] = _LanguageDetectionCacheItem(data: response);
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/models/space_model.dart';
|
||||
|
||||
class LocalSettings {
|
||||
late PangeaController _pangeaController;
|
||||
|
||||
LocalSettings(PangeaController pangeaController) : super() {
|
||||
_pangeaController = pangeaController;
|
||||
}
|
||||
|
||||
bool userLanguageToolSetting(ToolSetting setting) {
|
||||
final profileSetting =
|
||||
_pangeaController.pStoreService.read(setting.toString());
|
||||
if (profileSetting != null) {
|
||||
return profileSetting;
|
||||
}
|
||||
return setting == ToolSetting.immersionMode ? false : true;
|
||||
}
|
||||
|
||||
// bool get userEnableIT =>
|
||||
// _pangeaController.pStoreService.read(ToolSetting.interactiveTranslator.toString()) ?? true;
|
||||
|
||||
// bool get userEnableIGC =>
|
||||
// _pangeaController.pStoreService.read(ToolSetting.interactiveGrammar.toString()) ?? true;
|
||||
|
||||
// bool get userImmersionMode =>
|
||||
// _pangeaController.pStoreService.read(ToolSetting.immersionMode.toString()) ?? true;
|
||||
|
||||
// bool get userTranslationsTool =>
|
||||
// _pangeaController.pStoreService.read(ToolSetting.translations.toString()) ?? true;
|
||||
|
||||
// bool get userDefinitionsTool =>
|
||||
// _pangeaController.pStoreService.read(ToolSetting.definitions.toString()) ?? true;
|
||||
}
|
||||
|
|
@ -42,7 +42,6 @@ class AnalyticsController extends BaseController {
|
|||
try {
|
||||
final String? str = _pangeaController.pStoreService.read(
|
||||
_analyticsTimeSpanKey,
|
||||
local: true,
|
||||
);
|
||||
return str != null
|
||||
? TimeSpan.values.firstWhere((e) {
|
||||
|
|
@ -60,7 +59,6 @@ class AnalyticsController extends BaseController {
|
|||
await _pangeaController.pStoreService.save(
|
||||
_analyticsTimeSpanKey,
|
||||
timeSpan.toString(),
|
||||
local: true,
|
||||
);
|
||||
setState();
|
||||
}
|
||||
|
|
@ -72,7 +70,6 @@ class AnalyticsController extends BaseController {
|
|||
try {
|
||||
final String? str = _pangeaController.pStoreService.read(
|
||||
_analyticsSpaceLangKey,
|
||||
local: true,
|
||||
);
|
||||
return str != null
|
||||
? PangeaLanguage.byLangCode(str)
|
||||
|
|
@ -88,7 +85,6 @@ class AnalyticsController extends BaseController {
|
|||
await _pangeaController.pStoreService.save(
|
||||
_analyticsSpaceLangKey,
|
||||
lang.langCode,
|
||||
local: true,
|
||||
);
|
||||
setState();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,14 @@ class MessageDataController extends BaseController {
|
|||
) async {
|
||||
final accessToken = await _pangeaController.userController.accessToken;
|
||||
|
||||
if (accessToken == null) {
|
||||
ErrorHandler.logError(
|
||||
e: "null accessToken in _getTokens",
|
||||
s: StackTrace.current,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
final TokensResponseModel igcTextData =
|
||||
await TokensRepo.tokenize(accessToken, req);
|
||||
|
||||
|
|
@ -193,9 +201,19 @@ class MessageDataController extends BaseController {
|
|||
);
|
||||
|
||||
try {
|
||||
final String? accessToken =
|
||||
await _pangeaController.userController.accessToken;
|
||||
if (accessToken == null) {
|
||||
ErrorHandler.logError(
|
||||
e: "null accessToken in _getPangeaRepresentation",
|
||||
s: StackTrace.current,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
final FullTextTranslationResponseModel res =
|
||||
await FullTextTranslationRepo.translate(
|
||||
accessToken: await _pangeaController.userController.accessToken,
|
||||
accessToken: accessToken,
|
||||
request: req,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,6 @@ class MyAnalyticsController {
|
|||
_pangeaController.pStoreService.save(
|
||||
PLocalKey.messagesSinceUpdate,
|
||||
currentCache,
|
||||
local: true,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -155,7 +154,6 @@ class MyAnalyticsController {
|
|||
_pangeaController.pStoreService.save(
|
||||
PLocalKey.messagesSinceUpdate,
|
||||
[],
|
||||
local: true,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -167,14 +165,12 @@ class MyAnalyticsController {
|
|||
Logs().d('Reading messages since update from local storage');
|
||||
final dynamic locallySaved = _pangeaController.pStoreService.read(
|
||||
PLocalKey.messagesSinceUpdate,
|
||||
local: true,
|
||||
);
|
||||
if (locallySaved == null) {
|
||||
Logs().d('No locally saved messages found, initializing empty list.');
|
||||
_pangeaController.pStoreService.save(
|
||||
PLocalKey.messagesSinceUpdate,
|
||||
[],
|
||||
local: true,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
|
@ -201,7 +197,6 @@ class MyAnalyticsController {
|
|||
_pangeaController.pStoreService.save(
|
||||
PLocalKey.messagesSinceUpdate,
|
||||
[],
|
||||
local: true,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import 'package:fluffychat/pangea/controllers/contextual_definition_controller.d
|
|||
import 'package:fluffychat/pangea/controllers/language_controller.dart';
|
||||
import 'package:fluffychat/pangea/controllers/language_detection_controller.dart';
|
||||
import 'package:fluffychat/pangea/controllers/language_list_controller.dart';
|
||||
import 'package:fluffychat/pangea/controllers/local_settings.dart';
|
||||
import 'package:fluffychat/pangea/controllers/message_data_controller.dart';
|
||||
import 'package:fluffychat/pangea/controllers/my_analytics_controller.dart';
|
||||
import 'package:fluffychat/pangea/controllers/permissions_controller.dart';
|
||||
|
|
@ -47,7 +46,6 @@ class PangeaController {
|
|||
late AnalyticsController analytics;
|
||||
late MyAnalyticsController myAnalytics;
|
||||
late WordController wordNet;
|
||||
late LocalSettings localSettings;
|
||||
late MessageDataController messageData;
|
||||
late ContextualDefinitionController definitions;
|
||||
late ITFeedbackController itFeedback;
|
||||
|
|
@ -60,7 +58,7 @@ class PangeaController {
|
|||
late PracticeGenerationController practiceGenerationController;
|
||||
|
||||
///store Services
|
||||
late PLocalStore pStoreService;
|
||||
late PStore pStoreService;
|
||||
final pLanguageStore = PangeaLanguage();
|
||||
|
||||
///Matrix Variables
|
||||
|
|
@ -89,10 +87,9 @@ class PangeaController {
|
|||
|
||||
/// Initialize controllers
|
||||
_addRefInObjects() {
|
||||
pStoreService = PLocalStore(pangeaController: this);
|
||||
pStoreService = PStore(pangeaController: this);
|
||||
userController = UserController(this);
|
||||
languageController = LanguageController(this);
|
||||
localSettings = LocalSettings(this);
|
||||
classController = ClassController(this);
|
||||
permissionsController = PermissionsController(this);
|
||||
analytics = AnalyticsController(this);
|
||||
|
|
|
|||
|
|
@ -31,14 +31,11 @@ class PermissionsController extends BaseController {
|
|||
}
|
||||
|
||||
/// Returns false if user is null
|
||||
bool isUser18() {
|
||||
final dob = _pangeaController.pStoreService.read(
|
||||
MatrixProfile.dateOfBirth.title,
|
||||
);
|
||||
return dob != null
|
||||
? DateTime.parse(dob).isAtLeastYearsOld(AgeLimits.toAccessFeatures)
|
||||
: false;
|
||||
}
|
||||
bool isUser18() =>
|
||||
MatrixProfile.dateOfBirth?.isAtLeastYearsOld(
|
||||
AgeLimits.toAccessFeatures,
|
||||
) ??
|
||||
false;
|
||||
|
||||
/// A user can private chat if
|
||||
/// 1) they are 18 and outside a class context or
|
||||
|
|
@ -99,18 +96,32 @@ class PermissionsController extends BaseController {
|
|||
return classPermission == 0;
|
||||
}
|
||||
|
||||
bool userToolSetting(ToolSetting setting) =>
|
||||
_pangeaController.localSettings.userLanguageToolSetting(setting);
|
||||
bool userToolSetting(MatrixProfileEnum setting) {
|
||||
switch (setting.asToolSetting) {
|
||||
case ToolSetting.interactiveTranslator:
|
||||
return MatrixProfile.interactiveTranslator;
|
||||
case ToolSetting.interactiveGrammar:
|
||||
return MatrixProfile.interactiveGrammar;
|
||||
case ToolSetting.immersionMode:
|
||||
return MatrixProfile.immersionMode;
|
||||
case ToolSetting.definitions:
|
||||
return MatrixProfile.definitions;
|
||||
case ToolSetting.autoIGC:
|
||||
return MatrixProfile.autoIGC;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool isToolEnabled(ToolSetting setting, Room? room) {
|
||||
if (room?.isSpaceAdmin ?? false) {
|
||||
return userToolSetting(setting);
|
||||
return userToolSetting(setting.asMatrixProfileField);
|
||||
}
|
||||
final int? classPermission =
|
||||
room != null ? classLanguageToolPermission(room, setting) : 1;
|
||||
if (classPermission == 0) return false;
|
||||
if (classPermission == 2) return true;
|
||||
return userToolSetting(setting);
|
||||
return userToolSetting(setting.asMatrixProfileField);
|
||||
}
|
||||
|
||||
bool isWritingAssistanceEnabled(Room? room) {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class SpeechToTextController {
|
|||
_cacheClearTimer?.cancel();
|
||||
}
|
||||
|
||||
Future<SpeechToTextModel> get(
|
||||
Future<SpeechToTextModel?> get(
|
||||
SpeechToTextRequestModel requestModel,
|
||||
) async {
|
||||
final int cacheKey = requestModel.hashCode;
|
||||
|
|
@ -52,8 +52,18 @@ class SpeechToTextController {
|
|||
if (_cache.containsKey(cacheKey)) {
|
||||
return _cache[cacheKey]!.data;
|
||||
} else {
|
||||
final String? accessToken =
|
||||
await _pangeaController.userController.accessToken;
|
||||
if (accessToken == null) {
|
||||
ErrorHandler.logError(
|
||||
e: 'null accessToken in speech to text controller',
|
||||
s: StackTrace.current,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
final Future<SpeechToTextModel> response = _fetchResponse(
|
||||
accessToken: await _pangeaController.userController.accessToken,
|
||||
accessToken: accessToken,
|
||||
requestModel: requestModel,
|
||||
);
|
||||
_cache[cacheKey] = _SpeechToTextCacheItem(data: response);
|
||||
|
|
|
|||
|
|
@ -97,12 +97,10 @@ class SubscriptionController extends BaseController {
|
|||
} else {
|
||||
final bool? beganWebPayment = _pangeaController.pStoreService.read(
|
||||
PLocalKey.beganWebPayment,
|
||||
local: true,
|
||||
);
|
||||
if (beganWebPayment ?? false) {
|
||||
await _pangeaController.pStoreService.delete(
|
||||
PLocalKey.beganWebPayment,
|
||||
local: true,
|
||||
);
|
||||
if (_pangeaController.subscriptionController.isSubscribed) {
|
||||
subscriptionStream.add(true);
|
||||
|
|
@ -142,7 +140,6 @@ class SubscriptionController extends BaseController {
|
|||
await _pangeaController.pStoreService.save(
|
||||
PLocalKey.beganWebPayment,
|
||||
true,
|
||||
local: true,
|
||||
);
|
||||
setState();
|
||||
launchUrlString(
|
||||
|
|
@ -184,18 +181,12 @@ class SubscriptionController extends BaseController {
|
|||
|
||||
bool get _activatedNewUserTrial =>
|
||||
_pangeaController.userController.inTrialWindow &&
|
||||
(_pangeaController.pStoreService.read(
|
||||
MatrixProfile.activatedFreeTrial.title,
|
||||
) ??
|
||||
false);
|
||||
MatrixProfile.activatedFreeTrial;
|
||||
|
||||
void activateNewUserTrial() {
|
||||
_pangeaController.pStoreService
|
||||
.save(
|
||||
MatrixProfile.activatedFreeTrial.title,
|
||||
true,
|
||||
)
|
||||
.then((_) {
|
||||
MatrixProfile.saveProfileData({
|
||||
MatrixProfileEnum.activatedFreeTrial.title: true,
|
||||
}).then((_) {
|
||||
setNewUserTrial();
|
||||
trialActivationStream.add(true);
|
||||
});
|
||||
|
|
@ -242,7 +233,6 @@ class SubscriptionController extends BaseController {
|
|||
DateTime? get _lastDismissedPaywall {
|
||||
final lastDismissed = _pangeaController.pStoreService.read(
|
||||
PLocalKey.dismissedPaywall,
|
||||
local: true,
|
||||
);
|
||||
if (lastDismissed == null) return null;
|
||||
return DateTime.tryParse(lastDismissed);
|
||||
|
|
@ -251,7 +241,6 @@ class SubscriptionController extends BaseController {
|
|||
int? get _paywallBackoff {
|
||||
final backoff = _pangeaController.pStoreService.read(
|
||||
PLocalKey.paywallBackoff,
|
||||
local: true,
|
||||
);
|
||||
if (backoff == null) return null;
|
||||
return backoff;
|
||||
|
|
@ -269,20 +258,17 @@ class SubscriptionController extends BaseController {
|
|||
await _pangeaController.pStoreService.save(
|
||||
PLocalKey.dismissedPaywall,
|
||||
DateTime.now().toString(),
|
||||
local: true,
|
||||
);
|
||||
|
||||
if (_paywallBackoff == null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
PLocalKey.paywallBackoff,
|
||||
1,
|
||||
local: true,
|
||||
);
|
||||
} else {
|
||||
await _pangeaController.pStoreService.save(
|
||||
PLocalKey.paywallBackoff,
|
||||
_paywallBackoff! + 1,
|
||||
local: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import 'package:fluffychat/pangea/config/environment.dart';
|
|||
import 'package:fluffychat/pangea/constants/model_keys.dart';
|
||||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/network/urls.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
||||
import '../network/requests.dart';
|
||||
|
|
@ -93,14 +94,23 @@ class TextToSpeechController {
|
|||
_cacheClearTimer?.cancel();
|
||||
}
|
||||
|
||||
Future<TextToSpeechResponse> get(
|
||||
Future<TextToSpeechResponse?> get(
|
||||
TextToSpeechRequest params,
|
||||
) async {
|
||||
if (_cache.containsKey(params)) {
|
||||
return _cache[params]!.data;
|
||||
} else {
|
||||
final String? accessToken =
|
||||
await _pangeaController.userController.accessToken;
|
||||
if (accessToken == null) {
|
||||
ErrorHandler.logError(
|
||||
e: "null accessToken in text to speech controller",
|
||||
s: StackTrace.current,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
final Future<TextToSpeechResponse> response = _fetchResponse(
|
||||
await _pangeaController.userController.accessToken,
|
||||
accessToken,
|
||||
params,
|
||||
);
|
||||
_cache[params] = _TextToSpeechCacheItem(data: response);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import 'package:fluffychat/pangea/constants/language_constants.dart';
|
|||
import 'package:fluffychat/pangea/constants/model_keys.dart';
|
||||
import 'package:fluffychat/pangea/controllers/base_controller.dart';
|
||||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:jwt_decode/jwt_decode.dart';
|
||||
import 'package:matrix/matrix.dart' as matrix;
|
||||
|
||||
|
|
@ -13,14 +13,44 @@ import '../constants/local.key.dart';
|
|||
import '../models/user_model.dart';
|
||||
import '../repo/user_repo.dart';
|
||||
|
||||
/// Controller that manages saving and reading of user/profile information
|
||||
class UserController extends BaseController {
|
||||
late PangeaController _pangeaController;
|
||||
final Completer _completer = Completer();
|
||||
UserController(PangeaController pangeaController) : super() {
|
||||
_pangeaController = pangeaController;
|
||||
}
|
||||
|
||||
Future<void> createPangeaUser({required String dob}) async {
|
||||
/// Convenience function that returns the user ID currently stored in the client.
|
||||
String? get userId => _pangeaController.matrixState.client.userID;
|
||||
|
||||
/// Convenience function that returns the accessToken currently stored in the client.
|
||||
String? get _matrixAccessToken =>
|
||||
_pangeaController.matrixState.client.accessToken;
|
||||
|
||||
/// Returns the [PUserModel] object representing the current user.
|
||||
///
|
||||
/// This method retrieves the user data from the local storage using the [PLocalKey.user] key.
|
||||
/// If the data exists, it is converted to a [PUserModel] object using the [PUserModel.fromJson] method.
|
||||
/// If the data is null, indicating that the user is not logged in (or that
|
||||
/// profile fetching has not yet completed, or had an error), null is returned.
|
||||
PUserModel? get userModel {
|
||||
final data = _pangeaController.pStoreService.read(PLocalKey.user);
|
||||
return data != null ? PUserModel.fromJson(data) : null;
|
||||
}
|
||||
|
||||
/// Creates a user pangea chat profile, saves the user's profile information
|
||||
/// locally, and set the user's DOB in their matrix profile.
|
||||
///
|
||||
/// The [dob] parameter is required and represents the date of birth of the user.
|
||||
/// This method creates a new [PUserModel] using the [PUserRepo.repoCreatePangeaUser] method,
|
||||
/// and saves the user model in local storage.
|
||||
/// It also updates the user's matrix profile using the [updateMatrixProfile] method.
|
||||
Future<void> createProfile({required String dob}) async {
|
||||
if (userId == null || _matrixAccessToken == null) {
|
||||
ErrorHandler.logError(
|
||||
e: "calling createProfile with userId == null or matrixAccessToken == null",
|
||||
);
|
||||
}
|
||||
final PUserModel newUserModel = await PUserRepo.repoCreatePangeaUser(
|
||||
userID: userId!,
|
||||
fullName: fullname,
|
||||
|
|
@ -28,139 +58,167 @@ class UserController extends BaseController {
|
|||
matrixAccessToken: _matrixAccessToken!,
|
||||
);
|
||||
newUserModel.save(_pangeaController);
|
||||
await updateMatrixProfile(dateOfBirth: dob);
|
||||
await MatrixProfile.saveProfileData(
|
||||
{MatrixProfileEnum.dateOfBirth.title: dob},
|
||||
waitForDataInSync: true,
|
||||
);
|
||||
}
|
||||
|
||||
/// A boolean flag indicating whether the profile data is currently being fetched.
|
||||
bool _isFetching = false;
|
||||
|
||||
/// A completer for the profile model of a user.
|
||||
Completer<PUserModel?> _profileCompleter = Completer<PUserModel?>();
|
||||
|
||||
/// Fetches the user model.
|
||||
///
|
||||
/// This method retrieves the user model asynchronously. If the profile completer is already completed,
|
||||
/// it returns the future value of the completer. If the user model is currently being fetched,
|
||||
/// it waits for the completion of the completer and returns the future value. Otherwise, it sets
|
||||
/// the fetching flag, fetches the user model, completes the profile completer with the fetched user model,
|
||||
/// and returns the future value of the completer.
|
||||
///
|
||||
/// Returns the future value of the user model completer.
|
||||
Future<PUserModel?> fetchUserModel() async {
|
||||
try {
|
||||
if (_matrixAccessToken == null) {
|
||||
throw Exception(
|
||||
"calling fetchUserModel with matrixAccesstoken == null",
|
||||
);
|
||||
}
|
||||
|
||||
final PUserModel? newUserModel = await PUserRepo.fetchPangeaUserInfo(
|
||||
userID: userId!,
|
||||
matrixAccessToken: _matrixAccessToken!,
|
||||
);
|
||||
newUserModel?.save(_pangeaController);
|
||||
await migrateMatrixProfile();
|
||||
_completeCompleter();
|
||||
|
||||
return newUserModel;
|
||||
} catch (err) {
|
||||
debugPrint(
|
||||
"User model not found. Probably first signup and needs Pangea account",
|
||||
);
|
||||
rethrow;
|
||||
if (_profileCompleter.isCompleted) return _profileCompleter.future;
|
||||
if (_isFetching) {
|
||||
await _profileCompleter.future;
|
||||
return _profileCompleter.future;
|
||||
}
|
||||
|
||||
_isFetching = true;
|
||||
PUserModel? fetchedUserModel;
|
||||
|
||||
try {
|
||||
fetchedUserModel = await _fetchUserModel();
|
||||
} catch (err, s) {
|
||||
ErrorHandler.logError(e: err, s: s);
|
||||
return null;
|
||||
}
|
||||
|
||||
_isFetching = false;
|
||||
_profileCompleter.complete(fetchedUserModel);
|
||||
return _profileCompleter.future;
|
||||
}
|
||||
|
||||
dynamic migratedProfileInfo(MatrixProfile key) {
|
||||
final dynamic localValue = _pangeaController.pStoreService.read(
|
||||
key.title,
|
||||
local: true,
|
||||
/// Fetches the user model asynchronously.
|
||||
///
|
||||
/// This method fetches the user model by calling the [fetchPangeaUserInfo] method
|
||||
/// from the [PUserRepo] class. It requires the [_matrixAccessToken] and [userId]
|
||||
/// to be non-null. If either of them is null, an error is logged.
|
||||
///
|
||||
/// The fetched [newUserModel] is then saved locally.
|
||||
/// The [migrateMatrixProfile] method is called, to migrate any information that is
|
||||
/// already saved in the user's pangea profile but is not yet saved in the
|
||||
/// user's matrix profile. Finally, the [newUserModel] is returned.
|
||||
Future<PUserModel?> _fetchUserModel() async {
|
||||
if (_matrixAccessToken == null || userId == null) {
|
||||
ErrorHandler.logError(
|
||||
e: "calling fetchUserModel with userId == null or matrixAccessToken == null",
|
||||
);
|
||||
return null;
|
||||
}
|
||||
final PUserModel? newUserModel = await PUserRepo.fetchPangeaUserInfo(
|
||||
userID: userId!,
|
||||
matrixAccessToken: _matrixAccessToken!,
|
||||
);
|
||||
final dynamic matrixValue = _pangeaController.pStoreService.read(
|
||||
key.title,
|
||||
);
|
||||
return localValue != null && matrixValue != localValue ? localValue : null;
|
||||
newUserModel?.save(_pangeaController);
|
||||
await migrateMatrixProfile();
|
||||
return newUserModel;
|
||||
}
|
||||
|
||||
/// Reinitializes the user's profile
|
||||
///
|
||||
/// This method sets up the necessary variables and fetches the user model.
|
||||
/// It completes the [_profileCompleter] with the fetched user model.
|
||||
/// This method should be called whenever the user's login status changes
|
||||
Future<void> reinitialize() async {
|
||||
_profileCompleter = Completer<PUserModel?>();
|
||||
_isFetching = false;
|
||||
await fetchUserModel();
|
||||
}
|
||||
|
||||
/// Migrates the user's profile from Pangea to Matrix.
|
||||
///
|
||||
/// This method retrieves the user's profile / local settings information from Pangea and checks for corresponding information stored in Matrix.
|
||||
/// If any of the profile fields in Pangea have information, but the corresponding fields in Matrix are null, the values are updated in Matrix.
|
||||
/// The profile fields that are checked for migration include date of birth, creation date, target language, source language, country, and public profile.
|
||||
/// Additionally, several profile settings related to auto play, trial activation, interactive features, and instructional messages are also checked for migration.
|
||||
///
|
||||
/// This method calls the [updateMatrixProfile] method to update the user's profile in Matrix with the migrated values.
|
||||
///
|
||||
/// Note: This method assumes that the [userModel] and [_pangeaController] instances are properly initialized before calling this method.
|
||||
Future<void> migrateMatrixProfile() async {
|
||||
// This function relies on the client's account data being loaded.
|
||||
// The account data is loaded during
|
||||
// the first sync, so wait for that to complete.
|
||||
final client = _pangeaController.matrixState.client;
|
||||
if (client.prevBatch == null) {
|
||||
await client.onSync.stream.first;
|
||||
}
|
||||
|
||||
final Map<String, dynamic> profileUpdates = {};
|
||||
final Profile? pangeaProfile = userModel?.profile;
|
||||
|
||||
final String? pangeaDob = pangeaProfile?.dateOfBirth;
|
||||
final String? matrixDob = _pangeaController.pStoreService.read(
|
||||
ModelKey.userDateOfBirth,
|
||||
);
|
||||
final String? dob =
|
||||
pangeaDob != null && matrixDob != pangeaDob ? pangeaDob : null;
|
||||
for (final field in MatrixProfile.pangeaProfileFields) {
|
||||
final dynamic matrixValue = MatrixProfile.getProfileData(field);
|
||||
dynamic pangeaValue;
|
||||
switch (field) {
|
||||
case MatrixProfileEnum.dateOfBirth:
|
||||
pangeaValue = pangeaProfile?.dateOfBirth;
|
||||
break;
|
||||
case MatrixProfileEnum.createdAt:
|
||||
pangeaValue = pangeaProfile?.createdAt;
|
||||
break;
|
||||
case MatrixProfileEnum.targetLanguage:
|
||||
pangeaValue = pangeaProfile?.targetLanguage;
|
||||
break;
|
||||
case MatrixProfileEnum.sourceLanguage:
|
||||
pangeaValue = pangeaProfile?.sourceLanguage;
|
||||
break;
|
||||
case MatrixProfileEnum.country:
|
||||
pangeaValue = pangeaProfile?.country;
|
||||
break;
|
||||
case MatrixProfileEnum.publicProfile:
|
||||
pangeaValue = pangeaProfile?.publicProfile;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (pangeaValue != null && matrixValue == null) {
|
||||
profileUpdates[field.title] = pangeaValue;
|
||||
}
|
||||
}
|
||||
|
||||
final pangeaCreatedAt = pangeaProfile?.createdAt;
|
||||
final matrixCreatedAt = _pangeaController.pStoreService.read(
|
||||
MatrixProfile.createdAt.title,
|
||||
);
|
||||
final String? createdAt =
|
||||
pangeaCreatedAt != null && matrixCreatedAt != pangeaCreatedAt
|
||||
? pangeaCreatedAt
|
||||
: null;
|
||||
for (final value in MatrixProfileEnum.values) {
|
||||
if (profileUpdates.containsKey(value.title)) continue;
|
||||
final dynamic localValue =
|
||||
_pangeaController.pStoreService.read(value.title);
|
||||
final dynamic matrixValue = MatrixProfile.getProfileData(value);
|
||||
final dynamic unmigratedValue =
|
||||
localValue != null && matrixValue == null ? localValue : null;
|
||||
if (unmigratedValue != null) {
|
||||
profileUpdates[value.title] = unmigratedValue;
|
||||
}
|
||||
}
|
||||
|
||||
final String? pangeaTargetLanguage = pangeaProfile?.targetLanguage;
|
||||
final String? matrixTargetLanguage = _pangeaController.pStoreService.read(
|
||||
MatrixProfile.targetLanguage.title,
|
||||
);
|
||||
final String? targetLanguage = pangeaTargetLanguage != null &&
|
||||
matrixTargetLanguage != pangeaTargetLanguage
|
||||
? pangeaTargetLanguage
|
||||
: null;
|
||||
|
||||
final String? pangeaSourceLanguage = pangeaProfile?.sourceLanguage;
|
||||
final String? matrixSourceLanguage = _pangeaController.pStoreService.read(
|
||||
MatrixProfile.sourceLanguage.title,
|
||||
);
|
||||
final String? sourceLanguage = pangeaSourceLanguage != null &&
|
||||
matrixSourceLanguage != pangeaSourceLanguage
|
||||
? pangeaSourceLanguage
|
||||
: null;
|
||||
|
||||
final String? pangeaCountry = pangeaProfile?.country;
|
||||
final String? matrixCountry = _pangeaController.pStoreService.read(
|
||||
MatrixProfile.country.title,
|
||||
);
|
||||
final String? country =
|
||||
pangeaCountry != null && matrixCountry != pangeaCountry
|
||||
? pangeaCountry
|
||||
: null;
|
||||
|
||||
final bool? pangeaPublicProfile = pangeaProfile?.publicProfile;
|
||||
final bool? matrixPublicProfile = _pangeaController.pStoreService.read(
|
||||
MatrixProfile.publicProfile.title,
|
||||
);
|
||||
final bool? publicProfile = pangeaPublicProfile != null &&
|
||||
matrixPublicProfile != pangeaPublicProfile
|
||||
? pangeaPublicProfile
|
||||
: null;
|
||||
|
||||
final bool? autoPlay = migratedProfileInfo(MatrixProfile.autoPlayMessages);
|
||||
final bool? itAutoPlay = migratedProfileInfo(MatrixProfile.itAutoPlay);
|
||||
final bool? trial = migratedProfileInfo(MatrixProfile.activatedFreeTrial);
|
||||
final bool? interactiveTranslator =
|
||||
migratedProfileInfo(MatrixProfile.interactiveTranslator);
|
||||
final bool? interactiveGrammar =
|
||||
migratedProfileInfo(MatrixProfile.interactiveGrammar);
|
||||
final bool? immersionMode =
|
||||
migratedProfileInfo(MatrixProfile.immersionMode);
|
||||
final bool? definitions = migratedProfileInfo(MatrixProfile.definitions);
|
||||
// final bool? translations = migratedProfileInfo(MatrixProfile.translations);
|
||||
final bool? showItInstructions =
|
||||
migratedProfileInfo(MatrixProfile.showedItInstructions);
|
||||
final bool? showClickMessage =
|
||||
migratedProfileInfo(MatrixProfile.showedClickMessage);
|
||||
final bool? showBlurMeansTranslate =
|
||||
migratedProfileInfo(MatrixProfile.showedBlurMeansTranslate);
|
||||
|
||||
await updateMatrixProfile(
|
||||
dateOfBirth: dob,
|
||||
autoPlayMessages: autoPlay,
|
||||
itAutoPlay: itAutoPlay,
|
||||
activatedFreeTrial: trial,
|
||||
interactiveTranslator: interactiveTranslator,
|
||||
interactiveGrammar: interactiveGrammar,
|
||||
immersionMode: immersionMode,
|
||||
definitions: definitions,
|
||||
// translations: translations,
|
||||
showedItInstructions: showItInstructions,
|
||||
showedClickMessage: showClickMessage,
|
||||
showedBlurMeansTranslate: showBlurMeansTranslate,
|
||||
createdAt: createdAt,
|
||||
targetLanguage: targetLanguage,
|
||||
sourceLanguage: sourceLanguage,
|
||||
country: country,
|
||||
publicProfile: publicProfile,
|
||||
await MatrixProfile.saveProfileData(
|
||||
profileUpdates,
|
||||
waitForDataInSync: true,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates the user's profile with the provided information.
|
||||
///
|
||||
/// The [dateOfBirth] parameter is the new date of birth for the user.
|
||||
/// The [targetLanguage] parameter is the new target language for the user.
|
||||
/// The [sourceLanguage] parameter is the new source language for the user.
|
||||
/// The [country] parameter is the new country for the user.
|
||||
/// The [interests] parameter is a list of new interests for the user.
|
||||
/// The [speaks] parameter is a list of new languages the user speaks.
|
||||
/// The [publicProfile] parameter indicates whether the user's profile should be public or not.
|
||||
///
|
||||
/// Throws an error if [userModel] or [accessToken] is null.
|
||||
Future<void> updateUserProfile({
|
||||
String? dateOfBirth,
|
||||
String? targetLanguage,
|
||||
|
|
@ -170,7 +228,14 @@ class UserController extends BaseController {
|
|||
List<String>? speaks,
|
||||
bool? publicProfile,
|
||||
}) async {
|
||||
if (userModel == null) throw Exception("Local userModel not defined");
|
||||
final String? accessToken = await this.accessToken;
|
||||
if (userModel == null || accessToken == null) {
|
||||
ErrorHandler.logError(
|
||||
e: "calling updateUserProfile with userModel == null or accessToken == null",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final profileJson = userModel!.profile!.toJson();
|
||||
|
||||
if (dateOfBirth != null) {
|
||||
|
|
@ -194,230 +259,95 @@ class UserController extends BaseController {
|
|||
if (publicProfile != null) {
|
||||
profileJson[ModelKey.publicProfile] = publicProfile;
|
||||
}
|
||||
|
||||
final Profile updatedUserProfile = await PUserRepo.updateUserProfile(
|
||||
Profile.fromJson(profileJson),
|
||||
await accessToken,
|
||||
accessToken,
|
||||
);
|
||||
|
||||
PUserModel(
|
||||
access: await accessToken,
|
||||
access: accessToken,
|
||||
refresh: userModel!.refresh,
|
||||
profile: updatedUserProfile,
|
||||
).save(_pangeaController);
|
||||
|
||||
await updateMatrixProfile(
|
||||
dateOfBirth: dateOfBirth,
|
||||
targetLanguage: targetLanguage,
|
||||
sourceLanguage: sourceLanguage,
|
||||
country: country,
|
||||
publicProfile: publicProfile,
|
||||
);
|
||||
}
|
||||
|
||||
PUserModel? get userModel {
|
||||
final data = _pangeaController.pStoreService.read(
|
||||
PLocalKey.user,
|
||||
local: true,
|
||||
);
|
||||
return data != null ? PUserModel.fromJson(data) : null;
|
||||
}
|
||||
|
||||
Future<void> updateMatrixProfile({
|
||||
String? dateOfBirth,
|
||||
bool? autoPlayMessages,
|
||||
bool? itAutoPlay,
|
||||
bool? activatedFreeTrial,
|
||||
bool? interactiveTranslator,
|
||||
bool? interactiveGrammar,
|
||||
bool? immersionMode,
|
||||
bool? definitions,
|
||||
// bool? translations,
|
||||
bool? showedItInstructions,
|
||||
bool? showedClickMessage,
|
||||
bool? showedBlurMeansTranslate,
|
||||
bool? showedTooltipInstructions,
|
||||
String? createdAt,
|
||||
String? targetLanguage,
|
||||
String? sourceLanguage,
|
||||
String? country,
|
||||
bool? publicProfile,
|
||||
}) async {
|
||||
if (dateOfBirth != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.dateOfBirth.title,
|
||||
dateOfBirth,
|
||||
);
|
||||
}
|
||||
if (autoPlayMessages != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.autoPlayMessages.title,
|
||||
autoPlayMessages,
|
||||
);
|
||||
}
|
||||
if (itAutoPlay != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.itAutoPlay.title,
|
||||
itAutoPlay,
|
||||
);
|
||||
}
|
||||
if (activatedFreeTrial != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.activatedFreeTrial.title,
|
||||
activatedFreeTrial,
|
||||
);
|
||||
}
|
||||
if (interactiveTranslator != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.interactiveTranslator.title,
|
||||
interactiveTranslator,
|
||||
);
|
||||
}
|
||||
if (interactiveGrammar != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.interactiveGrammar.title,
|
||||
interactiveGrammar,
|
||||
);
|
||||
}
|
||||
if (immersionMode != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.immersionMode.title,
|
||||
immersionMode,
|
||||
);
|
||||
}
|
||||
if (definitions != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.definitions.title,
|
||||
definitions,
|
||||
);
|
||||
}
|
||||
// if (translations != null) {
|
||||
// await _pangeaController.pStoreService.save(
|
||||
// MatrixProfile.translations.title,
|
||||
// translations,
|
||||
// );
|
||||
// }
|
||||
if (showedItInstructions != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.showedItInstructions.title,
|
||||
showedItInstructions,
|
||||
);
|
||||
}
|
||||
if (showedClickMessage != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.showedClickMessage.title,
|
||||
showedClickMessage,
|
||||
);
|
||||
}
|
||||
if (showedBlurMeansTranslate != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.showedBlurMeansTranslate.title,
|
||||
showedBlurMeansTranslate,
|
||||
);
|
||||
}
|
||||
if (showedTooltipInstructions != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.showedTooltipInstructions.title,
|
||||
showedTooltipInstructions,
|
||||
);
|
||||
}
|
||||
if (createdAt != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.createdAt.title,
|
||||
createdAt,
|
||||
);
|
||||
}
|
||||
if (targetLanguage != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.targetLanguage.title,
|
||||
targetLanguage,
|
||||
);
|
||||
}
|
||||
if (sourceLanguage != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.sourceLanguage.title,
|
||||
sourceLanguage,
|
||||
);
|
||||
}
|
||||
if (country != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.country.title,
|
||||
country,
|
||||
);
|
||||
}
|
||||
if (publicProfile != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.publicProfile.title,
|
||||
publicProfile,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _completeCompleter() {
|
||||
if (!_completer.isCompleted) {
|
||||
_completer.complete(null);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Completer> get completer async {
|
||||
if (await isPUserDataAvailable) {
|
||||
_completeCompleter();
|
||||
}
|
||||
return _completer;
|
||||
MatrixProfile.saveProfileData({
|
||||
MatrixProfileEnum.dateOfBirth.title: dateOfBirth,
|
||||
MatrixProfileEnum.targetLanguage.title: targetLanguage,
|
||||
MatrixProfileEnum.sourceLanguage.title: sourceLanguage,
|
||||
MatrixProfileEnum.country.title: country,
|
||||
MatrixProfileEnum.publicProfile.title: publicProfile,
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns a boolean value indicating whether a new JWT (JSON Web Token) is needed.
|
||||
/// It checks if the `userModel` has a non-null `access` token and if the token is expired using the `Jwt.isExpired()` method.
|
||||
/// If the `userModel` is null or the `access` token is null, it returns true indicating that a new JWT is needed.
|
||||
bool get needNewJWT =>
|
||||
userModel?.access != null ? Jwt.isExpired(userModel!.access) : true;
|
||||
|
||||
Future<String> get accessToken async {
|
||||
await (await completer).future;
|
||||
// if userModel null or access token expired then fetchUserModel
|
||||
/// Retrieves the access token for the user.
|
||||
///
|
||||
/// If the locally stored user model is null or the access token has
|
||||
/// expired, it fetches the user model.
|
||||
/// If the user model is still null after fetching, an error is logged.
|
||||
///
|
||||
/// Returns the access token as a string, or null if the user model is null.
|
||||
Future<String?> get accessToken async {
|
||||
final PUserModel? useThisOne =
|
||||
needNewJWT ? await fetchUserModel() : userModel;
|
||||
|
||||
if (useThisOne == null) {
|
||||
//debugger(when: kDebugMode);
|
||||
throw Exception("trying to get accessToken with userModel = null");
|
||||
ErrorHandler.logError(
|
||||
e: "trying to get accessToken with userModel = null",
|
||||
);
|
||||
}
|
||||
return useThisOne.access;
|
||||
return useThisOne?.access;
|
||||
}
|
||||
|
||||
String? get userId {
|
||||
return _pangeaController.matrixState.client.userID;
|
||||
}
|
||||
|
||||
String get fullname {
|
||||
final String? userID = userId;
|
||||
if (userID == null) {
|
||||
throw Exception('User ID not found');
|
||||
/// Returns the full name of the user.
|
||||
/// If the [userId] is null, an error will be logged and null will be returned.
|
||||
/// The full name is obtained by extracting the substring before the first occurrence of ":" in the [userId]
|
||||
/// and then replacing all occurrences of "@" with an empty string.
|
||||
String? get fullname {
|
||||
if (userId == null) {
|
||||
ErrorHandler.logError(
|
||||
e: "calling fullname with userId == null",
|
||||
);
|
||||
return null;
|
||||
}
|
||||
return userID.substring(0, userID.indexOf(":")).replaceAll("@", "");
|
||||
return userId!.substring(0, userId!.indexOf(":")).replaceAll("@", "");
|
||||
}
|
||||
|
||||
/// Checks if the user data is available.
|
||||
/// Returns a [Future] that completes with a [bool] value
|
||||
/// indicating whether the user data is available or not.
|
||||
Future<bool> get isPUserDataAvailable async {
|
||||
try {
|
||||
final PUserModel? toCheck = userModel ?? (await fetchUserModel());
|
||||
return toCheck != null ? true : false;
|
||||
} catch (err) {
|
||||
return toCheck != null;
|
||||
} catch (err, s) {
|
||||
ErrorHandler.logError(e: err, s: s);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if user data is available and the date of birth is set.
|
||||
/// Returns a [Future] that completes with a [bool] value indicating
|
||||
/// whether the user data is available and the date of birth is set.
|
||||
Future<bool> get isUserDataAvailableAndDateOfBirthSet async {
|
||||
try {
|
||||
final client = _pangeaController.matrixState.client;
|
||||
if (client.prevBatch == null) {
|
||||
await client.onSync.stream.first;
|
||||
}
|
||||
// the function fetchUserModel() uses a completer, so it shouldn't
|
||||
// re-call the endpoint if it has already been called
|
||||
await fetchUserModel();
|
||||
final localAccountData = _pangeaController.pStoreService.read(
|
||||
ModelKey.userDateOfBirth,
|
||||
);
|
||||
return localAccountData != null;
|
||||
} catch (err) {
|
||||
return MatrixProfile.dateOfBirth != null;
|
||||
} catch (err, s) {
|
||||
ErrorHandler.logError(e: err, s: s);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a boolean value indicating whether the user is currently in the trial window.
|
||||
bool get inTrialWindow {
|
||||
final String? createdAt = userModel?.profile?.createdAt;
|
||||
if (createdAt == null) {
|
||||
|
|
@ -428,6 +358,14 @@ class UserController extends BaseController {
|
|||
);
|
||||
}
|
||||
|
||||
/// Checks if the user's languages are set.
|
||||
/// Returns a [Future] that completes with a [bool] value
|
||||
/// indicating whether the user's languages are set.
|
||||
///
|
||||
/// A user's languages are considered set if the source and target languages
|
||||
/// are not null, not empty, and not equal to the [LanguageKeys.unknownLanguage] constant.
|
||||
///
|
||||
/// If an error occurs during the process, it logs the error and returns `false`.
|
||||
Future<bool> get areUserLanguagesSet async {
|
||||
try {
|
||||
final PUserModel? toCheck = userModel ?? (await fetchUserModel());
|
||||
|
|
@ -442,18 +380,29 @@ class UserController extends BaseController {
|
|||
tgtLang.isNotEmpty &&
|
||||
srcLang != LanguageKeys.unknownLanguage &&
|
||||
tgtLang != LanguageKeys.unknownLanguage;
|
||||
} catch (err) {
|
||||
} catch (err, s) {
|
||||
ErrorHandler.logError(e: err, s: s);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
String? get _matrixAccessToken =>
|
||||
_pangeaController.matrixState.client.accessToken;
|
||||
|
||||
/// Returns a boolean value indicating whether the user's profile is public.
|
||||
bool get isPublic =>
|
||||
_pangeaController.userController.userModel?.profile?.publicProfile ??
|
||||
false;
|
||||
|
||||
/// Retrieves the user's email address.
|
||||
///
|
||||
/// This method fetches the user's email address by making a request to the
|
||||
/// Matrix server. It uses the `_pangeaController` instance to access the
|
||||
/// Matrix client and retrieve the account's third-party identifiers. It then
|
||||
/// filters the identifiers to find the first one with the medium set to
|
||||
/// `ThirdPartyIdentifierMedium.email`. Finally, it returns the email address
|
||||
/// associated with the identifier, or `null` if no email address is found.
|
||||
///
|
||||
/// Returns:
|
||||
/// - The user's email address as a [String], or `null` if no email address
|
||||
/// is found.
|
||||
Future<String?> get userEmail async {
|
||||
final List<matrix.ThirdPartyIdentifier>? identifiers =
|
||||
await _pangeaController.matrixState.client.getAccount3PIDs();
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import 'package:fluffychat/pangea/constants/language_constants.dart';
|
||||
import 'package:fluffychat/pangea/repo/word_repo.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../models/word_data_model.dart';
|
||||
import 'base_controller.dart';
|
||||
import 'pangea_controller.dart';
|
||||
|
|
@ -31,7 +32,7 @@ class WordController extends BaseController {
|
|||
),
|
||||
);
|
||||
|
||||
Future<WordData> getWordDataGlobal({
|
||||
Future<WordData?> getWordDataGlobal({
|
||||
required String word,
|
||||
required String fullText,
|
||||
required String? userL1,
|
||||
|
|
@ -53,8 +54,18 @@ class WordController extends BaseController {
|
|||
|
||||
if (local != null) return local;
|
||||
|
||||
final String? accessToken =
|
||||
await _pangeaController.userController.accessToken;
|
||||
if (accessToken == null) {
|
||||
ErrorHandler.logError(
|
||||
e: "null accessToken in word controller",
|
||||
s: StackTrace.current,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
final WordData remote = await WordRepo.getWordNetData(
|
||||
accessToken: await _pangeaController.userController.accessToken,
|
||||
accessToken: accessToken,
|
||||
fullText: fullText,
|
||||
word: word,
|
||||
userL1: userL1,
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class PangeaMessageEvent {
|
|||
return _latestEdit;
|
||||
}
|
||||
|
||||
Future<PangeaAudioFile> getMatrixAudioFile(
|
||||
Future<PangeaAudioFile?> getMatrixAudioFile(
|
||||
String langCode,
|
||||
BuildContext context,
|
||||
) async {
|
||||
|
|
@ -102,11 +102,15 @@ class PangeaMessageEvent {
|
|||
langCode: langCode,
|
||||
);
|
||||
|
||||
final TextToSpeechResponse response =
|
||||
final TextToSpeechResponse? response =
|
||||
await MatrixState.pangeaController.textToSpeech.get(
|
||||
params,
|
||||
);
|
||||
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final audioBytes = base64.decode(response.audioContent);
|
||||
final eventIdParam = _event.eventId;
|
||||
final fileName =
|
||||
|
|
@ -177,10 +181,13 @@ class PangeaMessageEvent {
|
|||
langCode: langCode,
|
||||
);
|
||||
|
||||
final TextToSpeechResponse response =
|
||||
final TextToSpeechResponse? response =
|
||||
await MatrixState.pangeaController.textToSpeech.get(
|
||||
params,
|
||||
);
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final audioBytes = base64.decode(response.audioContent);
|
||||
|
||||
|
|
@ -323,7 +330,7 @@ class PangeaMessageEvent {
|
|||
debugPrint("mimeType ${matrixFile.mimeType}");
|
||||
debugPrint("encoding ${mimeTypeToAudioEncoding(matrixFile.mimeType)}");
|
||||
|
||||
final SpeechToTextModel response =
|
||||
final SpeechToTextModel? response =
|
||||
await MatrixState.pangeaController.speechToText.get(
|
||||
SpeechToTextRequestModel(
|
||||
audioContent: matrixFile.bytes,
|
||||
|
|
@ -339,6 +346,10 @@ class PangeaMessageEvent {
|
|||
),
|
||||
);
|
||||
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
_representations?.add(
|
||||
RepresentationEvent(
|
||||
timeline: timeline,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:fluffychat/pangea/models/user_model.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -309,4 +310,19 @@ extension SettingCopy on ToolSetting {
|
|||
return L10n.of(context)!.autoIGCToolDescription;
|
||||
}
|
||||
}
|
||||
|
||||
MatrixProfileEnum get asMatrixProfileField {
|
||||
switch (this) {
|
||||
case ToolSetting.interactiveTranslator:
|
||||
return MatrixProfileEnum.interactiveTranslator;
|
||||
case ToolSetting.interactiveGrammar:
|
||||
return MatrixProfileEnum.interactiveGrammar;
|
||||
case ToolSetting.immersionMode:
|
||||
return MatrixProfileEnum.immersionMode;
|
||||
case ToolSetting.definitions:
|
||||
return MatrixProfileEnum.definitions;
|
||||
case ToolSetting.autoIGC:
|
||||
return MatrixProfileEnum.autoIGC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,18 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:country_picker/country_picker.dart';
|
||||
import 'package:fluffychat/pangea/constants/local.key.dart';
|
||||
import 'package:fluffychat/pangea/constants/model_keys.dart';
|
||||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/enum/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/models/space_model.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import '../constants/language_constants.dart';
|
||||
import 'language_model.dart';
|
||||
|
||||
PUserModel pUserModelFromJson(String str) =>
|
||||
PUserModel.fromJson(json.decode(str));
|
||||
|
||||
String pUserModelToJson(PUserModel data) => json.encode(data.toJson());
|
||||
|
||||
class PUserModel {
|
||||
String access;
|
||||
String refresh;
|
||||
|
|
@ -46,12 +42,12 @@ class PUserModel {
|
|||
await pangeaController.pStoreService.save(
|
||||
PLocalKey.user,
|
||||
toJson(),
|
||||
local: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum MatrixProfile {
|
||||
/// A list of all the fields in the user profile saved to matrix
|
||||
enum MatrixProfileEnum {
|
||||
dateOfBirth,
|
||||
autoPlayMessages,
|
||||
itAutoPlay,
|
||||
|
|
@ -60,7 +56,6 @@ enum MatrixProfile {
|
|||
interactiveGrammar,
|
||||
immersionMode,
|
||||
definitions,
|
||||
// translations,
|
||||
showedItInstructions,
|
||||
showedClickMessage,
|
||||
showedBlurMeansTranslate,
|
||||
|
|
@ -73,49 +68,182 @@ enum MatrixProfile {
|
|||
autoIGC,
|
||||
}
|
||||
|
||||
extension MatrixProfileExtension on MatrixProfile {
|
||||
extension MatrixProfileEnumExtension on MatrixProfileEnum {
|
||||
String get title {
|
||||
switch (this) {
|
||||
case MatrixProfile.dateOfBirth:
|
||||
case MatrixProfileEnum.dateOfBirth:
|
||||
return ModelKey.userDateOfBirth;
|
||||
case MatrixProfile.autoPlayMessages:
|
||||
return PLocalKey.autoPlayMessages;
|
||||
case MatrixProfile.itAutoPlay:
|
||||
return PLocalKey.itAutoPlay;
|
||||
case MatrixProfile.activatedFreeTrial:
|
||||
return PLocalKey.activatedTrialKey;
|
||||
case MatrixProfile.interactiveTranslator:
|
||||
case MatrixProfileEnum.autoPlayMessages:
|
||||
return ModelKey.autoPlayMessages;
|
||||
case MatrixProfileEnum.itAutoPlay:
|
||||
return ModelKey.itAutoPlay;
|
||||
case MatrixProfileEnum.activatedFreeTrial:
|
||||
return ModelKey.activatedTrialKey;
|
||||
case MatrixProfileEnum.interactiveTranslator:
|
||||
return ToolSetting.interactiveTranslator.toString();
|
||||
case MatrixProfile.interactiveGrammar:
|
||||
case MatrixProfileEnum.interactiveGrammar:
|
||||
return ToolSetting.interactiveGrammar.toString();
|
||||
case MatrixProfile.immersionMode:
|
||||
case MatrixProfileEnum.immersionMode:
|
||||
return ToolSetting.immersionMode.toString();
|
||||
case MatrixProfile.definitions:
|
||||
case MatrixProfileEnum.definitions:
|
||||
return ToolSetting.definitions.toString();
|
||||
// case MatrixProfile.translations:
|
||||
// return ToolSetting.translations.toString();
|
||||
case MatrixProfile.autoIGC:
|
||||
case MatrixProfileEnum.autoIGC:
|
||||
return ToolSetting.autoIGC.toString();
|
||||
case MatrixProfile.showedItInstructions:
|
||||
case MatrixProfileEnum.showedItInstructions:
|
||||
return InstructionsEnum.itInstructions.toString();
|
||||
case MatrixProfile.showedClickMessage:
|
||||
case MatrixProfileEnum.showedClickMessage:
|
||||
return InstructionsEnum.clickMessage.toString();
|
||||
case MatrixProfile.showedBlurMeansTranslate:
|
||||
case MatrixProfileEnum.showedBlurMeansTranslate:
|
||||
return InstructionsEnum.blurMeansTranslate.toString();
|
||||
case MatrixProfile.showedTooltipInstructions:
|
||||
case MatrixProfileEnum.showedTooltipInstructions:
|
||||
return InstructionsEnum.tooltipInstructions.toString();
|
||||
case MatrixProfile.createdAt:
|
||||
case MatrixProfileEnum.createdAt:
|
||||
return ModelKey.userCreatedAt;
|
||||
case MatrixProfile.targetLanguage:
|
||||
case MatrixProfileEnum.targetLanguage:
|
||||
return ModelKey.l2LanguageKey;
|
||||
case MatrixProfile.sourceLanguage:
|
||||
case MatrixProfileEnum.sourceLanguage:
|
||||
return ModelKey.l1LanguageKey;
|
||||
case MatrixProfile.country:
|
||||
case MatrixProfileEnum.country:
|
||||
return ModelKey.userCountry;
|
||||
case MatrixProfile.publicProfile:
|
||||
case MatrixProfileEnum.publicProfile:
|
||||
return ModelKey.publicProfile;
|
||||
}
|
||||
}
|
||||
|
||||
ToolSetting? get asToolSetting {
|
||||
switch (this) {
|
||||
case MatrixProfileEnum.interactiveTranslator:
|
||||
return ToolSetting.interactiveTranslator;
|
||||
case MatrixProfileEnum.interactiveGrammar:
|
||||
return ToolSetting.interactiveGrammar;
|
||||
case MatrixProfileEnum.immersionMode:
|
||||
return ToolSetting.immersionMode;
|
||||
case MatrixProfileEnum.definitions:
|
||||
return ToolSetting.definitions;
|
||||
case MatrixProfileEnum.autoIGC:
|
||||
return ToolSetting.autoIGC;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper around the matrix account data for the user profile.
|
||||
/// Enables easy access to the profile data and saving new data.
|
||||
/// The matrix profile doesn't function exactly the same as a 'model',
|
||||
/// since all the data here is already stored in the client as account
|
||||
/// data, and duplicating that data could lead to some inconsistenies.
|
||||
/// So this class is more of a helper class to make it easier to
|
||||
/// access and save the data.
|
||||
class MatrixProfile {
|
||||
/// Returns the profile of the user.
|
||||
///
|
||||
/// The profile is retrieved from the `MatrixState.pangeaController.matrixState.client.accountData`
|
||||
/// using the key `ModelKey.userProfile`. It returns a `Map<String, dynamic>` object
|
||||
/// representing the user's profile information.
|
||||
static Map<String, dynamic>? get profile => MatrixState.pangeaController
|
||||
.matrixState.client.accountData[ModelKey.userProfile]?.content;
|
||||
|
||||
static dynamic getProfileData(MatrixProfileEnum key) => profile?[key.title];
|
||||
|
||||
/// Saves the profile data by updating the current user's profile with the provided updates.
|
||||
///
|
||||
/// The [updates] parameter is a map containing the key-value pairs of the profile fields to be updated.
|
||||
/// Only non-null values in the [updates] map will be applied to the current profile.
|
||||
///
|
||||
/// If the updated profile is equal to the current profile, no changes will be made.
|
||||
///
|
||||
/// If [waitForDataInSync] is true, the function will wait for the updated data in a sync update
|
||||
/// If this is set to false, after this function completes there may be a gap where the
|
||||
/// data has been sent but is not in the client's account data, as the sync update has not yet been received.
|
||||
static Future<void> saveProfileData(
|
||||
Map<String, dynamic> updates, {
|
||||
waitForDataInSync = false,
|
||||
}) async {
|
||||
final currentProfile = toJson();
|
||||
for (final entry in updates.entries) {
|
||||
if (entry.value == null) continue;
|
||||
currentProfile[entry.key] = entry.value;
|
||||
}
|
||||
if (mapEquals(MatrixProfile.toJson(), currentProfile)) return;
|
||||
|
||||
final PangeaController pangeaController = MatrixState.pangeaController;
|
||||
final Client client = pangeaController.matrixState.client;
|
||||
|
||||
final List<String> profileKeys =
|
||||
MatrixProfileEnum.values.map((e) => e.title).toList();
|
||||
|
||||
Future<SyncUpdate>? waitForUpdate;
|
||||
if (waitForDataInSync) {
|
||||
waitForUpdate = client.onSync.stream.firstWhere(
|
||||
(sync) =>
|
||||
sync.accountData != null &&
|
||||
sync.accountData!.any(
|
||||
(event) => event.content.keys.any((k) => profileKeys.contains(k)),
|
||||
),
|
||||
);
|
||||
}
|
||||
await client.setAccountData(
|
||||
client.userID!,
|
||||
ModelKey.userProfile,
|
||||
currentProfile,
|
||||
);
|
||||
if (waitForDataInSync) await waitForUpdate;
|
||||
}
|
||||
|
||||
/// Converts the Matrix Profile to a JSON representation.
|
||||
static Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> json = {};
|
||||
for (final value in MatrixProfileEnum.values) {
|
||||
json[value.title] = getProfileData(value);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
// below are some convenience methods for accessing the profile data
|
||||
// getProfileData could be used directly, but these methods reduce the
|
||||
// need for repeating the same code (like parsing DateTimes or
|
||||
// assigning default values to null booleans) when accessing specific values.
|
||||
|
||||
static DateTime? get dateOfBirth {
|
||||
final dob = getProfileData(MatrixProfileEnum.dateOfBirth);
|
||||
return dob != null ? DateTime.parse(dob) : null;
|
||||
}
|
||||
|
||||
static bool get autoPlayMessages =>
|
||||
getProfileData(MatrixProfileEnum.autoPlayMessages) ?? false;
|
||||
static bool get itAutoPlay =>
|
||||
getProfileData(MatrixProfileEnum.itAutoPlay) ?? false;
|
||||
static bool get activatedFreeTrial =>
|
||||
getProfileData(MatrixProfileEnum.activatedFreeTrial) ?? false;
|
||||
static bool get interactiveTranslator =>
|
||||
getProfileData(MatrixProfileEnum.interactiveTranslator) ?? true;
|
||||
static bool get interactiveGrammar =>
|
||||
getProfileData(MatrixProfileEnum.interactiveGrammar) ?? true;
|
||||
static bool get immersionMode =>
|
||||
getProfileData(MatrixProfileEnum.immersionMode) ?? false;
|
||||
static bool get definitions =>
|
||||
getProfileData(MatrixProfileEnum.definitions) ?? true;
|
||||
static bool get autoIGC => getProfileData(MatrixProfileEnum.autoIGC) ?? false;
|
||||
|
||||
/// A list of all the fields in MatrixProfileEnum that correspond to tool settings
|
||||
static List<MatrixProfileEnum> get toolSettings => [
|
||||
MatrixProfileEnum.interactiveTranslator,
|
||||
MatrixProfileEnum.interactiveGrammar,
|
||||
MatrixProfileEnum.immersionMode,
|
||||
MatrixProfileEnum.definitions,
|
||||
MatrixProfileEnum.autoIGC,
|
||||
];
|
||||
|
||||
/// A list of all the fields in MatrixProfileEnum that correspond to pangea profile values
|
||||
static List<MatrixProfileEnum> pangeaProfileFields = [
|
||||
MatrixProfileEnum.dateOfBirth,
|
||||
MatrixProfileEnum.createdAt,
|
||||
MatrixProfileEnum.targetLanguage,
|
||||
MatrixProfileEnum.sourceLanguage,
|
||||
MatrixProfileEnum.country,
|
||||
MatrixProfileEnum.publicProfile,
|
||||
];
|
||||
}
|
||||
|
||||
class Profile {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'dart:async';
|
|||
import 'package:country_picker/country_picker.dart';
|
||||
import 'package:fluffychat/pangea/models/language_model.dart';
|
||||
import 'package:fluffychat/pangea/models/user_model.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../widgets/matrix.dart';
|
||||
|
|
@ -91,9 +92,19 @@ class FindPartnerController extends State<FindPartner> {
|
|||
if (loading || nextUrl == null) return;
|
||||
setState(() => loading = true);
|
||||
|
||||
final String? accessToken =
|
||||
await pangeaController.userController.accessToken;
|
||||
if (accessToken == null) {
|
||||
ErrorHandler.logError(
|
||||
e: "null accessToken in find partner controller",
|
||||
s: StackTrace.current,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final UserProfileSearchResponse response =
|
||||
await PUserRepo.searchUserProfiles(
|
||||
accessToken: await pangeaController.userController.accessToken,
|
||||
accessToken: accessToken,
|
||||
targetLanguage: targetLanguageSearch.langCode,
|
||||
sourceLanguage: sourceLanguageSearch.langCode,
|
||||
country: countrySearch,
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class PUserAgeController extends State<PUserAge> {
|
|||
final String date = DateFormat('yyyy-MM-dd').format(selectedDate!);
|
||||
|
||||
if (pangeaController.userController.userModel?.access == null) {
|
||||
await pangeaController.userController.createPangeaUser(dob: date);
|
||||
await pangeaController.userController.createProfile(dob: date);
|
||||
} else {
|
||||
await pangeaController.userController.updateUserProfile(
|
||||
dateOfBirth: date,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:fluffychat/pangea/constants/local.key.dart';
|
||||
import 'package:fluffychat/pangea/models/space_model.dart';
|
||||
import 'package:fluffychat/pangea/models/user_model.dart';
|
||||
import 'package:fluffychat/pangea/pages/settings_learning/settings_learning.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/widgets/user_settings/country_picker_tile.dart';
|
||||
|
|
@ -52,34 +52,30 @@ class SettingsLearningView extends StatelessWidget {
|
|||
ListTile(
|
||||
subtitle: Text(L10n.of(context)!.toggleToolSettingsDescription),
|
||||
),
|
||||
for (final setting in ToolSetting.values)
|
||||
PSettingsSwitchListTile.adaptive(
|
||||
defaultValue: controller.pangeaController.localSettings
|
||||
.userLanguageToolSetting(setting),
|
||||
title: setting.toolName(context),
|
||||
subtitle: setting.toolDescription(context),
|
||||
pStoreKey: setting.toString(),
|
||||
local: false,
|
||||
),
|
||||
PSettingsSwitchListTile.adaptive(
|
||||
defaultValue: controller.pangeaController.pStoreService.read(
|
||||
PLocalKey.itAutoPlay,
|
||||
) ??
|
||||
false,
|
||||
title: L10n.of(context)!.interactiveTranslatorAutoPlaySliderHeader,
|
||||
for (final setting in MatrixProfile.toolSettings)
|
||||
setting.asToolSetting != null
|
||||
? ProfileSettingsSwitchListTile.adaptive(
|
||||
defaultValue: controller
|
||||
.pangeaController.permissionsController
|
||||
.userToolSetting(setting),
|
||||
title: setting.asToolSetting!.toolName(context),
|
||||
subtitle:
|
||||
setting.asToolSetting!.toolDescription(context),
|
||||
profileKey: setting.asToolSetting!.asMatrixProfileField,
|
||||
)
|
||||
: const SizedBox(),
|
||||
ProfileSettingsSwitchListTile.adaptive(
|
||||
defaultValue: MatrixProfile.itAutoPlay,
|
||||
title:
|
||||
L10n.of(context)!.interactiveTranslatorAutoPlaySliderHeader,
|
||||
subtitle: L10n.of(context)!.interactiveTranslatorAutoPlayDesc,
|
||||
pStoreKey: PLocalKey.itAutoPlay,
|
||||
local: false,
|
||||
profileKey: MatrixProfileEnum.itAutoPlay,
|
||||
),
|
||||
PSettingsSwitchListTile.adaptive(
|
||||
defaultValue: controller.pangeaController.pStoreService.read(
|
||||
PLocalKey.autoPlayMessages,
|
||||
) ??
|
||||
false,
|
||||
ProfileSettingsSwitchListTile.adaptive(
|
||||
defaultValue: MatrixProfile.autoPlayMessages,
|
||||
title: L10n.of(context)!.autoPlayTitle,
|
||||
subtitle: L10n.of(context)!.autoPlayDesc,
|
||||
pStoreKey: PLocalKey.autoPlayMessages,
|
||||
local: false,
|
||||
profileKey: MatrixProfileEnum.autoPlayMessages,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,133 +1,71 @@
|
|||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
class PLocalStore {
|
||||
/// Utility to save and read data both in the matrix profile (this is the default
|
||||
/// behavior) and in the local storage (local needs to be specificied). An
|
||||
/// instance of this class is created in the PangeaController.
|
||||
class PStore {
|
||||
final GetStorage _box = GetStorage();
|
||||
final PangeaController pangeaController;
|
||||
|
||||
PLocalStore({required this.pangeaController});
|
||||
PStore({required this.pangeaController});
|
||||
|
||||
/// save data in local
|
||||
/// Saves the provided [data] with the specified [key] in the local storage.
|
||||
///
|
||||
/// By default, the [data] is considered as account data, but you can set
|
||||
/// [isAccountData] to false if it's not account-related data.
|
||||
///
|
||||
/// Example usage:
|
||||
/// ```dart
|
||||
/// await save('user', {'name': 'John Doe', 'age': 25});
|
||||
/// ```
|
||||
Future<void> save(
|
||||
String key,
|
||||
dynamic data, {
|
||||
bool addClientIdToKey = true,
|
||||
bool local = false,
|
||||
bool isAccountData = true,
|
||||
}) async {
|
||||
local
|
||||
? await saveLocal(
|
||||
key,
|
||||
data,
|
||||
addClientIdToKey: addClientIdToKey,
|
||||
)
|
||||
: await saveProfile(key, data);
|
||||
await _box.write(_key(key, isAccountData: isAccountData), data);
|
||||
}
|
||||
|
||||
/// fetch data from local
|
||||
dynamic read(
|
||||
String key, {
|
||||
bool addClientIdToKey = true,
|
||||
local = false,
|
||||
}) {
|
||||
return local
|
||||
? readLocal(
|
||||
key,
|
||||
addClientIdToKey: addClientIdToKey,
|
||||
)
|
||||
: readProfile(key);
|
||||
}
|
||||
|
||||
/// delete data from local
|
||||
Future<void> delete(
|
||||
String key, {
|
||||
bool addClientIdToKey = true,
|
||||
local = false,
|
||||
}) async {
|
||||
return local
|
||||
? deleteLocal(
|
||||
key,
|
||||
addClientIdToKey: addClientIdToKey,
|
||||
)
|
||||
: deleteProfile(key);
|
||||
}
|
||||
|
||||
/// save data in local
|
||||
Future<void> saveLocal(
|
||||
String key,
|
||||
dynamic data, {
|
||||
bool addClientIdToKey = true,
|
||||
}) async {
|
||||
await _box.write(_key(key, addClientIdToKey: addClientIdToKey), data);
|
||||
}
|
||||
|
||||
Future<void> saveProfile(
|
||||
String key,
|
||||
dynamic data,
|
||||
) async {
|
||||
final waitForAccountSync =
|
||||
pangeaController.matrixState.client.onSync.stream.firstWhere(
|
||||
(sync) =>
|
||||
sync.accountData != null &&
|
||||
sync.accountData!.any(
|
||||
(event) => event.content.keys.any(
|
||||
(k) => k == key,
|
||||
),
|
||||
),
|
||||
);
|
||||
await pangeaController.matrixState.client.setAccountData(
|
||||
pangeaController.matrixState.client.userID!,
|
||||
key,
|
||||
{key: data},
|
||||
);
|
||||
await waitForAccountSync;
|
||||
await pangeaController.matrixState.client.onSyncStatus.stream.firstWhere(
|
||||
(syncStatus) => syncStatus.status == SyncStatus.finished,
|
||||
);
|
||||
}
|
||||
|
||||
/// fetch data from local
|
||||
dynamic readLocal(String key, {bool addClientIdToKey = true}) {
|
||||
/// Reads the value associated with the given [key] from the local store.
|
||||
///
|
||||
/// If [isAccountData] is true, tries to find key assosiated with the logged in user.
|
||||
/// Otherwise, it is read from the general store.
|
||||
///
|
||||
/// Returns the value associated with the [key], or
|
||||
/// null if the user ID is null or value hasn't been set.
|
||||
dynamic read(String key, {bool isAccountData = true}) {
|
||||
return pangeaController.matrixState.client.userID != null
|
||||
? _box.read(_key(key, addClientIdToKey: addClientIdToKey))
|
||||
? _box.read(_key(key, isAccountData: isAccountData))
|
||||
: null;
|
||||
}
|
||||
|
||||
dynamic readProfile(String key) {
|
||||
try {
|
||||
return pangeaController.matrixState.client.accountData[key]?.content[key];
|
||||
} catch (err) {
|
||||
ErrorHandler.logError(e: err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// delete data from local
|
||||
Future<void> deleteLocal(String key, {bool addClientIdToKey = true}) async {
|
||||
/// Deletes the value associated with the given [key] from the local store.
|
||||
///
|
||||
/// If [isAccountData] is true (default), will try to use key assosiated with the logged in user's ID
|
||||
///
|
||||
/// Returns a [Future] that completes when the value is successfully deleted.
|
||||
/// If the user is not logged in, the value will not be deleted and the [Future] will complete with null.
|
||||
Future<void> delete(String key, {bool isAccountData = true}) async {
|
||||
return pangeaController.matrixState.client.userID != null
|
||||
? _box.remove(_key(key, addClientIdToKey: addClientIdToKey))
|
||||
? _box.remove(_key(key, isAccountData: isAccountData))
|
||||
: null;
|
||||
}
|
||||
|
||||
Future<void> deleteProfile(key) async {
|
||||
return pangeaController.matrixState.client.userID != null
|
||||
? pangeaController.matrixState.client.setAccountData(
|
||||
pangeaController.matrixState.client.userID!,
|
||||
key,
|
||||
{key: null},
|
||||
)
|
||||
: null;
|
||||
}
|
||||
|
||||
_key(String key, {bool addClientIdToKey = true}) {
|
||||
return addClientIdToKey
|
||||
/// Returns the key for storing data in the pangea store.
|
||||
///
|
||||
/// The [key] parameter represents the base key for the data.
|
||||
/// The [isAccountData] parameter indicates whether the data is account-specific.
|
||||
/// If [isAccountData] is true, the account-specific key is returned by appending the user ID to the base key.
|
||||
/// If [isAccountData] is false, the base key is returned as is.
|
||||
String _key(String key, {bool isAccountData = true}) {
|
||||
return isAccountData
|
||||
? pangeaController.matrixState.client.userID! + key
|
||||
: key;
|
||||
}
|
||||
|
||||
/// clear all local storage
|
||||
clearStorage() {
|
||||
/// Clears the storage by erasing all data in the box.
|
||||
void clearStorage() {
|
||||
_box.erase();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ import 'dart:developer';
|
|||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
import 'package:fluffychat/pangea/constants/local.key.dart';
|
||||
import 'package:fluffychat/pangea/enum/message_mode_enum.dart';
|
||||
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
|
||||
import 'package:fluffychat/pangea/models/user_model.dart';
|
||||
import 'package:fluffychat/pangea/utils/any_state_holder.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/utils/overlay.dart';
|
||||
|
|
@ -329,17 +329,12 @@ class MessageToolbarState extends State<MessageToolbar> {
|
|||
});
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
final bool autoplay = MatrixState.pangeaController.pStoreService.read(
|
||||
PLocalKey.autoPlayMessages,
|
||||
) ??
|
||||
false;
|
||||
|
||||
if (widget.pangeaMessageEvent.isAudioMessage) {
|
||||
updateMode(MessageMode.speechToText);
|
||||
return;
|
||||
}
|
||||
|
||||
autoplay
|
||||
MatrixProfile.autoPlayMessages
|
||||
? updateMode(MessageMode.textToSpeech)
|
||||
: updateMode(MessageMode.translation);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -58,9 +58,17 @@ class MessageTranslationCardState extends State<MessageTranslationCard> {
|
|||
}
|
||||
|
||||
oldSelectedText = widget.selection.selectedText;
|
||||
final String accessToken =
|
||||
final String? accessToken =
|
||||
await MatrixState.pangeaController.userController.accessToken;
|
||||
|
||||
if (accessToken == null) {
|
||||
ErrorHandler.logError(
|
||||
e: "null accessToken in translateSelection",
|
||||
s: StackTrace.current,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final resp = await FullTextTranslationRepo.translate(
|
||||
accessToken: accessToken,
|
||||
request: FullTextTranslationRequestModel(
|
||||
|
|
|
|||
|
|
@ -43,8 +43,7 @@ class _JoinClassWithLinkState extends State<JoinClassWithLink> {
|
|||
await _pangeaController.pStoreService.save(
|
||||
PLocalKey.cachedClassCodeToJoin,
|
||||
classCode,
|
||||
addClientIdToKey: false,
|
||||
local: true,
|
||||
isAccountData: false,
|
||||
);
|
||||
context.go("/home");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/constants/local.key.dart';
|
||||
import 'package:fluffychat/pangea/enum/span_data_type.dart';
|
||||
import 'package:fluffychat/pangea/models/span_data.dart';
|
||||
import 'package:fluffychat/pangea/models/user_model.dart';
|
||||
import 'package:fluffychat/pangea/utils/bot_style.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/utils/match_copy.dart';
|
||||
|
|
@ -154,21 +154,18 @@ class WordMatchContent extends StatelessWidget {
|
|||
.selected = true;
|
||||
|
||||
controller.setState(
|
||||
() => (
|
||||
controller.currentExpression =
|
||||
controller
|
||||
.widget
|
||||
.scm
|
||||
.choreographer
|
||||
.igc
|
||||
.igcTextData
|
||||
!.matches[controller.widget.scm.matchIndex]
|
||||
.match
|
||||
.choices![index]
|
||||
.isBestCorrection
|
||||
() => (controller.currentExpression = controller
|
||||
.widget
|
||||
.scm
|
||||
.choreographer
|
||||
.igc
|
||||
.igcTextData!
|
||||
.matches[controller.widget.scm.matchIndex]
|
||||
.match
|
||||
.choices![index]
|
||||
.isBestCorrection
|
||||
? BotExpression.gold
|
||||
: BotExpression.surprised
|
||||
),
|
||||
: BotExpression.surprised),
|
||||
);
|
||||
// if (controller.widget.scm.pangeaMatch.match.choices![index].type ==
|
||||
// SpanChoiceType.distractor) {
|
||||
|
|
@ -510,12 +507,11 @@ class DontShowSwitchListTileState extends State<DontShowSwitchListTile> {
|
|||
activeColor: AppConfig.activeToggleColor,
|
||||
title: Text(L10n.of(context)!.interactiveTranslatorAutoPlaySliderHeader),
|
||||
value: switchValue,
|
||||
onChanged: (value) => {
|
||||
widget.controller.pStoreService.save(
|
||||
PLocalKey.itAutoPlay.toString(),
|
||||
value,
|
||||
),
|
||||
setState(() => switchValue = value),
|
||||
onChanged: (value) {
|
||||
MatrixProfile.saveProfileData(
|
||||
{MatrixProfileEnum.itAutoPlay.title: value},
|
||||
);
|
||||
setState(() => switchValue = value);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,37 +1,34 @@
|
|||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/models/user_model.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PSettingsSwitchListTile extends StatefulWidget {
|
||||
class ProfileSettingsSwitchListTile extends StatefulWidget {
|
||||
final bool defaultValue;
|
||||
final String pStoreKey;
|
||||
final MatrixProfileEnum profileKey;
|
||||
final String title;
|
||||
final String? subtitle;
|
||||
final bool local;
|
||||
|
||||
const PSettingsSwitchListTile.adaptive({
|
||||
const ProfileSettingsSwitchListTile.adaptive({
|
||||
super.key,
|
||||
this.defaultValue = false,
|
||||
required this.pStoreKey,
|
||||
required this.profileKey,
|
||||
required this.title,
|
||||
this.subtitle,
|
||||
this.local = false,
|
||||
});
|
||||
|
||||
@override
|
||||
PSettingsSwitchListTileState createState() => PSettingsSwitchListTileState();
|
||||
}
|
||||
|
||||
class PSettingsSwitchListTileState extends State<PSettingsSwitchListTile> {
|
||||
class PSettingsSwitchListTileState
|
||||
extends State<ProfileSettingsSwitchListTile> {
|
||||
bool currentValue = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
currentValue = MatrixState.pangeaController.pStoreService.read(
|
||||
widget.pStoreKey,
|
||||
local: widget.local,
|
||||
currentValue = MatrixProfile.getProfileData(
|
||||
widget.profileKey,
|
||||
) ??
|
||||
widget.defaultValue;
|
||||
super.initState();
|
||||
|
|
@ -39,7 +36,6 @@ class PSettingsSwitchListTileState extends State<PSettingsSwitchListTile> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final PangeaController pangeaController = MatrixState.pangeaController;
|
||||
return SwitchListTile.adaptive(
|
||||
value: currentValue,
|
||||
title: Text(widget.title),
|
||||
|
|
@ -47,20 +43,17 @@ class PSettingsSwitchListTileState extends State<PSettingsSwitchListTile> {
|
|||
subtitle: widget.subtitle != null ? Text(widget.subtitle!) : null,
|
||||
onChanged: (bool newValue) async {
|
||||
try {
|
||||
await pangeaController.pStoreService.save(
|
||||
widget.pStoreKey,
|
||||
newValue,
|
||||
local: widget.local,
|
||||
);
|
||||
currentValue = newValue;
|
||||
MatrixProfile.saveProfileData({
|
||||
widget.profileKey.title: newValue,
|
||||
});
|
||||
setState(() => currentValue = newValue);
|
||||
} catch (err, s) {
|
||||
ErrorHandler.logError(
|
||||
e: err,
|
||||
m: "Failed to updates user setting ${widget.pStoreKey}",
|
||||
m: "Failed to updates user setting ${widget.profileKey.title}",
|
||||
s: s,
|
||||
);
|
||||
}
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -342,8 +342,11 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
|||
} else {
|
||||
// #Pangea
|
||||
if (state == LoginState.loggedIn) {
|
||||
await (await pangeaController.userController.completer).future;
|
||||
await pangeaController.subscriptionController.reinitialize();
|
||||
final futures = [
|
||||
pangeaController.userController.reinitialize(),
|
||||
pangeaController.subscriptionController.reinitialize(),
|
||||
];
|
||||
await Future.wait(futures);
|
||||
}
|
||||
String routeDestination;
|
||||
if (state == LoginState.loggedIn) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue