porting page reducers to new iteration

This commit is contained in:
Adam Brown 2022-11-02 20:55:50 +00:00
parent 7a13f530b0
commit f0e5ae4502
4 changed files with 105 additions and 91 deletions

View File

@ -1,6 +1,7 @@
package app.dapk.st.core package app.dapk.st.core
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlin.reflect.KClass
class JobBag { class JobBag {
@ -11,8 +12,17 @@ class JobBag {
jobs[key] = job jobs[key] = job
} }
fun replace(key: KClass<*>, job: Job) {
jobs[key.java.canonicalName]?.cancel()
jobs[key.java.canonicalName] = job
}
fun cancel(key: String) { fun cancel(key: String) {
jobs.remove(key)?.cancel() jobs.remove(key)?.cancel()
} }
fun cancel(key: KClass<*>) {
jobs.remove(key.java.canonicalName)?.cancel()
}
} }

View File

@ -4,32 +4,12 @@ import app.dapk.st.design.components.SpiderPage
import app.dapk.state.* import app.dapk.state.*
import kotlin.reflect.KClass import kotlin.reflect.KClass
fun <P : Any> createPageReducer( sealed interface PageAction<out P> : Action {
initialPage: SpiderPage<out P> data class GoTo<P : Any>(val page: SpiderPage<P>) : PageAction<P>
): ReducerFactory<PageContainer<P>> {
return createReducer(
initialState = PageContainer(
page = initialPage
),
change(PageAction.GoTo::class) { action, state ->
state.copy(page = action.page as SpiderPage<P>)
},
change(PageAction.UpdatePage::class) { action, state ->
val isSamePage = state.page.state::class == action.pageContent::class
if (isSamePage) {
val updatedPageContent = (state.page as SpiderPage<Any>).copy(state = action.pageContent)
state.copy(page = updatedPageContent as SpiderPage<out P>)
} else {
state
}
},
)
} }
sealed interface PageAction<P> : Action { sealed interface PageStateChange : Action {
data class GoTo<P : Any>(val page: SpiderPage<P>) : PageAction<P> data class ChangePage<P : Any>(val previous: SpiderPage<out P>, val newPage: SpiderPage<out P>) : PageAction<P>
data class UpdatePage<P : Any>(val pageContent: P) : PageAction<P> data class UpdatePage<P : Any>(val pageContent: P) : PageAction<P>
} }
@ -37,26 +17,23 @@ data class PageContainer<P>(
val page: SpiderPage<out P> val page: SpiderPage<out P>
) )
fun PageContainer<*>.isDifferentPage(page: SpiderPage<*>): Boolean { interface PageReducerScope<P> {
return page::class != this.page::class
}
interface PageReducerScope {
fun <PC : Any> withPageContent(page: KClass<PC>, block: PageDispatchScope<PC>.() -> Unit) fun <PC : Any> withPageContent(page: KClass<PC>, block: PageDispatchScope<PC>.() -> Unit)
fun rawPage(): SpiderPage<out P>
} }
interface PageDispatchScope<P> { interface PageDispatchScope<PC> {
fun ReducerScope<*>.pageDispatch(action: PageAction<P>) fun ReducerScope<*>.pageDispatch(action: PageAction<PC>)
fun getPageState(): P? fun getPageState(): PC?
} }
fun <P : Any, S : Any> createPageReducer( fun <P : Any, S : Any> createPageReducer(
initialPage: SpiderPage<out P>, initialPage: SpiderPage<out P>,
factory: PageReducerScope.() -> ReducerFactory<S>, factory: PageReducerScope<P>.() -> ReducerFactory<S>,
): ReducerFactory<Combined2<PageContainer<P>, S>> = shareState { ): ReducerFactory<Combined2<PageContainer<P>, S>> = shareState {
combineReducers( combineReducers(
createPageReducer(initialPage), createPageReducer(initialPage),
factory(object : PageReducerScope { factory(object : PageReducerScope<P> {
override fun <PC : Any> withPageContent(page: KClass<PC>, block: PageDispatchScope<PC>.() -> Unit) { override fun <PC : Any> withPageContent(page: KClass<PC>, block: PageDispatchScope<PC>.() -> Unit) {
val currentPage = getSharedState().state1.page.state val currentPage = getSharedState().state1.page.state
if (currentPage::class == page) { if (currentPage::class == page) {
@ -73,11 +50,45 @@ fun <P : Any, S : Any> createPageReducer(
block(pageDispatchScope) block(pageDispatchScope)
} }
} }
override fun rawPage() = getSharedState().state1.page
}) })
) )
} }
inline fun <reified PC : Any> PageReducerScope.withPageContext(crossinline block: PageDispatchScope<PC>.(PC) -> Unit) { @Suppress("UNCHECKED_CAST")
private fun <P : Any> createPageReducer(
initialPage: SpiderPage<out P>
): ReducerFactory<PageContainer<P>> {
return createReducer(
initialState = PageContainer(
page = initialPage
),
async(PageAction.GoTo::class) { action ->
val state = getState()
if (state.page.state::class != action.page.state::class) {
dispatch(PageStateChange.ChangePage(previous = state.page, newPage = action.page))
}
},
change(PageStateChange.ChangePage::class) { action, state ->
state.copy(page = action.newPage as SpiderPage<out P>)
},
change(PageStateChange.UpdatePage::class) { action, state ->
val isSamePage = state.page.state::class == action.pageContent::class
if (isSamePage) {
val updatedPageContent = (state.page as SpiderPage<Any>).copy(state = action.pageContent)
state.copy(page = updatedPageContent as SpiderPage<out P>)
} else {
state
}
},
)
}
inline fun <reified PC : Any> PageReducerScope<*>.withPageContext(crossinline block: PageDispatchScope<PC>.(PC) -> Unit) {
withPageContent(PC::class) { getPageState()?.let { block(it) } } withPageContent(PC::class) { getPageState()?.let { block(it) } }
} }

View File

@ -3,11 +3,15 @@ package app.dapk.st.messenger.gallery.state
import app.dapk.st.core.JobBag import app.dapk.st.core.JobBag
import app.dapk.st.core.Lce import app.dapk.st.core.Lce
import app.dapk.st.core.page.PageAction import app.dapk.st.core.page.PageAction
import app.dapk.st.core.page.PageContainer import app.dapk.st.core.page.PageStateChange
import app.dapk.st.core.page.isDifferentPage import app.dapk.st.core.page.createPageReducer
import app.dapk.st.core.page.withPageContext
import app.dapk.st.design.components.SpiderPage import app.dapk.st.design.components.SpiderPage
import app.dapk.st.messenger.gallery.* import app.dapk.st.messenger.gallery.FetchMediaFoldersUseCase
import app.dapk.state.* import app.dapk.st.messenger.gallery.FetchMediaUseCase
import app.dapk.state.async
import app.dapk.state.createReducer
import app.dapk.state.sideEffect
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
fun imageGalleryReducer( fun imageGalleryReducer(
@ -15,54 +19,47 @@ fun imageGalleryReducer(
foldersUseCase: FetchMediaFoldersUseCase, foldersUseCase: FetchMediaFoldersUseCase,
fetchMediaUseCase: FetchMediaUseCase, fetchMediaUseCase: FetchMediaUseCase,
jobBag: JobBag, jobBag: JobBag,
) = shareState { ) = createPageReducer(
combineReducers( initialPage = SpiderPage<ImageGalleryPage>(
createPageReducer(roomName),
createImageGalleryPageReducer(jobBag, foldersUseCase, fetchMediaUseCase),
)
}
private fun createPageReducer(roomName: String): ReducerFactory<PageContainer<ImageGalleryPage>> = app.dapk.st.core.page.createPageReducer(
initialPage = SpiderPage(
route = ImageGalleryPage.Routes.folders, route = ImageGalleryPage.Routes.folders,
label = "Send to $roomName", label = "Send to $roomName",
parent = null, parent = null,
state = ImageGalleryPage.Folders(Lce.Loading()) state = ImageGalleryPage.Folders(Lce.Loading())
) ),
) factory = {
createReducer(
private fun SharedStateScope<Combined2<PageContainer<ImageGalleryPage>, Unit>>.createImageGalleryPageReducer(
jobBag: JobBag,
foldersUseCase: FetchMediaFoldersUseCase,
fetchMediaUseCase: FetchMediaUseCase
) = createReducer(
initialState = Unit, initialState = Unit,
async(ImageGalleryActions.Visible::class) { async(ImageGalleryActions.Visible::class) {
jobBag.replace("page", coroutineScope.launch { jobBag.replace(ImageGalleryPage.Folders::class, coroutineScope.launch {
val folders = foldersUseCase.fetchFolders() val folders = foldersUseCase.fetchFolders()
dispatch(PageAction.UpdatePage(ImageGalleryPage.Folders(Lce.Content(folders)))) withPageContext<ImageGalleryPage.Folders> {
pageDispatch(PageStateChange.UpdatePage(it.copy(content = Lce.Content(folders))))
}
}) })
}, },
async(ImageGalleryActions.SelectFolder::class) { action -> async(ImageGalleryActions.SelectFolder::class) { action ->
val page = SpiderPage( val page = SpiderPage(
route = ImageGalleryPage.Routes.files, route = ImageGalleryPage.Routes.files,
label = getSharedState().state1.page.label, label = rawPage().label,
parent = ImageGalleryPage.Routes.folders, parent = ImageGalleryPage.Routes.folders,
state = ImageGalleryPage.Files(Lce.Loading(), action.folder) state = ImageGalleryPage.Files(Lce.Loading(), action.folder)
) )
dispatch(PageAction.GoTo(page)) dispatch(PageAction.GoTo(page))
jobBag.replace("page", coroutineScope.launch { jobBag.replace(ImageGalleryPage.Files::class, coroutineScope.launch {
val media = fetchMediaUseCase.getMediaInBucket(action.folder.bucketId) val media = fetchMediaUseCase.getMediaInBucket(action.folder.bucketId)
dispatch(PageAction.UpdatePage(page.state.copy(content = Lce.Content(media)))) withPageContext<ImageGalleryPage.Files> {
pageDispatch(PageStateChange.UpdatePage(it.copy(content = Lce.Content(media))))
}
}) })
}, },
sideEffect(PageAction.GoTo::class) { action, _ -> sideEffect(PageStateChange.ChangePage::class) { action, _ ->
if (getSharedState().state1.isDifferentPage(action.page)) { jobBag.cancel(action.previous::class)
jobBag.cancel("page") },
} )
} }
) )

View File

@ -5,10 +5,7 @@ import app.dapk.st.core.JobBag
import app.dapk.st.core.Lce import app.dapk.st.core.Lce
import app.dapk.st.core.State import app.dapk.st.core.State
import app.dapk.st.core.ThemeStore import app.dapk.st.core.ThemeStore
import app.dapk.st.core.page.PageAction import app.dapk.st.core.page.*
import app.dapk.st.core.page.PageContainer
import app.dapk.st.core.page.createPageReducer
import app.dapk.st.core.page.withPageContext
import app.dapk.st.design.components.SpiderPage import app.dapk.st.design.components.SpiderPage
import app.dapk.st.domain.StoreCleaner import app.dapk.st.domain.StoreCleaner
import app.dapk.st.domain.application.eventlog.LoggingStore import app.dapk.st.domain.application.eventlog.LoggingStore
@ -57,14 +54,14 @@ internal fun settingsReducer(
async(RootActions.FetchProviders::class) { async(RootActions.FetchProviders::class) {
withPageContext<Page.PushProviders> { withPageContext<Page.PushProviders> {
pageDispatch(PageAction.UpdatePage(it.copy(options = Lce.Loading()))) pageDispatch(PageStateChange.UpdatePage(it.copy(options = Lce.Loading())))
} }
val currentSelection = pushTokenRegistrars.currentSelection() val currentSelection = pushTokenRegistrars.currentSelection()
val options = pushTokenRegistrars.options() val options = pushTokenRegistrars.options()
withPageContext<Page.PushProviders> { withPageContext<Page.PushProviders> {
pageDispatch( pageDispatch(
PageAction.UpdatePage( PageStateChange.UpdatePage(
it.copy( it.copy(
selection = currentSelection, selection = currentSelection,
options = Lce.Content(options) options = Lce.Content(options)
@ -82,7 +79,7 @@ internal fun settingsReducer(
async(RootActions.ImportKeysFromFile::class) { action -> async(RootActions.ImportKeysFromFile::class) { action ->
withPageContext<Page.ImportRoomKey> { withPageContext<Page.ImportRoomKey> {
pageDispatch(PageAction.UpdatePage(it.copy(importProgress = ImportResult.Update(0)))) pageDispatch(PageStateChange.UpdatePage(it.copy(importProgress = ImportResult.Update(0))))
} }
with(chatEngine) { with(chatEngine) {
@ -92,7 +89,7 @@ internal fun settingsReducer(
fileStream.importRoomKeys(action.passphrase) fileStream.importRoomKeys(action.passphrase)
.onEach { progress -> .onEach { progress ->
withPageContext<Page.ImportRoomKey> { withPageContext<Page.ImportRoomKey> {
pageDispatch(PageAction.UpdatePage(it.copy(importProgress = progress))) pageDispatch(PageStateChange.UpdatePage(it.copy(importProgress = progress)))
} }
} }
.launchIn(coroutineScope) .launchIn(coroutineScope)
@ -100,7 +97,7 @@ internal fun settingsReducer(
onFailure = { onFailure = {
withPageContext<Page.ImportRoomKey> { withPageContext<Page.ImportRoomKey> {
pageDispatch(PageAction.UpdatePage(it.copy(importProgress = ImportResult.Error(ImportResult.Error.Type.UnableToOpenFile)))) pageDispatch(PageStateChange.UpdatePage(it.copy(importProgress = ImportResult.Error(ImportResult.Error.Type.UnableToOpenFile))))
} }
} }
) )
@ -114,7 +111,7 @@ internal fun settingsReducer(
) )
withPageContext<Page.ImportRoomKey> { withPageContext<Page.ImportRoomKey> {
pageDispatch(PageAction.UpdatePage(it.copy(selectedFile = namedFile))) pageDispatch(PageStateChange.UpdatePage(it.copy(selectedFile = namedFile)))
} }
}, },
@ -181,5 +178,4 @@ internal fun settingsReducer(
} }
) )
internal typealias SettingsState = State<Combined2<PageContainer<Page>, Unit>, SettingsEvent> internal typealias SettingsState = State<Combined2<PageContainer<Page>, Unit>, SettingsEvent>