Add rememberRecipientField composable function for reading live recipient fields.

This commit is contained in:
Alex Hart
2025-05-30 15:35:11 -03:00
committed by Cody Henthorne
parent df170dac32
commit 7616ec1fd2
3 changed files with 64 additions and 34 deletions

View File

@@ -9,23 +9,16 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.viewinterop.AndroidView
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.rx3.asFlow
import org.thoughtcrime.securesms.components.AvatarImageView
import org.thoughtcrime.securesms.database.model.ProfileAvatarFileDetails
import org.thoughtcrime.securesms.profiles.AvatarHelper
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.NameUtil
import org.thoughtcrime.securesms.recipients.rememberRecipientField
@Composable
fun AvatarImage(
@@ -41,15 +34,12 @@ fun AvatarImage(
)
} else {
val context = LocalContext.current
var state: AvatarImageState by remember {
mutableStateOf(AvatarImageState(null, recipient, ProfileAvatarFileDetails.NO_DETAILS))
}
LaunchedEffect(recipient.id) {
Recipient.observable(recipient.id).asFlow()
.collectLatest {
state = AvatarImageState(NameUtil.getAbbreviation(it.getDisplayName(context)), it, AvatarHelper.getAvatarFileDetails(context, it.id))
}
val avatarImageState by rememberRecipientField(recipient) {
AvatarImageState(
getDisplayName(context),
this,
profileAvatarFileDetails
)
}
AndroidView(
@@ -62,9 +52,9 @@ fun AvatarImage(
modifier = modifier.background(color = Color.Transparent, shape = CircleShape)
) {
if (useProfile) {
it.setAvatarUsingProfile(state.self)
it.setAvatarUsingProfile(avatarImageState.self)
} else {
it.setAvatar(state.self)
it.setAvatar(avatarImageState.self)
}
}
}

View File

@@ -67,10 +67,6 @@ import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.rx3.asFlow
import org.signal.core.ui.compose.DropdownMenus
import org.signal.core.ui.compose.IconButtons
import org.signal.core.ui.compose.Previews
@@ -79,11 +75,11 @@ import org.signal.core.ui.compose.TextFields
import org.signal.core.ui.compose.Tooltips
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.avatar.AvatarImage
import org.thoughtcrime.securesms.badges.models.Badge
import org.thoughtcrime.securesms.calls.log.CallLogFilter
import org.thoughtcrime.securesms.components.settings.app.subscription.BadgeImageSmall
import org.thoughtcrime.securesms.conversationlist.model.ConversationFilter
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.rememberRecipientField
interface MainToolbarCallback {
fun onNewGroupClick()
@@ -353,16 +349,7 @@ private fun PrimaryToolbar(
}
)
var badge by remember { mutableStateOf<Badge?>(null) }
LaunchedEffect(state.self.id) {
Recipient.observable(state.self.id)
.asFlow()
.map { it.featuredBadge }
.distinctUntilChanged()
.collectLatest {
badge = it
}
}
val badge by rememberRecipientField(state.self) { featuredBadge }
BadgeImageSmall(
badge = badge,

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.recipients
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import kotlinx.coroutines.rx3.asFlow
import kotlinx.coroutines.withContext
import org.signal.core.util.concurrent.SignalDispatchers
/**
* Allows composable UI to subscribe to changes for a specific field on a recipient.
*/
@Composable
fun <T> rememberRecipientField(recipient: Recipient, toField: Recipient.() -> T): State<T> {
return rememberRecipientField(recipientId = recipient.id, initialData = recipient, toField = toField)
}
/**
* Allows composable UI to subscribe to changes for a specific field on a recipient.
*/
@Composable
fun <T> rememberRecipientField(recipientId: RecipientId, toField: Recipient.() -> T): State<T> {
return rememberRecipientField(recipientId = recipientId, initialData = Recipient.UNKNOWN, toField = toField)
}
@Composable
private fun <T> rememberRecipientField(recipientId: RecipientId, initialData: Recipient, toField: Recipient.() -> T): State<T> {
var recipientAndCounter by remember(recipientId) { mutableStateOf(initialData to 0L) }
LaunchedEffect(recipientId) {
withContext(SignalDispatchers.IO) {
Recipient.observable(recipientId)
.asFlow()
.collect {
recipientAndCounter = it to (recipientAndCounter.second + 1L)
}
}
}
return remember(recipientAndCounter) {
derivedStateOf { toField(recipientAndCounter.first) }
}
}