feat: Add ordering functionality for friends list
This commit is contained in:
@@ -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()
|
||||
|
||||
+61
-1
@@ -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) },
|
||||
|
||||
Reference in New Issue
Block a user