@rpcbase/client 0.340.0 → 0.341.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.
package/dist/index.d.ts CHANGED
@@ -9,4 +9,5 @@ export * from './ssrErrorState';
9
9
  export * from './components/SsrErrorFallback';
10
10
  export * from './components/RouteErrorBoundary';
11
11
  export * from './notifications';
12
+ export * from './notificationsRealtime';
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAE9B,cAAc,aAAa,CAAA;AAC3B,cAAc,kBAAkB,CAAA;AAChC,cAAc,SAAS,CAAA;AACvB,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,+BAA+B,CAAA;AAC7C,cAAc,iCAAiC,CAAA;AAC/C,cAAc,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAE9B,cAAc,aAAa,CAAA;AAC3B,cAAc,kBAAkB,CAAA;AAChC,cAAc,SAAS,CAAA;AACvB,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,+BAA+B,CAAA;AAC7C,cAAc,iCAAiC,CAAA;AAC/C,cAAc,iBAAiB,CAAA;AAC/B,cAAc,yBAAyB,CAAA"}
package/dist/index.js CHANGED
@@ -1,12 +1,13 @@
1
- import { Toaster } from "sonner";
2
- import { toast } from "sonner";
1
+ import { Toaster, toast } from "sonner";
2
+ import { toast as toast2 } from "sonner";
3
3
  import env from "@rpcbase/env";
4
4
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
5
- import { StrictMode, useState, useEffect, useRef, useCallback, useLayoutEffect, useSyncExternalStore } from "react";
5
+ import { StrictMode, useState, useEffect, useRef, useCallback, useLayoutEffect, useSyncExternalStore, createContext, useMemo, useContext } from "react";
6
6
  import posthog from "posthog-js";
7
7
  import { getNavigationGuards, createRoutesFromElements, createBrowserRouter, RouterProvider, useLocation, useRouteError, isRouteErrorResponse } from "@rpcbase/router";
8
8
  import { hydrateRoot } from "react-dom/client";
9
9
  import { PostHogProvider } from "posthog-js/react/dist/esm/index.js";
10
+ import { m as useQuery } from "./useQuery-5oC6VOlA.js";
10
11
  let apiClient;
