UNPKG

@atlaskit/editor-plugin-collab-edit

Version:

Collab Edit plugin for @atlaskit/editor-core

146 lines (140 loc) 5.98 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createPlugin = exports.createFilterTransaction = void 0; exports.generateTransactionKey = generateTransactionKey; exports.trackSpammingStepsPluginKey = exports.sanitizeFilteredStep = void 0; var _steps = require("@atlaskit/adf-schema/steps"); var _safePlugin = require("@atlaskit/editor-common/safe-plugin"); var _state = require("@atlaskit/editor-prosemirror/state"); var _transform = require("@atlaskit/editor-prosemirror/transform"); var THRESHOLD = 50; // 50 milliseconds /** * Sanitizes a given ProseMirror step by extracting its type and non-UCG relevant attributes. * * @param {Step} step - The ProseMirror step to be sanitized. * @returns {SanitizedFilteredStep} - The sanitized step with only necessary information. * * @example * ``` * const step = new AttrStep(10, 'colwidth', [123, 451] ); * const sanitized = sanitizeFilteredStep(step); * * // Output: { stepType: 'attr', stepInstance: 'AttrStep', attr: 'example' } * ``` */ var sanitizeFilteredStep = exports.sanitizeFilteredStep = function sanitizeFilteredStep(step) { var serializedStep = step.toJSON(); var sanitizedStep = { stepType: serializedStep.stepType, stepInstance: 'unknown' }; if (step instanceof _transform.AttrStep) { sanitizedStep.attr = step.attr; sanitizedStep.stepInstance = 'AttrStep'; } else if (step instanceof _transform.DocAttrStep) { sanitizedStep.attr = step.attr; sanitizedStep.stepInstance = 'DocAttrStep'; } else if (step instanceof _steps.SetAttrsStep) { // Combines all attrs keys separated by _ to one single string sanitizedStep.attr = Object.keys(step.attrs).sort().join('_'); sanitizedStep.stepInstance = 'SetAttrsStep'; } else if (step instanceof _transform.AddMarkStep) { sanitizedStep.markType = step.mark.type.name; sanitizedStep.stepInstance = 'AddMarkStep'; } else if (step instanceof _transform.RemoveMarkStep) { sanitizedStep.markType = step.mark.type.name; sanitizedStep.stepInstance = 'RemoveMarkStep'; } else if (step instanceof _transform.RemoveNodeMarkStep) { sanitizedStep.markType = step.mark.type.name; sanitizedStep.stepInstance = 'RemoveNodeMarkStep'; } else if (step instanceof _transform.AddNodeMarkStep) { sanitizedStep.markType = step.mark.type.name; sanitizedStep.stepInstance = 'AddNodeMarkStep'; } else if (step instanceof _transform.ReplaceStep) { sanitizedStep.stepInstance = 'ReplaceStep'; } else if (step instanceof _transform.ReplaceAroundStep) { sanitizedStep.stepInstance = 'ReplaceAroundStep'; } else if (step instanceof _steps.AnalyticsStep) { sanitizedStep.stepInstance = 'AnalyticsStep'; } else if (step instanceof _steps.InsertTypeAheadStep) { sanitizedStep.stepInstance = 'InsertTypeAheadStep'; } else if (step instanceof _steps.LinkMetaStep) { sanitizedStep.stepInstance = 'LinkMetaStep'; } return sanitizedStep; }; var createFilterTransaction = exports.createFilterTransaction = function createFilterTransaction(recentTransactionsTimestamps, trackFilteredTransaction) { return function (tr) { if (Boolean(tr.getMeta('appendTransaction'))) { return true; } var isRemote = Boolean(tr.getMeta('isRemote')); if (isRemote) { return true; } var containsExcludedSteps = tr.steps.some(function (step) { return step instanceof _steps.AnalyticsStep || step instanceof _transform.ReplaceStep || step instanceof _transform.ReplaceAroundStep || step instanceof _steps.LinkMetaStep; }); if (containsExcludedSteps) { return true; } if (tr.docChanged && tr.doc.eq(tr.before)) { var transactionKey = generateTransactionKey(tr); if (!transactionKey) { return true; } //Clean up tracked transactions when time over threshold recentTransactionsTimestamps.forEach(function (value, key) { if (tr.time - value.timestamp > THRESHOLD) { // Delete tracked transaction when over threshold recentTransactionsTimestamps.delete(key); } }); var lastTransactionEntry = recentTransactionsTimestamps.get(transactionKey); if (!lastTransactionEntry) { // If no timestamp exists for the given transaction, allow transaction and add an entry recentTransactionsTimestamps.set(transactionKey, { timestamp: tr.time, steps: tr.steps }); return true; } // Track analytics for the filtered transaction trackFilteredTransaction(tr); // Filter transaction return false; } return true; // Allow the transaction }; }; // Helper function to create a u ique transaction key function generateTransactionKey(tr) { var stepPositions = tr.steps.map(function (step) { if (step instanceof _transform.RemoveNodeMarkStep || step instanceof _transform.AddNodeMarkStep || step instanceof _steps.SetAttrsStep || step instanceof _transform.AttrStep) { if (step.pos !== undefined) { return "".concat(step.pos); } } else if (step instanceof _transform.AddMarkStep || step instanceof _transform.RemoveMarkStep) { return "from_".concat(step.from, "_to_").concat(step.to); } else if (step instanceof _steps.InsertTypeAheadStep) { return "insertTypeAheadStep"; } return ''; }); if (stepPositions.some(function (step) { return Boolean(step); })) { return stepPositions.join('_'); } return ''; } var trackSpammingStepsPluginKey = exports.trackSpammingStepsPluginKey = new _state.PluginKey('trackAndFilterSpammingStepsPluginKey'); var createPlugin = exports.createPlugin = function createPlugin(trackFilteredTransaction) { var recentTransactionsTimestamps = new Map(); return new _safePlugin.SafePlugin({ key: trackSpammingStepsPluginKey, filterTransaction: createFilterTransaction(recentTransactionsTimestamps, trackFilteredTransaction) }); };