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)?.then(resolveOnce)?.catch(reject);
60      });
61    }
62    return convertPermissionStatus(status);
63  } else if (typeof navigator !== 'undefined' && navigator?.permissions?.query) {
64    // TODO(Bacon): Support `push` in the future when it's stable.
65    const query = await navigator.permissions.query({ name: 'notifications' });
66    return convertPermissionStatus(query.state);
67  }
68  // Platforms like iOS Safari don't support Notifications so return denied.
69  return convertPermissionStatus('denied');
70}
71
72export default {
73  addListener: () => {},
74  removeListeners: () => {},
75  async getPermissionsAsync(): Promise<NotificationPermissionsStatus> {
76    return resolvePermissionAsync({ shouldAsk: false });
77  },
78  async requestPermissionsAsync(
79    request: NativeNotificationPermissionsRequest
80  ): Promise<NotificationPermissionsStatus> {
81    return resolvePermissionAsync({ shouldAsk: true });
82  },
83} as NotificationPermissionsModule;
84