@realtimexsco/live-chat 1.4.0 → 1.4.1
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 +288 -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 +277 -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,282 @@ 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 = (path) => {
|
|
17785
|
+
const base = (exports.getChatBaseUrl() || String(apiClient.defaults.baseURL || "")).replace(/\/$/, "").replace(/\/v1$/, "/v2");
|
|
17786
|
+
return `${base}${path}`;
|
|
17787
|
+
};
|
|
17788
|
+
var apiGetPushConfig = async () => {
|
|
17789
|
+
const { data } = await apiClient.get(v2Url("/notifications/push/config"));
|
|
17790
|
+
return data?.data;
|
|
17791
|
+
};
|
|
17792
|
+
var apiRegisterPushToken = async (token, deviceLabel) => {
|
|
17793
|
+
const { data } = await apiClient.post(v2Url("/notifications/push/token"), {
|
|
17794
|
+
token,
|
|
17795
|
+
deviceLabel
|
|
17796
|
+
});
|
|
17797
|
+
return data?.data;
|
|
17798
|
+
};
|
|
17799
|
+
var apiRemovePushToken = async (token) => {
|
|
17800
|
+
const { data } = await apiClient.delete(v2Url("/notifications/push/token"), {
|
|
17801
|
+
data: { token }
|
|
17802
|
+
});
|
|
17803
|
+
return data;
|
|
17804
|
+
};
|
|
17805
|
+
var apiGetPushPreference = async () => {
|
|
17806
|
+
const { data } = await apiClient.get(
|
|
17807
|
+
v2Url("/notifications/push/preference")
|
|
17808
|
+
);
|
|
17809
|
+
return data?.data;
|
|
17810
|
+
};
|
|
17811
|
+
var apiUpdatePushPreference = async (enabled) => {
|
|
17812
|
+
const { data } = await apiClient.patch(
|
|
17813
|
+
v2Url("/notifications/push/preference"),
|
|
17814
|
+
{ enabled }
|
|
17815
|
+
);
|
|
17816
|
+
return data?.data;
|
|
17817
|
+
};
|
|
17818
|
+
var isPushSupported = () => typeof window !== "undefined" && "Notification" in window && "serviceWorker" in navigator && "PushManager" in window;
|
|
17819
|
+
var getStoredPushToken = () => {
|
|
17820
|
+
try {
|
|
17821
|
+
return localStorage.getItem(PUSH_TOKEN_STORAGE_KEY);
|
|
17822
|
+
} catch {
|
|
17823
|
+
return null;
|
|
17824
|
+
}
|
|
17825
|
+
};
|
|
17826
|
+
var storePushToken = (token) => {
|
|
17827
|
+
try {
|
|
17828
|
+
if (token) localStorage.setItem(PUSH_TOKEN_STORAGE_KEY, token);
|
|
17829
|
+
else localStorage.removeItem(PUSH_TOKEN_STORAGE_KEY);
|
|
17830
|
+
} catch {
|
|
17831
|
+
}
|
|
17832
|
+
};
|
|
17833
|
+
var defaultDeviceLabel = () => {
|
|
17834
|
+
const ua = navigator.userAgent;
|
|
17835
|
+
const browser = /edg\//i.test(ua) ? "Edge" : /chrome/i.test(ua) ? "Chrome" : /safari/i.test(ua) ? "Safari" : /firefox/i.test(ua) ? "Firefox" : "Browser";
|
|
17836
|
+
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";
|
|
17837
|
+
return `${browser} on ${os}`;
|
|
17838
|
+
};
|
|
17839
|
+
var loadFirebaseMessaging = async () => {
|
|
17840
|
+
try {
|
|
17841
|
+
const [{ initializeApp, getApps }, messagingModule] = await Promise.all([
|
|
17842
|
+
import('firebase/app'),
|
|
17843
|
+
import('firebase/messaging')
|
|
17844
|
+
]);
|
|
17845
|
+
return { initializeApp, getApps, ...messagingModule };
|
|
17846
|
+
} catch {
|
|
17847
|
+
throw new Error(
|
|
17848
|
+
'Push notifications need the "firebase" package \u2014 run: npm install firebase'
|
|
17849
|
+
);
|
|
17850
|
+
}
|
|
17851
|
+
};
|
|
17852
|
+
var getFirebaseMessaging = async (firebaseConfig) => {
|
|
17853
|
+
const fb = await loadFirebaseMessaging();
|
|
17854
|
+
const app = fb.getApps().length ? fb.getApps()[0] : fb.initializeApp(firebaseConfig);
|
|
17855
|
+
return { fb, messaging: fb.getMessaging(app) };
|
|
17856
|
+
};
|
|
17857
|
+
var enablePushOnThisDevice = async () => {
|
|
17858
|
+
if (!isPushSupported()) {
|
|
17859
|
+
throw new Error("Push notifications are not supported in this browser");
|
|
17860
|
+
}
|
|
17861
|
+
const config = await apiGetPushConfig();
|
|
17862
|
+
if (!config?.enabled) {
|
|
17863
|
+
throw new Error("Push notifications are disabled for this workspace");
|
|
17864
|
+
}
|
|
17865
|
+
if (!config.configured || !config.firebaseConfig || !config.vapidKey) {
|
|
17866
|
+
throw new Error(
|
|
17867
|
+
"Push notifications are not configured on the server yet"
|
|
17868
|
+
);
|
|
17869
|
+
}
|
|
17870
|
+
const permission = await Notification.requestPermission();
|
|
17871
|
+
if (permission !== "granted") {
|
|
17872
|
+
throw new Error("Notification permission was not granted");
|
|
17873
|
+
}
|
|
17874
|
+
let registration;
|
|
17875
|
+
try {
|
|
17876
|
+
registration = await navigator.serviceWorker.register(
|
|
17877
|
+
`${SERVICE_WORKER_PATH}?config=${encodeURIComponent(
|
|
17878
|
+
JSON.stringify(config.firebaseConfig)
|
|
17879
|
+
)}`
|
|
17880
|
+
);
|
|
17881
|
+
} catch {
|
|
17882
|
+
throw new Error(
|
|
17883
|
+
`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`
|
|
17884
|
+
);
|
|
17885
|
+
}
|
|
17886
|
+
const { fb, messaging } = await getFirebaseMessaging(config.firebaseConfig);
|
|
17887
|
+
const token = await fb.getToken(messaging, {
|
|
17888
|
+
vapidKey: config.vapidKey,
|
|
17889
|
+
serviceWorkerRegistration: registration
|
|
17890
|
+
});
|
|
17891
|
+
if (!token) {
|
|
17892
|
+
throw new Error("Could not obtain a push token from Firebase");
|
|
17893
|
+
}
|
|
17894
|
+
await apiRegisterPushToken(token, defaultDeviceLabel());
|
|
17895
|
+
storePushToken(token);
|
|
17896
|
+
return token;
|
|
17897
|
+
};
|
|
17898
|
+
var disablePushOnThisDevice = async () => {
|
|
17899
|
+
const token = getStoredPushToken();
|
|
17900
|
+
if (!token) return;
|
|
17901
|
+
try {
|
|
17902
|
+
const config = await apiGetPushConfig();
|
|
17903
|
+
if (config?.firebaseConfig) {
|
|
17904
|
+
const { fb, messaging } = await getFirebaseMessaging(
|
|
17905
|
+
config.firebaseConfig
|
|
17906
|
+
);
|
|
17907
|
+
await fb.deleteToken(messaging).catch(() => void 0);
|
|
17908
|
+
}
|
|
17909
|
+
} catch {
|
|
17910
|
+
}
|
|
17911
|
+
await apiRemovePushToken(token).catch(() => void 0);
|
|
17912
|
+
storePushToken(null);
|
|
17913
|
+
};
|
|
17914
|
+
var onForegroundPush = async (callback) => {
|
|
17915
|
+
const config = await apiGetPushConfig();
|
|
17916
|
+
if (!config?.configured || !config.firebaseConfig) return () => void 0;
|
|
17917
|
+
const { fb, messaging } = await getFirebaseMessaging(config.firebaseConfig);
|
|
17918
|
+
return fb.onMessage(messaging, (payload) => {
|
|
17919
|
+
callback({
|
|
17920
|
+
title: payload?.notification?.title,
|
|
17921
|
+
body: payload?.notification?.body,
|
|
17922
|
+
data: payload?.data
|
|
17923
|
+
});
|
|
17924
|
+
});
|
|
17925
|
+
};
|
|
17926
|
+
var usePushNotifications = () => {
|
|
17927
|
+
const [state, setState] = React2.useState({
|
|
17928
|
+
supported: false,
|
|
17929
|
+
allowedByWorkspace: false,
|
|
17930
|
+
configured: false,
|
|
17931
|
+
permission: "unsupported",
|
|
17932
|
+
userEnabled: true,
|
|
17933
|
+
deviceEnabled: false,
|
|
17934
|
+
loading: true,
|
|
17935
|
+
error: null
|
|
17936
|
+
});
|
|
17937
|
+
const refresh = React2.useCallback(async () => {
|
|
17938
|
+
if (!isPushSupported()) {
|
|
17939
|
+
setState((prev) => ({
|
|
17940
|
+
...prev,
|
|
17941
|
+
supported: false,
|
|
17942
|
+
loading: false
|
|
17943
|
+
}));
|
|
17944
|
+
return;
|
|
17945
|
+
}
|
|
17946
|
+
try {
|
|
17947
|
+
const [config, preference] = await Promise.all([
|
|
17948
|
+
apiGetPushConfig(),
|
|
17949
|
+
apiGetPushPreference().catch(() => null)
|
|
17950
|
+
]);
|
|
17951
|
+
setState({
|
|
17952
|
+
supported: true,
|
|
17953
|
+
allowedByWorkspace: Boolean(config?.enabled),
|
|
17954
|
+
configured: Boolean(config?.configured),
|
|
17955
|
+
permission: Notification.permission,
|
|
17956
|
+
userEnabled: preference?.enabled ?? true,
|
|
17957
|
+
deviceEnabled: Notification.permission === "granted" && Boolean(getStoredPushToken()),
|
|
17958
|
+
loading: false,
|
|
17959
|
+
error: null
|
|
17960
|
+
});
|
|
17961
|
+
} catch (error) {
|
|
17962
|
+
setState((prev) => ({
|
|
17963
|
+
...prev,
|
|
17964
|
+
supported: true,
|
|
17965
|
+
loading: false,
|
|
17966
|
+
error: error instanceof Error ? error.message : "Could not load push settings"
|
|
17967
|
+
}));
|
|
17968
|
+
}
|
|
17969
|
+
}, []);
|
|
17970
|
+
React2.useEffect(() => {
|
|
17971
|
+
void refresh();
|
|
17972
|
+
}, [refresh]);
|
|
17973
|
+
const enable = React2.useCallback(async () => {
|
|
17974
|
+
setState((prev) => ({ ...prev, loading: true, error: null }));
|
|
17975
|
+
try {
|
|
17976
|
+
await enablePushOnThisDevice();
|
|
17977
|
+
await refresh();
|
|
17978
|
+
return true;
|
|
17979
|
+
} catch (error) {
|
|
17980
|
+
setState((prev) => ({
|
|
17981
|
+
...prev,
|
|
17982
|
+
loading: false,
|
|
17983
|
+
permission: isPushSupported() ? Notification.permission : "unsupported",
|
|
17984
|
+
error: error instanceof Error ? error.message : "Could not enable push notifications"
|
|
17985
|
+
}));
|
|
17986
|
+
return false;
|
|
17987
|
+
}
|
|
17988
|
+
}, [refresh]);
|
|
17989
|
+
const disable = React2.useCallback(async () => {
|
|
17990
|
+
setState((prev) => ({ ...prev, loading: true, error: null }));
|
|
17991
|
+
try {
|
|
17992
|
+
await disablePushOnThisDevice();
|
|
17993
|
+
} finally {
|
|
17994
|
+
await refresh();
|
|
17995
|
+
}
|
|
17996
|
+
}, [refresh]);
|
|
17997
|
+
const setUserEnabled = React2.useCallback(
|
|
17998
|
+
async (enabled) => {
|
|
17999
|
+
setState((prev) => ({ ...prev, userEnabled: enabled }));
|
|
18000
|
+
try {
|
|
18001
|
+
await apiUpdatePushPreference(enabled);
|
|
18002
|
+
} catch (error) {
|
|
18003
|
+
setState((prev) => ({
|
|
18004
|
+
...prev,
|
|
18005
|
+
userEnabled: !enabled,
|
|
18006
|
+
error: error instanceof Error ? error.message : "Could not update push preference"
|
|
18007
|
+
}));
|
|
18008
|
+
}
|
|
18009
|
+
},
|
|
18010
|
+
[]
|
|
18011
|
+
);
|
|
18012
|
+
return { ...state, enable, disable, setUserEnabled, refresh };
|
|
18013
|
+
};
|
|
18014
|
+
var PushNotificationToggle = ({
|
|
18015
|
+
className,
|
|
18016
|
+
label = "Push notifications",
|
|
18017
|
+
description = "Get notified about new messages on this device"
|
|
18018
|
+
}) => {
|
|
18019
|
+
const push = usePushNotifications();
|
|
18020
|
+
if (!push.supported || !push.allowedByWorkspace || !push.configured) {
|
|
18021
|
+
return null;
|
|
18022
|
+
}
|
|
18023
|
+
const isOn = push.deviceEnabled && push.userEnabled;
|
|
18024
|
+
const permissionBlocked = push.permission === "denied";
|
|
18025
|
+
const handleChange = async (checked) => {
|
|
18026
|
+
if (checked) {
|
|
18027
|
+
if (!push.userEnabled) await push.setUserEnabled(true);
|
|
18028
|
+
if (!push.deviceEnabled) await push.enable();
|
|
18029
|
+
} else {
|
|
18030
|
+
await push.disable();
|
|
18031
|
+
}
|
|
18032
|
+
};
|
|
18033
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
18034
|
+
"div",
|
|
18035
|
+
{
|
|
18036
|
+
className: className ?? "flex items-center justify-between gap-4 rounded-xl border border-border bg-background px-4 py-3",
|
|
18037
|
+
children: [
|
|
18038
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
18039
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-foreground", children: label }),
|
|
18040
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: permissionBlocked ? "Notifications are blocked in your browser settings" : push.error ?? description })
|
|
18041
|
+
] }),
|
|
18042
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
18043
|
+
Switch,
|
|
18044
|
+
{
|
|
18045
|
+
checked: isOn,
|
|
18046
|
+
disabled: push.loading || permissionBlocked,
|
|
18047
|
+
onCheckedChange: (checked) => void handleChange(checked),
|
|
18048
|
+
"aria-label": `Toggle ${label}`
|
|
18049
|
+
}
|
|
18050
|
+
)
|
|
18051
|
+
]
|
|
18052
|
+
}
|
|
18053
|
+
);
|
|
18054
|
+
};
|
|
18055
|
+
|
|
17780
18056
|
// src/index.ts
|
|
17781
18057
|
var index_default = ChatMain_default;
|
|
17782
18058
|
|
|
@@ -17805,6 +18081,12 @@ exports.LoggedUserDetails = LoggedUserDetails_default;
|
|
|
17805
18081
|
exports.MessageInput = ChannelMessageBoxView_default;
|
|
17806
18082
|
exports.MessageItem = MessageItemView;
|
|
17807
18083
|
exports.MessageList = ActiveChannelMessagesView_default;
|
|
18084
|
+
exports.PushNotificationToggle = PushNotificationToggle;
|
|
18085
|
+
exports.apiGetPushConfig = apiGetPushConfig;
|
|
18086
|
+
exports.apiGetPushPreference = apiGetPushPreference;
|
|
18087
|
+
exports.apiRegisterPushToken = apiRegisterPushToken;
|
|
18088
|
+
exports.apiRemovePushToken = apiRemovePushToken;
|
|
18089
|
+
exports.apiUpdatePushPreference = apiUpdatePushPreference;
|
|
17808
18090
|
exports.capitalizeFirst = capitalizeFirst;
|
|
17809
18091
|
exports.clampJumpDate = clampJumpDate;
|
|
17810
18092
|
exports.default = index_default;
|
|
@@ -17812,13 +18094,18 @@ exports.defaultChatClassNames = defaultChatClassNames;
|
|
|
17812
18094
|
exports.defaultChatComponents = defaultChatComponents;
|
|
17813
18095
|
exports.defaultChatFeatures = defaultChatFeatures;
|
|
17814
18096
|
exports.defaultChatLayoutConfig = defaultChatLayoutConfig;
|
|
18097
|
+
exports.disablePushOnThisDevice = disablePushOnThisDevice;
|
|
17815
18098
|
exports.downloadFile = downloadFile;
|
|
18099
|
+
exports.enablePushOnThisDevice = enablePushOnThisDevice;
|
|
17816
18100
|
exports.getFirstAndLastNameOneCharacter = getFirstAndLastNameOneCharacter;
|
|
17817
18101
|
exports.getResponsiveWidth = getResponsiveWidth;
|
|
17818
18102
|
exports.getSocketConfig = getSocketConfig;
|
|
18103
|
+
exports.getStoredPushToken = getStoredPushToken;
|
|
17819
18104
|
exports.isEmpty = isEmpty;
|
|
18105
|
+
exports.isPushSupported = isPushSupported;
|
|
17820
18106
|
exports.mergeChatCustomization = mergeChatCustomization;
|
|
17821
18107
|
exports.mergeChatLayoutConfig = mergeChatLayoutConfig;
|
|
18108
|
+
exports.onForegroundPush = onForegroundPush;
|
|
17822
18109
|
exports.packageDefaultComponents = packageDefaultComponents;
|
|
17823
18110
|
exports.resolveApiUrl = resolveApiUrl;
|
|
17824
18111
|
exports.resolveChatApiKey = resolveChatApiKey;
|
|
@@ -17840,5 +18127,6 @@ exports.useChatFeatures = useChatFeatures;
|
|
|
17840
18127
|
exports.useChatFeaturesOptional = useChatFeaturesOptional;
|
|
17841
18128
|
exports.useChatLocale = useChatLocale;
|
|
17842
18129
|
exports.useChatTheme = useChatTheme;
|
|
18130
|
+
exports.usePushNotifications = usePushNotifications;
|
|
17843
18131
|
//# sourceMappingURL=index.cjs.map
|
|
17844
18132
|
//# sourceMappingURL=index.cjs.map
|