diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index 1e7119aa3c..813e11a3e4 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -32,7 +32,13 @@ extension Pixel { case appLaunch case refreshPressed case pullToRefresh - + + case deviceOrientationLandscape + + case keyboardGoWhileOnNTP + case keyboardGoWhileOnWebsite + case keyboardGoWhileOnSERP + case forgetAllPressedBrowsing case forgetAllPressedTabSwitching case forgetAllExecuted @@ -48,7 +54,12 @@ extension Pixel { case tabSwitcherNewLayoutSeen case tabSwitcherListEnabled case tabSwitcherGridEnabled - + case tabSwitcherNewTab + case tabSwitcherSwitchTabs + case tabSwitcherClickCloseTab + case tabSwitcherSwipeCloseTab + case tabSwitchLongPressNewTab + case settingsDoNotSellShown case settingsDoNotSellOn case settingsDoNotSellOff @@ -77,7 +88,17 @@ extension Pixel { case addressBarShare case addressBarSettings - + case addressBarCancelPressedOnNTP + case addressBarCancelPressedOnWebsite + case addressBarCancelPressedOnSERP + case addressBarClickOnNTP + case addressBarClickOnWebsite + case addressBarClickOnSERP + case addressBarClearPressedOnNTP + case addressBarClearPressedOnWebsite + case addressBarClearPressedOnSERP + case addressBarGestureDismiss + case shareSheetResultSuccess case shareSheetResultFail case shareSheetActivityCopy @@ -95,10 +116,13 @@ extension Pixel { case tabBarTabSwitcherPressed case homeScreenShown - case homeScreenFavouriteLaunched case homeScreenEditFavorite case homeScreenDeleteFavorite - + + case favoriteLaunchedNTP + case favoriteLaunchedWebsite + case favoriteLaunchedWidget + case autocompleteClickPhrase case autocompleteClickWebsite case autocompleteClickBookmark @@ -135,7 +159,14 @@ extension Pixel { case voiceSearchDone case openVoiceSearch case voiceSearchCancelled - + + case bookmarkLaunchList + case bookmarkLaunchScored + case bookmarkAddFavoriteFromBookmark + case bookmarkRemoveFavoriteFromBookmark + case bookmarkAddFavoriteBySwipe + case bookmarkDeletedFromBookmark + case bookmarkImportSuccess case bookmarkImportFailure case bookmarkImportFailureParsingDL @@ -509,7 +540,8 @@ extension Pixel { case swipeTabsUsed case swipeTabsUsedDaily - + case swipeToOpenNewTab + case bookmarksCleanupFailed case bookmarksCleanupAttemptedWhileSyncWasEnabled case favoritesCleanupFailed @@ -657,7 +689,13 @@ extension Pixel.Event { case .appLaunch: return "ml" case .refreshPressed: return "m_r" case .pullToRefresh: return "m_pull-to-reload" - + + case .deviceOrientationLandscape: return "m_device_orientation_landscape" + + case .keyboardGoWhileOnNTP: return "m_keyboard_go_click_ntp" + case .keyboardGoWhileOnWebsite: return "m_keyboard_go_click_website" + case .keyboardGoWhileOnSERP: return "m_keyboard_go_click_serp" + case .forgetAllPressedBrowsing: return "mf_bp" case .forgetAllPressedTabSwitching: return "mf_tp" case .forgetAllExecuted: return "mf" @@ -673,7 +711,12 @@ extension Pixel.Event { case .tabSwitcherNewLayoutSeen: return "m_ts_n" case .tabSwitcherListEnabled: return "m_ts_l" case .tabSwitcherGridEnabled: return "m_ts_g" - + case .tabSwitcherNewTab: return "m_tab_manager_new_tab_click" + case .tabSwitcherSwitchTabs: return "m_tab_manager_switch_tabs" + case .tabSwitcherClickCloseTab: return "m_tab_manager_close_tab_click" + case .tabSwitcherSwipeCloseTab: return "m_tab_manager_close_tab_swipe" + case .tabSwitchLongPressNewTab: return "m_tab_manager_long_press_new_tab" + case .settingsDoNotSellShown: return "ms_dns" case .settingsDoNotSellOn: return "ms_dns_on" case .settingsDoNotSellOff: return "ms_dns_off" @@ -700,9 +743,20 @@ extension Pixel.Event { case .browsingMenuAutofill: return "m_nav_autofill_menu_item_pressed" case .browsingMenuShare: return "m_browsingmenu_share" - + case .addressBarShare: return "m_addressbar_share" case .addressBarSettings: return "m_addressbar_settings" + case .addressBarCancelPressedOnNTP: return "m_addressbar_cancel_ntp" + case .addressBarCancelPressedOnWebsite: return "m_addressbar_cancel_website" + case .addressBarCancelPressedOnSERP: return "m_addressbar_cancel_serp" + case .addressBarClickOnNTP: return "m_addressbar_click_ntp" + case .addressBarClickOnWebsite: return "m_addressbar_click_website" + case .addressBarClickOnSERP: return "m_addressbar_click_serp" + case .addressBarClearPressedOnNTP: return "m_addressbar_focus_clear_entry_ntp" + case .addressBarClearPressedOnWebsite: return "m_addressbar_focus_clear_entry_website" + case .addressBarClearPressedOnSERP: return "m_addressbar_focus_clear_entry_serp" + case .addressBarGestureDismiss: return "m_addressbar_focus_dismiss_gesture" + case .shareSheetResultSuccess: return "m_sharesheet_result_success" case .shareSheetResultFail: return "m_sharesheet_result_fail" case .shareSheetActivityCopy: return "m_sharesheet_activity_copy" @@ -718,12 +772,22 @@ extension Pixel.Event { case .bookmarksButtonPressed: return "mt_bm" case .tabBarBookmarksLongPressed: return "mt_bl" case .tabBarTabSwitcherPressed: return "mt_tb" - + + case .bookmarkLaunchList: return "m_bookmark_launch_list" + case .bookmarkLaunchScored: return "m_bookmark_launch_scored" + case .bookmarkAddFavoriteFromBookmark: return "m_add_favorite_from_bookmark" + case .bookmarkRemoveFavoriteFromBookmark: return "m_remove_favorite_from_bookmark" + case .bookmarkAddFavoriteBySwipe: return "m_add_favorite_by_swipe" + case .bookmarkDeletedFromBookmark: return "m_bookmark_deleted_from_bookmark" + case .homeScreenShown: return "mh" - case .homeScreenFavouriteLaunched: return "mh_fl" case .homeScreenEditFavorite: return "mh_ef" case .homeScreenDeleteFavorite: return "mh_df" - + + case .favoriteLaunchedNTP: return "m_favorite_launched_ntp" + case .favoriteLaunchedWebsite: return "m_favorite_launched_website" + case .favoriteLaunchedWidget: return "m_favorite_launched_widget" + case .autocompleteClickPhrase: return "m_autocomplete_click_phrase" case .autocompleteClickWebsite: return "m_autocomplete_click_website" case .autocompleteClickBookmark: return "m_autocomplete_click_bookmark" @@ -1120,7 +1184,8 @@ extension Pixel.Event { case .swipeTabsUsed: return "m_swipe-tabs-used" case .swipeTabsUsedDaily: return "m_swipe-tabs-used-daily" - + case .swipeToOpenNewTab: return "m_addressbar_swipe_new_tab" + case .bookmarksCleanupFailed: return "m_d_bookmarks_cleanup_failed" case .bookmarksCleanupAttemptedWhileSyncWasEnabled: return "m_d_bookmarks_cleanup_attempted_while_sync_was_enabled" case .favoritesCleanupFailed: return "m_d_favorites_cleanup_failed" diff --git a/DuckDuckGo/AddOrEditBookmarkViewController.swift b/DuckDuckGo/AddOrEditBookmarkViewController.swift index 827efd99cf..06bc2f66e9 100644 --- a/DuckDuckGo/AddOrEditBookmarkViewController.swift +++ b/DuckDuckGo/AddOrEditBookmarkViewController.swift @@ -134,6 +134,15 @@ class AddOrEditBookmarkViewController: UIViewController { } func saveAndDismiss() { + let changes = viewModel.bookmark.changedValues() + if changes.contains(where: { $0.key == #keyPath(BookmarkEntity.favoriteFolders) }) { + if viewModel.bookmark.isFavorite(on: viewModel.favoritesDisplayMode.displayedFolder) { + Pixel.fire(pixel: .bookmarkAddFavoriteFromBookmark) + } else { + Pixel.fire(pixel: .bookmarkRemoveFavoriteFromBookmark) + } + } + viewModel.save() WidgetCenter.shared.reloadAllTimelines() self.delegate?.finishedEditing(self, entityID: viewModel.bookmark.objectID) diff --git a/DuckDuckGo/AutocompleteViewController.swift b/DuckDuckGo/AutocompleteViewController.swift index a94516a273..de7c414104 100644 --- a/DuckDuckGo/AutocompleteViewController.swift +++ b/DuckDuckGo/AutocompleteViewController.swift @@ -230,6 +230,7 @@ class AutocompleteViewController: UIViewController { } @IBAction func onAutocompleteDismissed(_ sender: Any) { + Pixel.fire(pixel: .addressBarGestureDismiss) delegate?.autocompleteWasDismissed() } } diff --git a/DuckDuckGo/BlankSnapshotViewController.swift b/DuckDuckGo/BlankSnapshotViewController.swift index 2143424548..83a62921b1 100644 --- a/DuckDuckGo/BlankSnapshotViewController.swift +++ b/DuckDuckGo/BlankSnapshotViewController.swift @@ -175,6 +175,10 @@ extension BlankSnapshotViewController: OmniBarDelegate { userInteractionDetected() } + func onTextFieldWillBeginEditing(_ omniBar: OmniBar, tapped: Bool) { + // No-op + } + func onTextFieldDidBeginEditing(_ omniBar: OmniBar) -> Bool { DispatchQueue.main.async { self.viewCoordinator.omniBar.resignFirstResponder() @@ -186,6 +190,10 @@ extension BlankSnapshotViewController: OmniBarDelegate { func onEnterPressed() { userInteractionDetected() } + + func onClearPressed() { + // No-op + } } extension BlankSnapshotViewController: TabSwitcherButtonDelegate { diff --git a/DuckDuckGo/BookmarksViewController.swift b/DuckDuckGo/BookmarksViewController.swift index 37a2f47bfe..ba753f694c 100644 --- a/DuckDuckGo/BookmarksViewController.swift +++ b/DuckDuckGo/BookmarksViewController.swift @@ -266,6 +266,8 @@ class BookmarksViewController: UIViewController, UITableViewDelegate { private func didSelectScoredBookmarkAtIndex(_ index: Int) { guard searchDataSource.results.indices.contains(index) else { return } dismiss() + + Pixel.fire(pixel: .bookmarkLaunchScored) delegate?.bookmarksDidSelect(url: searchDataSource.results[index].url) } @@ -351,6 +353,10 @@ class BookmarksViewController: UIViewController, UITableViewDelegate { } private func toggleFavoriteAfterSwipe(_ bookmark: BookmarkEntity, _ indexPath: IndexPath) { + if !bookmark.isFavorite(on: viewModel.favoritesDisplayMode.displayedFolder) { + Pixel.fire(pixel: .bookmarkAddFavoriteBySwipe) + } + self.viewModel.toggleFavorite(bookmark) WidgetCenter.shared.reloadAllTimelines() @@ -826,6 +832,7 @@ class BookmarksViewController: UIViewController, UITableViewDelegate { fileprivate func select(bookmark: BookmarkEntity) { guard let url = bookmark.urlObject else { return } dismiss() + Pixel.fire(pixel: .bookmarkLaunchList) delegate?.bookmarksDidSelect(url: url) } @@ -926,6 +933,7 @@ extension BookmarksViewController: AddOrEditBookmarkViewControllerDelegate { assertionFailure() return } + Pixel.fire(pixel: .bookmarkDeletedFromBookmark) showBookmarkDeletedMessage(bookmark) viewModel.softDeleteBookmark(bookmark) refreshFooterView() diff --git a/DuckDuckGo/HomeViewController.swift b/DuckDuckGo/HomeViewController.swift index 255fd1fe27..4523edcc2f 100644 --- a/DuckDuckGo/HomeViewController.swift +++ b/DuckDuckGo/HomeViewController.swift @@ -376,7 +376,7 @@ extension HomeViewController: FavoritesHomeViewSectionRendererDelegate { func favoritesRenderer(_ renderer: FavoritesHomeViewSectionRenderer, didSelect favorite: BookmarkEntity) { guard let url = favorite.urlObject else { return } - Pixel.fire(pixel: .homeScreenFavouriteLaunched) + Pixel.fire(pixel: .favoriteLaunchedNTP) Favicons.shared.loadFavicon(forDomain: url.host, intoCache: .fireproof, fromCache: .tabs) delegate?.home(self, didRequestUrl: url) } diff --git a/DuckDuckGo/MainViewController+KeyCommands.swift b/DuckDuckGo/MainViewController+KeyCommands.swift index f7fffded92..695be22846 100644 --- a/DuckDuckGo/MainViewController+KeyCommands.swift +++ b/DuckDuckGo/MainViewController+KeyCommands.swift @@ -162,7 +162,7 @@ extension MainViewController { guard tabSwitcherController == nil else { return } findInPageView.done() hideSuggestionTray() - onCancelPressed() + performCancel() } @objc func keyboardNewTab() { @@ -188,7 +188,7 @@ extension MainViewController { guard let tab = currentTab else { return } guard let index = tabManager.model.indexOf(tab: tab.tabModel) else { return } let targetTabIndex = index + 1 >= tabManager.model.count ? 0 : index + 1 - onCancelPressed() + performCancel() select(tabAt: targetTabIndex) } @@ -198,14 +198,14 @@ extension MainViewController { guard let tab = currentTab else { return } guard let index = tabManager.model.indexOf(tab: tab.tabModel) else { return } let targetTabIndex = index - 1 < 0 ? tabManager.model.count - 1 : index - 1 - onCancelPressed() + performCancel() select(tabAt: targetTabIndex) } @objc func keyboardShowAllTabs() { guard tabSwitcherController == nil else { return } - onCancelPressed() + performCancel() showTabSwitcher() } diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 49b978243b..5da2afe477 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -342,7 +342,7 @@ class MainViewController: UIViewController { assertionFailure() super.performSegue(withIdentifier: identifier, sender: sender) } - + private func installSwipeTabs() { guard swipeTabsCoordinator == nil else { return } @@ -357,6 +357,7 @@ class MainViewController: UIViewController { self?.select(tabAt: $0) } newTab: { [weak self] in + Pixel.fire(pixel: .swipeToOpenNewTab) self?.newTab() } onSwipeStarted: { [weak self] in self?.hideKeyboard() @@ -457,6 +458,32 @@ class MainViewController: UIViewController { selector: #selector(keyboardWillChangeFrame), name: UIResponder.keyboardWillChangeFrameNotification, object: nil) + + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), + name: UIResponder.keyboardWillHideNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow), + name: UIResponder.keyboardDidShowNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidHide), + name: UIResponder.keyboardDidHideNotification, object: nil) + } + + var keyboardShowing = false + + @objc + private func keyboardDidShow() { + keyboardShowing = true + } + + @objc + private func keyboardWillHide() { + if homeController?.collectionView.isDragging == true, keyboardShowing { + Pixel.fire(pixel: .addressBarGestureDismiss) + } + } + + @objc + private func keyboardDidHide() { + keyboardShowing = false } private func registerForSyncPausedNotifications() { @@ -1060,6 +1087,8 @@ class MainViewController: UIViewController { viewCoordinator.omniBar.forwardButton.isEnabled = viewCoordinator.toolbarForwardButton.isEnabled } + var orientationPixelWorker: DispatchWorkItem? + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) @@ -1071,11 +1100,25 @@ class MainViewController: UIViewController { coordinator.animate { _ in self.swipeTabsCoordinator?.refresh(tabsModel: self.tabManager.model, scrollToSelected: true) + + self.deferredFireOrientationPixel() } completion: { _ in ViewHighlighter.updatePositions() } } - + + private func deferredFireOrientationPixel() { + orientationPixelWorker?.cancel() + orientationPixelWorker = nil + if UIDevice.current.orientation.isLandscape { + let worker = DispatchWorkItem { + Pixel.fire(pixel: .deviceOrientationLandscape) + } + DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + 3, execute: worker) + orientationPixelWorker = worker + } + } + private func applyWidth() { if AppWidthObserver.shared.isLargeWidth { @@ -1720,11 +1763,25 @@ extension MainViewController: OmniBarDelegate { } func onEnterPressed() { + fireControllerAwarePixel(ntp: .keyboardGoWhileOnNTP, serp: .keyboardGoWhileOnSERP, website: .keyboardGoWhileOnWebsite) + guard !viewCoordinator.suggestionTrayContainer.isHidden else { return } suggestionTrayController?.willDismiss(with: viewCoordinator.omniBar.textField.text ?? "") } + func fireControllerAwarePixel(ntp: Pixel.Event, serp: Pixel.Event, website: Pixel.Event) { + if homeController != nil { + Pixel.fire(pixel: ntp) + } else if let currentTab { + if currentTab.url?.isDuckDuckGoSearch == true { + Pixel.fire(pixel: serp) + } else { + Pixel.fire(pixel: website) + } + } + } + func onDismissed() { dismissOmniBar() } @@ -1744,23 +1801,40 @@ extension MainViewController: OmniBarDelegate { } } - func onCancelPressed() { + func performCancel() { dismissOmniBar() hideSuggestionTray() homeController?.omniBarCancelPressed() self.showMenuHighlighterIfNeeded() } - + + func onCancelPressed() { + fireControllerAwarePixel(ntp: .addressBarCancelPressedOnNTP, + serp: .addressBarCancelPressedOnSERP, + website: .addressBarCancelPressedOnWebsite) + performCancel() + } + + func onClearPressed() { + fireControllerAwarePixel(ntp: .addressBarClearPressedOnNTP, + serp: .addressBarClearPressedOnSERP, + website: .addressBarClearPressedOnWebsite) + } + private var isSERPPresented: Bool { guard let tabURL = currentTab?.url else { return false } return tabURL.isDuckDuckGoSearch } - func onTextFieldWillBeginEditing(_ omniBar: OmniBar) { + func onTextFieldWillBeginEditing(_ omniBar: OmniBar, tapped: Bool) { if let currentTab { viewCoordinator.omniBar.refreshText(forUrl: currentTab.url, forceFullURL: true) } + if tapped { + fireControllerAwarePixel(ntp: .addressBarClickOnNTP, serp: .addressBarClickOnSERP, website: .addressBarClickOnWebsite) + } + guard homeController == nil else { return } if !skipSERPFlow, isSERPPresented, let query = omniBar.textField.text { @@ -1819,7 +1893,7 @@ extension MainViewController: FavoritesOverlayDelegate { func favoritesOverlay(_ overlay: FavoritesOverlay, didSelect favorite: BookmarkEntity) { guard let url = favorite.urlObject else { return } - Pixel.fire(pixel: .homeScreenFavouriteLaunched) + Pixel.fire(pixel: .favoriteLaunchedWebsite) homeController?.chromeDelegate = nil dismissOmniBar() Favicons.shared.loadFavicon(forDomain: url.host, intoCache: .fireproof, fromCache: .tabs) @@ -2165,7 +2239,7 @@ extension MainViewController: TabDelegate { guard let index = tabManager.model.indexOf(tab: tab) else { return } select(tabAt: index) DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { - self.onCancelPressed() + self.performCancel() } } @@ -2224,6 +2298,7 @@ extension MainViewController: TabSwitcherDelegate { extension MainViewController: BookmarksDelegate { func bookmarksDidSelect(url: URL) { + dismissOmniBar() if url.isBookmarklet() { executeBookmarklet(url) @@ -2236,6 +2311,7 @@ extension MainViewController: BookmarksDelegate { extension MainViewController: TabSwitcherButtonDelegate { func launchNewTab(_ button: TabSwitcherButton) { + Pixel.fire(pixel: .tabSwitchLongPressNewTab) newTab() } diff --git a/DuckDuckGo/OmniBar.swift b/DuckDuckGo/OmniBar.swift index 531133392d..751a15283d 100644 --- a/DuckDuckGo/OmniBar.swift +++ b/DuckDuckGo/OmniBar.swift @@ -174,8 +174,18 @@ class OmniBar: UIView { guard let range = field.selectedTextRange else { return } UIPasteboard.general.string = field.text(in: range) } + + textField.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector( onTextFieldTapped ))) } + var textFieldTapped = false + + @objc + private func onTextFieldTapped() { + textFieldTapped = true + textField.becomeFirstResponder() + } + private func configureSeparator() { separatorHeightConstraint.constant = 1.0 / UIScreen.main.scale } @@ -416,6 +426,7 @@ class OmniBar: UIView { } @IBAction func onClearButtonPressed(_ sender: Any) { + omniDelegate?.onClearPressed() refreshState(state.onTextClearedState) } @@ -487,9 +498,10 @@ extension OmniBar: UITextFieldDelegate { self.refreshState(self.state.onEditingStartedState) return true } - + func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { - omniDelegate?.onTextFieldWillBeginEditing(self) + omniDelegate?.onTextFieldWillBeginEditing(self, tapped: textFieldTapped) + textFieldTapped = false return true } diff --git a/DuckDuckGo/OmniBarDelegate.swift b/DuckDuckGo/OmniBarDelegate.swift index 698b0c73c2..ea9f7592ff 100644 --- a/DuckDuckGo/OmniBarDelegate.swift +++ b/DuckDuckGo/OmniBarDelegate.swift @@ -40,6 +40,8 @@ protocol OmniBarDelegate: AnyObject { func onSettingsLongPressed() + func onClearPressed() + func onCancelPressed() func onEnterPressed() @@ -54,7 +56,7 @@ protocol OmniBarDelegate: AnyObject { func onShareLongPressed() - func onTextFieldWillBeginEditing(_ omniBar: OmniBar) + func onTextFieldWillBeginEditing(_ omniBar: OmniBar, tapped: Bool) // Returns whether field should select the text or not func onTextFieldDidBeginEditing(_ omniBar: OmniBar) -> Bool diff --git a/DuckDuckGo/TabSwitcherViewController.swift b/DuckDuckGo/TabSwitcherViewController.swift index cb9b155f4e..e8354d995b 100644 --- a/DuckDuckGo/TabSwitcherViewController.swift +++ b/DuckDuckGo/TabSwitcherViewController.swift @@ -278,6 +278,7 @@ class TabSwitcherViewController: UIViewController { } @IBAction func onAddPressed(_ sender: UIBarButtonItem) { + Pixel.fire(pixel: .tabSwitcherNewTab) delegate.tabSwitcherDidRequestNewTab(tabSwitcher: self) dismiss() } @@ -416,6 +417,7 @@ extension TabSwitcherViewController: UICollectionViewDataSource { extension TabSwitcherViewController: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + Pixel.fire(pixel: .tabSwitcherSwitchTabs) currentSelection = indexPath.row markCurrentAsViewedAndDismiss() } diff --git a/DuckDuckGo/TabViewCell.swift b/DuckDuckGo/TabViewCell.swift index 0d98022045..d073d03871 100644 --- a/DuckDuckGo/TabViewCell.swift +++ b/DuckDuckGo/TabViewCell.swift @@ -128,7 +128,8 @@ class TabViewCell: UICollectionViewCell { }, completion: { _ in self.isHidden = true self.isDeleting = true - self.deleteTab() + Pixel.fire(pixel: .tabSwitcherSwipeCloseTab) + self.closeTab() }) } @@ -142,13 +143,17 @@ class TabViewCell: UICollectionViewCell { preview: UIImage?, reorderRecognizer: UIGestureRecognizer?) {} - @IBAction func deleteTab() { + func closeTab() { guard let tab = tab else { return } guard isNotReordering(with: collectionReorderRecognizer) else { return } - self.delegate?.deleteTab(tab: tab) } + @IBAction func deleteTab() { + Pixel.fire(pixel: .tabSwitcherClickCloseTab) + closeTab() + } + private func isNotReordering(with gestureRecognizer: UIGestureRecognizer?) -> Bool { guard let gestureRecognizer = gestureRecognizer else { return true } let inactiveStates: [UIGestureRecognizer.State] = [.possible, .failed, .cancelled, .ended]