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