From abef3d13f27d283305ea428e5a176eb983c456d7 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Wed, 27 Dec 2023 17:00:41 -0500 Subject: [PATCH] Improves the design of the discovery cards for Live Activity and Chats. --- .../amethyst/ui/layouts/LeftPictureLayout.kt | 139 ++++++++++++++++++ .../amethyst/ui/note/ChannelCardCompose.kt | 130 +++++++--------- .../amethyst/ui/note/ReactionsRow.kt | 2 +- .../quartz/events/LiveActivitiesEvent.kt | 5 + 4 files changed, 196 insertions(+), 80 deletions(-) create mode 100644 app/src/main/java/com/vitorpamplona/amethyst/ui/layouts/LeftPictureLayout.kt diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/layouts/LeftPictureLayout.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/layouts/LeftPictureLayout.kt new file mode 100644 index 000000000..dac59a88f --- /dev/null +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/layouts/LeftPictureLayout.kt @@ -0,0 +1,139 @@ +package com.vitorpamplona.amethyst.ui.layouts + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.sp +import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.ui.note.LikeIcon +import com.vitorpamplona.amethyst.ui.note.TextCount +import com.vitorpamplona.amethyst.ui.note.ZappedIcon +import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer +import com.vitorpamplona.amethyst.ui.theme.QuoteBorder +import com.vitorpamplona.amethyst.ui.theme.Size16Modifier +import com.vitorpamplona.amethyst.ui.theme.Size20Modifier +import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer +import com.vitorpamplona.amethyst.ui.theme.ThemeComparison +import com.vitorpamplona.amethyst.ui.theme.placeholderText + +@Composable +@Preview +fun LeftPictureLayoutPreview() { + ThemeComparison( + onDark = { LeftPictureLayoutPreviewCard() }, + onLight = { LeftPictureLayoutPreviewCard() } + ) +} + +@Composable +fun LeftPictureLayoutPreviewCard() { + LeftPictureLayout( + onImage = { + Image( + painter = painterResource(R.drawable.github), + contentDescription = stringResource(id = R.string.profile_banner), + contentScale = ContentScale.FillWidth, + modifier = Modifier + .fillMaxSize() + .clip(QuoteBorder) + ) + }, + onTitleRow = { + Text( + text = "This is my title", + fontWeight = FontWeight.Bold, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.weight(1f) + ) + + Spacer(modifier = StdHorzSpacer) + LikeIcon( + iconSizeModifier = Size16Modifier, + grayTint = MaterialTheme.colorScheme.onSurface + ) + TextCount(12, MaterialTheme.colorScheme.onSurface) + Spacer(modifier = StdHorzSpacer) + ZappedIcon(Size20Modifier) + TextCount(120, MaterialTheme.colorScheme.onSurface) + }, + onDescription = { + Text( + "This is 3-line description, This is 3-line description, This is 3-line description, This is 3-line description", + color = MaterialTheme.colorScheme.placeholderText, + maxLines = 3, + overflow = TextOverflow.Ellipsis, + fontSize = 14.sp + ) + }, + onBottomRow = { + Text("This is my Moderator List") + } + ) +} + +@Composable +fun LeftPictureLayout( + onImage: @Composable () -> Unit, + onTitleRow: @Composable RowScope.() -> Unit, + onDescription: @Composable () -> Unit, + onBottomRow: @Composable RowScope.() -> Unit +) { + Row(Modifier.aspectRatio(ratio = 4f)) { + Column( + modifier = Modifier + .fillMaxWidth(0.25f) + .aspectRatio(ratio = 1f) + ) { + onImage() + } + + Spacer(modifier = DoubleHorzSpacer) + + Column( + modifier = Modifier + .fillMaxWidth(), + verticalArrangement = Arrangement.SpaceBetween + ) { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + onTitleRow() + } + + Row( + modifier = Modifier + .fillMaxWidth() + .weight(1f) + ) { + onDescription() + } + + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + onBottomRow() + } + } + } +} diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChannelCardCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChannelCardCompose.kt index 7b8559241..53aa8d2d5 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChannelCardCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChannelCardCompose.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -32,7 +31,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment.Companion.BottomStart import androidx.compose.ui.Alignment.Companion.TopEnd import androidx.compose.ui.Modifier @@ -54,6 +52,7 @@ import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.ParticipantListBuilder import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.ui.components.SensitivityWarning +import com.vitorpamplona.amethyst.ui.layouts.LeftPictureLayout import com.vitorpamplona.amethyst.ui.screen.equalImmutableLists import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.ChannelHeader @@ -64,7 +63,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.OfflineFlag import com.vitorpamplona.amethyst.ui.screen.loggedIn.ScheduledFlag import com.vitorpamplona.amethyst.ui.screen.loggedIn.showAmountAxis import com.vitorpamplona.amethyst.ui.theme.DividerThickness -import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer import com.vitorpamplona.amethyst.ui.theme.HalfPadding import com.vitorpamplona.amethyst.ui.theme.QuoteBorder @@ -689,12 +687,8 @@ fun RenderCommunitiesThumb(baseNote: Note, accountViewModel: AccountViewModel, n ) ) - Row(Modifier.fillMaxWidth()) { - Column( - modifier = Modifier - .fillMaxWidth(0.3f) - .aspectRatio(ratio = 1f) - ) { + LeftPictureLayout( + onImage = { card.cover?.let { Box(contentAlignment = BottomStart) { AsyncImage( @@ -711,29 +705,22 @@ fun RenderCommunitiesThumb(baseNote: Note, accountViewModel: AccountViewModel, n DisplayAuthorBanner(it) } } - } - - Spacer(modifier = DoubleHorzSpacer) - - Column( - modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.SpaceBetween - ) { - Row(verticalAlignment = Alignment.CenterVertically) { - Text( - text = card.name, - fontWeight = FontWeight.Bold, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - modifier = Modifier.weight(1f) - ) - - Spacer(modifier = StdHorzSpacer) - LikeReaction(baseNote = baseNote, grayTint = MaterialTheme.colorScheme.onSurface, accountViewModel = accountViewModel, nav) - Spacer(modifier = StdHorzSpacer) - ZapReaction(baseNote = baseNote, grayTint = MaterialTheme.colorScheme.onSurface, accountViewModel = accountViewModel, nav = nav) - } + }, + onTitleRow = { + Text( + text = card.name, + fontWeight = FontWeight.Bold, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.weight(1f) + ) + Spacer(modifier = StdHorzSpacer) + LikeReaction(baseNote = baseNote, grayTint = MaterialTheme.colorScheme.onSurface, accountViewModel = accountViewModel, nav) + Spacer(modifier = StdHorzSpacer) + ZapReaction(baseNote = baseNote, grayTint = MaterialTheme.colorScheme.onSurface, accountViewModel = accountViewModel, nav = nav) + }, + onDescription = { card.description?.let { Spacer(modifier = StdVertSpacer) Row() { @@ -746,17 +733,16 @@ fun RenderCommunitiesThumb(baseNote: Note, accountViewModel: AccountViewModel, n ) } } - + }, + onBottomRow = { + Spacer(modifier = StdVertSpacer) LoadModerators(card.moderators, baseNote, accountViewModel) { participantUsers -> if (participantUsers.isNotEmpty()) { - Spacer(modifier = StdVertSpacer) - Row(modifier = Modifier.fillMaxWidth()) { - Gallery(participantUsers, accountViewModel) - } + Gallery(participantUsers, accountViewModel) } } } - } + ) } @Composable @@ -901,12 +887,8 @@ fun RenderChannelThumb(baseNote: Note, channel: Channel, accountViewModel: Accou } } - Row(Modifier.fillMaxWidth()) { - Column( - modifier = Modifier - .fillMaxWidth(0.3f) - .aspectRatio(ratio = 1f) - ) { + LeftPictureLayout( + onImage = { cover?.let { Box(contentAlignment = BottomStart) { AsyncImage( @@ -923,43 +905,33 @@ fun RenderChannelThumb(baseNote: Note, channel: Channel, accountViewModel: Accou DisplayAuthorBanner(it) } } - } - - Spacer(modifier = DoubleHorzSpacer) - - Column( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight() - ) { - Row(verticalAlignment = Alignment.CenterVertically) { - Text( - text = name, - fontWeight = FontWeight.Bold, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - modifier = Modifier.weight(1f) - ) - - Spacer(modifier = StdHorzSpacer) - LikeReaction(baseNote = baseNote, grayTint = MaterialTheme.colorScheme.onSurface, accountViewModel = accountViewModel, nav) - Spacer(modifier = StdHorzSpacer) - ZapReaction(baseNote = baseNote, grayTint = MaterialTheme.colorScheme.onSurface, accountViewModel = accountViewModel, nav = nav) - } + }, + onTitleRow = { + Text( + text = name, + fontWeight = FontWeight.Bold, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.weight(1f) + ) + Spacer(modifier = StdHorzSpacer) + LikeReaction(baseNote = baseNote, grayTint = MaterialTheme.colorScheme.onSurface, accountViewModel = accountViewModel, nav) + Spacer(modifier = StdHorzSpacer) + ZapReaction(baseNote = baseNote, grayTint = MaterialTheme.colorScheme.onSurface, accountViewModel = accountViewModel, nav = nav) + }, + onDescription = { description?.let { - Spacer(modifier = StdVertSpacer) - Row() { - Text( - text = it, - color = MaterialTheme.colorScheme.placeholderText, - maxLines = 3, - overflow = TextOverflow.Ellipsis, - fontSize = 14.sp - ) - } + Text( + text = it, + color = MaterialTheme.colorScheme.placeholderText, + maxLines = 3, + overflow = TextOverflow.Ellipsis, + fontSize = 14.sp + ) } - + }, + onBottomRow = { if (participantUsers.isNotEmpty()) { Spacer(modifier = StdVertSpacer) Row() { @@ -967,7 +939,7 @@ fun RenderChannelThumb(baseNote: Note, channel: Channel, accountViewModel: Accou } } } - } + ) } @OptIn(ExperimentalLayoutApi::class) @@ -980,7 +952,7 @@ fun Gallery(users: ImmutableList, accountViewModel: AccountViewModel) { if (users.size > 6) { Text( - text = remember(users) { " + " + (showCount(users.size - 6)).toString() }, + text = remember(users) { " + " + (showCount(users.size - 6)) }, fontSize = 13.sp, color = MaterialTheme.colorScheme.onSurface ) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt index 882b446b8..670a9c160 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt @@ -594,7 +594,7 @@ val slideAnimation: ContentTransform = ) @Composable -private fun TextCount(count: Int, textColor: Color) { +fun TextCount(count: Int, textColor: Color) { Text( text = showCount(count), fontSize = Font14SP, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/LiveActivitiesEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/LiveActivitiesEvent.kt index 469056485..7d533b6b3 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/LiveActivitiesEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/LiveActivitiesEvent.kt @@ -29,8 +29,13 @@ class LiveActivitiesEvent( fun participants() = tags.filter { it.size > 1 && it[0] == "p" }.map { Participant(it[1], it.getOrNull(3)) } + fun hasHost() = tags.any { it.size > 3 && it[0] == "p" && it[3].equals("Host", true) } + fun host() = tags.firstOrNull { it.size > 3 && it[0] == "p" && it[3].equals("Host", true) }?.get(1) + fun hosts() = tags.filter { it.size > 3 && it[0] == "p" && it[3].equals("Host", true) }.map { it[1] } + + fun checkStatus(eventStatus: String?): String? { return if (eventStatus == STATUS_LIVE && createdAt < TimeUtils.eightHoursAgo()) { STATUS_ENDED