Close #22402: Add top placeholder for home

upstream-sync
Roger Yang 3 years ago committed by mergify[bot]
parent dde916038d
commit 24e4452cb5

@ -25,7 +25,7 @@ private const val EXPECTED_SUPPRESSION_COUNT = 19
@Suppress("TopLevelPropertyNaming") // it's silly this would have a different naming convention b/c no const
private val EXPECTED_RUNBLOCKING_RANGE = 0..1 // CI has +1 counts compared to local runs: increment these together
private const val EXPECTED_RECYCLER_VIEW_CONSTRAINT_LAYOUT_CHILDREN = 4
private const val EXPECTED_NUMBER_OF_INFLATION = 12
private const val EXPECTED_NUMBER_OF_INFLATION = 13
private val failureMsgStrictMode = getErrorMessage(
shortName = "StrictMode suppression",

@ -377,8 +377,7 @@ class HomeFragment : Fragment() {
homeFragmentStore,
binding.sessionControlRecyclerView,
viewLifecycleOwner,
sessionControlInteractor,
homeViewModel
sessionControlInteractor
)
updateSessionControlView()
@ -546,8 +545,6 @@ class HomeFragment : Fragment() {
if (bundleArgs.getBoolean(FOCUS_ON_ADDRESS_BAR)) {
navigateToSearch()
} else if (bundleArgs.getLong(FOCUS_ON_COLLECTION, -1) >= 0) {
// No need to scroll to async'd loaded TopSites if we want to scroll to collections.
homeViewModel.shouldScrollToTopSites = false
/* Triggered when the user has added a tab to a collection and has tapped
* the View action on the [TabsTrayDialogFragment] snackbar.*/
scrollAndAnimateCollection(bundleArgs.getLong(FOCUS_ON_COLLECTION, -1))

@ -11,9 +11,4 @@ class HomeScreenViewModel : ViewModel() {
* Used to delete a specific session once the home screen is resumed
*/
var sessionToDelete: String? = null
/**
* Used to remember if we need to scroll to top of the homeFragment's recycleView (top sites) see #8561
* */
var shouldScrollToTopSites: Boolean = true
}

@ -0,0 +1,24 @@
/* 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.home
import android.view.View
import org.mozilla.fenix.R
import org.mozilla.fenix.utils.view.ViewHolder
/**
* View holder for a synchronous, unconditional and invisible placeholder. This is to anchor home to
* the top when home is created.
*/
class TopPlaceholderViewHolder(
view: View
) : ViewHolder(view) {
fun bind() = Unit
companion object {
const val LAYOUT_ID = R.layout.top_placeholder_item
}
}

@ -23,6 +23,7 @@ import org.mozilla.fenix.components.tips.Tip
import org.mozilla.fenix.historymetadata.view.HistoryMetadataGroupViewHolder
import org.mozilla.fenix.historymetadata.view.HistoryMetadataHeaderViewHolder
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.home.TopPlaceholderViewHolder
import org.mozilla.fenix.home.OnboardingState
import org.mozilla.fenix.home.recentbookmarks.view.RecentBookmarksViewHolder
import org.mozilla.fenix.home.recenttabs.view.RecentTabViewHolder
@ -49,6 +50,7 @@ import org.mozilla.fenix.home.topsites.TopSitePagerViewHolder
import mozilla.components.feature.tab.collections.Tab as ComponentTab
sealed class AdapterItem(@LayoutRes val viewType: Int) {
object TopPlaceholderItem : AdapterItem(TopPlaceholderViewHolder.LAYOUT_ID)
data class TipItem(val tip: Tip) : AdapterItem(
ButtonTipViewHolder.LAYOUT_ID
)
@ -255,6 +257,7 @@ class SessionControlAdapter(
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
return when (viewType) {
TopPlaceholderViewHolder.LAYOUT_ID -> TopPlaceholderViewHolder(view)
ButtonTipViewHolder.LAYOUT_ID -> ButtonTipViewHolder(view, interactor)
TopSitePagerViewHolder.LAYOUT_ID -> TopSitePagerViewHolder(view, interactor)
PrivateBrowsingDescriptionViewHolder.LAYOUT_ID -> PrivateBrowsingDescriptionViewHolder(
@ -350,6 +353,9 @@ class SessionControlAdapter(
val tipItem = item as AdapterItem.TipItem
holder.bind(tipItem.tip)
}
is TopPlaceholderViewHolder -> {
holder.bind()
}
is TopSitePagerViewHolder -> {
holder.bind((item as AdapterItem.TopSitePager).topSites)
}

@ -20,7 +20,6 @@ import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.historymetadata.HistoryMetadataGroup
import org.mozilla.fenix.home.HomeFragmentState
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.home.HomeScreenViewModel
import org.mozilla.fenix.home.Mode
import org.mozilla.fenix.home.OnboardingState
import org.mozilla.fenix.home.recenttabs.RecentTab
@ -46,6 +45,9 @@ internal fun normalModeAdapterItems(
val items = mutableListOf<AdapterItem>()
var shouldShowCustomizeHome = false
// Add a synchronous, unconditional and invisible placeholder so home is anchored to the top when created.
items.add(AdapterItem.TopPlaceholderItem)
tip?.let { items.add(AdapterItem.TipItem(it)) }
if (showSetAsDefaultBrowserCard) {
@ -182,8 +184,7 @@ class SessionControlView(
store: HomeFragmentStore,
val containerView: View,
viewLifecycleOwner: LifecycleOwner,
internal val interactor: SessionControlInteractor,
private var homeScreenViewModel: HomeScreenViewModel
internal val interactor: SessionControlInteractor
) {
val view: RecyclerView = containerView as RecyclerView
@ -222,20 +223,6 @@ class SessionControlView(
if (shouldReportMetrics) interactor.reportSessionMetrics(state)
val stateAdapterList = state.toAdapterList()
if (homeScreenViewModel.shouldScrollToTopSites) {
sessionControlAdapter.submitList(stateAdapterList) {
val loadedTopSites = stateAdapterList.find { adapterItem ->
adapterItem is AdapterItem.TopSitePager && adapterItem.topSites.isNotEmpty()
}
loadedTopSites?.run {
homeScreenViewModel.shouldScrollToTopSites = false
view.scrollToPosition(0)
}
}
} else {
sessionControlAdapter.submitList(stateAdapterList)
}
sessionControlAdapter.submitList(state.toAdapterList())
}
}

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="0dp"/>

@ -14,6 +14,7 @@ import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.feature.top.sites.TopSite
import mozilla.components.service.pocket.PocketRecommendedStory
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Assert.assertFalse
import org.junit.Test
@ -96,8 +97,7 @@ class SessionControlViewTest {
mockk(relaxed = true),
view,
mockk(relaxed = true),
interactor,
mockk(relaxed = true)
interactor
)
val recentTabs = listOf<RecentTab>(mockk(relaxed = true))
@ -118,8 +118,7 @@ class SessionControlViewTest {
mockk(relaxed = true),
view,
mockk(relaxed = true),
interactor,
mockk(relaxed = true)
interactor
)
val state = HomeFragmentState()
@ -155,8 +154,9 @@ class SessionControlViewTest {
pocketArticles
)
assertTrue(results[0] is AdapterItem.RecentBookmarks)
assertTrue(results[1] is AdapterItem.CustomizeHomeButton)
assertTrue(results[0] is AdapterItem.TopPlaceholderItem)
assertTrue(results[1] is AdapterItem.RecentBookmarks)
assertTrue(results[2] is AdapterItem.CustomizeHomeButton)
}
@Test
@ -182,9 +182,10 @@ class SessionControlViewTest {
pocketArticles
)
assertTrue(results[0] is AdapterItem.RecentTabsHeader)
assertTrue(results[1] is AdapterItem.RecentTabItem)
assertTrue(results[2] is AdapterItem.CustomizeHomeButton)
assertTrue(results[0] is AdapterItem.TopPlaceholderItem)
assertTrue(results[1] is AdapterItem.RecentTabsHeader)
assertTrue(results[2] is AdapterItem.RecentTabItem)
assertTrue(results[3] is AdapterItem.CustomizeHomeButton)
}
@Test
@ -210,9 +211,10 @@ class SessionControlViewTest {
pocketArticles
)
assertTrue(results[0] is AdapterItem.HistoryMetadataHeader)
assertTrue(results[1] is AdapterItem.HistoryMetadataGroup)
assertTrue(results[2] is AdapterItem.CustomizeHomeButton)
assertTrue(results[0] is AdapterItem.TopPlaceholderItem)
assertTrue(results[1] is AdapterItem.HistoryMetadataHeader)
assertTrue(results[2] is AdapterItem.HistoryMetadataGroup)
assertTrue(results[3] is AdapterItem.CustomizeHomeButton)
}
@Test
@ -238,8 +240,9 @@ class SessionControlViewTest {
pocketArticles
)
assertTrue(results[0] is AdapterItem.PocketStoriesItem)
assertTrue(results[1] is AdapterItem.CustomizeHomeButton)
assertTrue(results[0] is AdapterItem.TopPlaceholderItem)
assertTrue(results[1] is AdapterItem.PocketStoriesItem)
assertTrue(results[2] is AdapterItem.CustomizeHomeButton)
}
@Test
@ -264,6 +267,36 @@ class SessionControlViewTest {
historyMetadata,
pocketArticles
)
assertTrue(results.isEmpty())
assertEquals(results.size, 1)
assertTrue(results[0] is AdapterItem.TopPlaceholderItem)
}
@Test
fun `GIVEN all items THEN top placeholder item is always the first item`() {
val collection = mockk<TabCollection> {
every { id } returns 123L
}
val topSites = listOf<TopSite>(mockk())
val collections = listOf(collection)
val expandedCollections = emptySet<Long>()
val recentBookmarks = listOf<BookmarkNode>(mockk())
val recentTabs = listOf<RecentTab.Tab>(mockk())
val historyMetadata = listOf<HistoryMetadataGroup>(mockk())
val pocketArticles = listOf<PocketRecommendedStory>(mockk())
val results = normalModeAdapterItems(
topSites,
collections,
expandedCollections,
null,
recentBookmarks,
false,
false,
recentTabs,
historyMetadata,
pocketArticles
)
assertTrue(results[0] is AdapterItem.TopPlaceholderItem)
}
}

Loading…
Cancel
Save