From 89a13d4b2183ebede98c37d1dda2c52687aa34a7 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 22 Aug 2017 15:55:49 +0200 Subject: [PATCH] work on contacts2 until feature parity is reached --- customize.dist/pages.js | 4 + customize.dist/template.js | 7 +- www/contacts2/index.html | 30 ++++ www/contacts2/inner.html | 17 ++ www/contacts2/inner.js | 13 ++ www/contacts2/main.js | 81 +++++++++ www/contacts2/main.less | 244 +++++++++++++++++++++++++ www/contacts2/messenger-ui.js | 324 ++++++++++++++++++++++++++++++++++ 8 files changed, 718 insertions(+), 2 deletions(-) create mode 100644 www/contacts2/index.html create mode 100644 www/contacts2/inner.html create mode 100644 www/contacts2/inner.js create mode 100644 www/contacts2/main.js create mode 100644 www/contacts2/main.less create mode 100644 www/contacts2/messenger-ui.js 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; +});