separation of users use cases

This commit is contained in:
2024-08-14 21:03:35 +03:00
parent bafebfa4ae
commit 58e2906c0f
13 changed files with 170 additions and 80 deletions
+2 -2
View File
@@ -12,8 +12,8 @@ android {
defaultConfig { defaultConfig {
applicationId = "dev.meloda.fast" applicationId = "dev.meloda.fast"
versionCode = 4 versionCode = libs.versions.versionCode.get().toInt()
versionName = "0.1.1" versionName = libs.versions.versionName.get()
} }
signingConfigs { signingConfigs {
@@ -1,9 +1,9 @@
package dev.meloda.fast.data package dev.meloda.fast.data
import com.slack.eithernet.ApiResult
import dev.meloda.fast.network.OAuthErrorDomain import dev.meloda.fast.network.OAuthErrorDomain
import dev.meloda.fast.network.RestApiErrorDomain import dev.meloda.fast.network.RestApiErrorDomain
import dev.meloda.fast.network.VkErrorCode import dev.meloda.fast.network.VkErrorCode
import com.slack.eithernet.ApiResult
sealed class State<out T> { sealed class State<out T> {
@@ -72,3 +72,12 @@ fun <T : Any> ApiResult<T, RestApiErrorDomain>.mapToState() = when (this) {
is ApiResult.Failure.HttpFailure -> this.error.toStateApiError() is ApiResult.Failure.HttpFailure -> this.error.toStateApiError()
is ApiResult.Failure.ApiFailure -> this.error.toStateApiError() is ApiResult.Failure.ApiFailure -> this.error.toStateApiError()
} }
fun <T : Any, N> ApiResult<T, RestApiErrorDomain>.mapToState(successMapper: (T) -> N) = when (this) {
is ApiResult.Success -> State.Success(successMapper(this.value))
is ApiResult.Failure.NetworkFailure -> State.Error.ConnectionError
is ApiResult.Failure.UnknownFailure -> State.UNKNOWN_ERROR
is ApiResult.Failure.HttpFailure -> this.error.toStateApiError()
is ApiResult.Failure.ApiFailure -> this.error.toStateApiError()
}
@@ -0,0 +1,23 @@
package dev.meloda.fast.domain
import dev.meloda.fast.data.State
import dev.meloda.fast.data.api.users.UsersRepository
import dev.meloda.fast.model.api.domain.VkUser
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
class GetLocalUserByIdUseCase(private val repository: UsersRepository) {
operator fun invoke(userId: Int): Flow<State<VkUser?>> = flow {
emit(State.Loading)
val newState = kotlin.runCatching {
repository.getLocalUsers(userIds = listOf(userId)).singleOrNull()
}.fold(
onSuccess = { user -> State.Success(user) },
onFailure = { State.Error.InternalError }
)
emit(newState)
}
}
@@ -0,0 +1,23 @@
package dev.meloda.fast.domain
import dev.meloda.fast.data.State
import dev.meloda.fast.data.api.users.UsersRepository
import dev.meloda.fast.model.api.domain.VkUser
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
class GetLocalUsersByIdsUseCase(private val repository: UsersRepository) {
operator fun invoke(userIds: List<Int>): Flow<State<List<VkUser>>> = flow {
emit(State.Loading)
val newState = kotlin.runCatching {
repository.getLocalUsers(userIds = userIds)
}.fold(
onSuccess = { user -> State.Success(user) },
onFailure = { State.Error.InternalError }
)
emit(newState)
}
}
@@ -0,0 +1,28 @@
package dev.meloda.fast.domain
import dev.meloda.fast.common.VkConstants
import dev.meloda.fast.data.State
import dev.meloda.fast.data.api.users.UsersRepository
import dev.meloda.fast.data.mapToState
import dev.meloda.fast.model.api.domain.VkUser
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
class LoadUserByIdUseCase(private val repository: UsersRepository) {
operator fun invoke(
userId: Int?,
fields: String = VkConstants.USER_FIELDS,
nomCase: String? = null
): Flow<State<VkUser?>> = flow {
emit(State.Loading)
val newState = repository.get(
userIds = userId?.let(::listOf),
fields = fields,
nomCase = nomCase
).mapToState(List<VkUser>::singleOrNull)
emit(newState)
}
}
@@ -0,0 +1,28 @@
package dev.meloda.fast.domain
import dev.meloda.fast.common.VkConstants
import dev.meloda.fast.data.State
import dev.meloda.fast.data.api.users.UsersRepository
import dev.meloda.fast.data.mapToState
import dev.meloda.fast.model.api.domain.VkUser
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
class LoadUsersByIdsUseCase(private val repository: UsersRepository) {
operator fun invoke(
userIds: List<Int>?,
fields: String = VkConstants.USER_FIELDS,
nomCase: String? = null
): Flow<State<List<VkUser>>> = flow {
emit(State.Loading)
val newState = repository.get(
userIds = userIds,
fields = fields,
nomCase = nomCase
).mapToState()
emit(newState)
}
}
@@ -0,0 +1,28 @@
package dev.meloda.fast.domain
import dev.meloda.fast.data.State
import dev.meloda.fast.data.api.users.UsersRepository
import dev.meloda.fast.model.api.domain.VkUser
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
class StoreUsersUseCase(private val repository: UsersRepository) {
operator fun invoke(users: List<VkUser>): Flow<State<Unit>> = flow {
emit(State.Loading)
val newState = kotlin.runCatching {
repository.storeUsers(users)
}.fold(
onSuccess = {
State.Success(Unit)
},
onFailure = { error ->
error.printStackTrace()
State.Error.InternalError
}
)
emit(newState)
}
}
@@ -1,19 +0,0 @@
package dev.meloda.fast.domain
import dev.meloda.fast.data.State
import dev.meloda.fast.model.api.domain.VkUser
import kotlinx.coroutines.flow.Flow
interface UsersUseCase {
fun get(
userIds: List<Int>?,
fields: String?,
nomCase: String?
): Flow<State<List<VkUser>>>
fun getLocalUser(userId: Int): Flow<State<VkUser?>>
suspend fun storeUser(user: VkUser)
suspend fun storeUsers(users: List<VkUser>)
}
@@ -1,40 +0,0 @@
package dev.meloda.fast.domain
import dev.meloda.fast.data.State
import dev.meloda.fast.data.api.users.UsersRepository
import dev.meloda.fast.data.mapToState
import dev.meloda.fast.model.api.domain.VkUser
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
class UsersUseCaseImpl(
private val repository: UsersRepository,
) : UsersUseCase {
override fun get(
userIds: List<Int>?,
fields: String?,
nomCase: String?
): Flow<State<List<VkUser>>> = flow {
emit(State.Loading)
val newState = repository.get(userIds, fields, nomCase).mapToState()
emit(newState)
}
override fun getLocalUser(userId: Int): Flow<State<VkUser?>> = flow {
emit(State.Loading)
val newState = kotlin.runCatching {
repository.getLocalUsers(listOf(userId)).singleOrNull()
}.fold(
onSuccess = { user -> State.Success(user) },
onFailure = { State.Error.InternalError }
)
emit(newState)
}
override suspend fun storeUser(user: VkUser) = repository.storeUsers(listOf(user))
override suspend fun storeUsers(users: List<VkUser>) = repository.storeUsers(users)
}
@@ -4,8 +4,11 @@ import dev.meloda.fast.data.di.dataModule
import dev.meloda.fast.domain.AccountUseCase import dev.meloda.fast.domain.AccountUseCase
import dev.meloda.fast.domain.AccountUseCaseImpl import dev.meloda.fast.domain.AccountUseCaseImpl
import dev.meloda.fast.domain.GetCurrentAccountUseCase import dev.meloda.fast.domain.GetCurrentAccountUseCase
import dev.meloda.fast.domain.UsersUseCase import dev.meloda.fast.domain.GetLocalUserByIdUseCase
import dev.meloda.fast.domain.UsersUseCaseImpl import dev.meloda.fast.domain.GetLocalUsersByIdsUseCase
import dev.meloda.fast.domain.LoadUserByIdUseCase
import dev.meloda.fast.domain.LoadUsersByIdsUseCase
import dev.meloda.fast.domain.StoreUsersUseCase
import org.koin.core.module.dsl.singleOf import org.koin.core.module.dsl.singleOf
import org.koin.dsl.bind import org.koin.dsl.bind
import org.koin.dsl.module import org.koin.dsl.module
@@ -13,7 +16,12 @@ import org.koin.dsl.module
val domainModule = module { val domainModule = module {
includes(dataModule) includes(dataModule)
singleOf(::UsersUseCaseImpl) bind UsersUseCase::class singleOf(::GetLocalUserByIdUseCase)
singleOf(::GetLocalUsersByIdsUseCase)
singleOf(::LoadUserByIdUseCase)
singleOf(::LoadUsersByIdsUseCase)
singleOf(::StoreUsersUseCase)
singleOf(::AccountUseCaseImpl) bind AccountUseCase::class singleOf(::AccountUseCaseImpl) bind AccountUseCase::class
singleOf(::GetCurrentAccountUseCase) singleOf(::GetCurrentAccountUseCase)
} }
@@ -20,8 +20,8 @@ import dev.meloda.fast.data.UserConfig
import dev.meloda.fast.data.db.AccountsRepository import dev.meloda.fast.data.db.AccountsRepository
import dev.meloda.fast.data.processState import dev.meloda.fast.data.processState
import dev.meloda.fast.datastore.AppSettings import dev.meloda.fast.datastore.AppSettings
import dev.meloda.fast.domain.LoadUserByIdUseCase
import dev.meloda.fast.domain.OAuthUseCase import dev.meloda.fast.domain.OAuthUseCase
import dev.meloda.fast.domain.UsersUseCase
import dev.meloda.fast.model.database.AccountEntity import dev.meloda.fast.model.database.AccountEntity
import dev.meloda.fast.network.OAuthErrorDomain import dev.meloda.fast.network.OAuthErrorDomain
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -71,7 +71,7 @@ interface LoginViewModel {
class LoginViewModelImpl( class LoginViewModelImpl(
private val oAuthUseCase: OAuthUseCase, private val oAuthUseCase: OAuthUseCase,
private val usersUseCase: UsersUseCase, private val loadUserByIdUseCase: LoadUserByIdUseCase,
private val accountsRepository: AccountsRepository, private val accountsRepository: AccountsRepository,
private val loginValidator: LoginValidator, private val loginValidator: LoginValidator,
private val longPollController: LongPollController private val longPollController: LongPollController
@@ -171,8 +171,8 @@ class LoginViewModelImpl(
UserConfig.trustedHash = account.trustedHash UserConfig.trustedHash = account.trustedHash
} }
usersUseCase.get( loadUserByIdUseCase(
userIds = null, userId = null,
fields = VkConstants.USER_FIELDS, fields = VkConstants.USER_FIELDS,
nomCase = null nomCase = null
).listenValue(viewModelScope) { state -> ).listenValue(viewModelScope) { state ->
@@ -185,7 +185,7 @@ class LoginViewModelImpl(
// TODO: 19/07/2024, Danil Nikolaev: show error? // TODO: 19/07/2024, Danil Nikolaev: show error?
}, },
success = { response -> success = { response ->
val actualUserId = response.first().id val actualUserId = requireNotNull(response).id
currentAccount = currentAccount.copy(userId = actualUserId) currentAccount = currentAccount.copy(userId = actualUserId)
@@ -245,8 +245,8 @@ class LoginViewModelImpl(
return@processState return@processState
} }
usersUseCase.get( loadUserByIdUseCase(
userIds = listOf(userId), userId = userId,
fields = VkConstants.USER_FIELDS, fields = VkConstants.USER_FIELDS,
nomCase = null nomCase = null
) )
@@ -8,7 +8,8 @@ import dev.meloda.fast.common.extensions.setValue
import dev.meloda.fast.data.State import dev.meloda.fast.data.State
import dev.meloda.fast.data.UserConfig import dev.meloda.fast.data.UserConfig
import dev.meloda.fast.data.processState import dev.meloda.fast.data.processState
import dev.meloda.fast.domain.UsersUseCase import dev.meloda.fast.domain.GetLocalUserByIdUseCase
import dev.meloda.fast.domain.LoadUserByIdUseCase
import dev.meloda.fast.model.BaseError import dev.meloda.fast.model.BaseError
import dev.meloda.fast.network.VkErrorCode import dev.meloda.fast.network.VkErrorCode
import dev.meloda.fast.profile.model.ProfileScreenState import dev.meloda.fast.profile.model.ProfileScreenState
@@ -21,7 +22,8 @@ interface ProfileViewModel {
} }
class ProfileViewModelImpl( class ProfileViewModelImpl(
private val usersUseCase: UsersUseCase private val getLocalUserByIdUseCase: GetLocalUserByIdUseCase,
private val loadUserByIdUseCase: LoadUserByIdUseCase
) : ViewModel(), ProfileViewModel { ) : ViewModel(), ProfileViewModel {
override val screenState = MutableStateFlow(ProfileScreenState.EMPTY) override val screenState = MutableStateFlow(ProfileScreenState.EMPTY)
@@ -32,7 +34,7 @@ class ProfileViewModelImpl(
} }
private fun getLocalAccountInfo() { private fun getLocalAccountInfo() {
usersUseCase.getLocalUser(UserConfig.userId) getLocalUserByIdUseCase(UserConfig.userId)
.listenValue(viewModelScope) { state -> .listenValue(viewModelScope) { state ->
state.processState( state.processState(
error = { error -> error = { error ->
@@ -67,8 +69,8 @@ class ProfileViewModelImpl(
} }
private fun loadAccountInfo() { private fun loadAccountInfo() {
usersUseCase.get( loadUserByIdUseCase(
userIds = null, userId = null,
fields = VkConstants.USER_FIELDS, fields = VkConstants.USER_FIELDS,
nomCase = null nomCase = null
).listenValue(viewModelScope) { state -> ).listenValue(viewModelScope) { state ->
@@ -77,7 +79,7 @@ class ProfileViewModelImpl(
// TODO: 12/07/2024, Danil Nikolaev: if local info is null then show error view // TODO: 12/07/2024, Danil Nikolaev: if local info is null then show error view
}, },
success = { response -> success = { response ->
val user = response.single() val user = requireNotNull(response)
screenState.setValue { old -> screenState.setValue { old ->
old.copy( old.copy(
+2 -2
View File
@@ -2,8 +2,8 @@
minSdk = "24" minSdk = "24"
targetSdk = "35" targetSdk = "35"
compileSdk = "35" compileSdk = "35"
versionCode = "4" versionCode = "5"
versionName = "0.1.1" versionName = "0.1.2"
agp = "8.5.2" agp = "8.5.2"
converterMoshi = "2.11.0" converterMoshi = "2.11.0"