From 837865a6991e5140cec3632fce73c1ca7f79081e Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Thu, 14 Mar 2024 11:11:38 -0400 Subject: [PATCH] Adds simplified views to the video and chat feeds. --- .../ui/note/ChatroomMessageCompose.kt | 166 ++++++++---------- .../ui/screen/loggedIn/VideoScreen.kt | 62 +++---- .../vitorpamplona/amethyst/ui/theme/Shape.kt | 5 +- 3 files changed, 112 insertions(+), 121 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt index 8ea57ab90..17d717e3b 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.amethyst.ui.note import androidx.compose.animation.Crossfade +import androidx.compose.animation.animateContentSize import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.clickable import androidx.compose.foundation.combinedClickable @@ -33,7 +34,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface @@ -41,6 +41,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.State import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState @@ -51,7 +52,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.compositeOver @@ -66,7 +66,6 @@ import androidx.lifecycle.map import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.User -import com.vitorpamplona.amethyst.ui.components.CreateClickableTextWithEmoji import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage import com.vitorpamplona.amethyst.ui.components.SensitivityWarning @@ -84,10 +83,9 @@ import com.vitorpamplona.amethyst.ui.theme.ReactionRowHeightChat import com.vitorpamplona.amethyst.ui.theme.Size10dp import com.vitorpamplona.amethyst.ui.theme.Size15Modifier import com.vitorpamplona.amethyst.ui.theme.Size20dp -import com.vitorpamplona.amethyst.ui.theme.Size25dp import com.vitorpamplona.amethyst.ui.theme.Size5dp import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer -import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer +import com.vitorpamplona.amethyst.ui.theme.chatAuthorImage import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.subtleBorder @@ -306,12 +304,16 @@ fun NormalChatNote( val modif2 = if (innerQuote) Modifier else ChatBubbleMaxSizeModifier + val showDetails = remember { mutableStateOf(false) } + val clickableModifier = remember { Modifier.combinedClickable( onClick = { if (note.event is ChannelCreateEvent) { nav("Channel/${note.idHex}") + } else { + showDetails.value = !showDetails.value } }, onLongClick = { popupExpanded = true }, @@ -341,6 +343,7 @@ fun NormalChatNote( onWantsToReply, canPreview, availableBubbleSize, + showDetails, accountViewModel, nav, ) @@ -367,6 +370,7 @@ private fun RenderBubble( onWantsToReply: (Note) -> Unit, canPreview: Boolean, availableBubbleSize: MutableState, + showDetails: State, accountViewModel: AccountViewModel, nav: (String) -> Unit, ) { @@ -375,12 +379,13 @@ private fun RenderBubble( val bubbleModifier = remember { Modifier - .padding(start = 10.dp, end = 5.dp, bottom = 5.dp) + .padding(start = 10.dp, end = 10.dp, bottom = 5.dp) .onSizeChanged { if (bubbleSize.intValue != it.width) { bubbleSize.intValue = it.width } } + .animateContentSize() } Column(modifier = bubbleModifier) { @@ -388,14 +393,15 @@ private fun RenderBubble( drawAuthorInfo, baseNote, alignment, - nav, + availableBubbleSize, innerQuote, backgroundBubbleColor, - accountViewModel, + bubbleSize, onWantsToReply, canPreview, - bubbleSize, - availableBubbleSize, + showDetails, + accountViewModel, + nav, ) } } @@ -405,14 +411,15 @@ private fun MessageBubbleLines( drawAuthorInfo: Boolean, baseNote: Note, alignment: Arrangement.Horizontal, - nav: (String) -> Unit, + availableBubbleSize: MutableState, innerQuote: Boolean, backgroundBubbleColor: MutableState, - accountViewModel: AccountViewModel, + bubbleSize: MutableState, onWantsToReply: (Note) -> Unit, canPreview: Boolean, - bubbleSize: MutableState, - availableBubbleSize: MutableState, + showDetails: State, + accountViewModel: AccountViewModel, + nav: (String) -> Unit, ) { if (drawAuthorInfo) { DrawAuthorInfo( @@ -421,8 +428,6 @@ private fun MessageBubbleLines( accountViewModel.settings.showProfilePictures.value, nav, ) - } else { - Spacer(modifier = StdVertSpacer) } RenderReplyRow( @@ -442,32 +447,34 @@ private fun MessageBubbleLines( nav = nav, ) - ConstrainedStatusRow( - bubbleSize = bubbleSize, - availableBubbleSize = availableBubbleSize, - firstColumn = { - IncognitoBadge(baseNote) - ChatTimeAgo(baseNote) - RelayBadgesHorizontal(baseNote, accountViewModel, nav = nav) - Spacer(modifier = DoubleHorzSpacer) - }, - secondColumn = { - LikeReaction(baseNote, MaterialTheme.colorScheme.placeholderText, accountViewModel, nav) - Spacer(modifier = StdHorzSpacer) - ZapReaction(baseNote, MaterialTheme.colorScheme.placeholderText, accountViewModel, nav = nav) - Spacer(modifier = DoubleHorzSpacer) - ReplyReaction( - baseNote = baseNote, - grayTint = MaterialTheme.colorScheme.placeholderText, - accountViewModel = accountViewModel, - showCounter = false, - iconSizeModifier = Size15Modifier, - ) { - onWantsToReply(baseNote) - } - Spacer(modifier = StdHorzSpacer) - }, - ) + if (showDetails.value || baseNote.zaps.isNotEmpty() || baseNote.zapPayments.isNotEmpty() || baseNote.reactions.isNotEmpty()) { + ConstrainedStatusRow( + bubbleSize = bubbleSize, + availableBubbleSize = availableBubbleSize, + firstColumn = { + IncognitoBadge(baseNote) + ChatTimeAgo(baseNote) + RelayBadgesHorizontal(baseNote, accountViewModel, nav = nav) + Spacer(modifier = DoubleHorzSpacer) + }, + secondColumn = { + LikeReaction(baseNote, MaterialTheme.colorScheme.placeholderText, accountViewModel, nav) + Spacer(modifier = StdHorzSpacer) + ZapReaction(baseNote, MaterialTheme.colorScheme.placeholderText, accountViewModel, nav = nav) + Spacer(modifier = DoubleHorzSpacer) + ReplyReaction( + baseNote = baseNote, + grayTint = MaterialTheme.colorScheme.placeholderText, + accountViewModel = accountViewModel, + showCounter = false, + iconSizeModifier = Size15Modifier, + ) { + onWantsToReply(baseNote) + } + Spacer(modifier = StdHorzSpacer) + }, + ) + } } @Composable @@ -479,9 +486,7 @@ private fun RenderReplyRow( nav: (String) -> Unit, onWantsToReply: (Note) -> Unit, ) { - val hasReply by remember { derivedStateOf { !innerQuote && note.replyTo?.lastOrNull() != null } } - - if (hasReply) { + if (!innerQuote && note.replyTo?.lastOrNull() != null) { RenderReply(note, backgroundBubbleColor, accountViewModel, nav, onWantsToReply) } } @@ -504,8 +509,8 @@ private fun RenderReply( replyTo.value?.let { note -> ChatroomMessageCompose( - note, - null, + baseNote = note, + routeForLastRead = null, innerQuote = true, parentBackgroundColor = backgroundBubbleColor, accountViewModel = accountViewModel, @@ -525,7 +530,7 @@ private fun NoteRow( nav: (String) -> Unit, ) { Row(verticalAlignment = Alignment.CenterVertically) { - when (remember(note) { note.event }) { + when (note.event) { is ChannelCreateEvent -> { RenderCreateChannelNote(note) } @@ -719,41 +724,36 @@ private fun DrawAuthorInfo( loadProfilePicture: Boolean, nav: (String) -> Unit, ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = alignment, - modifier = Modifier.padding(top = Size10dp), - ) { - DisplayAndWatchNoteAuthor(baseNote, loadProfilePicture, nav) + baseNote.author?.let { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = alignment, + modifier = + Modifier + .padding(top = Size10dp) + .clickable { + nav("User/${baseNote.author?.pubkeyHex}") + }, + ) { + WatchAndDisplayUser(it, loadProfilePicture, nav) + } } } -@Composable -private fun DisplayAndWatchNoteAuthor( - baseNote: Note, - loadProfilePicture: Boolean, - nav: (String) -> Unit, -) { - val author = remember { baseNote.author } - author?.let { WatchAndDisplayUser(it, loadProfilePicture, nav) } -} - @Composable private fun WatchAndDisplayUser( author: User, loadProfilePicture: Boolean, nav: (String) -> Unit, ) { - val route = "User/${author.pubkeyHex}" - val userState by author.live().userMetadataInfo.observeAsState() - UserIcon(author.pubkeyHex, userState?.picture, loadProfilePicture, nav, route) + UserIcon(author.pubkeyHex, userState?.picture, loadProfilePicture) - userState?.let { - it.bestName()?.let { name -> - DisplayMessageUsername(name, it.tags, route, nav) - } + if (userState != null) { + DisplayMessageUsername(userState?.bestName() ?: author.pubkeyDisplayHex(), userState?.tags ?: EmptyTagList) + } else { + DisplayMessageUsername(author.pubkeyDisplayHex(), EmptyTagList) } } @@ -762,40 +762,26 @@ private fun UserIcon( pubkeyHex: String, userProfilePicture: String?, loadProfilePicture: Boolean, - nav: (String) -> Unit, - route: String, ) { RobohashFallbackAsyncImage( robot = pubkeyHex, model = userProfilePicture, contentDescription = stringResource(id = R.string.profile_image), loadProfilePicture = loadProfilePicture, - modifier = - remember { - Modifier - .width(Size25dp) - .height(Size25dp) - .clip(shape = CircleShape) - .clickable(onClick = { nav(route) }) - }, + modifier = chatAuthorImage, ) } @Composable private fun DisplayMessageUsername( userDisplayName: String, - userTags: ImmutableListOfLists?, - route: String, - nav: (String) -> Unit, + userTags: ImmutableListOfLists, ) { Spacer(modifier = StdHorzSpacer) - CreateClickableTextWithEmoji( - clickablePart = userDisplayName, - maxLines = 1, + CreateTextWithEmoji( + text = userDisplayName, tags = userTags, + maxLines = 1, fontWeight = FontWeight.Bold, - overrideColor = MaterialTheme.colorScheme.onBackground, - route = route, - nav = nav, ) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt index 59eb824e3..cf77d9a21 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt @@ -35,7 +35,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.VerticalPager import androidx.compose.foundation.pager.rememberPagerState @@ -64,6 +63,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.model.FeatureSetType import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.service.NostrVideoDataSource import com.vitorpamplona.amethyst.ui.actions.NewPostView @@ -91,11 +91,14 @@ import com.vitorpamplona.amethyst.ui.screen.NostrVideoFeedViewModel import com.vitorpamplona.amethyst.ui.screen.RefresheableBox import com.vitorpamplona.amethyst.ui.screen.ScrollStateKeys import com.vitorpamplona.amethyst.ui.screen.rememberForeverPagerState +import com.vitorpamplona.amethyst.ui.theme.AuthorInfoVideoFeed +import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer import com.vitorpamplona.amethyst.ui.theme.Size35Modifier import com.vitorpamplona.amethyst.ui.theme.Size35dp import com.vitorpamplona.amethyst.ui.theme.Size39Modifier import com.vitorpamplona.amethyst.ui.theme.Size40Modifier import com.vitorpamplona.amethyst.ui.theme.Size40dp +import com.vitorpamplona.amethyst.ui.theme.Size55dp import com.vitorpamplona.amethyst.ui.theme.onBackgroundColorFilter import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.quartz.events.FileHeaderEvent @@ -321,8 +324,8 @@ private fun RenderVideoOrPictureNote( accountViewModel: AccountViewModel, nav: (String) -> Unit, ) { - Column(remember { Modifier.fillMaxSize(1f) }, verticalArrangement = Arrangement.Center) { - Row(remember { Modifier.weight(1f) }, verticalAlignment = Alignment.CenterVertically) { + Column(Modifier.fillMaxSize(1f), verticalArrangement = Arrangement.Center) { + Row(Modifier.weight(1f), verticalAlignment = Alignment.CenterVertically) { val noteEvent = remember { note.event } if (noteEvent is FileHeaderEvent) { FileHeaderDisplay(note, false, accountViewModel) @@ -332,18 +335,17 @@ private fun RenderVideoOrPictureNote( } } - Row(verticalAlignment = Alignment.Bottom, modifier = remember { Modifier.fillMaxSize(1f) }) { - Column(remember { Modifier.weight(1f) }) { + Row(modifier = Modifier.fillMaxSize(1f), verticalAlignment = Alignment.Bottom) { + Column(Modifier.weight(1f), verticalArrangement = Arrangement.Center) { RenderAuthorInformation(note, nav, accountViewModel) } Column( - remember { Modifier.width(65.dp).padding(bottom = 10.dp) }, + modifier = AuthorInfoVideoFeed, verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, ) { - Row(horizontalArrangement = Arrangement.Center) { - ReactionsColumn(note, accountViewModel, nav) - } + ReactionsColumn(note, accountViewModel, nav) } } } @@ -354,32 +356,34 @@ private fun RenderAuthorInformation( nav: (String) -> Unit, accountViewModel: AccountViewModel, ) { - Row(remember { Modifier.padding(10.dp) }, verticalAlignment = Alignment.Bottom) { - Column(remember { Modifier.size(55.dp) }, verticalArrangement = Arrangement.Center) { - NoteAuthorPicture(note, nav, accountViewModel, 55.dp) - } + Row(modifier = Modifier.padding(start = 10.dp, end = 10.dp, bottom = 10.dp), verticalAlignment = Alignment.CenterVertically) { + NoteAuthorPicture(note, nav, accountViewModel, Size55dp) + + Spacer(modifier = DoubleHorzSpacer) Column( - remember { Modifier.padding(start = 10.dp, end = 10.dp).height(65.dp).weight(1f) }, + Modifier.height(65.dp).weight(1f), verticalArrangement = Arrangement.Center, ) { Row(verticalAlignment = Alignment.CenterVertically) { NoteUsernameDisplay(note, remember { Modifier.weight(1f) }) VideoUserOptionAction(note, accountViewModel, nav) } - Row(verticalAlignment = Alignment.CenterVertically) { - ObserveDisplayNip05Status( - note.author!!, - Modifier.weight(1f), - accountViewModel, - nav = nav, - ) - } - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(top = 2.dp), - ) { - RelayBadges(baseNote = note, accountViewModel, nav) + if (accountViewModel.settings.featureSet != FeatureSetType.SIMPLIFIED) { + Row(verticalAlignment = Alignment.CenterVertically) { + ObserveDisplayNip05Status( + note.author!!, + Modifier.weight(1f), + accountViewModel, + nav = nav, + ) + } + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(top = 2.dp), + ) { + RelayBadges(baseNote = note, accountViewModel, nav) + } } } } @@ -457,11 +461,9 @@ fun ReactionsColumn( ) } - Spacer(modifier = Modifier.height(8.dp)) - Column( horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.padding(bottom = 75.dp, end = 20.dp), + modifier = Modifier.padding(bottom = 75.dp, end = 10.dp), ) { ReplyReaction( baseNote = baseNote, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt index e3d769455..96424ca0a 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt @@ -175,7 +175,7 @@ val ZeroPadding = PaddingValues(0.dp) val FeedPadding = PaddingValues(top = 10.dp, bottom = 10.dp) val ButtonPadding = PaddingValues(vertical = 6.dp, horizontal = 16.dp) -val ChatPaddingInnerQuoteModifier = Modifier.padding(top = 10.dp, end = 5.dp) +val ChatPaddingInnerQuoteModifier = Modifier.padding(top = 10.dp) val ChatPaddingModifier = Modifier.fillMaxWidth(1f) .padding( @@ -228,3 +228,6 @@ val liveStreamTag = .clip(SmallBorder) .background(Color.Black) .padding(horizontal = Size5dp) + +val chatAuthorImage = Modifier.size(20.dp).clip(shape = CircleShape) +val AuthorInfoVideoFeed = Modifier.width(75.dp)