From 56ce747ffc61723aac2730ba3f9af54d601cd734 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 21 Nov 2022 05:29:43 -0600 Subject: [PATCH] fix(mobile): freeze on splash screen due to accessing bad state (#998) --- .../providers/authentication.provider.dart | 11 +- mobile/lib/modules/login/ui/login_form.dart | 1 + mobile/lib/shared/views/splash_screen.dart | 34 +++-- .../openapi/lib/model/asset_response_dto.dart | 127 ++++++++++-------- .../openapi/lib/model/user_response_dto.dart | 94 +++++++------ 5 files changed, 150 insertions(+), 117 deletions(-) diff --git a/mobile/lib/modules/login/providers/authentication.provider.dart b/mobile/lib/modules/login/providers/authentication.provider.dart index 89202f838a..1864d0e024 100644 --- a/mobile/lib/modules/login/providers/authentication.provider.dart +++ b/mobile/lib/modules/login/providers/authentication.provider.dart @@ -90,6 +90,7 @@ class AuthenticationNotifier extends StateNotifier { return setSuccessLoginInfo( accessToken: loginResponse.accessToken, + serverUrl: serverEndpoint, isSavedLoginInfo: isSavedLoginInfo, ); } catch (e) { @@ -159,16 +160,18 @@ class AuthenticationNotifier extends StateNotifier { Future setSuccessLoginInfo({ required String accessToken, + required String serverUrl, required bool isSavedLoginInfo, }) async { - Hive.box(userInfoBox).put(accessTokenKey, accessToken); - _apiService.setAccessToken(accessToken); var userResponseDto = await _apiService.userApi.getMyUserInfo(); if (userResponseDto != null) { + var userInfoHiveBox = await Hive.openBox(userInfoBox); var deviceInfo = await _deviceInfoService.getDeviceInfo(); - Hive.box(userInfoBox).put(deviceIdKey, deviceInfo["deviceId"]); + userInfoHiveBox.put(deviceIdKey, deviceInfo["deviceId"]); + userInfoHiveBox.put(accessTokenKey, accessToken); + userInfoHiveBox.put(serverEndpointKey, serverUrl); state = state.copyWith( isAuthenticated: true, @@ -191,7 +194,7 @@ class AuthenticationNotifier extends StateNotifier { email: "", password: "", isSaveLogin: true, - serverUrl: Hive.box(userInfoBox).get(serverEndpointKey), + serverUrl: serverUrl, accessToken: accessToken, ), ); diff --git a/mobile/lib/modules/login/ui/login_form.dart b/mobile/lib/modules/login/ui/login_form.dart index 82f723f01e..a1c32f80d7 100644 --- a/mobile/lib/modules/login/ui/login_form.dart +++ b/mobile/lib/modules/login/ui/login_form.dart @@ -380,6 +380,7 @@ class OAuthLoginButton extends ConsumerWidget { .setSuccessLoginInfo( accessToken: loginResponseDto.accessToken, isSavedLoginInfo: isSavedLoginInfo, + serverUrl: serverEndpointController.text, ); if (isSuccess) { diff --git a/mobile/lib/shared/views/splash_screen.dart b/mobile/lib/shared/views/splash_screen.dart index b62e5d6b09..e659c1241a 100644 --- a/mobile/lib/shared/views/splash_screen.dart +++ b/mobile/lib/shared/views/splash_screen.dart @@ -20,22 +20,28 @@ class SplashScreenPage extends HookConsumerWidget { Hive.box(hiveLoginInfoBox).get(savedLoginInfoKey); void performLoggingIn() async { - if (loginInfo != null) { - // Make sure API service is initialized - apiService.setEndpoint(loginInfo.serverUrl); + try { + if (loginInfo != null) { + // Make sure API service is initialized + apiService.setEndpoint(loginInfo.serverUrl); - var isSuccess = - await ref.read(authenticationProvider.notifier).setSuccessLoginInfo( - accessToken: loginInfo.accessToken, - isSavedLoginInfo: true, - ); - if (isSuccess) { - // Resume backup (if enable) then navigate - ref.watch(backupProvider.notifier).resumeBackup(); - AutoRouter.of(context).replace(const TabControllerRoute()); - } else { - AutoRouter.of(context).replace(const LoginRoute()); + var isSuccess = await ref + .read(authenticationProvider.notifier) + .setSuccessLoginInfo( + accessToken: loginInfo.accessToken, + isSavedLoginInfo: true, + serverUrl: loginInfo.serverUrl, + ); + if (isSuccess) { + // Resume backup (if enable) then navigate + ref.watch(backupProvider.notifier).resumeBackup(); + AutoRouter.of(context).replace(const TabControllerRoute()); + } else { + AutoRouter.of(context).replace(const LoginRoute()); + } } + } catch (_) { + AutoRouter.of(context).replace(const LoginRoute()); } } diff --git a/mobile/openapi/lib/model/asset_response_dto.dart b/mobile/openapi/lib/model/asset_response_dto.dart index 164a426dc1..f916088ba2 100644 --- a/mobile/openapi/lib/model/asset_response_dto.dart +++ b/mobile/openapi/lib/model/asset_response_dto.dart @@ -79,71 +79,74 @@ class AssetResponseDto { String? livePhotoVideoId; @override - bool operator ==(Object other) => identical(this, other) || other is AssetResponseDto && - other.type == type && - other.id == id && - other.deviceAssetId == deviceAssetId && - other.ownerId == ownerId && - other.deviceId == deviceId && - other.originalPath == originalPath && - other.resizePath == resizePath && - other.createdAt == createdAt && - other.modifiedAt == modifiedAt && - other.isFavorite == isFavorite && - other.mimeType == mimeType && - other.duration == duration && - other.webpPath == webpPath && - other.encodedVideoPath == encodedVideoPath && - other.exifInfo == exifInfo && - other.smartInfo == smartInfo && - other.livePhotoVideoId == livePhotoVideoId; + bool operator ==(Object other) => + identical(this, other) || + other is AssetResponseDto && + other.type == type && + other.id == id && + other.deviceAssetId == deviceAssetId && + other.ownerId == ownerId && + other.deviceId == deviceId && + other.originalPath == originalPath && + other.resizePath == resizePath && + other.createdAt == createdAt && + other.modifiedAt == modifiedAt && + other.isFavorite == isFavorite && + other.mimeType == mimeType && + other.duration == duration && + other.webpPath == webpPath && + other.encodedVideoPath == encodedVideoPath && + other.exifInfo == exifInfo && + other.smartInfo == smartInfo && + other.livePhotoVideoId == livePhotoVideoId; @override int get hashCode => - // ignore: unnecessary_parenthesis - (type.hashCode) + - (id.hashCode) + - (deviceAssetId.hashCode) + - (ownerId.hashCode) + - (deviceId.hashCode) + - (originalPath.hashCode) + - (resizePath == null ? 0 : resizePath!.hashCode) + - (createdAt.hashCode) + - (modifiedAt.hashCode) + - (isFavorite.hashCode) + - (mimeType == null ? 0 : mimeType!.hashCode) + - (duration.hashCode) + - (webpPath == null ? 0 : webpPath!.hashCode) + - (encodedVideoPath == null ? 0 : encodedVideoPath!.hashCode) + - (exifInfo == null ? 0 : exifInfo!.hashCode) + - (smartInfo == null ? 0 : smartInfo!.hashCode) + - (livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode); + // ignore: unnecessary_parenthesis + (type.hashCode) + + (id.hashCode) + + (deviceAssetId.hashCode) + + (ownerId.hashCode) + + (deviceId.hashCode) + + (originalPath.hashCode) + + (resizePath == null ? 0 : resizePath!.hashCode) + + (createdAt.hashCode) + + (modifiedAt.hashCode) + + (isFavorite.hashCode) + + (mimeType == null ? 0 : mimeType!.hashCode) + + (duration.hashCode) + + (webpPath == null ? 0 : webpPath!.hashCode) + + (encodedVideoPath == null ? 0 : encodedVideoPath!.hashCode) + + (exifInfo == null ? 0 : exifInfo!.hashCode) + + (smartInfo == null ? 0 : smartInfo!.hashCode) + + (livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode); @override - String toString() => 'AssetResponseDto[type=$type, id=$id, deviceAssetId=$deviceAssetId, ownerId=$ownerId, deviceId=$deviceId, originalPath=$originalPath, resizePath=$resizePath, createdAt=$createdAt, modifiedAt=$modifiedAt, isFavorite=$isFavorite, mimeType=$mimeType, duration=$duration, webpPath=$webpPath, encodedVideoPath=$encodedVideoPath, exifInfo=$exifInfo, smartInfo=$smartInfo, livePhotoVideoId=$livePhotoVideoId]'; + String toString() => + 'AssetResponseDto[type=$type, id=$id, deviceAssetId=$deviceAssetId, ownerId=$ownerId, deviceId=$deviceId, originalPath=$originalPath, resizePath=$resizePath, createdAt=$createdAt, modifiedAt=$modifiedAt, isFavorite=$isFavorite, mimeType=$mimeType, duration=$duration, webpPath=$webpPath, encodedVideoPath=$encodedVideoPath, exifInfo=$exifInfo, smartInfo=$smartInfo, livePhotoVideoId=$livePhotoVideoId]'; Map toJson() { final _json = {}; - _json[r'type'] = type; - _json[r'id'] = id; - _json[r'deviceAssetId'] = deviceAssetId; - _json[r'ownerId'] = ownerId; - _json[r'deviceId'] = deviceId; - _json[r'originalPath'] = originalPath; + _json[r'type'] = type; + _json[r'id'] = id; + _json[r'deviceAssetId'] = deviceAssetId; + _json[r'ownerId'] = ownerId; + _json[r'deviceId'] = deviceId; + _json[r'originalPath'] = originalPath; if (resizePath != null) { _json[r'resizePath'] = resizePath; } else { _json[r'resizePath'] = null; } - _json[r'createdAt'] = createdAt; - _json[r'modifiedAt'] = modifiedAt; - _json[r'isFavorite'] = isFavorite; + _json[r'createdAt'] = createdAt; + _json[r'modifiedAt'] = modifiedAt; + _json[r'isFavorite'] = isFavorite; if (mimeType != null) { _json[r'mimeType'] = mimeType; } else { _json[r'mimeType'] = null; } - _json[r'duration'] = duration; + _json[r'duration'] = duration; if (webpPath != null) { _json[r'webpPath'] = webpPath; } else { @@ -182,13 +185,13 @@ class AssetResponseDto { // Ensure that the map contains the required keys. // Note 1: the values aren't checked for validity beyond being non-null. // Note 2: this code is stripped in release mode! - assert(() { - requiredKeys.forEach((key) { - assert(json.containsKey(key), 'Required key "AssetResponseDto[$key]" is missing from JSON.'); - assert(json[key] != null, 'Required key "AssetResponseDto[$key]" has a null value in JSON.'); - }); - return true; - }()); + // assert(() { + // requiredKeys.forEach((key) { + // assert(json.containsKey(key), 'Required key "AssetResponseDto[$key]" is missing from JSON.'); + // assert(json[key] != null, 'Required key "AssetResponseDto[$key]" has a null value in JSON.'); + // }); + // return true; + // }()); return AssetResponseDto( type: AssetTypeEnum.fromJson(json[r'type'])!, @@ -213,7 +216,10 @@ class AssetResponseDto { return null; } - static List? listFromJson(dynamic json, {bool growable = false,}) { + static List? listFromJson( + dynamic json, { + bool growable = false, + }) { final result = []; if (json is List && json.isNotEmpty) { for (final row in json) { @@ -241,12 +247,18 @@ class AssetResponseDto { } // maps a json object with a list of AssetResponseDto-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + static Map> mapListFromJson( + dynamic json, { + bool growable = false, + }) { final map = >{}; if (json is Map && json.isNotEmpty) { json = json.cast(); // ignore: parameter_assignments for (final entry in json.entries) { - final value = AssetResponseDto.listFromJson(entry.value, growable: growable,); + final value = AssetResponseDto.listFromJson( + entry.value, + growable: growable, + ); if (value != null) { map[entry.key] = value; } @@ -274,4 +286,3 @@ class AssetResponseDto { 'livePhotoVideoId', }; } - diff --git a/mobile/openapi/lib/model/user_response_dto.dart b/mobile/openapi/lib/model/user_response_dto.dart index a6242d2c84..63c176378f 100644 --- a/mobile/openapi/lib/model/user_response_dto.dart +++ b/mobile/openapi/lib/model/user_response_dto.dart @@ -43,43 +43,46 @@ class UserResponseDto { DateTime? deletedAt; @override - bool operator ==(Object other) => identical(this, other) || other is UserResponseDto && - other.id == id && - other.email == email && - other.firstName == firstName && - other.lastName == lastName && - other.createdAt == createdAt && - other.profileImagePath == profileImagePath && - other.shouldChangePassword == shouldChangePassword && - other.isAdmin == isAdmin && - other.deletedAt == deletedAt; + bool operator ==(Object other) => + identical(this, other) || + other is UserResponseDto && + other.id == id && + other.email == email && + other.firstName == firstName && + other.lastName == lastName && + other.createdAt == createdAt && + other.profileImagePath == profileImagePath && + other.shouldChangePassword == shouldChangePassword && + other.isAdmin == isAdmin && + other.deletedAt == deletedAt; @override int get hashCode => - // ignore: unnecessary_parenthesis - (id.hashCode) + - (email.hashCode) + - (firstName.hashCode) + - (lastName.hashCode) + - (createdAt.hashCode) + - (profileImagePath.hashCode) + - (shouldChangePassword.hashCode) + - (isAdmin.hashCode) + - (deletedAt == null ? 0 : deletedAt!.hashCode); + // ignore: unnecessary_parenthesis + (id.hashCode) + + (email.hashCode) + + (firstName.hashCode) + + (lastName.hashCode) + + (createdAt.hashCode) + + (profileImagePath.hashCode) + + (shouldChangePassword.hashCode) + + (isAdmin.hashCode) + + (deletedAt == null ? 0 : deletedAt!.hashCode); @override - String toString() => 'UserResponseDto[id=$id, email=$email, firstName=$firstName, lastName=$lastName, createdAt=$createdAt, profileImagePath=$profileImagePath, shouldChangePassword=$shouldChangePassword, isAdmin=$isAdmin, deletedAt=$deletedAt]'; + String toString() => + 'UserResponseDto[id=$id, email=$email, firstName=$firstName, lastName=$lastName, createdAt=$createdAt, profileImagePath=$profileImagePath, shouldChangePassword=$shouldChangePassword, isAdmin=$isAdmin, deletedAt=$deletedAt]'; Map toJson() { final _json = {}; - _json[r'id'] = id; - _json[r'email'] = email; - _json[r'firstName'] = firstName; - _json[r'lastName'] = lastName; - _json[r'createdAt'] = createdAt; - _json[r'profileImagePath'] = profileImagePath; - _json[r'shouldChangePassword'] = shouldChangePassword; - _json[r'isAdmin'] = isAdmin; + _json[r'id'] = id; + _json[r'email'] = email; + _json[r'firstName'] = firstName; + _json[r'lastName'] = lastName; + _json[r'createdAt'] = createdAt; + _json[r'profileImagePath'] = profileImagePath; + _json[r'shouldChangePassword'] = shouldChangePassword; + _json[r'isAdmin'] = isAdmin; if (deletedAt != null) { _json[r'deletedAt'] = deletedAt!.toUtc().toIso8601String(); } else { @@ -98,13 +101,13 @@ class UserResponseDto { // Ensure that the map contains the required keys. // Note 1: the values aren't checked for validity beyond being non-null. // Note 2: this code is stripped in release mode! - assert(() { - requiredKeys.forEach((key) { - assert(json.containsKey(key), 'Required key "UserResponseDto[$key]" is missing from JSON.'); - assert(json[key] != null, 'Required key "UserResponseDto[$key]" has a null value in JSON.'); - }); - return true; - }()); + // assert(() { + // requiredKeys.forEach((key) { + // assert(json.containsKey(key), 'Required key "UserResponseDto[$key]" is missing from JSON.'); + // assert(json[key] != null, 'Required key "UserResponseDto[$key]" has a null value in JSON.'); + // }); + // return true; + // }()); return UserResponseDto( id: mapValueOfType(json, r'id')!, @@ -113,7 +116,8 @@ class UserResponseDto { lastName: mapValueOfType(json, r'lastName')!, createdAt: mapValueOfType(json, r'createdAt')!, profileImagePath: mapValueOfType(json, r'profileImagePath')!, - shouldChangePassword: mapValueOfType(json, r'shouldChangePassword')!, + shouldChangePassword: + mapValueOfType(json, r'shouldChangePassword')!, isAdmin: mapValueOfType(json, r'isAdmin')!, deletedAt: mapDateTime(json, r'deletedAt', ''), ); @@ -121,7 +125,10 @@ class UserResponseDto { return null; } - static List? listFromJson(dynamic json, {bool growable = false,}) { + static List? listFromJson( + dynamic json, { + bool growable = false, + }) { final result = []; if (json is List && json.isNotEmpty) { for (final row in json) { @@ -149,12 +156,18 @@ class UserResponseDto { } // maps a json object with a list of UserResponseDto-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + static Map> mapListFromJson( + dynamic json, { + bool growable = false, + }) { final map = >{}; if (json is Map && json.isNotEmpty) { json = json.cast(); // ignore: parameter_assignments for (final entry in json.entries) { - final value = UserResponseDto.listFromJson(entry.value, growable: growable,); + final value = UserResponseDto.listFromJson( + entry.value, + growable: growable, + ); if (value != null) { map[entry.key] = value; } @@ -176,4 +189,3 @@ class UserResponseDto { 'deletedAt', }; } -