add upload button

This commit is contained in:
2026-06-30 10:12:13 +07:00
parent 01e10f4680
commit 0c6b23b9e6
7 changed files with 101 additions and 47 deletions

View File

@@ -133,6 +133,7 @@ class AccountViewModel(
suspend fun importIdentity(secret: String) {
appViewModel.setBusy(true)
try {
val signer = createSigner(secret)
nostr.setSigner(signer)
@@ -150,15 +151,14 @@ class AccountViewModel(
bio: String?,
picture: ByteArray?,
contentType: String? = "image/jpeg",
profileViewModel: ProfileViewModel
) {
appViewModel.setBusy(true)
val keys = Keys.generate()
val secret = keys.secretKey().toBech32()
try {
val avatarUrl = picture?.let { profileViewModel.blossomUpload(it, contentType) }
val avatarUrl = picture?.let { appViewModel.blossomUpload(it, contentType) }
nostr.profiles.createIdentity(keys = keys, name = name, bio, picture = avatarUrl)
// Set credentials in persistent storage
secretStore.set("user_signer", secret)

View File

@@ -2,6 +2,9 @@ package su.reya.coop.viewmodels
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import io.ktor.client.HttpClient
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
@@ -9,6 +12,8 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import su.reya.coop.blossom.BlossomClient
import su.reya.coop.nostr.Nostr
import su.reya.coop.storage.SecretStorage
@@ -53,6 +58,29 @@ class AppViewModel(
}
}
suspend fun blossomUpload(file: ByteArray, contentType: String? = "image/jpeg"): String? {
val blossom = BlossomClient(
url = "https://blossom.band",
client = HttpClient {
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true
prettyPrint = true
isLenient = true
})
}
}
)
val descriptor = blossom.upload(
file = file,
contentType = contentType,
signer = nostr.signer.get()
)
return descriptor?.url
}
override fun onCleared() {
super.onCleared()
viewModelScope.launch {

View File

@@ -149,7 +149,11 @@ class ChatViewModel(
}
}
fun sendMessage(roomId: Long, message: String, replies: List<EventId> = emptyList()) {
fun sendMessage(
roomId: Long,
message: String,
replies: List<EventId> = emptyList()
) {
if (message.isEmpty()) {
appViewModel.showError("Message cannot be empty")
return
@@ -173,6 +177,23 @@ class ChatViewModel(
}
}
fun sendFileMessage(
roomId: Long,
file: ByteArray?,
contentType: String? = "image/jpeg",
replies: List<EventId> = emptyList()
) {
if (file == null) return
viewModelScope.launch {
try {
val uri = appViewModel.blossomUpload(file, contentType) ?: return@launch
sendMessage(roomId, uri, replies)
} catch (e: Exception) {
appViewModel.showError("Error: ${e.message}")
}
}
}
fun isMessageSent(id: EventId): Boolean {
val giftWrapId = nostr.messages.rumorMap[id]
return if (giftWrapId != null) {

View File

@@ -2,9 +2,6 @@ package su.reya.coop.viewmodels
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import io.ktor.client.HttpClient
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
@@ -13,11 +10,9 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.serialization.json.Json
import rust.nostr.sdk.Metadata
import rust.nostr.sdk.PublicKey
import rust.nostr.sdk.Timestamp
import su.reya.coop.blossom.BlossomClient
import su.reya.coop.nostr.Nostr
import kotlin.time.Clock
import kotlin.time.Duration.Companion.milliseconds
@@ -110,45 +105,18 @@ class ProfileViewModel(
}
}
suspend fun blossomUpload(file: ByteArray, contentType: String?): String? {
try {
val blossom = BlossomClient(
url = "https://blossom.band",
client = HttpClient {
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true
prettyPrint = true
isLenient = true
})
}
}
)
val descriptor = blossom.upload(
file = file,
contentType = contentType,
signer = nostr.signer.get()
)
return descriptor?.url
} catch (e: Exception) {
appViewModel.showError("Error: ${e.message}")
return null
}
}
suspend fun updateProfile(
name: String? = null,
bio: String? = null,
picture: ByteArray? = null,
contentType: String? = null
contentType: String? = "image/jpeg"
) {
appViewModel.setBusy(true)
try {
val avatarUrl = picture?.let { blossomUpload(it, contentType ?: "image/jpeg") }
val avatarUrl = picture?.let { appViewModel.blossomUpload(it, contentType) }
val newMetadata = nostr.profiles.updateProfile(name, bio, avatarUrl)
val currentUser = nostr.signer.getPublicKeyAsync() ?: throw Exception("User not found")
updateMetadata(currentUser, newMetadata)
} catch (e: Exception) {
appViewModel.showError("Error: ${e.message}")