feat: Add ordering functionality for friends list

This commit is contained in:
2025-03-26 01:28:50 +03:00
parent 3dbf2bd8a4
commit 0ae05709db
9 changed files with 149 additions and 47 deletions
@@ -34,6 +34,8 @@ interface FriendsViewModel {
fun setScrollIndex(index: Int)
fun setScrollOffset(offset: Int)
fun onOrderTypeChanged(newOrderType: String)
}
abstract class BaseFriendsViewModelImpl : ViewModel(), FriendsViewModel {
@@ -69,6 +71,12 @@ abstract class BaseFriendsViewModelImpl : ViewModel(), FriendsViewModel {
screenState.setValue { old -> old.copy(scrollOffset = offset) }
}
override fun onOrderTypeChanged(newOrderType: String) {
if (screenState.value.orderType == newOrderType) return
screenState.setValue { old -> old.copy(orderType = newOrderType) }
loadFriends(offset = 0)
}
abstract fun loadFriends(offset: Int = currentOffset.value)
protected fun handleError(error: State.Error) {
@@ -138,52 +146,55 @@ class FriendsViewModelImpl(
}
override fun loadFriends(offset: Int) {
friendsUseCase.getFriends(count = LOAD_COUNT, offset = offset)
.listenValue(viewModelScope) { state ->
state.processState(
error = ::handleError,
success = { response ->
val itemsCountSufficient = response.size == LOAD_COUNT
canPaginate.setValue { itemsCountSufficient }
friendsUseCase.getFriends(
order = screenState.value.orderType,
count = LOAD_COUNT,
offset = offset
).listenValue(viewModelScope) { state ->
state.processState(
error = ::handleError,
success = { response ->
val itemsCountSufficient = response.size == LOAD_COUNT
canPaginate.setValue { itemsCountSufficient }
val paginationExhausted = !itemsCountSufficient &&
screenState.value.friends.size >= LOAD_COUNT
val paginationExhausted = !itemsCountSufficient &&
screenState.value.friends.size >= LOAD_COUNT
imagesToPreload.setValue {
response.mapNotNull(VkUser::photo100)
imagesToPreload.setValue {
response.mapNotNull(VkUser::photo100)
}
friendsUseCase.storeUsers(response)
val loadedFriends = response.map {
it.asPresentation(userSettings.useContactNames.value)
}
val newState = screenState.value.copy(
isPaginationExhausted = paginationExhausted
)
if (offset == 0) {
friends.emit(response)
screenState.setValue {
newState.copy(friends = loadedFriends)
}
friendsUseCase.storeUsers(response)
val loadedFriends = response.map {
it.asPresentation(userSettings.useContactNames.value)
}
val newState = screenState.value.copy(
isPaginationExhausted = paginationExhausted
)
if (offset == 0) {
friends.emit(response)
screenState.setValue {
newState.copy(friends = loadedFriends)
}
} else {
friends.emit(friends.value.plus(response))
screenState.setValue {
newState.copy(friends = newState.friends.plus(loadedFriends))
}
} else {
friends.emit(friends.value.plus(response))
screenState.setValue {
newState.copy(friends = newState.friends.plus(loadedFriends))
}
}
)
screenState.setValue { old ->
old.copy(
isLoading = offset == 0 && state.isLoading(),
isPaginating = offset > 0 && state.isLoading()
)
}
)
screenState.setValue { old ->
old.copy(
isLoading = offset == 0 && state.isLoading(),
isPaginating = offset > 0 && state.isLoading()
)
}
}
}
}
@@ -11,6 +11,7 @@ data class FriendsScreenState(
val isPaginationExhausted: Boolean,
val scrollIndex: Int,
val scrollOffset: Int,
val orderType: String,
) {
companion object {
@@ -20,7 +21,8 @@ data class FriendsScreenState(
isPaginating = false,
isPaginationExhausted = false,
scrollIndex = 0,
scrollOffset = 0
scrollOffset = 0,
orderType = "hints"
)
}
}
@@ -45,6 +45,7 @@ import org.koin.androidx.compose.koinViewModel
@Composable
fun FriendsScreen(
modifier: Modifier = Modifier,
orderType: String,
padding: PaddingValues,
tabIndex: Int,
onSessionExpiredLogOutButtonClicked: () -> Unit = {},
@@ -60,6 +61,10 @@ fun FriendsScreen(
koinViewModel<OnlineFriendsViewModelImpl>()
}
LaunchedEffect(orderType) {
viewModel.onOrderTypeChanged(orderType)
}
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
val baseError by viewModel.baseError.collectAsStateWithLifecycle()
val canPaginate by viewModel.canPaginate.collectAsStateWithLifecycle()
@@ -13,6 +13,8 @@ import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.PrimaryTabRow
import androidx.compose.material3.Scaffold
@@ -32,6 +34,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
@@ -40,9 +43,15 @@ import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import dev.chrisbanes.haze.materials.HazeMaterials
import dev.meloda.fast.model.BaseError
import dev.meloda.fast.ui.R
import dev.meloda.fast.ui.components.ActionInvokeDismiss
import dev.meloda.fast.ui.components.MaterialDialog
import dev.meloda.fast.ui.components.SelectionType
import dev.meloda.fast.ui.model.TabItem
import dev.meloda.fast.ui.theme.LocalHazeState
import dev.meloda.fast.ui.theme.LocalThemeConfig
import dev.meloda.fast.ui.util.ImmutableList
import dev.meloda.fast.ui.R as UiR
@OptIn(ExperimentalMaterial3Api::class, ExperimentalHazeMaterialsApi::class)
@Composable
@@ -98,6 +107,44 @@ fun FriendsRoute(
)
}
var orderType: String by remember { mutableStateOf("hints") }
var showOrderDialog by remember { mutableStateOf(false) }
val orderItems = remember {
mapOf(
"hints" to "Priority",
"name" to "Name",
"random" to "Random",
"mobile" to "Mobile",
"smart" to "Smart"
)
}
var selectedIndex by remember {
mutableIntStateOf(0)
}
if (showOrderDialog) {
MaterialDialog(
onDismissRequest = { showOrderDialog = false },
confirmText = stringResource(R.string.ok),
confirmAction = {
orderType =
orderItems.keys.toCollection(mutableListOf())[selectedIndex]
},
cancelText = stringResource(R.string.cancel),
selectionType = SelectionType.Single,
items = ImmutableList.copyOf(orderItems.values),
preSelectedItems = ImmutableList.of(selectedIndex),
onItemClick = {
selectedIndex = it
},
title = "Order type",
actionInvokeDismiss = ActionInvokeDismiss.Always
)
}
Scaffold(
modifier = Modifier.fillMaxSize(),
contentWindowInsets = WindowInsets.statusBars,
@@ -129,7 +176,19 @@ fun FriendsRoute(
colors = TopAppBarDefaults.topAppBarColors(
containerColor = Color.Transparent
),
modifier = Modifier.fillMaxWidth()
modifier = Modifier.fillMaxWidth(),
actions = {
IconButton(
onClick = {
showOrderDialog = true
}
) {
Icon(
painter = painterResource(UiR.drawable.round_filter_list_24),
contentDescription = null
)
}
}
)
PrimaryTabRow(
selectedTabIndex = selectedTabIndex,
@@ -175,6 +234,7 @@ fun FriendsRoute(
modifier = Modifier.fillMaxSize(),
) { index ->
FriendsScreen(
orderType = orderType,
padding = padding,
tabIndex = index,
onSessionExpiredLogOutButtonClicked = { onError(BaseError.SessionExpired) },