Skip to content

Commit 8308f2f

Browse files
authored
Add FXIOS-14998 [News Transition] Homepage redesign layout for iPad (#32822)
1 parent 9e0091c commit 8308f2f

9 files changed

Lines changed: 34 additions & 102 deletions

File tree

firefox-ios/Client/FeatureFlags/LegacyFeatureFlagsManager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ extension FeatureFlaggable {
1616
var isHomepageStoriesScrollDirectionVertical: Bool {
1717
let scrollDirection: ScrollDirection = featureFlags
1818
.getCustomState(for: .homepageStoriesScrollDirection) ?? .baseline
19-
return scrollDirection == .vertical && UIDevice.current.userInterfaceIdiom == .phone
19+
return scrollDirection == .vertical
2020
}
2121
}
2222

firefox-ios/Client/Frontend/Home/Homepage/HomepageDiffableDataSource.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,8 @@ final class HomepageDiffableDataSource:
133133
snapshot.appendItems(bookmarks, toSection: .bookmarks(textColor))
134134
}
135135

136-
if state.shouldShowSpacer {
137-
snapshot.appendSections([.spacer])
138-
snapshot.appendItems([.spacer], toSection: .spacer)
139-
}
136+
snapshot.appendSections([.spacer])
137+
snapshot.appendItems([.spacer], toSection: .spacer)
140138

141139
if state.searchState.shouldShowSearchBar {
142140
snapshot.appendSections([.searchBar])

firefox-ios/Client/Frontend/Home/Homepage/HomepageViewController.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ final class HomepageViewController: UIViewController,
6565
private var shouldUseNewsTransitionHeader: Bool {
6666
let scrollDirection: ScrollDirection = featureFlags.getCustomState(for: .homepageStoriesScrollDirection)
6767
?? .baseline
68-
return scrollDirection == .vertical && UIDevice.current.userInterfaceIdiom == .phone
68+
return scrollDirection == .vertical
6969
}
7070

7171
private var availableWidth: CGFloat {
@@ -487,6 +487,7 @@ final class HomepageViewController: UIViewController,
487487
)
488488

489489
collectionView.keyboardDismissMode = .onDrag
490+
collectionView.contentInsetAdjustmentBehavior = .never
490491
collectionView.addGestureRecognizer(longPressRecognizer)
491492
collectionView.showsVerticalScrollIndicator = false
492493
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]

