Browse Central Hub leaderboard rankings, track the signed-in user's rank, and paginate through additional results.
Use CentralHubLeaderboardProvider to load rankings for a specific category and timeframe.
The provider state includes:
entries,userRank,loadingStatus for the initial request,previousPageLoadingStatus for pagination, andallDataLoaded and currentPage.class LeaderboardViewModel : ViewModel(), KoinComponent {
private val leaderboardProvider: CentralHubLeaderboardProvider = get()
val state = leaderboardProvider.state
fun loadFollowersWeeklyLeaderboard() {
viewModelScope.launch {
leaderboardProvider.loadLeaderboard(
category = LeaderboardCategoryType.FOLLOWERS,
timeframe = LeaderboardTimeframeType.WEEKLY,
)
}
}
override fun onCleared() {
super.onCleared()
leaderboardProvider.clear()
}
}@Composable
fun LeaderboardScreen(
vm: LeaderboardViewModel = viewModel(),
) {
val state by vm.state.collectAsStateWithLifecycle()
LaunchedEffect(Unit) {
vm.loadFollowersWeeklyLeaderboard()
}
when (state.loadingStatus) {
LoadingStatus.LOADING,
LoadingStatus.INITIAL,
-> CircularProgressIndicator()
LoadingStatus.ERROR -> Text("Failed to load leaderboard")
LoadingStatus.IDLE -> LazyColumn {
items(state.entries) { entry ->
Text(text = "#${entry.rank} ${entry.user.displayName} · ${entry.count}")
}
}
}
}Call loadMoreLeaderboard() to fetch the next page for the most recently loaded category and timeframe.
Use these fields to drive your pagination UI:
previousPageLoadingStatusallDataLoadedcurrentPageDisable your load-more UI while previousPageLoadingStatus is LOADING or when allDataLoaded is true.
fun loadNextPage() {
viewModelScope.launch {
leaderboardProvider.loadMoreLeaderboard()
}
}val canLoadMore =
state.previousPageLoadingStatus != LoadingStatus.LOADING &&
!state.allDataLoaded
Button(
onClick = vm::loadNextPage,
enabled = canLoadMore,
) {
Text(
when {
state.previousPageLoadingStatus == LoadingStatus.LOADING -> "Loading..."
state.allDataLoaded -> "All data loaded"
else -> "Load more"
}
)
}During the first request:
loadingStatus moves from INITIAL to LOADING.entries are replaced with the new first page.currentPage resets to 0.userRank is populated when that request succeeds.During pagination:
previousPageLoadingStatus moves to LOADING.currentPage advances after success.allDataLoaded becomes true when there are no more items to fetch.If you keep the provider alive across multiple screens, call clear() when you want a fresh leaderboard session.
FOLLOWERSPOPULAR_BETSBET_COMMENTSDAILYWEEKLYMONTHLYALL_TIMEdata class LeaderboardState(
val entries: List<LeaderboardEntry>,
val userRank: UserLeaderboardRank?,
val loadingStatus: LoadingStatus,
val previousPageLoadingStatus: LoadingStatus,
val allDataLoaded: Boolean,
val currentPage: Int,
)data class LeaderboardEntry(
val rank: Int,
val user: User,
val count: Int,
)data class UserLeaderboardRank(
val rank: Int,
val score: Int,
val totalUsers: Int,
)