refactor
This commit is contained in:
@@ -21,7 +21,6 @@ import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import su.reya.coop.nostr.NostrManager
|
||||
import su.reya.coop.nostr.roomId
|
||||
import java.io.File
|
||||
|
||||
private const val GROUP_KEY_MESSAGES = "su.reya.coop.MESSAGES"
|
||||
|
||||
@@ -70,7 +70,6 @@ import coop.composeapp.generated.resources.ic_cancel
|
||||
import coop.composeapp.generated.resources.ic_check_circle
|
||||
import coop.composeapp.generated.resources.ic_send
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
@@ -80,15 +79,14 @@ import rust.nostr.sdk.UnsignedEvent
|
||||
import su.reya.coop.LocalNavigator
|
||||
import su.reya.coop.LocalNostrViewModel
|
||||
import su.reya.coop.LocalSnackbarHostState
|
||||
import su.reya.coop.Room
|
||||
import su.reya.coop.Screen
|
||||
import su.reya.coop.nostr.Room
|
||||
import su.reya.coop.nostr.formatAsGroupHeader
|
||||
import su.reya.coop.nostr.humanReadable
|
||||
import su.reya.coop.nostr.roomId
|
||||
import su.reya.coop.formatAsGroupHeader
|
||||
import su.reya.coop.humanReadable
|
||||
import su.reya.coop.rememberUiState
|
||||
import su.reya.coop.roomId
|
||||
import su.reya.coop.shared.Avatar
|
||||
import su.reya.coop.shared.getExpressiveFontFamily
|
||||
import su.reya.coop.shared.nameFlow
|
||||
import su.reya.coop.shared.pictureFlow
|
||||
import su.reya.coop.short
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@@ -101,6 +99,9 @@ fun ChatScreen(id: Long, screening: Boolean = false) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
// Get current user
|
||||
val currentUser by nostrViewModel.currentUserProfile.collectAsStateWithLifecycle()
|
||||
|
||||
// Get chat room by ID
|
||||
val chatRooms by nostrViewModel.chatRooms.collectAsStateWithLifecycle()
|
||||
val room by remember(id) { derivedStateOf { chatRooms.firstOrNull { it.id == id } } }
|
||||
@@ -120,15 +121,7 @@ fun ChatScreen(id: Long, screening: Boolean = false) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
val displayName by remember(room) {
|
||||
room?.nameFlow(nostrViewModel) ?: flowOf("Loading...")
|
||||
}.collectAsStateWithLifecycle("Loading...")
|
||||
|
||||
val picture by remember(room) {
|
||||
room?.pictureFlow(nostrViewModel) ?: flowOf(null)
|
||||
}.collectAsStateWithLifecycle(null)
|
||||
|
||||
val roomState by (room as Room).rememberUiState(nostrViewModel, currentUser?.publicKey)
|
||||
var text by remember { mutableStateOf("") }
|
||||
var loading by remember { mutableStateOf(true) }
|
||||
var newOtherMessages by remember { mutableIntStateOf(0) }
|
||||
@@ -212,14 +205,14 @@ fun ChatScreen(id: Long, screening: Boolean = false) {
|
||||
LoadingIndicator(modifier = Modifier.size(32.dp))
|
||||
} else {
|
||||
Avatar(
|
||||
picture = picture,
|
||||
description = displayName,
|
||||
picture = roomState.picture,
|
||||
description = roomState.name,
|
||||
size = 32.dp,
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.size(8.dp))
|
||||
Text(
|
||||
text = displayName,
|
||||
text = roomState.name,
|
||||
style = MaterialTheme.typography.titleMediumEmphasized,
|
||||
)
|
||||
}
|
||||
@@ -279,7 +272,10 @@ fun ChatScreen(id: Long, screening: Boolean = false) {
|
||||
items = messagesInGroup,
|
||||
key = { it.id()?.toBech32() ?: it.hashCode() }
|
||||
) {
|
||||
ChatMessage(it)
|
||||
ChatMessage(
|
||||
rumor = it,
|
||||
isMine = currentUser?.publicKey == it.author()
|
||||
)
|
||||
}
|
||||
item {
|
||||
DateSeparator(dateHeader)
|
||||
@@ -381,12 +377,8 @@ fun ScreenerCard(room: Room) {
|
||||
var mutualContacts by remember { mutableStateOf<Set<PublicKey>>(emptySet()) }
|
||||
var lastActivity by remember { mutableStateOf<Timestamp?>(null) }
|
||||
|
||||
val metadataFlow = remember(pubkey) { nostrViewModel.getMetadata(pubkey) }
|
||||
val metadata by metadataFlow.collectAsStateWithLifecycle()
|
||||
|
||||
val profile = metadata?.asRecord()
|
||||
val displayName = profile?.displayName ?: profile?.name ?: "No name"
|
||||
val picture = profile?.picture
|
||||
val profileFlow = remember(pubkey) { nostrViewModel.getMetadata(pubkey) }
|
||||
val profile by profileFlow.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(pubkey) {
|
||||
scope.launch {
|
||||
@@ -412,7 +404,7 @@ fun ScreenerCard(room: Room) {
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Avatar(
|
||||
picture = picture,
|
||||
picture = profile?.picture,
|
||||
description = "Profile picture",
|
||||
modifier = Modifier.size(120.dp),
|
||||
shape = MaterialShapes.Cookie12Sided.toShape(),
|
||||
@@ -422,7 +414,7 @@ fun ScreenerCard(room: Room) {
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = displayName,
|
||||
text = profile?.name ?: "No name",
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.titleLargeEmphasized.copy(
|
||||
fontFamily = getExpressiveFontFamily()
|
||||
@@ -511,12 +503,9 @@ fun DateSeparator(date: String) {
|
||||
|
||||
@Composable
|
||||
fun ChatMessage(
|
||||
rumor: UnsignedEvent
|
||||
rumor: UnsignedEvent,
|
||||
isMine: Boolean = false,
|
||||
) {
|
||||
val nostrViewModel = LocalNostrViewModel.current
|
||||
val currentUser = nostrViewModel.nostr.signer.currentUser
|
||||
val isMine = rumor.author() == currentUser
|
||||
|
||||
val bubbleShape = if (isMine) {
|
||||
RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp, bottomStart = 20.dp, bottomEnd = 4.dp)
|
||||
} else {
|
||||
@@ -542,17 +531,7 @@ fun ChatMessage(
|
||||
color = containerColor,
|
||||
contentColor = contentColor,
|
||||
shape = bubbleShape,
|
||||
modifier = Modifier
|
||||
.widthIn(max = 280.dp)
|
||||
.clickable(
|
||||
onClick = {
|
||||
val id = rumor.id()
|
||||
if (id != null) {
|
||||
val sent = nostrViewModel.isMessageSent(id)
|
||||
println("Sent: $sent")
|
||||
}
|
||||
}
|
||||
)
|
||||
modifier = Modifier.widthIn(max = 280.dp)
|
||||
) {
|
||||
Text(
|
||||
text = rumor.content(),
|
||||
|
||||
@@ -284,12 +284,8 @@ fun ContactListItem(
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
val nostrViewModel = LocalNostrViewModel.current
|
||||
val metadataFlow = remember(pubkey) { nostrViewModel.getMetadata(pubkey) }
|
||||
val metadata by metadataFlow.collectAsState(initial = null)
|
||||
|
||||
val profile = metadata?.asRecord()
|
||||
val displayName = profile?.name ?: profile?.displayName ?: pubkey.short()
|
||||
val picture = profile?.picture
|
||||
val profileFlow = remember(pubkey) { nostrViewModel.getMetadata(pubkey) }
|
||||
val profile by profileFlow.collectAsState(initial = null)
|
||||
|
||||
SegmentedListItem(
|
||||
onClick = onClick,
|
||||
@@ -300,15 +296,15 @@ fun ContactListItem(
|
||||
),
|
||||
leadingContent = {
|
||||
Avatar(
|
||||
picture = picture,
|
||||
description = displayName,
|
||||
picture = profile?.picture,
|
||||
description = profile?.name,
|
||||
size = 36.dp
|
||||
)
|
||||
},
|
||||
supportingContent = { Text(text = pubkey.short()) },
|
||||
content = {
|
||||
Text(
|
||||
text = displayName,
|
||||
text = profile?.name ?: "No name",
|
||||
style = MaterialTheme.typography.titleMediumEmphasized,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -87,7 +87,6 @@ import coop.composeapp.generated.resources.ic_new_chat
|
||||
import coop.composeapp.generated.resources.ic_qr
|
||||
import coop.composeapp.generated.resources.ic_request
|
||||
import coop.composeapp.generated.resources.ic_scanner
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
import rust.nostr.sdk.PublicKey
|
||||
@@ -95,15 +94,13 @@ import su.reya.coop.LocalNavigator
|
||||
import su.reya.coop.LocalNostrViewModel
|
||||
import su.reya.coop.LocalScanResult
|
||||
import su.reya.coop.LocalSnackbarHostState
|
||||
import su.reya.coop.Room
|
||||
import su.reya.coop.RoomKind
|
||||
import su.reya.coop.Screen
|
||||
import su.reya.coop.nostr.Room
|
||||
import su.reya.coop.nostr.RoomKind
|
||||
import su.reya.coop.nostr.ago
|
||||
import su.reya.coop.ago
|
||||
import su.reya.coop.rememberUiState
|
||||
import su.reya.coop.shared.Avatar
|
||||
import su.reya.coop.shared.getExpressiveFontFamily
|
||||
import su.reya.coop.shared.nameFlow
|
||||
import su.reya.coop.shared.pictureFlow
|
||||
import su.reya.coop.short
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -120,11 +117,9 @@ fun HomeScreen() {
|
||||
val listState = rememberLazyListState()
|
||||
val pullToRefreshState = rememberPullToRefreshState()
|
||||
|
||||
val currentUser = nostrViewModel.nostr.signer.currentUser ?: return
|
||||
val currentUserProfile = nostrViewModel.getMetadata(currentUser)
|
||||
|
||||
val userProfile by currentUserProfile.collectAsStateWithLifecycle()
|
||||
val userProfile by nostrViewModel.currentUserProfile.collectAsStateWithLifecycle()
|
||||
val chatRooms by nostrViewModel.chatRooms.collectAsStateWithLifecycle()
|
||||
|
||||
val isRelayListEmpty by nostrViewModel.isRelayListEmpty.collectAsStateWithLifecycle()
|
||||
val isSyncing by nostrViewModel.isSyncing.collectAsStateWithLifecycle()
|
||||
val isPartialProcessedGiftWrap by nostrViewModel.isPartialProcessedGiftWrap.collectAsStateWithLifecycle()
|
||||
@@ -209,8 +204,8 @@ fun HomeScreen() {
|
||||
// User
|
||||
IconButton(onClick = { showBottomSheet = true }) {
|
||||
Avatar(
|
||||
picture = userProfile?.asRecord()?.picture,
|
||||
description = userProfile?.asRecord()?.displayName,
|
||||
picture = userProfile?.picture,
|
||||
description = userProfile?.name ?: "No name",
|
||||
size = 32.dp,
|
||||
)
|
||||
}
|
||||
@@ -391,14 +386,6 @@ fun HomeScreen() {
|
||||
onDismissRequest = { showBottomSheet = false },
|
||||
sheetState = sheetState,
|
||||
) {
|
||||
val pubkey = nostrViewModel.nostr.signer.currentUser
|
||||
val shortPubkey = pubkey?.short() ?: "Not available"
|
||||
|
||||
val userName =
|
||||
userProfile?.asRecord()?.displayName
|
||||
?: userProfile?.asRecord()?.name
|
||||
?: "No name"
|
||||
|
||||
val dismissAndRun: (suspend () -> Unit) -> Unit = { action ->
|
||||
scope.launch {
|
||||
sheetState.hide()
|
||||
@@ -423,8 +410,8 @@ fun HomeScreen() {
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Avatar(
|
||||
picture = userProfile?.asRecord()?.picture,
|
||||
description = userProfile?.asRecord()?.displayName,
|
||||
picture = userProfile?.picture,
|
||||
description = userProfile?.name ?: "No name",
|
||||
shape = MaterialShapes.Cookie9Sided.toShape(),
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
@@ -434,7 +421,7 @@ fun HomeScreen() {
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = userName,
|
||||
text = userProfile?.name ?: "No name",
|
||||
style = MaterialTheme.typography.titleLargeEmphasized,
|
||||
)
|
||||
}
|
||||
@@ -445,16 +432,15 @@ fun HomeScreen() {
|
||||
OutlinedButton(
|
||||
onClick = {
|
||||
scope.launch {
|
||||
pubkey?.let {
|
||||
userProfile?.publicKey?.let {
|
||||
val bech32 = it.toBech32()
|
||||
val data =
|
||||
ClipData.newPlainText(bech32, bech32)
|
||||
val data = ClipData.newPlainText(bech32, bech32)
|
||||
clipboardManager.setClipEntry(ClipEntry(data))
|
||||
}
|
||||
}
|
||||
},
|
||||
) {
|
||||
Text(text = shortPubkey)
|
||||
Text(text = userProfile?.shortPublicKey ?: "Unknown")
|
||||
}
|
||||
FilledIconButton(
|
||||
onClick = {
|
||||
@@ -585,7 +571,7 @@ fun HomeScreen() {
|
||||
scope.launch {
|
||||
isBusy = true
|
||||
try {
|
||||
nostrViewModel.refetchMsgRelays(currentUser)
|
||||
nostrViewModel.refetchMsgRelays()
|
||||
} catch (e: Exception) {
|
||||
snackbarHostState.showSnackbar("Failed to refresh metadata: ${e.message}")
|
||||
}
|
||||
@@ -634,28 +620,23 @@ fun NewRequests(requests: List<Room>) {
|
||||
val firstRoom = requests.getOrNull(0)
|
||||
val secondRoom = requests.getOrNull(1)
|
||||
|
||||
val firstName by remember(firstRoom) {
|
||||
firstRoom?.nameFlow(nostrViewModel) ?: flowOf("")
|
||||
}.collectAsStateWithLifecycle("Loading...")
|
||||
|
||||
val secondName by remember(secondRoom) {
|
||||
secondRoom?.nameFlow(nostrViewModel) ?: flowOf("")
|
||||
}.collectAsStateWithLifecycle("")
|
||||
val firstRoomState by (firstRoom as Room).rememberUiState(nostrViewModel)
|
||||
val secondRoomState by (secondRoom as Room).rememberUiState(nostrViewModel)
|
||||
|
||||
val supportingText = when {
|
||||
total == 1 && firstRoom != null -> {
|
||||
total == 1 -> {
|
||||
val message = firstRoom.lastMessage ?: ""
|
||||
"$firstName: $message"
|
||||
"${firstRoomState.name}: $message"
|
||||
}
|
||||
|
||||
total == 2 -> {
|
||||
"$firstName and $secondName"
|
||||
"${firstRoomState.name} and ${secondRoomState.name}"
|
||||
}
|
||||
|
||||
total > 2 -> {
|
||||
val othersCount = total - 2
|
||||
val othersText = if (othersCount == 1) "1 other" else "$othersCount others"
|
||||
"$firstName, $secondName and $othersText"
|
||||
"${firstRoomState.name}, ${secondRoomState.name} and $othersText"
|
||||
}
|
||||
|
||||
else -> ""
|
||||
@@ -712,13 +693,12 @@ fun NewRequests(requests: List<Room>) {
|
||||
@Composable
|
||||
fun ChatRoom(room: Room, onClick: () -> Unit) {
|
||||
val nostrViewModel = LocalNostrViewModel.current
|
||||
val displayName by remember(room) { room.nameFlow(nostrViewModel) }.collectAsStateWithLifecycle("Loading...")
|
||||
val picture by remember(room) { room.pictureFlow(nostrViewModel) }.collectAsStateWithLifecycle(null)
|
||||
val roomState by room.rememberUiState(nostrViewModel)
|
||||
|
||||
ListItem(
|
||||
modifier = Modifier.clickable(onClick = onClick),
|
||||
leadingContent = {
|
||||
Avatar(picture = picture, description = displayName)
|
||||
Avatar(picture = roomState.picture, description = roomState.picture)
|
||||
},
|
||||
headlineContent = {
|
||||
Row(
|
||||
@@ -726,7 +706,7 @@ fun ChatRoom(room: Room, onClick: () -> Unit) {
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = displayName,
|
||||
text = roomState.name,
|
||||
style = MaterialTheme.typography.titleMediumEmphasized,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
@@ -65,7 +65,6 @@ import su.reya.coop.LocalSnackbarHostState
|
||||
import su.reya.coop.Screen
|
||||
import su.reya.coop.shared.Avatar
|
||||
import su.reya.coop.shared.getExpressiveFontFamily
|
||||
import su.reya.coop.short
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
@@ -82,15 +81,10 @@ fun ImportScreen() {
|
||||
var secret by remember { mutableStateOf("") }
|
||||
var pubkey by remember { mutableStateOf<PublicKey?>(null) }
|
||||
|
||||
// Get metadata when pubkey changes
|
||||
val metadata by remember(pubkey) {
|
||||
val profile by remember(pubkey) {
|
||||
pubkey?.let(nostrViewModel::getMetadata) ?: flowOf(null)
|
||||
}.collectAsStateWithLifecycle(null)
|
||||
|
||||
val profile = metadata?.asRecord()
|
||||
val displayName = profile?.displayName ?: profile?.name ?: pubkey?.short() ?: "Unknown"
|
||||
val picture = profile?.picture
|
||||
|
||||
LaunchedEffect(qrScanResult.content) {
|
||||
qrScanResult.content?.let { result ->
|
||||
runCatching {
|
||||
@@ -164,7 +158,7 @@ fun ImportScreen() {
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Avatar(
|
||||
picture = picture,
|
||||
picture = profile?.picture,
|
||||
description = "Profile picture",
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
shape = MaterialShapes.Cookie9Sided.toShape(),
|
||||
@@ -172,7 +166,7 @@ fun ImportScreen() {
|
||||
}
|
||||
Spacer(modifier = Modifier.size(8.dp))
|
||||
Text(
|
||||
text = displayName,
|
||||
text = profile?.name ?: "",
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.titleLargeEmphasized.copy(
|
||||
fontFamily = getExpressiveFontFamily()
|
||||
|
||||
@@ -13,8 +13,10 @@ import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import coop.composeapp.generated.resources.Res
|
||||
import coop.composeapp.generated.resources.ic_arrow_back
|
||||
import io.github.alexzhirkevich.qrose.rememberQrCodePainter
|
||||
@@ -27,8 +29,8 @@ import su.reya.coop.LocalSnackbarHostState
|
||||
fun MyQrScreen() {
|
||||
val navigator = LocalNavigator.current
|
||||
val snackbarHostState = LocalSnackbarHostState.current
|
||||
val accountViewModel = LocalNostrViewModel.current
|
||||
val currentUser = accountViewModel.nostr.signer.currentUser ?: return
|
||||
val nostrViewModel = LocalNostrViewModel.current
|
||||
val currentUser by nostrViewModel.currentUserProfile.collectAsStateWithLifecycle()
|
||||
|
||||
Scaffold(
|
||||
snackbarHost = { SnackbarHost(snackbarHostState) },
|
||||
@@ -59,7 +61,7 @@ fun MyQrScreen() {
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Image(
|
||||
painter = rememberQrCodePainter(currentUser.toBech32()),
|
||||
painter = rememberQrCodePainter(currentUser?.publicKey?.toBech32() ?: ""),
|
||||
contentDescription = "My QR"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -289,12 +289,8 @@ fun ReceiverChip(
|
||||
onRemove: () -> Unit
|
||||
) {
|
||||
val nostrViewModel = LocalNostrViewModel.current
|
||||
val metadataFlow = remember(pubkey) { nostrViewModel.getMetadata(pubkey) }
|
||||
val metadata by metadataFlow.collectAsState(initial = null)
|
||||
|
||||
val profile = metadata?.asRecord()
|
||||
val displayName = profile?.name ?: profile?.displayName ?: pubkey.short()
|
||||
val picture = profile?.picture
|
||||
val profileFlow = remember(pubkey) { nostrViewModel.getMetadata(pubkey) }
|
||||
val profile by profileFlow.collectAsState(initial = null)
|
||||
|
||||
CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
|
||||
InputChip(
|
||||
@@ -302,7 +298,7 @@ fun ReceiverChip(
|
||||
onClick = onRemove,
|
||||
label = {
|
||||
Text(
|
||||
text = displayName,
|
||||
text = profile?.name ?: "No name",
|
||||
style = MaterialTheme.typography.bodyMedium.copy(
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
@@ -310,8 +306,8 @@ fun ReceiverChip(
|
||||
},
|
||||
avatar = {
|
||||
Avatar(
|
||||
picture = picture,
|
||||
description = displayName,
|
||||
picture = profile?.picture,
|
||||
description = profile?.name ?: "No name",
|
||||
size = 24.dp
|
||||
)
|
||||
},
|
||||
@@ -375,12 +371,8 @@ fun ContactListItem(
|
||||
onLongClick: () -> Unit
|
||||
) {
|
||||
val nostrViewModel = LocalNostrViewModel.current
|
||||
val metadataFlow = remember(pubkey) { nostrViewModel.getMetadata(pubkey) }
|
||||
val metadata by metadataFlow.collectAsState(initial = null)
|
||||
|
||||
val profile = metadata?.asRecord()
|
||||
val displayName = profile?.name ?: profile?.displayName ?: pubkey.short()
|
||||
val picture = profile?.picture
|
||||
val profileFlow = remember(pubkey) { nostrViewModel.getMetadata(pubkey) }
|
||||
val profile by profileFlow.collectAsState(initial = null)
|
||||
|
||||
SegmentedListItem(
|
||||
selected = isSelected,
|
||||
@@ -392,15 +384,15 @@ fun ContactListItem(
|
||||
),
|
||||
leadingContent = {
|
||||
Avatar(
|
||||
picture = picture,
|
||||
description = displayName,
|
||||
picture = profile?.picture,
|
||||
description = profile?.name ?: "",
|
||||
size = 36.dp
|
||||
)
|
||||
},
|
||||
supportingContent = { Text(text = pubkey.short()) },
|
||||
content = {
|
||||
Text(
|
||||
text = displayName,
|
||||
text = profile?.name ?: "",
|
||||
style = MaterialTheme.typography.titleMediumEmphasized,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -63,19 +63,18 @@ fun ProfileScreen(pubkey: String) {
|
||||
|
||||
val pubkeyObj = runCatching { PublicKey.parse(pubkey) }.getOrNull() ?: return
|
||||
|
||||
val metadataFlow = remember(pubkeyObj) { nostrViewModel.getMetadata(pubkeyObj) }
|
||||
val metadata by metadataFlow.collectAsStateWithLifecycle()
|
||||
val profileFlow = remember(pubkeyObj) { nostrViewModel.getMetadata(pubkeyObj) }
|
||||
val profile by profileFlow.collectAsStateWithLifecycle()
|
||||
|
||||
val profile = metadata?.asRecord()
|
||||
val displayName = profile?.displayName ?: profile?.name ?: "No name"
|
||||
val nip05 = profile?.nip05 ?: pubkeyObj.short()
|
||||
val picture = profile?.picture
|
||||
val metadata = profile?.metadata?.asRecord()
|
||||
val nip05 = metadata?.nip05 ?: pubkeyObj.short()
|
||||
val picture = metadata?.picture
|
||||
|
||||
val details = remember(profile) {
|
||||
listOf(
|
||||
"Username:" to (profile?.name ?: "None"),
|
||||
"Website:" to (profile?.website ?: "None"),
|
||||
"₿ Lightning Address:" to (profile?.lud16 ?: "None"),
|
||||
"Username:" to (metadata?.name ?: "None"),
|
||||
"Website:" to (metadata?.website ?: "None"),
|
||||
"₿ Lightning Address:" to (metadata?.lud16 ?: "None"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -133,7 +132,7 @@ fun ProfileScreen(pubkey: String) {
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Text(
|
||||
text = displayName,
|
||||
text = profile?.name ?: "No name",
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.titleLargeEmphasized.copy(
|
||||
fontFamily = getExpressiveFontFamily()
|
||||
@@ -159,7 +158,8 @@ fun ProfileScreen(pubkey: String) {
|
||||
onClick = {
|
||||
scope.launch {
|
||||
try {
|
||||
val roomId = nostrViewModel.createChatRoom(listOf(pubkeyObj))
|
||||
val roomId =
|
||||
nostrViewModel.createChatRoom(listOf(pubkeyObj))
|
||||
navigator.navigate(Screen.Chat(roomId))
|
||||
} catch (e: Exception) {
|
||||
e.message?.let { snackbarHostState.showSnackbar(it) }
|
||||
|
||||
@@ -40,8 +40,8 @@ import kotlinx.coroutines.launch
|
||||
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
|
||||
import su.reya.coop.nostr.RoomKind
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
||||
@@ -15,11 +15,9 @@ fun UpdateProfileScreen() {
|
||||
val navigator = LocalNavigator.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val currentUser = nostrViewModel.nostr.signer.currentUser ?: return
|
||||
val metadata by nostrViewModel.getMetadata(currentUser).collectAsStateWithLifecycle()
|
||||
val isBusy by nostrViewModel.isBusy.collectAsStateWithLifecycle(false)
|
||||
|
||||
val profile = metadata?.asRecord()
|
||||
val currentUser by nostrViewModel.currentUserProfile.collectAsStateWithLifecycle()
|
||||
val profile = currentUser?.metadata?.asRecord()
|
||||
|
||||
ProfileEditor(
|
||||
title = "Update profile",
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
package su.reya.coop.shared
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import rust.nostr.sdk.PublicKey
|
||||
import su.reya.coop.nostr.Room
|
||||
import su.reya.coop.short
|
||||
import su.reya.coop.NostrViewModel
|
||||
|
||||
fun Room.nameFlow(
|
||||
nostrViewModel: NostrViewModel,
|
||||
currentUser: PublicKey? = null
|
||||
): Flow<String> {
|
||||
// Return early if there's a custom subject/room name
|
||||
subject?.takeIf { it.isNotBlank() }?.let { return flowOf(it) }
|
||||
|
||||
val displayMembers = if (isGroup()) members.take(2) else members.take(1)
|
||||
if (displayMembers.isEmpty()) return flowOf("Unknown")
|
||||
|
||||
return combine(displayMembers.map { nostrViewModel.getMetadata(it) }) { metadataArray ->
|
||||
val names = metadataArray.mapIndexed { i, metadata ->
|
||||
val profile = metadata?.asRecord()
|
||||
profile?.displayName?.takeIf { it.isNotBlank() }
|
||||
?: profile?.name?.takeIf { it.isNotBlank() }
|
||||
?: displayMembers[i].short()
|
||||
}
|
||||
|
||||
if (isGroup()) {
|
||||
val combined = names.joinToString(", ")
|
||||
val extraCount = members.size - names.size
|
||||
if (extraCount > 0) "$combined, +$extraCount" else combined
|
||||
} else {
|
||||
val name = names.first()
|
||||
if (displayMembers.first() == currentUser) "$name (you)" else name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Room.pictureFlow(nostrViewModel: NostrViewModel): Flow<String?> {
|
||||
val firstMember = members.firstOrNull() ?: return flowOf(null)
|
||||
return nostrViewModel.getMetadata(firstMember).map { it?.asRecord()?.picture }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user