mirror of
https://github.com/Ashinch/ReadYou.git
synced 2025-02-07 14:43:50 +01:00
Replace deprecated SwipeRefresh with recomended PullRefreshIndicator and implement pull to refresh for Feeds Page. (#850)
* Flow Page: replace SwipeRefresh with PullRefreshIndicator. * Feed Page: use PullRefreshIndicator for pull down refresh. * SwipeRefresh: Removed due to deprecation. * Switch from MD2 PullRefresh to MD3 PullToRefresh --------- Co-authored-by: Will McCall <532b81611e7e@mail.fordibben.zone>
This commit is contained in:
parent
006396c310
commit
24087803b1
@ -1,31 +0,0 @@
|
||||
package me.ash.reader.ui.component.base
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.google.accompanist.swiperefresh.SwipeRefreshIndicator
|
||||
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
||||
import me.ash.reader.ui.theme.palette.onDark
|
||||
|
||||
@Composable
|
||||
fun SwipeRefresh(
|
||||
isRefresh: Boolean = false,
|
||||
onRefresh: () -> Unit = {},
|
||||
content: @Composable () -> Unit = {},
|
||||
) {
|
||||
com.google.accompanist.swiperefresh.SwipeRefresh(
|
||||
state = rememberSwipeRefreshState(isRefresh),
|
||||
onRefresh = onRefresh,
|
||||
indicator = { state, trigger ->
|
||||
SwipeRefreshIndicator(
|
||||
state = state,
|
||||
refreshTriggerDistance = trigger,
|
||||
fade = true,
|
||||
scale = true,
|
||||
contentColor = MaterialTheme.colorScheme.primary,
|
||||
backgroundColor = MaterialTheme.colorScheme.surface onDark MaterialTheme.colorScheme.surfaceVariant,
|
||||
)
|
||||
}
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
@ -1,16 +1,12 @@
|
||||
package me.ash.reader.ui.page.home.feeds
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.infiniteRepeatable
|
||||
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
@ -26,13 +22,15 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.outlined.Settings
|
||||
import androidx.compose.material.icons.rounded.Add
|
||||
import androidx.compose.material.icons.rounded.Refresh
|
||||
import androidx.compose.material.icons.rounded.UnfoldLess
|
||||
import androidx.compose.material.icons.rounded.UnfoldMore
|
||||
import androidx.compose.material.rememberModalBottomSheetState
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@ -47,7 +45,6 @@ import androidx.compose.runtime.snapshots.SnapshotStateMap
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@ -90,7 +87,7 @@ import kotlin.collections.set
|
||||
import kotlin.math.ln
|
||||
|
||||
@OptIn(
|
||||
androidx.compose.foundation.ExperimentalFoundationApi::class
|
||||
ExperimentalMaterial3Api::class
|
||||
)
|
||||
@Composable
|
||||
fun FeedsPage(
|
||||
@ -130,7 +127,17 @@ fun FeedsPage(
|
||||
if (groupWithFeedList.isNotEmpty()) feedsUiState.listState else rememberLazyListState()
|
||||
|
||||
val owner = LocalLifecycleOwner.current
|
||||
|
||||
var isSyncing by remember { mutableStateOf(false) }
|
||||
val syncingState = rememberPullToRefreshState()
|
||||
val syncingScope = rememberCoroutineScope()
|
||||
val doSync:() -> Unit = {
|
||||
isSyncing = true
|
||||
syncingScope.launch {
|
||||
|
||||
homeViewModel.sync()
|
||||
}
|
||||
}
|
||||
|
||||
DisposableEffect(owner) {
|
||||
homeViewModel.syncWorkLiveData.observe(owner) { workInfoList ->
|
||||
@ -141,15 +148,6 @@ fun FeedsPage(
|
||||
onDispose { homeViewModel.syncWorkLiveData.removeObservers(owner) }
|
||||
}
|
||||
|
||||
val infiniteTransition = rememberInfiniteTransition()
|
||||
val angle by infiniteTransition.animateFloat(
|
||||
initialValue = 0f,
|
||||
targetValue = 360f,
|
||||
animationSpec = infiniteRepeatable(
|
||||
animation = tween(1000, easing = LinearEasing)
|
||||
)
|
||||
)
|
||||
|
||||
val feedBadgeAlpha by remember { derivedStateOf { (ln(groupListTonalElevation.value + 1.4f) + 2f) / 100f } }
|
||||
val groupAlpha by remember { derivedStateOf { groupListTonalElevation.value.dp.alphaLN(weight = 1.2f) } }
|
||||
val groupIndicatorAlpha by remember {
|
||||
@ -221,15 +219,6 @@ fun FeedsPage(
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
FeedbackIconButton(
|
||||
modifier = Modifier.rotate(if (isSyncing) angle else 0f),
|
||||
imageVector = Icons.Rounded.Refresh,
|
||||
contentDescription = stringResource(R.string.refresh),
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
enabled = !isSyncing
|
||||
) {
|
||||
if (!isSyncing) homeViewModel.sync()
|
||||
}
|
||||
if (subscribeViewModel.rssService.get().addSubscription) {
|
||||
FeedbackIconButton(
|
||||
imageVector = Icons.Rounded.Add,
|
||||
@ -241,153 +230,160 @@ fun FeedsPage(
|
||||
}
|
||||
},
|
||||
content = {
|
||||
LazyColumn(
|
||||
state = listState
|
||||
PullToRefreshBox(
|
||||
state=syncingState,
|
||||
isRefreshing = isSyncing,
|
||||
onRefresh = doSync
|
||||
) {
|
||||
item {
|
||||
DisplayText(
|
||||
text = feedsUiState.account?.name ?: "",
|
||||
desc = if (isSyncing) stringResource(R.string.syncing) else "",
|
||||
) { accountTabVisible = true }
|
||||
}
|
||||
item {
|
||||
Banner(
|
||||
title = filterUiState.filter.toName(),
|
||||
desc = importantSum,
|
||||
icon = filterUiState.filter.iconOutline,
|
||||
action = {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Outlined.KeyboardArrowRight,
|
||||
contentDescription = stringResource(R.string.go_to),
|
||||
)
|
||||
},
|
||||
) {
|
||||
filterChange(
|
||||
navController = navController,
|
||||
homeViewModel = homeViewModel,
|
||||
filterState = filterUiState.copy(
|
||||
group = null,
|
||||
feed = null,
|
||||
)
|
||||
)
|
||||
LazyColumn(
|
||||
modifier=Modifier.fillMaxSize(),
|
||||
state = listState
|
||||
) {
|
||||
item {
|
||||
DisplayText(
|
||||
text = feedsUiState.account?.name ?: "",
|
||||
desc = "",
|
||||
) { accountTabVisible = true }
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 26.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.feeds),
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(end = 12.dp)
|
||||
.size(20.dp)
|
||||
.clip(CircleShape)
|
||||
.clickable { if (hasGroupVisible) collapseAllGroups() else expandAllGroups() },
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
item {
|
||||
Banner(
|
||||
title = filterUiState.filter.toName(),
|
||||
desc = importantSum,
|
||||
icon = filterUiState.filter.iconOutline,
|
||||
action = {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Outlined.KeyboardArrowRight,
|
||||
contentDescription = stringResource(R.string.go_to),
|
||||
)
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (hasGroupVisible) Icons.Rounded.UnfoldLess else Icons.Rounded.UnfoldMore,
|
||||
contentDescription = stringResource(R.string.unfold_less),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
filterChange(
|
||||
navController = navController,
|
||||
homeViewModel = homeViewModel,
|
||||
filterState = filterUiState.copy(
|
||||
group = null,
|
||||
feed = null,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
|
||||
val defaultGroupId = context.currentAccountId.getDefaultGroupId()
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 26.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.feeds),
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(end = 12.dp)
|
||||
.size(20.dp)
|
||||
.clip(CircleShape)
|
||||
.clickable { if (hasGroupVisible) collapseAllGroups() else expandAllGroups() },
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (hasGroupVisible) Icons.Rounded.UnfoldLess else Icons.Rounded.UnfoldMore,
|
||||
contentDescription = stringResource(R.string.unfold_less),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
|
||||
itemsIndexed(groupWithFeedList) { index, groupWithFeed ->
|
||||
when (groupWithFeed) {
|
||||
is GroupFeedsView.Group -> {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
val defaultGroupId = context.currentAccountId.getDefaultGroupId()
|
||||
|
||||
if (groupWithFeed.group.id != defaultGroupId || groupWithFeed.group.feeds > 0) {
|
||||
GroupItem(
|
||||
isExpanded = {
|
||||
groupsVisible.getOrPut(
|
||||
groupWithFeed.group.id,
|
||||
groupListExpand::value
|
||||
)
|
||||
},
|
||||
group = groupWithFeed.group,
|
||||
alpha = groupAlpha,
|
||||
indicatorAlpha = groupIndicatorAlpha,
|
||||
roundedBottomCorner = { index == groupWithFeedList.lastIndex || groupWithFeed.group.feeds == 0 },
|
||||
onExpanded = {
|
||||
groupsVisible[groupWithFeed.group.id] =
|
||||
itemsIndexed(groupWithFeedList) { index, groupWithFeed ->
|
||||
when (groupWithFeed) {
|
||||
is GroupFeedsView.Group -> {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
if (groupWithFeed.group.id != defaultGroupId || groupWithFeed.group.feeds > 0) {
|
||||
GroupItem(
|
||||
isExpanded = {
|
||||
groupsVisible.getOrPut(
|
||||
groupWithFeed.group.id,
|
||||
groupListExpand::value
|
||||
).not()
|
||||
hasGroupVisible =
|
||||
if (groupsVisible[groupWithFeed.group.id] == true) {
|
||||
true
|
||||
} else {
|
||||
groupsVisible.any { it.value }
|
||||
)
|
||||
},
|
||||
group = groupWithFeed.group,
|
||||
alpha = groupAlpha,
|
||||
indicatorAlpha = groupIndicatorAlpha,
|
||||
roundedBottomCorner = { index == groupWithFeedList.lastIndex || groupWithFeed.group.feeds == 0 },
|
||||
onExpanded = {
|
||||
groupsVisible[groupWithFeed.group.id] =
|
||||
groupsVisible.getOrPut(
|
||||
groupWithFeed.group.id,
|
||||
groupListExpand::value
|
||||
).not()
|
||||
hasGroupVisible =
|
||||
if (groupsVisible[groupWithFeed.group.id] == true) {
|
||||
true
|
||||
} else {
|
||||
groupsVisible.any { it.value }
|
||||
}
|
||||
},
|
||||
onLongClick = {
|
||||
scope.launch {
|
||||
groupDrawerState.show()
|
||||
}
|
||||
},
|
||||
onLongClick = {
|
||||
scope.launch {
|
||||
groupDrawerState.show()
|
||||
}
|
||||
}
|
||||
) {
|
||||
filterChange(
|
||||
navController = navController,
|
||||
homeViewModel = homeViewModel,
|
||||
filterState = filterUiState.copy(
|
||||
group = groupWithFeed.group,
|
||||
feed = null,
|
||||
) {
|
||||
filterChange(
|
||||
navController = navController,
|
||||
homeViewModel = homeViewModel,
|
||||
filterState = filterUiState.copy(
|
||||
group = groupWithFeed.group,
|
||||
feed = null,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is GroupFeedsView.Feed -> {
|
||||
FeedItem(
|
||||
feed = groupWithFeed.feed,
|
||||
alpha = groupAlpha,
|
||||
badgeAlpha = feedBadgeAlpha,
|
||||
isEnded = { index == groupWithFeedList.lastIndex || groupWithFeedList[index + 1] is GroupFeedsView.Group },
|
||||
isExpanded = {
|
||||
groupsVisible.getOrPut(
|
||||
groupWithFeed.feed.groupId,
|
||||
groupListExpand::value
|
||||
)
|
||||
}, onClick = {
|
||||
filterChange(
|
||||
navController = navController,
|
||||
homeViewModel = homeViewModel,
|
||||
filterState = filterUiState.copy(
|
||||
group = null,
|
||||
feed = groupWithFeed.feed,
|
||||
is GroupFeedsView.Feed -> {
|
||||
FeedItem(
|
||||
feed = groupWithFeed.feed,
|
||||
alpha = groupAlpha,
|
||||
badgeAlpha = feedBadgeAlpha,
|
||||
isEnded = { index == groupWithFeedList.lastIndex || groupWithFeedList[index + 1] is GroupFeedsView.Group },
|
||||
isExpanded = {
|
||||
groupsVisible.getOrPut(
|
||||
groupWithFeed.feed.groupId,
|
||||
groupListExpand::value
|
||||
)
|
||||
)
|
||||
}, onLongClick = {
|
||||
scope.launch {
|
||||
feedDrawerState.show()
|
||||
}, onClick = {
|
||||
filterChange(
|
||||
navController = navController,
|
||||
homeViewModel = homeViewModel,
|
||||
filterState = filterUiState.copy(
|
||||
group = null,
|
||||
feed = groupWithFeed.feed,
|
||||
)
|
||||
)
|
||||
}, onLongClick = {
|
||||
scope.launch {
|
||||
feedDrawerState.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(128.dp))
|
||||
Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(128.dp))
|
||||
Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -14,7 +14,10 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
|
||||
import androidx.compose.material.icons.rounded.DoneAll
|
||||
import androidx.compose.material.icons.rounded.Search
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@ -55,13 +58,12 @@ import me.ash.reader.ui.component.base.DisplayText
|
||||
import me.ash.reader.ui.component.base.FeedbackIconButton
|
||||
import me.ash.reader.ui.component.base.RYExtensibleVisibility
|
||||
import me.ash.reader.ui.component.base.RYScaffold
|
||||
import me.ash.reader.ui.component.base.SwipeRefresh
|
||||
import me.ash.reader.ui.ext.collectAsStateValue
|
||||
import me.ash.reader.ui.page.common.RouteName
|
||||
import me.ash.reader.ui.page.home.HomeViewModel
|
||||
|
||||
@OptIn(
|
||||
androidx.compose.ui.ExperimentalComposeUiApi::class,
|
||||
ExperimentalMaterial3Api::class,
|
||||
)
|
||||
@Composable
|
||||
fun FlowPage(
|
||||
@ -94,7 +96,16 @@ fun FlowPage(
|
||||
var onSearch by remember { mutableStateOf(false) }
|
||||
|
||||
val owner = LocalLifecycleOwner.current
|
||||
|
||||
var isSyncing by remember { mutableStateOf(false) }
|
||||
val syncingState = rememberPullToRefreshState()
|
||||
val syncingScope = rememberCoroutineScope()
|
||||
val doSync: () -> Unit = {
|
||||
isSyncing = true
|
||||
syncingScope.launch {
|
||||
flowViewModel.sync()
|
||||
}
|
||||
}
|
||||
|
||||
DisposableEffect(owner) {
|
||||
homeViewModel.syncWorkLiveData.observe(owner) { workInfoList ->
|
||||
@ -242,14 +253,11 @@ fun FlowPage(
|
||||
}
|
||||
},
|
||||
content = {
|
||||
SwipeRefresh(
|
||||
onRefresh = {
|
||||
if (!isSyncing) {
|
||||
flowViewModel.sync()
|
||||
}
|
||||
}
|
||||
PullToRefreshBox(
|
||||
state = syncingState,
|
||||
isRefreshing = isSyncing,
|
||||
onRefresh = doSync
|
||||
) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = listState,
|
||||
@ -262,7 +270,7 @@ fun FlowPage(
|
||||
filterUiState.feed != null -> filterUiState.feed.name
|
||||
else -> filterUiState.filter.toName()
|
||||
},
|
||||
desc = if (isSyncing) stringResource(R.string.syncing) else "",
|
||||
desc = "",
|
||||
)
|
||||
RYExtensibleVisibility(visible = markAsRead) {
|
||||
Spacer(modifier = Modifier.height((56 + 24 + 10).dp))
|
||||
|
Loading…
x
Reference in New Issue
Block a user