import 'dart:collection'; import 'package:flutter/foundation.dart'; import 'package:logging/logging.dart'; import 'package:result_monad/result_monad.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../di_initialization.dart'; import '../friendica_client/friendica_client.dart'; import '../globals.dart'; import '../models/auth/credentials_intf.dart'; import '../models/auth/profile.dart'; import '../models/exec_error.dart'; import '../update_timer_initialization.dart'; import 'secrets_service.dart'; import 'status_service.dart'; class AccountsService extends ChangeNotifier { static final _logger = Logger('$AccountsService'); Profile? _currentProfile; final _loggedInProfiles = {}; final _loggedOutProfiles = {}; var _initializing = false; final SecretsService secretsService; AccountsService(this.secretsService); bool get loggedIn => _currentProfile != null; bool get initializing => _initializing; List get loggedInProfiles => UnmodifiableListView(_loggedInProfiles); List get loggedOutProfiles => UnmodifiableListView(_loggedOutProfiles); Profile get currentProfile => _currentProfile!; FutureResult initialize() async { final lastActiveProfile = await _getStoredLoginState(); _initializing = true; final result = await runCatchingAsync(() async { final initialProfiles = secretsService.profiles; for (final p in initialProfiles) { if (!p.loggedIn) { _loggedOutProfiles.add(p); continue; } final pr = await signIn(p.credentials, withNotification: false); if (pr.isSuccess) { final profile = pr.value; if (profile.id.isNotEmpty && profile.id == lastActiveProfile) { _currentProfile = profile; Future.delayed( const Duration(seconds: 10), () async => await executeUpdatesForProfile(profile), ); } } else { await signOut(p, withNotification: false); } } if (_currentProfile == null && _loggedInProfiles.isNotEmpty) { await setActiveProfile(_loggedInProfiles.first); } notifyListeners(); return Result.ok(loggedIn); }); _initializing = false; return result.execErrorCast(); } FutureResult signIn(ICredentials credentials, {bool withNotification = true}) async { ICredentials? credentialsCache; final result = await credentials.signIn().andThenAsync((signedInCredentials) async { final client = ProfileClient(Profile.credentialsOnly(signedInCredentials)); credentialsCache = signedInCredentials; getIt().setStatus( 'Getting user profile from ${signedInCredentials.serverName}'); return await client.getMyProfile(); }).andThenAsync((profileData) async { final loginProfile = Profile( credentials: credentialsCache!, username: profileData.name, serverName: credentialsCache!.serverName, avatar: profileData.avatarUrl, userId: profileData.id, loggedIn: true, ); getIt() .setStatus('Loaded user profile ${profileData.handle}'); if (_loggedInProfiles.isEmpty) { await setActiveProfile(loginProfile, withNotification: withNotification); } _loggedInProfiles.add(loginProfile); _loggedOutProfiles.remove(loginProfile); await secretsService.addOrUpdateProfile(loginProfile); await updateProfileDependencyInjectors(loginProfile); if (withNotification) { notifyListeners(); } return Result.ok(loginProfile); }); if (result.isFailure) { getIt().setStatus('Error signing in: ${result.error}'); _logger.severe('Error signing in: ${result.error}'); } return result.execErrorCast(); } Future signOut(Profile profile, {bool withNotification = true}) async { if (_currentProfile == profile) { await clearActiveProfile(withNotification: withNotification); } _loggedInProfiles.remove(profile); _loggedOutProfiles.add(profile.copyWithLoginUpdate(false)); await secretsService.addOrUpdateProfile(profile.copyWithLoginUpdate(false)); if (_loggedInProfiles.isNotEmpty) { setActiveProfile( _loggedInProfiles.first, withNotification: withNotification, ); } if (withNotification) { notifyListeners(); } } Future removeProfile(Profile profile, {bool withNotification = true}) async { if (_currentProfile == profile) { await clearActiveProfile(withNotification: withNotification); } _loggedInProfiles.remove(profile); _loggedOutProfiles.remove(profile); await secretsService.removeProfile(profile); if (_loggedInProfiles.isNotEmpty) { setActiveProfile( _loggedInProfiles.first, withNotification: withNotification, ); } if (withNotification) { notifyListeners(); } } Future clearActiveProfile({bool withNotification = true}) async { _currentProfile = null; if (withNotification) { notifyListeners(); } await _saveStoredLoginState(); } Future setActiveProfile(Profile profile, {bool withNotification = true}) async { _currentProfile = profile; if (withNotification) { notifyListeners(); } await _saveStoredLoginState(); } Future _saveStoredLoginState() async { final prefs = await SharedPreferences.getInstance(); await prefs.setString('active_profile_id', _currentProfile?.id ?? ''); } Future _getStoredLoginState() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('active_profile_id') ?? ''; } }