mirror of
https://github.com/immich-app/immich.git
synced 2025-07-31 15:08:44 -04:00
fix: mobile storage status check (#19986)
* fix: _shouldUseLocalAsset check * show storage indicators in local album view * update local thumb provider to work with remote asset * update checks * do not show upload button when selection is only merged assets --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
parent
03ff425664
commit
2046dcc5b4
@ -165,15 +165,25 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||||||
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
|
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
|
||||||
useColumns: false,
|
useColumns: false,
|
||||||
),
|
),
|
||||||
|
leftOuterJoin(
|
||||||
|
_db.remoteAssetEntity,
|
||||||
|
_db.localAssetEntity.checksum
|
||||||
|
.equalsExp(_db.remoteAssetEntity.checksum),
|
||||||
|
useColumns: false,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
..addColumns([_db.remoteAssetEntity.id])
|
||||||
..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
|
..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
|
||||||
..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)])
|
..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)])
|
||||||
..limit(count, offset: offset);
|
..limit(count, offset: offset);
|
||||||
|
|
||||||
return query
|
return query.map((row) {
|
||||||
.map((row) => row.readTable(_db.localAssetEntity).toDto())
|
final asset = row.readTable(_db.localAssetEntity).toDto();
|
||||||
.get();
|
return asset.copyWith(
|
||||||
|
remoteId: row.read(_db.remoteAssetEntity.id),
|
||||||
|
);
|
||||||
|
}).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
TimelineQuery remoteAlbum(String albumId, GroupAssetsBy groupBy) => (
|
TimelineQuery remoteAlbum(String albumId, GroupAssetsBy groupBy) => (
|
||||||
|
@ -30,6 +30,7 @@ class LocalTimelinePage extends StatelessWidget {
|
|||||||
child: Timeline(
|
child: Timeline(
|
||||||
appBar: MesmerizingSliverAppBar(title: album.name),
|
appBar: MesmerizingSliverAppBar(title: album.name),
|
||||||
bottomSheet: const LocalAlbumBottomSheet(),
|
bottomSheet: const LocalAlbumBottomSheet(),
|
||||||
|
showStorageIndicator: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ class _SheetLocationDetailsState extends ConsumerState<SheetLocationDetails> {
|
|||||||
|
|
||||||
// Guard no lat/lng
|
// Guard no lat/lng
|
||||||
if (!hasCoordinates ||
|
if (!hasCoordinates ||
|
||||||
(asset is LocalAsset && !(asset as LocalAsset).hasRemote)) {
|
(asset != null && asset is LocalAsset && asset!.hasRemote)) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,13 @@ ImageProvider getFullImageProvider(
|
|||||||
// Create new provider and cache it
|
// Create new provider and cache it
|
||||||
final ImageProvider provider;
|
final ImageProvider provider;
|
||||||
if (_shouldUseLocalAsset(asset)) {
|
if (_shouldUseLocalAsset(asset)) {
|
||||||
provider = LocalFullImageProvider(asset: asset as LocalAsset, size: size);
|
final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).localId!;
|
||||||
|
provider = LocalFullImageProvider(
|
||||||
|
id: id,
|
||||||
|
name: asset.name,
|
||||||
|
size: size,
|
||||||
|
type: asset.type,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
final String assetId;
|
final String assetId;
|
||||||
if (asset is LocalAsset && asset.hasRemote) {
|
if (asset is LocalAsset && asset.hasRemote) {
|
||||||
@ -43,7 +49,13 @@ ImageProvider getThumbnailImageProvider({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_shouldUseLocalAsset(asset!)) {
|
if (_shouldUseLocalAsset(asset!)) {
|
||||||
return LocalThumbProvider(asset: asset as LocalAsset, size: size);
|
final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).localId!;
|
||||||
|
return LocalThumbProvider(
|
||||||
|
id: id,
|
||||||
|
updatedAt: asset.updatedAt,
|
||||||
|
name: asset.name,
|
||||||
|
size: size,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final String assetId;
|
final String assetId;
|
||||||
@ -59,5 +71,5 @@ ImageProvider getThumbnailImageProvider({
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _shouldUseLocalAsset(BaseAsset asset) =>
|
bool _shouldUseLocalAsset(BaseAsset asset) =>
|
||||||
asset is LocalAsset &&
|
asset.hasLocal &&
|
||||||
(!asset.hasRemote || !AppSetting.get(Setting.preferRemoteImage));
|
(!asset.hasRemote || !AppSetting.get(Setting.preferRemoteImage));
|
||||||
|
@ -21,11 +21,15 @@ class LocalThumbProvider extends ImageProvider<LocalThumbProvider> {
|
|||||||
const AssetMediaRepository();
|
const AssetMediaRepository();
|
||||||
final CacheManager? cacheManager;
|
final CacheManager? cacheManager;
|
||||||
|
|
||||||
final LocalAsset asset;
|
final String id;
|
||||||
|
final DateTime updatedAt;
|
||||||
|
final String name;
|
||||||
final Size size;
|
final Size size;
|
||||||
|
|
||||||
const LocalThumbProvider({
|
const LocalThumbProvider({
|
||||||
required this.asset,
|
required this.id,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.name,
|
||||||
this.size = const Size.square(kTimelineFixedTileExtent),
|
this.size = const Size.square(kTimelineFixedTileExtent),
|
||||||
this.cacheManager,
|
this.cacheManager,
|
||||||
});
|
});
|
||||||
@ -46,7 +50,10 @@ class LocalThumbProvider extends ImageProvider<LocalThumbProvider> {
|
|||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
informationCollector: () => <DiagnosticsNode>[
|
informationCollector: () => <DiagnosticsNode>[
|
||||||
DiagnosticsProperty<ImageProvider>('Image provider', this),
|
DiagnosticsProperty<ImageProvider>('Image provider', this),
|
||||||
DiagnosticsProperty<LocalAsset>('Asset', key.asset),
|
DiagnosticsProperty<String>('Id', key.id),
|
||||||
|
DiagnosticsProperty<DateTime>('Updated at', key.updatedAt),
|
||||||
|
DiagnosticsProperty<String>('Name', key.name),
|
||||||
|
DiagnosticsProperty<Size>('Size', key.size),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -57,7 +64,7 @@ class LocalThumbProvider extends ImageProvider<LocalThumbProvider> {
|
|||||||
ImageDecoderCallback decode,
|
ImageDecoderCallback decode,
|
||||||
) async {
|
) async {
|
||||||
final cacheKey =
|
final cacheKey =
|
||||||
'${key.asset.id}-${key.asset.updatedAt}-${key.size.width}x${key.size.height}';
|
'${key.id}-${key.updatedAt}-${key.size.width}x${key.size.height}';
|
||||||
|
|
||||||
final fileFromCache = await cache.getFileFromCache(cacheKey);
|
final fileFromCache = await cache.getFileFromCache(cacheKey);
|
||||||
if (fileFromCache != null) {
|
if (fileFromCache != null) {
|
||||||
@ -69,11 +76,11 @@ class LocalThumbProvider extends ImageProvider<LocalThumbProvider> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final thumbnailBytes =
|
final thumbnailBytes =
|
||||||
await _assetMediaRepository.getThumbnail(key.asset.id, size: key.size);
|
await _assetMediaRepository.getThumbnail(key.id, size: key.size);
|
||||||
if (thumbnailBytes == null) {
|
if (thumbnailBytes == null) {
|
||||||
PaintingBinding.instance.imageCache.evict(key);
|
PaintingBinding.instance.imageCache.evict(key);
|
||||||
throw StateError(
|
throw StateError(
|
||||||
"Loading thumb for local photo ${key.asset.name} failed",
|
"Loading thumb for local photo ${key.name} failed",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,14 +93,13 @@ class LocalThumbProvider extends ImageProvider<LocalThumbProvider> {
|
|||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) return true;
|
||||||
if (other is LocalThumbProvider) {
|
if (other is LocalThumbProvider) {
|
||||||
return asset.id == other.asset.id &&
|
return id == other.id && updatedAt == other.updatedAt;
|
||||||
asset.updatedAt == other.asset.updatedAt;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => asset.id.hashCode ^ asset.updatedAt.hashCode;
|
int get hashCode => id.hashCode ^ updatedAt.hashCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
||||||
@ -101,12 +107,16 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
|||||||
const AssetMediaRepository();
|
const AssetMediaRepository();
|
||||||
final StorageRepository _storageRepository = const StorageRepository();
|
final StorageRepository _storageRepository = const StorageRepository();
|
||||||
|
|
||||||
final LocalAsset asset;
|
final String id;
|
||||||
|
final String name;
|
||||||
final Size size;
|
final Size size;
|
||||||
|
final AssetType type;
|
||||||
|
|
||||||
const LocalFullImageProvider({
|
const LocalFullImageProvider({
|
||||||
required this.asset,
|
required this.id,
|
||||||
|
required this.name,
|
||||||
required this.size,
|
required this.size,
|
||||||
|
required this.type,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -123,7 +133,7 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
|||||||
codec: _codec(key, decode),
|
codec: _codec(key, decode),
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
informationCollector: () sync* {
|
informationCollector: () sync* {
|
||||||
yield ErrorDescription(asset.name);
|
yield ErrorDescription(name);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -134,24 +144,24 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
|||||||
ImageDecoderCallback decode,
|
ImageDecoderCallback decode,
|
||||||
) async* {
|
) async* {
|
||||||
try {
|
try {
|
||||||
switch (key.asset.type) {
|
switch (key.type) {
|
||||||
case AssetType.image:
|
case AssetType.image:
|
||||||
yield* _decodeProgressive(key, decode);
|
yield* _decodeProgressive(key, decode);
|
||||||
break;
|
break;
|
||||||
case AssetType.video:
|
case AssetType.video:
|
||||||
final codec = await _getThumbnailCodec(key, decode);
|
final codec = await _getThumbnailCodec(key, decode);
|
||||||
if (codec == null) {
|
if (codec == null) {
|
||||||
throw StateError("Failed to load preview for ${key.asset.name}");
|
throw StateError("Failed to load preview for ${key.name}");
|
||||||
}
|
}
|
||||||
yield codec;
|
yield codec;
|
||||||
break;
|
break;
|
||||||
case AssetType.other:
|
case AssetType.other:
|
||||||
case AssetType.audio:
|
case AssetType.audio:
|
||||||
throw StateError('Unsupported asset type ${key.asset.type}');
|
throw StateError('Unsupported asset type ${key.type}');
|
||||||
}
|
}
|
||||||
} catch (error, stack) {
|
} catch (error, stack) {
|
||||||
Logger('ImmichLocalImageProvider')
|
Logger('ImmichLocalImageProvider')
|
||||||
.severe('Error loading local image ${key.asset.name}', error, stack);
|
.severe('Error loading local image ${key.name}', error, stack);
|
||||||
throw const ImageLoadingException(
|
throw const ImageLoadingException(
|
||||||
'Could not load image from local storage',
|
'Could not load image from local storage',
|
||||||
);
|
);
|
||||||
@ -163,7 +173,7 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
|||||||
ImageDecoderCallback decode,
|
ImageDecoderCallback decode,
|
||||||
) async {
|
) async {
|
||||||
final thumbBytes =
|
final thumbBytes =
|
||||||
await _assetMediaRepository.getThumbnail(key.asset.id, size: key.size);
|
await _assetMediaRepository.getThumbnail(key.id, size: key.size);
|
||||||
if (thumbBytes == null) {
|
if (thumbBytes == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -175,9 +185,9 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
|||||||
LocalFullImageProvider key,
|
LocalFullImageProvider key,
|
||||||
ImageDecoderCallback decode,
|
ImageDecoderCallback decode,
|
||||||
) async* {
|
) async* {
|
||||||
final file = await _storageRepository.getFileForAsset(key.asset.id);
|
final file = await _storageRepository.getFileForAsset(key.id);
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
throw StateError("Opening file for asset ${key.asset.name} failed");
|
throw StateError("Opening file for asset ${key.name} failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
final fileSize = await file.length();
|
final fileSize = await file.length();
|
||||||
@ -195,7 +205,7 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
|||||||
(key.size.height * progressiveMultiplier).clamp(256, 1024),
|
(key.size.height * progressiveMultiplier).clamp(256, 1024),
|
||||||
);
|
);
|
||||||
final mediumThumb =
|
final mediumThumb =
|
||||||
await _assetMediaRepository.getThumbnail(key.asset.id, size: size);
|
await _assetMediaRepository.getThumbnail(key.id, size: size);
|
||||||
if (mediumThumb != null) {
|
if (mediumThumb != null) {
|
||||||
final mediumBuffer = await ImmutableBuffer.fromUint8List(mediumThumb);
|
final mediumBuffer = await ImmutableBuffer.fromUint8List(mediumThumb);
|
||||||
yield await decode(mediumBuffer);
|
yield await decode(mediumBuffer);
|
||||||
@ -212,7 +222,7 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
|||||||
(key.size.height * progressiveMultiplier).clamp(512, 2048),
|
(key.size.height * progressiveMultiplier).clamp(512, 2048),
|
||||||
);
|
);
|
||||||
final highThumb =
|
final highThumb =
|
||||||
await _assetMediaRepository.getThumbnail(key.asset.id, size: size);
|
await _assetMediaRepository.getThumbnail(key.id, size: size);
|
||||||
if (highThumb != null) {
|
if (highThumb != null) {
|
||||||
final highBuffer = await ImmutableBuffer.fromUint8List(highThumb);
|
final highBuffer = await ImmutableBuffer.fromUint8List(highThumb);
|
||||||
yield await decode(highBuffer);
|
yield await decode(highBuffer);
|
||||||
@ -228,14 +238,15 @@ class LocalFullImageProvider extends ImageProvider<LocalFullImageProvider> {
|
|||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) return true;
|
||||||
if (other is LocalFullImageProvider) {
|
if (other is LocalFullImageProvider) {
|
||||||
return asset.id == other.asset.id &&
|
return id == other.id &&
|
||||||
asset.updatedAt == other.asset.updatedAt &&
|
size == other.size &&
|
||||||
size == other.size;
|
type == other.type &&
|
||||||
|
name == other.name;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
asset.id.hashCode ^ asset.updatedAt.hashCode ^ size.hashCode;
|
id.hashCode ^ size.hashCode ^ type.hashCode ^ name.hashCode;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user