diff --git a/mobile/.vscode/settings.json b/mobile/.vscode/settings.json index 6a7cc4f016..c959187bb5 100644 --- a/mobile/.vscode/settings.json +++ b/mobile/.vscode/settings.json @@ -1,5 +1,5 @@ { - "dart.flutterSdkPath": ".fvm/versions/3.22.1", + "dart.flutterSdkPath": ".fvm/versions/3.22.3", "search.exclude": { "**/.fvm": true }, diff --git a/mobile/lib/pages/common/splash_screen.page.dart b/mobile/lib/pages/common/splash_screen.page.dart index 7ed601734b..d23e25372c 100644 --- a/mobile/lib/pages/common/splash_screen.page.dart +++ b/mobile/lib/pages/common/splash_screen.page.dart @@ -9,7 +9,6 @@ import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:logging/logging.dart'; -import 'package:openapi/api.dart'; @RoutePage() class SplashScreenPage extends HookConsumerWidget { @@ -19,45 +18,22 @@ class SplashScreenPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final apiService = ref.watch(apiServiceProvider); final serverUrl = Store.tryGet(StoreKey.serverUrl); + final endpoint = Store.tryGet(StoreKey.serverEndpoint); final accessToken = Store.tryGet(StoreKey.accessToken); final log = Logger("SplashScreenPage"); void performLoggingIn() async { - bool isSuccess = false; - bool deviceIsOffline = false; + bool isAuthSuccess = false; - if (accessToken != null && serverUrl != null) { - try { - // Resolve API server endpoint from user provided serverUrl - await apiService.resolveAndSetEndpoint(serverUrl); - } on ApiException catch (error, stackTrace) { - log.severe( - "Failed to resolve endpoint [ApiException]", - error, - stackTrace, - ); - // okay, try to continue anyway if offline - if (error.code == 503) { - deviceIsOffline = true; - log.warning("Device seems to be offline upon launch"); - } else { - log.severe("Failed to resolve endpoint", error); - } - } catch (error, stackTrace) { - log.severe( - "Failed to resolve endpoint [Catch All]", - error, - stackTrace, - ); - } + if (accessToken != null && serverUrl != null && endpoint != null) { + apiService.setEndpoint(endpoint); try { - isSuccess = await ref + isAuthSuccess = await ref .read(authenticationProvider.notifier) .setSuccessLoginInfo( accessToken: accessToken, serverUrl: serverUrl, - offlineLogin: deviceIsOffline, ); } catch (error, stackTrace) { log.severe( @@ -66,39 +42,35 @@ class SplashScreenPage extends HookConsumerWidget { stackTrace, ); } + } else { + isAuthSuccess = false; + log.severe( + 'Missing authentication, server, or endpoint info from the local store', + ); } - // If the device is offline and there is a currentUser stored locallly - // Proceed into the app - if (deviceIsOffline && Store.tryGet(StoreKey.currentUser) != null) { - context.replaceRoute(const TabControllerRoute()); - } else if (isSuccess) { - // If device was able to login through the internet successfully - final hasPermission = - await ref.read(galleryPermissionNotifier.notifier).hasPermission; - if (hasPermission) { - // Resume backup (if enable) then navigate - ref.watch(backupProvider.notifier).resumeBackup(); - } - context.replaceRoute(const TabControllerRoute()); - } else { + if (!isAuthSuccess) { log.severe( - 'Unable to login through offline or online methods - logging out completely', + 'Unable to login using offline or online methods - Logging out completely', ); - ref.read(authenticationProvider.notifier).logout(); - // User was unable to login through either offline or online methods context.replaceRoute(const LoginRoute()); + return; + } + + context.replaceRoute(const TabControllerRoute()); + + final hasPermission = + await ref.read(galleryPermissionNotifier.notifier).hasPermission; + if (hasPermission) { + // Resume backup (if enable) then navigate + ref.watch(backupProvider.notifier).resumeBackup(); } } useEffect( () { - if (serverUrl != null && accessToken != null) { - performLoggingIn(); - } else { - context.replaceRoute(const LoginRoute()); - } + performLoggingIn(); return null; }, [], diff --git a/mobile/lib/providers/authentication.provider.dart b/mobile/lib/providers/authentication.provider.dart index 3d98cb0e20..5846bb78cc 100644 --- a/mobile/lib/providers/authentication.provider.dart +++ b/mobile/lib/providers/authentication.provider.dart @@ -101,7 +101,7 @@ class AuthenticationNotifier extends StateNotifier { try { String? userEmail = Store.tryGet(StoreKey.currentUser)?.email; - _apiService.authenticationApi + await _apiService.authenticationApi .logout() .then((_) => log.info("Logout was successful for $userEmail")) .onError( @@ -156,7 +156,6 @@ class AuthenticationNotifier extends StateNotifier { Future setSuccessLoginInfo({ required String accessToken, required String serverUrl, - bool offlineLogin = false, }) async { _apiService.setAccessToken(accessToken); @@ -165,57 +164,56 @@ class AuthenticationNotifier extends StateNotifier { Store.tryGet(StoreKey.deviceId) ?? await FlutterUdid.consistentUdid; bool shouldChangePassword = false; - User? user; + User? user = Store.tryGet(StoreKey.currentUser); - bool retResult = false; - User? offlineUser = Store.tryGet(StoreKey.currentUser); - - // If the user is offline and there is a user saved on the device, - // if not try an online login - if (offlineLogin && offlineUser != null) { - user = offlineUser; - retResult = false; - } else { - UserAdminResponseDto? userResponseDto; - UserPreferencesResponseDto? userPreferences; - try { - userResponseDto = await _apiService.usersApi.getMyUser(); - userPreferences = await _apiService.usersApi.getMyPreferences(); - } on ApiException catch (error, stackTrace) { - _log.severe( - "Error getting user information from the server [API EXCEPTION]", - error, - stackTrace, - ); - if (error.innerException is SocketException) { - state = state.copyWith(isAuthenticated: true); - } - } catch (error, stackTrace) { - _log.severe( - "Error getting user information from the server [CATCH ALL]", - error, - stackTrace, - ); - } - - if (userResponseDto != null) { - Store.put(StoreKey.deviceId, deviceId); - Store.put(StoreKey.deviceIdHash, fastHash(deviceId)); - Store.put( - StoreKey.currentUser, - User.fromUserDto(userResponseDto, userPreferences), - ); - Store.put(StoreKey.serverUrl, serverUrl); - Store.put(StoreKey.accessToken, accessToken); - - shouldChangePassword = userResponseDto.shouldChangePassword; - user = User.fromUserDto(userResponseDto, userPreferences); - - retResult = true; - } else { - _log.severe("Unable to get user information from the server."); + UserAdminResponseDto? userResponse; + UserPreferencesResponseDto? userPreferences; + try { + final responses = await Future.wait([ + _apiService.usersApi.getMyUser(), + _apiService.usersApi.getMyPreferences(), + ]); + userResponse = responses[0] as UserAdminResponseDto; + userPreferences = responses[1] as UserPreferencesResponseDto; + } on ApiException catch (error, stackTrace) { + if (error.code == 401) { + _log.severe("Unauthorized access, token likely expired. Logging out."); return false; } + _log.severe( + "Error getting user information from the server [API EXCEPTION]", + stackTrace, + ); + } catch (error, stackTrace) { + _log.severe( + "Error getting user information from the server [CATCH ALL]", + error, + stackTrace, + ); + } + + // If the user information is successfully retrieved, update the store + // Due to the flow of the code, this will always happen on first login + if (userResponse != null) { + Store.put(StoreKey.deviceId, deviceId); + Store.put(StoreKey.deviceIdHash, fastHash(deviceId)); + Store.put( + StoreKey.currentUser, + User.fromUserDto(userResponse, userPreferences), + ); + Store.put(StoreKey.serverUrl, serverUrl); + Store.put(StoreKey.accessToken, accessToken); + + shouldChangePassword = userResponse.shouldChangePassword; + user = User.fromUserDto(userResponse, userPreferences); + } else { + _log.severe("Unable to get user information from the server."); + } + + // If the user is null, the login was not successful + // and we don't have a local copy of the user from a prior successful login + if (user == null) { + return false; } state = state.copyWith( @@ -229,7 +227,7 @@ class AuthenticationNotifier extends StateNotifier { deviceId: deviceId, ); - return retResult; + return true; } }