@realtimexsco/live-chat 1.4.0 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/firebase-messaging-sw.js +68 -0
- package/dist/index.cjs +285 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +87 -1
- package/dist/index.d.ts +87 -1
- package/dist/index.mjs +274 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -5
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* RealtimeX live-chat push service worker (FCM).
|
|
2
|
+
*
|
|
3
|
+
* Copy this file into your app's public root so it is served at
|
|
4
|
+
* /firebase-messaging-sw.js:
|
|
5
|
+
*
|
|
6
|
+
* cp node_modules/@realtimexsco/live-chat/dist/firebase-messaging-sw.js public/
|
|
7
|
+
*
|
|
8
|
+
* Do NOT edit the Firebase config into this file — the chat package passes it
|
|
9
|
+
* via the registration URL (?config=...), so the copy stays generic.
|
|
10
|
+
*
|
|
11
|
+
* If your app already has its own service worker at the root scope, merge the
|
|
12
|
+
* contents of this file into it instead (two workers cannot share a scope).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/* global firebase, importScripts */
|
|
16
|
+
|
|
17
|
+
importScripts(
|
|
18
|
+
"https://www.gstatic.com/firebasejs/10.14.1/firebase-app-compat.js",
|
|
19
|
+
);
|
|
20
|
+
importScripts(
|
|
21
|
+
"https://www.gstatic.com/firebasejs/10.14.1/firebase-messaging-compat.js",
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
var params = new URL(self.location.href).searchParams;
|
|
25
|
+
var rawConfig = params.get("config");
|
|
26
|
+
|
|
27
|
+
if (rawConfig) {
|
|
28
|
+
try {
|
|
29
|
+
var firebaseConfig = JSON.parse(rawConfig);
|
|
30
|
+
|
|
31
|
+
firebase.initializeApp(firebaseConfig);
|
|
32
|
+
|
|
33
|
+
// Background messages that carry a `notification` payload are displayed
|
|
34
|
+
// by FCM automatically; initializing messaging here is what enables it.
|
|
35
|
+
firebase.messaging();
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error("[realtimex] Invalid firebase config in SW URL:", error);
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
console.warn(
|
|
41
|
+
"[realtimex] firebase-messaging-sw.js registered without ?config= — " +
|
|
42
|
+
"push notifications will not be delivered",
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Focus (or open) the chat when a notification is clicked.
|
|
47
|
+
self.addEventListener("notificationclick", function (event) {
|
|
48
|
+
event.notification.close();
|
|
49
|
+
|
|
50
|
+
var targetUrl =
|
|
51
|
+
(event.notification.data &&
|
|
52
|
+
event.notification.data.FCM_MSG &&
|
|
53
|
+
event.notification.data.FCM_MSG.data &&
|
|
54
|
+
event.notification.data.FCM_MSG.data.link) ||
|
|
55
|
+
"/";
|
|
56
|
+
|
|
57
|
+
event.waitUntil(
|
|
58
|
+
clients
|
|
59
|
+
.matchAll({ type: "window", includeUncontrolled: true })
|
|
60
|
+
.then(function (windowClients) {
|
|
61
|
+
for (var i = 0; i < windowClients.length; i++) {
|
|
62
|
+
if ("focus" in windowClients[i]) return windowClients[i].focus();
|
|
63
|
+
}
|
|
64
|
+
if (clients.openWindow) return clients.openWindow(targetUrl);
|
|
65
|
+
return undefined;
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
});
|
package/dist/index.cjs
CHANGED
|
@@ -17777,6 +17777,279 @@ var packageDefaultComponents = {
|
|
|
17777
17777
|
ChatSocketStatus: ChatSocketStatus_default
|
|
17778
17778
|
};
|
|
17779
17779
|
|
|
17780
|
+
// src/notifications/push.service.ts
|
|
17781
|
+
init_client();
|
|
17782
|
+
var PUSH_TOKEN_STORAGE_KEY = "realtimex-push-token";
|
|
17783
|
+
var SERVICE_WORKER_PATH = "/firebase-messaging-sw.js";
|
|
17784
|
+
var v2Url = "https://realtimex.softwareco.com/api/v2";
|
|
17785
|
+
var apiGetPushConfig = async () => {
|
|
17786
|
+
const { data } = await apiClient.get(`${v2Url}/notifications/push/config`);
|
|
17787
|
+
return data?.data;
|
|
17788
|
+
};
|
|
17789
|
+
var apiRegisterPushToken = async (token, deviceLabel) => {
|
|
17790
|
+
const { data } = await apiClient.post(`${v2Url}/notifications/push/token`, {
|
|
17791
|
+
token,
|
|
17792
|
+
deviceLabel
|
|
17793
|
+
});
|
|
17794
|
+
return data?.data;
|
|
17795
|
+
};
|
|
17796
|
+
var apiRemovePushToken = async (token) => {
|
|
17797
|
+
const { data } = await apiClient.delete(`${v2Url}/notifications/push/token`, {
|
|
17798
|
+
data: { token }
|
|
17799
|
+
});
|
|
17800
|
+
return data;
|
|
17801
|
+
};
|
|
17802
|
+
var apiGetPushPreference = async () => {
|
|
17803
|
+
const { data } = await apiClient.get(
|
|
17804
|
+
`${v2Url}/notifications/push/preference`
|
|
17805
|
+
);
|
|
17806
|
+
return data?.data;
|
|
17807
|
+
};
|
|
17808
|
+
var apiUpdatePushPreference = async (enabled) => {
|
|
17809
|
+
const { data } = await apiClient.patch(
|
|
17810
|
+
`${v2Url}/notifications/push/preference`,
|
|
17811
|
+
{ enabled }
|
|
17812
|
+
);
|
|
17813
|
+
return data?.data;
|
|
17814
|
+
};
|
|
17815
|
+
var isPushSupported = () => typeof window !== "undefined" && "Notification" in window && "serviceWorker" in navigator && "PushManager" in window;
|
|
17816
|
+
var getStoredPushToken = () => {
|
|
17817
|
+
try {
|
|
17818
|
+
return localStorage.getItem(PUSH_TOKEN_STORAGE_KEY);
|
|
17819
|
+
} catch {
|
|
17820
|
+
return null;
|
|
17821
|
+
}
|
|
17822
|
+
};
|
|
17823
|
+
var storePushToken = (token) => {
|
|
17824
|
+
try {
|
|
17825
|
+
if (token) localStorage.setItem(PUSH_TOKEN_STORAGE_KEY, token);
|
|
17826
|
+
else localStorage.removeItem(PUSH_TOKEN_STORAGE_KEY);
|
|
17827
|
+
} catch {
|
|
17828
|
+
}
|
|
17829
|
+
};
|
|
17830
|
+
var defaultDeviceLabel = () => {
|
|
17831
|
+
const ua = navigator.userAgent;
|
|
17832
|
+
const browser = /edg\//i.test(ua) ? "Edge" : /chrome/i.test(ua) ? "Chrome" : /safari/i.test(ua) ? "Safari" : /firefox/i.test(ua) ? "Firefox" : "Browser";
|
|
17833
|
+
const os = /mac/i.test(ua) ? "macOS" : /windows/i.test(ua) ? "Windows" : /android/i.test(ua) ? "Android" : /iphone|ipad/i.test(ua) ? "iOS" : /linux/i.test(ua) ? "Linux" : "Unknown OS";
|
|
17834
|
+
return `${browser} on ${os}`;
|
|
17835
|
+
};
|
|
17836
|
+
var loadFirebaseMessaging = async () => {
|
|
17837
|
+
try {
|
|
17838
|
+
const [{ initializeApp, getApps }, messagingModule] = await Promise.all([
|
|
17839
|
+
import('firebase/app'),
|
|
17840
|
+
import('firebase/messaging')
|
|
17841
|
+
]);
|
|
17842
|
+
return { initializeApp, getApps, ...messagingModule };
|
|
17843
|
+
} catch {
|
|
17844
|
+
throw new Error(
|
|
17845
|
+
'Push notifications need the "firebase" package \u2014 run: npm install firebase'
|
|
17846
|
+
);
|
|
17847
|
+
}
|
|
17848
|
+
};
|
|
17849
|
+
var getFirebaseMessaging = async (firebaseConfig) => {
|
|
17850
|
+
const fb = await loadFirebaseMessaging();
|
|
17851
|
+
const app = fb.getApps().length ? fb.getApps()[0] : fb.initializeApp(firebaseConfig);
|
|
17852
|
+
return { fb, messaging: fb.getMessaging(app) };
|
|
17853
|
+
};
|
|
17854
|
+
var enablePushOnThisDevice = async () => {
|
|
17855
|
+
if (!isPushSupported()) {
|
|
17856
|
+
throw new Error("Push notifications are not supported in this browser");
|
|
17857
|
+
}
|
|
17858
|
+
const config = await apiGetPushConfig();
|
|
17859
|
+
if (!config?.enabled) {
|
|
17860
|
+
throw new Error("Push notifications are disabled for this workspace");
|
|
17861
|
+
}
|
|
17862
|
+
if (!config.configured || !config.firebaseConfig || !config.vapidKey) {
|
|
17863
|
+
throw new Error(
|
|
17864
|
+
"Push notifications are not configured on the server yet"
|
|
17865
|
+
);
|
|
17866
|
+
}
|
|
17867
|
+
const permission = await Notification.requestPermission();
|
|
17868
|
+
if (permission !== "granted") {
|
|
17869
|
+
throw new Error("Notification permission was not granted");
|
|
17870
|
+
}
|
|
17871
|
+
let registration;
|
|
17872
|
+
try {
|
|
17873
|
+
registration = await navigator.serviceWorker.register(
|
|
17874
|
+
`${SERVICE_WORKER_PATH}?config=${encodeURIComponent(
|
|
17875
|
+
JSON.stringify(config.firebaseConfig)
|
|
17876
|
+
)}`
|
|
17877
|
+
);
|
|
17878
|
+
} catch {
|
|
17879
|
+
throw new Error(
|
|
17880
|
+
`Could not register ${SERVICE_WORKER_PATH} \u2014 copy it from node_modules/@realtimexsco/live-chat/dist/firebase-messaging-sw.js into your app's public folder`
|
|
17881
|
+
);
|
|
17882
|
+
}
|
|
17883
|
+
const { fb, messaging } = await getFirebaseMessaging(config.firebaseConfig);
|
|
17884
|
+
const token = await fb.getToken(messaging, {
|
|
17885
|
+
vapidKey: config.vapidKey,
|
|
17886
|
+
serviceWorkerRegistration: registration
|
|
17887
|
+
});
|
|
17888
|
+
if (!token) {
|
|
17889
|
+
throw new Error("Could not obtain a push token from Firebase");
|
|
17890
|
+
}
|
|
17891
|
+
await apiRegisterPushToken(token, defaultDeviceLabel());
|
|
17892
|
+
storePushToken(token);
|
|
17893
|
+
return token;
|
|
17894
|
+
};
|
|
17895
|
+
var disablePushOnThisDevice = async () => {
|
|
17896
|
+
const token = getStoredPushToken();
|
|
17897
|
+
if (!token) return;
|
|
17898
|
+
try {
|
|
17899
|
+
const config = await apiGetPushConfig();
|
|
17900
|
+
if (config?.firebaseConfig) {
|
|
17901
|
+
const { fb, messaging } = await getFirebaseMessaging(
|
|
17902
|
+
config.firebaseConfig
|
|
17903
|
+
);
|
|
17904
|
+
await fb.deleteToken(messaging).catch(() => void 0);
|
|
17905
|
+
}
|
|
17906
|
+
} catch {
|
|
17907
|
+
}
|
|
17908
|
+
await apiRemovePushToken(token).catch(() => void 0);
|
|
17909
|
+
storePushToken(null);
|
|
17910
|
+
};
|
|
17911
|
+
var onForegroundPush = async (callback) => {
|
|
17912
|
+
const config = await apiGetPushConfig();
|
|
17913
|
+
if (!config?.configured || !config.firebaseConfig) return () => void 0;
|
|
17914
|
+
const { fb, messaging } = await getFirebaseMessaging(config.firebaseConfig);
|
|
17915
|
+
return fb.onMessage(messaging, (payload) => {
|
|
17916
|
+
callback({
|
|
17917
|
+
title: payload?.notification?.title,
|
|
17918
|
+
body: payload?.notification?.body,
|
|
17919
|
+
data: payload?.data
|
|
17920
|
+
});
|
|
17921
|
+
});
|
|
17922
|
+
};
|
|
17923
|
+
var usePushNotifications = () => {
|
|
17924
|
+
const [state, setState] = React2.useState({
|
|
17925
|
+
supported: false,
|
|
17926
|
+
allowedByWorkspace: false,
|
|
17927
|
+
configured: false,
|
|
17928
|
+
permission: "unsupported",
|
|
17929
|
+
userEnabled: true,
|
|
17930
|
+
deviceEnabled: false,
|
|
17931
|
+
loading: true,
|
|
17932
|
+
error: null
|
|
17933
|
+
});
|
|
17934
|
+
const refresh = React2.useCallback(async () => {
|
|
17935
|
+
if (!isPushSupported()) {
|
|
17936
|
+
setState((prev) => ({
|
|
17937
|
+
...prev,
|
|
17938
|
+
supported: false,
|
|
17939
|
+
loading: false
|
|
17940
|
+
}));
|
|
17941
|
+
return;
|
|
17942
|
+
}
|
|
17943
|
+
try {
|
|
17944
|
+
const [config, preference] = await Promise.all([
|
|
17945
|
+
apiGetPushConfig(),
|
|
17946
|
+
apiGetPushPreference().catch(() => null)
|
|
17947
|
+
]);
|
|
17948
|
+
setState({
|
|
17949
|
+
supported: true,
|
|
17950
|
+
allowedByWorkspace: Boolean(config?.enabled),
|
|
17951
|
+
configured: Boolean(config?.configured),
|
|
17952
|
+
permission: Notification.permission,
|
|
17953
|
+
userEnabled: preference?.enabled ?? true,
|
|
17954
|
+
deviceEnabled: Notification.permission === "granted" && Boolean(getStoredPushToken()),
|
|
17955
|
+
loading: false,
|
|
17956
|
+
error: null
|
|
17957
|
+
});
|
|
17958
|
+
} catch (error) {
|
|
17959
|
+
setState((prev) => ({
|
|
17960
|
+
...prev,
|
|
17961
|
+
supported: true,
|
|
17962
|
+
loading: false,
|
|
17963
|
+
error: error instanceof Error ? error.message : "Could not load push settings"
|
|
17964
|
+
}));
|
|
17965
|
+
}
|
|
17966
|
+
}, []);
|
|
17967
|
+
React2.useEffect(() => {
|
|
17968
|
+
void refresh();
|
|
17969
|
+
}, [refresh]);
|
|
17970
|
+
const enable = React2.useCallback(async () => {
|
|
17971
|
+
setState((prev) => ({ ...prev, loading: true, error: null }));
|
|
17972
|
+
try {
|
|
17973
|
+
await enablePushOnThisDevice();
|
|
17974
|
+
await refresh();
|
|
17975
|
+
return true;
|
|
17976
|
+
} catch (error) {
|
|
17977
|
+
setState((prev) => ({
|
|
17978
|
+
...prev,
|
|
17979
|
+
loading: false,
|
|
17980
|
+
permission: isPushSupported() ? Notification.permission : "unsupported",
|
|
17981
|
+
error: error instanceof Error ? error.message : "Could not enable push notifications"
|
|
17982
|
+
}));
|
|
17983
|
+
return false;
|
|
17984
|
+
}
|
|
17985
|
+
}, [refresh]);
|
|
17986
|
+
const disable = React2.useCallback(async () => {
|
|
17987
|
+
setState((prev) => ({ ...prev, loading: true, error: null }));
|
|
17988
|
+
try {
|
|
17989
|
+
await disablePushOnThisDevice();
|
|
17990
|
+
} finally {
|
|
17991
|
+
await refresh();
|
|
17992
|
+
}
|
|
17993
|
+
}, [refresh]);
|
|
17994
|
+
const setUserEnabled = React2.useCallback(
|
|
17995
|
+
async (enabled) => {
|
|
17996
|
+
setState((prev) => ({ ...prev, userEnabled: enabled }));
|
|
17997
|
+
try {
|
|
17998
|
+
await apiUpdatePushPreference(enabled);
|
|
17999
|
+
} catch (error) {
|
|
18000
|
+
setState((prev) => ({
|
|
18001
|
+
...prev,
|
|
18002
|
+
userEnabled: !enabled,
|
|
18003
|
+
error: error instanceof Error ? error.message : "Could not update push preference"
|
|
18004
|
+
}));
|
|
18005
|
+
}
|
|
18006
|
+
},
|
|
18007
|
+
[]
|
|
18008
|
+
);
|
|
18009
|
+
return { ...state, enable, disable, setUserEnabled, refresh };
|
|
18010
|
+
};
|
|
18011
|
+
var PushNotificationToggle = ({
|
|
18012
|
+
className,
|
|
18013
|
+
label = "Push notifications",
|
|
18014
|
+
description = "Get notified about new messages on this device"
|
|
18015
|
+
}) => {
|
|
18016
|
+
const push = usePushNotifications();
|
|
18017
|
+
if (!push.supported || !push.allowedByWorkspace || !push.configured) {
|
|
18018
|
+
return null;
|
|
18019
|
+
}
|
|
18020
|
+
const isOn = push.deviceEnabled && push.userEnabled;
|
|
18021
|
+
const permissionBlocked = push.permission === "denied";
|
|
18022
|
+
const handleChange = async (checked) => {
|
|
18023
|
+
if (checked) {
|
|
18024
|
+
if (!push.userEnabled) await push.setUserEnabled(true);
|
|
18025
|
+
if (!push.deviceEnabled) await push.enable();
|
|
18026
|
+
} else {
|
|
18027
|
+
await push.disable();
|
|
18028
|
+
}
|
|
18029
|
+
};
|
|
18030
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
18031
|
+
"div",
|
|
18032
|
+
{
|
|
18033
|
+
className: className ?? "flex items-center justify-between gap-4 rounded-xl border border-border bg-background px-4 py-3",
|
|
18034
|
+
children: [
|
|
18035
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
18036
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-foreground", children: label }),
|
|
18037
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: permissionBlocked ? "Notifications are blocked in your browser settings" : push.error ?? description })
|
|
18038
|
+
] }),
|
|
18039
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
18040
|
+
Switch,
|
|
18041
|
+
{
|
|
18042
|
+
checked: isOn,
|
|
18043
|
+
disabled: push.loading || permissionBlocked,
|
|
18044
|
+
onCheckedChange: (checked) => void handleChange(checked),
|
|
18045
|
+
"aria-label": `Toggle ${label}`
|
|
18046
|
+
}
|
|
18047
|
+
)
|
|
18048
|
+
]
|
|
18049
|
+
}
|
|
18050
|
+
);
|
|
18051
|
+
};
|
|
18052
|
+
|
|
17780
18053
|
// src/index.ts
|
|
17781
18054
|
var index_default = ChatMain_default;
|
|
17782
18055
|
|
|
@@ -17805,6 +18078,12 @@ exports.LoggedUserDetails = LoggedUserDetails_default;
|
|
|
17805
18078
|
exports.MessageInput = ChannelMessageBoxView_default;
|
|
17806
18079
|
exports.MessageItem = MessageItemView;
|
|
17807
18080
|
exports.MessageList = ActiveChannelMessagesView_default;
|
|
18081
|
+
exports.PushNotificationToggle = PushNotificationToggle;
|
|
18082
|
+
exports.apiGetPushConfig = apiGetPushConfig;
|
|
18083
|
+
exports.apiGetPushPreference = apiGetPushPreference;
|
|
18084
|
+
exports.apiRegisterPushToken = apiRegisterPushToken;
|
|
18085
|
+
exports.apiRemovePushToken = apiRemovePushToken;
|
|
18086
|
+
exports.apiUpdatePushPreference = apiUpdatePushPreference;
|
|
17808
18087
|
exports.capitalizeFirst = capitalizeFirst;
|
|
17809
18088
|
exports.clampJumpDate = clampJumpDate;
|
|
17810
18089
|
exports.default = index_default;
|
|
@@ -17812,13 +18091,18 @@ exports.defaultChatClassNames = defaultChatClassNames;
|
|
|
17812
18091
|
exports.defaultChatComponents = defaultChatComponents;
|
|
17813
18092
|
exports.defaultChatFeatures = defaultChatFeatures;
|
|
17814
18093
|
exports.defaultChatLayoutConfig = defaultChatLayoutConfig;
|
|
18094
|
+
exports.disablePushOnThisDevice = disablePushOnThisDevice;
|
|
17815
18095
|
exports.downloadFile = downloadFile;
|
|
18096
|
+
exports.enablePushOnThisDevice = enablePushOnThisDevice;
|
|
17816
18097
|
exports.getFirstAndLastNameOneCharacter = getFirstAndLastNameOneCharacter;
|
|
17817
18098
|
exports.getResponsiveWidth = getResponsiveWidth;
|
|
17818
18099
|
exports.getSocketConfig = getSocketConfig;
|
|
18100
|
+
exports.getStoredPushToken = getStoredPushToken;
|
|
17819
18101
|
exports.isEmpty = isEmpty;
|
|
18102
|
+
exports.isPushSupported = isPushSupported;
|
|
17820
18103
|
exports.mergeChatCustomization = mergeChatCustomization;
|
|
17821
18104
|
exports.mergeChatLayoutConfig = mergeChatLayoutConfig;
|
|
18105
|
+
exports.onForegroundPush = onForegroundPush;
|
|
17822
18106
|
exports.packageDefaultComponents = packageDefaultComponents;
|
|
17823
18107
|
exports.resolveApiUrl = resolveApiUrl;
|
|
17824
18108
|
exports.resolveChatApiKey = resolveChatApiKey;
|
|
@@ -17840,5 +18124,6 @@ exports.useChatFeatures = useChatFeatures;
|
|
|
17840
18124
|
exports.useChatFeaturesOptional = useChatFeaturesOptional;
|
|
17841
18125
|
exports.useChatLocale = useChatLocale;
|
|
17842
18126
|
exports.useChatTheme = useChatTheme;
|
|
18127
|
+
exports.usePushNotifications = usePushNotifications;
|
|
17843
18128
|
//# sourceMappingURL=index.cjs.map
|
|
17844
18129
|
//# sourceMappingURL=index.cjs.map
|