feat: extend friends data support and refactor profile state management

This commit is contained in:
2026-05-30 20:30:55 +03:00
parent 2381d4ca0a
commit 8c053905ce
9 changed files with 65 additions and 44 deletions
@@ -2,6 +2,8 @@ package dev.meloda.fast.common.extensions
import android.os.Build
import android.os.Bundle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
@@ -40,6 +42,10 @@ fun <T> MutableList<T>.removeIfCompat(condition: (T) -> Boolean): Boolean {
return removed
}
context(viewModel: ViewModel)
fun <T> Flow<T>.listenValue(action: suspend (T) -> Unit): Job =
listenValue(viewModel.viewModelScope, action)
fun <T> Flow<T>.listenValue(
coroutineScope: CoroutineScope,
action: suspend (T) -> Unit
@@ -35,7 +35,7 @@ object VkMemoryCache {
}
fun appendContacts(contacts: List<VkContactDomain>) {
contacts.forEach { contact -> VkMemoryCache.contacts[contact.userId] = contact }
contacts.forEach { contact -> VkMemoryCache[contact.userId] = contact }
}
operator fun set(userid: Long, user: VkUser) {
@@ -129,6 +129,10 @@ class ConvosRepositoryImpl(
val groupsList = response.groups.orEmpty().map(VkGroupData::mapToDomain)
val contactsList = response.contacts.orEmpty().map(VkContactData::mapToDomain)
VkMemoryCache.appendUsers(profilesList)
VkMemoryCache.appendGroups(groupsList)
VkMemoryCache.appendContacts(contactsList)
val usersMap = VkUsersMap.forUsers(profilesList)
val groupsMap = VkGroupsMap.forGroups(groupsList)
@@ -147,10 +151,6 @@ class ConvosRepositoryImpl(
groupDao.insertAll(groupsList.map(VkGroupDomain::asEntity))
}
VkMemoryCache.appendUsers(profilesList)
VkMemoryCache.appendGroups(groupsList)
VkMemoryCache.appendContacts(contactsList)
convos
},
errorMapper = { error ->
@@ -1,9 +1,12 @@
package dev.meloda.fast.data.api.friends
import com.slack.eithernet.ApiResult
import com.slack.eithernet.successOrElse
import dev.meloda.fast.common.VkConstants
import dev.meloda.fast.data.VkMemoryCache
import dev.meloda.fast.database.dao.UserDao
import dev.meloda.fast.model.FriendsInfo
import dev.meloda.fast.model.api.data.VkContactData
import dev.meloda.fast.model.api.data.VkUserData
import dev.meloda.fast.model.api.domain.VkUser
import dev.meloda.fast.model.api.domain.asEntity
@@ -13,8 +16,6 @@ import dev.meloda.fast.network.RestApiErrorDomain
import dev.meloda.fast.network.mapApiDefault
import dev.meloda.fast.network.mapApiResult
import dev.meloda.fast.network.service.friends.FriendsService
import com.slack.eithernet.ApiResult
import com.slack.eithernet.successOrElse
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.withContext
@@ -51,14 +52,18 @@ class FriendsRepositoryImpl(
order = order,
count = count,
offset = offset,
fields = VkConstants.USER_FIELDS
fields = VkConstants.USER_FIELDS,
extended = true
)
service.getFriends(requestModel.map).mapApiResult(
successMapper = { apiResponse ->
val response = apiResponse.requireResponse()
val users = response.items.map(VkUserData::mapToDomain)
val contactsList = response.contacts.orEmpty().map(VkContactData::mapToDomain)
VkMemoryCache.appendUsers(users)
VkMemoryCache.appendContacts(contactsList)
users
},
@@ -4,7 +4,8 @@ data class GetFriendsRequest(
val order: String?,
val count: Int?,
val offset: Int?,
val fields: String?
val fields: String?,
val extended: Boolean?
) {
val map
@@ -14,6 +15,7 @@ data class GetFriendsRequest(
count?.let { this["count"] = it.toString() }
offset?.let { this["offset"] = it.toString() }
fields?.let { this["fields"] = it }
extended?.let { this["extended"] = it.toString() }
}
}
@@ -1,11 +1,13 @@
package dev.meloda.fast.model.api.responses
import dev.meloda.fast.model.api.data.VkUserData
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import dev.meloda.fast.model.api.data.VkContactData
import dev.meloda.fast.model.api.data.VkUserData
@JsonClass(generateAdapter = true)
data class GetFriendsResponse(
@Json(name = "count") val count: Int,
@Json(name = "items") val items: List<VkUserData>
@Json(name = "items") val items: List<VkUserData>,
@Json(name = "contacts") val contacts: List<VkContactData>?
)
@@ -21,11 +21,11 @@ fun NavGraphBuilder.friendsScreen(
onMessageClicked: (userId: Long) -> Unit,
onScrolledToTop: () -> Unit
) {
composable<Friends> {
val friendsViewModel: FriendsViewModel = activity.getViewModel<FriendsViewModelImpl>()
val onlineFriendsViewModel =
activity.getViewModel<OnlineFriendsViewModelImpl>()
composable<Friends> {
FriendsRoute(
friendsViewModel = friendsViewModel,
onlineFriendsViewModel = onlineFriendsViewModel,
@@ -9,49 +9,56 @@ import dev.meloda.fast.data.UserConfig
import dev.meloda.fast.data.processState
import dev.meloda.fast.domain.GetLocalUserByIdUseCase
import dev.meloda.fast.domain.LoadUserByIdUseCase
import dev.meloda.fast.logger.FastLogger
import dev.meloda.fast.profile.model.ProfileScreenState
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
class ProfileViewModel(
private val getLocalUserByIdUseCase: GetLocalUserByIdUseCase,
private val loadUserByIdUseCase: LoadUserByIdUseCase
private val loadUserByIdUseCase: LoadUserByIdUseCase,
private val logger: FastLogger
) : ViewModel() {
private val screenState = MutableStateFlow(ProfileScreenState.EMPTY)
val screenStateFlow get() = screenState.asStateFlow()
init {
getLocalAccountInfo()
}
fun screenStateFlow(): StateFlow<ProfileScreenState> = screenState.asStateFlow()
private fun getLocalAccountInfo() {
getLocalUserByIdUseCase(UserConfig.userId)
.listenValue(viewModelScope) { state ->
logger.debug(this@ProfileViewModel::class, "START")
emit(screenState.value.copy(isLoading = true))
getLocalUserByIdUseCase(UserConfig.userId).listenValue { state ->
logger.debug(this@ProfileViewModel::class, "LOADED: $state")
emit(screenState.value.copy(isLoading = false))
state.processState(
error = {
screenState.setValue { old ->
old.copy(
avatarUrl = null,
fullName = null
)
}
logger.debug(this@ProfileViewModel::class, "ERROR")
emit(screenState.value.copy(avatarUrl = null, fullName = null))
},
success = { user ->
screenState.setValue { old ->
old.copy(
logger.debug(this@ProfileViewModel::class, "SUCCESS")
emit(
screenState.value.copy(
avatarUrl = user?.photo200,
fullName = user?.fullName
)
}
)
},
any = ::loadAccountInfo
)
}
}
private fun emit(state: ProfileScreenState) {
screenState.setValue { state }
}
private fun loadAccountInfo() {
loadUserByIdUseCase(
userId = null,
@@ -18,10 +18,9 @@ fun NavGraphBuilder.profileScreen(
onSettingsButtonClicked: () -> Unit,
onPhotoClicked: (url: String) -> Unit
) {
val viewModel: ProfileViewModel = with(activity) { getViewModel() }
composable<Profile> {
val screenState by viewModel.screenStateFlow().collectAsStateWithLifecycle()
val viewModel: ProfileViewModel = activity.getViewModel()
val screenState by viewModel.screenStateFlow.collectAsStateWithLifecycle()
ProfileRoute(
screenState = screenState,