feat(user): add swipe actions to user detail
This commit is contained in:
parent
578176a449
commit
0ecb9fb42a
@ -22,8 +22,8 @@ import androidx.compose.material.Icon
|
|||||||
import androidx.compose.material.SwipeToDismiss
|
import androidx.compose.material.SwipeToDismiss
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.ThumbDown
|
import androidx.compose.material.icons.filled.ArrowCircleDown
|
||||||
import androidx.compose.material.icons.filled.ThumbUp
|
import androidx.compose.material.icons.filled.ArrowCircleUp
|
||||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||||
import androidx.compose.material.pullrefresh.pullRefresh
|
import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
@ -261,8 +261,8 @@ class CommunityDetailScreen(
|
|||||||
DismissDirection.EndToStart -> Alignment.CenterEnd
|
DismissDirection.EndToStart -> Alignment.CenterEnd
|
||||||
}
|
}
|
||||||
val icon = when (direction) {
|
val icon = when (direction) {
|
||||||
DismissDirection.StartToEnd -> Icons.Default.ThumbDown
|
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
|
||||||
DismissDirection.EndToStart -> Icons.Default.ThumbUp
|
DismissDirection.EndToStart -> Icons.Default.ArrowCircleUp
|
||||||
}
|
}
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
|
@ -54,7 +54,7 @@ fun PostCard(
|
|||||||
PostCardTitle(post)
|
PostCardTitle(post)
|
||||||
PostCardSubtitle(
|
PostCardSubtitle(
|
||||||
community = post.community,
|
community = post.community,
|
||||||
creator = post.creator,
|
creator = post.creator?.copy(avatar = null),
|
||||||
onOpenCommunity = onOpenCommunity,
|
onOpenCommunity = onOpenCommunity,
|
||||||
onOpenCreator = onOpenCreator,
|
onOpenCreator = onOpenCreator,
|
||||||
)
|
)
|
||||||
|
@ -7,13 +7,13 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material.icons.Icons
|
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.Bookmark
|
||||||
import androidx.compose.material.icons.filled.BookmarkBorder
|
import androidx.compose.material.icons.filled.BookmarkBorder
|
||||||
import androidx.compose.material.icons.filled.Chat
|
import androidx.compose.material.icons.filled.Chat
|
||||||
import androidx.compose.material.icons.filled.ThumbDown
|
import androidx.compose.material.icons.outlined.ArrowCircleDown
|
||||||
import androidx.compose.material.icons.filled.ThumbUp
|
import androidx.compose.material.icons.outlined.ArrowCircleUp
|
||||||
import androidx.compose.material.icons.outlined.ThumbDown
|
|
||||||
import androidx.compose.material.icons.outlined.ThumbUp
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@ -80,9 +80,9 @@ fun PostCardFooter(
|
|||||||
onUpVote?.invoke()
|
onUpVote?.invoke()
|
||||||
},
|
},
|
||||||
imageVector = if (upVoted) {
|
imageVector = if (upVoted) {
|
||||||
Icons.Filled.ThumbUp
|
Icons.Filled.ArrowCircleUp
|
||||||
} else {
|
} else {
|
||||||
Icons.Outlined.ThumbUp
|
Icons.Outlined.ArrowCircleUp
|
||||||
},
|
},
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
colorFilter = ColorFilter.tint(
|
colorFilter = ColorFilter.tint(
|
||||||
@ -101,9 +101,9 @@ fun PostCardFooter(
|
|||||||
onDownVote?.invoke()
|
onDownVote?.invoke()
|
||||||
},
|
},
|
||||||
imageVector = if (downVoted) {
|
imageVector = if (downVoted) {
|
||||||
Icons.Filled.ThumbDown
|
Icons.Filled.ArrowCircleDown
|
||||||
} else {
|
} else {
|
||||||
Icons.Outlined.ThumbDown
|
Icons.Outlined.ArrowCircleDown
|
||||||
},
|
},
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
colorFilter = ColorFilter.tint(
|
colorFilter = ColorFilter.tint(
|
||||||
|
@ -2,7 +2,6 @@ package com.github.diegoberaldin.raccoonforlemmy.core.commonui.components
|
|||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
@ -45,12 +44,12 @@ fun PostCardSubtitle(
|
|||||||
if (communityName.isNotEmpty()) {
|
if (communityName.isNotEmpty()) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxHeight()
|
|
||||||
.onClick {
|
.onClick {
|
||||||
if (community != null) {
|
if (community != null) {
|
||||||
onOpenCommunity?.invoke(community)
|
onOpenCommunity?.invoke(community)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(Spacing.xs),
|
||||||
) {
|
) {
|
||||||
if (communityIcon.isNotEmpty()) {
|
if (communityIcon.isNotEmpty()) {
|
||||||
val painterResource = asyncPainterResource(data = communityIcon)
|
val painterResource = asyncPainterResource(data = communityIcon)
|
||||||
@ -65,6 +64,7 @@ fun PostCardSubtitle(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
Text(
|
Text(
|
||||||
|
modifier = Modifier.padding(vertical = Spacing.xs),
|
||||||
text = buildString {
|
text = buildString {
|
||||||
append(communityName)
|
append(communityName)
|
||||||
if (communityHost.isNotEmpty()) {
|
if (communityHost.isNotEmpty()) {
|
||||||
@ -78,12 +78,12 @@ fun PostCardSubtitle(
|
|||||||
if (creatorName.isNotEmpty()) {
|
if (creatorName.isNotEmpty()) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxHeight()
|
|
||||||
.onClick {
|
.onClick {
|
||||||
if (creator != null) {
|
if (creator != null) {
|
||||||
onOpenCreator?.invoke(creator)
|
onOpenCreator?.invoke(creator)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(Spacing.xs),
|
||||||
) {
|
) {
|
||||||
if (communityName.isNotEmpty()) {
|
if (communityName.isNotEmpty()) {
|
||||||
Text(
|
Text(
|
||||||
@ -105,6 +105,7 @@ fun PostCardSubtitle(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Text(
|
Text(
|
||||||
|
modifier = Modifier.padding(vertical = Spacing.xs),
|
||||||
text = buildString {
|
text = buildString {
|
||||||
append(creatorName)
|
append(creatorName)
|
||||||
if (creatorHost.isNotEmpty() && communityHost != creatorHost) {
|
if (creatorHost.isNotEmpty() && communityHost != creatorHost) {
|
||||||
|
@ -19,8 +19,8 @@ import androidx.compose.material.Icon
|
|||||||
import androidx.compose.material.SwipeToDismiss
|
import androidx.compose.material.SwipeToDismiss
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.ThumbDown
|
import androidx.compose.material.icons.filled.ArrowCircleDown
|
||||||
import androidx.compose.material.icons.filled.ThumbUp
|
import androidx.compose.material.icons.filled.ArrowCircleUp
|
||||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||||
import androidx.compose.material.pullrefresh.pullRefresh
|
import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
@ -110,7 +110,7 @@ class PostDetailScreen(
|
|||||||
PostCardTitle(post)
|
PostCardTitle(post)
|
||||||
PostCardSubtitle(
|
PostCardSubtitle(
|
||||||
community = post.community,
|
community = post.community,
|
||||||
creator = post.creator,
|
creator = post.creator?.copy(avatar = null),
|
||||||
onOpenCommunity = { community ->
|
onOpenCommunity = { community ->
|
||||||
navigator.push(
|
navigator.push(
|
||||||
CommunityDetailScreen(
|
CommunityDetailScreen(
|
||||||
@ -236,8 +236,8 @@ class PostDetailScreen(
|
|||||||
DismissDirection.EndToStart -> Alignment.CenterEnd
|
DismissDirection.EndToStart -> Alignment.CenterEnd
|
||||||
}
|
}
|
||||||
val icon = when (direction) {
|
val icon = when (direction) {
|
||||||
DismissDirection.StartToEnd -> Icons.Default.ThumbDown
|
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
|
||||||
DismissDirection.EndToStart -> Icons.Default.ThumbUp
|
DismissDirection.EndToStart -> Icons.Default.ArrowCircleUp
|
||||||
}
|
}
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.comments
|
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.comments
|
||||||
|
|
||||||
|
import androidx.compose.animation.animateColorAsState
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@ -11,18 +13,35 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material.DismissDirection
|
||||||
|
import androidx.compose.material.DismissValue
|
||||||
import androidx.compose.material.ExperimentalMaterialApi
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
|
import androidx.compose.material.FractionalThreshold
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.SwipeToDismiss
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.ArrowCircleDown
|
||||||
|
import androidx.compose.material.icons.filled.ArrowCircleUp
|
||||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||||
import androidx.compose.material.pullrefresh.pullRefresh
|
import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
|
import androidx.compose.material.rememberDismissState
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.toSize
|
||||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||||
import cafe.adriel.voyager.core.screen.Screen
|
import cafe.adriel.voyager.core.screen.Screen
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||||
@ -55,8 +74,11 @@ internal class UserDetailCommentsScreen(
|
|||||||
Box(
|
Box(
|
||||||
modifier = modifier.pullRefresh(pullRefreshState),
|
modifier = modifier.pullRefresh(pullRefreshState),
|
||||||
) {
|
) {
|
||||||
|
var width by remember { mutableStateOf(0f) }
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize().onGloballyPositioned {
|
||||||
|
width = it.size.toSize().width
|
||||||
|
},
|
||||||
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
|
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
@ -76,6 +98,89 @@ internal class UserDetailCommentsScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
items(uiState.comments, key = { it.id.toString() + it.myVote }) { comment ->
|
items(uiState.comments, key = { it.id.toString() + it.myVote }) { comment ->
|
||||||
|
val dismissState = rememberDismissState(
|
||||||
|
confirmStateChange = {
|
||||||
|
when (it) {
|
||||||
|
DismissValue.DismissedToEnd -> {
|
||||||
|
model.reduce(
|
||||||
|
UserCommentsMviModel.Intent.DownVoteComment(
|
||||||
|
comment = comment,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
DismissValue.DismissedToStart -> {
|
||||||
|
model.reduce(
|
||||||
|
UserCommentsMviModel.Intent.UpVoteComment(
|
||||||
|
comment = comment,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
|
false
|
||||||
|
},
|
||||||
|
)
|
||||||
|
var willDismissDirection: DismissDirection? by remember {
|
||||||
|
mutableStateOf(null)
|
||||||
|
}
|
||||||
|
val threshold = 0.15f
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
snapshotFlow { dismissState.offset.value }.collect {
|
||||||
|
willDismissDirection = when {
|
||||||
|
it > width * threshold -> DismissDirection.StartToEnd
|
||||||
|
it < -width * threshold -> DismissDirection.EndToStart
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LaunchedEffect(willDismissDirection) {
|
||||||
|
if (willDismissDirection != null) {
|
||||||
|
model.reduce(UserCommentsMviModel.Intent.HapticIndication)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SwipeToDismiss(
|
||||||
|
state = dismissState,
|
||||||
|
directions = setOf(
|
||||||
|
DismissDirection.StartToEnd,
|
||||||
|
DismissDirection.EndToStart,
|
||||||
|
),
|
||||||
|
dismissThresholds = {
|
||||||
|
FractionalThreshold(threshold)
|
||||||
|
},
|
||||||
|
background = {
|
||||||
|
val direction =
|
||||||
|
dismissState.dismissDirection ?: return@SwipeToDismiss
|
||||||
|
val color by animateColorAsState(
|
||||||
|
when (dismissState.targetValue) {
|
||||||
|
DismissValue.Default -> Color.Transparent
|
||||||
|
DismissValue.DismissedToEnd -> MaterialTheme.colorScheme.secondary
|
||||||
|
DismissValue.DismissedToStart,
|
||||||
|
-> MaterialTheme.colorScheme.secondary
|
||||||
|
},
|
||||||
|
)
|
||||||
|
val alignment = when (direction) {
|
||||||
|
DismissDirection.StartToEnd -> Alignment.CenterStart
|
||||||
|
DismissDirection.EndToStart -> Alignment.CenterEnd
|
||||||
|
}
|
||||||
|
val icon = when (direction) {
|
||||||
|
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
|
||||||
|
DismissDirection.EndToStart -> Icons.Default.ArrowCircleUp
|
||||||
|
}
|
||||||
|
|
||||||
|
Box(
|
||||||
|
Modifier.fillMaxSize().background(color)
|
||||||
|
.padding(horizontal = 20.dp),
|
||||||
|
contentAlignment = alignment,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) {
|
||||||
CommentCard(
|
CommentCard(
|
||||||
comment = comment,
|
comment = comment,
|
||||||
onSave = {
|
onSave = {
|
||||||
@ -104,6 +209,7 @@ internal class UserDetailCommentsScreen(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
item {
|
item {
|
||||||
if (!uiState.loading && !uiState.refreshing && uiState.canFetchMore) {
|
if (!uiState.loading && !uiState.refreshing && uiState.canFetchMore) {
|
||||||
model.reduce(UserCommentsMviModel.Intent.LoadNextPage)
|
model.reduce(UserCommentsMviModel.Intent.LoadNextPage)
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.posts
|
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.posts
|
||||||
|
|
||||||
|
import androidx.compose.animation.animateColorAsState
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@ -11,18 +13,35 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material.DismissDirection
|
||||||
|
import androidx.compose.material.DismissValue
|
||||||
import androidx.compose.material.ExperimentalMaterialApi
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
|
import androidx.compose.material.FractionalThreshold
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.SwipeToDismiss
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.ArrowCircleDown
|
||||||
|
import androidx.compose.material.icons.filled.ArrowCircleUp
|
||||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||||
import androidx.compose.material.pullrefresh.pullRefresh
|
import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
|
import androidx.compose.material.rememberDismissState
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.toSize
|
||||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||||
import cafe.adriel.voyager.core.screen.Screen
|
import cafe.adriel.voyager.core.screen.Screen
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
@ -63,8 +82,11 @@ internal class UserDetailPostsScreen(
|
|||||||
Box(
|
Box(
|
||||||
modifier = modifier.pullRefresh(pullRefreshState),
|
modifier = modifier.pullRefresh(pullRefreshState),
|
||||||
) {
|
) {
|
||||||
|
var width by remember { mutableStateOf(0f) }
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize().onGloballyPositioned {
|
||||||
|
width = it.size.toSize().width
|
||||||
|
},
|
||||||
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
|
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
@ -84,6 +106,89 @@ internal class UserDetailPostsScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
items(uiState.posts, key = { it.id.toString() + it.myVote }) { post ->
|
items(uiState.posts, key = { it.id.toString() + it.myVote }) { post ->
|
||||||
|
val dismissState = rememberDismissState(
|
||||||
|
confirmStateChange = {
|
||||||
|
when (it) {
|
||||||
|
DismissValue.DismissedToEnd -> {
|
||||||
|
model.reduce(
|
||||||
|
UserPostsMviModel.Intent.DownVotePost(
|
||||||
|
post = post,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
DismissValue.DismissedToStart -> {
|
||||||
|
model.reduce(
|
||||||
|
UserPostsMviModel.Intent.UpVotePost(
|
||||||
|
post = post,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
|
false
|
||||||
|
},
|
||||||
|
)
|
||||||
|
var willDismissDirection: DismissDirection? by remember {
|
||||||
|
mutableStateOf(null)
|
||||||
|
}
|
||||||
|
val threshold = 0.15f
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
snapshotFlow { dismissState.offset.value }.collect {
|
||||||
|
willDismissDirection = when {
|
||||||
|
it > width * threshold -> DismissDirection.StartToEnd
|
||||||
|
it < -width * threshold -> DismissDirection.EndToStart
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LaunchedEffect(willDismissDirection) {
|
||||||
|
if (willDismissDirection != null) {
|
||||||
|
model.reduce(UserPostsMviModel.Intent.HapticIndication)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SwipeToDismiss(
|
||||||
|
state = dismissState,
|
||||||
|
directions = setOf(
|
||||||
|
DismissDirection.StartToEnd,
|
||||||
|
DismissDirection.EndToStart,
|
||||||
|
),
|
||||||
|
dismissThresholds = {
|
||||||
|
FractionalThreshold(threshold)
|
||||||
|
},
|
||||||
|
background = {
|
||||||
|
val direction =
|
||||||
|
dismissState.dismissDirection ?: return@SwipeToDismiss
|
||||||
|
val color by animateColorAsState(
|
||||||
|
when (dismissState.targetValue) {
|
||||||
|
DismissValue.Default -> Color.Transparent
|
||||||
|
DismissValue.DismissedToEnd -> MaterialTheme.colorScheme.secondary
|
||||||
|
DismissValue.DismissedToStart,
|
||||||
|
-> MaterialTheme.colorScheme.secondary
|
||||||
|
},
|
||||||
|
)
|
||||||
|
val alignment = when (direction) {
|
||||||
|
DismissDirection.StartToEnd -> Alignment.CenterStart
|
||||||
|
DismissDirection.EndToStart -> Alignment.CenterEnd
|
||||||
|
}
|
||||||
|
val icon = when (direction) {
|
||||||
|
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
|
||||||
|
DismissDirection.EndToStart -> Icons.Default.ArrowCircleUp
|
||||||
|
}
|
||||||
|
|
||||||
|
Box(
|
||||||
|
Modifier.fillMaxSize().background(color)
|
||||||
|
.padding(horizontal = 20.dp),
|
||||||
|
contentAlignment = alignment,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) {
|
||||||
PostCard(
|
PostCard(
|
||||||
post = post,
|
post = post,
|
||||||
onUpVote = {
|
onUpVote = {
|
||||||
@ -122,6 +227,7 @@ internal class UserDetailPostsScreen(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
item {
|
item {
|
||||||
if (!uiState.loading && !uiState.refreshing && uiState.canFetchMore) {
|
if (!uiState.loading && !uiState.refreshing && uiState.canFetchMore) {
|
||||||
model.reduce(UserPostsMviModel.Intent.LoadNextPage)
|
model.reduce(UserPostsMviModel.Intent.LoadNextPage)
|
||||||
|
@ -17,8 +17,8 @@ import androidx.compose.material.FractionalThreshold
|
|||||||
import androidx.compose.material.Icon
|
import androidx.compose.material.Icon
|
||||||
import androidx.compose.material.SwipeToDismiss
|
import androidx.compose.material.SwipeToDismiss
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ThumbDown
|
import androidx.compose.material.icons.filled.ArrowCircleDown
|
||||||
import androidx.compose.material.icons.filled.ThumbUp
|
import androidx.compose.material.icons.filled.ArrowCircleUp
|
||||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||||
import androidx.compose.material.pullrefresh.pullRefresh
|
import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
@ -183,8 +183,8 @@ class PostListScreen : Screen {
|
|||||||
DismissDirection.EndToStart -> Alignment.CenterEnd
|
DismissDirection.EndToStart -> Alignment.CenterEnd
|
||||||
}
|
}
|
||||||
val icon = when (direction) {
|
val icon = when (direction) {
|
||||||
DismissDirection.StartToEnd -> Icons.Default.ThumbDown
|
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
|
||||||
DismissDirection.EndToStart -> Icons.Default.ThumbUp
|
DismissDirection.EndToStart -> Icons.Default.ArrowCircleUp
|
||||||
}
|
}
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user