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:
+5
@@ -37,6 +37,11 @@ import kotlinx.coroutines.flow.map
|
|||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import kotlin.coroutines.cancellation.CancellationException
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
|
|
||||||
|
data class StrippingFailureState(
|
||||||
|
val onConfirm: () -> Unit,
|
||||||
|
val onCancel: () -> Unit,
|
||||||
|
)
|
||||||
|
|
||||||
sealed class UploadingState {
|
sealed class UploadingState {
|
||||||
data object Ready : UploadingState()
|
data object Ready : UploadingState()
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ import androidx.compose.foundation.text.KeyboardOptions
|
|||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.CurrencyBitcoin
|
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.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
@@ -125,6 +127,24 @@ fun EditPostView(
|
|||||||
postViewModel.load(edit, versionLookingAt)
|
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(
|
Dialog(
|
||||||
onDismissRequest = { onClose() },
|
onDismissRequest = { onClose() },
|
||||||
properties =
|
properties =
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import com.vitorpamplona.amethyst.model.Note
|
|||||||
import com.vitorpamplona.amethyst.model.User
|
import com.vitorpamplona.amethyst.model.User
|
||||||
import com.vitorpamplona.amethyst.service.uploads.MediaCompressor
|
import com.vitorpamplona.amethyst.service.uploads.MediaCompressor
|
||||||
import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator
|
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.UploadOrchestrator
|
||||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
|
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
|
||||||
import com.vitorpamplona.amethyst.ui.actions.uploads.MediaUploadTracker
|
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.collections.immutable.ImmutableList
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
open class EditPostViewModel : ViewModel() {
|
open class EditPostViewModel : ViewModel() {
|
||||||
@@ -90,6 +92,10 @@ open class EditPostViewModel : ViewModel() {
|
|||||||
// Images and Videos
|
// Images and Videos
|
||||||
var multiOrchestrator by mutableStateOf<MultiOrchestrator?>(null)
|
var multiOrchestrator by mutableStateOf<MultiOrchestrator?>(null)
|
||||||
|
|
||||||
|
// Stripping failure dialog
|
||||||
|
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(null)
|
||||||
|
private set
|
||||||
|
|
||||||
// Codec selection: false = H264, true = H265
|
// Codec selection: false = H264, true = H265
|
||||||
var useH265Codec by mutableStateOf(false)
|
var useH265Codec by mutableStateOf(false)
|
||||||
|
|
||||||
@@ -197,6 +203,21 @@ open class EditPostViewModel : ViewModel() {
|
|||||||
context,
|
context,
|
||||||
useH265Codec,
|
useH265Codec,
|
||||||
stripMetadata,
|
stripMetadata,
|
||||||
|
onStrippingFailed = {
|
||||||
|
suspendCancellableCoroutine { continuation ->
|
||||||
|
strippingFailureDialog =
|
||||||
|
StrippingFailureState(
|
||||||
|
onConfirm = {
|
||||||
|
strippingFailureDialog = null
|
||||||
|
continuation.resume(true) {}
|
||||||
|
},
|
||||||
|
onCancel = {
|
||||||
|
strippingFailureDialog = null
|
||||||
|
continuation.resume(false) {}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if (results.allGood) {
|
if (results.allGood) {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
|
|||||||
import com.vitorpamplona.amethyst.model.Account
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
import com.vitorpamplona.amethyst.service.uploads.MediaCompressor
|
import com.vitorpamplona.amethyst.service.uploads.MediaCompressor
|
||||||
import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator
|
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.UploadOrchestrator
|
||||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS
|
import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS
|
||||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
|
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
|
||||||
@@ -44,6 +45,7 @@ import kotlinx.collections.immutable.ImmutableList
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.joinAll
|
import kotlinx.coroutines.joinAll
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
open class NewMediaModel : ViewModel() {
|
open class NewMediaModel : ViewModel() {
|
||||||
@@ -59,6 +61,10 @@ open class NewMediaModel : ViewModel() {
|
|||||||
var multiOrchestrator by mutableStateOf<MultiOrchestrator?>(null)
|
var multiOrchestrator by mutableStateOf<MultiOrchestrator?>(null)
|
||||||
var onceUploaded: () -> Unit = {}
|
var onceUploaded: () -> Unit = {}
|
||||||
|
|
||||||
|
// Stripping failure dialog
|
||||||
|
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(null)
|
||||||
|
private set
|
||||||
|
|
||||||
// 0 = Low, 1 = Medium, 2 = High, 3=UNCOMPRESSED
|
// 0 = Low, 1 = Medium, 2 = High, 3=UNCOMPRESSED
|
||||||
var mediaQualitySlider by mutableIntStateOf(1)
|
var mediaQualitySlider by mutableIntStateOf(1)
|
||||||
|
|
||||||
@@ -120,6 +126,21 @@ open class NewMediaModel : ViewModel() {
|
|||||||
context,
|
context,
|
||||||
useH265Codec,
|
useH265Codec,
|
||||||
stripMetadata,
|
stripMetadata,
|
||||||
|
onStrippingFailed = {
|
||||||
|
suspendCancellableCoroutine { continuation ->
|
||||||
|
strippingFailureDialog =
|
||||||
|
StrippingFailureState(
|
||||||
|
onConfirm = {
|
||||||
|
strippingFailureDialog = null
|
||||||
|
continuation.resume(true) {}
|
||||||
|
},
|
||||||
|
onCancel = {
|
||||||
|
strippingFailureDialog = null
|
||||||
|
continuation.resume(false) {}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if (results.allGood) {
|
if (results.allGood) {
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
@@ -89,6 +91,24 @@ fun NewMediaView(
|
|||||||
postViewModel.load(account, uris)
|
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(
|
Dialog(
|
||||||
onDismissRequest = { onClose() },
|
onDismissRequest = { onClose() },
|
||||||
properties =
|
properties =
|
||||||
|
|||||||
+21
@@ -42,6 +42,7 @@ import com.vitorpamplona.amethyst.model.User
|
|||||||
import com.vitorpamplona.amethyst.service.location.LocationState
|
import com.vitorpamplona.amethyst.service.location.LocationState
|
||||||
import com.vitorpamplona.amethyst.service.uploads.MediaCompressor
|
import com.vitorpamplona.amethyst.service.uploads.MediaCompressor
|
||||||
import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator
|
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.UploadOrchestrator
|
||||||
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
|
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
|
||||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
|
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.StateFlow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
open class CommentPostViewModel :
|
open class CommentPostViewModel :
|
||||||
@@ -162,6 +164,10 @@ open class CommentPostViewModel :
|
|||||||
// Images and Videos
|
// Images and Videos
|
||||||
var multiOrchestrator by mutableStateOf<MultiOrchestrator?>(null)
|
var multiOrchestrator by mutableStateOf<MultiOrchestrator?>(null)
|
||||||
|
|
||||||
|
// Stripping failure dialog
|
||||||
|
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(null)
|
||||||
|
private set
|
||||||
|
|
||||||
// Invoices
|
// Invoices
|
||||||
var canAddInvoice by mutableStateOf(false)
|
var canAddInvoice by mutableStateOf(false)
|
||||||
var wantsInvoice by mutableStateOf(false)
|
var wantsInvoice by mutableStateOf(false)
|
||||||
@@ -503,6 +509,21 @@ open class CommentPostViewModel :
|
|||||||
account,
|
account,
|
||||||
context,
|
context,
|
||||||
stripMetadata = stripMetadata,
|
stripMetadata = stripMetadata,
|
||||||
|
onStrippingFailed = {
|
||||||
|
suspendCancellableCoroutine { continuation ->
|
||||||
|
strippingFailureDialog =
|
||||||
|
StrippingFailureState(
|
||||||
|
onConfirm = {
|
||||||
|
strippingFailureDialog = null
|
||||||
|
continuation.resume(true) {}
|
||||||
|
},
|
||||||
|
onCancel = {
|
||||||
|
strippingFailureDialog = null
|
||||||
|
continuation.resume(false) {}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if (results.allGood) {
|
if (results.allGood) {
|
||||||
|
|||||||
+21
@@ -34,10 +34,13 @@ import androidx.compose.foundation.layout.imePadding
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||||
@@ -140,6 +143,24 @@ fun GenericCommentPostScreen(
|
|||||||
) {
|
) {
|
||||||
WatchAndLoadMyEmojiList(accountViewModel)
|
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 {
|
BackHandler {
|
||||||
accountViewModel.launchSigner {
|
accountViewModel.launchSigner {
|
||||||
postViewModel.sendDraftSync()
|
postViewModel.sendDraftSync()
|
||||||
|
|||||||
+28
-2
@@ -39,6 +39,7 @@ import com.vitorpamplona.amethyst.model.LocalCache
|
|||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.model.User
|
import com.vitorpamplona.amethyst.model.User
|
||||||
import com.vitorpamplona.amethyst.service.location.LocationState
|
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.NewMessageTagger
|
||||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
|
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
|
||||||
import com.vitorpamplona.amethyst.ui.note.creators.draftTags.DraftTagState
|
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.StateFlow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class ChatNewMessageViewModel :
|
class ChatNewMessageViewModel :
|
||||||
@@ -133,6 +135,11 @@ class ChatNewMessageViewModel :
|
|||||||
val replyTo = mutableStateOf<Note?>(null)
|
val replyTo = mutableStateOf<Note?>(null)
|
||||||
|
|
||||||
var uploadState by mutableStateOf<ChatFileUploadState?>(null)
|
var uploadState by mutableStateOf<ChatFileUploadState?>(null)
|
||||||
|
|
||||||
|
// Stripping failure dialog
|
||||||
|
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(null)
|
||||||
|
private set
|
||||||
|
|
||||||
val iMetaAttachments = IMetaAttachments()
|
val iMetaAttachments = IMetaAttachments()
|
||||||
|
|
||||||
var uploadsWaitingToBeSent by mutableStateOf<List<SuccessfulUploads>>(emptyList())
|
var uploadsWaitingToBeSent by mutableStateOf<List<SuccessfulUploads>>(emptyList())
|
||||||
@@ -395,6 +402,21 @@ class ChatNewMessageViewModel :
|
|||||||
return location!!
|
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(
|
fun uploadAndHold(
|
||||||
onError: (title: String, message: String) -> Unit,
|
onError: (title: String, message: String) -> Unit,
|
||||||
context: Context,
|
context: Context,
|
||||||
@@ -413,13 +435,14 @@ class ChatNewMessageViewModel :
|
|||||||
pendingRetryMode = RetryMode.HOLD
|
pendingRetryMode = RetryMode.HOLD
|
||||||
},
|
},
|
||||||
context,
|
context,
|
||||||
|
onStrippingFailed = ::showStrippingFailureDialog,
|
||||||
) {
|
) {
|
||||||
uploadsWaitingToBeSent += it
|
uploadsWaitingToBeSent += it
|
||||||
draftTag.newVersion()
|
draftTag.newVersion()
|
||||||
onceUploaded()
|
onceUploaded()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ChatFileUploader(account).justUploadNIP04(uploadState, onError, context) {
|
ChatFileUploader(account).justUploadNIP04(uploadState, onError, context, onStrippingFailed = ::showStrippingFailureDialog) {
|
||||||
uploadsWaitingToBeSent += it
|
uploadsWaitingToBeSent += it
|
||||||
draftTag.newVersion()
|
draftTag.newVersion()
|
||||||
onceUploaded()
|
onceUploaded()
|
||||||
@@ -450,13 +473,14 @@ class ChatNewMessageViewModel :
|
|||||||
pendingRetryOnceUploaded = onceUploaded
|
pendingRetryOnceUploaded = onceUploaded
|
||||||
},
|
},
|
||||||
context,
|
context,
|
||||||
|
onStrippingFailed = ::showStrippingFailureDialog,
|
||||||
) {
|
) {
|
||||||
ChatFileSender(room, account).sendNIP17(it)
|
ChatFileSender(room, account).sendNIP17(it)
|
||||||
draftTag.newVersion()
|
draftTag.newVersion()
|
||||||
onceUploaded()
|
onceUploaded()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ChatFileUploader(account).justUploadNIP04(uploadState, onError, context) {
|
ChatFileUploader(account).justUploadNIP04(uploadState, onError, context, onStrippingFailed = ::showStrippingFailureDialog) {
|
||||||
ChatFileSender(room, account).sendNIP04(it)
|
ChatFileSender(room, account).sendNIP04(it)
|
||||||
draftTag.newVersion()
|
draftTag.newVersion()
|
||||||
onceUploaded()
|
onceUploaded()
|
||||||
@@ -505,6 +529,7 @@ class ChatNewMessageViewModel :
|
|||||||
uploadState,
|
uploadState,
|
||||||
onError ?: accountViewModel.toastManager::toast,
|
onError ?: accountViewModel.toastManager::toast,
|
||||||
context,
|
context,
|
||||||
|
onStrippingFailed = ::showStrippingFailureDialog,
|
||||||
) {
|
) {
|
||||||
uploadsWaitingToBeSent += it
|
uploadsWaitingToBeSent += it
|
||||||
draftTag.newVersion()
|
draftTag.newVersion()
|
||||||
@@ -518,6 +543,7 @@ class ChatNewMessageViewModel :
|
|||||||
uploadState,
|
uploadState,
|
||||||
onError ?: accountViewModel.toastManager::toast,
|
onError ?: accountViewModel.toastManager::toast,
|
||||||
context,
|
context,
|
||||||
|
onStrippingFailed = ::showStrippingFailureDialog,
|
||||||
) {
|
) {
|
||||||
ChatFileSender(room, account).sendNIP17(it)
|
ChatFileSender(room, account).sendNIP17(it)
|
||||||
draftTag.newVersion()
|
draftTag.newVersion()
|
||||||
|
|||||||
+18
@@ -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 ->
|
channelScreenModel.encryptedUploadErrorTitle?.let { title ->
|
||||||
EncryptedUploadErrorDialog(
|
EncryptedUploadErrorDialog(
|
||||||
title = title,
|
title = title,
|
||||||
|
|||||||
+7
-1
@@ -41,6 +41,7 @@ class ChatFileUploader(
|
|||||||
onError: (title: String, message: String) -> Unit,
|
onError: (title: String, message: String) -> Unit,
|
||||||
onEncryptedUploadError: (title: String, message: String) -> Unit,
|
onEncryptedUploadError: (title: String, message: String) -> Unit,
|
||||||
context: Context,
|
context: Context,
|
||||||
|
onStrippingFailed: suspend () -> Boolean = { true },
|
||||||
onceUploaded: suspend (List<SuccessfulUploads>) -> Unit,
|
onceUploaded: suspend (List<SuccessfulUploads>) -> Unit,
|
||||||
) {
|
) {
|
||||||
val orchestrator = viewState.multiOrchestrator ?: return
|
val orchestrator = viewState.multiOrchestrator ?: return
|
||||||
@@ -59,6 +60,7 @@ class ChatFileUploader(
|
|||||||
account,
|
account,
|
||||||
context,
|
context,
|
||||||
stripMetadata = viewState.stripMetadata,
|
stripMetadata = viewState.stripMetadata,
|
||||||
|
onStrippingFailed = onStrippingFailed,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (results.allGood) {
|
if (results.allGood) {
|
||||||
@@ -82,7 +84,7 @@ class ChatFileUploader(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
justUploadNIP17Unencrypted(viewState, onError, context, onceUploaded)
|
justUploadNIP17Unencrypted(viewState, onError, context, onStrippingFailed, onceUploaded)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewState.mediaUploadTracker.finishUpload()
|
viewState.mediaUploadTracker.finishUpload()
|
||||||
@@ -92,6 +94,7 @@ class ChatFileUploader(
|
|||||||
viewState: ChatFileUploadState,
|
viewState: ChatFileUploadState,
|
||||||
onError: (title: String, message: String) -> Unit,
|
onError: (title: String, message: String) -> Unit,
|
||||||
context: Context,
|
context: Context,
|
||||||
|
onStrippingFailed: suspend () -> Boolean = { true },
|
||||||
onceUploaded: suspend (List<SuccessfulUploads>) -> Unit,
|
onceUploaded: suspend (List<SuccessfulUploads>) -> Unit,
|
||||||
) {
|
) {
|
||||||
val orchestrator = viewState.multiOrchestrator ?: return
|
val orchestrator = viewState.multiOrchestrator ?: return
|
||||||
@@ -106,6 +109,7 @@ class ChatFileUploader(
|
|||||||
account,
|
account,
|
||||||
context,
|
context,
|
||||||
stripMetadata = viewState.stripMetadata,
|
stripMetadata = viewState.stripMetadata,
|
||||||
|
onStrippingFailed = onStrippingFailed,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (results.allGood) {
|
if (results.allGood) {
|
||||||
@@ -137,6 +141,7 @@ class ChatFileUploader(
|
|||||||
viewState: ChatFileUploadState,
|
viewState: ChatFileUploadState,
|
||||||
onError: (title: String, message: String) -> Unit,
|
onError: (title: String, message: String) -> Unit,
|
||||||
context: Context,
|
context: Context,
|
||||||
|
onStrippingFailed: suspend () -> Boolean = { true },
|
||||||
onceUploaded: suspend (List<SuccessfulUploads>) -> Unit,
|
onceUploaded: suspend (List<SuccessfulUploads>) -> Unit,
|
||||||
) {
|
) {
|
||||||
val orchestrator = viewState.multiOrchestrator ?: return
|
val orchestrator = viewState.multiOrchestrator ?: return
|
||||||
@@ -151,6 +156,7 @@ class ChatFileUploader(
|
|||||||
account,
|
account,
|
||||||
context,
|
context,
|
||||||
stripMetadata = viewState.stripMetadata,
|
stripMetadata = viewState.stripMetadata,
|
||||||
|
onStrippingFailed = onStrippingFailed,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (results.allGood) {
|
if (results.allGood) {
|
||||||
|
|||||||
+22
@@ -46,6 +46,7 @@ import com.vitorpamplona.amethyst.model.Note
|
|||||||
import com.vitorpamplona.amethyst.model.User
|
import com.vitorpamplona.amethyst.model.User
|
||||||
import com.vitorpamplona.amethyst.service.location.LocationState
|
import com.vitorpamplona.amethyst.service.location.LocationState
|
||||||
import com.vitorpamplona.amethyst.service.uploads.MediaCompressor
|
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.service.uploads.UploadOrchestrator
|
||||||
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
|
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
|
||||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
|
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.StateFlow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
open class ChannelNewMessageViewModel :
|
open class ChannelNewMessageViewModel :
|
||||||
@@ -129,6 +131,11 @@ open class ChannelNewMessageViewModel :
|
|||||||
val replyTo = mutableStateOf<Note?>(null)
|
val replyTo = mutableStateOf<Note?>(null)
|
||||||
|
|
||||||
var uploadState by mutableStateOf<ChatFileUploadState?>(null)
|
var uploadState by mutableStateOf<ChatFileUploadState?>(null)
|
||||||
|
|
||||||
|
// Stripping failure dialog
|
||||||
|
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(null)
|
||||||
|
private set
|
||||||
|
|
||||||
val iMetaAttachments = IMetaAttachments()
|
val iMetaAttachments = IMetaAttachments()
|
||||||
var nip95attachments by mutableStateOf<List<Pair<FileStorageEvent, FileStorageHeaderEvent>>>(emptyList())
|
var nip95attachments by mutableStateOf<List<Pair<FileStorageEvent, FileStorageHeaderEvent>>>(emptyList())
|
||||||
|
|
||||||
@@ -349,6 +356,21 @@ open class ChannelNewMessageViewModel :
|
|||||||
account,
|
account,
|
||||||
context,
|
context,
|
||||||
stripMetadata = uploadState.stripMetadata,
|
stripMetadata = uploadState.stripMetadata,
|
||||||
|
onStrippingFailed = {
|
||||||
|
suspendCancellableCoroutine { continuation ->
|
||||||
|
strippingFailureDialog =
|
||||||
|
StrippingFailureState(
|
||||||
|
onConfirm = {
|
||||||
|
strippingFailureDialog = null
|
||||||
|
continuation.resume(true) {}
|
||||||
|
},
|
||||||
|
onCancel = {
|
||||||
|
strippingFailureDialog = null
|
||||||
|
continuation.resume(false) {}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if (results.allGood) {
|
if (results.allGood) {
|
||||||
|
|||||||
+20
@@ -26,6 +26,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
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.LocalTextStyle
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -70,6 +72,24 @@ fun EditFieldRow(
|
|||||||
nav.popBack()
|
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 {
|
channelScreenModel.replyTo.value?.let {
|
||||||
DisplayReplyingToNote(it, accountViewModel, nav) {
|
DisplayReplyingToNote(it, accountViewModel, nav) {
|
||||||
channelScreenModel.clearReply()
|
channelScreenModel.clearReply()
|
||||||
|
|||||||
+21
@@ -33,10 +33,13 @@ import androidx.compose.foundation.layout.imePadding
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||||
@@ -137,6 +140,24 @@ fun NewProductScreen(
|
|||||||
) {
|
) {
|
||||||
WatchAndLoadMyEmojiList(accountViewModel)
|
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 {
|
BackHandler {
|
||||||
accountViewModel.launchSigner {
|
accountViewModel.launchSigner {
|
||||||
postViewModel.sendDraftSync()
|
postViewModel.sendDraftSync()
|
||||||
|
|||||||
+21
@@ -42,6 +42,7 @@ import com.vitorpamplona.amethyst.model.User
|
|||||||
import com.vitorpamplona.amethyst.service.location.LocationState
|
import com.vitorpamplona.amethyst.service.location.LocationState
|
||||||
import com.vitorpamplona.amethyst.service.uploads.MediaCompressor
|
import com.vitorpamplona.amethyst.service.uploads.MediaCompressor
|
||||||
import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator
|
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.UploadOrchestrator
|
||||||
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
|
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
|
||||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
|
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.StateFlow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
open class NewProductViewModel :
|
open class NewProductViewModel :
|
||||||
@@ -146,6 +148,10 @@ open class NewProductViewModel :
|
|||||||
// Images and Videos
|
// Images and Videos
|
||||||
var multiOrchestrator by mutableStateOf<MultiOrchestrator?>(null)
|
var multiOrchestrator by mutableStateOf<MultiOrchestrator?>(null)
|
||||||
|
|
||||||
|
// Stripping failure dialog
|
||||||
|
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(null)
|
||||||
|
private set
|
||||||
|
|
||||||
// Classifieds
|
// Classifieds
|
||||||
var title by mutableStateOf(TextFieldValue(""))
|
var title by mutableStateOf(TextFieldValue(""))
|
||||||
var price by mutableStateOf(TextFieldValue(""))
|
var price by mutableStateOf(TextFieldValue(""))
|
||||||
@@ -411,6 +417,21 @@ open class NewProductViewModel :
|
|||||||
myAccount,
|
myAccount,
|
||||||
context,
|
context,
|
||||||
stripMetadata = stripMetadata,
|
stripMetadata = stripMetadata,
|
||||||
|
onStrippingFailed = {
|
||||||
|
suspendCancellableCoroutine { continuation ->
|
||||||
|
strippingFailureDialog =
|
||||||
|
StrippingFailureState(
|
||||||
|
onConfirm = {
|
||||||
|
strippingFailureDialog = null
|
||||||
|
continuation.resume(true) {}
|
||||||
|
},
|
||||||
|
onCancel = {
|
||||||
|
strippingFailureDialog = null
|
||||||
|
continuation.resume(false) {}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if (results.allGood) {
|
if (results.allGood) {
|
||||||
|
|||||||
+21
@@ -39,6 +39,8 @@ import androidx.compose.foundation.rememberScrollState
|
|||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Poll
|
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.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
@@ -46,6 +48,7 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@@ -179,6 +182,24 @@ private fun NewPostScreenInner(
|
|||||||
) {
|
) {
|
||||||
WatchAndLoadMyEmojiList(accountViewModel)
|
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 {
|
BackHandler {
|
||||||
accountViewModel.launchSigner {
|
accountViewModel.launchSigner {
|
||||||
postViewModel.sendDraftSync()
|
postViewModel.sendDraftSync()
|
||||||
|
|||||||
+21
@@ -45,6 +45,7 @@ import com.vitorpamplona.amethyst.service.location.LocationState
|
|||||||
import com.vitorpamplona.amethyst.service.uploads.CompressorQuality
|
import com.vitorpamplona.amethyst.service.uploads.CompressorQuality
|
||||||
import com.vitorpamplona.amethyst.service.uploads.MediaCompressor
|
import com.vitorpamplona.amethyst.service.uploads.MediaCompressor
|
||||||
import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator
|
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.UploadOrchestrator
|
||||||
import com.vitorpamplona.amethyst.service.uploads.UploadingState
|
import com.vitorpamplona.amethyst.service.uploads.UploadingState
|
||||||
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
|
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
|
||||||
@@ -133,6 +134,7 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
|
||||||
enum class UserSuggestionAnchor {
|
enum class UserSuggestionAnchor {
|
||||||
MAIN_MESSAGE,
|
MAIN_MESSAGE,
|
||||||
@@ -191,6 +193,10 @@ open class ShortNotePostViewModel :
|
|||||||
// Images and Videos
|
// Images and Videos
|
||||||
var multiOrchestrator by mutableStateOf<MultiOrchestrator?>(null)
|
var multiOrchestrator by mutableStateOf<MultiOrchestrator?>(null)
|
||||||
|
|
||||||
|
// Stripping failure dialog
|
||||||
|
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(null)
|
||||||
|
private set
|
||||||
|
|
||||||
// Voice Messages
|
// Voice Messages
|
||||||
var voiceRecording by mutableStateOf<RecordingResult?>(null)
|
var voiceRecording by mutableStateOf<RecordingResult?>(null)
|
||||||
var voiceLocalFile by mutableStateOf<java.io.File?>(null)
|
var voiceLocalFile by mutableStateOf<java.io.File?>(null)
|
||||||
@@ -846,6 +852,21 @@ open class ShortNotePostViewModel :
|
|||||||
context,
|
context,
|
||||||
useH265,
|
useH265,
|
||||||
stripMetadata,
|
stripMetadata,
|
||||||
|
onStrippingFailed = {
|
||||||
|
suspendCancellableCoroutine { continuation ->
|
||||||
|
strippingFailureDialog =
|
||||||
|
StrippingFailureState(
|
||||||
|
onConfirm = {
|
||||||
|
strippingFailureDialog = null
|
||||||
|
continuation.resume(true) {}
|
||||||
|
},
|
||||||
|
onCancel = {
|
||||||
|
strippingFailureDialog = null
|
||||||
|
continuation.resume(false) {}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if (results.allGood) {
|
if (results.allGood) {
|
||||||
|
|||||||
+20
@@ -33,6 +33,8 @@ import androidx.compose.foundation.layout.imePadding
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -131,6 +133,24 @@ fun NewPublicMessageScreen(
|
|||||||
|
|
||||||
WatchAndLoadMyEmojiList(accountViewModel)
|
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 {
|
BackHandler {
|
||||||
accountViewModel.launchSigner {
|
accountViewModel.launchSigner {
|
||||||
postViewModel.sendDraftSync()
|
postViewModel.sendDraftSync()
|
||||||
|
|||||||
+21
@@ -42,6 +42,7 @@ import com.vitorpamplona.amethyst.model.User
|
|||||||
import com.vitorpamplona.amethyst.service.location.LocationState
|
import com.vitorpamplona.amethyst.service.location.LocationState
|
||||||
import com.vitorpamplona.amethyst.service.uploads.MediaCompressor
|
import com.vitorpamplona.amethyst.service.uploads.MediaCompressor
|
||||||
import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator
|
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.UploadOrchestrator
|
||||||
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
|
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
|
||||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
|
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.StateFlow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class NewPublicMessageViewModel :
|
class NewPublicMessageViewModel :
|
||||||
@@ -164,6 +166,10 @@ class NewPublicMessageViewModel :
|
|||||||
// Images and Videos
|
// Images and Videos
|
||||||
var multiOrchestrator by mutableStateOf<MultiOrchestrator?>(null)
|
var multiOrchestrator by mutableStateOf<MultiOrchestrator?>(null)
|
||||||
|
|
||||||
|
// Stripping failure dialog
|
||||||
|
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(null)
|
||||||
|
private set
|
||||||
|
|
||||||
// Invoices
|
// Invoices
|
||||||
var canAddInvoice by mutableStateOf(false)
|
var canAddInvoice by mutableStateOf(false)
|
||||||
var wantsInvoice by mutableStateOf(false)
|
var wantsInvoice by mutableStateOf(false)
|
||||||
@@ -455,6 +461,21 @@ class NewPublicMessageViewModel :
|
|||||||
account,
|
account,
|
||||||
context,
|
context,
|
||||||
stripMetadata = stripMetadata,
|
stripMetadata = stripMetadata,
|
||||||
|
onStrippingFailed = {
|
||||||
|
suspendCancellableCoroutine { continuation ->
|
||||||
|
strippingFailureDialog =
|
||||||
|
StrippingFailureState(
|
||||||
|
onConfirm = {
|
||||||
|
strippingFailureDialog = null
|
||||||
|
continuation.resume(true) {}
|
||||||
|
},
|
||||||
|
onCancel = {
|
||||||
|
strippingFailureDialog = null
|
||||||
|
continuation.resume(false) {}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if (results.allGood) {
|
if (results.allGood) {
|
||||||
|
|||||||
Reference in New Issue
Block a user