mirror of
https://github.com/msfjarvis/compose-lobsters
synced 2025-08-18 03:17:03 +05:30
Merge #73
73: Reimplement LobstersItem to not require swipe actions r=msfjarvis a=msfjarvis Work towards #65 TODO: - [x] ~~Figure out why only the last tag shows in the UI~~ Fixed by using `Row` to lay it out like earlier <details> <summary>Screenshot</summary>  </details> Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
commit
3cb1c4b027
4 changed files with 116 additions and 106 deletions
|
@ -85,8 +85,8 @@ fun LobstersApp() {
|
||||||
HottestPosts(
|
HottestPosts(
|
||||||
posts = hottestPosts,
|
posts = hottestPosts,
|
||||||
listState = hottestPostsListState,
|
listState = hottestPostsListState,
|
||||||
saveAction = viewModel::savePost,
|
|
||||||
overscrollAction = viewModel::getMorePosts,
|
overscrollAction = viewModel::getMorePosts,
|
||||||
|
saveAction = viewModel::savePost,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(Destination.Saved.route) {
|
composable(Destination.Saved.route) {
|
||||||
|
|
|
@ -14,8 +14,8 @@ fun HottestPosts(
|
||||||
posts: List<LobstersPost>,
|
posts: List<LobstersPost>,
|
||||||
listState: LazyListState,
|
listState: LazyListState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
saveAction: (LobstersPost) -> Unit,
|
|
||||||
overscrollAction: () -> Unit,
|
overscrollAction: () -> Unit,
|
||||||
|
saveAction: (LobstersPost) -> Unit,
|
||||||
) {
|
) {
|
||||||
val urlLauncher = UrlLauncherAmbient.current
|
val urlLauncher = UrlLauncherAmbient.current
|
||||||
|
|
||||||
|
@ -32,9 +32,9 @@ fun HottestPosts(
|
||||||
}
|
}
|
||||||
LobstersItem(
|
LobstersItem(
|
||||||
post = item,
|
post = item,
|
||||||
linkOpenAction = { post -> urlLauncher.launch(post.url.ifEmpty { post.commentsUrl }) },
|
onClick = { urlLauncher.launch(item.url.ifEmpty { item.commentsUrl }) },
|
||||||
commentOpenAction = { post -> urlLauncher.launch(post.commentsUrl) },
|
onLongClick = { urlLauncher.launch(item.commentsUrl) },
|
||||||
saveAction = saveAction,
|
onSaveButtonClick = { saveAction.invoke(item) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,107 +4,143 @@ import androidx.compose.foundation.Text
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.ConstraintLayout
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.offsetPx
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumnFor
|
import androidx.compose.foundation.lazy.LazyColumnFor
|
||||||
import androidx.compose.foundation.lazy.LazyItemScope
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.FractionalThreshold
|
import androidx.compose.material.Surface
|
||||||
import androidx.compose.material.rememberSwipeableState
|
import androidx.compose.material.ripple.RippleIndication
|
||||||
import androidx.compose.material.swipeable
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.ConfigurationAmbient
|
|
||||||
import androidx.compose.ui.platform.DensityAmbient
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.ui.tooling.preview.Preview
|
import androidx.ui.tooling.preview.Preview
|
||||||
import coil.transform.CircleCropTransformation
|
import coil.transform.CircleCropTransformation
|
||||||
import dev.chrisbanes.accompanist.coil.CoilImage
|
import dev.chrisbanes.accompanist.coil.CoilImage
|
||||||
|
import dev.msfjarvis.lobsters.R
|
||||||
import dev.msfjarvis.lobsters.model.LobstersPost
|
import dev.msfjarvis.lobsters.model.LobstersPost
|
||||||
import dev.msfjarvis.lobsters.model.Submitter
|
import dev.msfjarvis.lobsters.model.Submitter
|
||||||
import dev.msfjarvis.lobsters.ui.theme.LobstersTheme
|
import dev.msfjarvis.lobsters.ui.theme.LobstersTheme
|
||||||
import dev.msfjarvis.lobsters.ui.theme.titleColor
|
import dev.msfjarvis.lobsters.ui.theme.titleColor
|
||||||
|
import dev.msfjarvis.lobsters.util.IconResource
|
||||||
|
|
||||||
private enum class SwipeState {
|
val TEST_POST = LobstersPost(
|
||||||
NotSwiped,
|
"zqyydb",
|
||||||
FullySwiped,
|
"https://lobste.rs/s/zqyydb",
|
||||||
}
|
"2020-09-21T07:11:14.000-05:00",
|
||||||
|
"k2k20 hackathon report: Bob Beck on LibreSSL progress",
|
||||||
|
"https://undeadly.org/cgi?action=article;sid=20200921105847",
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"",
|
||||||
|
"https://lobste.rs/s/zqyydb/k2k20_hackathon_report_bob_beck_on",
|
||||||
|
Submitter(
|
||||||
|
"Vigdis",
|
||||||
|
"2017-02-27T21:08:14.000-06:00",
|
||||||
|
false,
|
||||||
|
"Alleycat for the fun, sys/net admin for a living and OpenBSD contributions for the pleasure. (Not so) French dude in Montreal\r\n\r\nhttps://chown.me",
|
||||||
|
false,
|
||||||
|
76,
|
||||||
|
"/avatars/Vigdis-100.png",
|
||||||
|
"sevan",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
emptyList(),
|
||||||
|
),
|
||||||
|
listOf("openbsd", "linux", "containers", "hack the planet", "no thanks"),
|
||||||
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LazyItemScope.LobstersItem(
|
fun LobstersItem(
|
||||||
post: LobstersPost,
|
post: LobstersPost,
|
||||||
modifier: Modifier = Modifier,
|
onClick: () -> Unit,
|
||||||
linkOpenAction: (LobstersPost) -> Unit,
|
onLongClick: () -> Unit,
|
||||||
commentOpenAction: (LobstersPost) -> Unit,
|
onSaveButtonClick: () -> Unit,
|
||||||
saveAction: (LobstersPost) -> Unit,
|
|
||||||
) {
|
) {
|
||||||
val width = with(DensityAmbient.current) {
|
Surface(
|
||||||
ConfigurationAmbient.current.screenWidthDp.toDp().toPx()
|
modifier = Modifier.fillMaxWidth()
|
||||||
}
|
|
||||||
val swipeableState = rememberSwipeableState(SwipeState.NotSwiped)
|
|
||||||
val anchors = mapOf(0f to SwipeState.NotSwiped, width to SwipeState.FullySwiped)
|
|
||||||
if (swipeableState.offset.value >= (width / 2)) {
|
|
||||||
saveAction.invoke(post)
|
|
||||||
swipeableState.animateTo(SwipeState.NotSwiped)
|
|
||||||
}
|
|
||||||
|
|
||||||
Column(
|
|
||||||
modifier = modifier
|
|
||||||
.fillParentMaxWidth()
|
|
||||||
.swipeable(
|
|
||||||
state = swipeableState,
|
|
||||||
anchors = anchors,
|
|
||||||
thresholds = { _, _ -> FractionalThreshold(0.5f) },
|
|
||||||
orientation = Orientation.Horizontal
|
|
||||||
)
|
|
||||||
.offsetPx(swipeableState.offset)
|
|
||||||
.clickable(
|
.clickable(
|
||||||
onClick = { linkOpenAction.invoke(post) },
|
onClick = onClick,
|
||||||
onLongClick = { commentOpenAction.invoke(post) },
|
onLongClick = onLongClick,
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
Text(
|
ConstraintLayout(
|
||||||
text = post.title,
|
modifier = Modifier.padding(start = 4.dp, end = 4.dp),
|
||||||
color = titleColor,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
modifier = Modifier.padding(top = 4.dp)
|
|
||||||
)
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.padding(vertical = 8.dp),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
|
||||||
) {
|
|
||||||
post.tags.take(4).forEach { tag ->
|
|
||||||
Text(
|
|
||||||
text = tag,
|
|
||||||
modifier = Modifier
|
|
||||||
.background(Color(0xFFFFFCD7), RoundedCornerShape(8.dp))
|
|
||||||
.padding(vertical = 2.dp, horizontal = 6.dp),
|
|
||||||
color = Color.DarkGray,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.wrapContentHeight(),
|
|
||||||
) {
|
) {
|
||||||
|
val (title, tags, avatar, submitter, saveButton) = createRefs()
|
||||||
|
Text(
|
||||||
|
text = post.title,
|
||||||
|
color = titleColor,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
modifier = Modifier.padding(top = 4.dp)
|
||||||
|
.constrainAs(title) {
|
||||||
|
top.linkTo(parent.top)
|
||||||
|
start.linkTo(parent.start)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
TagRow(
|
||||||
|
tags = post.tags,
|
||||||
|
modifier = Modifier.constrainAs(tags) {
|
||||||
|
top.linkTo(title.bottom)
|
||||||
|
}.padding(vertical = 8.dp),
|
||||||
|
)
|
||||||
CoilImage(
|
CoilImage(
|
||||||
data = "https://lobste.rs/${post.submitterUser.avatarUrl}",
|
data = "https://lobste.rs/${post.submitterUser.avatarUrl}",
|
||||||
fadeIn = true,
|
fadeIn = true,
|
||||||
requestBuilder = {
|
requestBuilder = {
|
||||||
transformations(CircleCropTransformation())
|
transformations(CircleCropTransformation())
|
||||||
},
|
},
|
||||||
modifier = Modifier.width(30.dp).padding(4.dp).align(Alignment.CenterVertically),
|
modifier = Modifier.width(30.dp).padding(4.dp)
|
||||||
|
.constrainAs(avatar) {
|
||||||
|
top.linkTo(tags.bottom)
|
||||||
|
start.linkTo(parent.start)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "submitted by ${post.submitterUser.username}",
|
text = "submitted by ${post.submitterUser.username}",
|
||||||
modifier = Modifier.padding(bottom = 4.dp).align(Alignment.CenterVertically),
|
modifier = Modifier.padding(bottom = 4.dp).constrainAs(submitter) {
|
||||||
|
top.linkTo(tags.bottom)
|
||||||
|
start.linkTo(avatar.end)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
IconResource(
|
||||||
|
resourceId = R.drawable.ic_favorite_border_24px,
|
||||||
|
modifier = Modifier.padding(8.dp)
|
||||||
|
.clickable(
|
||||||
|
onClick = onSaveButtonClick,
|
||||||
|
indication = RippleIndication(),
|
||||||
|
)
|
||||||
|
.constrainAs(saveButton) {
|
||||||
|
end.linkTo(parent.end)
|
||||||
|
centerVerticallyTo(parent)
|
||||||
|
},
|
||||||
|
tint = Color(0xFFD97373),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TagRow(
|
||||||
|
tags: List<String>,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.then(modifier),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
) {
|
||||||
|
tags.take(3).forEach { tag ->
|
||||||
|
Text(
|
||||||
|
text = tag,
|
||||||
|
modifier = Modifier
|
||||||
|
.background(Color(0xFFFFFCD7), RoundedCornerShape(8.dp))
|
||||||
|
.padding(vertical = 2.dp, horizontal = 6.dp),
|
||||||
|
color = Color.DarkGray,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,40 +148,14 @@ fun LazyItemScope.LobstersItem(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview
|
@Preview
|
||||||
fun PreviewLobstersItem() {
|
fun Preview() {
|
||||||
val post = LobstersPost(
|
|
||||||
"zqyydb",
|
|
||||||
"https://lobste.rs/s/zqyydb",
|
|
||||||
"2020-09-21T07:11:14.000-05:00",
|
|
||||||
"k2k20 hackathon report: Bob Beck on LibreSSL progress",
|
|
||||||
"https://undeadly.org/cgi?action=article;sid=20200921105847",
|
|
||||||
4,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
"",
|
|
||||||
"https://lobste.rs/s/zqyydb/k2k20_hackathon_report_bob_beck_on",
|
|
||||||
Submitter(
|
|
||||||
"Vigdis",
|
|
||||||
"2017-02-27T21:08:14.000-06:00",
|
|
||||||
false,
|
|
||||||
"Alleycat for the fun, sys/net admin for a living and OpenBSD contributions for the pleasure. (Not so) French dude in Montreal\r\n\r\nhttps://chown.me",
|
|
||||||
false,
|
|
||||||
76,
|
|
||||||
"/avatars/Vigdis-100.png",
|
|
||||||
"sevan",
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
emptyList(),
|
|
||||||
),
|
|
||||||
listOf("openbsd"),
|
|
||||||
)
|
|
||||||
LobstersTheme {
|
LobstersTheme {
|
||||||
LazyColumnFor(items = listOf(post)) { item ->
|
LazyColumnFor(items = listOf(TEST_POST, TEST_POST, TEST_POST, TEST_POST, TEST_POST)) { item ->
|
||||||
LobstersItem(
|
LobstersItem(
|
||||||
post = item,
|
post = item,
|
||||||
linkOpenAction = {},
|
onClick = {},
|
||||||
commentOpenAction = {},
|
onLongClick = {},
|
||||||
saveAction = {},
|
onSaveButtonClick = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import dev.msfjarvis.lobsters.ui.urllauncher.UrlLauncherAmbient
|
||||||
fun SavedPosts(
|
fun SavedPosts(
|
||||||
posts: List<LobstersPost>,
|
posts: List<LobstersPost>,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
saveAction: (LobstersPost) -> Unit
|
saveAction: (LobstersPost) -> Unit,
|
||||||
) {
|
) {
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
val urlLauncher = UrlLauncherAmbient.current
|
val urlLauncher = UrlLauncherAmbient.current
|
||||||
|
@ -28,9 +28,9 @@ fun SavedPosts(
|
||||||
) { item ->
|
) { item ->
|
||||||
LobstersItem(
|
LobstersItem(
|
||||||
post = item,
|
post = item,
|
||||||
linkOpenAction = { post -> urlLauncher.launch(post.url.ifEmpty { post.commentsUrl }) },
|
onClick = { urlLauncher.launch(item.url.ifEmpty { item.commentsUrl }) },
|
||||||
commentOpenAction = { post -> urlLauncher.launch(post.commentsUrl) },
|
onLongClick = { urlLauncher.launch(item.commentsUrl) },
|
||||||
saveAction = saveAction,
|
onSaveButtonClick = { saveAction.invoke(item) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue