Skip to content

Commit

Permalink
Add FXIOS-10252 Show notifications for Close Remote Tab requests from…
Browse files Browse the repository at this point in the history
… other clients (#22440)

Add FXIOS-10252 Show notifications for Close Remote Tab requests from other clients
  • Loading branch information
skhamis authored Oct 18, 2024
1 parent 7c4c7f4 commit 3de77a9
Show file tree
Hide file tree
Showing 13 changed files with 121 additions and 26 deletions.
16 changes: 12 additions & 4 deletions firefox-ios/Account/FxAPushMessageHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import Shared
import Account
import Common
import enum MozillaAppServices.IncomingDeviceCommand

let PendingAccountDisconnectedKey = "PendingAccountDisconnect"

Expand Down Expand Up @@ -57,9 +58,11 @@ extension FxAPushMessageHandler {
case .tabReceived(_, let tabData):
let title = tabData.entries.last?.title ?? ""
let url = tabData.entries.last?.url ?? ""
completion(.success(PushMessage.commandReceived(tab: ["title": title, "url": url])))
default:
break
let command = CommandReceived.tabReceived(tab: ["title": title, "url": url])
completion(.success(PushMessage.commandReceived(command: command)))
case .tabsClosed(_, let payload):
let command = CommandReceived.tabsClosed(urls: payload.urls)
completion(.success(PushMessage.commandReceived(command: command)))
}
case .deviceConnected(let deviceName):
completion(.success(PushMessage.deviceConnected(deviceName)))
Expand Down Expand Up @@ -92,7 +95,7 @@ enum PushMessageType: String {
}

enum PushMessage: Equatable {
case commandReceived(tab: [String: String])
case commandReceived(command: CommandReceived)
case deviceConnected(String)
case deviceDisconnected
case profileUpdated
Expand Down Expand Up @@ -158,3 +161,8 @@ enum PushMessageError: MaybeErrorType {
}
}
}

enum CommandReceived: Equatable {
case tabReceived(tab: [String: String])
case tabsClosed(urls: [String])
}
23 changes: 17 additions & 6 deletions firefox-ios/Client/Application/AppDelegate+PushNotifications.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,23 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
let content = response.notification.request.content

if content.categoryIdentifier == NotificationSurfaceManager.Constant.notificationCategoryId {
switch response.actionIdentifier {
case UNNotificationDismissActionIdentifier:
notificationSurfaceManager.didDismissNotification(content.userInfo)
default:
notificationSurfaceManager.didTapNotification(content.userInfo)
}
switch response.actionIdentifier {
case UNNotificationDismissActionIdentifier:
notificationSurfaceManager.didDismissNotification(content.userInfo)
default:
notificationSurfaceManager.didTapNotification(content.userInfo)
}
} else if content.categoryIdentifier == NotificationCloseTabs.notificationCategoryId {
switch response.actionIdentifier {
case UNNotificationDefaultActionIdentifier:
// Since the notification is coming from the background, we should give a little
// time to ensure we can show the recently closed tabs panel
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
NotificationCenter.default.post(name: .RemoteTabNotificationTapped, object: nil)
}
default:
break
}
}
// We don't poll for commands here because we do that once the application wakes up
// The notification service ensures that when the application wakes up, the application will check
Expand Down
8 changes: 1 addition & 7 deletions firefox-ios/Client/Application/AppLaunchUtil.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,7 @@ class AppLaunchUtil {
}
}

// RustFirefoxAccounts doesn't have access to the feature flags
// So we check the nimbus flag here before sending it to the startup
var fxaFeatures: RustFxAFeatures = []
if LegacyFeatureFlagsManager.shared.isFeatureEnabled(.closeRemoteTabs, checking: .buildAndUser) {
fxaFeatures.insert(.closeRemoteTabs)
}
RustFirefoxAccounts.startup(prefs: profile.prefs, features: fxaFeatures) { _ in
RustFirefoxAccounts.startup(prefs: profile.prefs) { _ in
self.logger.log("RustFirefoxAccounts started", level: .info, category: .sync)
AppEventQueue.signal(event: .accountManagerInitialized)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,12 @@ class BrowserViewController: UIViewController,
selector: #selector(handlePageZoomLevelUpdated),
name: .PageZoomLevelUpdated,
object: nil)
notificationCenter.addObserver(
self,
selector: #selector(openRecentlyClosedTabs),
name: .RemoteTabNotificationTapped,
object: nil
)
}

private func switchToolbarIfNeeded() {
Expand Down Expand Up @@ -3587,6 +3593,14 @@ extension BrowserViewController: HomePanelDelegate {
func homePanelDidRequestBookmarkToast(url: URL?, action: BookmarkAction) {
showBookmarkToast(bookmarkURL: url, action: action)
}

@objc
func openRecentlyClosedTabs() {
DispatchQueue.main.async {
self.navigationHandler?.show(homepanelSection: .history)
self.notificationCenter.post(name: .OpenRecentlyClosedTabs)
}
}
}

// MARK: - SearchViewController
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,11 @@ class HistoryPanel: UIViewController,
}

showClearRecentHistory()
break
case .OpenRecentlyClosedTabs:
historyCoordinatorDelegate?.showRecentlyClosedTab()
applySnapshot(animatingDifferences: true)
break
default:
// no need to do anything at all
break
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ class HistoryPanelViewModel: FeatureFlaggable {
Notification.Name.PrivateDataClearedHistory,
Notification.Name.DynamicFontChanged,
Notification.Name.DatabaseWasReopened,
Notification.Name.OpenClearRecentHistory]
Notification.Name.OpenClearRecentHistory,
Notification.Name.OpenRecentlyClosedTabs]

// MARK: - Inits

Expand Down
14 changes: 14 additions & 0 deletions firefox-ios/Client/Frontend/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3725,6 +3725,20 @@ extension String {
tableName: nil,
value: "View",
comment: "Label for an action used to view one or more tabs from a notification.")

// MARK: - Close tab notifications
public static let CloseTab_ArrivingNotification_title = MZLocalizedString(
key: "CloseTab_ArrivingNotification_title",
tableName: "FxANotification",
value: "Firefox tabs closed: %1$@",
comment: "Title of notification shown when a remote device has requested to close a number of tabs defined by ($1)")

// Notification Actions
public static let CloseTabViewActionTitle = MZLocalizedString(
key: "CloseTab.ViewAction.title",
tableName: "FxANotification",
value: "View recently closed tabs",
comment: "Label for an action used to view recently closed tabs.")
}

// MARK: - Engagement notification
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@ struct NotificationSentTabs {
static let deviceNameKey = "deviceName"
}
}

