Merge branch 'staging' into inviteUI

pull/1/head
yflory 5 years ago
commit e6e1ef72e7

@ -103,7 +103,7 @@ define([
])*/ ])*/
]) ])
]), ]),
h('div.cp-version-footer', "CryptPad v3.7.0 (HimalayanQuail)") h('div.cp-version-footer', "CryptPad v3.8.0 (IsolobodonPortoricensis)")
]); ]);
}; };

@ -3,6 +3,7 @@
@import (reference) "./variables.less"; @import (reference) "./variables.less";
@import (reference) "./avatar.less"; @import (reference) "./avatar.less";
@import (reference) "./tools.less"; @import (reference) "./tools.less";
@import (reference) "./buttons.less";
.alertify_main() { .alertify_main() {
--LessLoader_require: LessLoader_currentFile(); --LessLoader_require: LessLoader_currentFile();
@ -225,28 +226,6 @@
::-ms-input-placeholder { /* Microsoft Edge */ ::-ms-input-placeholder { /* Microsoft Edge */
color: @cryptpad_color_grey; color: @cryptpad_color_grey;
} }
input:not(.form-control), textarea {
background-color: @alertify-input-fg;
color: @cryptpad_text_col;
border: 1px solid @alertify-input-bg;
margin-bottom: @alertify_padding-base;
width: 100%;
font-size: 100%;
padding: @alertify_padding-base;
&[readonly] {
background-color: @alertify-light-bg;
color: @cryptpad_text_col;
border-color: @alertify-light-bg;
}
}
textarea {
overflow: hidden;
padding: 8px;
&[readonly] {
resize: none;
}
}
span.cp-password-container { span.cp-password-container {
display: flex; display: flex;
@ -281,99 +260,17 @@
} }
} }
button:not(.btn):not(.pure-button):not(.md-button):not(.mdl-button) { .buttons_main();
input:not(.form-control), textarea {
margin-bottom: 15px;
}
background-color: @colortheme_alertify-cancel; button {
box-sizing: border-box;
position: relative;
outline: 0;
display: inline-block; display: inline-block;
align-items: center; position: relative;
padding: 0 6px;
margin: 6px 8px; margin: 6px 8px;
line-height: 36px;
min-height: 36px; min-height: 36px;
white-space: nowrap;
min-width: 88px; min-width: 88px;
text-align: center;
text-transform: uppercase;
font-size: 14px;
text-decoration: none;
cursor: pointer;
border-radius: 0;
color: @alertify-btn-fg;
border: 1px solid @alertify-btn-fg;
&.no-margin {
margin: 0;
}
&:hover, &:active {
background-color: @alertify-light-bg;
}
&.safe, &.danger {
color: @colortheme_old-base;
white-space: normal;
font-weight: bold;
}
&.danger {
background-color: @colortheme_alertify-red;
border-color: @colortheme_alertify-red-border;
color: @colortheme_alertify-red-color;
&:hover, &:active {
background-color: contrast(@colortheme_modal-bg, darken(@colortheme_alertify-red, 10%), lighten(@colortheme_alertify-red, 10%));
}
}
&.safe {
background-color: @colortheme_alertify-green;
border-color: @colortheme_alertify-green-border;
color: @colortheme_alertify-green-color;
&:hover, &:active {
background-color: contrast(@colortheme_modal-bg, darken(@colortheme_alertify-green, 10%), lighten(@colortheme_alertify-green, 10%));
}
}
&.primary {
background-color: @colortheme_alertify-primary;
color: @colortheme_alertify-primary-text;
border-color: @colortheme_alertify-primary-border;
font-weight: bold;
&:hover, &:active {
background-color: contrast(@colortheme_modal-bg, darken(@colortheme_alertify-primary, 10%), lighten(@colortheme_alertify-primary, 10%));
}
}
&.cancel {
border-color: @colortheme_alertify-cancel-border;
color: @colortheme_alertify-cancel-border;
&:hover, &:hover {
background-color: fade(@colortheme_alertify-cancel-border, 25%);
}
}
&:focus {
//border: 1px dotted @alertify-base;
box-shadow: 0px 0px 5px @colortheme_alertify-primary;
outline: none;
}
&::-moz-focus-inner {
border: 0;
}
&:disabled {
cursor: not-allowed !important;
background-color: @colortheme_alertify-disabled;
color: @colortheme_alertify-disabled-text;
border-color: @colortheme_alertify-disabled-border;
&:hover, &:active {
background-color: @colortheme_alertify-disabled;
}
}
} }
nav { nav {

@ -0,0 +1,129 @@
@import (reference) "./colortheme-all.less";
@import (reference) "./variables.less";
.buttons_main() {
@alertify-fore: @colortheme_modal-fg;
@alertify-btn-fg: @alertify-fore;
@alertify-light-bg: fade(@alertify-fore, 25%);
@alertify_padding-base: @variables_padding;
@alertify-input-bg: @colortheme_modal-input;
@alertify-input-fg: @colortheme_modal-input-fg;
input:not(.form-control), textarea {
background-color: @alertify-input-fg;
color: @cryptpad_text_col;
border: 1px solid @alertify-input-bg;
width: 100%;
font-size: 100%;
padding: @alertify_padding-base;
&[readonly] {
background-color: @alertify-light-bg;
color: @cryptpad_text_col;
border-color: @alertify-input-fg;
}
}
textarea {
overflow: hidden;
padding: 8px;
&[readonly] {
resize: none;
}
}
button:not(.pure-button):not(.md-button):not(.mdl-button) {
background-color: @colortheme_alertify-cancel;
box-sizing: border-box;
outline: 0;
align-items: center;
padding: 0 6px;
line-height: 36px;
white-space: nowrap;
text-align: center;
text-transform: uppercase;
font-size: 14px;
text-decoration: none;
cursor: pointer;
border-radius: 0;
.fa {
margin-right: 0.2em;
}
color: @alertify-btn-fg;
border: 1px solid @alertify-btn-fg;
&.no-margin {
margin: 0;
}
&:hover, &:active {
background-color: lighten(@alertify-fore, 35%);
}
&.safe, &.danger, &.btn-safe, &.btn-danger {
color: @colortheme_old-base;
white-space: normal;
font-weight: bold;
}
&.danger, &.btn-danger {
background-color: @colortheme_alertify-red;
border-color: @colortheme_alertify-red-border;
color: @colortheme_alertify-red-color;
&:hover, &:active {
background-color: contrast(@colortheme_modal-bg, darken(@colortheme_alertify-red, 10%), lighten(@colortheme_alertify-red, 10%));
}
}
&.safe, &.btn-safe {
background-color: @colortheme_alertify-green;
border-color: @colortheme_alertify-green-border;
color: @colortheme_alertify-green-color;
&:hover, &:active {
background-color: contrast(@colortheme_modal-bg, darken(@colortheme_alertify-green, 10%), lighten(@colortheme_alertify-green, 10%));
}
}
&.primary, &.btn-primary {
background-color: @colortheme_alertify-primary;
color: @colortheme_alertify-primary-text;
border-color: @colortheme_alertify-primary-border;
font-weight: bold;
&:hover, &:active {
background-color: contrast(@colortheme_modal-bg, darken(@colortheme_alertify-primary, 10%), lighten(@colortheme_alertify-primary, 10%));
}
}
&.cancel, &.btn-cancel {
border-color: @colortheme_alertify-cancel-border;
color: @colortheme_alertify-cancel-border;
&:hover, &:hover {
background-color: fade(@colortheme_alertify-cancel-border, 25%);
}
}
&:focus {
//border: 1px dotted @alertify-base;
box-shadow: 0px 0px 5px @colortheme_alertify-primary;
outline: none;
}
&::-moz-focus-inner {
border: 0;
}
&:disabled {
cursor: not-allowed !important;
background-color: @colortheme_alertify-disabled;
color: @colortheme_alertify-disabled-text;
border-color: @colortheme_alertify-disabled-border;
&:hover, &:active {
background-color: @colortheme_alertify-disabled;
}
}
}
}

@ -7,6 +7,13 @@
text-align: center; text-align: center;
} }
media-tag:empty {
width: 100px;
height: 100px;
display: inline-block;
border: 1px solid #BBB;
}
media-tag img { media-tag img {
flex: 1; flex: 1;
max-height: 100% !important; max-height: 100% !important;

@ -78,6 +78,9 @@
.cp-app-contacts-name { .cp-app-contacts-name {
white-space: nowrap; white-space: nowrap;
} }
.cp-app-contacts-icons {
text-align: right;
}
} }
&:hover { &:hover {
background-color: rgba(0,0,0,0.3); background-color: rgba(0,0,0,0.3);
@ -89,6 +92,7 @@
.cp-app-contacts-remove { .cp-app-contacts-remove {
cursor: pointer; cursor: pointer;
width: 20px; width: 20px;
text-align: center;
&:hover { &:hover {
color: darken(@color, 20%); color: darken(@color, 20%);
} }
@ -121,8 +125,30 @@
} }
} }
} }
.cp-app-contacts-muted-button {
margin: 10px;
border: 0;
display: none;
order: 3;
.fa-bell-slash {
margin-right: 10px;
}
}
} }
} }
.cp-contacts-muted-table {
.cp-contacts-muted-user {
margin-bottom: 5px;
.cp-avatar {
margin-right: 10px;
}
button {
margin-right: 0px;
}
}
}
#cp-app-contacts-container.cp-app-contacts-inapp { #cp-app-contacts-container.cp-app-contacts-inapp {
#cp-app-contacts-friendlist { #cp-app-contacts-friendlist {
display: none; display: none;

