@rpcbase/client 0.454.0 → 0.455.0

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.
@@ -0,0 +1,8 @@
1
+ export type BrowserNotificationInput = {
2
+ title: string;
3
+ options?: NotificationOptions;
4
+ onClick?: () => void;
5
+ };
6
+ export declare const canShowBrowserNotification: () => boolean;
7
+ export declare const showBrowserNotification: ({ title, options, onClick, }: BrowserNotificationInput) => Promise<boolean>;
8
+ //# sourceMappingURL=browserNotifications.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browserNotifications.d.ts","sourceRoot":"","sources":["../src/browserNotifications.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,wBAAwB,GAAG;IACrC,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,mBAAmB,CAAA;IAC7B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB,CAAA;AAiBD,eAAO,MAAM,0BAA0B,QAAO,OAI7C,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAU,8BAI3C,wBAAwB,KAAG,OAAO,CAAC,OAAO,CAoB5C,CAAA"}
package/dist/index.js CHANGED
@@ -2336,6 +2336,46 @@ const runNotificationDigest = async ({
2336
2336
  skippedReason: result.skippedReason
2337
2337
  };
2338
2338
  };
2339
+ const getActiveServiceWorkerRegistration = async () => {
2340
+ if (typeof navigator === "undefined") return null;
2341
+ const serviceWorker = navigator.serviceWorker;
2342
+ if (!serviceWorker || typeof serviceWorker.getRegistration !== "function") return null;
2343
+ try {
2344
+ const registration = await serviceWorker.getRegistration();
2345
+ if (!registration?.active) return null;
2346
+ return typeof registration.showNotification === "function" ? registration : null;
2347
+ } catch {
2348
+ return null;
2349
+ }
2350
+ };
2351
+ const canShowBrowserNotification = () => {
2352
+ if (typeof window === "undefined") return false;
2353
+ if (!("Notification" in window)) return false;
2354
+ return window.Notification.permission === "granted";
2355
+ };
2356
+ const showBrowserNotification = async ({
2357
+ title,
2358
+ options,
2359
+ onClick
2360
+ }) => {
2361
+ if (!canShowBrowserNotification()) return false;
2362
+ try {
2363
+ const browserNotification = new window.Notification(title, options);
2364
+ if (onClick) {
2365
+ browserNotification.onclick = onClick;
2366
+ }
2367
+ return true;
2368
+ } catch {
2369
+ const registration = await getActiveServiceWorkerRegistration();
2370
+ if (!registration) return false;
2371
+ try {
2372
+ await registration.showNotification(title, options);
2373
+ return true;
2374
+ } catch {
2375
+ return false;
2376
+ }
2377
+ }
2378
+ };
2339
2379
  const NEW_NOTIFICATION_START_TOLERANCE_MS = 5e3;
2340
2380
  const NATIVE_NOTIFICATION_TAG_PREFIX = "rb-notification";
2341
2381
  const ACTIVE_TAB_HEARTBEAT_MS = 5e3;
@@ -2688,6 +2728,17 @@ const hasRecentNotificationDelivery = (userId, appName, mode, notificationId) =>
2688
2728
  return false;
2689
2729
  }
2690
2730
  };
2731
+ const removeNotificationDeliveryMarkers = (items, userId, appName, mode) => {
2732
+ const storage = getLocalStorage();
2733
+ if (!storage) return;
2734
+ try {
2735
+ for (const item of items) {
2736
+ storage.removeItem(getDeliveryKey(userId, appName, mode, item.id));
2737
+ }
2738
+ } catch {
2739
+ return;
2740
+ }
2741
+ };
2691
2742
  const filterNotificationsWithoutDelivery = (items, userId, appName, mode) => items.filter((item) => !hasRecentNotificationDelivery(userId, appName, mode, item.id));
