diff --git a/components/feature/tabs/src/main/java/mozilla/components/feature/tabs/TabsUseCases.kt b/components/feature/tabs/src/main/java/mozilla/components/feature/tabs/TabsUseCases.kt index a520abc644b..a4cb8d30daa 100644 --- a/components/feature/tabs/src/main/java/mozilla/components/feature/tabs/TabsUseCases.kt +++ b/components/feature/tabs/src/main/java/mozilla/components/feature/tabs/TabsUseCases.kt @@ -22,6 +22,7 @@ import mozilla.components.browser.state.state.recover.toTabSessionStates import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.engine.EngineSession import mozilla.components.concept.engine.EngineSession.LoadUrlFlags +import mozilla.components.concept.storage.HistoryMetadataKey import mozilla.components.feature.session.SessionUseCases.LoadUrlUseCase /** @@ -369,18 +370,48 @@ class TabsUseCases( class SelectOrAddUseCase( private val store: BrowserStore ) { + /** + * Selects an already existing tab with the matching [HistoryMetadataKey] or otherwise + * creates a new tab with the given [url]. + * + * @param url The URL to be selected or loaded in the new tab. + * @param historyMetadata The [HistoryMetadataKey] to match for existing tabs. + * @return The ID of the selected or created tab. + */ + operator fun invoke( + url: String, + historyMetadata: HistoryMetadataKey + ): String { + val tab = store.state.tabs.find { it.historyMetadata == historyMetadata } + + return if (tab != null) { + store.dispatch(TabListAction.SelectTabAction(tab.id)) + tab.id + } else { + this.invoke(url) + } + } + /** * Selects an already existing tab displaying [url] or otherwise creates a new tab. + * + * @param url The URL to be loaded in the new tab. + * @param private Whether or not this session should use private mode. + * @param source The origin of a session to describe how and why it was created. + * @param flags The [LoadUrlFlags] to use when loading the provided URL. + * @return The ID of the selected or created tab. */ operator fun invoke( url: String, private: Boolean = false, source: Source = Source.NEW_TAB, flags: LoadUrlFlags = LoadUrlFlags.none() - ) { + ): String { val existingTab = store.state.findTabByUrl(url) - if (existingTab != null) { + + return if (existingTab != null) { store.dispatch(TabListAction.SelectTabAction(existingTab.id)) + existingTab.id } else { val tab = createTab( url = url, @@ -394,6 +425,7 @@ class TabsUseCases( url, flags )) + tab.id } } } diff --git a/components/feature/tabs/src/test/java/mozilla/components/feature/tabs/TabsUseCasesTest.kt b/components/feature/tabs/src/test/java/mozilla/components/feature/tabs/TabsUseCasesTest.kt index 57e301b73a0..fc2fe47441c 100644 --- a/components/feature/tabs/src/test/java/mozilla/components/feature/tabs/TabsUseCasesTest.kt +++ b/components/feature/tabs/src/test/java/mozilla/components/feature/tabs/TabsUseCasesTest.kt @@ -21,6 +21,7 @@ import mozilla.components.concept.engine.Engine import mozilla.components.concept.engine.EngineSession import mozilla.components.concept.engine.EngineSession.LoadUrlFlags import mozilla.components.concept.engine.EngineSessionState +import mozilla.components.concept.storage.HistoryMetadataKey import mozilla.components.support.test.any import mozilla.components.support.test.argumentCaptor import mozilla.components.support.test.ext.joinBlocking @@ -362,6 +363,31 @@ class TabsUseCasesTest { assertEquals(tab, store.state.selectedTab) } + @Test + fun `selectOrAddTab selects already existing tab with matching historyMetadata`() { + val historyMetadata = HistoryMetadataKey( + url = "https://mozilla.org", + referrerUrl = "https://firefox.com" + ) + + val tab = createTab("https://mozilla.org", historyMetadata = historyMetadata) + val otherTab = createTab("https://firefox.com") + + store.dispatch(TabListAction.AddTabAction(otherTab)).joinBlocking() + store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking() + + assertEquals(otherTab.id, store.state.selectedTabId) + assertEquals(otherTab, store.state.selectedTab) + assertEquals(2, store.state.tabs.size) + + tabsUseCases.selectOrAddTab(tab.content.url, historyMetadata = historyMetadata) + store.waitUntilIdle() + + assertEquals(2, store.state.tabs.size) + assertEquals(tab.id, store.state.selectedTabId) + assertEquals(tab, store.state.selectedTab) + } + @Test fun `selectOrAddTab adds new tab if no matching existing tab could be found`() { val tab = createTab("https://mozilla.org") @@ -376,6 +402,27 @@ class TabsUseCasesTest { assertNotNull(store.state.findTabByUrl("https://firefox.com")) } + @Test + fun `selectOrAddTab adds new tab if no matching existing history metadata could be found`() { + val tab = createTab("https://mozilla.org") + val historyMetadata = HistoryMetadataKey( + url = "https://mozilla.org", + referrerUrl = "https://firefox.com" + ) + + store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking() + + assertEquals(tab.id, store.state.selectedTabId) + assertEquals(tab, store.state.selectedTab) + assertEquals(1, store.state.tabs.size) + + tabsUseCases.selectOrAddTab("https://firefox.com", historyMetadata = historyMetadata) + store.waitUntilIdle() + + assertEquals(2, store.state.tabs.size) + assertNotNull(store.state.findTabByUrl("https://firefox.com")) + } + @Test fun `duplicateTab creates a duplicate of the given tab`() { store.dispatch(TabListAction.AddTabAction( diff --git a/docs/changelog.md b/docs/changelog.md index bd96c3cd72b..8a3353a294d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -17,6 +17,7 @@ permalink: /changelog/ * **feature-tabs** * Adds `lastAccess` to the `Tab` data class that is used in `TabsTray`. + * Adds a new SelectOrAddUseCase for reopening existing tab with matching HistoryMetadataKey. [#10611](https://github.com/mozilla-mobile/android-components/issues/10611) # 91.0.0 * [Commits](https://github.com/mozilla-mobile/android-components/compare/v91.0.0...master)