Moves error messages from uploading procedures to the full error dialog instead of just a toast.

This commit is contained in:
Vitor Pamplona 2024-08-15 14:55:25 -04:00
parent d6d8719c8d
commit f43c0e5cf5
14 changed files with 106 additions and 111 deletions

View File

@ -49,7 +49,7 @@ class FileHeader(
mimeType: String?,
dimPrecomputed: String?,
onReady: (FileHeader) -> Unit,
onError: (String?) -> Unit,
onError: (String) -> Unit,
) {
try {
val imageData: ByteArray? = ImageDownloader().waitAndGetImage(fileUrl)
@ -57,12 +57,12 @@ class FileHeader(
if (imageData != null) {
prepare(imageData, mimeType, dimPrecomputed, onReady, onError)
} else {
onError(null)
onError("Unable to download image from $fileUrl")
}
} catch (e: Exception) {
if (e is CancellationException) throw e
Log.e("ImageDownload", "Couldn't download image from server: ${e.message}")
onError(e.message)
onError(e.message ?: e.javaClass.simpleName)
}
}
@ -71,7 +71,7 @@ class FileHeader(
mimeType: String?,
dimPrecomputed: String?,
onReady: (FileHeader) -> Unit,
onError: (String?) -> Unit,
onError: (String) -> Unit,
) {
try {
val hash = CryptoUtils.sha256(data).toHexKey()
@ -178,7 +178,7 @@ class FileHeader(
} catch (e: Exception) {
if (e is CancellationException) throw e
Log.e("ImageDownload", "Couldn't convert image in to File Header: ${e.message}")
onError(e.message)
onError(e.message ?: e.javaClass.simpleName)
}
}
}

View File

@ -109,10 +109,8 @@ import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.theme.replyModifier
import com.vitorpamplona.amethyst.ui.theme.subtleBorder
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@ -135,12 +133,6 @@ fun EditPostView(
LaunchedEffect(Unit) {
postViewModel.load(edit, versionLookingAt, accountViewModel)
launch(Dispatchers.IO) {
postViewModel.imageUploadingError.collect { error ->
withContext(Dispatchers.Main) { Toast.makeText(context, error, Toast.LENGTH_SHORT).show() }
}
}
}
DisposableEffect(Unit) {
@ -339,13 +331,13 @@ fun EditPostView(
url,
accountViewModel.account.defaultFileServer,
onAdd = { alt, server, sensitiveContent ->
postViewModel.upload(url, alt, sensitiveContent, false, server, context)
postViewModel.upload(url, alt, sensitiveContent, false, server, accountViewModel::toast, context)
if (!server.isNip95) {
accountViewModel.account.changeDefaultFileServer(server.server)
}
},
onCancel = { postViewModel.contentToAddUrl = null },
onError = { scope.launch { postViewModel.imageUploadingError.emit(it) } },
onError = { scope.launch { Toast.makeText(context, context.resources.getText(it), Toast.LENGTH_SHORT).show() } },
accountViewModel = accountViewModel,
)
}

View File

@ -31,6 +31,7 @@ import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.compose.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
import com.vitorpamplona.amethyst.model.Account
@ -42,14 +43,13 @@ import com.vitorpamplona.amethyst.service.Nip96Uploader
import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource
import com.vitorpamplona.amethyst.ui.components.MediaCompressor
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.ammolite.relays.RelaySetupInfo
import com.vitorpamplona.quartz.events.FileHeaderEvent
import com.vitorpamplona.quartz.events.FileStorageEvent
import com.vitorpamplona.quartz.events.FileStorageHeaderEvent
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
@Stable
@ -68,8 +68,6 @@ open class EditPostViewModel : ViewModel() {
var message by mutableStateOf(TextFieldValue(""))
var urlPreview by mutableStateOf<String?>(null)
var isUploadingImage by mutableStateOf(false)
val imageUploadingError =
MutableSharedFlow<String?>(0, 3, onBufferOverflow = BufferOverflow.DROP_OLDEST)
var userSuggestions by mutableStateOf<List<User>>(emptyList())
var userSuggestionAnchor: TextRange? = null
@ -152,6 +150,7 @@ open class EditPostViewModel : ViewModel() {
sensitiveContent: Boolean,
isPrivate: Boolean = false,
server: ServerOption,
onError: (String, String) -> Unit,
context: Context,
) {
isUploadingImage = true
@ -169,7 +168,16 @@ open class EditPostViewModel : ViewModel() {
onReady = { fileUri, contentType, size ->
if (server.isNip95) {
contentResolver.openInputStream(fileUri)?.use {
createNIP95Record(it.readBytes(), contentType, alt, sensitiveContent)
createNIP95Record(
it.readBytes(),
contentType,
alt,
sensitiveContent,
onError = {
onError(stringRes(context, R.string.failed_to_upload_media_no_details), it)
},
context,
)
}
} else {
viewModelScope.launch(Dispatchers.IO) {
@ -193,6 +201,10 @@ open class EditPostViewModel : ViewModel() {
localContentType = contentType,
alt = alt,
sensitiveContent = sensitiveContent,
onError = {
onError(stringRes(context, R.string.failed_to_upload_media_no_details), it)
},
context = context,
)
} catch (e: Exception) {
if (e is CancellationException) throw e
@ -202,16 +214,14 @@ open class EditPostViewModel : ViewModel() {
e,
)
isUploadingImage = false
viewModelScope.launch {
imageUploadingError.emit("Failed to upload: ${e.message}")
}
onError(stringRes(context, R.string.failed_to_upload_media_no_details), e.message ?: e.javaClass.simpleName)
}
}
}
},
onError = {
isUploadingImage = false
viewModelScope.launch { imageUploadingError.emit(it) }
onError(stringRes(context, R.string.failed_to_upload_media_no_details), stringRes(context, it))
},
)
}
@ -306,6 +316,8 @@ open class EditPostViewModel : ViewModel() {
localContentType: String?,
alt: String?,
sensitiveContent: Boolean,
onError: (String) -> Unit = {},
context: Context,
) {
// Images don't seem to be ready immediately after upload
val imageUrl = uploadingResult.tags?.firstOrNull { it.size > 1 && it[0] == "url" }?.get(1)
@ -334,7 +346,7 @@ open class EditPostViewModel : ViewModel() {
Log.e("ImageDownload", "Couldn't download image from server")
cancel()
isUploadingImage = false
viewModelScope.launch { imageUploadingError.emit("Failed to upload the image / video") }
onError(stringRes(context, R.string.server_did_not_provide_a_url_after_uploading))
return
}
@ -353,7 +365,7 @@ open class EditPostViewModel : ViewModel() {
},
onError = {
isUploadingImage = false
viewModelScope.launch { imageUploadingError.emit("Failed to upload the image / video") }
onError(stringRes(context, R.string.could_not_prepare_header, it))
},
)
}
@ -363,10 +375,12 @@ open class EditPostViewModel : ViewModel() {
mimeType: String?,
alt: String?,
sensitiveContent: Boolean,
onError: (String) -> Unit = {},
context: Context,
) {
if (bytes.size > 80000) {
viewModelScope.launch {
imageUploadingError.emit("Media is too big for NIP-95")
onError(stringRes(context, id = R.string.media_too_big_for_nip95))
isUploadingImage = false
}
return
@ -393,7 +407,7 @@ open class EditPostViewModel : ViewModel() {
},
onError = {
isUploadingImage = false
viewModelScope.launch { imageUploadingError.emit("Failed to upload the image / video") }
onError(stringRes(context, R.string.could_not_prepare_header, it))
},
)
}

View File

@ -64,24 +64,22 @@ 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?,
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<RelaySetupInfo>? = null,
onError: (String) -> Unit = {},
) {
isUploadingImage = true
@ -110,6 +108,7 @@ open class NewMediaModel : ViewModel() {
alt,
sensitiveContent,
relayList = relayList,
onError = onError,
context,
)
}
@ -148,6 +147,7 @@ open class NewMediaModel : ViewModel() {
alt = alt,
sensitiveContent = sensitiveContent,
relayList = relayList,
onError = onError,
context,
)
} catch (e: Exception) {
@ -189,6 +189,7 @@ open class NewMediaModel : ViewModel() {
alt: String,
sensitiveContent: Boolean,
relayList: List<RelaySetupInfo>? = null,
onError: (String) -> Unit = {},
context: Context,
) {
uploadingPercentage.value = 0.40f
@ -282,11 +283,12 @@ open class NewMediaModel : ViewModel() {
alt: String,
sensitiveContent: Boolean,
relayList: List<RelaySetupInfo>? = null,
onError: (String) -> Unit = {},
context: Context,
) {
if (bytes.size > 80000) {
viewModelScope.launch {
onError("Media is too big for NIP-95")
onError(stringRes(context, id = R.string.media_too_big_for_nip95))
isUploadingImage = false
uploadingPercentage.value = 0.00f
uploadingDescription.value = null

View File

@ -94,9 +94,7 @@ fun NewMediaView(
LaunchedEffect(uri) {
val mediaType = resolver.getType(uri) ?: ""
postViewModel.load(account, uri, mediaType) {
accountViewModel.toast(stringRes(context, R.string.failed_to_upload_media_no_details), it)
}
postViewModel.load(account, uri, mediaType)
}
var showRelaysDialog by remember { mutableStateOf(false) }
@ -161,7 +159,9 @@ fun NewMediaView(
PostButton(
onPost = {
onClose()
postViewModel.upload(context, relayList)
postViewModel.upload(context, relayList) {
accountViewModel.toast(stringRes(context, R.string.failed_to_upload_media_no_details), it)
}
postViewModel.selectedServer?.let {
if (!it.isNip95) {
account.changeDefaultFileServer(it.server)

View File

@ -180,7 +180,6 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.lang.Math.round
@OptIn(ExperimentalMaterial3Api::class, FlowPreview::class)
@ -220,10 +219,6 @@ fun NewPostView(
LaunchedEffect(Unit) {
launch(Dispatchers.IO) {
postViewModel.load(accountViewModel, baseReplyTo, quote, fork, version, draft)
postViewModel.imageUploadingError.collect { error ->
withContext(Dispatchers.Main) { Toast.makeText(context, error, Toast.LENGTH_SHORT).show() }
}
}
}
@ -498,13 +493,13 @@ fun NewPostView(
url,
accountViewModel.account.defaultFileServer,
onAdd = { alt, server, sensitiveContent ->
postViewModel.upload(url, alt, sensitiveContent, false, server, context)
postViewModel.upload(url, alt, sensitiveContent, false, server, accountViewModel::toast, context)
if (!server.isNip95) {
accountViewModel.account.changeDefaultFileServer(server.server)
}
},
onCancel = { postViewModel.contentToAddUrl = null },
onError = { scope.launch { postViewModel.imageUploadingError.emit(it) } },
onError = { scope.launch { Toast.makeText(context, context.resources.getText(it), Toast.LENGTH_SHORT).show() } },
accountViewModel = accountViewModel,
)
}
@ -1616,7 +1611,7 @@ fun ImageVideoDescription(
defaultServer: Nip96MediaServers.ServerName,
onAdd: (String, ServerOption, Boolean) -> Unit,
onCancel: () -> Unit,
onError: (String) -> Unit,
onError: (Int) -> Unit,
accountViewModel: AccountViewModel,
) {
val resolver = LocalContext.current.contentResolver
@ -1753,7 +1748,7 @@ fun ImageVideoDescription(
bitmap = resolver.loadThumbnail(uri, Size(1200, 1000), null)
} catch (e: Exception) {
if (e is CancellationException) throw e
onError("Unable to load thumbnail")
onError(R.string.unable_to_load_thumbnail)
Log.w("NewPostView", "Couldn't create thumbnail, but the video can be uploaded", e)
}
}

View File

@ -35,6 +35,7 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.fonfon.kgeohash.toGeoHash
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.compose.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
import com.vitorpamplona.amethyst.model.Account
@ -48,6 +49,7 @@ import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource
import com.vitorpamplona.amethyst.ui.components.MediaCompressor
import com.vitorpamplona.amethyst.ui.components.Split
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.ammolite.relays.RelaySetupInfo
import com.vitorpamplona.quartz.encoders.Hex
import com.vitorpamplona.quartz.encoders.HexKey
@ -72,10 +74,8 @@ import com.vitorpamplona.quartz.events.findURLs
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -109,8 +109,6 @@ open class NewPostViewModel : ViewModel() {
var message by mutableStateOf(TextFieldValue(""))
var urlPreview by mutableStateOf<String?>(null)
var isUploadingImage by mutableStateOf(false)
val imageUploadingError =
MutableSharedFlow<String?>(0, 3, onBufferOverflow = BufferOverflow.DROP_OLDEST)
var userSuggestions by mutableStateOf<List<User>>(emptyList())
var userSuggestionAnchor: TextRange? = null
@ -768,6 +766,7 @@ open class NewPostViewModel : ViewModel() {
sensitiveContent: Boolean,
isPrivate: Boolean = false,
server: ServerOption,
onError: (title: String, message: String) -> Unit,
context: Context,
) {
isUploadingImage = true
@ -785,7 +784,16 @@ open class NewPostViewModel : ViewModel() {
onReady = { fileUri, contentType, size ->
if (server.isNip95) {
contentResolver.openInputStream(fileUri)?.use {
createNIP95Record(it.readBytes(), contentType, alt, sensitiveContent)
createNIP95Record(
it.readBytes(),
contentType,
alt,
sensitiveContent,
onError = {
onError(stringRes(context, R.string.failed_to_upload_media_no_details), it)
},
context,
)
}
} else {
viewModelScope.launch(Dispatchers.IO) {
@ -809,6 +817,10 @@ open class NewPostViewModel : ViewModel() {
localContentType = contentType,
alt = alt,
sensitiveContent = sensitiveContent,
onError = {
onError(stringRes(context, R.string.failed_to_upload_media_no_details), it)
},
context = context,
)
} catch (e: Exception) {
if (e is CancellationException) throw e
@ -818,16 +830,14 @@ open class NewPostViewModel : ViewModel() {
e,
)
isUploadingImage = false
viewModelScope.launch {
imageUploadingError.emit("Failed to upload: ${e.message}")
}
onError(stringRes(context, R.string.failed_to_upload_media_no_details), e.message ?: e.javaClass.simpleName)
}
}
}
},
onError = {
isUploadingImage = false
viewModelScope.launch { imageUploadingError.emit(it) }
onError(stringRes(context, R.string.failed_to_upload_media_no_details), stringRes(context, it))
},
)
}
@ -1064,6 +1074,8 @@ open class NewPostViewModel : ViewModel() {
localContentType: String?,
alt: String?,
sensitiveContent: Boolean,
onError: (message: String) -> Unit,
context: Context,
) {
// Images don't seem to be ready immediately after upload
val imageUrl = uploadingResult.tags?.firstOrNull { it.size > 1 && it[0] == "url" }?.get(1)
@ -1092,7 +1104,7 @@ open class NewPostViewModel : ViewModel() {
Log.e("ImageDownload", "Couldn't download image from server")
cancel()
isUploadingImage = false
viewModelScope.launch { imageUploadingError.emit("Failed to upload the image / video") }
onError(stringRes(context, R.string.server_did_not_provide_a_url_after_uploading))
return
}
@ -1112,7 +1124,7 @@ open class NewPostViewModel : ViewModel() {
},
onError = {
isUploadingImage = false
viewModelScope.launch { imageUploadingError.emit("Failed to upload the image / video") }
onError(stringRes(context, R.string.could_not_prepare_header, it))
},
)
}
@ -1126,10 +1138,12 @@ open class NewPostViewModel : ViewModel() {
mimeType: String?,
alt: String?,
sensitiveContent: Boolean,
onError: (message: String) -> Unit,
context: Context,
) {
if (bytes.size > 80000) {
viewModelScope.launch {
imageUploadingError.emit("Media is too big for NIP-95")
onError(stringRes(context, id = R.string.media_too_big_for_nip95))
isUploadingImage = false
}
return
@ -1157,7 +1171,7 @@ open class NewPostViewModel : ViewModel() {
},
onError = {
isUploadingImage = false
viewModelScope.launch { imageUploadingError.emit("Failed to upload the image / video") }
onError(stringRes(context, R.string.could_not_prepare_header, it))
},
)
}

View File

@ -20,7 +20,6 @@
*/
package com.vitorpamplona.amethyst.ui.actions
import android.widget.Toast
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@ -46,29 +45,20 @@ import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.placeholderText
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@Composable
fun NewUserMetadataView(
onClose: () -> Unit,
account: Account,
accountViewModel: AccountViewModel,
) {
val postViewModel: NewUserMetadataViewModel = viewModel()
val context = LocalContext.current
LaunchedEffect(Unit) {
postViewModel.load(account)
launch(Dispatchers.IO) {
postViewModel.imageUploadingError.collect { error ->
withContext(Dispatchers.Main) { Toast.makeText(context, error, Toast.LENGTH_SHORT).show() }
}
}
postViewModel.load(accountViewModel.account)
}
Dialog(
@ -164,7 +154,7 @@ fun NewUserMetadataView(
tint = MaterialTheme.colorScheme.placeholderText,
modifier = Modifier.padding(start = 5.dp),
) {
postViewModel.uploadForPicture(it, context)
postViewModel.uploadForPicture(it, context, onError = accountViewModel::toast)
}
},
singleLine = true,
@ -189,7 +179,7 @@ fun NewUserMetadataView(
tint = MaterialTheme.colorScheme.placeholderText,
modifier = Modifier.padding(start = 5.dp),
) {
postViewModel.uploadForBanner(it, context)
postViewModel.uploadForBanner(it, context, onError = accountViewModel::toast)
}
},
singleLine = true,

View File

@ -27,15 +27,15 @@ 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.Nip96Uploader
import com.vitorpamplona.amethyst.ui.components.MediaCompressor
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.quartz.events.GitHubIdentity
import com.vitorpamplona.quartz.events.MastodonIdentity
import com.vitorpamplona.quartz.events.TwitterIdentity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
import kotlin.coroutines.cancellation.CancellationException
@ -60,8 +60,6 @@ class NewUserMetadataViewModel : ViewModel() {
var isUploadingImageForPicture by mutableStateOf(false)
var isUploadingImageForBanner by mutableStateOf(false)
val imageUploadingError =
MutableSharedFlow<String?>(0, 3, onBufferOverflow = BufferOverflow.DROP_OLDEST)
fun load(account: Account) {
this.account = account
@ -130,6 +128,7 @@ class NewUserMetadataViewModel : ViewModel() {
fun uploadForPicture(
uri: Uri,
context: Context,
onError: (String, String) -> Unit,
) {
viewModelScope.launch(Dispatchers.IO) {
upload(
@ -137,6 +136,7 @@ class NewUserMetadataViewModel : ViewModel() {
context,
onUploading = { isUploadingImageForPicture = it },
onUploaded = { picture.value = it },
onError = onError,
)
}
}
@ -144,6 +144,7 @@ class NewUserMetadataViewModel : ViewModel() {
fun uploadForBanner(
uri: Uri,
context: Context,
onError: (String, String) -> Unit,
) {
viewModelScope.launch(Dispatchers.IO) {
upload(
@ -151,6 +152,7 @@ class NewUserMetadataViewModel : ViewModel() {
context,
onUploading = { isUploadingImageForBanner = it },
onUploaded = { banner.value = it },
onError = onError,
)
}
}
@ -160,6 +162,7 @@ class NewUserMetadataViewModel : ViewModel() {
context: Context,
onUploading: (Boolean) -> Unit,
onUploaded: (String) -> Unit,
onError: (String, String) -> Unit,
) {
onUploading(true)
@ -194,22 +197,19 @@ class NewUserMetadataViewModel : ViewModel() {
onUploaded(url)
} else {
onUploading(false)
viewModelScope.launch {
imageUploadingError.emit("Failed to upload the image / video")
}
onError(stringRes(context, R.string.failed_to_upload_media_no_details), stringRes(context, R.string.server_did_not_provide_a_url_after_uploading))
}
} catch (e: Exception) {
if (e is CancellationException) throw e
onUploading(false)
viewModelScope.launch {
imageUploadingError.emit("Failed to upload the image / video")
}
onError(stringRes(context, R.string.failed_to_upload_media_no_details), e.message ?: e.javaClass.simpleName)
}
}
},
onError = {
onUploading(false)
viewModelScope.launch { imageUploadingError.emit(it) }
onError(stringRes(context, R.string.error_when_compressing_media), stringRes(context, it))
},
)
}

View File

@ -30,6 +30,7 @@ import com.abedelazizshe.lightcompressorlibrary.VideoCompressor
import com.abedelazizshe.lightcompressorlibrary.VideoQuality
import com.abedelazizshe.lightcompressorlibrary.config.AppSpecificStorageConfiguration
import com.abedelazizshe.lightcompressorlibrary.config.Configuration
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.service.checkNotInMainThread
import id.zelory.compressor.Compressor
import id.zelory.compressor.constraint.default
@ -44,7 +45,7 @@ class MediaCompressor {
contentType: String?,
applicationContext: Context,
onReady: (Uri, String?, Long?) -> Unit,
onError: (String) -> Unit,
onError: (Int) -> Unit,
) {
checkNotInMainThread()
@ -87,7 +88,7 @@ class MediaCompressor {
if (path != null) {
onReady(Uri.fromFile(File(path)), contentType, size)
} else {
onError("Compression Returned null")
onError(R.string.compression_returned_null)
}
}
@ -100,7 +101,7 @@ class MediaCompressor {
}
override fun onCancelled(index: Int) {
onError("Compression Cancelled")
onError(R.string.compression_cancelled)
}
},
)

View File

@ -114,7 +114,6 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.AddressableNote
import com.vitorpamplona.amethyst.model.FeatureSetType
import com.vitorpamplona.amethyst.model.LocalCache
@ -866,7 +865,7 @@ private fun ProfileActions(
remember(accountViewModel) { derivedStateOf { accountViewModel.userProfile() == baseUser } }
if (isMe) {
EditButton(accountViewModel.account)
EditButton(accountViewModel)
}
WatchIsHiddenUser(baseUser, accountViewModel) { isHidden ->
@ -1865,11 +1864,11 @@ private fun MessageButton(
}
@Composable
private fun EditButton(account: Account) {
private fun EditButton(accountViewModel: AccountViewModel) {
var wantsToEdit by remember { mutableStateOf(false) }
if (wantsToEdit) {
NewUserMetadataView({ wantsToEdit = false }, account)
NewUserMetadataView({ wantsToEdit = false }, accountViewModel)
}
InnerEditButton { wantsToEdit = true }

View File

@ -20,7 +20,6 @@
*/
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms
import android.widget.Toast
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
@ -175,7 +174,6 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.Date
@ -242,14 +240,6 @@ fun ChannelScreen(
val lifeCycleOwner = LocalLifecycleOwner.current
LaunchedEffect(Unit) {
launch(Dispatchers.IO) {
newPostModel.imageUploadingError.collect { error ->
withContext(Dispatchers.Main) { Toast.makeText(context, error, Toast.LENGTH_SHORT).show() }
}
}
}
DisposableEffect(accountViewModel) {
NostrChannelDataSource.loadMessagesBetween(accountViewModel.account, channel)
NostrChannelDataSource.start()
@ -478,6 +468,7 @@ fun EditFieldRow(
alt = null,
sensitiveContent = false,
server = ServerOption(accountViewModel.account.defaultFileServer, false),
onError = accountViewModel::toast,
context = context,
)
}

View File

@ -20,7 +20,6 @@
*/
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms
import android.widget.Toast
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@ -129,7 +128,6 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@Composable
fun ChatroomScreen(
@ -275,14 +273,6 @@ fun ChatroomScreen(
val lifeCycleOwner = LocalLifecycleOwner.current
LaunchedEffect(room, accountViewModel) {
launch(Dispatchers.IO) {
newPostModel.imageUploadingError.collect { error ->
withContext(Dispatchers.Main) { Toast.makeText(context, error, Toast.LENGTH_SHORT).show() }
}
}
}
DisposableEffect(room, accountViewModel) {
NostrChatroomDataSource.loadMessagesBetween(accountViewModel.account, room)
NostrChatroomDataSource.start()
@ -458,6 +448,7 @@ fun PrivateMessageEditFieldRow(
sensitiveContent = false,
isPrivate = isPrivate,
server = ServerOption(accountViewModel.account.defaultFileServer, false),
onError = accountViewModel::toast,
context = context,
)
}

View File

@ -779,6 +779,12 @@
<string name="could_not_prepare_local_file_to_upload">Could not prepare local file to upload: %1$s</string>
<string name="failed_to_upload_with_message">Failed to upload: %1$s</string>
<string name="failed_to_delete_with_message">Failed to delete: %1$s</string>
<string name="media_too_big_for_nip95">Media is too big for NIP-95</string>
<string name="unable_to_load_thumbnail">Unable to load thumbnail</string>
<string name="could_not_prepare_header">Could not prepare header information: %1$s</string>
<string name="compression_cancelled">Compression Cancelled</string>
<string name="compression_returned_null">Compression failed to return a file</string>
<string name="edit_draft">Edit draft</string>