Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io';

import 'package:easy_localization/easy_localization.dart';
Expand Down Expand Up @@ -41,16 +42,20 @@ class ShareActionButton extends ConsumerWidget {
return;
}

final cancelCompleter = Completer<void>();
const preparingDialog = _SharePreparingDialog();
await showDialog(
context: context,
builder: (BuildContext buildContext) {
ref.read(actionProvider.notifier).shareAssets(source, context).then((ActionResult result) {
ref.read(multiSelectProvider.notifier).reset();

if (!context.mounted) {
ref.read(actionProvider.notifier).shareAssets(source, context, cancelCompleter: cancelCompleter).then((
ActionResult result,
) {
if (cancelCompleter.isCompleted || !context.mounted) {
return;
}

ref.read(multiSelectProvider.notifier).reset();

if (!result.success) {
ImmichToast.show(
context: context,
Expand All @@ -64,11 +69,15 @@ class ShareActionButton extends ConsumerWidget {
});

// show a loading spinner with a "Preparing" message
return const _SharePreparingDialog();
return preparingDialog;
},
barrierDismissible: false,
useRootNavigator: false,
);
).then((_) {
if (!cancelCompleter.isCompleted) {
cancelCompleter.complete();
}
});
}

@override
Expand Down
8 changes: 6 additions & 2 deletions mobile/lib/providers/infrastructure/action.provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -405,11 +405,15 @@ class ActionNotifier extends Notifier<void> {
}
}

Future<ActionResult> shareAssets(ActionSource source, BuildContext context) async {
Future<ActionResult> shareAssets(
ActionSource source,
BuildContext context, {
Completer<void>? cancelCompleter,
}) async {
final ids = _getAssets(source).toList(growable: false);

try {
await _service.shareAssets(ids, context);
await _service.shareAssets(ids, context, cancelCompleter: cancelCompleter);
return ActionResult(count: ids.length, success: true);
} catch (error, stack) {
_logger.severe('Failed to share assets', error, stack);
Expand Down
40 changes: 30 additions & 10 deletions mobile/lib/repositories/asset_media.repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ final assetMediaRepositoryProvider = Provider((ref) => AssetMediaRepository(ref.

class AssetMediaRepository {
final AssetApiRepository _assetApiRepository;

static final Logger _log = Logger("AssetMediaRepository");

const AssetMediaRepository(this._assetApiRepository);
Expand Down Expand Up @@ -58,6 +57,7 @@ class AssetMediaRepository {

static asset_entity.Asset? toAsset(AssetEntity? local) {
if (local == null) return null;

final asset_entity.Asset asset = asset_entity.Asset(
checksum: "",
localId: local.id,
Expand All @@ -72,19 +72,21 @@ class AssetMediaRepository {
height: local.height,
isFavorite: local.isFavorite,
);

if (asset.fileCreatedAt.year == 1970) {
asset.fileCreatedAt = asset.fileModifiedAt;
}

if (local.latitude != null) {
asset.exifInfo = ExifInfo(latitude: local.latitude, longitude: local.longitude);
}

asset.local = local;
return asset;
}

Future<String?> getOriginalFilename(String id) async {
final entity = await AssetEntity.fromId(id);

if (entity == null) {
return null;
}
Expand All @@ -101,12 +103,31 @@ class AssetMediaRepository {
}
}

/// Deletes temporary files in parallel
Future<void> _cleanupTempFiles(List<File> tempFiles) async {
await Future.wait(
tempFiles.map((file) async {
try {
await file.delete();
} catch (e) {
_log.warning("Failed to delete temporary file: ${file.path}", e);
}
}),
);
}

// TODO: make this more efficient
Future<int> shareAssets(List<BaseAsset> assets, BuildContext context) async {
Future<int> shareAssets(List<BaseAsset> assets, BuildContext context, {Completer<void>? cancelCompleter}) async {
final downloadedXFiles = <XFile>[];
final tempFiles = <File>[];

for (var asset in assets) {
if (cancelCompleter != null && cancelCompleter.isCompleted) {
// if cancelled, delete any temp files created so far
await _cleanupTempFiles(tempFiles);
return 0;
}

final localId = (asset is LocalAsset)
? asset.id
: asset is RemoteAsset
Expand Down Expand Up @@ -146,6 +167,11 @@ class AssetMediaRepository {
return 0;
}

if (cancelCompleter != null && cancelCompleter.isCompleted) {
await _cleanupTempFiles(tempFiles);
return 0;
}

// we dont want to await the share result since the
// "preparing" dialog will not disappear until
final size = context.sizeData;
Expand All @@ -154,13 +180,7 @@ class AssetMediaRepository {
downloadedXFiles,
sharePositionOrigin: Rect.fromPoints(Offset.zero, Offset(size.width / 3, size.height)),
).then((result) async {
for (var file in tempFiles) {
try {
await file.delete();
} catch (e) {
_log.warning("Failed to delete temporary file: ${file.path}", e);
}
}
await _cleanupTempFiles(tempFiles);
}),
);

Expand Down
4 changes: 2 additions & 2 deletions mobile/lib/services/action.service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,8 @@ class ActionService {
await _assetApiRepository.unStack(stackIds);
}

Future<int> shareAssets(List<BaseAsset> assets, BuildContext context) {
return _assetMediaRepository.shareAssets(assets, context);
Future<int> shareAssets(List<BaseAsset> assets, BuildContext context, {Completer<void>? cancelCompleter}) {
return _assetMediaRepository.shareAssets(assets, context, cancelCompleter: cancelCompleter);
}

Future<List<bool>> downloadAll(List<RemoteAsset> assets) {
Expand Down
Loading