kopia lustrzana https://gitlab.com/mysocialportal/relatica
271 wiersze
7.8 KiB
Dart
271 wiersze
7.8 KiB
Dart
import 'dart:async';
|
|
import 'dart:collection';
|
|
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:logging/logging.dart';
|
|
import 'package:result_monad/result_monad.dart';
|
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|
import 'package:stack_trace/stack_trace.dart';
|
|
|
|
import '../models/auth/credentials_intf.dart';
|
|
import '../models/auth/profile.dart';
|
|
import '../models/exec_error.dart';
|
|
import 'background_updater_services.dart';
|
|
import 'connection_manager_services.dart';
|
|
import 'globals_services.dart';
|
|
import 'instance_info_services.dart';
|
|
import 'networking/friendica_profile_client_services.dart';
|
|
import 'status_service.dart';
|
|
|
|
part 'account_services.g.dart';
|
|
|
|
@Riverpod(keepAlive: true)
|
|
class LoggedInProfiles extends _$LoggedInProfiles {
|
|
@override
|
|
List<Profile> build() {
|
|
return UnmodifiableListView([]);
|
|
}
|
|
|
|
void add(Profile profile) {
|
|
state = [...state, profile];
|
|
}
|
|
|
|
void remove(Profile profile) {
|
|
final profilesWithUpdate = List<Profile>.from(state);
|
|
profilesWithUpdate.removeWhere((p) => p == profile);
|
|
state = UnmodifiableListView(profilesWithUpdate);
|
|
}
|
|
|
|
void clear() {
|
|
state = UnmodifiableListView([]);
|
|
}
|
|
}
|
|
|
|
@Riverpod(keepAlive: true)
|
|
class LoggedOutProfiles extends _$LoggedOutProfiles {
|
|
@override
|
|
List<Profile> build() {
|
|
return [];
|
|
}
|
|
|
|
void add(Profile profile) {
|
|
state = [...state, profile];
|
|
}
|
|
|
|
void remove(Profile profile) {
|
|
final profilesWithUpdate = List<Profile>.from(state);
|
|
profilesWithUpdate.removeWhere((p) => p == profile);
|
|
state = UnmodifiableListView(profilesWithUpdate);
|
|
}
|
|
|
|
void clear() {
|
|
state = UnmodifiableListView([]);
|
|
}
|
|
}
|
|
|
|
const _activeProfileIdKey = 'active_profile_id';
|
|
|
|
@Riverpod(keepAlive: true)
|
|
class ActiveProfile extends _$ActiveProfile {
|
|
bool get hasActiveProfile => state != Profile.empty();
|
|
|
|
@override
|
|
Profile build() {
|
|
return Profile.empty();
|
|
}
|
|
|
|
void setActiveProfile(Profile profile) {
|
|
ref.read(backgroundUpdatersProvider(state).notifier).stop();
|
|
state = profile;
|
|
_saveStoredLoginState();
|
|
ref.read(backgroundUpdatersProvider(state).notifier).reset();
|
|
}
|
|
|
|
void clear() {
|
|
state = Profile.empty();
|
|
_saveStoredLoginState();
|
|
}
|
|
|
|
void _saveStoredLoginState() {
|
|
final prefs = ref.read(sharedPreferencesProvider);
|
|
prefs.setString(_activeProfileIdKey, state.id);
|
|
}
|
|
}
|
|
|
|
@riverpod
|
|
bool loggedIn(Ref ref) => ref.watch(activeProfileProvider) != Profile.empty();
|
|
|
|
final _pmLogger = Logger('ProfileManagerProvider');
|
|
|
|
@riverpod
|
|
class CredentialSignin extends _$CredentialSignin {
|
|
@override
|
|
bool build(ICredentials credentials) {
|
|
return false;
|
|
}
|
|
|
|
FutureResult<Profile, ExecError> signIn(bool activateProfileOnSuccess) async {
|
|
final result =
|
|
await credentials.signIn(ref).andThenAsync((signedInCredentials) async {
|
|
ref.read(statusServiceProvider.notifier).setStatus(
|
|
'Getting user profile from ${signedInCredentials.serverName}');
|
|
return await ref.read(
|
|
myProfileProvider(Profile.credentialsOnly(signedInCredentials))
|
|
.future);
|
|
}).andThenAsync((profileResult) async {
|
|
final profileData = profileResult.$1;
|
|
final profile = profileResult.$2;
|
|
final loginProfile = Profile(
|
|
credentials: profile.credentials,
|
|
username: profileData.name,
|
|
serverName: profile.credentials.serverName,
|
|
avatar: profileData.avatarUrl,
|
|
userId: profileData.id,
|
|
loggedIn: true,
|
|
);
|
|
|
|
ref
|
|
.read(statusServiceProvider.notifier)
|
|
.setStatus('Loaded user profile ${profileData.handle}');
|
|
if (activateProfileOnSuccess) {
|
|
ref.read(activeProfileProvider.notifier).setActiveProfile(loginProfile);
|
|
}
|
|
ref.read(loggedInProfilesProvider.notifier).add(loginProfile);
|
|
ref.read(loggedOutProfilesProvider.notifier).remove(loginProfile);
|
|
await ref.read(secretsServiceProvider).addOrUpdateProfile(loginProfile);
|
|
await ref.read(connectionRepoInitProvider(loginProfile).future);
|
|
ref.read(instanceRulesManagerProvider(loginProfile));
|
|
await ref
|
|
.read(instanceInfoManagerProvider(loginProfile).notifier)
|
|
.update();
|
|
state = true;
|
|
return Result.ok(loginProfile);
|
|
});
|
|
|
|
if (result.isFailure) {
|
|
ref
|
|
.read(statusServiceProvider.notifier)
|
|
.setStatus('Error signing in: ${result.error}');
|
|
_pmLogger.severe(
|
|
'Error signing in $credentials: ${result.error}', Trace.current());
|
|
}
|
|
|
|
return result.execErrorCast();
|
|
}
|
|
}
|
|
|
|
@Riverpod(keepAlive: true)
|
|
class ProfileManager extends _$ProfileManager {
|
|
@override
|
|
bool build(Profile profile) {
|
|
return false;
|
|
}
|
|
|
|
FutureResult<Profile, ExecError> signIn(bool activateProfileOnSuccess) async {
|
|
return await ref
|
|
.read(credentialSigninProvider(profile.credentials).notifier)
|
|
.signIn(activateProfileOnSuccess);
|
|
}
|
|
|
|
Future signOut({bool withNotification = true}) async {
|
|
if (ref.read(activeProfileProvider) == profile) {
|
|
ref.read(activeProfileProvider.notifier).clear();
|
|
}
|
|
final updatedProfile = profile.copyWithLoginUpdate(false);
|
|
ref.read(loggedInProfilesProvider.notifier).remove(profile);
|
|
ref.read(loggedOutProfilesProvider.notifier).add(updatedProfile);
|
|
ref.read(secretsServiceProvider).addOrUpdateProfile(updatedProfile);
|
|
|
|
if (ref.read(loggedInProfilesProvider).isNotEmpty) {
|
|
ref.read(activeProfileProvider.notifier).setActiveProfile(
|
|
ref.read(loggedInProfilesProvider).first,
|
|
);
|
|
}
|
|
|
|
if (withNotification) {
|
|
ref.notifyListeners();
|
|
}
|
|
}
|
|
}
|
|
|
|
@riverpod
|
|
class AccountServicesInitializer extends _$AccountServicesInitializer {
|
|
var initialized = false;
|
|
|
|
@override
|
|
bool build() {
|
|
initialized = false;
|
|
return false;
|
|
}
|
|
|
|
FutureResult<bool, ExecError> initialize() async {
|
|
var initializing = state;
|
|
if (initialized) {
|
|
return Result.ok(true);
|
|
}
|
|
|
|
if (initializing) {
|
|
return buildErrorResult(
|
|
type: ErrorType.localError, message: 'Already initializing');
|
|
}
|
|
|
|
final lastActiveProfile = _getStoredLoginState();
|
|
final secretsService = ref.read(secretsServiceProvider);
|
|
|
|
final result = await runCatchingAsync(() async {
|
|
final initialProfiles = secretsService.profiles;
|
|
for (final p in initialProfiles) {
|
|
if (!p.loggedIn) {
|
|
ref.read(loggedOutProfilesProvider.notifier).add(p);
|
|
continue;
|
|
}
|
|
|
|
final pr =
|
|
await ref.read(profileManagerProvider(p).notifier).signIn(false);
|
|
if (pr.isSuccess) {
|
|
final profile = pr.value;
|
|
if (profile.id.isNotEmpty && profile.id == lastActiveProfile) {
|
|
ref.read(activeProfileProvider.notifier).setActiveProfile(profile);
|
|
}
|
|
} else {
|
|
await ref.read(profileManagerProvider(p).notifier).signOut();
|
|
}
|
|
}
|
|
|
|
if (!ref.read(activeProfileProvider.notifier).hasActiveProfile &&
|
|
ref.read(loggedInProfilesProvider).isNotEmpty) {
|
|
final firstProfile = ref.read(loggedOutProfilesProvider).first;
|
|
ref.read(activeProfileProvider.notifier).setActiveProfile(firstProfile);
|
|
}
|
|
|
|
return Result.ok(
|
|
ref.read(activeProfileProvider.notifier).hasActiveProfile);
|
|
});
|
|
|
|
initialized = true;
|
|
state = false;
|
|
ref.notifyListeners();
|
|
return result.execErrorCast();
|
|
}
|
|
|
|
String _getStoredLoginState() {
|
|
final prefs = ref.read(sharedPreferencesProvider);
|
|
return prefs.getString('active_profile_id') ?? '';
|
|
}
|
|
}
|
|
|
|
@riverpod
|
|
class AccountClearer extends _$AccountClearer {
|
|
@override
|
|
bool build() {
|
|
return true;
|
|
}
|
|
|
|
void clearAllProfiles() {
|
|
ref.read(loggedInProfilesProvider.notifier).clear();
|
|
ref.read(loggedOutProfilesProvider.notifier).clear();
|
|
ref.read(activeProfileProvider.notifier).clear();
|
|
ref.read(secretsServiceProvider).clearCredentials();
|
|
}
|
|
}
|