Migrate editor-socketio-server.js to TypeScript

Signed-off-by: David Mehren <dmehren1@gmail.com>
This commit is contained in:
David Mehren 2020-04-13 16:21:44 +02:00
parent c7478157e2
commit dc3a3f2994
No known key found for this signature in database
GPG key ID: 6017AF117F9756CB
3 changed files with 157 additions and 165 deletions

View file

@ -1,164 +0,0 @@
'use strict';
var EventEmitter = require('events').EventEmitter;
var TextOperation = require('./text-operation');
var WrappedOperation = require('./wrapped-operation');
var Server = require('./server');
var Selection = require('./selection');
var util = require('util');
var logger = require('../logger');
function EditorSocketIOServer(document, operations, docId, mayWrite, operationCallback) {
EventEmitter.call(this);
Server.call(this, document, operations);
this.users = {};
this.docId = docId;
this.mayWrite = mayWrite || function (_, cb) {
cb(true);
};
this.operationCallback = operationCallback;
}
util.inherits(EditorSocketIOServer, Server);
extend(EditorSocketIOServer.prototype, EventEmitter.prototype);
function extend(target, source) {
for (var key in source) {
if (source.hasOwnProperty(key)) {
target[key] = source[key];
}
}
}
EditorSocketIOServer.prototype.addClient = function (socket) {
var self = this;
socket.join(this.docId);
var docOut = {
str: this.document,
revision: this.operations.length,
clients: this.users
};
socket.emit('doc', docOut);
socket.on('operation', function (revision, operation, selection) {
socket.origin = 'operation';
self.mayWrite(socket, function (mayWrite) {
if (!mayWrite) {
logger.info("User doesn't have the right to edit.");
return;
}
try {
self.onOperation(socket, revision, operation, selection);
if (typeof self.operationCallback === 'function')
self.operationCallback(socket, operation);
} catch (err) {
setTimeout(function() {
var docOut = {
str: self.document,
revision: self.operations.length,
clients: self.users,
force: true
};
socket.emit('doc', docOut);
}, 100);
}
});
});
socket.on('get_operations', function (base, head) {
self.onGetOperations(socket, base, head);
});
socket.on('selection', function (obj) {
socket.origin = 'selection';
self.mayWrite(socket, function (mayWrite) {
if (!mayWrite) {
logger.info("User doesn't have the right to edit.");
return;
}
self.updateSelection(socket, obj && Selection.fromJSON(obj));
});
});
socket.on('disconnect', function () {
logger.debug("Disconnect");
socket.leave(self.docId);
self.onDisconnect(socket);
/*
if (socket.manager && socket.manager.sockets.clients(self.docId).length === 0) {
self.emit('empty-room');
}
*/
});
};
EditorSocketIOServer.prototype.onOperation = function (socket, revision, operation, selection) {
var wrapped;
try {
wrapped = new WrappedOperation(
TextOperation.fromJSON(operation),
selection && Selection.fromJSON(selection)
);
} catch (exc) {
logger.error("Invalid operation received: ");
logger.error(exc);
throw new Error(exc);
}
try {
var clientId = socket.id;
var wrappedPrime = this.receiveOperation(revision, wrapped);
if(!wrappedPrime) return;
logger.debug("new operation: " + JSON.stringify(wrapped));
this.getClient(clientId).selection = wrappedPrime.meta;
revision = this.operations.length;
socket.emit('ack', revision);
socket.broadcast.in(this.docId).emit(
'operation', clientId, revision,
wrappedPrime.wrapped.toJSON(), wrappedPrime.meta
);
//set document is dirty
this.isDirty = true;
} catch (exc) {
logger.error(exc);
throw new Error(exc);
}
};
EditorSocketIOServer.prototype.onGetOperations = function (socket, base, head) {
var operations = this.operations.slice(base, head).map(function (op) {
return op.wrapped.toJSON();
});
socket.emit('operations', head, operations);
};
EditorSocketIOServer.prototype.updateSelection = function (socket, selection) {
var clientId = socket.id;
if (selection) {
this.getClient(clientId).selection = selection;
} else {
delete this.getClient(clientId).selection;
}
socket.broadcast.to(this.docId).emit('selection', clientId, selection);
};
EditorSocketIOServer.prototype.setName = function (socket, name) {
var clientId = socket.id;
this.getClient(clientId).name = name;
socket.broadcast.to(this.docId).emit('set_name', clientId, name);
};
EditorSocketIOServer.prototype.setColor = function (socket, color) {
var clientId = socket.id;
this.getClient(clientId).color = color;
socket.broadcast.to(this.docId).emit('set_color', clientId, color);
};
EditorSocketIOServer.prototype.getClient = function (clientId) {
return this.users[clientId] || (this.users[clientId] = {});
};
EditorSocketIOServer.prototype.onDisconnect = function (socket) {
var clientId = socket.id;
delete this.users[clientId];
socket.broadcast.to(this.docId).emit('client_left', clientId);
};
module.exports = EditorSocketIOServer;

