From 8cd9449ba6091eb145bf630610354d63149bb205 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Tue, 10 Mar 2026 19:49:15 -0400 Subject: [PATCH] Improves upload of music and documents. --- .../nipB7Blossom/BlossomServerListState.kt | 2 +- .../service/uploads/MultiOrchestrator.kt | 2 + .../amethyst/ui/actions/NewMediaView.kt | 4 +- .../ui/actions/uploads/SelectFromGallery.kt | 5 ++ .../ui/actions/uploads/ShowImageUploadItem.kt | 69 ++++++++++--------- .../creators/uploads/ImageVideoDescription.kt | 52 ++++++++------ .../chats/utils/ChatFileUploadDialog.kt | 4 +- 7 files changed, 81 insertions(+), 57 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nipB7Blossom/BlossomServerListState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nipB7Blossom/BlossomServerListState.kt index ca4fbc387..83aad4732 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nipB7Blossom/BlossomServerListState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nipB7Blossom/BlossomServerListState.kt @@ -79,7 +79,7 @@ class BlossomServerListState( emptyList(), ) - fun mergeServerList(blossom: List?): List = blossom?.map { ServerName(host(it), it, ServerType.Blossom) } ?: emptyList() + fun mergeServerList(blossom: List?): List = blossom?.map { ServerName(host(it), it, ServerType.Blossom) }?.ifEmpty { DEFAULT_MEDIA_SERVERS } ?: DEFAULT_MEDIA_SERVERS val hostNameFlow: StateFlow> = flow diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/MultiOrchestrator.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/MultiOrchestrator.kt index ada46f1dd..977b86852 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/MultiOrchestrator.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/MultiOrchestrator.kt @@ -49,6 +49,8 @@ class MultiOrchestrator( fun hasVideo() = list.any { it.media.mimeType?.startsWith("video", ignoreCase = true) == true } + fun hasNonMedia() = list.any { it.media.isNotMedia() } + suspend fun upload( alt: String?, contentWarningReason: String?, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt index 1263816fc..6bdb0a528 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt @@ -54,6 +54,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia import com.vitorpamplona.amethyst.ui.actions.uploads.ShowImageUploadGallery import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge @@ -204,7 +205,8 @@ fun ImageVideoPost( fileServers .firstOrNull { it == accountViewModel.account.settings.defaultFileServer } ?.name - ?: fileServers[0].name, + ?: fileServers.firstOrNull()?.name + ?: DEFAULT_MEDIA_SERVERS[0].name, options = fileServerOptions, onSelect = { postViewModel.selectedServer = fileServers[it] }, ) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/SelectFromGallery.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/SelectFromGallery.kt index 1b2314668..0e38451c5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/SelectFromGallery.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/SelectFromGallery.kt @@ -58,6 +58,11 @@ class SelectedMedia( fun isAudio() = mimeType?.startsWith("audio") + fun isNotMedia() = + mimeType?.let { + !(it.startsWith("image") || it.startsWith("video") || it.startsWith("audio")) + } ?: true + fun isDocument() = mimeType == "application/pdf" } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/ShowImageUploadItem.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/ShowImageUploadItem.kt index 261c9e8bf..f7c153611 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/ShowImageUploadItem.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/ShowImageUploadItem.kt @@ -129,7 +129,7 @@ fun ShowImageGallery( icon = Icons.Default.AudioFile, label = media.mimeType ?: "audio/*", ) - } else if (media.isDocument() == true) { + } else if (media.isDocument()) { FilePreviewPlaceholder( icon = Icons.Default.PictureAsPdf, label = media.mimeType ?: "application/pdf", @@ -236,39 +236,44 @@ fun UploadingState( progress: Double, progressState: UploadingState, ) { - Box(Modifier.size(55.dp), contentAlignment = Alignment.Center) { - val animatedProgress by animateFloatAsState( - targetValue = progress.toFloat(), - animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec, - ) + Box( + contentAlignment = Alignment.Center, + modifier = Modifier.fillMaxSize(), + ) { + Box(Modifier.size(55.dp), contentAlignment = Alignment.Center) { + val animatedProgress by animateFloatAsState( + targetValue = progress.toFloat(), + animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec, + ) - CircularProgressIndicator( - progress = { animatedProgress }, - modifier = - Size55Modifier - .clip(CircleShape) - .background(MaterialTheme.colorScheme.background), - strokeWidth = 5.dp, - ) + CircularProgressIndicator( + progress = { animatedProgress }, + modifier = + Size55Modifier + .clip(CircleShape) + .background(MaterialTheme.colorScheme.background), + strokeWidth = 5.dp, + ) - val txt = - when (progressState) { - is UploadingState.Ready -> stringRes(R.string.uploading_state_ready) - is UploadingState.Compressing -> stringRes(R.string.uploading_state_compressing) - is UploadingState.Uploading -> stringRes(R.string.uploading_state_uploading) - is UploadingState.ServerProcessing -> stringRes(R.string.uploading_state_server_processing) - is UploadingState.Downloading -> stringRes(R.string.uploading_state_downloading) - is UploadingState.Hashing -> stringRes(R.string.uploading_state_hashing) - is UploadingState.Finished -> stringRes(R.string.uploading_state_finished) - is UploadingState.Error -> stringRes(R.string.uploading_state_error) - } + val txt = + when (progressState) { + is UploadingState.Ready -> stringRes(R.string.uploading_state_ready) + is UploadingState.Compressing -> stringRes(R.string.uploading_state_compressing) + is UploadingState.Uploading -> stringRes(R.string.uploading_state_uploading) + is UploadingState.ServerProcessing -> stringRes(R.string.uploading_state_server_processing) + is UploadingState.Downloading -> stringRes(R.string.uploading_state_downloading) + is UploadingState.Hashing -> stringRes(R.string.uploading_state_hashing) + is UploadingState.Finished -> stringRes(R.string.uploading_state_finished) + is UploadingState.Error -> stringRes(R.string.uploading_state_error) + } - Text( - txt, - color = MaterialTheme.colorScheme.onSurface, - fontSize = 10.sp, - textAlign = TextAlign.Center, - ) + Text( + txt, + color = MaterialTheme.colorScheme.onSurface, + fontSize = 10.sp, + textAlign = TextAlign.Center, + ) + } } } @@ -280,7 +285,7 @@ fun FilePreviewPlaceholder( Box( modifier = Modifier - .fillMaxWidth() + .fillMaxSize() .background(MaterialTheme.colorScheme.surfaceVariant), contentAlignment = Alignment.Center, ) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/uploads/ImageVideoDescription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/uploads/ImageVideoDescription.kt index 075f0aaf7..9e40f6e1d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/uploads/ImageVideoDescription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/uploads/ImageVideoDescription.kt @@ -57,6 +57,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator +import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing import com.vitorpamplona.amethyst.ui.actions.uploads.ShowImageUploadGallery @@ -92,14 +93,16 @@ fun ImageVideoDescription( var selectedServer by remember { mutableStateOf( - fileServers.firstOrNull { it == defaultServer } ?: fileServers[0], + fileServers.firstOrNull { it == defaultServer } ?: fileServers.firstOrNull() ?: DEFAULT_MEDIA_SERVERS[0], ) } var message by remember { mutableStateOf("") } var sensitiveContent by remember { mutableStateOf(false) } // 0 = Low, 1 = Medium, 2 = High, 3=UNCOMPRESSED - var mediaQualitySlider by remember { mutableIntStateOf(1) } + var mediaQualitySlider by remember { + mutableIntStateOf(if (uris.hasNonMedia()) 3 else 1) + } // Codec selection: false = H264, true = H265 var useH265Codec by remember { mutableStateOf(false) } @@ -189,7 +192,8 @@ fun ImageVideoDescription( fileServers .firstOrNull { it == defaultServer } ?.name - ?: fileServers[0].name, + ?: fileServers.firstOrNull()?.name + ?: DEFAULT_MEDIA_SERVERS[0].name, options = fileServerOptions, onSelect = { selectedServer = fileServers[it] }, modifier = @@ -270,27 +274,31 @@ fun ImageVideoDescription( } } - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Box(modifier = Modifier.fillMaxWidth()) { - Text( - text = - when (mediaQualitySlider) { - 0 -> stringRes(R.string.media_compression_quality_low) - 1 -> stringRes(R.string.media_compression_quality_medium) - 2 -> stringRes(R.string.media_compression_quality_high) - 3 -> stringRes(R.string.media_compression_quality_uncompressed) - else -> stringRes(R.string.media_compression_quality_medium) - }, - modifier = Modifier.align(Alignment.Center), + val firstMedia = uris.first().media + + if (firstMedia.isVideo() == true || firstMedia.isImage() == true || firstMedia.isAudio() == true) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Box(modifier = Modifier.fillMaxWidth()) { + Text( + text = + when (mediaQualitySlider) { + 0 -> stringRes(R.string.media_compression_quality_low) + 1 -> stringRes(R.string.media_compression_quality_medium) + 2 -> stringRes(R.string.media_compression_quality_high) + 3 -> stringRes(R.string.media_compression_quality_uncompressed) + else -> stringRes(R.string.media_compression_quality_medium) + }, + modifier = Modifier.align(Alignment.Center), + ) + } + + Slider( + value = mediaQualitySlider.toFloat(), + onValueChange = { mediaQualitySlider = it.toInt() }, + valueRange = 0f..3f, + steps = 2, ) } - - Slider( - value = mediaQualitySlider.toFloat(), - onValueChange = { mediaQualitySlider = it.toInt() }, - valueRange = 0f..3f, - steps = 2, - ) } if (uris.first().media.isVideo() == true) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/utils/ChatFileUploadDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/utils/ChatFileUploadDialog.kt index de7626e47..f1ce97525 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/utils/ChatFileUploadDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/utils/ChatFileUploadDialog.kt @@ -55,6 +55,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS import com.vitorpamplona.amethyst.ui.actions.uploads.ShowImageUploadGallery import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge import com.vitorpamplona.amethyst.ui.components.TextSpinner @@ -196,7 +197,8 @@ private fun ImageVideoPostChat( fileServers .firstOrNull { it == accountViewModel.account.settings.defaultFileServer } ?.name - ?: fileServers[0].name, + ?: fileServers.firstOrNull()?.name + ?: DEFAULT_MEDIA_SERVERS[0].name, options = fileServerOptions, onSelect = { fileUploadState.selectedServer = fileServers[it] }, )