feat: Add ordering functionality for friends list
This commit is contained in:
@@ -8,11 +8,13 @@ import com.slack.eithernet.ApiResult
|
||||
interface FriendsRepository {
|
||||
|
||||
suspend fun getAllFriends(
|
||||
order: String,
|
||||
count: Int?,
|
||||
offset: Int?
|
||||
): ApiResult<FriendsInfo, RestApiErrorDomain>
|
||||
|
||||
suspend fun getFriends(
|
||||
order: String,
|
||||
count: Int?,
|
||||
offset: Int?
|
||||
): ApiResult<List<VkUser>, RestApiErrorDomain>
|
||||
|
||||
@@ -25,10 +25,11 @@ class FriendsRepositoryImpl(
|
||||
) : FriendsRepository {
|
||||
|
||||
override suspend fun getAllFriends(
|
||||
order: String,
|
||||
count: Int?,
|
||||
offset: Int?
|
||||
): ApiResult<FriendsInfo, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
val friends = async { getFriends(count, offset) }.await()
|
||||
val friends = async { getFriends(order, count, offset) }.await()
|
||||
.successOrElse { failure ->
|
||||
return@withContext failure
|
||||
}
|
||||
@@ -42,11 +43,12 @@ class FriendsRepositoryImpl(
|
||||
}
|
||||
|
||||
override suspend fun getFriends(
|
||||
order: String,
|
||||
count: Int?,
|
||||
offset: Int?
|
||||
): ApiResult<List<VkUser>, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
val requestModel = GetFriendsRequest(
|
||||
order = "hints",
|
||||
order = order,
|
||||
count = count,
|
||||
offset = offset,
|
||||
fields = VkConstants.USER_FIELDS
|
||||
|
||||
@@ -8,11 +8,13 @@ import kotlinx.coroutines.flow.Flow
|
||||
interface FriendsUseCase {
|
||||
|
||||
fun getAllFriends(
|
||||
order: String = "hints",
|
||||
count: Int?,
|
||||
offset: Int?
|
||||
): Flow<State<FriendsInfo>>
|
||||
|
||||
fun getFriends(
|
||||
order: String = "hints",
|
||||
count: Int?,
|
||||
offset: Int?
|
||||
): Flow<State<List<VkUser>>>
|
||||
|
||||
@@ -11,19 +11,26 @@ import kotlinx.coroutines.flow.flow
|
||||
class FriendsUseCaseImpl(private val repository: FriendsRepository) :
|
||||
FriendsUseCase {
|
||||
|
||||
override fun getAllFriends(count: Int?, offset: Int?): Flow<State<FriendsInfo>> = flow {
|
||||
override fun getAllFriends(order: String, count: Int?, offset: Int?): Flow<State<FriendsInfo>> = flow {
|
||||
emit(State.Loading)
|
||||
|
||||
val newState = repository.getAllFriends(count, offset).mapToState()
|
||||
val newState = repository.getAllFriends(order, count, offset).mapToState()
|
||||
emit(newState)
|
||||
}
|
||||
|
||||
override fun getFriends(
|
||||
count: Int?, offset: Int?
|
||||
order: String,
|
||||
count: Int?,
|
||||
offset: Int?
|
||||
): Flow<State<List<VkUser>>> = flow {
|
||||
emit(State.Loading)
|
||||
|
||||
val newState = repository.getFriends(count, offset).mapToState()
|
||||
val newState = repository.getFriends(
|
||||
order = order,
|
||||
count = count,
|
||||
offset = offset
|
||||
).mapToState()
|
||||
|
||||
emit(newState)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M11,18h2c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1h-2c-0.55,0 -1,0.45 -1,1s0.45,1 1,1zM3,7c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1L4,6c-0.55,0 -1,0.45 -1,1zM7,13h10c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1L7,11c-0.55,0 -1,0.45 -1,1s0.45,1 1,1z" />
|
||||
|
||||
</vector>
|
||||
@@ -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