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
import kotlinx.coroutines.Job
import kotlin.reflect.KClass
class JobBag {
@ -11,8 +12,17 @@ class JobBag {
jobs[key] = job
}
fun replace(key: KClass<*>, job: Job) {
jobs[key.java.canonicalName]?.cancel()
jobs[key.java.canonicalName] = job
}
fun cancel(key: String) {
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 kotlin.reflect.KClass
fun <P : Any> createPageReducer(
initialPage: SpiderPage<out 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<out P> : Action {
data class GoTo<P : Any>(val page: SpiderPage<P>) : PageAction<P>
}
sealed interface PageAction<P> : Action {
data class GoTo<P : Any>(val page: SpiderPage<P>) : PageAction<P>
sealed interface PageStateChange : Action {
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>
}
@ -37,26 +17,23 @@ data class PageContainer<P>(
val page: SpiderPage<out P>
)
fun PageContainer<*>.isDifferentPage(page: SpiderPage<*>): Boolean {
return page::class != this.page::class
}
interface PageReducerScope {
interface PageReducerScope<P> {
fun <PC : Any> withPageContent(page: KClass<PC>, block: PageDispatchScope<PC>.() -> Unit)
fun rawPage(): SpiderPage<out P>
}
interface PageDispatchScope<P> {
fun ReducerScope<*>.pageDispatch(action: PageAction<P>)
fun getPageState(): P?
interface PageDispatchScope<PC> {
fun ReducerScope<*>.pageDispatch(action: PageAction<PC>)
fun getPageState(): PC?
}
fun <P : Any, S : Any> createPageReducer(
initialPage: SpiderPage<out P>,
factory: PageReducerScope.() -> ReducerFactory<S>,
factory: PageReducerScope<P>.() -> ReducerFactory<S>,
): ReducerFactory<Combined2<PageContainer<P>, S>> = shareState {
combineReducers(
createPageReducer(initialPage),
factory(object : PageReducerScope {
factory(object : PageReducerScope<P> {
override fun <PC : Any> withPageContent(page: KClass<PC>, block: PageDispatchScope<PC>.() -> Unit) {
val currentPage = getSharedState().state1.page.state
if (currentPage::class == page) {
@ -73,11 +50,45 @@ fun <P : Any, S : Any> createPageReducer(
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) } }
}

View File

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

View File

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