From 1cce9eecb778286f1ac493fcbc5a465e8eb0448e Mon Sep 17 00:00:00 2001 From: DatLag Date: Sat, 9 Mar 2024 17:53:48 +0100 Subject: [PATCH] cache results --- .../datlag/burningseries/model/Cacheable.kt | 39 +++++++++++++++++++ .../network/state/HomeStateMachine.kt | 29 +++++++++++--- .../network/state/NetworkStateSaver.kt | 10 +++++ .../network/state/ReleaseStateMachine.kt | 28 ++++++++++--- .../network/state/SearchStateMachine.kt | 28 ++++++++++--- 5 files changed, 117 insertions(+), 17 deletions(-) create mode 100644 model/src/commonMain/kotlin/dev/datlag/burningseries/model/Cacheable.kt diff --git a/model/src/commonMain/kotlin/dev/datlag/burningseries/model/Cacheable.kt b/model/src/commonMain/kotlin/dev/datlag/burningseries/model/Cacheable.kt new file mode 100644 index 00000000..1ed5c010 --- /dev/null +++ b/model/src/commonMain/kotlin/dev/datlag/burningseries/model/Cacheable.kt @@ -0,0 +1,39 @@ +package dev.datlag.burningseries.model + +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant +import kotlin.time.Duration +import kotlin.time.Duration.Companion.hours + +class Cacheable(private val timeAlive: Duration = 2.hours) { + private var cachedOn: Instant? = null + private var data: T? = null + + fun cache(data: T): T { + this.data = data + this.cachedOn = Clock.System.now() + return data + } + + fun getAlive(): T? { + val cacheTime = cachedOn ?: return null + + return if (cacheTime.epochSeconds < Clock.System.now().minus(timeAlive).epochSeconds) { + null + } else { + data + } + } + + fun getUnAlive(): T? { + return data + } + + operator fun get(unAlive: Boolean = false): T? { + return if (unAlive) { + getUnAlive() + } else { + getAlive() + } + } +} \ No newline at end of file diff --git a/network/src/commonMain/kotlin/dev/datlag/burningseries/network/state/HomeStateMachine.kt b/network/src/commonMain/kotlin/dev/datlag/burningseries/network/state/HomeStateMachine.kt index 89ec3d01..de886c7d 100644 --- a/network/src/commonMain/kotlin/dev/datlag/burningseries/network/state/HomeStateMachine.kt +++ b/network/src/commonMain/kotlin/dev/datlag/burningseries/network/state/HomeStateMachine.kt @@ -19,14 +19,18 @@ class HomeStateMachine( private val json: Json, private val wrapApi: WrapAPI, private val wrapApiKey: String? -) : FlowReduxStateMachine(initialState = NetworkStateSaver.initialHomeState) { +) : FlowReduxStateMachine(initialState = currentState) { init { spec { inState { onEnterEffect { - NetworkStateSaver.initialHomeState = it + currentState = it } onEnter { state -> + NetworkStateSaver.Cache.home.getAlive()?.let { + return@onEnter state.override { HomeState.Success(it.first, it.second) } + } + val result = suspendCatchResult { var onDeviceReachable: Boolean = true val loadedHome = BurningSeries.getHome(client) ?: run { @@ -37,6 +41,7 @@ class HomeStateMachine( }!! if (loadedHome.episodes.isNotEmpty() || loadedHome.series.isNotEmpty()) { + NetworkStateSaver.Cache.home.cache(loadedHome to onDeviceReachable) HomeState.Success(loadedHome, onDeviceReachable) } else { HomeState.Error @@ -45,17 +50,23 @@ class HomeStateMachine( result.onError { Napier.e("HomeStateError", it) } - state.override { result.asSuccess { HomeState.Error } } + state.override { + result.asSuccess { + NetworkStateSaver.Cache.home.getUnAlive()?.let { + HomeState.Success(it.first, it.second) + } ?: HomeState.Error + } + } } } inState { onEnterEffect { - NetworkStateSaver.initialHomeState = it + currentState = it } } inState { onEnterEffect { - NetworkStateSaver.initialHomeState = it + currentState = it } on { _, state -> state.override { HomeState.Loading } @@ -63,4 +74,12 @@ class HomeStateMachine( } } } + + companion object { + var currentState: HomeState + set(value) { + NetworkStateSaver.initialHomeState = value + } + get() = NetworkStateSaver.initialHomeState + } } \ No newline at end of file diff --git a/network/src/commonMain/kotlin/dev/datlag/burningseries/network/state/NetworkStateSaver.kt b/network/src/commonMain/kotlin/dev/datlag/burningseries/network/state/NetworkStateSaver.kt index 6f489241..beaf7e9e 100644 --- a/network/src/commonMain/kotlin/dev/datlag/burningseries/network/state/NetworkStateSaver.kt +++ b/network/src/commonMain/kotlin/dev/datlag/burningseries/network/state/NetworkStateSaver.kt @@ -1,5 +1,9 @@ package dev.datlag.burningseries.network.state +import dev.datlag.burningseries.model.Cacheable +import dev.datlag.burningseries.model.Genre +import dev.datlag.burningseries.model.Home +import dev.datlag.burningseries.model.Release import dev.datlag.burningseries.model.state.* import dev.gitlive.firebase.auth.FirebaseUser @@ -15,4 +19,10 @@ data object NetworkStateSaver { var initialHomeState: HomeState = HomeState.Loading var initialReleaseState: ReleaseState = ReleaseState.Loading var initialSearchState: SearchState = SearchState.Loading + + internal data object Cache { + val home: Cacheable> = Cacheable() + val release: Cacheable> = Cacheable() + val search: Cacheable> = Cacheable() + } } \ No newline at end of file diff --git a/network/src/commonMain/kotlin/dev/datlag/burningseries/network/state/ReleaseStateMachine.kt b/network/src/commonMain/kotlin/dev/datlag/burningseries/network/state/ReleaseStateMachine.kt index d150b6fa..96811754 100644 --- a/network/src/commonMain/kotlin/dev/datlag/burningseries/network/state/ReleaseStateMachine.kt +++ b/network/src/commonMain/kotlin/dev/datlag/burningseries/network/state/ReleaseStateMachine.kt @@ -9,14 +9,18 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi @OptIn(ExperimentalCoroutinesApi::class) class ReleaseStateMachine( private val gitHub: GitHub -) : FlowReduxStateMachine(initialState = NetworkStateSaver.initialReleaseState) { +) : FlowReduxStateMachine(initialState = currentState) { init { spec { inState { onEnterEffect { - NetworkStateSaver.initialReleaseState = it + currentState = it } onEnter { state -> + NetworkStateSaver.Cache.release.getAlive()?.let { + return@onEnter state.override { ReleaseState.Success(it) } + } + val result = suspendCatchResult { val releases = gitHub.getReleases(owner = "DatL4g", repo = "Burning-Series") val filtered = releases.filterNot { it.draft || it.preRelease } @@ -24,10 +28,14 @@ class ReleaseStateMachine( if (filtered.isEmpty()) { ReleaseState.Error } else { - ReleaseState.Success(filtered) + ReleaseState.Success(filtered.also { + NetworkStateSaver.Cache.release.cache(it) + }) } }.asSuccess { - ReleaseState.Error + NetworkStateSaver.Cache.release.getUnAlive()?.let { + ReleaseState.Success(it) + } ?: ReleaseState.Error } state.override { result } @@ -35,14 +43,22 @@ class ReleaseStateMachine( } inState { onEnterEffect { - NetworkStateSaver.initialReleaseState = it + currentState = it } } inState { onEnterEffect { - NetworkStateSaver.initialReleaseState = it + currentState = it } } } } + + companion object { + var currentState: ReleaseState + set(value) { + NetworkStateSaver.initialReleaseState = value + } + get() = NetworkStateSaver.initialReleaseState + } } \ No newline at end of file diff --git a/network/src/commonMain/kotlin/dev/datlag/burningseries/network/state/SearchStateMachine.kt b/network/src/commonMain/kotlin/dev/datlag/burningseries/network/state/SearchStateMachine.kt index 07194b56..bde3e37b 100644 --- a/network/src/commonMain/kotlin/dev/datlag/burningseries/network/state/SearchStateMachine.kt +++ b/network/src/commonMain/kotlin/dev/datlag/burningseries/network/state/SearchStateMachine.kt @@ -22,14 +22,18 @@ class SearchStateMachine( private val wrapApiKey: String?, private val saveToDB: suspend (SearchState.Success) -> Unit, private val loadFromDB: suspend () -> List -) : FlowReduxStateMachine(initialState = NetworkStateSaver.initialSearchState) { +) : FlowReduxStateMachine(initialState = currentState) { init { spec { inState { onEnterEffect { - NetworkStateSaver.initialSearchState = it + currentState = it } onEnter { state -> + NetworkStateSaver.Cache.search.getAlive()?.let { + return@onEnter state.override { SearchState.Success(it) } + } + val result = suspendCatchResult { val loadedClient = suspendCatching { BurningSeries.getSearch(client) @@ -55,10 +59,14 @@ class SearchStateMachine( if (loadedGenres.isEmpty()) { SearchState.Error } else { - SearchState.Success(loadedGenres) + SearchState.Success(loadedGenres.also { + NetworkStateSaver.Cache.search.cache(it) + }) } }.asSuccess { - SearchState.Error + NetworkStateSaver.Cache.search.getUnAlive()?.let { + SearchState.Success(it) + } ?: SearchState.Error } state.override { result } @@ -67,14 +75,14 @@ class SearchStateMachine( inState { onEnterEffect { - NetworkStateSaver.initialSearchState = it + currentState = it saveToDB(it) } } inState { onEnterEffect { - NetworkStateSaver.initialSearchState = it + currentState = it } on { _, state -> state.override { SearchState.Loading } @@ -82,4 +90,12 @@ class SearchStateMachine( } } } + + companion object { + var currentState: SearchState + set(value) { + NetworkStateSaver.initialSearchState = value + } + get() = NetworkStateSaver.initialSearchState + } } \ No newline at end of file