1
0
Fork 0

No issue: Refactor readerview to use browser-state

master
Christian Sadilek 2020-04-06 17:18:41 -04:00
parent c427b0a70b
commit 0f1bff7402
9 changed files with 77 additions and 20 deletions

View File

@ -29,6 +29,7 @@ import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.addons.runIfFragmentIsAttached
import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.TabCollectionStorage import org.mozilla.fenix.components.TabCollectionStorage
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
@ -62,7 +63,6 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
override fun initializeUI(view: View): Session? { override fun initializeUI(view: View): Session? {
val context = requireContext() val context = requireContext()
val sessionManager = context.components.core.sessionManager
val components = context.components val components = context.components
return super.initializeUI(view)?.also { return super.initializeUI(view)?.also {
@ -70,12 +70,15 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
feature = ReaderViewFeature( feature = ReaderViewFeature(
context, context,
components.core.engine, components.core.engine,
sessionManager, components.core.store,
view.readerViewControlsBar view.readerViewControlsBar
) { available -> ) { available, _ ->
if (available) { if (available) {
components.analytics.metrics.track(Event.ReaderModeAvailable) components.analytics.metrics.track(Event.ReaderModeAvailable)
} }
runIfFragmentIsAttached {
browserToolbarView.toolbarIntegration.invalidateMenu()
}
}, },
owner = this, owner = this,
view = view view = view

View File

@ -31,6 +31,7 @@ import mozilla.components.feature.media.RecordingDevicesNotificationFeature
import mozilla.components.feature.media.middleware.MediaMiddleware import mozilla.components.feature.media.middleware.MediaMiddleware
import mozilla.components.feature.pwa.ManifestStorage import mozilla.components.feature.pwa.ManifestStorage
import mozilla.components.feature.pwa.WebAppShortcutManager import mozilla.components.feature.pwa.WebAppShortcutManager
import mozilla.components.feature.readerview.ReaderViewMiddleware
import mozilla.components.feature.session.HistoryDelegate import mozilla.components.feature.session.HistoryDelegate
import mozilla.components.feature.webcompat.WebCompatFeature import mozilla.components.feature.webcompat.WebCompatFeature
import mozilla.components.feature.webnotifications.WebNotificationFeature import mozilla.components.feature.webnotifications.WebNotificationFeature
@ -99,7 +100,8 @@ class Core(private val context: Context) {
val store by lazy { val store by lazy {
BrowserStore( BrowserStore(
middleware = listOf( middleware = listOf(
MediaMiddleware(context, MediaService::class.java) MediaMiddleware(context, MediaService::class.java),
ReaderViewMiddleware()
) )
) )
} }

View File

@ -18,6 +18,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import mozilla.components.browser.session.Session import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager import mozilla.components.browser.session.SessionManager
import mozilla.components.browser.state.selector.findTab
import mozilla.components.concept.engine.EngineView import mozilla.components.concept.engine.EngineView
import mozilla.components.concept.engine.prompt.ShareData import mozilla.components.concept.engine.prompt.ShareData
import mozilla.components.support.ktx.kotlin.isUrl import mozilla.components.support.ktx.kotlin.isUrl
@ -275,9 +276,9 @@ class DefaultBrowserToolbarController(
is ToolbarMenu.Item.ReaderMode -> { is ToolbarMenu.Item.ReaderMode -> {
activity.settings().readerModeOpened = true activity.settings().readerModeOpened = true
val enabled = currentSession?.readerMode val enabled = currentSession?.let {
?: activity.components.core.sessionManager.selectedSession?.readerMode activity.components.core.store.state.findTab(it.id)?.readerState?.active
?: false } ?: false
if (enabled) { if (enabled) {
readerModeController.hideReaderView() readerModeController.hideReaderView()

View File

@ -207,6 +207,7 @@ class BrowserToolbarView(
onItemTapped = { interactor.onBrowserToolbarMenuItemTapped(it) }, onItemTapped = { interactor.onBrowserToolbarMenuItemTapped(it) },
lifecycleOwner = lifecycleOwner, lifecycleOwner = lifecycleOwner,
sessionManager = sessionManager, sessionManager = sessionManager,
store = components.core.store,
bookmarksStorage = bookmarkStorage bookmarksStorage = bookmarkStorage
) )
view.display.setMenuDismissAction { view.display.setMenuDismissAction {

View File

@ -21,6 +21,8 @@ import mozilla.components.browser.menu.item.BrowserMenuImageText
import mozilla.components.browser.menu.item.BrowserMenuItemToolbar import mozilla.components.browser.menu.item.BrowserMenuItemToolbar
import mozilla.components.browser.session.Session import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager import mozilla.components.browser.session.SessionManager
import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.storage.BookmarksStorage import mozilla.components.concept.storage.BookmarksStorage
import mozilla.components.support.ktx.android.content.getColorFromAttr import mozilla.components.support.ktx.android.content.getColorFromAttr
import org.mozilla.fenix.Config import org.mozilla.fenix.Config
@ -47,6 +49,7 @@ import org.mozilla.fenix.utils.Settings
class DefaultToolbarMenu( class DefaultToolbarMenu(
private val context: Context, private val context: Context,
private val sessionManager: SessionManager, private val sessionManager: SessionManager,
private val store: BrowserStore,
hasAccountProblem: Boolean = false, hasAccountProblem: Boolean = false,
shouldReverseItems: Boolean, shouldReverseItems: Boolean,
private val onItemTapped: (ToolbarMenu.Item) -> Unit = {}, private val onItemTapped: (ToolbarMenu.Item) -> Unit = {},
@ -64,7 +67,7 @@ class DefaultToolbarMenu(
WebExtensionBrowserMenuBuilder( WebExtensionBrowserMenuBuilder(
menuItems, menuItems,
endOfMenuAlwaysVisible = !shouldReverseItems, endOfMenuAlwaysVisible = !shouldReverseItems,
store = context.components.core.store, store = store,
appendExtensionActionAtStart = !shouldReverseItems appendExtensionActionAtStart = !shouldReverseItems
) )
} }
@ -150,14 +153,18 @@ class DefaultToolbarMenu(
private fun shouldShowAddToHomescreen(): Boolean = private fun shouldShowAddToHomescreen(): Boolean =
session != null && context.components.useCases.webAppUseCases.isPinningSupported() session != null && context.components.useCases.webAppUseCases.isPinningSupported()
private fun shouldShowReaderMode(): Boolean = session?.readerable ?: false private fun shouldShowReaderMode(): Boolean = session?.let {
store.state.findTab(it.id)?.readerState?.readerable
} ?: false
private fun shouldShowOpenInApp(): Boolean = session?.let { session -> private fun shouldShowOpenInApp(): Boolean = session?.let { session ->
val appLink = context.components.useCases.appLinksUseCases.appLinkRedirect val appLink = context.components.useCases.appLinksUseCases.appLinkRedirect
appLink(session.url).hasExternalApp() appLink(session.url).hasExternalApp()
} ?: false } ?: false
private fun shouldShowReaderAppearance(): Boolean = session?.readerMode ?: false private fun shouldShowReaderAppearance(): Boolean = session?.let {
store.state.findTab(it.id)?.readerState?.active
} ?: false
// End of predicates // // End of predicates //
private val menuItems by lazy { private val menuItems by lazy {
@ -300,7 +307,9 @@ class DefaultToolbarMenu(
label = context.getString(R.string.browser_menu_read), label = context.getString(R.string.browser_menu_read),
startImageResource = R.drawable.ic_readermode, startImageResource = R.drawable.ic_readermode,
initialState = { initialState = {
session?.readerMode ?: false session?.let {
store.state.findTab(it.id)?.readerState?.active
} ?: false
}, },
highlight = BrowserMenuHighlight.LowPriority( highlight = BrowserMenuHighlight.LowPriority(
label = context.getString(R.string.browser_menu_read), label = context.getString(R.string.browser_menu_read),

View File

@ -22,20 +22,20 @@ class MenuPresenter(
/** Redraw the refresh/stop button */ /** Redraw the refresh/stop button */
override fun onLoadingStateChanged(session: Session, loading: Boolean) { override fun onLoadingStateChanged(session: Session, loading: Boolean) {
menuToolbar.invalidateActions() invalidateActions()
} }
/** Redraw the back and forward buttons */ /** Redraw the back and forward buttons */
override fun onNavigationStateChanged(session: Session, canGoBack: Boolean, canGoForward: Boolean) { override fun onNavigationStateChanged(session: Session, canGoBack: Boolean, canGoForward: Boolean) {
menuToolbar.invalidateActions() invalidateActions()
} }
/** Redraw the install web app button */ /** Redraw the install web app button */
override fun onWebAppManifestChanged(session: Session, manifest: WebAppManifest?) { override fun onWebAppManifestChanged(session: Session, manifest: WebAppManifest?) {
menuToolbar.invalidateActions() invalidateActions()
} }
override fun onReaderableStateUpdated(session: Session, readerable: Boolean) { fun invalidateActions() {
menuToolbar.invalidateActions() menuToolbar.invalidateActions()
} }
} }

View File

@ -45,7 +45,7 @@ abstract class ToolbarIntegration(
) )
) )
private var menuPresenter = private val menuPresenter =
MenuPresenter(toolbar, context.components.core.sessionManager, sessionId) MenuPresenter(toolbar, context.components.core.sessionManager, sessionId)
init { init {
@ -62,6 +62,10 @@ abstract class ToolbarIntegration(
menuPresenter.stop() menuPresenter.stop()
toolbarPresenter.stop() toolbarPresenter.stop()
} }
fun invalidateMenu() {
menuPresenter.invalidateActions()
}
} }
class DefaultToolbarIntegration( class DefaultToolbarIntegration(

View File

@ -27,6 +27,10 @@ import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.test.setMain import kotlinx.coroutines.test.setMain
import mozilla.components.browser.session.Session import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager import mozilla.components.browser.session.SessionManager
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.ReaderState
import mozilla.components.browser.state.state.createTab
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.engine.EngineView import mozilla.components.concept.engine.EngineView
import mozilla.components.feature.search.SearchUseCases import mozilla.components.feature.search.SearchUseCases
import mozilla.components.feature.session.SessionUseCases import mozilla.components.feature.session.SessionUseCases
@ -43,6 +47,7 @@ import org.mozilla.fenix.browser.BrowserAnimator
import org.mozilla.fenix.browser.BrowserFragment import org.mozilla.fenix.browser.BrowserFragment
import org.mozilla.fenix.browser.BrowserFragmentDirections import org.mozilla.fenix.browser.BrowserFragmentDirections
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
import org.mozilla.fenix.browser.readermode.ReaderModeController
import org.mozilla.fenix.collections.SaveCollectionStep import org.mozilla.fenix.collections.SaveCollectionStep
import org.mozilla.fenix.components.Analytics import org.mozilla.fenix.components.Analytics
import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.components.FenixSnackbar
@ -82,6 +87,13 @@ class DefaultBrowserToolbarControllerTest {
private val snackbar = mockk<FenixSnackbar>(relaxed = true) private val snackbar = mockk<FenixSnackbar>(relaxed = true)
private val tabCollectionStorage = mockk<TabCollectionStorage>(relaxed = true) private val tabCollectionStorage = mockk<TabCollectionStorage>(relaxed = true)
private val topSiteStorage = mockk<TopSiteStorage>(relaxed = true) private val topSiteStorage = mockk<TopSiteStorage>(relaxed = true)
private val readerModeController = mockk<ReaderModeController>(relaxed = true)
private val store: BrowserStore = BrowserStore(initialState = BrowserState(
listOf(
createTab("https://www.mozilla.org", id = "reader-inactive-tab"),
createTab("https://www.mozilla.org", id = "reader-active-tab", readerState = ReaderState(active = true))
))
)
private lateinit var controller: DefaultBrowserToolbarController private lateinit var controller: DefaultBrowserToolbarController
@ -104,7 +116,7 @@ class DefaultBrowserToolbarControllerTest {
tabCollectionStorage = tabCollectionStorage, tabCollectionStorage = tabCollectionStorage,
topSiteStorage = topSiteStorage, topSiteStorage = topSiteStorage,
bookmarkTapped = mockk(), bookmarkTapped = mockk(),
readerModeController = mockk(), readerModeController = readerModeController,
sessionManager = mockk(), sessionManager = mockk(),
store = mockk(), store = mockk(),
sharedViewModel = mockk() sharedViewModel = mockk()
@ -125,6 +137,7 @@ class DefaultBrowserToolbarControllerTest {
every { activity.components.useCases.sessionUseCases } returns sessionUseCases every { activity.components.useCases.sessionUseCases } returns sessionUseCases
every { activity.components.useCases.searchUseCases } returns searchUseCases every { activity.components.useCases.searchUseCases } returns searchUseCases
every { activity.components.core.sessionManager.selectedSession } returns currentSession every { activity.components.core.sessionManager.selectedSession } returns currentSession
every { activity.components.core.store } returns store
val onComplete = slot<() -> Unit>() val onComplete = slot<() -> Unit>()
every { browserAnimator.captureEngineViewAndDrawStatically(capture(onComplete)) } answers { onComplete.captured.invoke() } every { browserAnimator.captureEngineViewAndDrawStatically(capture(onComplete)) } answers { onComplete.captured.invoke() }
@ -559,4 +572,17 @@ class DefaultBrowserToolbarControllerTest {
verify { deleteAndQuit(activity, testScope, null) } verify { deleteAndQuit(activity, testScope, null) }
} }
@Test
fun handleToolbarReaderModePress() {
val item = ToolbarMenu.Item.ReaderMode(false)
every { currentSession.id } returns "reader-inactive-tab"
controller.handleToolbarItemInteraction(item)
verify { readerModeController.showReaderView() }
every { currentSession.id } returns "reader-active-tab"
controller.handleToolbarItemInteraction(item)
verify { readerModeController.hideReaderView() }
}
} }

View File

@ -8,6 +8,10 @@ import io.mockk.mockkStatic
import io.mockk.spyk import io.mockk.spyk
import mozilla.components.browser.session.Session import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager import mozilla.components.browser.session.SessionManager
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.ReaderState
import mozilla.components.browser.state.state.createTab
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.storage.BookmarksStorage import mozilla.components.concept.storage.BookmarksStorage
import mozilla.components.feature.app.links.AppLinkRedirect import mozilla.components.feature.app.links.AppLinkRedirect
import mozilla.components.feature.app.links.AppLinksUseCases import mozilla.components.feature.app.links.AppLinksUseCases
@ -28,12 +32,19 @@ class DefaultToolbarMenuTest {
private val lifecycleOwner: LifecycleOwner = mockk(relaxed = true) private val lifecycleOwner: LifecycleOwner = mockk(relaxed = true)
private val bookmarksStorage: BookmarksStorage = mockk(relaxed = true) private val bookmarksStorage: BookmarksStorage = mockk(relaxed = true)
private val store: BrowserStore = BrowserStore(initialState = BrowserState(
listOf(
createTab("https://www.mozilla.org", id = "readerable-tab", readerState = ReaderState(readerable = true))
)
))
@Before @Before
fun setUp() { fun setUp() {
defaultToolbarMenu = spyk( defaultToolbarMenu = spyk(
DefaultToolbarMenu( DefaultToolbarMenu(
context, context,
sessionManager, sessionManager,
store,
hasAccountProblem = false, hasAccountProblem = false,
shouldReverseItems = false, shouldReverseItems = false,
onItemTapped = onItemTapped, onItemTapped = onItemTapped,
@ -59,7 +70,7 @@ class DefaultToolbarMenuTest {
every { getAppLinkRedirect(any()) } returns appLinkRedirect every { getAppLinkRedirect(any()) } returns appLinkRedirect
val session: Session = mockk(relaxed = true) val session: Session = mockk(relaxed = true)
every { session.readerable } returns true every { session.id } returns "readerable-tab"
every { sessionManager.selectedSession } returns session every { sessionManager.selectedSession } returns session
val list = defaultToolbarMenu.getLowPrioHighlightItems() val list = defaultToolbarMenu.getLowPrioHighlightItems()
@ -84,7 +95,7 @@ class DefaultToolbarMenuTest {
every { getAppLinkRedirect(any()) } returns appLinkRedirect every { getAppLinkRedirect(any()) } returns appLinkRedirect
val session: Session = mockk(relaxed = true) val session: Session = mockk(relaxed = true)
every { session.readerable } returns true every { session.id } returns "readerable-tab"
every { sessionManager.selectedSession } returns session every { sessionManager.selectedSession } returns session
val list = defaultToolbarMenu.getLowPrioHighlightItems() val list = defaultToolbarMenu.getLowPrioHighlightItems()
@ -114,7 +125,7 @@ class DefaultToolbarMenuTest {
every { context.components.useCases.webAppUseCases.isInstallable() } returns true every { context.components.useCases.webAppUseCases.isInstallable() } returns true
val session: Session = mockk(relaxed = true) val session: Session = mockk(relaxed = true)
every { session.readerable } returns true every { session.id } returns "readerable-tab"
every { sessionManager.selectedSession } returns session every { sessionManager.selectedSession } returns session
val getAppLinkRedirect: AppLinksUseCases.GetAppLinkRedirect = mockk(relaxed = true) val getAppLinkRedirect: AppLinksUseCases.GetAppLinkRedirect = mockk(relaxed = true)