From 7a70c98158929561004615e88b3d897621f53976 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 4 Apr 2026 15:43:10 +0000 Subject: [PATCH] fix: image sharing fails due to coroutine scope cancellation The share media dialog used `rememberCoroutineScope()` which shadowed the outer `accountViewModel.viewModelScope`. When onDismiss() was called immediately after launching the share coroutine, the dialog-scoped coroutine was cancelled, causing CancellationException to be caught as a generic error and showing "Unable to share image" toast. Fix: use viewModelScope for share operations so they survive dialog dismissal, and re-throw CancellationException in shareImageFile. https://claude.ai/code/session_01Euj5mXfjneNCx9m49YTp92 --- .../amethyst/ui/components/ZoomableContentView.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt index 6321e4fd4..336d387db 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt @@ -762,7 +762,7 @@ fun ShareMediaAction( content: BaseMediaContent? = null, accountViewModel: AccountViewModel, ) { - val scope = accountViewModel.viewModelScope + val viewModelScope = accountViewModel.viewModelScope // Track if video is downloading - hoisted here to block menu dismiss during download val isDownloadingVideo = remember { mutableStateOf(false) } @@ -817,7 +817,7 @@ fun ShareMediaAction( videoUri?.let { if (videoUri.isNotEmpty()) { M3ActionRow(icon = Icons.Outlined.Share, text = stringRes(R.string.share_image)) { - scope.launch { shareImageFile(context, videoUri, mimeType) } + viewModelScope.launch { shareImageFile(context, videoUri, mimeType) } onDismiss() } } @@ -833,7 +833,7 @@ fun ShareMediaAction( enabled = !isDownloadingVideo.value, ) { isDownloadingVideo.value = true - scope.launch { + viewModelScope.launch { shareVideoFile( context = context, videoUrl = videoUri, @@ -858,7 +858,7 @@ fun ShareMediaAction( is MediaLocalVideo -> { content.localFile?.let { localFile -> M3ActionRow(icon = Icons.Outlined.Share, text = stringRes(R.string.share_video)) { - scope.launch { shareLocalVideoFile(context, localFile, mimeType) } + viewModelScope.launch { shareLocalVideoFile(context, localFile, mimeType) } onDismiss() } } @@ -894,6 +894,7 @@ private suspend fun shareImageFile( context.startActivity(Intent.createChooser(shareIntent, null)) } catch (e: Exception) { + if (e is CancellationException) throw e Log.w("ZoomableContentView", "Failed to share image: $videoUri", e) Toast.makeText(context, context.getString(R.string.unable_to_share_image), Toast.LENGTH_SHORT).show() }