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

Fix out-of-time notifications #6679

Merged
merged 1 commit into from
Sep 24, 2024
Merged

Conversation

rablador
Copy link
Contributor

@rablador rablador commented Aug 26, 2024

Currently the iOS app sends but a single notification about running out of time.

We should schedule 4 in-app notifications, with improved copy:

  • "You have 3 days left on this account."
  • “You have 2 days left on this account.”
  • “You have 1 day left on this account.”
  • "You have less than a day left on this account. Please add more time to continue using the VPN.”

We should also schedule 3 system notifications, with improved copy:

  • "You have 3 days left on this account. Please add more time to continue using the VPN.."
  • “You have one day left on this account. Please add more time to continue using the VPN.”
  • "Blocking internet: Your time on this account has expired. To continue using the internet, please add more time or disconnect the VPN.”

This change is Reviewable

@rablador rablador added the iOS Issues related to iOS label Aug 26, 2024
Copy link

linear bot commented Aug 26, 2024

Copy link
Contributor Author

@rablador rablador left a comment

Choose a reason for hiding this comment

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

Can be tested by invoking eg. the snippet below (please modify as needed) somewhere in initTunnelManagerOperation (AppDelegate), after tunnel manager is fully initialized. Account expired system notification cannot be tested this way due to it working in a different manner.

Task {
            try await delay(seconds: 5)

            print("Simulate close to expiry, 3 days")
            self.tunnelManager.simulateAccountExpiration(option: .closeToExpiry(days: 3))
            try await delay(seconds: 5)

            print("Simulate close to expiry, 2 days")
            self.tunnelManager.simulateAccountExpiration(option: .closeToExpiry(days: 2))
            try await delay(seconds: 5)

            print("Simulate close to expiry, 1 day")
            self.tunnelManager.simulateAccountExpiration(option: .closeToExpiry(days: 1))
            try await delay(seconds: 5)

            print("Simulate close to expiry, 0 days")
            self.tunnelManager.simulateAccountExpiration(option: .closeToExpiry(days: 0))
            try await delay(seconds: 5)

            print("Simulate expired account")
            self.tunnelManager.simulateAccountExpiration(option: .expired)
            try await delay(seconds: 5)

            print("Simulate active account")
            self.tunnelManager.simulateAccountExpiration(option: .active)
        }

Reviewable status: 0 of 8 files reviewed, all discussions resolved

@rablador rablador force-pushed the fix-out-of-time-notifications-ios-743 branch 5 times, most recently from 5ddd0e9 to ce3712a Compare August 26, 2024 12:37
Copy link
Contributor

@buggmagnet buggmagnet left a comment

Choose a reason for hiding this comment

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

It looks like you forgot to provide the implementation for delay, Task.sleep did the trick however.

Reviewed 6 of 6 files at r1, all commit messages.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @rablador)


ios/MullvadVPN/Notifications/Notification Providers/AccountExpiry.swift line 85 at r1 (raw file):

    // Used to compare dates with a precision of a minimum of seconds.
    var secondsPrecision: Date {
        Date(timeIntervalSince1970: TimeInterval(Int(timeIntervalSince1970)))

I have to say, it feels really convoluted to do this. I think we could have done entirely the same thing just using a single point in time, and calendar operations.

But it seems to work pretty well, so I'll leave it at that.


ios/MullvadVPN/Notifications/Notification Providers/AccountExpirySystemNotificationProvider.swift line 100 at r1 (raw file):

        let dateComponents = Calendar.current.dateComponents(
            [.second, .minute, .hour, .day, .month, .year],
            from: Date().addingTimeInterval(1) // Giving some leeway.

Can we clarify what "giving some leeway" means here ?

Copy link
Contributor Author

@rablador rablador left a comment

Choose a reason for hiding this comment

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

Ah, right!

Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @buggmagnet)


ios/MullvadVPN/Notifications/Notification Providers/AccountExpiry.swift line 85 at r1 (raw file):

Previously, buggmagnet wrote…

I have to say, it feels really convoluted to do this. I think we could have done entirely the same thing just using a single point in time, and calendar operations.

But it seems to work pretty well, so I'll leave it at that.

Indeed, done.


ios/MullvadVPN/Notifications/Notification Providers/AccountExpirySystemNotificationProvider.swift line 100 at r1 (raw file):

Previously, buggmagnet wrote…

Can we clarify what "giving some leeway" means here ?

Yep, done.

@rablador rablador force-pushed the fix-out-of-time-notifications-ios-743 branch from ce3712a to 57ed772 Compare September 2, 2024 09:33
@rablador rablador marked this pull request as draft September 4, 2024 08:43
buggmagnet
buggmagnet previously approved these changes Sep 9, 2024
Copy link
Contributor

@buggmagnet buggmagnet left a comment

Choose a reason for hiding this comment

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

:lgtm:

Reviewed 1 of 2 files at r2.
Reviewable status: 5 of 6 files reviewed, 1 unresolved discussion

Copy link
Collaborator

@mojganii mojganii left a comment

Choose a reason for hiding this comment

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

Reviewable status: 5 of 6 files reviewed, 4 unresolved discussions (waiting on @buggmagnet and @rablador)


ios/MullvadVPN/Notifications/Notification Providers/AccountExpirySystemNotificationProvider.swift line 117 at r2 (raw file):

        }

        let accountHasExpired = deviceState.accountData?.isExpired == true

