feat: add metadata stripping failure dialog to all upload ViewModels

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
davotoula
2026-03-18 11:12:36 +01:00
parent c0632e17bd
commit 20ab105e44
18 changed files with 349 additions and 3 deletions
@@ -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()
@@ -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 =
@@ -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<MultiOrchestrator?>(null)
// Stripping failure dialog
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(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) {
@@ -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<MultiOrchestrator?>(null)
var onceUploaded: () -> Unit = {}
// Stripping failure dialog
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(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) {
@@ -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 =
@@ -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<MultiOrchestrator?>(null)
// Stripping failure dialog
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(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) {
@@ -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()
@@ -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<Note?>(null)
var uploadState by mutableStateOf<ChatFileUploadState?>(null)
// Stripping failure dialog
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(null)
private set
val iMetaAttachments = IMetaAttachments()
var uploadsWaitingToBeSent by mutableStateOf<List<SuccessfulUploads>>(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()
@@ -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,
@@ -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<SuccessfulUploads>) -> 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<SuccessfulUploads>) -> 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<SuccessfulUploads>) -> Unit,
) {
val orchestrator = viewState.multiOrchestrator ?: return
@@ -151,6 +156,7 @@ class ChatFileUploader(
account,
context,
stripMetadata = viewState.stripMetadata,
onStrippingFailed = onStrippingFailed,
)
if (results.allGood) {
@@ -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<Note?>(null)
var uploadState by mutableStateOf<ChatFileUploadState?>(null)
// Stripping failure dialog
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(null)
private set
val iMetaAttachments = IMetaAttachments()
var nip95attachments by mutableStateOf<List<Pair<FileStorageEvent, FileStorageHeaderEvent>>>(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) {
@@ -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()
@@ -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()
@@ -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<MultiOrchestrator?>(null)
// Stripping failure dialog
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(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) {
@@ -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()
@@ -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<MultiOrchestrator?>(null)
// Stripping failure dialog
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(null)
private set
// Voice Messages
var voiceRecording by mutableStateOf<RecordingResult?>(null)
var voiceLocalFile by mutableStateOf<java.io.File?>(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) {
@@ -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()
@@ -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<MultiOrchestrator?>(null)
// Stripping failure dialog
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(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) {