Display other users' cursor

This commit is contained in:
yflory
2020-03-09 18:49:57 +01:00
parent 0674f410f5
commit 4690392cd9
3 changed files with 201 additions and 27 deletions

View File

@@ -8,6 +8,7 @@ define([
'/common/common-util.js',
'/common/common-hash.js',
'/common/common-interface.js',
'/common/common-ui-elements.js',
'/common/modes.js',
'/customize/messages.js',
'/common/hyperscript.js',
@@ -38,6 +39,7 @@ define([
Util,
Hash,
UI,
UIElements,
Modes,
Messages,
h,
@@ -51,6 +53,8 @@ define([
var verbose = function (x) { console.log(x); };
verbose = function () {}; // comment out to enable verbose logging
var onRedraw = Util.mkEvent();
var onCursorUpdate = Util.mkEvent();
var remoteCursors = {};
Messages.kanban_title = "Title"; // XXX
Messages.kanban_body = "Body"; // XXX
@@ -59,6 +63,7 @@ define([
Messages.kanban_delete = "Delete"; // XXX
Messages.kanban_tags = "Filter tags"; // XXX
Messages.kanban_noTags = "No tags"; // XXX
Messages.kanban_conflicts = "Currently editing:"; // XXX
// XXX
// Conflicts
@@ -83,6 +88,46 @@ define([
input.selectionEnd = selects[1];
};
var getTextColor = function (hex) {
if (hex && /^#/.test(hex)) { hex = hex.slice(1); }
if (!/^[0-9a-f]{6}$/i.test(hex)) {
return '#000000';
}
var r = parseInt(hex.slice(0,2), 16);
var g = parseInt(hex.slice(2,4), 16);
var b = parseInt(hex.slice(4,6), 16);
if ((r*0.213 + g*0.715 + b*0.072) > 255/2) {
return '#000000';
}
return '#FFFFFF';
};
var getAvatar = function (cursor, noClear) {
// Tippy
var html = '<span class="cp-cursor-avatar">';
if (cursor.avatar && UIElements.getAvatar(cursor.avatar)) {
html += UIElements.getAvatar(cursor.avatar);
}
html += cursor.name + '</span>';
var l = (cursor.name || Messages.anonymous).slice(0,1).toUpperCase();
var text = '';
if (cursor.color) {
text = 'color:'+getTextColor(cursor.color)+';';
}
var avatar = h('span.cp-cursor.cp-tippy-html', {
style: "background-color: " + (cursor.color || 'red') + ";"+text,
title: html
}, l);
if (!noClear) {
cursor.clear = function () {
$(avatar).remove();
};
}
return avatar;
};
var getExistingTags = function (boards) {
var tags = [];
boards = boards || {};
@@ -112,8 +157,12 @@ define([
addEditItemButton(framework, kanban);
};
if (editModal) { return editModal; }
var titleInput, tagsDiv, colors, text;
var conflicts, conflictContainer, titleInput, tagsDiv, colors, text;
var content = h('div', [
conflictContainer = h('div#cp-kanban-edit-conflicts', [
h('div', Messages.kanban_conflicts),
conflicts = h('div.cp-kanban-cursors')
]),
h('label', {for:'cp-kanban-edit-title'}, Messages.kanban_title),
titleInput = h('input#cp-kanban-edit-title'),
h('label', {for:'cp-kanban-edit-body'}, Messages.kanban_body),
@@ -126,6 +175,27 @@ define([
colors = h('div#cp-kanban-edit-colors'),
]);
var $conflict = $(conflicts);
var $cc = $(conflictContainer);
var conflict = {
setValue: function () {
$conflict.empty();
var i = 0;
$cc.hide();
Object.keys(remoteCursors).forEach(function (nid) {
var c = remoteCursors[nid];
var avatar = getAvatar(c, true);
if (Number(c.item) === Number(id) || Number(c.board) === Number(id)) {
$conflict.append(avatar);
i++;
}
});
if (!i) { return; }
$cc.show();
}
};
// Title
var $title = $(titleInput);
$title.on('change keyup', function () {
@@ -248,11 +318,17 @@ define([
isBoard = _isBoard;
id = _id;
if (_isBoard) {
onCursorUpdate.fire({
board: _id
});
dataObject = kanban.getBoardJSON(id);
$(content)
.find('#cp-kanban-edit-body, #cp-kanban-edit-tags, [for="cp-kanban-edit-body"], [for="cp-kanban-edit-tags"]')
.hide();
} else {
onCursorUpdate.fire({
item: _id
});
dataObject = kanban.getItemJSON(id);
$(content)
.find('#cp-kanban-edit-body, #cp-kanban-edit-tags, [for="cp-kanban-edit-body"], [for="cp-kanban-edit-tags"]')
@@ -287,6 +363,7 @@ define([
className: 'primary',
name: Messages.filePicker_close,
onClick: function () {
onCursorUpdate.fire({});
},
keys: []
}];
@@ -310,6 +387,7 @@ define([
return;
}
// Not deleted, apply updates
editModal.conflict.setValue();
PROPERTIES.forEach(function (type) {
editModal[type].setValue(dataObject[type], true);
});
@@ -321,7 +399,8 @@ define([
title: title,
body: body,
tags: tags,
color: color
color: color,
conflict: conflict
};
};
var getItemEditModal = function (framework, kanban, eid) {
@@ -331,6 +410,7 @@ define([
var boards = kanban.options.boards || {};
var item = (boards.items || {})[eid];
if (!item) { return void UI.warn(Messages.error); }
editModal.conflict.setValue();
PROPERTIES.forEach(function (type) {
if (!editModal[type]) { return; }
editModal[type].setValue(item[type]);
@@ -345,6 +425,7 @@ define([
var boards = kanban.options.boards || {};
var board = (boards.data || {})[id];
if (!board) { return void UI.warn(Messages.error); }
editModal.conflict.setValue();
BOARD_PROPERTIES.forEach(function (type) {
if (!editModal[type]) { return; }
editModal[type].setValue(board[type]);
@@ -482,6 +563,12 @@ define([
}
var eid = $(el).attr('data-eid');
kanban.inEditMode = eid;
setTimeout(function () {
// Make sure the click is sent after the "blur" in case we move from a card to another
onCursorUpdate.fire({
item: eid
});
});
var name = $(el).text();
$(el).html('');
@@ -500,8 +587,9 @@ define([
kanban.onChange();
// Unlock edit mode
kanban.inEditMode = false;
onCursorUpdate.fire({});
};
$input.blur(save);
//$input.blur(save);
$input.keydown(function (e) {
if (e.which === 13) {
e.preventDefault();
@@ -515,10 +603,7 @@ define([
if (e.which === 27) {
e.preventDefault();
e.stopPropagation();
$(el).text(name);
kanban.inEditMode = false;
addEditItemButton(framework, kanban);
return;
save();
}
});
$input.on('change keyup', function () {
@@ -540,6 +625,12 @@ define([
}
var boardId = $(el).closest('.kanban-board').attr("data-id");
kanban.inEditMode = boardId;
setTimeout(function () {
// Make sure the click is sent after the "blur" in case we move from a card to another
onCursorUpdate.fire({
board: boardId
});
});
var name = $(el).text();
$(el).html('');
@@ -559,6 +650,7 @@ define([
kanban.onChange();
// Unlock edit mode
kanban.inEditMode = false;
onCursorUpdate.fire({});
};
$input.blur(save);
$input.keydown(function (e) {
@@ -571,8 +663,7 @@ define([
if (e.which === 27) {
e.preventDefault();
e.stopPropagation();
$(el).text(name);
kanban.inEditMode = false;
save();
return;
}
});
@@ -604,6 +695,7 @@ define([
var save = function () {
$item.remove();
kanban.inEditMode = false;
onCursorUpdate.fire({});
if (!$input.val()) { return; }
var id = Util.createRandomInteger();
var item = {
@@ -630,6 +722,7 @@ define([
e.stopPropagation();
$item.remove();
kanban.inEditMode = false;
onCursorUpdate.fire({});
return;
}
});
@@ -638,6 +731,9 @@ define([
return DiffMd.render(md, true, false);
},
addItemButton: true,
getTextColor: getTextColor,
getAvatar: getAvatar,
cursors: remoteCursors,
boards: boards
});
@@ -664,9 +760,6 @@ define([
});
var $container = $('#cp-app-kanban-content');
$container[0].onclick = function (e) {
console.warn(e);
};
var $cContainer = $('#cp-app-kanban-container');
var addControls = function () {
// Quick or normal mode
@@ -986,6 +1079,47 @@ define([
kanban = initKanban(framework);
});
var myCursor = {};
onCursorUpdate.reg(function (data) {
myCursor = data;
framework.updateCursor();
});
framework.onCursorUpdate(function (data) {
if (!data) { return; }
var id = data.id;
// Clear existing cursor
if (remoteCursors[id] && remoteCursors[id].clear) {
remoteCursors[id].clear();
}
delete remoteCursors[id];
var cursor = data.cursor;
if (data.leave || !cursor) { return; }
if (!cursor.item && !cursor.board) { return; }
// Add new cursor
var avatar = getAvatar(cursor);
var $item = $('.kanban-item[data-eid="'+cursor.item+'"]');
var $board = $('.kanban-board[data-id="'+cursor.board+'"]');
if ($item.length) {
remoteCursors[id] = cursor;
$item.find('.cp-kanban-cursors').append(avatar);
return;
}
if ($board.length) {
remoteCursors[id] = cursor;
$board.find('header .cp-kanban-cursors').append(avatar);
}
});
framework.onCursorUpdate(function () {
if (!editModal || !editModal.conflict) { return; }
editModal.conflict.setValue();
});
framework.setCursorGetter(function () {
return myCursor;
});
framework.start();
};