diff --git a/lib/pangea/choreographer/controllers/choreographer.dart b/lib/pangea/choreographer/controllers/choreographer.dart index 9ccb197ee..e5108ae4d 100644 --- a/lib/pangea/choreographer/controllers/choreographer.dart +++ b/lib/pangea/choreographer/controllers/choreographer.dart @@ -513,8 +513,9 @@ class Choreographer { chatController.room, ); - bool get itAutoPlayEnabled => - pangeaController.userController.matrixProfile.itAutoPlay; + bool get itAutoPlayEnabled { + return pangeaController.userController.profile.userSettings.itAutoPlay; + } bool get definitionsEnabled => pangeaController.permissionsController.isToolEnabled( diff --git a/lib/pangea/constants/local.key.dart b/lib/pangea/constants/local.key.dart index b8c1a4f5d..08446ace4 100644 --- a/lib/pangea/constants/local.key.dart +++ b/lib/pangea/constants/local.key.dart @@ -1,5 +1,5 @@ class PLocalKey { - static const String user = 'user'; + static const String access = "access"; static const String cachedClassCodeToJoin = "cachedclasscodetojoin"; static const String beganWebPayment = "beganWebPayment"; static const String dismissedPaywall = 'dismissedPaywall'; diff --git a/lib/pangea/constants/model_keys.dart b/lib/pangea/constants/model_keys.dart index 08164d9f6..7c60b98d7 100644 --- a/lib/pangea/constants/model_keys.dart +++ b/lib/pangea/constants/model_keys.dart @@ -16,6 +16,9 @@ class ModelKey { static const String l1LanguageKey = 'source_language'; static const String publicProfile = 'public'; static const String userId = 'user_id'; + static const String toolSettings = 'tool_settings'; + static const String userSettings = 'user_settings'; + static const String instructionsSettings = 'instructions_settings'; // matrix profile keys // making this a random string so that it's harder to guess diff --git a/lib/pangea/controllers/language_controller.dart b/lib/pangea/controllers/language_controller.dart index b1bc02380..ccd33761f 100644 --- a/lib/pangea/controllers/language_controller.dart +++ b/lib/pangea/controllers/language_controller.dart @@ -1,12 +1,8 @@ -import 'dart:developer'; - import 'package:fluffychat/pangea/constants/language_constants.dart'; import 'package:fluffychat/pangea/controllers/language_list_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/models/language_model.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:sentry_flutter/sentry_flutter.dart'; import '../widgets/user_settings/p_language_dialog.dart'; @@ -18,15 +14,6 @@ class LanguageController { } //show diloag when user does not have languages selected showDialogOnEmptyLanguage(BuildContext dialogContext, Function callback) { - if (_pangeaController.userController.userModel?.profile == null) { - debugger(when: kDebugMode); - Sentry.addBreadcrumb( - Breadcrumb( - message: 'calling showDialogOnEmptyLanguagae with empty user', - ), - ); - return; - } if (!languagesSet) { pLanguageDialog(dialogContext, callback); } @@ -42,13 +29,13 @@ class LanguageController { String? get _userL1Code { final source = - _pangeaController.userController.userModel?.profile?.sourceLanguage; + _pangeaController.userController.profile.userSettings.sourceLanguage; return source == null || source.isEmpty ? null : source; } String? get _userL2Code { final target = - _pangeaController.userController.userModel?.profile?.targetLanguage; + _pangeaController.userController.profile.userSettings.targetLanguage; return target == null || target.isEmpty ? null : target; } diff --git a/lib/pangea/controllers/my_analytics_controller.dart b/lib/pangea/controllers/my_analytics_controller.dart index 058bad359..8fa8989b1 100644 --- a/lib/pangea/controllers/my_analytics_controller.dart +++ b/lib/pangea/controllers/my_analytics_controller.dart @@ -204,24 +204,24 @@ class MyAnalyticsController { Completer? _updateCompleter; Future updateAnalytics() async { - if (!(_updateCompleter?.isCompleted ?? true)) { - await _updateCompleter!.future; - return; - } - _updateCompleter = Completer(); - try { - await _updateAnalytics(); - clearMessagesSinceUpdate(); - } catch (err, s) { - ErrorHandler.logError( - e: err, - m: "Failed to update analytics", - s: s, - ); - } finally { - _updateCompleter?.complete(); - _updateCompleter = null; - } + // if (!(_updateCompleter?.isCompleted ?? true)) { + // await _updateCompleter!.future; + // return; + // } + // _updateCompleter = Completer(); + // try { + // await _updateAnalytics(); + // clearMessagesSinceUpdate(); + // } catch (err, s) { + // ErrorHandler.logError( + // e: err, + // m: "Failed to update analytics", + // s: s, + // ); + // } finally { + // _updateCompleter?.complete(); + // _updateCompleter = null; + // } } String? get userL2 => _pangeaController.languageController.activeL2Code(); diff --git a/lib/pangea/controllers/permissions_controller.dart b/lib/pangea/controllers/permissions_controller.dart index 463a8c9a3..ed84e13c3 100644 --- a/lib/pangea/controllers/permissions_controller.dart +++ b/lib/pangea/controllers/permissions_controller.dart @@ -4,7 +4,6 @@ import 'package:fluffychat/pangea/controllers/base_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/pangea/models/space_model.dart'; -import 'package:fluffychat/pangea/models/user_model.dart'; import 'package:fluffychat/pangea/utils/p_extension.dart'; import 'package:matrix/matrix.dart'; @@ -31,12 +30,11 @@ class PermissionsController extends BaseController { } /// Returns false if user is null - bool isUser18() => - _pangeaController.userController.matrixProfile.dateOfBirth - ?.isAtLeastYearsOld( - AgeLimits.toAccessFeatures, - ) ?? - false; + bool isUser18() { + final DateTime? dob = + _pangeaController.userController.profile.userSettings.dateOfBirth; + return dob?.isAtLeastYearsOld(AgeLimits.toAccessFeatures) ?? false; + } /// A user can private chat if /// 1) they are 18 and outside a class context or @@ -97,20 +95,22 @@ class PermissionsController extends BaseController { return classPermission == 0; } - bool userToolSetting(MatrixProfileEnum setting) { - switch (setting.asToolSetting) { + bool userToolSetting(ToolSetting setting) { + switch (setting) { case ToolSetting.interactiveTranslator: return _pangeaController - .userController.matrixProfile.interactiveTranslator; + .userController.profile.toolSettings.interactiveTranslator; case ToolSetting.interactiveGrammar: return _pangeaController - .userController.matrixProfile.interactiveGrammar; + .userController.profile.toolSettings.interactiveGrammar; case ToolSetting.immersionMode: - return _pangeaController.userController.matrixProfile.immersionMode; + return _pangeaController + .userController.profile.toolSettings.immersionMode; case ToolSetting.definitions: - return _pangeaController.userController.matrixProfile.definitions; + return _pangeaController + .userController.profile.toolSettings.definitions; case ToolSetting.autoIGC: - return _pangeaController.userController.matrixProfile.autoIGC; + return _pangeaController.userController.profile.toolSettings.autoIGC; default: return false; } @@ -118,13 +118,13 @@ class PermissionsController extends BaseController { bool isToolEnabled(ToolSetting setting, Room? room) { if (room?.isSpaceAdmin ?? false) { - return userToolSetting(setting.asMatrixProfileField); + return userToolSetting(setting); } final int? classPermission = room != null ? classLanguageToolPermission(room, setting) : 1; if (classPermission == 0) return false; if (classPermission == 2) return true; - return userToolSetting(setting.asMatrixProfileField); + return userToolSetting(setting); } bool isWritingAssistanceEnabled(Room? room) { diff --git a/lib/pangea/controllers/subscription_controller.dart b/lib/pangea/controllers/subscription_controller.dart index e825a7126..d5a04a82d 100644 --- a/lib/pangea/controllers/subscription_controller.dart +++ b/lib/pangea/controllers/subscription_controller.dart @@ -8,7 +8,6 @@ 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'; @@ -179,31 +178,35 @@ class SubscriptionController extends BaseController { } } - bool get _activatedNewUserTrial => - _pangeaController.userController.inTrialWindow && - _pangeaController.userController.matrixProfile.activatedFreeTrial; + bool get _activatedNewUserTrial { + final bool activated = _pangeaController + .userController.profile.userSettings.activatedFreeTrial; + return _pangeaController.userController.inTrialWindow && activated; + } void activateNewUserTrial() { - _pangeaController.userController.matrixProfile.saveProfileData({ - MatrixProfileEnum.activatedFreeTrial.title: true, - }).then((_) { - setNewUserTrial(); - trialActivationStream.add(true); - }); + _pangeaController.userController.updateProfile( + (profile) { + profile.userSettings.activatedFreeTrial = true; + return profile; + }, + ); + setNewUserTrial(); + trialActivationStream.add(true); } void setNewUserTrial() { - if (_pangeaController.userController.userModel?.profile == null) { + final DateTime? createdAt = + _pangeaController.userController.profile.userSettings.createdAt; + if (createdAt == null) { ErrorHandler.logError( - m: "Null user profile in subscription settings", + m: "Null user profile createAt in subscription settings", s: StackTrace.current, ); return; } - final String profileCreatedAt = - _pangeaController.userController.userModel!.profile!.createdAt; - final DateTime creationTimestamp = DateTime.parse(profileCreatedAt); - final DateTime expirationDate = creationTimestamp.add( + + final DateTime expirationDate = createdAt.add( const Duration(days: 7), ); subscription?.setTrial(expirationDate); diff --git a/lib/pangea/controllers/user_controller.dart b/lib/pangea/controllers/user_controller.dart index 1fd13acfc..a8c6ced14 100644 --- a/lib/pangea/controllers/user_controller.dart +++ b/lib/pangea/controllers/user_controller.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:collection/collection.dart'; 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:fluffychat/pangea/utils/error_handler.dart'; @@ -27,61 +26,62 @@ class UserController extends BaseController { String? get _matrixAccessToken => _pangeaController.matrixState.client.accessToken; - /// An instance of matrix profile. Used to update and access info from the user's matrix profile. - /// No information needs to be passing in the constructor as the matrix - /// profile get all of it's internal data the accountData stored in the client. - MatrixProfile matrixProfile = MatrixProfile(); + Profile? _cachedProfile; - /// 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; + /// Listen for updates to account data in syncs and update the cached profile + void addProfileListener() { + _pangeaController.matrixState.client.onSync.stream + .where((sync) => sync.accountData != null) + .listen((sync) { + final Profile? fromAccountData = Profile.fromAccountData(); + if (fromAccountData != null) { + _cachedProfile = fromAccountData; + } + }); } - /// 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 createProfile({required String dob}) async { - if (userId == null || _matrixAccessToken == null) { - ErrorHandler.logError( - e: "calling createProfile with userId == null or matrixAccessToken == null", - ); + /// The user's profile. Will be empty if the client's accountData hasn't + /// been loaded yet (if the first sync hasn't gone through yet) + /// or if the user hasn't yer set their date of birth. + Profile get profile { + /// if the profile is cached, return it + if (_cachedProfile != null) return _cachedProfile!; + + /// if account data is empty, return an empty profile + if (_pangeaController.matrixState.client.accountData.isEmpty) { + return Profile.emptyProfile; } - final PUserModel newUserModel = await PUserRepo.repoCreatePangeaUser( - userID: userId!, - fullName: fullname, - dob: dob, - matrixAccessToken: _matrixAccessToken!, - ); - newUserModel.save(_pangeaController); - await matrixProfile.saveProfileData( - {MatrixProfileEnum.dateOfBirth.title: dob}, - waitForDataInSync: true, + + /// try to get the account data in the up-to-date format + final Profile? fromAccountData = Profile.fromAccountData(); + if (fromAccountData != null) { + _cachedProfile = fromAccountData; + return fromAccountData; + } + + _cachedProfile = Profile.migrateFromAccountData(); + _cachedProfile?.saveProfileData(); + return _cachedProfile ?? Profile.emptyProfile; + } + + void updateProfile(Profile Function(Profile) update) { + final Profile updatedProfile = update(profile); + updatedProfile.saveProfileData(); + } + + Future createProfile({required DateTime dob}) async { + final userSettings = UserSettings( + dateOfBirth: dob, + createdAt: DateTime.now(), ); + final newProfile = Profile(userSettings: userSettings); + await newProfile.saveProfileData(waitForDataInSync: true); } /// A completer for the profile model of a user. - Completer? _profileCompleter; + Completer? _profileCompleter; - /// 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 fetchUserModel() async { + Future initialize() async { if (_profileCompleter?.isCompleted ?? false) { return _profileCompleter!.future; } @@ -91,213 +91,79 @@ class UserController extends BaseController { return _profileCompleter!.future; } - _profileCompleter = Completer(); - PUserModel? fetchedUserModel; + _profileCompleter = Completer(); try { - fetchedUserModel = await _fetchUserModel(); + await _initialize(); + addProfileListener(); } catch (err, s) { ErrorHandler.logError(e: err, s: s); } finally { - _profileCompleter!.complete(fetchedUserModel); + _profileCompleter!.complete(); } return _profileCompleter!.future; } - /// 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 _fetchUserModel() async { - if (_matrixAccessToken == null || userId == null) { - ErrorHandler.logError( - e: "calling fetchUserModel with userId == null or matrixAccessToken == null", - ); - return null; + Future _initialize() async { + await waitForAccountData(); + if (profile.userSettings.dateOfBirth != null) { + return; } - final PUserModel? newUserModel = await PUserRepo.fetchPangeaUserInfo( + final PangeaProfileResponse? resp = await PUserRepo.fetchPangeaUserInfo( userID: userId!, matrixAccessToken: _matrixAccessToken!, ); - newUserModel?.save(_pangeaController); - await migrateMatrixProfile(); - return newUserModel; + if (resp?.profile == null) { + return; + } + final userSetting = UserSettings.fromJson(resp!.profile.toJson()); + final newProfile = Profile(userSettings: userSetting); + await newProfile.saveProfileData(waitForDataInSync: true); } /// Reinitializes the user's profile /// This method should be called whenever the user's login status changes Future reinitialize() async { _profileCompleter = null; - await fetchUserModel(); + _cachedProfile = null; + await initialize(); } - /// 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 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. + /// Account data comes through in the first sync, so wait for that + Future waitForAccountData() async { final client = _pangeaController.matrixState.client; if (client.prevBatch == null) { await client.onSync.stream.first; } - - final Map profileUpdates = {}; - final Profile? pangeaProfile = userModel?.profile; - - 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; - } - } - - 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; - } - } - - 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. - Future updateUserProfile({ - String? dateOfBirth, - String? targetLanguage, - String? sourceLanguage, - String? country, - List? interests, - List? speaks, - bool? publicProfile, - }) async { - if (userModel == null) { - ErrorHandler.logError( - e: "calling updateUserProfile with userModel == null", - ); - return; - } - - 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); - - 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; + bool needNewJWT(String token) => Jwt.isExpired(token); - /// 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 thrown. - /// - /// Returns the access token as a string, or null if the user model is null. + /// Retrieves the access token for the user. Looks for it locally, + /// and if it's not found or expired, fetches it from the server. Future get accessToken async { - final PUserModel? useThisOne = - needNewJWT ? await fetchUserModel() : userModel; + final localAccessToken = + _pangeaController.pStoreService.read(PLocalKey.access); - if (useThisOne == null) { - throw ("Trying to get accessToken with null userModel"); + if (localAccessToken == null || needNewJWT(localAccessToken)) { + final PangeaProfileResponse? userModel = + await PUserRepo.fetchPangeaUserInfo( + userID: userId!, + matrixAccessToken: _matrixAccessToken!, + ); + if (userModel?.access == null) { + throw ("Trying to get accessToken with null userModel"); + } + _pangeaController.pStoreService.save( + PLocalKey.access, + userModel!.access, + ); + return userModel.access; } - return useThisOne.access; + + return localAccessToken; } /// Returns the full name of the user. @@ -314,19 +180,6 @@ class UserController extends BaseController { 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 get isPUserDataAvailable async { - try { - final PUserModel? toCheck = userModel ?? (await fetchUserModel()); - 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. @@ -334,8 +187,8 @@ class UserController extends BaseController { try { // the function fetchUserModel() uses a completer, so it shouldn't // re-call the endpoint if it has already been called - await fetchUserModel(); - return matrixProfile.dateOfBirth != null; + await initialize(); + return profile.userSettings.dateOfBirth != null; } catch (err, s) { ErrorHandler.logError(e: err, s: s); return false; @@ -344,11 +197,11 @@ class UserController extends BaseController { /// Returns a boolean value indicating whether the user is currently in the trial window. bool get inTrialWindow { - final String? createdAt = userModel?.profile?.createdAt; + final DateTime? createdAt = profile.userSettings.createdAt; if (createdAt == null) { return false; } - return DateTime.parse(createdAt).isAfter( + return createdAt.isAfter( DateTime.now().subtract(const Duration(days: 7)), ); } @@ -363,12 +216,8 @@ class UserController extends BaseController { /// If an error occurs during the process, it logs the error and returns `false`. Future get areUserLanguagesSet async { try { - final PUserModel? toCheck = userModel ?? (await fetchUserModel()); - if (toCheck?.profile == null) { - return false; - } - final String? srcLang = toCheck!.profile!.sourceLanguage; - final String? tgtLang = toCheck.profile!.targetLanguage; + final String? srcLang = profile.userSettings.sourceLanguage; + final String? tgtLang = profile.userSettings.targetLanguage; return srcLang != null && tgtLang != null && srcLang.isNotEmpty && @@ -382,7 +231,9 @@ class UserController extends BaseController { } /// Returns a boolean value indicating whether the user's profile is public. - bool get isPublic => userModel?.profile?.publicProfile ?? false; + bool get isPublic { + return profile.userSettings.publicProfile; + } /// Retrieves the user's email address. /// diff --git a/lib/pangea/models/space_model.dart b/lib/pangea/models/space_model.dart index 81306372c..232899bb8 100644 --- a/lib/pangea/models/space_model.dart +++ b/lib/pangea/models/space_model.dart @@ -1,6 +1,5 @@ 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'; @@ -270,19 +269,4 @@ 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; - } - } } diff --git a/lib/pangea/models/user_model.dart b/lib/pangea/models/user_model.dart index d87e125fa..51aa746d9 100644 --- a/lib/pangea/models/user_model.dart +++ b/lib/pangea/models/user_model.dart @@ -1,199 +1,320 @@ -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'; -class PUserModel { - String access; - String refresh; - Profile? profile; +class UserSettings { + DateTime? dateOfBirth; + DateTime? createdAt; + bool autoPlayMessages; + bool itAutoPlay; + bool activatedFreeTrial; + bool publicProfile; + String? targetLanguage; + String? sourceLanguage; + String? country; - PUserModel({required this.access, required this.refresh, this.profile}); + UserSettings({ + this.dateOfBirth, + this.createdAt, + this.autoPlayMessages = false, + this.itAutoPlay = false, + this.activatedFreeTrial = false, + this.publicProfile = false, + this.targetLanguage, + this.sourceLanguage, + this.country, + }); - factory PUserModel.fromJson(Map json) => PUserModel( - access: json[ModelKey.userAccess], - refresh: json[ModelKey.userRefresh], - profile: json[ModelKey.userProfile] != null - ? Profile.fromJson(json[ModelKey.userProfile]) + factory UserSettings.fromJson(Map json) => UserSettings( + dateOfBirth: DateTime.parse(json[ModelKey.userDateOfBirth]), + createdAt: json[ModelKey.userCreatedAt] != null + ? DateTime.parse(json[ModelKey.userCreatedAt]) : null, + autoPlayMessages: json[ModelKey.autoPlayMessages] ?? false, + itAutoPlay: json[ModelKey.itAutoPlay] ?? false, + activatedFreeTrial: json[ModelKey.activatedTrialKey] ?? false, + publicProfile: json[ModelKey.publicProfile] ?? false, + targetLanguage: json[ModelKey.l2LanguageKey], + sourceLanguage: json[ModelKey.l1LanguageKey], + country: json[ModelKey.userCountry], ); Map toJson() { final Map data = {}; - data[ModelKey.userAccess] = access; - data[ModelKey.userRefresh] = refresh; - if (profile != null) { - data[ModelKey.userProfile] = profile!.toJson(); - } + data[ModelKey.userDateOfBirth] = dateOfBirth?.toIso8601String(); + data[ModelKey.userCreatedAt] = createdAt?.toIso8601String(); + data[ModelKey.autoPlayMessages] = autoPlayMessages; + data[ModelKey.itAutoPlay] = itAutoPlay; + data[ModelKey.activatedTrialKey] = activatedFreeTrial; + data[ModelKey.publicProfile] = publicProfile; + data[ModelKey.l2LanguageKey] = targetLanguage; + data[ModelKey.l1LanguageKey] = sourceLanguage; + data[ModelKey.userCountry] = country; return data; } - Future save(PangeaController pangeaController) async { - await pangeaController.pStoreService.save( - PLocalKey.user, - toJson(), + static UserSettings? migrateFromAccountData() { + final accountData = + MatrixState.pangeaController.matrixState.client.accountData; + + if (!accountData.containsKey(ModelKey.userDateOfBirth)) return null; + final dobContent = accountData[ModelKey.userDateOfBirth]! + .content[ModelKey.userDateOfBirth]; + + String? dobString; + if (dobContent != null) { + dobString = dobContent as String; + } + + DateTime dob; + try { + dob = DateTime.parse(dobString!); + } catch (_) { + return null; + } + + final createdAtContent = + accountData[ModelKey.userCreatedAt]?.content[ModelKey.userCreatedAt]; + DateTime? createdAt; + if (createdAtContent != null) { + try { + createdAt = DateTime.parse(createdAtContent as String); + } catch (_) { + createdAt = null; + } + } + + return UserSettings( + dateOfBirth: dob, + createdAt: createdAt, + autoPlayMessages: (accountData[ModelKey.autoPlayMessages] + ?.content[ModelKey.autoPlayMessages] as bool?) ?? + false, + itAutoPlay: (accountData[ModelKey.itAutoPlay] + ?.content[ModelKey.itAutoPlay] as bool?) ?? + false, + activatedFreeTrial: (accountData[ModelKey.activatedTrialKey] + ?.content[ModelKey.activatedTrialKey] as bool?) ?? + false, + publicProfile: (accountData[ModelKey.publicProfile] + ?.content[ModelKey.publicProfile] as bool?) ?? + false, + targetLanguage: accountData[ModelKey.l2LanguageKey] + ?.content[ModelKey.l2LanguageKey] as String?, + sourceLanguage: accountData[ModelKey.l1LanguageKey] + ?.content[ModelKey.l1LanguageKey] as String?, + country: accountData[ModelKey.userCountry]?.content[ModelKey.userCountry] + as String?, ); } } -/// A list of all the fields in the user profile saved to matrix -enum MatrixProfileEnum { - dateOfBirth, - autoPlayMessages, - itAutoPlay, - activatedFreeTrial, - interactiveTranslator, - interactiveGrammar, - immersionMode, - definitions, - showedItInstructions, - showedClickMessage, - showedBlurMeansTranslate, - showedTooltipInstructions, - createdAt, - targetLanguage, - sourceLanguage, - country, - publicProfile, - autoIGC, -} +class UserToolSettings { + bool interactiveTranslator; + bool interactiveGrammar; + bool immersionMode; + bool definitions; + bool autoIGC; -extension MatrixProfileEnumExtension on MatrixProfileEnum { - String get title { - switch (this) { - case MatrixProfileEnum.dateOfBirth: - return ModelKey.userDateOfBirth; - 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 MatrixProfileEnum.interactiveGrammar: - return ToolSetting.interactiveGrammar.toString(); - case MatrixProfileEnum.immersionMode: - return ToolSetting.immersionMode.toString(); - case MatrixProfileEnum.definitions: - return ToolSetting.definitions.toString(); - case MatrixProfileEnum.autoIGC: - return ToolSetting.autoIGC.toString(); - case MatrixProfileEnum.showedItInstructions: - return InstructionsEnum.itInstructions.toString(); - case MatrixProfileEnum.showedClickMessage: - return InstructionsEnum.clickMessage.toString(); - case MatrixProfileEnum.showedBlurMeansTranslate: - return InstructionsEnum.blurMeansTranslate.toString(); - case MatrixProfileEnum.showedTooltipInstructions: - return InstructionsEnum.tooltipInstructions.toString(); - case MatrixProfileEnum.createdAt: - return ModelKey.userCreatedAt; - case MatrixProfileEnum.targetLanguage: - return ModelKey.l2LanguageKey; - case MatrixProfileEnum.sourceLanguage: - return ModelKey.l1LanguageKey; - case MatrixProfileEnum.country: - return ModelKey.userCountry; - case MatrixProfileEnum.publicProfile: - return ModelKey.publicProfile; - } + UserToolSettings({ + this.interactiveTranslator = true, + this.interactiveGrammar = true, + this.immersionMode = false, + this.definitions = true, + this.autoIGC = false, + }); + + factory UserToolSettings.fromJson(Map json) => + UserToolSettings( + interactiveTranslator: + json[ToolSetting.interactiveTranslator.toString()] ?? true, + interactiveGrammar: + json[ToolSetting.interactiveGrammar.toString()] ?? true, + immersionMode: json[ToolSetting.immersionMode.toString()] ?? false, + definitions: json[ToolSetting.definitions.toString()] ?? true, + autoIGC: json[ToolSetting.autoIGC.toString()] ?? false, + ); + + Map toJson() { + final Map data = {}; + data[ToolSetting.interactiveTranslator.toString()] = interactiveTranslator; + data[ToolSetting.interactiveGrammar.toString()] = interactiveGrammar; + data[ToolSetting.immersionMode.toString()] = immersionMode; + data[ToolSetting.definitions.toString()] = definitions; + data[ToolSetting.autoIGC.toString()] = autoIGC; + return data; } - 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; - } + factory UserToolSettings.migrateFromAccountData() { + final accountData = + MatrixState.pangeaController.matrixState.client.accountData; + return UserToolSettings( + interactiveTranslator: + (accountData[ToolSetting.interactiveTranslator.toString()] + ?.content[ToolSetting.interactiveTranslator.toString()] + as bool?) ?? + true, + interactiveGrammar: + (accountData[ToolSetting.interactiveGrammar.toString()] + ?.content[ToolSetting.interactiveGrammar.toString()] + as bool?) ?? + true, + immersionMode: (accountData[ToolSetting.immersionMode.toString()] + ?.content[ToolSetting.immersionMode.toString()] as bool?) ?? + false, + definitions: (accountData[ToolSetting.definitions.toString()] + ?.content[ToolSetting.definitions.toString()] as bool?) ?? + true, + autoIGC: (accountData[ToolSetting.autoIGC.toString()] + ?.content[ToolSetting.autoIGC.toString()] as bool?) ?? + false, + ); + } +} + +class UserInstructions { + bool showedItInstructions; + bool showedClickMessage; + bool showedBlurMeansTranslate; + bool showedTooltipInstructions; + + UserInstructions({ + this.showedItInstructions = false, + this.showedClickMessage = false, + this.showedBlurMeansTranslate = false, + this.showedTooltipInstructions = false, + }); + + factory UserInstructions.fromJson(Map json) => + UserInstructions( + showedItInstructions: + json[InstructionsEnum.itInstructions.toString()] ?? false, + showedClickMessage: + json[InstructionsEnum.clickMessage.toString()] ?? false, + showedBlurMeansTranslate: + json[InstructionsEnum.blurMeansTranslate.toString()] ?? false, + showedTooltipInstructions: + json[InstructionsEnum.tooltipInstructions.toString()] ?? false, + ); + + Map toJson() { + final Map data = {}; + data[InstructionsEnum.itInstructions.toString()] = showedItInstructions; + data[InstructionsEnum.clickMessage.toString()] = showedClickMessage; + data[InstructionsEnum.blurMeansTranslate.toString()] = + showedBlurMeansTranslate; + data[InstructionsEnum.tooltipInstructions.toString()] = + showedTooltipInstructions; + return data; + } + + factory UserInstructions.migrateFromAccountData() { + final accountData = + MatrixState.pangeaController.matrixState.client.accountData; + return UserInstructions( + showedItInstructions: + (accountData[InstructionsEnum.itInstructions.toString()] + ?.content[InstructionsEnum.itInstructions.toString()] + as bool?) ?? + false, + showedClickMessage: (accountData[InstructionsEnum.clickMessage.toString()] + ?.content[InstructionsEnum.clickMessage.toString()] as bool?) ?? + false, + showedBlurMeansTranslate: + (accountData[InstructionsEnum.blurMeansTranslate.toString()] + ?.content[InstructionsEnum.blurMeansTranslate.toString()] + as bool?) ?? + false, + showedTooltipInstructions: + (accountData[InstructionsEnum.tooltipInstructions.toString()] + ?.content[InstructionsEnum.tooltipInstructions.toString()] + as bool?) ?? + false, + ); } } /// 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 { - /// Convenience function get get user's account data from the client - Map get accountData => - MatrixState.pangeaController.matrixState.client.accountData; +class Profile { + late UserSettings userSettings; + late UserToolSettings toolSettings; + late UserInstructions instructionSettings; - /// Returns the profile of the user. - /// - /// The profile is retrieved from the user's account data - /// using the key `ModelKey.userProfile`. It returns a `Map` object - /// representing the user's profile information. - Map? get profile => - accountData[ModelKey.userProfile]?.content; - - /// Retrieves the profile data for the given [key]. - /// - /// This method first tries to get the data from the new profile format. If the data is found, - /// it is returned. If not, it checks if the data is stored in the old format. If it is, the data - /// is saved to the new format and returned. - dynamic getProfileData(MatrixProfileEnum key) { - // try to get the data from the new profile format - if (profile?[key.title] != null) { - return profile?[key.title]; - } - - // check if the data is stored in the old format - // and if so, save it to the new format - final prevFormatData = accountData[key.title]?.content[key.title]; - if (prevFormatData != null) { - saveProfileData({key.title: prevFormatData}); - return prevFormatData; - } + Profile({ + required this.userSettings, + UserToolSettings? toolSettings, + UserInstructions? instructionSettings, + }) { + this.toolSettings = toolSettings ?? UserToolSettings(); + this.instructionSettings = instructionSettings ?? UserInstructions(); } - /// 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. - Future saveProfileData( - Map updates, { + static Profile? fromAccountData() { + final profileData = MatrixState.pangeaController.matrixState.client + .accountData[ModelKey.userProfile]?.content; + if (profileData == null) return null; + + final userSettingsContent = profileData[ModelKey.userSettings]; + if (userSettingsContent == null) return null; + + final toolSettingsContent = profileData[ModelKey.toolSettings]; + final instructionSettingsContent = + profileData[ModelKey.instructionsSettings]; + + return Profile( + userSettings: + UserSettings.fromJson(userSettingsContent as Map), + toolSettings: toolSettingsContent != null + ? UserToolSettings.fromJson( + toolSettingsContent as Map, + ) + : UserToolSettings(), + instructionSettings: instructionSettingsContent != null + ? UserInstructions.fromJson( + instructionSettingsContent as Map, + ) + : UserInstructions(), + ); + } + + Map toJson() { + final Map json = { + ModelKey.userSettings: userSettings.toJson(), + ModelKey.toolSettings: toolSettings.toJson(), + ModelKey.instructionsSettings: instructionSettings.toJson(), + }; + return json; + } + + static Profile? migrateFromAccountData() { + final userSettings = UserSettings.migrateFromAccountData(); + if (userSettings == null) return null; + + final toolSettings = UserToolSettings.migrateFromAccountData(); + final instructionSettings = UserInstructions.migrateFromAccountData(); + return Profile( + userSettings: userSettings, + toolSettings: toolSettings, + instructionSettings: instructionSettings, + ); + } + + Future saveProfileData({ waitForDataInSync = false, }) async { - final currentProfile = toJson(); - for (final entry in updates.entries) { - if (entry.value == null) continue; - currentProfile[entry.key] = entry.value; - } - if (mapEquals(toJson(), currentProfile)) return; - final PangeaController pangeaController = MatrixState.pangeaController; final Client client = pangeaController.matrixState.client; - - final List profileKeys = - MatrixProfileEnum.values.map((e) => e.title).toList(); + final List profileKeys = [ + ModelKey.userSettings, + ModelKey.toolSettings, + ModelKey.instructionsSettings, + ]; Future? waitForUpdate; if (waitForDataInSync) { @@ -208,68 +329,26 @@ class MatrixProfile { await client.setAccountData( client.userID!, ModelKey.userProfile, - currentProfile, + toJson(), ); - if (waitForDataInSync) await waitForUpdate; - } - /// Converts the Matrix Profile to a JSON representation. - Map toJson() { - final Map json = {}; - for (final value in MatrixProfileEnum.values) { - json[value.title] = profile?[value.title]; + if (waitForDataInSync) { + await waitForUpdate; } - 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. - - DateTime? get dateOfBirth { - final dob = getProfileData(MatrixProfileEnum.dateOfBirth); - return dob != null ? DateTime.parse(dob) : null; + static Profile get emptyProfile { + return Profile( + userSettings: UserSettings(), + toolSettings: UserToolSettings(), + instructionSettings: UserInstructions(), + ); } - - bool get autoPlayMessages => - getProfileData(MatrixProfileEnum.autoPlayMessages) ?? false; - bool get itAutoPlay => getProfileData(MatrixProfileEnum.itAutoPlay) ?? false; - bool get activatedFreeTrial => - getProfileData(MatrixProfileEnum.activatedFreeTrial) ?? false; - bool get interactiveTranslator => - getProfileData(MatrixProfileEnum.interactiveTranslator) ?? true; - bool get interactiveGrammar => - getProfileData(MatrixProfileEnum.interactiveGrammar) ?? true; - bool get immersionMode => - getProfileData(MatrixProfileEnum.immersionMode) ?? false; - bool get definitions => getProfileData(MatrixProfileEnum.definitions) ?? true; - bool get autoIGC => getProfileData(MatrixProfileEnum.autoIGC) ?? false; - - /// A list of all the fields in MatrixProfileEnum that correspond to tool settings - static List 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 pangeaProfileFields = [ - MatrixProfileEnum.dateOfBirth, - MatrixProfileEnum.createdAt, - MatrixProfileEnum.targetLanguage, - MatrixProfileEnum.sourceLanguage, - MatrixProfileEnum.country, - MatrixProfileEnum.publicProfile, - ]; } -class Profile { - // i'm considering removing this field because it's duplicating info in the - // matrix database - // String? fullName; +/// Model of data from pangea chat server. Not used anymore, in favor of matrix account data. +/// This class if used to read in data from the server to be migrated to matrix account data. +class PangeaProfile { final String createdAt; final String pangeaUserId; String? dateOfBirth; @@ -279,8 +358,7 @@ class Profile { String? country; bool publicProfile; - Profile({ - // this.fullName, + PangeaProfile({ required this.createdAt, required this.pangeaUserId, this.dateOfBirth, @@ -290,16 +368,15 @@ class Profile { this.publicProfile = false, }); - factory Profile.fromJson(Map json) { + factory PangeaProfile.fromJson(Map json) { final l2 = LanguageModel.codeFromNameOrCode( - json[ModelKey.l2LanguageKey] ?? LanguageKeys.unknownLanguage, + json[ModelKey.l2LanguageKey], ); final l1 = LanguageModel.codeFromNameOrCode( - json[ModelKey.l1LanguageKey] ?? LanguageKeys.unknownLanguage, + json[ModelKey.l1LanguageKey], ); - return Profile( - // fullName: json[ModelKey.userFullName], + return PangeaProfile( createdAt: json[ModelKey.userCreatedAt], pangeaUserId: json[ModelKey.userPangeaUserId], dateOfBirth: json[ModelKey.userDateOfBirth], @@ -312,7 +389,6 @@ class Profile { Map toJson() { final Map data = {}; - // data[ModelKey.userFullName] = fullName; data[ModelKey.userCreatedAt] = createdAt; data[ModelKey.userPangeaUserId] = pangeaUserId; data[ModelKey.userDateOfBirth] = dateOfBirth; @@ -322,514 +398,21 @@ class Profile { data[ModelKey.userCountry] = country; return data; } +} - /// used in find a partner page for display partner's country - String get flagEmoji { - final String? countryName = this.country?.split(' (')[0]; - final Country? country = CountryService().findByName(countryName); - return country?.flagEmoji ?? ""; - } +class PangeaProfileResponse { + final PangeaProfile profile; + final String access; - String? countryDisplayName(BuildContext context) { - final String? countryName = this.country?.split(' (')[0]; - final Country? country = CountryService().findByName(countryName); - if (country?.countryCode == null) return null; - switch (country!.countryCode) { - case 'WW': - return L10n.of(context)!.wwCountryDisplayName; - case 'AF': - return L10n.of(context)!.afCountryDisplayName; - case 'AX': - return L10n.of(context)!.axCountryDisplayName; - case 'AL': - return L10n.of(context)!.alCountryDisplayName; - case 'DZ': - return L10n.of(context)!.dzCountryDisplayName; - case 'AS': - return L10n.of(context)!.asCountryDisplayName; - case 'AD': - return L10n.of(context)!.adCountryDisplayName; - case 'AO': - return L10n.of(context)!.aoCountryDisplayName; - case 'AI': - return L10n.of(context)!.aiCountryDisplayName; - case 'AG': - return L10n.of(context)!.agCountryDisplayName; - case 'AR': - return L10n.of(context)!.arCountryDisplayName; - case 'AM': - return L10n.of(context)!.amCountryDisplayName; - case 'AW': - return L10n.of(context)!.awCountryDisplayName; - case 'AC': - return L10n.of(context)!.acCountryDisplayName; - case 'AU': - return L10n.of(context)!.auCountryDisplayName; - case 'AT': - return L10n.of(context)!.atCountryDisplayName; - case 'AZ': - return L10n.of(context)!.azCountryDisplayName; - case 'BS': - return L10n.of(context)!.bsCountryDisplayName; - case 'BH': - return L10n.of(context)!.bhCountryDisplayName; - case 'BD': - return L10n.of(context)!.bdCountryDisplayName; - case 'BB': - return L10n.of(context)!.bbCountryDisplayName; - case 'BY': - return L10n.of(context)!.byCountryDisplayName; - case 'BE': - return L10n.of(context)!.beCountryDisplayName; - case 'BZ': - return L10n.of(context)!.bzCountryDisplayName; - case 'BJ': - return L10n.of(context)!.bjCountryDisplayName; - case 'BM': - return L10n.of(context)!.bmCountryDisplayName; - case 'BT': - return L10n.of(context)!.btCountryDisplayName; - case 'BO': - return L10n.of(context)!.boCountryDisplayName; - case 'BA': - return L10n.of(context)!.baCountryDisplayName; - case 'BW': - return L10n.of(context)!.bwCountryDisplayName; - case 'BR': - return L10n.of(context)!.brCountryDisplayName; - case 'IO': - return L10n.of(context)!.ioCountryDisplayName; - case 'VG': - return L10n.of(context)!.vgCountryDisplayName; - case 'BN': - return L10n.of(context)!.bnCountryDisplayName; - case 'BG': - return L10n.of(context)!.bgCountryDisplayName; - case 'BF': - return L10n.of(context)!.bfCountryDisplayName; - case 'BI': - return L10n.of(context)!.biCountryDisplayName; - case 'KH': - return L10n.of(context)!.khCountryDisplayName; - case 'CM': - return L10n.of(context)!.cmCountryDisplayName; - case 'CA': - return L10n.of(context)!.caCountryDisplayName; - case 'CV': - return L10n.of(context)!.cvCountryDisplayName; - case 'BQ': - return L10n.of(context)!.bqCountryDisplayName; - case 'KY': - return L10n.of(context)!.kyCountryDisplayName; - case 'CF': - return L10n.of(context)!.cfCountryDisplayName; - case 'TD': - return L10n.of(context)!.tdCountryDisplayName; - case 'CL': - return L10n.of(context)!.clCountryDisplayName; - case 'CN': - return L10n.of(context)!.cnCountryDisplayName; - case 'CX': - return L10n.of(context)!.cxCountryDisplayName; - case 'CC': - return L10n.of(context)!.ccCountryDisplayName; - case 'CO': - return L10n.of(context)!.coCountryDisplayName; - case 'KM': - return L10n.of(context)!.kmCountryDisplayName; - case 'CD': - return L10n.of(context)!.cdCountryDisplayName; - case 'CG': - return L10n.of(context)!.cgCountryDisplayName; - case 'CK': - return L10n.of(context)!.ckCountryDisplayName; - case 'CR': - return L10n.of(context)!.crCountryDisplayName; - case 'CI': - return L10n.of(context)!.ciCountryDisplayName; - case 'HR': - return L10n.of(context)!.hrCountryDisplayName; - case 'CU': - return L10n.of(context)!.cuCountryDisplayName; - case 'CW': - return L10n.of(context)!.cwCountryDisplayName; - case 'CY': - return L10n.of(context)!.cyCountryDisplayName; - case 'CZ': - return L10n.of(context)!.czCountryDisplayName; - case 'DK': - return L10n.of(context)!.dkCountryDisplayName; - case 'DJ': - return L10n.of(context)!.djCountryDisplayName; - case 'DM': - return L10n.of(context)!.dmCountryDisplayName; - case 'DO': - return L10n.of(context)!.doCountryDisplayName; - case 'TL': - return L10n.of(context)!.tlCountryDisplayName; - case 'EC': - return L10n.of(context)!.ecCountryDisplayName; - case 'EG': - return L10n.of(context)!.egCountryDisplayName; - case 'SV': - return L10n.of(context)!.svCountryDisplayName; - case 'GQ': - return L10n.of(context)!.gqCountryDisplayName; - case 'ER': - return L10n.of(context)!.erCountryDisplayName; - case 'EE': - return L10n.of(context)!.eeCountryDisplayName; - case 'SZ': - return L10n.of(context)!.szCountryDisplayName; - case 'ET': - return L10n.of(context)!.etCountryDisplayName; - case 'FK': - return L10n.of(context)!.fkCountryDisplayName; - case 'FO': - return L10n.of(context)!.foCountryDisplayName; - case 'FJ': - return L10n.of(context)!.fjCountryDisplayName; - case 'FI': - return L10n.of(context)!.fiCountryDisplayName; - case 'FR': - return L10n.of(context)!.frCountryDisplayName; - case 'GF': - return L10n.of(context)!.gfCountryDisplayName; - case 'PF': - return L10n.of(context)!.pfCountryDisplayName; - case 'GA': - return L10n.of(context)!.gaCountryDisplayName; - case 'GM': - return L10n.of(context)!.gmCountryDisplayName; - case 'GE': - return L10n.of(context)!.geCountryDisplayName; - case 'DE': - return L10n.of(context)!.deCountryDisplayName; - case 'GH': - return L10n.of(context)!.ghCountryDisplayName; - case 'GI': - return L10n.of(context)!.giCountryDisplayName; - case 'GR': - return L10n.of(context)!.grCountryDisplayName; - case 'GL': - return L10n.of(context)!.glCountryDisplayName; - case 'GD': - return L10n.of(context)!.gdCountryDisplayName; - case 'GP': - return L10n.of(context)!.gpCountryDisplayName; - case 'GU': - return L10n.of(context)!.guCountryDisplayName; - case 'GT': - return L10n.of(context)!.gtCountryDisplayName; - case 'GG': - return L10n.of(context)!.ggCountryDisplayName; - case 'GN': - return L10n.of(context)!.gnCountryDisplayName; - case 'GW': - return L10n.of(context)!.gwCountryDisplayName; - case 'GY': - return L10n.of(context)!.gyCountryDisplayName; - case 'HT': - return L10n.of(context)!.htCountryDisplayName; - case 'HM': - return L10n.of(context)!.hmCountryDisplayName; - case 'HN': - return L10n.of(context)!.hnCountryDisplayName; - case 'HK': - return L10n.of(context)!.hkCountryDisplayName; - case 'HU': - return L10n.of(context)!.huCountryDisplayName; - case 'IS': - return L10n.of(context)!.isCountryDisplayName; - case 'IN': - return L10n.of(context)!.inCountryDisplayName; - case 'ID': - return L10n.of(context)!.idCountryDisplayName; - case 'IR': - return L10n.of(context)!.irCountryDisplayName; - case 'IQ': - return L10n.of(context)!.iqCountryDisplayName; - case 'IE': - return L10n.of(context)!.ieCountryDisplayName; - case 'IM': - return L10n.of(context)!.imCountryDisplayName; - case 'IL': - return L10n.of(context)!.ilCountryDisplayName; - case 'IT': - return L10n.of(context)!.itCountryDisplayName; - case 'JM': - return L10n.of(context)!.jmCountryDisplayName; - case 'JP': - return L10n.of(context)!.jpCountryDisplayName; - case 'JE': - return L10n.of(context)!.jeCountryDisplayName; - case 'JO': - return L10n.of(context)!.joCountryDisplayName; - case 'KZ': - return L10n.of(context)!.kzCountryDisplayName; - case 'KE': - return L10n.of(context)!.keCountryDisplayName; - case 'KI': - return L10n.of(context)!.kiCountryDisplayName; - case 'XK': - return L10n.of(context)!.xkCountryDisplayName; - case 'KW': - return L10n.of(context)!.kwCountryDisplayName; - case 'KG': - return L10n.of(context)!.kgCountryDisplayName; - case 'LA': - return L10n.of(context)!.laCountryDisplayName; - case 'LV': - return L10n.of(context)!.lvCountryDisplayName; - case 'LB': - return L10n.of(context)!.lbCountryDisplayName; - case 'LS': - return L10n.of(context)!.lsCountryDisplayName; - case 'LR': - return L10n.of(context)!.lrCountryDisplayName; - case 'LY': - return L10n.of(context)!.lyCountryDisplayName; - case 'LI': - return L10n.of(context)!.liCountryDisplayName; - case 'LT': - return L10n.of(context)!.ltCountryDisplayName; - case 'LU': - return L10n.of(context)!.luCountryDisplayName; - case 'MO': - return L10n.of(context)!.moCountryDisplayName; - case 'MK': - return L10n.of(context)!.mkCountryDisplayName; - case 'MG': - return L10n.of(context)!.mgCountryDisplayName; - case 'MW': - return L10n.of(context)!.mwCountryDisplayName; - case 'MY': - return L10n.of(context)!.myCountryDisplayName; - case 'MV': - return L10n.of(context)!.mvCountryDisplayName; - case 'ML': - return L10n.of(context)!.mlCountryDisplayName; - case 'MT': - return L10n.of(context)!.mtCountryDisplayName; - case 'MH': - return L10n.of(context)!.mhCountryDisplayName; - case 'MQ': - return L10n.of(context)!.mqCountryDisplayName; - case 'MR': - return L10n.of(context)!.mrCountryDisplayName; - case 'MU': - return L10n.of(context)!.muCountryDisplayName; - case 'YT': - return L10n.of(context)!.ytCountryDisplayName; - case 'MX': - return L10n.of(context)!.mxCountryDisplayName; - case 'FM': - return L10n.of(context)!.fmCountryDisplayName; - case 'MD': - return L10n.of(context)!.mdCountryDisplayName; - case 'MC': - return L10n.of(context)!.mcCountryDisplayName; - case 'MN': - return L10n.of(context)!.mnCountryDisplayName; - case 'ME': - return L10n.of(context)!.meCountryDisplayName; - case 'MS': - return L10n.of(context)!.msCountryDisplayName; - case 'MA': - return L10n.of(context)!.maCountryDisplayName; - case 'MZ': - return L10n.of(context)!.mzCountryDisplayName; - case 'MM': - return L10n.of(context)!.mmCountryDisplayName; - case 'NA': - return L10n.of(context)!.naCountryDisplayName; - case 'NR': - return L10n.of(context)!.nrCountryDisplayName; - case 'NP': - return L10n.of(context)!.npCountryDisplayName; - case 'NL': - return L10n.of(context)!.nlCountryDisplayName; - case 'NC': - return L10n.of(context)!.ncCountryDisplayName; - case 'NZ': - return L10n.of(context)!.nzCountryDisplayName; - case 'NI': - return L10n.of(context)!.niCountryDisplayName; - case 'NE': - return L10n.of(context)!.neCountryDisplayName; - case 'NG': - return L10n.of(context)!.ngCountryDisplayName; - case 'NU': - return L10n.of(context)!.nuCountryDisplayName; - case 'NF': - return L10n.of(context)!.nfCountryDisplayName; - case 'KP': - return L10n.of(context)!.kpCountryDisplayName; - case 'MP': - return L10n.of(context)!.mpCountryDisplayName; - case 'NO': - return L10n.of(context)!.noCountryDisplayName; - case 'OM': - return L10n.of(context)!.omCountryDisplayName; - case 'PK': - return L10n.of(context)!.pkCountryDisplayName; - case 'PW': - return L10n.of(context)!.pwCountryDisplayName; - case 'PS': - return L10n.of(context)!.psCountryDisplayName; - case 'PA': - return L10n.of(context)!.paCountryDisplayName; - case 'PG': - return L10n.of(context)!.pgCountryDisplayName; - case 'PY': - return L10n.of(context)!.pyCountryDisplayName; - case 'PE': - return L10n.of(context)!.peCountryDisplayName; - case 'PH': - return L10n.of(context)!.phCountryDisplayName; - case 'PL': - return L10n.of(context)!.plCountryDisplayName; - case 'PT': - return L10n.of(context)!.ptCountryDisplayName; - case 'PR': - return L10n.of(context)!.prCountryDisplayName; - case 'QA': - return L10n.of(context)!.qaCountryDisplayName; - case 'RE': - return L10n.of(context)!.reCountryDisplayName; - case 'RO': - return L10n.of(context)!.roCountryDisplayName; - case 'RU': - return L10n.of(context)!.ruCountryDisplayName; - case 'RW': - return L10n.of(context)!.rwCountryDisplayName; - case 'BL': - return L10n.of(context)!.blCountryDisplayName; - case 'SH': - return L10n.of(context)!.shCountryDisplayName; - case 'KN': - return L10n.of(context)!.knCountryDisplayName; - case 'LC': - return L10n.of(context)!.lcCountryDisplayName; - case 'MF': - return L10n.of(context)!.mfCountryDisplayName; - case 'PM': - return L10n.of(context)!.pmCountryDisplayName; - case 'VC': - return L10n.of(context)!.vcCountryDisplayName; - case 'WS': - return L10n.of(context)!.wsCountryDisplayName; - case 'SM': - return L10n.of(context)!.smCountryDisplayName; - case 'ST': - return L10n.of(context)!.stCountryDisplayName; - case 'SA': - return L10n.of(context)!.saCountryDisplayName; - case 'SN': - return L10n.of(context)!.snCountryDisplayName; - case 'RS': - return L10n.of(context)!.rsCountryDisplayName; - case 'SC': - return L10n.of(context)!.scCountryDisplayName; - case 'SL': - return L10n.of(context)!.slCountryDisplayName; - case 'SG': - return L10n.of(context)!.sgCountryDisplayName; - case 'SX': - return L10n.of(context)!.sxCountryDisplayName; - case 'SK': - return L10n.of(context)!.skCountryDisplayName; - case 'SI': - return L10n.of(context)!.siCountryDisplayName; - case 'SB': - return L10n.of(context)!.sbCountryDisplayName; - case 'SO': - return L10n.of(context)!.soCountryDisplayName; - case 'ZA': - return L10n.of(context)!.zaCountryDisplayName; - case 'GS': - return L10n.of(context)!.gsCountryDisplayName; - case 'KR': - return L10n.of(context)!.krCountryDisplayName; - case 'SS': - return L10n.of(context)!.ssCountryDisplayName; - case 'ES': - return L10n.of(context)!.esCountryDisplayName; - case 'LK': - return L10n.of(context)!.lkCountryDisplayName; - case 'SD': - return L10n.of(context)!.sdCountryDisplayName; - case 'SR': - return L10n.of(context)!.srCountryDisplayName; - case 'SJ': - return L10n.of(context)!.sjCountryDisplayName; - case 'SE': - return L10n.of(context)!.seCountryDisplayName; - case 'CH': - return L10n.of(context)!.chCountryDisplayName; - case 'SY': - return L10n.of(context)!.syCountryDisplayName; - case 'TW': - return L10n.of(context)!.twCountryDisplayName; - case 'TJ': - return L10n.of(context)!.tjCountryDisplayName; - case 'TZ': - return L10n.of(context)!.tzCountryDisplayName; - case 'TH': - return L10n.of(context)!.thCountryDisplayName; - case 'TG': - return L10n.of(context)!.tgCountryDisplayName; - case 'TK': - return L10n.of(context)!.tkCountryDisplayName; - case 'TO': - return L10n.of(context)!.toCountryDisplayName; - case 'TT': - return L10n.of(context)!.ttCountryDisplayName; - case 'TN': - return L10n.of(context)!.tnCountryDisplayName; - case 'TR': - return L10n.of(context)!.trCountryDisplayName; - case 'TM': - return L10n.of(context)!.tmCountryDisplayName; - case 'TC': - return L10n.of(context)!.tcCountryDisplayName; - case 'TV': - return L10n.of(context)!.tvCountryDisplayName; - case 'VI': - return L10n.of(context)!.viCountryDisplayName; - case 'UG': - return L10n.of(context)!.ugCountryDisplayName; - case 'UA': - return L10n.of(context)!.uaCountryDisplayName; - case 'AE': - return L10n.of(context)!.aeCountryDisplayName; - case 'GB': - return L10n.of(context)!.gbCountryDisplayName; - case 'US': - return L10n.of(context)!.usCountryDisplayName; - case 'UY': - return L10n.of(context)!.uyCountryDisplayName; - case 'UZ': - return L10n.of(context)!.uzCountryDisplayName; - case 'VU': - return L10n.of(context)!.vuCountryDisplayName; - case 'VA': - return L10n.of(context)!.vaCountryDisplayName; - case 'VE': - return L10n.of(context)!.veCountryDisplayName; - case 'VN': - return L10n.of(context)!.vnCountryDisplayName; - case 'WF': - return L10n.of(context)!.wfCountryDisplayName; - case 'EH': - return L10n.of(context)!.ehCountryDisplayName; - case 'YE': - return L10n.of(context)!.yeCountryDisplayName; - case 'ZM': - return L10n.of(context)!.zmCountryDisplayName; - case 'ZW': - return L10n.of(context)!.zwCountryDisplayName; - } - return null; + PangeaProfileResponse({ + required this.profile, + required this.access, + }); + + factory PangeaProfileResponse.fromJson(Map json) { + return PangeaProfileResponse( + profile: PangeaProfile.fromJson(json), + access: json[ModelKey.userAccess], + ); } } diff --git a/lib/pangea/models/user_profile_search_model.dart b/lib/pangea/models/user_profile_search_model.dart index de8822b04..212e8e392 100644 --- a/lib/pangea/models/user_profile_search_model.dart +++ b/lib/pangea/models/user_profile_search_model.dart @@ -4,7 +4,7 @@ class UserProfileSearchResponse { int count; String? next; String? previous; - List results; + List results; UserProfileSearchResponse({ required this.count, @@ -19,9 +19,9 @@ class UserProfileSearchResponse { next: json["next"], previous: json["previous"], results: json["results"] - .map((p) => Profile.fromJson(p)) + .map((p) => PangeaProfile.fromJson(p)) .toList() - .cast(), + .cast(), ); } } diff --git a/lib/pangea/pages/find_partner/find_partner.dart b/lib/pangea/pages/find_partner/find_partner.dart index 46eb365ae..a953c7f19 100644 --- a/lib/pangea/pages/find_partner/find_partner.dart +++ b/lib/pangea/pages/find_partner/find_partner.dart @@ -37,7 +37,7 @@ class FindPartnerController extends State { Timer? coolDown; - final List _userProfilesCache = []; + final List _userProfilesCache = []; final scrollController = ScrollController(); String? error; @@ -83,7 +83,7 @@ class FindPartnerController extends State { return FindPartnerView(this); } - List get userProfiles => _userProfilesCache.where((p) { + List get userProfiles => _userProfilesCache.where((p) { return (p.targetLanguage != null && targetLanguageSearch.langCode == p.targetLanguage) && (p.sourceLanguage != null && @@ -127,8 +127,7 @@ class FindPartnerController extends State { nextUrl = response.next; nextPage++; - final String? currentUserId = - pangeaController.userController.userModel?.profile?.pangeaUserId; + final String? currentUserId = pangeaController.matrixState.client.userID; _userProfilesCache.addAll( response.results.where( (p) => diff --git a/lib/pangea/pages/find_partner/find_partner_view.dart b/lib/pangea/pages/find_partner/find_partner_view.dart index db6afa2d1..032ed7daf 100644 --- a/lib/pangea/pages/find_partner/find_partner_view.dart +++ b/lib/pangea/pages/find_partner/find_partner_view.dart @@ -2,6 +2,7 @@ import 'package:country_picker/country_picker.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pangea/models/user_model.dart'; +import 'package:fluffychat/pangea/utils/country_display.dart'; import 'package:fluffychat/pangea/widgets/common/list_placeholder.dart'; import 'package:fluffychat/pangea/widgets/common/pangea_logo_svg.dart'; import 'package:fluffychat/pangea/widgets/user_settings/p_language_dropdown.dart'; @@ -244,7 +245,7 @@ class LanguageSelectionRow extends StatelessWidget { } class UserProfileEntry extends StatelessWidget { - final Profile pangeaProfile; + final PangeaProfile pangeaProfile; final FindPartnerController controller; const UserProfileEntry({ @@ -287,7 +288,7 @@ class UserProfileEntry extends StatelessWidget { const SizedBox(width: 20), RichText( text: TextSpan( - text: pangeaProfile.flagEmoji, + text: CountryDisplayUtil.flagEmoji(pangeaProfile.country), style: const TextStyle(fontSize: 15), ), ), diff --git a/lib/pangea/pages/p_user_age/p_user_age.dart b/lib/pangea/pages/p_user_age/p_user_age.dart index adb060e38..5fe489485 100644 --- a/lib/pangea/pages/p_user_age/p_user_age.dart +++ b/lib/pangea/pages/p_user_age/p_user_age.dart @@ -10,7 +10,6 @@ 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:intl/intl.dart'; import '../../utils/bot_name.dart'; import '../../utils/error_handler.dart'; @@ -73,26 +72,24 @@ class PUserAgeController extends State { } //Note: used linear progress bar (also used in fluffychat signup button) for consistency - createUserInPangea() async { + Future createUserInPangea() async { try { - setState(() { - error = dobValidator(); - }); - + setState(() => error = dobValidator()); if (error?.isNotEmpty == true) return; + setState(() => loading = true); - setState(() { - loading = true; - }); + final DateTime? dob = + pangeaController.userController.profile.userSettings.dateOfBirth; - final String date = DateFormat('yyyy-MM-dd').format(selectedDate!); - - if (pangeaController.userController.userModel?.access == null) { - await pangeaController.userController.createProfile(dob: date); - } else { - await pangeaController.userController.updateUserProfile( - dateOfBirth: date, + if (dob == null) { + await pangeaController.userController.createProfile( + dob: selectedDate!, ); + } else { + pangeaController.userController.updateProfile((profile) { + profile.userSettings.dateOfBirth = selectedDate!; + return profile; + }); } FluffyChatApp.router.go('/rooms'); } catch (err, s) { diff --git a/lib/pangea/pages/settings_learning/settings_learning.dart b/lib/pangea/pages/settings_learning/settings_learning.dart index 4c4fe4d50..03e5f635a 100644 --- a/lib/pangea/pages/settings_learning/settings_learning.dart +++ b/lib/pangea/pages/settings_learning/settings_learning.dart @@ -2,6 +2,8 @@ import 'dart:async'; import 'package:country_picker/country_picker.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.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_view.dart'; import 'package:fluffychat/pangea/widgets/user_settings/p_language_dialog.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -18,31 +20,55 @@ class SettingsLearningController extends State { late StreamSubscription _userSubscription; PangeaController pangeaController = MatrixState.pangeaController; - setPublicProfile(bool b) async { - await pangeaController.userController.updateUserProfile(publicProfile: b); - setState(() {}); + Future changeLanguage() async { + await pLanguageDialog(context, () {}); } - @override - void initState() { - super.initState(); - - _userSubscription = - pangeaController.userController.stateStream.listen((event) { - setState(() {}); + Future setPublicProfile(bool isPublic) async { + pangeaController.userController.updateProfile((profile) { + profile.userSettings.publicProfile = isPublic; + return profile; }); } - Future changeLanguage() async { - await pLanguageDialog(context, () {}); - setState(() {}); + Future changeCountry(Country country) async { + pangeaController.userController.updateProfile((profile) { + profile.userSettings.country = country.displayNameNoCountryCode; + return profile; + }); } - Future changeCountry(Country country) async { - await pangeaController.userController.updateUserProfile( - country: country.displayNameNoCountryCode, - ); - setState(() {}); + void updateToolSetting(ToolSetting toolSetting, bool value) { + pangeaController.userController.updateProfile((Profile profile) { + switch (toolSetting) { + case ToolSetting.interactiveTranslator: + return profile..toolSettings.interactiveTranslator = value; + case ToolSetting.interactiveGrammar: + return profile..toolSettings.interactiveGrammar = value; + case ToolSetting.immersionMode: + return profile..toolSettings.immersionMode = value; + case ToolSetting.definitions: + return profile..toolSettings.definitions = value; + case ToolSetting.autoIGC: + return profile..toolSettings.autoIGC = value; + } + }); + } + + bool getToolSetting(ToolSetting toolSetting) { + final toolSettings = pangeaController.userController.profile.toolSettings; + switch (toolSetting) { + case ToolSetting.interactiveTranslator: + return toolSettings.interactiveTranslator; + case ToolSetting.interactiveGrammar: + return toolSettings.interactiveGrammar; + case ToolSetting.immersionMode: + return toolSettings.immersionMode; + case ToolSetting.definitions: + return toolSettings.definitions; + case ToolSetting.autoIGC: + return toolSettings.autoIGC; + } } @override diff --git a/lib/pangea/pages/settings_learning/settings_learning_view.dart b/lib/pangea/pages/settings_learning/settings_learning_view.dart index 06136cd7e..5422ebbe5 100644 --- a/lib/pangea/pages/settings_learning/settings_learning_view.dart +++ b/lib/pangea/pages/settings_learning/settings_learning_view.dart @@ -1,5 +1,4 @@ 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'; @@ -18,71 +17,97 @@ class SettingsLearningView extends StatelessWidget { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - centerTitle: true, - title: Text( - L10n.of(context)!.learningSettings, - ), + // rebuild this page each time a sync comes through with new account data + // this prevents having to call setState each time an individual setting is changed + return StreamBuilder( + stream: + controller.pangeaController.matrixState.client.onSync.stream.where( + (update) => update.accountData != null, ), - body: ListTileTheme( - iconColor: Theme.of(context).textTheme.bodyLarge!.color, - child: MaxWidthBody( - withScrolling: true, - child: Column( - children: [ - LanguageTile(controller), - CountryPickerTile(controller), - const SizedBox(height: 8), - const Divider(height: 1), - const SizedBox(height: 8), - if (controller.pangeaController.permissionsController.isUser18()) - SwitchListTile.adaptive( - activeColor: AppConfig.activeToggleColor, - title: Text(L10n.of(context)!.publicProfileTitle), - subtitle: Text(L10n.of(context)!.publicProfileDesc), - value: controller.pangeaController.userController.isPublic, - onChanged: (bool isPublicProfile) => showFutureLoadingDialog( - context: context, - future: () => controller.setPublicProfile(isPublicProfile), - onError: (err) => - ErrorHandler.logError(e: err, s: StackTrace.current), - ), - ), - ListTile( - subtitle: Text(L10n.of(context)!.toggleToolSettingsDescription), - ), - 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: controller - .pangeaController.userController.matrixProfile.itAutoPlay, - title: - L10n.of(context)!.interactiveTranslatorAutoPlaySliderHeader, - subtitle: L10n.of(context)!.interactiveTranslatorAutoPlayDesc, - profileKey: MatrixProfileEnum.itAutoPlay, - ), - ProfileSettingsSwitchListTile.adaptive( - defaultValue: controller.pangeaController.userController - .matrixProfile.autoPlayMessages, - title: L10n.of(context)!.autoPlayTitle, - subtitle: L10n.of(context)!.autoPlayDesc, - profileKey: MatrixProfileEnum.autoPlayMessages, - ), - ], + builder: (context, snapshot) { + return Scaffold( + appBar: AppBar( + centerTitle: true, + title: Text( + L10n.of(context)!.learningSettings, + ), ), - ), - ), + body: ListTileTheme( + iconColor: Theme.of(context).textTheme.bodyLarge!.color, + child: MaxWidthBody( + withScrolling: true, + child: Column( + children: [ + LanguageTile(controller), + CountryPickerTile(controller), + const SizedBox(height: 8), + const Divider(height: 1), + const SizedBox(height: 8), + if (controller.pangeaController.permissionsController + .isUser18()) + SwitchListTile.adaptive( + activeColor: AppConfig.activeToggleColor, + title: Text(L10n.of(context)!.publicProfileTitle), + subtitle: Text(L10n.of(context)!.publicProfileDesc), + value: + controller.pangeaController.userController.isPublic, + onChanged: (bool isPublicProfile) => + showFutureLoadingDialog( + context: context, + future: () => + controller.setPublicProfile(isPublicProfile), + onError: (err) => ErrorHandler.logError( + e: err, + s: StackTrace.current, + ), + ), + ), + ListTile( + subtitle: + Text(L10n.of(context)!.toggleToolSettingsDescription), + ), + for (final toolSetting in ToolSetting.values) + ProfileSettingsSwitchListTile.adaptive( + defaultValue: controller.getToolSetting(toolSetting), + title: toolSetting.toolName(context), + subtitle: toolSetting.toolDescription(context), + onChange: (bool value) => controller.updateToolSetting( + toolSetting, + value, + ), + ), + ProfileSettingsSwitchListTile.adaptive( + defaultValue: controller.pangeaController.userController + .profile.userSettings.itAutoPlay, + title: L10n.of(context)! + .interactiveTranslatorAutoPlaySliderHeader, + subtitle: + L10n.of(context)!.interactiveTranslatorAutoPlayDesc, + onChange: (bool value) => controller + .pangeaController.userController + .updateProfile((profile) { + profile.userSettings.itAutoPlay = value; + return profile; + }), + ), + ProfileSettingsSwitchListTile.adaptive( + defaultValue: controller.pangeaController.userController + .profile.userSettings.autoPlayMessages, + title: L10n.of(context)!.autoPlayTitle, + subtitle: L10n.of(context)!.autoPlayDesc, + onChange: (bool value) => controller + .pangeaController.userController + .updateProfile((profile) { + profile.userSettings.autoPlayMessages = value; + return profile; + }), + ), + ], + ), + ), + ), + ); + }, ); } } diff --git a/lib/pangea/repo/user_repo.dart b/lib/pangea/repo/user_repo.dart index 8f3b5a67f..47caaab0b 100644 --- a/lib/pangea/repo/user_repo.dart +++ b/lib/pangea/repo/user_repo.dart @@ -4,37 +4,13 @@ import 'dart:developer'; import 'package:fluffychat/pangea/constants/model_keys.dart'; import 'package:http/http.dart'; -import '../../widgets/matrix.dart'; import '../models/user_model.dart'; import '../models/user_profile_search_model.dart'; import '../network/requests.dart'; import '../network/urls.dart'; class PUserRepo { - static Future repoCreatePangeaUser({ - required String userID, - required String dob, - required fullName, - required String matrixAccessToken, - }) async { - final Requests req = Requests( - baseUrl: PApiUrls.baseAPI, - matrixAccessToken: matrixAccessToken, - ); - - final Map body = { - ModelKey.userFullName: fullName, - ModelKey.userPangeaUserId: userID, - ModelKey.userDateOfBirth: dob, - }; - final Response res = await req.post( - url: PApiUrls.createUser, - body: body, - ); - return PUserModel.fromJson(jsonDecode(res.body)); - } - - static Future fetchPangeaUserInfo({ + static Future fetchPangeaUserInfo({ required String userID, required String matrixAccessToken, }) async { @@ -49,7 +25,7 @@ class PUserRepo { objectId: userID, ); - return PUserModel.fromJson(jsonDecode(res.body)); + return PangeaProfileResponse.fromJson(jsonDecode(res.body)); } catch (err) { //status code should be 400 - PTODO - check ffor this. log("Most likely a first signup and needs to make an account"); @@ -57,32 +33,6 @@ class PUserRepo { } } - //notes for jordan - only replace non-null fields, return whole profile - //Jordan - should return pangeaUserId as well - static Future updateUserProfile( - Profile userProfile, - String accessToken, - ) async { - final Requests req = Requests( - baseUrl: PApiUrls.baseAPI, - accessToken: accessToken, - ); - final Response res = await req.put( - url: PApiUrls.updateUserProfile, - body: userProfile.toJson(), - ); - - //temp fix - final content = jsonDecode(res.body); - //PTODO - try taking this out and see where bug occurs - if (content[ModelKey.userPangeaUserId] == null) { - content[ModelKey.userPangeaUserId] = - MatrixState.pangeaController.matrixState.client.userID; - } - - return Profile.fromJson(content); - } - static Future searchUserProfiles({ // List? interests, String? targetLanguage, diff --git a/lib/pangea/utils/country_display.dart b/lib/pangea/utils/country_display.dart new file mode 100644 index 000000000..caba48442 --- /dev/null +++ b/lib/pangea/utils/country_display.dart @@ -0,0 +1,515 @@ +import 'package:country_picker/country_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +class CountryDisplayUtil { + /// used in find a partner page for display partner's country + static String flagEmoji(String? countryName) { + countryName = countryName?.split(' (')[0]; + final Country? country = CountryService().findByName(countryName); + return country?.flagEmoji ?? ""; + } + + static String? countryDisplayName(String? countryName, BuildContext context) { + countryName = countryName?.split(' (')[0]; + final Country? country = CountryService().findByName(countryName); + if (country?.countryCode == null) return null; + switch (country!.countryCode) { + case 'WW': + return L10n.of(context)!.wwCountryDisplayName; + case 'AF': + return L10n.of(context)!.afCountryDisplayName; + case 'AX': + return L10n.of(context)!.axCountryDisplayName; + case 'AL': + return L10n.of(context)!.alCountryDisplayName; + case 'DZ': + return L10n.of(context)!.dzCountryDisplayName; + case 'AS': + return L10n.of(context)!.asCountryDisplayName; + case 'AD': + return L10n.of(context)!.adCountryDisplayName; + case 'AO': + return L10n.of(context)!.aoCountryDisplayName; + case 'AI': + return L10n.of(context)!.aiCountryDisplayName; + case 'AG': + return L10n.of(context)!.agCountryDisplayName; + case 'AR': + return L10n.of(context)!.arCountryDisplayName; + case 'AM': + return L10n.of(context)!.amCountryDisplayName; + case 'AW': + return L10n.of(context)!.awCountryDisplayName; + case 'AC': + return L10n.of(context)!.acCountryDisplayName; + case 'AU': + return L10n.of(context)!.auCountryDisplayName; + case 'AT': + return L10n.of(context)!.atCountryDisplayName; + case 'AZ': + return L10n.of(context)!.azCountryDisplayName; + case 'BS': + return L10n.of(context)!.bsCountryDisplayName; + case 'BH': + return L10n.of(context)!.bhCountryDisplayName; + case 'BD': + return L10n.of(context)!.bdCountryDisplayName; + case 'BB': + return L10n.of(context)!.bbCountryDisplayName; + case 'BY': + return L10n.of(context)!.byCountryDisplayName; + case 'BE': + return L10n.of(context)!.beCountryDisplayName; + case 'BZ': + return L10n.of(context)!.bzCountryDisplayName; + case 'BJ': + return L10n.of(context)!.bjCountryDisplayName; + case 'BM': + return L10n.of(context)!.bmCountryDisplayName; + case 'BT': + return L10n.of(context)!.btCountryDisplayName; + case 'BO': + return L10n.of(context)!.boCountryDisplayName; + case 'BA': + return L10n.of(context)!.baCountryDisplayName; + case 'BW': + return L10n.of(context)!.bwCountryDisplayName; + case 'BR': + return L10n.of(context)!.brCountryDisplayName; + case 'IO': + return L10n.of(context)!.ioCountryDisplayName; + case 'VG': + return L10n.of(context)!.vgCountryDisplayName; + case 'BN': + return L10n.of(context)!.bnCountryDisplayName; + case 'BG': + return L10n.of(context)!.bgCountryDisplayName; + case 'BF': + return L10n.of(context)!.bfCountryDisplayName; + case 'BI': + return L10n.of(context)!.biCountryDisplayName; + case 'KH': + return L10n.of(context)!.khCountryDisplayName; + case 'CM': + return L10n.of(context)!.cmCountryDisplayName; + case 'CA': + return L10n.of(context)!.caCountryDisplayName; + case 'CV': + return L10n.of(context)!.cvCountryDisplayName; + case 'BQ': + return L10n.of(context)!.bqCountryDisplayName; + case 'KY': + return L10n.of(context)!.kyCountryDisplayName; + case 'CF': + return L10n.of(context)!.cfCountryDisplayName; + case 'TD': + return L10n.of(context)!.tdCountryDisplayName; + case 'CL': + return L10n.of(context)!.clCountryDisplayName; + case 'CN': + return L10n.of(context)!.cnCountryDisplayName; + case 'CX': + return L10n.of(context)!.cxCountryDisplayName; + case 'CC': + return L10n.of(context)!.ccCountryDisplayName; + case 'CO': + return L10n.of(context)!.coCountryDisplayName; + case 'KM': + return L10n.of(context)!.kmCountryDisplayName; + case 'CD': + return L10n.of(context)!.cdCountryDisplayName; + case 'CG': + return L10n.of(context)!.cgCountryDisplayName; + case 'CK': + return L10n.of(context)!.ckCountryDisplayName; + case 'CR': + return L10n.of(context)!.crCountryDisplayName; + case 'CI': + return L10n.of(context)!.ciCountryDisplayName; + case 'HR': + return L10n.of(context)!.hrCountryDisplayName; + case 'CU': + return L10n.of(context)!.cuCountryDisplayName; + case 'CW': + return L10n.of(context)!.cwCountryDisplayName; + case 'CY': + return L10n.of(context)!.cyCountryDisplayName; + case 'CZ': + return L10n.of(context)!.czCountryDisplayName; + case 'DK': + return L10n.of(context)!.dkCountryDisplayName; + case 'DJ': + return L10n.of(context)!.djCountryDisplayName; + case 'DM': + return L10n.of(context)!.dmCountryDisplayName; + case 'DO': + return L10n.of(context)!.doCountryDisplayName; + case 'TL': + return L10n.of(context)!.tlCountryDisplayName; + case 'EC': + return L10n.of(context)!.ecCountryDisplayName; + case 'EG': + return L10n.of(context)!.egCountryDisplayName; + case 'SV': + return L10n.of(context)!.svCountryDisplayName; + case 'GQ': + return L10n.of(context)!.gqCountryDisplayName; + case 'ER': + return L10n.of(context)!.erCountryDisplayName; + case 'EE': + return L10n.of(context)!.eeCountryDisplayName; + case 'SZ': + return L10n.of(context)!.szCountryDisplayName; + case 'ET': + return L10n.of(context)!.etCountryDisplayName; + case 'FK': + return L10n.of(context)!.fkCountryDisplayName; + case 'FO': + return L10n.of(context)!.foCountryDisplayName; + case 'FJ': + return L10n.of(context)!.fjCountryDisplayName; + case 'FI': + return L10n.of(context)!.fiCountryDisplayName; + case 'FR': + return L10n.of(context)!.frCountryDisplayName; + case 'GF': + return L10n.of(context)!.gfCountryDisplayName; + case 'PF': + return L10n.of(context)!.pfCountryDisplayName; + case 'GA': + return L10n.of(context)!.gaCountryDisplayName; + case 'GM': + return L10n.of(context)!.gmCountryDisplayName; + case 'GE': + return L10n.of(context)!.geCountryDisplayName; + case 'DE': + return L10n.of(context)!.deCountryDisplayName; + case 'GH': + return L10n.of(context)!.ghCountryDisplayName; + case 'GI': + return L10n.of(context)!.giCountryDisplayName; + case 'GR': + return L10n.of(context)!.grCountryDisplayName; + case 'GL': + return L10n.of(context)!.glCountryDisplayName; + case 'GD': + return L10n.of(context)!.gdCountryDisplayName; + case 'GP': + return L10n.of(context)!.gpCountryDisplayName; + case 'GU': + return L10n.of(context)!.guCountryDisplayName; + case 'GT': + return L10n.of(context)!.gtCountryDisplayName; + case 'GG': + return L10n.of(context)!.ggCountryDisplayName; + case 'GN': + return L10n.of(context)!.gnCountryDisplayName; + case 'GW': + return L10n.of(context)!.gwCountryDisplayName; + case 'GY': + return L10n.of(context)!.gyCountryDisplayName; + case 'HT': + return L10n.of(context)!.htCountryDisplayName; + case 'HM': + return L10n.of(context)!.hmCountryDisplayName; + case 'HN': + return L10n.of(context)!.hnCountryDisplayName; + case 'HK': + return L10n.of(context)!.hkCountryDisplayName; + case 'HU': + return L10n.of(context)!.huCountryDisplayName; + case 'IS': + return L10n.of(context)!.isCountryDisplayName; + case 'IN': + return L10n.of(context)!.inCountryDisplayName; + case 'ID': + return L10n.of(context)!.idCountryDisplayName; + case 'IR': + return L10n.of(context)!.irCountryDisplayName; + case 'IQ': + return L10n.of(context)!.iqCountryDisplayName; + case 'IE': + return L10n.of(context)!.ieCountryDisplayName; + case 'IM': + return L10n.of(context)!.imCountryDisplayName; + case 'IL': + return L10n.of(context)!.ilCountryDisplayName; + case 'IT': + return L10n.of(context)!.itCountryDisplayName; + case 'JM': + return L10n.of(context)!.jmCountryDisplayName; + case 'JP': + return L10n.of(context)!.jpCountryDisplayName; + case 'JE': + return L10n.of(context)!.jeCountryDisplayName; + case 'JO': + return L10n.of(context)!.joCountryDisplayName; + case 'KZ': + return L10n.of(context)!.kzCountryDisplayName; + case 'KE': + return L10n.of(context)!.keCountryDisplayName; + case 'KI': + return L10n.of(context)!.kiCountryDisplayName; + case 'XK': + return L10n.of(context)!.xkCountryDisplayName; + case 'KW': + return L10n.of(context)!.kwCountryDisplayName; + case 'KG': + return L10n.of(context)!.kgCountryDisplayName; + case 'LA': + return L10n.of(context)!.laCountryDisplayName; + case 'LV': + return L10n.of(context)!.lvCountryDisplayName; + case 'LB': + return L10n.of(context)!.lbCountryDisplayName; + case 'LS': + return L10n.of(context)!.lsCountryDisplayName; + case 'LR': + return L10n.of(context)!.lrCountryDisplayName; + case 'LY': + return L10n.of(context)!.lyCountryDisplayName; + case 'LI': + return L10n.of(context)!.liCountryDisplayName; + case 'LT': + return L10n.of(context)!.ltCountryDisplayName; + case 'LU': + return L10n.of(context)!.luCountryDisplayName; + case 'MO': + return L10n.of(context)!.moCountryDisplayName; + case 'MK': + return L10n.of(context)!.mkCountryDisplayName; + case 'MG': + return L10n.of(context)!.mgCountryDisplayName; + case 'MW': + return L10n.of(context)!.mwCountryDisplayName; + case 'MY': + return L10n.of(context)!.myCountryDisplayName; + case 'MV': + return L10n.of(context)!.mvCountryDisplayName; + case 'ML': + return L10n.of(context)!.mlCountryDisplayName; + case 'MT': + return L10n.of(context)!.mtCountryDisplayName; + case 'MH': + return L10n.of(context)!.mhCountryDisplayName; + case 'MQ': + return L10n.of(context)!.mqCountryDisplayName; + case 'MR': + return L10n.of(context)!.mrCountryDisplayName; + case 'MU': + return L10n.of(context)!.muCountryDisplayName; + case 'YT': + return L10n.of(context)!.ytCountryDisplayName; + case 'MX': + return L10n.of(context)!.mxCountryDisplayName; + case 'FM': + return L10n.of(context)!.fmCountryDisplayName; + case 'MD': + return L10n.of(context)!.mdCountryDisplayName; + case 'MC': + return L10n.of(context)!.mcCountryDisplayName; + case 'MN': + return L10n.of(context)!.mnCountryDisplayName; + case 'ME': + return L10n.of(context)!.meCountryDisplayName; + case 'MS': + return L10n.of(context)!.msCountryDisplayName; + case 'MA': + return L10n.of(context)!.maCountryDisplayName; + case 'MZ': + return L10n.of(context)!.mzCountryDisplayName; + case 'MM': + return L10n.of(context)!.mmCountryDisplayName; + case 'NA': + return L10n.of(context)!.naCountryDisplayName; + case 'NR': + return L10n.of(context)!.nrCountryDisplayName; + case 'NP': + return L10n.of(context)!.npCountryDisplayName; + case 'NL': + return L10n.of(context)!.nlCountryDisplayName; + case 'NC': + return L10n.of(context)!.ncCountryDisplayName; + case 'NZ': + return L10n.of(context)!.nzCountryDisplayName; + case 'NI': + return L10n.of(context)!.niCountryDisplayName; + case 'NE': + return L10n.of(context)!.neCountryDisplayName; + case 'NG': + return L10n.of(context)!.ngCountryDisplayName; + case 'NU': + return L10n.of(context)!.nuCountryDisplayName; + case 'NF': + return L10n.of(context)!.nfCountryDisplayName; + case 'KP': + return L10n.of(context)!.kpCountryDisplayName; + case 'MP': + return L10n.of(context)!.mpCountryDisplayName; + case 'NO': + return L10n.of(context)!.noCountryDisplayName; + case 'OM': + return L10n.of(context)!.omCountryDisplayName; + case 'PK': + return L10n.of(context)!.pkCountryDisplayName; + case 'PW': + return L10n.of(context)!.pwCountryDisplayName; + case 'PS': + return L10n.of(context)!.psCountryDisplayName; + case 'PA': + return L10n.of(context)!.paCountryDisplayName; + case 'PG': + return L10n.of(context)!.pgCountryDisplayName; + case 'PY': + return L10n.of(context)!.pyCountryDisplayName; + case 'PE': + return L10n.of(context)!.peCountryDisplayName; + case 'PH': + return L10n.of(context)!.phCountryDisplayName; + case 'PL': + return L10n.of(context)!.plCountryDisplayName; + case 'PT': + return L10n.of(context)!.ptCountryDisplayName; + case 'PR': + return L10n.of(context)!.prCountryDisplayName; + case 'QA': + return L10n.of(context)!.qaCountryDisplayName; + case 'RE': + return L10n.of(context)!.reCountryDisplayName; + case 'RO': + return L10n.of(context)!.roCountryDisplayName; + case 'RU': + return L10n.of(context)!.ruCountryDisplayName; + case 'RW': + return L10n.of(context)!.rwCountryDisplayName; + case 'BL': + return L10n.of(context)!.blCountryDisplayName; + case 'SH': + return L10n.of(context)!.shCountryDisplayName; + case 'KN': + return L10n.of(context)!.knCountryDisplayName; + case 'LC': + return L10n.of(context)!.lcCountryDisplayName; + case 'MF': + return L10n.of(context)!.mfCountryDisplayName; + case 'PM': + return L10n.of(context)!.pmCountryDisplayName; + case 'VC': + return L10n.of(context)!.vcCountryDisplayName; + case 'WS': + return L10n.of(context)!.wsCountryDisplayName; + case 'SM': + return L10n.of(context)!.smCountryDisplayName; + case 'ST': + return L10n.of(context)!.stCountryDisplayName; + case 'SA': + return L10n.of(context)!.saCountryDisplayName; + case 'SN': + return L10n.of(context)!.snCountryDisplayName; + case 'RS': + return L10n.of(context)!.rsCountryDisplayName; + case 'SC': + return L10n.of(context)!.scCountryDisplayName; + case 'SL': + return L10n.of(context)!.slCountryDisplayName; + case 'SG': + return L10n.of(context)!.sgCountryDisplayName; + case 'SX': + return L10n.of(context)!.sxCountryDisplayName; + case 'SK': + return L10n.of(context)!.skCountryDisplayName; + case 'SI': + return L10n.of(context)!.siCountryDisplayName; + case 'SB': + return L10n.of(context)!.sbCountryDisplayName; + case 'SO': + return L10n.of(context)!.soCountryDisplayName; + case 'ZA': + return L10n.of(context)!.zaCountryDisplayName; + case 'GS': + return L10n.of(context)!.gsCountryDisplayName; + case 'KR': + return L10n.of(context)!.krCountryDisplayName; + case 'SS': + return L10n.of(context)!.ssCountryDisplayName; + case 'ES': + return L10n.of(context)!.esCountryDisplayName; + case 'LK': + return L10n.of(context)!.lkCountryDisplayName; + case 'SD': + return L10n.of(context)!.sdCountryDisplayName; + case 'SR': + return L10n.of(context)!.srCountryDisplayName; + case 'SJ': + return L10n.of(context)!.sjCountryDisplayName; + case 'SE': + return L10n.of(context)!.seCountryDisplayName; + case 'CH': + return L10n.of(context)!.chCountryDisplayName; + case 'SY': + return L10n.of(context)!.syCountryDisplayName; + case 'TW': + return L10n.of(context)!.twCountryDisplayName; + case 'TJ': + return L10n.of(context)!.tjCountryDisplayName; + case 'TZ': + return L10n.of(context)!.tzCountryDisplayName; + case 'TH': + return L10n.of(context)!.thCountryDisplayName; + case 'TG': + return L10n.of(context)!.tgCountryDisplayName; + case 'TK': + return L10n.of(context)!.tkCountryDisplayName; + case 'TO': + return L10n.of(context)!.toCountryDisplayName; + case 'TT': + return L10n.of(context)!.ttCountryDisplayName; + case 'TN': + return L10n.of(context)!.tnCountryDisplayName; + case 'TR': + return L10n.of(context)!.trCountryDisplayName; + case 'TM': + return L10n.of(context)!.tmCountryDisplayName; + case 'TC': + return L10n.of(context)!.tcCountryDisplayName; + case 'TV': + return L10n.of(context)!.tvCountryDisplayName; + case 'VI': + return L10n.of(context)!.viCountryDisplayName; + case 'UG': + return L10n.of(context)!.ugCountryDisplayName; + case 'UA': + return L10n.of(context)!.uaCountryDisplayName; + case 'AE': + return L10n.of(context)!.aeCountryDisplayName; + case 'GB': + return L10n.of(context)!.gbCountryDisplayName; + case 'US': + return L10n.of(context)!.usCountryDisplayName; + case 'UY': + return L10n.of(context)!.uyCountryDisplayName; + case 'UZ': + return L10n.of(context)!.uzCountryDisplayName; + case 'VU': + return L10n.of(context)!.vuCountryDisplayName; + case 'VA': + return L10n.of(context)!.vaCountryDisplayName; + case 'VE': + return L10n.of(context)!.veCountryDisplayName; + case 'VN': + return L10n.of(context)!.vnCountryDisplayName; + case 'WF': + return L10n.of(context)!.wfCountryDisplayName; + case 'EH': + return L10n.of(context)!.ehCountryDisplayName; + case 'YE': + return L10n.of(context)!.yeCountryDisplayName; + case 'ZM': + return L10n.of(context)!.zmCountryDisplayName; + case 'ZW': + return L10n.of(context)!.zwCountryDisplayName; + } + return null; + } +} diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index b547efa7a..07b0ad0d5 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -333,7 +333,8 @@ class MessageToolbarState extends State { return; } - MatrixState.pangeaController.userController.matrixProfile.autoPlayMessages + MatrixState.pangeaController.userController.profile.userSettings + .autoPlayMessages ? updateMode(MessageMode.textToSpeech) : updateMode(MessageMode.translation); }); diff --git a/lib/pangea/widgets/igc/span_card.dart b/lib/pangea/widgets/igc/span_card.dart index c758ea213..26046f8d0 100644 --- a/lib/pangea/widgets/igc/span_card.dart +++ b/lib/pangea/widgets/igc/span_card.dart @@ -3,7 +3,6 @@ import 'dart:developer'; import 'package:fluffychat/config/app_config.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'; @@ -341,6 +340,12 @@ class WordMatchContent extends StatelessWidget { if (controller.widget.scm.pangeaMatch!.isITStart) DontShowSwitchListTile( controller: pangeaController, + onSwitch: (bool value) { + pangeaController.userController.updateProfile((profile) { + profile.userSettings.itAutoPlay = value; + return profile; + }); + }, ), ], ); @@ -483,10 +488,12 @@ class StartITButton extends StatelessWidget { class DontShowSwitchListTile extends StatefulWidget { final PangeaController controller; + final Function(bool) onSwitch; const DontShowSwitchListTile({ super.key, required this.controller, + required this.onSwitch, }); @override @@ -508,10 +515,7 @@ class DontShowSwitchListTileState extends State { title: Text(L10n.of(context)!.interactiveTranslatorAutoPlaySliderHeader), value: switchValue, onChanged: (value) { - MatrixState.pangeaController.userController.matrixProfile - .saveProfileData( - {MatrixProfileEnum.itAutoPlay.title: value}, - ); + widget.onSwitch(value); setState(() => switchValue = value); }, ); diff --git a/lib/pangea/widgets/user_settings/country_picker_tile.dart b/lib/pangea/widgets/user_settings/country_picker_tile.dart index 63677cc68..df292a837 100644 --- a/lib/pangea/widgets/user_settings/country_picker_tile.dart +++ b/lib/pangea/widgets/user_settings/country_picker_tile.dart @@ -3,6 +3,7 @@ import 'dart:developer'; import 'package:country_picker/country_picker.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/pages/settings_learning/settings_learning.dart'; +import 'package:fluffychat/pangea/utils/country_display.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -19,10 +20,13 @@ class CountryPickerTile extends StatelessWidget { @override Widget build(BuildContext context) { - final Profile? profile = pangeaController.userController.userModel?.profile; + final Profile profile = pangeaController.userController.profile; return ListTile( title: Text( - "${L10n.of(context)!.countryInformation}: ${profile?.countryDisplayName(context) ?? ''} ${profile?.flagEmoji}", + "${L10n.of(context)!.countryInformation}: ${CountryDisplayUtil.countryDisplayName( + profile.userSettings.country, + context, + ) ?? ''} ${CountryDisplayUtil.flagEmoji(profile.userSettings.country)}", ), trailing: const Icon(Icons.edit_outlined), onTap: () => showCountryPicker( diff --git a/lib/pangea/widgets/user_settings/p_language_dialog.dart b/lib/pangea/widgets/user_settings/p_language_dialog.dart index 8b7ae33b5..8635170a0 100644 --- a/lib/pangea/widgets/user_settings/p_language_dialog.dart +++ b/lib/pangea/widgets/user_settings/p_language_dialog.dart @@ -88,13 +88,14 @@ pLanguageDialog(BuildContext parentContext, Function callback) async { context: context, future: () async { try { - await pangeaController.userController - .updateUserProfile( - sourceLanguage: - selectedSourceLanguage.langCode, - targetLanguage: - selectedTargetLanguage.langCode, - ); + pangeaController.userController + .updateProfile((profile) { + profile.userSettings.sourceLanguage = + selectedSourceLanguage.langCode; + profile.userSettings.targetLanguage = + selectedTargetLanguage.langCode; + return profile; + }); Navigator.pop(context); } catch (err, s) { debugger(when: kDebugMode); 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 3f04c61fd..2649b28e2 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,20 +1,18 @@ import 'package:fluffychat/config/app_config.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 ProfileSettingsSwitchListTile extends StatefulWidget { final bool defaultValue; - final MatrixProfileEnum profileKey; final String title; final String? subtitle; + final Function(bool) onChange; const ProfileSettingsSwitchListTile.adaptive({ super.key, - this.defaultValue = false, - required this.profileKey, + required this.defaultValue, required this.title, + required this.onChange, this.subtitle, }); @@ -28,11 +26,7 @@ class PSettingsSwitchListTileState @override void initState() { - currentValue = MatrixState.pangeaController.userController.matrixProfile - .getProfileData( - widget.profileKey, - ) ?? - widget.defaultValue; + currentValue = widget.defaultValue; super.initState(); } @@ -45,15 +39,12 @@ class PSettingsSwitchListTileState subtitle: widget.subtitle != null ? Text(widget.subtitle!) : null, onChanged: (bool newValue) async { try { - MatrixState.pangeaController.userController.matrixProfile - .saveProfileData({ - widget.profileKey.title: newValue, - }); + widget.onChange(newValue); setState(() => currentValue = newValue); } catch (err, s) { ErrorHandler.logError( e: err, - m: "Failed to updates user setting ${widget.profileKey.title}", + m: "Failed to updates user setting", s: s, ); }