@atlaskit/editor-plugin-collab-edit
Version:
Collab Edit plugin for @atlaskit/editor-core
157 lines (153 loc) • 7.53 kB
JavaScript
// It is important to get all steps in that package
// Ignored via go/ees005
// eslint-disable-next-line import/no-namespace
import * as adfCustomSteps from '@atlaskit/adf-schema/steps';
// It is important to get all steps in that package
// Ignored via go/ees005
// eslint-disable-next-line import/no-namespace
import * as atlaskKitCustomSteps from '@atlaskit/custom-steps';
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, fireAnalyticsEvent } from '@atlaskit/editor-common/analytics';
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
import { Step } from '@atlaskit/editor-prosemirror/transform';
import { addSynchronyErrorAnalytics } from '../analytics';
import { initialize } from '../events/initialize';
import { pluginKey } from './plugin-key';
import { PluginState } from './plugin-state';
const enforceCustomStepRegisters = () => {
const tryToRegisterStep = obj => {
for (const customStep of Object.values(obj)) {
var _customStep$prototype;
// I know this seems awful
// But unfortunate ProseMirror does not expose the jsonID property p
// @ts-expect-error
const id = customStep === null || customStep === void 0 ? void 0 : (_customStep$prototype = customStep.prototype) === null || _customStep$prototype === void 0 ? void 0 : _customStep$prototype.jsonID;
if (typeof id === 'string') {
try {
// We are trying to re-register the steps here
// in the normal flow, those custom step should be already registred
// So, it should throw an expeception
// @ts-expect-error
Step.jsonID(id, customStep);
} catch (e) {
// This mean the step was already registred.
// It is ok to ignore this exception.
}
}
}
};
// @ts-expect-error
tryToRegisterStep(atlaskKitCustomSteps);
// @ts-expect-error
tryToRegisterStep(adfCustomSteps);
};
export const createPlugin = (dispatch, providerFactory, providerResolver, collabProviderCallback, options, featureFlags, pluginInjectionApi) => {
enforceCustomStepRegisters();
return new SafePlugin({
key: pluginKey,
state: {
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
init(config) {
return PluginState.init(config);
},
apply(transaction, prevPluginState, _oldEditorState, _newEditorState) {
const pluginState = prevPluginState.apply(transaction);
dispatch(pluginKey, pluginState);
return pluginState;
}
},
props: {
decorations(state) {
var _pluginKey$getState;
return (_pluginKey$getState = pluginKey.getState(state)) === null || _pluginKey$getState === void 0 ? void 0 : _pluginKey$getState.decorations;
},
handleDOMEvents: {
click(view, event) {
var _pluginKey$getState2, _pluginKey$getState2$, _pluginInjectionApi$a, _pluginInjectionApi$a2, _pluginInjectionApi$a3;
if (!(event.target instanceof HTMLElement)) {
return false;
}
if (!view.state.selection.empty) {
return false;
}
const {
pos
} = view.state.tr.selection.$from;
if (!pos) {
return false;
}
// check if the pos is the same pos as a telepointer decoration
const decorations = (_pluginKey$getState2 = pluginKey.getState(view.state)) === null || _pluginKey$getState2 === void 0 ? void 0 : (_pluginKey$getState2$ = _pluginKey$getState2.decorations) === null || _pluginKey$getState2$ === void 0 ? void 0 : _pluginKey$getState2$.find(pos, pos);
if (!(decorations !== null && decorations !== void 0 && decorations.length)) {
return false;
}
// analytics to track telepointer clicks as they sometimes cause broken selections
const fireAnalyticsCallback = fireAnalyticsEvent((_pluginInjectionApi$a = pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a2 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a2 === void 0 ? void 0 : (_pluginInjectionApi$a3 = _pluginInjectionApi$a2.sharedState.currentState()) === null || _pluginInjectionApi$a3 === void 0 ? void 0 : _pluginInjectionApi$a3.createAnalyticsEvent) !== null && _pluginInjectionApi$a !== void 0 ? _pluginInjectionApi$a : undefined);
fireAnalyticsCallback({
payload: {
action: ACTION.CLICKED,
actionSubject: ACTION_SUBJECT.TELEPOINTER,
actionSubjectId: ACTION_SUBJECT_ID.TELEPOINTER,
eventType: EVENT_TYPE.TRACK
}
});
}
}
},
filterTransaction(tr, state) {
const pluginState = pluginKey.getState(state);
const collabInitialiseTr = tr.getMeta('collabInitialised');
// Don't allow transactions that modifies the document before
// collab-plugin is ready.
if (collabInitialiseTr) {
return true;
}
if (!(pluginState !== null && pluginState !== void 0 && pluginState.isReady) && tr.docChanged) {
return false;
}
return true;
},
view(view) {
var _pluginInjectionApi$a4, _pluginInjectionApi$a8;
const addErrorAnalytics = addSynchronyErrorAnalytics(view.state, view.state.tr, featureFlags, pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a4 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a4 === void 0 ? void 0 : _pluginInjectionApi$a4.actions);
const onSyncUpError = attributes => {
var _pluginInjectionApi$a5, _pluginInjectionApi$a6, _pluginInjectionApi$a7;
const fireAnalyticsCallback = fireAnalyticsEvent((_pluginInjectionApi$a5 = pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a6 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a6 === void 0 ? void 0 : (_pluginInjectionApi$a7 = _pluginInjectionApi$a6.sharedState.currentState()) === null || _pluginInjectionApi$a7 === void 0 ? void 0 : _pluginInjectionApi$a7.createAnalyticsEvent) !== null && _pluginInjectionApi$a5 !== void 0 ? _pluginInjectionApi$a5 : undefined);
fireAnalyticsCallback({
payload: {
action: ACTION.NEW_COLLAB_SYNC_UP_ERROR_NO_STEPS,
actionSubject: ACTION_SUBJECT.EDITOR,
eventType: EVENT_TYPE.OPERATIONAL,
attributes
}
});
};
options.onSyncUpError = onSyncUpError;
const cleanup = collabProviderCallback(initialize({
view,
options,
providerFactory,
featureFlags,
pluginInjectionApi,
editorAnalyticsApi: pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$a8 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a8 === void 0 ? void 0 : _pluginInjectionApi$a8.actions
}), addErrorAnalytics);
providerFactory && providerFactory.subscribe('collabEditProvider', (_name, providerPromise) => {
if (providerPromise) {
providerPromise.then(provider => providerResolver(provider));
}
});
return {
destroy() {
providerFactory.unsubscribeAll('collabEditProvider');
if (cleanup) {
cleanup.then(unsubscribe => {
if (unsubscribe) {
unsubscribe();
}
});
}
}
};
}
});
};