nit : we can remove the equity expression. an use the variable for the next line


ios/MullvadVPN/Notifications/Notification Providers/AccountExpirySystemNotificationProvider.swift line 120 at r2 (raw file):

        let accountHasRecentlyExpired = deviceState.accountData?.isExpired != previousDeviceState.accountData?.isExpired

        self.accountHasRecentlyExpired = blockedStateByExpiredAccount && accountHasExpired && accountHasRecentlyExpired

isn't enough to compare the previous one state with the new one to realise if the account has been expired recently or not? must we evaluate the tunnel state for blocking state with the reason to be sure it's blocked? I feel we are over-evaluating to figure out if it's expired recently or not.


ios/MullvadVPN/Notifications/Notification Providers/AccountExpirySystemNotificationProvider.swift line 146 at r2 (raw file):

    }

    private var formattedRemainingDurationBody: String? {

nit : I think we can make it better :

Code snippet:

     private var formattedRemainingDurationBody: String? {
        guard !accountHasRecentlyExpired, let daysRemaining = accountExpiry.daysRemaining(for: .system)?.day else {
            return expiredText
        }
        return daysRemaining == 1 ? singleDayText : multipleDaysText
    }

Copy link
Contributor Author

@rablador rablador left a comment

Choose a reason for hiding this comment

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

Reviewable status: 5 of 6 files reviewed, 4 unresolved discussions (waiting on @buggmagnet and @mojganii)


ios/MullvadVPN/Notifications/Notification Providers/AccountExpirySystemNotificationProvider.swift line 120 at r2 (raw file):

Previously, mojganii wrote…

isn't enough to compare the previous one state with the new one to realise if the account has been expired recently or not? must we evaluate the tunnel state for blocking state with the reason to be sure it's blocked? I feel we are over-evaluating to figure out if it's expired recently or not.

I'm adding extra security here to make sure we don't accidentally spam users with notifications. Basically making sure that:

  1. Blocked state is because of account expiry, so that we don't trigger for any kind of blocked state.
  2. Current state is blocked state.
  3. Previous expiry state is not the same as current expiry state. This last check alone can't decide if we went from expired -> not expired, or the other way around. That's why I combine 2) and 3).

@rablador rablador force-pushed the fix-out-of-time-notifications-ios-743 branch 2 times, most recently from 9d288e4 to fbb3e81 Compare September 17, 2024 11:46
Copy link
Contributor Author

@rablador rablador left a comment

Choose a reason for hiding this comment

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

Reviewable status: 4 of 6 files reviewed, 3 unresolved discussions (waiting on @buggmagnet and @mojganii)


ios/MullvadVPN/Notifications/Notification Providers/AccountExpirySystemNotificationProvider.swift line 117 at r2 (raw file):

Previously, mojganii wrote…

nit : we can remove the equity expression. an use the variable for the next line

Done.


ios/MullvadVPN/Notifications/Notification Providers/AccountExpirySystemNotificationProvider.swift line 146 at r2 (raw file):

Previously, mojganii wrote…

nit : I think we can make it better :

This doesn't work since no .day should return nil rather than expiredText. This will in turn make notificationRequest return nil, which will prevent a notification being sent. accountHasRecentlyExpired will check that account expiry was recent, but for all other cases (account expired sometime in the past and no longer relevant) there should be no notification date to trigger.

@rablador rablador force-pushed the fix-out-of-time-notifications-ios-743 branch from fbb3e81 to d73de8a Compare September 18, 2024 11:45
@rablador rablador marked this pull request as ready for review September 18, 2024 11:46
Copy link
Contributor Author

@rablador rablador left a comment

Choose a reason for hiding this comment

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

Did some updates to fix the last system notification on account expiry. Code got less convoluted as well.

Reviewable status: 3 of 6 files reviewed, 3 unresolved discussions (waiting on @buggmagnet and @mojganii)

Copy link
Contributor

@buggmagnet buggmagnet left a comment

Choose a reason for hiding this comment

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

:lgtm:

Reviewed 1 of 2 files at r3, 2 of 2 files at r4, all commit messages.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @mojganii)

Copy link
Collaborator

@mojganii mojganii left a comment

Choose a reason for hiding this comment

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

Reviewable status: all files reviewed, 1 unresolved discussion

@buggmagnet buggmagnet force-pushed the fix-out-of-time-notifications-ios-743 branch from d73de8a to 2e2c318 Compare September 24, 2024 06:07
@buggmagnet buggmagnet merged commit d719e9b into main Sep 24, 2024
8 of 9 checks passed
@buggmagnet buggmagnet deleted the fix-out-of-time-notifications-ios-743 branch September 24, 2024 06:18
Copy link

🚨 End to end tests failed. Please check the failed workflow run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
iOS Issues related to iOS
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants