From 8acc793f0c6d9913f5512cdec38f1672aab53f19 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Fri, 12 Jul 2024 14:26:44 -0400 Subject: [PATCH] added documentation to user controller and user model --- lib/pangea/controllers/user_controller.dart | 27 ++++++++++++------- .../client_extension/client_extension.dart | 2 ++ .../general_info_extension.dart | 7 +++++ lib/pangea/models/user_model.dart | 9 +++++++ 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/lib/pangea/controllers/user_controller.dart b/lib/pangea/controllers/user_controller.dart index a8c6ced14..2ef0227e5 100644 --- a/lib/pangea/controllers/user_controller.dart +++ b/lib/pangea/controllers/user_controller.dart @@ -4,6 +4,7 @@ import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/constants/language_constants.dart'; import 'package:fluffychat/pangea/controllers/base_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; +import 'package:fluffychat/pangea/extensions/client_extension/client_extension.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:jwt_decode/jwt_decode.dart'; import 'package:matrix/matrix.dart' as matrix; @@ -26,11 +27,16 @@ class UserController extends BaseController { String? get _matrixAccessToken => _pangeaController.matrixState.client.accessToken; + /// Cached version of the user profile, so it doesn't have + /// to be read in from client's account data each time it is accessed. Profile? _cachedProfile; + /// Listens for account updates and updates the cached profile + StreamSubscription? _profileListener; + /// Listen for updates to account data in syncs and update the cached profile void addProfileListener() { - _pangeaController.matrixState.client.onSync.stream + _profileListener ??= _pangeaController.matrixState.client.onSync.stream .where((sync) => sync.accountData != null) .listen((sync) { final Profile? fromAccountData = Profile.fromAccountData(); @@ -64,11 +70,13 @@ class UserController extends BaseController { return _cachedProfile ?? Profile.emptyProfile; } + /// Updates the user's profile with the given [update] function and saves it. void updateProfile(Profile Function(Profile) update) { final Profile updatedProfile = update(profile); updatedProfile.saveProfileData(); } + /// Creates a new profile for the user with the given date of birth. Future createProfile({required DateTime dob}) async { final userSettings = UserSettings( dateOfBirth: dob, @@ -81,6 +89,9 @@ class UserController extends BaseController { /// A completer for the profile model of a user. Completer? _profileCompleter; + /// Initializes the user's profile. Runs a function to wait for account data to load, + /// read account data into profile, and migrate any missing info from the pangea profile. + /// Finally, it adds a listen to update the profile data when new account data comes in. Future initialize() async { if (_profileCompleter?.isCompleted ?? false) { return _profileCompleter!.future; @@ -105,11 +116,14 @@ class UserController extends BaseController { return _profileCompleter!.future; } + /// Initializes the user's profile by waiting for account data to load, reading in account + /// data to profile, and migrating from the pangea profile if the account data is not present. Future _initialize() async { - await waitForAccountData(); + await _pangeaController.matrixState.client.waitForAccountData(); if (profile.userSettings.dateOfBirth != null) { return; } + final PangeaProfileResponse? resp = await PUserRepo.fetchPangeaUserInfo( userID: userId!, matrixAccessToken: _matrixAccessToken!, @@ -117,6 +131,7 @@ class UserController extends BaseController { if (resp?.profile == null) { return; } + final userSetting = UserSettings.fromJson(resp!.profile.toJson()); final newProfile = Profile(userSettings: userSetting); await newProfile.saveProfileData(waitForDataInSync: true); @@ -130,14 +145,6 @@ class UserController extends BaseController { await initialize(); } - /// 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; - } - } - /// Returns a boolean value indicating whether a new JWT (JSON Web Token) is needed. bool needNewJWT(String token) => Jwt.isExpired(token); diff --git a/lib/pangea/extensions/client_extension/client_extension.dart b/lib/pangea/extensions/client_extension/client_extension.dart index addcfd0ed..bef384f6a 100644 --- a/lib/pangea/extensions/client_extension/client_extension.dart +++ b/lib/pangea/extensions/client_extension/client_extension.dart @@ -80,4 +80,6 @@ extension PangeaClient on Client { String? powerLevelName(int powerLevel, L10n l10n) => _powerLevelName(powerLevel, l10n); + + Future waitForAccountData() async => await _waitForAccountData(); } diff --git a/lib/pangea/extensions/client_extension/general_info_extension.dart b/lib/pangea/extensions/client_extension/general_info_extension.dart index aada940d7..ca5df40cc 100644 --- a/lib/pangea/extensions/client_extension/general_info_extension.dart +++ b/lib/pangea/extensions/client_extension/general_info_extension.dart @@ -78,4 +78,11 @@ extension GeneralInfoClientExtension on Client { 50: l10n.moderator, 100: l10n.admin, }[powerLevel]; + + /// Account data comes through in the first sync, so wait for that + Future _waitForAccountData() async { + if (prevBatch == null) { + await onSync.stream.first; + } + } } diff --git a/lib/pangea/models/user_model.dart b/lib/pangea/models/user_model.dart index 51aa746d9..972d7bbba 100644 --- a/lib/pangea/models/user_model.dart +++ b/lib/pangea/models/user_model.dart @@ -7,6 +7,7 @@ import 'package:matrix/matrix.dart'; import 'language_model.dart'; +/// The user's settings learning settings. class UserSettings { DateTime? dateOfBirth; DateTime? createdAt; @@ -114,6 +115,7 @@ class UserSettings { } } +/// The user's language tool settings. class UserToolSettings { bool interactiveTranslator; bool interactiveGrammar; @@ -177,6 +179,7 @@ class UserToolSettings { } } +/// The user's settings for whether or not to show instuction messages. class UserInstructions { bool showedItInstructions; bool showedClickMessage; @@ -255,6 +258,7 @@ class Profile { this.instructionSettings = instructionSettings ?? UserInstructions(); } + /// Load an instance of profile from the client's account data. static Profile? fromAccountData() { final profileData = MatrixState.pangeaController.matrixState.client .accountData[ModelKey.userProfile]?.content; @@ -292,6 +296,8 @@ class Profile { return json; } + /// Migrate data from the old matrix account data + /// format to the new matrix account data format. static Profile? migrateFromAccountData() { final userSettings = UserSettings.migrateFromAccountData(); if (userSettings == null) return null; @@ -305,6 +311,9 @@ class Profile { ); } + /// Saves the current configuration of the profile to the client's account data. + /// If [waitForDataInSync] is true, the function will wait for the updated account + /// data to come through in a sync, indicating that it has been set on the matrix server. Future saveProfileData({ waitForDataInSync = false, }) async {