From 01f63bf8b8f10dc7c2c12ca0812fc920188b55c2 Mon Sep 17 00:00:00 2001 From: Diego Beraldin Date: Tue, 19 Sep 2023 21:46:18 +0200 Subject: [PATCH] fix(common-ui): swipe actions visibility in comment cards --- .../core/commonui/components/CommentCard.kt | 4 +- .../core/commonui/components/SwipeableCard.kt | 31 +- .../commonui/postdetail/PostDetailScreen.kt | 290 +++++++++--------- .../postdetail/PostDetailViewModel.kt | 2 + .../feature/home/postlist/PostListScreen.kt | 14 +- 5 files changed, 167 insertions(+), 174 deletions(-) diff --git a/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/components/CommentCard.kt b/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/components/CommentCard.kt index 2e499fbde..d019e1e7c 100644 --- a/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/components/CommentCard.kt +++ b/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/components/CommentCard.kt @@ -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( diff --git a/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/components/SwipeableCard.kt b/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/components/SwipeableCard.kt index 7ed758df2..e615d1d22 100644 --- a/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/components/SwipeableCard.kt +++ b/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/components/SwipeableCard.kt @@ -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) diff --git a/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/postdetail/PostDetailScreen.kt b/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/postdetail/PostDetailScreen.kt index 287ba41cb..d03c31ea7 100644 --- a/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/postdetail/PostDetailScreen.kt +++ b/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/postdetail/PostDetailScreen.kt @@ -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)) } } } diff --git a/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/postdetail/PostDetailViewModel.kt b/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/postdetail/PostDetailViewModel.kt index c0ee9362d..f586af006 100644 --- a/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/postdetail/PostDetailViewModel.kt +++ b/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/postdetail/PostDetailViewModel.kt @@ -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 { diff --git a/feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/home/postlist/PostListScreen.kt b/feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/home/postlist/PostListScreen.kt index 28e430ded..d4921f4ee 100644 --- a/feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/home/postlist/PostListScreen.kt +++ b/feature-home/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/home/postlist/PostListScreen.kt @@ -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