From 20030ccd14aaff4ee2c04215d346e4224edad12c Mon Sep 17 00:00:00 2001 From: otsmr Date: Sun, 15 Jun 2025 21:25:54 +0200 Subject: [PATCH] use mutex when updating user --- lib/src/model/json/userdata.dart | 4 +++ lib/src/model/json/userdata.g.dart | 10 ++++++- lib/src/providers/settings.provider.dart | 7 ++--- lib/src/services/api.service.dart | 8 ++---- lib/src/services/api/media_send.dart | 14 ++++++---- .../services/backup.identitiy.service.dart | 20 +++++++++++++ lib/src/services/flame.service.dart | 6 ++-- lib/src/services/signal/identity.signal.dart | 14 ++++++---- lib/src/utils/storage.dart | 24 +++++++++++----- .../camera_preview_controller_view.dart | 7 ++--- .../image_editor/modules/all_emojis.dart | 28 +++++++++---------- .../views/camera/share_image_editor_view.dart | 7 ++--- .../views/settings/backup/backup.view.dart | 13 +++++---- .../backup/twonly_identity_backup.view.dart | 12 ++++---- .../settings/chat/chat_reactions.view.dart | 14 ++++------ .../views/settings/data_and_storage.view.dart | 18 ++++++------ lib/src/views/settings/help/help.view.dart | 8 +++--- .../settings/profile/modify_avatar.view.dart | 22 +++++++-------- .../views/settings/profile/profile.view.dart | 23 +++++++-------- .../views/settings/settings_main.view.dart | 21 +++++++------- .../subscription/additional_users.view.dart | 25 +++++++++-------- .../subscription/subscription.view.dart | 18 ++++++------ lib/src/views/tutorial/show_tutorial.dart | 7 ++++- 23 files changed, 194 insertions(+), 136 deletions(-) create mode 100644 lib/src/services/backup.identitiy.service.dart diff --git a/lib/src/model/json/userdata.dart b/lib/src/model/json/userdata.dart index 6b36c49..416e5b3 100644 --- a/lib/src/model/json/userdata.dart +++ b/lib/src/model/json/userdata.dart @@ -44,6 +44,10 @@ class UserData { DateTime? signalLastSignedPreKeyUpdated; + @JsonKey(defaultValue: false) + bool identityBackupEnabled = false; + DateTime? identityBackupLastBackupTime; + final int userId; factory UserData.fromJson(Map json) => diff --git a/lib/src/model/json/userdata.g.dart b/lib/src/model/json/userdata.g.dart index 09f27ce..61f557c 100644 --- a/lib/src/model/json/userdata.g.dart +++ b/lib/src/model/json/userdata.g.dart @@ -44,7 +44,12 @@ UserData _$UserDataFromJson(Map json) => UserData( ..signalLastSignedPreKeyUpdated = json['signalLastSignedPreKeyUpdated'] == null ? null - : DateTime.parse(json['signalLastSignedPreKeyUpdated'] as String); + : DateTime.parse(json['signalLastSignedPreKeyUpdated'] as String) + ..identityBackupEnabled = json['identityBackupEnabled'] as bool? ?? false + ..identityBackupLastBackupTime = + json['identityBackupLastBackupTime'] == null + ? null + : DateTime.parse(json['identityBackupLastBackupTime'] as String); Map _$UserDataToJson(UserData instance) => { 'username': instance.username, @@ -69,6 +74,9 @@ Map _$UserDataToJson(UserData instance) => { 'myBestFriendContactId': instance.myBestFriendContactId, 'signalLastSignedPreKeyUpdated': instance.signalLastSignedPreKeyUpdated?.toIso8601String(), + 'identityBackupEnabled': instance.identityBackupEnabled, + 'identityBackupLastBackupTime': + instance.identityBackupLastBackupTime?.toIso8601String(), 'userId': instance.userId, }; diff --git a/lib/src/providers/settings.provider.dart b/lib/src/providers/settings.provider.dart index 9181965..840f2d6 100644 --- a/lib/src/providers/settings.provider.dart +++ b/lib/src/providers/settings.provider.dart @@ -21,10 +21,9 @@ class SettingsChangeProvider with ChangeNotifier, DiagnosticableTreeMixin { notifyListeners(); - var user = await getUser(); - if (user != null) { + await updateUserdata((user) { user.themeMode = newThemeMode; - await updateUser(user); - } + return user; + }); } } diff --git a/lib/src/services/api.service.dart b/lib/src/services/api.service.dart index bcb9bbf..a64480d 100644 --- a/lib/src/services/api.service.dart +++ b/lib/src/services/api.service.dart @@ -13,7 +13,6 @@ import 'package:package_info_plus/package_info_plus.dart'; import 'package:twonly/globals.dart'; import 'package:twonly/app.dart'; import 'package:twonly/src/database/twonly_database.dart'; -import 'package:twonly/src/model/json/userdata.dart'; import 'package:twonly/src/model/protobuf/api/websocket/client_to_server.pbserver.dart'; import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart'; import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart' @@ -347,11 +346,10 @@ class ApiService { server.Response_Ok ok = result.value; if (ok.hasAuthenticated()) { server.Response_Authenticated authenticated = ok.authenticated; - UserData? user = await getUser(); - if (user != null) { + updateUserdata((user) { user.subscriptionPlan = authenticated.plan; - await updateUser(user); - } + return user; + }); } Log.info("websocket is authenticated"); onAuthenticated(); diff --git a/lib/src/services/api/media_send.dart b/lib/src/services/api/media_send.dart index 00149fa..eaf1a69 100644 --- a/lib/src/services/api/media_send.dart +++ b/lib/src/services/api/media_send.dart @@ -34,20 +34,24 @@ Future isAllowedToSend() async { return ErrorCode.PlanNotAllowed; } if (user.subscriptionPlan == "Free") { + int? todaysImageCounter = user.todaysImageCounter; if (user.lastImageSend != null && user.todaysImageCounter != null) { if (isToday(user.lastImageSend!)) { if (user.todaysImageCounter == 3) { return ErrorCode.PlanLimitReached; } - user.todaysImageCounter = user.todaysImageCounter! + 1; + todaysImageCounter = user.todaysImageCounter! + 1; } else { - user.todaysImageCounter = 1; + todaysImageCounter = 1; } } else { - user.todaysImageCounter = 1; + todaysImageCounter = 1; } - user.lastImageSend = DateTime.now(); - await updateUser(user); + await updateUserdata((user) { + user.lastImageSend = DateTime.now(); + user.todaysImageCounter = todaysImageCounter; + return user; + }); } return null; } diff --git a/lib/src/services/backup.identitiy.service.dart b/lib/src/services/backup.identitiy.service.dart new file mode 100644 index 0000000..aa10c46 --- /dev/null +++ b/lib/src/services/backup.identitiy.service.dart @@ -0,0 +1,20 @@ +import 'package:twonly/src/utils/storage.dart'; + +Future isIdentityBackupEnabled() async { + final user = await getUser(); + if (user == null) return false; + return user.identityBackupEnabled; +} + +Future getLastIdentityBackup() async { + final user = await getUser(); + if (user == null) return null; + return user.identityBackupLastBackupTime; +} + +Future enableIdentityBackup() async { + await updateUserdata((user) { + user.identityBackupEnabled = false; + return user; + }); +} diff --git a/lib/src/services/flame.service.dart b/lib/src/services/flame.service.dart index d350683..6ad7863 100644 --- a/lib/src/services/flame.service.dart +++ b/lib/src/services/flame.service.dart @@ -21,8 +21,10 @@ Future syncFlameCounters() async { contacts.firstWhere((x) => x.totalMediaCounter == maxMessageCounter); if (user.myBestFriendContactId != bestFriend.userId) { - user.myBestFriendContactId = bestFriend.userId; - await updateUser(user); + await updateUserdata((user) { + user.myBestFriendContactId = bestFriend.userId; + return user; + }); } for (Contact contact in contacts) { diff --git a/lib/src/services/signal/identity.signal.dart b/lib/src/services/signal/identity.signal.dart index 1ad5080..dde8daf 100644 --- a/lib/src/services/signal/identity.signal.dart +++ b/lib/src/services/signal/identity.signal.dart @@ -38,8 +38,10 @@ Future signalHandleNewServerConnection() async { Log.error("could not generate a new signed pre key!"); return; } - user.signalLastSignedPreKeyUpdated = DateTime.now(); - await updateUser(user); + await updateUserdata((user) { + user.signalLastSignedPreKeyUpdated = DateTime.now(); + return user; + }); Result res = await apiService.updateSignedPreKey( signedPreKey.id, signedPreKey.getKeyPair().publicKey.serialize(), @@ -47,10 +49,10 @@ Future signalHandleNewServerConnection() async { ); if (res.isError) { Log.error("could not update the signed pre key: ${res.error}"); - final UserData? user = await getUser(); - if (user == null) return; - user.signalLastSignedPreKeyUpdated = null; - await updateUser(user); + await updateUserdata((user) { + user.signalLastSignedPreKeyUpdated = null; + return user; + }); } else { Log.info("updated signed pre key"); } diff --git a/lib/src/utils/storage.dart b/lib/src/utils/storage.dart index 0993edd..6665da6 100644 --- a/lib/src/utils/storage.dart +++ b/lib/src/utils/storage.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:mutex/mutex.dart'; import 'package:path_provider/path_provider.dart'; import 'package:provider/provider.dart'; import 'package:twonly/src/model/json/userdata.dart'; @@ -33,18 +34,27 @@ Future getUser() async { Future updateUsersPlan(BuildContext context, String planId) async { context.read().plan = planId; - var user = await getUser(); - if (user != null) { + + await updateUserdata((user) { user.subscriptionPlan = planId; - await updateUser(user); - } + return user; + }); + if (!context.mounted) return; context.read().updatePlan(planId); } -Future updateUser(UserData userData) async { - final storage = FlutterSecureStorage(); - storage.write(key: "userData", value: jsonEncode(userData)); +Mutex updateProtection = Mutex(); + +Future updateUserdata(Function(UserData userData) updateUser) async { + return await updateProtection.protect(() async { + final user = await getUser(); + if (user == null) return null; + UserData updated = updateUser(user); + final storage = FlutterSecureStorage(); + storage.write(key: "userData", value: jsonEncode(updated)); + return user; + }); } Future deleteLocalUserData() async { diff --git a/lib/src/views/camera/camera_preview_controller_view.dart b/lib/src/views/camera/camera_preview_controller_view.dart index a08f6b3..fa017aa 100644 --- a/lib/src/views/camera/camera_preview_controller_view.dart +++ b/lib/src/views/camera/camera_preview_controller_view.dart @@ -558,11 +558,10 @@ class _CameraPreviewViewState extends State { onPressed: () async { useHighQuality = !useHighQuality; setState(() {}); - var user = await getUser(); - if (user != null) { + await updateUserdata((user) { user.useHighQuality = useHighQuality; - updateUser(user); - } + return user; + }); }, ), if (!hasAudioPermission) diff --git a/lib/src/views/camera/image_editor/modules/all_emojis.dart b/lib/src/views/camera/image_editor/modules/all_emojis.dart index 2b4796d..2c03402 100755 --- a/lib/src/views/camera/image_editor/modules/all_emojis.dart +++ b/lib/src/views/camera/image_editor/modules/all_emojis.dart @@ -29,21 +29,21 @@ class _EmojisState extends State { } Future selectEmojis(String emoji) async { - final user = await getUser(); - if (user == null) return; - if (user.lastUsedEditorEmojis == null) { - user.lastUsedEditorEmojis = [emoji]; - } else { - if (user.lastUsedEditorEmojis!.contains(emoji)) { - user.lastUsedEditorEmojis!.remove(emoji); + await updateUserdata((user) { + if (user.lastUsedEditorEmojis == null) { + user.lastUsedEditorEmojis = [emoji]; + } else { + if (user.lastUsedEditorEmojis!.contains(emoji)) { + user.lastUsedEditorEmojis!.remove(emoji); + } + user.lastUsedEditorEmojis!.insert(0, emoji); + if (user.lastUsedEditorEmojis!.length > 12) { + user.lastUsedEditorEmojis = user.lastUsedEditorEmojis!.sublist(0, 12); + } + user.lastUsedEditorEmojis!.toSet().toList(); } - user.lastUsedEditorEmojis!.insert(0, emoji); - if (user.lastUsedEditorEmojis!.length > 12) { - user.lastUsedEditorEmojis = user.lastUsedEditorEmojis!.sublist(0, 12); - } - user.lastUsedEditorEmojis!.toSet().toList(); - } - await updateUser(user); + return user; + }); if (!mounted) return; Navigator.pop( context, diff --git a/lib/src/views/camera/share_image_editor_view.dart b/lib/src/views/camera/share_image_editor_view.dart index 54d8e40..2a2e30f 100644 --- a/lib/src/views/camera/share_image_editor_view.dart +++ b/lib/src/views/camera/share_image_editor_view.dart @@ -233,11 +233,10 @@ class _ShareImageEditorView extends State { maxShowTime = gMediaShowInfinite; } setState(() {}); - var user = await getUser(); - if (user != null) { + await updateUserdata((user) { user.defaultShowTime = maxShowTime; - updateUser(user); - } + return user; + }); }, ), ), diff --git a/lib/src/views/settings/backup/backup.view.dart b/lib/src/views/settings/backup/backup.view.dart index 8c361aa..05a4d27 100644 --- a/lib/src/views/settings/backup/backup.view.dart +++ b/lib/src/views/settings/backup/backup.view.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:twonly/src/services/backup.identitiy.service.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/views/settings/backup/twonly_identity_backup.view.dart'; @@ -22,10 +23,10 @@ class _BackupViewState extends State { } Future initAsync() async { - setState(() { - _twonlyIdBackupEnabled = true; - _dataBackupEnabled = false; - }); + _twonlyIdBackupEnabled = await isIdentityBackupEnabled(); + _twonlyIdLastBackup = await getLastIdentityBackup(); + _dataBackupEnabled = false; + setState(() {}); } @override @@ -37,7 +38,7 @@ class _BackupViewState extends State { body: ListView( children: [ BackupOption( - title: 'twonly-Identity Backup', + title: 'twonly Safe', description: 'Back up your twonly identity, as this is the only way to restore your account if you uninstall or lose your phone.', lastBackup: _twonlyIdLastBackup, @@ -51,7 +52,7 @@ class _BackupViewState extends State { }, ), BackupOption( - title: 'Daten-Backup', + title: 'Daten-Backup (Coming Soon)', description: 'This backup contains besides of your twonly-Identity also all of your media files. This backup will also be encrypted using a password chosen by the user but stored locally on the smartphone. You then have to ensure to manually copy it onto your laptop or device of your choice.', autoBackupEnabled: _dataBackupEnabled, diff --git a/lib/src/views/settings/backup/twonly_identity_backup.view.dart b/lib/src/views/settings/backup/twonly_identity_backup.view.dart index 81de328..382aa8b 100644 --- a/lib/src/views/settings/backup/twonly_identity_backup.view.dart +++ b/lib/src/views/settings/backup/twonly_identity_backup.view.dart @@ -13,12 +13,14 @@ class _TwonlyIdentityBackupViewState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("twonly-Identity Backup"), + title: Text("twonly Safe"), + ), + body: ListView( + children: [ + Text( + 'Backup of your twonly-Identity. As twonly does not have any second factor like your phone number or email, this backup contains your twonly-Identity. If you lose your device, the only option to recover is with the twonly-ID Backup. This backup will be protected by a password chosen by you in the next step and anonymously uploaded to the twonly servers. Read more [here](https://twonly.eu/s/backup).'), + ], ), - body: ListView(children: [ - Text( - 'Backup of your twonly-Identity. As twonly does not have any second factor like your phone number or email, this backup contains your twonly-Identity. If you lose your device, the only option to recover is with the twonly-ID Backup. This backup will be protected by a password chosen by you in the next step and anonymously uploaded to the twonly servers. Read more [here](https://twonly.eu/s/backup).'), - ]), ); } } diff --git a/lib/src/views/settings/chat/chat_reactions.view.dart b/lib/src/views/settings/chat/chat_reactions.view.dart index 7385eca..b001e66 100644 --- a/lib/src/views/settings/chat/chat_reactions.view.dart +++ b/lib/src/views/settings/chat/chat_reactions.view.dart @@ -36,11 +36,10 @@ class _ChatReactionSelectionView extends State { } else { if (selectedEmojis.length < 12) { selectedEmojis.add(emoji); - var user = await getUser(); - if (user != null) { + await updateUserdata((user) { user.preSelectedEmojies = selectedEmojis; - await updateUser(user); - } + return user; + }); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -98,11 +97,10 @@ class _ChatReactionSelectionView extends State { selectedEmojis = EmojiAnimation.animatedIcons.keys.toList().sublist(0, 6); setState(() {}); - var user = await getUser(); - if (user != null) { + await updateUserdata((user) { user.preSelectedEmojies = selectedEmojis; - await updateUser(user); - } + return user; + }); }, child: Icon(Icons.settings_backup_restore_rounded), ), diff --git a/lib/src/views/settings/data_and_storage.view.dart b/lib/src/views/settings/data_and_storage.view.dart index 26b895a..919c078 100644 --- a/lib/src/views/settings/data_and_storage.view.dart +++ b/lib/src/views/settings/data_and_storage.view.dart @@ -48,10 +48,10 @@ class _DataAndStorageViewState extends State { } void toggleStoreInGallery() async { - final user = await getUser(); - if (user == null) return; - user.storeMediaFilesInGallery = !storeMediaFilesInGallery; - await updateUser(user); + await updateUserdata((u) { + u.storeMediaFilesInGallery = !storeMediaFilesInGallery; + return u; + }); initAsync(); } @@ -179,10 +179,12 @@ class _AutoDownloadOptionsDialogState extends State { } // Call the onUpdate callback to notify the parent widget - final user = await getUser(); - if (user == null) return; - user.autoDownloadOptions = autoDownloadOptions; - await updateUser(user); + + await updateUserdata((u) { + u.autoDownloadOptions = autoDownloadOptions; + return u; + }); + widget.onUpdate(); setState(() {}); } diff --git a/lib/src/views/settings/help/help.view.dart b/lib/src/views/settings/help/help.view.dart index a5356e3..e5755b4 100644 --- a/lib/src/views/settings/help/help.view.dart +++ b/lib/src/views/settings/help/help.view.dart @@ -39,10 +39,10 @@ class HelpView extends StatelessWidget { title: Text(context.lang.settingsResetTutorials), subtitle: Text(context.lang.settingsResetTutorialsDesc), onTap: () async { - final user = await getUser(); - if (user == null) return; - user.tutorialDisplayed = []; - await updateUser(user); + updateUserdata((user) { + user.tutorialDisplayed = []; + return user; + }); if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar( diff --git a/lib/src/views/settings/profile/modify_avatar.view.dart b/lib/src/views/settings/profile/modify_avatar.view.dart index c6b500f..84e2d64 100644 --- a/lib/src/views/settings/profile/modify_avatar.view.dart +++ b/lib/src/views/settings/profile/modify_avatar.view.dart @@ -2,7 +2,6 @@ import 'dart:math'; import 'package:avatar_maker/avatar_maker.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:twonly/src/model/json/userdata.dart'; import 'package:twonly/src/services/api/messages.dart'; import 'package:twonly/src/utils/misc.dart'; import "package:get/get.dart"; @@ -12,17 +11,16 @@ class ModifyAvatar extends StatelessWidget { const ModifyAvatar({super.key}); Future updateUserAvatar(String json, String svg) async { - UserData? user = await getUser(); - if (user == null) return null; - - user.avatarJson = json; - user.avatarSvg = svg; - if (user.avatarCounter == null) { - user.avatarCounter = 1; - } else { - user.avatarCounter = user.avatarCounter! + 1; - } - await updateUser(user); + await updateUserdata((user) { + user.avatarJson = json; + user.avatarSvg = svg; + if (user.avatarCounter == null) { + user.avatarCounter = 1; + } else { + user.avatarCounter = user.avatarCounter! + 1; + } + return user; + }); await notifyContactsAboutProfileChange(); } diff --git a/lib/src/views/settings/profile/profile.view.dart b/lib/src/views/settings/profile/profile.view.dart index 6046005..ba432ba 100644 --- a/lib/src/views/settings/profile/profile.view.dart +++ b/lib/src/views/settings/profile/profile.view.dart @@ -29,16 +29,17 @@ class _ProfileViewState extends State { setState(() {}); } - Future updateUserDisplayname(String displayName) async { - UserData? user = await getUser(); - if (user == null) return null; - user.displayName = displayName; - if (user.avatarCounter == null) { - user.avatarCounter = 1; - } else { - user.avatarCounter = user.avatarCounter! + 1; - } - await updateUser(user); + Future updateUserDisplayName(String displayName) async { + await updateUserdata((user) { + user.displayName = displayName; + if (user.avatarCounter == null) { + user.avatarCounter = 1; + } else { + user.avatarCounter = user.avatarCounter! + 1; + } + return user; + }); + await notifyContactsAboutProfileChange(); initAsync(); } @@ -83,7 +84,7 @@ class _ProfileViewState extends State { final displayName = await showDisplayNameChangeDialog(context, user!.displayName); if (context.mounted && displayName != null && displayName != "") { - updateUserDisplayname(displayName); + updateUserDisplayName(displayName); } }, ), diff --git a/lib/src/views/settings/settings_main.view.dart b/lib/src/views/settings/settings_main.view.dart index 2d20e3e..72e4bc8 100644 --- a/lib/src/views/settings/settings_main.view.dart +++ b/lib/src/views/settings/settings_main.view.dart @@ -7,6 +7,7 @@ import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/views/settings/account.view.dart'; import 'package:twonly/src/views/settings/appearance.view.dart'; +import 'package:twonly/src/views/settings/backup/backup.view.dart'; import 'package:twonly/src/views/settings/chat/chat_settings.view.dart'; import 'package:twonly/src/views/settings/data_and_storage.view.dart'; import 'package:twonly/src/views/settings/notification.view.dart'; @@ -120,16 +121,16 @@ class _SettingsMainViewState extends State { })); }, ), - // BetterListTile( - // icon: Icons.lock_clock_rounded, - // text: context.lang.settingsBackup, - // onTap: () { - // Navigator.push(context, - // MaterialPageRoute(builder: (context) { - // return BackupView(); - // })); - // }, - // ), + BetterListTile( + icon: Icons.lock_clock_rounded, + text: context.lang.settingsBackup, + onTap: () { + Navigator.push(context, + MaterialPageRoute(builder: (context) { + return BackupView(); + })); + }, + ), const Divider(), BetterListTile( icon: FontAwesomeIcons.sun, diff --git a/lib/src/views/settings/subscription/additional_users.view.dart b/lib/src/views/settings/subscription/additional_users.view.dart index 5eedd91..109a11c 100644 --- a/lib/src/views/settings/subscription/additional_users.view.dart +++ b/lib/src/views/settings/subscription/additional_users.view.dart @@ -14,24 +14,27 @@ import 'package:twonly/src/views/components/alert_dialog.dart'; import 'package:twonly/src/views/settings/subscription/subscription.view.dart'; Future?> loadAdditionalUserInvites() async { - List? ballance; - final user = await getUser(); - if (user == null) return ballance; - ballance = await apiService.getAdditionalUserInvites(); + final ballance = await apiService.getAdditionalUserInvites(); if (ballance != null) { - user.additionalUserInvites = - jsonEncode(ballance.map((x) => x.writeToJson()).toList()); - await updateUser(user); - } else if (user.lastPlanBallance != null) { + await updateUserdata((u) { + u.additionalUserInvites = + jsonEncode(ballance.map((x) => x.writeToJson()).toList()); + return u; + }); + return ballance; + } + final user = await getUser(); + if (user != null && user.lastPlanBallance != null) { try { List decoded = jsonDecode(user.additionalUserInvites!); - ballance = - decoded.map((x) => Response_AddAccountsInvite.fromJson(x)).toList(); + return decoded + .map((x) => Response_AddAccountsInvite.fromJson(x)) + .toList(); } catch (e) { Log.error("from json: $e"); } } - return ballance; + return null; } class AdditionalUsersView extends StatefulWidget { diff --git a/lib/src/views/settings/subscription/subscription.view.dart b/lib/src/views/settings/subscription/subscription.view.dart index 02cd6de..77708fa 100644 --- a/lib/src/views/settings/subscription/subscription.view.dart +++ b/lib/src/views/settings/subscription/subscription.view.dart @@ -36,16 +36,18 @@ String localePrizing(BuildContext context, int cents) { } Future loadPlanBalance({bool useCache = true}) async { - Response_PlanBallance? ballance; - final user = await getUser(); - if (user == null) return ballance; - ballance = await apiService.getPlanBallance(); + final ballance = await apiService.getPlanBallance(); if (ballance != null) { - user.lastPlanBallance = ballance.writeToJson(); - await updateUser(user); - } else if (user.lastPlanBallance != null && useCache) { + updateUserdata((u) { + u.lastPlanBallance = ballance.writeToJson(); + return u; + }); + return ballance; + } + final user = await getUser(); + if (user != null && user.lastPlanBallance != null && useCache) { try { - ballance = Response_PlanBallance.fromJson( + return Response_PlanBallance.fromJson( user.lastPlanBallance!, ); } catch (e) { diff --git a/lib/src/views/tutorial/show_tutorial.dart b/lib/src/views/tutorial/show_tutorial.dart index b238970..4b84ae6 100644 --- a/lib/src/views/tutorial/show_tutorial.dart +++ b/lib/src/views/tutorial/show_tutorial.dart @@ -47,7 +47,12 @@ Future checkIfTutorialAlreadyShown(String tutorialId) async { return true; } user.tutorialDisplayed!.add(tutorialId); - await updateUser(user); + + await updateUserdata((u) { + u.tutorialDisplayed = user.tutorialDisplayed; + return u; + }); + return false; }