Merge branch 'animal-guests' into staging

This commit is contained in:
ansuz 2021-08-27 19:49:42 +05:30
commit 0ca482ec68
20 changed files with 293 additions and 88 deletions

View File

@ -4,7 +4,11 @@
@width: 30px
) {
@avatar-width: @width;
@avatar-font-size: @width / 1.2;
@avatar-font-size: @width / 1.8;
// scale animal avatar to be somewhat larger, because:
// 1. emojis are wider than most latin characters
// 2. they should occupy the width of two average characters
@avatar-font-size-animal: @avatar-font-size * (6/5);
}
.avatar_main(@width: 30px) {
--LessLoader_require: LessLoader_currentFile();
@ -40,7 +44,9 @@
color: @cp_avatar-fg;
font-size: @avatar-font-size;
font-size: var(--avatar-font-size);
text-transform: capitalize;
.animal {
font-size: @avatar-font-size-animal;
}
}
media-tag {
min-height: @avatar-width;

View File

@ -855,10 +855,15 @@
span {
text-align: center;
width: 100%;
font-size: 48px;
.avatar_vars(72px);
font-size: @avatar-font-size;
display: inline-flex;
justify-content: center;
align-items: center;
.animal {
font-size: @avatar-font-size-animal;
}
}
&.cp-avatar {
.avatar_main(64px);

View File

@ -3,7 +3,9 @@ define([
'/common/sframe-common-codemirror.js',
'/customize/messages.js',
'/bower_components/chainpad/chainpad.dist.js',
], function (Util, SFCodeMirror, Messages, ChainPad) {
'/common/inner/common-mediatag.js',
'/common/common-interface.js',
], function (Util, SFCodeMirror, Messages, ChainPad, MT, UI) {
var Markers = {};
/* TODO Known Issues
@ -38,7 +40,17 @@ define([
});
}
uid = Number(uid);
var name = Util.fixHTML(author.name || Messages.anonymous);
var name = Util.fixHTML(UI.getDisplayName(author.name));
var animal;
if ((!name || name === Messages.anonymous) && typeof(author.uid) === 'string') {
animal = MT.getPseudorandomAnimal(author.uid);
if (animal) {
name = animal + ' ' + Messages.anonymous;
} else {
name = Messages.anonymous;
}
}
var col = Util.hexToRGB(author.color);
var rgba = 'rgba('+col[0]+','+col[1]+','+col[2]+','+Env.opacity+');';
return Env.editor.markText(from, to, {
@ -520,7 +532,8 @@ define([
Env.authormarks.authors[Env.myAuthorId] = {
name: userData.name,
curvePublic: userData.curvePublic,
color: userData.color
color: userData.color,
uid: userData.uid,
};
if (!old || (old.name === userData.name && old.color === userData.color)) { return; }
return true;

View File

@ -208,5 +208,7 @@ define(function() {
// the driveless mode by changing the following value to "false"
AppConfig.allowDrivelessMode = true;
AppConfig.emojiAvatars = '🙈 🦀 🐞 🦋 🐬 🐋 🐢 🦉 🦆 🐧 🦡 🦘 🦨 🦦 🦥 🐼 🐻 🦝 🦓 🐄 🐷 🐐 🦙 🦒 🐘 🦏 🐁 🐹 🐰 🦫 🦔 🐨 🐱 🐺 👺 👹 👽 👾 🤖'.split(/\s+/);
return AppConfig;
});

View File

@ -41,6 +41,10 @@ define([
return e;
};
UI.getDisplayName = function (name) {
return (typeof(name) === 'string'? name: "").trim() || Messages.anonymous;
};
// FIXME almost everywhere this is used would also be
// a good candidate for sframe-common's getMediatagFromHref
UI.mediaTag = function (src, key) {

View File

@ -17,7 +17,8 @@ define([
edPublic: proxy.edPublic,
curvePublic: proxy.curvePublic,
notifications: Util.find(proxy, ['mailboxes', 'notifications', 'channel']),
avatar: proxy.profile && proxy.profile.avatar
avatar: proxy.profile && proxy.profile.avatar,
uid: proxy.uid, // XXX test without this and see if it breaks things
};
if (hash === false) { delete data.channel; }
return data;

View File

@ -156,9 +156,11 @@ define([
var icons = Object.keys(users).map(function (key, i) {
var data = users[key];
var name = data.displayName || data.name || Messages.anonymous;
var avatar = h('span.cp-usergrid-avatar.cp-avatar');
common.displayAvatar($(avatar), data.avatar, name);
var name = UI.getDisplayName(data.displayName || data.name);
var avatar = h('span.cp-usergrid-avatar.cp-avatar', {
'aria-hidden': true, // XXX aria
});
common.displayAvatar($(avatar), data.avatar, name, Util.noop, data.uid);
var removeBtn, el;
if (config.remove) {
removeBtn = h('span.fa.fa-times');
@ -1989,13 +1991,16 @@ define([
var $displayName = $userAdmin.find('.'+displayNameCls);
var $avatar = $userAdmin.find('> button .cp-dropdown-button-title');
var $avatar = $userAdmin.find('> button .cp-dropdown-button-title'); // XXX alt="User menu"
var loadingAvatar;
var to;
var oldUrl = '';
var oldUid;
var oldName;
var updateButton = function () {
var myData = metadataMgr.getUserData();
var privateData = metadataMgr.getPrivateData();
var uid = myData.uid;
if (!priv.plan && privateData.plan) {
config.$initBlock.empty();
metadataMgr.off('change', updateButton);
@ -2010,18 +2015,21 @@ define([
return;
}
loadingAvatar = true;
var newName = myData.name;
var newName = UI.getDisplayName(myData.name);
var url = myData.avatar;
$displayName.text(newName || Messages.anonymous);
if (accountName && oldUrl !== url) {
$displayName.text(newName);
if ((accountName && oldUrl !== url) || !accountName && uid !== oldUid || oldName !== newName) {
$avatar.html('');
Common.displayAvatar($avatar, url,
newName || Messages.anonymous, function ($img) {
Common.displayAvatar($avatar, url, newName, function ($img) {
oldUrl = url;
oldUid = uid;
oldName = newName;
$userAdmin.find('> button').removeClass('cp-avatar');
if ($img) { $userAdmin.find('> button').addClass('cp-avatar'); }
loadingAvatar = false;
});
// XXX alt="User menu"
}, uid);
return;
}
loadingAvatar = false;
@ -2303,6 +2311,7 @@ define([
var teams = Object.keys(privateData.teams).map(function (id) {
var data = privateData.teams[id];
var avatar = h('span.cp-creation-team-avatar.cp-avatar');
// We assume that teams always have a non-empty name, so we don't need a UID
common.displayAvatar($(avatar), data.avatar, data.name);
return h('div.cp-creation-team', {
'data-id': id,
@ -3106,7 +3115,7 @@ define([
var sframeChan = common.getSframeChannel();
var msg = data.content.msg;
var name = Util.fixHTML(msg.content.user.displayName) || Messages.anonymous;
var name = Util.fixHTML(UI.getDisplayName(msg.content.user.displayName));
var title = Util.fixHTML(msg.content.title);
var text = Messages._getKey('owner_add', [name, title]);
@ -3238,7 +3247,7 @@ define([
var sframeChan = common.getSframeChannel();
var msg = data.content.msg;
var name = Util.fixHTML(msg.content.user.displayName) || Messages.anonymous;
var name = Util.fixHTML(UI.getDisplayName(msg.content.user.displayName));
var title = Util.fixHTML(msg.content.title);
var text = Messages._getKey('owner_team_add', [name, title]);
@ -3353,13 +3362,15 @@ define([
var verified = h('p');
var $verified = $(verified);
name = UI.getDisplayName(name);
if (priv.friends && priv.friends[curve]) {
$verified.addClass('cp-notifications-requestedit-verified');
var f = priv.friends[curve];
$verified.append(h('span.fa.fa-certificate'));
var $avatar = $(h('span.cp-avatar')).appendTo($verified);
$verified.append(h('p', Messages._getKey('isContact', [f.displayName])));
common.displayAvatar($avatar, f.avatar, f.displayName);
name = UI.getDisplayName(f.displayName);
$verified.append(h('p', Messages._getKey('isContact', [name])));
common.displayAvatar($avatar, f.avatar, name, Util.noop, f.uid);
} else {
$verified.append(Messages._getKey('isNotContact', [name]));
}
@ -3369,7 +3380,7 @@ define([
UIElements.displayInviteTeamModal = function (common, data) {
var msg = data.content.msg;
var name = Util.fixHTML(msg.content.user.displayName) || Messages.anonymous;
var name = Util.fixHTML(UI.getDisplayName(msg.content.user.displayName));
var teamName = Util.fixHTML(Util.find(msg, ['content', 'team', 'metadata', 'name']) || '');
var verified = UIElements.getVerifiedFriend(common, msg.author, name);
@ -3453,7 +3464,8 @@ define([
name: f.displayName,
curvePublic: f.curvePublic,
profile: f.profile,
notifications: f.notifications
notifications: f.notifications,
uid: f.uid,
};
});
};
@ -3552,7 +3564,7 @@ define([
};
// Set the value to receive from the autocomplete
var toInsert = function (data, key) {
var name = data.name.replace(/[^a-zA-Z0-9]+/g, "-");
var name = UI.getDisplayName(data.name.replace(/[^a-zA-Z0-9]+/g, "-"));
return "[@"+name+"|"+key+"]";
};
@ -3605,18 +3617,20 @@ define([
var avatar = h('span.cp-avatar', {
contenteditable: false
});
common.displayAvatar($(avatar), data.avatar, data.name);
var displayName = UI.getDisplayName(data.name);
common.displayAvatar($(avatar), data.avatar, displayName); // XXX
return h('span.cp-mentions', {
'data-curve': data.curvePublic,
'data-notifications': data.notifications,
'data-profile': data.profile,
'data-name': Util.fixHTML(data.name),
'data-name': Util.fixHTML(displayName),
'data-avatar': data.avatar || "",
}, [
avatar,
h('span.cp-mentions-name', {
contenteditable: false
}, data.name)
}, displayName)
]);
};
}
@ -3648,7 +3662,7 @@ define([
}).map(function (key) {
var data = sources[key];
return {
label: data.name,
label: UI.getDisplayName(data.name),
value: key
};
});
@ -3683,10 +3697,12 @@ define([
var obj = sources[key];
if (!obj) { return; }
var avatar = h('span.cp-avatar');
common.displayAvatar($(avatar), obj.avatar, obj.name);
var displayName = UI.getDisplayName(obj.name);
common.displayAvatar($(avatar), obj.avatar, displayName, Util.noop, obj.uid);
var li = h('li.cp-autocomplete-value', [
avatar,
h('span', obj.name)
h('span', displayName),
]);
return $(li).appendTo(ul);
};

View File

@ -171,7 +171,7 @@ define([
if (!Object.keys(_friends).length) {
var friendText;
if (!friendKeys.length) {
console.error(UIElements.noContactsMessage(common));
//console.error(UIElements.noContactsMessage(common));
var findContacts = UIElements.noContactsMessage(common);
friendText = h('span.cp-app-prop-content',
findContacts.content
@ -772,7 +772,8 @@ define([
if (friend.edPublic !== ed || c === 'me') { return; }
_owners[friend.edPublic] = {
name: friend.displayName,
avatar: friend.avatar
avatar: friend.avatar,
uid: friend.uid,
};
return true;
})) {
@ -782,6 +783,11 @@ define([
_owners[ed] = {
avatar: '?',
name: Messages.owner_unknownUser,
// TODO a possible enhancement is to use data from the context
// ie. if you have opened the access modal from within the pad
// its owner might be present or they might have left some data
// in the pad itself (as is the case of the uid in rich text comments)
// TODO or just implement "Acquaintances"
};
strangers++;
});

View File

@ -6,12 +6,13 @@ define([
'/common/hyperscript.js',
'/common/media-tag.js',
'/customize/messages.js',
'/customize/application_config.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
'/bower_components/croppie/croppie.min.js',
'/bower_components/file-saver/FileSaver.min.js',
'css!/bower_components/croppie/croppie.css',
], function ($, Util, Hash, UI, h, MediaTag, Messages) {
], function ($, Util, Hash, UI, h, MediaTag, Messages, AppConfig) {
var MT = {};
var Nacl = window.nacl;
@ -43,9 +44,16 @@ define([
});
};
var animal_avatars = {};
MT.getCursorAvatar = function (cursor) {
var uid = cursor.uid;
// TODO it would be nice to have "{0} is editing" instead of just their name
var html = '<span class="cp-cursor-avatar">';
html += (cursor.avatar && avatars[cursor.avatar]) || '';
if (cursor.avatar && avatars[cursor.avatar]) {
html += avatars[cursor.avatar];
} else if (animal_avatars[uid]) {
html += animal_avatars[uid] + ' ';
}
html += Util.fixHTML(cursor.name) + '</span>';
return html;
};
@ -79,12 +87,68 @@ define([
});
};
MT.displayAvatar = function (common, $container, href, name, _cb) {
// https://emojipedia.org/nature/
var ANIMALS = AppConfig.emojiAvatars || [];
var getRandomAnimal = function () { // XXX should never actually happen?
if (!ANIMALS.length) { return ''; }
return ANIMALS[Math.floor(Math.random() * ANIMALS.length)];
};
var getPseudorandomAnimal = MT.getPseudorandomAnimal = function (seed) {
if (!ANIMALS.length) { return ''; }
if (typeof(seed) !== 'string') { return getRandomAnimal(); }
seed = seed.replace(/\D/g, '').slice(0, 10); // XXX possible optimization for on-wire uid
seed = parseInt(seed);
if (!seed) { return getRandomAnimal(); }
return ANIMALS[seed % ANIMALS.length] || '';
};
var getPrettyInitials = MT.getPrettyInitials = function (name) {
var parts = name.split(/\s+/);
var text;
if (parts.length > 1) {
text = parts.slice(0, 2).map(Util.getFirstCharacter).join('');
} else {
text = Util.getFirstCharacter(name);
var second = Util.getFirstCharacter(name.replace(text, ''));
if (second && second !== '?') {
text += second;
}
}
return text;
};
MT.displayAvatar = function (common, $container, href, name, _cb, uid) {
var cb = Util.once(Util.mkAsync(_cb || function () {}));
var displayDefault = function () {
var text = Util.getFirstCharacter(name || Messages.anonymous);
var $avatar = $('<span>', {'class': 'cp-avatar-default'}).text(text);
var animal_avatar;
if (uid && animal_avatars[uid]) {
animal_avatar = animal_avatars[uid];
}
name = UI.getDisplayName(name);
var text;
if (ANIMALS.length && name === Messages.anonymous && uid) {
if (animal_avatar) {
text = animal_avatar;
} else {
text = animal_avatar = getPseudorandomAnimal(uid);
}
} else {
text = getPrettyInitials(name);
}
var $avatar = $('<span>', {
'class': 'cp-avatar-default' + (animal_avatar? ' animal': ''),
// XXX prevents screenreaders from trying to describe this
alt: '',
'aria-hidden': true,
}).text(text);
$container.append($avatar);
if (uid && animal_avatar) {
animal_avatars[uid] = animal_avatar;
}
if (cb) { cb(); }
};
if (!window.Symbol) { return void displayDefault(); } // IE doesn't have Symbol
@ -97,6 +161,7 @@ define([
return void cb($el);
}
var centerImage = function ($img, $image) {
var img = $image[0];
var w = img.width;
@ -131,7 +196,7 @@ define([
var $img = $(mt).appendTo($container);
MT.displayMediatagImage(common, $img, function (err, $image) {
if (err) { return void console.error(err); }
centerImage($img, $image);
centerImage($img, $image); // XXX add alt="" (unless the media-tag has an alt attr)
});
});
}

View File

@ -73,6 +73,7 @@ var factory = function () {
* @param {object} cfg Object {Plugins, allowed, download, pdf} containing infos about plugins
* @param {function} cb Callback function: (err, pluginElement) => {}
*/
// XXX add alt attributes if present in metadata
text: function (metadata, url, content, cfg, cb) {
var plainText = document.createElement('div');
plainText.className = "plain-text-reader";

View File

@ -190,9 +190,11 @@ define([
markup.message = function (msg) {
if (msg.type !== 'MSG') { return; }
var curvePublic = msg.author;
// FIXME this assignment looks like it has some holes in its logic
// but I'm scared to touch it because it looks like it was hacked to fix some bugs
var name = (typeof msg.name !== "undefined" || !contactsData[msg.author]) ?
(msg.name || Messages.anonymous) :
contactsData[msg.author].displayName;
contactsData[msg.author].displayName || Messages.anonymous;
var d = msg.time ? new Date(msg.time) : undefined;
var day = d ? d.toLocaleDateString() : '';
var hour = d ? d.toLocaleTimeString() : '';
@ -239,7 +241,7 @@ define([
});
var chan = state.channels[id];
var displayName = chan.name;
var displayName = UI.getDisplayName(chan.name || chan.displayName);
var fetching = false;
var $moreHistory = $(moreHistory).click(function () {
@ -364,7 +366,7 @@ define([
avatars[friend.avatar] = $img[0].outerHTML;
}
$(rightCol).insertAfter($avatar);
});
}, friend.uid);
}
var sending = false;
@ -544,7 +546,7 @@ define([
title: Messages.contacts_online
});
var rightCol = h('span.cp-app-contacts-right-col', [
h('span.cp-app-contacts-name', [room.name]),
h('span.cp-app-contacts-name', [room.isFriendChat? UI.getDisplayName(room.name): room.name]),
h('span.cp-app-contacts-icons', [
room.isFriendChat ? mute : undefined,
room.isFriendChat ? unmute : undefined,
@ -609,7 +611,7 @@ define([
avatars[friendData.avatar] = $img[0].outerHTML;
}
$room.append(rightCol);
});
}, friendData.uid);
}
$room.append(status);
return $room;
@ -631,9 +633,9 @@ define([
var el_message = markup.message(message);
if (message.type === 'MSG') {
var name = typeof message.name !== "undefined" ?
(message.name || Messages.anonymous) :
contactsData[message.author].displayName;
var name = UI.getDisplayName(typeof message.name !== "undefined" ?
message.name:
contactsData[message.author].displayName);
common.notify({
title: name,
msg: message.text,
@ -826,6 +828,11 @@ define([
}
};
/* The following block is for a disabled feature which allows users to switch
between pad chat (when in the context of a pad) and direct chats with their
contacts.
*/
/*
common.getMetadataMgr().onTitleChange(function () {
var padChat = common.getPadChat();
var md = common.getMetadataMgr().getMetadata();
@ -839,11 +846,14 @@ define([
$lAvatar.find('.cp-avatar-default, media-tag').remove();
var $div = $('<div>');
// There should always be a title here (defaultTitle if nothing else)
// so we don't ever need to supply a uid for an animal avatar
common.displayAvatar($div, null, name, function () {
$mAvatar.html($div.html());
$lAvatar.find('.cp-app-contacts-right-col').before($div.html());
});
});
*/
// TODO room
// var onJoinRoom
@ -878,7 +888,7 @@ define([
h('i.fa.fa-bell'),
Messages.contacts_unmute || 'unmute'
]);
common.displayAvatar($(avatar), data.avatar, data.name);
common.displayAvatar($(avatar), data.avatar, data.name, Util.noop, data.uid);
$(button).click(function () {
unmuteUser(curve, button);
execCommand('UNMUTE_USER', curve, function (e, data) {
@ -894,7 +904,7 @@ define([
});
return h('div.cp-contacts-muted-user', [
h('span', avatar),
h('span', data.name),
h('span', UI.getDisplayName(data.name)),
button
]);
});

View File

@ -187,6 +187,7 @@ define([
data.color = Util.find(proxy, ['settings', 'general', 'cursor', 'color']);
data.name = proxy[Constants.displayNameKey] || ctx.store.noDriveName || Messages.anonymous;
data.avatar = Util.find(proxy, ['profile', 'avatar']);
data.uid = Util.find(proxy, ['uid']) || ctx.store.noDriveUid;
c.cursor = data;
sendMyCursor(ctx, client);
cb();

View File

@ -249,11 +249,20 @@ define([
if (existing.indexOf(n) !== -1) { n = 0; }
return n;
};
funcs.getAuthorId = function(authors, curve) {
funcs.getAuthorId = function(authors, curve, tokenId) {
var existing = Object.keys(authors || {}).map(Number);
if (!funcs.isLoggedIn()) { return authorUid(existing); }
var uid;
var loggedIn = funcs.isLoggedIn();
if (!loggedIn && !tokenId) { return authorUid(existing); }
if (!loggedIn) {
existing.some(function (id) {
var author = authors[id];
if (!author || author.uid !== tokenId) { return; }
uid = Number(id);
return true;
});
return uid || authorUid(existing);
}
existing.some(function(id) {
var author = authors[id] || {};
if (author.curvePublic !== curve) { return; }

View File

@ -356,6 +356,8 @@ MessengerUI, Messages, Pages) {
});
}
if (data.profile) {
// XXX title to visit their profile "Visit {0}'s profile"
// Messages.contacts_info3 "Double-click their icon to view their profile",
$span.addClass('cp-userlist-clickable');
$span.click(function () {
Common.openURL(origin+'/profile/#' + data.profile);
@ -363,7 +365,7 @@ MessengerUI, Messages, Pages) {
}
Common.displayAvatar($span, data.avatar, name, function () {
$span.append($rightCol);
});
}, data.uid);
$span.data('uid', data.uid);
$editUsersList.append($span);
});
@ -1215,18 +1217,31 @@ MessengerUI, Messages, Pages) {
}
};
var getFancyGuestName = function (name, uid) {
name = UI.getDisplayName(name);
if (name === Messages.anonymous && uid) {
var animal = MT.getPseudorandomAnimal(uid);
if (animal) {
name = animal + ' ' + name;
}
}
return name;
};
// Notifications
var initNotifications = function (toolbar, config) {
// Display notifications when users are joining/leaving the session
var oldUserData;
if (!config.metadataMgr) { return; }
var metadataMgr = config.metadataMgr;
var notify = function(type, name, oldname) {
var notify = function(type, name, oldname, uid) {
if (toolbar.isAlone) { return; }
// type : 1 (+1 user), 0 (rename existing user), -1 (-1 user)
if (typeof name === "undefined") { return; }
name = name || Messages.anonymous;
if (Config.disableUserlistNotifications) { return; }
name = getFancyGuestName(name, uid);
oldname = getFancyGuestName(oldname, uid);
switch(type) {
case 1:
UI.log(Messages._getKey("notifyJoined", [name]));
@ -1275,7 +1290,7 @@ MessengerUI, Messages, Pages) {
delete oldUserData[u];
if (temp && newdata[userNetfluxId] && temp.uid === newdata[userNetfluxId].uid) { return; }
if (userPresent(u, temp, newdata || oldUserData) < 1) {
notify(-1, temp.name);
notify(-1, temp.name, undefined, temp.uid);
}
}
}
@ -1295,10 +1310,10 @@ MessengerUI, Messages, Pages) {
if (typeof oldUserData[k] === "undefined") {
// if the same uid is already present in the userdata, don't notify
if (!userPresent(k, newdata[k], oldUserData)) {
notify(1, newdata[k].name);
notify(1, newdata[k].name, undefined, newdata[k].uid);
}
} else if (oldUserData[k].name !== newdata[k].name) {
notify(0, newdata[k].name, oldUserData[k].name);
notify(0, newdata[k].name, oldUserData[k].name, newdata[k].uid);
}
}
}

View File

@ -159,6 +159,15 @@
margin-right: 5px;
.tools_unselectable();
cursor: default;
&.cp-cursor.cp-tippy-html {
.avatar_vars(20px);
background-color: var(--red);
// XXX figure out how to inherit this from avatar.less
font-size: @avatar-font-size; //var(11px; // 20px / 1.8 as per avatar.less..
&.animal {
font-size: @avatar-font-size-animal; //14px; // 20px / 1.8 * (6/5)...
}
}
}
}
.kanban-item {

View File

@ -97,16 +97,28 @@ define([
// Tippy
var html = MT.getCursorAvatar(cursor);
var l = Util.getFirstCharacter(cursor.name || Messages.anonymous);
var name = UI.getDisplayName(cursor.name);
var l; // label?
var animal = '';
if (cursor.name === Messages.anonymous && typeof(cursor.uid) === 'string') {
l = MT.getPseudorandomAnimal(cursor.uid);
if (l) {
animal = '.animal';
}
}
if (!l) {
l = MT.getPrettyInitials(name);
}
var text = '';
if (cursor.color) {
text = 'color:'+getTextColor(cursor.color)+';';
text = 'background-color:' + cursor.color + '; color:'+getTextColor(cursor.color)+';';
}
var avatar = h('span.cp-cursor.cp-tippy-html', {
style: "background-color: " + (cursor.color || 'red') + ";"+text,
var avatar = h('span.cp-cursor.cp-tippy-html' + animal, {
style: text,
'data-cptippy-html': true,
title: html
title: html,
}, l);
if (!noClear) {
cursor.clear = function () {
@ -1295,12 +1307,12 @@ define([
// 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;
}
var $board = $('.kanban-board[data-id="'+cursor.board+'"]');
if ($board.length) {
remoteCursors[id] = cursor;
$board.find('header .cp-kanban-cursors').append(avatar);

View File

@ -43,18 +43,21 @@ define([
var canonicalize = function(t) { return t.replace(/\r\n/g, '\n'); };
var getAuthorId = function(Env, curve) {
return Env.common.getAuthorId(Env.comments.authors, curve);
var getAuthorId = function(Env, curve, uid) {
return Env.common.getAuthorId(Env.comments.authors, curve, uid);
};
// Return the author ID and add/update the data for registered users
// Return the username for unregistered users
// Return the author ID and add/update user data
// associate data with a curvePublic for registered users and the uid otherwise
var updateAuthorData = function(Env, onChange) {
var userData = Env.metadataMgr.getUserData();
var myAuthorId;
if (!Env.common.isLoggedIn()) {
return userData.name;
myAuthorId = getAuthorId(Env, undefined, userData.uid);
} else {
myAuthorId = getAuthorId(Env, userData.curvePublic);
}
var myAuthorId = getAuthorId(Env, userData.curvePublic);
var data = Env.comments.authors[myAuthorId] = Env.comments.authors[myAuthorId] || {};
var old = Sortify(data);
data.name = userData.name;
@ -62,6 +65,8 @@ define([
data.profile = userData.profile;
data.curvePublic = userData.curvePublic;
data.notifications = userData.notifications;
data.uid = userData.uid;
if (typeof(onChange) === "function" && Sortify(data) !== old) {
onChange();
}
@ -82,6 +87,9 @@ define([
var userData = Env.metadataMgr.getUserData();
var privateData = Env.metadataMgr.getPrivateData();
var others = {};
// XXX mentioned users should be excluded from the list of notified recipients to avoid notifying them twice
// Get all the other registered users with a mailbox
thread.m.forEach(function(obj) {
var u = obj.u;
@ -93,7 +101,8 @@ define([
curvePublic: author.curvePublic,
comment: obj.m,
content: obj.v,
notifications: author.notifications
notifications: author.notifications,
uid: author.uid,
};
});
// Send the notification
@ -146,7 +155,7 @@ define([
'aria-required': true,
contenteditable: true,
});
Env.common.displayAvatar($(avatar), userData.avatar, name);
Env.common.displayAvatar($(avatar), userData.avatar, name, Util.noop, userData.uid);
var cancel = h('button.btn.btn-cancel', {
tabindex: 1
@ -224,7 +233,9 @@ define([
if (Env.common.isLoggedIn()) {
var authors = {};
Object.keys((Env.comments && Env.comments.authors) ||  {}).forEach(function(id) {
Object.keys((Env.comments && Env.comments.authors) ||  {})
.filter(function (id) { return Util.find(Env, ['commments', 'authors', id, 'curvePublic']); })
.forEach(function(id) {
var obj = Util.clone(Env.comments.authors[id]);
authors[obj.curvePublic] = obj;
});
@ -369,7 +380,7 @@ define([
var name = Util.fixHTML(author.name || Messages.anonymous);
var date = new Date(msg.t);
var avatar = h('span.cp-avatar');
Env.common.displayAvatar($(avatar), author.avatar, name);
Env.common.displayAvatar($(avatar), author.avatar, name, Util.noop, author.uid);
if (author.profile) {
$(avatar).click(function(e) {
Env.common.openURL(Hash.hashToHref(author.profile, 'profile'));
@ -393,7 +404,7 @@ define([
}
cleanMentions($el);
var avatar = h('span.cp-avatar');
Env.common.displayAvatar($(avatar), avatarUrl, name);
Env.common.displayAvatar($(avatar), avatarUrl, name, Util.noop, author.uid);
$el.append([
avatar,
h('span.cp-mentions-name', name)

View File

@ -3,7 +3,9 @@ define([
'/common/common-ui-elements.js',
'/common/common-interface.js',
'/bower_components/chainpad/chainpad.dist.js',
], function ($, UIElements, UI, ChainPad) {
'/customize/messages.js',
'/common/inner/common-mediatag.js',
], function ($, UIElements, UI, ChainPad, Messages, MT) {
var Cursor = {};
Cursor.isCursor = function (el) {
@ -40,8 +42,17 @@ define([
var cursors = {};
// XXX despite the name of this function this doesn't actually render as a tippy tooltip
// that means that emojis will use the system font that shows up in native tooltips
// so this might be of limited value/aesthetic appeal compared to other apps' cursors
var makeTippy = function (cursor) {
return cursor.name;
if (typeof(cursor.uid) === 'string' && (!cursor.name || cursor.name === Messages.anonymous)) {
var animal = MT.getPseudorandomAnimal(cursor.uid);
if (animal) {
return animal + ' ' + Messages.anonymous;
}
}
return cursor.name || Messages.anonymous;
};
var makeCursor = function (id, cursor) {

View File

@ -349,7 +349,7 @@ define([
$('<img>', {
src: '/customize/images/avatar.png',
title: Messages.profile_avatar,
alt: 'Avatar'
alt: 'Avatar' // XXX translate this "Default profile picture"
}).appendTo($span);
return;
}
@ -391,7 +391,7 @@ define([
}, function () {
sframeChan.query("Q_PROFILE_AVATAR_ADD", data.url, function (err, err2) {
if (err || err2) { return void UI.log(err || err2); }
displayAvatar(data.url);
displayAvatar(data.url); // XXX add "Profile picture"
});
});
};

View File

@ -693,6 +693,8 @@ define([
redrawRoster(common);
});
};
var getDisplayName = UI.getDisplayName;
var makeMember = function (common, data, me, roster) {
if (!data.curvePublic) { return; }
@ -701,11 +703,12 @@ define([
return user.role === "OWNER" && user.curvePublic !== me.curvePublic && !user.pendingOwner;
});
var displayName = getDisplayName(data.displayName);
// Avatar
var avatar = h('span.cp-avatar.cp-team-member-avatar');
common.displayAvatar($(avatar), data.avatar, data.displayName);
common.displayAvatar($(avatar), data.avatar, displayName, Util.noop, data.uid);
// Name
var name = h('span.cp-team-member-name', data.displayName);
var name = h('span.cp-team-member-name', displayName);
if (data.pendingOwner) {
$(name).append(h('em', {
title: Messages.team_pendingOwnerTitle
@ -789,7 +792,7 @@ define([
title: Messages.team_rosterKick
});
$(remove).click(function () {
UI.confirm(Messages._getKey('team_kickConfirm', [Util.fixHTML(data.displayName)]), function (yes) {
UI.confirm(Messages._getKey('team_kickConfirm', [Util.fixHTML(displayName)]), function (yes) {
if (!yes) { return; }
APP.module.execCommand('REMOVE_USER', {
pending: data.pending,
@ -1073,6 +1076,9 @@ define([
metadata: obj
}, function () {
$avatar.empty();
// the UI is not supposed to allow admins to remove team names
// so we expect that it will be there. Failing that the initials
// from the default name will be displayed
common.displayAvatar($avatar, data.url);
});
});
@ -1191,10 +1197,11 @@ define([
var displayUser = function (common, data) {
var avatar = h('span.cp-teams-invite-from-avatar.cp-avatar');
common.displayAvatar($(avatar), data.avatar, data.displayName);
var name = getDisplayName(data.displayName);
common.displayAvatar($(avatar), data.avatar, name);
return h('div.cp-teams-invite-from-author', [
avatar,
h('span.cp-teams-invite-from-name', data.displayName)
h('span.cp-teams-invite-from-name', name)
]);
};
@ -1319,20 +1326,21 @@ define([
nThen(function (waitFor) {
// Get preview content.
sframeChan.query('Q_ANON_GET_PREVIEW_CONTENT', { seeds: seeds }, waitFor(function (err, json) {
if (json && (json.error || !Object.keys(json).length)) {
if (json && (json.error || !Object.keys(json).length)) { // XXX team invite links are triggering this every time for me?
$(errorBlock).text(Messages.team_inviteInvalidLinkError).show();
waitFor.abort();
$div.empty();
return;
}
// XXX nothing guarantees that author, teamName, or message exist in json
$div.empty();
$div.append(h('div.cp-teams-invite-from', [
Messages.team_inviteFrom || 'From:',
Messages.team_inviteFrom,
displayUser(common, json.author)
]));
$div.append(UI.setHTML(h('p.cp-teams-invite-to'),
Messages._getKey('team_inviteFromMsg',
[Util.fixHTML(json.author.displayName),
[Util.fixHTML(getDisplayName(json.author.displayName)),
Util.fixHTML(json.teamName)])));
if (json.message) {
$div.append(h('div.cp-teams-invite-message', json.message));
@ -1449,10 +1457,10 @@ define([
// Update the name in the user menu
var $displayName = $bar.find('.' + Toolbar.constants.username);
metadataMgr.onChange(function () {
var name = metadataMgr.getUserData().name || Messages.anonymous;
var name = getDisplayName(metadataMgr.getUserData().name);
$displayName.text(name);
});
$displayName.text(user.name || Messages.anonymous);
$displayName.text(getDisplayName(user.name));
// Load the Team module
var onEvent = function (obj) {