From 27593bdb25cf0b0c7100191dcc1c92eeac094510 Mon Sep 17 00:00:00 2001 From: Ash Davies <1892070+ashdavies@users.noreply.github.com> Date: Thu, 5 Sep 2024 00:42:21 +0200 Subject: [PATCH] Refactor circuit component (#1151) * Refactor circuit component * Remote extra new line --------- Co-authored-by: Ashley Davies --- .../kotlin/io/ashdavies/party/MainActivity.kt | 6 +- .../ashdavies/party/config/CircuitConfig.kt | 95 +++++++++++++++---- .../ashdavies/party/events/EventsFactory.kt | 23 ----- .../ashdavies/party/events/EventsPresenter.kt | 39 +------- .../party/events/paging/EventPager.kt | 2 +- .../ashdavies/party/gallery/GalleryFactory.kt | 20 ---- .../io/ashdavies/party/home/HomeFactory.kt | 21 ---- .../io/ashdavies/party/home/HomePresenter.kt | 31 ------ 8 files changed, 83 insertions(+), 154 deletions(-) delete mode 100644 conferences-app/src/commonMain/kotlin/io/ashdavies/party/events/EventsFactory.kt delete mode 100644 conferences-app/src/commonMain/kotlin/io/ashdavies/party/gallery/GalleryFactory.kt delete mode 100644 conferences-app/src/commonMain/kotlin/io/ashdavies/party/home/HomeFactory.kt diff --git a/conferences-app/src/androidMain/kotlin/io/ashdavies/party/MainActivity.kt b/conferences-app/src/androidMain/kotlin/io/ashdavies/party/MainActivity.kt index 2e5fe062f..0cbb54004 100644 --- a/conferences-app/src/androidMain/kotlin/io/ashdavies/party/MainActivity.kt +++ b/conferences-app/src/androidMain/kotlin/io/ashdavies/party/MainActivity.kt @@ -8,11 +8,9 @@ import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import androidx.core.content.pm.PackageInfoCompat import com.slack.circuit.backstack.rememberSaveableBackStack -import com.slack.circuit.foundation.Circuit import com.slack.circuit.foundation.CircuitCompositionLocals import com.slack.circuit.foundation.NavigableCircuitContent import com.slack.circuit.foundation.rememberCircuitNavigator @@ -24,7 +22,7 @@ import io.ashdavies.http.ProvideHttpClient import io.ashdavies.http.publicStorage import io.ashdavies.io.resolveCacheDir import io.ashdavies.material.dynamicColorScheme -import io.ashdavies.party.config.Circuit +import io.ashdavies.party.config.rememberCircuit import io.ashdavies.party.home.HomeScreen import io.ashdavies.playground.BuildConfig import io.ashdavies.playground.PlaygroundDatabase @@ -67,7 +65,7 @@ private fun LauncherApp(context: Context = LocalContext.current) { } }, ) { - CircuitCompositionLocals(remember { Circuit(context) }) { + CircuitCompositionLocals(rememberCircuit(context)) { ContentWithOverlays { ProvideAppCheckToken { val transacter = rememberTransacter( diff --git a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/config/CircuitConfig.kt b/conferences-app/src/commonMain/kotlin/io/ashdavies/party/config/CircuitConfig.kt index 8ee0c0cff..6ae67a4ff 100644 --- a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/config/CircuitConfig.kt +++ b/conferences-app/src/commonMain/kotlin/io/ashdavies/party/config/CircuitConfig.kt @@ -1,27 +1,82 @@ package io.ashdavies.party.config +import androidx.compose.runtime.Composable +import androidx.compose.runtime.RememberObserver +import androidx.compose.runtime.Stable +import androidx.compose.runtime.remember +import androidx.paging.Pager import com.slack.circuit.foundation.Circuit +import com.slack.circuit.retained.rememberRetained +import com.slack.circuit.runtime.presenter.presenterOf import io.ashdavies.content.PlatformContext -import io.ashdavies.party.events.eventsPresenterFactory -import io.ashdavies.party.events.eventsUiFactory -import io.ashdavies.party.gallery.galleryPresenterFactory -import io.ashdavies.party.gallery.galleryUiFactory -import io.ashdavies.party.home.homePresenterFactory -import io.ashdavies.party.home.homeUiFactory +import io.ashdavies.identity.IdentityManager +import io.ashdavies.party.events.EventsPresenter +import io.ashdavies.party.events.EventsScreen +import io.ashdavies.party.events.paging.rememberEventPager +import io.ashdavies.party.gallery.GalleryPresenter +import io.ashdavies.party.gallery.GalleryScreen +import io.ashdavies.party.gallery.PathProvider +import io.ashdavies.party.gallery.StorageManager +import io.ashdavies.party.home.HomePresenter +import io.ashdavies.party.home.HomeScreen +import io.ashdavies.playground.PlaygroundDatabase +import io.ashdavies.sql.LocalTransacter +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.cancel +import kotlin.coroutines.CoroutineContext +import io.ashdavies.party.events.Event as DatabaseEvent -public fun Circuit(context: PlatformContext): Circuit = Circuit.Builder() - .addPresenterFactories(getPresenterFactories(context)) - .addUiFactories(getUiFactories(context)) - .build() +private const val COROUTINE_SCOPE = "COROUTINE_SCOPE" -private fun getPresenterFactories(context: PlatformContext) = listOf( - homePresenterFactory(context), - eventsPresenterFactory(), - galleryPresenterFactory(context), -) +@Composable +public fun rememberCircuit( + platformContext: PlatformContext, + eventPager: Pager = rememberEventPager(), + playgroundDatabase: PlaygroundDatabase = LocalTransacter.current as PlaygroundDatabase, + coroutineScope: CoroutineScope = rememberRetainedCoroutineScope(), +): Circuit = remember(platformContext) { + val identityManager = IdentityManager(platformContext, playgroundDatabase.credentialQueries) + val storageManager = StorageManager(PathProvider(platformContext)) -private fun getUiFactories(context: PlatformContext) = listOf( - homeUiFactory(context), - eventsUiFactory(), - galleryUiFactory(context), -) + Circuit.Builder() + .addPresenter { _, navigator, _ -> + presenterOf { HomePresenter(identityManager, coroutineScope, navigator) } + } + .addPresenter { _, _, _ -> + presenterOf { EventsPresenter(eventPager, coroutineScope) } + } + .addPresenter { _, _, _ -> + presenterOf { GalleryPresenter(platformContext) } + } + .addUi { state, modifier -> + HomeScreen(state, modifier) + } + .addUi { state, modifier -> + EventsScreen(state, modifier) + } + .addUi { state, modifier -> + GalleryScreen(state, storageManager, modifier) + } + .build() +} + +@Stable +private class StableCoroutineScope(scope: CoroutineScope) : CoroutineScope by scope + +@Composable +private fun rememberRetainedCoroutineScope( + key: String = COROUTINE_SCOPE, + context: CoroutineContext = Dispatchers.Main.immediate, +): StableCoroutineScope = rememberRetained(key) { + val coroutineScope = StableCoroutineScope(CoroutineScope(context + Job())) + rememberObserver(coroutineScope::cancel) + coroutineScope +} + +private fun rememberObserver(onForgotten: () -> Unit) = object : RememberObserver { + override fun onAbandoned() = onForgotten() + override fun onForgotten() = onForgotten() + override fun onRemembered() = Unit +} diff --git a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/events/EventsFactory.kt b/conferences-app/src/commonMain/kotlin/io/ashdavies/party/events/EventsFactory.kt deleted file mode 100644 index ee58b09b8..000000000 --- a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/events/EventsFactory.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.ashdavies.party.events - -import com.slack.circuit.runtime.presenter.Presenter -import com.slack.circuit.runtime.presenter.presenterOf -import com.slack.circuit.runtime.ui.Ui -import com.slack.circuit.runtime.ui.ui - -public fun eventsPresenterFactory(): Presenter.Factory = Presenter.Factory { screen, _, _ -> - when (screen) { - is EventsScreen -> presenterOf { EventsPresenter() } - else -> null - } -} - -public fun eventsUiFactory(): Ui.Factory = Ui.Factory { screen, _ -> - when (screen) { - is EventsScreen -> ui { state, modifier -> - EventsScreen(state, modifier) - } - - else -> null - } -} diff --git a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/events/EventsPresenter.kt b/conferences-app/src/commonMain/kotlin/io/ashdavies/party/events/EventsPresenter.kt index 58ad1df0c..583ce0443 100644 --- a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/events/EventsPresenter.kt +++ b/conferences-app/src/commonMain/kotlin/io/ashdavies/party/events/EventsPresenter.kt @@ -1,51 +1,22 @@ package io.ashdavies.party.events import androidx.compose.runtime.Composable -import androidx.compose.runtime.RememberObserver -import androidx.compose.runtime.Stable -import androidx.paging.ExperimentalPagingApi import androidx.paging.Pager import androidx.paging.cachedIn import com.slack.circuit.retained.rememberRetained import io.ashdavies.paging.collectAsLazyPagingItems -import io.ashdavies.party.events.paging.rememberEventPager import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.cancel -import kotlin.coroutines.CoroutineContext - -private const val COROUTINE_SCOPE = "COROUTINE_SCOPE" @Composable -@OptIn(ExperimentalPagingApi::class) internal fun EventsPresenter( - coroutineScope: CoroutineScope = rememberRetainedCoroutineScope(), - eventPager: Pager = rememberEventPager(), + eventPager: Pager, + coroutineScope: CoroutineScope, ): EventsScreen.State { - val pagingData = rememberRetained(coroutineScope) { + val pagingItems = rememberRetained(coroutineScope) { eventPager.flow.cachedIn(coroutineScope) - } + }.collectAsLazyPagingItems() return EventsScreen.State( - pagingItems = pagingData.collectAsLazyPagingItems(), + pagingItems = pagingItems, ) } - -@Stable -private class StableCoroutineScope(scope: CoroutineScope) : CoroutineScope by scope - -@Composable -private fun rememberRetainedCoroutineScope( - context: CoroutineContext = Dispatchers.Main.immediate, -): StableCoroutineScope = rememberRetained(COROUTINE_SCOPE) { - val coroutineScope = StableCoroutineScope(CoroutineScope(context + Job())) - rememberObserver(coroutineScope::cancel) - coroutineScope -} - -private fun rememberObserver(onForgotten: () -> Unit) = object : RememberObserver { - override fun onAbandoned() = onForgotten() - override fun onForgotten() = onForgotten() - override fun onRemembered() = Unit -} diff --git a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/events/paging/EventPager.kt b/conferences-app/src/commonMain/kotlin/io/ashdavies/party/events/paging/EventPager.kt index 3939097bc..dee63b9bf 100644 --- a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/events/paging/EventPager.kt +++ b/conferences-app/src/commonMain/kotlin/io/ashdavies/party/events/paging/EventPager.kt @@ -29,7 +29,7 @@ private const val DEFAULT_PAGE_SIZE = 10 private suspend fun RemoteConfig.isPagingEnabled() = getBoolean("paging_enabled") @Composable -@ExperimentalPagingApi +@OptIn(ExperimentalPagingApi::class) internal fun rememberEventPager( eventsQueries: EventsQueries = rememberLocalQueries { it.eventsQueries }, eventsCallable: PagedUpcomingEventsCallable = rememberUpcomingEventsCallable(), diff --git a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/gallery/GalleryFactory.kt b/conferences-app/src/commonMain/kotlin/io/ashdavies/party/gallery/GalleryFactory.kt deleted file mode 100644 index 8c9f55a74..000000000 --- a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/gallery/GalleryFactory.kt +++ /dev/null @@ -1,20 +0,0 @@ -package io.ashdavies.party.gallery - -import androidx.compose.runtime.remember -import com.slack.circuit.runtime.presenter.Presenter -import com.slack.circuit.runtime.ui.Ui -import io.ashdavies.circuit.presenterFactoryOf -import io.ashdavies.circuit.uiFactoryOf -import io.ashdavies.content.PlatformContext -import io.ashdavies.content.reportFullyDrawn - -public fun galleryPresenterFactory(context: PlatformContext): Presenter.Factory { - return presenterFactoryOf { _, _ -> GalleryPresenter(context) } -} - -public fun galleryUiFactory(context: PlatformContext): Ui.Factory { - return uiFactoryOf { _, state, modifier -> - GalleryScreen(state, remember { StorageManager(PathProvider(context)) }, modifier) - context.reportFullyDrawn() - } -} diff --git a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/home/HomeFactory.kt b/conferences-app/src/commonMain/kotlin/io/ashdavies/party/home/HomeFactory.kt deleted file mode 100644 index 6d56d30c5..000000000 --- a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/home/HomeFactory.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.ashdavies.party.home - -import com.slack.circuit.runtime.presenter.Presenter -import com.slack.circuit.runtime.ui.Ui -import io.ashdavies.circuit.presenterFactoryOf -import io.ashdavies.circuit.uiFactoryOf -import io.ashdavies.content.PlatformContext -import io.ashdavies.content.reportFullyDrawn - -public fun homePresenterFactory(context: PlatformContext): Presenter.Factory { - return presenterFactoryOf { _, navigator -> - HomePresenter(context, navigator) - } -} - -public fun homeUiFactory(context: PlatformContext): Ui.Factory { - return uiFactoryOf { _, state, modifier -> - HomeScreen(state, modifier) - context.reportFullyDrawn() - } -} diff --git a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/home/HomePresenter.kt b/conferences-app/src/commonMain/kotlin/io/ashdavies/party/home/HomePresenter.kt index e121d1d90..15a38fab7 100644 --- a/conferences-app/src/commonMain/kotlin/io/ashdavies/party/home/HomePresenter.kt +++ b/conferences-app/src/commonMain/kotlin/io/ashdavies/party/home/HomePresenter.kt @@ -4,42 +4,22 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import com.slack.circuit.foundation.onNavEvent import com.slack.circuit.retained.rememberRetained import com.slack.circuit.runtime.Navigator import com.slack.circuit.runtime.screen.Screen -import io.ashdavies.content.PlatformContext -import io.ashdavies.identity.CredentialQueries import io.ashdavies.identity.IdentityManager import io.ashdavies.identity.IdentityState import io.ashdavies.party.config.booleanConfigAsState import io.ashdavies.party.config.isHomeEnabled import io.ashdavies.party.events.EventsScreen import io.ashdavies.party.gallery.GalleryScreen -import io.ashdavies.party.sql.rememberLocalQueries import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @Composable internal fun HomePresenter( - platformContext: PlatformContext, - navigator: Navigator, -): HomeScreen.State = HomePresenter( - identityManager = rememberIdentityManager( - platformContext = platformContext, - credentialQueries = rememberLocalQueries { - it.credentialQueries - }, - ), - coroutineScope = rememberCoroutineScope(), - navigator = navigator, -) - -@Composable -private fun HomePresenter( identityManager: IdentityManager, coroutineScope: CoroutineScope, navigator: Navigator, @@ -61,14 +41,3 @@ private fun HomePresenter( } } } - -@Composable -private fun rememberIdentityManager( - platformContext: PlatformContext, - credentialQueries: CredentialQueries, -): IdentityManager = remember(platformContext, credentialQueries) { - IdentityManager( - platformContext = platformContext, - credentialQueries = credentialQueries, - ) -}