fix: show "preparing" when sharing in beta timeline (#21390)

* fix: show "preparing" when sharing in beta timeline

* embed dialog inside of share_action_button

* dont await the share sheet so "preparing" dialog disappears once share sheet presents

this mimics old timeline behavior

* chore: lint
This commit is contained in:
Brandon Wees 2025-08-30 13:51:32 -05:00 committed by GitHub
parent aacb27ea5f
commit 32955915dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 59 additions and 31 deletions

View file

@ -1,15 +1,34 @@
import 'dart:io'; import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart';
import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart';
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart';
class _SharePreparingDialog extends StatelessWidget {
const _SharePreparingDialog();
@override
Widget build(BuildContext context) {
return AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const CircularProgressIndicator(),
Container(margin: const EdgeInsets.only(top: 12), child: const Text('share_dialog_preparing').tr()),
],
),
);
}
}
class ShareActionButton extends ConsumerWidget { class ShareActionButton extends ConsumerWidget {
final ActionSource source; final ActionSource source;
@ -20,7 +39,10 @@ class ShareActionButton extends ConsumerWidget {
return; return;
} }
final result = await ref.read(actionProvider.notifier).shareAssets(source); showDialog(
context: context,
builder: (BuildContext buildContext) {
ref.read(actionProvider.notifier).shareAssets(source).then((ActionResult result) {
ref.read(multiSelectProvider.notifier).reset(); ref.read(multiSelectProvider.notifier).reset();
if (!context.mounted) { if (!context.mounted) {
@ -34,14 +56,17 @@ class ShareActionButton extends ConsumerWidget {
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
toastType: ToastType.error, toastType: ToastType.error,
); );
} else if (result.count > 0) {
ImmichToast.show(
context: context,
msg: 'share_action_prompt'.t(context: context, args: {'count': result.count.toString()}),
gravity: ToastGravity.BOTTOM,
toastType: ToastType.success,
);
} }
buildContext.pop();
});
// show a loading spinner with a "Preparing" message
return const _SharePreparingDialog();
},
barrierDismissible: false,
useRootNavigator: false,
);
} }
@override @override

View file

@ -334,8 +334,8 @@ class ActionNotifier extends Notifier<void> {
final ids = _getAssets(source).toList(growable: false); final ids = _getAssets(source).toList(growable: false);
try { try {
final count = await _service.shareAssets(ids); await _service.shareAssets(ids);
return ActionResult(count: count, success: true); return ActionResult(count: ids.length, success: true);
} catch (error, stack) { } catch (error, stack) {
_logger.severe('Failed to share assets', error, stack); _logger.severe('Failed to share assets', error, stack);
return ActionResult(count: ids.length, success: false, error: error.toString()); return ActionResult(count: ids.length, success: false, error: error.toString());

View file

@ -104,8 +104,9 @@ class AssetMediaRepository {
return 0; return 0;
} }
final result = await Share.shareXFiles(downloadedXFiles); // we dont want to await the share result since the
// "preparing" dialog will not disappear unti
Share.shareXFiles(downloadedXFiles).then((result) async {
for (var file in downloadedXFiles) { for (var file in downloadedXFiles) {
try { try {
await File(file.path).delete(); await File(file.path).delete();
@ -113,6 +114,8 @@ class AssetMediaRepository {
_log.warning("Failed to delete temporary file: ${file.path}", e); _log.warning("Failed to delete temporary file: ${file.path}", e);
} }
} }
return result.status == ShareResultStatus.success ? downloadedXFiles.length : 0; });
return downloadedXFiles.length;
} }
} }