Convert home view model to reducer
This commit is contained in:
parent
c6ad944994
commit
7a8c8ed88d
|
@ -67,7 +67,7 @@ class SmallTalkApplication : Application(), ModuleProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST", "IMPLICIT_CAST_TO_ANY")
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun <T : ProvidableModule> provide(klass: KClass<T>): T {
|
override fun <T : ProvidableModule> provide(klass: KClass<T>): T {
|
||||||
return when (klass) {
|
return when (klass) {
|
||||||
DirectoryModule::class -> featureModules.directoryModule
|
DirectoryModule::class -> featureModules.directoryModule
|
||||||
|
|
|
@ -190,6 +190,9 @@ internal class FeatureModules internal constructor(
|
||||||
storeModule.value.applicationStore(),
|
storeModule.value.applicationStore(),
|
||||||
buildMeta,
|
buildMeta,
|
||||||
),
|
),
|
||||||
|
profileModule,
|
||||||
|
loginModule,
|
||||||
|
directoryModule
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val settingsModule by unsafeLazy {
|
val settingsModule by unsafeLazy {
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
package app.dapk.st.directory
|
package app.dapk.st.directory
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import app.dapk.st.core.ProvidableModule
|
|
||||||
import app.dapk.st.state.createStateViewModel
|
|
||||||
import app.dapk.st.core.JobBag
|
import app.dapk.st.core.JobBag
|
||||||
|
import app.dapk.st.core.ProvidableModule
|
||||||
|
import app.dapk.st.directory.state.DirectoryEvent
|
||||||
import app.dapk.st.directory.state.DirectoryState
|
import app.dapk.st.directory.state.DirectoryState
|
||||||
import app.dapk.st.directory.state.directoryReducer
|
import app.dapk.st.directory.state.directoryReducer
|
||||||
import app.dapk.st.engine.ChatEngine
|
import app.dapk.st.engine.ChatEngine
|
||||||
|
import app.dapk.st.state.createStateViewModel
|
||||||
|
|
||||||
class DirectoryModule(
|
class DirectoryModule(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
|
@ -14,6 +15,8 @@ class DirectoryModule(
|
||||||
) : ProvidableModule {
|
) : ProvidableModule {
|
||||||
|
|
||||||
fun directoryState(): DirectoryState {
|
fun directoryState(): DirectoryState {
|
||||||
return createStateViewModel { directoryReducer(chatEngine, ShortcutHandler(context), JobBag(), it) }
|
return createStateViewModel { directoryReducer(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun directoryReducer(eventEmitter: suspend (DirectoryEvent) -> Unit) = directoryReducer(chatEngine, ShortcutHandler(context), JobBag(), eventEmitter)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ dependencies {
|
||||||
implementation project(":features:settings")
|
implementation project(":features:settings")
|
||||||
implementation project(":features:profile")
|
implementation project(":features:profile")
|
||||||
implementation project(":domains:android:compose-core")
|
implementation project(":domains:android:compose-core")
|
||||||
implementation project(":domains:android:viewmodel")
|
|
||||||
implementation project(':domains:store')
|
implementation project(':domains:store')
|
||||||
implementation 'screen-state:screen-android'
|
implementation 'screen-state:screen-android'
|
||||||
implementation project(":core")
|
implementation project(":core")
|
||||||
|
|
|
@ -1,27 +1,51 @@
|
||||||
package app.dapk.st.home
|
package app.dapk.st.home
|
||||||
|
|
||||||
|
import app.dapk.st.core.JobBag
|
||||||
import app.dapk.st.core.ProvidableModule
|
import app.dapk.st.core.ProvidableModule
|
||||||
import app.dapk.st.directory.state.DirectoryState
|
import app.dapk.st.directory.DirectoryModule
|
||||||
import app.dapk.st.domain.StoreModule
|
import app.dapk.st.domain.StoreModule
|
||||||
import app.dapk.st.engine.ChatEngine
|
import app.dapk.st.engine.ChatEngine
|
||||||
import app.dapk.st.login.state.LoginState
|
import app.dapk.st.home.state.createHomeReducer
|
||||||
import app.dapk.st.profile.state.ProfileState
|
import app.dapk.st.login.LoginModule
|
||||||
|
import app.dapk.st.profile.ProfileModule
|
||||||
|
import app.dapk.st.state.State
|
||||||
|
import app.dapk.st.state.createStateViewModel
|
||||||
|
import app.dapk.state.Action
|
||||||
|
import app.dapk.state.DynamicReducers
|
||||||
|
import app.dapk.state.combineReducers
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.filterIsInstance
|
||||||
|
|
||||||
class HomeModule(
|
class HomeModule(
|
||||||
private val chatEngine: ChatEngine,
|
private val chatEngine: ChatEngine,
|
||||||
private val storeModule: StoreModule,
|
private val storeModule: StoreModule,
|
||||||
val betaVersionUpgradeUseCase: BetaVersionUpgradeUseCase,
|
val betaVersionUpgradeUseCase: BetaVersionUpgradeUseCase,
|
||||||
|
private val profileModule: ProfileModule,
|
||||||
|
private val loginModule: LoginModule,
|
||||||
|
private val directoryModule: DirectoryModule,
|
||||||
) : ProvidableModule {
|
) : ProvidableModule {
|
||||||
|
|
||||||
internal fun homeViewModel(directory: DirectoryState, login: LoginState, profile: ProfileState): HomeViewModel {
|
internal fun compositeHomeState(): DynamicState {
|
||||||
return HomeViewModel(
|
return createStateViewModel {
|
||||||
chatEngine,
|
combineReducers(
|
||||||
directory,
|
listOf(
|
||||||
login,
|
homeReducerFactory(it),
|
||||||
profile,
|
loginModule.loginReducer(it),
|
||||||
storeModule.cacheCleaner(),
|
profileModule.profileReducer(),
|
||||||
betaVersionUpgradeUseCase,
|
directoryModule.directoryReducer(it)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
private fun homeReducerFactory(eventEmitter: suspend (Any) -> Unit) =
|
||||||
|
createHomeReducer(chatEngine, storeModule.cacheCleaner(), betaVersionUpgradeUseCase, JobBag(), eventEmitter)
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias DynamicState = State<DynamicReducers, Any>
|
||||||
|
|
||||||
|
inline fun <reified S, reified E> DynamicState.childState() = object : State<S, E> {
|
||||||
|
override fun dispatch(action: Action) = this@childState.dispatch(action)
|
||||||
|
override val events: Flow<E> = this@childState.events.filterIsInstance()
|
||||||
|
override val current: S = this@childState.current.getState()
|
||||||
|
}
|
||||||
|
|
|
@ -11,9 +11,11 @@ import app.dapk.st.core.components.CenteredLoading
|
||||||
import app.dapk.st.design.components.CircleishAvatar
|
import app.dapk.st.design.components.CircleishAvatar
|
||||||
import app.dapk.st.directory.DirectoryScreen
|
import app.dapk.st.directory.DirectoryScreen
|
||||||
import app.dapk.st.directory.state.DirectoryState
|
import app.dapk.st.directory.state.DirectoryState
|
||||||
import app.dapk.st.home.HomeScreenState.*
|
import app.dapk.st.home.state.HomeAction
|
||||||
import app.dapk.st.home.HomeScreenState.Page.Directory
|
import app.dapk.st.home.state.HomeScreenState.*
|
||||||
import app.dapk.st.home.HomeScreenState.Page.Profile
|
import app.dapk.st.home.state.HomeScreenState.Page.Directory
|
||||||
|
import app.dapk.st.home.state.HomeScreenState.Page.Profile
|
||||||
|
import app.dapk.st.home.state.HomeState
|
||||||
import app.dapk.st.login.LoginScreen
|
import app.dapk.st.login.LoginScreen
|
||||||
import app.dapk.st.login.state.LoginState
|
import app.dapk.st.login.state.LoginState
|
||||||
import app.dapk.st.profile.ProfileScreen
|
import app.dapk.st.profile.ProfileScreen
|
||||||
|
@ -21,18 +23,18 @@ import app.dapk.st.profile.state.ProfileState
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
internal fun HomeScreen(homeViewModel: HomeViewModel, directoryState: DirectoryState, loginState: LoginState, profileState: ProfileState) {
|
internal fun HomeScreen(homeState: HomeState, directoryState: DirectoryState, loginState: LoginState, profileState: ProfileState) {
|
||||||
LifecycleEffect(
|
LifecycleEffect(
|
||||||
onStart = { homeViewModel.start() },
|
onStart = { homeState.dispatch(HomeAction.LifecycleVisible) },
|
||||||
onStop = { homeViewModel.stop() }
|
onStop = { homeState.dispatch(HomeAction.LifecycleGone) }
|
||||||
)
|
)
|
||||||
|
|
||||||
when (val state = homeViewModel.state) {
|
when (val state = homeState.current) {
|
||||||
Loading -> CenteredLoading()
|
Loading -> CenteredLoading()
|
||||||
is SignedIn -> {
|
is SignedIn -> {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
BottomBar(state, homeViewModel)
|
BottomBar(state, homeState)
|
||||||
},
|
},
|
||||||
content = { innerPadding ->
|
content = { innerPadding ->
|
||||||
Box(modifier = Modifier.padding(innerPadding)) {
|
Box(modifier = Modifier.padding(innerPadding)) {
|
||||||
|
@ -40,7 +42,7 @@ internal fun HomeScreen(homeViewModel: HomeViewModel, directoryState: DirectoryS
|
||||||
Directory -> DirectoryScreen(directoryState)
|
Directory -> DirectoryScreen(directoryState)
|
||||||
Profile -> {
|
Profile -> {
|
||||||
ProfileScreen(profileState) {
|
ProfileScreen(profileState) {
|
||||||
homeViewModel.changePage(Directory)
|
homeState.dispatch(HomeAction.ChangePage(Directory))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +53,7 @@ internal fun HomeScreen(homeViewModel: HomeViewModel, directoryState: DirectoryS
|
||||||
|
|
||||||
SignedOut -> {
|
SignedOut -> {
|
||||||
LoginScreen(loginState) {
|
LoginScreen(loginState) {
|
||||||
homeViewModel.loggedIn()
|
homeState.dispatch(HomeAction.LoggedIn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +61,7 @@ internal fun HomeScreen(homeViewModel: HomeViewModel, directoryState: DirectoryS
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun BottomBar(state: SignedIn, homeViewModel: HomeViewModel) {
|
private fun BottomBar(state: SignedIn, homeState: HomeState) {
|
||||||
Column {
|
Column {
|
||||||
Divider(modifier = Modifier.fillMaxWidth(), color = Color.Black.copy(alpha = 0.2f), thickness = 0.5.dp)
|
Divider(modifier = Modifier.fillMaxWidth(), color = Color.Black.copy(alpha = 0.2f), thickness = 0.5.dp)
|
||||||
NavigationBar(containerColor = Color.Transparent, modifier = Modifier.height(IntrinsicSize.Min)) {
|
NavigationBar(containerColor = Color.Transparent, modifier = Modifier.height(IntrinsicSize.Min)) {
|
||||||
|
@ -70,8 +72,8 @@ private fun BottomBar(state: SignedIn, homeViewModel: HomeViewModel) {
|
||||||
selected = state.page == page,
|
selected = state.page == page,
|
||||||
onClick = {
|
onClick = {
|
||||||
when {
|
when {
|
||||||
state.page == page -> homeViewModel.scrollToTopOfMessages()
|
state.page == page -> homeState.dispatch(HomeAction.ScrollToTop)
|
||||||
else -> homeViewModel.changePage(page)
|
else -> homeState.dispatch(HomeAction.ChangePage(page))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -89,7 +91,7 @@ private fun BottomBar(state: SignedIn, homeViewModel: HomeViewModel) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
selected = state.page == page,
|
selected = state.page == page,
|
||||||
onClick = { homeViewModel.changePage(page) },
|
onClick = { homeState.dispatch(HomeAction.ChangePage(page)) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,127 +0,0 @@
|
||||||
package app.dapk.st.home
|
|
||||||
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import app.dapk.st.directory.state.ComponentLifecycle
|
|
||||||
import app.dapk.st.directory.state.DirectorySideEffect
|
|
||||||
import app.dapk.st.directory.state.DirectoryState
|
|
||||||
import app.dapk.st.domain.StoreCleaner
|
|
||||||
import app.dapk.st.engine.ChatEngine
|
|
||||||
import app.dapk.st.home.HomeScreenState.*
|
|
||||||
import app.dapk.st.login.state.LoginState
|
|
||||||
import app.dapk.st.profile.state.ProfileAction
|
|
||||||
import app.dapk.st.profile.state.ProfileState
|
|
||||||
import app.dapk.st.viewmodel.DapkViewModel
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
|
||||||
import kotlinx.coroutines.flow.onEach
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
internal class HomeViewModel(
|
|
||||||
private val chatEngine: ChatEngine,
|
|
||||||
private val directoryState: DirectoryState,
|
|
||||||
private val loginState: LoginState,
|
|
||||||
private val profileState: ProfileState,
|
|
||||||
private val cacheCleaner: StoreCleaner,
|
|
||||||
private val betaVersionUpgradeUseCase: BetaVersionUpgradeUseCase,
|
|
||||||
) : DapkViewModel<HomeScreenState, HomeEvent>(
|
|
||||||
initialState = Loading
|
|
||||||
) {
|
|
||||||
|
|
||||||
private var listenForInvitesJob: Job? = null
|
|
||||||
|
|
||||||
fun start() {
|
|
||||||
viewModelScope.launch {
|
|
||||||
state = if (chatEngine.isSignedIn()) {
|
|
||||||
_events.emit(HomeEvent.OnShowContent)
|
|
||||||
initialHomeContent()
|
|
||||||
} else {
|
|
||||||
SignedOut
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
viewModelScope.launch {
|
|
||||||
if (chatEngine.isSignedIn()) {
|
|
||||||
listenForInviteChanges()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun initialHomeContent(): SignedIn {
|
|
||||||
val me = chatEngine.me(forceRefresh = false)
|
|
||||||
return when (val current = state) {
|
|
||||||
Loading -> SignedIn(Page.Directory, me, invites = 0)
|
|
||||||
is SignedIn -> current.copy(me = me, invites = current.invites)
|
|
||||||
SignedOut -> SignedIn(Page.Directory, me, invites = 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loggedIn() {
|
|
||||||
viewModelScope.launch {
|
|
||||||
state = initialHomeContent()
|
|
||||||
_events.emit(HomeEvent.OnShowContent)
|
|
||||||
listenForInviteChanges()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun CoroutineScope.listenForInviteChanges() {
|
|
||||||
listenForInvitesJob?.cancel()
|
|
||||||
listenForInvitesJob = chatEngine.invites()
|
|
||||||
.onEach { invites ->
|
|
||||||
when (val currentState = state) {
|
|
||||||
is SignedIn -> updateState { currentState.copy(invites = invites.size) }
|
|
||||||
Loading,
|
|
||||||
SignedOut -> {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.launchIn(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun hasVersionChanged() = betaVersionUpgradeUseCase.hasVersionChanged()
|
|
||||||
|
|
||||||
fun clearCache() {
|
|
||||||
viewModelScope.launch {
|
|
||||||
cacheCleaner.cleanCache(removeCredentials = false)
|
|
||||||
betaVersionUpgradeUseCase.notifyUpgraded()
|
|
||||||
_events.emit(HomeEvent.Relaunch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun scrollToTopOfMessages() {
|
|
||||||
directoryState.dispatch(DirectorySideEffect.ScrollToTop)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun changePage(page: Page) {
|
|
||||||
state = when (val current = state) {
|
|
||||||
Loading -> current
|
|
||||||
is SignedIn -> {
|
|
||||||
when (page) {
|
|
||||||
current.page -> current
|
|
||||||
else -> current.copy(page = page).also {
|
|
||||||
pageChangeSideEffects(page)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SignedOut -> current
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun pageChangeSideEffects(page: Page) {
|
|
||||||
when (page) {
|
|
||||||
Page.Directory -> {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
Page.Profile -> {
|
|
||||||
directoryState.dispatch(ComponentLifecycle.OnGone)
|
|
||||||
profileState.dispatch(ProfileAction.Reset)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stop() {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,26 +12,24 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import app.dapk.st.core.DapkActivity
|
import app.dapk.st.core.DapkActivity
|
||||||
|
import app.dapk.st.core.extensions.unsafeLazy
|
||||||
import app.dapk.st.core.module
|
import app.dapk.st.core.module
|
||||||
import app.dapk.st.core.viewModel
|
import app.dapk.st.home.state.HomeAction
|
||||||
import app.dapk.st.directory.DirectoryModule
|
import app.dapk.st.home.state.HomeEvent
|
||||||
import app.dapk.st.login.LoginModule
|
import app.dapk.st.home.state.HomeState
|
||||||
import app.dapk.st.profile.ProfileModule
|
|
||||||
import app.dapk.st.state.state
|
import app.dapk.st.state.state
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
|
||||||
class MainActivity : DapkActivity() {
|
class MainActivity : DapkActivity() {
|
||||||
|
|
||||||
private val directoryState by state { module<DirectoryModule>().directoryState() }
|
private val homeModule by unsafeLazy { module<HomeModule>() }
|
||||||
private val loginState by state { module<LoginModule>().loginState() }
|
private val compositeState by state { homeModule.compositeHomeState() }
|
||||||
private val profileState by state { module<ProfileModule>().profileState() }
|
|
||||||
private val homeViewModel by viewModel { module<HomeModule>().homeViewModel(directoryState, loginState, profileState) }
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
val pushPermissionLauncher = registerPushPermission()
|
val pushPermissionLauncher = registerPushPermission()
|
||||||
homeViewModel.events.onEach {
|
compositeState.events.onEach {
|
||||||
when (it) {
|
when (it) {
|
||||||
HomeEvent.Relaunch -> recreate()
|
HomeEvent.Relaunch -> recreate()
|
||||||
HomeEvent.OnShowContent -> pushPermissionLauncher?.invoke()
|
HomeEvent.OnShowContent -> pushPermissionLauncher?.invoke()
|
||||||
|
@ -39,11 +37,12 @@ class MainActivity : DapkActivity() {
|
||||||
}.launchIn(lifecycleScope)
|
}.launchIn(lifecycleScope)
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
if (homeViewModel.hasVersionChanged()) {
|
val homeState: HomeState = compositeState.childState()
|
||||||
BetaUpgradeDialog()
|
if (homeModule.betaVersionUpgradeUseCase.hasVersionChanged()) {
|
||||||
|
BetaUpgradeDialog(homeState)
|
||||||
} else {
|
} else {
|
||||||
Surface(Modifier.fillMaxSize()) {
|
Surface(Modifier.fillMaxSize()) {
|
||||||
HomeScreen(homeViewModel, directoryState, loginState, profileState)
|
HomeScreen(homeState, compositeState.childState(), compositeState.childState(), compositeState.childState())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,20 +55,20 @@ class MainActivity : DapkActivity() {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@Composable
|
|
||||||
private fun BetaUpgradeDialog() {
|
@Composable
|
||||||
AlertDialog(
|
private fun BetaUpgradeDialog(homeState: HomeState) {
|
||||||
title = { Text(text = "BETA") },
|
AlertDialog(
|
||||||
text = { Text(text = "During the BETA, version upgrades require a cache clear") },
|
title = { Text(text = "BETA") },
|
||||||
onDismissRequest = {
|
text = { Text(text = "During the BETA, version upgrades require a cache clear") },
|
||||||
|
onDismissRequest = {
|
||||||
},
|
|
||||||
confirmButton = {
|
},
|
||||||
TextButton(onClick = { homeViewModel.clearCache() }) {
|
confirmButton = {
|
||||||
Text(text = "Clear cache".uppercase())
|
TextButton(onClick = { homeState.dispatch(HomeAction.ClearCache) }) {
|
||||||
}
|
Text(text = "Clear cache".uppercase())
|
||||||
},
|
}
|
||||||
)
|
},
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
package app.dapk.st.home.state
|
||||||
|
|
||||||
|
import app.dapk.st.core.JobBag
|
||||||
|
import app.dapk.st.directory.state.ComponentLifecycle
|
||||||
|
import app.dapk.st.directory.state.DirectorySideEffect
|
||||||
|
import app.dapk.st.domain.StoreCleaner
|
||||||
|
import app.dapk.st.engine.ChatEngine
|
||||||
|
import app.dapk.st.home.BetaVersionUpgradeUseCase
|
||||||
|
import app.dapk.st.profile.state.ProfileAction
|
||||||
|
import app.dapk.state.*
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
|
||||||
|
fun createHomeReducer(
|
||||||
|
chatEngine: ChatEngine,
|
||||||
|
cacheCleaner: StoreCleaner,
|
||||||
|
betaVersionUpgradeUseCase: BetaVersionUpgradeUseCase,
|
||||||
|
jobBag: JobBag,
|
||||||
|
eventEmitter: suspend (HomeEvent) -> Unit,
|
||||||
|
): ReducerFactory<HomeScreenState> {
|
||||||
|
return createReducer(
|
||||||
|
initialState = HomeScreenState.Loading,
|
||||||
|
|
||||||
|
change(HomeAction.UpdateState::class) { action, _ ->
|
||||||
|
action.state
|
||||||
|
},
|
||||||
|
|
||||||
|
change(HomeAction.UpdateInvitesCount::class) { action, state ->
|
||||||
|
when (state) {
|
||||||
|
HomeScreenState.Loading -> state
|
||||||
|
is HomeScreenState.SignedIn -> state.copy(invites = action.invitesCount)
|
||||||
|
HomeScreenState.SignedOut -> state
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async(HomeAction.LifecycleVisible::class) { _ ->
|
||||||
|
if (chatEngine.isSignedIn()) {
|
||||||
|
eventEmitter.invoke(HomeEvent.OnShowContent)
|
||||||
|
dispatch(HomeAction.InitialHome)
|
||||||
|
listenForInviteChanges(chatEngine, jobBag)
|
||||||
|
} else {
|
||||||
|
dispatch(HomeAction.UpdateState(HomeScreenState.SignedOut))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async(HomeAction.InitialHome::class) {
|
||||||
|
val me = chatEngine.me(forceRefresh = false)
|
||||||
|
val nextState = when (val current = getState()) {
|
||||||
|
HomeScreenState.Loading -> HomeScreenState.SignedIn(HomeScreenState.Page.Directory, me, invites = 0)
|
||||||
|
is HomeScreenState.SignedIn -> current.copy(me = me, invites = current.invites)
|
||||||
|
HomeScreenState.SignedOut -> HomeScreenState.SignedIn(HomeScreenState.Page.Directory, me, invites = 0)
|
||||||
|
}
|
||||||
|
dispatch(HomeAction.UpdateState(nextState))
|
||||||
|
},
|
||||||
|
|
||||||
|
async(HomeAction.LoggedIn::class) {
|
||||||
|
dispatch(HomeAction.InitialHome)
|
||||||
|
eventEmitter.invoke(HomeEvent.OnShowContent)
|
||||||
|
listenForInviteChanges(chatEngine, jobBag)
|
||||||
|
},
|
||||||
|
|
||||||
|
multi(HomeAction.ChangePage::class) { action ->
|
||||||
|
change { _, state ->
|
||||||
|
when (state) {
|
||||||
|
is HomeScreenState.SignedIn -> when (action.page) {
|
||||||
|
state.page -> state
|
||||||
|
else -> state.copy(page = action.page).also {
|
||||||
|
async {
|
||||||
|
when (action.page) {
|
||||||
|
HomeScreenState.Page.Directory -> {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
HomeScreenState.Page.Profile -> {
|
||||||
|
dispatch(ComponentLifecycle.OnGone)
|
||||||
|
dispatch(ProfileAction.Reset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HomeScreenState.Loading -> state
|
||||||
|
HomeScreenState.SignedOut -> state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async(HomeAction.ScrollToTop::class) {
|
||||||
|
dispatch(DirectorySideEffect.ScrollToTop)
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
sideEffect(HomeAction.ClearCache::class) { _, _ ->
|
||||||
|
cacheCleaner.cleanCache(removeCredentials = false)
|
||||||
|
betaVersionUpgradeUseCase.notifyUpgraded()
|
||||||
|
eventEmitter.invoke(HomeEvent.Relaunch)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ReducerScope<HomeScreenState>.listenForInviteChanges(chatEngine: ChatEngine, jobBag: JobBag) {
|
||||||
|
jobBag.replace(
|
||||||
|
"invites-count",
|
||||||
|
chatEngine.invites()
|
||||||
|
.onEach { invites ->
|
||||||
|
when (getState()) {
|
||||||
|
is HomeScreenState.SignedIn -> dispatch(HomeAction.UpdateInvitesCount(invites.size))
|
||||||
|
HomeScreenState.Loading,
|
||||||
|
HomeScreenState.SignedOut -> {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.launchIn(coroutineScope)
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package app.dapk.st.home.state
|
||||||
|
|
||||||
|
import app.dapk.st.home.state.HomeScreenState.Page
|
||||||
|
import app.dapk.state.Action
|
||||||
|
|
||||||
|
sealed interface HomeAction : Action {
|
||||||
|
object LifecycleVisible : HomeAction
|
||||||
|
object LifecycleGone : HomeAction
|
||||||
|
|
||||||
|
object ScrollToTop : HomeAction
|
||||||
|
object ClearCache : HomeAction
|
||||||
|
object LoggedIn : HomeAction
|
||||||
|
|
||||||
|
data class ChangePage(val page: Page) : HomeAction
|
||||||
|
data class UpdateInvitesCount(val invitesCount: Int) : HomeAction
|
||||||
|
data class UpdateState(val state: HomeScreenState) : HomeAction
|
||||||
|
object InitialHome : HomeAction
|
||||||
|
}
|
|
@ -1,10 +1,13 @@
|
||||||
package app.dapk.st.home
|
package app.dapk.st.home.state
|
||||||
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Menu
|
import androidx.compose.material.icons.filled.Menu
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import app.dapk.st.engine.Me
|
import app.dapk.st.engine.Me
|
||||||
|
import app.dapk.st.state.State
|
||||||
|
|
||||||
|
typealias HomeState = State<HomeScreenState, HomeEvent>
|
||||||
|
|
||||||
sealed interface HomeScreenState {
|
sealed interface HomeScreenState {
|
||||||
|
|
|
@ -3,11 +3,10 @@ package app.dapk.st.login
|
||||||
import app.dapk.st.core.ProvidableModule
|
import app.dapk.st.core.ProvidableModule
|
||||||
import app.dapk.st.core.extensions.ErrorTracker
|
import app.dapk.st.core.extensions.ErrorTracker
|
||||||
import app.dapk.st.engine.ChatEngine
|
import app.dapk.st.engine.ChatEngine
|
||||||
import app.dapk.st.login.state.LoginState
|
import app.dapk.st.login.state.*
|
||||||
import app.dapk.st.login.state.LoginUseCase
|
|
||||||
import app.dapk.st.login.state.loginReducer
|
|
||||||
import app.dapk.st.push.PushModule
|
import app.dapk.st.push.PushModule
|
||||||
import app.dapk.st.state.createStateViewModel
|
import app.dapk.st.state.createStateViewModel
|
||||||
|
import app.dapk.state.ReducerFactory
|
||||||
|
|
||||||
class LoginModule(
|
class LoginModule(
|
||||||
private val chatEngine: ChatEngine,
|
private val chatEngine: ChatEngine,
|
||||||
|
@ -17,8 +16,12 @@ class LoginModule(
|
||||||
|
|
||||||
fun loginState(): LoginState {
|
fun loginState(): LoginState {
|
||||||
return createStateViewModel {
|
return createStateViewModel {
|
||||||
val loginUseCase = LoginUseCase(chatEngine, pushModule.pushTokenRegistrars(), errorTracker)
|
loginReducer(it)
|
||||||
loginReducer(loginUseCase, it)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun loginReducer(eventEmitter: suspend (LoginEvent) -> Unit): ReducerFactory<LoginScreenState> {
|
||||||
|
val loginUseCase = LoginUseCase(chatEngine, pushModule.pushTokenRegistrars(), errorTracker)
|
||||||
|
return loginReducer(loginUseCase, eventEmitter)
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -15,7 +15,9 @@ class ProfileModule(
|
||||||
) : ProvidableModule {
|
) : ProvidableModule {
|
||||||
|
|
||||||
fun profileState(): ProfileState {
|
fun profileState(): ProfileState {
|
||||||
return createStateViewModel { profileReducer(chatEngine, errorTracker, ProfileUseCase(chatEngine, errorTracker), JobBag()) }
|
return createStateViewModel { profileReducer() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun profileReducer() = profileReducer(chatEngine, errorTracker, ProfileUseCase(chatEngine, errorTracker), JobBag())
|
||||||
|
|
||||||
}
|
}
|
|
@ -1 +1 @@
|
||||||
Subproject commit a0425cb9196ba728309b1f2ab616df6ad1168b90
|
Subproject commit 9abb6b4418f451d81f09c4ba2b26f2b1ffd19f55
|
Loading…
Reference in New Issue