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