Adds upload cover url

This commit is contained in:
Vitor Pamplona
2026-03-22 12:35:19 -04:00
parent ffa60c5fd3
commit 26a3dcbd85
2 changed files with 94 additions and 0 deletions
@@ -65,6 +65,7 @@ import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.actions.UrlUserTagTransformation
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromFiles
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectSingleFromGallery
import com.vitorpamplona.amethyst.ui.actions.uploads.TakePictureButton
import com.vitorpamplona.amethyst.ui.components.markdown.RenderContentAsMarkdown
import com.vitorpamplona.amethyst.ui.navigation.navs.Nav
@@ -226,6 +227,8 @@ private fun MarkdownPostScreenBody(
),
)
val context = LocalContext.current
// Cover image URL field
OutlinedTextField(
value = postViewModel.coverImageUrl,
@@ -239,6 +242,21 @@ private fun MarkdownPostScreenBody(
.fillMaxWidth()
.padding(horizontal = Size10dp, vertical = Size5dp),
singleLine = true,
placeholder = {
Text(
text = "https://mywebsite.com/mypost.jpg",
color = MaterialTheme.colorScheme.placeholderText,
)
},
leadingIcon = {
SelectSingleFromGallery(
isUploading = postViewModel.isUploadingCoverImage,
tint = MaterialTheme.colorScheme.placeholderText,
modifier = Modifier.padding(start = 5.dp),
) {
postViewModel.uploadCoverImage(it, context, onError = accountViewModel.toastManager::toast)
}
},
)
HorizontalDivider(modifier = Modifier.padding(vertical = Size5dp))
@@ -40,11 +40,15 @@ import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.location.LocationState
import com.vitorpamplona.amethyst.service.uploads.CompressorQuality
import com.vitorpamplona.amethyst.service.uploads.MediaCompressor
import com.vitorpamplona.amethyst.service.uploads.MultiOrchestrator
import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator
import com.vitorpamplona.amethyst.service.uploads.blossom.BlossomUploader
import com.vitorpamplona.amethyst.service.uploads.nip96.Nip96Uploader
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMediaProcessing
import com.vitorpamplona.amethyst.ui.note.creators.draftTags.DraftTagState
@@ -105,6 +109,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlin.coroutines.cancellation.CancellationException
import kotlin.uuid.ExperimentalUuidApi
@Stable
@@ -138,6 +143,8 @@ class LongFormPostViewModel :
var coverImageUrl by mutableStateOf("")
var publishedAt by mutableLongStateOf(TimeUtils.now())
var isUploadingCoverImage by mutableStateOf(false)
override var message by mutableStateOf(TextFieldValue(""))
var showPreview by mutableStateOf(false)
@@ -383,6 +390,75 @@ class LongFormPostViewModel :
}
}
fun uploadCoverImage(
uri: SelectedMedia,
context: Context,
onError: (String, String) -> Unit,
) {
accountViewModel.launchSigner {
isUploadingCoverImage = true
try {
directUpload(
galleryUri = uri,
context = context,
onError = onError,
)?.let {
coverImageUrl = it
}
} finally {
isUploadingCoverImage = false
}
}
}
private suspend fun directUpload(
galleryUri: SelectedMedia,
context: Context,
onError: (String, String) -> Unit,
): String? {
val compResult = MediaCompressor().compress(galleryUri.uri, galleryUri.mimeType, CompressorQuality.MEDIUM, context.applicationContext)
return try {
val result =
if (account.settings.defaultFileServer.type == ServerType.NIP96) {
Nip96Uploader().upload(
uri = compResult.uri,
contentType = compResult.contentType,
size = compResult.size,
alt = null,
sensitiveContent = null,
serverBaseUrl = account.settings.defaultFileServer.baseUrl,
okHttpClient = Amethyst.instance.roleBasedHttpClientBuilder::okHttpClientForUploads,
onProgress = {},
httpAuth = account::createHTTPAuthorization,
context = context,
)
} else {
BlossomUploader().upload(
uri = compResult.uri,
contentType = compResult.contentType,
size = compResult.size,
alt = null,
sensitiveContent = null,
serverBaseUrl = account.settings.defaultFileServer.baseUrl,
okHttpClient = Amethyst.instance.roleBasedHttpClientBuilder::okHttpClientForUploads,
httpAuth = account::createBlossomUploadAuth,
context = context,
)
}
if (result.url == null) {
onError(stringRes(context, R.string.failed_to_upload_media_no_details), stringRes(context, R.string.server_did_not_provide_a_url_after_uploading))
}
result.url
} catch (e: Exception) {
if (e is CancellationException) throw e
onError(stringRes(context, R.string.failed_to_upload_media_no_details), e.message ?: e.javaClass.simpleName)
null
}
}
fun upload(
alt: String?,
contentWarningReason: String?,