firefox-ios/Client/Frontend/Home/Homepage/Layout/HomepageSectionLayoutProvider.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ final class HomepageSectionLayoutProvider: FeatureFlaggable {
5959

6060
/// `storiesPeekOffset` is how much we want the stories section (not including section header)
6161
/// to peek in vertically from the bottom of the homepage viewport
62-
static let storiesPeekOffset: CGFloat = 14
62+
static let storiesPeekOffset: CGFloat = 16
63+
static let storiesPeekOffsetiPad: CGFloat = 36
6364

6465
@MainActor
6566
static func getAbsoluteCellWidth(device: UIUserInterfaceIdiom = UIDevice.current.userInterfaceIdiom,
@@ -88,6 +89,13 @@ final class HomepageSectionLayoutProvider: FeatureFlaggable {
8889

8990
return UX.PocketConstants.getAbsoluteCellWidth(collectionViewWidth: containerWidth)
9091
}
92+
93+
@MainActor
94+
static func getStoriesPeekOffset(
95+
device: UIUserInterfaceIdiom = UIDevice.current.userInterfaceIdiom
96+
) -> CGFloat {
97+
return device == .pad ? UX.PocketConstants.storiesPeekOffsetiPad : UX.PocketConstants.storiesPeekOffset
98+
}
9199
}
92100

93101
struct JumpBackInConstants {
@@ -592,7 +600,7 @@ final class HomepageSectionLayoutProvider: FeatureFlaggable {
592600
} else {
593601
let headerHeight = HomepageDimensionCalculator.fittingHeight(for: NewsTransitionHeaderView(),
594602
width: environment.container.contentSize.width)
595-
spacerHeight = max(0.1, rawSpacerHeight - headerHeight - UX.PocketConstants.storiesPeekOffset)
603+
spacerHeight = max(0.1, rawSpacerHeight - headerHeight - UX.PocketConstants.getStoriesPeekOffset())
596604
}
597605
}
598606

@@ -914,7 +922,7 @@ final class HomepageSectionLayoutProvider: FeatureFlaggable {
914922

915923
let newsAffordanceHeaderHeight = HomepageDimensionCalculator
916924
.fittingHeight(for: NewsTransitionHeaderView(), width: environment.container.contentSize.width)
917-
let fullPeekOffset = newsAffordanceHeaderHeight + UX.PocketConstants.storiesPeekOffset
925+
let fullPeekOffset = newsAffordanceHeaderHeight + UX.PocketConstants.getStoriesPeekOffset()
918926

919927
if rawSpacerHeight >= fullPeekOffset {
920928
// Enough free space to show the full affordance header and the full peek offset.

firefox-ios/Client/Frontend/Home/Homepage/Merino/MerinoState.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ struct MerinoState: StateType, Equatable {
9393
private static func initializeSectionHeaderState() -> SectionHeaderConfiguration {
9494
let scrollDirection: ScrollDirection = LegacyFeatureFlagsManager.shared
9595
.getCustomState(for: .homepageStoriesScrollDirection) ?? .baseline
96-
let isScrollDirectionVertical = scrollDirection == .vertical && UIDeviceDetails.userInterfaceIdiom == .phone
96+
let isScrollDirectionVertical = scrollDirection == .vertical
9797

9898
return SectionHeaderConfiguration(
9999
title: .FirefoxHomepage.Pocket.NewsSectionTitle,

firefox-ios/Client/Frontend/Home/Homepage/Redux/HomepageMiddleware.swift

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ final class HomepageMiddleware: FeatureFlaggable, Notifiable {
5959
case HomepageActionType.initialize:
6060
self.dispatchPrivacyNoticeConfigurationAction(action: action)
6161
self.dispatchSearchBarConfigurationAction(action: action)
62-
self.dispatchSpacerConfigurationAction(action: action)
6362

6463
case HomepageActionType.viewWillTransition, ToolbarActionType.cancelEdit,
6564
GeneralBrowserActionType.navigateBack, GeneralBrowserActionType.didCloseTabFromToolbar:
@@ -99,16 +98,6 @@ final class HomepageMiddleware: FeatureFlaggable, Notifiable {
9998
)
10099
}
101100

102-
private func dispatchSpacerConfigurationAction(action: Action) {
103-
store.dispatch(
104-
HomepageAction(
105-
shouldShowSpacer: self.shouldShowSpacer(),
106-
windowUUID: action.windowUUID,
107-
actionType: HomepageMiddlewareActionType.configuredSpacer
108-
)
109-
)
110-
}
111-
112101
private func shouldShowSearchBar(
113102
for device: UIUserInterfaceIdiom = UIDevice.current.userInterfaceIdiom,
114103
and isLandscape: Bool = UIWindow.isLandscape
@@ -122,10 +111,6 @@ final class HomepageMiddleware: FeatureFlaggable, Notifiable {
122111
return true
123112
}
124113

125-
private func shouldShowSpacer(for device: UIUserInterfaceIdiom = UIDevice.current.userInterfaceIdiom) -> Bool {
126-
return device == .phone
127-
}
128-
129114
// MARK: - Notifications
130115
private func observeNotifications() {
131116
let notifications: [Notification.Name] = [

firefox-ios/Client/Frontend/Home/Homepage/Redux/HomepageState.swift

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,6 @@ struct HomepageState: ScreenState, Equatable {
3434
/// new privacy notice is available after a user has already accepted the ToS/ToU
3535
let shouldShowPrivacyNotice: Bool
3636

37-
/// `shouldShowSpacer` is true when the homepage redesign, which pins the stories section to the bottom of the homepage,
38-
/// is enabled on iPhone. This forces the space between the shortcuts section and the stories section to be as far apart
39-
/// as possible. This value is kept in state because it depends on the feature flag manager
40-
let shouldShowSpacer: Bool
41-
4237
/// `availableContentHeight` represents the height available for the homepage content to occupy when the address is not
4338
/// being edited. This is used to keep the homepage layout constant, such that it doesn't shift when the homepage's
4439
/// view size changes eg when the address bar is tapped and the keyboard is presented. This value is kept in state
@@ -72,7 +67,6 @@ struct HomepageState: ScreenState, Equatable {
7267
isZeroSearch: homepageState.isZeroSearch,
7368
shouldTriggerImpression: homepageState.shouldTriggerImpression,
7469
shouldShowPrivacyNotice: homepageState.shouldShowPrivacyNotice,
75-
shouldShowSpacer: homepageState.shouldShowSpacer,
7670
availableContentHeight: homepageState.availableContentHeight,
7771
availableWallpaperHeight: homepageState.availableWallpaperHeight
7872
)
@@ -92,7 +86,6 @@ struct HomepageState: ScreenState, Equatable {
9286
isZeroSearch: false,
9387
shouldTriggerImpression: false,
9488
shouldShowPrivacyNotice: false,
95-
shouldShowSpacer: false,
9689
availableContentHeight: 0,
9790
availableWallpaperHeight: 0
9891
)
@@ -111,7 +104,6 @@ struct HomepageState: ScreenState, Equatable {
111104
isZeroSearch: Bool,
112105
shouldTriggerImpression: Bool,
113106
shouldShowPrivacyNotice: Bool,
114-
shouldShowSpacer: Bool,
115107
availableContentHeight: CGFloat,
116108
availableWallpaperHeight: CGFloat
117109
) {
@@ -127,7 +119,6 @@ struct HomepageState: ScreenState, Equatable {
127119
self.isZeroSearch = isZeroSearch
128120
self.shouldTriggerImpression = shouldTriggerImpression
129121
self.shouldShowPrivacyNotice = shouldShowPrivacyNotice
130-
self.shouldShowSpacer = shouldShowSpacer
131122
self.availableContentHeight = availableContentHeight
132123
self.availableWallpaperHeight = availableWallpaperHeight
133124
}
@@ -155,8 +146,6 @@ struct HomepageState: ScreenState, Equatable {
155146
return handleDidTabChangeToHomepageAction(state: state, action: action)
156147
case HomepageMiddlewareActionType.configuredPrivacyNotice:
157148
return handlePrivacyNoticeInitialization(action: action, state: state)
158-
case HomepageMiddlewareActionType.configuredSpacer:
159-
return handleSpacerInitialization(action: action, state: state)
160149
default:
161150
return passthroughState(from: state, action: action)
162151
}
@@ -177,7 +166,6 @@ struct HomepageState: ScreenState, Equatable {
177166
isZeroSearch: state.isZeroSearch,
178167
shouldTriggerImpression: false,
179168
shouldShowPrivacyNotice: state.shouldShowPrivacyNotice,
180-
shouldShowSpacer: state.shouldShowSpacer,
181169
availableContentHeight: state.availableContentHeight,
182170
availableWallpaperHeight: state.availableWallpaperHeight
183171
)
@@ -200,7 +188,6 @@ struct HomepageState: ScreenState, Equatable {
200188
isZeroSearch: isZeroSearch,
201189
shouldTriggerImpression: false,
202190
shouldShowPrivacyNotice: state.shouldShowPrivacyNotice,
203-
shouldShowSpacer: state.shouldShowSpacer,
204191
availableContentHeight: state.availableContentHeight,
205192
availableWallpaperHeight: state.availableWallpaperHeight
206193
)
@@ -229,7 +216,6 @@ struct HomepageState: ScreenState, Equatable {
229216
isZeroSearch: state.isZeroSearch,
230217
shouldTriggerImpression: false,
231218
shouldShowPrivacyNotice: state.shouldShowPrivacyNotice,
232-
shouldShowSpacer: state.shouldShowSpacer,
233219
availableContentHeight: availableContentHeight,
234220
availableWallpaperHeight: availableWallpaperHeight
235221
)
@@ -250,7 +236,6 @@ struct HomepageState: ScreenState, Equatable {
250236
isZeroSearch: state.isZeroSearch,
251237
shouldTriggerImpression: false,
252238
shouldShowPrivacyNotice: false,
253-
shouldShowSpacer: state.shouldShowSpacer,
254239
availableContentHeight: state.availableContentHeight,
255240
availableWallpaperHeight: state.availableWallpaperHeight
256241
)
@@ -271,7 +256,6 @@ struct HomepageState: ScreenState, Equatable {
271256
isZeroSearch: state.isZeroSearch,
272257
shouldTriggerImpression: true,
273258
shouldShowPrivacyNotice: state.shouldShowPrivacyNotice,
274-
shouldShowSpacer: state.shouldShowSpacer,
275259
availableContentHeight: state.availableContentHeight,
276260
availableWallpaperHeight: state.availableWallpaperHeight
277261
)
@@ -292,32 +276,6 @@ struct HomepageState: ScreenState, Equatable {
292276
isZeroSearch: state.isZeroSearch,
293277
shouldTriggerImpression: false,
294278
shouldShowPrivacyNotice: true,
295-
shouldShowSpacer: state.shouldShowSpacer,
296-
availableContentHeight: state.availableContentHeight,
297-
availableWallpaperHeight: state.availableWallpaperHeight
298-
)
299-
}
300-
301-
@MainActor
302-
private static func handleSpacerInitialization(action: Action, state: Self) -> HomepageState {
303-
guard let isSpacerEnabled = (action as? HomepageAction)?.shouldShowSpacer else {
304-
return defaultState(from: state)
305-
}
306-
307-
return HomepageState(
308-
windowUUID: state.windowUUID,
309-
headerState: HeaderState.reducer(state.headerState, action),
310-
messageState: MessageCardState.reducer(state.messageState, action),
311-
topSitesState: TopSitesSectionState.reducer(state.topSitesState, action),
312-
searchState: SearchBarState.reducer(state.searchState, action),
313-
jumpBackInState: JumpBackInSectionState.reducer(state.jumpBackInState, action),
314-
bookmarkState: BookmarksSectionState.reducer(state.bookmarkState, action),
315-
pocketState: MerinoState.reducer(state.merinoState, action),
316-
wallpaperState: WallpaperState.reducer(state.wallpaperState, action),
317-
isZeroSearch: state.isZeroSearch,
318-
shouldTriggerImpression: false,
319-
shouldShowPrivacyNotice: state.shouldShowPrivacyNotice,
320-
shouldShowSpacer: isSpacerEnabled,
321279
availableContentHeight: state.availableContentHeight,
322280
availableWallpaperHeight: state.availableWallpaperHeight
323281
)
@@ -347,7 +305,6 @@ struct HomepageState: ScreenState, Equatable {
347305
isZeroSearch: state.isZeroSearch,
348306
shouldTriggerImpression: false,
349307
shouldShowPrivacyNotice: state.shouldShowPrivacyNotice,
350-
shouldShowSpacer: state.shouldShowSpacer,
351308
availableContentHeight: state.availableContentHeight,
352309
availableWallpaperHeight: state.availableWallpaperHeight
353310
)
@@ -375,7 +332,6 @@ struct HomepageState: ScreenState, Equatable {
375332
isZeroSearch: state.isZeroSearch,
376333
shouldTriggerImpression: false,
377334
shouldShowPrivacyNotice: state.shouldShowPrivacyNotice,
378-
shouldShowSpacer: state.shouldShowSpacer,
379335
availableContentHeight: state.availableContentHeight,
380336
availableWallpaperHeight: state.availableWallpaperHeight
381337
)

firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Homepage/HomepageDiffableDataSourceTests.swift

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,10 @@ final class HomepageDiffableDataSourceTests: XCTestCase {
4343
)
4444

4545
let snapshot = dataSource.snapshot()
46-
XCTAssertEqual(snapshot.numberOfSections, 1)
47-
XCTAssertEqual(snapshot.sectionIdentifiers, [.header])
46+
XCTAssertEqual(snapshot.numberOfSections, 2)
47+
XCTAssertEqual(snapshot.sectionIdentifiers, [.header, .spacer])
4848
XCTAssertEqual(snapshot.numberOfItems(inSection: .header), 1)
49+
XCTAssertEqual(snapshot.numberOfItems(inSection: .spacer), 1)
4950
}
5051

5152
@MainActor
@@ -86,6 +87,7 @@ final class HomepageDiffableDataSourceTests: XCTestCase {
8687
XCTAssertEqual(snapshot.numberOfItems(inSection: .pocket(.systemCyan)), 20)
8788
let expectedSections: [HomepageSection] = [
8889
.header,
90+
.spacer,
8991
.pocket(.systemCyan)
9092
]
9193
XCTAssertEqual(snapshot.sectionIdentifiers, expectedSections)
@@ -121,7 +123,8 @@ final class HomepageDiffableDataSourceTests: XCTestCase {
121123
XCTAssertEqual(snapshot.numberOfItems(inSection: .topSites(nil, numberOfTilesPerRow, true)), displayedTopSitesCount)
122124
let expectedSections: [HomepageSection] = [
123125
.header,
124-
.topSites(nil, numberOfTilesPerRow, true)
126+
.topSites(nil, numberOfTilesPerRow, true),
127+
.spacer
125128
]
126129
XCTAssertEqual(snapshot.sectionIdentifiers, expectedSections)
127130
}
@@ -157,7 +160,8 @@ final class HomepageDiffableDataSourceTests: XCTestCase {
157160
XCTAssertEqual(snapshot.numberOfItems(inSection: .topSites(nil, numberOfTilesPerRow, false)), topSitesCount)
158161
let expectedSections: [HomepageSection] = [
159162
.header,
160-
.topSites(nil, numberOfTilesPerRow, false)
163+
.topSites(nil, numberOfTilesPerRow, false),
164+
.spacer
161165
]
162166
XCTAssertEqual(snapshot.sectionIdentifiers, expectedSections)
163167
}
@@ -181,6 +185,7 @@ final class HomepageDiffableDataSourceTests: XCTestCase {
181185
XCTAssertEqual(snapshot.numberOfItems(inSection: .pocket(nil)), 20)
182186
let expectedSections: [HomepageSection] = [
183187
.header,
188+
.spacer,
184189
.pocket(nil)
185190
]
186191
XCTAssertEqual(snapshot.sectionIdentifiers, expectedSections)
@@ -211,7 +216,8 @@ final class HomepageDiffableDataSourceTests: XCTestCase {
211216
XCTAssertEqual(snapshot.itemIdentifiers(inSection: .messageCard).first, HomepageItem.messageCard(configuration))
212217
let expectedSections: [HomepageSection] = [
213218
.header,
214-
.messageCard
219+
.messageCard,
220+
.spacer
215221
]
216222
XCTAssertEqual(snapshot.sectionIdentifiers, expectedSections)
217223
}
@@ -249,7 +255,8 @@ final class HomepageDiffableDataSourceTests: XCTestCase {
249255
XCTAssertEqual(snapshot.numberOfItems(inSection: .bookmarks(nil)), 1)
250256
let expectedSections: [HomepageSection] = [
251257
.header,
252-
.bookmarks(nil)
258+
.bookmarks(nil),
259+
.spacer
253260
]
254261
XCTAssertEqual(snapshot.sectionIdentifiers, expectedSections)
255262
}
@@ -281,7 +288,8 @@ final class HomepageDiffableDataSourceTests: XCTestCase {
281288
XCTAssertEqual(snapshot.numberOfItems(inSection: .jumpBackIn(nil, mockSectionConfig)), 1)
282289
let expectedSections: [HomepageSection] = [
283290
.header,
284-
.jumpBackIn(nil, mockSectionConfig)
291+
.jumpBackIn(nil, mockSectionConfig),
292+
.spacer
285293
]
286294
XCTAssertEqual(snapshot.sectionIdentifiers, expectedSections)
287295
}
@@ -302,7 +310,8 @@ final class HomepageDiffableDataSourceTests: XCTestCase {
302310
let snapshot = dataSource.snapshot()
303311
let expectedSections: [HomepageSection] = [
304312
.header,
305-
.privacyNotice
313+
.privacyNotice,
314+
.spacer
306315
]
307316
XCTAssertEqual(snapshot.sectionIdentifiers, expectedSections)
308317
}

firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Homepage/Redux/HomepageMiddlewareTests.swift

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -489,31 +489,6 @@ final class HomepageMiddlewareTests: XCTestCase, StoreTestUtility {
489489
XCTAssertEqual(configuredSearchBarAction.isSearchBarEnabled, false)
490490
}
491491

492-
// MARK: - Spacer
493-
func test_initializeAction_configuresSpacer() throws {
494-
let subject = createSubject()
495-
let action = HomepageAction(
496-
windowUUID: .XCTestDefaultUUID,
497-
actionType: HomepageActionType.initialize
498-
)
499-
let dispatchExpectation = XCTestExpectation(description: "Spacer configured middleware action dispatched")
500-
501-
mockStore.dispatchCalled = {
502-
dispatchExpectation.fulfill()
503-
}
504-
505-
subject.homepageProvider(AppState(), action)
506-
507-
wait(for: [dispatchExpectation], timeout: 1)
508-
509-
let (configuredSpacerAction, configuredSpacerActionCount) = try getActionInfo(for: .configuredSpacer)
510-
let configuredSpacerActionType = try XCTUnwrap(configuredSpacerAction.actionType as? HomepageMiddlewareActionType)
511-
512-
XCTAssertEqual(configuredSpacerActionCount, 1)
513-
XCTAssertEqual(configuredSpacerActionType, .configuredSpacer)
514-
XCTAssertEqual(configuredSpacerAction.shouldShowSpacer, true)
515-
}
516-
517492
func test_initializeAction_dispatchesConfiguresPrivacyNotice_withTrueValue() throws {
518493
let mockPrivacyNoticeHelper = MockPrivacyNoticeHelper()
519494
mockPrivacyNoticeHelper.shouldShowResult = true

0 commit comments

Comments
 (0)