ui: show three-state favorite stars in people sheet

Mirror the private-chat header so peers who favorited us get an orange outline until we favorite back.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
callebtc 2026-07-27 21:11:37 +02:00
parent 0f312a13f0
commit 32d1916ec0

View File

@ -358,7 +358,8 @@ fun PeopleSection(
// Observe reactive state for favorites and fingerprints // Observe reactive state for favorites and fingerprints
val hasUnreadPrivateMessages by viewModel.unreadPrivateMessages.collectAsStateWithLifecycle() val hasUnreadPrivateMessages by viewModel.unreadPrivateMessages.collectAsStateWithLifecycle()
val privateChats by viewModel.privateChats.collectAsStateWithLifecycle() val privateChats by viewModel.privateChats.collectAsStateWithLifecycle()
val favoritePeers by viewModel.favoritePeers.collectAsStateWithLifecycle() val favoritePeers by viewModel.favoritePeers.collectAsStateWithLifecycle()
val peerFavoritedUs by viewModel.peerFavoritedUs.collectAsStateWithLifecycle()
val peerFingerprints by viewModel.peerFingerprints.collectAsStateWithLifecycle() val peerFingerprints by viewModel.peerFingerprints.collectAsStateWithLifecycle()
val verifiedFingerprints by viewModel.verifiedFingerprints.collectAsStateWithLifecycle() val verifiedFingerprints by viewModel.verifiedFingerprints.collectAsStateWithLifecycle()
@ -370,6 +371,22 @@ fun PeopleSection(
} }
} }
// Same "they favorited us" signal the private-chat header uses for orange outline stars.
val peerTheyFavoritedUsStates = remember(peerFavoritedUs, peerFingerprints, connectedPeers) {
connectedPeers.associateWith { peerID ->
val fingerprint = peerFingerprints[peerID]
if (fingerprint != null && peerFavoritedUs.contains(fingerprint)) {
true
} else {
try {
FavoritesPersistenceService.shared.getFavoriteStatus(peerID)?.theyFavoritedUs == true
} catch (_: Exception) {
false
}
}
}
}
val peerVerifiedStates = remember(verifiedFingerprints, peerFingerprints, connectedPeers) { val peerVerifiedStates = remember(verifiedFingerprints, peerFingerprints, connectedPeers) {
connectedPeers.associateWith { peerID -> connectedPeers.associateWith { peerID ->
viewModel.isPeerVerified(peerID, verifiedFingerprints) viewModel.isPeerVerified(peerID, verifiedFingerprints)
@ -474,6 +491,7 @@ fun PeopleSection(
val peerID = connectedPeerForRow val peerID = connectedPeerForRow
val conversationID = ContactDirectory.canonicalConversationId(peerID) val conversationID = ContactDirectory.canonicalConversationId(peerID)
val isFavorite = peerFavoriteStates[peerID] ?: false val isFavorite = peerFavoriteStates[peerID] ?: false
val theyFavoritedUs = peerTheyFavoritedUsStates[peerID] ?: false
val isVerified = peerVerifiedStates[peerID] ?: false val isVerified = peerVerifiedStates[peerID] ?: false
// fingerprint and favorite relationship resolution not needed here; UI will show Nostr globe for appended offline favorites below // fingerprint and favorite relationship resolution not needed here; UI will show Nostr globe for appended offline favorites below
@ -499,6 +517,7 @@ fun PeopleSection(
isWifiAware = peerID in wifiAwarePeerIDs, isWifiAware = peerID in wifiAwarePeerIDs,
isSelected = conversationID == selectedPrivatePeer || peerID == selectedPrivatePeer, isSelected = conversationID == selectedPrivatePeer || peerID == selectedPrivatePeer,
isFavorite = isFavorite, isFavorite = isFavorite,
theyFavoritedUs = theyFavoritedUs,
isVerified = isVerified, isVerified = isVerified,
hasUnreadDM = combinedHasUnread, hasUnreadDM = combinedHasUnread,
colorScheme = colorScheme, colorScheme = colorScheme,
@ -548,6 +567,7 @@ fun PeopleSection(
isDirect = false, isDirect = false,
isSelected = conversationID == selectedPrivatePeer || (mappedConnectedPeerID ?: favPeerID) == selectedPrivatePeer, isSelected = conversationID == selectedPrivatePeer || (mappedConnectedPeerID ?: favPeerID) == selectedPrivatePeer,
isFavorite = true, isFavorite = true,
theyFavoritedUs = fav.theyFavoritedUs,
isVerified = isVerified, isVerified = isVerified,
hasUnreadDM = hasUnread, hasUnreadDM = hasUnread,
colorScheme = colorScheme, colorScheme = colorScheme,
@ -577,6 +597,7 @@ private fun PeerItem(
isWifiAware: Boolean = false, isWifiAware: Boolean = false,
isSelected: Boolean, isSelected: Boolean,
isFavorite: Boolean, isFavorite: Boolean,
theyFavoritedUs: Boolean = false,
isVerified: Boolean, isVerified: Boolean,
hasUnreadDM: Boolean, hasUnreadDM: Boolean,
colorScheme: ColorScheme, colorScheme: ColorScheme,
@ -703,13 +724,15 @@ private fun PeerItem(
.clickable(onClick = onToggleFavorite), .clickable(onClick = onToggleFavorite),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
// Three-state star (matches private-chat header): grey outline (no relation),
// orange outline (they favorited us), filled orange (we favorited them).
Icon( Icon(
painter = painterResource( painter = painterResource(
if (isFavorite) R.drawable.ic_spec_star_filled else R.drawable.ic_spec_star if (isFavorite) R.drawable.ic_spec_star_filled else R.drawable.ic_spec_star
), ),
contentDescription = if (isFavorite) "Remove from favorites" else "Add to favorites", contentDescription = if (isFavorite) "Remove from favorites" else "Add to favorites",
modifier = Modifier.size(PeerRowIconSize), modifier = Modifier.size(PeerRowIconSize),
tint = if (isFavorite) palette.accentOrange else palette.textTertiary tint = if (isFavorite || theyFavoritedUs) palette.accentOrange else palette.textTertiary
) )
} }
} }