11
12
  const initApiClient = async (args) => {
12
13
  if (env.SSR) {
@@ -1930,7 +1931,133 @@ const runNotificationDigest = async ({ force = false } = {}) => {
1930
1931
  }
1931
1932
  return { sent: result.sent === true, skippedReason: result.skippedReason };
1932
1933
  };
1934
+ const NotificationsRealtimeContext = createContext(null);
1935
+ const toIso = (value) => {
1936
+ if (value instanceof Date) return value.toISOString();
1937
+ if (typeof value === "string") return value;
1938
+ return void 0;
1939
+ };
1940
+ const toNotificationItem = (doc) => {
1941
+ const id = typeof doc._id === "string" ? doc._id : String(doc._id ?? "");
1942
+ if (!id) return null;
1943
+ return {
1944
+ id,
1945
+ topic: typeof doc.topic === "string" ? doc.topic : void 0,
1946
+ title: typeof doc.title === "string" ? doc.title : "",
1947
+ body: typeof doc.body === "string" ? doc.body : void 0,
1948
+ url: typeof doc.url === "string" ? doc.url : void 0,
1949
+ createdAt: toIso(doc.createdAt) ?? (/* @__PURE__ */ new Date()).toISOString(),
1950
+ seenAt: toIso(doc.seenAt),
1951
+ readAt: toIso(doc.readAt),
1952
+ archivedAt: toIso(doc.archivedAt),
1953
+ metadata: typeof doc.metadata === "object" && doc.metadata !== null ? doc.metadata : void 0
1954
+ };
1955
+ };
1956
+ const buildDisabledTopics = (settings) => {
1957
+ const raw = settings?.topicPreferences;
1958
+ if (!Array.isArray(raw) || raw.length === 0) return [];
1959
+ return raw.map((pref) => {
1960
+ if (!pref || typeof pref !== "object") return null;
1961
+ const topic = typeof pref.topic === "string" ? pref.topic.trim() : "";
1962
+ if (!topic) return null;
1963
+ return pref.inApp === false ? topic : null;
1964
+ }).filter((topic) => Boolean(topic));
1965
+ };
1966
+ function NotificationsRealtimeProvider({
1967
+ userId,
1968
+ limit = 200,
1969
+ toastOnNew = true,
1970
+ children
1971
+ }) {
1972
+ const trimmedUserId = typeof userId === "string" ? userId.trim() : "";
1973
+ const canUseRts = Boolean(trimmedUserId);
1974
+ const settingsQuery = useQuery(
1975
+ canUseRts ? "RBNotificationSettings" : "",
1976
+ useMemo(() => canUseRts ? { userId: trimmedUserId } : {}, [canUseRts, trimmedUserId]),
1977
+ useMemo(() => ({ key: "rb.notifications.settings", limit: 1 }), [])
1978
+ );
1979
+ const settings = settingsQuery.data?.[0] ?? null;
1980
+ const disabledTopics = useMemo(() => buildDisabledTopics(settings), [settings]);
1981
+ const query = useMemo(() => {
1982
+ if (!canUseRts) return {};
1983
+ const base = {
1984
+ userId: trimmedUserId,
1985
+ archivedAt: { $exists: false }
1986
+ };
1987
+ if (disabledTopics.length > 0) {
1988
+ base.topic = { $nin: disabledTopics };
1989
+ }
1990
+ return base;
1991
+ }, [canUseRts, disabledTopics, trimmedUserId]);
1992
+ const notificationsQuery = useQuery(
1993
+ canUseRts ? "RBNotification" : "",
1994
+ query,
1995
+ useMemo(() => ({
1996
+ key: "rb.notifications",
1997
+ sort: { createdAt: -1 },
1998
+ limit
1999
+ }), [limit])
2000
+ );
2001
+ const notifications = useMemo(() => {
2002
+ const raw = notificationsQuery.data;
2003
+ if (!Array.isArray(raw)) return [];
2004
+ return raw.map((doc) => toNotificationItem(doc)).filter((n) => Boolean(n));
2005
+ }, [notificationsQuery.data]);
2006
+ const unreadCount = useMemo(() => notifications.reduce((acc, n) => acc + (n.readAt ? 0 : 1), 0), [notifications]);
2007
+ const unseenCount = useMemo(() => notifications.reduce((acc, n) => acc + (n.seenAt ? 0 : 1), 0), [notifications]);
2008
+ const lastToastIds = useRef(/* @__PURE__ */ new Set());
2009
+ const hasSeededToastIds = useRef(false);
2010
+ const toastStartMs = useRef(Date.now());
2011
+ useEffect(() => {
2012
+ hasSeededToastIds.current = false;
2013
+ lastToastIds.current = /* @__PURE__ */ new Set();
2014
+ toastStartMs.current = Date.now();
2015
+ }, [trimmedUserId]);
2016
+ useEffect(() => {
2017
+ if (!toastOnNew) return;
2018
+ if (!canUseRts) return;
2019
+ if (notificationsQuery.loading) return;
2020
+ if (!hasSeededToastIds.current) {
2021
+ hasSeededToastIds.current = true;
2022
+ lastToastIds.current = new Set(notifications.map((n) => n.id));
2023
+ return;
2024
+ }
2025
+ const nextNew = notifications.filter((n) => !lastToastIds.current.has(n.id));
2026
+ if (!nextNew.length) return;
2027
+ for (const n of nextNew) {
2028
+ lastToastIds.current.add(n.id);
2029
+ }
2030
+ const isTabActive = typeof document !== "undefined" && document.visibilityState === "visible";
2031
+ if (!isTabActive) return;
2032
+ const eligible = nextNew.filter((n) => {
2033
+ const createdAtMs = Date.parse(n.createdAt);
2034
+ if (!Number.isFinite(createdAtMs)) return true;
2035
+ return createdAtMs >= toastStartMs.current;
2036
+ });
2037
+ if (!eligible.length) return;
2038
+ if (eligible.length > 3) {
2039
+ toast(`${eligible.length} new notifications`, { description: "Open the notifications drawer to view them." });
2040
+ return;
2041
+ }
2042
+ for (const n of eligible) {
2043
+ toast(n.title || "New notification", { description: n.body });
2044
+ }
2045
+ }, [canUseRts, notifications, notificationsQuery.loading, toastOnNew]);
2046
+ const value = useMemo(() => ({
2047
+ notifications,
2048
+ unreadCount,
2049
+ unseenCount,
2050
+ loading: notificationsQuery.loading,
2051
+ error: notificationsQuery.error
2052
+ }), [notifications, notificationsQuery.error, notificationsQuery.loading, unreadCount, unseenCount]);
2053
+ return /* @__PURE__ */ jsx(NotificationsRealtimeContext.Provider, { value: canUseRts ? value : null, children });
2054
+ }
2055
+ const useNotificationsRealtime = () => {
2056
+ return useContext(NotificationsRealtimeContext);
2057
+ };
1933
2058
  export {
2059
+ NotificationsRealtimeContext,
2060
+ NotificationsRealtimeProvider,
1934
2061
  RootProvider,
1935
2062
  RouteErrorBoundary,
1936
2063
  SSR_ERROR_STATE_GLOBAL_KEY,
@@ -1948,8 +2075,9 @@ export {
1948
2075
  peekClientSsrErrorState,
1949
2076
  runNotificationDigest,
1950
2077
  serializeSsrErrorState,
1951
- toast,
2078
+ toast2 as toast,
1952
2079
  updateNotificationSettings,
1953
2080
  useMediaQuery,
2081
+ useNotificationsRealtime,
1954
2082
  useThrottledMeasure
1955
2083
  };
@@ -0,0 +1,18 @@
1
+ import { ReactNode } from 'react';
2
+ import { NotificationItem } from './notifications';
3
+ export type NotificationsRealtimeState = {
4
+ notifications: NotificationItem[];
5
+ unreadCount: number;
6
+ unseenCount: number;
7
+ loading: boolean;
8
+ error: unknown;
9
+ };
10
+ export declare const NotificationsRealtimeContext: import('react').Context<NotificationsRealtimeState | null>;
11
+ export declare function NotificationsRealtimeProvider({ userId, limit, toastOnNew, children, }: {
12
+ userId?: string;
13
+ limit?: number;
14
+ toastOnNew?: boolean;
15
+ children: ReactNode;
16
+ }): import("react/jsx-runtime").JSX.Element;
17
+ export declare const useNotificationsRealtime: () => NotificationsRealtimeState | null;
18
+ //# sourceMappingURL=notificationsRealtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notificationsRealtime.d.ts","sourceRoot":"","sources":["../src/notificationsRealtime.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAI7F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AA0BvD,MAAM,MAAM,0BAA0B,GAAG;IACvC,aAAa,EAAE,gBAAgB,EAAE,CAAA;IACjC,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AAED,eAAO,MAAM,4BAA4B,4DAAyD,CAAA;AAwClG,wBAAgB,6BAA6B,CAAC,EAC5C,MAAM,EACN,KAAW,EACX,UAAiB,EACjB,QAAQ,GACT,EAAE;IACD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,QAAQ,EAAE,SAAS,CAAA;CACpB,2CA2GA;AAED,eAAO,MAAM,wBAAwB,QAAO,0BAA0B,GAAG,IAExE,CAAA"}