twoFa -> validation naming; fixes for preview for screens (separating view model from ui); some improvements & fixes
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package com.meloda.app.fast.friends
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.meloda.app.fast.common.extensions.listenValue
|
||||
import com.meloda.app.fast.common.extensions.setValue
|
||||
import com.meloda.app.fast.data.State
|
||||
@@ -9,30 +8,24 @@ import com.meloda.app.fast.data.api.friends.FriendsUseCase
|
||||
import com.meloda.app.fast.data.processState
|
||||
import com.meloda.app.fast.datastore.UserSettings
|
||||
import com.meloda.app.fast.friends.model.FriendsScreenState
|
||||
import com.meloda.app.fast.friends.model.UiFriend
|
||||
import com.meloda.app.fast.friends.util.asPresentation
|
||||
import com.meloda.app.fast.model.BaseError
|
||||
import com.meloda.app.fast.model.api.domain.VkUser
|
||||
import com.meloda.app.fast.network.VkErrorCodes
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
|
||||
// TODO: 13/07/2024, Danil Nikolaev: separate two lists and their pagination
|
||||
interface FriendsViewModel {
|
||||
|
||||
val screenState: StateFlow<FriendsScreenState>
|
||||
val uiFriends: StateFlow<List<UiFriend>>
|
||||
val uiOnlineFriends: StateFlow<List<UiFriend>>
|
||||
val baseError: StateFlow<BaseError?>
|
||||
val imagesToPreload: StateFlow<List<String>>
|
||||
val currentOffset: StateFlow<Int>
|
||||
val canPaginate: StateFlow<Boolean>
|
||||
|
||||
fun onMetPaginationCondition()
|
||||
fun onPaginationConditionsMet()
|
||||
|
||||
fun onRefresh()
|
||||
|
||||
@@ -46,11 +39,6 @@ class FriendsViewModelImpl(
|
||||
|
||||
override val screenState = MutableStateFlow(FriendsScreenState.EMPTY)
|
||||
|
||||
override val uiFriends = screenState.map { it.friends }
|
||||
.stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
|
||||
|
||||
override val uiOnlineFriends = MutableStateFlow<List<UiFriend>>(emptyList())
|
||||
|
||||
override val baseError = MutableStateFlow<BaseError?>(null)
|
||||
override val imagesToPreload = MutableStateFlow<List<String>>(emptyList())
|
||||
override val currentOffset = MutableStateFlow(0)
|
||||
@@ -64,7 +52,7 @@ class FriendsViewModelImpl(
|
||||
loadFriends()
|
||||
}
|
||||
|
||||
override fun onMetPaginationCondition() {
|
||||
override fun onPaginationConditionsMet() {
|
||||
currentOffset.update { screenState.value.friends.size }
|
||||
loadFriends()
|
||||
}
|
||||
@@ -130,19 +118,19 @@ class FriendsViewModelImpl(
|
||||
if (offset == 0) {
|
||||
friends.emit(response)
|
||||
screenState.setValue {
|
||||
newState.copy(friends = loadedFriends)
|
||||
newState.copy(
|
||||
friends = loadedFriends,
|
||||
onlineFriends = loadedOnlineFriends
|
||||
)
|
||||
}
|
||||
uiOnlineFriends.setValue { loadedOnlineFriends }
|
||||
} else {
|
||||
friends.emit(friends.value.plus(response))
|
||||
screenState.setValue {
|
||||
newState.copy(
|
||||
friends = newState.friends.plus(loadedFriends)
|
||||
friends = newState.friends.plus(loadedFriends),
|
||||
onlineFriends = newState.onlineFriends.plus(loadedOnlineFriends)
|
||||
)
|
||||
}
|
||||
uiOnlineFriends.setValue { old ->
|
||||
old.plus(loadedFriends)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -164,17 +152,19 @@ class FriendsViewModelImpl(
|
||||
conversation.asPresentation(useContactNames)
|
||||
}
|
||||
|
||||
val onlineUiFriends = uiOnlineFriends.value.mapNotNull { friend ->
|
||||
val onlineUiFriends = screenState.value.onlineFriends.mapNotNull { friend ->
|
||||
uiFriends.find { it.userId == friend.userId }
|
||||
}
|
||||
|
||||
screenState.setValue { old ->
|
||||
old.copy(friends = uiFriends)
|
||||
old.copy(
|
||||
friends = uiFriends,
|
||||
onlineFriends = onlineUiFriends
|
||||
)
|
||||
}
|
||||
uiOnlineFriends.setValue { onlineUiFriends }
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LOAD_COUNT = 30
|
||||
const val LOAD_COUNT = 60
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -6,6 +6,7 @@ import androidx.compose.runtime.Immutable
|
||||
data class FriendsScreenState(
|
||||
val isLoading: Boolean,
|
||||
val friends: List<UiFriend>,
|
||||
val onlineFriends: List<UiFriend>,
|
||||
val isPaginating: Boolean,
|
||||
val isPaginationExhausted: Boolean
|
||||
) {
|
||||
@@ -14,6 +15,7 @@ data class FriendsScreenState(
|
||||
val EMPTY: FriendsScreenState = FriendsScreenState(
|
||||
isLoading = true,
|
||||
friends = emptyList(),
|
||||
onlineFriends = emptyList(),
|
||||
isPaginating = false,
|
||||
isPaginationExhausted = false
|
||||
)
|
||||
|
||||
+3
-3
@@ -6,14 +6,14 @@ import androidx.navigation.compose.composable
|
||||
import com.meloda.app.fast.common.extensions.navigation.sharedViewModel
|
||||
import com.meloda.app.fast.friends.FriendsViewModel
|
||||
import com.meloda.app.fast.friends.FriendsViewModelImpl
|
||||
import com.meloda.app.fast.friends.presentation.FriendsScreen
|
||||
import com.meloda.app.fast.friends.presentation.FriendsRoute
|
||||
import com.meloda.app.fast.model.BaseError
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
object Friends
|
||||
|
||||
fun NavGraphBuilder.friendsRoute(
|
||||
fun NavGraphBuilder.friendsScreen(
|
||||
onError: (BaseError) -> Unit,
|
||||
navController: NavController
|
||||
) {
|
||||
@@ -21,7 +21,7 @@ fun NavGraphBuilder.friendsRoute(
|
||||
val viewModel: FriendsViewModel =
|
||||
it.sharedViewModel<FriendsViewModelImpl>(navController = navController)
|
||||
|
||||
FriendsScreen(
|
||||
FriendsRoute(
|
||||
onError = onError,
|
||||
viewModel = viewModel
|
||||
)
|
||||
+44
-21
@@ -58,6 +58,7 @@ import com.meloda.app.fast.designsystem.components.FullScreenLoader
|
||||
import com.meloda.app.fast.designsystem.components.NoItemsView
|
||||
import com.meloda.app.fast.friends.FriendsViewModel
|
||||
import com.meloda.app.fast.friends.FriendsViewModelImpl
|
||||
import com.meloda.app.fast.friends.model.FriendsScreenState
|
||||
import com.meloda.app.fast.model.BaseError
|
||||
import com.meloda.app.fast.ui.ErrorView
|
||||
import dev.chrisbanes.haze.haze
|
||||
@@ -67,30 +68,53 @@ import dev.chrisbanes.haze.materials.HazeMaterials
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import com.meloda.app.fast.designsystem.R as UiR
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalHazeMaterialsApi::class)
|
||||
@Composable
|
||||
fun FriendsScreen(
|
||||
fun FriendsRoute(
|
||||
onError: (BaseError) -> Unit,
|
||||
viewModel: FriendsViewModel = koinViewModel<FriendsViewModelImpl>()
|
||||
) {
|
||||
val baseError by viewModel.baseError.collectAsStateWithLifecycle()
|
||||
|
||||
val context = LocalContext.current
|
||||
|
||||
val imagesToPreload by viewModel.imagesToPreload.collectAsStateWithLifecycle()
|
||||
imagesToPreload.forEach { url ->
|
||||
context.imageLoader.enqueue(
|
||||
ImageRequest.Builder(context)
|
||||
.data(url)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
val baseError by viewModel.baseError.collectAsStateWithLifecycle()
|
||||
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
|
||||
val friends by viewModel.uiFriends.collectAsStateWithLifecycle()
|
||||
val onlineFriends by viewModel.uiOnlineFriends.collectAsStateWithLifecycle()
|
||||
val canPaginate by viewModel.canPaginate.collectAsStateWithLifecycle()
|
||||
|
||||
val imagesToPreload by viewModel.imagesToPreload.collectAsStateWithLifecycle()
|
||||
LaunchedEffect(imagesToPreload) {
|
||||
imagesToPreload.forEach { url ->
|
||||
context.imageLoader.enqueue(
|
||||
ImageRequest.Builder(context)
|
||||
.data(url)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
FriendsScreen(
|
||||
screenState = screenState,
|
||||
baseError = baseError,
|
||||
canPaginate = canPaginate,
|
||||
onSessionExpiredLogOutButtonClicked = { onError(BaseError.SessionExpired) },
|
||||
onPaginationConditionsMet = viewModel::onPaginationConditionsMet,
|
||||
onRefresh = viewModel::onRefresh
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// TODO: 13/07/2024, Danil Nikolaev: support for online
|
||||
@OptIn(
|
||||
ExperimentalMaterial3Api::class,
|
||||
ExperimentalHazeMaterialsApi::class
|
||||
)
|
||||
@Composable
|
||||
fun FriendsScreen(
|
||||
screenState: FriendsScreenState = FriendsScreenState.EMPTY,
|
||||
baseError: BaseError? = null,
|
||||
canPaginate: Boolean = false,
|
||||
onSessionExpiredLogOutButtonClicked: () -> Unit = {},
|
||||
onPaginationConditionsMet: () -> Unit = {},
|
||||
onRefresh: () -> Unit = {}
|
||||
) {
|
||||
val currentTheme = LocalTheme.current
|
||||
|
||||
val maxLines by remember {
|
||||
@@ -111,7 +135,7 @@ fun FriendsScreen(
|
||||
|
||||
LaunchedEffect(paginationConditionMet) {
|
||||
if (paginationConditionMet && !screenState.isPaginating) {
|
||||
viewModel.onMetPaginationCondition()
|
||||
onPaginationConditionsMet()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,11 +247,11 @@ fun FriendsScreen(
|
||||
ErrorView(
|
||||
text = "Session expired",
|
||||
buttonText = "Log out",
|
||||
onButtonClick = { onError(BaseError.SessionExpired) }
|
||||
onButtonClick = onSessionExpiredLogOutButtonClicked
|
||||
)
|
||||
}
|
||||
|
||||
screenState.isLoading && friends.isEmpty() -> FullScreenLoader()
|
||||
screenState.isLoading && screenState.friends.isEmpty() -> FullScreenLoader()
|
||||
|
||||
else -> {
|
||||
val pullToRefreshState = rememberPullToRefreshState()
|
||||
@@ -259,8 +283,7 @@ fun FriendsScreen(
|
||||
.padding(bottom = padding.calculateBottomPadding())
|
||||
.nestedScroll(pullToRefreshState.nestedScrollConnection)
|
||||
) {
|
||||
val friendsToDisplay = if (index == 0) friends
|
||||
else onlineFriends
|
||||
val friendsToDisplay = screenState.friends
|
||||
|
||||
FriendsList(
|
||||
modifier = if (currentTheme.usingBlur) {
|
||||
@@ -289,7 +312,7 @@ fun FriendsScreen(
|
||||
|
||||
if (pullToRefreshState.isRefreshing) {
|
||||
LaunchedEffect(true) {
|
||||
viewModel.onRefresh()
|
||||
onRefresh()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user