fix(posts, profile): layout improvements

This commit is contained in:
Diego Beraldin 2023-08-11 22:39:18 +02:00
parent 0ecb9fb42a
commit a25a930085
13 changed files with 296 additions and 70 deletions

View File

@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.DismissDirection
import androidx.compose.material.DismissValue
@ -264,15 +265,46 @@ class CommunityDetailScreen(
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
DismissDirection.EndToStart -> Icons.Default.ArrowCircleUp
}
val (iconModifier, iconTint) = when {
direction == DismissDirection.StartToEnd && post.myVote < 0 -> {
Modifier.background(
color = Color.Transparent,
shape = CircleShape,
) to MaterialTheme.colorScheme.onSecondary
}
direction == DismissDirection.StartToEnd -> {
Modifier.background(
color = MaterialTheme.colorScheme.onSecondary,
shape = CircleShape,
) to MaterialTheme.colorScheme.secondary
}
direction == DismissDirection.EndToStart && post.myVote > 0 -> {
Modifier.background(
color = Color.Transparent,
shape = CircleShape,
) to MaterialTheme.colorScheme.onSecondary
}
else -> {
Modifier.background(
color = MaterialTheme.colorScheme.onSecondary,
shape = CircleShape,
) to MaterialTheme.colorScheme.secondary
}
}
Box(
Modifier.fillMaxSize().background(color)
Modifier.fillMaxSize()
.background(color)
.padding(horizontal = 20.dp),
contentAlignment = alignment,
) {
Icon(
modifier = iconModifier,
imageVector = icon,
contentDescription = null,
tint = iconTint,
)
}
},

View File

