mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
refactor(mobile): reworked Asset, store all required fields from local & remote (#1539)
replace usage of AssetResponseDto with Asset Add new class ExifInfo to store data from ExifResponseDto
This commit is contained in:
parent
7bd2455175
commit
0048662182
28 changed files with 626 additions and 504 deletions
|
|
@ -1,76 +0,0 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class ImmichAssetGroupByDate {
|
||||
final String date;
|
||||
List<AssetResponseDto> assets;
|
||||
ImmichAssetGroupByDate({
|
||||
required this.date,
|
||||
required this.assets,
|
||||
});
|
||||
|
||||
ImmichAssetGroupByDate copyWith({
|
||||
String? date,
|
||||
List<AssetResponseDto>? assets,
|
||||
}) {
|
||||
return ImmichAssetGroupByDate(
|
||||
date: date ?? this.date,
|
||||
assets: assets ?? this.assets,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'ImmichAssetGroupByDate(date: $date, assets: $assets)';
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is ImmichAssetGroupByDate &&
|
||||
other.date == date &&
|
||||
listEquals(other.assets, assets);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => date.hashCode ^ assets.hashCode;
|
||||
}
|
||||
|
||||
class GetAllAssetResponse {
|
||||
final int count;
|
||||
final List<ImmichAssetGroupByDate> data;
|
||||
final String nextPageKey;
|
||||
GetAllAssetResponse({
|
||||
required this.count,
|
||||
required this.data,
|
||||
required this.nextPageKey,
|
||||
});
|
||||
|
||||
GetAllAssetResponse copyWith({
|
||||
int? count,
|
||||
List<ImmichAssetGroupByDate>? data,
|
||||
String? nextPageKey,
|
||||
}) {
|
||||
return GetAllAssetResponse(
|
||||
count: count ?? this.count,
|
||||
data: data ?? this.data,
|
||||
nextPageKey: nextPageKey ?? this.nextPageKey,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'GetAllAssetResponse(count: $count, data: $data, nextPageKey: $nextPageKey)';
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is GetAllAssetResponse &&
|
||||
other.count == count &&
|
||||
listEquals(other.data, data) &&
|
||||
other.nextPageKey == nextPageKey;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => count.hashCode ^ data.hashCode ^ nextPageKey.hashCode;
|
||||
}
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/hive_box.dart';
|
||||
import 'package:immich_mobile/modules/backup/background_service/background.service.dart';
|
||||
import 'package:immich_mobile/modules/backup/models/hive_backup_albums.model.dart';
|
||||
import 'package:immich_mobile/modules/backup/services/backup.service.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/shared/services/api.service.dart';
|
||||
import 'package:immich_mobile/utils/openapi_extensions.dart';
|
||||
import 'package:immich_mobile/utils/tuple.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
final assetServiceProvider = Provider(
|
||||
(ref) => AssetService(
|
||||
ref.watch(apiServiceProvider),
|
||||
ref.watch(backupServiceProvider),
|
||||
ref.watch(backgroundServiceProvider),
|
||||
),
|
||||
);
|
||||
|
||||
class AssetService {
|
||||
final ApiService _apiService;
|
||||
final BackupService _backupService;
|
||||
final BackgroundService _backgroundService;
|
||||
final log = Logger('AssetService');
|
||||
|
||||
AssetService(this._apiService, this._backupService, this._backgroundService);
|
||||
|
||||
/// Returns `null` if the server state did not change, else list of assets
|
||||
Future<Pair<List<Asset>?, String?>> getRemoteAssets({String? etag}) async {
|
||||
try {
|
||||
final Pair<List<AssetResponseDto>, String?>? remote =
|
||||
await _apiService.assetApi.getAllAssetsWithETag(eTag: etag);
|
||||
if (remote == null) {
|
||||
return const Pair(null, null);
|
||||
}
|
||||
return Pair(
|
||||
remote.first.map(Asset.remote).toList(growable: false),
|
||||
remote.second,
|
||||
);
|
||||
} catch (e, stack) {
|
||||
log.severe('Error while getting remote assets', e, stack);
|
||||
return const Pair(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// if [urgent] is `true`, do not block by waiting on the background service
|
||||
/// to finish running. Returns `null` instead after a timeout.
|
||||
Future<List<Asset>?> getLocalAssets({bool urgent = false}) async {
|
||||
try {
|
||||
final Future<bool> hasAccess = urgent
|
||||
? _backgroundService.hasAccess
|
||||
.timeout(const Duration(milliseconds: 250))
|
||||
: _backgroundService.hasAccess;
|
||||
if (!await hasAccess) {
|
||||
throw Exception("Error [getAllAsset] failed to gain access");
|
||||
}
|
||||
final box = await Hive.openBox<HiveBackupAlbums>(hiveBackupInfoBox);
|
||||
final HiveBackupAlbums? backupAlbumInfo = box.get(backupInfoKey);
|
||||
if (backupAlbumInfo != null) {
|
||||
return (await _backupService
|
||||
.buildUploadCandidates(backupAlbumInfo.deepCopy()))
|
||||
.map(Asset.local)
|
||||
.toList(growable: false);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint("Error [_getLocalAssets] ${e.toString()}");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<Asset?> getAssetById(String assetId) async {
|
||||
try {
|
||||
return Asset.remote(await _apiService.assetApi.getAssetById(assetId));
|
||||
} catch (e) {
|
||||
debugPrint("Error [getAssetById] ${e.toString()}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<DeleteAssetResponseDto>?> deleteAssets(
|
||||
Iterable<AssetResponseDto> deleteAssets,
|
||||
) async {
|
||||
try {
|
||||
final List<String> payload = [];
|
||||
|
||||
for (final asset in deleteAssets) {
|
||||
payload.add(asset.id);
|
||||
}
|
||||
|
||||
return await _apiService.assetApi
|
||||
.deleteAsset(DeleteAssetDto(ids: payload));
|
||||
} catch (e) {
|
||||
debugPrint("Error getAllAsset ${e.toString()}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/services/json_cache.dart';
|
||||
|
||||
class AssetCacheService extends JsonCache<List<Asset>> {
|
||||
AssetCacheService() : super("asset_cache");
|
||||
|
||||
static Future<List<Map<String, dynamic>>> _computeSerialize(
|
||||
List<Asset> assets,
|
||||
) async {
|
||||
return assets.map((e) => e.toJson()).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
void put(List<Asset> data) async {
|
||||
putRawData(await compute(_computeSerialize, data));
|
||||
}
|
||||
|
||||
static Future<List<Asset>> _computeEncode(List<dynamic> data) async {
|
||||
return data.map((e) => Asset.fromJson(e)).whereNotNull().toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Asset>> get() async {
|
||||
try {
|
||||
final mapList = await readRawData() as List<dynamic>;
|
||||
|
||||
final responseData = await compute(_computeEncode, mapList);
|
||||
|
||||
return responseData;
|
||||
} catch (e) {
|
||||
debugPrint(e.toString());
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final assetCacheServiceProvider = Provider(
|
||||
(ref) => AssetCacheService(),
|
||||
);
|
||||
|
|
@ -24,7 +24,6 @@ class ImmichAssetGridState extends State<ImmichAssetGrid> {
|
|||
bool _scrolling = false;
|
||||
final Set<String> _selectedAssets = HashSet();
|
||||
|
||||
|
||||
Set<Asset> _getSelectedAssets() {
|
||||
return _selectedAssets
|
||||
.map((e) => widget.allAssets.firstWhereOrNull((a) => a.id == e))
|
||||
|
|
@ -103,7 +102,7 @@ class ImmichAssetGridState extends State<ImmichAssetGrid> {
|
|||
return Row(
|
||||
key: Key("asset-row-${row.assets.first.id}"),
|
||||
children: row.assets.map((Asset asset) {
|
||||
bool last = asset == row.assets.last;
|
||||
bool last = asset.id == row.assets.last.id;
|
||||
|
||||
return Container(
|
||||
key: Key("asset-${asset.id}"),
|
||||
|
|
@ -224,7 +223,6 @@ class ImmichAssetGridState extends State<ImmichAssetGrid> {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
Future<bool> onWillPop() async {
|
||||
if (widget.selectionActive && _selectedAssets.isNotEmpty) {
|
||||
_deselectAll();
|
||||
|
|
@ -234,8 +232,6 @@ class ImmichAssetGridState extends State<ImmichAssetGrid> {
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return WillPopScope(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue