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

Remote Messaging Framework for macOS #2937

Merged
merged 59 commits into from
Jul 9, 2024
Merged

Remote Messaging Framework for macOS #2937

merged 59 commits into from
Jul 9, 2024

Conversation

ayoy
Copy link
Collaborator

@ayoy ayoy commented Jul 3, 2024

Task/Issue URL: https://app.asana.com/0/72649045549333/1202913520695928/f

Description:
RMF is now hidden behind a feature flag on macOS.

Steps to test this PR:

  1. In RemoteMessagingClient.swift, update endpoint to return https://www.jsonblob.com/api/1258315611053613056 for debug builds.
  2. Run ./clean-app.sh debug.
  3. Run the app and complete onboarding.
  4. After the onboarding, open new tab page
  5. Verify that you see "Message 1: Placeholder Title" message.
  6. Don't dismiss the message and restart the app.
  7. Verify that you see "Message 1: Placeholder Title" message again. Dismiss the message and restart the app.
  8. Verify that you see "Message 2: Placeholder Title" message.
  9. Dismiss the message and restart the app 3 more times, verify that on each subsequent run you're seeing message 3, 4 and 5. After dismissing message 5 and restarting the app, no message should be shown.

Also check multiple windows:

  1. With a message shown on a new tab page, open a few new windows.
  2. Interact with a message.
  3. Ensure that the message disappears from all new tab pages.

Definition of Done:


Internal references:

Pull Request Review Checklist
Software Engineering Expectations
Technical Design Template
Pull Request Documentation

@jaceklyp jaceklyp self-requested a review July 8, 2024 11:57
@@ -103,6 +105,26 @@ extension HomePage.Views {
}
}

@ViewBuilder
func remoteMessage() -> some View {
if let remoteMessage = activeRemoteMessageModel.remoteMessage, let modelType = remoteMessage.content, modelType.isSupported {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe guard would be nicer here to avoid this extra indentation?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that would be great, but guard won't work here because it's a view builder – guard requires a return statement and ViewBuilder doesn't expect return statements :/

DuckDuckGo/RemoteMessaging/ActiveRemoteMessageModel.swift Outdated Show resolved Hide resolved
DuckDuckGo/RemoteMessaging/RemoteMessagingClient.swift Outdated Show resolved Hide resolved
Comment on lines 114 to 133
func startRefreshingRemoteMessages() {
guard remoteMessagingAvailabilityProvider.isRemoteMessagingAvailable else {
stopRefreshingRemoteMessages()
return
}
/// Put the actual timer start into a separate function to allow it to be called unconditionally from
/// `isRemoteMessagingAvailablePublisher` event handler, where the new value (true) is emitted but it's
/// not yet available from `remoteMessagingAvailabilityProvider.isRemoteMessagingAvailable` property getter.
startMessagesRefreshTimer()
}

private func startMessagesRefreshTimer() {
scheduledRefreshCancellable = Timer.publish(every: Constants.minimumConfigurationRefreshInterval, on: .main, in: .default)
.autoconnect()
.prepend(Date())
.asVoid()
.sink { [weak self] in
self?.refreshRemoteMessages()
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having these two methods makes the code somewhat confusing, though I understand the use case. To simplify, consider adding a default argument to startRefreshingRemoteMessages, like this:
func startRefreshingRemoteMessages(forceStart: Bool = false). If forceStart is set to true, it bypasses the guard, allowing safe calls from the publisher. This also enables you to place your comment next to the forceStart argument, slightly making the documentation more obvious to the user :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that makes perfect sense. Let me update it.

import NetworkProtection
import Subscription

final class RemoteMessagingConfigMatcherProvider: RemoteMessagingConfigMatcherProviding {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you think this code could be also unified between platforms?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can't be unified (at least not without a fundamental rewrite which would also be worth considering), because the userAttributeMatcher argument is a different object on iOS and macOS (macOS doesn't have isWidgetInstalled and will get a few more attributes in the future).

Comment on lines 72 to 78
case .autoRenewable: queryItems.append(URLQueryItem(name: parameter.rawValue, value: "auto_renewable"))
case .notAutoRenewable: queryItems.append(URLQueryItem(name: parameter.rawValue, value: "not_auto_renewable"))
case .gracePeriod: queryItems.append(URLQueryItem(name: parameter.rawValue, value: "grace_period"))
case .inactive: queryItems.append(URLQueryItem(name: parameter.rawValue, value: "inactive"))
case .expired: queryItems.append(URLQueryItem(name: parameter.rawValue, value: "expired"))
case .unknown: queryItems.append(URLQueryItem(name: parameter.rawValue, value: "unknown"))
case nil: break
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps consider making subscription status conform to String, or if it is not possible make custom rawValue func so you can just change it to
guard let status = subscription?.status else { break }
queryItems.append(URLQueryItem(name: parameter.rawValue, value: status.rawValue))

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, great idea. I haven't spent much time with this class, it was copied over 1:1 from iOS, and I actually considered moving it to BSK. Let my try to do it now.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, I've moved DefaultRemoteMessagingSurveyURLBuilder to BSK (which resulted in an extra commit to BSK and iOS 😇 , but it was worth it!) cc @samsymons

Comment on lines 83 to 94
case .apple: queryItems.append(URLQueryItem(name: parameter.rawValue, value: "apple"))
case .google: queryItems.append(URLQueryItem(name: parameter.rawValue, value: "google"))
case .stripe: queryItems.append(URLQueryItem(name: parameter.rawValue, value: "stripe"))
case .unknown: queryItems.append(URLQueryItem(name: parameter.rawValue, value: "unknown"))
case nil: break
}
case .privacyProBilling:
switch subscription?.billingPeriod {
case .monthly: queryItems.append(URLQueryItem(name: parameter.rawValue, value: "monthly"))
case .yearly: queryItems.append(URLQueryItem(name: parameter.rawValue, value: "yearly"))
case .unknown: queryItems.append(URLQueryItem(name: parameter.rawValue, value: "unknown"))
case nil: break
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same for these

Comment on lines 123 to 133
private func hardwareModel() -> String {
var systemInfo = utsname()
uname(&systemInfo)

let machineMirror = Mirror(reflecting: systemInfo.machine)
let identifier = machineMirror.children.reduce("") { identifier, element in
guard let value = element.value as? Int8, value != 0 else { return identifier }
return identifier + String(UnicodeScalar(UInt8(value)))
}

return identifier
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like it belongs to some common class, not necessarily here

@ayoy ayoy marked this pull request as ready for review July 8, 2024 15:52
@ayoy ayoy requested a review from jaceklyp July 8, 2024 15:52
@ayoy ayoy merged commit 6658805 into main Jul 9, 2024
19 checks passed
@ayoy ayoy deleted the dominik/rmf-macos branch July 9, 2024 06:41
@ayoy ayoy restored the dominik/rmf-macos branch October 10, 2024 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants