diff --git a/app/screenshots/debug/dev.msfjarvis.lobsters.ui.posts.LobstersItemTest_multiplePosts.png b/app/screenshots/debug/dev.msfjarvis.lobsters.ui.posts.LobstersItemTest_multiplePosts.png new file mode 100644 index 00000000..6e3fd517 Binary files /dev/null and b/app/screenshots/debug/dev.msfjarvis.lobsters.ui.posts.LobstersItemTest_multiplePosts.png differ diff --git a/app/screenshots/debug/dev.msfjarvis.lobsters.ui.posts.LobstersItemTest_multiplePostsWithLesserTags.png b/app/screenshots/debug/dev.msfjarvis.lobsters.ui.posts.LobstersItemTest_multiplePostsWithLesserTags.png new file mode 100644 index 00000000..89983267 Binary files /dev/null and b/app/screenshots/debug/dev.msfjarvis.lobsters.ui.posts.LobstersItemTest_multiplePostsWithLesserTags.png differ diff --git a/app/screenshots/debug/dev.msfjarvis.lobsters.ui.posts.LobstersItemTest_postsAreRenderedCorrectlyOnScreen.png b/app/screenshots/debug/dev.msfjarvis.lobsters.ui.posts.LobstersItemTest_postsAreRenderedCorrectlyOnScreen.png deleted file mode 100644 index 434d0ec6..00000000 Binary files a/app/screenshots/debug/dev.msfjarvis.lobsters.ui.posts.LobstersItemTest_postsAreRenderedCorrectlyOnScreen.png and /dev/null differ diff --git a/app/screenshots/debug/dev.msfjarvis.lobsters.ui.posts.LobstersItemTest_singlePost.png b/app/screenshots/debug/dev.msfjarvis.lobsters.ui.posts.LobstersItemTest_singlePost.png new file mode 100644 index 00000000..9eb47105 Binary files /dev/null and b/app/screenshots/debug/dev.msfjarvis.lobsters.ui.posts.LobstersItemTest_singlePost.png differ diff --git a/app/src/androidTest/java/dev/msfjarvis/lobsters/ui/posts/LobstersItemTest.kt b/app/src/androidTest/java/dev/msfjarvis/lobsters/ui/posts/LobstersItemTest.kt index 759366fd..9af570f9 100644 --- a/app/src/androidTest/java/dev/msfjarvis/lobsters/ui/posts/LobstersItemTest.kt +++ b/app/src/androidTest/java/dev/msfjarvis/lobsters/ui/posts/LobstersItemTest.kt @@ -1,11 +1,13 @@ package dev.msfjarvis.lobsters.ui.posts +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.ui.graphics.asAndroidBitmap import androidx.compose.ui.test.captureToImage import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onRoot import com.karumi.shot.ScreenshotTest import dev.msfjarvis.lobsters.ui.DarkTestTheme +import dev.msfjarvis.lobsters.ui.LightTestTheme import kotlin.test.Test import org.junit.Rule @@ -15,7 +17,7 @@ class LobstersItemTest : ScreenshotTest { val composeTestRule = createComposeRule() @Test - fun postsAreRenderedCorrectlyOnScreen() { + fun singlePost() { composeTestRule.setContent { DarkTestTheme { LobstersItem( @@ -29,4 +31,44 @@ class LobstersItemTest : ScreenshotTest { } compareScreenshot(composeTestRule.onRoot().captureToImage().asAndroidBitmap()) } + + @Test + fun multiplePosts() { + composeTestRule.setContent { + LightTestTheme { + LazyColumn { + items(10) { + LobstersItem( + post = TEST_POST, + viewPost = { /*TODO*/ }, + viewComments = { /*TODO*/ }, + toggleSave = { /*TODO*/ }, + isSaved = true, + ) + } + } + } + } + compareScreenshot(composeTestRule.onRoot().captureToImage().asAndroidBitmap()) + } + + @Test + fun multiplePostsWithLesserTags() { + composeTestRule.setContent { + LightTestTheme { + LazyColumn { + items(10) { + LobstersItem( + post = TEST_POST.copy(tags = listOf("openbsd", "linux")), + viewPost = { /*TODO*/ }, + viewComments = { /*TODO*/ }, + toggleSave = { /*TODO*/ }, + isSaved = true, + ) + } + } + } + } + compareScreenshot(composeTestRule.onRoot().captureToImage().asAndroidBitmap()) + } } diff --git a/app/src/main/java/dev/msfjarvis/lobsters/ui/posts/LobstersItem.kt b/app/src/main/java/dev/msfjarvis/lobsters/ui/posts/LobstersItem.kt index 3e596dfb..a55b285d 100644 --- a/app/src/main/java/dev/msfjarvis/lobsters/ui/posts/LobstersItem.kt +++ b/app/src/main/java/dev/msfjarvis/lobsters/ui/posts/LobstersItem.kt @@ -4,12 +4,14 @@ import androidx.compose.animation.Crossfade import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredSize +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape @@ -43,7 +45,7 @@ val TEST_POST = SavedPost( createdAt = "2020-09-21T07:11:14.000-05:00", commentsUrl = "https://lobste.rs/s/zqyydb/k2k20_hackathon_report_bob_beck_on", submitterName = "Vigdis", - submitterAvatarUrl = "/avatars/Vigdis-100.png", + submitterAvatarUrl = "/404.html", tags = listOf("openbsd", "linux", "containers", "hack the planet", "no thanks"), ) @@ -55,97 +57,126 @@ fun LobstersItem( viewPost: () -> Unit, viewComments: () -> Unit, toggleSave: () -> Unit, + modifier: Modifier = Modifier, ) { Surface( modifier = Modifier - .clickable { viewPost.invoke() }, + .clickable { viewPost.invoke() } + .then(modifier), ) { - Row( - modifier = Modifier.padding(start = 12.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween, + Column( + modifier = Modifier + .padding(horizontal = 12.dp, vertical = 4.dp), ) { - Box( - modifier = Modifier.weight(0.8f), + PostTitle( + title = post.title, + modifier = Modifier + .padding(bottom = 4.dp), + ) + Row( + modifier = Modifier + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, ) { - PostDetails( - post, + TagRow( + tags = post.tags, + modifier = Modifier.weight(0.65f), ) - } - Box( - modifier = Modifier.weight(0.1f), - ) { SaveButton( - isSaved, - toggleSave, + isSaved = isSaved, + onClick = toggleSave, + ) + Spacer( + modifier = Modifier.width(8.dp), ) - } - Box( - modifier = Modifier.weight(0.1f), - ) { CommentsButton( onClick = viewComments, ) } + SubmitterName( + name = post.submitterName, + avatarUrl = post.submitterAvatarUrl, + ) } } } @Composable -fun PostDetails( - post: SavedPost, +fun PostTitle( + title: String, + modifier: Modifier = Modifier, ) { - Column( - modifier = Modifier.padding(top = 8.dp, bottom = 8.dp), + Text( + text = title, + color = titleColor, + fontWeight = FontWeight.Bold, + modifier = Modifier.then(modifier), + ) +} + +@Composable +fun SubmitterName( + name: String, + avatarUrl: String, + modifier: Modifier = Modifier, +) { + Row( + modifier = Modifier.then(modifier), + verticalAlignment = Alignment.CenterVertically, ) { - Text( - text = post.title, - color = titleColor, - fontWeight = FontWeight.Bold, - modifier = Modifier - .padding(bottom = 4.dp), + SubmitterAvatar( + name = name, + avatarUrl = avatarUrl, ) - TagRow( - tags = post.tags, - modifier = Modifier - .padding(bottom = 4.dp), + SubmitterNameText( + name = name, ) - Row( - verticalAlignment = Alignment.CenterVertically, - ) { - CoilImage( - data = "${LobstersApi.BASE_URL}/${post.submitterAvatarUrl}", - contentDescription = stringResource( - R.string.avatar_content_description, - post.submitterName - ), - fadeIn = true, - requestBuilder = { - transformations(CircleCropTransformation()) - }, - modifier = Modifier - .requiredSize(24.dp) - .padding(bottom = 4.dp), - ) - Text( - text = stringResource(id = R.string.submitted_by, post.submitterName), - modifier = Modifier - .padding(start = 4.dp), - ) - } } } +@Composable +fun SubmitterAvatar( + name: String, + avatarUrl: String, +) { + CoilImage( + data = "${LobstersApi.BASE_URL}/$avatarUrl", + contentDescription = stringResource( + R.string.avatar_content_description, + name, + ), + fadeIn = true, + requestBuilder = { + transformations(CircleCropTransformation()) + }, + modifier = Modifier + .requiredSize(24.dp), + ) +} + +@Composable +fun SubmitterNameText( + name: String, +) { + Text( + text = stringResource(id = R.string.submitted_by, name), + modifier = Modifier + .padding(start = 4.dp), + ) +} + @Composable fun SaveButton( isSaved: Boolean, - onSaveButtonClick: () -> Unit, + onClick: () -> Unit, + modifier: Modifier = Modifier, ) { IconToggleButton( checked = isSaved, - onCheckedChange = { onSaveButtonClick.invoke() }, + onCheckedChange = { onClick.invoke() }, modifier = Modifier - .requiredSize(24.dp), + .requiredSize(32.dp) + .then(modifier), ) { Crossfade(targetState = isSaved) { saved -> IconResource( @@ -160,11 +191,13 @@ fun SaveButton( @Composable fun CommentsButton( onClick: () -> Unit, + modifier: Modifier = Modifier, ) { IconButton( onClick = onClick, modifier = Modifier - .requiredSize(24.dp), + .requiredSize(32.dp) + .then(modifier), ) { IconResource( resourceId = R.drawable.ic_insert_comment_24px, @@ -179,19 +212,22 @@ fun TagRow( tags: List, modifier: Modifier = Modifier, ) { - FlowLayout( + Box( modifier = Modifier.then(modifier), - horizontalSpacing = 8.dp, - verticalSpacing = 8.dp, ) { - tags.forEach { tag -> - Text( - text = tag, - modifier = Modifier - .background(Color(0xFFFFFCD7), RoundedCornerShape(8.dp)) - .padding(vertical = 2.dp, horizontal = 6.dp), - color = Color.DarkGray, - ) + FlowLayout( + horizontalSpacing = 8.dp, + verticalSpacing = 8.dp, + ) { + tags.forEach { tag -> + Text( + text = tag, + modifier = Modifier + .background(Color(0xFFFFFCD7), RoundedCornerShape(8.dp)) + .padding(vertical = 2.dp, horizontal = 6.dp), + color = Color.DarkGray, + ) + } } } }