A
This commit is contained in:
7
frontend/pshared/lib/models/auth/state.dart
Normal file
7
frontend/pshared/lib/models/auth/state.dart
Normal file
@@ -0,0 +1,7 @@
|
||||
enum AuthState {
|
||||
idle,
|
||||
checking,
|
||||
ready,
|
||||
empty,
|
||||
error,
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pshared/models/auth/state.dart';
|
||||
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
@@ -26,12 +27,15 @@ class AccountProvider extends ChangeNotifier {
|
||||
|
||||
static String get currentUserRef => Constants.nilObjectRef;
|
||||
|
||||
/// Auth lifecycle state to avoid multiple ad-hoc flags.
|
||||
AuthState _authState = AuthState.idle;
|
||||
AuthState get authState => _authState;
|
||||
|
||||
// The resource now wraps our Account? state along with its loading/error state.
|
||||
Resource<Account?> _resource = Resource(data: null);
|
||||
Resource<Account?> get resource => _resource;
|
||||
late LocaleProvider _localeProvider;
|
||||
PendingLogin? _pendingLogin;
|
||||
Future<void>? _restoreFuture;
|
||||
|
||||
Account? get account => _resource.data;
|
||||
PendingLogin? get pendingLogin => _pendingLogin;
|
||||
@@ -89,6 +93,7 @@ class AccountProvider extends ChangeNotifier {
|
||||
locale: locale,
|
||||
));
|
||||
if (outcome.account != null) {
|
||||
_authState = AuthState.ready;
|
||||
_setResource(Resource(data: outcome.account, isLoading: false));
|
||||
_pickupLocale(outcome.account!.locale);
|
||||
} else {
|
||||
@@ -98,10 +103,12 @@ class AccountProvider extends ChangeNotifier {
|
||||
}
|
||||
await VerificationService.requestLoginCode(pending);
|
||||
_pendingLogin = pending;
|
||||
_authState = AuthState.idle;
|
||||
_setResource(_resource.copyWith(isLoading: false));
|
||||
}
|
||||
return outcome;
|
||||
} catch (e) {
|
||||
_authState = AuthState.error;
|
||||
_setResource(_resource.copyWith(isLoading: false, error: toException(e)));
|
||||
rethrow;
|
||||
}
|
||||
@@ -109,6 +116,7 @@ class AccountProvider extends ChangeNotifier {
|
||||
|
||||
void completePendingLogin(Account account) {
|
||||
_pendingLogin = null;
|
||||
_authState = AuthState.ready;
|
||||
_setResource(Resource(data: account, isLoading: false, error: null));
|
||||
_pickupLocale(account.locale);
|
||||
}
|
||||
@@ -116,13 +124,17 @@ class AccountProvider extends ChangeNotifier {
|
||||
Future<bool> isAuthorizationStored() async => AuthorizationService.isAuthorizationStored();
|
||||
|
||||
Future<Account?> restore() async {
|
||||
_authState = AuthState.checking;
|
||||
notifyListeners();
|
||||
_setResource(_resource.copyWith(isLoading: true, error: null));
|
||||
try {
|
||||
final acc = await AccountService.restore();
|
||||
_authState = AuthState.ready;
|
||||
_setResource(Resource(data: acc, isLoading: false));
|
||||
_pickupLocale(acc.locale);
|
||||
return acc;
|
||||
} catch (e) {
|
||||
_authState = AuthState.error;
|
||||
_setResource(_resource.copyWith(isLoading: false, error: toException(e)));
|
||||
rethrow;
|
||||
}
|
||||
@@ -154,11 +166,14 @@ class AccountProvider extends ChangeNotifier {
|
||||
}
|
||||
|
||||
Future<void> logout() async {
|
||||
_authState = AuthState.empty;
|
||||
_setResource(_resource.copyWith(isLoading: true, error: null));
|
||||
_pendingLogin = null;
|
||||
try {
|
||||
await AccountService.logout();
|
||||
_setResource(Resource(data: null, isLoading: false));
|
||||
} catch (e) {
|
||||
_authState = AuthState.error;
|
||||
_setResource(_resource.copyWith(isLoading: false, error: toException(e)));
|
||||
rethrow;
|
||||
}
|
||||
@@ -235,10 +250,14 @@ class AccountProvider extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> restoreIfPossible() {
|
||||
return _restoreFuture ??= AuthorizationService.isAuthorizationStored().then<void>((hasAuth) async {
|
||||
if (!hasAuth) return;
|
||||
await restore();
|
||||
});
|
||||
Future<void> restoreIfPossible() async {
|
||||
if (_authState == AuthState.checking || _authState == AuthState.ready) return;
|
||||
final hasAuth = await AuthorizationService.isAuthorizationStored();
|
||||
if (!hasAuth) {
|
||||
_authState = AuthState.empty;
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
await restore();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,4 +80,12 @@ class OrganizationsProvider extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> reset() async {
|
||||
_resource = Resource(data: []);
|
||||
_currentOrg = null;
|
||||
notifyListeners();
|
||||
// Best-effort cleanup of stored selection to avoid using stale org on next login.
|
||||
await SecureStorageService.delete(Constants.currentOrgKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,11 @@ class PermissionsProvider extends ChangeNotifier {
|
||||
}
|
||||
|
||||
/// Load the [UserAccess] for the current venue.
|
||||
Future<UserAccess?> load() async {
|
||||
Future<UserAccess?> load() async {
|
||||
if (!_organizations.isOrganizationSet) {
|
||||
// Organization is not ready yet; skip loading until it becomes available.
|
||||
return _userAccess.data;
|
||||
}
|
||||
_userAccess = _userAccess.copyWith(isLoading: true, error: null);
|
||||
notifyListeners();
|
||||
|
||||
@@ -179,6 +183,10 @@ class PermissionsProvider extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> resetAsync() async {
|
||||
reset();
|
||||
}
|
||||
|
||||
bool canRead(ResourceType r, {Object? objectRef}) => canAccessResource(r, action: perm.Action.read, objectRef: objectRef);
|
||||
bool canUpdate(ResourceType r, {Object? objectRef}) => canAccessResource(r, action: perm.Action.update, objectRef: objectRef);
|
||||
bool canDelete(ResourceType r, {Object? objectRef}) => canAccessResource(r, action: perm.Action.delete, objectRef: objectRef);
|
||||
|
||||
Reference in New Issue
Block a user