diff --git a/customize.dist/pages.js b/customize.dist/pages.js
index 9e2759786..aac6ddfa7 100644
--- a/customize.dist/pages.js
+++ b/customize.dist/pages.js
@@ -635,6 +635,10 @@ define([
return loadingScreen();
};
+ Pages['/contacts2/'] = Pages['/contacts2/index.html'] = function () {
+ return loadingScreen();
+ };
+
Pages['/pad/'] = Pages['/pad/index.html'] = function () {
return loadingScreen();
};
diff --git a/customize.dist/template.js b/customize.dist/template.js
index a9067f35f..a980270a3 100644
--- a/customize.dist/template.js
+++ b/customize.dist/template.js
@@ -9,7 +9,7 @@ define([
$(function () {
var $body = $('body');
var isMainApp = function () {
- return /^\/(pad|code|slide|poll|whiteboard|file|media|contacts|drive|settings|profile|todo)\/$/.test(location.pathname);
+ return /^\/(pad|code|slide|poll|whiteboard|file|media|contacts|contacts2|drive|settings|profile|todo)\/$/.test(location.pathname);
};
var infoPage = function () {
@@ -52,9 +52,12 @@ $(function () {
} else if (/\/file\//.test(pathname)) {
$('body').append(h('body', Pages[pathname]()).innerHTML);
require([ '/file/main.js' ], ready);
- } else if (/contacts/.test(pathname)) {
+ } else if (/^\/contacts\/$/.test(pathname)) {
$('body').append(h('body', Pages[pathname]()).innerHTML);
require([ '/contacts/main.js' ], ready);
+ } else if (/contacts2/.test(pathname)) {
+ $('body').append(h('body', Pages[pathname]()).innerHTML);
+ require([ '/contacts2/main.js' ], ready);
} else if (/pad/.test(pathname)) {
$('body').append(h('body', Pages[pathname]()).innerHTML);
require([ '/pad/main.js' ], ready);
diff --git a/www/contacts2/index.html b/www/contacts2/index.html
new file mode 100644
index 000000000..a76879a6d
--- /dev/null
+++ b/www/contacts2/index.html
@@ -0,0 +1,30 @@
+
+
+
+ CryptPad
+
+
+
+
+
+
+
+
diff --git a/www/contacts2/inner.html b/www/contacts2/inner.html
new file mode 100644
index 000000000..ebfb164c8
--- /dev/null
+++ b/www/contacts2/inner.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/www/contacts2/inner.js b/www/contacts2/inner.js
new file mode 100644
index 000000000..556f37ee3
--- /dev/null
+++ b/www/contacts2/inner.js
@@ -0,0 +1,13 @@
+define([
+ 'jquery',
+ 'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
+ 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
+ 'less!/contacts/main.less',
+ 'less!/customize/src/less/toolbar.less',
+], function ($) {
+ $('.loading-hidden').removeClass('loading-hidden');
+ // dirty hack to get rid the flash of the lock background
+ setTimeout(function () {
+ $('#app').addClass('ready');
+ }, 100);
+});
diff --git a/www/contacts2/main.js b/www/contacts2/main.js
new file mode 100644
index 000000000..9e318dac9
--- /dev/null
+++ b/www/contacts2/main.js
@@ -0,0 +1,81 @@
+define([
+ 'jquery',
+ '/bower_components/chainpad-crypto/crypto.js',
+ '/common/toolbar2.js',
+ '/common/cryptpad-common.js',
+
+ '/common/common-messenger.js',
+ '/contacts2/messenger-ui.js',
+
+ 'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
+ 'less!/customize/src/less/cryptpad.less',
+], function ($, Crypto, Toolbar, Cryptpad, Messenger, UI) {
+ var Messages = Cryptpad.Messages;
+
+ var APP = window.APP = {
+ Cryptpad: Cryptpad
+ };
+
+ $(function () {
+
+ var andThen = function () {
+ Cryptpad.addLoadingScreen();
+
+ var ifrw = $('#pad-iframe')[0].contentWindow;
+ var $iframe = $('#pad-iframe').contents();
+ //var $appContainer = $iframe.find('#app');
+ var $list = $iframe.find('#friendList');
+ var $messages = $iframe.find('#messaging');
+ var $bar = $iframe.find('.toolbar-container');
+
+ var displayed = ['useradmin', 'newpad', 'limit', 'pageTitle'];
+
+
+ var configTb = {
+ displayed: displayed,
+ ifrw: ifrw,
+ common: Cryptpad,
+ $container: $bar,
+ network: Cryptpad.getNetwork(),
+ pageTitle: Messages.contacts_title,
+ };
+ var toolbar = APP.toolbar = Toolbar.create(configTb);
+ toolbar.$rightside.html(''); // Remove the drawer if we don't use it to hide the toolbar
+
+ Cryptpad.getProxy().on('disconnect', function () {
+ Cryptpad.alert(Messages.common_connectionLost, undefined, true);
+ Cryptpad.enableMessaging(false);
+ });
+ Cryptpad.getProxy().on('reconnect', function (uid) {
+ console.error('reconnecting: ', uid);
+ Cryptpad.findOKButton().click();
+
+ //APP.messenger.cleanFriendChannels();
+ //APP.messenger.openFriendChannels();
+ //APP.messenger.setEditable(true);
+ });
+
+ var $infoBlock = $('', {'class': 'info'}).appendTo($messages);
+ $('
').text(Messages.contacts_info1).appendTo($infoBlock);
+ var $ul = $('').appendTo($infoBlock);
+ $('- ').text(Messages.contacts_info2).appendTo($ul);
+ $('
- ').text(Messages.contacts_info3).appendTo($ul);
+ //$('
- ').text(Messages.contacts_info4).appendTo($ul);
+
+
+ //var ui = APP.ui = Cryptpad.initMessagingUI(Cryptpad, $list, $messages);
+ //APP.messenger = Cryptpad.initMessaging(Cryptpad, ui);
+
+ var messenger = window.messenger = Messenger.messenger(Cryptpad);
+ UI.create(messenger, $list, $messages);
+
+ Cryptpad.removeLoadingScreen();
+ };
+
+ Cryptpad.ready(function () {
+ andThen();
+ Cryptpad.reportAppUsage();
+ });
+
+ });
+});
diff --git a/www/contacts2/main.less b/www/contacts2/main.less
new file mode 100644
index 000000000..6805aa82d
--- /dev/null
+++ b/www/contacts2/main.less
@@ -0,0 +1,244 @@
+@import "/customize/src/less/variables.less";
+@import "/customize/src/less/mixins.less";
+
+@button-border: 2px;
+@bg-color: @toolbar-friends-bg;
+@color: @toolbar-friends-color;
+
+html, body {
+ margin: 0px;
+ height: 100%;
+}
+
+#toolbar {
+ display: flex; // We need this to remove a 3px border at the bottom of the toolbar
+}
+
+body {
+ display: flex;
+ flex-flow: column;
+}
+#app {
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ min-height: 0;
+}
+
+#app.ready {
+ //background: url('/customize/bg3.jpg') no-repeat center center;
+ background-size: cover;
+ background-position: center;
+}
+.cryptpad-toolbar {
+ padding: 0px;
+ display: inline-block;
+}
+
+
+@keyframes example {
+ 0% {
+ background: rgba(0,0,0,0.1);
+ }
+ 50% {
+ background: rgba(0,0,0,0.3);
+ }
+ 100% {
+ background: rgba(0,0,0,0.1);
+ }
+}
+#friendList {
+ width: 350px;
+ height: 100%;
+ background-color: lighten(@bg-color, 10%);
+ .friend {
+ background: rgba(0,0,0,0.1);
+ padding: 5px;
+ margin: 10px;
+ cursor: pointer;
+ &:hover {
+ background-color: rgba(0,0,0,0.3);
+ }
+ &.notify {
+ animation: example 2s ease-in-out infinite;
+ }
+ }
+}
+
+#friendList .friend, #messaging .avatar {
+ .avatar(30px);
+ &.avatar {
+ display: flex;
+ }
+ cursor: pointer;
+ color: @color;
+ media-tag {
+ img {
+ color: #000;
+ }
+ }
+ media-tag, .default {
+ margin-right: 5px;
+ }
+ .status {
+ width: 5px;
+ display: inline-block;
+ position: absolute;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ opacity: 0.7;
+ background-color: #777;
+ &.online {
+ background-color: green;
+ }
+ &.offline {
+ background-color: red;
+ }
+ }
+}
+
+#friendList {
+ .friend {
+ position: relative;
+ }
+ .remove {
+ cursor: pointer;
+ width: 20px;
+ &:hover {
+ color: darken(@color, 20%);
+ }
+ }
+}
+
+.placeholder (@color: #bbb) {
+ &::-webkit-input-placeholder { /* WebKit, Blink, Edge */
+ color: @color;
+ }
+ &:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
+ color: @color;
+ opacity: 1;
+ }
+ &::-moz-placeholder { /* Mozilla Firefox 19+ */
+ color: @color;
+ opacity: 1;
+ }
+ &:-ms-input-placeholder { /* Internet Explorer 10-11 */
+ color: @color;
+ }
+ &::-ms-input-placeholder { /* Microsoft Edge */
+ color: @color;
+ }
+}
+
+#messaging {
+ flex: 1;
+ height: 100%;
+ background-color: lighten(@bg-color, 20%);
+ min-width: 0;
+
+ .info {
+ padding: 20px;
+ }
+ .header {
+ background-color: lighten(@bg-color, 15%);
+ padding: 0;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ height: 50px;
+
+ .hover () {
+ height: 100%;
+ line-height: 30px;
+ padding: 10px;
+ &:hover {
+ background-color: rgba(50,50,50,0.3);
+ }
+ }
+
+ .avatar,
+ .right-col {
+ flex:1 1 auto;
+ }
+ .remove-history {
+ .hover;
+ }
+ .avatar {
+ margin: 10px;
+ }
+ .more-history {
+ display: none;
+ //.hover;
+ }
+ }
+ .chat {
+ height: 100%;
+ display: flex;
+ flex-flow: column;
+ .messages {
+ padding: 0 20px;
+ margin: 10px 0;
+ flex: 1;
+ overflow-x: auto;
+ .message {
+ & > div {
+ padding: 0 10px;
+ }
+ .content {
+ overflow: hidden;
+ word-wrap: break-word;
+ &> * {
+ margin: 0;
+ }
+ }
+ .date {
+ display: none;
+ font-style: italic;
+ }
+ .sender {
+ margin-top: 10px;
+ font-weight: bold;
+ background-color: rgba(0,0,0,0.1);
+ }
+ }
+ }
+ }
+ .input {
+ background-color: lighten(@bg-color, 15%);
+ height: auto;
+ min-height: 50px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 0 75px;
+ textarea {
+ margin: 5px 0;
+ padding: 0 10px;
+ border: none;
+ height: 50px;
+ flex: 1;
+ background-color: darken(@bg-color, 10%);
+ color: @color;
+ resize: none;
+ line-height: 50px;
+ overflow-y: auto;
+ .placeholder(#bbb);
+ &[disabled=true] {
+ .placeholder(#999);
+ }
+ &:placeholder-shown { line-height: 50px; }
+ }
+ button {
+ height: 50px;
+ border-radius: 0;
+ border: none;
+ background-color: darken(@bg-color, 15%);
+ &:hover {
+ background-color: darken(@bg-color, 20%);
+ }
+ }
+ }
+}
+
diff --git a/www/contacts2/messenger-ui.js b/www/contacts2/messenger-ui.js
new file mode 100644
index 000000000..9f80f4ac6
--- /dev/null
+++ b/www/contacts2/messenger-ui.js
@@ -0,0 +1,324 @@
+define([
+ 'jquery',
+ '/common/cryptpad-common.js',
+ '/common/hyperscript.js',
+ '/bower_components/marked/marked.min.js',
+], function ($, Cryptpad, h, Marked) {
+ // TODO use our fancy markdown and support media-tags
+ Marked.setOptions({ sanitize: true, });
+
+ var UI = {};
+ var Messages = Cryptpad.Messages;
+
+ var stub = function (label) {
+ console.error('stub: ' + label);
+ };
+
+ var dataQuery = function (curvePublic) {
+ return '[data-key="' + curvePublic + '"]';
+ };
+
+ UI.create = function (messenger, $userlist, $messages) {
+ var state = {
+ active: '',
+ };
+ var avatars = state.avatars = {};
+ var setActive = function (curvePublic) {
+ state.active = curvePublic;
+ };
+ var isActive = function (curvePublic) {
+ return curvePublic === state.active;
+ };
+
+ var find = {};
+ find.inList = function (curvePublic) {
+ return $userlist.find(dataQuery(curvePublic));
+ };
+
+ var notify = function (curvePublic) {
+ find.inList(curvePublic).addClass('notify');
+ };
+ var unnotify = function (curvePublic) {
+ find.inList(curvePublic).removeClass('notify');
+ };
+
+ var markup = {};
+ markup.message = function (msg, name) {
+ return h('div.message', {
+ title: msg.time? new Date(msg.time).toLocaleString(): '?',
+ }, [
+ name? h('div.sender', name): undefined,
+ h('div.content', msg.text),
+ ]);
+ };
+
+ markup.chatbox = function (curvePublic, data) {
+ var moreHistory = h('span.more-history', ['get more history']); // TODO translate
+ var displayName = data.displayName;
+
+ $(moreHistory).click(function () {
+ stub('get older history');
+ console.log('getting history');
+ });
+
+ var removeHistory = h('span.remove-history.fa.fa-eraser', {
+ title: Messages.contacts_removeHistoryTitle
+ });
+
+ $(removeHistory).click(function () {
+ Cryptpad.confirm(Messages.contacts_confirmRemoveHistory, function (yes) {
+ if (!yes) { return; }
+ Cryptpad.clearOwnedChannel(data.channel, function (e) {
+ if (e) {
+ console.error(e);
+ Cryptpad.alert(Messages.contacts_removeHistoryServerError);
+ return;
+ }
+ });
+ });
+ });
+
+ var avatar = h('div.avatar');
+ var header = h('div.header', [
+ avatar,
+ moreHistory,
+ removeHistory,
+ ]);
+ var messages = h('div.messages');
+ var input = h('textarea', {
+ placeholder: Messages.contacts_typeHere
+ });
+ var sendButton = h('button.btn.btn-primary.fa.fa-paper-plane', {
+ title: Messages.contacts_send,
+ });
+
+ var rightCol = h('span.right-col', [
+ h('span.name', displayName),
+ ]);
+
+ var $avatar = $(avatar);
+ if (data.avatar && avatars[data.avatar]) {
+ $avatar.append(avatars[data.avatar]).append(rightCol);
+ } else {
+ Cryptpad.displayAvatar($avatar, data.avatar, data.displayName, function ($img) {
+ if (data.avatar && $img) {
+ avatars[data.avatar] = $img[0].outerHTML;
+ }
+ $avatar.append(rightCol);
+ });
+ }
+
+ var sending = false;
+ var send = function (content) {
+ if (sending) { return false; }
+ sending = true;
+ messenger.sendMessage(curvePublic, content, function (e) {
+ if (e) {
+ // failed to send
+ return void console.error('failed to send');
+ }
+ input.value = '';
+ sending = false;
+ console.log('sent successfully');
+ });
+ };
+
+ var onKeyDown = function (e) {
+ // ignore anything that isn't 'enter'
+ if (e.keyCode !== 13) { return; }
+ // send unless they're holding a ctrl-key or shift
+ if (!e.ctrlKey && !e.shiftKey) {
+ send(this.value);
+ return false;
+ }
+
+ // insert a newline if they're holding either
+ var val = this.value;
+ var start = this.selectionState;
+ var end = this.selectionEnd;
+
+ if (![start,end].some(function (x) {
+ return typeof(x) !== 'number';
+ })) {
+ this.value = val.slice(0, start) + '\n' + val.slice(end);
+ this.selectionStart = this.selectionEnd = start + 1;
+ } else if (document.selection && document.selection.createRange) {
+ this.focus();
+ var range = document.selection.createRange();
+ range.text = '\r\n';
+ range.collapse(false);
+ range.select();
+ }
+ return false;
+ };
+ $(input).on('keydown', onKeyDown);
+ $(sendButton).click(function () { send(input.value); });
+
+ return h('div.chat', {
+ 'data-key': curvePublic,
+ }, [
+ header,
+ messages,
+ h('div.input', [
+ input,
+ sendButton,
+ ]),
+ ]);
+ };
+
+ var hideInfo = function () {
+ $messages.find('.info').hide();
+ };
+
+ var getChat = function (curvePublic) {
+ return $messages.find(dataQuery(curvePublic));
+ };
+
+ var updateStatus = function (curvePublic) {
+ var $status = find.inList(curvePublic).find('.status');
+ messenger.getStatus(curvePublic, function (e, online) {
+ if (e) { return void console.error(e); }
+ if (online) {
+ return void $status
+ .removeClass('offline').addClass('online');
+ }
+ $status.removeClass('online').addClass('offline');
+ });
+ };
+
+ var display = function (curvePublic) {
+ setActive(curvePublic);
+ unnotify(curvePublic);
+ var $chat = getChat(curvePublic);
+ hideInfo();
+ $messages.find('div.chat[data-key]').hide();
+ if ($chat.length) {
+ return void $chat.show();
+ }
+ messenger.getFriendInfo(curvePublic, function (e, info) {
+ if (e) { return void console.error(e); } // FIXME
+ $messages.append(markup.chatbox(curvePublic, info));
+ });
+ };
+
+ var removeFriend = function (curvePublic) {
+ messenger.removeFriend(curvePublic, function (e, removed) {
+ if (e) { return void console.error(e); }
+ console.log(removed);
+ });
+ };
+
+ var friendExistsInUserList = function (curvePublic) {
+ return !!$userlist.find(dataQuery(curvePublic)).length;
+ };
+
+ markup.friend = function (data) {
+ var curvePublic = data.curvePublic;
+ var friend = h('div.friend.avatar', {
+ 'data-key': curvePublic,
+ });
+
+ var remove = h('span.remove.fa.fa-user-times', {
+ title: Messages.contacts_remove
+ });
+ var status = h('span.status');
+ var rightCol = h('span.right-col', [
+ h('span.name', [data.displayName]),
+ remove,
+ ]);
+
+ var $friend = $(friend)
+ .click(function () {
+ display(curvePublic);
+ })
+ .dblclick(function () {
+ if (data.profile) { window.open('/profile/#' + data.profile); }
+ });
+
+ $(remove).click(function (e) {
+ e.stopPropagation();
+ Cryptpad.confirm(Messages._getKey('contacts_confirmRemove', [
+ Cryptpad.fixHTML(data.displayName)
+ ]), function (yes) {
+ if (!yes) { return; }
+ stub('remove friend: ' + curvePublic);
+ removeFriend(curvePublic);
+ });
+ });
+
+ if (data.avatar && avatars[data.avatar]) {
+ $friend.append(avatars[data.avatar]);
+ $friend.append(rightCol);
+ } else {
+ Cryptpad.displayAvatar($friend, data.avatar, data.displayName, function ($img) {
+ if (data.avatar && $img) {
+ avatars[data.avatar] = $img[0].outerHTML;
+ }
+ $friend.append(rightCol);
+ });
+ }
+ $friend.append(status);
+ return $friend;
+ };
+
+ var displayNames = {};
+
+ messenger.on('message', function (message) {
+ console.log(JSON.stringify(message));
+ Cryptpad.notify();
+ var curvePublic = message.curve;
+
+ if (!isActive(curvePublic)) { notify(curvePublic); }
+
+ var name = displayNames[curvePublic];
+ var chat = getChat(curvePublic, name);
+ var el_message = markup.message(message, name);
+
+ var $chat = $(chat);
+ console.log(chat, $chat, el_message.outerHTML);
+ $chat.find('.messages').append(el_message);
+
+ // TODO notify if a message is newer than `lastKnownHash`
+ });
+
+ messenger.on('join', function (curvePublic, channel) {
+ console.log('join', curvePublic, channel);
+ updateStatus(curvePublic);
+ });
+ messenger.on('leave', function (curvePublic, channel) {
+ console.log('leave', curvePublic, channel);
+ updateStatus(curvePublic);
+ });
+
+ // change in your friend list
+ messenger.on('update', function (info, curvePublic) {
+ curvePublic = curvePublic;
+ });
+
+ Cryptpad.onDisplayNameChanged(function () {
+ messenger.checkNewFriends();
+ messenger.updateMyData();
+ });
+
+ messenger.getFriendList(function (e, keys) {
+ keys.forEach(function (k) {
+ messenger.openFriendChannel(k, function (e) {
+ if (e) { return void console.error(e); }
+ // don't add friends that are already in your userlist
+ if (friendExistsInUserList(k)) { return; }
+
+ messenger.getFriendInfo(k, function (e, info) {
+ if (e) { return console.error(e); }
+ var curvePublic = info.curvePublic;
+ var name = displayNames[curvePublic] = info.displayName;
+ var friend = markup.friend(info, name);
+ $userlist.append(friend);
+ updateStatus(curvePublic);
+ });
+ });
+ });
+ });
+ };
+
+ return UI;
+});