Skip to content

Commit

Permalink
fix initial state problems
Browse files Browse the repository at this point in the history
  • Loading branch information
stslex committed May 19, 2024
1 parent f32c1be commit 40097b2
Show file tree
Hide file tree
Showing 19 changed files with 224 additions and 129 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,40 +31,40 @@ import st.slex.csplashscreen.core.ui.mvi.Store.State

abstract class BaseViewModel<S : State, E : Event, A : Action, N : Navigation>(
private val router: Router<N>,
private val appDispatcher: AppDispatcher
private val appDispatcher: AppDispatcher,
initialState: S
) : ViewModel() {

abstract val initialState: S
abstract fun sendAction(action: A)

protected open val _state: MutableStateFlow<S> = MutableStateFlow(initialState)
private val _state: MutableStateFlow<S> = MutableStateFlow(initialState)
val state: StateFlow<S>
get() = _state.asStateFlow()

val event: MutableSharedFlow<E> = MutableSharedFlow()

fun updateState(update: (S) -> S) {
_state.update(update)
}
abstract fun sendAction(action: A)

fun sendEvent(event: E) {
viewModelScope.launch(appDispatcher.default) {
this@BaseViewModel.event.emit(event)
}
}

fun navigate(event: N) {
protected fun navigate(event: N) {
router(event)
}

fun <K : Any, T : Any, R : Any> Pager<K, T>.state(
protected fun updateState(update: (S) -> S) {
_state.update(update)
}

protected fun <K : Any, T : Any, R : Any> Pager<K, T>.state(
transform: suspend (value: T) -> R
): StateFlow<PagingData<R>> = this
.flow
.map { pagingData -> pagingData.map(transform) }
.state()

fun <T : Any> Flow<PagingData<T>>.state(): StateFlow<PagingData<T>> = this
protected fun <T : Any> Flow<PagingData<T>>.state(): StateFlow<PagingData<T>> = this
.flowOn(appDispatcher.default)
.cachedIn(viewModelScope)
.stateIn(
Expand All @@ -73,14 +73,14 @@ abstract class BaseViewModel<S : State, E : Event, A : Action, N : Navigation>(
started = SharingStarted.Lazily
)

fun launch(
protected fun launch(
block: suspend CoroutineScope.() -> Unit
): Job = viewModelScope.launch(
context = appDispatcher.default,
block = block
)

fun <T> launchCatching(
protected fun <T> launchCatching(
block: suspend CoroutineScope.() -> T,
onFailure: suspend (Throwable) -> Unit = {},
onSuccess: (T) -> Unit,
Expand All @@ -95,7 +95,7 @@ abstract class BaseViewModel<S : State, E : Event, A : Action, N : Navigation>(
}
}

fun <T> Flow<T>.launch(
protected fun <T> Flow<T>.launch(
onError: suspend (cause: Throwable) -> Unit = {},
each: suspend (T) -> Unit
): Job = this
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package st.slex.csplashscreen.feature.collection.navigation

import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.navigation.NavGraphBuilder
import androidx.paging.compose.collectAsLazyPagingItems
import st.slex.csplashscreen.core.core.coroutine.CoroutineExt.mapState
import st.slex.csplashscreen.core.navigation.AppArguments
import st.slex.csplashscreen.core.navigation.AppDestination
import st.slex.csplashscreen.core.ui.base.createScreen
Expand All @@ -24,8 +23,7 @@ fun NavGraphBuilder.singleCollectionGraph(
featureBuilder = SingleCollectionBuilder
) { viewModel: SingleCollectionViewModel, args ->
val arguments = args.first().let(AppArguments::CollectionScreen)
val state by remember { viewModel.state }.collectAsState()
val photos = remember { state.photos() }.collectAsLazyPagingItems()
val photos = remember { viewModel.state.mapState { it.photos } }.collectAsLazyPagingItems()

LaunchedEffect(Unit) {
viewModel.sendAction(Action.Init(arguments.collectionId))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@ package st.slex.csplashscreen.feature.collection.ui.presenter

import androidx.compose.runtime.Stable
import androidx.paging.PagingData
import kotlinx.coroutines.flow.StateFlow
import st.slex.csplashscreen.core.photos.ui.model.PhotoModel
import st.slex.csplashscreen.core.ui.mvi.Store

interface SingleCollectionStore {

@Stable
data class State(
val photos: () -> StateFlow<PagingData<PhotoModel>>,
val photos: PagingData<PhotoModel>,
val collectionId: String,
) : Store.State
) : Store.State {
companion object {
val INITIAL = State(
photos = PagingData.empty(),
collectionId = ""
)
}
}

@Stable
sealed interface Event : Store.Event
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.map
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
Expand All @@ -28,14 +27,11 @@ class SingleCollectionViewModel @Inject constructor(
private val interactor: SingleCollectionInteractor,
appDispatcher: AppDispatcher,
router: SingleCollectionRouter
) : BaseViewModel<State, Event, Action, Navigation>(router, appDispatcher) {

override val initialState: State = State(
photos = ::allPhotos,
collectionId = ""
)

override val _state: MutableStateFlow<State> = MutableStateFlow(initialState)
) : BaseViewModel<State, Event, Action, Navigation>(
router = router,
appDispatcher = appDispatcher,
initialState = State.INITIAL
) {

override fun sendAction(action: Action) {
when (action) {
Expand All @@ -51,6 +47,13 @@ class SingleCollectionViewModel @Inject constructor(
collectionId = action.collectionId
)
}
allPhotos.launch { pagingData ->
updateState { currentState ->
currentState.copy(
photos = pagingData
)
}
}
}

@OptIn(ExperimentalCoroutinesApi::class)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package st.slex.csplashscreen.feature.favourite.navigation

import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.navigation.NavGraphBuilder
import androidx.paging.compose.collectAsLazyPagingItems
import st.slex.csplashscreen.core.core.coroutine.CoroutineExt.mapState
import st.slex.csplashscreen.core.navigation.AppDestination
import st.slex.csplashscreen.core.ui.base.createScreen
import st.slex.csplashscreen.core.ui.utils.CollectAsEvent
Expand All @@ -21,9 +21,14 @@ fun NavGraphBuilder.favouriteGraph(
appDestination = AppDestination.FAVOURITE,
featureBuilder = FavouriteComponentBuilder
) { viewModel: FavouriteViewModel, _ ->
val state by remember { viewModel.state }.collectAsState()

val photos = remember(state.photos).collectAsLazyPagingItems()
LaunchedEffect(Unit) {
viewModel.sendAction(Action.Init)
}

val photos = remember {
viewModel.state.mapState { it.photos }
}.collectAsLazyPagingItems()

viewModel.event.CollectAsEvent { event ->
// TODO NOT IMPLEMENTED YET
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@ package st.slex.csplashscreen.feature.favourite.ui.presenter

import androidx.compose.runtime.Stable
import androidx.paging.PagingData
import kotlinx.coroutines.flow.StateFlow
import st.slex.csplashscreen.core.photos.ui.model.PhotoModel
import st.slex.csplashscreen.core.ui.mvi.Store

interface FavouriteStore : Store {

@Stable
data class State(
val photos: () -> StateFlow<PagingData<PhotoModel>>
) : Store.State
val photos: PagingData<PhotoModel>
) : Store.State {

companion object {
val INITIAL = State(
photos = PagingData.empty()
)
}
}

@Stable
sealed interface Event : Store.Event
Expand All @@ -36,6 +42,8 @@ interface FavouriteStore : Store {
@Stable
sealed interface Action : Store.Action {

data object Init : Action

data class OnUserClick(
val username: String
) : Action
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package st.slex.csplashscreen.feature.favourite.ui.presenter

import androidx.paging.PagingData
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import st.slex.csplashscreen.core.core.coroutine.AppDispatcher
import st.slex.csplashscreen.core.photos.ui.model.PhotoModel
import st.slex.csplashscreen.core.ui.mvi.BaseViewModel
import st.slex.csplashscreen.feature.favourite.domain.FavouriteInteractor
import st.slex.csplashscreen.feature.favourite.navigation.FavouriteRouter
Expand All @@ -19,29 +15,34 @@ class FavouriteViewModel @Inject constructor(
private val interactor: FavouriteInteractor,
appDispatcher: AppDispatcher,
router: FavouriteRouter
) : BaseViewModel<State, Event, Action, Navigation>(router, appDispatcher) {

override val initialState: State = State(
photos = ::photos
)

override val _state: MutableStateFlow<State> = MutableStateFlow(initialState)

private val photos: StateFlow<PagingData<PhotoModel>>
get() = interactor.photos
.map { pagingData ->
pagingData
}
.state()
) : BaseViewModel<State, Event, Action, Navigation>(
router = router,
appDispatcher = appDispatcher,
initialState = State.INITIAL
) {

override fun sendAction(action: Action) {
when (action) {
is Action.Init -> actionInit()
is Action.GoToPhotosClick -> actionGoHome()
is Action.OnImageClick -> actionImageClick(action)
is Action.OnUserClick -> actionUserClick(action)
}
}

private fun actionInit() {
interactor.photos
.map { pagingData ->
pagingData
}
.state()
.launch { data ->
updateState { state ->
state.copy(photos = data)
}
}
}

private fun actionGoHome() {
navigate(Navigation.Home)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package st.slex.csplashscreen.feature.home.navigation

import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.navigation.NavGraphBuilder
import androidx.paging.compose.collectAsLazyPagingItems
import st.slex.csplashscreen.core.core.coroutine.CoroutineExt.mapState
import st.slex.csplashscreen.core.navigation.AppDestination
import st.slex.csplashscreen.core.ui.base.createScreen
import st.slex.csplashscreen.core.ui.utils.CollectAsEvent
import st.slex.csplashscreen.feature.home.di.HomeComponentBuilder
import st.slex.csplashscreen.feature.home.ui.MainScreen
import st.slex.csplashscreen.feature.home.ui.presenter.HomeStore
import st.slex.csplashscreen.feature.home.ui.presenter.HomeStore.Action
import st.slex.csplashscreen.feature.home.ui.presenter.HomeViewModel

fun NavGraphBuilder.homeGraph(
Expand All @@ -22,10 +22,17 @@ fun NavGraphBuilder.homeGraph(
featureBuilder = HomeComponentBuilder
) { viewModel: HomeViewModel, _ ->

val state by remember { viewModel.state }.collectAsState()
LaunchedEffect(Unit) {
viewModel.sendAction(Action.Init)
}

val collections = remember {
viewModel.state.mapState { it.collections }
}.collectAsLazyPagingItems()

val collections = remember(state.collections).collectAsLazyPagingItems()
val photos = remember(state.photos).collectAsLazyPagingItems()
val photos = remember {
viewModel.state.mapState { it.photos }
}.collectAsLazyPagingItems()

viewModel.event.CollectAsEvent { event ->
// TODO NOT IMPLEMENTED YET
Expand All @@ -35,17 +42,17 @@ fun NavGraphBuilder.homeGraph(
modifier = modifier,
navToProfile = remember {
{ username ->
viewModel.sendAction(HomeStore.Action.OnUserClick(username))
viewModel.sendAction(Action.OnUserClick(username))
}
},
navToCollection = remember {
{ uuid ->
viewModel.sendAction(HomeStore.Action.OnCollectionClick(uuid))
viewModel.sendAction(Action.OnCollectionClick(uuid))
}
},
navToImage = remember {
{ uuid ->
viewModel.sendAction(HomeStore.Action.OnImageClick(uuid))
viewModel.sendAction(Action.OnImageClick(uuid))
}
},
collections = remember { collections },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package st.slex.csplashscreen.feature.home.ui.presenter

import androidx.compose.runtime.Stable
import androidx.paging.PagingData
import kotlinx.coroutines.flow.StateFlow
import st.slex.csplashscreen.core.collection.ui.model.CollectionModel
import st.slex.csplashscreen.core.photos.ui.model.PhotoModel
import st.slex.csplashscreen.core.ui.mvi.Store
Expand All @@ -11,9 +10,17 @@ interface HomeStore : Store {

@Stable
data class State(
val collections: () -> StateFlow<PagingData<CollectionModel>>,
val photos: () -> StateFlow<PagingData<PhotoModel>>
) : Store.State
val collections: PagingData<CollectionModel>,
val photos: PagingData<PhotoModel>
) : Store.State {

companion object {
val INIT = State(
collections = PagingData.empty(),
photos = PagingData.empty()
)
}
}

@Stable
sealed interface Event : Store.Event
Expand All @@ -40,6 +47,8 @@ interface HomeStore : Store {
@Stable
sealed interface Action : Store.Action {

data object Init : Action

@Stable
data class OnUserClick(
val username: String
Expand Down
Loading

0 comments on commit 40097b2

Please sign in to comment.