@ -1,5 +1,6 @@
@import (reference) "/customize/src/less2/include/colortheme-all.less"; @import (reference) "/customize/src/less2/include/colortheme-all.less";
@import (reference) "/customize/src/less2/include/leftside-menu.less"; @import (reference) "/customize/src/less2/include/leftside-menu.less";
@import (reference) "/customize/src/less2/include/buttons.less";
@sidebar_button-width: 400px; @sidebar_button-width: 400px;
@ -95,9 +96,11 @@
} }
} }
margin-bottom: 20px; margin-bottom: 20px;
.buttons_main();
} }
[type="text"], [type="password"], button { [type="text"], [type="password"], button {
vertical-align: middle; vertical-align: middle;
min-width: 40px;
height: 40px; height: 40px;
box-sizing: border-box; box-sizing: border-box;
} }
@ -106,12 +109,12 @@
width: @sidebar_button-width; width: @sidebar_button-width;
input { input {
flex: 1; flex: 1;
border-radius: 0.25em 0 0 0.25em; //border-radius: 0.25em 0 0 0.25em;
border: 1px solid #adadad; border: 1px solid #adadad;
border-right: 0px; border-right: 0px;
} }
button { button {
border-radius: 0 0.25em 0.25em 0; //border-radius: 0 0.25em 0.25em 0;
//border: 1px solid #adadad; //border: 1px solid #adadad;
border-left: 0px; border-left: 0px;
} }
@ -119,6 +122,13 @@
&>div { &>div {
margin: 10px 0; margin: 10px 0;
} }
button.btn {
margin: 0 5px 0 0;
}
span.cp-password-container {
margin-bottom: 1px;
}
/*
button.btn { button.btn {
@button-bg: @colortheme_sidebar-button-bg; @button-bg: @colortheme_sidebar-button-bg;
@button-red-bg: @colortheme_sidebar-button-red-bg; @button-red-bg: @colortheme_sidebar-button-red-bg;
@ -126,6 +136,9 @@
background-color: @button-bg; background-color: @button-bg;
border-color: darken(@button-bg, 10%); border-color: darken(@button-bg, 10%);
color: white; color: white;
.fa {
margin-right: 0.2em;
}
&:hover { &:hover {
background-color: darken(@button-bg, 10%); background-color: darken(@button-bg, 10%);
} }
@ -146,6 +159,7 @@
} }
} }
} }
*/
} }
} }
} }

