For #24114 - Refactor HomeFragmentStore parameters to AppStore

This commit is contained in:
Gabriel Luong 2022-03-06 20:13:19 -05:00 committed by mergify[bot]
parent c2e3d251cb
commit aaa3d7b977
20 changed files with 140 additions and 149 deletions

View File

@ -88,6 +88,7 @@ import org.mozilla.fenix.components.PrivateShortcutCreateManager
import org.mozilla.fenix.components.StoreProvider
import org.mozilla.fenix.components.TabCollectionStorage
import org.mozilla.fenix.components.accounts.AccountState
import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.toolbar.FenixTabCounterMenu
import org.mozilla.fenix.components.toolbar.ToolbarPosition
@ -269,9 +270,9 @@ class HomeFragment : Fragment() {
.groupBy { story -> story.category }
.map { (category, stories) -> PocketRecommendedStoriesCategory(category, stories) }
homeFragmentStore.dispatch(HomeFragmentAction.PocketStoriesCategoriesChange(categories))
components.appStore.dispatch(AppAction.PocketStoriesCategoriesChange(categories))
} else {
homeFragmentStore.dispatch(HomeFragmentAction.PocketStoriesChange(emptyList()))
components.appStore.dispatch(AppAction.PocketStoriesChange(emptyList()))
}
}
@ -279,7 +280,7 @@ class HomeFragment : Fragment() {
topSitesFeature.set(
feature = TopSitesFeature(
view = DefaultTopSitesView(
store = homeFragmentStore,
store = components.appStore,
settings = components.settings
),
storage = components.core.topSitesStorage,
@ -294,7 +295,7 @@ class HomeFragment : Fragment() {
recentTabsListFeature.set(
feature = RecentTabsListFeature(
browserStore = components.core.store,
homeStore = homeFragmentStore
appStore = components.appStore
),
owner = viewLifecycleOwner,
view = binding.root
@ -304,7 +305,7 @@ class HomeFragment : Fragment() {
if (requireContext().settings().showRecentBookmarksFeature) {
recentBookmarksFeature.set(
feature = RecentBookmarksFeature(
homeStore = homeFragmentStore,
appStore = components.appStore,
bookmarksUseCase = run {
requireContext().components.useCases.bookmarksUseCases
},
@ -318,7 +319,7 @@ class HomeFragment : Fragment() {
if (requireContext().settings().historyMetadataUIFeature) {
historyMetadataFeature.set(
feature = RecentVisitsFeature(
homeStore = homeFragmentStore,
appStore = components.appStore,
historyMetadataStorage = components.core.historyStorage,
historyHighlightsStorage = components.core.lazyHistoryStorage,
scope = viewLifecycleOwner.lifecycleScope
@ -340,7 +341,7 @@ class HomeFragment : Fragment() {
restoreUseCase = components.useCases.tabsUseCases.restore,
reloadUrlUseCase = components.useCases.sessionUseCases.reload,
selectTabUseCase = components.useCases.tabsUseCases.selectTab,
fragmentStore = homeFragmentStore,
appStore = components.appStore,
navController = findNavController(),
viewLifecycleScope = viewLifecycleOwner.lifecycleScope,
hideOnboarding = ::hideOnboardingAndOpenSearch,
@ -353,16 +354,16 @@ class HomeFragment : Fragment() {
navController = findNavController(),
metrics = requireComponents.analytics.metrics,
store = components.core.store,
homeStore = homeFragmentStore,
appStore = components.appStore,
),
recentBookmarksController = DefaultRecentBookmarksController(
activity = activity,
navController = findNavController(),
homeStore = homeFragmentStore,
appStore = components.appStore,
),
recentVisitsController = DefaultRecentVisitsController(
navController = findNavController(),
homeStore = homeFragmentStore,
appStore = components.appStore,
selectOrAddTabUseCase = components.useCases.tabsUseCases.selectOrAddTab,
storage = components.core.historyStorage,
scope = viewLifecycleOwner.lifecycleScope,
@ -371,7 +372,7 @@ class HomeFragment : Fragment() {
),
pocketStoriesController = DefaultPocketStoriesController(
homeActivity = activity,
homeStore = homeFragmentStore,
appStore = components.appStore,
navController = findNavController(),
metrics = requireComponents.analytics.metrics
)
@ -379,7 +380,7 @@ class HomeFragment : Fragment() {
updateLayout(binding.root)
sessionControlView = SessionControlView(
homeFragmentStore,
components.appStore,
binding.sessionControlRecyclerView,
viewLifecycleOwner,
sessionControlInteractor
@ -435,13 +436,13 @@ class HomeFragment : Fragment() {
*/
private fun updateSessionControlView() {
if (browsingModeManager.mode == BrowsingMode.Private) {
binding.root.consumeFrom(homeFragmentStore, viewLifecycleOwner) {
binding.root.consumeFrom(requireContext().components.appStore, viewLifecycleOwner) {
sessionControlView?.update(it)
}
} else {
sessionControlView?.update(homeFragmentStore.state)
sessionControlView?.update(requireContext().components.appStore.state)
binding.root.consumeFrom(homeFragmentStore, viewLifecycleOwner) {
binding.root.consumeFrom(requireContext().components.appStore, viewLifecycleOwner) {
sessionControlView?.update(it, shouldReportMetrics = true)
}
}
@ -763,7 +764,7 @@ class HomeFragment : Fragment() {
private fun dispatchModeChanges(mode: Mode) {
if (mode != Mode.fromBrowsingMode(browsingModeManager.mode)) {
homeFragmentStore.dispatch(HomeFragmentAction.ModeChange(mode))
requireContext().components.appStore.dispatch(AppAction.ModeChange(mode))
}
}
@ -884,8 +885,8 @@ class HomeFragment : Fragment() {
private fun hideOnboardingIfNeeded() {
if (!onboarding.userHasBeenOnboarded()) {
onboarding.finish()
homeFragmentStore.dispatch(
HomeFragmentAction.ModeChange(
requireContext().components.appStore.dispatch(
AppAction.ModeChange(
mode = currentMode.getCurrentMode()
)
)
@ -1019,7 +1020,7 @@ class HomeFragment : Fragment() {
private fun subscribeToTabCollections(): Observer<List<TabCollection>> {
return Observer<List<TabCollection>> {
requireComponents.core.tabCollectionStorage.cachedTabCollections = it
homeFragmentStore.dispatch(HomeFragmentAction.CollectionsChange(it))
requireComponents.appStore.dispatch(AppAction.CollectionsChange(it))
}.also { observer ->
requireComponents.core.tabCollectionStorage.getCollections().observe(this, observer)
}

View File

@ -7,6 +7,7 @@ package org.mozilla.fenix.home
import androidx.annotation.VisibleForTesting
import androidx.datastore.core.DataStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import mozilla.components.lib.state.Action
@ -15,32 +16,35 @@ import mozilla.components.lib.state.MiddlewareContext
import mozilla.components.lib.state.Store
import mozilla.components.service.pocket.PocketRecommendedStory
import mozilla.components.service.pocket.PocketStoriesService
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.components.appstate.AppState
import org.mozilla.fenix.datastore.SelectedPocketStoriesCategories
import org.mozilla.fenix.datastore.SelectedPocketStoriesCategories.SelectedPocketStoriesCategory
import org.mozilla.fenix.home.pocket.PocketRecommendedStoriesCategory
import org.mozilla.fenix.home.pocket.PocketRecommendedStoriesSelectedCategory
/**
* [HomeFragmentStore] middleware reacting in response to Pocket related [Action]s.
* [AppStore] middleware reacting in response to Pocket related [Action]s.
*
* @param coroutineScope [CoroutineScope] used for long running operations like disk IO.
* @param pocketStoriesService [PocketStoriesService] used for updating details about the Pocket recommended stories.
* @param selectedPocketCategoriesDataStore [DataStore] used for reading or persisting details about the
* currently selected Pocket recommended stories categories.
* @param coroutineScope [CoroutineScope] used for long running operations like disk IO.
*/
class PocketUpdatesMiddleware(
private val coroutineScope: CoroutineScope,
private val pocketStoriesService: PocketStoriesService,
private val selectedPocketCategoriesDataStore: DataStore<SelectedPocketStoriesCategories>
) : Middleware<HomeFragmentState, HomeFragmentAction> {
private val selectedPocketCategoriesDataStore: DataStore<SelectedPocketStoriesCategories>,
private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO),
) : Middleware<AppState, AppAction> {
override fun invoke(
context: MiddlewareContext<HomeFragmentState, HomeFragmentAction>,
next: (HomeFragmentAction) -> Unit,
action: HomeFragmentAction
context: MiddlewareContext<AppState, AppAction>,
next: (AppAction) -> Unit,
action: AppAction
) {
// Pre process actions
when (action) {
is HomeFragmentAction.PocketStoriesCategoriesChange -> {
is AppAction.PocketStoriesCategoriesChange -> {
// Intercept the original action which would only update categories and
// dispatch a new action which also updates which categories are selected by the user
// from previous locally persisted data.
@ -60,7 +64,7 @@ class PocketUpdatesMiddleware(
// Post process actions
when (action) {
is HomeFragmentAction.PocketStoriesShown -> {
is AppAction.PocketStoriesShown -> {
persistStories(
coroutineScope = coroutineScope,
pocketStoriesService = pocketStoriesService,
@ -69,8 +73,8 @@ class PocketUpdatesMiddleware(
}
)
}
is HomeFragmentAction.SelectPocketStoriesCategory,
is HomeFragmentAction.DeselectPocketStoriesCategory -> {
is AppAction.SelectPocketStoriesCategory,
is AppAction.DeselectPocketStoriesCategory -> {
persistSelectedCategories(
coroutineScope = coroutineScope,
currentCategoriesSelections = context.state.pocketStoriesCategoriesSelections,
@ -135,7 +139,7 @@ internal fun persistSelectedCategories(
/**
* Combines [currentCategories] with the locally persisted data about previously selected categories
* and emits a new [HomeFragmentAction.PocketStoriesCategoriesSelectionsChange] to update these in store.
* and emits a new [AppAction.PocketStoriesCategoriesSelectionsChange] to update these in store.
*
* @param coroutineScope [CoroutineScope] used for reading the locally persisted data.
* @param currentCategories Stories categories currently available
@ -147,13 +151,13 @@ internal fun persistSelectedCategories(
internal fun restoreSelectedCategories(
coroutineScope: CoroutineScope,
currentCategories: List<PocketRecommendedStoriesCategory>,
store: Store<HomeFragmentState, HomeFragmentAction>,
store: Store<AppState, AppAction>,
selectedPocketCategoriesDataStore: DataStore<SelectedPocketStoriesCategories>
) {
coroutineScope.launch {
selectedPocketCategoriesDataStore.data.collect { persistedSelectedCategories ->
store.dispatch(
HomeFragmentAction.PocketStoriesCategoriesSelectionsChange(
AppAction.PocketStoriesCategoriesSelectionsChange(
currentCategories,
persistedSelectedCategories.valuesList.map {
PocketRecommendedStoriesSelectedCategory(

View File

@ -6,8 +6,8 @@ package org.mozilla.fenix.home.blocklist
import mozilla.components.lib.state.Middleware
import mozilla.components.lib.state.MiddlewareContext
import org.mozilla.fenix.home.HomeFragmentAction
import org.mozilla.fenix.home.HomeFragmentState
import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.components.appstate.AppState
import org.mozilla.fenix.home.recenttabs.RecentTab
/**
@ -18,46 +18,46 @@ import org.mozilla.fenix.home.recenttabs.RecentTab
*/
class BlocklistMiddleware(
private val blocklistHandler: BlocklistHandler
) : Middleware<HomeFragmentState, HomeFragmentAction> {
) : Middleware<AppState, AppAction> {
/**
* Will filter "Change" actions using the blocklist and use "Remove" actions to update
* the blocklist.
*/
override fun invoke(
context: MiddlewareContext<HomeFragmentState, HomeFragmentAction>,
next: (HomeFragmentAction) -> Unit,
action: HomeFragmentAction
context: MiddlewareContext<AppState, AppAction>,
next: (AppAction) -> Unit,
action: AppAction
) {
next(getUpdatedAction(context.state, action))
}
private fun getUpdatedAction(
state: HomeFragmentState,
action: HomeFragmentAction
state: AppState,
action: AppAction
) = with(blocklistHandler) {
when (action) {
is HomeFragmentAction.Change -> {
is AppAction.Change -> {
action.copy(
recentBookmarks = action.recentBookmarks.filteredByBlocklist(),
recentTabs = action.recentTabs.filteredByBlocklist(),
recentHistory = action.recentHistory.filteredByBlocklist()
)
}
is HomeFragmentAction.RecentTabsChange -> {
is AppAction.RecentTabsChange -> {
action.copy(
recentTabs = action.recentTabs.filteredByBlocklist()
)
}
is HomeFragmentAction.RecentBookmarksChange -> {
is AppAction.RecentBookmarksChange -> {
action.copy(
recentBookmarks = action.recentBookmarks.filteredByBlocklist()
)
}
is HomeFragmentAction.RecentHistoryChange -> {
is AppAction.RecentHistoryChange -> {
action.copy(recentHistory = action.recentHistory.filteredByBlocklist())
}
is HomeFragmentAction.RemoveRecentTab -> {
is AppAction.RemoveRecentTab -> {
if (action.recentTab is RecentTab.Tab) {
addUrlToBlocklist(action.recentTab.state.content.url)
state.toActionFilteringAllState(this)
@ -65,13 +65,13 @@ class BlocklistMiddleware(
action
}
}
is HomeFragmentAction.RemoveRecentBookmark -> {
is AppAction.RemoveRecentBookmark -> {
action.recentBookmark.url?.let { url ->
addUrlToBlocklist(url)
state.toActionFilteringAllState(this)
} ?: action
}
is HomeFragmentAction.RemoveRecentHistoryHighlight -> {
is AppAction.RemoveRecentHistoryHighlight -> {
addUrlToBlocklist(action.highlightUrl)
state.toActionFilteringAllState(this)
}
@ -83,9 +83,9 @@ class BlocklistMiddleware(
// relevant parts that contain it.
// This is a candidate for refactoring once context receivers lands in Kotlin 1.6.20
// https://blog.jetbrains.com/kotlin/2022/02/kotlin-1-6-20-m1-released/#prototype-of-context-receivers-for-kotlin-jvm
private fun HomeFragmentState.toActionFilteringAllState(blocklistHandler: BlocklistHandler) =
private fun AppState.toActionFilteringAllState(blocklistHandler: BlocklistHandler) =
with(blocklistHandler) {
HomeFragmentAction.Change(
AppAction.Change(
recentTabs = recentTabs.filteredByBlocklist(),
recentBookmarks = recentBookmarks.filteredByBlocklist(),
recentHistory = recentHistory.filteredByBlocklist(),

View File

@ -21,25 +21,24 @@ import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView
import mozilla.components.lib.state.ext.observeAsComposableState
import org.mozilla.fenix.R
import org.mozilla.fenix.components.components
import org.mozilla.fenix.compose.ComposeViewHolder
import org.mozilla.fenix.compose.SectionHeader
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.theme.FirefoxTheme
internal const val POCKET_CATEGORIES_SELECTED_AT_A_TIME_COUNT = 8
/**
* [RecyclerView.ViewHolder] for displaying the list of [PocketRecommendedStoriesCategory]s from [HomeFragmentStore].
* [RecyclerView.ViewHolder] for displaying the list of [PocketRecommendedStoriesCategory]s from
* [AppStore].
*
* @param composeView [ComposeView] which will be populated with Jetpack Compose UI content.
* @param viewLifecycleOwner [LifecycleOwner] to which this Composable will be tied to.
* @param store [HomeFragmentStore] containing the list of Pocket stories categories to be displayed.
* @param interactor [PocketStoriesInteractor] callback for user interaction.
*/
class PocketCategoriesViewHolder(
composeView: ComposeView,
viewLifecycleOwner: LifecycleOwner,
private val store: HomeFragmentStore,
private val interactor: PocketStoriesInteractor
) : ComposeViewHolder(composeView, viewLifecycleOwner) {
@ -49,9 +48,9 @@ class PocketCategoriesViewHolder(
composeView.resources.getDimensionPixelSize(R.dimen.home_item_horizontal_margin)
composeView.setPadding(horizontalPadding, 0, horizontalPadding, 0)
val categories = store
val categories = components.appStore
.observeAsComposableState { state -> state.pocketStoriesCategories }.value
val categoriesSelections = store
val categoriesSelections = components.appStore
.observeAsComposableState { state -> state.pocketStoriesCategoriesSelections }.value
Column {

View File

@ -6,15 +6,14 @@ package org.mozilla.fenix.home.pocket
import androidx.annotation.VisibleForTesting
import androidx.navigation.NavController
import mozilla.components.lib.state.Store
import mozilla.components.service.pocket.PocketRecommendedStory
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.home.HomeFragmentAction
import org.mozilla.fenix.home.HomeFragmentStore
/**
* Contract for how all user interactions with the Pocket recommended stories feature are to be handled.
@ -61,26 +60,26 @@ interface PocketStoriesController {
* Default behavior for handling all user interactions with the Pocket recommended stories feature.
*
* @param homeActivity [HomeActivity] used to open URLs in a new tab.
* @param homeStore [Store] from which to read the current Pocket recommendations and dispatch new actions on.
* @param appStore [AppStore] from which to read the current Pocket recommendations and dispatch new actions on.
* @param navController [NavController] used for navigation.
*/
internal class DefaultPocketStoriesController(
private val homeActivity: HomeActivity,
private val homeStore: HomeFragmentStore,
private val appStore: AppStore,
private val navController: NavController,
private val metrics: MetricController
) : PocketStoriesController {
override fun handleStoriesShown(storiesShown: List<PocketRecommendedStory>) {
homeStore.dispatch(HomeFragmentAction.PocketStoriesShown(storiesShown))
appStore.dispatch(AppAction.PocketStoriesShown(storiesShown))
metrics.track(Event.PocketHomeRecsShown)
}
override fun handleCategoryClick(categoryClicked: PocketRecommendedStoriesCategory) {
val initialCategoriesSelections = homeStore.state.pocketStoriesCategoriesSelections
val initialCategoriesSelections = appStore.state.pocketStoriesCategoriesSelections
// First check whether the category is clicked to be deselected.
if (initialCategoriesSelections.map { it.name }.contains(categoryClicked.name)) {
homeStore.dispatch(HomeFragmentAction.DeselectPocketStoriesCategory(categoryClicked.name))
appStore.dispatch(AppAction.DeselectPocketStoriesCategory(categoryClicked.name))
metrics.track(
Event.PocketHomeRecsCategoryClicked(
categoryClicked.name,
@ -100,11 +99,11 @@ internal class DefaultPocketStoriesController(
null
}
oldestCategoryToDeselect?.let {
homeStore.dispatch(HomeFragmentAction.DeselectPocketStoriesCategory(it.name))
appStore.dispatch(AppAction.DeselectPocketStoriesCategory(it.name))
}
// Finally update the selection.
homeStore.dispatch(HomeFragmentAction.SelectPocketStoriesCategory(categoryClicked.name))
appStore.dispatch(AppAction.SelectPocketStoriesCategory(categoryClicked.name))
metrics.track(
Event.PocketHomeRecsCategoryClicked(

View File

@ -26,25 +26,23 @@ import mozilla.components.lib.state.ext.observeAsComposableState
import mozilla.components.service.pocket.PocketRecommendedStory
import org.mozilla.fenix.R
import org.mozilla.fenix.R.string
import org.mozilla.fenix.components.components
import org.mozilla.fenix.compose.ComposeViewHolder
import org.mozilla.fenix.compose.SectionHeader
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.theme.FirefoxTheme
internal const val POCKET_STORIES_TO_SHOW_COUNT = 8
/**
* [RecyclerView.ViewHolder] for displaying the list of [PocketRecommendedStory]s from [HomeFragmentStore].
* [RecyclerView.ViewHolder] for displaying the list of [PocketRecommendedStory]s from [AppStore].
*
* @param composeView [ComposeView] which will be populated with Jetpack Compose UI content.
* @param viewLifecycleOwner [LifecycleOwner] to which this Composable will be tied to.
* @param store [HomeFragmentStore] containing the list of Pocket stories to be displayed.
* @param interactor [PocketStoriesInteractor] callback for user interaction.
*/
class PocketStoriesViewHolder(
composeView: ComposeView,
viewLifecycleOwner: LifecycleOwner,
private val store: HomeFragmentStore,
private val interactor: PocketStoriesInteractor
) : ComposeViewHolder(composeView, viewLifecycleOwner) {
@ -56,7 +54,7 @@ class PocketStoriesViewHolder(
override fun Content() {
val horizontalPadding = dimensionResource(R.dimen.home_item_horizontal_margin)
val stories = store
val stories = components.appStore
.observeAsComposableState { state -> state.pocketStories }.value
LaunchedEffect(stories) {

View File

@ -11,32 +11,32 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import mozilla.components.concept.storage.BookmarkNode
import mozilla.components.support.base.feature.LifecycleAwareFeature
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.components.bookmarks.BookmarksUseCase
import org.mozilla.fenix.home.HomeFragmentAction
import org.mozilla.fenix.home.HomeFragmentStore
/**
* View-bound feature that retrieves a list of recently added [BookmarkNode]s and dispatches
* updates to the [HomeFragmentStore].
* updates to the [AppStore].
*
* @param homeStore the [HomeFragmentStore]
* @param appStore the [AppStore]
* @param bookmarksUseCase the [BookmarksUseCase] for retrieving the list of recently saved
* bookmarks from storage.
* @param scope the [CoroutineScope] used to fetch the bookmarks list
* @param ioDispatcher the [CoroutineDispatcher] for performing read/write operations.
*/
class RecentBookmarksFeature(
private val homeStore: HomeFragmentStore,
private val appStore: AppStore,
private val bookmarksUseCase: BookmarksUseCase,
private val scope: CoroutineScope,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) : LifecycleAwareFeature {
internal var job: Job? = null
private var job: Job? = null
override fun start() {
job = scope.launch(ioDispatcher) {
val bookmarks = bookmarksUseCase.retrieveRecentBookmarks()
homeStore.dispatch(HomeFragmentAction.RecentBookmarksChange(bookmarks))
appStore.dispatch(AppAction.RecentBookmarksChange(bookmarks))
}
}

View File

@ -13,11 +13,11 @@ import mozilla.components.concept.engine.EngineSession.LoadUrlFlags.Companion.AL
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.home.HomeFragmentAction
import org.mozilla.fenix.home.HomeFragmentDirections
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.home.recentbookmarks.RecentBookmark
import org.mozilla.fenix.home.recentbookmarks.interactor.RecentBookmarksInteractor
@ -49,7 +49,7 @@ interface RecentBookmarksController {
class DefaultRecentBookmarksController(
private val activity: HomeActivity,
private val navController: NavController,
private val homeStore: HomeFragmentStore,
private val appStore: AppStore,
) : RecentBookmarksController {
override fun handleBookmarkClicked(bookmark: RecentBookmark) {
@ -72,7 +72,7 @@ class DefaultRecentBookmarksController(
}
override fun handleBookmarkRemoved(bookmark: RecentBookmark) {
homeStore.dispatch(HomeFragmentAction.RemoveRecentBookmark(bookmark))
appStore.dispatch(AppAction.RemoveRecentBookmark(bookmark))
}
@VisibleForTesting(otherwise = PRIVATE)

View File

@ -11,16 +11,15 @@ import androidx.compose.ui.res.stringResource
import androidx.lifecycle.LifecycleOwner
import mozilla.components.lib.state.ext.observeAsComposableState
import org.mozilla.fenix.R
import org.mozilla.fenix.components.components
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.compose.ComposeViewHolder
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.home.recentbookmarks.interactor.RecentBookmarksInteractor
class RecentBookmarksViewHolder(
composeView: ComposeView,
viewLifecycleOwner: LifecycleOwner,
private val store: HomeFragmentStore,
val interactor: RecentBookmarksInteractor,
val metrics: MetricController
) : ComposeViewHolder(composeView, viewLifecycleOwner) {
@ -35,7 +34,8 @@ class RecentBookmarksViewHolder(
@Composable
override fun Content() {
val recentBookmarks = store.observeAsComposableState { state -> state.recentBookmarks }
val recentBookmarks = components.appStore
.observeAsComposableState { state -> state.recentBookmarks }
RecentBookmarks(
bookmarks = recentBookmarks.value ?: emptyList(),

View File

@ -14,18 +14,18 @@ import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.lib.state.helpers.AbstractBinding
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.ext.asRecentTabs
import org.mozilla.fenix.home.HomeFragmentAction
import org.mozilla.fenix.home.HomeFragmentStore
/**
* View-bound feature that dispatches recent tab changes to the [HomeFragmentStore] when the
* View-bound feature that dispatches recent tab changes to the [AppStore] when the
* [BrowserStore] is updated.
*/
@OptIn(ExperimentalCoroutinesApi::class)
class RecentTabsListFeature(
browserStore: BrowserStore,
private val homeStore: HomeFragmentStore
private val appStore: AppStore
) : AbstractBinding<BrowserState>(browserStore) {
override suspend fun onState(flow: Flow<BrowserState>) {
@ -35,7 +35,7 @@ class RecentTabsListFeature(
.map { it.asRecentTabs() }
.ifChanged()
.collect {
homeStore.dispatch(HomeFragmentAction.RecentTabsChange(it))
appStore.dispatch(AppAction.RecentTabsChange(it))
}
}
}

View File

@ -10,12 +10,12 @@ import androidx.navigation.NavController
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.feature.tabs.TabsUseCases.SelectTabUseCase
import org.mozilla.fenix.R
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.ext.inProgressMediaTab
import org.mozilla.fenix.home.HomeFragmentAction
import org.mozilla.fenix.home.HomeFragmentDirections
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.home.recenttabs.RecentTab
import org.mozilla.fenix.home.recenttabs.interactor.RecentTabInteractor
@ -56,7 +56,7 @@ class DefaultRecentTabsController(
private val navController: NavController,
private val metrics: MetricController,
private val store: BrowserStore,
private val homeStore: HomeFragmentStore,
private val appStore: AppStore,
) : RecentTabController {
override fun handleRecentTabClicked(tabId: String) {
@ -86,7 +86,7 @@ class DefaultRecentTabsController(
}
override fun handleRecentTabRemoved(tab: RecentTab.Tab) {
homeStore.dispatch(HomeFragmentAction.RemoveRecentTab(tab))
appStore.dispatch(AppAction.RemoveRecentTab(tab))
}
@VisibleForTesting(otherwise = PRIVATE)

View File

@ -11,21 +11,19 @@ import androidx.compose.ui.res.stringResource
import androidx.lifecycle.LifecycleOwner
import mozilla.components.lib.state.ext.observeAsComposableState
import org.mozilla.fenix.R
import org.mozilla.fenix.components.components
import org.mozilla.fenix.compose.ComposeViewHolder
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.home.recenttabs.interactor.RecentTabInteractor
/**
* View holder for a recent tab item.
*
* @param composeView [ComposeView] which will be populated with Jetpack Compose UI content.
* @param store [HomeFragmentStore] containing the list of recent tabs to be displayed.
* @param interactor [RecentTabInteractor] which will have delegated to all user interactions.
*/
class RecentTabViewHolder(
composeView: ComposeView,
viewLifecycleOwner: LifecycleOwner,
private val store: HomeFragmentStore,
private val interactor: RecentTabInteractor
) : ComposeViewHolder(composeView, viewLifecycleOwner) {
@ -41,7 +39,7 @@ class RecentTabViewHolder(
@Composable
override fun Content() {
val recentTabs = store.observeAsComposableState { state -> state.recentTabs }
val recentTabs = components.appStore.observeAsComposableState { state -> state.recentTabs }
RecentTabs(
recentTabs = recentTabs.value ?: emptyList(),

View File

@ -18,10 +18,9 @@ import mozilla.components.concept.storage.HistoryMetadata
import mozilla.components.concept.storage.HistoryMetadataStorage
import mozilla.components.support.base.feature.LifecycleAwareFeature
import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.FeatureFlags.historyImprovementFeatures
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.home.HomeFragment
import org.mozilla.fenix.home.HomeFragmentAction
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem.RecentHistoryGroup
import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem.RecentHistoryHighlight
import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItemInternal.HistoryGroupInternal
@ -35,10 +34,10 @@ import kotlin.math.max
/**
* View-bound feature that retrieves a list of [HistoryHighlight]s and [HistoryMetadata] items
* which will be mapped to [RecentlyVisitedItem]s and then dispatched to [HomeFragmentStore]
* to be displayed on the homescreen.
* which will be mapped to [RecentlyVisitedItem]s and then dispatched to [AppStore]
* to be displayed on the home screen.
*
* @param homeStore The [HomeFragmentStore] that holds the state of the [HomeFragment].
* @param appStore The [AppStore] that holds the state of the [HomeFragment].
* @param historyMetadataStorage The storage that manages [HistoryMetadata].
* @param historyHighlightsStorage The storage that manages [PlacesHistoryStorage].
* @param scope The [CoroutineScope] used for IO operations related to querying history
@ -46,7 +45,7 @@ import kotlin.math.max
* @param ioDispatcher The [CoroutineDispatcher] for performing read/write operations.
*/
class RecentVisitsFeature(
private val homeStore: HomeFragmentStore,
private val appStore: AppStore,
private val historyMetadataStorage: HistoryMetadataStorage,
private val historyHighlightsStorage: Lazy<PlacesHistoryStorage>,
private val scope: CoroutineScope,
@ -81,8 +80,8 @@ class RecentVisitsFeature(
historyHighlights: List<HistoryHighlightInternal>,
historyGroups: List<HistoryGroupInternal>
) {
homeStore.dispatch(
HomeFragmentAction.RecentHistoryChange(
appStore.dispatch(
AppAction.RecentHistoryChange(
getCombinedHistory(historyHighlights, historyGroups)
)
)

View File

@ -14,12 +14,11 @@ import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.storage.HistoryMetadataStorage
import mozilla.components.feature.tabs.TabsUseCases.SelectOrAddUseCase
import org.mozilla.fenix.R
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.home.HomeFragmentAction
import org.mozilla.fenix.home.HomeFragmentAction.RemoveRecentHistoryHighlight
import org.mozilla.fenix.home.HomeFragmentDirections
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem.RecentHistoryGroup
import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem.RecentHistoryHighlight
import org.mozilla.fenix.library.history.toHistoryMetadata
@ -68,7 +67,7 @@ interface RecentVisitsController {
*/
class DefaultRecentVisitsController(
private val store: BrowserStore,
private val homeStore: HomeFragmentStore,
private val appStore: AppStore,
private val selectOrAddTabUseCase: SelectOrAddUseCase,
private val navController: NavController,
private val storage: HistoryMetadataStorage,
@ -111,7 +110,7 @@ class DefaultRecentVisitsController(
// First, dispatch actions that will clean up search groups in the two stores that have
// metadata-related state.
store.dispatch(HistoryMetadataAction.DisbandSearchGroupAction(searchTerm = groupTitle))
homeStore.dispatch(HomeFragmentAction.DisbandSearchGroupAction(searchTerm = groupTitle))
appStore.dispatch(AppAction.DisbandSearchGroupAction(searchTerm = groupTitle))
// Then, perform the expensive IO work of removing search groups from storage.
scope.launch {
storage.deleteHistoryMetadata(groupTitle)
@ -136,7 +135,7 @@ class DefaultRecentVisitsController(
* @param highlightUrl The title of the [RecentHistoryHighlight] to be removed.
*/
override fun handleRemoveRecentHistoryHighlight(highlightUrl: String) {
homeStore.dispatch(RemoveRecentHistoryHighlight(highlightUrl))
appStore.dispatch(AppAction.RemoveRecentHistoryHighlight(highlightUrl))
scope.launch {
storage.deleteHistoryMetadataForUrl(highlightUrl)
}

View File

@ -11,10 +11,10 @@ import androidx.compose.ui.res.stringResource
import androidx.lifecycle.LifecycleOwner
import mozilla.components.lib.state.ext.observeAsComposableState
import org.mozilla.fenix.R
import org.mozilla.fenix.components.components
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.compose.ComposeViewHolder
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem
import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem.RecentHistoryGroup
import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem.RecentHistoryHighlight
@ -24,14 +24,12 @@ import org.mozilla.fenix.home.recentvisits.interactor.RecentVisitsInteractor
* View holder for [RecentlyVisitedItem]s.
*
* @param composeView [ComposeView] which will be populated with Jetpack Compose UI content.
* @param store [HomeFragmentStore] containing the list of [RecentlyVisitedItem] to be displayed.
* @property interactor [RecentVisitsInteractor] which will have delegated to all user interactions.
* @property metrics [MetricController] that handles telemetry events.
*/
class RecentlyVisitedViewHolder(
composeView: ComposeView,
viewLifecycleOwner: LifecycleOwner,
private val store: HomeFragmentStore,
private val interactor: RecentVisitsInteractor,
private val metrics: MetricController
) : ComposeViewHolder(composeView, viewLifecycleOwner) {
@ -44,7 +42,8 @@ class RecentlyVisitedViewHolder(
@Composable
override fun Content() {
val recentVisits = store.observeAsComposableState { state -> state.recentHistory }
val recentVisits = components.appStore
.observeAsComposableState { state -> state.recentHistory }
RecentlyVisited(
recentVisits = recentVisits.value ?: emptyList(),

View File

@ -16,9 +16,9 @@ import androidx.recyclerview.widget.RecyclerView
import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.feature.top.sites.TopSite
import mozilla.components.ui.widgets.WidgetSiteItemView
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.components.Components
import org.mozilla.fenix.home.BottomSpacerViewHolder
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.home.TopPlaceholderViewHolder
import org.mozilla.fenix.home.pocket.PocketCategoriesViewHolder
import org.mozilla.fenix.home.pocket.PocketRecommendationsHeaderViewHolder
@ -198,7 +198,7 @@ class AdapterItemDiffCallback : DiffUtil.ItemCallback<AdapterItem>() {
@Suppress("LongParameterList")
class SessionControlAdapter(
private val store: HomeFragmentStore,
private val store: AppStore,
private val interactor: SessionControlInteractor,
private val viewLifecycleOwner: LifecycleOwner,
private val components: Components
@ -216,13 +216,11 @@ class SessionControlAdapter(
PocketStoriesViewHolder.LAYOUT_ID -> return PocketStoriesViewHolder(
composeView = ComposeView(parent.context),
viewLifecycleOwner = viewLifecycleOwner,
store = store,
interactor = interactor
)
PocketCategoriesViewHolder.LAYOUT_ID -> return PocketCategoriesViewHolder(
composeView = ComposeView(parent.context),
viewLifecycleOwner = viewLifecycleOwner,
store = store,
interactor = interactor
)
PocketRecommendationsHeaderViewHolder.LAYOUT_ID -> return PocketRecommendationsHeaderViewHolder(
@ -233,20 +231,17 @@ class SessionControlAdapter(
RecentBookmarksViewHolder.LAYOUT_ID -> return RecentBookmarksViewHolder(
composeView = ComposeView(parent.context),
viewLifecycleOwner,
store = store,
interactor = interactor,
metrics = components.analytics.metrics
)
RecentTabViewHolder.LAYOUT_ID -> return RecentTabViewHolder(
composeView = ComposeView(parent.context),
viewLifecycleOwner,
store = store,
interactor = interactor
)
RecentlyVisitedViewHolder.LAYOUT_ID -> return RecentlyVisitedViewHolder(
composeView = ComposeView(parent.context),
viewLifecycleOwner,
store = store,
interactor = interactor,
metrics = components.analytics.metrics
)

View File

@ -36,7 +36,10 @@ import org.mozilla.fenix.R
import org.mozilla.fenix.browser.BrowserFragmentDirections
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.collections.SaveCollectionStep
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.components.TabCollectionStorage
import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.components.appstate.AppState
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.components.metrics.MetricsUtils
@ -46,10 +49,7 @@ import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.openSetDefaultBrowserOption
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.home.HomeFragment
import org.mozilla.fenix.home.HomeFragmentAction
import org.mozilla.fenix.home.HomeFragmentDirections
import org.mozilla.fenix.home.HomeFragmentState
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.home.Mode
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.settings.SupportUtils.SumoTopic.PRIVATE_BROWSING_MYTHS
@ -200,7 +200,7 @@ interface SessionControlController {
/**
* @see [SessionControlInteractor.reportSessionMetrics]
*/
fun handleReportSessionMetrics(state: HomeFragmentState)
fun handleReportSessionMetrics(state: AppState)
}
@Suppress("TooManyFunctions", "LargeClass")
@ -215,7 +215,7 @@ class DefaultSessionControlController(
private val restoreUseCase: TabsUseCases.RestoreUseCase,
private val reloadUrlUseCase: SessionUseCases.ReloadUrlUseCase,
private val selectTabUseCase: TabsUseCases.SelectTabUseCase,
private val fragmentStore: HomeFragmentStore,
private val appStore: AppStore,
private val navController: NavController,
private val viewLifecycleScope: CoroutineScope,
private val hideOnboarding: () -> Unit,
@ -521,7 +521,7 @@ class DefaultSessionControlController(
}
override fun handleToggleCollectionExpanded(collection: TabCollection, expand: Boolean) {
fragmentStore.dispatch(HomeFragmentAction.CollectionExpanded(collection, expand))
appStore.dispatch(AppAction.CollectionExpanded(collection, expand))
}
private fun showTabTrayCollectionCreation() {
@ -561,7 +561,7 @@ class DefaultSessionControlController(
override fun handleRemoveCollectionsPlaceholder() {
settings.showCollectionsPlaceholderOnHome = false
fragmentStore.dispatch(HomeFragmentAction.RemoveCollectionsPlaceholder)
appStore.dispatch(AppAction.RemoveCollectionsPlaceholder)
}
private fun showShareFragment(shareSubject: String, data: List<ShareData>) {
@ -613,7 +613,7 @@ class DefaultSessionControlController(
override fun handleCloseExperimentCard() {
settings.userDismissedExperimentCard = true
fragmentStore.dispatch(HomeFragmentAction.RemoveSetDefaultBrowserCard)
appStore.dispatch(AppAction.RemoveSetDefaultBrowserCard)
}
override fun handlePrivateModeButtonClicked(
@ -625,8 +625,8 @@ class DefaultSessionControlController(
}
if (userHasBeenOnboarded) {
fragmentStore.dispatch(
HomeFragmentAction.ModeChange(Mode.fromBrowsingMode(newMode))
appStore.dispatch(
AppAction.ModeChange(Mode.fromBrowsingMode(newMode))
)
if (navController.currentDestination?.id == R.id.searchDialogFragment) {
@ -639,7 +639,7 @@ class DefaultSessionControlController(
}
}
override fun handleReportSessionMetrics(state: HomeFragmentState) {
override fun handleReportSessionMetrics(state: AppState) {
with(metrics) {
track(
if (state.recentTabs.isEmpty()) Event.RecentTabsSectionIsNotVisible

View File

@ -9,7 +9,7 @@ import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.feature.top.sites.TopSite
import mozilla.components.service.pocket.PocketRecommendedStory
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.home.HomeFragmentState
import org.mozilla.fenix.components.appstate.AppState
import org.mozilla.fenix.home.pocket.PocketRecommendedStoriesCategory
import org.mozilla.fenix.home.pocket.PocketStoriesController
import org.mozilla.fenix.home.pocket.PocketStoriesInteractor
@ -44,7 +44,7 @@ interface TabSessionInteractor {
*
* * @param state The state the homepage from which to report desired metrics.
*/
fun reportSessionMetrics(state: HomeFragmentState)
fun reportSessionMetrics(state: AppState)
}
/**
@ -444,7 +444,7 @@ class SessionControlInteractor(
pocketStoriesController.handleDiscoverMoreClicked(link)
}
override fun reportSessionMetrics(state: HomeFragmentState) {
override fun reportSessionMetrics(state: AppState) {
controller.handleReportSessionMetrics(state)
}
}

View File

@ -13,10 +13,10 @@ import androidx.recyclerview.widget.RecyclerView
import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.feature.top.sites.TopSite
import mozilla.components.service.pocket.PocketRecommendedStory
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.components.appstate.AppState
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.home.HomeFragmentState
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.home.Mode
import org.mozilla.fenix.home.OnboardingState
import org.mozilla.fenix.home.recentbookmarks.RecentBookmark
@ -148,7 +148,7 @@ private fun onboardingAdapterItems(onboardingState: OnboardingState): List<Adapt
return items
}
private fun HomeFragmentState.toAdapterList(): List<AdapterItem> = when (mode) {
private fun AppState.toAdapterList(): List<AdapterItem> = when (mode) {
is Mode.Normal -> normalModeAdapterItems(
topSites,
collections,
@ -165,7 +165,7 @@ private fun HomeFragmentState.toAdapterList(): List<AdapterItem> = when (mode) {
}
@VisibleForTesting
internal fun HomeFragmentState.shouldShowHomeOnboardingDialog(settings: Settings): Boolean {
internal fun AppState.shouldShowHomeOnboardingDialog(settings: Settings): Boolean {
val isAnySectionsVisible = recentTabs.isNotEmpty() || recentBookmarks.isNotEmpty() ||
recentHistory.isNotEmpty() || pocketStories.isNotEmpty()
return isAnySectionsVisible && !settings.hasShownHomeOnboardingDialog
@ -177,7 +177,7 @@ private fun collectionTabItems(collection: TabCollection) =
}
class SessionControlView(
store: HomeFragmentStore,
store: AppStore,
val containerView: View,
viewLifecycleOwner: LifecycleOwner,
internal val interactor: SessionControlInteractor
@ -212,7 +212,7 @@ class SessionControlView(
}
}
fun update(state: HomeFragmentState, shouldReportMetrics: Boolean = false) {
fun update(state: AppState, shouldReportMetrics: Boolean = false) {
if (state.shouldShowHomeOnboardingDialog(view.context.settings())) {
interactor.showOnboardingDialog()
}

View File

@ -6,19 +6,19 @@ package org.mozilla.fenix.home.topsites
import mozilla.components.feature.top.sites.TopSite
import mozilla.components.feature.top.sites.view.TopSitesView
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.ext.sort
import org.mozilla.fenix.home.HomeFragmentAction
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.utils.Settings
class DefaultTopSitesView(
val store: HomeFragmentStore,
val store: AppStore,
val settings: Settings
) : TopSitesView {
override fun displayTopSites(topSites: List<TopSite>) {
store.dispatch(
HomeFragmentAction.TopSitesChange(
AppAction.TopSitesChange(
if (!settings.showContileFeature) {
topSites
} else {