@trycourier/courier-react
Version:
The React components for the Courier web UI
396 lines (395 loc) • 13.2 kB
JavaScript
import { jsx, Fragment } from "react/jsx-runtime";
import React, { createContext, forwardRef, useContext, useRef, useEffect, useState } from "react";
import { Courier } from "@trycourier/courier-js";
import { CourierInboxDatastore, CourierInboxDataStoreListener } from "@trycourier/courier-ui-inbox";
import { archiveMessage, clickMessage, defaultDarkTheme, defaultLightTheme, markAsRead, markAsUnread, mergeTheme, openMessage } from "@trycourier/courier-ui-inbox";
import { flushSync } from "react-dom";
import { createRoot } from "react-dom/client";
const useCourier = () => {
const signIn = (props) => Courier.shared.signIn(props);
const signOut = () => Courier.shared.signOut();
const loadInbox = (props) => CourierInboxDatastore.shared.load(props);
const fetchNextPageOfMessages = (props) => CourierInboxDatastore.shared.fetchNextPageOfMessages(props);
const setPaginationLimit = (limit) => Courier.shared.paginationLimit = limit;
const readMessage = (message) => CourierInboxDatastore.shared.readMessage({ message });
const unreadMessage = (message) => CourierInboxDatastore.shared.unreadMessage({ message });
const clickMessage2 = (message) => CourierInboxDatastore.shared.clickMessage({ message });
const archiveMessage2 = (message) => CourierInboxDatastore.shared.archiveMessage({ message });
const openMessage2 = (message) => CourierInboxDatastore.shared.openMessage({ message });
const unarchiveMessage = (message) => CourierInboxDatastore.shared.unarchiveMessage({ message });
const readAllMessages = () => CourierInboxDatastore.shared.readAllMessages();
const [auth, setAuth] = React.useState({
userId: void 0,
signIn,
signOut
});
const [inbox, setInbox] = React.useState({
load: loadInbox,
fetchNextPageOfMessages,
setPaginationLimit,
readMessage,
unreadMessage,
clickMessage: clickMessage2,
archiveMessage: archiveMessage2,
openMessage: openMessage2,
unarchiveMessage,
readAllMessages
});
React.useEffect(() => {
const listener = Courier.shared.addAuthenticationListener(() => refreshAuth());
const inboxListener = new CourierInboxDataStoreListener({
onError: (error) => refreshInbox(error),
onDataSetChange: () => refreshInbox(),
onPageAdded: () => refreshInbox(),
onMessageAdd: () => refreshInbox(),
onMessageRemove: () => refreshInbox(),
onMessageUpdate: () => refreshInbox(),
onUnreadCountChange: () => refreshInbox()
});
CourierInboxDatastore.shared.addDataStoreListener(inboxListener);
refreshAuth();
refreshInbox();
return () => {
listener.remove();
inboxListener.remove();
};
}, []);
const refreshAuth = () => {
var _a;
const options = (_a = Courier.shared.client) == null ? void 0 : _a.options;
setAuth({
userId: options == null ? void 0 : options.userId,
signIn,
signOut
});
};
const refreshInbox = (error) => {
const datastore = CourierInboxDatastore.shared;
setInbox({
load: loadInbox,
fetchNextPageOfMessages,
setPaginationLimit,
readMessage,
unreadMessage,
clickMessage: clickMessage2,
archiveMessage: archiveMessage2,
openMessage: openMessage2,
unarchiveMessage,
readAllMessages,
inbox: datastore.inboxDataSet,
archive: datastore.archiveDataSet,
unreadCount: datastore.unreadCount,
error
});
};
return {
shared: Courier.shared,
auth,
inbox
};
};
const CourierClientComponent = ({ children }) => {
const [isMounted, setIsMounted] = useState(false);
useEffect(() => {
setIsMounted(true);
}, []);
if (typeof window === "undefined") {
return null;
}
if (!isMounted) {
return null;
}
return /* @__PURE__ */ jsx(Fragment, { children });
};
const CourierRenderContext = createContext(null);
const CourierInboxComponent = forwardRef((props, ref) => {
const render = useContext(CourierRenderContext);
if (!render) {
throw new Error("RenderContext not found. Ensure CourierInbox is wrapped in a CourierRenderContext.");
}
const inboxRef = useRef(null);
function handleRef(el) {
if (ref) {
if (typeof ref === "function") {
ref(el);
} else {
ref.current = el;
}
}
inboxRef.current = el;
}
function getEl() {
return inboxRef.current;
}
useEffect(() => {
const inbox = getEl();
if (!inbox) return;
inbox.onMessageClick(props.onMessageClick);
}, [props.onMessageClick]);
useEffect(() => {
const inbox = getEl();
if (!inbox) return;
inbox.onMessageActionClick(props.onMessageActionClick);
}, [props.onMessageActionClick]);
useEffect(() => {
const inbox = getEl();
if (!inbox) return;
inbox.onMessageLongPress(props.onMessageLongPress);
}, [props.onMessageLongPress]);
useEffect(() => {
const inbox = getEl();
if (!inbox || !props.renderHeader) return;
queueMicrotask(() => {
inbox.setHeader((headerProps) => {
const reactNode = props.renderHeader(headerProps);
return render(reactNode);
});
});
}, [props.renderHeader]);
useEffect(() => {
const inbox = getEl();
if (!inbox || !props.renderListItem) return;
queueMicrotask(() => {
inbox.setListItem((itemProps) => {
const reactNode = props.renderListItem(itemProps);
return render(reactNode);
});
});
}, [props.renderListItem]);
useEffect(() => {
const inbox = getEl();
if (!inbox || !props.renderEmptyState) return;
queueMicrotask(() => {
inbox.setEmptyState((emptyStateProps) => {
const reactNode = props.renderEmptyState(emptyStateProps);
return render(reactNode);
});
});
}, [props.renderEmptyState]);
useEffect(() => {
const inbox = getEl();
if (!inbox || !props.renderLoadingState) return;
queueMicrotask(() => {
inbox.setLoadingState((loadingStateProps) => {
const reactNode = props.renderLoadingState(loadingStateProps);
return render(reactNode);
});
});
}, [props.renderLoadingState]);
useEffect(() => {
const inbox = getEl();
if (!inbox || !props.renderErrorState) return;
queueMicrotask(() => {
inbox.setErrorState((errorStateProps) => {
const reactNode = props.renderErrorState(errorStateProps);
return render(reactNode);
});
});
}, [props.renderErrorState]);
useEffect(() => {
const inbox = getEl();
if (!inbox || !props.renderPaginationItem) return;
queueMicrotask(() => {
inbox.setPaginationItem((paginationProps) => {
const reactNode = props.renderPaginationItem(paginationProps);
return render(reactNode);
});
});
}, [props.renderPaginationItem]);
useEffect(() => {
const inbox = getEl();
if (!inbox) return;
queueMicrotask(() => {
inbox.setFeedType(props.feedType || "inbox");
});
}, [props.feedType]);
const children = (
/* @ts-ignore */
/* @__PURE__ */ jsx(
"courier-inbox",
{
ref: handleRef,
height: props.height,
"light-theme": props.lightTheme ? JSON.stringify(props.lightTheme) : void 0,
"dark-theme": props.darkTheme ? JSON.stringify(props.darkTheme) : void 0,
mode: props.mode
}
)
);
return /* @__PURE__ */ jsx(CourierClientComponent, { children });
});
const CourierInboxPopupMenuComponent = forwardRef(
(props, ref) => {
const render = useContext(CourierRenderContext);
if (!render) {
throw new Error("RenderContext not found. Ensure CourierInboxPopupMenu is wrapped in a CourierRenderContext.");
}
const inboxRef = useRef(null);
function handleRef(el) {
if (ref) {
if (typeof ref === "function") {
ref(el);
} else {
ref.current = el;
}
}
inboxRef.current = el;
}
function getEl() {
return inboxRef.current;
}
const lastFeedTypeRef = useRef(void 0);
useEffect(() => {
const menu = getEl();
if (!menu) return;
if (props.feedType !== lastFeedTypeRef.current) {
lastFeedTypeRef.current = props.feedType;
queueMicrotask(() => {
var _a;
(_a = menu.setFeedType) == null ? void 0 : _a.call(menu, props.feedType ?? "inbox");
});
}
}, [props.feedType]);
useEffect(() => {
const menu = getEl();
if (!menu) return;
menu.onMessageClick(props.onMessageClick);
}, [props.onMessageClick]);
useEffect(() => {
const menu = getEl();
if (!menu) return;
menu.onMessageActionClick(props.onMessageActionClick);
}, [props.onMessageActionClick]);
useEffect(() => {
const menu = getEl();
if (!menu) return;
menu.onMessageLongPress(props.onMessageLongPress);
}, [props.onMessageLongPress]);
useEffect(() => {
const menu = getEl();
if (!menu || !props.renderHeader) return;
queueMicrotask(() => {
menu.setHeader((headerProps) => {
const reactNode = props.renderHeader(headerProps);
return render(reactNode);
});
});
}, [props.renderHeader]);
useEffect(() => {
const menu = getEl();
if (!menu || !props.renderListItem) return;
queueMicrotask(() => {
menu.setListItem((itemProps) => {
const reactNode = props.renderListItem(itemProps);
return render(reactNode);
});
});
}, [props.renderListItem]);
useEffect(() => {
const menu = getEl();
if (!menu || !props.renderEmptyState) return;
queueMicrotask(() => {
menu.setEmptyState((emptyStateProps) => {
const reactNode = props.renderEmptyState(emptyStateProps);
return render(reactNode);
});
});
}, [props.renderEmptyState]);
useEffect(() => {
const menu = getEl();
if (!menu || !props.renderLoadingState) return;
queueMicrotask(() => {
menu.setLoadingState((loadingStateProps) => {
const reactNode = props.renderLoadingState(loadingStateProps);
return render(reactNode);
});
});
}, [props.renderLoadingState]);
useEffect(() => {
const menu = getEl();
if (!menu || !props.renderErrorState) return;
queueMicrotask(() => {
menu.setErrorState((errorStateProps) => {
const reactNode = props.renderErrorState(errorStateProps);
return render(reactNode);
});
});
}, [props.renderErrorState]);
useEffect(() => {
const menu = getEl();
if (!menu || !props.renderPaginationItem) return;
queueMicrotask(() => {
menu.setPaginationItem((paginationProps) => {
const reactNode = props.renderPaginationItem(paginationProps);
return render(reactNode);
});
});
}, [props.renderPaginationItem]);
useEffect(() => {
const menu = getEl();
if (!menu || !props.renderMenuButton) return;
queueMicrotask(() => {
menu.setMenuButton((buttonProps) => {
const reactNode = props.renderMenuButton(buttonProps);
return render(reactNode);
});
});
}, [props.renderMenuButton]);
const children = (
/* @ts-ignore */
/* @__PURE__ */ jsx(
"courier-inbox-popup-menu",
{
ref: handleRef,
"popup-alignment": props.popupAlignment,
"popup-width": props.popupWidth,
"popup-height": props.popupHeight,
left: props.left,
top: props.top,
right: props.right,
bottom: props.bottom,
"light-theme": props.lightTheme ? JSON.stringify(props.lightTheme) : void 0,
"dark-theme": props.darkTheme ? JSON.stringify(props.darkTheme) : void 0,
mode: props.mode
}
)
);
return /* @__PURE__ */ jsx(CourierClientComponent, { children });
}
);
function reactNodeToHTMLElement(node) {
const container = document.createElement("div");
const root = createRoot(container);
flushSync(() => {
root.render(node);
});
const element = container.firstElementChild;
if (!(element instanceof HTMLElement)) {
throw new Error(
"renderListItem must return a single JSX element that renders to an HTMLElement (e.g., <div>)"
);
}
return element;
}
const CourierInbox = forwardRef((props, ref) => {
return /* @__PURE__ */ jsx(CourierRenderContext.Provider, { value: reactNodeToHTMLElement, children: /* @__PURE__ */ jsx(CourierInboxComponent, { ...props, ref }) });
});
CourierInbox.displayName = "CourierInbox";
const CourierInboxPopupMenu = forwardRef((props, ref) => {
return /* @__PURE__ */ jsx(CourierRenderContext.Provider, { value: reactNodeToHTMLElement, children: /* @__PURE__ */ jsx(CourierInboxPopupMenuComponent, { ...props, ref }) });
});
CourierInboxPopupMenu.displayName = "CourierInboxPopupMenu";
export {
CourierInbox,
CourierInboxPopupMenu,
archiveMessage,
clickMessage,
defaultDarkTheme,
defaultLightTheme,
markAsRead,
markAsUnread,
mergeTheme,
openMessage,
useCourier
};
//# sourceMappingURL=index.mjs.map