@atlaskit/editor-plugin-collab-edit
Version:
Collab Edit plugin for @atlaskit/editor-core
146 lines (140 loc) • 5.98 kB
JavaScript
;
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)
});
};