Adds an option to render a user gallery from HexKeys instead of full User objects

This commit is contained in:
Vitor Pamplona
2025-11-10 11:18:18 -05:00
parent 58128d9ad0
commit ffaa06fed7
2 changed files with 124 additions and 0 deletions
@@ -38,8 +38,10 @@ import androidx.compose.ui.unit.sp
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
import com.vitorpamplona.amethyst.ui.navigation.routes.routeForUser
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.Size25dp
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import kotlinx.collections.immutable.ImmutableList
@OptIn(ExperimentalLayoutApi::class)
@@ -81,3 +83,45 @@ fun Gallery(
}
}
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun GalleryUnloaded(
users: ImmutableList<HexKey>,
modifier: Modifier,
accountViewModel: AccountViewModel,
nav: INav,
maxPictures: Int = 6,
) {
FlowRow(
modifier,
verticalArrangement = Arrangement.Center,
horizontalArrangement = Arrangement.spacedBy((-5).dp),
) {
users.take(maxPictures).forEach {
ClickableUserPicture(
it,
Size25dp,
accountViewModel,
onClick = {
nav.nav {
routeForUser(it)
}
},
)
}
if (users.size > maxPictures) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.size(Size25dp).clip(shape = CircleShape).background(MaterialTheme.colorScheme.secondaryContainer),
) {
Text(
text = "+" + showCount(users.size - maxPictures),
fontSize = 10.sp,
color = MaterialTheme.colorScheme.onSurface,
)
}
}
}
}
@@ -46,6 +46,7 @@ import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.LoadUser
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
@Composable
@@ -175,6 +176,40 @@ fun ClickableUserPicture(
BaseUserPicture(baseUser, size, accountViewModel, modifier, myModifier)
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ClickableUserPicture(
baseUserHex: HexKey,
size: Dp,
accountViewModel: AccountViewModel,
modifier: Modifier = Modifier,
onClick: ((HexKey) -> Unit)? = null,
onLongClick: ((HexKey) -> Unit)? = null,
) {
// BaseUser is the same reference as accountState.user
val myModifier =
remember(baseUserHex) {
if (onClick != null && onLongClick != null) {
Modifier
.size(size)
.combinedClickable(
onClick = { onClick(baseUserHex) },
onLongClick = { onLongClick(baseUserHex) },
)
} else if (onClick != null) {
Modifier
.size(size)
.clickable(
onClick = { onClick(baseUserHex) },
)
} else {
Modifier.size(size)
}
}
BaseUserPicture(baseUserHex, size, accountViewModel, modifier, myModifier)
}
@Composable
fun NonClickableUserPictures(
room: ChatroomKey,
@@ -322,6 +357,34 @@ fun BaseUserPicture(
}
}
@Composable
fun BaseUserPicture(
baseUserHex: HexKey,
size: Dp,
accountViewModel: AccountViewModel,
innerModifier: Modifier = Modifier,
outerModifier: Modifier = Modifier.size(size),
) {
Box(outerModifier, contentAlignment = Alignment.TopEnd) {
LoadUserProfilePicture(baseUserHex, accountViewModel) { userProfilePicture, userName ->
InnerUserPicture(
userHex = baseUserHex,
userPicture = userProfilePicture,
userName = userName,
size = size,
modifier = innerModifier,
accountViewModel = accountViewModel,
)
}
WatchUserFollows(baseUserHex, accountViewModel) { newFollowingState ->
if (newFollowingState) {
FollowingIcon(Modifier.size(size.div(3.5f)))
}
}
}
}
@Composable
fun LoadUserProfilePicture(
baseUser: User,
@@ -333,6 +396,23 @@ fun LoadUserProfilePicture(
innerContent(userProfile?.profilePicture(), userProfile?.bestName())
}
@Composable
fun LoadUserProfilePicture(
baseUserHex: HexKey,
accountViewModel: AccountViewModel,
innerContent: @Composable (String?, String?) -> Unit,
) {
LoadUser(baseUserHex, accountViewModel) {
if (it != null) {
val userProfile by observeUserInfo(it, accountViewModel)
innerContent(userProfile?.profilePicture(), userProfile?.bestName())
} else {
innerContent(null, null)
}
}
}
@Composable
fun InnerUserPicture(
userHex: String,