This commit is contained in:
2026-07-11 16:41:39 +07:00
parent c5b76e9b2f
commit f2c1587efa
19 changed files with 528 additions and 381 deletions

View File

@@ -35,7 +35,6 @@ import androidx.navigation3.runtime.rememberNavBackStack
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
import androidx.navigation3.ui.NavDisplay
import kotlinx.coroutines.launch
import su.reya.coop.screens.chat.ChatScreen
import su.reya.coop.screens.ContactListScreen
import su.reya.coop.screens.HomeScreen
import su.reya.coop.screens.ImportScreen
@@ -48,10 +47,10 @@ 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.screens.chat.ChatScreen
import su.reya.coop.viewmodel.ChatViewModel
import su.reya.coop.viewmodel.NostrViewModel
import su.reya.coop.viewmodel.RelayViewModel
import su.reya.coop.viewmodel.account.AccountViewModel
val LocalNostrViewModel = staticCompositionLocalOf<NostrViewModel> {
error("No NostrViewModel provided")
@@ -61,13 +60,10 @@ val LocalChatViewModel = staticCompositionLocalOf<ChatViewModel> {
error("No ChatViewModel provided")
}
val LocalAuthViewModel = staticCompositionLocalOf<AuthViewModel> {
error("No AuthViewModel provided")
val LocalAccountViewModel = staticCompositionLocalOf<AccountViewModel> {
error("No AccountViewModel provided")
}
val LocalRelayViewModel = staticCompositionLocalOf<RelayViewModel> {
error("No RelayViewModel provided")
}
val LocalSnackbarHostState = staticCompositionLocalOf<SnackbarHostState> {
error("No SnackbarHostState provided")
@@ -85,9 +81,8 @@ val LocalScanResult = staticCompositionLocalOf<QrScanResult> {
@Composable
fun App(
nostrViewModel: NostrViewModel,
relayViewModel: RelayViewModel,
chatViewModel: ChatViewModel,
authViewModel: AuthViewModel,
accountViewModel: AccountViewModel,
) {
val context = LocalContext.current
val activity = context as? ComponentActivity
@@ -96,8 +91,8 @@ fun App(
val qrScanResult = remember { QrScanResult() }
// Get the signer required state
val authState by authViewModel.state.collectAsStateWithLifecycle()
val signerRequired = authState.signerRequired
val accountState by accountViewModel.state.collectAsStateWithLifecycle()
val signerRequired = accountState.signerRequired
// Snackbar
val snackbarHostState = remember { SnackbarHostState() }
@@ -125,7 +120,7 @@ fun App(
LaunchedEffect(Unit) {
launch {
authViewModel.errorEvents.collect { message ->
accountViewModel.errorEvents.collect { message ->
snackbarHostState.showSnackbar(message)
}
}
@@ -139,11 +134,6 @@ fun App(
snackbarHostState.showSnackbar(message)
}
}
launch {
relayViewModel.errorEvents.collect { message ->
snackbarHostState.showSnackbar(message)
}
}
}
LaunchedEffect(activity) {
@@ -185,9 +175,8 @@ fun App(
) {
CompositionLocalProvider(
LocalNostrViewModel provides nostrViewModel,
LocalRelayViewModel provides relayViewModel,
LocalChatViewModel provides chatViewModel,
LocalAuthViewModel provides authViewModel,
LocalAccountViewModel provides accountViewModel,
LocalSnackbarHostState provides snackbarHostState,
LocalNavigator provides navigator,
LocalScanResult provides qrScanResult,

View File

@@ -14,10 +14,9 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import su.reya.coop.nostr.NostrManager
import su.reya.coop.repository.MediaRepository
import su.reya.coop.viewmodel.AuthViewModel
import su.reya.coop.viewmodel.ChatViewModel
import su.reya.coop.viewmodel.NostrViewModel
import su.reya.coop.viewmodel.RelayViewModel
import su.reya.coop.viewmodel.account.AccountViewModel
import kotlin.system.exitProcess
class MainActivity : ComponentActivity() {
@@ -29,20 +28,18 @@ class MainActivity : ComponentActivity() {
object : ViewModelProvider.Factory {
private val storage = AppStore(this@MainActivity)
private val mediaRepository = MediaRepository()
private val nostrViewModel = NostrViewModel(NostrManager.instance, mediaRepository)
private val relayViewModel = RelayViewModel(NostrManager.instance)
private val nostrViewModel = NostrViewModel(NostrManager.instance)
private val chatViewModel = ChatViewModel(NostrManager.instance, mediaRepository)
private val androidSigner =
AndroidExternalSigner(this@MainActivity, externalSignerLauncher)
private val authViewModel =
AuthViewModel(NostrManager.instance, storage, mediaRepository, androidSigner)
private val accountViewModel =
AccountViewModel(NostrManager.instance, storage, mediaRepository, androidSigner)
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return when {
modelClass.isAssignableFrom(NostrViewModel::class.java) -> nostrViewModel
modelClass.isAssignableFrom(RelayViewModel::class.java) -> relayViewModel
modelClass.isAssignableFrom(ChatViewModel::class.java) -> chatViewModel
modelClass.isAssignableFrom(AuthViewModel::class.java) -> authViewModel
modelClass.isAssignableFrom(AccountViewModel::class.java) -> accountViewModel
else -> throw IllegalArgumentException("Unknown ViewModel class")
} as T
}
@@ -50,9 +47,8 @@ class MainActivity : ComponentActivity() {
}
private val nostrViewModel: NostrViewModel by viewModels { factory }
private val relayViewModel: RelayViewModel by viewModels { factory }
private val chatViewModel: ChatViewModel by viewModels { factory }
private val authViewModel: AuthViewModel by viewModels { factory }
private val accountViewModel: AccountViewModel by viewModels { factory }
override fun onCreate(savedInstanceState: Bundle?) {
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
@@ -91,15 +87,14 @@ class MainActivity : ComponentActivity() {
// Keep the splash screen visible until the signer check is complete
splashScreen.setKeepOnScreenCondition {
authViewModel.state.value.signerRequired == null
accountViewModel.state.value.signerRequired == null
}
setContent {
App(
nostrViewModel = nostrViewModel,
relayViewModel = relayViewModel,
chatViewModel = chatViewModel,
authViewModel = authViewModel,
accountViewModel = accountViewModel,
)
}
}

View File

@@ -58,6 +58,7 @@ import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.painterResource
import rust.nostr.sdk.Nip05Address
import rust.nostr.sdk.PublicKey
import su.reya.coop.LocalAccountViewModel
import su.reya.coop.LocalChatViewModel
import su.reya.coop.LocalNavigator
import su.reya.coop.LocalNostrViewModel
@@ -72,9 +73,10 @@ fun ContactListScreen() {
val navigator = LocalNavigator.current
val snackbarHostState = LocalSnackbarHostState.current
val nostrViewModel = LocalNostrViewModel.current
val accountViewModel = LocalAccountViewModel.current
val chatViewModel = LocalChatViewModel.current
val contactList by nostrViewModel.contactList.collectAsStateWithLifecycle()
val contactList by accountViewModel.contactList.collectAsStateWithLifecycle()
var openAddContactDialog by remember { mutableStateOf(false) }
var contactToDelete by remember { mutableStateOf<PublicKey?>(null) }
@@ -201,7 +203,7 @@ fun ContactListScreen() {
confirmButton = {
TextButton(
onClick = {
contactToDelete?.let { nostrViewModel.removeContact(it) }
contactToDelete?.let { accountViewModel.removeContact(it) }
contactToDelete = null
}
) {
@@ -222,6 +224,7 @@ fun ContactListScreen() {
fun AddContactDialog(onDismissRequest: () -> Unit) {
val snackbarHostState = LocalSnackbarHostState.current
val nostrViewModel = LocalNostrViewModel.current
val accountViewModel = LocalAccountViewModel.current
val focusRequester = remember { FocusRequester() }
var contact by remember { mutableStateOf("") }
var isError by remember { mutableStateOf(false) }
@@ -265,7 +268,7 @@ fun AddContactDialog(onDismissRequest: () -> Unit) {
},
actions = {
IconButton(onClick = {
nostrViewModel.addContact(contact)
accountViewModel.addContact(contact)
onDismissRequest()
}) {
Icon(

View File

@@ -89,11 +89,10 @@ import coop.composeapp.generated.resources.ic_scanner
import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.painterResource
import rust.nostr.sdk.PublicKey
import su.reya.coop.LocalAuthViewModel
import su.reya.coop.LocalAccountViewModel
import su.reya.coop.LocalChatViewModel
import su.reya.coop.LocalNavigator
import su.reya.coop.LocalNostrViewModel
import su.reya.coop.LocalRelayViewModel
import su.reya.coop.LocalScanResult
import su.reya.coop.LocalSnackbarHostState
import su.reya.coop.Room
@@ -114,23 +113,22 @@ fun HomeScreen() {
val clipboardManager = LocalClipboard.current
val nostrViewModel = LocalNostrViewModel.current
val chatViewModel = LocalChatViewModel.current
val authViewModel = LocalAuthViewModel.current
val relayViewModel = LocalRelayViewModel.current
val accountViewModel = LocalAccountViewModel.current
val scope = rememberCoroutineScope()
val sheetState = rememberModalBottomSheetState(true)
val listState = rememberLazyListState()
val pullToRefreshState = rememberPullToRefreshState()
val userProfile by nostrViewModel.currentUserProfile.collectAsStateWithLifecycle()
val userProfile by accountViewModel.currentUserProfile.collectAsStateWithLifecycle()
val chatRooms by chatViewModel.chatRooms.collectAsStateWithLifecycle()
val isRelayListEmpty by relayViewModel.isRelayListEmpty.collectAsStateWithLifecycle()
val isRelayListEmpty by accountViewModel.isRelayListEmpty.collectAsStateWithLifecycle()
val isSyncing by chatViewModel.isSyncing.collectAsStateWithLifecycle()
val isPartialProcessedGiftWrap by chatViewModel.isPartialProcessedGiftWrap.collectAsStateWithLifecycle()
val authState by authViewModel.state.collectAsStateWithLifecycle()
val isBannerDismissed = authState.isNotificationBannerDismissed
val accountState by accountViewModel.state.collectAsStateWithLifecycle()
val isBannerDismissed = accountState.isNotificationBannerDismissed
val expandedFab by remember { derivedStateOf { listState.firstVisibleItemIndex == 0 } }
var showBottomSheet by remember { mutableStateOf(false) }
@@ -157,7 +155,7 @@ fun HomeScreen() {
onPauseOrDispose { }
}
LaunchedEffect(authState.signerRequired) {
LaunchedEffect(accountState.signerRequired) {
chatViewModel.refreshChatRooms()
}
@@ -282,7 +280,7 @@ fun HomeScreen() {
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
TextButton(
onClick = { authViewModel.dismissNotificationBanner() },
onClick = { accountViewModel.dismissNotificationBanner() },
modifier = Modifier.weight(1f),
) {
Text(text = "Maybe later")
@@ -469,7 +467,7 @@ fun HomeScreen() {
// Show the relay setup dialog if the msg relay list is empty
if (isRelayListEmpty) {
ModalBottomSheet(
onDismissRequest = { relayViewModel.dismissRelayWarning() },
onDismissRequest = { accountViewModel.dismissRelayWarning() },
sheetState = sheetState,
containerColor = MaterialTheme.colorScheme.surfaceContainer,
) {
@@ -574,7 +572,7 @@ fun HomeScreen() {
enabled = !isBusy,
onClick = {
isBusy = true
relayViewModel.refetchMsgRelays()
accountViewModel.refetchMsgRelays()
isBusy = false
},
modifier = Modifier
@@ -589,7 +587,7 @@ fun HomeScreen() {
Button(
enabled = !isBusy,
onClick = {
relayViewModel.useDefaultMsgRelayList()
accountViewModel.useDefaultMsgRelayList()
scope.launch { sheetState.hide() }
},
modifier = Modifier
@@ -738,7 +736,7 @@ fun BottomMenuList(
val navigator = LocalNavigator.current
val nostrViewModel = LocalNostrViewModel.current
val chatViewModel = LocalChatViewModel.current
val authViewModel = LocalAuthViewModel.current
val accountViewModel = LocalAccountViewModel.current
val defaultMenuList = listOf(
"Update Profile" to { navigator.navigate(Screen.UpdateProfile) },
@@ -770,8 +768,8 @@ fun BottomMenuList(
FilledTonalButton(
onClick = {
onDismiss {
authViewModel.logout(onLogout = {
nostrViewModel.resetInternalState()
accountViewModel.logout(onLogout = {
accountViewModel.resetInternalState()
chatViewModel.resetInternalState()
})
}

View File

@@ -50,7 +50,7 @@ import coop.composeapp.generated.resources.ic_scanner
import org.jetbrains.compose.resources.painterResource
import rust.nostr.sdk.Keys
import rust.nostr.sdk.NostrConnectUri
import su.reya.coop.LocalAuthViewModel
import su.reya.coop.LocalAccountViewModel
import su.reya.coop.LocalNavigator
import su.reya.coop.LocalScanResult
import su.reya.coop.LocalSnackbarHostState
@@ -63,9 +63,9 @@ fun ImportScreen() {
val navigator = LocalNavigator.current
val qrScanResult = LocalScanResult.current
val focusManager = LocalFocusManager.current
val authViewModel = LocalAuthViewModel.current
val accountViewModel = LocalAccountViewModel.current
val authState by authViewModel.state.collectAsStateWithLifecycle()
val accountState by accountViewModel.state.collectAsStateWithLifecycle()
var secret by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
@@ -98,15 +98,15 @@ fun ImportScreen() {
}
// Navigate to Home on successful import (signerRequired becomes false)
LaunchedEffect(authState.signerRequired) {
if (authState.signerRequired == false) {
LaunchedEffect(accountState.signerRequired) {
if (accountState.signerRequired == false) {
navigator.navigate(Screen.Home)
}
}
// Show import errors via snackbar
LaunchedEffect(authState.importError) {
authState.importError?.let {
LaunchedEffect(accountState.importError) {
accountState.importError?.let {
snackbarHostState.showSnackbar(it)
}
}
@@ -177,7 +177,7 @@ fun ImportScreen() {
BasicTextField(
value = secret,
onValueChange = { secret = it },
enabled = !authState.isImporting,
enabled = !accountState.isImporting,
modifier = Modifier.fillMaxWidth(),
singleLine = true,
keyboardOptions = KeyboardOptions(
@@ -222,7 +222,7 @@ fun ImportScreen() {
BasicTextField(
value = password,
onValueChange = { password = it },
enabled = !authState.isImporting && requirePassword,
enabled = !accountState.isImporting && requirePassword,
modifier = Modifier.fillMaxWidth(),
singleLine = true,
keyboardOptions = KeyboardOptions(
@@ -250,14 +250,14 @@ fun ImportScreen() {
Spacer(modifier = Modifier.size(16.dp))
Button(
onClick = {
authViewModel.importIdentity(secret, password)
accountViewModel.importIdentity(secret, password)
},
modifier = Modifier
.fillMaxWidth()
.height(ButtonDefaults.MediumContainerHeight),
enabled = secret.isNotBlank() && !authState.isImporting,
enabled = secret.isNotBlank() && !accountState.isImporting,
) {
if (authState.isImporting) {
if (accountState.isImporting) {
LoadingIndicator()
} else {
Text(

View File

@@ -21,16 +21,16 @@ import coop.composeapp.generated.resources.Res
import coop.composeapp.generated.resources.ic_arrow_back
import io.github.alexzhirkevich.qrose.rememberQrCodePainter
import org.jetbrains.compose.resources.painterResource
import su.reya.coop.LocalAccountViewModel
import su.reya.coop.LocalNavigator
import su.reya.coop.LocalNostrViewModel
import su.reya.coop.LocalSnackbarHostState
@Composable
fun MyQrScreen() {
val navigator = LocalNavigator.current
val snackbarHostState = LocalSnackbarHostState.current
val nostrViewModel = LocalNostrViewModel.current
val currentUser by nostrViewModel.currentUserProfile.collectAsStateWithLifecycle()
val accountViewModel = LocalAccountViewModel.current
val currentUser by accountViewModel.currentUserProfile.collectAsStateWithLifecycle()
Scaffold(
snackbarHost = { SnackbarHost(snackbarHostState) },

View File

@@ -55,6 +55,7 @@ import coop.composeapp.generated.resources.ic_scanner
import kotlinx.coroutines.delay
import org.jetbrains.compose.resources.painterResource
import rust.nostr.sdk.PublicKey
import su.reya.coop.LocalAccountViewModel
import su.reya.coop.LocalChatViewModel
import su.reya.coop.LocalNavigator
import su.reya.coop.LocalNostrViewModel
@@ -72,9 +73,10 @@ fun NewChatScreen() {
val navigator = LocalNavigator.current
val qrScanResult = LocalScanResult.current
val nostrViewModel = LocalNostrViewModel.current
val accountViewModel = LocalAccountViewModel.current
val chatViewModel = LocalChatViewModel.current
val contactList by nostrViewModel.contactList.collectAsStateWithLifecycle()
val contactList by accountViewModel.contactList.collectAsStateWithLifecycle()
var query by remember { mutableStateOf("") }
val createGroup = remember { mutableStateOf(false) }
@@ -96,13 +98,13 @@ fun NewChatScreen() {
selectedReceivers.add(pubkey)
}
} else if (query.contains("@")) {
nostrViewModel.searchByAddress(query) { pubkey ->
accountViewModel.searchByAddress(query) { pubkey ->
if (pubkey != null) {
selectedReceivers.add(pubkey)
}
}
} else {
nostrViewModel.searchByNostr(query) { results ->
accountViewModel.searchByNostr(query) { results ->
searchResults.clear()
searchResults.addAll(results)
}

View File

@@ -1,14 +1,14 @@
package su.reya.coop.screens
import androidx.compose.runtime.Composable
import su.reya.coop.LocalAuthViewModel
import su.reya.coop.LocalAccountViewModel
import su.reya.coop.LocalNavigator
import su.reya.coop.Screen
import su.reya.coop.shared.ProfileEditor
@Composable
fun NewIdentityScreen() {
val authViewModel = LocalAuthViewModel.current
val accountViewModel = LocalAccountViewModel.current
val navigator = LocalNavigator.current
ProfileEditor(
@@ -16,7 +16,7 @@ fun NewIdentityScreen() {
buttonLabel = "Continue",
onBack = { navigator.goBack() },
onConfirm = { name, bio, bytes, type ->
authViewModel.createIdentity(name, bio, bytes, type)
accountViewModel.createIdentity(name, bio, bytes, type)
navigator.navigate(Screen.Home)
}
)

View File

@@ -48,7 +48,7 @@ import coop.composeapp.generated.resources.coop
import kotlinx.coroutines.launch
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.jetbrains.compose.resources.painterResource
import su.reya.coop.LocalAuthViewModel
import su.reya.coop.LocalAccountViewModel
import su.reya.coop.LocalNavigator
import su.reya.coop.LocalSnackbarHostState
import su.reya.coop.Screen
@@ -60,21 +60,21 @@ fun OnboardingScreen() {
val context = LocalContext.current
val snackbarHostState = LocalSnackbarHostState.current
val navigator = LocalNavigator.current
val authViewModel = LocalAuthViewModel.current
val accountViewModel = LocalAccountViewModel.current
val authState by authViewModel.state.collectAsStateWithLifecycle()
val accountState by accountViewModel.state.collectAsStateWithLifecycle()
val scope = rememberCoroutineScope()
// Navigate to Home on successful external signer connection
LaunchedEffect(authState.signerRequired) {
if (authState.signerRequired == false) {
LaunchedEffect(accountState.signerRequired) {
if (accountState.signerRequired == false) {
navigator.navigate(Screen.Home)
}
}
// Show connection errors
LaunchedEffect(authState.importError) {
authState.importError?.let {
LaunchedEffect(accountState.importError) {
accountState.importError?.let {
snackbarHostState.showSnackbar(it)
}
}
@@ -176,10 +176,10 @@ fun OnboardingScreen() {
Spacer(modifier = Modifier.size(8.dp))
FilledTonalButton(
onClick = {
if (authViewModel.isExternalSignerAvailable()) {
if (accountViewModel.isExternalSignerAvailable()) {
// Connect to the external signer
// TODO: show all available signers?
authViewModel.connectExternalSigner()
accountViewModel.connectExternalSigner()
} else {
scope.launch {
val result = snackbarHostState.showSnackbar(

View File

@@ -68,7 +68,7 @@ import rust.nostr.sdk.RelayMetadata
import rust.nostr.sdk.RelayUrl
import su.reya.coop.LocalNavigator
import su.reya.coop.LocalNostrViewModel
import su.reya.coop.LocalRelayViewModel
import su.reya.coop.LocalAccountViewModel
import su.reya.coop.LocalSnackbarHostState
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@@ -76,7 +76,7 @@ import su.reya.coop.LocalSnackbarHostState
fun RelayScreen() {
val navigator = LocalNavigator.current
val nostrViewModel = LocalNostrViewModel.current
val relayViewModel = LocalRelayViewModel.current
val accountViewModel = LocalAccountViewModel.current
val snackbarHostState = LocalSnackbarHostState.current
val scope = rememberCoroutineScope()
@@ -99,12 +99,12 @@ fun RelayScreen() {
var relayToDelete by remember { mutableStateOf<String?>(null) }
LaunchedEffect(Unit) {
relayViewModel.loadCurrentUserRelayList()
relayViewModel.loadCurrentUserMsgRelayList()
accountViewModel.loadCurrentUserRelayList()
accountViewModel.loadCurrentUserMsgRelayList()
}
val loadedRelayList by relayViewModel.currentUserRelayList.collectAsStateWithLifecycle()
val loadedMsgRelayList by relayViewModel.currentUserMsgRelayList.collectAsStateWithLifecycle()
val loadedRelayList by accountViewModel.currentUserRelayList.collectAsStateWithLifecycle()
val loadedMsgRelayList by accountViewModel.currentUserMsgRelayList.collectAsStateWithLifecycle()
LaunchedEffect(loadedRelayList) {
if (loadedRelayList.isNotEmpty()) {
@@ -341,7 +341,7 @@ fun RelayScreen() {
relayToDelete = null
return@TextButton
}
relayViewModel.removeMsgRelay(relayToDelete!!)
accountViewModel.removeMsgRelay(relayToDelete!!)
msgRelayList.removeIf { it.toString() == relayToDelete }
relayToDelete = null
}
@@ -365,7 +365,7 @@ fun AddRelayDialog(
onMsgRelayAdded: (newRelay: String) -> Unit,
onRelayAdded: (newRelay: String, metadata: RelayMetadata?) -> Unit,
) {
val relayViewModel = LocalRelayViewModel.current
val accountViewModel = LocalAccountViewModel.current
val snackbarHostState = LocalSnackbarHostState.current
val scope = rememberCoroutineScope()
@@ -416,17 +416,17 @@ fun AddRelayDialog(
if (!isError) {
when (selected) {
"Messaging" -> {
relayViewModel.addMsgRelay(relayAddress)
accountViewModel.addMsgRelay(relayAddress)
onMsgRelayAdded(relayAddress)
}
"Inbox" -> {
relayViewModel.addInboxRelay(relayAddress)
accountViewModel.addInboxRelay(relayAddress)
onRelayAdded(relayAddress, RelayMetadata.WRITE)
}
"Outbox" -> {
relayViewModel.addOutboxRelay(relayAddress)
accountViewModel.addOutboxRelay(relayAddress)
onRelayAdded(relayAddress, RelayMetadata.READ)
}
}

View File

@@ -3,16 +3,16 @@ package su.reya.coop.screens
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import su.reya.coop.LocalAccountViewModel
import su.reya.coop.LocalNavigator
import su.reya.coop.LocalNostrViewModel
import su.reya.coop.shared.ProfileEditor
@Composable
fun UpdateProfileScreen() {
val nostrViewModel = LocalNostrViewModel.current
val accountViewModel = LocalAccountViewModel.current
val navigator = LocalNavigator.current
val currentUser by nostrViewModel.currentUserProfile.collectAsStateWithLifecycle()
val currentUser by accountViewModel.currentUserProfile.collectAsStateWithLifecycle()
val profile = currentUser?.metadata?.asRecord()
ProfileEditor(
@@ -23,7 +23,7 @@ fun UpdateProfileScreen() {
initialPicture = profile?.picture,
onBack = { navigator.goBack() },
onConfirm = { name, bio, bytes, type ->
nostrViewModel.updateProfile(name, bio, bytes, type)
accountViewModel.updateProfile(name, bio, bytes, type)
navigator.goBack()
}
)

View File

@@ -31,6 +31,7 @@ import coop.composeapp.generated.resources.ic_check_circle
import org.jetbrains.compose.resources.painterResource
import rust.nostr.sdk.PublicKey
import rust.nostr.sdk.Timestamp
import su.reya.coop.LocalAccountViewModel
import su.reya.coop.LocalNostrViewModel
import su.reya.coop.Room
import su.reya.coop.humanReadable
@@ -44,6 +45,7 @@ fun ScreenerCard(room: Room) {
val pubkey = room.members.firstOrNull() ?: return
val nostrViewModel = LocalNostrViewModel.current
val accountViewModel = LocalAccountViewModel.current
var isContact by remember { mutableStateOf(false) }
var mutualContacts by remember { mutableStateOf<Set<PublicKey>>(emptySet()) }
@@ -54,11 +56,11 @@ fun ScreenerCard(room: Room) {
LaunchedEffect(pubkey) {
// Check contact
nostrViewModel.verifyContact(pubkey) { isContact = it }
accountViewModel.verifyContact(pubkey) { isContact = it }
// Get mutual contacts
nostrViewModel.mutualContacts(pubkey) { mutualContacts = it }
accountViewModel.mutualContacts(pubkey) { mutualContacts = it }
// Get the last activity
nostrViewModel.verifyActivity(pubkey) { lastActivity = it }
accountViewModel.verifyActivity(pubkey) { lastActivity = it }
}
Column(

View File

@@ -65,6 +65,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jetbrains.compose.resources.painterResource
import rust.nostr.sdk.UnsignedEvent
import su.reya.coop.LocalAccountViewModel
import su.reya.coop.LocalChatViewModel
import su.reya.coop.LocalNavigator
import su.reya.coop.LocalNostrViewModel
@@ -92,7 +93,8 @@ fun ChatScreen(
val listState = rememberLazyListState()
// Get current user
val currentUser by nostrViewModel.currentUserProfile.collectAsStateWithLifecycle()
val accountViewModel = LocalAccountViewModel.current
val currentUser by accountViewModel.currentUserProfile.collectAsStateWithLifecycle()
// Get chat room by ID
val chatRooms by chatViewModel.chatRooms.collectAsStateWithLifecycle()