mirror of https://github.com/readrops/Readrops.git
Restore swipe to mark as read/unread
This commit is contained in:
parent
683ca54acc
commit
c31a0cde20
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -403,9 +403,11 @@ object TimelineTab : Tab {
|
||||||
context
|
context
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
onSetReadState = {
|
||||||
|
screenModel.updateItemReadState(itemWithFeed.item)
|
||||||
|
},
|
||||||
size = state.itemSize
|
size = state.itemSize
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue