- Adds i18n for error messages when uploading images.

- Forces nip95 to be under 80Kb
- Fixes bug that wouldn't show an error when uploading images in the reels page.
- Only saves default server if not NIP95
This commit is contained in:
Vitor Pamplona
2023-12-17 17:26:17 -05:00
parent 238ade9ccb
commit 9026c877ac
6 changed files with 71 additions and 50 deletions
@@ -28,7 +28,7 @@ class FileHeader(
mimeType: String?,
dimPrecomputed: String?,
onReady: (FileHeader) -> Unit,
onError: () -> Unit
onError: (String?) -> Unit
) {
try {
val imageData: ByteArray? = ImageDownloader().waitAndGetImage(fileUrl)
@@ -36,11 +36,11 @@ class FileHeader(
if (imageData != null) {
prepare(imageData, mimeType, dimPrecomputed, onReady, onError)
} else {
onError()
onError(null)
}
} catch (e: Exception) {
Log.e("ImageDownload", "Couldn't download image from server: ${e.message}")
onError()
onError(e.message)
}
}
@@ -49,7 +49,7 @@ class FileHeader(
mimeType: String?,
dimPrecomputed: String?,
onReady: (FileHeader) -> Unit,
onError: () -> Unit
onError: (String?) -> Unit
) {
try {
val hash = CryptoUtils.sha256(data).toHexKey()
@@ -123,7 +123,7 @@ class FileHeader(
onReady(FileHeader(mimeType, hash, size, dim, blurHash))
} catch (e: Exception) {
Log.e("ImageDownload", "Couldn't convert image in to File Header: ${e.message}")
onError()
onError(e.message)
}
}
}
@@ -9,6 +9,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.service.FileHeader
import com.vitorpamplona.amethyst.service.Nip96MediaServers
@@ -16,8 +17,6 @@ import com.vitorpamplona.amethyst.service.Nip96Uploader
import com.vitorpamplona.amethyst.service.relays.Relay
import com.vitorpamplona.amethyst.ui.components.MediaCompressor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
data class ServerOption(
@@ -30,7 +29,6 @@ open class NewMediaModel : ViewModel() {
var account: Account? = null
var isUploadingImage by mutableStateOf(false)
val imageUploadingError = MutableSharedFlow<String?>(0, 3, onBufferOverflow = BufferOverflow.DROP_OLDEST)
var mediaType by mutableStateOf<String?>(null)
var selectedServer by mutableStateOf<ServerOption?>(null)
@@ -44,12 +42,14 @@ open class NewMediaModel : ViewModel() {
var uploadingDescription = mutableStateOf<String?>(null)
var onceUploaded: () -> Unit = {}
var onError: (String) -> Unit = {}
open fun load(account: Account, uri: Uri, contentType: String?) {
open fun load(account: Account, uri: Uri, contentType: String?, onError: (String) -> Unit) {
this.account = account
this.galleryUri = uri
this.mediaType = contentType
this.selectedServer = ServerOption(defaultServer(), false)
this.onError = onError
}
fun upload(context: Context, relayList: List<Relay>? = null) {
@@ -73,11 +73,11 @@ open class NewMediaModel : ViewModel() {
uploadingPercentage.value = 0.2f
uploadingDescription.value = "Loading"
contentResolver.openInputStream(fileUri)?.use {
createNIP95Record(it.readBytes(), contentType, alt, sensitiveContent, relayList = relayList)
createNIP95Record(it.readBytes(), contentType, alt, sensitiveContent, relayList = relayList, context)
}
?: run {
viewModelScope.launch {
imageUploadingError.emit("Failed to upload the image / video")
onError(context.getString(R.string.could_not_open_the_compressed_file))
isUploadingImage = false
uploadingPercentage.value = 0.00f
uploadingDescription.value = null
@@ -106,15 +106,14 @@ open class NewMediaModel : ViewModel() {
localContentType = contentType,
alt = alt,
sensitiveContent = sensitiveContent,
relayList = relayList
relayList = relayList,
context
)
} catch (e: Exception) {
isUploadingImage = false
uploadingPercentage.value = 0.00f
uploadingDescription.value = null
viewModelScope.launch {
imageUploadingError.emit("Failed to upload: ${e.message}")
}
onError(context.getString(R.string.failed_to_upload_media, e.message))
}
}
}
@@ -123,9 +122,7 @@ open class NewMediaModel : ViewModel() {
isUploadingImage = false
uploadingPercentage.value = 0.00f
uploadingDescription.value = null
viewModelScope.launch {
imageUploadingError.emit("Failed to upload the image / video")
}
onError(context.getString(R.string.error_when_compressing_media, it))
}
)
}
@@ -151,7 +148,8 @@ open class NewMediaModel : ViewModel() {
localContentType: String?,
alt: String,
sensitiveContent: Boolean,
relayList: List<Relay>? = null
relayList: List<Relay>? = null,
context: Context
) {
uploadingPercentage.value = 0.40f
uploadingDescription.value = "Server Processing"
@@ -169,9 +167,7 @@ open class NewMediaModel : ViewModel() {
uploadingPercentage.value = 0.00f
uploadingDescription.value = null
isUploadingImage = false
viewModelScope.launch {
imageUploadingError.emit("Failed to upload the image / video")
}
onError(context.getString(R.string.server_did_not_provide_a_url_after_uploading))
return
}
@@ -203,9 +199,7 @@ open class NewMediaModel : ViewModel() {
uploadingPercentage.value = 0.00f
uploadingDescription.value = null
isUploadingImage = false
viewModelScope.launch {
imageUploadingError.emit("Failed to upload the image / video")
}
onError(context.getString(R.string.could_not_prepare_local_file_to_upload, it))
}
)
} else {
@@ -214,13 +208,28 @@ open class NewMediaModel : ViewModel() {
uploadingPercentage.value = 0.00f
uploadingDescription.value = null
isUploadingImage = false
viewModelScope.launch {
imageUploadingError.emit("Failed to upload the image / video")
}
onError(context.getString(R.string.could_not_download_from_the_server))
}
}
fun createNIP95Record(bytes: ByteArray, mimeType: String?, alt: String, sensitiveContent: Boolean, relayList: List<Relay>? = null) {
fun createNIP95Record(
bytes: ByteArray,
mimeType: String?,
alt: String,
sensitiveContent: Boolean,
relayList: List<Relay>? = null,
context: Context
) {
if (bytes.size > 80000) {
viewModelScope.launch {
onError("Media is too big for NIP-95")
isUploadingImage = false
uploadingPercentage.value = 0.00f
uploadingDescription.value = null
}
return
}
uploadingPercentage.value = 0.30f
uploadingDescription.value = "Hashing"
@@ -248,9 +257,7 @@ open class NewMediaModel : ViewModel() {
uploadingPercentage.value = 0.00f
isUploadingImage = false
cancel()
viewModelScope.launch {
imageUploadingError.emit("Failed to upload the image / video")
}
onError(context.getString(R.string.could_not_prepare_local_file_to_upload, it))
}
)
}
@@ -5,7 +5,6 @@ import android.net.Uri
import android.os.Build
import android.util.Log
import android.util.Size
import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -55,7 +54,6 @@ import com.vitorpamplona.amethyst.ui.theme.placeholderText
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@Composable
fun NewMediaView(uri: Uri, onClose: () -> Unit, postViewModel: NewMediaModel, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
@@ -67,14 +65,8 @@ fun NewMediaView(uri: Uri, onClose: () -> Unit, postViewModel: NewMediaModel, ac
LaunchedEffect(uri) {
val mediaType = resolver.getType(uri) ?: ""
postViewModel.load(account, uri, mediaType)
launch(Dispatchers.IO) {
postViewModel.imageUploadingError.collect { error ->
withContext(Dispatchers.Main) {
Toast.makeText(context, error, Toast.LENGTH_SHORT).show()
}
}
postViewModel.load(account, uri, mediaType) {
accountViewModel.toast(context.getString(R.string.failed_to_upload_media_no_details), it)
}
}
@@ -146,7 +138,11 @@ fun NewMediaView(uri: Uri, onClose: () -> Unit, postViewModel: NewMediaModel, ac
onPost = {
onClose()
postViewModel.upload(context, relayList)
postViewModel.selectedServer?.let { account.changeDefaultFileServer(it.server) }
postViewModel.selectedServer?.let {
if (!it.isNip95) {
account.changeDefaultFileServer(it.server)
}
}
},
isActive = postViewModel.canPost()
)
@@ -211,7 +207,6 @@ fun ImageVideoPost(postViewModel: NewMediaModel, accountViewModel: AccountViewMo
try {
bitmap = resolver.loadThumbnail(it, Size(1200, 1000), null)
} catch (e: Exception) {
postViewModel.imageUploadingError.emit("Unable to load thumbnail, but the video can be uploaded")
Log.w("NewPostView", "Couldn't create thumbnail, but the video can be uploaded", e)
}
}
@@ -405,8 +405,10 @@ fun NewPostView(
url,
accountViewModel.account.defaultFileServer,
onAdd = { alt, server, sensitiveContent ->
postViewModel.upload(url, alt, sensitiveContent, false, server, context, relayList)
accountViewModel.account.changeDefaultFileServer(server.server)
postViewModel.upload(url, alt, sensitiveContent, false, server, context)
if (!server.isNip95) {
accountViewModel.account.changeDefaultFileServer(server.server)
}
},
onCancel = {
postViewModel.contentToAddUrl = null
@@ -44,6 +44,7 @@ import com.vitorpamplona.quartz.events.TextNoteEvent
import com.vitorpamplona.quartz.events.ZapSplitSetup
import com.vitorpamplona.quartz.events.findURLs
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -206,7 +207,7 @@ open class NewPostViewModel() : ViewModel() {
val dmUsers = toUsersTagger.mentions
val zapReceiver = if (wantsForwardZapTo) {
forwardZapTo?.items?.map {
forwardZapTo.items.map {
ZapSplitSetup(
lnAddressOrPubKeyHex = it.key.pubkeyHex,
relay = it.key.relaysBeingUsed.keys.firstOrNull(),
@@ -361,8 +362,7 @@ open class NewPostViewModel() : ViewModel() {
sensitiveContent: Boolean,
isPrivate: Boolean = false,
server: ServerOption,
context: Context,
relayList: List<Relay>? = null
context: Context
) {
isUploadingImage = true
contentToAddUrl = null
@@ -569,7 +569,7 @@ open class NewPostViewModel() : ViewModel() {
TextRange(lastWordStart + wordToInsert.length, lastWordStart + wordToInsert.length)
)
} else if (userSuggestionsMainMessage == UserSuggestionAnchor.FORWARD_ZAPS) {
forwardZapTo?.addItem(item)
forwardZapTo.addItem(item)
forwardZapToEditting = TextFieldValue("")
/*
val lastWord = forwardZapToEditting.text.substring(0, it.end).substringAfterLast("\n").substringAfterLast(" ")
@@ -740,6 +740,14 @@ open class NewPostViewModel() : ViewModel() {
alt: String?,
sensitiveContent: Boolean
) {
if (bytes.size > 80000) {
viewModelScope.launch {
imageUploadingError.emit("Media is too big for NIP-95")
isUploadingImage = false
}
return
}
viewModelScope.launch(Dispatchers.IO) {
FileHeader.prepare(
bytes,
@@ -773,6 +781,7 @@ open class NewPostViewModel() : ViewModel() {
contentToAddUrl = uri
}
@OptIn(ExperimentalCoroutinesApi::class)
fun startLocation(context: Context) {
locUtil = LocationUtil(context)
locUtil?.let {
+8
View File
@@ -684,4 +684,12 @@
<string name="classifieds_category_food">Food</string>
<string name="classifieds_category_misc">Miscellaneous</string>
<string name="classifieds_category_other">Other</string>
<string name="failed_to_upload_media_no_details">Failed to upload media</string>
<string name="could_not_open_the_compressed_file">Could not open the compressed file</string>
<string name="error_when_compressing_media">Error when compressing media: %1$s</string>
<string name="failed_to_upload_media">Uploading error: %1$s</string>
<string name="server_did_not_provide_a_url_after_uploading">Server did not provide a URL after uploading</string>
<string name="could_not_download_from_the_server">Could not download uploaded media from the server</string>
<string name="could_not_prepare_local_file_to_upload">Could not prepare local file to upload: %1$s</string>
</resources>