recoder-code
Version:
Complete AI-powered development platform with ML model training, plugin registry, real-time collaboration, monitoring, infrastructure automation, and enterprise deployment capabilities
143 lines (121 loc) • 5.15 kB
JavaScript
var LocalPresence = require('./local-presence');
var ShareDBError = require('../../error');
var util = require('../../util');
var ERROR_CODE = ShareDBError.CODES;
module.exports = LocalDocPresence;
function LocalDocPresence(presence, presenceId) {
LocalPresence.call(this, presence, presenceId);
this.collection = this.presence.collection;
this.id = this.presence.id;
this._doc = this.connection.get(this.collection, this.id);
this._emitter = this.connection._docPresenceEmitter;
this._isSending = false;
this._docDataVersionByPresenceVersion = Object.create(null);
this._opHandler = this._transformAgainstOp.bind(this);
this._createOrDelHandler = this._handleCreateOrDel.bind(this);
this._loadHandler = this._handleLoad.bind(this);
this._destroyHandler = this.destroy.bind(this);
this._registerWithDoc();
}
LocalDocPresence.prototype = Object.create(LocalPresence.prototype);
LocalDocPresence.prototype.submit = function(value, callback) {
if (!this._doc.type) {
// If the Doc hasn't been created, we already assume all presence to
// be null. Let's early return, instead of error since this is a harmless
// no-op
if (value === null) return this._callbackOrEmit(null, callback);
var error = {
code: ERROR_CODE.ERR_DOC_DOES_NOT_EXIST,
message: 'Cannot submit presence. Document has not been created'
};
return this._callbackOrEmit(error, callback);
};
// Record the current data state version to check if we need to transform
// the presence later
this._docDataVersionByPresenceVersion[this.presenceVersion] = this._doc._dataStateVersion;
LocalPresence.prototype.submit.call(this, value, callback);
};
LocalDocPresence.prototype.destroy = function(callback) {
this._emitter.removeEventListener(this._doc, 'op', this._opHandler);
this._emitter.removeEventListener(this._doc, 'create', this._createOrDelHandler);
this._emitter.removeEventListener(this._doc, 'del', this._createOrDelHandler);
this._emitter.removeEventListener(this._doc, 'load', this._loadHandler);
this._emitter.removeEventListener(this._doc, 'destroy', this._destroyHandler);
LocalPresence.prototype.destroy.call(this, callback);
};
LocalDocPresence.prototype._sendPending = function() {
if (this._isSending) return;
this._isSending = true;
var presence = this;
this._doc.whenNothingPending(function() {
presence._isSending = false;
if (!presence.connection.canSend) return;
presence._pendingMessages.forEach(function(message) {
message.t = presence._doc.type.uri;
message.v = presence._doc.version;
presence.connection.send(message);
});
presence._pendingMessages = [];
presence._docDataVersionByPresenceVersion = Object.create(null);
});
};
LocalDocPresence.prototype._registerWithDoc = function() {
this._emitter.addEventListener(this._doc, 'op', this._opHandler);
this._emitter.addEventListener(this._doc, 'create', this._createOrDelHandler);
this._emitter.addEventListener(this._doc, 'del', this._createOrDelHandler);
this._emitter.addEventListener(this._doc, 'load', this._loadHandler);
this._emitter.addEventListener(this._doc, 'destroy', this._destroyHandler);
};
LocalDocPresence.prototype._transformAgainstOp = function(op, source) {
var presence = this;
var docDataVersion = this._doc._dataStateVersion;
this._pendingMessages.forEach(function(message) {
// Check if the presence needs transforming against the op - this is to check against
// edge cases where presence is submitted from an 'op' event
var messageDocDataVersion = presence._docDataVersionByPresenceVersion[message.pv];
if (messageDocDataVersion >= docDataVersion) return;
try {
message.p = presence._transformPresence(message.p, op, source);
// Ensure the presence's data version is kept consistent to deal with "deep" op
// submissions
presence._docDataVersionByPresenceVersion[message.pv] = docDataVersion;
} catch (error) {
var callback = presence._getCallback(message.pv);
presence._callbackOrEmit(error, callback);
}
});
try {
this.value = this._transformPresence(this.value, op, source);
} catch (error) {
this.emit('error', error);
}
};
LocalDocPresence.prototype._handleCreateOrDel = function() {
this._pendingMessages.forEach(function(message) {
message.p = null;
});
this.value = null;
};
LocalDocPresence.prototype._handleLoad = function() {
this.value = null;
this._pendingMessages = [];
this._docDataVersionByPresenceVersion = Object.create(null);
};
LocalDocPresence.prototype._message = function() {
var message = LocalPresence.prototype._message.call(this);
message.c = this.collection,
message.d = this.id,
message.v = null;
message.t = null;
return message;
};
LocalDocPresence.prototype._transformPresence = function(value, op, source) {
var type = this._doc.type;
if (!util.supportsPresence(type)) {
throw new ShareDBError(
ERROR_CODE.ERR_TYPE_DOES_NOT_SUPPORT_PRESENCE,
'Type does not support presence: ' + type.name
);
}
return type.transformPresence(value, op, source);
};