Page MenuHomePhabricator

Use notifications permission request API on Android 13 while keeping old behavior on older Androids.
ClosedPublic

Authored by marcin on Mar 21 2023, 7:20 AM.
Tags
None
Referenced Files
Unknown Object (File)
Mon, Apr 8, 11:36 AM
Unknown Object (File)
Sun, Apr 7, 10:39 AM
Unknown Object (File)
Sun, Apr 7, 9:37 AM
Unknown Object (File)
Sun, Apr 7, 2:30 AM
Unknown Object (File)
Mon, Apr 1, 10:47 AM
Unknown Object (File)
Thu, Mar 28, 2:47 PM
Unknown Object (File)
Thu, Mar 28, 2:47 PM
Unknown Object (File)
Thu, Mar 28, 2:47 PM
Subscribers

Details

Summary

This differential uses native API to request notifications permissions in JavaScript on Android 13 devices.

Test Plan

Build and install the app on Android 13 device. Ensure the following:

  1. After you log in the OS prompts you to grant notifications permission. If you see this prompt before you log in, it is an error.
  2. If you don't allow this permission you are never prompted again (this is what happens on iOS).
  3. If you don't allow this permission ensure you don't see "Unable to initialize notifs" alert that is present on older Androids.

Build the app on older Android device. Ensure no change in behavior occurred.

Diff Detail

Repository
rCOMM Comm
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

This revision is now accepted and ready to land.Mar 21 2023, 10:08 AM
ashoat requested changes to this revision.Mar 22 2023, 11:42 AM

Just a question

native/push/push-handler.react.js
330 ↗(On Diff #23925)

Can you explain why it's necessary to check the permission if we already have it?

This revision now requires changes to proceed.Mar 22 2023, 11:42 AM
native/push/push-handler.react.js
330 ↗(On Diff #23925)

This if does not check whether we have permission but whether we can request it. It is basically more idiomatic way to check whether we are on Android 13 and we should try requesting permission from user since now they are not granted by default. On older androids canRequestNotificationsPermissionFromUser will resolve to hasPermission if a JS developer accidentally calls this method, so this check does not save us from an error, but just eliminates unnecessary call.

However I have an intuition that what you actually asked we rather why do we do this:
if (canRequestPermission)
instead of this:
if (!hasPermission && canRequestPermission).
If this is what you asked for then you are right. We shouldn't issue a call to CommAndroidNotifications.canRequestNotificationsPermissionFromUser if we already have permission. I will make this change.

native/push/push-handler.react.js
326–328 ↗(On Diff #23925)

There is no reason these two promises should be run in sequence! Not sure how I missed this earlier

330 ↗(On Diff #23925)

Yes it's the second thing

native/push/push-handler.react.js
326–328 ↗(On Diff #23925)

I don't understand the concern here. Both of those promises are awaited before we use their results to decide whether to issue notifications permission request.

More robust check as to whether issue notifications permission request.

native/push/push-handler.react.js
326–328 ↗(On Diff #23925)

I think the suggestion might be to use await Promise.all([...]) so they can both be "kicked off" simultaneously?

Please make the requested changes before landing

native/push/push-handler.react.js
326–328 ↗(On Diff #24082)

Yes, @atul is right. You are running these promises in sequence instead of in parallel. Please use Promise.all to run them in parallel, and please always try to reduce the number of await keywords in an async function

333 ↗(On Diff #24082)

We don't need this anymore... we know hasPermission is false because of the condition on line 330

This revision is now accepted and ready to land.Mar 24 2023, 1:57 PM
ashoat requested changes to this revision.Mar 25 2023, 2:26 AM
ashoat added inline comments.
native/push/push-handler.react.js
332 ↗(On Diff #24082)

Following discussion in D7106 I took a closer look at this code. The risk I highlighted there (which I remain concerned about) is the possibility of a second ensureAndroidPushNotifsEnabled call before the first completes, which could potentially result from eg. backgrounding / foregrounding the app quickly, or eg. a foreground timed at the same time as a logic.

I think @marcin's proposal in D7106 there was reasonable: instead of throwing, have the second call return a special value that tells the caller to basically "do nothing". This would avoid any weird behavior from the second call, and then presumably the first call would handle the intention of the second call.

My proposal has the same benefits, but arguably is a little cleaner. We can guarantee on the JS side that we only call requestNotificationsPermission once at a time:

function useRequestNotificationsPermissionFunc(): () => Promise<boolean> {
  const promiseRef = React.useRef<?Promise<boolean>>();
  return React.useCallback(() => {
    if (!promiseRef.current) {
      promiseRef.current = (async () => {
        const notifPermission =
          await CommAndroidNotifications.requestNotificationsPermission();
        promiseRef.current = undefined;
        return notifPermission;
      })();
    }
    return promiseRef.current;
  }, []);
}
This revision now requires changes to proceed.Mar 25 2023, 2:26 AM
native/push/push-handler.react.js
332 ↗(On Diff #24082)

s/logic/login, sorry

  1. Ensure there is only one promise calling Java notifications permission code at a time.
  2. Use Promise.all

Looks great! Please wait for Android build to complete successfully before landing

This revision is now accepted and ready to land.Mar 27 2023, 6:50 AM

Sleep to ensure logged out model disappears before the prompt launches. Rebase before landing