diff --git a/firefox-ios/Client.xcodeproj/project.pbxproj b/firefox-ios/Client.xcodeproj/project.pbxproj index cb931602e120b..65b2e565bb667 100644 --- a/firefox-ios/Client.xcodeproj/project.pbxproj +++ b/firefox-ios/Client.xcodeproj/project.pbxproj @@ -949,6 +949,7 @@ 8A6B799B2CDBCF3D003C3077 /* TopSitesManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A6B79992CDBCE2E003C3077 /* TopSitesManagerTests.swift */; }; 8A6B799D2CDBDAE4003C3077 /* MockContileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A6B799C2CDBDAE4003C3077 /* MockContileProvider.swift */; }; 8A6B79A02CDBDB0C003C3077 /* MockGoogleTopSiteManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A6B799F2CDBDB0C003C3077 /* MockGoogleTopSiteManager.swift */; }; + 8A6CDB472DF9E78400F11139 /* MockTabDisplayViewDragAndDropInteraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A6CDB462DF9E78400F11139 /* MockTabDisplayViewDragAndDropInteraction.swift */; }; 8A6E13982A71BA4E00A88FA8 /* TabWebViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A6E13972A71BA4E00A88FA8 /* TabWebViewTests.swift */; }; 8A6E63C52D4946760040D355 /* JumpBackInCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A6E63C42D49466C0040D355 /* JumpBackInCell.swift */; }; 8A6E63C72D4946B90040D355 /* JumpBackInSectionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A6E63C62D4946B40040D355 /* JumpBackInSectionState.swift */; }; @@ -8347,6 +8348,7 @@ 8A6B79992CDBCE2E003C3077 /* TopSitesManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopSitesManagerTests.swift; sourceTree = ""; }; 8A6B799C2CDBDAE4003C3077 /* MockContileProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockContileProvider.swift; sourceTree = ""; }; 8A6B799F2CDBDB0C003C3077 /* MockGoogleTopSiteManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockGoogleTopSiteManager.swift; sourceTree = ""; }; + 8A6CDB462DF9E78400F11139 /* MockTabDisplayViewDragAndDropInteraction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTabDisplayViewDragAndDropInteraction.swift; sourceTree = ""; }; 8A6E13972A71BA4E00A88FA8 /* TabWebViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabWebViewTests.swift; sourceTree = ""; }; 8A6E63C42D49466C0040D355 /* JumpBackInCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JumpBackInCell.swift; sourceTree = ""; }; 8A6E63C62D4946B40040D355 /* JumpBackInSectionState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JumpBackInSectionState.swift; sourceTree = ""; }; @@ -13893,6 +13895,7 @@ C889D7D22858C85200121E1D /* Mocks */ = { isa = PBXGroup; children = ( + 8A6CDB462DF9E78400F11139 /* MockTabDisplayViewDragAndDropInteraction.swift */, 8A01FE3F2DF0CE49002C483B /* MockDateProvider.swift */, 8A9F4F042DC8F4ED004644B9 /* MockRemoteTabs.swift */, 8A9F4F022DC8F4E6004644B9 /* MockPlaces.swift */, @@ -18622,6 +18625,7 @@ 21FA8FB22AE856EB0013B815 /* MockTabTrayCoordinatorDelegate.swift in Sources */, 8A87B4382CC1A92D003A9239 /* MockPocketManager.swift in Sources */, C8699152289177F5007ACC5C /* WallpaperNetworkingTests.swift in Sources */, + 8A6CDB472DF9E78400F11139 /* MockTabDisplayViewDragAndDropInteraction.swift in Sources */, C818AD452A2100BA007F30BC /* OnboardingNotificationCardHelperTests.swift in Sources */, E1AEC178286E0CF500062E29 /* LegacyHomepageViewControllerTests.swift in Sources */, 8A4EA0D42C01100200E4E4F1 /* MicrosurveySurfaceManagerTests.swift in Sources */, diff --git a/firefox-ios/Client/Coordinators/TabTray/TabTrayCoordinator.swift b/firefox-ios/Client/Coordinators/TabTray/TabTrayCoordinator.swift index c4d13efbcb0ea..5da22c700e9e4 100644 --- a/firefox-ios/Client/Coordinators/TabTray/TabTrayCoordinator.swift +++ b/firefox-ios/Client/Coordinators/TabTray/TabTrayCoordinator.swift @@ -48,7 +48,7 @@ class TabTrayCoordinator: BaseCoordinator, let tabTrayViewController = TabTrayViewController(panelType: panelType, windowUUID: tabManager.windowUUID) router.setRootViewController(tabTrayViewController) self.tabTrayViewController = tabTrayViewController - tabTrayViewController.childPanelControllers = makeChildPanels() + tabTrayViewController.childPanelControllers = makeChildPanels(dragAndDropDelegate: tabTrayViewController) tabTrayViewController.childPanelThemes = makeChildPanelThemes() tabTrayViewController.delegate = self tabTrayViewController.navigationHandler = self @@ -58,10 +58,14 @@ class TabTrayCoordinator: BaseCoordinator, tabTrayViewController?.setupOpenPanel(panelType: tabTraySection) } - private func makeChildPanels() -> [UINavigationController] { + private func makeChildPanels(dragAndDropDelegate: TabDisplayViewDragAndDropInteraction) -> [UINavigationController] { let windowUUID = tabManager.windowUUID - let regularTabsPanel = TabDisplayPanelViewController(isPrivateMode: false, windowUUID: windowUUID) - let privateTabsPanel = TabDisplayPanelViewController(isPrivateMode: true, windowUUID: windowUUID) + let regularTabsPanel = TabDisplayPanelViewController(isPrivateMode: false, + windowUUID: windowUUID, + dragAndDropDelegate: dragAndDropDelegate) + let privateTabsPanel = TabDisplayPanelViewController(isPrivateMode: true, + windowUUID: windowUUID, + dragAndDropDelegate: dragAndDropDelegate) let syncTabs = RemoteTabsPanel(windowUUID: windowUUID) let panels: [UIViewController] diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayPanelViewController.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayPanelViewController.swift index b0ead97847926..33a15cd7cdd11 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayPanelViewController.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayPanelViewController.swift @@ -76,13 +76,15 @@ class TabDisplayPanelViewController: UIViewController, init(isPrivateMode: Bool, windowUUID: WindowUUID, notificationCenter: NotificationProtocol = NotificationCenter.default, - themeManager: ThemeManager = AppContainer.shared.resolve()) { + themeManager: ThemeManager = AppContainer.shared.resolve(), + dragAndDropDelegate: TabDisplayViewDragAndDropInteraction) { self.panelType = isPrivateMode ? .privateTabs : .tabs self.tabsState = TabsPanelState(windowUUID: windowUUID, isPrivateMode: isPrivateMode) self.notificationCenter = notificationCenter self.themeManager = themeManager self.windowUUID = windowUUID super.init(nibName: nil, bundle: nil) + tabDisplayView.dragAndDropDelegate = dragAndDropDelegate } required init?(coder: NSCoder) { diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayView.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayView.swift index b3db98d8fab3a..7171abf82693c 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayView.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayView.swift @@ -6,6 +6,11 @@ import Common import Redux import UIKit +protocol TabDisplayViewDragAndDropInteraction: AnyObject { + func dragAndDropStarted() + func dragAndDropEnded() +} + class TabDisplayView: UIView, ThemeApplicable, UICollectionViewDelegate, @@ -26,6 +31,7 @@ class TabDisplayView: UIView, private let windowUUID: WindowUUID private let inactiveTabsTelemetry = InactiveTabsTelemetry() var theme: Theme? + weak var dragAndDropDelegate: TabDisplayViewDragAndDropInteraction? lazy var dataSource = TabDisplayDiffableDataSource( @@ -457,4 +463,12 @@ extension TabDisplayView: UICollectionViewDragDelegate, UICollectionViewDropDele store.dispatch(action) } + + func collectionView(_ collectionView: UICollectionView, dragSessionWillBegin session: UIDragSession) { + dragAndDropDelegate?.dragAndDropStarted() + } + + func collectionView(_ collectionView: UICollectionView, dragSessionDidEnd session: UIDragSession) { + dragAndDropDelegate?.dragAndDropEnded() + } } diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabTrayViewController.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabTrayViewController.swift index 86adba86e4609..d428b88ccb286 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabTrayViewController.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabTrayViewController.swift @@ -30,7 +30,8 @@ class TabTrayViewController: UIViewController, StoreSubscriber, FeatureFlaggable, TabTraySelectorDelegate, - TabTrayAnimationDelegate { + TabTrayAnimationDelegate, + TabDisplayViewDragAndDropInteraction { typealias SubscriberStateType = TabTrayState private struct UX { struct NavigationMenu { @@ -64,6 +65,7 @@ class TabTrayViewController: UIViewController, private lazy var panelContainer: UIView = .build { _ in } private var pageViewController: UIPageViewController? + private weak var pageScrollView: UIScrollView? private var swipeFromIndex: Int? private lazy var themeAnimator = TabTrayThemeAnimator() @@ -718,6 +720,7 @@ class TabTrayViewController: UIViewController, if let scrollView = pageVC.view.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView { scrollView.delegate = self + self.pageScrollView = scrollView } self.pageViewController = pageVC @@ -995,4 +998,14 @@ class TabTrayViewController: UIViewController, func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { swipeFromIndex = nil } + + // MARK: TabDisplayViewDragAndDropInteraction + + func dragAndDropStarted() { + pageScrollView?.isScrollEnabled = false + } + + func dragAndDropEnded() { + pageScrollView?.isScrollEnabled = true + } } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockTabDisplayViewDragAndDropInteraction.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockTabDisplayViewDragAndDropInteraction.swift new file mode 100644 index 0000000000000..03a9f3150096a --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockTabDisplayViewDragAndDropInteraction.swift @@ -0,0 +1,18 @@ +// 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/ + +@testable import Client + +class MockTabDisplayViewDragAndDropInteraction: TabDisplayViewDragAndDropInteraction { + var dragAndDropStartedCalled = 0 + var dragAndDropEndedCalled = 0 + + func dragAndDropStarted() { + dragAndDropStartedCalled += 1 + } + + func dragAndDropEnded() { + dragAndDropEndedCalled += 1 + } +} diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/TabDisplayPanelTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/TabDisplayPanelTests.swift index 1cbcae636f390..d825b4e05d878 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/TabDisplayPanelTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/TabDisplayPanelTests.swift @@ -50,7 +50,10 @@ final class TabDisplayPanelTests: XCTestCase { let subjectState = createSubjectState(isPrivateMode: isPrivateMode, emptyTabs: emptyTabs, emptyInactiveTabs: emptyInactiveTabs) - let subject = TabDisplayPanelViewController(isPrivateMode: isPrivateMode, windowUUID: .XCTestDefaultUUID) + let delegate = MockTabDisplayViewDragAndDropInteraction() + let subject = TabDisplayPanelViewController(isPrivateMode: isPrivateMode, + windowUUID: .XCTestDefaultUUID, + dragAndDropDelegate: delegate) subject.newState(state: subjectState) trackForMemoryLeaks(subject, file: file, line: line) diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/TabTrayViewControllerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/TabTrayViewControllerTests.swift index 3dd7febcf60be..9a094bfb80956 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/TabTrayViewControllerTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/TabTrayViewControllerTests.swift @@ -145,8 +145,13 @@ final class TabTrayViewControllerTests: XCTestCase { } private func makeChildPanels() -> [UINavigationController] { - let regularTabsPanel = TabDisplayPanelViewController(isPrivateMode: false, windowUUID: .XCTestDefaultUUID) - let privateTabsPanel = TabDisplayPanelViewController(isPrivateMode: true, windowUUID: .XCTestDefaultUUID) + let delegate = MockTabDisplayViewDragAndDropInteraction() + let regularTabsPanel = TabDisplayPanelViewController(isPrivateMode: false, + windowUUID: .XCTestDefaultUUID, + dragAndDropDelegate: delegate) + let privateTabsPanel = TabDisplayPanelViewController(isPrivateMode: true, + windowUUID: .XCTestDefaultUUID, + dragAndDropDelegate: delegate) let syncTabs = RemoteTabsPanel(windowUUID: .XCTestDefaultUUID) return [ ThemedNavigationController(rootViewController: regularTabsPanel, windowUUID: windowUUID),