Skip to content

Commit 77e73d1

Browse files
committed
pref: Use Paging3 for changelogs instead of custom pagination
1 parent 742e6e8 commit 77e73d1

File tree

8 files changed

+103
-191
lines changed

8 files changed

+103
-191
lines changed

app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ dependencies {
3838
implementation(libs.compose.material.icons.extended)
3939
implementation(libs.compose.material3)
4040
implementation(libs.navigation.compose)
41+
implementation(libs.paging3)
4142

4243
// Accompanist
4344
implementation(libs.accompanist.drawablepainter)

app/src/main/java/app/revanced/manager/domain/repository/ChangelogsRepository.kt

Lines changed: 25 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package app.revanced.manager.domain.repository
22

33
import android.os.Parcelable
44
import androidx.core.net.toUri
5+
import androidx.paging.PagingSource
6+
import androidx.paging.PagingState
57
import app.revanced.manager.network.api.ReVancedAPI
68
import app.revanced.manager.network.dto.ReVancedAssetHistory
79
import app.revanced.manager.network.utils.getOrThrow
@@ -18,50 +20,29 @@ sealed interface ChangelogSource : Parcelable {
1820
}
1921

2022
class ChangelogsRepository(
21-
private val api: ReVancedAPI
22-
) {
23-
private var all: List<ReVancedAssetHistory> = emptyList()
24-
private var page = 0
25-
26-
suspend fun loadInitial(
27-
source: ChangelogSource,
28-
pageSize: Int
29-
): PageResult<ReVancedAssetHistory> {
30-
all = when (source) {
31-
is ChangelogSource.Manager ->
32-
api.getAppHistory().getOrThrow()
33-
34-
is ChangelogSource.Patches ->
35-
api.getPatchesHistory(source.baseUrl, source.prerelease).getOrThrow()
23+
private val api: ReVancedAPI,
24+
private val source: ChangelogSource,
25+
) : PagingSource<Int, ReVancedAssetHistory>() {
26+
27+
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, ReVancedAssetHistory> {
28+
return try {
29+
val items = when (source) {
30+
is ChangelogSource.Manager ->
31+
api.getAppHistory().getOrThrow()
32+
33+
is ChangelogSource.Patches ->
34+
api.getPatchesHistory(source.baseUrl, source.prerelease).getOrThrow()
35+
}
36+
37+
LoadResult.Page(
38+
data = items,
39+
prevKey = null,
40+
nextKey = null
41+
)
42+
} catch (e: Exception) {
43+
LoadResult.Error(e)
3644
}
37-
38-
page = 1
39-
40-
val items = all.take(pageSize)
41-
return PageResult(
42-
items = items,
43-
hasMore = hasMore(pageSize)
44-
)
45-
}
46-
47-
fun loadNext(pageSize: Int): PageResult<ReVancedAssetHistory> {
48-
val items = all
49-
.drop(page * pageSize)
50-
.take(pageSize)
51-
52-
page++
53-
54-
return PageResult(
55-
items = items,
56-
hasMore = hasMore(pageSize)
57-
)
5845
}
5946

60-
private fun hasMore(pageSize: Int) =
61-
page * pageSize < all.size
62-
}
63-
64-
data class PageResult<T>(
65-
val items: List<T>,
66-
val hasMore: Boolean
67-
)
47+
override fun getRefreshKey(state: PagingState<Int, ReVancedAssetHistory>): Int? = null
48+
}

app/src/main/java/app/revanced/manager/ui/component/ChangelogList.kt

Lines changed: 39 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -31,85 +31,65 @@ import androidx.compose.ui.platform.LocalContext
3131
import androidx.compose.ui.res.stringResource
3232
import androidx.compose.ui.text.font.FontWeight
3333
import androidx.compose.ui.unit.dp
34+
import androidx.paging.LoadState
35+
import androidx.paging.compose.LazyPagingItems
3436
import app.revanced.manager.R
3537
import app.revanced.manager.network.dto.ReVancedAssetHistory
3638
import app.revanced.manager.util.relativeTime
3739

38-
sealed interface ChangelogUiState {
39-
data object Loading : ChangelogUiState
40-
data class Error(val error: String) : ChangelogUiState
41-
data class Success(
42-
val changelogs: List<ReVancedAssetHistory>,
43-
val hasMore: Boolean = false,
44-
val isLoadingMore: Boolean = false,
45-
) : ChangelogUiState
46-
}
47-
4840
@Composable
4941
fun ChangelogList(
50-
state: ChangelogUiState,
51-
onLoadMore: () -> Unit,
42+
changelogs: LazyPagingItems<ReVancedAssetHistory>,
5243
modifier: Modifier = Modifier,
5344
) {
54-
val listState = rememberLazyListState()
55-
56-
val shouldLoadMore by remember {
57-
derivedStateOf {
58-
val lastVisible = listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0
59-
val total = listState.layoutInfo.totalItemsCount
60-
val canScroll = listState.canScrollForward || listState.canScrollBackward
61-
62-
(lastVisible >= total - 2 || !canScroll) && total > 0
63-
}
64-
}
65-
66-
LaunchedEffect(shouldLoadMore, state) {
67-
if (shouldLoadMore) onLoadMore()
68-
}
69-
7045
Box(
71-
modifier = modifier.then(Modifier.fillMaxSize()),
46+
modifier = modifier.fillMaxSize(),
7247
contentAlignment = Alignment.Center
7348
) {
74-
when (state) {
75-
is ChangelogUiState.Loading -> LoadingIndicator()
49+
when {
50+
changelogs.loadState.refresh is LoadState.Loading -> LoadingIndicator()
7651

77-
is ChangelogUiState.Error -> Text(
78-
text = state.error,
52+
changelogs.loadState.refresh is LoadState.Error -> {
53+
val error = changelogs.loadState.refresh as LoadState.Error
54+
Text(
55+
text = error.error.message ?: stringResource(R.string.changelog_download_fail),
56+
style = MaterialTheme.typography.titleLarge
57+
)
58+
}
59+
60+
changelogs.itemCount == 0 -> Text(
61+
text = stringResource(R.string.no_changelogs_found),
7962
style = MaterialTheme.typography.titleLarge
8063
)
8164

82-
is ChangelogUiState.Success -> {
83-
if (state.changelogs.isEmpty()) {
84-
Text(
85-
text = stringResource(R.string.no_changelogs_found),
86-
style = MaterialTheme.typography.titleLarge
87-
)
88-
} else {
89-
LazyColumnWithScrollbar(
90-
modifier = Modifier.fillMaxSize(),
91-
state = listState
92-
) {
93-
items(
94-
items = state.changelogs,
95-
key = { it.version }
96-
) { changelog ->
65+
else -> {
66+
val listState = rememberLazyListState()
67+
68+
LazyColumnWithScrollbar(
69+
modifier = Modifier.fillMaxSize(),
70+
state = listState
71+
) {
72+
items(
73+
count = changelogs.itemCount,
74+
key = { changelogs.peek(it)?.version ?: it }
75+
) { index ->
76+
changelogs[index]?.let { changelog ->
9777
ChangelogItem(
9878
changelog = changelog,
99-
showDivider = changelog != state.changelogs.last()
79+
showDivider = index < changelogs.itemCount - 1
10080
)
10181
}
82+
}
10283

103-
if (state.isLoadingMore) {
104-
item(key = "loading_more") {
105-
Box(
106-
modifier = Modifier
107-
.fillMaxWidth()
108-
.padding(16.dp),
109-
contentAlignment = Alignment.Center
110-
) {
111-
CircularProgressIndicator()
112-
}
84+
if (changelogs.loadState.append is LoadState.Loading) {
85+
item(key = "loading_more") {
86+
Box(
87+
modifier = Modifier
88+
.fillMaxWidth()
89+
.padding(16.dp),
90+
contentAlignment = Alignment.Center
91+
) {
92+
CircularProgressIndicator()
11393
}
11494
}
11595
}

app/src/main/java/app/revanced/manager/ui/screen/UpdateScreen.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import androidx.compose.ui.Modifier
3232
import androidx.compose.ui.input.nestedscroll.nestedScroll
3333
import androidx.compose.ui.res.stringResource
3434
import androidx.compose.ui.unit.dp
35+
import androidx.paging.compose.collectAsLazyPagingItems
3536
import app.revanced.manager.R
3637
import app.revanced.manager.ui.component.AppTopBar
3738
import app.revanced.manager.ui.component.BottomContentBar
@@ -48,6 +49,7 @@ fun UpdateScreen(
4849
vm: UpdateViewModel = koinViewModel()
4950
) {
5051
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
52+
val changelogs = vm.changelogs.collectAsLazyPagingItems()
5153

5254
val buttonConfig = when (vm.state) {
5355
State.CAN_DOWNLOAD -> Triple(
@@ -123,10 +125,7 @@ fun UpdateScreen(
123125
)
124126
}
125127

126-
ChangelogList(
127-
state = vm.changelogsState,
128-
onLoadMore = vm::loadNextPage,
129-
)
128+
ChangelogList(changelogs = changelogs, modifier = Modifier.padding(paddingValues))
130129
}
131130
}
132131
}

app/src/main/java/app/revanced/manager/ui/screen/settings/update/ChangelogsSettingsScreen.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import androidx.compose.material3.rememberTopAppBarState
88
import androidx.compose.runtime.Composable
99
import androidx.compose.ui.Modifier
1010
import androidx.compose.ui.input.nestedscroll.nestedScroll
11+
import androidx.paging.compose.collectAsLazyPagingItems
1112
import app.revanced.manager.domain.repository.ChangelogSource
1213
import app.revanced.manager.ui.component.AppTopBar
1314
import app.revanced.manager.ui.component.ChangelogList
@@ -23,6 +24,7 @@ fun ChangelogsSettingsScreen(
2324
vm: ChangelogsViewModel = koinViewModel { parametersOf(source) }
2425
) {
2526
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
27+
val changelogs = vm.changelogs.collectAsLazyPagingItems()
2628

2729
Scaffold(
2830
topBar = {
@@ -34,6 +36,6 @@ fun ChangelogsSettingsScreen(
3436
},
3537
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
3638
) { paddingValues ->
37-
ChangelogList(modifier = Modifier.padding(paddingValues), state = vm.state, onLoadMore =vm::loadNextPage)
39+
ChangelogList(changelogs = changelogs, modifier = Modifier.padding(paddingValues))
3840
}
3941
}
Lines changed: 15 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,26 @@
11
package app.revanced.manager.ui.viewmodel
22

3-
import android.app.Application
4-
import androidx.compose.runtime.getValue
5-
import androidx.compose.runtime.mutableStateOf
6-
import androidx.compose.runtime.setValue
73
import androidx.lifecycle.ViewModel
84
import androidx.lifecycle.viewModelScope
9-
import app.revanced.manager.R
5+
import androidx.paging.Pager
6+
import androidx.paging.PagingConfig
7+
import androidx.paging.PagingData
8+
import androidx.paging.cachedIn
109
import app.revanced.manager.domain.repository.ChangelogSource
1110
import app.revanced.manager.domain.repository.ChangelogsRepository
12-
import app.revanced.manager.ui.component.ChangelogUiState
13-
import app.revanced.manager.util.uiSafe
14-
import kotlinx.coroutines.launch
11+
import app.revanced.manager.network.api.ReVancedAPI
12+
import app.revanced.manager.network.dto.ReVancedAssetHistory
13+
import kotlinx.coroutines.flow.Flow
1514

1615
class ChangelogsViewModel(
17-
private val repository: ChangelogsRepository,
18-
private val app: Application,
16+
private val api: ReVancedAPI,
1917
private val source: ChangelogSource,
2018
) : ViewModel() {
21-
22-
var state: ChangelogUiState by mutableStateOf(ChangelogUiState.Loading)
23-
private set
24-
25-
private val pageSize = 2
26-
27-
init {
28-
viewModelScope.launch {
29-
uiSafe(app, R.string.changelog_download_fail, "Failed to download changelog") {
30-
val result = repository.loadInitial(source, pageSize)
31-
32-
state = ChangelogUiState.Success(
33-
changelogs = result.items,
34-
hasMore = result.hasMore
35-
)
36-
}
37-
38-
if (state is ChangelogUiState.Loading) {
39-
state = ChangelogUiState.Error(
40-
app.getString(R.string.changelog_download_fail)
41-
)
42-
}
43-
}
44-
}
45-
46-
fun loadNextPage() {
47-
val current = state as? ChangelogUiState.Success ?: return
48-
if (current.isLoadingMore || !current.hasMore) return
49-
50-
state = current.copy(isLoadingMore = true)
51-
52-
val result = repository.loadNext(pageSize)
53-
54-
state = current.copy(
55-
changelogs = current.changelogs + result.items,
56-
isLoadingMore = false,
57-
hasMore = result.hasMore
58-
)
59-
}
19+
val changelogs: Flow<PagingData<ReVancedAssetHistory>> = Pager(
20+
config = PagingConfig(
21+
pageSize = 10,
22+
enablePlaceholders = false
23+
),
24+
pagingSourceFactory = { ChangelogsRepository(api, source) }
25+
).flow.cachedIn(viewModelScope)
6026
}

0 commit comments

Comments
 (0)