2
package-lock.json generated

@ -1,6 +1,6 @@
{ {
"name": "cryptpad", "name": "cryptpad",
"version": "3.7.0", "version": "3.8.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

@ -1,7 +1,7 @@
{ {
"name": "cryptpad", "name": "cryptpad",
"description": "realtime collaborative visual editor with zero knowlege server", "description": "realtime collaborative visual editor with zero knowlege server",
"version": "3.7.0", "version": "3.8.0",
"license": "AGPL-3.0+", "license": "AGPL-3.0+",
"repository": { "repository": {
"type": "git", "type": "git",

@ -98,6 +98,13 @@
max-height: 90vh; max-height: 90vh;
} }
} }
media-tag:empty {
width: 100px;
height: 100px;
display: inline-block;
border: 1px solid #BBB;
}
.markdown_main(); .markdown_main();
.cp-app-code-preview-empty { .cp-app-code-preview-empty {
display: none; display: none;

@ -592,6 +592,7 @@ define([
}]; }];
var modal = dialog.customModal(content, {buttons: buttons}); var modal = dialog.customModal(content, {buttons: buttons});
UI.openCustomModal(modal); UI.openCustomModal(modal);
return modal;
}; };
UI.log = function (msg) { UI.log = function (msg) {

@ -84,6 +84,7 @@ define([
var myData = createData(store.proxy, false); var myData = createData(store.proxy, false);
if (store.proxy.friends) { if (store.proxy.friends) {
store.proxy.friends.me = myData; store.proxy.friends.me = myData;
delete store.proxy.friends.me.channel;
} }
if (store.modules['team']) { if (store.modules['team']) {
store.modules['team'].updateMyData(myData); store.modules['team'].updateMyData(myData);

@ -4157,7 +4157,7 @@ define([
}; };
var content = h('div.cp-share-modal', [ var content = h('div.cp-share-modal', [
setHTML(h('p'), text) setHTML(h('p'), text),
]); ]);
UI.proposal(content, todo); UI.proposal(content, todo);
}; };

@ -435,8 +435,6 @@
return mediaObject; return mediaObject;
} }
mediaObject.tag.innerHTML = '<img style="width: 100px; height: 100px;">';
// Download the encrypted blob // Download the encrypted blob
download(src, function (err, u8Encrypted) { download(src, function (err, u8Encrypted) {
if (err) { if (err) {

@ -3,9 +3,10 @@ define([
'/customize/messages.js', '/customize/messages.js',
'/common/common-util.js', '/common/common-util.js',
'/common/common-interface.js', '/common/common-interface.js',
'/common/common-ui-elements.js',
'/common/hyperscript.js', '/common/hyperscript.js',
'/common/diffMarked.js', '/common/diffMarked.js',
], function ($, Messages, Util, UI, h, DiffMd) { ], function ($, Messages, Util, UI, UIElements, h, DiffMd) {
'use strict'; 'use strict';
var debug = console.log; var debug = console.log;
@ -13,6 +14,8 @@ define([
var MessengerUI = {}; var MessengerUI = {};
var mutedUsers = {};
var dataQuery = function (id) { var dataQuery = function (id) {
return '[data-key="' + id + '"]'; return '[data-key="' + id + '"]';
}; };
@ -67,8 +70,11 @@ define([
h('div.cp-app-contacts-category-content') h('div.cp-app-contacts-category-content')
]), ]),
h('div.cp-app-contacts-friends.cp-app-contacts-category', [ h('div.cp-app-contacts-friends.cp-app-contacts-category', [
h('div.cp-app-contacts-category-content'), h('button.cp-app-contacts-muted-button',[
h('h2.cp-app-contacts-category-title', Messages.contacts_friends), h('i.fa.fa-bell-slash'),
Messages.contacts_manageMuted
]),
h('div.cp-app-contacts-category-content.cp-contacts-friends')
]), ]),
h('div.cp-app-contacts-rooms.cp-app-contacts-category', [ h('div.cp-app-contacts-rooms.cp-app-contacts-category', [
h('div.cp-app-contacts-category-content'), h('div.cp-app-contacts-category-content'),
@ -184,7 +190,7 @@ define([
markup.message = function (msg) { markup.message = function (msg) {
if (msg.type !== 'MSG') { return; } if (msg.type !== 'MSG') { return; }
var curvePublic = msg.author; var curvePublic = msg.author;
var name = typeof msg.name !== "undefined" ? var name = (typeof msg.name !== "undefined" || !contactsData[msg.author]) ?
(msg.name || Messages.anonymous) : (msg.name || Messages.anonymous) :
contactsData[msg.author].displayName; contactsData[msg.author].displayName;
var d = msg.time ? new Date(msg.time) : undefined; var d = msg.time ? new Date(msg.time) : undefined;
@ -486,6 +492,20 @@ define([
} }
}; };
var unmuteUser = function (curve) {
execCommand('UNMUTE_USER', curve, function (e) {
if (e) { return void console.error(e); }
});
};
var muteUser = function (data) {
execCommand('MUTE_USER', {
curvePublic: data.curvePublic,
name: data.displayName || data.name,
avatar: data.avatar
}, function (e /*, removed */) {
if (e) { return void console.error(e); }
});
};
var removeFriend = function (curvePublic) { var removeFriend = function (curvePublic) {
execCommand('REMOVE_FRIEND', curvePublic, function (e /*, removed */) { execCommand('REMOVE_FRIEND', curvePublic, function (e /*, removed */) {
if (e) { return void console.error(e); } if (e) { return void console.error(e); }
@ -499,6 +519,21 @@ define([
title: room.name title: room.name
}); });
var curve;
if (room.isFriendChat) {
var __channel = state.channels[id];
curve = __channel.curvePublic;
}
var unmute = h('span.cp-app-contacts-remove.fa.fa-bell.cp-unmute-icon', {
title: Messages.contacts_unmute || 'unmute',
style: (curve && mutedUsers[curve]) ? undefined : 'display: none;'
});
var mute = h('span.cp-app-contacts-remove.fa.fa-bell-slash.cp-mute-icon', {
title: Messages.contacts_mute || 'mute',
style: (curve && mutedUsers[curve]) ? 'display: none;' : undefined
});
var remove = h('span.cp-app-contacts-remove.fa.fa-user-times', { var remove = h('span.cp-app-contacts-remove.fa.fa-user-times', {
title: Messages.contacts_remove title: Messages.contacts_remove
}); });
@ -511,8 +546,12 @@ define([
}); });
var rightCol = h('span.cp-app-contacts-right-col', [ var rightCol = h('span.cp-app-contacts-right-col', [
h('span.cp-app-contacts-name', [room.name]), h('span.cp-app-contacts-name', [room.name]),
room.isFriendChat ? remove : h('span.cp-app-contacts-icons', [
(room.isPadChat || room.isTeamChat) ? undefined : leaveRoom, room.isFriendChat ? mute : undefined,
room.isFriendChat ? unmute : undefined,
room.isFriendChat ? remove :
(room.isPadChat || room.isTeamChat) ? undefined : leaveRoom,
])
]); ]);
var friendData = room.isFriendChat ? userlist[0] : {}; var friendData = room.isFriendChat ? userlist[0] : {};
@ -523,23 +562,43 @@ define([
if (friendData.profile) { window.open(origin + '/profile/#' + friendData.profile); } if (friendData.profile) { window.open(origin + '/profile/#' + friendData.profile); }
}); });
$(unmute).on('click dblclick', function (e) {
e.stopPropagation();
var channel = state.channels[id];
if (!channel.isFriendChat) { return; }
var curvePublic = channel.curvePublic;
$(mute).show();
$(unmute).hide();
unmuteUser(curvePublic);
});
$(mute).on('click dblclick', function (e) {
e.stopPropagation();
var channel = state.channels[id];
if (!channel.isFriendChat) { return; }
var curvePublic = channel.curvePublic;
var friend = contactsData[curvePublic] || friendData;
$(mute).hide();
$(unmute).show();
muteUser(friend);
});
$(remove).click(function (e) { $(remove).click(function (e) {
e.stopPropagation(); e.stopPropagation();
var channel = state.channels[id]; var channel = state.channels[id];
if (!channel.isFriendChat) { return; } if (!channel.isFriendChat) { return; }
var curvePublic = channel.curvePublic; var curvePublic = channel.curvePublic;
var friend = contactsData[curvePublic] || friendData; var friend = contactsData[curvePublic] || friendData;
UI.confirm(Messages._getKey('contacts_confirmRemove', [ var content = h('div', [
Util.fixHTML(friend.name) UI.setHTML(h('p'), Messages._getKey('contacts_confirmRemove', [Util.fixHTML(friend.name)])),
]), function (yes) { ]);
UI.confirm(content, function (yes) {
if (!yes) { return; } if (!yes) { return; }
removeFriend(curvePublic, function (e) { removeFriend(curvePublic);
if (e) { return void console.error(e); }
});
// TODO remove friend from userlist ui // TODO remove friend from userlist ui
// FIXME seems to trigger EJOINED from netflux-websocket (from server); // FIXME seems to trigger EJOINED from netflux-websocket (from server);
// (tried to join a channel in which you were already present) // (tried to join a channel in which you were already present)
}, undefined, true); });
}); });
if (friendData.avatar && avatars[friendData.avatar]) { if (friendData.avatar && avatars[friendData.avatar]) {
@ -792,6 +851,62 @@ define([
// var onJoinRoom // var onJoinRoom
// var onLeaveRoom // var onLeaveRoom
var updateMutedList = function () {
execCommand('GET_MUTED_USERS', null, function (err, muted) {
if (err) { return void console.error(err); }
mutedUsers = muted;
var $button = $userlist.find('.cp-app-contacts-muted-button');
$('.cp-app-contacts-friend[data-user]')
.find('.cp-mute-icon').show();
$('.cp-app-contacts-friend[data-user]')
.find('.cp-unmute-icon').hide();
if (!muted || Object.keys(muted).length === 0) {
$button.hide();
return;
}
var rows = Object.keys(muted).map(function (curve) {
$('.cp-app-contacts-friend[data-user="'+curve+'"]')
.find('.cp-mute-icon').hide();
$('.cp-app-contacts-friend[data-user="'+curve+'"]')
.find('.cp-unmute-icon').show();
var data = muted[curve];
var avatar = h('span.cp-avatar');
var button = h('button', {
'data-user': curve
}, [
h('i.fa.fa-bell'),
Messages.contacts_unmute || 'unmute'
]);
UIElements.displayAvatar(common, $(avatar), data.avatar, data.name);
$(button).click(function () {
unmuteUser(curve, button);
execCommand('UNMUTE_USER', curve, function (e, data) {
if (e) { return void console.error(e); }
$(button).closest('div').remove();
if (!data) { $button.hide(); }
$('.cp-app-contacts-friend[data-user="'+curve+'"]')
.find('.cp-mute-icon').show();
});
});
return h('div.cp-contacts-muted-user', [
h('span', avatar),
h('span', data.name),
button
]);
});
var content = h('div', [
h('h4', Messages.contacts_mutedUsers || 'Muted users...'),
h('div.cp-contacts-muted-table', rows)
]);
$button.off('click');
$button.click(function () {
UI.alert(content);
}).show();
});
};
var ready = false; var ready = false;
var onMessengerReady = function () { var onMessengerReady = function () {
@ -806,6 +921,8 @@ define([
rooms.forEach(initializeRoom); rooms.forEach(initializeRoom);
}); });
updateMutedList();
$container.removeClass('cp-app-contacts-initializing'); $container.removeClass('cp-app-contacts-initializing');
}; };
@ -882,6 +999,10 @@ define([
onUpdateData(data); onUpdateData(data);
return; return;
} }
if (cmd === 'UPDATE_MUTED') {
updateMutedList();
return;
}
if (cmd === 'MESSAGE') { if (cmd === 'MESSAGE') {
onMessage(data); onMessage(data);
return; return;

@ -12,6 +12,13 @@ define([
var handlers = {}; var handlers = {};
var removeHandlers = {}; var removeHandlers = {};
var isMuted = function (ctx, data) {
var muted = ctx.store.proxy.mutedUsers || {};
var curvePublic = Util.find(data, ['msg', 'author']);
if (!curvePublic) { return false; }
return Boolean(muted[curvePublic]);
};
// Store the friend request displayed to avoid duplicates // Store the friend request displayed to avoid duplicates
var friendRequest = {}; var friendRequest = {};
handlers['FRIEND_REQUEST'] = function (ctx, box, data, cb) { handlers['FRIEND_REQUEST'] = function (ctx, box, data, cb) {
@ -21,6 +28,8 @@ define([
return void cb(true); return void cb(true);
} }
if (isMuted(ctx, data)) { return void cb(true); }
// Don't show duplicate friend request: if we already have a friend request // Don't show duplicate friend request: if we already have a friend request
// in memory from the same user, dismiss the new one // in memory from the same user, dismiss the new one
if (friendRequest[data.msg.author]) { return void cb(true); } if (friendRequest[data.msg.author]) { return void cb(true); }
@ -30,10 +39,22 @@ define([
// If the user is already in our friend list, automatically accept the request // If the user is already in our friend list, automatically accept the request
if (Messaging.getFriend(ctx.store.proxy, data.msg.author) || if (Messaging.getFriend(ctx.store.proxy, data.msg.author) ||
ctx.store.proxy.friends_pending[data.msg.author]) { ctx.store.proxy.friends_pending[data.msg.author]) {
delete ctx.store.proxy.friends_pending[data.msg.author];
Messaging.acceptFriendRequest(ctx.store, data.msg.content, function (obj) { Messaging.acceptFriendRequest(ctx.store, data.msg.content, function (obj) {
if (obj && obj.error) { if (obj && obj.error) {
return void cb(); return void cb();
} }
Messaging.addToFriendList({
proxy: ctx.store.proxy,
realtime: ctx.store.realtime,
pinPads: ctx.pinPads
}, data.msg.content, function (err) {
if (err) { console.error(err); }
if (ctx.store.messenger) {
ctx.store.messenger.onFriendAdded(data.msg.content);
}
});
ctx.updateMetadata();
cb(true); cb(true);
}); });
return; return;
@ -170,6 +191,8 @@ define([
var content = msg.content; var content = msg.content;
// content.name, content.title, content.href, content.password // content.name, content.title, content.href, content.password
if (isMuted(ctx, data)) { return void cb(true); }
var channel = Hash.hrefToHexChannelId(content.href, content.password); var channel = Hash.hrefToHexChannelId(content.href, content.password);
var parsed = Hash.parsePadUrl(content.href); var parsed = Hash.parsePadUrl(content.href);
var mode = parsed.hashData && parsed.hashData.mode || 'n/a'; var mode = parsed.hashData && parsed.hashData.mode || 'n/a';
@ -212,6 +235,9 @@ define([
supportMessage = true; supportMessage = true;
cb(); cb();
}; };
removeHandlers['SUPPORT_MESSAGE'] = function () {
supportMessage = false;
};
// Incoming edit rights request: add data before sending it to inner // Incoming edit rights request: add data before sending it to inner
handlers['REQUEST_PAD_ACCESS'] = function (ctx, box, data, cb) { handlers['REQUEST_PAD_ACCESS'] = function (ctx, box, data, cb) {
@ -220,6 +246,8 @@ define([
if (msg.author !== content.user.curvePublic) { return void cb(true); } if (msg.author !== content.user.curvePublic) { return void cb(true); }
if (isMuted(ctx, data)) { return void cb(true); }
var channel = content.channel; var channel = content.channel;
var res = ctx.store.manager.findChannel(channel); var res = ctx.store.manager.findChannel(channel);
@ -270,6 +298,9 @@ define([
var content = msg.content; var content = msg.content;
if (msg.author !== content.user.curvePublic) { return void cb(true); } if (msg.author !== content.user.curvePublic) { return void cb(true); }
if (isMuted(ctx, data)) { return void cb(true); }
if (!content.teamChannel && !(content.href && content.title && content.channel)) { if (!content.teamChannel && !(content.href && content.title && content.channel)) {
console.log('Remove invalid notification'); console.log('Remove invalid notification');
return void cb(true); return void cb(true);
@ -327,6 +358,9 @@ define([
var content = msg.content; var content = msg.content;
if (msg.author !== content.user.curvePublic) { return void cb(true); } if (msg.author !== content.user.curvePublic) { return void cb(true); }
if (isMuted(ctx, data)) { return void cb(true); }
if (!content.team) { if (!content.team) {
console.log('Remove invalid notification'); console.log('Remove invalid notification');
return void cb(true); return void cb(true);

@ -428,20 +428,21 @@ define([
} }
var channel = ctx.channels[data.channel]; var channel = ctx.channels[data.channel];
if (!channel) {
return void cb({error: "NO_SUCH_CHANNEL"});
}
// Unfriend with mailbox // Unfriend with mailbox
if (ctx.store.mailbox && data.curvePublic && data.notifications) { if (ctx.store.mailbox && data.curvePublic && data.notifications) {
Messaging.removeFriend(ctx.store, curvePublic, function (obj) { Messaging.removeFriend(ctx.store, curvePublic, function (obj) {
if (obj && obj.error) { return void cb({error:obj.error}); } if (obj && obj.error) { return void cb({error:obj.error}); }
ctx.updateMetadata();
cb(obj); cb(obj);
}); });
return; return;
} }
// Unfriend with channel // Unfriend with channel
if (!channel) {
return void cb({error: "NO_SUCH_CHANNEL"});
}
try { try {
var msg = [Types.unfriend, proxy.curvePublic, +new Date()]; var msg = [Types.unfriend, proxy.curvePublic, +new Date()];
var msgStr = JSON.stringify(msg); var msgStr = JSON.stringify(msg);
@ -458,6 +459,40 @@ define([
} }
}; };
var getAllClients = function (ctx) {
var all = [];
Array.prototype.push.apply(all, ctx.friendsClients);
Object.keys(ctx.channels).forEach(function (id) {
Array.prototype.push.apply(all, ctx.channels[id].clients);
});
return Util.deduplicateString(all);
};
var muteUser = function (ctx, data, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
var proxy = ctx.store.proxy;
var muted = proxy.mutedUsers = proxy.mutedUsers || {};
if (muted[data.curvePublic]) { return void cb(); }
muted[data.curvePublic] = data;
ctx.emit('UPDATE_MUTED', null, getAllClients(ctx));
cb();
};
var unmuteUser = function (ctx, curvePublic, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
var proxy = ctx.store.proxy;
var muted = proxy.mutedUsers = proxy.mutedUsers || {};
delete muted[curvePublic];
ctx.emit('UPDATE_MUTED', null, getAllClients(ctx));
cb(Object.keys(muted).length);
};
var getMutedUsers = function (ctx, cb) {
var proxy = ctx.store.proxy;
if (cb) {
return void cb(proxy.mutedUsers || {});
}
return proxy.mutedUsers || {};
};
var openChannel = function (ctx, data) { var openChannel = function (ctx, data) {
var proxy = ctx.store.proxy; var proxy = ctx.store.proxy;
var network = ctx.store.network; var network = ctx.store.network;
@ -664,7 +699,14 @@ define([
nThen(function (waitFor) { nThen(function (waitFor) {
// Load or get all friends channels // Load or get all friends channels
Object.keys(friends).forEach(function (key) { Object.keys(friends).forEach(function (key) {
if (key === 'me') { return; } if (key === 'me') {
// At some point a bug inserted a friend's channel into our "me" data.
// This led to displaying our name instead of our friend's name in the
// contacts app. The following line is here to prevent this issue to happen
// again.
delete friends.me.channel;
return;
}
var friend = clone(friends[key]); var friend = clone(friends[key]);
if (typeof(friend) !== 'object') { return; } if (typeof(friend) !== 'object') { return; }
if (!friend.channel) { return; } if (!friend.channel) { return; }
@ -887,15 +929,6 @@ define([
}); });
}; };
var getAllClients = function (ctx) {
var all = [];
Array.prototype.push.apply(all, ctx.friendsClients);
Object.keys(ctx.channels).forEach(function (id) {
Array.prototype.push.apply(all, ctx.channels[id].clients);
});
return Util.deduplicateString(all);
};
Msg.init = function (cfg, waitFor, emit) { Msg.init = function (cfg, waitFor, emit) {
var messenger = {}; var messenger = {};
var store = cfg.store; var store = cfg.store;
@ -911,6 +944,9 @@ define([
range_requests: {} range_requests: {}
}; };
store.proxy.on('change', ['mutedUsers'], function () {
ctx.emit('UPDATE_MUTED', null, getAllClients(ctx));
});
ctx.store.network.on('message', function(msg, sender) { ctx.store.network.on('message', function(msg, sender) {
onDirectMessage(ctx, msg, sender); onDirectMessage(ctx, msg, sender);
@ -942,6 +978,12 @@ define([
var channel = friend.channel; var channel = friend.channel;
if (!channel) { return; } if (!channel) { return; }
// Already friend? don't load the channel a second time
var chanId = friend.channel;
var chan = ctx.channels[chanId];
if (chan) { return; }
// Load the channel and add the friend to the contacts app
loadFriend(ctx, null, friend, function () { loadFriend(ctx, null, friend, function () {
emit('FRIEND', { emit('FRIEND', {
curvePublic: friend.curvePublic, curvePublic: friend.curvePublic,
@ -990,6 +1032,9 @@ define([
if (cmd === 'GET_ROOMS') { if (cmd === 'GET_ROOMS') {
return void getRooms(ctx, data, cb); return void getRooms(ctx, data, cb);
} }
if (cmd === 'GET_MUTED_USERS') {
return void getMutedUsers(ctx, cb);
}
if (cmd === 'GET_USERLIST') { if (cmd === 'GET_USERLIST') {
return void getUserList(ctx, data, cb); return void getUserList(ctx, data, cb);
} }
@ -1002,6 +1047,12 @@ define([
if (cmd === 'REMOVE_FRIEND') { if (cmd === 'REMOVE_FRIEND') {
return void removeFriend(ctx, data, cb); return void removeFriend(ctx, data, cb);
} }
if (cmd === 'MUTE_USER') {
return void muteUser(ctx, data, cb);
}
if (cmd === 'UNMUTE_USER') {
return void unmuteUser(ctx, data, cb);
}
if (cmd === 'GET_STATUS') { if (cmd === 'GET_STATUS') {
return void getStatus(ctx, data, cb); return void getStatus(ctx, data, cb);
} }

@ -1,5 +1,7 @@
@import (reference) '../../customize/src/less2/include/framework.less'; @import (reference) '../../customize/src/less2/include/framework.less';
@import (reference) '../../customize/src/less2/include/messenger.less'; @import (reference) '../../customize/src/less2/include/messenger.less';
@import (reference) '../../customize/src/less2/include/avatar.less';
@import (reference) '../../customize/src/less2/include/buttons.less';
// body // body
&.cp-app-contacts { &.cp-app-contacts {
@ -17,6 +19,22 @@
display: flex; // We need this to remove a 3px border at the bottom of the toolbar display: flex; // We need this to remove a 3px border at the bottom of the toolbar
} }
.cp-app-contacts-friends {
.buttons_main();
}
.cp-contacts-muted-table {
.avatar_main(50px);
.cp-contacts-muted-user {
display: flex;
align-items: center;
span:nth-child(2) {
flex: 1;
}
}
}
.messenger_main(); .messenger_main();
} }

@ -10,8 +10,15 @@
); );
.sidebar-layout_main(); .sidebar-layout_main();
@cp-profile-is-your-friend: #777;
display: flex; display: flex;
flex-flow: column; flex-flow: column;
#cp-sidebarlayout-leftside {
display: none !important;
}
#cp-app-profile-header { #cp-app-profile-header {
display: flex; display: flex;
#cp-app-profile-rightside { #cp-app-profile-rightside {
@ -64,8 +71,9 @@
} }
} }
button { button {
min-width: 40px;
height: 40px; height: 40px;
margin: 5px; margin: 5px !important;
} }
} }
.cp-app-profile-resizer { .cp-app-profile-resizer {
@ -108,10 +116,18 @@
display: none; display: none;
} }
& > button:empty { & > button:empty {
margin-left: 25px; margin-left: 25px !important;
} }
} }
.cp-app-profile-friend {
display: flex;
align-items: center;
color: @cp-profile-is-your-friend;
.fa {
margin-right: 0.2em;
}
}
.cp-app-profile-friend-request { .cp-app-profile-friend-request {
flex: 0; flex: 0;
width: 400px; width: 400px;
@ -124,11 +140,7 @@
} }
.cp-app-profile-viewprofile-button { .cp-app-profile-viewprofile-button {
margin-bottom: 20px; margin-bottom: 20px;
float: right; width: 300px;
margin-left: 5px;
&> span {
margin-left: 10px;
}
} }
#cp-app-profile-description { #cp-app-profile-description {
position: relative; position: relative;
@ -166,13 +178,6 @@
line-height: inherit; line-height: inherit;
} }
} }
.cp-app-profile-description-edit {
& > button {
span {
margin-left: 10px;
}
}
}
} }
#cp-app-profile-create { #cp-app-profile-create {
height: 100%; height: 100%;
@ -181,5 +186,12 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.cp-app-profile-mute-container {
margin-top: 5px;
p {
margin-top: 5px;
font-size: 13px;
}
}
} }

@ -178,9 +178,6 @@ define([
var addFriendRequest = function ($container) { var addFriendRequest = function ($container) {
if (!APP.readOnly || !APP.common.isLoggedIn()) { return; } if (!APP.readOnly || !APP.common.isLoggedIn()) { return; }
APP.$friend = $('<button>', {
'class': 'btn btn-success cp-app-profile-friend-request',
});
APP.$friend = $(h('div.cp-app-profile-friend-container')); APP.$friend = $(h('div.cp-app-profile-friend-container'));
$container.append(APP.$friend); $container.append(APP.$friend);
}; };
@ -195,42 +192,124 @@ define([
APP.$friend.html(''); APP.$friend.html('');
var module = common.makeUniversal('messenger');
var name = Util.fixHTML(data.name) || Messages.anonymous;
var friends = common.getMetadataMgr().getPrivateData().friends; var friends = common.getMetadataMgr().getPrivateData().friends;
// This is a friend: display the "friend" message and an "unfriend" button
if (friends[data.curvePublic]) { if (friends[data.curvePublic]) {
APP.$friend.append(h('p.cp-app-profile-friend', Messages._getKey('profile_friend', [data.name || Messages.anonymous]))); // Add friend message
APP.$friend.append(h('p.cp-app-profile-friend', [
h('i.fa.fa-address-book'),
Messages._getKey('profile_friend', [name])
]));
if (!friends[data.curvePublic].notifications) { return; }
// Add unfriend button
var unfriendButton = h('button.btn.btn-primary.cp-app-profile-friend-request', [
h('i.fa.fa-user-times'),
Messages.contacts_remove
]);
$(unfriendButton).click(function () {
// Unfriend confirm
var content = h('div', [
UI.setHTML(h('p'), Messages._getKey('contacts_confirmRemove', [name]))
]);
UI.confirm(content, function (yes) {
if (!yes) { return; }
module.execCommand('REMOVE_FRIEND', data.curvePublic, function (e) {
if (e) { return void console.error(e); }
});
});
}).appendTo(APP.$friend);
return; return;
} }
var $button = $('<button>', { var button = h('button.btn.btn-success.cp-app-profile-friend-request', [
'class': 'btn btn-success cp-app-profile-friend-request', h('i.fa.fa-user-plus'),
}).appendTo(APP.$friend); ]);
var $button = $(button).appendTo(APP.$friend);
// If this curve has sent us a friend request, we should not be able to sent it to them // If this curve has sent us a friend request, we should not be able to sent it to them
var friendRequests = common.getFriendRequests(); var friendRequests = common.getFriendRequests();
if (friendRequests[data.curvePublic]) { if (friendRequests[data.curvePublic]) {
$button.html(Messages._getKey('friendRequest_received', [data.name || Messages.anonymous])) $button.append(Messages._getKey('friendRequest_received', [data.name || Messages.anonymous]))
.click(function () { .click(function () {
UIElements.displayFriendRequestModal(common, friendRequests[data.curvePublic]); UIElements.displayFriendRequestModal(common, friendRequests[data.curvePublic]);
}); });
return; return;
} }
// Pending friend (we've sent a friend request)
var pendingFriends = APP.common.getPendingFriends(); // Friend requests sent var pendingFriends = APP.common.getPendingFriends(); // Friend requests sent
if (pendingFriends[data.curvePublic]) { if (pendingFriends[data.curvePublic]) {
$button.attr('disabled', 'disabled').text(Messages.profile_friendRequestSent); $button.attr('disabled', 'disabled').append(Messages.profile_friendRequestSent);
return; return;
} }
// This is not a friend yet: we can send a friend request
$button.text(Messages._getKey('userlist_addAsFriendTitle', [data.name || Messages.anonymous])) $button.text(Messages._getKey('userlist_addAsFriendTitle', [data.name || Messages.anonymous]))
.click(function () { .click(function () {
APP.common.sendFriendRequest({ APP.common.sendFriendRequest({
curvePublic: data.curvePublic, curvePublic: data.curvePublic,
notifications: data.notifications notifications: data.notifications
}, function () { }, function () {
$button.attr('disabled', 'disabled').text(Messages.profile_friendRequestSent); $button.attr('disabled', 'disabled').append(Messages.profile_friendRequestSent);
}); });
}); });
}; };
var addMuteButton = function ($container) {
if (!APP.readOnly || !APP.common.isLoggedIn()) { return; }
APP.$mute = $(h('div.cp-app-profile-mute-container'));
$container.append(APP.$mute);
};
var refreshMute = function (data) {
if (!APP.$mute) { return; }
var me = common.getMetadataMgr().getUserData().curvePublic;
if (data.curvePublic === me) {
APP.$mute.remove();
return;
}
// Add mute/unmute buttons
var $mute = APP.$mute;
var module = common.makeUniversal('messenger');
module.execCommand('GET_MUTED_USERS', null, function (muted) {
if (!muted || typeof(muted) !== "object") { return; }
$mute.html('');
var isMuted = muted[data.curvePublic];
if (isMuted) {
var unmuteButton = h('button.btn.btn-secondary.cp-app-profile-friend-request', [
h('i.fa.fa-bell'),
Messages.contacts_unmute || 'unmute'
]);
$(unmuteButton).click(function () {
module.execCommand('UNMUTE_USER', data.curvePublic, function (e) {
if (e) { console.error(e); return void UI.warn(Messages.error); }
refreshMute(data);
});
}).appendTo($mute);
return;
}
var muteButton = h('button.btn.btn-danger.cp-app-profile-friend-request', [
h('i.fa.fa-bell-slash'),
Messages.contacts_mute || 'mute'
]);
$(muteButton).click(function () {
module.execCommand('MUTE_USER', {
curvePublic: data.curvePublic,
name: data.displayName || data.name,
avatar: data.avatar
}, function (e) {
if (e) { console.error(e); return void UI.warn(Messages.error); }
refreshMute(data);
});
}).appendTo($mute);
$(UI.setHTML(h('p'), Messages.contacts_muteInfo)).appendTo($mute);
});
};
var displayAvatar = function (val) { var displayAvatar = function (val) {
var sframeChan = common.getSframeChannel(); var sframeChan = common.getSframeChannel();
var $span = APP.$avatar; var $span = APP.$avatar;
@ -304,7 +383,7 @@ define([
}; };
var addDescription = function ($container) { var addDescription = function ($container) {
var $block = $('<div>', {id: DESCRIPTION_ID}).appendTo($container); var $block = $('<div>', {id: DESCRIPTION_ID, class:'cp-sidebarlayout-element'}).appendTo($container);
APP.$description = $('<div>', {'class': 'cp-app-profile-description-rendered'}).appendTo($block); APP.$description = $('<div>', {'class': 'cp-app-profile-description-rendered'}).appendTo($block);
APP.$descriptionEdit = $(); APP.$descriptionEdit = $();
@ -390,14 +469,15 @@ define([
APP.$container.find('#'+CREATE_ID).remove(); APP.$container.find('#'+CREATE_ID).remove();
if (!APP.initialized) { if (!APP.initialized) {
var $header = $('<div>', {id: HEADER_ID}).appendTo(APP.$rightside); var $header = $('<div>', {id: HEADER_ID, class:'cp-sidebarlayout-element'}).appendTo(APP.$rightside);
addAvatar($header); addAvatar($header);
var $rightside = $('<div>', {id: HEADER_RIGHT_ID}).appendTo($header); var $rightside = $('<div>', {id: HEADER_RIGHT_ID}).appendTo($header);
addDisplayName($rightside); addDisplayName($rightside);
addLink($rightside); addLink($rightside);
addFriendRequest($rightside); addFriendRequest($rightside);
addMuteButton($rightside);
addDescription(APP.$rightside); addDescription(APP.$rightside);
addViewButton(APP.$rightside); addViewButton($rightside);
APP.initialized = true; APP.initialized = true;
createLeftside(); createLeftside();
} }
@ -409,6 +489,7 @@ define([
refreshLink(data); refreshLink(data);
refreshDescription(data); refreshDescription(data);
refreshFriendRequest(data); refreshFriendRequest(data);
refreshMute(data);
}; };
var createToolbar = function () { var createToolbar = function () {

@ -184,12 +184,12 @@
&:checked { &:checked {
& + label { & + label {
font-weight: bold; font-weight: bold;
background-color: lighten(@colortheme_loading-bg, 20%); background-color: @colortheme_logo-2;
cursor: default; cursor: default;
border: 1px solid #c1158e; border: 1px solid @colortheme_logo-2;
color: @colortheme_loading-color; color: @colortheme_loading-color;
&:hover { &:hover {
background-color: lighten(@colortheme_loading-bg, 20%); background-color: @colortheme_logo-2;
} }
} }
} }
@ -203,10 +203,11 @@
height: 50px; height: 50px;
padding: 5px; padding: 5px;
margin: 0 20px; margin: 0 20px;
border: 1px solid black; color: @colortheme_logo-2;
border: 1px solid @colortheme_logo-2;
cursor: pointer; cursor: pointer;
&:hover { &:hover {
background-color: lighten(@colortheme_loading-bg, 10%); background-color: lighten(@colortheme_logo-2, 35%);
} }
} }
.fa { .fa {
@ -235,6 +236,7 @@
} }
input { input {
width: 70px; width: 70px;
padding: 0 5px;
} }
select { select {
width: 100px; width: 100px;

@ -493,8 +493,9 @@ define([
refreshCreate(common, cb); refreshCreate(common, cb);
}); });
makeBlock('drive', function (common, cb) { makeBlock('drive', function (common, cb, $div) {
$('div.cp-team-drive').empty(); $('div.cp-team-drive').empty();
$div.removeClass('cp-sidebarlayout-element'); // Don't apply buttons and input styles from sidebarlayout
var content = [ var content = [
h('div.cp-app-drive-container', {tabindex:0}, [ h('div.cp-app-drive-container', {tabindex:0}, [
h('div#cp-app-drive-tree'), h('div#cp-app-drive-tree'),

Loading…
Cancel
Save