From e891ab138ace445bc429dd06b3ee6e12c676627e Mon Sep 17 00:00:00 2001 From: Gabby Gurdin Date: Mon, 29 Apr 2024 11:52:50 -0400 Subject: [PATCH] added ability to save key value data in matrix profile so it persists across sessions --- lib/pangea/constants/local.key.dart | 1 - lib/pangea/controllers/class_controller.dart | 4 +- .../message_analytics_controller.dart | 13 +- .../controllers/permissions_controller.dart | 5 +- .../controllers/subscription_controller.dart | 38 +- lib/pangea/controllers/user_controller.dart | 343 +++++++++++------- .../extensions/pangea_room_extension.dart | 12 +- lib/pangea/guard/p_vguard.dart | 20 +- lib/pangea/models/user_model.dart | 65 +++- .../settings_learning_view.dart | 2 + lib/pangea/utils/instructions.dart | 18 +- lib/pangea/utils/p_store.dart | 97 ++++- lib/pangea/widgets/class/join_with_link.dart | 10 +- .../p_settings_switch_list_tile.dart | 36 +- 14 files changed, 466 insertions(+), 198 deletions(-) diff --git a/lib/pangea/constants/local.key.dart b/lib/pangea/constants/local.key.dart index 839e6d89da..c0390c2ba9 100644 --- a/lib/pangea/constants/local.key.dart +++ b/lib/pangea/constants/local.key.dart @@ -1,6 +1,5 @@ class PLocalKey { static const String user = 'user'; - static const String matrixProfile = 'matrixProfile'; static const String classes = 'classes'; diff --git a/lib/pangea/controllers/class_controller.dart b/lib/pangea/controllers/class_controller.dart index a767036f7b..2c684b4597 100644 --- a/lib/pangea/controllers/class_controller.dart +++ b/lib/pangea/controllers/class_controller.dart @@ -49,12 +49,14 @@ class ClassController extends BaseController { final String? classCode = _pangeaController.pStoreService.read( PLocalKey.cachedClassCodeToJoin, addClientIdToKey: false, + local: true, ); if (classCode != null) { - _pangeaController.pStoreService.delete( + await _pangeaController.pStoreService.delete( PLocalKey.cachedClassCodeToJoin, addClientIdToKey: false, + local: true, ); await joinClasswithCode( context, diff --git a/lib/pangea/controllers/message_analytics_controller.dart b/lib/pangea/controllers/message_analytics_controller.dart index d20e0bf36d..c71af9a929 100644 --- a/lib/pangea/controllers/message_analytics_controller.dart +++ b/lib/pangea/controllers/message_analytics_controller.dart @@ -33,8 +33,10 @@ class AnalyticsController extends BaseController { TimeSpan get currentAnalyticsTimeSpan { try { - final String? str = - _pangeaController.pStoreService.read(_analyticsTimeSpanKey); + final String? str = _pangeaController.pStoreService.read( + _analyticsTimeSpanKey, + local: true, + ); return str != null ? TimeSpan.values.firstWhere((e) { final spanString = e.toString(); @@ -48,8 +50,11 @@ class AnalyticsController extends BaseController { } Future setCurrentAnalyticsTimeSpan(TimeSpan timeSpan) async { - await _pangeaController.pStoreService - .save(_analyticsTimeSpanKey, timeSpan.toString()); + await _pangeaController.pStoreService.save( + _analyticsTimeSpanKey, + timeSpan.toString(), + local: true, + ); } Future> allClassAnalytics() async { diff --git a/lib/pangea/controllers/permissions_controller.dart b/lib/pangea/controllers/permissions_controller.dart index 314b17c296..6c9a063968 100644 --- a/lib/pangea/controllers/permissions_controller.dart +++ b/lib/pangea/controllers/permissions_controller.dart @@ -4,6 +4,7 @@ import 'package:fluffychat/pangea/controllers/base_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/models/class_model.dart'; +import 'package:fluffychat/pangea/models/user_model.dart'; import 'package:fluffychat/pangea/utils/p_extension.dart'; import 'package:matrix/matrix.dart'; @@ -31,7 +32,9 @@ class PermissionsController extends BaseController { /// Returns false if user is null bool isUser18() { - final dob = _pangeaController.userController.matrixProfile?.dateOfBirth; + final dob = _pangeaController.pStoreService.read( + MatrixProfile.dateOfBirth.title, + ); return dob != null ? DateTime.parse(dob).isAtLeastYearsOld(AgeLimits.toAccessFeatures) : false; diff --git a/lib/pangea/controllers/subscription_controller.dart b/lib/pangea/controllers/subscription_controller.dart index 828fd5365e..7dac081d7f 100644 --- a/lib/pangea/controllers/subscription_controller.dart +++ b/lib/pangea/controllers/subscription_controller.dart @@ -8,6 +8,7 @@ import 'package:fluffychat/pangea/controllers/base_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/models/base_subscription_info.dart'; import 'package:fluffychat/pangea/models/mobile_subscriptions.dart'; +import 'package:fluffychat/pangea/models/user_model.dart'; import 'package:fluffychat/pangea/models/web_subscriptions.dart'; import 'package:fluffychat/pangea/network/requests.dart'; import 'package:fluffychat/pangea/network/urls.dart'; @@ -78,9 +79,13 @@ class SubscriptionController extends BaseController { } else { final bool? beganWebPayment = _pangeaController.pStoreService.read( PLocalKey.beganWebPayment, + local: true, ); if (beganWebPayment ?? false) { - _pangeaController.pStoreService.delete(PLocalKey.beganWebPayment); + await _pangeaController.pStoreService.delete( + PLocalKey.beganWebPayment, + local: true, + ); if (_pangeaController.subscriptionController.isSubscribed) { subscriptionStream.add(true); } @@ -116,9 +121,10 @@ class SubscriptionController extends BaseController { selectedSubscription.duration!, isPromo: isPromo, ); - _pangeaController.pStoreService.save( + await _pangeaController.pStoreService.save( PLocalKey.beganWebPayment, true, + local: true, ); setState(); launchUrlString( @@ -160,12 +166,18 @@ class SubscriptionController extends BaseController { bool get _activatedNewUserTrial => _pangeaController.userController.inTrialWindow && - (_pangeaController.pStoreService.read(PLocalKey.activatedTrialKey) ?? + (_pangeaController.pStoreService.read( + MatrixProfile.activatedFreeTrial.title, + ) ?? false); void activateNewUserTrial() { - _pangeaController.pStoreService.save(PLocalKey.activatedTrialKey, true); - setNewUserTrial(); + _pangeaController.pStoreService + .save( + MatrixProfile.activatedFreeTrial.title, + true, + ) + .then((_) => setNewUserTrial()); } void setNewUserTrial() { @@ -206,6 +218,7 @@ 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); @@ -214,6 +227,7 @@ class SubscriptionController extends BaseController { int? get _paywallBackoff { final backoff = _pangeaController.pStoreService.read( PLocalKey.paywallBackoff, + local: true, ); if (backoff == null) return null; return backoff; @@ -227,18 +241,24 @@ class SubscriptionController extends BaseController { (24 * (_paywallBackoff ?? 1))); } - void dismissPaywall() { - _pangeaController.pStoreService.save( + void dismissPaywall() async { + await _pangeaController.pStoreService.save( PLocalKey.dismissedPaywall, DateTime.now().toString(), + local: true, ); if (_paywallBackoff == null) { - _pangeaController.pStoreService.save(PLocalKey.paywallBackoff, 1); + await _pangeaController.pStoreService.save( + PLocalKey.paywallBackoff, + 1, + local: true, + ); } else { - _pangeaController.pStoreService.save( + await _pangeaController.pStoreService.save( PLocalKey.paywallBackoff, _paywallBackoff! + 1, + local: true, ); } } diff --git a/lib/pangea/controllers/user_controller.dart b/lib/pangea/controllers/user_controller.dart index e1b326eaba..a49376cd3c 100644 --- a/lib/pangea/controllers/user_controller.dart +++ b/lib/pangea/controllers/user_controller.dart @@ -1,14 +1,11 @@ import 'dart:async'; -import 'dart:developer'; import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/constants/language_keys.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart'; -import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/controllers/base_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; -import 'package:fluffychat/pangea/utils/error_handler.dart'; -import 'package:fluffychat/widgets/fluffy_chat_app.dart'; +import 'package:flutter/material.dart'; import 'package:jwt_decode/jwt_decode.dart'; import 'package:matrix/matrix.dart' as matrix; @@ -23,6 +20,17 @@ class UserController extends BaseController { _pangeaController = pangeaController; } + Future createPangeaUser({required String dob}) async { + final PUserModel newUserModel = await PUserRepo.repoCreatePangeaUser( + userID: userId!, + fullName: fullname, + dob: dob, + matrixAccessToken: _matrixAccessToken!, + ); + newUserModel.save(_pangeaController); + await updateMatrixProfile(dateOfBirth: dob); + } + Future fetchUserModel() async { try { if (_matrixAccessToken == null) { @@ -30,48 +38,209 @@ class UserController extends BaseController { "calling fetchUserModel with matrixAccesstoken == null", ); } + final PUserModel? newUserModel = await PUserRepo.fetchPangeaUserInfo( userID: userId!, matrixAccessToken: _matrixAccessToken!, ); - - if (newUserModel != null) { - _savePUserModel(newUserModel); - if (newUserModel.profile!.dateOfBirth != null) { - await setMatrixProfile(newUserModel.profile!.dateOfBirth!); - } - final MatrixProfile? matrixProfile = await getMatrixProfile(); - _saveMatrixProfile(matrixProfile); - } + newUserModel?.save(_pangeaController); + await migrateMatrixProfile(); _completeCompleter(); return newUserModel; } catch (err) { - log("User model not found. Probably first signup and needs Pangea account"); + debugPrint( + "User model not found. Probably first signup and needs Pangea account", + ); rethrow; } } - Future setMatrixProfile(String dob) async { - await _pangeaController.matrixState.client.setAccountData( - userId!, - PangeaEventTypes.userAge, - {ModelKey.userDateOfBirth: dob}, + dynamic migratedProfileInfo(MatrixProfile key) { + final dynamic localValue = _pangeaController.pStoreService.read( + key.title, + local: true, ); - final MatrixProfile? matrixProfile = await getMatrixProfile(); - _saveMatrixProfile(matrixProfile); + final dynamic matrixValue = _pangeaController.pStoreService.read( + key.title, + ); + return localValue != null && matrixValue != localValue ? localValue : null; } - Future getMatrixProfile() async { - try { - final Map accountData = - await _pangeaController.matrixState.client.getAccountData( - userId!, - PangeaEventTypes.userAge, + Future migrateMatrixProfile() async { + final String? pangeaDob = userModel?.profile?.dateOfBirth; + final String? matrixDob = _pangeaController.pStoreService.read( + ModelKey.userDateOfBirth, + ); + final String? dob = + pangeaDob != null && matrixDob != pangeaDob ? pangeaDob : null; + + final bool? autoPlay = migratedProfileInfo(MatrixProfile.autoPlayMessages); + 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, + activatedFreeTrial: trial, + interactiveTranslator: interactiveTranslator, + interactiveGrammar: interactiveGrammar, + immersionMode: immersionMode, + definitions: definitions, + translations: translations, + showedItInstructions: showItInstructions, + showedClickMessage: showClickMessage, + showedBlurMeansTranslate: showBlurMeansTranslate, + ); + } + + Future updateUserProfile({ + String? dateOfBirth, + String? targetLanguage, + String? sourceLanguage, + String? country, + List? interests, + List? speaks, + bool? publicProfile, + }) async { + if (userModel == null) throw Exception("Local userModel not defined"); + final profileJson = userModel!.profile!.toJson(); + + if (dateOfBirth != null) { + profileJson[ModelKey.userDateOfBirth] = dateOfBirth; + } + if (targetLanguage != null) { + profileJson[ModelKey.userTargetLanguage] = targetLanguage; + } + if (sourceLanguage != null) { + profileJson[ModelKey.userSourceLanguage] = sourceLanguage; + } + if (interests != null) { + profileJson[ModelKey.userInterests] = interests.toString(); + } + if (speaks != null) { + profileJson[ModelKey.userSpeaks] = speaks.toString(); + } + if (country != null) { + profileJson[ModelKey.userCountry] = country; + } + if (publicProfile != null) { + profileJson[ModelKey.publicProfile] = publicProfile; + } + final Profile updatedUserProfile = await PUserRepo.updateUserProfile( + Profile.fromJson(profileJson), + await accessToken, + ); + + PUserModel( + access: await accessToken, + refresh: userModel!.refresh, + profile: updatedUserProfile, + ).save(_pangeaController); + if (dateOfBirth != null) { + await updateMatrixProfile(dateOfBirth: dateOfBirth); + } + } + + PUserModel? get userModel { + final data = _pangeaController.pStoreService.read( + PLocalKey.user, + local: true, + ); + return data != null ? PUserModel.fromJson(data) : null; + } + + Future updateMatrixProfile({ + String? dateOfBirth, + bool? autoPlayMessages, + bool? activatedFreeTrial, + bool? interactiveTranslator, + bool? interactiveGrammar, + bool? immersionMode, + bool? definitions, + bool? translations, + bool? showedItInstructions, + bool? showedClickMessage, + bool? showedBlurMeansTranslate, + }) async { + if (dateOfBirth != null) { + await _pangeaController.pStoreService.save( + MatrixProfile.dateOfBirth.title, + dateOfBirth, + ); + } + if (autoPlayMessages != null) { + await _pangeaController.pStoreService.save( + MatrixProfile.autoPlayMessages.title, + autoPlayMessages, + ); + } + 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, ); - return MatrixProfile.fromJson(accountData); - } catch (_) { - return null; } } @@ -116,16 +285,6 @@ class UserController extends BaseController { return userID.substring(0, userID.indexOf(":")).replaceAll("@", ""); } - PUserModel? get userModel { - final data = _pangeaController.pStoreService.read(PLocalKey.user); - return data != null ? PUserModel.fromJson(data) : null; - } - - MatrixProfile? get matrixProfile { - final data = _pangeaController.pStoreService.read(PLocalKey.matrixProfile); - return data != null ? MatrixProfile.fromJson(data) : null; - } - Future get isPUserDataAvailable async { try { final PUserModel? toCheck = userModel ?? (await fetchUserModel()); @@ -137,10 +296,15 @@ class UserController extends BaseController { Future get isUserDataAvailableAndDateOfBirthSet async { try { - if (matrixProfile == null) { - await fetchUserModel(); + final client = _pangeaController.matrixState.client; + if (client.prevBatch == null) { + await client.onSync.stream.first; } - return matrixProfile?.dateOfBirth != null ? true : false; + await fetchUserModel(); + final localAccountData = _pangeaController.pStoreService.read( + ModelKey.userDateOfBirth, + ); + return localAccountData != null; } catch (err) { return false; } @@ -175,99 +339,6 @@ class UserController extends BaseController { } } - redirectToUserInfo() { - // _pangeaController.matrix.router!.currentState!.to( - // "/home/connect/user_age", - // queryParameters: - // _pangeaController.matrix.router!.currentState!.queryParameters, - // ); - FluffyChatApp.router.go("/rooms/user_age"); - } - - _saveMatrixProfile(MatrixProfile? matrixProfile) { - if (matrixProfile != null) { - _pangeaController.pStoreService.save( - PLocalKey.matrixProfile, - matrixProfile.toJson(), - ); - setState(data: matrixProfile); - } - } - - _savePUserModel(PUserModel? pUserModel) { - if (pUserModel == null) { - ErrorHandler.logError(e: "trying to save null userModel"); - return; - } - final jsonUser = pUserModel.toJson(); - _pangeaController.pStoreService.save(PLocalKey.user, jsonUser); - - setState(data: pUserModel); - } - - Future updateUserProfile({ - String? dateOfBirth, - String? targetLanguage, - String? sourceLanguage, - String? country, - List? interests, - List? speaks, - bool? publicProfile, - }) async { - if (userModel == null) throw Exception("Local userModel not defined"); - final profileJson = userModel!.profile!.toJson(); - - if (dateOfBirth != null) { - profileJson[ModelKey.userDateOfBirth] = dateOfBirth; - } - if (targetLanguage != null) { - profileJson[ModelKey.userTargetLanguage] = targetLanguage; - } - if (sourceLanguage != null) { - profileJson[ModelKey.userSourceLanguage] = sourceLanguage; - } - if (interests != null) { - profileJson[ModelKey.userInterests] = interests.toString(); - } - if (speaks != null) { - profileJson[ModelKey.userSpeaks] = speaks.toString(); - } - if (country != null) { - profileJson[ModelKey.userCountry] = country; - } - if (publicProfile != null) { - profileJson[ModelKey.publicProfile] = publicProfile; - } - final Profile updatedUserProfile = await PUserRepo.updateUserProfile( - Profile.fromJson(profileJson), - await accessToken, - ); - - await _savePUserModel( - PUserModel( - access: await accessToken, - refresh: userModel!.refresh, - profile: updatedUserProfile, - ), - ); - - if (dateOfBirth != null) { - await setMatrixProfile(dateOfBirth); - } - } - - Future createPangeaUser({required String dob}) async { - final PUserModel newUserModel = await PUserRepo.repoCreatePangeaUser( - userID: userId!, - fullName: fullname, - dob: dob, - matrixAccessToken: _matrixAccessToken!, - ); - await _savePUserModel(newUserModel); - - await setMatrixProfile(dob); - } - String? get _matrixAccessToken => _pangeaController.matrixState.client.accessToken; diff --git a/lib/pangea/extensions/pangea_room_extension.dart b/lib/pangea/extensions/pangea_room_extension.dart index 79e69e1fb7..93d5d33acd 100644 --- a/lib/pangea/extensions/pangea_room_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension.dart @@ -490,7 +490,11 @@ extension PangeaRoom on Room { final String migratedAnalyticsKey = "MIGRATED_ANALYTICS_KEY${id.localpart}"; - if (storageService?.read(migratedAnalyticsKey) ?? false) return; + if (storageService?.read( + migratedAnalyticsKey, + local: true, + ) ?? + false) return; if (!isPangeaClass && !isExchange) { throw Exception( @@ -522,7 +526,11 @@ extension PangeaRoom on Room { ); myAnalEvent.bulkUpdate(updateMessages); - storageService?.save(migratedAnalyticsKey, true); + await storageService?.save( + migratedAnalyticsKey, + true, + local: true, + ); } catch (err, s) { if (kDebugMode) rethrow; // debugger(when: kDebugMode); diff --git a/lib/pangea/guard/p_vguard.dart b/lib/pangea/guard/p_vguard.dart index 3e5368fad4..ce3805a634 100644 --- a/lib/pangea/guard/p_vguard.dart +++ b/lib/pangea/guard/p_vguard.dart @@ -1,10 +1,9 @@ import 'dart:async'; +import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; - import 'package:go_router/go_router.dart'; -import 'package:fluffychat/widgets/matrix.dart'; import '../controllers/pangea_controller.dart'; class PAuthGaurd { @@ -16,10 +15,10 @@ class PAuthGaurd { GoRouterState state, ) async { if (pController != null) { - final bool setDob = await pController! - .userController.isUserDataAvailableAndDateOfBirthSet; if (Matrix.of(context).client.isLogged()) { - return !setDob ? '/user_age' : '/rooms'; + final bool dobIsSet = await pController! + .userController.isUserDataAvailableAndDateOfBirthSet; + return dobIsSet ? '/rooms' : '/user_age'; } return null; } else { @@ -34,13 +33,12 @@ class PAuthGaurd { GoRouterState state, ) async { if (pController != null) { - final bool setDob = await pController! + if (!Matrix.of(context).client.isLogged()) { + return '/home'; + } + final bool dobIsSet = await pController! .userController.isUserDataAvailableAndDateOfBirthSet; - return !Matrix.of(context).client.isLogged() - ? '/home' - : !setDob - ? '/user_age' - : null; + return dobIsSet ? null : '/user_age'; } else { debugPrint("controller is null in pguard check"); return Matrix.of(context).client.isLogged() ? null : '/home'; diff --git a/lib/pangea/models/user_model.dart b/lib/pangea/models/user_model.dart index c0e5a58f0e..62e172da14 100644 --- a/lib/pangea/models/user_model.dart +++ b/lib/pangea/models/user_model.dart @@ -1,7 +1,11 @@ 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/models/class_model.dart'; +import 'package:fluffychat/pangea/utils/instructions.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -37,23 +41,56 @@ class PUserModel { } return data; } -} - -class MatrixProfile { - String dateOfBirth; - MatrixProfile({ - required this.dateOfBirth, - }); + Future save(PangeaController pangeaController) async { + await pangeaController.pStoreService.save( + PLocalKey.user, + toJson(), + local: true, + ); + } +} - factory MatrixProfile.fromJson(Map json) => MatrixProfile( - dateOfBirth: json[ModelKey.userDateOfBirth], - ); +enum MatrixProfile { + dateOfBirth, + autoPlayMessages, + activatedFreeTrial, + interactiveTranslator, + interactiveGrammar, + immersionMode, + definitions, + translations, + showedItInstructions, + showedClickMessage, + showedBlurMeansTranslate, +} - Map toJson() { - final Map data = {}; - data[ModelKey.userDateOfBirth] = dateOfBirth; - return data; +extension MatrixProfileExtension on MatrixProfile { + String get title { + switch (this) { + case MatrixProfile.dateOfBirth: + return ModelKey.userDateOfBirth; + case MatrixProfile.autoPlayMessages: + return PLocalKey.autoPlayMessages; + case MatrixProfile.activatedFreeTrial: + return PLocalKey.activatedTrialKey; + case MatrixProfile.interactiveTranslator: + return ToolSetting.interactiveTranslator.toString(); + case MatrixProfile.interactiveGrammar: + return ToolSetting.interactiveGrammar.toString(); + case MatrixProfile.immersionMode: + return ToolSetting.immersionMode.toString(); + case MatrixProfile.definitions: + return ToolSetting.definitions.toString(); + case MatrixProfile.translations: + return ToolSetting.translations.toString(); + case MatrixProfile.showedItInstructions: + return InstructionsEnum.itInstructions.toString(); + case MatrixProfile.showedClickMessage: + return InstructionsEnum.clickMessage.toString(); + case MatrixProfile.showedBlurMeansTranslate: + return InstructionsEnum.blurMeansTranslate.toString(); + } } } diff --git a/lib/pangea/pages/settings_learning/settings_learning_view.dart b/lib/pangea/pages/settings_learning/settings_learning_view.dart index f397a834ab..125b2ac999 100644 --- a/lib/pangea/pages/settings_learning/settings_learning_view.dart +++ b/lib/pangea/pages/settings_learning/settings_learning_view.dart @@ -59,6 +59,7 @@ class SettingsLearningView extends StatelessWidget { title: setting.toolName(context), subtitle: setting.toolDescription(context), pStoreKey: setting.toString(), + local: false, ), PSettingsSwitchListTile.adaptive( defaultValue: controller.pangeaController.pStoreService.read( @@ -68,6 +69,7 @@ class SettingsLearningView extends StatelessWidget { title: L10n.of(context)!.autoPlayTitle, subtitle: L10n.of(context)!.autoPlayDesc, pStoreKey: PLocalKey.autoPlayMessages, + local: false, ), ], ), diff --git a/lib/pangea/utils/instructions.dart b/lib/pangea/utils/instructions.dart index 0d84aa270e..f1fa8b59f2 100644 --- a/lib/pangea/utils/instructions.dart +++ b/lib/pangea/utils/instructions.dart @@ -25,8 +25,14 @@ class InstructionsController { _instructionsClosed[key] ?? false; - void updateEnableInstructions(InstructionsEnum key, bool value) => - _pangeaController.pStoreService.save(key.toString(), value); + Future updateEnableInstructions( + InstructionsEnum key, + bool value, + ) async => + await _pangeaController.pStoreService.save( + key.toString(), + value, + ); Future show( BuildContext context, @@ -149,9 +155,11 @@ class InstructionsToggleState extends State { title: Text(L10n.of(context)!.doNotShowAgain), value: pangeaController.instructions .wereInstructionsTurnedOff(widget.instructionsKey), - onChanged: ((value) { - pangeaController.instructions - .updateEnableInstructions(widget.instructionsKey, value); + onChanged: ((value) async { + await pangeaController.instructions.updateEnableInstructions( + widget.instructionsKey, + value, + ); setState(() {}); }), ); diff --git a/lib/pangea/utils/p_store.dart b/lib/pangea/utils/p_store.dart index bdc0efc6c9..f92e411768 100644 --- a/lib/pangea/utils/p_store.dart +++ b/lib/pangea/utils/p_store.dart @@ -1,6 +1,7 @@ -import 'package:get_storage/get_storage.dart'; - 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 { final GetStorage _box = GetStorage(); @@ -13,24 +14,112 @@ class PLocalStore { String key, dynamic data, { bool addClientIdToKey = true, + bool local = false, + }) async { + local + ? await saveLocal( + key, + data, + addClientIdToKey: addClientIdToKey, + ) + : await saveProfile(key, 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 delete( + String key, { + bool addClientIdToKey = true, + local = false, + }) async { + return local + ? deleteLocal( + key, + addClientIdToKey: addClientIdToKey, + ) + : deleteProfile(key); + } + + /// save data in local + Future saveLocal( + String key, + dynamic data, { + bool addClientIdToKey = true, }) async { await _box.write(_key(key, addClientIdToKey: addClientIdToKey), data); } + Future 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 read(String key, {bool addClientIdToKey = true}) { + dynamic readLocal(String key, {bool addClientIdToKey = true}) { return pangeaController.matrixState.client.userID != null ? _box.read(_key(key, addClientIdToKey: addClientIdToKey)) : 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 delete(String key, {bool addClientIdToKey = true}) async { + Future deleteLocal(String key, {bool addClientIdToKey = true}) async { return pangeaController.matrixState.client.userID != null ? _box.remove(_key(key, addClientIdToKey: addClientIdToKey)) : null; } + Future 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 ? pangeaController.matrixState.client.userID! + key diff --git a/lib/pangea/widgets/class/join_with_link.dart b/lib/pangea/widgets/class/join_with_link.dart index 8011d718be..15b52bd41e 100644 --- a/lib/pangea/widgets/class/join_with_link.dart +++ b/lib/pangea/widgets/class/join_with_link.dart @@ -1,12 +1,11 @@ -import 'package:flutter/material.dart'; - -import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:go_router/go_router.dart'; - import 'package:fluffychat/pangea/constants/url_query_parameter_keys.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/utils/class_code.dart'; import 'package:fluffychat/widgets/layouts/empty_page.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:go_router/go_router.dart'; + import '../../../widgets/matrix.dart'; import '../../constants/local.key.dart'; import '../../utils/error_handler.dart'; @@ -49,6 +48,7 @@ class _JoinClassWithLinkState extends State { PLocalKey.cachedClassCodeToJoin, classCode, addClientIdToKey: false, + local: true, ); context.go("/home"); }); diff --git a/lib/pangea/widgets/user_settings/p_settings_switch_list_tile.dart b/lib/pangea/widgets/user_settings/p_settings_switch_list_tile.dart index 1ae2277a02..4c3e775996 100644 --- a/lib/pangea/widgets/user_settings/p_settings_switch_list_tile.dart +++ b/lib/pangea/widgets/user_settings/p_settings_switch_list_tile.dart @@ -1,14 +1,15 @@ -import 'package:flutter/material.dart'; - import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; +import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/widgets/matrix.dart'; +import 'package:flutter/material.dart'; class PSettingsSwitchListTile extends StatefulWidget { final bool defaultValue; final String pStoreKey; final String title; final String? subtitle; + final bool local; const PSettingsSwitchListTile.adaptive({ super.key, @@ -16,6 +17,7 @@ class PSettingsSwitchListTile extends StatefulWidget { required this.pStoreKey, required this.title, this.subtitle, + this.local = false, }); @override @@ -23,17 +25,41 @@ class PSettingsSwitchListTile extends StatefulWidget { } class PSettingsSwitchListTileState extends State { + bool currentValue = true; + + @override + void initState() { + currentValue = MatrixState.pangeaController.pStoreService.read( + widget.pStoreKey, + local: widget.local, + ) ?? + widget.defaultValue; + super.initState(); + } + @override Widget build(BuildContext context) { final PangeaController pangeaController = MatrixState.pangeaController; return SwitchListTile.adaptive( - value: pangeaController.pStoreService.read(widget.pStoreKey) ?? - widget.defaultValue, + value: currentValue, title: Text(widget.title), activeColor: AppConfig.activeToggleColor, subtitle: widget.subtitle != null ? Text(widget.subtitle!) : null, onChanged: (bool newValue) async { - pangeaController.pStoreService.save(widget.pStoreKey, newValue); + try { + await pangeaController.pStoreService.save( + widget.pStoreKey, + newValue, + local: widget.local, + ); + currentValue = newValue; + } catch (err, s) { + ErrorHandler.logError( + e: err, + m: "Failed to updates user setting ${widget.pStoreKey}", + s: s, + ); + } setState(() {}); }, );