@atlaskit/editor-plugin-mentions
Version:
Mentions plugin for @atlaskit/editor-core
115 lines (114 loc) • 4.56 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { NodeDataProvider } from '@atlaskit/node-data-provider/node-data-provider';
const getAppType = userType => userType === 'APP' || userType === 'AGENT' ? 'agent' : 'user';
const enrichMentionNodeData = (mention, data) => {
var _data$appType;
return {
...data,
appType: (_data$appType = data === null || data === void 0 ? void 0 : data.appType) !== null && _data$appType !== void 0 ? _data$appType : getAppType(mention.userType)
};
};
const toMentionNode = ({
id,
userType
}) => ({
attrs: {
id,
// NodeDataProvider uses an ADF node internally, so normalize the runtime-only value.
userType: userType === 'AGENT' ? 'APP' : userType
},
type: 'mention'
});
const isMentionNode = node => node.type === 'mention' && node.attrs !== undefined && 'id' in node.attrs && typeof node.attrs.id === 'string';
/**
* Page-scoped data provider for mention avatars.
*
* Products inject their resolution strategy so this package remains independent
* of Jira and Confluence APIs. Reuse the instance for the lifetime of a renderer
* or editor rather than creating one for every mention.
*
* Deterministic products can supply `resolveMentionNodeData` to return an
* SSR-safe avatar URL synchronously. It must return the same value on the server
* and the client's first render to preserve hydration parity.
*
* Network-backed products supply `fetchMentionNodeData`. Calls made in the same
* microtask are combined and account IDs are deduplicated before the fetcher is
* invoked. A Relay adapter can query `users(accountIds:)`, then map each user's
* `picture` and app type to a `MentionNodeData` record keyed by account ID. The
* resolved result is cached by the underlying `NodeDataProvider`.
*/
export class MentionNodeDataProvider extends NodeDataProvider {
constructor(fetchMentionNodeData, resolveMentionNodeData = undefined) {
super();
_defineProperty(this, "name", 'mentionNodeDataProvider');
_defineProperty(this, "queuedFetches", []);
_defineProperty(this, "isFlushScheduled", false);
this.fetchMentionNodeData = fetchMentionNodeData;
this.resolveMentionNodeData = resolveMentionNodeData;
}
isNodeSupported(node) {
return isMentionNode(node) && node.attrs.userType !== 'SPECIAL';
}
nodeDataKey(node) {
return node.attrs.id;
}
async fetchNodesData(nodes) {
const accountIds = Array.from(new Set(nodes.map(({
attrs
}) => attrs.id)));
const dataByAccountId = await this.enqueueFetch(accountIds);
return nodes.map(({
attrs
}) => enrichMentionNodeData(attrs, dataByAccountId[attrs.id]));
}
getMentionData(mention, callback) {
var _this$resolveMentionN;
const resolvedData = (_this$resolveMentionN = this.resolveMentionNodeData) === null || _this$resolveMentionN === void 0 ? void 0 : _this$resolveMentionN.call(this, mention);
if (resolvedData) {
callback({
data: enrichMentionNodeData(mention, resolvedData)
});
return;
}
this.getData(toMentionNode(mention), callback);
}
getMentionDataFromCache(mention) {
var _this$resolveMentionN2, _this$getNodeDataFrom;
const resolvedData = (_this$resolveMentionN2 = this.resolveMentionNodeData) === null || _this$resolveMentionN2 === void 0 ? void 0 : _this$resolveMentionN2.call(this, mention);
if (resolvedData) {
return enrichMentionNodeData(mention, resolvedData);
}
const data = (_this$getNodeDataFrom = this.getNodeDataFromCache(toMentionNode(mention))) === null || _this$getNodeDataFrom === void 0 ? void 0 : _this$getNodeDataFrom.data;
return data ? enrichMentionNodeData(mention, data) : undefined;
}
enqueueFetch(accountIds) {
const result = new Promise((resolve, reject) => {
this.queuedFetches.push({
accountIds,
reject,
resolve
});
});
if (!this.isFlushScheduled) {
this.isFlushScheduled = true;
void Promise.resolve().then(() => this.flushFetches());
}
return result;
}
async flushFetches() {
this.isFlushScheduled = false;
const queuedFetches = this.queuedFetches;
this.queuedFetches = [];
const accountIds = Array.from(new Set(queuedFetches.flatMap(request => request.accountIds)));
try {
const data = await this.fetchMentionNodeData(accountIds);
queuedFetches.forEach(({
resolve
}) => resolve(data));
} catch (error) {
queuedFetches.forEach(({
reject
}) => reject(error));
}
}
}