Created
October 20, 2023 11:39
-
-
Save Sardorbekcyber/1765a5b467bc62b432999207c125029a to your computer and use it in GitHub Desktop.
Follower ViewModel for Code Example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package app.skylab.profile.ui.fragments.followers | |
import androidx.lifecycle.SavedStateHandle | |
import androidx.lifecycle.ViewModel | |
import androidx.lifecycle.viewModelScope | |
import androidx.paging.Pager | |
import androidx.paging.PagingConfig | |
import androidx.paging.PagingData | |
import androidx.paging.cachedIn | |
import app.skylab.core.ActionDispatcher | |
import app.skylab.core.InAppAction | |
import app.skylab.core.coroutines.CoroutineDispatchers | |
import app.skylab.core.status.FollowStatus | |
import app.skylab.feature.auth.api.AccountSource | |
import app.skylab.feature.profile.model.AccountFollowerUi | |
import app.skylab.feature.profile.usecase.FollowUseCase | |
import app.skylab.feature.profile.usecase.RemoveFollowerUseCase | |
import app.skylab.feature.profile.usecase.TotalPendingFollowersUseCase | |
import app.skylab.feature.profile.usecase.UnfollowUsecase | |
import app.skylab.feature.profile.usecase.paging.FollowersPagingSource | |
import app.skylab.network.Try | |
import app.skylab.profile.ui.fragments.followers.FollowersContract.Intent | |
import app.skylab.profile.ui.fragments.followers.FollowersContract.Router | |
import app.skylab.profile.ui.fragments.followers.FollowersContract.SideEffect | |
import app.skylab.profile.ui.fragments.followers.FollowersContract.State | |
import dagger.Lazy | |
import dagger.hilt.android.lifecycle.HiltViewModel | |
import kotlinx.coroutines.ExperimentalCoroutinesApi | |
import kotlinx.coroutines.flow.Flow | |
import kotlinx.coroutines.flow.MutableSharedFlow | |
import kotlinx.coroutines.flow.distinctUntilChanged | |
import kotlinx.coroutines.flow.flatMapLatest | |
import kotlinx.coroutines.flow.onStart | |
import kotlinx.coroutines.launch | |
import org.orbitmvi.orbit.syntax.simple.intent | |
import org.orbitmvi.orbit.syntax.simple.postSideEffect | |
import org.orbitmvi.orbit.syntax.simple.reduce | |
import org.orbitmvi.orbit.syntax.simple.repeatOnSubscription | |
import org.orbitmvi.orbit.viewmodel.container | |
import javax.inject.Inject | |
@OptIn(ExperimentalCoroutinesApi::class) | |
@HiltViewModel | |
class FollowersViewModel @Inject constructor( | |
private val router: Router, | |
private val removeFollowerUseCase: Lazy<RemoveFollowerUseCase>, | |
private val totalPendingFollowersUseCase: Lazy<TotalPendingFollowersUseCase>, | |
private val unfollowUsecase: Lazy<UnfollowUsecase>, | |
private val followUseCase: Lazy<FollowUseCase>, | |
private val followersFactory: FollowersPagingSource.Factory, | |
private val actionDispatcher: ActionDispatcher, | |
private val dispatchers: CoroutineDispatchers, | |
accountSource: AccountSource, | |
savedStateHandle: SavedStateHandle | |
) : ViewModel(), FollowersContract.ViewModel { | |
private val accountId = savedStateHandle[ARG_ACCOUNT_ID] ?: accountSource.accountId | |
private val showBack = savedStateHandle["SHOW_BACK"] ?: false | |
private val searchQuery: MutableSharedFlow<String> = MutableSharedFlow() | |
override val container = container<State, SideEffect>( | |
State( | |
accountId = accountId, | |
isSelf = accountId == accountSource.accountId, | |
showBack = showBack | |
), savedStateHandle | |
) { | |
getTotalPendingCount() | |
listenActions() | |
} | |
override val followers: Flow<PagingData<AccountFollowerUi>> | |
init { | |
followers = searchQuery | |
.onStart { emit("") } | |
.distinctUntilChanged() | |
.flatMapLatest { provideFollowers(it) } | |
.cachedIn(viewModelScope) | |
} | |
override fun dispatch(intent: Intent) { | |
viewModelScope.launch { | |
when (intent) { | |
Intent.PendingRequestsClick -> router.navigateToPending() | |
Intent.BackClick -> router.navigateBack() | |
is Intent.ProfileClick -> router.navigateToProfile(intent.accountId) | |
is Intent.RemoveClick -> remove(intent.accountId) | |
is Intent.SearchChanged -> searchQuery.emit(intent.query) | |
is Intent.FollowClick -> followClick(intent.account) | |
} | |
} | |
} | |
private fun provideFollowers(searchText: String) = | |
Pager( | |
config = PagingConfig( | |
pageSize = ITEMS_PER_PAGE, | |
enablePlaceholders = false, | |
initialLoadSize = ITEMS_PER_PAGE, | |
prefetchDistance = ITEMS_PER_PAGE, | |
), | |
pagingSourceFactory = { followersFactory.create(accountId, searchText) } | |
).flow | |
private fun listenActions() = intent(registerIdling = false) { | |
repeatOnSubscription { | |
viewModelScope.launch(dispatchers.IO) { | |
actionDispatcher.actions.collect { actions -> | |
when (actions) { | |
InAppAction.FOLLOW_CHANGED -> postSideEffect(SideEffect.RefreshGeneralAdapter) | |
} | |
} | |
} | |
} | |
} | |
private fun getTotalPendingCount() = intent { | |
viewModelScope.launch { | |
if (state.isSelf) { | |
when (val response = totalPendingFollowersUseCase.get()(accountId)) { | |
is Try.Failure -> postSideEffect(SideEffect.ServerError(response.exception.message)) | |
is Try.Success -> reduce { state.copy(pendingCount = response.data) } | |
} | |
} | |
} | |
} | |
private fun followClick(account: AccountFollowerUi) = intent { | |
when (account.followStatus) { | |
FollowStatus.FOLLOWER -> unfollow(account.accountId) | |
FollowStatus.FOLLOW_REQUESTED -> postSideEffect(SideEffect.FollowRequested) | |
FollowStatus.NOT_FOLLOWER -> follow(account.accountId) | |
} | |
} | |
private fun remove(accountId: Long) = intent { | |
viewModelScope.launch(dispatchers.IO) { | |
when (val response = removeFollowerUseCase.get()(accountId)) { | |
is Try.Failure -> postSideEffect(SideEffect.ServerError(response.exception.message)) | |
is Try.Success -> postSideEffect(SideEffect.RefreshSelfAdapter) | |
} | |
} | |
} | |
private fun follow(accountId: Long) = intent { | |
viewModelScope.launch(dispatchers.IO) { | |
when (val response = followUseCase.get()(accountId)) { | |
is Try.Failure -> postSideEffect(SideEffect.ServerError(response.exception.message)) | |
is Try.Success -> { | |
//Following Immediately reflects in Ui disable Success message unless asked by QA | |
// postSideEffect(SideEffect.ServerMessage(response.message)) | |
actionDispatcher.sendAction(InAppAction.FOLLOW_CHANGED) | |
} | |
} | |
} | |
} | |
private fun unfollow(accountId: Long) = intent { | |
viewModelScope.launch(dispatchers.IO) { | |
when (val response = unfollowUsecase.get()(accountId)) { | |
is Try.Failure -> postSideEffect(SideEffect.ServerError(response.exception.message)) | |
is Try.Success -> actionDispatcher.sendAction(InAppAction.FOLLOW_CHANGED) | |
} | |
} | |
} | |
private companion object { | |
const val ARG_ACCOUNT_ID = "ACCOUNT_ID" | |
const val ITEMS_PER_PAGE = 30 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment