fix(common-ui): swipe actions visibility in comment cards

This commit is contained in:
Diego Beraldin 2023-09-19 21:46:18 +02:00
parent 79afe2e40e
commit 01f63bf8b8
5 changed files with 167 additions and 174 deletions

View File

@ -47,7 +47,9 @@ fun CommentCard(
fontScale = fontScale,
),
) {
Column {
Column(
modifier = Modifier.background(MaterialTheme.colorScheme.surface)
) {
var commentHeight by remember { mutableStateOf(0f) }
val barWidth = 2.dp
val barColor = themeRepository.getCommentBarColor(

View File

@ -24,6 +24,9 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.toSize
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
@OptIn(ExperimentalMaterialApi::class)
@Composable
@ -59,23 +62,20 @@ fun SwipeableCard(
false
},
)
var willDismissDirection: DismissDirection? by remember {
mutableStateOf(null)
}
val threshold = 0.15f
LaunchedEffect(Unit) {
snapshotFlow { dismissState.offset.value }.collect {
willDismissDirection = when {
val threshold = 0.25f
LaunchedEffect(dismissState) {
snapshotFlow { dismissState.offset.value }.map {
when {
it > width * threshold -> DismissDirection.StartToEnd
it < -width * threshold -> DismissDirection.EndToStart
else -> null
}
}
}
LaunchedEffect(willDismissDirection) {
if (willDismissDirection != null) {
onGestureBegin()
}
}.onEach { willDismissDirection ->
if (willDismissDirection != null) {
onGestureBegin()
}
}.launchIn(this)
}
SwipeToDismiss(
modifier = modifier.onGloballyPositioned {
@ -97,7 +97,10 @@ fun SwipeableCard(
DismissDirection.EndToStart -> Alignment.CenterEnd
}
Box(
Modifier.fillMaxSize().background(bgColor).padding(horizontal = 20.dp),
Modifier
.fillMaxSize()
.background(bgColor)
.padding(horizontal = 20.dp),
contentAlignment = alignment,
) {
swipeContent(direction)

View File

@ -7,9 +7,9 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.background
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.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
@ -223,13 +223,13 @@ class PostDetailScreen(
model.reduce(PostDetailMviModel.Intent.Refresh)
})
Box(
modifier = Modifier
.padding(padding)
modifier = Modifier.padding(padding)
.nestedScroll(scrollBehavior.nestedScrollConnection)
.nestedScroll(fabNestedScrollConnection)
.pullRefresh(pullRefreshState),
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
) {
item {
@ -267,16 +267,14 @@ class PostDetailScreen(
)
},
)
PostCardImage(
imageUrl = statePost.thumbnailUrl.orEmpty(),
PostCardImage(imageUrl = statePost.thumbnailUrl.orEmpty(),
onImageClick = {
navigator?.push(
ZoomableImageScreen(
url = statePost.thumbnailUrl.orEmpty()
),
)
}
)
})
PostCardBody(
text = statePost.text,
)
@ -286,8 +284,7 @@ class PostDetailScreen(
it?.contains("pictrs/image") == false
}.orEmpty(),
)
PostCardFooter(
comments = statePost.comments,
PostCardFooter(comments = statePost.comments,
score = statePost.score,
upVoted = statePost.myVote > 0,
downVoted = statePost.myVote < 0,
@ -335,121 +332,130 @@ class PostDetailScreen(
)
bottomSheetNavigator.show(
CreatePostScreen(
editedPost = post,
editedPost = statePost,
)
)
}
else -> model.reduce(PostDetailMviModel.Intent.SharePost)
}
}
)
})
}
}
}
itemsIndexed(uiState.comments) { idx, comment ->
Column {
SwipeableCard(
modifier = Modifier.fillMaxWidth(),
enabled = uiState.swipeActionsEnabled,
backgroundColor = {
when (it) {
DismissValue.DismissedToStart -> MaterialTheme.colorScheme.secondary
DismissValue.DismissedToEnd -> MaterialTheme.colorScheme.tertiary
DismissValue.Default -> Color.Transparent
SwipeableCard(
modifier = Modifier.fillMaxWidth(),
enabled = uiState.swipeActionsEnabled,
backgroundColor = {
when (it) {
DismissValue.DismissedToStart -> MaterialTheme.colorScheme.secondary
DismissValue.DismissedToEnd -> MaterialTheme.colorScheme.tertiary
DismissValue.Default -> Color.Transparent
}
},
onGestureBegin = {
model.reduce(PostDetailMviModel.Intent.HapticIndication)
},
onDismissToStart = {
model.reduce(
PostDetailMviModel.Intent.UpVoteComment(idx),
)
},
onDismissToEnd = {
model.reduce(
PostDetailMviModel.Intent.DownVoteComment(idx),
)
},
swipeContent = { direction ->
val icon = when (direction) {
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.onTertiary
}
},
onGestureBegin = {
model.reduce(PostDetailMviModel.Intent.HapticIndication)
},
onDismissToStart = {
model.reduce(
PostDetailMviModel.Intent.UpVoteComment(idx),
)
},
onDismissToEnd = {
model.reduce(
PostDetailMviModel.Intent.DownVoteComment(idx),
)
},
swipeContent = { direction ->
val icon = when (direction) {
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown
DismissDirection.EndToStart -> Icons.Default.ArrowCircleUp
direction == DismissDirection.StartToEnd -> {
Modifier.background(
color = MaterialTheme.colorScheme.onTertiary,
shape = CircleShape,
) to MaterialTheme.colorScheme.tertiary
}
val (iconModifier, iconTint) = when {
direction == DismissDirection.StartToEnd && statePost.myVote < 0 -> {
Modifier.background(
color = Color.Transparent,
shape = CircleShape,
) to MaterialTheme.colorScheme.onTertiary
}
direction == DismissDirection.StartToEnd -> {
Modifier.background(
color = MaterialTheme.colorScheme.onTertiary,
shape = CircleShape,
) to MaterialTheme.colorScheme.tertiary
}
direction == DismissDirection.EndToStart && comment.myVote > 0 -> {
Modifier.background(
color = Color.Transparent,
shape = CircleShape,
) to MaterialTheme.colorScheme.onSecondary
}
direction == DismissDirection.EndToStart && statePost.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
}
}
Icon(
modifier = iconModifier,
imageVector = icon,
contentDescription = null,
tint = iconTint,
)
},
content = {
CommentCard(comment = comment, options = buildList {
if (comment.creator?.id == uiState.currentUserId) {
add(stringResource(MR.strings.comment_action_delete))
}
}, onUpVote = {
model.reduce(
PostDetailMviModel.Intent.UpVoteComment(
index = idx,
feedback = true,
),
)
}, onDownVote = {
model.reduce(
PostDetailMviModel.Intent.DownVoteComment(
index = idx,
feedback = true,
),
)
}, onSave = {
model.reduce(
PostDetailMviModel.Intent.SaveComment(
index = idx,
feedback = true,
),
)
}, onReply = {
val screen = CreateCommentScreen(
originalPost = statePost,
originalComment = comment,
)
notificationCenter.addObserver(
{
model.reduce(PostDetailMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.CommentCreated
)
bottomSheetNavigator.show(screen)
}, onOptionSelected = { idx ->
when (idx) {
1 -> model.reduce(
PostDetailMviModel.Intent.DeleteComment(
comment.id
)
)
else -> {
Modifier.background(
color = MaterialTheme.colorScheme.onSecondary,
shape = CircleShape,
) to MaterialTheme.colorScheme.secondary
}
}
Icon(
modifier = iconModifier,
imageVector = icon,
contentDescription = null,
tint = iconTint,
)
},
content = {
CommentCard(
comment = comment,
options = buildList {
if (comment.creator?.id == uiState.currentUserId) {
add(stringResource(MR.strings.comment_action_delete))
}
},
onUpVote = {
model.reduce(
PostDetailMviModel.Intent.UpVoteComment(
index = idx,
feedback = true,
),
)
},
onDownVote = {
model.reduce(
PostDetailMviModel.Intent.DownVoteComment(
index = idx,
feedback = true,
),
)
},
onSave = {
model.reduce(
PostDetailMviModel.Intent.SaveComment(
index = idx,
feedback = true,
),
)
},
onReply = {
val screen = CreateCommentScreen(
originalPost = statePost,
originalComment = comment,
)
notificationCenter.addObserver(
{
model.reduce(PostDetailMviModel.Intent.Refresh)
@ -457,54 +463,34 @@ class PostDetailScreen(
key,
NotificationCenterContractKeys.CommentCreated
)
bottomSheetNavigator.show(screen)
},
onOptionSelected = { idx ->
when (idx) {
1 -> model.reduce(
PostDetailMviModel.Intent.DeleteComment(
comment.id
)
bottomSheetNavigator.show(
CreateCommentScreen(
editedComment = comment,
)
else -> {
notificationCenter.addObserver(
{
model.reduce(PostDetailMviModel.Intent.Refresh)
},
key,
NotificationCenterContractKeys.CommentCreated
)
bottomSheetNavigator.show(
CreateCommentScreen(
editedComment = comment,
)
)
}
}
}
)
},
)
if ((comment.comments
?: 0) > 0 && comment.depth == CommentRepository.MAX_COMMENT_DEPTH && (idx < uiState.comments.lastIndex && uiState.comments[idx + 1].depth < comment.depth)
) {
Row {
Spacer(modifier = Modifier.weight(1f))
Button(onClick = {
model.reduce(
PostDetailMviModel.Intent.FetchMoreComments(
parentId = comment.id
)
)
}) {
Text(
text = stringResource(MR.strings.post_detail_load_more_comments),
style = MaterialTheme.typography.labelMedium,
)
}
}
Spacer(modifier = Modifier.weight(1f))
})
},
)
if ((comment.comments
?: 0) > 0 && comment.depth == CommentRepository.MAX_COMMENT_DEPTH && (idx < uiState.comments.lastIndex && uiState.comments[idx + 1].depth < comment.depth)
) {
Row {
Spacer(modifier = Modifier.weight(1f))
Button(onClick = {
model.reduce(
PostDetailMviModel.Intent.FetchMoreComments(
parentId = comment.id
)
)
}) {
Text(
text = stringResource(MR.strings.post_detail_load_more_comments),
style = MaterialTheme.typography.labelMedium,
)
}
Spacer(modifier = Modifier.weight(1f))
}
}
}

View File

@ -41,9 +41,11 @@ class PostDetailViewModel(
override fun onStarted() {
mvi.onStarted()
val sortType = keyStore[KeyStoreKeys.DefaultCommentSortType, 3].toSortType()
val swipeActionsEnabled = keyStore[KeyStoreKeys.EnableSwipeActions, true]
mvi.updateState {
it.copy(
sortType = sortType,
swipeActionsEnabled = swipeActionsEnabled,
)
}
mvi.scope?.launch {

View File

@ -164,6 +164,13 @@ class PostListScreen : Screen {
SwipeableCard(
modifier = Modifier.fillMaxWidth(),
enabled = uiState.swipeActionsEnabled,
backgroundColor = {
when (it) {
DismissValue.DismissedToStart -> MaterialTheme.colorScheme.secondary
DismissValue.DismissedToEnd -> MaterialTheme.colorScheme.tertiary
DismissValue.Default -> Color.Transparent
}
},
onGestureBegin = {
model.reduce(PostListMviModel.Intent.HapticIndication)
},
@ -173,13 +180,6 @@ class PostListScreen : Screen {
onDismissToEnd = {
model.reduce(PostListMviModel.Intent.DownVotePost(idx))
},
backgroundColor = {
when (it) {
DismissValue.DismissedToStart -> MaterialTheme.colorScheme.secondary
DismissValue.DismissedToEnd -> MaterialTheme.colorScheme.tertiary
else -> Color.Transparent
}
},
swipeContent = { direction ->
val icon = when (direction) {
DismissDirection.StartToEnd -> Icons.Default.ArrowCircleDown