This commit is contained in:
2026-07-02 15:29:49 +07:00
parent 830375f562
commit 771441e97e
11 changed files with 22 additions and 53 deletions

View File

@@ -47,6 +47,9 @@ import su.reya.coop.screens.RelayScreen
import su.reya.coop.screens.RequestListScreen
import su.reya.coop.screens.ScanScreen
import su.reya.coop.screens.UpdateProfileScreen
import su.reya.coop.viewmodel.AuthViewModel
import su.reya.coop.viewmodel.ChatViewModel
import su.reya.coop.viewmodel.NostrViewModel
val LocalNostrViewModel = staticCompositionLocalOf<NostrViewModel> {
error("No NostrViewModel provided")

View File

@@ -14,6 +14,9 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import su.reya.coop.coop.storage.SecretStore
import su.reya.coop.nostr.NostrManager
import su.reya.coop.viewmodel.AuthViewModel
import su.reya.coop.viewmodel.ChatViewModel
import su.reya.coop.viewmodel.NostrViewModel
import kotlin.system.exitProcess
class MainActivity : ComponentActivity() {

View File

@@ -60,7 +60,6 @@ import androidx.compose.material3.rememberTooltipState
import androidx.compose.material3.toShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -127,7 +126,7 @@ fun HomeScreen() {
val isRelayListEmpty by nostrViewModel.isRelayListEmpty.collectAsStateWithLifecycle()
val isSyncing by chatViewModel.isSyncing.collectAsStateWithLifecycle()
val isPartialProcessedGiftWrap by chatViewModel.isPartialProcessedGiftWrap.collectAsStateWithLifecycle()
val authState by authViewModel.state.collectAsStateWithLifecycle()
val isBannerDismissed = authState.isNotificationBannerDismissed
@@ -621,7 +620,6 @@ fun HomeScreen() {
fun NewRequests(requests: List<Room>) {
val navigator = LocalNavigator.current
val nostrViewModel = LocalNostrViewModel.current
val authViewModel = LocalAuthViewModel.current
val total = requests.size
val firstRoom = requests.getOrNull(0)
@@ -700,7 +698,6 @@ fun NewRequests(requests: List<Room>) {
@Composable
fun ChatRoom(room: Room, onClick: () -> Unit) {
val nostrViewModel = LocalNostrViewModel.current
val authViewModel = LocalAuthViewModel.current
val roomState by room.rememberUiState(nostrViewModel)
ListItem(

View File

@@ -7,13 +7,11 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.coroutines.launch
import su.reya.coop.LocalAuthViewModel
import su.reya.coop.LocalNavigator
import su.reya.coop.LocalNostrViewModel
import su.reya.coop.Screen
import su.reya.coop.shared.ProfileEditor
@Composable
fun NewIdentityScreen() {
val nostrViewModel = LocalNostrViewModel.current
val authViewModel = LocalAuthViewModel.current
val navigator = LocalNavigator.current
val scope = rememberCoroutineScope()

View File

@@ -47,7 +47,6 @@ import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.painterResource
import su.reya.coop.LocalAuthViewModel
import su.reya.coop.LocalNavigator
import su.reya.coop.LocalNostrViewModel
import su.reya.coop.LocalSnackbarHostState
import su.reya.coop.Screen
import su.reya.coop.shared.getExpressiveFontFamily
@@ -58,13 +57,12 @@ fun OnboardingScreen() {
val context = LocalContext.current
val snackbarHostState = LocalSnackbarHostState.current
val navigator = LocalNavigator.current
val nostrViewModel = LocalNostrViewModel.current
val authViewModel = LocalAuthViewModel.current
val scope = rememberCoroutineScope()
val logoPainter = painterResource(Res.drawable.coop)
val expressiveFont = getExpressiveFontFamily()
val annotatedText = buildAnnotatedString {
append("By using Coop, you agree to accept\nour ")
// Push "Terms of Use" link

View File

@@ -39,7 +39,6 @@ import coop.composeapp.generated.resources.ic_arrow_back
import kotlinx.coroutines.launch
import su.reya.coop.LocalChatViewModel
import su.reya.coop.LocalNavigator
import su.reya.coop.LocalNostrViewModel
import su.reya.coop.LocalSnackbarHostState
import su.reya.coop.RoomKind
import su.reya.coop.Screen
@@ -49,7 +48,6 @@ import su.reya.coop.Screen
fun RequestListScreen() {
val navigator = LocalNavigator.current
val snackbarHostState = LocalSnackbarHostState.current
val nostrViewModel = LocalNostrViewModel.current
val chatViewModel = LocalChatViewModel.current
val scope = rememberCoroutineScope()

View File

@@ -15,6 +15,7 @@ import kotlinx.datetime.toLocalDateTime
import rust.nostr.sdk.PublicKey
import rust.nostr.sdk.Timestamp
import rust.nostr.sdk.UnsignedEvent
import su.reya.coop.viewmodel.NostrViewModel
import kotlin.time.Clock
import kotlin.time.Instant

View File

@@ -1,21 +1,16 @@
package su.reya.coop
package su.reya.coop.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.flow.MutableStateFlow
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.AsyncNostrSigner
import rust.nostr.sdk.Keys
import rust.nostr.sdk.NostrConnect
import rust.nostr.sdk.NostrConnectUri
import rust.nostr.sdk.PublicKey
import su.reya.coop.blossom.BlossomClient
import su.reya.coop.nostr.ExternalSignerHandler
import su.reya.coop.nostr.ExternalSignerProxy
import su.reya.coop.nostr.Nostr
@@ -241,11 +236,15 @@ class AuthViewModel(
val secret = keys.secretKey().toBech32()
try {
val avatarUrl = picture?.let { blossomUpload(it, contentType ?: "image/jpeg") }
val avatarUrl =
picture?.let { blossomUpload(nostr.signer.get(), it, contentType ?: "image/jpeg") }
// Create identity
nostr.profiles.createIdentity(keys = keys, name = name, bio = bio, picture = avatarUrl)
// Persist the secret in the secret storage
secretStore.set(KEY_USER_SIGNER, secret)
// Update local states
_state.update { it.copy(isBusy = false, signerRequired = false) }
} catch (e: Exception) {
@@ -253,35 +252,4 @@ class AuthViewModel(
_state.update { it.copy(isBusy = false) }
}
}
private suspend fun blossomUpload(
file: ByteArray,
contentType: String? = "image/jpeg"
): 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) {
showError("Error: ${e.message}")
return null
}
}
}

View File

@@ -1,4 +1,4 @@
package su.reya.coop
package su.reya.coop.viewmodel
import androidx.lifecycle.ViewModel
import io.ktor.client.HttpClient
@@ -6,6 +6,7 @@ import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.json.Json
import rust.nostr.sdk.AsyncNostrSigner
import su.reya.coop.ErrorManager
import su.reya.coop.blossom.BlossomClient
abstract class BaseViewModel : ViewModel() {

View File

@@ -1,10 +1,9 @@
package su.reya.coop
package su.reya.coop.viewmodel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
@@ -19,7 +18,9 @@ import rust.nostr.sdk.PublicKey
import rust.nostr.sdk.RelayUrl
import rust.nostr.sdk.Tag
import rust.nostr.sdk.UnsignedEvent
import su.reya.coop.Room
import su.reya.coop.nostr.Nostr
import su.reya.coop.roomId
data class ChatState(
val isSyncing: Boolean = false,

View File

@@ -1,4 +1,4 @@
package su.reya.coop
package su.reya.coop.viewmodel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -27,6 +27,7 @@ import rust.nostr.sdk.PublicKey
import rust.nostr.sdk.RelayMetadata
import rust.nostr.sdk.RelayUrl
import rust.nostr.sdk.Timestamp
import su.reya.coop.Profile
import su.reya.coop.nostr.Nostr
import kotlin.time.Clock
import kotlin.time.Duration.Companion.milliseconds