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 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pshared/models/auth/state.dart';
|
||||||
|
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
@@ -26,12 +27,15 @@ class AccountProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
static String get currentUserRef => Constants.nilObjectRef;
|
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.
|
// The resource now wraps our Account? state along with its loading/error state.
|
||||||
Resource<Account?> _resource = Resource(data: null);
|
Resource<Account?> _resource = Resource(data: null);
|
||||||
Resource<Account?> get resource => _resource;
|
Resource<Account?> get resource => _resource;
|
||||||
late LocaleProvider _localeProvider;
|
late LocaleProvider _localeProvider;
|
||||||
PendingLogin? _pendingLogin;
|
PendingLogin? _pendingLogin;
|
||||||
Future<void>? _restoreFuture;
|
|
||||||
|
|
||||||
Account? get account => _resource.data;
|
Account? get account => _resource.data;
|
||||||
PendingLogin? get pendingLogin => _pendingLogin;
|
PendingLogin? get pendingLogin => _pendingLogin;
|
||||||
@@ -89,6 +93,7 @@ class AccountProvider extends ChangeNotifier {
|
|||||||
locale: locale,
|
locale: locale,
|
||||||
));
|
));
|
||||||
if (outcome.account != null) {
|
if (outcome.account != null) {
|
||||||
|
_authState = AuthState.ready;
|
||||||
_setResource(Resource(data: outcome.account, isLoading: false));
|
_setResource(Resource(data: outcome.account, isLoading: false));
|
||||||
_pickupLocale(outcome.account!.locale);
|
_pickupLocale(outcome.account!.locale);
|
||||||
} else {
|
} else {
|
||||||
@@ -98,10 +103,12 @@ class AccountProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
await VerificationService.requestLoginCode(pending);
|
await VerificationService.requestLoginCode(pending);
|
||||||
_pendingLogin = pending;
|
_pendingLogin = pending;
|
||||||
|
_authState = AuthState.idle;
|
||||||
_setResource(_resource.copyWith(isLoading: false));
|
_setResource(_resource.copyWith(isLoading: false));
|
||||||
}
|
}
|
||||||
return outcome;
|
return outcome;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
_authState = AuthState.error;
|
||||||
_setResource(_resource.copyWith(isLoading: false, error: toException(e)));
|
_setResource(_resource.copyWith(isLoading: false, error: toException(e)));
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
@@ -109,6 +116,7 @@ class AccountProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
void completePendingLogin(Account account) {
|
void completePendingLogin(Account account) {
|
||||||
_pendingLogin = null;
|
_pendingLogin = null;
|
||||||
|
_authState = AuthState.ready;
|
||||||
_setResource(Resource(data: account, isLoading: false, error: null));
|
_setResource(Resource(data: account, isLoading: false, error: null));
|
||||||
_pickupLocale(account.locale);
|
_pickupLocale(account.locale);
|
||||||
}
|
}
|
||||||
@@ -116,13 +124,17 @@ class AccountProvider extends ChangeNotifier {
|
|||||||
Future<bool> isAuthorizationStored() async => AuthorizationService.isAuthorizationStored();
|
Future<bool> isAuthorizationStored() async => AuthorizationService.isAuthorizationStored();
|
||||||
|
|
||||||
Future<Account?> restore() async {
|
Future<Account?> restore() async {
|
||||||
|
_authState = AuthState.checking;
|
||||||
|
notifyListeners();
|
||||||
_setResource(_resource.copyWith(isLoading: true, error: null));
|
_setResource(_resource.copyWith(isLoading: true, error: null));
|
||||||
try {
|
try {
|
||||||
final acc = await AccountService.restore();
|
final acc = await AccountService.restore();
|
||||||
|
_authState = AuthState.ready;
|
||||||
_setResource(Resource(data: acc, isLoading: false));
|
_setResource(Resource(data: acc, isLoading: false));
|
||||||
_pickupLocale(acc.locale);
|
_pickupLocale(acc.locale);
|
||||||
return acc;
|
return acc;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
_authState = AuthState.error;
|
||||||
_setResource(_resource.copyWith(isLoading: false, error: toException(e)));
|
_setResource(_resource.copyWith(isLoading: false, error: toException(e)));
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
@@ -154,11 +166,14 @@ class AccountProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> logout() async {
|
Future<void> logout() async {
|
||||||
|
_authState = AuthState.empty;
|
||||||
_setResource(_resource.copyWith(isLoading: true, error: null));
|
_setResource(_resource.copyWith(isLoading: true, error: null));
|
||||||
|
_pendingLogin = null;
|
||||||
try {
|
try {
|
||||||
await AccountService.logout();
|
await AccountService.logout();
|
||||||
_setResource(Resource(data: null, isLoading: false));
|
_setResource(Resource(data: null, isLoading: false));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
_authState = AuthState.error;
|
||||||
_setResource(_resource.copyWith(isLoading: false, error: toException(e)));
|
_setResource(_resource.copyWith(isLoading: false, error: toException(e)));
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
@@ -235,10 +250,14 @@ class AccountProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> restoreIfPossible() {
|
Future<void> restoreIfPossible() async {
|
||||||
return _restoreFuture ??= AuthorizationService.isAuthorizationStored().then<void>((hasAuth) async {
|
if (_authState == AuthState.checking || _authState == AuthState.ready) return;
|
||||||
if (!hasAuth) return;
|
final hasAuth = await AuthorizationService.isAuthorizationStored();
|
||||||
|
if (!hasAuth) {
|
||||||
|
_authState = AuthState.empty;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
await restore();
|
await restore();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,4 +80,12 @@ class OrganizationsProvider extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
return true;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,10 @@ class PermissionsProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
/// Load the [UserAccess] for the current venue.
|
/// 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);
|
_userAccess = _userAccess.copyWith(isLoading: true, error: null);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|
||||||
@@ -179,6 +183,10 @@ class PermissionsProvider extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> resetAsync() async {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
bool canRead(ResourceType r, {Object? objectRef}) => canAccessResource(r, action: perm.Action.read, objectRef: objectRef);
|
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 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);
|
bool canDelete(ResourceType r, {Object? objectRef}) => canAccessResource(r, action: perm.Action.delete, objectRef: objectRef);
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:logging/logging.dart';
|
|
||||||
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/models/auth/state.dart';
|
||||||
import 'package:pshared/provider/account.dart';
|
import 'package:pshared/provider/account.dart';
|
||||||
|
|
||||||
import 'package:pweb/app/router/pages.dart';
|
import 'package:pweb/app/router/pages.dart';
|
||||||
@@ -12,57 +11,63 @@ import 'package:pweb/widgets/error/snackbar.dart';
|
|||||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||||
|
|
||||||
|
|
||||||
class AccountLoader extends StatelessWidget {
|
class AccountLoader extends StatefulWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
const AccountLoader({super.key, required this.child});
|
const AccountLoader({super.key, required this.child});
|
||||||
|
|
||||||
static final _logger = Logger('loader.account');
|
@override
|
||||||
|
State<AccountLoader> createState() => _AccountLoaderState();
|
||||||
|
}
|
||||||
|
|
||||||
void _notifyErrorAndRedirect(BuildContext context, Object error) {
|
class _AccountLoaderState extends State<AccountLoader> {
|
||||||
|
AuthState? _handledState;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (!context.mounted) return;
|
if (!mounted) return;
|
||||||
|
Provider.of<AccountProvider>(context, listen: false).restoreIfPossible();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleSideEffects(AccountProvider provider) {
|
||||||
|
if (_handledState == provider.authState) return;
|
||||||
|
_handledState = provider.authState;
|
||||||
|
|
||||||
|
void goToLogin() {
|
||||||
|
if (!mounted) return;
|
||||||
|
navigateAndReplace(context, Pages.login);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (provider.authState) {
|
||||||
|
case AuthState.error:
|
||||||
|
final error = provider.error ?? Exception('Authorization failed');
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (!mounted) return;
|
||||||
postNotifyUserOfErrorX(
|
postNotifyUserOfErrorX(
|
||||||
context: context,
|
context: context,
|
||||||
errorSituation: AppLocalizations.of(context)!.errorLogin,
|
errorSituation: AppLocalizations.of(context)!.errorLogin,
|
||||||
exception: error,
|
exception: error,
|
||||||
);
|
);
|
||||||
navigateAndReplace(context, Pages.login);
|
goToLogin();
|
||||||
});
|
});
|
||||||
|
break;
|
||||||
|
case AuthState.empty:
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) => goToLogin());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _restoreAndHandleResult(BuildContext context, AccountProvider provider) {
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
|
||||||
try {
|
|
||||||
await provider.restoreIfPossible();
|
|
||||||
} catch (error, stack) {
|
|
||||||
_logger.warning('Account restore failed: $error', error, stack);
|
|
||||||
}
|
|
||||||
if (!context.mounted) return;
|
|
||||||
final latest = Provider.of<AccountProvider>(context, listen: false);
|
|
||||||
if (latest.account != null) return;
|
|
||||||
if (latest.error != null) {
|
|
||||||
_notifyErrorAndRedirect(context, latest.error!);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!latest.isLoading) {
|
|
||||||
navigateAndReplace(context, Pages.login);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Consumer<AccountProvider>(builder: (context, provider, _) {
|
return Consumer<AccountProvider>(builder: (context, provider, _) {
|
||||||
if (provider.account != null) return child;
|
_handleSideEffects(provider);
|
||||||
if (provider.error != null) {
|
if (provider.authState == AuthState.ready && provider.account != null) {
|
||||||
_notifyErrorAndRedirect(context, provider.error!);
|
return widget.child;
|
||||||
return const Center(child: CircularProgressIndicator());
|
|
||||||
}
|
}
|
||||||
if (!provider.isLoading) {
|
|
||||||
_restoreAndHandleResult(context, provider);
|
|
||||||
}
|
|
||||||
if (provider.isLoading) return const Center(child: CircularProgressIndicator());
|
|
||||||
// In case of error or missing account we show loader while side effects handle navigation.
|
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,6 @@ import 'package:provider/provider.dart';
|
|||||||
|
|
||||||
import 'package:pshared/provider/organizations.dart';
|
import 'package:pshared/provider/organizations.dart';
|
||||||
|
|
||||||
import 'package:pweb/app/router/pages.dart';
|
|
||||||
import 'package:pweb/widgets/error/snackbar.dart';
|
|
||||||
|
|
||||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||||
|
|
||||||
|
|
||||||
@@ -19,12 +16,20 @@ class OrganizationLoader extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) => Consumer<OrganizationsProvider>(builder: (context, provider, _) {
|
Widget build(BuildContext context) => Consumer<OrganizationsProvider>(builder: (context, provider, _) {
|
||||||
if (provider.isLoading) return const Center(child: CircularProgressIndicator());
|
if (provider.isLoading) return const Center(child: CircularProgressIndicator());
|
||||||
if (provider.error != null) {
|
if (provider.error != null) {
|
||||||
postNotifyUserOfErrorX(
|
final loc = AppLocalizations.of(context)!;
|
||||||
context: context,
|
return Center(
|
||||||
errorSituation: AppLocalizations.of(context)!.errorLogin,
|
child: Column(
|
||||||
exception: provider.error!,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(loc.errorLogin),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: provider.load,
|
||||||
|
child: Text(loc.retry),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
navigateAndReplace(context, Pages.login);
|
|
||||||
}
|
}
|
||||||
if ((provider.error == null) && (!provider.isOrganizationSet)) {
|
if ((provider.error == null) && (!provider.isOrganizationSet)) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
|||||||
@@ -5,9 +5,6 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:pshared/provider/account.dart';
|
import 'package:pshared/provider/account.dart';
|
||||||
import 'package:pshared/provider/permissions.dart';
|
import 'package:pshared/provider/permissions.dart';
|
||||||
|
|
||||||
import 'package:pweb/app/router/pages.dart';
|
|
||||||
import 'package:pweb/widgets/error/snackbar.dart';
|
|
||||||
|
|
||||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||||
|
|
||||||
|
|
||||||
@@ -15,26 +12,38 @@ class PermissionsLoader extends StatelessWidget {
|
|||||||
final Widget child;
|
final Widget child;
|
||||||
const PermissionsLoader({super.key, required this.child});
|
const PermissionsLoader({super.key, required this.child});
|
||||||
|
|
||||||
void _notifyError(BuildContext context, Exception exception) {
|
void _triggerLoadIfNeeded(PermissionsProvider provider) {
|
||||||
|
if (!provider.isLoading && !provider.isReady) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
postNotifyUserOfErrorX(
|
if (!provider.isLoading && !provider.isReady) {
|
||||||
context: context,
|
provider.load();
|
||||||
errorSituation: AppLocalizations.of(context)!.errorLogin,
|
}
|
||||||
exception: exception,
|
|
||||||
);
|
|
||||||
navigateAndReplace(context, Pages.login);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Consumer2<PermissionsProvider, AccountProvider>(
|
return Consumer2<PermissionsProvider, AccountProvider>(
|
||||||
builder: (context, provider, accountProvider, _) {
|
builder: (context, provider, _accountProvider, _) {
|
||||||
if (provider.isLoading) {
|
|
||||||
return const Center(child: CircularProgressIndicator());
|
|
||||||
}
|
|
||||||
if (provider.error != null) {
|
if (provider.error != null) {
|
||||||
_notifyError(context, provider.error!);
|
final loc = AppLocalizations.of(context)!;
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(loc.errorLogin),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: provider.load,
|
||||||
|
child: Text(loc.retry),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_triggerLoadIfNeeded(provider);
|
||||||
|
if (provider.isLoading || !provider.isReady) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
}
|
}
|
||||||
return child;
|
return child;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class TwoFactorProvider extends ChangeNotifier {
|
|||||||
bool _hasError = false;
|
bool _hasError = false;
|
||||||
bool _verificationSuccess = false;
|
bool _verificationSuccess = false;
|
||||||
String? _errorMessage;
|
String? _errorMessage;
|
||||||
|
String? _currentPendingToken;
|
||||||
|
|
||||||
bool get isSubmitting => _isSubmitting;
|
bool get isSubmitting => _isSubmitting;
|
||||||
bool get hasError => _hasError;
|
bool get hasError => _hasError;
|
||||||
@@ -26,6 +27,12 @@ class TwoFactorProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
void update(AccountProvider accountProvider) {
|
void update(AccountProvider accountProvider) {
|
||||||
_accountProvider = accountProvider;
|
_accountProvider = accountProvider;
|
||||||
|
final pending = accountProvider.pendingLogin;
|
||||||
|
final token = pending?.pendingToken.token;
|
||||||
|
if (token != _currentPendingToken || accountProvider.account == null) {
|
||||||
|
_resetState();
|
||||||
|
_currentPendingToken = token;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> submitCode(String code) async {
|
Future<void> submitCode(String code) async {
|
||||||
@@ -46,6 +53,7 @@ class TwoFactorProvider extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
_accountProvider.completePendingLogin(account);
|
_accountProvider.completePendingLogin(account);
|
||||||
_verificationSuccess = true;
|
_verificationSuccess = true;
|
||||||
|
_currentPendingToken = null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_hasError = true;
|
_hasError = true;
|
||||||
_errorMessage = e.toString();
|
_errorMessage = e.toString();
|
||||||
@@ -71,4 +79,17 @@ class TwoFactorProvider extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
_resetState();
|
||||||
|
_currentPendingToken = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _resetState() {
|
||||||
|
_isSubmitting = false;
|
||||||
|
_hasError = false;
|
||||||
|
_errorMessage = null;
|
||||||
|
_verificationSuccess = false;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,31 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:pshared/provider/account.dart';
|
import 'package:pshared/provider/account.dart';
|
||||||
|
import 'package:pshared/provider/organizations.dart';
|
||||||
import 'package:pshared/provider/permissions.dart';
|
import 'package:pshared/provider/permissions.dart';
|
||||||
|
|
||||||
import 'package:pweb/app/router/pages.dart';
|
import 'package:pweb/app/router/pages.dart';
|
||||||
|
import 'package:pweb/providers/two_factor.dart';
|
||||||
|
|
||||||
|
|
||||||
void logoutUtil(BuildContext context) {
|
Future<void> logoutUtil(BuildContext context) async {
|
||||||
context.read<AccountProvider>().logout();
|
final accountProvider = context.read<AccountProvider>();
|
||||||
context.read<PermissionsProvider>().reset();
|
final permissionsProvider = context.read<PermissionsProvider>();
|
||||||
navigateAndReplace(context, Pages.login);
|
final organizationsProvider = context.read<OrganizationsProvider>();
|
||||||
|
final twoFactorProvider = context.read<TwoFactorProvider>();
|
||||||
|
await accountProvider.logout();
|
||||||
|
permissionsProvider.reset();
|
||||||
|
await organizationsProvider.reset();
|
||||||
|
twoFactorProvider.reset();
|
||||||
|
|
||||||
|
final router = GoRouter.of(context);
|
||||||
|
final loginPath = routerPage(Pages.login);
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (!context.mounted) return;
|
||||||
|
router.go(loginPath);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class PayoutAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
final Widget title;
|
final Widget title;
|
||||||
final VoidCallback onAddFundsPressed;
|
final VoidCallback onAddFundsPressed;
|
||||||
final List<Widget>? actions;
|
final List<Widget>? actions;
|
||||||
final VoidCallback? onLogout;
|
final Future<void> Function()? onLogout;
|
||||||
final String? avatarUrl;
|
final String? avatarUrl;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ class ProfileAvatar extends StatelessWidget {
|
|||||||
const ProfileAvatar({super.key, this.avatarUrl, this.onLogout});
|
const ProfileAvatar({super.key, this.avatarUrl, this.onLogout});
|
||||||
|
|
||||||
final String? avatarUrl;
|
final String? avatarUrl;
|
||||||
final VoidCallback? onLogout;
|
final Future<void> Function()? onLogout;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => PopupMenuButton<int>(
|
Widget build(BuildContext context) => PopupMenuButton<int>(
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:pweb/utils/logout.dart';
|
||||||
|
|
||||||
import 'package:pshared/provider/account.dart';
|
|
||||||
|
|
||||||
import 'package:pweb/app/router/pages.dart';
|
|
||||||
|
|
||||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||||
|
|
||||||
@@ -23,10 +19,8 @@ class LogoutTile extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _logout(BuildContext context) {
|
Future<void> _logout(BuildContext context) async {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
final accountProvider = Provider.of<AccountProvider>(context, listen: false);
|
await logoutUtil(context);
|
||||||
accountProvider.logout();
|
|
||||||
navigateAndReplace(context, Pages.login);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user