mirror of https://github.com/xwiki-labs/cryptpad
Show users editing and lag, properly send message to indicate that all users have left channel and show basic error box if disconnected.
This commit is contained in:
parent
5ae599fa07
commit
6cbdcdec65
|
@ -103,7 +103,7 @@ var dropClient = function (ctx, userpass) {
|
|||
var chanName = client.channels[i];
|
||||
var chan = ctx.channels[chanName];
|
||||
var idx = chan.indexOf(client);
|
||||
if (idx < 0) { throw new Error(); }
|
||||
if (idx < 0) { continue; }
|
||||
console.log("Removing ["+client.userName+"] from channel ["+chanName+"]");
|
||||
chan.splice(idx, 1);
|
||||
if (chan.length === 0) {
|
||||
|
@ -122,6 +122,7 @@ var handleMessage = function (ctx, socket, msg) {
|
|||
msg = popPassword(msg);
|
||||
|
||||
if (parsed.content[0] === REGISTER) {
|
||||
if (parsed.user.length === 0) { throw new Error(); }
|
||||
console.log("[" + userPass + "] registered");
|
||||
var client = ctx.registeredClients[userPass] = ctx.registeredClients[userPass] || {
|
||||
channels: [parsed.channelId],
|
||||
|
@ -131,17 +132,27 @@ console.log("[" + userPass + "] registered");
|
|||
client.socket = socket;
|
||||
|
||||
var chan = ctx.channels[parsed.channelId] = ctx.channels[parsed.channelId] || [];
|
||||
var newChan = (chan.length === 0);
|
||||
chan.name = parsed.channelId;
|
||||
chan.push(client);
|
||||
|
||||
// we send a register ack right away but then we fallthrough
|
||||
// to let other users know that we were registered.
|
||||
sendMsg(mkMessage('', parsed.channelId, [1,0]), socket);
|
||||
sendChannelMessage(ctx, chan, msg, function () {
|
||||
ctx.store.getMessages(chan.name, function (msg) {
|
||||
sendMsg(msg, socket);
|
||||
|
||||
var sendMsgs = function () {
|
||||
sendChannelMessage(ctx, chan, msg, function () {
|
||||
chan.push(client);
|
||||
ctx.store.getMessages(chan.name, function (msg) {
|
||||
sendMsg(msg, socket);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
if (newChan) {
|
||||
sendChannelMessage(ctx, chan, mkMessage('', chan.name, [DISCONNECT,0]), sendMsgs);
|
||||
} else {
|
||||
sendMsgs();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (parsed.content[0] === PING) {
|
||||
|
|
|
@ -31,7 +31,7 @@ var insert = function (coll, channelName, content, cb) {
|
|||
};
|
||||
|
||||
var getMessages = function (coll, channelName, cb) {
|
||||
coll.find({chan:channelName}).forEach(function (doc) {
|
||||
coll.find({chan:channelName}).sort( { _id: 1 } ).forEach(function (doc) {
|
||||
cb(doc.msg);
|
||||
}, function (err) {
|
||||
if (!err) { return; }
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"jquery": "~2.1.1",
|
||||
"tweetnacl": "~0.12.2",
|
||||
"ckeditor": "~4.4.5",
|
||||
"requirejs": "~2.1.15"
|
||||
"requirejs": "~2.1.15",
|
||||
"modalBox": "~1.0.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -933,6 +933,11 @@ var handleMessage = ChainPad.handleMessage = function (realtime, msgStr) {
|
|||
}
|
||||
|
||||
if (msg.messageType === Message.DISCONNECT) {
|
||||
if (msg.userName === '') {
|
||||
realtime.userList = [];
|
||||
userListChange(realtime);
|
||||
return;
|
||||
}
|
||||
var idx = realtime.userList.indexOf(msg.userName);
|
||||
if (Common.PARANOIA) { Common.assert(idx > -1); }
|
||||
if (idx > -1) {
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright 2014 XWiki SAS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
require.config({
|
||||
'shim': {
|
||||
'bower/modalBox/modalBox-min': ['bower/jquery/dist/jquery.min'],
|
||||
}
|
||||
});
|
||||
define([
|
||||
'messages',
|
||||
'bower/modalBox/modalBox-min'
|
||||
], function (Messages) {
|
||||
|
||||
var STYLE = [
|
||||
'<style>',
|
||||
'.modalBox {',
|
||||
' padding:5px;',
|
||||
' border:1px solid #CCC;',
|
||||
' background:#FFF;',
|
||||
' height:500px;',
|
||||
' width:700px;',
|
||||
' display:none;',
|
||||
'}',
|
||||
'img.iw-closeImg {',
|
||||
' width:24px;',
|
||||
' height:24px',
|
||||
'}',
|
||||
'.modalFooter {',
|
||||
' color:#FFF;',
|
||||
' position:absolute;',
|
||||
' bottom:0px',
|
||||
'}',
|
||||
'.modalFooter span {',
|
||||
' cursor:pointer;',
|
||||
'}',
|
||||
'.iw-modalOverlay {',
|
||||
' background:#000;',
|
||||
' opacity:.5',
|
||||
'}',
|
||||
'</style>'
|
||||
].join('');
|
||||
|
||||
var CONTENT = [
|
||||
'<center><h2 class="errorType"></h2></center>',
|
||||
'<br>',
|
||||
'<p class="errorExplanation"></p>'
|
||||
].join('');
|
||||
|
||||
var ERROR_ADDITIONAL = [
|
||||
'<p class="errorMoreExplanation"></p>',
|
||||
'<label for="errorBox_detailsBox" class="errorDetailsLabel"></label>',
|
||||
'<textarea id="errorBox_detailsBox" class="errorData"></textarea>',
|
||||
].join('');
|
||||
|
||||
var showError = function (errorType, docHtml, moreInfo) {
|
||||
$('body').append('<div class="modalBox"></div>');
|
||||
var $modalbox = $('.modalBox')
|
||||
$modalbox.append(CONTENT + STYLE);
|
||||
|
||||
$modalbox.find('.errorType').text(Messages['errorBox_errorType_' + errorType]);
|
||||
$modalbox.find('.errorExplanation').text(Messages['errorBox_errorExplanation_' + errorType]);
|
||||
if (moreInfo) {
|
||||
$modalbox.append(ERROR_ADDITIONAL);
|
||||
$modalbox.find('.errorMoreExplanation').text(Messages.errorBox_moreExplanation);
|
||||
$modalbox.find('.errorData').text(Messages['errorBox_' + errorType]);
|
||||
}
|
||||
|
||||
$modalbox.modalBox({
|
||||
onOpen: boxOpened,
|
||||
onClose: function () { $('.modalBox').remove(); }
|
||||
});
|
||||
$('.iw-modalOverlay').css({'z-index':10000});
|
||||
};
|
||||
|
||||
return {
|
||||
show: showError
|
||||
};
|
||||
});
|
21
www/main.js
21
www/main.js
|
@ -1,29 +1,15 @@
|
|||
define([
|
||||
'api/config?cb=' + Math.random().toString(16).substring(2),
|
||||
'realtime-wysiwyg',
|
||||
'messages',
|
||||
'bower/jquery/dist/jquery.min',
|
||||
'bower/ckeditor/ckeditor',
|
||||
'bower/tweetnacl/nacl-fast.min',
|
||||
], function (Config, RTWysiwyg) {
|
||||
], function (Config, RTWysiwyg, Messages) {
|
||||
var Ckeditor = window.CKEDITOR;
|
||||
var Nacl = window.nacl;
|
||||
var $ = jQuery;
|
||||
|
||||
var INITIAL_STATE = [
|
||||
'<p>',
|
||||
'This is <strong>CryptPad</strong>, the zero knowledge realtime collaborative editor.',
|
||||
'<br>',
|
||||
'What you type here is encrypted so only people who have the link can access it.',
|
||||
'<br>',
|
||||
'Even the server cannot see what you type.',
|
||||
'</p>',
|
||||
'<p>',
|
||||
'<small>',
|
||||
'<i>What you see here, what you hear here, when you leave here, let it stay here</i>',
|
||||
'</small>',
|
||||
'</p>',
|
||||
].join('');
|
||||
|
||||
var module = { exports: {} };
|
||||
|
||||
var parseKey = function (str) {
|
||||
|
@ -54,12 +40,11 @@ define([
|
|||
editor.on('instanceReady', function () {
|
||||
editor.execCommand('maximize');
|
||||
var ifr = window.ifr = $('iframe')[0];
|
||||
ifr.contentDocument.body.innerHTML = INITIAL_STATE;
|
||||
ifr.contentDocument.body.innerHTML = Messages.initialState;
|
||||
|
||||
var rtw =
|
||||
RTWysiwyg.start(Config.websocketURL,
|
||||
userName(),
|
||||
{},
|
||||
Nacl.util.encodeBase64(key.lookupKey).substring(0,10),
|
||||
key.cryptKey);
|
||||
editor.on('change', function () { rtw.onEvent(); });
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
define(function () {
|
||||
var out = {};
|
||||
|
||||
out.errorBox_errorType_disconnected = 'Connection Lost';
|
||||
out.errorBox_errorExplanation_disconnected = [
|
||||
'Lost connection to server, you may reconnect by reloading the page or review your work ',
|
||||
'by clicking outside of this box.'
|
||||
].join('');
|
||||
|
||||
out.editingAlone = 'Editing alone';
|
||||
out.editingWithOneOtherPerson = 'Editing with one other person';
|
||||
out.editingWith = 'Editing with';
|
||||
out.otherPeople = 'other people';
|
||||
out.disconnected = 'Disconnected';
|
||||
out.lag = 'Lag';
|
||||
|
||||
out.initialState = [
|
||||
'<p>',
|
||||
'This is <strong>CryptPad</strong>, the zero knowledge realtime collaborative editor.',
|
||||
'<br>',
|
||||
'What you type here is encrypted so only people who have the link can access it.',
|
||||
'<br>',
|
||||
'Even the server cannot see what you type.',
|
||||
'</p>',
|
||||
'<p>',
|
||||
'<small>',
|
||||
'<i>What you see here, what you hear here, when you leave here, let it stay here</i>',
|
||||
'</small>',
|
||||
'</p>',
|
||||
].join('');
|
||||
|
||||
return out;
|
||||
});
|
|
@ -16,12 +16,17 @@
|
|||
*/
|
||||
define([
|
||||
'html-patcher',
|
||||
'errorbox',
|
||||
'messages',
|
||||
'rangy',
|
||||
'chainpad',
|
||||
'otaml',
|
||||
'bower/jquery/dist/jquery.min',
|
||||
'bower/tweetnacl/nacl-fast.min'
|
||||
], function (HTMLPatcher) {
|
||||
], function (HTMLPatcher, ErrorBox, Messages) {
|
||||
|
||||
window.ErrorBox = ErrorBox;
|
||||
|
||||
var $ = window.jQuery;
|
||||
var Rangy = window.rangy;
|
||||
Rangy.init();
|
||||
|
@ -29,12 +34,8 @@ define([
|
|||
var Otaml = window.Otaml;
|
||||
var Nacl = window.nacl;
|
||||
|
||||
var ErrorBox = {};
|
||||
|
||||
var PARANOIA = true;
|
||||
|
||||
|
||||
|
||||
var module = { exports: {} };
|
||||
|
||||
/**
|
||||
|
@ -106,26 +107,34 @@ var ErrorBox = {};
|
|||
};
|
||||
|
||||
var isSocketDisconnected = function (socket, realtime) {
|
||||
return socket._socket.readyState === socket.CLOSING
|
||||
|| socket._socket.readyState === socket.CLOSED
|
||||
var sock = socket._socket;
|
||||
return sock.readyState === sock.CLOSING
|
||||
|| sock.readyState === sock.CLOSED
|
||||
|| (realtime.getLag().waiting && realtime.getLag().lag > MAX_LAG_BEFORE_DISCONNECT);
|
||||
};
|
||||
|
||||
var updateUserList = function (myUserName, listElement, userList, messages) {
|
||||
var updateUserList = function (myUserName, listElement, userList) {
|
||||
var meIdx = userList.indexOf(myUserName);
|
||||
if (meIdx === -1) {
|
||||
listElement.text(messages.disconnected);
|
||||
listElement.text(Messages.disconnected);
|
||||
return;
|
||||
}
|
||||
listElement.text(messages.editingWith + ' ' + (userList.length - 1) + ' people');
|
||||
if (userList.length === 1) {
|
||||
listElement.text(Messages.editingAlone);
|
||||
} else if (userList.length === 2) {
|
||||
listElement.text(Messages.editingWithOneOtherPerson);
|
||||
} else {
|
||||
listElement.text(Messages.editingWith + ' ' + (userList.length - 1) +
|
||||
Messages.otherPeople);
|
||||
}
|
||||
};
|
||||
|
||||
var createUserList = function (realtime, myUserName, container, messages) {
|
||||
var createUserList = function (realtime, myUserName, container) {
|
||||
var id = uid();
|
||||
$(container).prepend('<div class="' + USER_LIST_CLS + '" id="'+id+'"></div>');
|
||||
var listElement = $('#'+id);
|
||||
realtime.onUserListChange(function (userList) {
|
||||
updateUserList(myUserName, listElement, userList, messages);
|
||||
updateUserList(myUserName, listElement, userList);
|
||||
});
|
||||
return listElement;
|
||||
};
|
||||
|
@ -269,10 +278,10 @@ var ErrorBox = {};
|
|||
return 'rtwysiwyg-uid-' + String(Math.random()).substring(2);
|
||||
};
|
||||
|
||||
var checkLag = function (realtime, lagElement, messages) {
|
||||
var checkLag = function (realtime, lagElement) {
|
||||
var lag = realtime.getLag();
|
||||
var lagSec = lag.lag/1000;
|
||||
var lagMsg = messages.lag + ' ';
|
||||
var lagMsg = Messages.lag + ' ';
|
||||
if (lag.waiting && lagSec > 1) {
|
||||
lagMsg += "?? " + Math.floor(lagSec);
|
||||
} else {
|
||||
|
@ -281,12 +290,12 @@ var ErrorBox = {};
|
|||
lagElement.text(lagMsg);
|
||||
};
|
||||
|
||||
var createLagElement = function (socket, realtime, container, messages) {
|
||||
var createLagElement = function (socket, realtime, container) {
|
||||
var id = uid();
|
||||
$(container).append('<div class="' + LAG_ELEM_CLS + '" id="'+id+'"></div>');
|
||||
var lagElement = $('#'+id);
|
||||
var intr = setInterval(function () {
|
||||
checkLag(realtime, lagElement, messages);
|
||||
checkLag(realtime, lagElement);
|
||||
}, 3000);
|
||||
socket.onClose.push(function () { clearTimeout(intr); });
|
||||
return lagElement;
|
||||
|
@ -306,11 +315,11 @@ var ErrorBox = {};
|
|||
'.' + TOOLBAR_CLS + ' {',
|
||||
' color: #666;',
|
||||
' font-weight: bold;',
|
||||
' background-color: #f0f0ee;',
|
||||
' border-bottom: 1px solid #DDD;',
|
||||
' border-top: 3px solid #CCC;',
|
||||
' border-right: 2px solid #CCC;',
|
||||
' border-left: 2px solid #CCC;',
|
||||
// ' background-color: #f0f0ee;',
|
||||
// ' border-bottom: 1px solid #DDD;',
|
||||
// ' border-top: 3px solid #CCC;',
|
||||
// ' border-right: 2px solid #CCC;',
|
||||
// ' border-left: 2px solid #CCC;',
|
||||
' height: 26px;',
|
||||
' margin-bottom: -3px;',
|
||||
' display: inline-block;',
|
||||
|
@ -319,7 +328,7 @@ var ErrorBox = {};
|
|||
'.' + TOOLBAR_CLS + ' div {',
|
||||
' padding: 0 10px;',
|
||||
' height: 1.5em;',
|
||||
' background: #f0f0ee;',
|
||||
// ' background: #f0f0ee;',
|
||||
' line-height: 25px;',
|
||||
' height: 22px;',
|
||||
'}',
|
||||
|
@ -338,6 +347,7 @@ var ErrorBox = {};
|
|||
'.' + DEBUG_LINK_CLS + ':link { color:transparent; }',
|
||||
'.' + DEBUG_LINK_CLS + ':link:hover { color:blue; }',
|
||||
'.gwt-TabPanelBottom { border-top: 0 none; }',
|
||||
|
||||
'</style>'
|
||||
].join('\n'));
|
||||
return toolbar;
|
||||
|
@ -417,8 +427,7 @@ var ErrorBox = {};
|
|||
return spl[0] + res.length + ':' + res;
|
||||
};
|
||||
|
||||
var start = module.exports.start =
|
||||
function (websocketUrl, userName, messages, channel, cryptKey)
|
||||
var start = module.exports.start = function (websocketUrl, userName, channel, cryptKey)
|
||||
{
|
||||
var passwd = 'y';
|
||||
var wysiwygDiv = document.getElementById('cke_1_contents');
|
||||
|
@ -427,10 +436,11 @@ var ErrorBox = {};
|
|||
var socket = makeWebsocket(websocketUrl);
|
||||
var onEvent = function () { };
|
||||
|
||||
var toolbar = createRealtimeToolbar('#xwikieditcontent');
|
||||
var toolbar = createRealtimeToolbar('#cke_1_toolbox');
|
||||
|
||||
socket.onClose.push(function () {
|
||||
$(toolbar).remove();
|
||||
checkSocket();
|
||||
});
|
||||
|
||||
var allMessages = [];
|
||||
|
@ -474,17 +484,15 @@ var ErrorBox = {};
|
|||
getDocHTML(doc),
|
||||
{ transformFunction: Otaml.transform });
|
||||
|
||||
//createDebugLink(realtime, doc, allMessages, toolbar, messages);
|
||||
//createDebugLink(realtime, doc, allMessages, toolbar);
|
||||
|
||||
createLagElement(socket,
|
||||
realtime,
|
||||
toolbar.find('.rtwysiwyg-toolbar-rightside'),
|
||||
messages);
|
||||
toolbar.find('.rtwysiwyg-toolbar-rightside'));
|
||||
|
||||
createUserList(realtime,
|
||||
userName,
|
||||
toolbar.find('.rtwysiwyg-toolbar-leftside'),
|
||||
messages);
|
||||
toolbar.find('.rtwysiwyg-toolbar-leftside'));
|
||||
|
||||
onEvent = function () {
|
||||
if (isErrorState) { return; }
|
||||
|
@ -522,11 +530,10 @@ var ErrorBox = {};
|
|||
};
|
||||
|
||||
realtime.onUserListChange(function (userList) {
|
||||
if (!initializing && userList.indexOf(userName) === -1) { return; }
|
||||
if (!initializing || userList.indexOf(userName) === -1) { return; }
|
||||
// if we spot ourselves being added to the document, we'll switch
|
||||
// 'initializing' off because it means we're fully synced.
|
||||
initializing = false;
|
||||
userDocBeforePatch = realtime.getUserDoc();
|
||||
incomingPatch();
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue