feature(mobile): allow app to be used offline (#1932)

* feature(mobile): allow app to be used offline

* translatable server/network error message

* adjust profile drawer error message

* call getAllAsset after cold app starts

* fix analyzer error

* update asset state if length differs

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
Fynn Petersen-Frey
2023-03-15 22:29:07 +01:00
committed by GitHub
parent 54831878e0
commit 04955a4123
14 changed files with 123 additions and 66 deletions

View File

@@ -104,7 +104,10 @@ class AssetNotifier extends StateNotifier<AssetsState> {
final bool newLocal = await _albumService.refreshDeviceAlbums();
log.info("Load assets: ${stopwatch.elapsedMilliseconds}ms");
stopwatch.reset();
if (!newRemote && !newLocal) {
if (!newRemote &&
!newLocal &&
state.allAssets.length ==
await _db.assets.filter().ownerIdEqualTo(me.isarId).count()) {
log.info("state is already up-to-date");
return;
}

View File

@@ -1,3 +1,4 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/shared/models/server_info_state.model.dart';
@@ -28,8 +29,7 @@ class ServerInfoNotifier extends StateNotifier<ServerInfoState> {
if (serverVersion == null) {
state = state.copyWith(
isVersionMismatch: true,
versionMismatchErrorMessage:
"Server is out of date. Some functionalities might not working correctly. Download and rebuild server",
versionMismatchErrorMessage: "common_server_error".tr(),
);
return;
}

View File

@@ -11,15 +11,17 @@ import 'package:photo_manager/photo_manager.dart';
class ImmichImage extends StatelessWidget {
const ImmichImage(
this.asset, {
required this.width,
required this.height,
this.width,
this.height,
this.fit = BoxFit.cover,
this.useGrayBoxPlaceholder = false,
super.key,
});
final Asset? asset;
final bool useGrayBoxPlaceholder;
final double width;
final double height;
final double? width;
final double? height;
final BoxFit fit;
@override
Widget build(BuildContext context) {
@@ -47,7 +49,7 @@ class ImmichImage extends StatelessWidget {
),
width: width,
height: height,
fit: BoxFit.cover,
fit: fit,
frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
if (wasSynchronouslyLoaded || frame != null) {
return child;
@@ -93,7 +95,7 @@ class ImmichImage extends StatelessWidget {
// keeping memCacheWidth, memCacheHeight, maxWidthDiskCache and
// maxHeightDiskCache = null allows to simply store the webp thumbnail
// from the server and use it for all rendered thumbnail sizes
fit: BoxFit.cover,
fit: fit,
fadeInDuration: const Duration(milliseconds: 250),
progressIndicatorBuilder: (context, url, downloadProgress) {
if (useGrayBoxPlaceholder) {

View File

@@ -21,31 +21,30 @@ class SplashScreenPage extends HookConsumerWidget {
Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox).get(savedLoginInfoKey);
void performLoggingIn() async {
try {
if (loginInfo != null) {
bool isSuccess = false;
if (loginInfo != null) {
try {
// Resolve API server endpoint from user provided serverUrl
await apiService.resolveAndSetEndpoint(loginInfo.serverUrl);
var isSuccess = await ref
.read(authenticationProvider.notifier)
.setSuccessLoginInfo(
accessToken: loginInfo.accessToken,
serverUrl: loginInfo.serverUrl,
);
if (isSuccess) {
final hasPermission = await ref
.read(galleryPermissionNotifier.notifier)
.hasPermission;
if (hasPermission) {
// 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 (e) {
// okay, try to continue anyway if offline
}
} catch (_) {
isSuccess =
await ref.read(authenticationProvider.notifier).setSuccessLoginInfo(
accessToken: loginInfo.accessToken,
serverUrl: loginInfo.serverUrl,
);
}
if (isSuccess) {
final hasPermission =
await ref.read(galleryPermissionNotifier.notifier).hasPermission;
if (hasPermission) {
// Resume backup (if enable) then navigate
ref.watch(backupProvider.notifier).resumeBackup();
}
AutoRouter.of(context).replace(const TabControllerRoute());
} else {
AutoRouter.of(context).replace(const LoginRoute());
}
}