Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@cord-sdk/react

Package Overview
Dependencies
Maintainers
12
Versions
170
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@cord-sdk/react - npm Package Compare versions

Comparing version 0.0.42 to 0.0.43

dist/cjs/common/ui/atomicClasses/fonts.css.ts.vanilla.css

438

dist/cjs/index.js

@@ -9,3 +9,8 @@ 'use strict';

var types = require('@cord-sdk/types');
var cx = require('classnames');
require('./common/ui/atomicClasses/fonts.css.ts.vanilla.css');
require('./components/Comments.css.ts.vanilla.css');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
function _interopNamespace(e) {

@@ -30,2 +35,3 @@ if (e && e.__esModule) return e;

var React__namespace = /*#__PURE__*/_interopNamespace(React);
var cx__default = /*#__PURE__*/_interopDefaultLegacy(cx);

@@ -96,3 +102,3 @@ // This function determines if X has property Y and does so in a

});
function CordProvider({ clientAuthToken, enableTasks, enableAnnotations, blurScreenshots, enableScreenshotCapture, showBlurredScreenshots, screenshotOptions, annotationMode, cordScriptUrl, navigate, threadOptions, children, onLoad, onInitError, }) {
function CordProvider({ clientAuthToken, enableTasks, enableAnnotations, blurScreenshots, enableScreenshotCapture, showBlurredScreenshots, screenshotOptions, customRenderers, annotationMode, cordScriptUrl, navigate, threadOptions, children, onLoad, onInitError, customEventMetadata, }) {
if ((clientAuthToken === null || clientAuthToken === void 0 ? void 0 : clientAuthToken.length) === 0) {

@@ -176,3 +182,3 @@ console.warn(`CordProvider was given an empty string as token. Cord components will not be rendered.`);

navigate,
react_package_version: "0.0.42",
react_package_version: "0.0.43",
thread_options: threadOptions

@@ -184,3 +190,5 @@ ? {

screenshot_options: backwardsCompatibleScreenshotOptions,
custom_event_metadata: customEventMetadata,
onInitError,
custom_renderers: customRenderers,
})

@@ -204,2 +212,4 @@ .then(() => {

screenshotOptions,
customRenderers,
customEventMetadata,
]);

@@ -266,2 +276,3 @@ React.useEffect(() => {

const [element, setElement] = React.useState(null);
const [listenersAttached, setListenersAttached] = React.useState(false);
React.useEffect(() => {

@@ -282,2 +293,3 @@ if (!element) {

});
setListenersAttached(true);
return () => {

@@ -287,5 +299,6 @@ for (const [eventName, handler] of handlers) {

}
setListenersAttached(false);
};
}, [element, events, sourceComponentName]);
return setElement;
return [setElement, listenersAttached];
}

@@ -342,8 +355,11 @@

function useCustomElementRef(events, forwardedRef) {
const updateCustomEventListeners = useCustomEventListeners(events);
const [updateCustomEventListeners, listenersAttached] = useCustomEventListeners(events);
const updateForwardedRef = useForwardCustomElementRef(forwardedRef);
return React.useCallback((e) => {
updateForwardedRef(e);
updateCustomEventListeners(e);
}, [updateCustomEventListeners, updateForwardedRef]);
return [
React.useCallback((e) => {
updateForwardedRef(e);
updateCustomEventListeners(e);
}, [updateCustomEventListeners, updateForwardedRef]),
listenersAttached,
];
}

@@ -353,5 +369,5 @@

function MultipleCursorsWithForwardedRef(props, forwardedRef) {
const ref = useCustomElementRef({}, forwardedRef);
const [ref, listenersAttached] = useCustomElementRef({}, forwardedRef);
const location = useCordLocation();
return (jsxRuntime.jsx("cord-multiple-cursors", { id: props.id, class: props.className, style: props.style, ref: ref, ...propsToAttributes$k({ location, ...props }) }));
return (jsxRuntime.jsx("cord-multiple-cursors", { id: props.id, class: props.className, style: props.style, ref: ref, "buffer-events": !listenersAttached, ...propsToAttributes$k({ location, ...props }) }));
}

@@ -363,7 +379,7 @@ const MultipleCursors = React__namespace.forwardRef(MultipleCursorsWithForwardedRef);

const { onUpdate } = props;
const setRef = useCustomEventListeners({
const [setRef, listenersAttached] = useCustomEventListeners({
update: onUpdate,
});
const location = useCordLocation();
return (jsxRuntime.jsx("cord-page-presence", { id: props.id, class: props.className, style: props.style, ref: setRef, ...propsToAttributes$j({ location, ...props }) }));
return (jsxRuntime.jsx("cord-page-presence", { id: props.id, class: props.className, style: props.style, ref: setRef, "buffer-events": !listenersAttached, ...propsToAttributes$j({ location, ...props }) }));
}

@@ -374,7 +390,7 @@

const { onUpdate } = props;
const setRef = useCustomEventListeners({
const [setRef, listenersAttached] = useCustomEventListeners({
update: onUpdate,
});
const location = useCordLocation();
return (jsxRuntime.jsx("cord-presence-facepile", { id: props.id, class: props.className, style: props.style, ref: setRef, ...propsToAttributes$i({ location, ...props }) }));
return (jsxRuntime.jsx("cord-presence-facepile", { id: props.id, class: props.className, style: props.style, ref: setRef, "buffer-events": !listenersAttached, ...propsToAttributes$i({ location, ...props }) }));
}

@@ -385,7 +401,7 @@

const { onChange } = props;
const setRef = useCustomEventListeners({
const [setRef, listenersAttached] = useCustomEventListeners({
change: onChange,
});
const location = useCordLocation();
return (jsxRuntime.jsx("cord-presence-observer", { id: props.id, class: props.className, style: props.style, ref: setRef, ...propsToAttributes$h({ location, ...props }), children: props.children }));
return (jsxRuntime.jsx("cord-presence-observer", { id: props.id, class: props.className, style: props.style, ref: setRef, "buffer-events": !listenersAttached, ...propsToAttributes$h({ location, ...props }), children: props.children }));
}

@@ -415,3 +431,3 @@

const { onOpen, onClose, onThreadOpen, onThreadClose } = props;
const ref = useCustomElementRef({
const [ref, listenersAttached] = useCustomElementRef({
open: onOpen,

@@ -428,3 +444,3 @@ close: onClose,

const location = useCordLocation();
return (jsxRuntime.jsx("cord-sidebar", { id: props.id, class: props.className, style: props.style, ref: combinedSetRef, ...propsToAttributes$g({ location, ...props }) }));
return (jsxRuntime.jsx("cord-sidebar", { id: props.id, class: props.className, style: props.style, ref: combinedSetRef, "buffer-events": !listenersAttached, ...propsToAttributes$g({ location, ...props }) }));
}

@@ -436,6 +452,6 @@ const Sidebar = React__namespace.forwardRef(SidebarWithForwardedRef);

const { onClick } = props;
const setRef = useCustomEventListeners({
const [setRef, listenersAttached] = useCustomEventListeners({
click: onClick,
});
return (jsxRuntime.jsx("cord-sidebar-launcher", { id: props.id, class: props.className, style: props.style, ref: setRef, ...propsToAttributes$f(props) }));
return (jsxRuntime.jsx("cord-sidebar-launcher", { id: props.id, class: props.className, style: props.style, ref: setRef, "buffer-events": !listenersAttached, ...propsToAttributes$f(props) }));
}

@@ -445,3 +461,3 @@

function Thread(props) {
const threadEventsListenerSetRef = useCustomEventListeners({
const [threadEventsListenerSetRef, threadListenersAttached] = useCustomEventListeners({
threadinfochange: props.onThreadInfoChange,

@@ -452,4 +468,4 @@ close: props.onClose,

loading: props.onLoading,
});
const composerEventsListenerSetRef = useCustomEventListeners({
}, 'cord-thread');
const [composerEventsListenerSetRef, composerListenersAttached] = useCustomEventListeners({
focus: props.onFocusComposer,

@@ -477,3 +493,3 @@ blur: props.onBlurComposer,

const location = useCordLocation();
return (jsxRuntime.jsx("cord-thread", { id: props.id, class: props.className, style: props.style, ref: combinedSetRef, ...propsToAttributes$e({ location, ...props }), children: props.children }));
return (jsxRuntime.jsx("cord-thread", { id: props.id, "buffer-events": !threadListenersAttached || !composerListenersAttached, class: props.className, style: props.style, ref: combinedSetRef, ...propsToAttributes$e({ location, ...props }), children: props.children }));
}

@@ -483,3 +499,3 @@

function ThreadList(props) {
const setRef = useCustomEventListeners({
const [setRef, listenersAttached] = useCustomEventListeners({
threadclick: props.onThreadClick,

@@ -500,3 +516,3 @@ threadmouseenter: props.onThreadMouseEnter,

const location = useCordLocation();
return (jsxRuntime.jsx("cord-thread-list", { id: props.id, class: props.className, style: props.style, ref: combinedSetRef, ...propsToAttributes$d({ location, ...props }) }));
return (jsxRuntime.jsx("cord-thread-list", { id: props.id, class: props.className, style: props.style, ref: combinedSetRef, "buffer-events": !listenersAttached, ...propsToAttributes$d({ location, ...props }) }));
}

@@ -585,6 +601,6 @@

const { onClick } = props;
const setRef = useCustomEventListeners({
const [setRef, listenersAttached] = useCustomEventListeners({
click: onClick,
});
return (jsxRuntime.jsx("cord-inbox-launcher", { id: props.id, class: props.className, style: props.style, ref: setRef, ...propsToAttributes$c(props) }));
return (jsxRuntime.jsx("cord-inbox-launcher", { id: props.id, class: props.className, style: props.style, ref: setRef, "buffer-events": !listenersAttached, ...propsToAttributes$c(props) }));
}

@@ -595,6 +611,6 @@

const { onCloseRequested } = props;
const setRef = useCustomEventListeners({
const [setRef, listenersAttached] = useCustomEventListeners({
closeRequested: onCloseRequested,
});
return (jsxRuntime.jsx("cord-inbox", { id: props.id, class: props.className, style: props.style, ref: setRef, ...propsToAttributes$b(props) }));
return (jsxRuntime.jsx("cord-inbox", { id: props.id, class: props.className, style: props.style, ref: setRef, "buffer-events": !listenersAttached, ...propsToAttributes$b(props) }));
}

@@ -616,3 +632,3 @@

function FloatingThreadsWithForwardedRef(props, forwardedRef) {
const setRef = useCustomElementRef({
const [setRef, listenersAttached] = useCustomElementRef({
start: props.onStart,

@@ -631,3 +647,3 @@ finish: props.onFinish,

}, []);
return (jsxRuntime.jsx("cord-floating-threads", { ref: combinedSetRef, id: props.id, class: props.className, style: props.style, ...propsToAttributes$9(props) }));
return (jsxRuntime.jsx("cord-floating-threads", { ref: combinedSetRef, id: props.id, "buffer-events": !listenersAttached, class: props.className, style: props.style, ...propsToAttributes$9(props) }));
}

@@ -644,6 +660,6 @@ const FloatingThreads = React__namespace.forwardRef(FloatingThreadsWithForwardedRef);

const { onClick } = props;
const setRef = useCustomEventListeners({
const [setRef, listenersAttached] = useCustomEventListeners({
click: onClick,
});
return (jsxRuntime.jsx("cord-notification-list-launcher", { id: props.id, class: props.className, style: props.style, ref: setRef, ...propsToAttributes$7(props) }));
return (jsxRuntime.jsx("cord-notification-list-launcher", { id: props.id, class: props.className, style: props.style, ref: setRef, "buffer-events": !listenersAttached, ...propsToAttributes$7(props) }));
}

@@ -658,3 +674,3 @@

function Composer(props) {
const setRef = useCustomEventListeners({
const [setRef, listenersAttached] = useCustomEventListeners({
focus: props.onFocus,

@@ -669,3 +685,3 @@ blur: props.onBlur,

const location = useCordLocation();
return (jsxRuntime.jsx("cord-composer", { id: props.id, class: props.className, style: props.style, ref: combinedSetRef, ...propsToAttributes$5({ location, ...props }) }));
return (jsxRuntime.jsx("cord-composer", { id: props.id, class: props.className, style: props.style, ref: combinedSetRef, "buffer-events": !listenersAttached, ...propsToAttributes$5({ location, ...props }) }));
}

@@ -688,2 +704,20 @@

const propsToAttributes$1 = components.propsToAttributeConverter(components.componentAttributes.Pin);
function Pin(props) {
const [setRef, listenersAttached] = useCustomEventListeners({
resolve: props.onResolve,
click: props.onClick,
mouseenter: props.onMouseEnter,
mouseleave: props.onMouseLeave,
});
const combinedSetRef = React.useCallback((element) => {
if (props.forwardRef) {
props.forwardRef.current = element;
}
setRef(element);
}, [props.forwardRef, setRef]);
const location = useCordLocation();
return (jsxRuntime.jsx("cord-pin", { id: props.id, class: props.className, "buffer-events": !listenersAttached, style: props.style, ref: combinedSetRef, ...propsToAttributes$1({ location, ...props }), children: props.children }));
}
function useNotificationSummaryInternal(notificationSDK, isCordInternalCall) {

@@ -716,13 +750,43 @@ const [summary, setSummary] = React.useState(null);

/**
* This method allows you to observe users who are
* [present](https://docs.cord.com/js-apis-and-hooks/presence-api) at a
* particular [location](https://docs.cord.com/reference/location), including
* live updates.
* @example Overview
* ```javascript
* import { presence } from '@cord-sdk/react';
* const result = presence.useLocationData(location, options);
* ```
* @example Usage
* ```javascript
* import { presence } from '@cord-sdk/react';
* const present = presence.useLocationData(
* { page: "https://cord.com", block: "id123" },
* { exclude_durable: true },
* );
* return (
* <>
* {present.map((user) => <div>{user.id} is present!</div>)}
* </>
* );
* ```
* @param location - The [location](https://docs.cord.com/reference/location) to
* fetch presence information for.
* @param options - Miscellaneous options. See below.
* @returns An array of objects, one for each user present at the
* [location](https://docs.cord.com/reference/location) which was passed to this
* hook. Each object will contain the fields described under "Available Data"
* above. The component will automatically re-render if any of the data changes,
* i.e., this data is always "live".
*/
function useLocationData$1(location, options = {}) {
const { excludeViewer = false, includeUserDetails = false, onlyPresentUsers = false, partialMatch = false, } = options;
const { sdk } = useCordContext$1('presence.useLocationData');
const presenceSDK = sdk === null || sdk === void 0 ? void 0 : sdk.presence;
const userSDK = sdk === null || sdk === void 0 ? void 0 : sdk.user;
const locationString = types.locationJson(location);
const [userLocations, setUserLocations] = React.useState({});
const [userDetails, setUserDetails] = React.useState({});
const userDetailsRef = React.useRef({});
userDetailsRef.current = userDetails;
const [viewerID, setViewerID] = React.useState();
const optionsMemo = React.useMemo(() => ({
partial_match: options === null || options === void 0 ? void 0 : options.partial_match,
exclude_durable: options === null || options === void 0 ? void 0 : options.exclude_durable,
}), [options === null || options === void 0 ? void 0 : options.partial_match, options === null || options === void 0 ? void 0 : options.exclude_durable]);
const [presenceData, setPresenceData] = React.useState();
React.useEffect(() => {

@@ -732,162 +796,10 @@ if (!presenceSDK) {

}
// eslint-disable-next-line @typescript-eslint/no-shadow -- Disabling for pre-existing problems. Please do not copy this comment, and consider fixing this one!
const location = JSON.parse(locationString);
let current = true;
const listenerRef = presenceSDK.addListener((userLocationUpdate) => {
if (!current) {
return;
}
// eslint-disable-next-line @typescript-eslint/no-shadow -- Disabling for pre-existing problems. Please do not copy this comment, and consider fixing this one!
setUserLocations((userLocations) => ({
...userLocations,
[userLocationUpdate.id]: {
...userLocations[userLocationUpdate.id],
...userLocationUpdate,
},
}));
}, location, { partial_match: partialMatch });
presenceSDK
.getPresent(location, {
partial_match: partialMatch,
exclude_durable: onlyPresentUsers,
})
.then((userLocationDataList) => {
if (current) {
setUserLocations(Object.fromEntries(userLocationDataList.map((data) => [data.id, data])));
}
});
const ref = presenceSDK.observeLocationData(JSON.parse(locationString), (data) => {
setPresenceData(data);
}, optionsMemo);
return () => {
current = false;
presenceSDK.removeListener(listenerRef);
presenceSDK.unobserveLocationData(ref);
};
}, [presenceSDK, locationString, partialMatch, onlyPresentUsers]);
const userDetailsListenersRef = React.useRef(new Map());
const mountedRef = React.useRef(true);
const currentUserIDs = React.useMemo(() => new Set(Object.keys(userLocations)), [userLocations]);
React.useEffect(() => {
if (!userSDK || !includeUserDetails) {
return;
}
mountedRef.current = true;
// make sure all current user IDs are being observed
for (const id of currentUserIDs) {
if (!userDetailsListenersRef.current.has(id)) {
const listenerRef = userSDK.observeUserData(id, (user) => {
if (mountedRef.current && user) {
// eslint-disable-next-line @typescript-eslint/no-shadow -- Disabling for pre-existing problems. Please do not copy this comment, and consider fixing this one!
setUserDetails((userDetails) => ({
...userDetails,
[user.id]: user,
}));
}
});
userDetailsListenersRef.current.set(id, listenerRef);
}
}
for (const [id, ref] of userDetailsListenersRef.current.entries()) {
if (!currentUserIDs.has(id)) {
userSDK.unobserveUserData(ref);
userDetailsListenersRef.current.delete(id);
}
}
}, [userSDK, currentUserIDs, includeUserDetails]);
React.useEffect(() => {
if (!userSDK) {
return;
}
mountedRef.current = true;
// fetch viewer details on mount
const viewerRef = userSDK.observeViewerData((viewer) => {
if (mountedRef.current) {
setViewerID(viewer.id);
}
});
// remove user listeners on unmount
const listeners = userDetailsListenersRef.current;
return () => {
mountedRef.current = false;
for (const ref of listeners.values()) {
userSDK.unobserveUserData(ref);
}
userSDK.unobserveViewerData(viewerRef);
};
}, [userSDK]);
return React.useMemo(() => {
if (excludeViewer && viewerID === undefined) {
// if we're supposed to exclude the viewer, don't return anything
// until we know who that is
return [];
}
const now = new Date();
return Object.entries(userLocations)
.filter(([id, locationData]) => {
var _a, _b;
if (excludeViewer && viewerID === id) {
// exclude the viewer if requested
return false;
}
if (includeUserDetails && !userDetails[id]) {
// exclude users whose details we don't have yet
return false;
}
const present = Boolean((_b = (_a = locationData.ephemeral) === null || _a === void 0 ? void 0 : _a.locations) === null || _b === void 0 ? void 0 : _b.length);
if (onlyPresentUsers && !present) {
// if the user isn't present in any location for this matcher
return false;
}
return true;
})
.map(([id, locationData]) => {
var _a, _b, _c, _d, _e, _f;
return ({
...(includeUserDetails
? userDetails[id]
: {
name: '',
shortName: null,
profilePictureURL: null,
metadata: {},
}),
id,
present: Boolean((_b = (_a = locationData.ephemeral) === null || _a === void 0 ? void 0 : _a.locations) === null || _b === void 0 ? void 0 : _b.length),
lastPresent: (_d = (_c = locationData.durable) === null || _c === void 0 ? void 0 : _c.timestamp) !== null && _d !== void 0 ? _d : now,
presentLocations: (_f = (_e = locationData.ephemeral) === null || _e === void 0 ? void 0 : _e.locations) !== null && _f !== void 0 ? _f : [],
});
})
.sort((u1, u2) => {
if (u1.present && !u2.present) {
// present users go before not present ones
return -1;
}
else if (!u1.present && u2.present) {
// present users go before not present ones
return 1;
}
else if (!u1.present && !u2.present) {
// both users are absent, order by lastPresent
return u2.lastPresent.getTime() - u1.lastPresent.getTime();
}
else {
// both users are present, place the viewer first if one of the users is
if (u1.id === viewerID) {
return -1;
}
else if (u2.id === viewerID) {
return 1;
}
else {
// neither is the viewer, order by id so that the list of present users
// is stable (people don't jump around if they update their presence often)
return u1.id < u2.id ? -1 : 1;
}
}
});
}, [
userLocations,
userDetails,
viewerID,
excludeViewer,
includeUserDetails,
onlyPresentUsers,
]);
}, [presenceSDK, locationString, optionsMemo]);
return presenceData;
}

@@ -1100,3 +1012,3 @@

* import { user } from '@cord-sdk/react';
* const data = thread.useViewerData();
* const data = user.useViewerData();
* ```

@@ -1106,3 +1018,3 @@ * @example Usage

* import { user } from '@cord-sdk/react';
* const data = thread.useViewerData();
* const data = user.useViewerData();
* return (

@@ -1148,20 +1060,2 @@ * <div>

const propsToAttributes$1 = components.propsToAttributeConverter(components.componentAttributes.Pin);
function Pin(props) {
const setRef = useCustomEventListeners({
resolve: props.onResolve,
click: props.onClick,
mouseEnter: props.onMouseEnter,
mouseLeave: props.onMouseLeave,
});
const combinedSetRef = React.useCallback((element) => {
if (props.forwardRef) {
props.forwardRef.current = element;
}
setRef(element);
}, [props.forwardRef, setRef]);
const location = useCordLocation();
return (jsxRuntime.jsx("cord-pin", { id: props.id, class: props.className, style: props.style, ref: combinedSetRef, ...propsToAttributes$1({ location, ...props }), children: props.children }));
}
const propsToAttributes = components.propsToAttributeConverter(components.componentAttributes.SelectionComments);

@@ -1187,7 +1081,104 @@ function SelectionComments(props) {

/**
* Prepend "cord-" to a classname. Useful mostly to grep all places
* where we've added a stable classname.
*/
/**
* Returns a work in singular or plural form depending on given number. This includes the count, e.g.:
* - `pluralize(0, 'hour')` -> '0 hours'
* - `pluralize(1, 'hour')` -> '1 hour'
* - `pluralize(2, 'hour')` -> '2 hours'
* - `pluralize(0, 'box', 'boxes')` -> '0 boxes'
* - `pluralize(1, 'box', 'boxes')` -> '1 box'
*/
function pluralize(n, what, plural) {
return `${n} ${pluralizeWord(n, what, plural)}`;
}
/**
* Returns a work in singular or plural form depending on given number. This does not include the count, e.g.:
* - `pluralizeWord(0, 'hour')` -> 'hours'
* - `pluralizeWord(1, 'hour')` -> 'hour'
* - `pluralizeWord(2, 'hour')` -> 'hours'
* - `pluralizeWord(0, 'box', 'boxes')` -> 'boxes'
* - `pluralizeWord(1, 'box', 'boxes')` -> 'box'
*/
function pluralizeWord(n, what, plural = what + 's') {
return n === 1 ? what : plural;
}
var fontSmall = 'cord-font-small';
var avatar = 'cord-avatar';
var comments = 'cord-comments';
var commentsThread = 'cord-comments-thread';
var composer = 'cord-composer';
var firstThreadMessage = 'cord-first-thread-message';
var hr = 'cord-hr';
var messageActionButton = 'cord-message-action-button';
var replyMessages = 'cord-reply-messages';
var reverseOrder = 'cord-reverse-order';
var threadActionButtonWithReplies = 'cord-thread-action-button-with-replies';
var threadList = 'cord-thread-list';
var threadSummaryFacepile = 'cord-thread-summary-facepile';
var unread = 'cord-unread';
var viewerAvatarWithComposer = 'cord-viewer-avatar-with-composer';
function Comments({ location, messageOrder = 'newest_on_top', composerPosition = 'top', composerExpanded = false, }) {
const { threads, hasMore, fetchMore } = useLocationData(location, {
sortBy: 'first_message_timestamp',
sortDirection: 'descending',
includeResolved: false,
});
return (jsxRuntime.jsxs("div", { className: cx__default["default"](comments, {
[reverseOrder]: composerPosition === 'bottom',
}), children: [jsxRuntime.jsx(Composer, { location: location, showExpanded: composerExpanded }), jsxRuntime.jsxs("div", { className: cx__default["default"](threadList, {
[reverseOrder]: messageOrder === 'newest_on_bottom',
}), children: [threads.map((thread) => (jsxRuntime.jsx(CommentsThread, { threadId: thread.id }, thread.id))), hasMore && (jsxRuntime.jsx("button", { className: cx__default["default"](messageActionButton, fontSmall, hr), onClick: () => fetchMore(5), children: 'Fetch more' }))] })] }));
}
function CommentsThread({ threadId }) {
var _a, _b;
const threadSummary = useThreadSummary(threadId);
const [showingReplies, setShowingReplies] = React.useState(false);
if (!threadSummary || !((_a = threadSummary.firstMessage) === null || _a === void 0 ? void 0 : _a.id)) {
return null;
}
return (jsxRuntime.jsxs("div", { className: commentsThread, children: [jsxRuntime.jsx(Message, { className: firstThreadMessage, messageId: (_b = threadSummary.firstMessage) === null || _b === void 0 ? void 0 : _b.id, threadId: threadId }), showingReplies ? (jsxRuntime.jsx(ThreadReplies, { threadId: threadId, setShowingReplies: setShowingReplies })) : (jsxRuntime.jsx(CollapsedReplies, { threadSummary: threadSummary, setShowingReplies: setShowingReplies }))] }));
}
function CollapsedReplies({ threadSummary, setShowingReplies, }) {
const hasUnread = threadSummary.unread > 0;
const hasReplies = threadSummary.total > 1;
const replyNumber = threadSummary.total - 1;
const unreadNumber = threadSummary.unread;
return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: hasReplies ? (jsxRuntime.jsxs("button", { className: cx__default["default"](threadActionButtonWithReplies, fontSmall, {
[unread]: hasUnread,
}), onClick: () => setShowingReplies(true), children: [jsxRuntime.jsx(Facepile, { users: threadSummary.participants.map((p) => { var _a; return (_a = p.userID) !== null && _a !== void 0 ? _a : ''; }), className: threadSummaryFacepile, enableTooltip: false }), hasUnread
? pluralize(unreadNumber, 'new reply', 'new replies')
: pluralize(replyNumber, 'reply', 'replies')] })) : (jsxRuntime.jsx(ReplyButton, { onClick: () => setShowingReplies(true) })) }));
}
function ThreadReplies({ threadId, setShowingReplies, }) {
const { messages, hasMore, fetchMore } = useThreadData(threadId);
const [showingReplyComposer, setShowingReplyComposer] = React.useState(true);
// The useThreadData hook will also return the first message, but
// since we are already rendering it, we need to remove it when
// we receive it
const restOfMessages = hasMore ? messages : messages.slice(1);
const hasReplies = restOfMessages.length > 0;
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [hasReplies && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("button", { className: cx__default["default"](messageActionButton, fontSmall), onClick: () => setShowingReplies(false), children: 'Hide replies' }), hasMore && (jsxRuntime.jsx("button", { className: cx__default["default"](messageActionButton, fontSmall, hr), onClick: () => fetchMore(5), children: 'Show more' })), jsxRuntime.jsx("div", { className: replyMessages, children: restOfMessages.map((message) => {
return (jsxRuntime.jsx(Message, { threadId: threadId, messageId: message.id }, message.id));
}) })] })), showingReplyComposer ? (jsxRuntime.jsx(ViewerAvatarWithComposer, { threadId: threadId, setShowingReplyComposer: setShowingReplyComposer })) : (jsxRuntime.jsx(ReplyButton, { onClick: () => setShowingReplyComposer(true) }))] }));
}
function ViewerAvatarWithComposer({ threadId, setShowingReplyComposer, }) {
const viewerData = useViewerData();
const userId = viewerData === null || viewerData === void 0 ? void 0 : viewerData.id;
return (jsxRuntime.jsxs("div", { className: viewerAvatarWithComposer, children: [userId && jsxRuntime.jsx(Avatar, { userId: userId, className: avatar }), jsxRuntime.jsx(Composer, { threadId: threadId, className: composer, showCloseButton: true, onClose: () => setShowingReplyComposer(false), size: 'small' })] }));
}
function ReplyButton({ onClick }) {
return (jsxRuntime.jsx("button", { className: cx__default["default"](threadActionButtonWithReplies, fontSmall), onClick: onClick, children: 'Reply' }));
}
var experimental = /*#__PURE__*/Object.freeze({
__proto__: null,
Pin: Pin,
SelectionComments: SelectionComments,
ThreadFacepile: ThreadFacepile
ThreadFacepile: ThreadFacepile,
Comments: Comments
});

@@ -1224,2 +1215,3 @@

exports.PagePresence = PagePresence;
exports.Pin = Pin;
exports.PresenceFacepile = PresenceFacepile;

@@ -1226,0 +1218,0 @@ exports.PresenceObserver = PresenceObserver;

import type { ReactPropsWithStandardHTMLAttributes } from '../types';
export type AddReactionReactComponentProps = {
threadId: string;
messageId: string;
threadId?: string;
messageId?: string;
enableTooltip?: boolean;
disabled?: boolean;
};
export declare function AddReaction(props: ReactPropsWithStandardHTMLAttributes<AddReactionReactComponentProps>): JSX.Element;
import type { PropsWithChildren } from 'react';
import type { PinWebComponentEvents } from '@cord-sdk/types';
import type { PropsWithRef, ReactPropsWithLocation, ReactPropsWithStandardHTMLAttributes } from '../types';
export type PinReactComponentProps = ReactPropsWithLocation<{
export type PinReactComponentProps = React.PropsWithChildren<ReactPropsWithLocation<{
threadId: string;
threadName?: string;
onResolve?: (...args: PinWebComponentEvents['resolve']) => unknown;
onClick?: (...args: PinWebComponentEvents['click']) => unknown;
onMouseEnter?: (...args: PinWebComponentEvents['mouseEnter']) => unknown;
onMouseLeave?: (...args: PinWebComponentEvents['mouseLeave']) => unknown;
}>;
onMouseEnter?: (...args: PinWebComponentEvents['mouseenter']) => unknown;
onMouseLeave?: (...args: PinWebComponentEvents['mouseleave']) => unknown;
}>>;
export declare function Pin(props: PropsWithRef<PropsWithChildren<ReactPropsWithStandardHTMLAttributes<PinReactComponentProps>>>): JSX.Element;
import * as React from 'react';
import type { AnnotationMode, BlurDisplayLocation, ICordSDK, Location, NavigateFn, InitErrorCallback, LoadCallback, ScreenshotOptions } from '@cord-sdk/types';
import type { AnnotationMode, BlurDisplayLocation, ICordSDK, Location, NavigateFn, InitErrorCallback, LoadCallback, ScreenshotOptions, JsonObject } from '@cord-sdk/types';
type CordContextValue = {

@@ -21,2 +21,3 @@ sdk: ICordSDK | null;

screenshotOptions?: ScreenshotOptions;
customRenderers?: Record<string, (m: Record<string, unknown>) => HTMLElement>;
/**

@@ -35,2 +36,3 @@ * @deprecated The annotationMode prop has been reverted to enableAnnotations

enableScreenshotCapture?: boolean;
customEventMetadata?: JsonObject;
};

@@ -40,3 +42,3 @@ type ThreadOptions = {

};
export declare function CordProvider({ clientAuthToken, enableTasks, enableAnnotations, blurScreenshots, enableScreenshotCapture, showBlurredScreenshots, screenshotOptions, annotationMode, cordScriptUrl, navigate, threadOptions, children, onLoad, onInitError, }: React.PropsWithChildren<Props>): JSX.Element;
export declare function CordProvider({ clientAuthToken, enableTasks, enableAnnotations, blurScreenshots, enableScreenshotCapture, showBlurredScreenshots, screenshotOptions, customRenderers, annotationMode, cordScriptUrl, navigate, threadOptions, children, onLoad, onInitError, customEventMetadata, }: React.PropsWithChildren<Props>): JSX.Element;
export declare function useCordContext(hook: string): {

@@ -43,0 +45,0 @@ sdk: ICordSDK | null;

@@ -1,3 +0,3 @@

export { Pin, PinReactComponentProps } from './components/Pin';
export { SelectionComments, SelectionCommentsReactComponentProps, } from './components/SelectionComments';
export { ThreadFacepile } from './components/ThreadFacepile';
export { Comments } from './components/Comments';

@@ -1,9 +0,34 @@

import type { Location, UserPresenceInformation, UserData } from '@cord-sdk/types';
type Options = {
excludeViewer?: boolean;
onlyPresentUsers?: boolean;
partialMatch?: boolean;
includeUserDetails?: boolean;
};
export declare function useLocationData(location: Location, options?: Options): Array<UserData & UserPresenceInformation>;
export {};
import type { Location, ObservePresenceOptions, UserLocationData } from '@cord-sdk/types';
/**
* This method allows you to observe users who are
* [present](https://docs.cord.com/js-apis-and-hooks/presence-api) at a
* particular [location](https://docs.cord.com/reference/location), including
* live updates.
* @example Overview
* ```javascript
* import { presence } from '@cord-sdk/react';
* const result = presence.useLocationData(location, options);
* ```
* @example Usage
* ```javascript
* import { presence } from '@cord-sdk/react';
* const present = presence.useLocationData(
* { page: "https://cord.com", block: "id123" },
* { exclude_durable: true },
* );
* return (
* <>
* {present.map((user) => <div>{user.id} is present!</div>)}
* </>
* );
* ```
* @param location - The [location](https://docs.cord.com/reference/location) to
* fetch presence information for.
* @param options - Miscellaneous options. See below.
* @returns An array of objects, one for each user present at the
* [location](https://docs.cord.com/reference/location) which was passed to this
* hook. Each object will contain the fields described under "Available Data"
* above. The component will automatically re-render if any of the data changes,
* i.e., this data is always "live".
*/
export declare function useLocationData(location: Location, options?: ObservePresenceOptions): Array<UserLocationData> | undefined;
import type * as React from 'react';
import type { HTMLCordElement } from '@cord-sdk/types';
import type { CustomEventsDefinition } from './useCustomEventListener';
export declare function useCustomElementRef<Events extends Record<string, unknown[]>, HTMLElementInterface extends HTMLCordElement>(events: CustomEventsDefinition<Events>, forwardedRef: React.ForwardedRef<HTMLElementInterface>): (e: HTMLElementInterface | null) => void;
export declare function useCustomElementRef<Events extends Record<string, unknown[]>, HTMLElementInterface extends HTMLCordElement>(events: CustomEventsDefinition<Events>, forwardedRef: React.ForwardedRef<HTMLElementInterface>): readonly [(e: HTMLElementInterface | null) => void, boolean];
export type CustomEventsDefinition<T extends Record<string, unknown[]>> = {
[P in keyof T]: ((...args: T[P]) => unknown) | undefined;
};
export declare function useCustomEventListeners<T extends Record<string, unknown[]>>(events: CustomEventsDefinition<T>, sourceComponentName?: string): import("react").Dispatch<import("react").SetStateAction<Element | null>>;
export declare function useCustomEventListeners<T extends Record<string, unknown[]>>(events: CustomEventsDefinition<T>, sourceComponentName?: string): readonly [import("react").Dispatch<import("react").SetStateAction<Element | null>>, boolean];

@@ -76,3 +76,3 @@ import type { UserData, ViewerUserData } from '@cord-sdk/types';

* import { user } from '@cord-sdk/react';
* const data = thread.useViewerData();
* const data = user.useViewerData();
* ```

@@ -82,3 +82,3 @@ * @example Usage

* import { user } from '@cord-sdk/react';
* const data = thread.useViewerData();
* const data = user.useViewerData();
* return (

@@ -85,0 +85,0 @@ * <div>

@@ -24,2 +24,3 @@ export { CordProvider, CordContext } from './contexts/CordContext';

export { MessageReactComponentProps, Message } from './components/Message';
export { Pin, PinReactComponentProps } from './components/Pin';
import * as notification from './hooks/notification';

@@ -26,0 +27,0 @@ import * as presence from './hooks/presence';

@@ -1,2 +0,2 @@

import { jsx } from 'react/jsx-runtime';
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import * as React from 'react';

@@ -6,2 +6,5 @@ import { useMemo, useState, useRef, useEffect, useContext, useCallback } from 'react';

import { isEqualLocation, locationJson, CORD_ANNOTATION_LOCATION_DATA_ATTRIBUTE } from '@cord-sdk/types';
import cx from 'classnames';
import './common/ui/atomicClasses/fonts.css.ts.vanilla.css';
import './components/Comments.css.ts.vanilla.css';

@@ -72,3 +75,3 @@ // This function determines if X has property Y and does so in a

});
function CordProvider({ clientAuthToken, enableTasks, enableAnnotations, blurScreenshots, enableScreenshotCapture, showBlurredScreenshots, screenshotOptions, annotationMode, cordScriptUrl, navigate, threadOptions, children, onLoad, onInitError, }) {
function CordProvider({ clientAuthToken, enableTasks, enableAnnotations, blurScreenshots, enableScreenshotCapture, showBlurredScreenshots, screenshotOptions, customRenderers, annotationMode, cordScriptUrl, navigate, threadOptions, children, onLoad, onInitError, customEventMetadata, }) {
if ((clientAuthToken === null || clientAuthToken === void 0 ? void 0 : clientAuthToken.length) === 0) {

@@ -152,3 +155,3 @@ console.warn(`CordProvider was given an empty string as token. Cord components will not be rendered.`);

navigate,
react_package_version: "0.0.42",
react_package_version: "0.0.43",
thread_options: threadOptions

@@ -160,3 +163,5 @@ ? {

screenshot_options: backwardsCompatibleScreenshotOptions,
custom_event_metadata: customEventMetadata,
onInitError,
custom_renderers: customRenderers,
})

@@ -180,2 +185,4 @@ .then(() => {

screenshotOptions,
customRenderers,
customEventMetadata,
]);

@@ -242,2 +249,3 @@ useEffect(() => {

const [element, setElement] = useState(null);
const [listenersAttached, setListenersAttached] = useState(false);
useEffect(() => {

@@ -258,2 +266,3 @@ if (!element) {

});
setListenersAttached(true);
return () => {

@@ -263,5 +272,6 @@ for (const [eventName, handler] of handlers) {

}
setListenersAttached(false);
};
}, [element, events, sourceComponentName]);
return setElement;
return [setElement, listenersAttached];
}

@@ -318,8 +328,11 @@

function useCustomElementRef(events, forwardedRef) {
const updateCustomEventListeners = useCustomEventListeners(events);
const [updateCustomEventListeners, listenersAttached] = useCustomEventListeners(events);
const updateForwardedRef = useForwardCustomElementRef(forwardedRef);
return useCallback((e) => {
updateForwardedRef(e);
updateCustomEventListeners(e);
}, [updateCustomEventListeners, updateForwardedRef]);
return [
useCallback((e) => {
updateForwardedRef(e);
updateCustomEventListeners(e);
}, [updateCustomEventListeners, updateForwardedRef]),
listenersAttached,
];
}

@@ -329,5 +342,5 @@

function MultipleCursorsWithForwardedRef(props, forwardedRef) {
const ref = useCustomElementRef({}, forwardedRef);
const [ref, listenersAttached] = useCustomElementRef({}, forwardedRef);
const location = useCordLocation();
return (jsx("cord-multiple-cursors", { id: props.id, class: props.className, style: props.style, ref: ref, ...propsToAttributes$k({ location, ...props }) }));
return (jsx("cord-multiple-cursors", { id: props.id, class: props.className, style: props.style, ref: ref, "buffer-events": !listenersAttached, ...propsToAttributes$k({ location, ...props }) }));
}

@@ -339,7 +352,7 @@ const MultipleCursors = React.forwardRef(MultipleCursorsWithForwardedRef);

const { onUpdate } = props;
const setRef = useCustomEventListeners({
const [setRef, listenersAttached] = useCustomEventListeners({
update: onUpdate,
});
const location = useCordLocation();
return (jsx("cord-page-presence", { id: props.id, class: props.className, style: props.style, ref: setRef, ...propsToAttributes$j({ location, ...props }) }));
return (jsx("cord-page-presence", { id: props.id, class: props.className, style: props.style, ref: setRef, "buffer-events": !listenersAttached, ...propsToAttributes$j({ location, ...props }) }));
}

@@ -350,7 +363,7 @@

const { onUpdate } = props;
const setRef = useCustomEventListeners({
const [setRef, listenersAttached] = useCustomEventListeners({
update: onUpdate,
});
const location = useCordLocation();
return (jsx("cord-presence-facepile", { id: props.id, class: props.className, style: props.style, ref: setRef, ...propsToAttributes$i({ location, ...props }) }));
return (jsx("cord-presence-facepile", { id: props.id, class: props.className, style: props.style, ref: setRef, "buffer-events": !listenersAttached, ...propsToAttributes$i({ location, ...props }) }));
}

@@ -361,7 +374,7 @@

const { onChange } = props;
const setRef = useCustomEventListeners({
const [setRef, listenersAttached] = useCustomEventListeners({
change: onChange,
});
const location = useCordLocation();
return (jsx("cord-presence-observer", { id: props.id, class: props.className, style: props.style, ref: setRef, ...propsToAttributes$h({ location, ...props }), children: props.children }));
return (jsx("cord-presence-observer", { id: props.id, class: props.className, style: props.style, ref: setRef, "buffer-events": !listenersAttached, ...propsToAttributes$h({ location, ...props }), children: props.children }));
}

@@ -391,3 +404,3 @@

const { onOpen, onClose, onThreadOpen, onThreadClose } = props;
const ref = useCustomElementRef({
const [ref, listenersAttached] = useCustomElementRef({
open: onOpen,

@@ -404,3 +417,3 @@ close: onClose,

const location = useCordLocation();
return (jsx("cord-sidebar", { id: props.id, class: props.className, style: props.style, ref: combinedSetRef, ...propsToAttributes$g({ location, ...props }) }));
return (jsx("cord-sidebar", { id: props.id, class: props.className, style: props.style, ref: combinedSetRef, "buffer-events": !listenersAttached, ...propsToAttributes$g({ location, ...props }) }));
}

@@ -412,6 +425,6 @@ const Sidebar = React.forwardRef(SidebarWithForwardedRef);

const { onClick } = props;
const setRef = useCustomEventListeners({
const [setRef, listenersAttached] = useCustomEventListeners({
click: onClick,
});
return (jsx("cord-sidebar-launcher", { id: props.id, class: props.className, style: props.style, ref: setRef, ...propsToAttributes$f(props) }));
return (jsx("cord-sidebar-launcher", { id: props.id, class: props.className, style: props.style, ref: setRef, "buffer-events": !listenersAttached, ...propsToAttributes$f(props) }));
}

@@ -421,3 +434,3 @@

function Thread(props) {
const threadEventsListenerSetRef = useCustomEventListeners({
const [threadEventsListenerSetRef, threadListenersAttached] = useCustomEventListeners({
threadinfochange: props.onThreadInfoChange,

@@ -428,4 +441,4 @@ close: props.onClose,

loading: props.onLoading,
});
const composerEventsListenerSetRef = useCustomEventListeners({
}, 'cord-thread');
const [composerEventsListenerSetRef, composerListenersAttached] = useCustomEventListeners({
focus: props.onFocusComposer,

@@ -453,3 +466,3 @@ blur: props.onBlurComposer,

const location = useCordLocation();
return (jsx("cord-thread", { id: props.id, class: props.className, style: props.style, ref: combinedSetRef, ...propsToAttributes$e({ location, ...props }), children: props.children }));
return (jsx("cord-thread", { id: props.id, "buffer-events": !threadListenersAttached || !composerListenersAttached, class: props.className, style: props.style, ref: combinedSetRef, ...propsToAttributes$e({ location, ...props }), children: props.children }));
}

@@ -459,3 +472,3 @@

function ThreadList(props) {
const setRef = useCustomEventListeners({
const [setRef, listenersAttached] = useCustomEventListeners({
threadclick: props.onThreadClick,

@@ -476,3 +489,3 @@ threadmouseenter: props.onThreadMouseEnter,

const location = useCordLocation();
return (jsx("cord-thread-list", { id: props.id, class: props.className, style: props.style, ref: combinedSetRef, ...propsToAttributes$d({ location, ...props }) }));
return (jsx("cord-thread-list", { id: props.id, class: props.className, style: props.style, ref: combinedSetRef, "buffer-events": !listenersAttached, ...propsToAttributes$d({ location, ...props }) }));
}

@@ -561,6 +574,6 @@

const { onClick } = props;
const setRef = useCustomEventListeners({
const [setRef, listenersAttached] = useCustomEventListeners({
click: onClick,
});
return (jsx("cord-inbox-launcher", { id: props.id, class: props.className, style: props.style, ref: setRef, ...propsToAttributes$c(props) }));
return (jsx("cord-inbox-launcher", { id: props.id, class: props.className, style: props.style, ref: setRef, "buffer-events": !listenersAttached, ...propsToAttributes$c(props) }));
}

@@ -571,6 +584,6 @@

const { onCloseRequested } = props;
const setRef = useCustomEventListeners({
const [setRef, listenersAttached] = useCustomEventListeners({
closeRequested: onCloseRequested,
});
return (jsx("cord-inbox", { id: props.id, class: props.className, style: props.style, ref: setRef, ...propsToAttributes$b(props) }));
return (jsx("cord-inbox", { id: props.id, class: props.className, style: props.style, ref: setRef, "buffer-events": !listenersAttached, ...propsToAttributes$b(props) }));
}

@@ -592,3 +605,3 @@

function FloatingThreadsWithForwardedRef(props, forwardedRef) {
const setRef = useCustomElementRef({
const [setRef, listenersAttached] = useCustomElementRef({
start: props.onStart,

@@ -607,3 +620,3 @@ finish: props.onFinish,

}, []);
return (jsx("cord-floating-threads", { ref: combinedSetRef, id: props.id, class: props.className, style: props.style, ...propsToAttributes$9(props) }));
return (jsx("cord-floating-threads", { ref: combinedSetRef, id: props.id, "buffer-events": !listenersAttached, class: props.className, style: props.style, ...propsToAttributes$9(props) }));
}

@@ -620,6 +633,6 @@ const FloatingThreads = React.forwardRef(FloatingThreadsWithForwardedRef);

const { onClick } = props;
const setRef = useCustomEventListeners({
const [setRef, listenersAttached] = useCustomEventListeners({
click: onClick,
});
return (jsx("cord-notification-list-launcher", { id: props.id, class: props.className, style: props.style, ref: setRef, ...propsToAttributes$7(props) }));
return (jsx("cord-notification-list-launcher", { id: props.id, class: props.className, style: props.style, ref: setRef, "buffer-events": !listenersAttached, ...propsToAttributes$7(props) }));
}

@@ -634,3 +647,3 @@

function Composer(props) {
const setRef = useCustomEventListeners({
const [setRef, listenersAttached] = useCustomEventListeners({
focus: props.onFocus,

@@ -645,3 +658,3 @@ blur: props.onBlur,

const location = useCordLocation();
return (jsx("cord-composer", { id: props.id, class: props.className, style: props.style, ref: combinedSetRef, ...propsToAttributes$5({ location, ...props }) }));
return (jsx("cord-composer", { id: props.id, class: props.className, style: props.style, ref: combinedSetRef, "buffer-events": !listenersAttached, ...propsToAttributes$5({ location, ...props }) }));
}

@@ -664,2 +677,20 @@

const propsToAttributes$1 = propsToAttributeConverter(componentAttributes.Pin);
function Pin(props) {
const [setRef, listenersAttached] = useCustomEventListeners({
resolve: props.onResolve,
click: props.onClick,
mouseenter: props.onMouseEnter,
mouseleave: props.onMouseLeave,
});
const combinedSetRef = useCallback((element) => {
if (props.forwardRef) {
props.forwardRef.current = element;
}
setRef(element);
}, [props.forwardRef, setRef]);
const location = useCordLocation();
return (jsx("cord-pin", { id: props.id, class: props.className, "buffer-events": !listenersAttached, style: props.style, ref: combinedSetRef, ...propsToAttributes$1({ location, ...props }), children: props.children }));
}
function useNotificationSummaryInternal(notificationSDK, isCordInternalCall) {

@@ -692,13 +723,43 @@ const [summary, setSummary] = useState(null);

/**
* This method allows you to observe users who are
* [present](https://docs.cord.com/js-apis-and-hooks/presence-api) at a
* particular [location](https://docs.cord.com/reference/location), including
* live updates.
* @example Overview
* ```javascript
* import { presence } from '@cord-sdk/react';
* const result = presence.useLocationData(location, options);
* ```
* @example Usage
* ```javascript
* import { presence } from '@cord-sdk/react';
* const present = presence.useLocationData(
* { page: "https://cord.com", block: "id123" },
* { exclude_durable: true },
* );
* return (
* <>
* {present.map((user) => <div>{user.id} is present!</div>)}
* </>
* );
* ```
* @param location - The [location](https://docs.cord.com/reference/location) to
* fetch presence information for.
* @param options - Miscellaneous options. See below.
* @returns An array of objects, one for each user present at the
* [location](https://docs.cord.com/reference/location) which was passed to this
* hook. Each object will contain the fields described under "Available Data"
* above. The component will automatically re-render if any of the data changes,
* i.e., this data is always "live".
*/
function useLocationData$1(location, options = {}) {
const { excludeViewer = false, includeUserDetails = false, onlyPresentUsers = false, partialMatch = false, } = options;
const { sdk } = useCordContext$1('presence.useLocationData');
const presenceSDK = sdk === null || sdk === void 0 ? void 0 : sdk.presence;
const userSDK = sdk === null || sdk === void 0 ? void 0 : sdk.user;
const locationString = locationJson(location);
const [userLocations, setUserLocations] = useState({});
const [userDetails, setUserDetails] = useState({});
const userDetailsRef = useRef({});
userDetailsRef.current = userDetails;
const [viewerID, setViewerID] = useState();
const optionsMemo = useMemo(() => ({
partial_match: options === null || options === void 0 ? void 0 : options.partial_match,
exclude_durable: options === null || options === void 0 ? void 0 : options.exclude_durable,
}), [options === null || options === void 0 ? void 0 : options.partial_match, options === null || options === void 0 ? void 0 : options.exclude_durable]);
const [presenceData, setPresenceData] = useState();
useEffect(() => {

@@ -708,162 +769,10 @@ if (!presenceSDK) {

}
// eslint-disable-next-line @typescript-eslint/no-shadow -- Disabling for pre-existing problems. Please do not copy this comment, and consider fixing this one!
const location = JSON.parse(locationString);
let current = true;
const listenerRef = presenceSDK.addListener((userLocationUpdate) => {
if (!current) {
return;
}
// eslint-disable-next-line @typescript-eslint/no-shadow -- Disabling for pre-existing problems. Please do not copy this comment, and consider fixing this one!
setUserLocations((userLocations) => ({
...userLocations,
[userLocationUpdate.id]: {
...userLocations[userLocationUpdate.id],
...userLocationUpdate,
},
}));
}, location, { partial_match: partialMatch });
presenceSDK
.getPresent(location, {
partial_match: partialMatch,
exclude_durable: onlyPresentUsers,
})
.then((userLocationDataList) => {
if (current) {
setUserLocations(Object.fromEntries(userLocationDataList.map((data) => [data.id, data])));
}
});
const ref = presenceSDK.observeLocationData(JSON.parse(locationString), (data) => {
setPresenceData(data);
}, optionsMemo);
return () => {
current = false;
presenceSDK.removeListener(listenerRef);
presenceSDK.unobserveLocationData(ref);
};
}, [presenceSDK, locationString, partialMatch, onlyPresentUsers]);
const userDetailsListenersRef = useRef(new Map());
const mountedRef = useRef(true);
const currentUserIDs = useMemo(() => new Set(Object.keys(userLocations)), [userLocations]);
useEffect(() => {
if (!userSDK || !includeUserDetails) {
return;
}
mountedRef.current = true;
// make sure all current user IDs are being observed
for (const id of currentUserIDs) {
if (!userDetailsListenersRef.current.has(id)) {
const listenerRef = userSDK.observeUserData(id, (user) => {
if (mountedRef.current && user) {
// eslint-disable-next-line @typescript-eslint/no-shadow -- Disabling for pre-existing problems. Please do not copy this comment, and consider fixing this one!
setUserDetails((userDetails) => ({
...userDetails,
[user.id]: user,
}));
}
});
userDetailsListenersRef.current.set(id, listenerRef);
}
}
for (const [id, ref] of userDetailsListenersRef.current.entries()) {
if (!currentUserIDs.has(id)) {
userSDK.unobserveUserData(ref);
userDetailsListenersRef.current.delete(id);
}
}
}, [userSDK, currentUserIDs, includeUserDetails]);
useEffect(() => {
if (!userSDK) {
return;
}
mountedRef.current = true;
// fetch viewer details on mount
const viewerRef = userSDK.observeViewerData((viewer) => {
if (mountedRef.current) {
setViewerID(viewer.id);
}
});
// remove user listeners on unmount
const listeners = userDetailsListenersRef.current;
return () => {
mountedRef.current = false;
for (const ref of listeners.values()) {
userSDK.unobserveUserData(ref);
}
userSDK.unobserveViewerData(viewerRef);
};
}, [userSDK]);
return useMemo(() => {
if (excludeViewer && viewerID === undefined) {
// if we're supposed to exclude the viewer, don't return anything
// until we know who that is
return [];
}
const now = new Date();
return Object.entries(userLocations)
.filter(([id, locationData]) => {
var _a, _b;
if (excludeViewer && viewerID === id) {
// exclude the viewer if requested
return false;
}
if (includeUserDetails && !userDetails[id]) {
// exclude users whose details we don't have yet
return false;
}
const present = Boolean((_b = (_a = locationData.ephemeral) === null || _a === void 0 ? void 0 : _a.locations) === null || _b === void 0 ? void 0 : _b.length);
if (onlyPresentUsers && !present) {
// if the user isn't present in any location for this matcher
return false;
}
return true;
})
.map(([id, locationData]) => {
var _a, _b, _c, _d, _e, _f;
return ({
...(includeUserDetails
? userDetails[id]
: {
name: '',
shortName: null,
profilePictureURL: null,
metadata: {},
}),
id,
present: Boolean((_b = (_a = locationData.ephemeral) === null || _a === void 0 ? void 0 : _a.locations) === null || _b === void 0 ? void 0 : _b.length),
lastPresent: (_d = (_c = locationData.durable) === null || _c === void 0 ? void 0 : _c.timestamp) !== null && _d !== void 0 ? _d : now,
presentLocations: (_f = (_e = locationData.ephemeral) === null || _e === void 0 ? void 0 : _e.locations) !== null && _f !== void 0 ? _f : [],
});
})
.sort((u1, u2) => {
if (u1.present && !u2.present) {
// present users go before not present ones
return -1;
}
else if (!u1.present && u2.present) {
// present users go before not present ones
return 1;
}
else if (!u1.present && !u2.present) {
// both users are absent, order by lastPresent
return u2.lastPresent.getTime() - u1.lastPresent.getTime();
}
else {
// both users are present, place the viewer first if one of the users is
if (u1.id === viewerID) {
return -1;
}
else if (u2.id === viewerID) {
return 1;
}
else {
// neither is the viewer, order by id so that the list of present users
// is stable (people don't jump around if they update their presence often)
return u1.id < u2.id ? -1 : 1;
}
}
});
}, [
userLocations,
userDetails,
viewerID,
excludeViewer,
includeUserDetails,
onlyPresentUsers,
]);
}, [presenceSDK, locationString, optionsMemo]);
return presenceData;
}

@@ -1076,3 +985,3 @@

* import { user } from '@cord-sdk/react';
* const data = thread.useViewerData();
* const data = user.useViewerData();
* ```

@@ -1082,3 +991,3 @@ * @example Usage

* import { user } from '@cord-sdk/react';
* const data = thread.useViewerData();
* const data = user.useViewerData();
* return (

@@ -1124,20 +1033,2 @@ * <div>

const propsToAttributes$1 = propsToAttributeConverter(componentAttributes.Pin);
function Pin(props) {
const setRef = useCustomEventListeners({
resolve: props.onResolve,
click: props.onClick,
mouseEnter: props.onMouseEnter,
mouseLeave: props.onMouseLeave,
});
const combinedSetRef = useCallback((element) => {
if (props.forwardRef) {
props.forwardRef.current = element;
}
setRef(element);
}, [props.forwardRef, setRef]);
const location = useCordLocation();
return (jsx("cord-pin", { id: props.id, class: props.className, style: props.style, ref: combinedSetRef, ...propsToAttributes$1({ location, ...props }), children: props.children }));
}
const propsToAttributes = propsToAttributeConverter(componentAttributes.SelectionComments);

@@ -1163,7 +1054,104 @@ function SelectionComments(props) {

/**
* Prepend "cord-" to a classname. Useful mostly to grep all places
* where we've added a stable classname.
*/
/**
* Returns a work in singular or plural form depending on given number. This includes the count, e.g.:
* - `pluralize(0, 'hour')` -> '0 hours'
* - `pluralize(1, 'hour')` -> '1 hour'
* - `pluralize(2, 'hour')` -> '2 hours'
* - `pluralize(0, 'box', 'boxes')` -> '0 boxes'
* - `pluralize(1, 'box', 'boxes')` -> '1 box'
*/
function pluralize(n, what, plural) {
return `${n} ${pluralizeWord(n, what, plural)}`;
}
/**
* Returns a work in singular or plural form depending on given number. This does not include the count, e.g.:
* - `pluralizeWord(0, 'hour')` -> 'hours'
* - `pluralizeWord(1, 'hour')` -> 'hour'
* - `pluralizeWord(2, 'hour')` -> 'hours'
* - `pluralizeWord(0, 'box', 'boxes')` -> 'boxes'
* - `pluralizeWord(1, 'box', 'boxes')` -> 'box'
*/
function pluralizeWord(n, what, plural = what + 's') {
return n === 1 ? what : plural;
}
var fontSmall = 'cord-font-small';
var avatar = 'cord-avatar';
var comments = 'cord-comments';
var commentsThread = 'cord-comments-thread';
var composer = 'cord-composer';
var firstThreadMessage = 'cord-first-thread-message';
var hr = 'cord-hr';
var messageActionButton = 'cord-message-action-button';
var replyMessages = 'cord-reply-messages';
var reverseOrder = 'cord-reverse-order';
var threadActionButtonWithReplies = 'cord-thread-action-button-with-replies';
var threadList = 'cord-thread-list';
var threadSummaryFacepile = 'cord-thread-summary-facepile';
var unread = 'cord-unread';
var viewerAvatarWithComposer = 'cord-viewer-avatar-with-composer';
function Comments({ location, messageOrder = 'newest_on_top', composerPosition = 'top', composerExpanded = false, }) {
const { threads, hasMore, fetchMore } = useLocationData(location, {
sortBy: 'first_message_timestamp',
sortDirection: 'descending',
includeResolved: false,
});
return (jsxs("div", { className: cx(comments, {
[reverseOrder]: composerPosition === 'bottom',
}), children: [jsx(Composer, { location: location, showExpanded: composerExpanded }), jsxs("div", { className: cx(threadList, {
[reverseOrder]: messageOrder === 'newest_on_bottom',
}), children: [threads.map((thread) => (jsx(CommentsThread, { threadId: thread.id }, thread.id))), hasMore && (jsx("button", { className: cx(messageActionButton, fontSmall, hr), onClick: () => fetchMore(5), children: 'Fetch more' }))] })] }));
}
function CommentsThread({ threadId }) {
var _a, _b;
const threadSummary = useThreadSummary(threadId);
const [showingReplies, setShowingReplies] = useState(false);
if (!threadSummary || !((_a = threadSummary.firstMessage) === null || _a === void 0 ? void 0 : _a.id)) {
return null;
}
return (jsxs("div", { className: commentsThread, children: [jsx(Message, { className: firstThreadMessage, messageId: (_b = threadSummary.firstMessage) === null || _b === void 0 ? void 0 : _b.id, threadId: threadId }), showingReplies ? (jsx(ThreadReplies, { threadId: threadId, setShowingReplies: setShowingReplies })) : (jsx(CollapsedReplies, { threadSummary: threadSummary, setShowingReplies: setShowingReplies }))] }));
}
function CollapsedReplies({ threadSummary, setShowingReplies, }) {
const hasUnread = threadSummary.unread > 0;
const hasReplies = threadSummary.total > 1;
const replyNumber = threadSummary.total - 1;
const unreadNumber = threadSummary.unread;
return (jsx(Fragment, { children: hasReplies ? (jsxs("button", { className: cx(threadActionButtonWithReplies, fontSmall, {
[unread]: hasUnread,
}), onClick: () => setShowingReplies(true), children: [jsx(Facepile, { users: threadSummary.participants.map((p) => { var _a; return (_a = p.userID) !== null && _a !== void 0 ? _a : ''; }), className: threadSummaryFacepile, enableTooltip: false }), hasUnread
? pluralize(unreadNumber, 'new reply', 'new replies')
: pluralize(replyNumber, 'reply', 'replies')] })) : (jsx(ReplyButton, { onClick: () => setShowingReplies(true) })) }));
}
function ThreadReplies({ threadId, setShowingReplies, }) {
const { messages, hasMore, fetchMore } = useThreadData(threadId);
const [showingReplyComposer, setShowingReplyComposer] = useState(true);
// The useThreadData hook will also return the first message, but
// since we are already rendering it, we need to remove it when
// we receive it
const restOfMessages = hasMore ? messages : messages.slice(1);
const hasReplies = restOfMessages.length > 0;
return (jsxs(Fragment, { children: [hasReplies && (jsxs(Fragment, { children: [jsx("button", { className: cx(messageActionButton, fontSmall), onClick: () => setShowingReplies(false), children: 'Hide replies' }), hasMore && (jsx("button", { className: cx(messageActionButton, fontSmall, hr), onClick: () => fetchMore(5), children: 'Show more' })), jsx("div", { className: replyMessages, children: restOfMessages.map((message) => {
return (jsx(Message, { threadId: threadId, messageId: message.id }, message.id));
}) })] })), showingReplyComposer ? (jsx(ViewerAvatarWithComposer, { threadId: threadId, setShowingReplyComposer: setShowingReplyComposer })) : (jsx(ReplyButton, { onClick: () => setShowingReplyComposer(true) }))] }));
}
function ViewerAvatarWithComposer({ threadId, setShowingReplyComposer, }) {
const viewerData = useViewerData();
const userId = viewerData === null || viewerData === void 0 ? void 0 : viewerData.id;
return (jsxs("div", { className: viewerAvatarWithComposer, children: [userId && jsx(Avatar, { userId: userId, className: avatar }), jsx(Composer, { threadId: threadId, className: composer, showCloseButton: true, onClose: () => setShowingReplyComposer(false), size: 'small' })] }));
}
function ReplyButton({ onClick }) {
return (jsx("button", { className: cx(threadActionButtonWithReplies, fontSmall), onClick: onClick, children: 'Reply' }));
}
var experimental = /*#__PURE__*/Object.freeze({
__proto__: null,
Pin: Pin,
SelectionComments: SelectionComments,
ThreadFacepile: ThreadFacepile
ThreadFacepile: ThreadFacepile,
Comments: Comments
});

@@ -1185,3 +1173,3 @@

export { AddReaction, FloatingThreads as AnchoredThreads, Avatar, Composer, CordContext, CordProvider, Facepile, FloatingThreads, Inbox, InboxLauncher, Message, MultipleCursors, NotificationList, NotificationListLauncher, PagePresence, PresenceFacepile, PresenceObserver, Settings, Sidebar, SidebarLauncher, Thread, ThreadList, beta, experimental, notification, presence, thread, useCordAnnotationCaptureHandler, useCordAnnotationClickHandler, useCordAnnotationRenderer, useCordAnnotationTargetRef, useCordContext, useCordLocation, useSummary as useCordNotificationSummary, useLocationData$1 as useCordPresentUsers, useLocationSummary as useCordThreadActivitySummary, useThreadSummary as useCordThreadSummary, user };
export { AddReaction, FloatingThreads as AnchoredThreads, Avatar, Composer, CordContext, CordProvider, Facepile, FloatingThreads, Inbox, InboxLauncher, Message, MultipleCursors, NotificationList, NotificationListLauncher, PagePresence, Pin, PresenceFacepile, PresenceObserver, Settings, Sidebar, SidebarLauncher, Thread, ThreadList, beta, experimental, notification, presence, thread, useCordAnnotationCaptureHandler, useCordAnnotationClickHandler, useCordAnnotationRenderer, useCordAnnotationTargetRef, useCordContext, useCordLocation, useSummary as useCordNotificationSummary, useLocationData$1 as useCordPresentUsers, useLocationSummary as useCordThreadActivitySummary, useThreadSummary as useCordThreadSummary, user };
//# sourceMappingURL=index.js.map
{
"name": "@cord-sdk/react",
"description": "React components for Cord",
"version": "0.0.42",
"version": "0.0.43",
"homepage": "https://docs.cord.com/",

@@ -22,4 +22,5 @@ "license": "MIT",

"dependencies": {
"@cord-sdk/types": "0.0.42",
"@cord-sdk/components": "0.0.42"
"@cord-sdk/components": "0.0.43",
"@cord-sdk/types": "0.0.43",
"classnames": "^2.3.1"
},

@@ -30,8 +31,10 @@ "peerDependencies": {

"devDependencies": {
"@cord-sdk/jsx": "0.0.42",
"@cord-sdk/jsx": "0.0.43",
"@types/react": "^17.0.43",
"@vanilla-extract/css": "^1.10.0",
"@vanilla-extract/rollup-plugin": "^1.2.2",
"rollup": "^2.70.1",
"rollup-plugin-typescript2": "^0.31.2",
"typescript": "^4.6.3"
"typescript": "^4.9.4"
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc