refactor(mobile): share action button in new timeline (#19967)

* share asset button

* include source

* move to repository

* formatting
This commit is contained in:
Mert
2025-07-17 19:41:30 +03:00
committed by GitHub
parent 531515daf9
commit 055b930066
15 changed files with 158 additions and 24 deletions
@@ -1,27 +1,40 @@
import 'dart:io';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/exif.model.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/entities/asset.entity.dart' as asset_entity;
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/repositories/asset_api.repository.dart';
import 'package:immich_mobile/utils/hash.dart';
import 'package:photo_manager/photo_manager.dart' hide AssetType;
import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart';
import 'package:photo_manager/photo_manager.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/extensions/response_extensions.dart';
import 'package:share_plus/share_plus.dart';
final assetMediaRepositoryProvider =
Provider((ref) => const AssetMediaRepository());
final assetMediaRepositoryProvider = Provider(
(ref) => AssetMediaRepository(ref.watch(assetApiRepositoryProvider)),
);
class AssetMediaRepository {
const AssetMediaRepository();
final AssetApiRepository _assetApiRepository;
static final Logger _log = Logger("AssetMediaRepository");
const AssetMediaRepository(this._assetApiRepository);
Future<List<String>> deleteAll(List<String> ids) =>
PhotoManager.editor.deleteWithIds(ids);
Future<Asset?> get(String id) async {
Future<asset_entity.Asset?> get(String id) async {
final entity = await AssetEntity.fromId(id);
return toAsset(entity);
}
static Asset? toAsset(AssetEntity? local) {
static asset_entity.Asset? toAsset(AssetEntity? local) {
if (local == null) return null;
final Asset asset = Asset(
final asset_entity.Asset asset = asset_entity.Asset(
checksum: "",
localId: local.id,
ownerId: fastHash(Store.get(StoreKey.currentUser).id),
@@ -29,7 +42,7 @@ class AssetMediaRepository {
fileModifiedAt: local.modifiedDateTime,
updatedAt: local.modifiedDateTime,
durationInSeconds: local.duration,
type: AssetType.values[local.typeInt],
type: asset_entity.AssetType.values[local.typeInt],
fileName: local.title!,
width: local.width,
height: local.height,
@@ -57,4 +70,57 @@ class AssetMediaRepository {
// otherwise using the `entity.title` would return a random GUID
return await entity.titleAsync;
}
// TODO: make this more efficient
Future<int> shareAssets(List<BaseAsset> assets) async {
final downloadedXFiles = <XFile>[];
for (var asset in assets) {
final localId = (asset is LocalAsset)
? asset.id
: asset is RemoteAsset
? asset.localId
: null;
if (localId != null) {
File? f =
await AssetEntity(id: localId, width: 1, height: 1, typeInt: 0)
.originFile;
downloadedXFiles.add(XFile(f!.path));
} else if (asset is RemoteAsset) {
final tempDir = await getTemporaryDirectory();
final name = asset.name;
final tempFile = await File('${tempDir.path}/$name').create();
final res = await _assetApiRepository.downloadAsset(asset.id);
if (res.statusCode != 200) {
_log.severe("Download for $name failed", res.toLoggerString());
continue;
}
await tempFile.writeAsBytes(res.bodyBytes);
downloadedXFiles.add(XFile(tempFile.path));
} else {
_log.warning("Asset type not supported for sharing: $asset");
continue;
}
}
if (downloadedXFiles.isEmpty) {
_log.warning("No asset can be retrieved for share");
return 0;
}
final result = await Share.shareXFiles(downloadedXFiles);
for (var file in downloadedXFiles) {
try {
await File(file.path).delete();
} catch (e) {
_log.warning("Failed to delete temporary file: ${file.path}", e);
}
}
return result.status == ShareResultStatus.success
? downloadedXFiles.length
: 0;
}
}