Restore swipe to mark as read/unread

This commit is contained in:
Shinokuni 2024-09-01 22:35:59 +02:00
parent 683ca54acc
commit c31a0cde20
4 changed files with 161 additions and 33 deletions

View File

@ -1,9 +1,31 @@
package com.readrops.app.timelime package com.readrops.app.timelime
import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SwipeToDismissBox
import androidx.compose.material3.SwipeToDismissBoxValue
import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.material3.rememberSwipeToDismissBoxState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.readrops.app.R
import com.readrops.app.util.DefaultPreview import com.readrops.app.util.DefaultPreview
import com.readrops.app.util.theme.ReadropsTheme import com.readrops.app.util.theme.ReadropsTheme
import com.readrops.app.util.theme.spacing
import com.readrops.db.entities.Folder import com.readrops.db.entities.Folder
import com.readrops.db.pojo.ItemWithFeed import com.readrops.db.pojo.ItemWithFeed
import java.time.LocalDateTime import java.time.LocalDateTime
@ -14,42 +36,118 @@ enum class TimelineItemSize {
LARGE LARGE
} }
const val readAlpha = 0.6f
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun TimelineItem( fun TimelineItem(
itemWithFeed: ItemWithFeed, itemWithFeed: ItemWithFeed,
onClick: () -> Unit, onClick: () -> Unit,
onFavorite: () -> Unit, onFavorite: () -> Unit,
onShare: () -> Unit, onShare: () -> Unit,
onSetReadState: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
size: TimelineItemSize = TimelineItemSize.LARGE, size: TimelineItemSize = TimelineItemSize.LARGE
) { ) {
when (size) { val swipeState = rememberSwipeToDismissBoxState()
TimelineItemSize.COMPACT -> {
CompactTimelineItem( LaunchedEffect(swipeState.currentValue) {
itemWithFeed = itemWithFeed, if (swipeState.currentValue == SwipeToDismissBoxValue.EndToStart) {
onClick = onClick, onSetReadState()
onFavorite = onFavorite, swipeState.snapTo(SwipeToDismissBoxValue.Settled)
onShare = onShare,
modifier = modifier
)
} }
TimelineItemSize.REGULAR -> { }
RegularTimelineItem(
itemWithFeed = itemWithFeed, SwipeToDismissBox(
onClick = onClick, state = swipeState,
onFavorite = onFavorite, enableDismissFromStartToEnd = false,
onShare = onShare, backgroundContent = {
modifier = modifier val color by animateColorAsState(
targetValue = when (swipeState.targetValue) {
SwipeToDismissBoxValue.EndToStart -> MaterialTheme.colorScheme.primary
else -> Color.Transparent
},
label = "Swipe to dismiss background color"
) )
val iconColor by animateColorAsState(
targetValue = when (swipeState.targetValue) {
SwipeToDismissBoxValue.EndToStart -> MaterialTheme.colorScheme.onPrimary
else -> Color.Transparent
},
label = "Swipe to dismiss icon color"
)
Box(
modifier = Modifier.padding(
horizontal = if (size == TimelineItemSize.COMPACT) {
0.dp
} else {
MaterialTheme.spacing.shortSpacing
}
)
) {
Box(
contentAlignment = Alignment.CenterEnd,
modifier = Modifier
.fillMaxSize()
.then(
if (size == TimelineItemSize.COMPACT) {
Modifier
} else {
Modifier.clip(CardDefaults.shape)
}
)
.background(color)
) {
Icon(
painter = painterResource(
id = if (itemWithFeed.item.isRead) {
R.drawable.ic_remove_done
} else {
R.drawable.ic_done_all
}
),
contentDescription = null,
tint = iconColor,
modifier = Modifier
.minimumInteractiveComponentSize()
.padding(end = MaterialTheme.spacing.mediumSpacing)
)
}
}
} }
TimelineItemSize.LARGE -> { ) {
LargeTimelineItem( when (size) {
itemWithFeed = itemWithFeed, TimelineItemSize.COMPACT -> {
onClick = onClick, CompactTimelineItem(
onFavorite = onFavorite, itemWithFeed = itemWithFeed,
onShare = onShare, onClick = onClick,
modifier = modifier onFavorite = onFavorite,
) onShare = onShare,
modifier = modifier
)
}
TimelineItemSize.REGULAR -> {
RegularTimelineItem(
itemWithFeed = itemWithFeed,
onClick = onClick,
onFavorite = onFavorite,
onShare = onShare,
modifier = modifier
)
}
TimelineItemSize.LARGE -> {
LargeTimelineItem(
itemWithFeed = itemWithFeed,
onClick = onClick,
onFavorite = onFavorite,
onShare = onShare,
modifier = modifier
)
}
} }
} }
} }

