1import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
2import { PermissionStatus } from 'unimodules-permissions-interface';
3
4import {
5  NativeNotificationPermissionsRequest,
6  NotificationPermissionsStatus,
7} from './NotificationPermissions.types';
8import { NotificationPermissionsModule } from './NotificationPermissionsModule.types';
9
10function convertPermissionStatus(
11  status?: NotificationPermission | 'prompt'
12): NotificationPermissionsStatus {
13  switch (status) {
14    case 'granted':
15      return {
16        status: PermissionStatus.GRANTED,
17        expires: 'never',
18        canAskAgain: false,
19        granted: true,
20      };
21    case 'denied':
22      return {
23        status: PermissionStatus.DENIED,
24        expires: 'never',
25        canAskAgain: false,
26        granted: false,
27      };
28    default:
29      return {
30        status: PermissionStatus.UNDETERMINED,
31        expires: 'never',
32        canAskAgain: true,
33        granted: false,
34      };
35  }
36}
37
38async function resolvePermissionAsync({
39  shouldAsk,
40}: {
41  shouldAsk: boolean;
42}): Promise<NotificationPermissionsStatus> {
43  if (!canUseDOM) {
44    return convertPermissionStatus('denied');
45  }
46
47  const { Notification = {} } = window as any;
48  if (typeof Notification.requestPermission !== 'undefined') {
49    let status = Notification.permission;
50    if (shouldAsk) {
51      status = await new Promise((resolve, reject) => {
52        let resolved = false;
53        function resolveOnce(status: string) {
54          if (!resolved) {
55            resolved = true;
56            resolve(status);
57          }
58        }
59        // Some browsers require a callback argument and some return a Promise
60        Notification.requestPermission(resolveOnce)
61          ?.then(resolveOnce)
62          ?.catch(reject);
63      });
64    }
65    return convertPermissionStatus(status);
66  } else if (typeof navigator !== 'undefined' && navigator?.permissions?.query) {
67    // TODO(Bacon): Support `push` in the future when it's stable.
68    const query = await navigator.permissions.query({ name: 'notifications' });
69    return convertPermissionStatus(query.state);
70  }
71  // Platforms like iOS Safari don't support Notifications so return denied.
72  return convertPermissionStatus('denied');
73}
74
75export default {
76  addListener: () => {},
77  removeListeners: () => {},
78  async getPermissionsAsync(): Promise<NotificationPermissionsStatus> {
79    return resolvePermissionAsync({ shouldAsk: false });
80  },
81  async requestPermissionsAsync(
82    request: NativeNotificationPermissionsRequest
83  ): Promise<NotificationPermissionsStatus> {
84    return resolvePermissionAsync({ shouldAsk: true });
85  },
86} as NotificationPermissionsModule;
87