diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/UploadOrchestrator.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/UploadOrchestrator.kt index 6ee9ce22e..661248ff7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/UploadOrchestrator.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/UploadOrchestrator.kt @@ -37,6 +37,11 @@ import kotlinx.coroutines.flow.map import okhttp3.OkHttpClient import kotlin.coroutines.cancellation.CancellationException +data class StrippingFailureState( + val onConfirm: () -> Unit, + val onCancel: () -> Unit, +) + sealed class UploadingState { data object Ready : UploadingState() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostView.kt index a7812207b..4958bf3c8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostView.kt @@ -39,6 +39,8 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CurrencyBitcoin +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -125,6 +127,24 @@ fun EditPostView( postViewModel.load(edit, versionLookingAt) } + postViewModel.strippingFailureDialog?.let { dialogState -> + AlertDialog( + onDismissRequest = { dialogState.onCancel() }, + title = { Text(stringRes(R.string.metadata_strip_failed_title)) }, + text = { Text(stringRes(R.string.metadata_strip_failed_body)) }, + confirmButton = { + Button(onClick = { dialogState.onConfirm() }) { + Text(stringRes(R.string.metadata_strip_failed_upload)) + } + }, + dismissButton = { + Button(onClick = { dialogState.onCancel() }) { + Text(stringRes(R.string.cancel)) + } + }, + ) + } + Dialog( onDismissRequest = { onClose() }, properties = diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt index 63504ed0e..a624f73a4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt @@ -37,6 +37,7 @@ import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator +import com.vitorpamplona.amethyst.service.uploads.StrippingFailureState import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName import com.vitorpamplona.amethyst.ui.actions.uploads.MediaUploadTracker @@ -64,6 +65,7 @@ import com.vitorpamplona.quartz.nip94FileMetadata.size import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine @Stable open class EditPostViewModel : ViewModel() { @@ -90,6 +92,10 @@ open class EditPostViewModel : ViewModel() { // Images and Videos var multiOrchestrator by mutableStateOf(null) + // Stripping failure dialog + var strippingFailureDialog by mutableStateOf(null) + private set + // Codec selection: false = H264, true = H265 var useH265Codec by mutableStateOf(false) @@ -197,6 +203,21 @@ open class EditPostViewModel : ViewModel() { context, useH265Codec, stripMetadata, + onStrippingFailed = { + suspendCancellableCoroutine { continuation -> + strippingFailureDialog = + StrippingFailureState( + onConfirm = { + strippingFailureDialog = null + continuation.resume(true) {} + }, + onCancel = { + strippingFailureDialog = null + continuation.resume(false) {} + }, + ) + } + }, ) if (results.allGood) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaModel.kt index d4c726b49..9f889b335 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaModel.kt @@ -33,6 +33,7 @@ import com.vitorpamplona.amethyst.commons.richtext.RichTextParser import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator +import com.vitorpamplona.amethyst.service.uploads.StrippingFailureState import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName @@ -44,6 +45,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.joinAll import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine @Stable open class NewMediaModel : ViewModel() { @@ -59,6 +61,10 @@ open class NewMediaModel : ViewModel() { var multiOrchestrator by mutableStateOf(null) var onceUploaded: () -> Unit = {} + // Stripping failure dialog + var strippingFailureDialog by mutableStateOf(null) + private set + // 0 = Low, 1 = Medium, 2 = High, 3=UNCOMPRESSED var mediaQualitySlider by mutableIntStateOf(1) @@ -120,6 +126,21 @@ open class NewMediaModel : ViewModel() { context, useH265Codec, stripMetadata, + onStrippingFailed = { + suspendCancellableCoroutine { continuation -> + strippingFailureDialog = + StrippingFailureState( + onConfirm = { + strippingFailureDialog = null + continuation.resume(true) {} + }, + onCancel = { + strippingFailureDialog = null + continuation.resume(false) {} + }, + ) + } + }, ) if (results.allGood) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt index b0b88a392..147a12215 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt @@ -32,6 +32,8 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField @@ -89,6 +91,24 @@ fun NewMediaView( postViewModel.load(account, uris) } + postViewModel.strippingFailureDialog?.let { dialogState -> + AlertDialog( + onDismissRequest = { dialogState.onCancel() }, + title = { Text(stringRes(R.string.metadata_strip_failed_title)) }, + text = { Text(stringRes(R.string.metadata_strip_failed_body)) }, + confirmButton = { + Button(onClick = { dialogState.onConfirm() }) { + Text(stringRes(R.string.metadata_strip_failed_upload)) + } + }, + dismissButton = { + Button(onClick = { dialogState.onCancel() }) { + Text(stringRes(R.string.cancel)) + } + }, + ) + } + Dialog( onDismissRequest = { onClose() }, properties = diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/CommentPostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/CommentPostViewModel.kt index abf55cc6c..da687602a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/CommentPostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/CommentPostViewModel.kt @@ -42,6 +42,7 @@ import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.service.location.LocationState import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator +import com.vitorpamplona.amethyst.service.uploads.StrippingFailureState import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName @@ -111,6 +112,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine @Stable open class CommentPostViewModel : @@ -162,6 +164,10 @@ open class CommentPostViewModel : // Images and Videos var multiOrchestrator by mutableStateOf(null) + // Stripping failure dialog + var strippingFailureDialog by mutableStateOf(null) + private set + // Invoices var canAddInvoice by mutableStateOf(false) var wantsInvoice by mutableStateOf(false) @@ -503,6 +509,21 @@ open class CommentPostViewModel : account, context, stripMetadata = stripMetadata, + onStrippingFailed = { + suspendCancellableCoroutine { continuation -> + strippingFailureDialog = + StrippingFailureState( + onConfirm = { + strippingFailureDialog = null + continuation.resume(true) {} + }, + onCancel = { + strippingFailureDialog = null + continuation.resume(false) {} + }, + ) + } + }, ) if (results.allGood) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/GenericCommentPostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/GenericCommentPostScreen.kt index edc9a55c1..fa11b4639 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/GenericCommentPostScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/GenericCommentPostScreen.kt @@ -34,10 +34,13 @@ import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment.Companion.CenterVertically @@ -140,6 +143,24 @@ fun GenericCommentPostScreen( ) { WatchAndLoadMyEmojiList(accountViewModel) + postViewModel.strippingFailureDialog?.let { dialogState -> + AlertDialog( + onDismissRequest = { dialogState.onCancel() }, + title = { Text(stringRes(R.string.metadata_strip_failed_title)) }, + text = { Text(stringRes(R.string.metadata_strip_failed_body)) }, + confirmButton = { + Button(onClick = { dialogState.onConfirm() }) { + Text(stringRes(R.string.metadata_strip_failed_upload)) + } + }, + dismissButton = { + Button(onClick = { dialogState.onCancel() }) { + Text(stringRes(R.string.cancel)) + } + }, + ) + } + BackHandler { accountViewModel.launchSigner { postViewModel.sendDraftSync() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/ChatNewMessageViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/ChatNewMessageViewModel.kt index 250b18264..550c839d1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/ChatNewMessageViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/ChatNewMessageViewModel.kt @@ -39,6 +39,7 @@ import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.service.location.LocationState +import com.vitorpamplona.amethyst.service.uploads.StrippingFailureState import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.note.creators.draftTags.DraftTagState @@ -99,6 +100,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine @Stable class ChatNewMessageViewModel : @@ -133,6 +135,11 @@ class ChatNewMessageViewModel : val replyTo = mutableStateOf(null) var uploadState by mutableStateOf(null) + + // Stripping failure dialog + var strippingFailureDialog by mutableStateOf(null) + private set + val iMetaAttachments = IMetaAttachments() var uploadsWaitingToBeSent by mutableStateOf>(emptyList()) @@ -395,6 +402,21 @@ class ChatNewMessageViewModel : return location!! } + private suspend fun showStrippingFailureDialog(): Boolean = + suspendCancellableCoroutine { continuation -> + strippingFailureDialog = + StrippingFailureState( + onConfirm = { + strippingFailureDialog = null + continuation.resume(true) {} + }, + onCancel = { + strippingFailureDialog = null + continuation.resume(false) {} + }, + ) + } + fun uploadAndHold( onError: (title: String, message: String) -> Unit, context: Context, @@ -413,13 +435,14 @@ class ChatNewMessageViewModel : pendingRetryMode = RetryMode.HOLD }, context, + onStrippingFailed = ::showStrippingFailureDialog, ) { uploadsWaitingToBeSent += it draftTag.newVersion() onceUploaded() } } else { - ChatFileUploader(account).justUploadNIP04(uploadState, onError, context) { + ChatFileUploader(account).justUploadNIP04(uploadState, onError, context, onStrippingFailed = ::showStrippingFailureDialog) { uploadsWaitingToBeSent += it draftTag.newVersion() onceUploaded() @@ -450,13 +473,14 @@ class ChatNewMessageViewModel : pendingRetryOnceUploaded = onceUploaded }, context, + onStrippingFailed = ::showStrippingFailureDialog, ) { ChatFileSender(room, account).sendNIP17(it) draftTag.newVersion() onceUploaded() } } else { - ChatFileUploader(account).justUploadNIP04(uploadState, onError, context) { + ChatFileUploader(account).justUploadNIP04(uploadState, onError, context, onStrippingFailed = ::showStrippingFailureDialog) { ChatFileSender(room, account).sendNIP04(it) draftTag.newVersion() onceUploaded() @@ -505,6 +529,7 @@ class ChatNewMessageViewModel : uploadState, onError ?: accountViewModel.toastManager::toast, context, + onStrippingFailed = ::showStrippingFailureDialog, ) { uploadsWaitingToBeSent += it draftTag.newVersion() @@ -518,6 +543,7 @@ class ChatNewMessageViewModel : uploadState, onError ?: accountViewModel.toastManager::toast, context, + onStrippingFailed = ::showStrippingFailureDialog, ) { ChatFileSender(room, account).sendNIP17(it) draftTag.newVersion() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt index b5b38901d..4bcf304ad 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt @@ -118,6 +118,24 @@ fun PrivateMessageEditFieldRow( } } + channelScreenModel.strippingFailureDialog?.let { dialogState -> + AlertDialog( + onDismissRequest = { dialogState.onCancel() }, + title = { Text(stringRes(R.string.metadata_strip_failed_title)) }, + text = { Text(stringRes(R.string.metadata_strip_failed_body)) }, + confirmButton = { + TextButton(onClick = { dialogState.onConfirm() }) { + Text(stringRes(R.string.metadata_strip_failed_upload)) + } + }, + dismissButton = { + TextButton(onClick = { dialogState.onCancel() }) { + Text(stringRes(R.string.cancel)) + } + }, + ) + } + channelScreenModel.encryptedUploadErrorTitle?.let { title -> EncryptedUploadErrorDialog( title = title, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/upload/ChatFileUploader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/upload/ChatFileUploader.kt index 2c265f155..bee033b79 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/upload/ChatFileUploader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/upload/ChatFileUploader.kt @@ -41,6 +41,7 @@ class ChatFileUploader( onError: (title: String, message: String) -> Unit, onEncryptedUploadError: (title: String, message: String) -> Unit, context: Context, + onStrippingFailed: suspend () -> Boolean = { true }, onceUploaded: suspend (List) -> Unit, ) { val orchestrator = viewState.multiOrchestrator ?: return @@ -59,6 +60,7 @@ class ChatFileUploader( account, context, stripMetadata = viewState.stripMetadata, + onStrippingFailed = onStrippingFailed, ) if (results.allGood) { @@ -82,7 +84,7 @@ class ChatFileUploader( ) } } else { - justUploadNIP17Unencrypted(viewState, onError, context, onceUploaded) + justUploadNIP17Unencrypted(viewState, onError, context, onStrippingFailed, onceUploaded) } viewState.mediaUploadTracker.finishUpload() @@ -92,6 +94,7 @@ class ChatFileUploader( viewState: ChatFileUploadState, onError: (title: String, message: String) -> Unit, context: Context, + onStrippingFailed: suspend () -> Boolean = { true }, onceUploaded: suspend (List) -> Unit, ) { val orchestrator = viewState.multiOrchestrator ?: return @@ -106,6 +109,7 @@ class ChatFileUploader( account, context, stripMetadata = viewState.stripMetadata, + onStrippingFailed = onStrippingFailed, ) if (results.allGood) { @@ -137,6 +141,7 @@ class ChatFileUploader( viewState: ChatFileUploadState, onError: (title: String, message: String) -> Unit, context: Context, + onStrippingFailed: suspend () -> Boolean = { true }, onceUploaded: suspend (List) -> Unit, ) { val orchestrator = viewState.multiOrchestrator ?: return @@ -151,6 +156,7 @@ class ChatFileUploader( account, context, stripMetadata = viewState.stripMetadata, + onStrippingFailed = onStrippingFailed, ) if (results.allGood) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt index 2c4f58fe0..24bc7de3d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt @@ -46,6 +46,7 @@ import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.service.location.LocationState import com.vitorpamplona.amethyst.service.uploads.MediaCompressor +import com.vitorpamplona.amethyst.service.uploads.StrippingFailureState import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia @@ -101,6 +102,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine @Stable open class ChannelNewMessageViewModel : @@ -129,6 +131,11 @@ open class ChannelNewMessageViewModel : val replyTo = mutableStateOf(null) var uploadState by mutableStateOf(null) + + // Stripping failure dialog + var strippingFailureDialog by mutableStateOf(null) + private set + val iMetaAttachments = IMetaAttachments() var nip95attachments by mutableStateOf>>(emptyList()) @@ -349,6 +356,21 @@ open class ChannelNewMessageViewModel : account, context, stripMetadata = uploadState.stripMetadata, + onStrippingFailed = { + suspendCancellableCoroutine { continuation -> + strippingFailureDialog = + StrippingFailureState( + onConfirm = { + strippingFailureDialog = null + continuation.resume(true) {} + }, + onCancel = { + strippingFailureDialog = null + continuation.resume(false) {} + }, + ) + } + }, ) if (results.allGood) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/EditFieldRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/EditFieldRow.kt index 1e66b8f75..9972d2c42 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/EditFieldRow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/EditFieldRow.kt @@ -26,6 +26,8 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -70,6 +72,24 @@ fun EditFieldRow( nav.popBack() } + channelScreenModel.strippingFailureDialog?.let { dialogState -> + AlertDialog( + onDismissRequest = { dialogState.onCancel() }, + title = { Text(stringRes(R.string.metadata_strip_failed_title)) }, + text = { Text(stringRes(R.string.metadata_strip_failed_body)) }, + confirmButton = { + Button(onClick = { dialogState.onConfirm() }) { + Text(stringRes(R.string.metadata_strip_failed_upload)) + } + }, + dismissButton = { + Button(onClick = { dialogState.onCancel() }) { + Text(stringRes(R.string.cancel)) + } + }, + ) + } + channelScreenModel.replyTo.value?.let { DisplayReplyingToNote(it, accountViewModel, nav) { channelScreenModel.clearReply() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductScreen.kt index 05af11b0a..6f4ad9b82 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductScreen.kt @@ -33,10 +33,13 @@ import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment.Companion.CenterVertically @@ -137,6 +140,24 @@ fun NewProductScreen( ) { WatchAndLoadMyEmojiList(accountViewModel) + postViewModel.strippingFailureDialog?.let { dialogState -> + AlertDialog( + onDismissRequest = { dialogState.onCancel() }, + title = { Text(stringRes(R.string.metadata_strip_failed_title)) }, + text = { Text(stringRes(R.string.metadata_strip_failed_body)) }, + confirmButton = { + Button(onClick = { dialogState.onConfirm() }) { + Text(stringRes(R.string.metadata_strip_failed_upload)) + } + }, + dismissButton = { + Button(onClick = { dialogState.onCancel() }) { + Text(stringRes(R.string.cancel)) + } + }, + ) + } + BackHandler { accountViewModel.launchSigner { postViewModel.sendDraftSync() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductViewModel.kt index 3aedd419c..718359e38 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductViewModel.kt @@ -42,6 +42,7 @@ import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.service.location.LocationState import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator +import com.vitorpamplona.amethyst.service.uploads.StrippingFailureState import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName @@ -100,6 +101,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine @Stable open class NewProductViewModel : @@ -146,6 +148,10 @@ open class NewProductViewModel : // Images and Videos var multiOrchestrator by mutableStateOf(null) + // Stripping failure dialog + var strippingFailureDialog by mutableStateOf(null) + private set + // Classifieds var title by mutableStateOf(TextFieldValue("")) var price by mutableStateOf(TextFieldValue("")) @@ -411,6 +417,21 @@ open class NewProductViewModel : myAccount, context, stripMetadata = stripMetadata, + onStrippingFailed = { + suspendCancellableCoroutine { continuation -> + strippingFailureDialog = + StrippingFailureState( + onConfirm = { + strippingFailureDialog = null + continuation.resume(true) {} + }, + onCancel = { + strippingFailureDialog = null + continuation.resume(false) {} + }, + ) + } + }, ) if (results.allGood) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt index b74d08f26..09ac0dd61 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt @@ -39,6 +39,8 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Poll +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -46,6 +48,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Switch +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect @@ -179,6 +182,24 @@ private fun NewPostScreenInner( ) { WatchAndLoadMyEmojiList(accountViewModel) + postViewModel.strippingFailureDialog?.let { dialogState -> + AlertDialog( + onDismissRequest = { dialogState.onCancel() }, + title = { Text(stringRes(R.string.metadata_strip_failed_title)) }, + text = { Text(stringRes(R.string.metadata_strip_failed_body)) }, + confirmButton = { + Button(onClick = { dialogState.onConfirm() }) { + Text(stringRes(R.string.metadata_strip_failed_upload)) + } + }, + dismissButton = { + Button(onClick = { dialogState.onCancel() }) { + Text(stringRes(R.string.cancel)) + } + }, + ) + } + BackHandler { accountViewModel.launchSigner { postViewModel.sendDraftSync() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt index fe4daa50d..74c2679fc 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt @@ -45,6 +45,7 @@ import com.vitorpamplona.amethyst.service.location.LocationState import com.vitorpamplona.amethyst.service.uploads.CompressorQuality import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator +import com.vitorpamplona.amethyst.service.uploads.StrippingFailureState import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.service.uploads.UploadingState import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger @@ -133,6 +134,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine enum class UserSuggestionAnchor { MAIN_MESSAGE, @@ -191,6 +193,10 @@ open class ShortNotePostViewModel : // Images and Videos var multiOrchestrator by mutableStateOf(null) + // Stripping failure dialog + var strippingFailureDialog by mutableStateOf(null) + private set + // Voice Messages var voiceRecording by mutableStateOf(null) var voiceLocalFile by mutableStateOf(null) @@ -846,6 +852,21 @@ open class ShortNotePostViewModel : context, useH265, stripMetadata, + onStrippingFailed = { + suspendCancellableCoroutine { continuation -> + strippingFailureDialog = + StrippingFailureState( + onConfirm = { + strippingFailureDialog = null + continuation.resume(true) {} + }, + onCancel = { + strippingFailureDialog = null + continuation.resume(false) {} + }, + ) + } + }, ) if (results.allGood) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageScreen.kt index a468d605c..8c5e7463c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageScreen.kt @@ -33,6 +33,8 @@ import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme @@ -131,6 +133,24 @@ fun NewPublicMessageScreen( WatchAndLoadMyEmojiList(accountViewModel) + postViewModel.strippingFailureDialog?.let { dialogState -> + AlertDialog( + onDismissRequest = { dialogState.onCancel() }, + title = { Text(stringRes(R.string.metadata_strip_failed_title)) }, + text = { Text(stringRes(R.string.metadata_strip_failed_body)) }, + confirmButton = { + Button(onClick = { dialogState.onConfirm() }) { + Text(stringRes(R.string.metadata_strip_failed_upload)) + } + }, + dismissButton = { + Button(onClick = { dialogState.onCancel() }) { + Text(stringRes(R.string.cancel)) + } + }, + ) + } + BackHandler { accountViewModel.launchSigner { postViewModel.sendDraftSync() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageViewModel.kt index aeda3d043..a6bd5ebbc 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageViewModel.kt @@ -42,6 +42,7 @@ import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.service.location.LocationState import com.vitorpamplona.amethyst.service.uploads.MediaCompressor import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator +import com.vitorpamplona.amethyst.service.uploads.StrippingFailureState import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName @@ -114,6 +115,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine @Stable class NewPublicMessageViewModel : @@ -164,6 +166,10 @@ class NewPublicMessageViewModel : // Images and Videos var multiOrchestrator by mutableStateOf(null) + // Stripping failure dialog + var strippingFailureDialog by mutableStateOf(null) + private set + // Invoices var canAddInvoice by mutableStateOf(false) var wantsInvoice by mutableStateOf(false) @@ -455,6 +461,21 @@ class NewPublicMessageViewModel : account, context, stripMetadata = stripMetadata, + onStrippingFailed = { + suspendCancellableCoroutine { continuation -> + strippingFailureDialog = + StrippingFailureState( + onConfirm = { + strippingFailureDialog = null + continuation.resume(true) {} + }, + onCancel = { + strippingFailureDialog = null + continuation.resume(false) {} + }, + ) + } + }, ) if (results.allGood) {