Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FXIOS-10913 [Homepage] [Top Sites] full layout configuration for top sites #23918

Merged
merged 5 commits into from
Dec 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions firefox-ios/Client.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1046,15 +1046,14 @@
8AE1E1D927B1BD380024C45E /* UIStackViewExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE1E1D827B1BD380024C45E /* UIStackViewExtensionsTests.swift */; };
8AE1E1DB27B1C1320024C45E /* SearchBarSettingsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE1E1DA27B1C1320024C45E /* SearchBarSettingsViewModelTests.swift */; };
8AE459922CEFB1DC002D6679 /* InactiveTabsTelemetry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE459912CEFB1DC002D6679 /* InactiveTabsTelemetry.swift */; };
8AE80BAD2891957C00BC12EA /* TopSitesDimensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE80BAC2891957C00BC12EA /* TopSitesDimensionTests.swift */; };
8AE80BAD2891957C00BC12EA /* LegacyTopSitesDimensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE80BAC2891957C00BC12EA /* LegacyTopSitesDimensionTests.swift */; };
8AE80BAF2891960300BC12EA /* MockTraitCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE80BAE2891960300BC12EA /* MockTraitCollection.swift */; };
8AE80BB62891AEA100BC12EA /* MockDispatchGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE80BB42891AE6700BC12EA /* MockDispatchGroup.swift */; };
8AE80BB82891BE0700BC12EA /* JumpBackInDataAdaptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE80BB72891BE0700BC12EA /* JumpBackInDataAdaptor.swift */; };
8AE80BBA2891C0C300BC12EA /* JumpBackInSectionLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE80BB92891C0C300BC12EA /* JumpBackInSectionLayout.swift */; };
8AE80BBC2891C20D00BC12EA /* JumpBackInList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE80BBB2891C20D00BC12EA /* JumpBackInList.swift */; };
8AE80BBE2891C21A00BC12EA /* JumpBackInSyncedTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE80BBD2891C21A00BC12EA /* JumpBackInSyncedTab.swift */; };
8AE938192CD91D5A0020E6CF /* TopSiteState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE938182CD91D5A0020E6CF /* TopSiteState.swift */; };
8AE9381B2CD91FDB0020E6CF /* TopSitesSectionStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE9381A2CD91FDB0020E6CF /* TopSitesSectionStateTests.swift */; };
8AE9381D2CD920310020E6CF /* TopSiteCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE9381C2CD920310020E6CF /* TopSiteCell.swift */; };
8AE9FD262CF66301001053EE /* UnifiedAdsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE9FD252CF662FF001053EE /* UnifiedAdsProvider.swift */; };
8AEAD9F32C3D7B3E001A2C5A /* FeatureFlagsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AEAD9F22C3D7B3E001A2C5A /* FeatureFlagsSettings.swift */; };
Expand All @@ -1067,6 +1066,7 @@
8AEE62C92756BA34003207D1 /* LoginsHelper.js in Resources */ = {isa = PBXBuildFile; fileRef = 8AEE62C62756BA34003207D1 /* LoginsHelper.js */; };
8AEE62CA2756BA34003207D1 /* TrackingProtectionStats.js in Resources */ = {isa = PBXBuildFile; fileRef = 8AEE62C72756BA34003207D1 /* TrackingProtectionStats.js */; };
8AEE62CB2756BA34003207D1 /* DownloadHelper.js in Resources */ = {isa = PBXBuildFile; fileRef = 8AEE62C82756BA34003207D1 /* DownloadHelper.js */; };
8AEF41602D15D6290013925D /* TopSitesSectionStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE9381A2CD91FDB0020E6CF /* TopSitesSectionStateTests.swift */; };
8AF10D8A29D713F50086351D /* LaunchScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AF10D8929D713F50086351D /* LaunchScreenViewModelTests.swift */; };
8AF10D8F29D774090086351D /* SceneSetupHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AF10D8E29D774090086351D /* SceneSetupHelper.swift */; };
8AF10D9129D7761A0086351D /* MockLaunchScreenManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AF10D9029D776190086351D /* MockLaunchScreenManager.swift */; };
Expand Down Expand Up @@ -7635,7 +7635,7 @@
8AE1E1D827B1BD380024C45E /* UIStackViewExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIStackViewExtensionsTests.swift; sourceTree = "<group>"; };
8AE1E1DA27B1C1320024C45E /* SearchBarSettingsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBarSettingsViewModelTests.swift; sourceTree = "<group>"; };
8AE459912CEFB1DC002D6679 /* InactiveTabsTelemetry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InactiveTabsTelemetry.swift; sourceTree = "<group>"; };
8AE80BAC2891957C00BC12EA /* TopSitesDimensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopSitesDimensionTests.swift; sourceTree = "<group>"; };
8AE80BAC2891957C00BC12EA /* LegacyTopSitesDimensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyTopSitesDimensionTests.swift; sourceTree = "<group>"; };
8AE80BAE2891960300BC12EA /* MockTraitCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTraitCollection.swift; sourceTree = "<group>"; };
8AE80BB42891AE6700BC12EA /* MockDispatchGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockDispatchGroup.swift; sourceTree = "<group>"; };
8AE80BB72891BE0700BC12EA /* JumpBackInDataAdaptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JumpBackInDataAdaptor.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -11987,7 +11987,7 @@
8A7A93ED2810ADF2005E7E1B /* ContileProviderTests.swift */,
961577932A39008100391E8D /* SponsoredTileDataUtilityTests.swift */,
8A33221E27DFE318008F809E /* TopSitesDataAdaptorTests.swift */,
8AE80BAC2891957C00BC12EA /* TopSitesDimensionTests.swift */,
8AE80BAC2891957C00BC12EA /* LegacyTopSitesDimensionTests.swift */,
3B6F40171DC7849C00656CC6 /* TopSitesViewModelTests.swift */,
);
path = TopSites;
Expand Down Expand Up @@ -15715,7 +15715,6 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
8AE9381B2CD91FDB0020E6CF /* TopSitesSectionStateTests.swift in Sources */,
4590912E2A2E4F7700061F0C /* AutopushTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -17038,6 +17037,7 @@
E18259E329B2A51B00E6BE76 /* MockNotificationManager.swift in Sources */,
96A5F736298D8EDF00234E5F /* MockSearchEngineProvider.swift in Sources */,
8A5D1CA02A30C9D7005AD35C /* MockAppSettingsDelegate.swift in Sources */,
8AEF41602D15D6290013925D /* TopSitesSectionStateTests.swift in Sources */,
1D7B789F2AE088930011E9F2 /* EventQueueTests.swift in Sources */,
21A1C3C72996AFF800181B7C /* OverlayModeManagerTests.swift in Sources */,
ED07C0F52CCB020B006C0627 /* SearchEngineSelectionMiddlewareTests.swift in Sources */,
Expand Down Expand Up @@ -17296,7 +17296,7 @@
8AFC4E472CAF53E100C54B43 /* RemoteDataTypeTests.swift in Sources */,
8A7AF0C72C9A119A009691B5 /* TabPeekStateTests.swift in Sources */,
0AFF7F642C7784D600265214 /* MockDataCleaner.swift in Sources */,
8AE80BAD2891957C00BC12EA /* TopSitesDimensionTests.swift in Sources */,
8AE80BAD2891957C00BC12EA /* LegacyTopSitesDimensionTests.swift in Sources */,
D82ED2641FEB3C420059570B /* DefaultSearchPrefsTests.swift in Sources */,
1D74FF502B2797EA00FF01D0 /* WindowManagerTests.swift in Sources */,
CA24B53B24ABFE5D0093848C /* PasswordManagerDataSourceHelperTests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ final class HomepageDiffableDataSource:
snapshot.appendSections([.header, .topSites, .pocket(textColor), .customizeHomepage])
snapshot.appendItems([.header(textColor)], toSection: .header)

let topSites: [HomeItem] = state.topSitesState.topSitesData.compactMap { .topSite($0, textColor) }
let topSites = getTopSites(with: state.topSitesState, and: textColor)
snapshot.appendItems(topSites, toSection: .topSites)

let stories: [HomeItem] = state.pocketState.pocketData.compactMap { .pocket($0) }
Expand All @@ -68,4 +68,19 @@ final class HomepageDiffableDataSource:

apply(snapshot, animatingDifferences: true)
}

/// Gets the proper amount of top sites based on layout configuration
/// which is determined by the number of rows and number of tiles per row
/// - Parameters:
/// - topSiteState: state object for top site section
/// - textColor: text color from wallpaper configuration
private func getTopSites(
with topSitesState: TopSitesSectionState,
and textColor: TextColor?
) -> [HomepageDiffableDataSource.HomeItem] {
guard topSitesState.numberOfTilesPerRow != 0 else { return [] }
let topSites: [HomeItem] = topSitesState.topSitesData.compactMap { .topSite($0, textColor) }
let filterTopSites = topSites.prefix(Int(topSitesState.numberOfRows) * topSitesState.numberOfTilesPerRow)
return Array(filterTopSites)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,13 @@ final class HomepageSectionLayoutProvider {
}

private var logger: Logger
private var windowUUID: WindowUUID
private var dimensionImplementation: TopSitesDimensionImplementation

init(logger: Logger = DefaultLogger.shared) {
init(windowUUID: WindowUUID, logger: Logger = DefaultLogger.shared) {
self.windowUUID = windowUUID
self.logger = logger
self.dimensionImplementation = TopSitesDimensionImplementation(windowUUID: windowUUID)
}

func createCompositionalLayout() -> UICollectionViewCompositionalLayout {
Expand Down Expand Up @@ -166,11 +170,11 @@ final class HomepageSectionLayoutProvider {
return section
}

func createTopSitesSectionLayout(
private func createTopSitesSectionLayout(
for traitCollection: UITraitCollection,
availableWidth: CGFloat
) -> NSCollectionLayoutSection {
let numberOfTilesPerRow = TopSitesDimensionImplementation().getNumberOfTilesPerRow(
let numberOfTilesPerRow = dimensionImplementation.getNumberOfTilesPerRow(
availableWidth: availableWidth,
leadingInset: UX.leadingInset(
traitCollection: traitCollection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ final class HomepageViewController: UIViewController,
private var dataSource: HomepageDiffableDataSource?
// TODO: FXIOS-10541 will handle scrolling for wallpaper and other scroll issues
private lazy var wallpaperView: WallpaperBackgroundView = .build { _ in }
private var layoutConfiguration = HomepageSectionLayoutProvider().createCompositionalLayout()
private var overlayManager: OverlayModeManager
private var logger: Logger
private var homepageState: HomepageState
Expand Down Expand Up @@ -252,6 +251,7 @@ final class HomepageViewController: UIViewController,
}

private func configureCollectionView() {
let layoutConfiguration = HomepageSectionLayoutProvider(windowUUID: windowUUID).createCompositionalLayout()
let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layoutConfiguration)

HomepageItem.cellTypes.forEach {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,27 @@ import Redux

final class TopSitesAction: Action {
var topSites: [TopSiteState]?
var numberOfRows: Int?
var numberOfTilesPerRow: Int?

init(
topSites: [TopSiteState]? = nil,
numberOfRows: Int? = nil,
numberOfTilesPerRow: Int? = nil,
windowUUID: WindowUUID,
actionType: any ActionType
) {
self.topSites = topSites
self.numberOfRows = numberOfRows
self.numberOfTilesPerRow = numberOfTilesPerRow
super.init(windowUUID: windowUUID, actionType: actionType)
}
}

enum TopSitesActionType: ActionType {
case fetchTopSites
case updatedNumberOfRows
case updatedNumberOfTilesPerRow
}

enum TopSitesMiddlewareActionType: ActionType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,35 @@
// 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/

struct TopSitesDimensionImplementation {
/// Get the number of tiles (top sites) per row the user will see. This depends on the UI interface the user has.
import Common

class TopSitesDimensionImplementation {
/// The update count of number of tiles per row based on device layout
/// After updating the value, the top sites state should be updated respectively
private var numberOfTilesPerRow: Int? {
willSet {
guard newValue != numberOfTilesPerRow else { return }
store.dispatch(
TopSitesAction(
numberOfTilesPerRow: newValue,
windowUUID: self.windowUUID,
actionType: TopSitesActionType.updatedNumberOfTilesPerRow
)
)
}
}

private let windowUUID: WindowUUID
private let queue: DispatchQueueInterface
init(windowUUID: WindowUUID, queue: DispatchQueueInterface = DispatchQueue.main) {
self.windowUUID = windowUUID
self.queue = queue
}

/// Updates the number of tiles (top sites) per row the user will see. This depends on the UI interface the user has.
/// - Parameter availableWidth: available width size depending on device
/// - Parameter leadingInset: padding for top site section
/// - Parameter cellWidth: width of individual top site tiles
/// - Returns: The number of tiles per row the user will see, which is based on layout.
func getNumberOfTilesPerRow(availableWidth: CGFloat, leadingInset: CGFloat, cellWidth: CGFloat) -> Int {
var availableWidth = availableWidth - leadingInset * 2
var numberOfTiles = 0
Expand All @@ -17,6 +40,13 @@ struct TopSitesDimensionImplementation {
availableWidth = availableWidth - cellWidth - HomepageSectionLayoutProvider.UX.standardSpacing
}
let minCardsConstant = HomepageSectionLayoutProvider.UX.TopSitesConstants.minCards
return numberOfTiles < minCardsConstant ? minCardsConstant : numberOfTiles
let tilesPerRowCount = numberOfTiles < minCardsConstant ? minCardsConstant : numberOfTiles

// TODO: FXIOS-10972 - Investigate a better way to solve the crash issue that is resolved by adding this
queue.async {
self.numberOfTilesPerRow = tilesPerRowCount
}
Cramsden marked this conversation as resolved.
Show resolved Hide resolved

return tilesPerRowCount
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,40 @@
import Common
import Foundation
import Redux
import Shared

/// State for the top sites section that is used in the homepage
/// The state does not only contain the top sites list, but needs to also know about the number of rows
/// and tiles per row in order to only show a specific amount of the top sites data.
struct TopSitesSectionState: StateType, Equatable {
var windowUUID: WindowUUID
var topSitesData: [TopSiteState]
var numberOfRows: Int
var numberOfTilesPerRow: Int

init(profile: Profile = AppContainer.shared.resolve(), windowUUID: WindowUUID) {
let preferredNumberOfRows = profile.prefs.intForKey(PrefsKeys.NumberOfTopSiteRows)
let defaultNumberOfRows = TopSitesRowCountSettingsController.defaultNumberOfRows
let numberOfRows = Int(preferredNumberOfRows ?? defaultNumberOfRows)

init(windowUUID: WindowUUID) {
self.init(
windowUUID: windowUUID,
topSitesData: []
topSitesData: [],
numberOfRows: numberOfRows,
numberOfTilesPerRow: 0
)
}

private init(
windowUUID: WindowUUID,
topSitesData: [TopSiteState]
topSitesData: [TopSiteState],
numberOfRows: Int,
numberOfTilesPerRow: Int
) {
self.windowUUID = windowUUID
self.topSitesData = topSitesData
self.numberOfRows = numberOfRows
self.numberOfTilesPerRow = numberOfTilesPerRow
}

static let reducer: Reducer<Self> = { state, action in
Expand All @@ -42,7 +57,35 @@ struct TopSitesSectionState: StateType, Equatable {

return TopSitesSectionState(
windowUUID: state.windowUUID,
topSitesData: sites
topSitesData: sites,
numberOfRows: state.numberOfRows,
numberOfTilesPerRow: state.numberOfTilesPerRow
)
case TopSitesActionType.updatedNumberOfRows:
guard let topSitesAction = action as? TopSitesAction,
let numberOfRows = topSitesAction.numberOfRows
else {
return defaultState(from: state)
}

return TopSitesSectionState(
windowUUID: state.windowUUID,
topSitesData: state.topSitesData,
numberOfRows: numberOfRows,
numberOfTilesPerRow: state.numberOfTilesPerRow
)
case TopSitesActionType.updatedNumberOfTilesPerRow:
guard let topSitesAction = action as? TopSitesAction,
let numberOfTilesPerRow = topSitesAction.numberOfTilesPerRow
else {
return defaultState(from: state)
}

return TopSitesSectionState(
windowUUID: state.windowUUID,
topSitesData: state.topSitesData,
numberOfRows: state.numberOfRows,
numberOfTilesPerRow: numberOfTilesPerRow
)
default:
return defaultState(from: state)
Expand All @@ -52,7 +95,9 @@ struct TopSitesSectionState: StateType, Equatable {
static func defaultState(from state: TopSitesSectionState) -> TopSitesSectionState {
return TopSitesSectionState(
windowUUID: state.windowUUID,
topSitesData: state.topSitesData
topSitesData: state.topSitesData,
numberOfRows: state.numberOfRows,
numberOfTilesPerRow: state.numberOfTilesPerRow
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,13 @@ extension TopSitesSettingsViewController {
override var status: NSAttributedString {
let defaultValue = TopSitesRowCountSettingsController.defaultNumberOfRows
let numberOfRows = profile?.prefs.intForKey(PrefsKeys.NumberOfTopSiteRows) ?? defaultValue

store.dispatch(
TopSitesAction(
numberOfRows: Int(numberOfRows),
windowUUID: self.windowUUID,
actionType: TopSitesActionType.updatedNumberOfRows
)
)
return NSAttributedString(string: String(format: "%d", numberOfRows))
}

Expand Down
Loading