@atlaskit/editor-plugin-avatar-group
Version:
Avatar Group plugin for @atlaskit/editor-core.
124 lines (120 loc) • 5.49 kB
JavaScript
/**
* @jsxRuntime classic
* @jsx jsx
*/
import React from 'react';
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled, @typescript-eslint/consistent-type-imports
import { jsx } from '@emotion/react';
import { useIntl } from 'react-intl';
import AvatarGroup from '@atlaskit/avatar-group';
import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
import { Selection } from '@atlaskit/editor-prosemirror/state';
import { fg } from '@atlaskit/platform-feature-flags';
import { fetchUserNames } from '../services/fetch-user-names';
import { avatarContainerStyles } from './styles';
import toAvatar from './to-avatar';
const scrollToCollabCursor = (editorAPI, participants, sessionId, index, editorAnalyticsAPI) => {
const selectedUser = participants[index];
const cursorPos = selectedUser.cursorPos;
if (selectedUser && cursorPos !== undefined && selectedUser.sessionId !== sessionId) {
var _editorAPI$core, _editorAPI$core$actio, _editorAPI$core2, _editorAPI$core2$acti;
const analyticsPayload = {
action: ACTION.MATCHED,
actionSubject: ACTION_SUBJECT.SELECTION,
eventType: EVENT_TYPE.TRACK
};
editorAPI === null || editorAPI === void 0 ? void 0 : (_editorAPI$core = editorAPI.core) === null || _editorAPI$core === void 0 ? void 0 : (_editorAPI$core$actio = _editorAPI$core.actions) === null || _editorAPI$core$actio === void 0 ? void 0 : _editorAPI$core$actio.execute(({
tr
}) => {
tr.setSelection(Selection.near(tr.doc.resolve(cursorPos)));
editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 ? void 0 : editorAnalyticsAPI.attachAnalyticsEvent(analyticsPayload)(tr);
tr.scrollIntoView();
return tr;
});
editorAPI === null || editorAPI === void 0 ? void 0 : (_editorAPI$core2 = editorAPI.core) === null || _editorAPI$core2 === void 0 ? void 0 : (_editorAPI$core2$acti = _editorAPI$core2.actions) === null || _editorAPI$core2$acti === void 0 ? void 0 : _editorAPI$core2$acti.focus();
}
};
const getMissingActingUserIdsKey = (participants, userNamesById) => {
const participantUserIds = new Set();
const missingActingUserIds = new Set();
for (const participant of participants) {
if (participant.userId) {
participantUserIds.add(participant.userId);
}
}
for (const participant of participants) {
const actingUserId = participant.actingUserId;
if (actingUserId && !participantUserIds.has(actingUserId) && !userNamesById[actingUserId]) {
missingActingUserIds.add(actingUserId);
}
}
let missingActingUserIdsKey = '';
for (const actingUserId of missingActingUserIds) {
missingActingUserIdsKey = missingActingUserIdsKey ? `${missingActingUserIdsKey}|${actingUserId}` : actingUserId;
}
return missingActingUserIdsKey;
};
export const Avatars = /*#__PURE__*/React.memo(props => {
var _props$participants, _props$participants$t;
const {
sessionId,
featureFlags,
editorAPI
} = props;
const intl = useIntl();
const [userNamesById, setUserNamesById] = React.useState({});
// .slice() turns ReadonlyArray<CollabParticipant> into a mutable CollabParticipant[]
const participants = (_props$participants = props.participants) === null || _props$participants === void 0 ? void 0 : (_props$participants$t = _props$participants.toArray()) === null || _props$participants$t === void 0 ? void 0 : _props$participants$t.slice();
const missingActingUserIdsKey = React.useMemo(() => {
if (!participants || !fg('platform_move_presence_agents')) {
return '';
}
return getMissingActingUserIdsKey(participants, userNamesById);
}, [participants, userNamesById]);
React.useEffect(() => {
if (!missingActingUserIdsKey) {
return;
}
let cancelled = false;
fetchUserNames(missingActingUserIdsKey.split('|')).then(namesById => {
if (cancelled || !Object.keys(namesById).length) {
return;
}
setUserNamesById(currentNamesById => ({
...currentNamesById,
...namesById
}));
});
return () => {
cancelled = true;
};
}, [missingActingUserIdsKey]);
if (!participants) {
return null;
}
// eslint-disable-next-line @atlassian/perf-linting/no-expensive-computations-in-render -- Ignored via go/ees017 (to be fixed)
const avatars = participants.sort(p => p.sessionId === sessionId ? -1 : 1).map(participant => toAvatar(participant, editorAPI, intl.formatMessage, participants, userNamesById));
if (!avatars.length) {
return null;
}
return (
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
jsx("div", {
css: avatarContainerStyles
}, jsx(AvatarGroup, {
appearance: "stack",
size: "medium",
data: avatars,
maxCount: 3
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
,
onAvatarClick: (_event, _analytics, index) => {
const allowCollabAvatarScroll = featureFlags === null || featureFlags === void 0 ? void 0 : featureFlags.collabAvatarScroll;
// user does not need to scroll to their own position (index 0)
if (allowCollabAvatarScroll && index > 0) {
scrollToCollabCursor(editorAPI, participants, props.sessionId, index, props.editorAnalyticsAPI);
}
}
}), props.children)
);
});