Target.kt
/* 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 mozilla.components.browser.state.helper
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import mozilla.components.browser.state.selector.findCustomTab
import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.selector.selectedTab
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.SessionState
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.lib.state.Store
import mozilla.components.lib.state.ext.observeAsComposableState
/**
* Helper for allowing a component consumer to specify which tab a component should target (e.g.
* the selected tab, a specific pinned tab or a custom tab). Additional helper methods make it
* easier to lookup the current state of the tab or observe changes.
*/
sealed class Target {
/**
* Looks up this target in the given [BrowserStore] and returns the matching [SessionState] if
* available. Otherwise returns `null`.
*
* @param store to lookup this target in.
*/
fun lookupIn(store: BrowserStore): SessionState? = lookupIn(store.state)
/**
* Looks up this target in the given [BrowserState] and returns the matching [SessionState] if
* available. Otherwise returns `null`.
*
* @param state to lookup this target in.
*/
abstract fun lookupIn(state: BrowserState): SessionState?
/**
* Observes this target and represents the mapped state (using [map]) via [State].
*
* Everytime the [Store] state changes and the result of the [observe] function changes for this
* state, the returned [State] will be updated causing recomposition of every [State.value] usage.
*
* The [Store] observer will automatically be removed when this composable disposes or the current
* [LifecycleOwner] moves to the [Lifecycle.State.DESTROYED] state.
*
* @param store that should get observed
* @param observe function that maps a [SessionState] to the (sub) state that should get observed
* for changes.
*/
@Composable
fun <R> observeAsComposableStateFrom(
store: BrowserStore,
observe: (SessionState?) -> R,
): State<SessionState?> {
return store.observeAsComposableState(
map = { state -> lookupIn(state) },
observe = { state -> observe(lookupIn(state)) },
)
}
/**
* Targets the selected tab.
*/
object SelectedTab : Target() {
override fun lookupIn(state: BrowserState): SessionState? {
return state.selectedTab
}
}
/**
* Targets a specific tab by its [tabId].
*
* @param tabId The ID of the tab to be targeted.
*/
class Tab(val tabId: String) : Target() {
override fun lookupIn(state: BrowserState): SessionState? {
return state.findTab(tabId)
}
}
/**
* Targets a specific custom tab by its [customTabId].
*
* @param customTabId The ID of the custom tab to be targeted.
*/
class CustomTab(val customTabId: String) : Target() {
override fun lookupIn(state: BrowserState): SessionState? {
return state.findCustomTab(customTabId)
}
}
}