diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsAction.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsAction.kt new file mode 100644 index 0000000000000..99c5759edca87 --- /dev/null +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsAction.kt @@ -0,0 +1,28 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings + +import mozilla.components.concept.ai.controls.AIFeatureMetadata +import mozilla.components.lib.state.Action + +/** + * Actions that can be dispatched on the settings [SettingsStore]. + */ +sealed class SettingsAction : Action { + /** Dispatched once when the settings screen is first created. */ + data object SettingsViewCreated : SettingsAction() + + /** Emitted when an AI feature's enabled state has been loaded. */ + data class AIControlsFeatureStateLoaded( + val enabled: Boolean, + val id: AIFeatureMetadata.FeatureId, + ) : SettingsAction() + + /** Emitted when the page-summaries settings have been loaded from disk. */ + data class PageSummariesSettingsLoaded( + val isFeatureEnabled: Boolean, + val isGestureEnabled: Boolean, + ) : SettingsAction() +} diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt index b9c948180af6d..7fbcc03b22235 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt @@ -41,6 +41,8 @@ import mozilla.components.concept.sync.AuthType import mozilla.components.concept.sync.OAuthAccount import mozilla.components.concept.sync.Profile import mozilla.components.feature.addons.ui.AddonFilePicker +import mozilla.components.feature.summarize.settings.SummarizationSettings +import mozilla.components.lib.state.helpers.StoreProvider.Companion.navBackStackStore import mozilla.components.service.fxrelay.eligibility.Eligible import mozilla.components.support.base.feature.ViewBoundFeatureWrapper import mozilla.components.support.ktx.android.view.showKeyboard @@ -83,6 +85,7 @@ import org.mozilla.fenix.snackbar.FenixSnackbarDelegate import org.mozilla.fenix.snackbar.SnackbarBinding import org.mozilla.fenix.utils.Settings import java.lang.ref.WeakReference +import kotlin.getValue import kotlin.system.exitProcess import mozilla.components.ui.icons.R as iconsR import org.mozilla.fenix.GleanMetrics.Settings as SettingsMetrics @@ -132,6 +135,8 @@ class SettingsFragment : PreferenceFragmentCompat(), SystemInsetsPaddedFragment components = requireContext().components + initializeSettingsStore() + accountUiView = AccountUiView( fragment = this, scope = lifecycleScope, @@ -205,6 +210,27 @@ class SettingsFragment : PreferenceFragmentCompat(), SystemInsetsPaddedFragment setPreferencesFromResource(R.xml.preferences, rootKey) } + private fun initializeSettingsStore() { + val settingsOwner = findNavController().getBackStackEntry(R.id.settingsFragment) + val settingsStore: SettingsStore by findNavController().currentBackStackEntry!!.navBackStackStore( + initialState = SettingsState(), + factory = { + SettingsStore( + initialState = SettingsState(), + reducer = ::settingsReducer, + middleware = listOf( + SettingsMiddleware( + featureRegistry = components.aiFeatureRegistry, + summarizationSettings = SummarizationSettings.dataStore(requireContext()), + scope = settingsOwner.lifecycleScope, + ), + ), + ) + }, + ) + settingsStore.dispatch(SettingsAction.SettingsViewCreated) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsMiddleware.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsMiddleware.kt new file mode 100644 index 0000000000000..286b76d74a8a0 --- /dev/null +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsMiddleware.kt @@ -0,0 +1,56 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.launch +import mozilla.components.concept.ai.controls.AIFeatureRegistry +import mozilla.components.feature.summarize.settings.SummarizationSettings +import mozilla.components.lib.state.Middleware +import mozilla.components.lib.state.Store + +/** + * Middleware that observes external sources (AI feature registry, summarization settings) + * and dispatches load actions into the settings [SettingsStore]. + */ +class SettingsMiddleware( + val featureRegistry: AIFeatureRegistry, + val summarizationSettings: SummarizationSettings, + val scope: CoroutineScope, +) : Middleware { + override fun invoke( + store: Store, + next: (SettingsAction) -> Unit, + action: SettingsAction, + ) { + next(action) + when (action) { + is SettingsAction.SettingsViewCreated -> { + featureRegistry.getFeatures().forEach { feature -> + scope.launch { + feature.isEnabled.collect { + store.dispatch(SettingsAction.AIControlsFeatureStateLoaded(it, feature.id)) + } + } + } + scope.launch { + combine( + summarizationSettings.getFeatureEnabledUserStatus(), + summarizationSettings.getGestureEnabledUserStatus(), + ) { isFeatureEnabled, isGestureEnabled -> + SettingsAction.PageSummariesSettingsLoaded( + isFeatureEnabled = isFeatureEnabled, + isGestureEnabled = isGestureEnabled, + ) + }.collect { store.dispatch(it) } + } + } + + is SettingsAction.AIControlsFeatureStateLoaded -> Unit + is SettingsAction.PageSummariesSettingsLoaded -> Unit + } + } +} diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsReducer.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsReducer.kt new file mode 100644 index 0000000000000..19649e542730f --- /dev/null +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsReducer.kt @@ -0,0 +1,23 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings + +/** + * Reducer for [SettingsStore] — applies [SettingsAction]s to [SettingsState]. + */ +fun settingsReducer(state: SettingsState, action: SettingsAction): SettingsState = when (action) { + is SettingsAction.AIControlsFeatureStateLoaded -> state.copy( + aiControlsState = state.aiControlsState.copy( + featuresEnabled = state.aiControlsState.featuresEnabled + (action.id to action.enabled), + ), + ) + is SettingsAction.PageSummariesSettingsLoaded -> state.copy( + summarizeSettingsState = state.summarizeSettingsState.copy( + isFeatureEnabled = action.isFeatureEnabled, + isGestureEnabled = action.isGestureEnabled, + ), + ) + SettingsAction.SettingsViewCreated -> state +} diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsState.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsState.kt new file mode 100644 index 0000000000000..f273ab11a63a9 --- /dev/null +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsState.kt @@ -0,0 +1,22 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings + +import mozilla.components.concept.ai.controls.AIFeatureMetadata +import mozilla.components.feature.summarize.settings.SummarizeSettingsState +import mozilla.components.lib.state.State + +/** + * State for the AI Controls section of the settings screen. + */ +data class AIControlsState(val featuresEnabled: Map = mapOf()) + +/** + * State for the settings screen. + */ +data class SettingsState( + val aiControlsState: AIControlsState = AIControlsState(), + val summarizeSettingsState: SummarizeSettingsState = SummarizeSettingsState(), +) : State diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsStore.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsStore.kt new file mode 100644 index 0000000000000..d33fbd7599211 --- /dev/null +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsStore.kt @@ -0,0 +1,12 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings + +import mozilla.components.lib.state.Store + +/** + * [Store] for handling [SettingsState] and dispatching [SettingsAction]s. + */ +typealias SettingsStore = Store diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/ai/AIControlsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/ai/AIControlsFragment.kt index fbe793eb2217f..7ec194dfbeb57 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/ai/AIControlsFragment.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/ai/AIControlsFragment.kt @@ -8,21 +8,29 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.fragment.app.Fragment import androidx.fragment.compose.content +import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import mozilla.components.lib.state.helpers.StoreProvider.Companion.storeProvider import mozilla.telemetry.glean.private.NoExtras import org.mozilla.fenix.GleanMetrics.GenaiAiControls import org.mozilla.fenix.R import org.mozilla.fenix.e2e.SystemInsetsPaddedFragment import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.showToolbar +import org.mozilla.fenix.settings.SettingsState +import org.mozilla.fenix.settings.SettingsStore import org.mozilla.fenix.settings.SupportUtils +import org.mozilla.fenix.settings.settingsReducer import org.mozilla.fenix.theme.FirefoxTheme +import kotlin.getValue /** * A fragment displaying the AI Controls settings screen. @@ -34,74 +42,98 @@ class AIControlsFragment : Fragment(), SystemInsetsPaddedFragment { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, - ): View = content { - val registry = requireComponents.aiFeatureRegistry - val features = remember { registry.getFeatures() } - val featureBlock = requireComponents.aiControlsFeatureBlock - val scope = rememberCoroutineScope() + ): View { + val navController = findNavController() + val settingsStore: SettingsStore = + navController.getBackStackEntry(R.id.settingsFragment) + .storeProvider.get { persistedState -> + SettingsStore(persistedState ?: SettingsState(), ::settingsReducer, middleware = listOf()) + } - val aiBlockUiController = remember { - AIBlockUIController.default( - featureBlock = featureBlock, - scope = scope, - ) - } + return content { + val registry = requireComponents.aiFeatureRegistry + val features = remember { registry.getFeatures() } + val featureBlock = requireComponents.aiControlsFeatureBlock + val scope = rememberCoroutineScope() + + val aiBlockUiController = remember { + AIBlockUIController.default( + featureBlock = featureBlock, + scope = scope, + ) + } - val showDialog = aiBlockUiController.showDialogFlow.collectAsState() - val isBlocked = featureBlock.isBlocked.collectAsState(initial = false) + val showDialog = aiBlockUiController.showDialogFlow.collectAsState() + val isBlocked = featureBlock.isBlocked.collectAsState(initial = false) + val settingsState = settingsStore.stateFlow.collectAsState() + val callbacks = rememberAIControlsCallbacks(aiBlockUiController, scope) - FirefoxTheme { - AIControlsScreen( - registeredFeatures = features, - showDialog = showDialog.value, - isBlocked = isBlocked.value, - itemToScrollTo = args.preferenceToScrollTo, - onDialogDismiss = { - GenaiAiControls.globalPrefConfirmationClick.record( - GenaiAiControls.GlobalPrefConfirmationClickExtra(element = "cancel"), - ) - aiBlockUiController.onDialogDismiss() - }, - onDialogConfirm = { - GenaiAiControls.globalPrefConfirmationClick.record( - GenaiAiControls.GlobalPrefConfirmationClickExtra(element = "block"), - ) - aiBlockUiController.onDialogConfirm() - }, - onToggle = { currentlyBlocked -> - GenaiAiControls.globalPrefToggle.record( - GenaiAiControls.GlobalPrefToggleExtra(block = !currentlyBlocked), - ) - if (!currentlyBlocked) { - GenaiAiControls.globalPrefConfirmationShown.record(NoExtras()) - } - aiBlockUiController.onToggle(currentlyBlocked) - }, - onFeatureToggle = { feature, enabled -> - GenaiAiControls.featurePrefChange.record( - GenaiAiControls.FeaturePrefChangeExtra( - feature = feature.id.value, - selection = if (enabled) "enabled" else "blocked", - ), - ) - scope.launch { feature.set(enabled) } - }, - onFeatureNavLinkClick = { destination, featureId -> - GenaiAiControls.featureLinkClick.record( - GenaiAiControls.FeatureLinkClickExtra(link = featureId), - ) - destination.nav(this) - }, - onBannerLearnMoreClick = { - GenaiAiControls.featureLinkClick.record( - GenaiAiControls.FeatureLinkClickExtra(link = "global_control"), - ) - openAiControlsSumoPage() - }, - ) + FirefoxTheme { + AIControlsScreen( + state = AIControlsScreenState( + featureEnabledState = settingsState.value.aiControlsState.featuresEnabled, + registeredFeatures = features, + showDialog = showDialog.value, + isBlocked = isBlocked.value, + itemToScrollTo = args.preferenceToScrollTo, + ), + callbacks = callbacks, + ) + } } } + @Composable + private fun rememberAIControlsCallbacks( + aiBlockUiController: AIBlockUIController, + scope: CoroutineScope, + ): AIControlsCallbacks = remember { + AIControlsCallbacks( + onDialogDismiss = { + GenaiAiControls.globalPrefConfirmationClick.record( + GenaiAiControls.GlobalPrefConfirmationClickExtra(element = "cancel"), + ) + aiBlockUiController.onDialogDismiss() + }, + onDialogConfirm = { + GenaiAiControls.globalPrefConfirmationClick.record( + GenaiAiControls.GlobalPrefConfirmationClickExtra(element = "block"), + ) + aiBlockUiController.onDialogConfirm() + }, + onToggle = { currentlyBlocked -> + GenaiAiControls.globalPrefToggle.record( + GenaiAiControls.GlobalPrefToggleExtra(block = !currentlyBlocked), + ) + if (!currentlyBlocked) { + GenaiAiControls.globalPrefConfirmationShown.record(NoExtras()) + } + aiBlockUiController.onToggle(currentlyBlocked) + }, + onFeatureToggle = { feature, enabled -> + GenaiAiControls.featurePrefChange.record( + GenaiAiControls.FeaturePrefChangeExtra( + feature = feature.id.value, + selection = if (enabled) "enabled" else "blocked", + ), + ) + scope.launch { feature.set(enabled) } + }, + onFeatureNavLinkClick = { destination, featureId -> + GenaiAiControls.featureLinkClick.record( + GenaiAiControls.FeatureLinkClickExtra(link = featureId), + ) + destination.nav(this@AIControlsFragment) + }, + onBannerLearnMoreClick = { + GenaiAiControls.featureLinkClick.record( + GenaiAiControls.FeatureLinkClickExtra(link = "global_control"), + ) + openAiControlsSumoPage() + }, + ) + } + override fun onResume() { super.onResume() // Ensures the toolbar shows when navigating to this fragment via Global Directions. diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/ai/AIControlsScreen.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/ai/AIControlsScreen.kt index 745fa2230c91c..2c0bb8617959c 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/ai/AIControlsScreen.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/ai/AIControlsScreen.kt @@ -39,7 +39,6 @@ import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.collectAsStateWithLifecycle import mozilla.components.compose.base.InfoCard import mozilla.components.compose.base.InfoType import mozilla.components.compose.base.LinkText @@ -48,6 +47,7 @@ import mozilla.components.compose.base.PromoCard import mozilla.components.compose.base.annotation.FlexibleWindowPreview import mozilla.components.compose.base.button.TextButton import mozilla.components.concept.ai.controls.AIControllableFeature +import mozilla.components.concept.ai.controls.AIFeatureMetadata import org.mozilla.fenix.R import org.mozilla.fenix.compose.list.IconListItem import org.mozilla.fenix.compose.list.SwitchListItem @@ -63,40 +63,51 @@ import mozilla.components.ui.icons.R as iconsR private const val HEADER_ITEM_COUNT = 2 +/** + * UI state for [AIControlsScreen]. + */ +internal data class AIControlsScreenState( + val featureEnabledState: Map = mapOf(), + val registeredFeatures: List = emptyList(), + val showDialog: Boolean = false, + val isBlocked: Boolean = false, + val itemToScrollTo: String? = null, +) + +/** + * Callback bundle for [AIControlsScreen]. + */ +internal data class AIControlsCallbacks( + val onDialogDismiss: () -> Unit = {}, + val onDialogConfirm: () -> Unit = {}, + val onToggle: (Boolean) -> Unit = {}, + val onFeatureToggle: (AIControllableFeature, Boolean) -> Unit = { _, _ -> }, + val onFeatureNavLinkClick: (AIFeatureMetadataDestination, String) -> Unit = { _, _ -> }, + val onBannerLearnMoreClick: () -> Unit = {}, +) + @Composable internal fun AIControlsScreen( - registeredFeatures: List = emptyList(), - showDialog: Boolean, - isBlocked: Boolean, - itemToScrollTo: String? = null, - onDialogDismiss: () -> Unit, - onDialogConfirm: () -> Unit, - onToggle: (Boolean) -> Unit, - onFeatureToggle: (AIControllableFeature, Boolean) -> Unit = { _, _ -> }, - onFeatureNavLinkClick: (AIFeatureMetadataDestination, String) -> Unit, - onBannerLearnMoreClick: () -> Unit, + state: AIControlsScreenState, + callbacks: AIControlsCallbacks, ) { Surface { - if (showDialog) { + if (state.showDialog) { BlockAIDialog( - registeredFeatures = registeredFeatures, - onDismiss = { onDialogDismiss() }, - onConfirm = { onDialogConfirm() }, + registeredFeatures = state.registeredFeatures, + onDismiss = { callbacks.onDialogDismiss() }, + onConfirm = { callbacks.onDialogConfirm() }, ) } val lazyListState = rememberLazyListState() - ScrollToItemEffect(itemToScrollTo, registeredFeatures, lazyListState) + ScrollToItemEffect(state.itemToScrollTo, state.registeredFeatures, lazyListState) AIControlsList( + state = state, lazyListState = lazyListState, - registeredFeatures = registeredFeatures, - isBlocked = isBlocked, - onToggle = onToggle, - onFeatureToggle = onFeatureToggle, - onFeatureNavLinkClick = onFeatureNavLinkClick, - onBannerLearnMoreClick = onBannerLearnMoreClick, + callbacks = callbacks, ) } } @@ -122,13 +133,9 @@ private fun ScrollToItemEffect( @Composable private fun AIControlsList( + state: AIControlsScreenState, lazyListState: LazyListState, - registeredFeatures: List, - isBlocked: Boolean, - onToggle: (Boolean) -> Unit, - onFeatureToggle: (AIControllableFeature, Boolean) -> Unit, - onFeatureNavLinkClick: (AIFeatureMetadataDestination, String) -> Unit, - onBannerLearnMoreClick: () -> Unit, + callbacks: AIControlsCallbacks, ) { LazyColumn( modifier = Modifier.fillMaxSize(), @@ -136,9 +143,9 @@ private fun AIControlsList( ) { item { AIControlsHeader( - isBlocked = isBlocked, - onToggle = onToggle, - onBannerLearnMoreClick = onBannerLearnMoreClick, + isBlocked = state.isBlocked, + onToggle = callbacks.onToggle, + onBannerLearnMoreClick = callbacks.onBannerLearnMoreClick, ) } @@ -147,13 +154,14 @@ private fun AIControlsList( } items( - items = registeredFeatures, + items = state.registeredFeatures, key = { it.id.value }, ) { feature -> FeatureRow( + featureEnabledState = state.featureEnabledState, feature = feature, - onFeatureToggle = onFeatureToggle, - onFeatureNavLinkClick = onFeatureNavLinkClick, + onFeatureToggle = callbacks.onFeatureToggle, + onFeatureNavLinkClick = callbacks.onFeatureNavLinkClick, ) } } @@ -214,11 +222,12 @@ private fun AIFeaturesHeader() { @Composable private fun FeatureRow( + featureEnabledState: Map, feature: AIControllableFeature, onFeatureToggle: (AIControllableFeature, Boolean) -> Unit, onFeatureNavLinkClick: (AIFeatureMetadataDestination, String) -> Unit, ) { - val isEnabled by feature.isEnabled.collectAsStateWithLifecycle(initialValue = true) + val isEnabled = featureEnabledState[feature.id] ?: false Column { SwitchListItem( @@ -383,13 +392,8 @@ private fun AIControlsScreenPreview( ) { FirefoxTheme(theme) { AIControlsScreen( - showDialog = false, - isBlocked = false, - onDialogDismiss = {}, - onDialogConfirm = {}, - onToggle = {}, - onFeatureNavLinkClick = { _, _ -> }, - onBannerLearnMoreClick = {}, + state = AIControlsScreenState(), + callbacks = AIControlsCallbacks(), ) } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/pagesummaries/PageSummariesSettingsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/pagesummaries/PageSummariesSettingsFragment.kt index 85c514ba42337..2b89449f951ff 100644 --- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/pagesummaries/PageSummariesSettingsFragment.kt +++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/pagesummaries/PageSummariesSettingsFragment.kt @@ -9,21 +9,26 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.fragment.app.Fragment import androidx.fragment.compose.content import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.findNavController import mozilla.components.feature.summarize.settings.SummarizationSettings import mozilla.components.feature.summarize.settings.SummarizeSettingsContent import mozilla.components.feature.summarize.settings.SummarizeSettingsMiddleware -import mozilla.components.feature.summarize.settings.SummarizeSettingsState import mozilla.components.feature.summarize.settings.SummarizeSettingsStore import mozilla.components.feature.summarize.settings.summarizeSettingsReducer +import mozilla.components.lib.state.helpers.StoreProvider.Companion.storeProvider import org.mozilla.fenix.R import org.mozilla.fenix.e2e.SystemInsetsPaddedFragment import org.mozilla.fenix.ext.showToolbar +import org.mozilla.fenix.settings.SettingsState +import org.mozilla.fenix.settings.SettingsStore import org.mozilla.fenix.settings.SupportUtils +import org.mozilla.fenix.settings.settingsReducer import org.mozilla.fenix.theme.FirefoxTheme /** @@ -42,19 +47,28 @@ class PageSummariesSettingsFragment : Fragment(), SystemInsetsPaddedFragment { savedInstanceState: Bundle?, ): View { val summarizeSettings = SummarizationSettings.dataStore(requireContext()) - val store = SummarizeSettingsStore( - initialState = SummarizeSettingsState(), - reducer = ::summarizeSettingsReducer, - middleware = listOf( - SummarizeSettingsMiddleware( - settings = summarizeSettings, - onLearnMoreClicked = { openLearnMoreLink() }, - scope = viewLifecycleOwner.lifecycleScope, - ), - ), - ) + val navController = findNavController() + val settingsStore: SettingsStore = + navController.getBackStackEntry(R.id.settingsFragment) + .storeProvider.get { persistedState -> + SettingsStore(persistedState ?: SettingsState(), ::settingsReducer, middleware = listOf()) + } return content { + val store = remember { + SummarizeSettingsStore( + initialState = settingsStore.state.summarizeSettingsState, + reducer = ::summarizeSettingsReducer, + middleware = listOf( + SummarizeSettingsMiddleware( + settings = summarizeSettings, + onLearnMoreClicked = { openLearnMoreLink() }, + scope = viewLifecycleOwner.lifecycleScope, + ), + ), + ) + } + FirefoxTheme { SummarizeSettingsContent( store = store,