- Hide privacy toggle when video compression is selected (compression
already handles metadata stripping) - Pass stripMetadata=false when toggle is hidden for compressed video - Hide compression quality for audio and other non-compressible media types - refactor
This commit is contained in:
+42
-26
@@ -252,11 +252,7 @@ class MetadataStripper {
|
||||
return StrippingResult(uri, false)
|
||||
}
|
||||
|
||||
val extension =
|
||||
when {
|
||||
primaryMime.contains("mp4a") || primaryMime.contains("aac") -> ".m4a"
|
||||
else -> ".mp4"
|
||||
}
|
||||
val extension = ".m4a"
|
||||
|
||||
tempOutputFile = File.createTempFile("stripped_audio_", extension, context.cacheDir)
|
||||
muxer = MediaMuxer(tempOutputFile.absolutePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
|
||||
@@ -321,41 +317,61 @@ class MetadataStripper {
|
||||
}
|
||||
} ?: return StrippingResult(uri, false)
|
||||
|
||||
val bytes = tempInputFile.readBytes()
|
||||
val fileSize = tempInputFile.length().toInt()
|
||||
var startOffset = 0
|
||||
var endOffset = bytes.size
|
||||
var endOffset = fileSize
|
||||
|
||||
// Strip ID3v2 header (at beginning of file)
|
||||
if (bytes.size >= 10 &&
|
||||
bytes[0] == 'I'.code.toByte() &&
|
||||
bytes[1] == 'D'.code.toByte() &&
|
||||
bytes[2] == '3'.code.toByte()
|
||||
// Read first 10 bytes to check for ID3v2 header
|
||||
val header = ByteArray(10)
|
||||
tempInputFile.inputStream().use { it.read(header) }
|
||||
|
||||
if (fileSize >= 10 &&
|
||||
header[0] == 'I'.code.toByte() &&
|
||||
header[1] == 'D'.code.toByte() &&
|
||||
header[2] == '3'.code.toByte()
|
||||
) {
|
||||
val size =
|
||||
(bytes[6].toInt() and 0x7F shl 21) or
|
||||
(bytes[7].toInt() and 0x7F shl 14) or
|
||||
(bytes[8].toInt() and 0x7F shl 7) or
|
||||
(bytes[9].toInt() and 0x7F)
|
||||
(header[6].toInt() and 0x7F shl 21) or
|
||||
(header[7].toInt() and 0x7F shl 14) or
|
||||
(header[8].toInt() and 0x7F shl 7) or
|
||||
(header[9].toInt() and 0x7F)
|
||||
startOffset = 10 + size
|
||||
}
|
||||
|
||||
// Strip ID3v1 tag (last 128 bytes)
|
||||
if (endOffset - startOffset >= 128 &&
|
||||
bytes[endOffset - 128] == 'T'.code.toByte() &&
|
||||
bytes[endOffset - 127] == 'A'.code.toByte() &&
|
||||
bytes[endOffset - 126] == 'G'.code.toByte()
|
||||
) {
|
||||
endOffset -= 128
|
||||
// Read last 128 bytes to check for ID3v1 tag
|
||||
if (endOffset - startOffset >= 128) {
|
||||
val tail = ByteArray(128)
|
||||
java.io.RandomAccessFile(tempInputFile, "r").use { raf ->
|
||||
raf.seek((endOffset - 128).toLong())
|
||||
raf.readFully(tail)
|
||||
}
|
||||
if (tail[0] == 'T'.code.toByte() &&
|
||||
tail[1] == 'A'.code.toByte() &&
|
||||
tail[2] == 'G'.code.toByte()
|
||||
) {
|
||||
endOffset -= 128
|
||||
}
|
||||
}
|
||||
|
||||
if (startOffset == 0 && endOffset == bytes.size) {
|
||||
if (startOffset == 0 && endOffset == fileSize) {
|
||||
tempInputFile.delete()
|
||||
return StrippingResult(uri, true) // no tags found, already clean
|
||||
}
|
||||
|
||||
val tempOutputFile = File.createTempFile("stripped_mp3_", ".mp3", context.cacheDir)
|
||||
tempOutputFile.outputStream().use { output ->
|
||||
output.write(bytes, startOffset, endOffset - startOffset)
|
||||
java.io.RandomAccessFile(tempInputFile, "r").use { raf ->
|
||||
raf.seek(startOffset.toLong())
|
||||
tempOutputFile.outputStream().use { output ->
|
||||
val buffer = ByteArray(8192)
|
||||
var remaining = endOffset - startOffset
|
||||
while (remaining > 0) {
|
||||
val toRead = minOf(buffer.size, remaining)
|
||||
val read = raf.read(buffer, 0, toRead)
|
||||
if (read <= 0) break
|
||||
output.write(buffer, 0, read)
|
||||
remaining -= read
|
||||
}
|
||||
}
|
||||
}
|
||||
tempInputFile.delete()
|
||||
|
||||
|
||||
@@ -39,8 +39,6 @@ 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
|
||||
@@ -127,23 +125,7 @@ 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))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
StrippingFailureDialog(postViewModel.strippingFailureDialog)
|
||||
|
||||
Dialog(
|
||||
onDismissRequest = { onClose() },
|
||||
|
||||
@@ -96,6 +96,21 @@ open class EditPostViewModel : ViewModel() {
|
||||
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(null)
|
||||
private set
|
||||
|
||||
private suspend fun showStrippingFailureDialog(): Boolean =
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
strippingFailureDialog =
|
||||
StrippingFailureState(
|
||||
onConfirm = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(true) {}
|
||||
},
|
||||
onCancel = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(false) {}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Codec selection: false = H264, true = H265
|
||||
var useH265Codec by mutableStateOf(false)
|
||||
|
||||
@@ -203,21 +218,7 @@ open class EditPostViewModel : ViewModel() {
|
||||
context,
|
||||
useH265Codec,
|
||||
stripMetadata,
|
||||
onStrippingFailed = {
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
strippingFailureDialog =
|
||||
StrippingFailureState(
|
||||
onConfirm = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(true) {}
|
||||
},
|
||||
onCancel = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(false) {}
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
onStrippingFailed = ::showStrippingFailureDialog,
|
||||
)
|
||||
|
||||
if (results.allGood) {
|
||||
|
||||
@@ -65,6 +65,21 @@ open class NewMediaModel : ViewModel() {
|
||||
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(null)
|
||||
private set
|
||||
|
||||
private suspend fun showStrippingFailureDialog(): Boolean =
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
strippingFailureDialog =
|
||||
StrippingFailureState(
|
||||
onConfirm = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(true) {}
|
||||
},
|
||||
onCancel = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(false) {}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// 0 = Low, 1 = Medium, 2 = High, 3=UNCOMPRESSED
|
||||
var mediaQualitySlider by mutableIntStateOf(1)
|
||||
|
||||
@@ -126,21 +141,7 @@ open class NewMediaModel : ViewModel() {
|
||||
context,
|
||||
useH265Codec,
|
||||
stripMetadata,
|
||||
onStrippingFailed = {
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
strippingFailureDialog =
|
||||
StrippingFailureState(
|
||||
onConfirm = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(true) {}
|
||||
},
|
||||
onCancel = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(false) {}
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
onStrippingFailed = ::showStrippingFailureDialog,
|
||||
)
|
||||
|
||||
if (results.allGood) {
|
||||
|
||||
@@ -32,8 +32,6 @@ 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
|
||||
@@ -91,23 +89,7 @@ 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))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
StrippingFailureDialog(postViewModel.strippingFailureDialog)
|
||||
|
||||
Dialog(
|
||||
onDismissRequest = { onClose() },
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.actions
|
||||
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.service.uploads.StrippingFailureState
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
|
||||
@Composable
|
||||
fun StrippingFailureDialog(dialogState: StrippingFailureState?) {
|
||||
dialogState ?: return
|
||||
|
||||
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))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
+44
-36
@@ -250,47 +250,52 @@ fun ImageVideoDescription(
|
||||
)
|
||||
}
|
||||
|
||||
SettingSwitchItem(
|
||||
title = R.string.strip_metadata_label,
|
||||
description = R.string.strip_metadata_description,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 8.dp),
|
||||
checked = stripMetadata,
|
||||
onCheckedChange = { stripMetadata = it },
|
||||
)
|
||||
// Hide privacy toggle when video compression is selected (compression already strips metadata)
|
||||
val isVideoWithCompression =
|
||||
uris.first().media.isVideo() == true && mediaQualitySlider != 3
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp))
|
||||
.padding(vertical = 8.dp),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1.0f),
|
||||
verticalArrangement = Arrangement.spacedBy(Size5dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(R.string.media_compression_quality_label),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Text(
|
||||
text = stringRes(R.string.media_compression_quality_explainer),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = Color.Gray,
|
||||
maxLines = 5,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
if (!isVideoWithCompression) {
|
||||
SettingSwitchItem(
|
||||
title = R.string.strip_metadata_label,
|
||||
description = R.string.strip_metadata_description,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 8.dp),
|
||||
checked = stripMetadata,
|
||||
onCheckedChange = { stripMetadata = it },
|
||||
)
|
||||
}
|
||||
|
||||
val firstMedia = uris.first().media
|
||||
|
||||
if (firstMedia.isVideo() == true || firstMedia.isImage() == true) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp))
|
||||
.padding(vertical = 8.dp),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1.0f),
|
||||
verticalArrangement = Arrangement.spacedBy(Size5dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(R.string.media_compression_quality_label),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Text(
|
||||
text = stringRes(R.string.media_compression_quality_explainer),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = Color.Gray,
|
||||
maxLines = 5,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Box(modifier = Modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
@@ -334,7 +339,10 @@ fun ImageVideoDescription(
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 10.dp),
|
||||
enabled = !isUploading,
|
||||
onClick = { onAdd(message, selectedServer, sensitiveContent, mediaQualitySlider, useH265Codec, stripMetadata) },
|
||||
onClick = {
|
||||
val effectiveStripMetadata = if (isVideoWithCompression) false else stripMetadata
|
||||
onAdd(message, selectedServer, sensitiveContent, mediaQualitySlider, useH265Codec, effectiveStripMetadata)
|
||||
},
|
||||
shape = QuoteBorder,
|
||||
colors =
|
||||
ButtonDefaults.buttonColors(
|
||||
|
||||
+16
-15
@@ -168,6 +168,21 @@ open class CommentPostViewModel :
|
||||
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(null)
|
||||
private set
|
||||
|
||||
private suspend fun showStrippingFailureDialog(): Boolean =
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
strippingFailureDialog =
|
||||
StrippingFailureState(
|
||||
onConfirm = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(true) {}
|
||||
},
|
||||
onCancel = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(false) {}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Invoices
|
||||
var canAddInvoice by mutableStateOf(false)
|
||||
var wantsInvoice by mutableStateOf(false)
|
||||
@@ -509,21 +524,7 @@ open class CommentPostViewModel :
|
||||
account,
|
||||
context,
|
||||
stripMetadata = stripMetadata,
|
||||
onStrippingFailed = {
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
strippingFailureDialog =
|
||||
StrippingFailureState(
|
||||
onConfirm = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(true) {}
|
||||
},
|
||||
onCancel = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(false) {}
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
onStrippingFailed = ::showStrippingFailureDialog,
|
||||
)
|
||||
|
||||
if (results.allGood) {
|
||||
|
||||
+2
-20
@@ -34,13 +34,10 @@ 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
|
||||
@@ -51,6 +48,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromFiles
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
|
||||
@@ -143,23 +141,7 @@ 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))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
StrippingFailureDialog(postViewModel.strippingFailureDialog)
|
||||
|
||||
BackHandler {
|
||||
accountViewModel.launchSigner {
|
||||
|
||||
+2
-18
@@ -20,7 +20,6 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send
|
||||
|
||||
import android.R.attr.maxLines
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -42,6 +41,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog
|
||||
import com.vitorpamplona.amethyst.ui.actions.UrlUserTagTransformation
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery
|
||||
import com.vitorpamplona.amethyst.ui.components.ThinPaddingTextField
|
||||
@@ -118,23 +118,7 @@ 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))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
StrippingFailureDialog(channelScreenModel.strippingFailureDialog)
|
||||
|
||||
channelScreenModel.encryptedUploadErrorTitle?.let { title ->
|
||||
EncryptedUploadErrorDialog(
|
||||
|
||||
+16
-15
@@ -136,6 +136,21 @@ open class ChannelNewMessageViewModel :
|
||||
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(null)
|
||||
private set
|
||||
|
||||
private suspend fun showStrippingFailureDialog(): Boolean =
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
strippingFailureDialog =
|
||||
StrippingFailureState(
|
||||
onConfirm = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(true) {}
|
||||
},
|
||||
onCancel = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(false) {}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
val iMetaAttachments = IMetaAttachments()
|
||||
var nip95attachments by mutableStateOf<List<Pair<FileStorageEvent, FileStorageHeaderEvent>>>(emptyList())
|
||||
|
||||
@@ -356,21 +371,7 @@ 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) {}
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
onStrippingFailed = ::showStrippingFailureDialog,
|
||||
)
|
||||
|
||||
if (results.allGood) {
|
||||
|
||||
+2
-19
@@ -26,8 +26,6 @@ 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
|
||||
@@ -39,6 +37,7 @@ import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.style.TextDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog
|
||||
import com.vitorpamplona.amethyst.ui.actions.UrlUserTagTransformation
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery
|
||||
import com.vitorpamplona.amethyst.ui.components.ThinPaddingTextField
|
||||
@@ -72,23 +71,7 @@ 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))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
StrippingFailureDialog(channelScreenModel.strippingFailureDialog)
|
||||
|
||||
channelScreenModel.replyTo.value?.let {
|
||||
DisplayReplyingToNote(it, accountViewModel, nav) {
|
||||
|
||||
+2
-20
@@ -33,13 +33,10 @@ 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
|
||||
@@ -50,6 +47,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromFiles
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
|
||||
@@ -140,23 +138,7 @@ 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))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
StrippingFailureDialog(postViewModel.strippingFailureDialog)
|
||||
|
||||
BackHandler {
|
||||
accountViewModel.launchSigner {
|
||||
|
||||
+16
-15
@@ -152,6 +152,21 @@ open class NewProductViewModel :
|
||||
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(null)
|
||||
private set
|
||||
|
||||
private suspend fun showStrippingFailureDialog(): Boolean =
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
strippingFailureDialog =
|
||||
StrippingFailureState(
|
||||
onConfirm = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(true) {}
|
||||
},
|
||||
onCancel = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(false) {}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Classifieds
|
||||
var title by mutableStateOf(TextFieldValue(""))
|
||||
var price by mutableStateOf(TextFieldValue(""))
|
||||
@@ -417,21 +432,7 @@ open class NewProductViewModel :
|
||||
myAccount,
|
||||
context,
|
||||
stripMetadata = stripMetadata,
|
||||
onStrippingFailed = {
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
strippingFailureDialog =
|
||||
StrippingFailureState(
|
||||
onConfirm = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(true) {}
|
||||
},
|
||||
onCancel = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(false) {}
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
onStrippingFailed = ::showStrippingFailureDialog,
|
||||
)
|
||||
|
||||
if (results.allGood) {
|
||||
|
||||
+2
-20
@@ -39,8 +39,6 @@ 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
|
||||
@@ -48,7 +46,6 @@ 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
|
||||
@@ -63,6 +60,7 @@ import androidx.core.util.Consumer
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.FileServerSelectionRow
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.MAX_VOICE_RECORD_SECONDS
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.RecordVoiceButton
|
||||
@@ -182,23 +180,7 @@ 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))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
StrippingFailureDialog(postViewModel.strippingFailureDialog)
|
||||
|
||||
BackHandler {
|
||||
accountViewModel.launchSigner {
|
||||
|
||||
+16
-15
@@ -197,6 +197,21 @@ open class ShortNotePostViewModel :
|
||||
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(null)
|
||||
private set
|
||||
|
||||
private suspend fun showStrippingFailureDialog(): Boolean =
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
strippingFailureDialog =
|
||||
StrippingFailureState(
|
||||
onConfirm = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(true) {}
|
||||
},
|
||||
onCancel = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(false) {}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Voice Messages
|
||||
var voiceRecording by mutableStateOf<RecordingResult?>(null)
|
||||
var voiceLocalFile by mutableStateOf<java.io.File?>(null)
|
||||
@@ -852,21 +867,7 @@ open class ShortNotePostViewModel :
|
||||
context,
|
||||
useH265,
|
||||
stripMetadata,
|
||||
onStrippingFailed = {
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
strippingFailureDialog =
|
||||
StrippingFailureState(
|
||||
onConfirm = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(true) {}
|
||||
},
|
||||
onCancel = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(false) {}
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
onStrippingFailed = ::showStrippingFailureDialog,
|
||||
)
|
||||
|
||||
if (results.allGood) {
|
||||
|
||||
+2
-19
@@ -33,8 +33,6 @@ 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
|
||||
@@ -58,6 +56,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog
|
||||
import com.vitorpamplona.amethyst.ui.actions.UrlUserTagTransformation
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromFiles
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery
|
||||
@@ -133,23 +132,7 @@ 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))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
StrippingFailureDialog(postViewModel.strippingFailureDialog)
|
||||
|
||||
BackHandler {
|
||||
accountViewModel.launchSigner {
|
||||
|
||||
+16
-15
@@ -170,6 +170,21 @@ class NewPublicMessageViewModel :
|
||||
var strippingFailureDialog by mutableStateOf<StrippingFailureState?>(null)
|
||||
private set
|
||||
|
||||
private suspend fun showStrippingFailureDialog(): Boolean =
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
strippingFailureDialog =
|
||||
StrippingFailureState(
|
||||
onConfirm = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(true) {}
|
||||
},
|
||||
onCancel = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(false) {}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Invoices
|
||||
var canAddInvoice by mutableStateOf(false)
|
||||
var wantsInvoice by mutableStateOf(false)
|
||||
@@ -461,21 +476,7 @@ class NewPublicMessageViewModel :
|
||||
account,
|
||||
context,
|
||||
stripMetadata = stripMetadata,
|
||||
onStrippingFailed = {
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
strippingFailureDialog =
|
||||
StrippingFailureState(
|
||||
onConfirm = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(true) {}
|
||||
},
|
||||
onCancel = {
|
||||
strippingFailureDialog = null
|
||||
continuation.resume(false) {}
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
onStrippingFailed = ::showStrippingFailureDialog,
|
||||
)
|
||||
|
||||
if (results.allGood) {
|
||||
|
||||
Reference in New Issue
Block a user