-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathKnockPushNotificationProvider.tsx
More file actions
127 lines (113 loc) · 3.68 KB
/
KnockPushNotificationProvider.tsx
File metadata and controls
127 lines (113 loc) · 3.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import { ChannelData } from "@knocklabs/client";
import { useKnockClient } from "@knocklabs/react-core";
import React, { createContext, useCallback, useContext } from "react";
export interface Device {
token: string;
locale?: string;
timezone?: string;
}
export interface KnockPushNotificationContextType {
registerPushTokenToChannel(
token: string,
channelId: string,
locale?: string,
): Promise<ChannelData | void>;
unregisterPushTokenFromChannel(
token: string,
channelId: string,
): Promise<ChannelData | void>;
}
const KnockPushNotificationContext = createContext<
KnockPushNotificationContextType | undefined
>(undefined);
export interface KnockPushNotificationProviderProps {
children?: React.ReactElement;
}
export const KnockPushNotificationProvider: React.FC<
KnockPushNotificationProviderProps
> = ({ children }) => {
const knockClient = useKnockClient();
const setChannelData = useCallback(
async (devices: Device[], channelId: string): Promise<ChannelData> => {
return knockClient.user.setChannelData({
channelId: channelId,
channelData: { devices: devices },
});
},
[knockClient],
);
// Acts like an upsert. Inserts or updates
const registerPushTokenToChannel = useCallback(
async (
token: string,
channelId: string,
locale: string = Intl.DateTimeFormat().resolvedOptions().locale,
): Promise<ChannelData | void> => {
const newDevice: Device = {
token,
locale,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
};
return knockClient.user
.getChannelData({ channelId: channelId })
.then((result: ChannelData) => {
const devices: Device[] = result.data["devices"] || [];
const existingDeviceIndex = devices.findIndex(
(device) => device.token === token,
);
// add device to devices array
if (existingDeviceIndex === -1) {
devices.push(newDevice);
}
// update device case
else {
devices[existingDeviceIndex] = newDevice;
}
knockClient.log("[Knock] registerPushTokenToChannel success");
return setChannelData(devices, channelId);
})
.catch((_) => {
// no existing devices case
return setChannelData([newDevice], channelId);
});
},
[knockClient, setChannelData],
);
const unregisterPushTokenFromChannel = useCallback(
async (token: string, channelId: string): Promise<ChannelData | void> => {
return knockClient.user
.getChannelData({ channelId: channelId })
.then((result: ChannelData) => {
const devices: Device[] = result.data["devices"] || [];
const updatedDevices = devices.filter(
(device) => device.token !== token,
);
knockClient.log("unregisterPushTokenFromChannel success");
return setChannelData(updatedDevices, channelId);
})
.catch((error) => {
console.error(
`[Knock] Error deregistering device from channel:`,
error,
);
});
},
[knockClient, setChannelData],
);
return (
<KnockPushNotificationContext.Provider
value={{ registerPushTokenToChannel, unregisterPushTokenFromChannel }}
>
{children}
</KnockPushNotificationContext.Provider>
);
};
export const usePushNotifications = (): KnockPushNotificationContextType => {
const context = useContext(KnockPushNotificationContext);
if (context === undefined) {
throw new Error(
"[Knock] usePushNotifications must be used within a KnockPushNotificationProvider",
);
}
return context;
};