155
lib/ot/editor-socketio-server.ts Executable file
View file

@ -0,0 +1,155 @@
import { EventEmitter } from 'events'
import { logger } from '../logger'
import Selection from './selection'
import Server from './server'
import TextOperation from './text-operation'
import WrappedOperation from './wrapped-operation'
export class EditorSocketIOServer extends Server {
private readonly users: {}
private readonly docId: any
private mayWrite: any
constructor (document, operations, docId, mayWrite, operationCallback) {
super(document, operations)
// Whatever that does?
EventEmitter.call(this)
this.users = {}
this.docId = docId
this.mayWrite = mayWrite || function (_, cb) {
cb(true)
}
this.operationCallback = operationCallback
}
addClient (socket) {
const self = this
socket.join(this.docId)
const docOut = {
str: this.document,
revision: this.operations.length,
clients: this.users
}
socket.emit('doc', docOut)
socket.on('operation', function (revision, operation, selection) {
socket.origin = 'operation'
self.mayWrite(socket, function (mayWrite) {
if (!mayWrite) {
logger.info("User doesn't have the right to edit.")
return
}
try {
self.onOperation(socket, revision, operation, selection)
if (typeof self.operationCallback === 'function')
self.operationCallback(socket, operation)
} catch (err) {
setTimeout(function () {
const docOut = {
str: self.document,
revision: self.operations.length,
clients: self.users,
force: true
}
socket.emit('doc', docOut)
}, 100)
}
})
})
socket.on('get_operations', function (base, head) {
self.onGetOperations(socket, base, head)
})
socket.on('selection', function (obj) {
socket.origin = 'selection'
self.mayWrite(socket, function (mayWrite) {
if (!mayWrite) {
logger.info("User doesn't have the right to edit.")
return
}
self.updateSelection(socket, obj && Selection.fromJSON(obj))
})
})
socket.on('disconnect', function () {
logger.debug("Disconnect")
socket.leave(self.docId)
self.onDisconnect(socket)
/*
if (socket.manager && socket.manager.sockets.clients(self.docId).length === 0) {
self.emit('empty-room');
}
*/
})
};
onOperation (socket, revision, operation, selection) {
let wrapped
try {
wrapped = new WrappedOperation(
TextOperation.fromJSON(operation),
selection && Selection.fromJSON(selection)
)
} catch (exc) {
logger.error("Invalid operation received: ")
logger.error(exc)
throw new Error(exc)
}
try {
const clientId = socket.id
const wrappedPrime = this.receiveOperation(revision, wrapped)
if (!wrappedPrime) return
logger.debug("new operation: " + JSON.stringify(wrapped))
this.getClient(clientId).selection = wrappedPrime.meta
revision = this.operations.length
socket.emit('ack', revision)
socket.broadcast.in(this.docId).emit(
'operation', clientId, revision,
wrappedPrime.wrapped.toJSON(), wrappedPrime.meta
)
//set document is dirty
this.isDirty = true
} catch (exc) {
logger.error(exc)
throw new Error(exc)
}
};
onGetOperations (socket, base, head) {
const operations = this.operations.slice(base, head).map(function (op) {
return op.wrapped.toJSON()
})
socket.emit('operations', head, operations)
};
updateSelection (socket, selection) {
const clientId = socket.id
if (selection) {
this.getClient(clientId).selection = selection
} else {
delete this.getClient(clientId).selection
}
socket.broadcast.to(this.docId).emit('selection', clientId, selection)
};
setName (socket, name) {
const clientId = socket.id
this.getClient(clientId).name = name
socket.broadcast.to(this.docId).emit('set_name', clientId, name)
};
setColor (socket, color) {
const clientId = socket.id
this.getClient(clientId).color = color
socket.broadcast.to(this.docId).emit('set_color', clientId, color)
};
getClient (clientId) {
return this.users[clientId] || (this.users[clientId] = {})
};
onDisconnect (socket) {
const clientId = socket.id
delete this.users[clientId]
socket.broadcast.to(this.docId).emit('client_left', clientId)
};
}

View file

@ -10,6 +10,7 @@ import async from 'async'
import cookieParser from 'cookie-parser'
import cookie from 'cookie'
import Chance from 'chance'
import { EditorSocketIOServer } from './ot/editor-socketio-server'
const chance = new Chance()
@ -577,7 +578,7 @@ function startConnection (socket) {
const body = note.content
const createtime = note.createdAt
const updatetime = note.lastchangeAt
const server = new ot.EditorSocketIOServer(body, [], noteId, ifMayEdit, operationCallback)
const server = new EditorSocketIOServer(body, [], noteId, ifMayEdit, operationCallback)
const authors = {}
for (let i = 0; i < note.authors.length; i++) {