feat: Add UserInfoSheet and enhance ChannelSideDrawer with user context functionality
This commit introduces a new UserInfoSheet for displaying user details and updates the ChannelSideDrawer to support user context interactions. - Implemented UserInfoSheet to fetch and display user profiles, including error handling for user retrieval. - Enhanced ChannelSideDrawer to manage user context sheets, allowing users to view detailed information about other users. - Updated existing components to replace channel context interactions with user context interactions where applicable. - Refactored related functions to maintain clean architecture principles and improve code organization.
This commit is contained in:
parent
bd4fd09559
commit
48513b761b
|
|
@ -207,7 +207,7 @@ class ShareTargetScreenViewModel @Inject constructor(
|
|||
}
|
||||
)
|
||||
attachmentIds.add(id)
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
|
|
@ -351,7 +351,8 @@ fun ShareTargetScreen(
|
|||
isCurrent = selectedChannel == channel.id,
|
||||
hasUnread = false,
|
||||
onDestinationChanged = { selectedChannel = channel.id },
|
||||
onOpenChannelContextSheet = {}
|
||||
onOpenChannelContextSheet = {},
|
||||
onOpenUserInfoSheet = {}
|
||||
)
|
||||
|
||||
else -> ChannelItem(
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ import chat.peptide.composables.screens.chat.ChannelIcon
|
|||
import chat.peptide.composables.screens.chat.discover.DiscoverServersList
|
||||
import chat.peptide.screens.chat.ChatRouterDestination
|
||||
import chat.peptide.sheets.ChannelContextSheet
|
||||
import chat.peptide.sheets.UserInfoSheet
|
||||
|
||||
@OptIn(
|
||||
ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class,
|
||||
|
|
@ -169,6 +170,7 @@ fun ChannelSideDrawer(
|
|||
}.sortedBy { it.id }))
|
||||
|
||||
var channelContextSheetTarget by remember { mutableStateOf<String?>(null) }
|
||||
var userContextSheetTarget by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
if (channelContextSheetTarget != null) {
|
||||
val channelContextSheetState = rememberModalBottomSheetState()
|
||||
|
|
@ -189,6 +191,25 @@ fun ChannelSideDrawer(
|
|||
}
|
||||
}
|
||||
|
||||
if (userContextSheetTarget != null) {
|
||||
val userContextSheetState = rememberModalBottomSheetState()
|
||||
ModalBottomSheet(
|
||||
sheetState = userContextSheetState,
|
||||
onDismissRequest = {
|
||||
userContextSheetTarget = null
|
||||
}
|
||||
) {
|
||||
UserInfoSheet(
|
||||
userId = userContextSheetTarget!!,
|
||||
serverId = null,
|
||||
dismissSheet = {
|
||||
userContextSheetState.hide()
|
||||
userContextSheetTarget = null
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Box {
|
||||
|
|
@ -561,7 +582,7 @@ fun ChannelSideDrawer(
|
|||
currentDestination,
|
||||
onDestinationChanged,
|
||||
channelListState,
|
||||
onOpenChannelContextSheet = { channelContextSheetTarget = it }
|
||||
onOpenUserInfoSheet = { userId -> userContextSheetTarget = userId }
|
||||
)
|
||||
} else {
|
||||
ServerChannelListRenderer(
|
||||
|
|
@ -647,7 +668,7 @@ private fun DirectMessagesChannelListRendererPreview() {
|
|||
currentDestination = ChatRouterDestination.Home,
|
||||
onDestinationChanged = {},
|
||||
channelListState = rememberLazyListState(),
|
||||
onOpenChannelContextSheet = {}
|
||||
onOpenUserInfoSheet = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -670,7 +691,7 @@ private fun DirectMessagesChannelListRendererEmptyPreview() {
|
|||
currentDestination = ChatRouterDestination.Home,
|
||||
onDestinationChanged = {},
|
||||
channelListState = rememberLazyListState(),
|
||||
onOpenChannelContextSheet = {}
|
||||
onOpenUserInfoSheet = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -680,7 +701,7 @@ private fun ColumnScope.DirectMessagesChannelListRenderer(
|
|||
currentDestination: ChatRouterDestination,
|
||||
onDestinationChanged: (ChatRouterDestination) -> Unit,
|
||||
channelListState: LazyListState,
|
||||
onOpenChannelContextSheet: (String) -> Unit,
|
||||
onOpenUserInfoSheet: (String) -> Unit,
|
||||
) {
|
||||
val dmAbleChannels =
|
||||
PeptideAPI.channelCache.values
|
||||
|
|
@ -805,7 +826,8 @@ private fun ColumnScope.DirectMessagesChannelListRenderer(
|
|||
onDestinationChanged = { dest ->
|
||||
onDestinationChanged(dest)
|
||||
},
|
||||
onOpenChannelContextSheet = onOpenChannelContextSheet
|
||||
onOpenChannelContextSheet = onOpenUserInfoSheet,
|
||||
onOpenUserInfoSheet = onOpenUserInfoSheet
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1042,7 +1064,8 @@ fun DMOrGroupItem(
|
|||
hasUnread: Boolean,
|
||||
isMuted: Boolean = false,
|
||||
onDestinationChanged: (ChatRouterDestination) -> Unit,
|
||||
onOpenChannelContextSheet: (String) -> Unit
|
||||
onOpenChannelContextSheet: (String) -> Unit,
|
||||
onOpenUserInfoSheet: (String) -> Unit
|
||||
) {
|
||||
val currentIndicatorOpacity = animateFloatAsState(
|
||||
targetValue = if (isCurrent) 1f else 0f,
|
||||
|
|
@ -1060,9 +1083,12 @@ fun DMOrGroupItem(
|
|||
.combinedClickable(
|
||||
onLongClickLabel = stringResource(R.string.channel_context_sheet_open),
|
||||
onLongClick = {
|
||||
channel.id?.let { chId ->
|
||||
partner?.id?.let { userId ->
|
||||
onOpenUserInfoSheet(userId)
|
||||
} ?: channel.id?.let { chId ->
|
||||
onOpenChannelContextSheet(chId)
|
||||
}
|
||||
|
||||
},
|
||||
onClick = {
|
||||
channel.id?.let { chId ->
|
||||
|
|
@ -1122,7 +1148,18 @@ fun DMOrGroupItem(
|
|||
avatar = partner?.avatar ?: channel.icon,
|
||||
size = 28.dp,
|
||||
presenceSize = 12.dp
|
||||
)
|
||||
).let { avatar ->
|
||||
// Placed inside a Box, otherwise the combinedClickable modifier doesn't work.
|
||||
Box(
|
||||
Modifier.combinedClickable(
|
||||
onLongClick = {
|
||||
partner?.id?.let { uid -> onOpenUserInfoSheet(uid) }
|
||||
},
|
||||
onClick = {}
|
||||
),
|
||||
content = { avatar }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Column(Modifier.weight(1f)) {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import chat.peptide.api.internals.BrushCompat
|
|||
import chat.peptide.api.internals.ULID
|
||||
import chat.peptide.api.internals.solidColor
|
||||
import chat.peptide.api.routes.user.fetchUserProfile
|
||||
import chat.peptide.api.routes.user.getOrFetchUser
|
||||
import chat.peptide.api.schemas.Profile
|
||||
import chat.peptide.api.settings.Experiments
|
||||
import chat.peptide.api.settings.FeatureFlags
|
||||
|
|
@ -65,7 +66,17 @@ fun UserInfoSheet(
|
|||
serverId: String? = null,
|
||||
dismissSheet: suspend () -> Unit
|
||||
) {
|
||||
val user = PeptideAPI.userCache[userId]
|
||||
var resolvedUser by remember { mutableStateOf(PeptideAPI.userCache[userId]) }
|
||||
|
||||
LaunchedEffect(userId) {
|
||||
try {
|
||||
if (resolvedUser == null) {
|
||||
resolvedUser = getOrFetchUser(userId)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
val member = serverId?.let { PeptideAPI.members.getMember(it, userId) }
|
||||
|
||||
|
|
@ -74,9 +85,9 @@ fun UserInfoSheet(
|
|||
var profile by remember { mutableStateOf<Profile?>(null) }
|
||||
var profileNotFound by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(user) {
|
||||
LaunchedEffect(resolvedUser) {
|
||||
try {
|
||||
user?.id?.let { fetchUserProfile(it) }?.let { profile = it }
|
||||
resolvedUser?.id?.let { fetchUserProfile(it) }?.let { profile = it }
|
||||
} catch (e: Exception) {
|
||||
if (e.message == "NotFound") {
|
||||
profileNotFound = true
|
||||
|
|
@ -85,8 +96,7 @@ fun UserInfoSheet(
|
|||
}
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
// TODO fetch user in this scenario
|
||||
if (resolvedUser == null) {
|
||||
NonIdealState(
|
||||
icon = {
|
||||
Icon(
|
||||
|
|
@ -117,7 +127,7 @@ fun UserInfoSheet(
|
|||
sheetState = sheetState,
|
||||
onDismissRequest = { showUserCard = false }
|
||||
) {
|
||||
UserCardSheet(user)
|
||||
UserCardSheet(resolvedUser!!)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +139,7 @@ fun UserInfoSheet(
|
|||
onDismissRequest = { showServerIdentityOptions = false }
|
||||
) {
|
||||
ServerIdentityOptionsSheet(
|
||||
userId = user.id!!
|
||||
userId = resolvedUser!!.id!!
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -142,7 +152,7 @@ fun UserInfoSheet(
|
|||
) {
|
||||
item(key = "overview", span = StaggeredGridItemSpan.FullLine) {
|
||||
Box {
|
||||
RawUserOverview(user, profile, internalPadding = false)
|
||||
RawUserOverview(resolvedUser!!, profile, internalPadding = false)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
|
|
@ -214,9 +224,9 @@ fun UserInfoSheet(
|
|||
}
|
||||
}
|
||||
}
|
||||
val accountAt = user.id?.let {
|
||||
val accountAt = resolvedUser!!.id?.let {
|
||||
DateUtils.getRelativeTimeSpanString(
|
||||
ULID.asTimestamp(user.id),
|
||||
ULID.asTimestamp(resolvedUser!!.id!!),
|
||||
System.currentTimeMillis(),
|
||||
DateUtils.MINUTE_IN_MILLIS
|
||||
).toString()
|
||||
|
|
@ -292,22 +302,22 @@ fun UserInfoSheet(
|
|||
}
|
||||
}
|
||||
|
||||
if ((user.badges ?: 0) > 0) {
|
||||
if ((resolvedUser?.badges ?: 0) > 0) {
|
||||
item(key = "info") {
|
||||
SheetTile(
|
||||
header = {
|
||||
Text(stringResource(R.string.user_info_sheet_category_badges))
|
||||
},
|
||||
contentPreview = {
|
||||
user.badges?.let { UserBadgeRow(badges = it) }
|
||||
resolvedUser?.badges?.let { UserBadgeRow(badges = it) }
|
||||
}
|
||||
) {
|
||||
user.badges?.let { UserBadgeList(badges = it) }
|
||||
resolvedUser?.badges?.let { UserBadgeList(badges = it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (user.status?.text != null) {
|
||||
if (resolvedUser?.status?.text != null) {
|
||||
item(key = "status") {
|
||||
SheetTile(
|
||||
header = {
|
||||
|
|
@ -315,7 +325,7 @@ fun UserInfoSheet(
|
|||
},
|
||||
contentPreview = {
|
||||
Text(
|
||||
text = user.status.text,
|
||||
text = resolvedUser!!.status!!.text ?: "",
|
||||
fontSize = 14.sp,
|
||||
maxLines = 5,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
|
|
@ -323,15 +333,15 @@ fun UserInfoSheet(
|
|||
}
|
||||
) {
|
||||
Text(
|
||||
text = user.status.text,
|
||||
text = resolvedUser!!.status!!.text ?: "",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (user.bot != null) {
|
||||
val resolvedOwner = user.bot.owner?.let { PeptideAPI.userCache[it] }
|
||||
if (resolvedUser?.bot != null) {
|
||||
val resolvedOwner = resolvedUser!!.bot!!.owner?.let { PeptideAPI.userCache[it] }
|
||||
|
||||
item(key = "bot-owner") {
|
||||
SheetTile(
|
||||
|
|
@ -345,7 +355,7 @@ fun UserInfoSheet(
|
|||
resolvedOwner?.let {
|
||||
UserAvatar(
|
||||
username = it.displayName ?: it.username
|
||||
?: stringResource(R.string.unknown),
|
||||
?: stringResource(R.string.unknown),
|
||||
avatar = it.avatar,
|
||||
userId = it.id!!,
|
||||
size = 32.dp
|
||||
|
|
@ -353,7 +363,7 @@ fun UserInfoSheet(
|
|||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = it.displayName ?: it.username
|
||||
?: stringResource(R.string.unknown),
|
||||
?: stringResource(R.string.unknown),
|
||||
fontSize = 14.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
|
|
@ -380,7 +390,7 @@ fun UserInfoSheet(
|
|||
NonIdealState(
|
||||
icon = {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.icn_error_24dp),
|
||||
painter = painterResource(id = R.drawable.icn_error_24dp),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
|
|
@ -414,7 +424,7 @@ fun UserInfoSheet(
|
|||
}
|
||||
|
||||
item(key = "actions", span = StaggeredGridItemSpan.FullLine) {
|
||||
UserButtons(user, dismissSheet)
|
||||
UserButtons(resolvedUser!!, dismissSheet)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue