Add mark all as read feature
This commit is contained in:
parent
f95108fa67
commit
aaf032332b
|
@ -6,46 +6,68 @@ import kotlinx.coroutines.flow.Flow
|
|||
import me.ash.reader.data.entity.Article
|
||||
import me.ash.reader.data.entity.ArticleWithFeed
|
||||
import me.ash.reader.data.entity.ImportantCount
|
||||
import java.util.*
|
||||
|
||||
@Dao
|
||||
interface ArticleDao {
|
||||
@Query(
|
||||
"""
|
||||
UPDATE article SET isUnread = 0
|
||||
UPDATE article SET isUnread = :isUnread
|
||||
WHERE accountId = :accountId
|
||||
AND isUnread = 1
|
||||
AND date <= :before
|
||||
AND date < :before
|
||||
"""
|
||||
)
|
||||
suspend fun markAllAsRead(accountId: Int, before: Long)
|
||||
suspend fun markAllAsRead(
|
||||
accountId: Int,
|
||||
isUnread: Boolean,
|
||||
before: Date,
|
||||
)
|
||||
|
||||
@Query(
|
||||
"""
|
||||
UPDATE article SET isUnread = 0
|
||||
WHERE accountId = :accountId
|
||||
AND isUnread = 1
|
||||
AND date <= :before
|
||||
AND feedId = :feedId
|
||||
UPDATE article SET isUnread = :isUnread
|
||||
WHERE feedId IN (
|
||||
SELECT id FROM feed
|
||||
WHERE groupId = :groupId
|
||||
)
|
||||
AND accountId = :accountId
|
||||
AND date < :before
|
||||
"""
|
||||
)
|
||||
suspend fun markAllAsReadByFeedId(accountId: Int, before: Long, feedId: String)
|
||||
//
|
||||
// @Query(
|
||||
// """
|
||||
// UPDATE article SET isUnread = 0
|
||||
// WHERE accountId = :accountId
|
||||
// AND isUnread = 1
|
||||
// AND date <= :before
|
||||
// AND feedId = :feedId
|
||||
//
|
||||
// SELECT * FROM `group` AS a, feed AS b, article AS c
|
||||
// WHERE a.accountId = :accountId
|
||||
// AND a.id = b.groupId
|
||||
// AND b.groupId = :groupId
|
||||
// AND c.feedId = b.id
|
||||
// """
|
||||
// )
|
||||
// suspend fun markAllAsReadByGroupId(accountId: Int, before: Long, groupId: String)
|
||||
suspend fun markAllAsReadByGroupId(
|
||||
accountId: Int,
|
||||
groupId: String,
|
||||
isUnread: Boolean,
|
||||
before: Date,
|
||||
)
|
||||
|
||||
@Query(
|
||||
"""
|
||||
UPDATE article SET isUnread = :isUnread
|
||||
WHERE feedId = :feedId
|
||||
AND accountId = :accountId
|
||||
AND date < :before
|
||||
"""
|
||||
)
|
||||
suspend fun markAllAsReadByFeedId(
|
||||
accountId: Int,
|
||||
feedId: String,
|
||||
isUnread: Boolean,
|
||||
before: Date,
|
||||
)
|
||||
|
||||
@Query(
|
||||
"""
|
||||
UPDATE article SET isUnread = :isUnread
|
||||
WHERE id = :articleId
|
||||
AND accountId = :accountId
|
||||
"""
|
||||
)
|
||||
suspend fun markAsReadByArticleId(
|
||||
accountId: Int,
|
||||
articleId: String,
|
||||
isUnread: Boolean,
|
||||
)
|
||||
|
||||
@Query(
|
||||
"""
|
||||
|
|
|
@ -38,6 +38,14 @@ abstract class AbstractRssRepository constructor(
|
|||
|
||||
abstract suspend fun sync(coroutineWorker: CoroutineWorker): ListenableWorker.Result
|
||||
|
||||
abstract suspend fun markAsRead(
|
||||
groupId: String?,
|
||||
feedId: String?,
|
||||
articleId: String?,
|
||||
before: Date?,
|
||||
isUnread: Boolean,
|
||||
)
|
||||
|
||||
fun doSync() {
|
||||
workManager.enqueueUniquePeriodicWork(
|
||||
SyncWorker.WORK_NAME,
|
||||
|
|
|
@ -125,6 +125,40 @@ class LocalRssRepository @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun markAsRead(
|
||||
groupId: String?,
|
||||
feedId: String?,
|
||||
articleId: String?,
|
||||
before: Date?,
|
||||
isUnread: Boolean,
|
||||
) {
|
||||
val accountId = context.currentAccountId
|
||||
when {
|
||||
groupId != null -> {
|
||||
articleDao.markAllAsReadByGroupId(
|
||||
accountId = accountId,
|
||||
groupId = groupId,
|
||||
isUnread = isUnread,
|
||||
before = before ?: Date(Long.MAX_VALUE)
|
||||
)
|
||||
}
|
||||
feedId != null -> {
|
||||
articleDao.markAllAsReadByFeedId(
|
||||
accountId = accountId,
|
||||
feedId = feedId,
|
||||
isUnread = isUnread,
|
||||
before = before ?: Date(Long.MAX_VALUE)
|
||||
)
|
||||
}
|
||||
articleId != null -> {
|
||||
articleDao.markAsReadByArticleId(accountId, articleId, isUnread)
|
||||
}
|
||||
else -> {
|
||||
articleDao.markAllAsRead(accountId, isUnread, before ?: Date(Long.MAX_VALUE))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class ArticleNotify(
|
||||
val articles: List<Article>,
|
||||
val isNotify: Boolean,
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package me.ash.reader.ui.component
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.*
|
||||
import androidx.compose.ui.window.Popup
|
||||
import androidx.compose.ui.window.PopupPositionProvider
|
||||
import androidx.compose.ui.window.PopupProperties
|
||||
import com.google.accompanist.insets.LocalWindowInsets
|
||||
|
||||
@Composable
|
||||
fun AnimatedPopup(
|
||||
visible: Boolean = false,
|
||||
absoluteY: Dp = Dp.Hairline,
|
||||
absoluteX: Dp = Dp.Hairline,
|
||||
onDismissRequest: () -> Unit = {},
|
||||
content: @Composable () -> Unit = {},
|
||||
) {
|
||||
val density = LocalDensity.current
|
||||
val insets = LocalWindowInsets.current
|
||||
|
||||
Popup(
|
||||
properties = PopupProperties(focusable = visible),
|
||||
onDismissRequest = onDismissRequest,
|
||||
popupPositionProvider = object : PopupPositionProvider {
|
||||
override fun calculatePosition(
|
||||
anchorBounds: IntRect,
|
||||
windowSize: IntSize,
|
||||
layoutDirection: LayoutDirection,
|
||||
popupContentSize: IntSize
|
||||
): IntOffset {
|
||||
return IntOffset(
|
||||
x = with(density) { (absoluteX).roundToPx() },
|
||||
y = with(density) { (absoluteY).roundToPx() + insets.statusBars.top }
|
||||
)
|
||||
}
|
||||
},
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = visible,
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = fadeOut() + shrinkVertically(),
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,11 +3,13 @@ package me.ash.reader.ui.component
|
|||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.BaselineShift
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
|
@ -28,8 +30,11 @@ fun DisplayText(
|
|||
)
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.height(44.dp),
|
||||
text = text,
|
||||
style = MaterialTheme.typography.displaySmall,
|
||||
style = MaterialTheme.typography.displaySmall.copy(
|
||||
baselineShift = BaselineShift.Superscript
|
||||
),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
|
@ -40,8 +45,11 @@ fun DisplayText(
|
|||
exit = fadeOut() + shrinkVertically(),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.height(16.dp),
|
||||
text = desc,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
style = MaterialTheme.typography.labelMedium.copy(
|
||||
baselineShift = BaselineShift.Superscript
|
||||
),
|
||||
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
|
|
|
@ -70,28 +70,30 @@ fun Modifier.combinedFeedbackClickable(
|
|||
isSound: Boolean? = false,
|
||||
onPressDown: (() -> Unit)? = null,
|
||||
onPressUp: (() -> Unit)? = null,
|
||||
onTap: (() -> Unit)? = null,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
onDoubleClick: (() -> Unit)? = null,
|
||||
onClick: (() -> Unit)? = null,
|
||||
): Modifier {
|
||||
val view = LocalView.current
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
return if (onPressDown != null || onPressUp != null) {
|
||||
return if (onPressDown != null || onPressUp != null || onTap != null) {
|
||||
indication(interactionSource, LocalIndication.current)
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
onPress = { offset ->
|
||||
onPressDown?.let {
|
||||
it()
|
||||
if (isHaptic == true) view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||
val press = PressInteraction.Press(offset)
|
||||
interactionSource.emit(press)
|
||||
tryAwaitRelease()
|
||||
onPressUp?.invoke()
|
||||
interactionSource.emit(PressInteraction.Release(press))
|
||||
it()
|
||||
}
|
||||
},
|
||||
onTap = {
|
||||
onPressUp?.let {
|
||||
onTap?.let {
|
||||
if (isHaptic == true) view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||
if (isSound == true) view.playSoundEffect(SoundEffectConstants.CLICK)
|
||||
it()
|
||||
|
|
|
@ -64,7 +64,7 @@ fun FeedItem(
|
|||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.clip(CircleShape)
|
||||
.background(MaterialTheme.colorScheme.outline),
|
||||
.background(MaterialTheme.colorScheme.outline.copy(alpha = 0.2f))
|
||||
) {}
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 12.dp, end = 6.dp),
|
||||
|
|
|
@ -109,7 +109,6 @@ fun FeedsPage(
|
|||
title = {},
|
||||
navigationIcon = {
|
||||
FeedbackIconButton(
|
||||
isHaptic = false,
|
||||
modifier = Modifier.size(20.dp),
|
||||
imageVector = Icons.Outlined.Settings,
|
||||
contentDescription = stringResource(R.string.settings),
|
||||
|
@ -120,7 +119,6 @@ fun FeedsPage(
|
|||
},
|
||||
actions = {
|
||||
FeedbackIconButton(
|
||||
isHaptic = false,
|
||||
modifier = Modifier.rotate(if (isSyncing) angle else 0f),
|
||||
imageVector = Icons.Rounded.Refresh,
|
||||
contentDescription = stringResource(R.string.refresh),
|
||||
|
@ -131,7 +129,6 @@ fun FeedsPage(
|
|||
}
|
||||
}
|
||||
FeedbackIconButton(
|
||||
isHaptic = false,
|
||||
imageVector = Icons.Rounded.Add,
|
||||
contentDescription = stringResource(R.string.subscribe),
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
|
|
|
@ -3,8 +3,10 @@ package me.ash.reader.ui.page.home.flow
|
|||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.ArrowBack
|
||||
|
@ -16,7 +18,6 @@ import androidx.compose.material3.Scaffold
|
|||
import androidx.compose.material3.SmallTopAppBar
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
@ -71,28 +72,13 @@ fun FlowPage(
|
|||
)
|
||||
}
|
||||
|
||||
// LaunchedEffect(viewState.listState.isScrollInProgress) {
|
||||
// Log.i("RLog", "isScrollInProgress: ${viewState.listState.isScrollInProgress}")
|
||||
// if (viewState.listState.isScrollInProgress) {
|
||||
// Log.i("RLog", "isScrollInProgress: ${true}")
|
||||
// markAsRead = false
|
||||
// }
|
||||
// }
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.pointerInput(markAsRead) {
|
||||
detectTapGestures {
|
||||
markAsRead = false
|
||||
}
|
||||
},
|
||||
modifier = Modifier.background(MaterialTheme.colorScheme.surface),
|
||||
topBar = {
|
||||
SmallTopAppBar(
|
||||
title = {},
|
||||
navigationIcon = {
|
||||
FeedbackIconButton(
|
||||
isHaptic = false,
|
||||
imageVector = Icons.Rounded.ArrowBack,
|
||||
contentDescription = stringResource(R.string.back),
|
||||
tint = MaterialTheme.colorScheme.onSurface
|
||||
|
@ -107,7 +93,6 @@ fun FlowPage(
|
|||
exit = fadeOut() + shrinkVertically(),
|
||||
) {
|
||||
FeedbackIconButton(
|
||||
isHaptic = false,
|
||||
imageVector = Icons.Rounded.DoneAll,
|
||||
contentDescription = stringResource(R.string.mark_all_as_read),
|
||||
tint = if (markAsRead) {
|
||||
|
@ -123,7 +108,6 @@ fun FlowPage(
|
|||
}
|
||||
}
|
||||
FeedbackIconButton(
|
||||
isHaptic = false,
|
||||
imageVector = Icons.Rounded.Search,
|
||||
contentDescription = stringResource(R.string.search),
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
|
@ -162,17 +146,30 @@ fun FlowPage(
|
|||
enter = fadeIn() + expandVertically(),
|
||||
exit = fadeOut() + shrinkVertically(),
|
||||
) {
|
||||
Column {
|
||||
MarkAsReadBar()
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
}
|
||||
Spacer(modifier = Modifier.height((56 + 24 + 10).dp))
|
||||
}
|
||||
MarkAsReadBar(
|
||||
visible = markAsRead,
|
||||
absoluteY = if (isSyncing) (4 + 16 + 180).dp else 180.dp,
|
||||
onDismissRequest = {
|
||||
markAsRead = false
|
||||
},
|
||||
) {
|
||||
markAsRead = false
|
||||
flowViewModel.dispatch(
|
||||
FlowViewAction.MarkAsRead(
|
||||
groupId = filterState.group?.id,
|
||||
feedId = filterState.feed?.id,
|
||||
articleId = null,
|
||||
markAsReadBefore = it,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
generateArticleList(
|
||||
context = context,
|
||||
pagingItems = pagingItems,
|
||||
) {
|
||||
markAsRead = false
|
||||
onItemClick(it)
|
||||
}
|
||||
item {
|
||||
|
@ -190,14 +187,7 @@ fun FlowPage(
|
|||
.height(60.dp)
|
||||
.fillMaxWidth(),
|
||||
filter = filterState.filter,
|
||||
filterOnClick = {
|
||||
markAsRead = false
|
||||
onFilterChange(
|
||||
filterState.copy(
|
||||
filter = it
|
||||
)
|
||||
)
|
||||
},
|
||||
filterOnClick = { onFilterChange(filterState.copy(filter = it)) },
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -14,6 +14,7 @@ import kotlinx.coroutines.launch
|
|||
import me.ash.reader.data.entity.ArticleWithFeed
|
||||
import me.ash.reader.data.repository.RssRepository
|
||||
import me.ash.reader.ui.page.home.FilterState
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
|
@ -28,14 +29,11 @@ class FlowViewModel @Inject constructor(
|
|||
is FlowViewAction.FetchData -> fetchData(action.filterState)
|
||||
is FlowViewAction.ChangeRefreshing -> changeRefreshing(action.isRefreshing)
|
||||
is FlowViewAction.ScrollToItem -> scrollToItem(action.index)
|
||||
is FlowViewAction.PeekSyncWork -> peekSyncWork()
|
||||
}
|
||||
}
|
||||
|
||||
private fun peekSyncWork() {
|
||||
_viewState.update {
|
||||
it.copy(
|
||||
syncWorkInfo = rssRepository.get().peekWork()
|
||||
is FlowViewAction.MarkAsRead -> markAsRead(
|
||||
action.groupId,
|
||||
action.feedId,
|
||||
action.articleId,
|
||||
action.markAsReadBefore,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +74,37 @@ class FlowViewModel @Inject constructor(
|
|||
it.copy(isRefreshing = isRefreshing)
|
||||
}
|
||||
}
|
||||
|
||||
private fun markAsRead(
|
||||
groupId: String?,
|
||||
feedId: String?,
|
||||
articleId: String?,
|
||||
markAsReadBefore: MarkAsReadBefore
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
rssRepository.get().markAsRead(
|
||||
groupId = groupId,
|
||||
feedId = feedId,
|
||||
articleId = articleId,
|
||||
before = when (markAsReadBefore) {
|
||||
MarkAsReadBefore.All -> null
|
||||
MarkAsReadBefore.OneDay -> Calendar.getInstance().apply {
|
||||
time = Date()
|
||||
add(Calendar.DAY_OF_MONTH, -1)
|
||||
}.time
|
||||
MarkAsReadBefore.ThreeDays -> Calendar.getInstance().apply {
|
||||
time = Date()
|
||||
add(Calendar.DAY_OF_MONTH, -3)
|
||||
}.time
|
||||
MarkAsReadBefore.SevenDays -> Calendar.getInstance().apply {
|
||||
time = Date()
|
||||
add(Calendar.DAY_OF_MONTH, -7)
|
||||
}.time
|
||||
},
|
||||
isUnread = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class ArticleViewState(
|
||||
|
@ -99,5 +128,17 @@ sealed class FlowViewAction {
|
|||
val index: Int
|
||||
) : FlowViewAction()
|
||||
|
||||
object PeekSyncWork : FlowViewAction()
|
||||
data class MarkAsRead(
|
||||
val groupId: String?,
|
||||
val feedId: String?,
|
||||
val articleId: String?,
|
||||
val markAsReadBefore: MarkAsReadBefore
|
||||
) : FlowViewAction()
|
||||
}
|
||||
|
||||
enum class MarkAsReadBefore {
|
||||
SevenDays,
|
||||
ThreeDays,
|
||||
OneDay,
|
||||
All,
|
||||
}
|
|
@ -1,5 +1,10 @@
|
|||
package me.ash.reader.ui.page.home.flow
|
||||
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.SoundEffectConstants
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
|
@ -7,38 +12,68 @@ import androidx.compose.material3.MaterialTheme
|
|||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.ui.component.AnimatedPopup
|
||||
|
||||
@Composable
|
||||
fun MarkAsReadBar() {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 24.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
fun MarkAsReadBar(
|
||||
visible: Boolean = false,
|
||||
absoluteY: Dp = Dp.Hairline,
|
||||
onDismissRequest: () -> Unit = {},
|
||||
onItemClick: (MarkAsReadBefore) -> Unit = {},
|
||||
) {
|
||||
val animated = remember { Animatable(absoluteY.value) }
|
||||
|
||||
LaunchedEffect(absoluteY) {
|
||||
animated.animateTo(absoluteY.value, spring(stiffness = Spring.StiffnessMediumLow))
|
||||
}
|
||||
|
||||
AnimatedPopup(
|
||||
visible = visible,
|
||||
absoluteY = animated.value.dp,
|
||||
onDismissRequest = onDismissRequest,
|
||||
) {
|
||||
MarkAsReadBarItem(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = stringResource(R.string.seven_days),
|
||||
)
|
||||
MarkAsReadBarItem(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = stringResource(R.string.three_days),
|
||||
)
|
||||
MarkAsReadBarItem(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = stringResource(R.string.one_day),
|
||||
)
|
||||
MarkAsReadBarItem(
|
||||
modifier = Modifier.weight(2.5f),
|
||||
text = stringResource(R.string.mark_all_as_read),
|
||||
isPrimary = true,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 24.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
MarkAsReadBarItem(
|
||||
modifier = Modifier.width(56.dp),
|
||||
text = stringResource(R.string.seven_days),
|
||||
) {
|
||||
onItemClick(MarkAsReadBefore.SevenDays)
|
||||
}
|
||||
MarkAsReadBarItem(
|
||||
modifier = Modifier.width(56.dp),
|
||||
text = stringResource(R.string.three_days),
|
||||
) {
|
||||
onItemClick(MarkAsReadBefore.ThreeDays)
|
||||
}
|
||||
MarkAsReadBarItem(
|
||||
modifier = Modifier.width(56.dp),
|
||||
text = stringResource(R.string.one_day),
|
||||
) {
|
||||
onItemClick(MarkAsReadBefore.OneDay)
|
||||
}
|
||||
MarkAsReadBarItem(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = stringResource(R.string.mark_all_as_read),
|
||||
isPrimary = true,
|
||||
) {
|
||||
onItemClick(MarkAsReadBefore.All)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,12 +82,19 @@ fun MarkAsReadBarItem(
|
|||
modifier: Modifier = Modifier,
|
||||
text: String,
|
||||
isPrimary: Boolean = false,
|
||||
onClick: () -> Unit = {},
|
||||
) {
|
||||
val view = LocalView.current
|
||||
|
||||
Surface(
|
||||
modifier = modifier
|
||||
.height(52.dp)
|
||||
.height(56.dp)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.clickable { },
|
||||
.clickable {
|
||||
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||
view.playSoundEffect(SoundEffectConstants.CLICK)
|
||||
onClick()
|
||||
},
|
||||
tonalElevation = 2.dp,
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
color = if (isPrimary) {
|
||||
|
@ -70,7 +112,7 @@ fun MarkAsReadBarItem(
|
|||
text = text,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
color = if (isPrimary) {
|
||||
MaterialTheme.colorScheme.onPrimaryContainer
|
||||
MaterialTheme.colorScheme.onSurface
|
||||
} else {
|
||||
MaterialTheme.colorScheme.secondary
|
||||
},
|
||||
|
|
|
@ -143,7 +143,6 @@ private fun TopBar(
|
|||
title = {},
|
||||
navigationIcon = {
|
||||
FeedbackIconButton(
|
||||
isHaptic = false,
|
||||
imageVector = Icons.Rounded.Close,
|
||||
contentDescription = stringResource(R.string.close),
|
||||
tint = MaterialTheme.colorScheme.onSurface
|
||||
|
@ -156,7 +155,6 @@ private fun TopBar(
|
|||
actions = {
|
||||
if (isShowActions) {
|
||||
FeedbackIconButton(
|
||||
isHaptic = false,
|
||||
modifier = Modifier.size(22.dp),
|
||||
imageVector = Icons.Outlined.Headphones,
|
||||
contentDescription = stringResource(R.string.mark_all_as_read),
|
||||
|
@ -164,7 +162,6 @@ private fun TopBar(
|
|||
) {
|
||||
}
|
||||
FeedbackIconButton(
|
||||
isHaptic = false,
|
||||
imageVector = Icons.Outlined.MoreVert,
|
||||
contentDescription = stringResource(R.string.search),
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
|
|
|
@ -79,7 +79,7 @@ class ReadViewModel @Inject constructor(
|
|||
|
||||
private fun markUnread(isUnread: Boolean) {
|
||||
val articleWithFeed = _viewState.value.articleWithFeed ?: return
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
viewModelScope.launch {
|
||||
_viewState.update {
|
||||
it.copy(
|
||||
articleWithFeed = articleWithFeed.copy(
|
||||
|
@ -89,10 +89,12 @@ class ReadViewModel @Inject constructor(
|
|||
)
|
||||
)
|
||||
}
|
||||
rssRepository.get().updateArticleInfo(
|
||||
articleWithFeed.article.copy(
|
||||
isUnread = isUnread
|
||||
)
|
||||
rssRepository.get().markAsRead(
|
||||
groupId = null,
|
||||
feedId = null,
|
||||
articleId = _viewState.value.articleWithFeed!!.article.id,
|
||||
before = null,
|
||||
isUnread = isUnread,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,6 @@ fun SettingsPage(
|
|||
title = {},
|
||||
navigationIcon = {
|
||||
FeedbackIconButton(
|
||||
isHaptic = false,
|
||||
imageVector = Icons.Rounded.ArrowBack,
|
||||
contentDescription = stringResource(R.string.back),
|
||||
tint = MaterialTheme.colorScheme.onSurface
|
||||
|
|
Loading…
Reference in New Issue
Block a user