struct NotificationCloseTabs {
static let closeTabsKey = "closeRemoteTabs"
static let notificationCategoryId: String = "org.mozilla.ios.fxa.notification.category"
static let messageIdKey: String = "messageId"
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,13 @@ class SyncDataDisplay {
}

switch message {
case .commandReceived(let tab):
displayNewSentTabNotification(tab: tab)
case .commandReceived(let command):
switch command {
case .tabReceived(let tab):
displayNewSentTabNotification(tab: tab)
case .tabsClosed(let urls):
displayClosedTabNotification(urls: urls)
}
case .deviceConnected(let deviceName):
displayDeviceConnectedNotification(deviceName)
case .deviceDisconnected:
Expand Down Expand Up @@ -214,6 +219,18 @@ class SyncDataDisplay {
}
}

func displayClosedTabNotification(urls: [String]) {
notificationContent.userInfo[NotificationCloseTabs.closeTabsKey]
= [NotificationCloseTabs.messageIdKey: "closeRemoteTab"]

notificationContent.categoryIdentifier = NotificationCloseTabs.notificationCategoryId

presentNotification(
title: String(format: .CloseTab_ArrivingNotification_title, "\(urls.count)"),
body: .CloseTabViewActionTitle
)
}

func presentNotification(title: String, body: String, titleArg: String? = nil, bodyArg: String? = nil) {
func stringWithOptionalArg(_ s: String, _ a: String?) -> String {
if let a = a {
Expand Down
7 changes: 1 addition & 6 deletions firefox-ios/RustFxA/RustFirefoxAccounts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ final class Unknown: NSObject, NSCoding {
public struct RustFxAFeatures: OptionSet {
public let rawValue: Int

public static let closeRemoteTabs = RustFxAFeatures(rawValue: 1 << 0)

public init(rawValue: Int) {
self.rawValue = rawValue
}
Expand Down Expand Up @@ -177,10 +175,7 @@ open class RustFirefoxAccounts {

let type = UIDevice.current.userInterfaceIdiom == .pad ? DeviceType.tablet : DeviceType.mobile

var capabilities: [DeviceCapability] = [.sendTab]
if features.contains(.closeRemoteTabs) {
capabilities.append(.closeTabs)
}
let capabilities: [DeviceCapability] = [.sendTab, .closeTabs]
let deviceConfig = DeviceConfig(
name: DeviceInfo.defaultClientName(),
deviceType: type,
Expand Down
4 changes: 4 additions & 0 deletions firefox-ios/Shared/NotificationConstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ extension Notification.Name {

public static let OpenClearRecentHistory = Notification.Name("OpenClearRecentHistory")

public static let RemoteTabNotificationTapped = Notification.Name("RemoteTabNotificationTapped")

public static let OpenRecentlyClosedTabs = Notification.Name("OpenRecentlyClosedTabs")

public static let LibraryPanelStateDidChange = Notification.Name("LibraryPanelStateDidChange")

public static let SearchSettingsChanged = Notification.Name("SearchSettingsChanged")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,21 @@ class NotificationManagerTests: XCTestCase {
XCTAssertEqual(self.center.pendingRequests.count, 1)
}

func testCloseRemoteTabNotification() {
let notificationContent = UNMutableNotificationContent()
// Test with the categoryIdentify being the close remote tab identifier
notificationContent.categoryIdentifier = NotificationCloseTabs.notificationCategoryId
let request = UNNotificationRequest(identifier: "id1",
content: notificationContent,
trigger: nil)

UNUserNotificationCenter.current().add(request) { (error) in
if let error = error {
XCTFail("Error adding notification request: \(error)")
}
}
}

// MARK: - Helpers

private func buildRequest(title: String, body: String, id: String) -> UNNotificationRequest {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@ import Common

class HistoryPanelTests: XCTestCase {
let windowUUID: WindowUUID = .XCTestDefaultUUID
private var notificationCenter: MockNotificationCenter!
override func setUp() {
super.setUp()
LegacyFeatureFlagsManager.shared.initializeDeveloperFeatures(with: MockProfile())
DependencyHelperMock().bootstrapDependencies()
notificationCenter = MockNotificationCenter()
}

override func tearDown() {
super.tearDown()
DependencyHelperMock().reset()
notificationCenter = nil
}

func testHistoryButtons() {
Expand Down Expand Up @@ -88,6 +91,14 @@ class HistoryPanelTests: XCTestCase {
XCTAssertTrue(panel.shouldDismissOnDone())
}

func testHistoryPanel_ShouldReceiveClosedTabNotification() {
let panel = createSubject()
panel.loadView()
notificationCenter.post(name: .OpenRecentlyClosedTabs)

XCTAssertEqual(notificationCenter.postCallCount, 1)
}

private func createSubject() -> HistoryPanel {
let profile = MockProfile()
let subject = HistoryPanel(profile: profile, windowUUID: windowUUID)
Expand Down

0 comments on commit 3de77a9

Please sign in to comment.