From 771441e97e319e08d02d5d33b47a864c5baf06df Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Thu, 2 Jul 2026 15:29:49 +0700 Subject: [PATCH] . --- .../androidMain/kotlin/su/reya/coop/App.kt | 3 ++ .../kotlin/su/reya/coop/MainActivity.kt | 3 ++ .../kotlin/su/reya/coop/screens/HomeScreen.kt | 5 +-- .../su/reya/coop/screens/NewIdentityScreen.kt | 2 - .../su/reya/coop/screens/OnboardingScreen.kt | 4 +- .../su/reya/coop/screens/RequestListScreen.kt | 2 - .../commonMain/kotlin/su/reya/coop/Room.kt | 1 + .../coop/{ => viewmodel}/AuthViewModel.kt | 44 +++---------------- .../coop/{ => viewmodel}/BaseViewModel.kt | 3 +- .../coop/{ => viewmodel}/ChatViewModel.kt | 5 ++- .../coop/{ => viewmodel}/NostrViewModel.kt | 3 +- 11 files changed, 22 insertions(+), 53 deletions(-) rename shared/src/commonMain/kotlin/su/reya/coop/{ => viewmodel}/AuthViewModel.kt (87%) rename shared/src/commonMain/kotlin/su/reya/coop/{ => viewmodel}/BaseViewModel.kt (95%) rename shared/src/commonMain/kotlin/su/reya/coop/{ => viewmodel}/ChatViewModel.kt (99%) rename shared/src/commonMain/kotlin/su/reya/coop/{ => viewmodel}/NostrViewModel.kt (99%) diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/App.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/App.kt index 067a5ba..fb56b4e 100644 --- a/composeApp/src/androidMain/kotlin/su/reya/coop/App.kt +++ b/composeApp/src/androidMain/kotlin/su/reya/coop/App.kt @@ -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 { error("No NostrViewModel provided") diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/MainActivity.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/MainActivity.kt index 0bcb529..ec77b12 100644 --- a/composeApp/src/androidMain/kotlin/su/reya/coop/MainActivity.kt +++ b/composeApp/src/androidMain/kotlin/su/reya/coop/MainActivity.kt @@ -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() { diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/HomeScreen.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/HomeScreen.kt index b9d1ee2..d0283e3 100644 --- a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/HomeScreen.kt +++ b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/HomeScreen.kt @@ -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) { 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) { @Composable fun ChatRoom(room: Room, onClick: () -> Unit) { val nostrViewModel = LocalNostrViewModel.current - val authViewModel = LocalAuthViewModel.current val roomState by room.rememberUiState(nostrViewModel) ListItem( diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/NewIdentityScreen.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/NewIdentityScreen.kt index 26e3837..758d75d 100644 --- a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/NewIdentityScreen.kt +++ b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/NewIdentityScreen.kt @@ -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() diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/OnboardingScreen.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/OnboardingScreen.kt index 704a023..bbdf7d6 100644 --- a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/OnboardingScreen.kt +++ b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/OnboardingScreen.kt @@ -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 diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/RequestListScreen.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/RequestListScreen.kt index 64985f1..4580f5f 100644 --- a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/RequestListScreen.kt +++ b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/RequestListScreen.kt @@ -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() diff --git a/shared/src/commonMain/kotlin/su/reya/coop/Room.kt b/shared/src/commonMain/kotlin/su/reya/coop/Room.kt index 38d7014..b1c6c3e 100644 --- a/shared/src/commonMain/kotlin/su/reya/coop/Room.kt +++ b/shared/src/commonMain/kotlin/su/reya/coop/Room.kt @@ -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 diff --git a/shared/src/commonMain/kotlin/su/reya/coop/AuthViewModel.kt b/shared/src/commonMain/kotlin/su/reya/coop/viewmodel/AuthViewModel.kt similarity index 87% rename from shared/src/commonMain/kotlin/su/reya/coop/AuthViewModel.kt rename to shared/src/commonMain/kotlin/su/reya/coop/viewmodel/AuthViewModel.kt index 7233789..56b924d 100644 --- a/shared/src/commonMain/kotlin/su/reya/coop/AuthViewModel.kt +++ b/shared/src/commonMain/kotlin/su/reya/coop/viewmodel/AuthViewModel.kt @@ -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 - } - } } diff --git a/shared/src/commonMain/kotlin/su/reya/coop/BaseViewModel.kt b/shared/src/commonMain/kotlin/su/reya/coop/viewmodel/BaseViewModel.kt similarity index 95% rename from shared/src/commonMain/kotlin/su/reya/coop/BaseViewModel.kt rename to shared/src/commonMain/kotlin/su/reya/coop/viewmodel/BaseViewModel.kt index c54c3af..d09c30a 100644 --- a/shared/src/commonMain/kotlin/su/reya/coop/BaseViewModel.kt +++ b/shared/src/commonMain/kotlin/su/reya/coop/viewmodel/BaseViewModel.kt @@ -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() { diff --git a/shared/src/commonMain/kotlin/su/reya/coop/ChatViewModel.kt b/shared/src/commonMain/kotlin/su/reya/coop/viewmodel/ChatViewModel.kt similarity index 99% rename from shared/src/commonMain/kotlin/su/reya/coop/ChatViewModel.kt rename to shared/src/commonMain/kotlin/su/reya/coop/viewmodel/ChatViewModel.kt index ebbc671..caf6e23 100644 --- a/shared/src/commonMain/kotlin/su/reya/coop/ChatViewModel.kt +++ b/shared/src/commonMain/kotlin/su/reya/coop/viewmodel/ChatViewModel.kt @@ -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, diff --git a/shared/src/commonMain/kotlin/su/reya/coop/NostrViewModel.kt b/shared/src/commonMain/kotlin/su/reya/coop/viewmodel/NostrViewModel.kt similarity index 99% rename from shared/src/commonMain/kotlin/su/reya/coop/NostrViewModel.kt rename to shared/src/commonMain/kotlin/su/reya/coop/viewmodel/NostrViewModel.kt index 1d9f6df..4ab20c4 100644 --- a/shared/src/commonMain/kotlin/su/reya/coop/NostrViewModel.kt +++ b/shared/src/commonMain/kotlin/su/reya/coop/viewmodel/NostrViewModel.kt @@ -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