create shared initialization function

This commit is contained in:
otsmr 2026-04-21 18:10:52 +02:00
parent 1c902bb64d
commit 1ea97d58ea
62 changed files with 222 additions and 201 deletions

View file

@ -133,9 +133,9 @@ class _AppMainWidgetState extends State<AppMainWidget> {
if (_isUserCreated) { if (_isUserCreated) {
if (_isTwonlyLocked) { if (_isTwonlyLocked) {
// do not change in case twonly was already unlocked at some point // do not change in case twonly was already unlocked at some point
_isTwonlyLocked = appSession.currentUser.screenLockEnabled; _isTwonlyLocked = userService.currentUser.screenLockEnabled;
} }
if (appSession.currentUser.appVersion < 62) { if (userService.currentUser.appVersion < 62) {
_showDatabaseMigration = true; _showDatabaseMigration = true;
} }
} }
@ -178,7 +178,7 @@ class _AppMainWidgetState extends State<AppMainWidget> {
_isTwonlyLocked = false; _isTwonlyLocked = false;
}), }),
); );
} else if (appSession.currentUser.twonlySafeBackup == null && } else if (userService.currentUser.twonlySafeBackup == null &&
!_skipBackup) { !_skipBackup) {
child = SetupBackupView( child = SetupBackupView(
callBack: () => setState(() { callBack: () => setState(() {

View file

@ -12,6 +12,6 @@ void setupLocator() {
..registerLazySingleton<TwonlyDB>(TwonlyDB.new); ..registerLazySingleton<TwonlyDB>(TwonlyDB.new);
} }
UserService get appSession => locator<UserService>(); UserService get userService => locator<UserService>();
ApiService get apiService => locator<ApiService>(); ApiService get apiService => locator<ApiService>();
TwonlyDB get twonlyDB => locator<TwonlyDB>(); TwonlyDB get twonlyDB => locator<TwonlyDB>();

View file

@ -29,7 +29,9 @@ import 'package:twonly/src/utils/avatars.dart';
import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/utils/storage.dart';
void main() async { /// This function is used to initialized the absolute minimum so it
/// can also be used by the backend without the UI was loaded.
Future<void> twonlyMinimumInitialization() async {
SentryWidgetsFlutterBinding.ensureInitialized(); SentryWidgetsFlutterBinding.ensureInitialized();
await AppEnvironment.init(); await AppEnvironment.init();
@ -46,24 +48,25 @@ void main() async {
dataDirectory: AppEnvironment.supportDir, dataDirectory: AppEnvironment.supportDir,
), ),
); );
}
void main() async {
await twonlyMinimumInitialization();
await initFCMService(); await initFCMService();
var user = await getUser(); final userExists = await userService.tryInit();
if (Platform.isIOS && user != null) { if (Platform.isIOS && userExists) {
final db = File('${AppEnvironment.supportDir}/twonly.sqlite'); final db = File('${AppEnvironment.supportDir}/twonly.sqlite');
if (!db.existsSync()) { if (!db.existsSync()) {
Log.error('[twonly] IOS: App was removed and then reinstalled again...'); Log.error('[twonly] IOS: App was removed and then reinstalled again...');
await const FlutterSecureStorage().deleteAll(); await const FlutterSecureStorage().deleteAll();
user = await getUser();
} }
} }
if (user != null) { if (userExists) {
appSession.currentUser = user; if (userService.currentUser.allowErrorTrackingViaSentry) {
if (user.allowErrorTrackingViaSentry) {
AppState.allowErrorTrackingViaSentry = true; AppState.allowErrorTrackingViaSentry = true;
await SentryFlutter.init( await SentryFlutter.init(
(options) => options (options) => options
@ -86,33 +89,20 @@ void main() async {
await settingsController.loadSettings(); await settingsController.loadSettings();
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
unawaited(setupPushNotification()); await initFileDownloader();
if (user != null) { if (userExists) {
if (appSession.currentUser.appVersion < 90) { await runMigrations();
// BUG: Requested media files for reupload where not reuploaded because the wrong state...
await twonlyDB.mediaFilesDao.updateAllRetransmissionUploadingState();
await updateUser((u) {
u.appVersion = 90;
});
}
if (appSession.currentUser.appVersion < 91) {
// BUG: Requested media files for reupload where not reuploaded because the wrong state...
await makeMigrationToVersion91();
await updateUser((u) {
u.appVersion = 91;
});
}
}
await twonlyDB.messagesDao.purgeMessageTable(); await twonlyDB.messagesDao.purgeMessageTable();
await twonlyDB.receiptsDao.purgeReceivedReceipts(); await twonlyDB.receiptsDao.purgeReceivedReceipts();
unawaited(MediaFileService.purgeTempFolder()); unawaited(MediaFileService.purgeTempFolder());
await initFileDownloader(); unawaited(setupPushNotification());
unawaited(finishStartedPreprocessing()); unawaited(finishStartedPreprocessing());
unawaited(createPushAvatars()); unawaited(createPushAvatars());
}
runApp( runApp(
MultiProvider( MultiProvider(
@ -126,3 +116,20 @@ void main() async {
), ),
); );
} }
Future<void> runMigrations() async {
if (userService.currentUser.appVersion < 90) {
// BUG: Requested media files for reupload where not reuploaded because the wrong state...
await twonlyDB.mediaFilesDao.updateAllRetransmissionUploadingState();
await updateUser((u) {
u.appVersion = 90;
});
}
if (userService.currentUser.appVersion < 91) {
// BUG: Requested media files for reupload where not reuploaded because the wrong state...
await makeMigrationToVersion91();
await updateUser((u) {
u.appVersion = 91;
});
}
}

View file

@ -140,7 +140,7 @@ class ContactsDao extends DatabaseAccessor<TwonlyDB> with _$ContactsDaoMixin {
t.userDiscoveryVersion.isNotNull() & t.userDiscoveryVersion.isNotNull() &
t.userDiscoveryExcluded.equals(false) & t.userDiscoveryExcluded.equals(false) &
t.mediaSendCounter.isBiggerOrEqualValue( t.mediaSendCounter.isBiggerOrEqualValue(
appSession.currentUser.minimumRequiredImagesExchanged, userService.currentUser.minimumRequiredImagesExchanged,
), ),
)) ))
.watch(); .watch();

View file

@ -113,7 +113,10 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
int contactId, int contactId,
GroupsCompanion group, GroupsCompanion group,
) async { ) async {
final groupIdDirectChat = getUUIDforDirectChat(contactId, appSession.currentUser.userId); final groupIdDirectChat = getUUIDforDirectChat(
contactId,
userService.currentUser.userId,
);
final insertGroup = group.copyWith( final insertGroup = group.copyWith(
groupId: Value(groupIdDirectChat), groupId: Value(groupIdDirectChat),
isDirectChat: const Value(true), isDirectChat: const Value(true),
@ -209,7 +212,10 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
} }
Stream<Group?> watchDirectChat(int contactId) { Stream<Group?> watchDirectChat(int contactId) {
final groupId = getUUIDforDirectChat(contactId, appSession.currentUser.userId); final groupId = getUUIDforDirectChat(
contactId,
userService.currentUser.userId,
);
return (select( return (select(
groups, groups,
)..where((t) => t.groupId.equals(groupId))).watchSingleOrNull(); )..where((t) => t.groupId.equals(groupId))).watchSingleOrNull();

View file

@ -127,7 +127,7 @@ class ApiService {
unawaited(UserDiscoveryService.checkForNewAnnouncedUsers()); unawaited(UserDiscoveryService.checkForNewAnnouncedUsers());
if (appSession.currentUser.userStudyParticipantsToken != null) { if (userService.currentUser.userStudyParticipantsToken != null) {
// In case the user participates in the user study, call the handler after authenticated, to be sure there is a internet connection // In case the user participates in the user study, call the handler after authenticated, to be sure there is a internet connection
unawaited(handleUserStudyUpload()); unawaited(handleUserStudyUpload());
} }

View file

@ -162,7 +162,7 @@ Future<void> handleGroupUpdate(
case GroupActionType.demoteToMember: case GroupActionType.demoteToMember:
int? affectedContactId = update.affectedContactId.toInt(); int? affectedContactId = update.affectedContactId.toInt();
if (affectedContactId == appSession.currentUser.userId) { if (affectedContactId == userService.currentUser.userId) {
affectedContactId = null; affectedContactId = null;
if (actionType == GroupActionType.removedMember) { if (actionType == GroupActionType.removedMember) {
// Oh no, I just got removed from the group... // Oh no, I just got removed from the group...

View file

@ -34,7 +34,7 @@ Future<void> handleUserDiscoveryRequest(
) async { ) async {
Log.info('Got a user discovery request'); Log.info('Got a user discovery request');
if (!appSession.currentUser.isUserDiscoveryEnabled) { if (!userService.currentUser.isUserDiscoveryEnabled) {
Log.warn('Got a user discovery request while it is disabled'); Log.warn('Got a user discovery request while it is disabled');
return; return;
} }
@ -42,10 +42,10 @@ Future<void> handleUserDiscoveryRequest(
if (contact == null) return; if (contact == null) return;
if (contact.mediaSendCounter < if (contact.mediaSendCounter <
appSession.currentUser.minimumRequiredImagesExchanged || userService.currentUser.minimumRequiredImagesExchanged ||
contact.userDiscoveryExcluded) { contact.userDiscoveryExcluded) {
Log.warn( Log.warn(
'Got a request to update user discovery, but mediaSendCounter (${contact.mediaSendCounter}) < ${appSession.currentUser.minimumRequiredImagesExchanged} or user is excluded ${contact.userDiscoveryExcluded}', 'Got a request to update user discovery, but mediaSendCounter (${contact.mediaSendCounter}) < ${userService.currentUser.minimumRequiredImagesExchanged} or user is excluded ${contact.userDiscoveryExcluded}',
); );
return; return;
} }
@ -73,7 +73,7 @@ Future<void> handleUserDiscoveryUpdate(
int fromUserId, int fromUserId,
EncryptedContent_UserDiscoveryUpdate update, EncryptedContent_UserDiscoveryUpdate update,
) async { ) async {
if (!appSession.currentUser.isUserDiscoveryEnabled) { if (!userService.currentUser.isUserDiscoveryEnabled) {
Log.warn('Got a user discovery update while it is disabled'); Log.warn('Got a user discovery update while it is disabled');
return; return;
} }

View file

@ -98,7 +98,7 @@ Future<bool> isAllowedToDownload(MediaType type) async {
final connectivityResult = await Connectivity().checkConnectivity(); final connectivityResult = await Connectivity().checkConnectivity();
final options = final options =
appSession.currentUser.autoDownloadOptions ?? defaultAutoDownloadOptions; userService.currentUser.autoDownloadOptions ?? defaultAutoDownloadOptions;
if (connectivityResult.contains(ConnectivityResult.mobile)) { if (connectivityResult.contains(ConnectivityResult.mobile)) {
if (type == MediaType.video) { if (type == MediaType.video) {

View file

@ -359,7 +359,7 @@ Future<void> startBackgroundMediaUpload(MediaFileService mediaService) async {
// if the user has enabled auto storing and the file // if the user has enabled auto storing and the file
// was send with unlimited counter not in twonly-Mode then store the file // was send with unlimited counter not in twonly-Mode then store the file
if (appSession.currentUser.autoStoreAllSendUnlimitedMediaFiles && if (userService.currentUser.autoStoreAllSendUnlimitedMediaFiles &&
!mediaService.mediaFile.requiresAuthentication && !mediaService.mediaFile.requiresAuthentication &&
!mediaService.storedPath.existsSync() && !mediaService.storedPath.existsSync() &&
mediaService.mediaFile.displayLimitInMilliseconds == null) { mediaService.mediaFile.displayLimitInMilliseconds == null) {

View file

@ -346,12 +346,15 @@ Future<(Uint8List, Uint8List?)?> sendCipherText(
return null; return null;
} }
} }
encryptedContent.senderProfileCounter = Int64(appSession.currentUser.avatarCounter); encryptedContent.senderProfileCounter = Int64(
userService.currentUser.avatarCounter,
);
if (appSession.currentUser.isUserDiscoveryEnabled && messageId != null) { if (userService.currentUser.isUserDiscoveryEnabled && messageId != null) {
final contact = await twonlyDB.contactsDao.getContactById(contactId); final contact = await twonlyDB.contactsDao.getContactById(contactId);
if (contact != null && if (contact != null &&
contact.mediaSendCounter >= appSession.currentUser.minimumRequiredImagesExchanged && contact.mediaSendCounter >=
userService.currentUser.minimumRequiredImagesExchanged &&
!contact.userDiscoveryExcluded) { !contact.userDiscoveryExcluded) {
final version = await UserDiscoveryService.getCurrentVersion(); final version = await UserDiscoveryService.getCurrentVersion();
if (version != null) { if (version != null) {
@ -407,7 +410,7 @@ Future<(Uint8List, Uint8List?)?> sendCipherText(
} }
Future<void> sendTypingIndication(String groupId, bool isTyping) async { Future<void> sendTypingIndication(String groupId, bool isTyping) async {
if (!appSession.currentUser.typingIndicators) return; if (!userService.currentUser.typingIndicators) return;
await sendCipherTextToGroup( await sendCipherTextToGroup(
groupId, groupId,
pb.EncryptedContent( pb.EncryptedContent(
@ -463,15 +466,17 @@ Future<void> notifyContactAboutOpeningMessage(
Future<void> sendContactMyProfileData(int contactId) async { Future<void> sendContactMyProfileData(int contactId) async {
List<int>? avatarSvgCompressed; List<int>? avatarSvgCompressed;
if (appSession.currentUser.avatarSvg != null) { if (userService.currentUser.avatarSvg != null) {
avatarSvgCompressed = gzip.encode(utf8.encode(appSession.currentUser.avatarSvg!)); avatarSvgCompressed = gzip.encode(
utf8.encode(userService.currentUser.avatarSvg!),
);
} }
final encryptedContent = pb.EncryptedContent( final encryptedContent = pb.EncryptedContent(
contactUpdate: pb.EncryptedContent_ContactUpdate( contactUpdate: pb.EncryptedContent_ContactUpdate(
type: pb.EncryptedContent_ContactUpdate_Type.UPDATE, type: pb.EncryptedContent_ContactUpdate_Type.UPDATE,
avatarSvgCompressed: avatarSvgCompressed, avatarSvgCompressed: avatarSvgCompressed,
displayName: appSession.currentUser.displayName, displayName: userService.currentUser.displayName,
username: appSession.currentUser.username, username: userService.currentUser.username,
), ),
); );
await sendCipherText(contactId, encryptedContent); await sendCipherText(contactId, encryptedContent);

View file

@ -265,7 +265,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
await twonlyDB.receiptsDao.markMessagesForRetry(fromUserId); await twonlyDB.receiptsDao.markMessagesForRetry(fromUserId);
final senderProfileCounter = await checkForProfileUpdate(fromUserId, content); final senderProfileCounter = await checkForProfileUpdate(fromUserId, content);
if (appSession.currentUser.isUserDiscoveryEnabled && if (userService.currentUser.isUserDiscoveryEnabled &&
content.hasSenderUserDiscoveryVersion()) { content.hasSenderUserDiscoveryVersion()) {
await checkForUserDiscoveryChanges( await checkForUserDiscoveryChanges(
fromUserId, fromUserId,
@ -354,7 +354,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
/// Verify that the user is (still) in that group... /// Verify that the user is (still) in that group...
if (!await twonlyDB.groupsDao.isContactInGroup(fromUserId, content.groupId)) { if (!await twonlyDB.groupsDao.isContactInGroup(fromUserId, content.groupId)) {
if (getUUIDforDirectChat(appSession.currentUser.userId, fromUserId) == if (getUUIDforDirectChat(userService.currentUser.userId, fromUserId) ==
content.groupId) { content.groupId) {
final contact = await twonlyDB.contactsDao final contact = await twonlyDB.contactsDao
.getContactByUserId(fromUserId) .getContactByUserId(fromUserId)

View file

@ -1,12 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'package:mutex/mutex.dart'; import 'package:mutex/mutex.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:twonly/globals.dart'; import 'package:twonly/globals.dart';
import 'package:twonly/locator.dart'; import 'package:twonly/locator.dart';
import 'package:twonly/main.dart';
import 'package:twonly/src/constants/keyvalue.keys.dart'; import 'package:twonly/src/constants/keyvalue.keys.dart';
import 'package:twonly/src/services/api/mediafiles/upload.api.dart'; import 'package:twonly/src/services/api/mediafiles/upload.api.dart';
import 'package:twonly/src/services/user.service.dart';
import 'package:twonly/src/utils/exclusive_access.dart'; import 'package:twonly/src/utils/exclusive_access.dart';
import 'package:twonly/src/utils/keyvalue.dart'; import 'package:twonly/src/utils/keyvalue.dart';
import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/log.dart';
@ -54,21 +53,15 @@ Future<bool> initBackgroundExecution() async {
if (_isInitialized) { if (_isInitialized) {
// Reload the users, as on Android the background isolate can // Reload the users, as on Android the background isolate can
// stay alive for multiple hours between task executions // stay alive for multiple hours between task executions
final user = await getUser(); return userService.tryInit();
if (user == null) return false;
appSession.currentUser = user;
return true;
} }
SentryWidgetsFlutterBinding.ensureInitialized(); await twonlyMinimumInitialization();
await AppEnvironment.init();
Log.init();
final user = await getUser(); if (!await userService.tryInit()) {
if (user == null) return false; Log.info('Early return as user is not registered yet.');
return false;
setupLocator(); }
appSession.currentUser = user;
AppState.isInBackgroundTask = true; AppState.isInBackgroundTask = true;

View file

@ -14,7 +14,7 @@ import 'package:twonly/src/utils/misc.dart';
Future<void> enableTwonlySafe(String password) async { Future<void> enableTwonlySafe(String password) async {
final (backupId, encryptionKey) = await getMasterKey( final (backupId, encryptionKey) = await getMasterKey(
password, password,
appSession.currentUser.username, userService.currentUser.username,
); );
await updateUser((user) { await updateUser((user) {
@ -66,10 +66,10 @@ Future<(Uint8List, Uint8List)> getMasterKey(
} }
String? getTwonlySafeBackupUrl() { String? getTwonlySafeBackupUrl() {
if (appSession.currentUser.twonlySafeBackup == null) return null; if (userService.currentUser.twonlySafeBackup == null) return null;
return getTwonlySafeBackupUrlFromServer( return getTwonlySafeBackupUrlFromServer(
appSession.currentUser.twonlySafeBackup!.backupId, userService.currentUser.twonlySafeBackup!.backupId,
appSession.currentUser.backupServer, userService.currentUser.backupServer,
); );
} }

View file

@ -23,18 +23,18 @@ import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
Future<void> performTwonlySafeBackup({bool force = false}) async { Future<void> performTwonlySafeBackup({bool force = false}) async {
if (appSession.currentUser.twonlySafeBackup == null) { if (userService.currentUser.twonlySafeBackup == null) {
return; return;
} }
if (appSession.currentUser.twonlySafeBackup!.backupUploadState == if (userService.currentUser.twonlySafeBackup!.backupUploadState ==
LastBackupUploadState.pending) { LastBackupUploadState.pending) {
Log.warn('Backup upload is already pending.'); Log.warn('Backup upload is already pending.');
return; return;
} }
final lastUpdateTime = final lastUpdateTime =
appSession.currentUser.twonlySafeBackup!.lastBackupDone; userService.currentUser.twonlySafeBackup!.lastBackupDone;
if (!force && lastUpdateTime != null) { if (!force && lastUpdateTime != null) {
if (lastUpdateTime.isAfter(clock.now().subtract(const Duration(days: 1)))) { if (lastUpdateTime.isAfter(clock.now().subtract(const Duration(days: 1)))) {
return; return;
@ -122,8 +122,8 @@ Future<void> performTwonlySafeBackup({bool force = false}) async {
final backupHash = uint8ListToHex((await Sha256().hash(backupBytes)).bytes); final backupHash = uint8ListToHex((await Sha256().hash(backupBytes)).bytes);
if (appSession.currentUser.twonlySafeBackup!.lastBackupDone == null || if (userService.currentUser.twonlySafeBackup!.lastBackupDone == null ||
appSession.currentUser.twonlySafeBackup!.lastBackupDone!.isAfter( userService.currentUser.twonlySafeBackup!.lastBackupDone!.isAfter(
clock.now().subtract(const Duration(days: 90)), clock.now().subtract(const Duration(days: 90)),
)) { )) {
force = true; force = true;
@ -152,7 +152,7 @@ Future<void> performTwonlySafeBackup({bool force = false}) async {
final secretBox = await chacha20.encrypt( final secretBox = await chacha20.encrypt(
backupBytes, backupBytes,
secretKey: SecretKey( secretKey: SecretKey(
appSession.currentUser.twonlySafeBackup!.encryptionKey, userService.currentUser.twonlySafeBackup!.encryptionKey,
), ),
nonce: nonce, nonce: nonce,
); );
@ -175,9 +175,9 @@ Future<void> performTwonlySafeBackup({bool force = false}) async {
'Create twonly Backup with a size of ${encryptedBackupBytes.length} bytes.', 'Create twonly Backup with a size of ${encryptedBackupBytes.length} bytes.',
); );
if (appSession.currentUser.backupServer != null) { if (userService.currentUser.backupServer != null) {
if (encryptedBackupBytes.length > if (encryptedBackupBytes.length >
appSession.currentUser.backupServer!.maxBackupBytes) { userService.currentUser.backupServer!.maxBackupBytes) {
Log.error('Backup is to big for the alternative backup server.'); Log.error('Backup is to big for the alternative backup server.');
await updateUser((user) { await updateUser((user) {
user.twonlySafeBackup!.backupUploadState = LastBackupUploadState.failed; user.twonlySafeBackup!.backupUploadState = LastBackupUploadState.failed;

View file

@ -17,7 +17,7 @@ Future<void> syncFlameCounters({String? forceForGroup}) async {
(x) => x.totalMediaCounter == maxMessageCounter, (x) => x.totalMediaCounter == maxMessageCounter,
); );
if (appSession.currentUser.myBestFriendGroupId != bestFriend.groupId) { if (userService.currentUser.myBestFriendGroupId != bestFriend.groupId) {
await updateUser((user) { await updateUser((user) {
user.myBestFriendGroupId = bestFriend.groupId; user.myBestFriendGroupId = bestFriend.groupId;
}); });

View file

@ -43,8 +43,8 @@ Future<bool> createNewGroup(String groupName, List<Contact> members) async {
final memberIds = members.map((x) => Int64(x.userId)).toList(); final memberIds = members.map((x) => Int64(x.userId)).toList();
final groupState = EncryptedGroupState( final groupState = EncryptedGroupState(
memberIds: [Int64(appSession.currentUser.userId)] + memberIds, memberIds: [Int64(userService.currentUser.userId)] + memberIds,
adminIds: [Int64(appSession.currentUser.userId)], adminIds: [Int64(userService.currentUser.userId)],
groupName: groupName, groupName: groupName,
deleteMessagesAfterMilliseconds: Int64( deleteMessagesAfterMilliseconds: Int64(
defaultDeleteMessagesAfterMilliseconds, defaultDeleteMessagesAfterMilliseconds,
@ -284,9 +284,9 @@ Future<(int, EncryptedGroupState)?> fetchGroupState(Group group) async {
final myPubKey = keyPair.getPublicKey().serialize().toList(); final myPubKey = keyPair.getPublicKey().serialize().toList();
if (listEquals(appendedPubKey, myPubKey)) { if (listEquals(appendedPubKey, myPubKey)) {
adminIds.remove(Int64(appSession.currentUser.userId)); adminIds.remove(Int64(userService.currentUser.userId));
memberIds.remove( memberIds.remove(
Int64(appSession.currentUser.userId), Int64(userService.currentUser.userId),
); // -> Will remove the user later... ); // -> Will remove the user later...
} else { } else {
Log.info('A non admin left the group!!!'); Log.info('A non admin left the group!!!');
@ -304,7 +304,7 @@ Future<(int, EncryptedGroupState)?> fetchGroupState(Group group) async {
} }
} }
if (!memberIds.contains(Int64(appSession.currentUser.userId))) { if (!memberIds.contains(Int64(userService.currentUser.userId))) {
// OH no, I am no longer a member of this group... // OH no, I am no longer a member of this group...
// Return from the group... // Return from the group...
await twonlyDB.groupsDao.updateGroup( await twonlyDB.groupsDao.updateGroup(
@ -318,7 +318,7 @@ Future<(int, EncryptedGroupState)?> fetchGroupState(Group group) async {
final isGroupAdmin = final isGroupAdmin =
adminIds.firstWhereOrNull( adminIds.firstWhereOrNull(
(t) => t.toInt() == appSession.currentUser.userId, (t) => t.toInt() == userService.currentUser.userId,
) != ) !=
null; null;
@ -372,7 +372,7 @@ Future<(int, EncryptedGroupState)?> fetchGroupState(Group group) async {
// First find and insert NEW members // First find and insert NEW members
for (final memberId in memberIds) { for (final memberId in memberIds) {
if (memberId == Int64(appSession.currentUser.userId)) { if (memberId == Int64(userService.currentUser.userId)) {
continue; continue;
} }
if (currentGroupMembers.any((t) => t.contactId == memberId.toInt())) { if (currentGroupMembers.any((t) => t.contactId == memberId.toInt())) {
@ -842,7 +842,7 @@ Future<bool> removeMemberFromGroup(
groupId: Value(group.groupId), groupId: Value(group.groupId),
type: const Value(GroupActionType.removedMember), type: const Value(GroupActionType.removedMember),
affectedContactId: Value( affectedContactId: Value(
removeContactId == appSession.currentUser.userId removeContactId == userService.currentUser.userId
? null ? null
: removeContactId, : removeContactId,
), ),
@ -951,7 +951,7 @@ Future<bool> leaveAsNonAdminFromGroup(Group group) async {
EncryptedContent( EncryptedContent(
groupUpdate: EncryptedContent_GroupUpdate( groupUpdate: EncryptedContent_GroupUpdate(
groupActionType: groupActionType.name, groupActionType: groupActionType.name,
affectedContactId: Int64(appSession.currentUser.userId), affectedContactId: Int64(userService.currentUser.userId),
), ),
), ),
); );

View file

@ -33,7 +33,7 @@ Future<bool> handleIntentUrl(BuildContext context, Uri uri) async {
if (!context.mounted) return false; if (!context.mounted) return false;
if (username == appSession.currentUser.username) { if (username == userService.currentUser.username) {
await context.push(Routes.settingsPublicProfile); await context.push(Routes.settingsPublicProfile);
return true; return true;
} }
@ -116,7 +116,7 @@ Future<void> handleIntentMediaFile(
final newMediaService = await initializeMediaUpload( final newMediaService = await initializeMediaUpload(
type, type,
appSession.currentUser.defaultShowTime, userService.currentUser.defaultShowTime,
); );
if (newMediaService == null) { if (newMediaService == null) {
Log.error('Could not create new media file for intent shared file'); Log.error('Could not create new media file for intent shared file');

View file

@ -239,7 +239,7 @@ class MediaFileService {
} }
if (tempPath.existsSync()) { if (tempPath.existsSync()) {
await tempPath.copy(storedPath.path); await tempPath.copy(storedPath.path);
if (appSession.currentUser.storeMediaFilesInGallery) { if (userService.currentUser.storeMediaFilesInGallery) {
if (mediaFile.type == MediaType.video) { if (mediaFile.type == MediaType.video) {
await saveVideoToGallery(storedPath.path); await saveVideoToGallery(storedPath.path);
} else { } else {

View file

@ -74,7 +74,7 @@ Future<void> checkForTokenUpdates() async {
} }
Future<void> initFCMAfterAuthenticated({bool force = false}) async { Future<void> initFCMAfterAuthenticated({bool force = false}) async {
if (appSession.currentUser.updateFCMToken || force) { if (userService.currentUser.updateFCMToken || force) {
const storage = FlutterSecureStorage(); const storage = FlutterSecureStorage();
final storedToken = await storage.read(key: SecureStorageKeys.googleFcm); final storedToken = await storage.read(key: SecureStorageKeys.googleFcm);
if (storedToken != null) { if (storedToken != null) {

View file

@ -21,10 +21,10 @@ Future<IdentityKeyPair?> getSignalIdentityKeyPair() async {
// This function runs after the clients authenticated with the server. // This function runs after the clients authenticated with the server.
// It then checks if it should update a new session key // It then checks if it should update a new session key
Future<void> signalHandleNewServerConnection() async { Future<void> signalHandleNewServerConnection() async {
if (appSession.currentUser.signalLastSignedPreKeyUpdated != null) { if (userService.currentUser.signalLastSignedPreKeyUpdated != null) {
final fortyEightHoursAgo = clock.now().subtract(const Duration(hours: 48)); final fortyEightHoursAgo = clock.now().subtract(const Duration(hours: 48));
final isYoungerThan48Hours = final isYoungerThan48Hours =
(appSession.currentUser.signalLastSignedPreKeyUpdated!).isAfter( (userService.currentUser.signalLastSignedPreKeyUpdated!).isAfter(
fortyEightHoursAgo, fortyEightHoursAgo,
); );
if (isYoungerThan48Hours) { if (isYoungerThan48Hours) {

View file

@ -18,6 +18,13 @@ class UserService {
final _userDataUpdateController = StreamController<void>.broadcast(); final _userDataUpdateController = StreamController<void>.broadcast();
Stream<void> get onUserUpdated => _userDataUpdateController.stream; Stream<void> get onUserUpdated => _userDataUpdateController.stream;
Future<bool> tryInit() async {
final user = await getUser();
if (user == null) return false;
userService.currentUser = user;
return true;
}
void triggerUserUpdate() { void triggerUserUpdate() {
_userDataUpdateController.add(null); _userDataUpdateController.add(null);
} }
@ -32,7 +39,7 @@ Future<bool> isUserCreated() async {
if (user == null) { if (user == null) {
return false; return false;
} }
appSession.currentUser = user; userService.currentUser = user;
return true; return true;
} }
@ -84,8 +91,8 @@ Future<void> updateUser(
key: SecureStorageKeys.userData, key: SecureStorageKeys.userData,
value: jsonEncode(user), value: jsonEncode(user),
); );
appSession.currentUser = user; userService.currentUser = user;
}); });
appSession.triggerUserUpdate(); userService.triggerUserUpdate();
} }

View file

@ -54,7 +54,7 @@ class UserDiscoveryService {
try { try {
await FlutterUserDiscovery.initializeOrUpdate( await FlutterUserDiscovery.initializeOrUpdate(
threshold: threshold, threshold: threshold,
userId: appSession.currentUser.userId, userId: userService.currentUser.userId,
publicKey: await getUserPublicKey(), publicKey: await getUserPublicKey(),
); );
await updateUser( await updateUser(

View file

@ -15,7 +15,7 @@ const surveyUrlBase = 'https://survey.twonly.org/upload.php';
Future<void> handleUserStudyUpload() async { Future<void> handleUserStudyUpload() async {
try { try {
final token = appSession.currentUser.userStudyParticipantsToken; final token = userService.currentUser.userStudyParticipantsToken;
if (token == null) return; if (token == null) return;
// in case the survey was taken offline try again // in case the survey was taken offline try again
@ -35,8 +35,8 @@ Future<void> handleUserStudyUpload() async {
await KeyValueStore.delete(userStudySurveyKey); await KeyValueStore.delete(userStudySurveyKey);
} }
if (appSession.currentUser.lastUserStudyDataUpload != null && if (userService.currentUser.lastUserStudyDataUpload != null &&
isToday(appSession.currentUser.lastUserStudyDataUpload!)) { isToday(userService.currentUser.lastUserStudyDataUpload!)) {
// Only send updates once a day. // Only send updates once a day.
// This enables to see if improvements to actually work. // This enables to see if improvements to actually work.
return; return;

View file

@ -53,13 +53,13 @@ File avatarPNGFile(int contactId) {
} }
Future<Uint8List> getUserAvatar() async { Future<Uint8List> getUserAvatar() async {
if (appSession.currentUser.avatarSvg == null) { if (userService.currentUser.avatarSvg == null) {
final data = await rootBundle.load('assets/images/default_avatar.png'); final data = await rootBundle.load('assets/images/default_avatar.png');
return data.buffer.asUint8List(); return data.buffer.asUint8List();
} }
final pictureInfo = await vg.loadPicture( final pictureInfo = await vg.loadPicture(
SvgStringLoader(appSession.currentUser.avatarSvg!), SvgStringLoader(userService.currentUser.avatarSvg!),
null, null,
); );
@ -68,7 +68,7 @@ Future<Uint8List> getUserAvatar() async {
final byteData = await image.toByteData(format: ui.ImageByteFormat.png); final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
final pngBytes = byteData!.buffer.asUint8List(); final pngBytes = byteData!.buffer.asUint8List();
final file = avatarPNGFile(appSession.currentUser.userId) final file = avatarPNGFile(userService.currentUser.userId)
..writeAsBytesSync(pngBytes); ..writeAsBytesSync(pngBytes);
pictureInfo.picture.dispose(); pictureInfo.picture.dispose();

View file

@ -17,8 +17,8 @@ Future<Uint8List> getProfileQrCodeData() async {
final signedPreKey = (await signalStore.loadSignedPreKeys())[0]; final signedPreKey = (await signalStore.loadSignedPreKeys())[0];
final publicProfile = PublicProfile( final publicProfile = PublicProfile(
userId: Int64(appSession.currentUser.userId), userId: Int64(userService.currentUser.userId),
username: appSession.currentUser.username, username: userService.currentUser.username,
publicIdentityKey: (await signalStore.getIdentityKeyPair()) publicIdentityKey: (await signalStore.getIdentityKeyPair())
.getPublicKey() .getPublicKey()
.serialize(), .serialize(),

View file

@ -93,19 +93,19 @@ class _AvatarIconState extends State<AvatarIcon> {
setState(() {}); setState(() {});
}); });
} else if (widget.myAvatar) { } else if (widget.myAvatar) {
_userSub = appSession.onUserUpdated.listen((_) { _userSub = userService.onUserUpdated.listen((_) {
if (mounted) { if (mounted) {
setState(() { setState(() {
if (appSession.currentUser.avatarSvg != null) { if (userService.currentUser.avatarSvg != null) {
_avatarSvg = appSession.currentUser.avatarSvg; _avatarSvg = userService.currentUser.avatarSvg;
} else { } else {
_avatarContacts = []; _avatarContacts = [];
} }
}); });
} }
}); });
if (appSession.currentUser.avatarSvg != null) { if (userService.currentUser.avatarSvg != null) {
_avatarSvg = appSession.currentUser.avatarSvg; _avatarSvg = userService.currentUser.avatarSvg;
} }
} else if (widget.contactId != null) { } else if (widget.contactId != null) {
contactStream = twonlyDB.contactsDao contactStream = twonlyDB.contactsDao

View file

@ -49,7 +49,7 @@ class _FlameCounterWidgetState extends State<FlameCounterWidget> {
} }
if (groupId != null && group != null) { if (groupId != null && group != null) {
isBestFriend = isBestFriend =
appSession.currentUser.myBestFriendGroupId == groupId && userService.currentUser.myBestFriendGroupId == groupId &&
group.alsoBestFriend; group.alsoBestFriend;
final stream = twonlyDB.groupsDao.watchFlameCounter(groupId); final stream = twonlyDB.groupsDao.watchFlameCounter(groupId);
flameCounterSub = stream.listen((counter) { flameCounterSub = stream.listen((counter) {

View file

@ -210,7 +210,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
_hasAudioPermission = await Permission.microphone.isGranted; _hasAudioPermission = await Permission.microphone.isGranted;
if (!_hasAudioPermission && if (!_hasAudioPermission &&
!appSession.currentUser.requestedAudioPermission) { !userService.currentUser.requestedAudioPermission) {
await updateUser((u) { await updateUser((u) {
u.requestedAudioPermission = true; u.requestedAudioPermission = true;
}); });
@ -322,7 +322,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
((videoFilePath != null) ? MediaType.video : MediaType.image); ((videoFilePath != null) ? MediaType.video : MediaType.image);
final mediaFileService = await initializeMediaUpload( final mediaFileService = await initializeMediaUpload(
type, type,
appSession.currentUser.defaultShowTime, userService.currentUser.defaultShowTime,
isDraftMedia: true, isDraftMedia: true,
); );
if (!mounted) return true; if (!mounted) return true;

View file

@ -137,7 +137,7 @@ class MainCameraController {
await cameraController?.initialize(); await cameraController?.initialize();
await cameraController?.startImageStream(_processCameraImage); await cameraController?.startImageStream(_processCameraImage);
await cameraController?.setZoomLevel(selectedCameraDetails.scaleFactor); await cameraController?.setZoomLevel(selectedCameraDetails.scaleFactor);
if (appSession.currentUser.videoStabilizationEnabled && !kDebugMode) { if (userService.currentUser.videoStabilizationEnabled && !kDebugMode) {
await cameraController?.setVideoStabilizationMode( await cameraController?.setVideoStabilizationMode(
VideoStabilizationMode.level1, VideoStabilizationMode.level1,
); );
@ -397,7 +397,7 @@ class MainCameraController {
} }
} }
} else { } else {
if (profile.username != appSession.currentUser.username) { if (profile.username != userService.currentUser.username) {
if (scannedNewProfiles[profile.userId.toInt()] == null) { if (scannedNewProfiles[profile.userId.toInt()] == null) {
await HapticFeedback.heavyImpact(); await HapticFeedback.heavyImpact();
scannedNewProfiles[profile.userId.toInt()] = ScannedNewProfile( scannedNewProfiles[profile.userId.toInt()] = ScannedNewProfile(

View file

@ -256,7 +256,7 @@ class _ShareImageView extends State<ShareImageView> {
children: [ children: [
if (widget.mediaFileService.mediaFile.type == MediaType.image && if (widget.mediaFileService.mediaFile.type == MediaType.image &&
_screenshotImage?.image != null && _screenshotImage?.image != null &&
appSession.currentUser.showShowImagePreviewWhenSending) userService.currentUser.showShowImagePreviewWhenSending)
SizedBox( SizedBox(
height: 100, height: 100,
width: 100 * 9 / 16, width: 100 * 9 / 16,

View file

@ -93,7 +93,7 @@ class _SearchUsernameView extends State<AddNewUserView> {
} }
Future<void> _requestNewUserByUsername(String username) async { Future<void> _requestNewUserByUsername(String username) async {
if (appSession.currentUser.username == username) return; if (userService.currentUser.username == username) return;
setState(() { setState(() {
_isLoading = true; _isLoading = true;

View file

@ -45,7 +45,7 @@ class _ChatListViewState extends State<ChatListView> {
@override @override
void initState() { void initState() {
initAsync(); initAsync();
_userSub = appSession.onUserUpdated.listen((_) { _userSub = userService.onUserUpdated.listen((_) {
if (mounted) setState(() {}); if (mounted) setState(() {});
}); });
super.initState(); super.initState();
@ -90,8 +90,8 @@ class _ChatListViewState extends State<ChatListView> {
Sha256().hash, Sha256().hash,
changeLog.codeUnits, changeLog.codeUnits,
)).bytes; )).bytes;
if (!appSession.currentUser.hideChangeLog && if (!userService.currentUser.hideChangeLog &&
appSession.currentUser.lastChangeLogHash.toString() != userService.currentUser.lastChangeLogHash.toString() !=
changeLogHash.toString()) { changeLogHash.toString()) {
await updateUser((u) { await updateUser((u) {
u.lastChangeLogHash = changeLogHash; u.lastChangeLogHash = changeLogHash;

View file

@ -122,7 +122,7 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
_receiverDeletedAccount = groupContacts.first.accountDeleted; _receiverDeletedAccount = groupContacts.first.accountDeleted;
} }
if (appSession.currentUser.typingIndicators) { if (userService.currentUser.typingIndicators) {
unawaited(sendTypingIndication(widget.groupId, false)); unawaited(sendTypingIndication(widget.groupId, false));
_nextTypingIndicator = Timer.periodic(const Duration(seconds: 4), ( _nextTypingIndicator = Timer.periodic(const Duration(seconds: 4), (
_, _,
@ -290,7 +290,7 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
itemScrollController: itemScrollController, itemScrollController: itemScrollController,
itemBuilder: (context, i) { itemBuilder: (context, i) {
if (i == 0) { if (i == 0) {
return appSession.currentUser.typingIndicators return userService.currentUser.typingIndicators
? TypingIndicator(group: group) ? TypingIndicator(group: group)
: Container(); : Container();
} }

View file

@ -102,7 +102,7 @@ class _ContactRowState extends State<_ContactRow> {
bool _isLoading = false; bool _isLoading = false;
Future<void> _onContactClick(bool isAdded) async { Future<void> _onContactClick(bool isAdded) async {
if (widget.contact.userId.toInt() == appSession.currentUser.userId) { if (widget.contact.userId.toInt() == userService.currentUser.userId) {
await context.push(Routes.settingsProfile); await context.push(Routes.settingsProfile);
return; return;
} }
@ -162,7 +162,7 @@ class _ContactRowState extends State<_ContactRow> {
final contactInDb = snapshot.data; final contactInDb = snapshot.data;
final isAdded = final isAdded =
contactInDb != null || contactInDb != null ||
widget.contact.userId.toInt() == appSession.currentUser.userId; widget.contact.userId.toInt() == userService.currentUser.userId;
return GestureDetector( return GestureDetector(
onTap: _isLoading ? null : () => _onContactClick(isAdded), onTap: _isLoading ? null : () => _onContactClick(isAdded),

View file

@ -73,7 +73,7 @@ class _MessageInputState extends State<MessageInput> {
_textFieldController.text = widget.group.draftMessage!; _textFieldController.text = widget.group.draftMessage!;
} }
widget.textFieldFocus.addListener(_handleTextFocusChange); widget.textFieldFocus.addListener(_handleTextFocusChange);
if (appSession.currentUser.typingIndicators) { if (userService.currentUser.typingIndicators) {
_nextTypingIndicator = Timer.periodic(const Duration(seconds: 1), ( _nextTypingIndicator = Timer.periodic(const Duration(seconds: 1), (
_, _,
) async { ) async {

View file

@ -50,8 +50,8 @@ class _ReactionButtonsState extends State<ReactionButtons> {
} }
Future<void> initAsync() async { Future<void> initAsync() async {
if (appSession.currentUser.preSelectedEmojies != null) { if (userService.currentUser.preSelectedEmojies != null) {
selectedEmojis = appSession.currentUser.preSelectedEmojies!; selectedEmojis = userService.currentUser.preSelectedEmojies!;
} }
setState(() {}); setState(() {});
} }

View file

@ -41,7 +41,7 @@ class _RestoreFlameCompState extends State<RestoreFlameComp> {
void initState() { void initState() {
_groupId = getUUIDforDirectChat( _groupId = getUUIDforDirectChat(
widget.contactId, widget.contactId,
appSession.currentUser.userId, userService.currentUser.userId,
); );
final stream = twonlyDB.groupsDao.watchGroup(_groupId); final stream = twonlyDB.groupsDao.watchGroup(_groupId);
_groupSub = stream.listen((update) { _groupSub = stream.listen((update) {
@ -57,7 +57,9 @@ class _RestoreFlameCompState extends State<RestoreFlameComp> {
} }
Future<void> _restoreFlames() async { Future<void> _restoreFlames() async {
final currentPlan = planFromString(appSession.currentUser.subscriptionPlan); final currentPlan = planFromString(
userService.currentUser.subscriptionPlan,
);
if (!isUserAllowed(currentPlan, PremiumFeatures.RestoreFlames) && if (!isUserAllowed(currentPlan, PremiumFeatures.RestoreFlames) &&
kReleaseMode) { kReleaseMode) {
await context.push(Routes.settingsSubscription); await context.push(Routes.settingsSubscription);

View file

@ -207,7 +207,7 @@ class _ContactViewState extends State<ContactView> {
SelectChatDeletionTimeListTitle( SelectChatDeletionTimeListTitle(
groupId: getUUIDforDirectChat( groupId: getUUIDforDirectChat(
widget.userId, widget.userId,
appSession.currentUser.userId, userService.currentUser.userId,
), ),
), ),
const Divider(), const Divider(),
@ -226,17 +226,17 @@ class _ContactViewState extends State<ContactView> {
setState(() {}); setState(() {});
}, },
), ),
if (appSession.currentUser.isUserDiscoveryEnabled) if (userService.currentUser.isUserDiscoveryEnabled)
BetterListTile( BetterListTile(
icon: FontAwesomeIcons.usersViewfinder, icon: FontAwesomeIcons.usersViewfinder,
text: context.lang.userDiscoverySettingsTitle, text: context.lang.userDiscoverySettingsTitle,
subtitle: subtitle:
!contact.userDiscoveryExcluded && !contact.userDiscoveryExcluded &&
contact.mediaSendCounter < contact.mediaSendCounter <
appSession.currentUser.minimumRequiredImagesExchanged userService.currentUser.minimumRequiredImagesExchanged
? Text( ? Text(
context.lang.contactUserDiscoveryImagesLeft( context.lang.contactUserDiscoveryImagesLeft(
appSession.currentUser.minimumRequiredImagesExchanged - userService.currentUser.minimumRequiredImagesExchanged -
contact.mediaSendCounter, contact.mediaSendCounter,
getContactDisplayName(contact), getContactDisplayName(contact),
), ),

View file

@ -143,7 +143,7 @@ class _GroupViewState extends State<GroupView> {
success = await removeMemberFromGroup( success = await removeMemberFromGroup(
_group!, _group!,
keyPair.getPublicKey().serialize(), keyPair.getPublicKey().serialize(),
appSession.currentUser.userId, userService.currentUser.userId,
); );
} else { } else {
success = await leaveAsNonAdminFromGroup(_group!); success = await leaveAsNonAdminFromGroup(_group!);

View file

@ -137,7 +137,7 @@ class HomeViewState extends State<HomeView> {
); );
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
if (widget.initialPage == 1 && if (widget.initialPage == 1 &&
!appSession.currentUser.startWithCameraOpen || !userService.currentUser.startWithCameraOpen ||
widget.initialPage == 0) { widget.initialPage == 0) {
globalUpdateOfHomeViewPageIndex(0); globalUpdateOfHomeViewPageIndex(0);
} }

View file

@ -143,7 +143,7 @@ class _RegisterViewState extends State<RegisterView> {
value: jsonEncode(userData), value: jsonEncode(userData),
); );
appSession.currentUser = userData; userService.currentUser = userData;
await apiService.authenticate(); await apiService.authenticate();
widget.callbackOnSuccess(); widget.callbackOnSuccess();

View file

@ -107,7 +107,7 @@ class _PublicProfileViewState extends State<PublicProfileView> {
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
Text( Text(
appSession.currentUser.username, userService.currentUser.username,
style: const TextStyle(fontSize: 24), style: const TextStyle(fontSize: 24),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
@ -127,12 +127,12 @@ class _PublicProfileViewState extends State<PublicProfileView> {
subtitle: (_publicKey == null) subtitle: (_publicKey == null)
? null ? null
: Text( : Text(
'https://me.twonly.eu/${appSession.currentUser.username}', 'https://me.twonly.eu/${userService.currentUser.username}',
), ),
onTap: () { onTap: () {
final params = ShareParams( final params = ShareParams(
text: text:
'https://me.twonly.eu/${appSession.currentUser.username}#${base64Url.encode(_publicKey!)}', 'https://me.twonly.eu/${userService.currentUser.username}#${base64Url.encode(_publicKey!)}',
); );
SharePlus.instance.share(params); SharePlus.instance.share(params);
}, },

View file

@ -98,7 +98,7 @@ class _AppearanceViewState extends State<AppearanceView> {
title: Text(context.lang.settingsAppearance), title: Text(context.lang.settingsAppearance),
), ),
body: StreamBuilder<void>( body: StreamBuilder<void>(
stream: appSession.onUserUpdated, stream: userService.onUserUpdated,
builder: (context, snapshot) { builder: (context, snapshot) {
return ListView( return ListView(
children: [ children: [
@ -116,7 +116,7 @@ class _AppearanceViewState extends State<AppearanceView> {
title: Text(context.lang.contactUsShortcut), title: Text(context.lang.contactUsShortcut),
onTap: toggleShowFeedbackIcon, onTap: toggleShowFeedbackIcon,
trailing: Switch( trailing: Switch(
value: !appSession.currentUser.showFeedbackShortcut, value: !userService.currentUser.showFeedbackShortcut,
onChanged: (a) => toggleShowFeedbackIcon(), onChanged: (a) => toggleShowFeedbackIcon(),
), ),
), ),
@ -124,7 +124,7 @@ class _AppearanceViewState extends State<AppearanceView> {
title: Text(context.lang.startWithCameraOpen), title: Text(context.lang.startWithCameraOpen),
onTap: toggleStartWithCameraOpen, onTap: toggleStartWithCameraOpen,
trailing: Switch( trailing: Switch(
value: appSession.currentUser.startWithCameraOpen, value: userService.currentUser.startWithCameraOpen,
onChanged: (a) => toggleStartWithCameraOpen(), onChanged: (a) => toggleStartWithCameraOpen(),
), ),
), ),
@ -132,7 +132,8 @@ class _AppearanceViewState extends State<AppearanceView> {
title: Text(context.lang.showImagePreviewWhenSending), title: Text(context.lang.showImagePreviewWhenSending),
onTap: toggleShowImagePreviewWhenSending, onTap: toggleShowImagePreviewWhenSending,
trailing: Switch( trailing: Switch(
value: appSession.currentUser.showShowImagePreviewWhenSending, value:
userService.currentUser.showShowImagePreviewWhenSending,
onChanged: (a) => toggleShowImagePreviewWhenSending(), onChanged: (a) => toggleShowImagePreviewWhenSending(),
), ),
), ),

View file

@ -32,8 +32,8 @@ class _BackupServerViewState extends State<BackupServerView> {
} }
Future<void> initAsync() async { Future<void> initAsync() async {
if (appSession.currentUser.backupServer != null) { if (userService.currentUser.backupServer != null) {
final uri = Uri.parse(appSession.currentUser.backupServer!.serverUrl); final uri = Uri.parse(userService.currentUser.backupServer!.serverUrl);
// remove user auth data // remove user auth data
final serverUrl = Uri( final serverUrl = Uri(
scheme: uri.scheme, scheme: uri.scheme,

View file

@ -38,10 +38,10 @@ class _BackupViewState extends State<BackupView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return StreamBuilder<void>( return StreamBuilder<void>(
stream: appSession.onUserUpdated, stream: userService.onUserUpdated,
builder: (context, _) { builder: (context, _) {
final backupServer = final backupServer =
appSession.currentUser.backupServer ?? _defaultBackupServer; userService.currentUser.backupServer ?? _defaultBackupServer;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(context.lang.settingsBackup), title: Text(context.lang.settingsBackup),
@ -57,7 +57,7 @@ class _BackupViewState extends State<BackupView> {
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
if (appSession.currentUser.twonlySafeBackup != null) if (userService.currentUser.twonlySafeBackup != null)
Column( Column(
children: [ children: [
const SizedBox(height: 32), const SizedBox(height: 32),
@ -87,7 +87,7 @@ class _BackupViewState extends State<BackupView> {
context.lang.backupLastBackupDate, context.lang.backupLastBackupDate,
formatDateTime( formatDateTime(
context, context,
appSession userService
.currentUser .currentUser
.twonlySafeBackup! .twonlySafeBackup!
.lastBackupDone, .lastBackupDone,
@ -96,7 +96,7 @@ class _BackupViewState extends State<BackupView> {
( (
context.lang.backupLastBackupSize, context.lang.backupLastBackupSize,
formatBytes( formatBytes(
appSession userService
.currentUser .currentUser
.twonlySafeBackup! .twonlySafeBackup!
.lastBackupSize, .lastBackupSize,
@ -105,7 +105,7 @@ class _BackupViewState extends State<BackupView> {
( (
context.lang.backupLastBackupResult, context.lang.backupLastBackupResult,
_backupStatus( _backupStatus(
appSession userService
.currentUser .currentUser
.twonlySafeBackup! .twonlySafeBackup!
.backupUploadState, .backupUploadState,
@ -156,7 +156,7 @@ class _BackupViewState extends State<BackupView> {
onPressed: () => onPressed: () =>
context.push(Routes.settingsBackupSetup, extra: true), context.push(Routes.settingsBackupSetup, extra: true),
child: Text( child: Text(
appSession.currentUser.twonlySafeBackup == null userService.currentUser.twonlySafeBackup == null
? context.lang.backupEnableBackup ? context.lang.backupEnableBackup
: context.lang.backupChangePassword, : context.lang.backupChangePassword,
), ),

View file

@ -211,7 +211,7 @@ class _SetupBackupViewState extends State<SetupBackupView> {
) )
: const Icon(Icons.lock_clock_rounded), : const Icon(Icons.lock_clock_rounded),
label: Text( label: Text(
appSession.currentUser.twonlySafeBackup == null userService.currentUser.twonlySafeBackup == null
? context.lang.backupEnableBackup ? context.lang.backupEnableBackup
: context.lang.backupChangePassword, : context.lang.backupChangePassword,
), ),

View file

@ -16,7 +16,7 @@ class _ChatReactionSelectionView extends State<ChatReactionSelectionView> {
List<String> _selectedEmojis = []; List<String> _selectedEmojis = [];
List<String> _emojisFromSession() { List<String> _emojisFromSession() {
final user = appSession.currentUser; final user = userService.currentUser;
if (user.preSelectedEmojies != null) { if (user.preSelectedEmojies != null) {
return user.preSelectedEmojies!; return user.preSelectedEmojies!;
} }
@ -53,7 +53,7 @@ class _ChatReactionSelectionView extends State<ChatReactionSelectionView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return StreamBuilder<void>( return StreamBuilder<void>(
stream: appSession.onUserUpdated, stream: userService.onUserUpdated,
builder: (context, _) { builder: (context, _) {
_selectedEmojis = _emojisFromSession(); _selectedEmojis = _emojisFromSession();
return Scaffold( return Scaffold(

View file

@ -28,7 +28,7 @@ class _DataAndStorageViewState extends State<DataAndStorageView> {
builder: (context) { builder: (context) {
return AutoDownloadOptionsDialog( return AutoDownloadOptionsDialog(
autoDownloadOptions: autoDownloadOptions:
appSession.currentUser.autoDownloadOptions ?? userService.currentUser.autoDownloadOptions ??
defaultAutoDownloadOptions, defaultAutoDownloadOptions,
connectionMode: connectionMode, connectionMode: connectionMode,
onUpdate: () {}, onUpdate: () {},
@ -57,10 +57,10 @@ class _DataAndStorageViewState extends State<DataAndStorageView> {
title: Text(context.lang.settingsStorageData), title: Text(context.lang.settingsStorageData),
), ),
body: StreamBuilder<void>( body: StreamBuilder<void>(
stream: appSession.onUserUpdated, stream: userService.onUserUpdated,
builder: (context, _) { builder: (context, _) {
final autoDownloadOptions = final autoDownloadOptions =
appSession.currentUser.autoDownloadOptions ?? userService.currentUser.autoDownloadOptions ??
defaultAutoDownloadOptions; defaultAutoDownloadOptions;
return ListView( return ListView(
children: [ children: [
@ -71,7 +71,7 @@ class _DataAndStorageViewState extends State<DataAndStorageView> {
), ),
onTap: toggleStoreInGallery, onTap: toggleStoreInGallery,
trailing: Switch( trailing: Switch(
value: appSession.currentUser.storeMediaFilesInGallery, value: userService.currentUser.storeMediaFilesInGallery,
onChanged: (a) => toggleStoreInGallery(), onChanged: (a) => toggleStoreInGallery(),
), ),
), ),
@ -83,7 +83,7 @@ class _DataAndStorageViewState extends State<DataAndStorageView> {
), ),
onTap: toggleAutoStoreMediaFiles, onTap: toggleAutoStoreMediaFiles,
trailing: Switch( trailing: Switch(
value: appSession value: userService
.currentUser .currentUser
.autoStoreAllSendUnlimitedMediaFiles, .autoStoreAllSendUnlimitedMediaFiles,
onChanged: (a) => toggleAutoStoreMediaFiles(), onChanged: (a) => toggleAutoStoreMediaFiles(),

View file

@ -44,7 +44,7 @@ class _DeveloperSettingsViewState extends State<DeveloperSettingsView> {
title: const Text('Developer Settings'), title: const Text('Developer Settings'),
), ),
body: StreamBuilder<void>( body: StreamBuilder<void>(
stream: appSession.onUserUpdated, stream: userService.onUserUpdated,
builder: (context, _) { builder: (context, _) {
return ListView( return ListView(
children: [ children: [
@ -52,7 +52,7 @@ class _DeveloperSettingsViewState extends State<DeveloperSettingsView> {
title: const Text('Show Developer Settings'), title: const Text('Show Developer Settings'),
onTap: toggleDeveloperSettings, onTap: toggleDeveloperSettings,
trailing: Switch( trailing: Switch(
value: appSession.currentUser.isDeveloper, value: userService.currentUser.isDeveloper,
onChanged: (_) => toggleDeveloperSettings(), onChanged: (_) => toggleDeveloperSettings(),
), ),
), ),
@ -66,7 +66,7 @@ class _DeveloperSettingsViewState extends State<DeveloperSettingsView> {
title: const Text('Toggle Video Stabilization'), title: const Text('Toggle Video Stabilization'),
onTap: toggleVideoStabilization, onTap: toggleVideoStabilization,
trailing: Switch( trailing: Switch(
value: appSession.currentUser.videoStabilizationEnabled, value: userService.currentUser.videoStabilizationEnabled,
onChanged: (a) => toggleVideoStabilization(), onChanged: (a) => toggleVideoStabilization(),
), ),
), ),

View file

@ -93,7 +93,7 @@ class _ChangeLogViewState extends State<ChangeLogView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return StreamBuilder<void>( return StreamBuilder<void>(
stream: appSession.onUserUpdated, stream: userService.onUserUpdated,
builder: (context, _) { builder: (context, _) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
@ -113,7 +113,7 @@ class _ChangeLogViewState extends State<ChangeLogView> {
children: [ children: [
Text(context.lang.openChangeLog), Text(context.lang.openChangeLog),
Switch( Switch(
value: !appSession.currentUser.hideChangeLog, value: !userService.currentUser.hideChangeLog,
onChanged: (_) => onChanged: (_) =>
updateUser((u) => u.hideChangeLog = !u.hideChangeLog), updateUser((u) => u.hideChangeLog = !u.hideChangeLog),
), ),

View file

@ -31,7 +31,7 @@ class _HelpViewState extends State<HelpView> {
title: Text(context.lang.settingsHelp), title: Text(context.lang.settingsHelp),
), ),
body: StreamBuilder<void>( body: StreamBuilder<void>(
stream: appSession.onUserUpdated, stream: userService.onUserUpdated,
builder: (context, _) { builder: (context, _) {
return ListView( return ListView(
children: [ children: [
@ -52,7 +52,7 @@ class _HelpViewState extends State<HelpView> {
), ),
onTap: toggleAllowErrorTrackingViaSentry, onTap: toggleAllowErrorTrackingViaSentry,
trailing: Switch( trailing: Switch(
value: appSession.currentUser.allowErrorTrackingViaSentry, value: userService.currentUser.allowErrorTrackingViaSentry,
onChanged: (a) => toggleAllowErrorTrackingViaSentry(), onChanged: (a) => toggleAllowErrorTrackingViaSentry(),
), ),
), ),
@ -61,7 +61,7 @@ class _HelpViewState extends State<HelpView> {
onTap: () => context.push(Routes.settingsHelpDiagnostics), onTap: () => context.push(Routes.settingsHelpDiagnostics),
), ),
const Divider(), const Divider(),
if (appSession.currentUser.userStudyParticipantsToken == null || if (userService.currentUser.userStudyParticipantsToken == null ||
kDebugMode) kDebugMode)
ListTile( ListTile(
title: const Text('Teilnahme an Nutzerstudie'), title: const Text('Teilnahme an Nutzerstudie'),

View file

@ -20,7 +20,7 @@ class _PrivacyViewState extends State<PrivacyView> {
Future<void> toggleAuthRequirementOnStartup() async { Future<void> toggleAuthRequirementOnStartup() async {
final isAuth = await authenticateUser( final isAuth = await authenticateUser(
appSession.currentUser.screenLockEnabled userService.currentUser.screenLockEnabled
? context.lang.settingsScreenLockAuthMessageDisable ? context.lang.settingsScreenLockAuthMessageDisable
: context.lang.settingsScreenLockAuthMessageEnable, : context.lang.settingsScreenLockAuthMessageEnable,
); );
@ -84,7 +84,7 @@ class _PrivacyViewState extends State<PrivacyView> {
subtitle: Text(context.lang.settingsTypingIndicationSubtitle), subtitle: Text(context.lang.settingsTypingIndicationSubtitle),
onTap: toggleTypingIndicators, onTap: toggleTypingIndicators,
trailing: Switch( trailing: Switch(
value: appSession.currentUser.typingIndicators, value: userService.currentUser.typingIndicators,
onChanged: (a) => toggleTypingIndicators(), onChanged: (a) => toggleTypingIndicators(),
), ),
), ),
@ -94,7 +94,7 @@ class _PrivacyViewState extends State<PrivacyView> {
subtitle: Text(context.lang.settingsScreenLockSubtitle), subtitle: Text(context.lang.settingsScreenLockSubtitle),
onTap: toggleAuthRequirementOnStartup, onTap: toggleAuthRequirementOnStartup,
trailing: Switch( trailing: Switch(
value: appSession.currentUser.screenLockEnabled, value: userService.currentUser.screenLockEnabled,
onChanged: (a) => toggleAuthRequirementOnStartup(), onChanged: (a) => toggleAuthRequirementOnStartup(),
), ),
), ),

View file

@ -19,9 +19,9 @@ class _UserDiscoverySettingsViewState extends State<UserDiscoverySettingsView> {
title: const Text('Freunde finden'), title: const Text('Freunde finden'),
), ),
body: StreamBuilder<void>( body: StreamBuilder<void>(
stream: appSession.onUserUpdated, stream: userService.onUserUpdated,
builder: (context, _) { builder: (context, _) {
return appSession.currentUser.isUserDiscoveryEnabled return userService.currentUser.isUserDiscoveryEnabled
? const UserDiscoveryEnabledComp() ? const UserDiscoveryEnabledComp()
: const UserDiscoveryDisabledComp(); : const UserDiscoveryDisabledComp();
}, },

View file

@ -114,7 +114,7 @@ class _UserDiscoveryEnabledCompState extends State<UserDiscoveryEnabledComp> {
), ),
subtitle: subtitle:
(version != null && (version != null &&
(appSession.currentUser.isDeveloper || (userService.currentUser.isDeveloper ||
!kReleaseMode)) !kReleaseMode))
? Text( ? Text(
context.lang.userDiscoveryEnabledVersion( context.lang.userDiscoveryEnabledVersion(
@ -168,7 +168,7 @@ class _UserDiscoveryEnabledCompState extends State<UserDiscoveryEnabledComp> {
onTap: _disableUserDiscovery, onTap: _disableUserDiscovery,
), ),
if (_version != null && if (_version != null &&
(appSession.currentUser.isDeveloper || !kReleaseMode)) (userService.currentUser.isDeveloper || !kReleaseMode))
ListTile( ListTile(
title: Text( title: Text(
context.lang.userDiscoveryEnabledYourVersion( context.lang.userDiscoveryEnabledYourVersion(

View file

@ -23,14 +23,14 @@ class _UserDiscoverySettingsViewState extends State<UserDiscoverySettingsView> {
@override @override
void initState() { void initState() {
_minimumRequiredImagesExchanged = _minimumRequiredImagesExchanged =
appSession.currentUser.minimumRequiredImagesExchanged; userService.currentUser.minimumRequiredImagesExchanged;
_userDiscoveryThreshold = appSession.currentUser.userDiscoveryThreshold; _userDiscoveryThreshold = userService.currentUser.userDiscoveryThreshold;
super.initState(); super.initState();
} }
Future<void> _saveChanges() async { Future<void> _saveChanges() async {
final requiresNewInitialization = final requiresNewInitialization =
appSession.currentUser.userDiscoveryThreshold != userService.currentUser.userDiscoveryThreshold !=
_userDiscoveryThreshold; _userDiscoveryThreshold;
await updateUser((u) { await updateUser((u) {
@ -41,9 +41,9 @@ class _UserDiscoverySettingsViewState extends State<UserDiscoverySettingsView> {
if (requiresNewInitialization) { if (requiresNewInitialization) {
await UserDiscoveryService.initializeOrUpdate( await UserDiscoveryService.initializeOrUpdate(
threshold: appSession.currentUser.userDiscoveryThreshold, threshold: userService.currentUser.userDiscoveryThreshold,
minimumRequiredImagesExchanged: minimumRequiredImagesExchanged:
appSession.currentUser.minimumRequiredImagesExchanged, userService.currentUser.minimumRequiredImagesExchanged,
); );
} }
if (mounted) Navigator.pop(context); if (mounted) Navigator.pop(context);
@ -115,9 +115,9 @@ class _UserDiscoverySettingsViewState extends State<UserDiscoverySettingsView> {
height: 30, height: 30,
), ),
if (_minimumRequiredImagesExchanged != if (_minimumRequiredImagesExchanged !=
appSession.currentUser.minimumRequiredImagesExchanged || userService.currentUser.minimumRequiredImagesExchanged ||
_userDiscoveryThreshold != _userDiscoveryThreshold !=
appSession.currentUser.userDiscoveryThreshold) userService.currentUser.userDiscoveryThreshold)
Padding( Padding(
padding: const EdgeInsets.all(17), padding: const EdgeInsets.all(17),
child: FilledButton( child: FilledButton(

View file

@ -122,7 +122,7 @@ class _ModifyAvatarViewState extends State<ModifyAvatarView> {
onPopInvokedWithResult: (didPop, result) async { onPopInvokedWithResult: (didPop, result) async {
if (didPop) return; if (didPop) return;
if (_avatarMakerController.getJsonOptionsSync() != if (_avatarMakerController.getJsonOptionsSync() !=
appSession.currentUser.avatarJson) { userService.currentUser.avatarJson) {
// there where changes // there where changes
final shouldPop = await _showBackDialog() ?? false; final shouldPop = await _showBackDialog() ?? false;
if (context.mounted && shouldPop) { if (context.mounted && shouldPop) {

View file

@ -107,7 +107,7 @@ class _ProfileViewState extends State<ProfileView> {
title: Text(context.lang.settingsProfile), title: Text(context.lang.settingsProfile),
), ),
body: StreamBuilder<void>( body: StreamBuilder<void>(
stream: appSession.onUserUpdated, stream: userService.onUserUpdated,
builder: (context, _) { builder: (context, _) {
return ListView( return ListView(
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
@ -154,11 +154,11 @@ class _ProfileViewState extends State<ProfileView> {
), ),
), ),
text: context.lang.registerUsernameDecoration, text: context.lang.registerUsernameDecoration,
subtitle: Text(appSession.currentUser.username), subtitle: Text(userService.currentUser.username),
onTap: () async { onTap: () async {
final username = await showDisplayNameChangeDialog( final username = await showDisplayNameChangeDialog(
context, context,
appSession.currentUser.username, userService.currentUser.username,
context.lang.registerUsernameDecoration, context.lang.registerUsernameDecoration,
context.lang.registerUsernameDecoration, context.lang.registerUsernameDecoration,
maxLength: 12, maxLength: 12,
@ -177,11 +177,11 @@ class _ProfileViewState extends State<ProfileView> {
BetterListTile( BetterListTile(
icon: FontAwesomeIcons.userPen, icon: FontAwesomeIcons.userPen,
text: context.lang.settingsProfileEditDisplayName, text: context.lang.settingsProfileEditDisplayName,
subtitle: Text(appSession.currentUser.displayName), subtitle: Text(userService.currentUser.displayName),
onTap: () async { onTap: () async {
final displayName = await showDisplayNameChangeDialog( final displayName = await showDisplayNameChangeDialog(
context, context,
appSession.currentUser.displayName, userService.currentUser.displayName,
context.lang.settingsProfileEditDisplayName, context.lang.settingsProfileEditDisplayName,
context.lang.settingsProfileEditDisplayNameNew, context.lang.settingsProfileEditDisplayNameNew,
maxLength: 30, maxLength: 30,

View file

@ -48,14 +48,14 @@ class _SettingsMainViewState extends State<SettingsMainView> {
children: [ children: [
Text( Text(
substringBy( substringBy(
appSession.currentUser.displayName, userService.currentUser.displayName,
27, 27,
), ),
style: const TextStyle(fontSize: 20), style: const TextStyle(fontSize: 20),
textAlign: TextAlign.left, textAlign: TextAlign.left,
), ),
Text( Text(
appSession.currentUser.username, userService.currentUser.username,
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 14,
), ),
@ -131,7 +131,7 @@ class _SettingsMainViewState extends State<SettingsMainView> {
}); });
}, },
), ),
if (appSession.currentUser.isDeveloper) if (userService.currentUser.isDeveloper)
BetterListTile( BetterListTile(
icon: FontAwesomeIcons.code, icon: FontAwesomeIcons.code,
text: 'Developer Settings', text: 'Developer Settings',

View file

@ -290,7 +290,7 @@ class _PlanCardState extends State<PlanCard> {
var url = 'https://apps.apple.com/account/subscriptions'; var url = 'https://apps.apple.com/account/subscriptions';
if (Platform.isAndroid) { if (Platform.isAndroid) {
url = url =
'https://play.google.com/store/account/subscriptions?sku=${appSession.currentUser.subscriptionPlanIdStore}&package=eu.twonly'; 'https://play.google.com/store/account/subscriptions?sku=${userService.currentUser.subscriptionPlanIdStore}&package=eu.twonly';
} }
await launchUrl( await launchUrl(
Uri.parse(url), Uri.parse(url),

View file

@ -98,7 +98,7 @@ class _MemoriesPhotoSliderViewState extends State<MemoriesPhotoSliderView> {
final newMediaService = await initializeMediaUpload( final newMediaService = await initializeMediaUpload(
orgMediaService.mediaFile.type, orgMediaService.mediaFile.type,
appSession.currentUser.defaultShowTime, userService.currentUser.defaultShowTime,
); );
if (newMediaService == null) { if (newMediaService == null) {
Log.error('Could not create new mediaFIle'); Log.error('Could not create new mediaFIle');

View file

@ -52,7 +52,7 @@ void main() {
) )
..registerSingleton<UserService>(UserService()); ..registerSingleton<UserService>(UserService());
appSession.currentUser = UserData( userService.currentUser = UserData(
userId: 0x133337, userId: 0x133337,
username: 'test_user', username: 'test_user',
displayName: 'Test User', displayName: 'Test User',