Skip to content

Commit

Permalink
Merge pull request #23 from c0de-wizard/feature/refactor-interactor-i…
Browse files Browse the repository at this point in the history
…mplementation

Refactor Interactor implementation
  • Loading branch information
thomaskioko authored Dec 26, 2021
2 parents a97764a + 105637c commit 4753d75
Show file tree
Hide file tree
Showing 38 changed files with 601 additions and 423 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/package-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
with:
bump_version_scheme: minor
tag_prefix: v
release_name: "release_<RELEASE_VERSION>"
release_name: "<RELEASE_VERSION>"

- name: Access tag name of current workflow
run: |
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ Tv-Maniac is a Multiplatform app (Android & iOS) for viewing TV Shows informatio
- [Coroutines Extensions](https://cashapp.github.io/sqldelight/js_sqlite/coroutines/) Consume queries as Flow
* [Napier](https://github.com/AAkira/Napier) - Logging
* [Mockk](https://github.com/mockk/mockk) - mocking library for Kotlin.
* [KMP-NativeCoroutines](https://github.com/rickclephas/KMP-NativeCoroutines) - A library to use Kotlin Coroutines from Swift code in KMP apps.
## Roadmap
Android
Expand All @@ -94,8 +93,10 @@ iOS
- [ ] Implement Watchlist UI
- [ ] Implement Load more
Core
Shared
- [x] Use SQLDelight extensions to consume queries as Flow
- [x] Refactor interactor implementation.
- [ ] Modularize `shared` module
- [ ] Better MVI implementation
- Have `shared-core` module have most of the implementation.
- Improve error state, add retry.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,74 +4,79 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.thomaskioko.tvmaniac.core.Store
import com.thomaskioko.tvmaniac.core.discover.DiscoverShowAction
import com.thomaskioko.tvmaniac.core.discover.DiscoverShowAction.Error
import com.thomaskioko.tvmaniac.core.discover.DiscoverShowAction.LoadTvShows
import com.thomaskioko.tvmaniac.core.discover.DiscoverShowEffect
import com.thomaskioko.tvmaniac.core.discover.DiscoverShowState
import com.thomaskioko.tvmaniac.core.usecase.scope.CoroutineScopeOwner
import com.thomaskioko.tvmaniac.datasource.enums.ShowCategory.FEATURED
import com.thomaskioko.tvmaniac.datasource.enums.ShowCategory.POPULAR
import com.thomaskioko.tvmaniac.datasource.enums.ShowCategory.TOP_RATED
import com.thomaskioko.tvmaniac.datasource.enums.ShowCategory.TRENDING
import com.thomaskioko.tvmaniac.datasource.repository.TrendingShowData
import com.thomaskioko.tvmaniac.interactor.GetDiscoverShowListInteractor
import com.thomaskioko.tvmaniac.util.DomainResultState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class DiscoverViewModel @Inject constructor(
private val interactor: GetDiscoverShowListInteractor,
) : Store<DiscoverShowState, DiscoverShowAction, DiscoverShowEffect>, ViewModel() {
) : Store<DiscoverShowState, DiscoverShowAction, DiscoverShowEffect>,
CoroutineScopeOwner, ViewModel() {

override val coroutineScope: CoroutineScope
get() = viewModelScope

private val state = MutableStateFlow(DiscoverShowState(false, emptyList()))
private val sideEffect = MutableSharedFlow<DiscoverShowEffect>()

init {
dispatch(
DiscoverShowAction.LoadTvShows(listOf(FEATURED, TRENDING, TOP_RATED, POPULAR))
)
dispatch(LoadTvShows(listOf(FEATURED, TRENDING, TOP_RATED, POPULAR)))
}

override fun observeState(): StateFlow<DiscoverShowState> = state

override fun observeSideEffect(): Flow<DiscoverShowEffect> = sideEffect

@OptIn(InternalCoroutinesApi::class)
override fun dispatch(action: DiscoverShowAction) {
val oldState = state.value

when (action) {
is DiscoverShowAction.LoadTvShows -> {
viewModelScope.launch {
interactor.invoke(action.tvShowType)
.collect {
state.emit(it.stateReducer(oldState))
is LoadTvShows -> {
with(state) {
interactor.execute(action.tvShowType) {
onStart {
coroutineScope.launch { emit(oldState.copy(isLoading = true)) }
}

onNext {
coroutineScope.launch {
emit(
oldState.copy(
isLoading = false,
list = it
)
)
}
}

onError {
coroutineScope.launch { emit(oldState.copy(isLoading = false)) }
dispatch(Error(it.message ?: "Something went wrong"))
}
}
}
}
is DiscoverShowAction.Error -> {
viewModelScope.launch {
is Error -> {
coroutineScope.launch {
sideEffect.emit(DiscoverShowEffect.Error(action.message))
}
}
}
}

private fun DomainResultState<List<TrendingShowData>>.stateReducer(
state: DiscoverShowState,
): DiscoverShowState {
return when (this) {
is DomainResultState.Error -> {
dispatch(DiscoverShowAction.Error(message))
state.copy(isLoading = false)
}
is DomainResultState.Loading -> state.copy(isLoading = true)
is DomainResultState.Success -> state.copy(isLoading = false, list = data)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,16 @@ fun EpisodesScreen(

ColumnSpacer(8)

if (tvSeasons.isNotEmpty()) TvShowSeasons(
tvSeasons,
onSeasonSelected
)

if (isLoading) {
LoadingView()
}

TvShowSeasons(
tvSeasons,
onSeasonSelected,
episodeList
)

episodeList.forEachIndexed { index, episode ->
EpisodeItem(episode, index)
ColumnSpacer(16)
Expand All @@ -72,13 +73,17 @@ fun EpisodesScreen(
@Composable
fun TvShowSeasons(
tvSeasons: List<Season>,
onSeasonSelected: (EpisodeQuery) -> Unit
onSeasonSelected: (EpisodeQuery) -> Unit,
episodeList: List<Episode>
) {

val selectedPosition by remember { mutableStateOf(0) }
var selectedCategory by remember { mutableStateOf(tvSeasons.first()) }

if (tvSeasons.isNotEmpty() && selectedPosition == 0) {
/**
* Invoke fetchEpisode when season is loaded and user has not clicked on anything.
*/
if (tvSeasons.isNotEmpty() && episodeList.isEmpty() && selectedPosition == 0) {
onSeasonSelected(
EpisodeQuery(
tvShowId = selectedCategory.tvShowId,
Expand Down
Loading

0 comments on commit 4753d75

Please sign in to comment.