View File

@ -3,6 +3,7 @@ package com.readrops.app.timelime
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.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@ -24,6 +25,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@ -94,11 +97,18 @@ fun CompactTimelineItem(
onShare: () -> Unit, onShare: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val containerColor = MaterialTheme.colorScheme.background
Surface( Surface(
color = MaterialTheme.colorScheme.surfaceVariant, color = MaterialTheme.colorScheme.surfaceVariant,
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.alpha(if (itemWithFeed.item.isRead) 0.6f else 1f) .drawBehind {
// if some alpha is applied to the card, the swipe to dismiss background appears behind it
// so we draw a rect with the current screen background color behind the card but in front of the dismiss background
drawRect(containerColor)
}
.alpha(if (itemWithFeed.item.isRead) readAlpha else 1f)
.clickable { onClick() } .clickable { onClick() }
) { ) {
Column( Column(
@ -147,7 +157,8 @@ fun LargeTimelineItem(
itemWithFeed = itemWithFeed, itemWithFeed = itemWithFeed,
onClick = onClick, onClick = onClick,
onFavorite = onFavorite, onFavorite = onFavorite,
onShare = onShare onShare = onShare,
modifier = modifier
) )
} else { } else {
TimelineItemContainer( TimelineItemContainer(
@ -222,13 +233,24 @@ fun TimelineItemContainer(
isRead: Boolean, isRead: Boolean,
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
padding: PaddingValues = PaddingValues(horizontal = MaterialTheme.spacing.shortSpacing),
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
val containerColor = MaterialTheme.colorScheme.background
Card( Card(
modifier = modifier modifier = modifier
.padding(horizontal = MaterialTheme.spacing.shortSpacing) .padding(padding)
.fillMaxWidth() .fillMaxWidth()
.alpha(if (isRead) 0.6f else 1f) .drawBehind {
// if some alpha is applied to the card, the swipe to dismiss background appears behind it
// so we draw a rect with the current screen background color behind the card but in front of the dismiss background
drawRoundRect(
color = containerColor,
cornerRadius = CornerRadius(12.dp.toPx())
)
}
.alpha(if (isRead) readAlpha else 1f)
.clickable { onClick() } .clickable { onClick() }
) { ) {
content() content()

View File

@ -297,15 +297,21 @@ class TimelineScreenModel(
fun setItemRead(item: Item) { fun setItemRead(item: Item) {
item.isRead = true item.isRead = true
updateItemReadState(item)
}
private fun updateItemReadState(item: Item) {
screenModelScope.launch(dispatcher) { screenModelScope.launch(dispatcher) {
repository?.setItemReadState(item) repository?.setItemReadState(item)
} }
} }
fun updateItemReadState(item: Item) {
screenModelScope.launch(dispatcher) {
with(item) {
isRead = !isRead
repository?.setItemReadState(this)
}
}
}
fun updateStarState(item: Item) { fun updateStarState(item: Item) {
screenModelScope.launch(dispatcher) { screenModelScope.launch(dispatcher) {
with(item) { with(item) {

View File

@ -403,9 +403,11 @@ object TimelineTab : Tab {
context context
) )
}, },
onSetReadState = {
screenModel.updateItemReadState(itemWithFeed.item)
},
size = state.itemSize size = state.itemSize
) )
} }
} }
} }