Display other users' cursor
This commit is contained in:
@@ -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();
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user