diff --git a/app/src/main/java/chat/peptide/activities/ShareTargetActivity.kt b/app/src/main/java/chat/peptide/activities/ShareTargetActivity.kt index a310d332..1e921063 100644 --- a/app/src/main/java/chat/peptide/activities/ShareTargetActivity.kt +++ b/app/src/main/java/chat/peptide/activities/ShareTargetActivity.kt @@ -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( diff --git a/app/src/main/java/chat/peptide/composables/screens/chat/drawer/ChannelSideDrawer.kt b/app/src/main/java/chat/peptide/composables/screens/chat/drawer/ChannelSideDrawer.kt index 77c20e4c..3b26eff3 100644 --- a/app/src/main/java/chat/peptide/composables/screens/chat/drawer/ChannelSideDrawer.kt +++ b/app/src/main/java/chat/peptide/composables/screens/chat/drawer/ChannelSideDrawer.kt @@ -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(null) } + var userContextSheetTarget by remember { mutableStateOf(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)) { diff --git a/app/src/main/java/chat/peptide/sheets/UserInfoSheet.kt b/app/src/main/java/chat/peptide/sheets/UserInfoSheet.kt index 5f935b34..9108eb40 100644 --- a/app/src/main/java/chat/peptide/sheets/UserInfoSheet.kt +++ b/app/src/main/java/chat/peptide/sheets/UserInfoSheet.kt @@ -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(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) } }