Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reimplement LobstersItem to not require swipe actions #73

Merged
merged 8 commits into from Nov 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -85,8 +85,8 @@ fun LobstersApp() {
HottestPosts(
posts = hottestPosts,
listState = hottestPostsListState,
saveAction = viewModel::savePost,
overscrollAction = viewModel::getMorePosts,
saveAction = viewModel::savePost,
)
}
composable(Destination.Saved.route) {
Expand Down
Expand Up @@ -14,8 +14,8 @@ fun HottestPosts(
posts: List<LobstersPost>,
listState: LazyListState,
modifier: Modifier = Modifier,
saveAction: (LobstersPost) -> Unit,
overscrollAction: () -> Unit,
saveAction: (LobstersPost) -> Unit,
) {
val urlLauncher = UrlLauncherAmbient.current

Expand All @@ -32,9 +32,9 @@ fun HottestPosts(
}
LobstersItem(
post = item,
linkOpenAction = { post -> urlLauncher.launch(post.url.ifEmpty { post.commentsUrl }) },
commentOpenAction = { post -> urlLauncher.launch(post.commentsUrl) },
saveAction = saveAction,
onClick = { urlLauncher.launch(item.url.ifEmpty { item.commentsUrl }) },
onLongClick = { urlLauncher.launch(item.commentsUrl) },
onSaveButtonClick = { saveAction.invoke(item) },
)
}
}
Expand Down
204 changes: 107 additions & 97 deletions app/src/main/java/dev/msfjarvis/lobsters/ui/posts/LobstersItem.kt
Expand Up @@ -4,148 +4,158 @@ import androidx.compose.foundation.Text
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
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.offsetPx
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumnFor
import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.FractionalThreshold
import androidx.compose.material.rememberSwipeableState
import androidx.compose.material.swipeable
import androidx.compose.material.Surface
import androidx.compose.material.ripple.RippleIndication
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
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.unit.dp
import androidx.ui.tooling.preview.Preview
import coil.transform.CircleCropTransformation
import dev.chrisbanes.accompanist.coil.CoilImage
import dev.msfjarvis.lobsters.R
import dev.msfjarvis.lobsters.model.LobstersPost
import dev.msfjarvis.lobsters.model.Submitter
import dev.msfjarvis.lobsters.ui.theme.LobstersTheme
import dev.msfjarvis.lobsters.ui.theme.titleColor
import dev.msfjarvis.lobsters.util.IconResource

private enum class SwipeState {
NotSwiped,
FullySwiped,
}
val TEST_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", "linux", "containers", "hack the planet", "no thanks"),
)

@Composable
fun LazyItemScope.LobstersItem(
fun LobstersItem(
post: LobstersPost,
modifier: Modifier = Modifier,
linkOpenAction: (LobstersPost) -> Unit,
commentOpenAction: (LobstersPost) -> Unit,
saveAction: (LobstersPost) -> Unit,
onClick: () -> Unit,
onLongClick: () -> Unit,
onSaveButtonClick: () -> Unit,
) {
val width = with(DensityAmbient.current) {
ConfigurationAmbient.current.screenWidthDp.toDp().toPx()
}
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)
Surface(
modifier = Modifier.fillMaxWidth()
.clickable(
onClick = { linkOpenAction.invoke(post) },
onLongClick = { commentOpenAction.invoke(post) },
onClick = onClick,
onLongClick = onLongClick,
),
) {
Text(
text = post.title,
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(),
ConstraintLayout(
modifier = Modifier.padding(start = 4.dp, end = 4.dp),
) {
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(
data = "https://lobste.rs/${post.submitterUser.avatarUrl}",
fadeIn = true,
requestBuilder = {
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 = "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,
)
}
}
}

@Composable
@Preview
fun PreviewLobstersItem() {
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"),
)
fun Preview() {
LobstersTheme {
LazyColumnFor(items = listOf(post)) { item ->
LazyColumnFor(items = listOf(TEST_POST, TEST_POST, TEST_POST, TEST_POST, TEST_POST)) { item ->
LobstersItem(
post = item,
linkOpenAction = {},
commentOpenAction = {},
saveAction = {},
onClick = {},
onLongClick = {},
onSaveButtonClick = {},
)
}
}
Expand Down
Expand Up @@ -13,7 +13,7 @@ import dev.msfjarvis.lobsters.ui.urllauncher.UrlLauncherAmbient
fun SavedPosts(
posts: List<LobstersPost>,
modifier: Modifier = Modifier,
saveAction: (LobstersPost) -> Unit
saveAction: (LobstersPost) -> Unit,
) {
val listState = rememberLazyListState()
val urlLauncher = UrlLauncherAmbient.current
Expand All @@ -28,9 +28,9 @@ fun SavedPosts(
) { item ->
LobstersItem(
post = item,
linkOpenAction = { post -> urlLauncher.launch(post.url.ifEmpty { post.commentsUrl }) },
commentOpenAction = { post -> urlLauncher.launch(post.commentsUrl) },
saveAction = saveAction,
onClick = { urlLauncher.launch(item.url.ifEmpty { item.commentsUrl }) },
onLongClick = { urlLauncher.launch(item.commentsUrl) },
onSaveButtonClick = { saveAction.invoke(item) },
)
}
}
Expand Down