2692
2743
  const claimNotificationDeliveriesFromStorage = (items, userId, appName, mode) => {
2693
2744
  const storage = getLocalStorage();
@@ -2804,54 +2855,55 @@ const sortNotificationsByCreatedAt = (items) => [...items].sort((a2, b2) => {
2804
2855
  const bMs = Date.parse(b2.createdAt);
2805
2856
  return (Number.isFinite(aMs) ? aMs : 0) - (Number.isFinite(bMs) ? bMs : 0);
2806
2857
  });
2807
- const canShowNativeNotification = () => {
2808
- if (typeof window === "undefined") return false;
2809
- if (!("Notification" in window)) return false;
2810
- return window.Notification.permission === "granted";
2811
- };
2812
2858
  const focusNotificationAction = (action) => {
2813
2859
  window.focus();
2814
2860
  if (action) {
2815
2861
  applyNotificationAction(action);
2816
2862
  }
2817
2863
  };
2818
- const showNativeNotification = (item) => {
2819
- if (!canShowNativeNotification()) return;
2820
- try {
2821
- const action = getNotificationAction(item);
2822
- const browserNotification = new window.Notification(item.title, {
2823
- body: item.body,
2824
- icon: item.image,
2825
- tag: `${NATIVE_NOTIFICATION_TAG_PREFIX}:${item.id}`,
2826
- data: {
2827
- ...item.data,
2828
- notificationId: item.id,
2829
- link: action?.link ?? "",
2830
- linkMode: action?.mode ?? "navigate"
2831
- }
2832
- });
2833
- browserNotification.onclick = () => focusNotificationAction(action);
2834
- } catch {
2835
- return;
2836
- }
2864
+ const getNativeNotificationOptions = (item) => {
2865
+ const action = getNotificationAction(item);
2866
+ return {
2867
+ body: item.body,
2868
+ icon: item.image,
2869
+ tag: `${NATIVE_NOTIFICATION_TAG_PREFIX}:${item.id}`,
2870
+ data: {
2871
+ ...item.data,
2872
+ notificationId: item.id,
2873
+ link: action?.link ?? "",
2874
+ linkMode: action?.mode ?? "navigate"
2875
+ }
2876
+ };
2877
+ };
2878
+ const showNativeNotification = async (item) => {
2879
+ const action = getNotificationAction(item);
2880
+ return showBrowserNotification({
2881
+ title: item.title,
2882
+ options: getNativeNotificationOptions(item),
2883
+ onClick: () => focusNotificationAction(action)
2884
+ });
2837
2885
  };
2838
- const showNativeNotifications = (items) => {
2839
- if (!canShowNativeNotification()) return;
2886
+ const showNativeNotifications = async (items) => {
2887
+ if (!canShowBrowserNotification()) return [];
2840
2888
  if (items.length > 3) {
2841
- try {
2842
- const browserNotification = new window.Notification(`${items.length} new notifications`, {
2889
+ const shown = await showBrowserNotification({
2890
+ title: `${items.length} new notifications`,
2891
+ options: {
2843
2892
  body: "Open the notifications drawer to view them.",
2844
2893
  tag: `${NATIVE_NOTIFICATION_TAG_PREFIX}:summary`
2845
- });
2846
- browserNotification.onclick = () => focusNotificationAction(null);
2847
- } catch {
2848
- return;
2849
- }
2850
- return;
2894
+ },
2895
+ onClick: () => focusNotificationAction(null)
2896
+ });
2897
+ return shown ? items : [];
2851
2898
  }
2899
+ const shownItems = [];
2852
2900
  for (const item of items) {
2853
- showNativeNotification(item);
2901
+ const shown = await showNativeNotification(item);
2902
+ if (shown) {
2903
+ shownItems.push(item);
2904
+ }
2854
2905
  }
2906
+ return shownItems;
2855
2907
  };
2856
2908
  const showNotificationsForCurrentFocusState = async (items, userId, appName) => {
2857
2909
  writeNotificationTabState(userId, appName);
@@ -2862,7 +2914,7 @@ const showNotificationsForCurrentFocusState = async (items, userId, appName) =>
2862
2914
  }
2863
2915
  return [];
2864
2916
  }
2865
- if (!canShowNativeNotification()) return items;
2917
+ if (!canShowBrowserNotification()) return items;
2866
2918
  let nativeItems = items;
2867
2919
  if (hasActiveNotificationTab(userId, appName)) {
2868
2920
  await new Promise((resolve) => window.setTimeout(resolve, ACTIVE_TAB_NATIVE_GRACE_MS));
@@ -2871,7 +2923,11 @@ const showNotificationsForCurrentFocusState = async (items, userId, appName) =>
2871
2923
  }
2872
2924
  const claimed = await claimNotificationDeliveries(nativeItems, userId, appName, "native");
2873
2925
  if (claimed.length > 0) {
2874
- showNativeNotifications(claimed);
2926
+ const shown = await showNativeNotifications(claimed);
2927
+ if (shown.length < claimed.length) {
2928
+ const shownIds = new Set(shown.map((item) => item.id));
2929
+ removeNotificationDeliveryMarkers(claimed.filter((item) => !shownIds.has(item.id)), userId, appName, "native");
2930
+ }
2875
2931
  }
2876
2932
  return nativeItems;
2877
2933
  };