@ -1,19 +1,19 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.components
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowCircleDown
import androidx.compose.material.icons.filled.ArrowCircleUp
import androidx.compose.material.icons.filled.Bookmark
import androidx.compose.material.icons.filled.BookmarkBorder
import androidx.compose.material.icons.filled.Chat
import androidx.compose.material.icons.outlined.ArrowCircleDown
import androidx.compose.material.icons.outlined.ArrowCircleUp
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -76,18 +76,28 @@ fun PostCardFooter(
),
)
Image(
modifier = buttonModifier.onClick {
onUpVote?.invoke()
},
modifier = buttonModifier
.let {
if (upVoted) {
it.background(
color = MaterialTheme.colorScheme.onSurface,
shape = CircleShape,
)
} else {
it
}
}.onClick {
onUpVote?.invoke()
},
imageVector = if (upVoted) {
Icons.Filled.ArrowCircleUp
Icons.Default.ArrowCircleUp
} else {
Icons.Outlined.ArrowCircleUp
Icons.Default.ArrowCircleUp
},
contentDescription = null,
colorFilter = ColorFilter.tint(
color = if (upVoted) {
MaterialTheme.colorScheme.secondary
MaterialTheme.colorScheme.surface
} else {
MaterialTheme.colorScheme.onSurface
},
@ -97,18 +107,29 @@ fun PostCardFooter(
text = "$score",
)
Image(
modifier = buttonModifier.onClick {
onDownVote?.invoke()
},
modifier = buttonModifier
.let {
if (downVoted) {
it.background(
color = MaterialTheme.colorScheme.onSurface,
shape = CircleShape,
)
} else {
it
}
}
.onClick {
onDownVote?.invoke()
},
imageVector = if (downVoted) {
Icons.Filled.ArrowCircleDown
Icons.Default.ArrowCircleDown
} else {
Icons.Outlined.ArrowCircleDown
Icons.Default.ArrowCircleDown
},
contentDescription = null,
colorFilter = ColorFilter.tint(
color = if (downVoted) {
MaterialTheme.colorScheme.secondary
MaterialTheme.colorScheme.surface
} else {
MaterialTheme.colorScheme.onSurface
},

View File

@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@ -42,78 +43,86 @@ fun PostCardSubtitle(
horizontalArrangement = Arrangement.spacedBy(Spacing.xs),
) {
if (communityName.isNotEmpty()) {
Row(
Surface(
modifier = Modifier
.onClick {
if (community != null) {
onOpenCommunity?.invoke(community)
}
},
horizontalArrangement = Arrangement.spacedBy(Spacing.xs),
) {
if (communityIcon.isNotEmpty()) {
val painterResource = asyncPainterResource(data = communityIcon)
KamelImage(
modifier = Modifier
.padding(Spacing.xxxs)
.size(iconSize)
.clip(RoundedCornerShape(iconSize / 2)),
resource = painterResource,
contentDescription = null,
contentScale = ContentScale.FillBounds,
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(Spacing.xs),
) {
if (communityIcon.isNotEmpty()) {
val painterResource = asyncPainterResource(data = communityIcon)
KamelImage(
modifier = Modifier
.padding(Spacing.xxxs)
.size(iconSize)
.clip(RoundedCornerShape(iconSize / 2)),
resource = painterResource,
contentDescription = null,
contentScale = ContentScale.FillBounds,
)
}
Text(
modifier = Modifier.padding(vertical = Spacing.xs),
text = buildString {
append(communityName)
if (communityHost.isNotEmpty()) {
append("@$communityHost")
}
},
style = MaterialTheme.typography.bodySmall,
)
}
Text(
modifier = Modifier.padding(vertical = Spacing.xs),
text = buildString {
append(communityName)
if (communityHost.isNotEmpty()) {
append("@$communityHost")
}
},
style = MaterialTheme.typography.bodySmall,
)
}
}
if (creatorName.isNotEmpty()) {
Row(
Surface(
modifier = Modifier
.onClick {
if (creator != null) {
onOpenCreator?.invoke(creator)
}
},
horizontalArrangement = Arrangement.spacedBy(Spacing.xs),
) {
if (communityName.isNotEmpty()) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(Spacing.xs),
) {
if (communityName.isNotEmpty()) {
Text(
text = "",
style = MaterialTheme.typography.bodySmall,
)
}
if (creatorAvatar.isNotEmpty()) {
val painterResource = asyncPainterResource(data = creatorAvatar)
KamelImage(
modifier = Modifier
.padding(Spacing.xxxs)
.size(iconSize)
.clip(RoundedCornerShape(iconSize / 2)),
resource = painterResource,
contentDescription = null,
contentScale = ContentScale.FillBounds,
)
}
Text(
text = "",
modifier = Modifier.padding(vertical = Spacing.xs),
text = buildString {
append(creatorName)
if (creatorHost.isNotEmpty() && communityHost != creatorHost) {
append("@$creatorHost")
}
},
style = MaterialTheme.typography.bodySmall,
)
}
if (creatorAvatar.isNotEmpty()) {
val painterResource = asyncPainterResource(data = creatorAvatar)
KamelImage(
modifier = Modifier
.padding(Spacing.xxxs)
.size(iconSize)
.clip(RoundedCornerShape(iconSize / 2)),
resource = painterResource,
contentDescription = null,
contentScale = ContentScale.FillBounds,
)
}
}
Text(
modifier = Modifier.padding(vertical = Spacing.xs),
text = buildString {
append(creatorName)
if (creatorHost.isNotEmpty() && communityHost != creatorHost) {
append("@$creatorHost")
}
},
style = MaterialTheme.typography.bodySmall,
)
}
}
}

View File

@ -16,6 +16,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.github.diegoberaldin.racconforlemmy.core.utils.DateTime
import com.github.diegoberaldin.racconforlemmy.core.utils.getPrettyNumber
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
@ -36,7 +37,10 @@ fun UserCounters(
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
) {
Text(
text = (user.score?.postScore ?: 0).toString(),
text = (user.score?.postScore ?: 0).getPrettyNumber(
millionLabel = stringResource(MR.strings.profile_million_short),
thousandLabel = stringResource(MR.strings.profile_thousand_short),
),
style = MaterialTheme.typography.headlineSmall,
)
Text(
@ -55,7 +59,10 @@ fun UserCounters(
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
) {
Text(
text = (user.score?.commentScore ?: 0).toString(),
text = (user.score?.commentScore ?: 0).getPrettyNumber(
millionLabel = stringResource(MR.strings.profile_million_short),
thousandLabel = stringResource(MR.strings.profile_thousand_short),
),
style = MaterialTheme.typography.headlineSmall,
)
Text(

View File

@ -1,14 +1,18 @@
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.CircularProgressIndicator
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.unit.dp
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
import io.kamel.image.KamelImage
import io.kamel.image.asyncPainterResource
@ -18,18 +22,30 @@ fun UserHeader(
user: UserModel,
) {
val avatar = user.avatar.orEmpty()
val profileImageSize = 100.dp
if (avatar.isNotEmpty()) {
val painterResource = asyncPainterResource(data = avatar)
val profileImageSize = 100.dp
KamelImage(
modifier = Modifier.size(profileImageSize)
.clip(RoundedCornerShape(profileImageSize / 2)),
resource = painterResource,
contentDescription = null,
onLoading = {
CircularProgressIndicator()
},
)
} else {
Box(
modifier = Modifier.padding(Spacing.xxxs).size(profileImageSize)
.background(
color = MaterialTheme.colorScheme.primary,
shape = RoundedCornerShape(profileImageSize / 2),
),
contentAlignment = Alignment.Center,
) {
Text(
text = user.name.firstOrNull()?.toString().orEmpty().uppercase(),
style = MaterialTheme.typography.headlineLarge,
color = MaterialTheme.colorScheme.onPrimary,
)
}
}
Text(
text = user.name,

View File

@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.DismissDirection
import androidx.compose.material.DismissValue
import androidx.compose.material.ExperimentalMaterialApi
@ -239,15 +240,45 @@ class PostDetailScreen(
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
DismissDirection.EndToStart -> Icons.Default.ArrowCircleUp
}
val (iconModifier, iconTint) = when {
direction == DismissDirection.StartToEnd && post.myVote < 0 -> {
Modifier.background(
color = Color.Transparent,
shape = CircleShape,
) to MaterialTheme.colorScheme.onSecondary
}
direction == DismissDirection.StartToEnd -> {
Modifier.background(
color = MaterialTheme.colorScheme.onSecondary,
shape = CircleShape,
) to MaterialTheme.colorScheme.secondary
}
direction == DismissDirection.EndToStart && post.myVote > 0 -> {
Modifier.background(
color = Color.Transparent,
shape = CircleShape,
) to MaterialTheme.colorScheme.onSecondary
}
else -> {
Modifier.background(
color = MaterialTheme.colorScheme.onSecondary,
shape = CircleShape,
) to MaterialTheme.colorScheme.secondary
}
}
Box(
Modifier.fillMaxSize().background(color)
.padding(horizontal = 20.dp),
contentAlignment = alignment,
) {
Icon(
modifier = iconModifier,
imageVector = icon,
contentDescription = null,
tint = iconTint,
)
}
},

View File

@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.DismissDirection
import androidx.compose.material.DismissValue
import androidx.compose.material.ExperimentalMaterialApi
@ -168,15 +169,45 @@ internal class UserDetailCommentsScreen(
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
DismissDirection.EndToStart -> Icons.Default.ArrowCircleUp
}
val (iconModifier, iconTint) = when {
direction == DismissDirection.StartToEnd && comment.myVote < 0 -> {
Modifier.background(
color = Color.Transparent,
shape = CircleShape,
) to MaterialTheme.colorScheme.onSecondary
}
direction == DismissDirection.StartToEnd -> {
Modifier.background(
color = MaterialTheme.colorScheme.onSecondary,
shape = CircleShape,
) to MaterialTheme.colorScheme.secondary
}
direction == DismissDirection.EndToStart && comment.myVote > 0 -> {
Modifier.background(
color = Color.Transparent,
shape = CircleShape,
) to MaterialTheme.colorScheme.onSecondary
}
else -> {
Modifier.background(
color = MaterialTheme.colorScheme.onSecondary,
shape = CircleShape,
) to MaterialTheme.colorScheme.secondary
}
}
Box(
Modifier.fillMaxSize().background(color)
.padding(horizontal = 20.dp),
contentAlignment = alignment,
) {
Icon(
modifier = iconModifier,
imageVector = icon,
contentDescription = null,
tint = iconTint,
)
}
},

View File

@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.DismissDirection
import androidx.compose.material.DismissValue
import androidx.compose.material.ExperimentalMaterialApi
@ -176,15 +177,45 @@ internal class UserDetailPostsScreen(
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
DismissDirection.EndToStart -> Icons.Default.ArrowCircleUp
}
val (iconModifier, iconTint) = when {
direction == DismissDirection.StartToEnd && post.myVote < 0 -> {
Modifier.background(
color = Color.Transparent,
shape = CircleShape,
) to MaterialTheme.colorScheme.onSecondary
}
direction == DismissDirection.StartToEnd -> {
Modifier.background(
color = MaterialTheme.colorScheme.onSecondary,
shape = CircleShape,
) to MaterialTheme.colorScheme.secondary
}
direction == DismissDirection.EndToStart && post.myVote > 0 -> {
Modifier.background(
color = Color.Transparent,
shape = CircleShape,
) to MaterialTheme.colorScheme.onSecondary
}
else -> {
Modifier.background(
color = MaterialTheme.colorScheme.onSecondary,
shape = CircleShape,
) to MaterialTheme.colorScheme.secondary
}
}
Box(
Modifier.fillMaxSize().background(color)
.padding(horizontal = 20.dp),
contentAlignment = alignment,
) {
Icon(
modifier = iconModifier,
imageVector = icon,
contentDescription = null,
tint = iconTint,
)
}
},

View File

@ -10,6 +10,7 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
import dev.icerock.moko.resources.compose.stringResource
import kotlin.math.roundToInt
fun Modifier.onClick(onClick: () -> Unit): Modifier = composed {
clickable(
@ -30,3 +31,14 @@ fun String.toLanguageName() = when (this) {
fun Dp.toLocalPixel(): Float = with(LocalDensity.current) {
value * density
}
fun Int.getPrettyNumber(
millionLabel: String,
thousandLabel: String,
): String {
return when {
this > 1_000_000 -> (((this / 1_000_000.0) * 10).roundToInt() / 10.0).toString() + millionLabel
this > 1_000 -> (((this / 1_000.0) * 10).roundToInt() / 10.0).toString() + thousandLabel
else -> this.toString()
}
}

View File

@ -28,6 +28,7 @@ class UserRepository(
avatar = dto.personView.person.avatar,
host = dto.personView.person.actorId.toHost(),
score = dto.personView.counts.toModel(),
accountAge = dto.personView.person.published,
)
}

View File

@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.DismissDirection
import androidx.compose.material.DismissValue
import androidx.compose.material.ExperimentalMaterialApi
@ -186,15 +187,45 @@ class PostListScreen : Screen {
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
DismissDirection.EndToStart -> Icons.Default.ArrowCircleUp
}
val (iconModifier, iconTint) = when {
direction == DismissDirection.StartToEnd && post.myVote < 0 -> {
Modifier.background(
color = Color.Transparent,
shape = CircleShape,
) to MaterialTheme.colorScheme.onSecondary
}
direction == DismissDirection.StartToEnd -> {
Modifier.background(
color = MaterialTheme.colorScheme.onSecondary,
shape = CircleShape,
) to MaterialTheme.colorScheme.secondary
}
direction == DismissDirection.EndToStart && post.myVote > 0 -> {
Modifier.background(
color = Color.Transparent,
shape = CircleShape,
) to MaterialTheme.colorScheme.onSecondary
}
else -> {
Modifier.background(
color = MaterialTheme.colorScheme.onSecondary,
shape = CircleShape,
) to MaterialTheme.colorScheme.secondary
}
}
Box(
Modifier.fillMaxSize().background(color)
.padding(horizontal = 20.dp),
contentAlignment = alignment,
) {
Icon(
modifier = iconModifier,
imageVector = icon,
contentDescription = null,
tint = iconTint,
)
}
},

View File

@ -51,6 +51,8 @@
<string name="profile_year_short">y</string>
<string name="profile_month_short">m</string>
<string name="profile_day_short">d</string>
<string name="profile_thousand_short">k</string>
<string name="profile_million_short">m</string>
<string name="profile_section_posts">Posts</string>
<string name="profile_section_comments">Comments</string>
<string name="profile_section_saved">Saved</string>

View File

@ -48,6 +48,8 @@
<string name="profile_year_short">a</string>
<string name="profile_month_short">m</string>
<string name="profile_day_short">g</string>
<string name="profile_thousand_short">k</string>
<string name="profile_million_short">m</string>
<string name="profile_section_posts">Post</string>
<string name="profile_section_comments">Commenti</string>
<string name="profile_section_saved">Salvati</string>