From 81c2df389a5816c26d9c6ff0f5001a38bda37f84 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 17 Sep 2019 11:05:32 +0200 Subject: [PATCH] Team chat --- .../src/less2/include/messenger.less | 6 +- www/common/messenger-ui.js | 32 ++++++-- www/common/outer/messenger.js | 73 ++++++++++++++++++- www/common/outer/team.js | 23 +++++- www/common/sframe-common.js | 9 +++ www/team/app-team.less | 10 +++ www/team/inner.js | 20 ++++- 7 files changed, 159 insertions(+), 14 deletions(-) diff --git a/customize.dist/src/less2/include/messenger.less b/customize.dist/src/less2/include/messenger.less index 4570297d7..7f4f7c342 100644 --- a/customize.dist/src/less2/include/messenger.less +++ b/customize.dist/src/less2/include/messenger.less @@ -369,12 +369,12 @@ } } button { - height: 54px; + height: 54px !important; border-radius: 0; border: none; - background-color: darken(@bg-color, 15%); + background-color: darken(@bg-color, 15%) !important; &:hover { - background-color: darken(@bg-color, 20%); + background-color: darken(@bg-color, 20%) !important; } } } diff --git a/www/common/messenger-ui.js b/www/common/messenger-ui.js index eb9c33bb0..e11b33b93 100644 --- a/www/common/messenger-ui.js +++ b/www/common/messenger-ui.js @@ -21,7 +21,7 @@ define([ }; var initChannel = function (state, info) { - console.log('initializing channel for [%s]', info.id); + console.debug('initializing channel for [%s]', info.id); var h, t; if (Array.isArray(info.messages) && info.messages.length) { h = info.messages[info.messages.length -1].sig; @@ -31,8 +31,9 @@ define([ messages: info.messages || [], name: info.name, isFriendChat: info.isFriendChat, - needMoreHistory: !info.isPadChat, isPadChat: info.isPadChat, + isTeamChat: info.isTeamChat, + needMoreHistory: !info.isPadChat && !info.isTeamChat, curvePublic: info.curvePublic, HEAD: h || info.lastKnownHash, TAIL: t || null, @@ -507,7 +508,7 @@ define([ var rightCol = h('span.cp-app-contacts-right-col', [ h('span.cp-app-contacts-name', [room.name]), room.isFriendChat ? remove : - room.isPadChat ? undefined : leaveRoom, + (room.isPadChat || room.isTeamChat) ? undefined : leaveRoom, ]); var friendData = room.isFriendChat ? userlist[0] : {}; @@ -685,7 +686,7 @@ define([ execCommand('GET_USERLIST', {id: id}, function (e, list) { if (e || list.error) { return void console.error(e || list.error); } - if (!room.isPadChat && (!Array.isArray(list) || !list.length)) { + if (!room.isPadChat && !room.isTeamChat && (!Array.isArray(list) || !list.length)) { return void console.error("Empty room!"); } debug('userlist: ' + JSON.stringify(list), id); @@ -714,6 +715,8 @@ define([ var $parentEl; if (room.isFriendChat) { $parentEl = $userlist.find('.cp-app-contacts-friends'); + } else if (room.isTeamChat) { + $parentEl = $userlist.find('.cp-app-contacts-padchat'); // XXX } else if (room.isPadChat) { $parentEl = $userlist.find('.cp-app-contacts-padchat'); } else { @@ -725,7 +728,7 @@ define([ updateStatus(id); - if (isApp && room.isPadChat) { + if (isApp && (room.isPadChat || room.isTeamChat)) { $container.removeClass('cp-app-contacts-initializing'); display(room.id); } @@ -816,6 +819,21 @@ define([ }); }; + var onTeamChatReady = function (data) { + var teamChat = common.getTeamChat(); + if (data !== teamChat) { return; } + if (state.channels[data]) { return; } + execCommand('GET_ROOMS', {teamChat: data}, function (err, rooms) { + if (err) { return void console.error(err); } + if (!Array.isArray(rooms) || rooms.length !== 1) { + return void console.error('Invalid team chat'); + } + var room = rooms[0]; + room.name = 'TEAMS'; // XXX + rooms.forEach(initializeRoom); + }); + }; + var onDisconnect = function () { debug('disconnected'); $messages.find('.cp-app-contacts-input textarea').prop('disabled', true); @@ -841,6 +859,10 @@ define([ onPadChatReady(data); return; } + if (cmd === 'TEAMCHAT_READY') { + onTeamChatReady(data); + return; + } if (cmd === 'DISCONNECT') { onDisconnect(); return; diff --git a/www/common/outer/messenger.js b/www/common/outer/messenger.js index d19f3d809..d8ced9637 100644 --- a/www/common/outer/messenger.js +++ b/www/common/outer/messenger.js @@ -120,7 +120,7 @@ define([ var network = ctx.store.network; console.log('Fetching [%s] messages since [%s]', channel.id, data.lastKnownHash || ''); - if (channel.isPadChat) { + if (channel.isPadChat || channel.isTeamChat) { // We need to use GET_HISTORY_RANGE to make sure we won't get the full history var txid = Util.uid(); initRangeRequest(ctx, txid, channel.id, undefined); @@ -137,8 +137,6 @@ define([ return; } - // XXX team chat - // Friend chat, intial history var proxy = ctx.store.proxy; var friend = getFriendFromChannel(ctx, channel.id) || {}; @@ -165,6 +163,8 @@ define([ friend.lastKnownHash = hash; } else if (channel.isPadChat) { // Nothing to do + } else if (channel.isTeamChat) { + // Nothing to do } else { // TODO room return void cb({error: 'NOT_IMPLEMENTED'}); @@ -453,6 +453,7 @@ define([ id: data.channel, isFriendChat: data.isFriendChat, isPadChat: data.isPadChat, + isTeamChat: data.isTeamChat, padChan: data.padChan, // Channel ID of the pad linked to this pad chat readOnly: data.readOnly, ready: false, @@ -671,6 +672,17 @@ define([ }]); } + // Team chat room + if (data && data.teamChat) { + var tCChannel = ctx.channels[data.teamChat]; + if (!tCChannel) { return void cb({error: 'NO_SUCH_CHANNEL'}); } + return void cb([{ + id: tCChannel.id, + isTeamChat: true, + messages: tCChannel.messages + }]); + } + // Existing friends... var rooms = Object.keys(ctx.channels).map(function (id) { var r = ctx.channels[id]; @@ -683,6 +695,8 @@ define([ curvePublic = friend.curvePublic; } else if (r.isPadChat) { return; + } else if (r.isTeamChat) { + return; } else { // TODO room get metadata (name) && lastKnownHash } @@ -752,6 +766,52 @@ define([ openChannel(ctx, chanData); }; + var openTeamChat = function (ctx, clientId, data, _cb) { + var teams = ctx.store.modules['team']; + var team = teams.getTeam(data.teamId); + if (!team) { return; } + + var chatData = team.getChatData(); + var chanId = chatData.channel; + + var cb = Util.once(Util.mkAsync(function () { + ctx.emit('TEAMCHAT_READY', chanId, [clientId]); + _cb({ + channel: chanId + }); + })); + + var channel = ctx.channels[chanId]; + if (channel) { + return void channel.onReady.reg(function () { + if (channel.clients.indexOf(clientId) === -1) { + channel.clients.push(clientId); + } + cb(); + }); + } + + var secret = chatData.secret; + if (secret.keys.cryptKey) { + secret.keys.cryptKey = convertToUint8(secret.keys.cryptKey); + } + var encryptor = Crypto.createEncryptor(secret.keys); + var vKey = (secret.keys && secret.keys.validateKey) || chatData.validateKey; + var chanData = { + teamId: data.teamId, + readOnly: typeof(secret.keys) === "object" && !secret.keys.validateKey, + encryptor: encryptor, + channel: chanId, + isTeamChat: true, + decrypt: function (msg) { + return encryptor.decrypt(msg, vKey); + }, + clients: [clientId], + onReady: cb + }; + openChannel(ctx, chanData); + }; + var clearOwnedChannel = function (ctx, id, cb) { var channel = ctx.clients[id]; if (!channel) { return void cb({error: 'NO_CHANNEL'}); } @@ -877,6 +937,10 @@ define([ }); }; + messenger.openTeamChat = function (data, cId, cb) { + openTeamChat(ctx, cId, data, cb); + }; + messenger.removeClient = function (clientId) { removeClient(ctx, clientId); }; @@ -892,6 +956,9 @@ define([ if (cmd === 'GET_USERLIST') { return void getUserList(ctx, data, cb); } + if (cmd === 'OPEN_TEAM_CHAT') { + return void openTeamChat(ctx, clientId, data, cb); + } if (cmd === 'OPEN_PAD_CHAT') { return void openPadChat(ctx, clientId, data, cb); } diff --git a/www/common/outer/team.js b/www/common/outer/team.js index a683e9aaa..a70097b80 100644 --- a/www/common/outer/team.js +++ b/www/common/outer/team.js @@ -138,6 +138,19 @@ define([ })); }; + team.getChatData = function () { + var hash = Util.find(proxy, ['metadata', 'chat']); + if (!hash) { + hash = proxy.metadata.chat = Hash.createRandomHash('chat'); + } + var secret = Hash.getSecrets('chat', hash); + return { + channel: secret.channel, + secret: secret, + validateKey: secret.keys.validateKey + }; + }; + team.pin = function (data, cb) { return void cb({error: 'EFORBIDDEN'}); }; team.unpin = function (data, cb) { return void cb({error: 'EFORBIDDEN'}); }; nThen(function (waitFor) { @@ -276,6 +289,9 @@ define([ var membersSecret = Hash.getSecrets('members'); var membersHashes = Hash.getHashes(membersSecret); + var chatSecret = Hash.getSecrets('chat'); + var chatHashes = Hash.getHashes(chatSecret); + var config = { network: ctx.store.network, channel: secret.channel, @@ -319,6 +335,7 @@ define([ proxy.drive = {}; // Create metadata proxy.metadata = { + chat: chatHashes.editHash, name: name, members: membersHashes.viewHash, }; @@ -424,7 +441,7 @@ define([ Object.keys(teams).forEach(function (id) { ctx.onReadyHandlers[id] = []; openChannel(ctx, teams[id], id, waitFor(function () { - console.error('team '+id+' ready'); + console.debug('Team '+id+' ready'); })); }); @@ -448,7 +465,6 @@ define([ removeClient(ctx, clientId); }; team.execCommand = function (clientId, obj, cb) { - console.log(obj); var cmd = obj.cmd; var data = obj.data; if (cmd === 'SUBSCRIBE') { @@ -458,6 +474,9 @@ define([ if (cmd === 'LIST_TEAMS') { return void cb(store.proxy.teams); } + if (cmd === 'OPEN_TEAM_CHAT') { + return void ctx.store.messenger.openTeamChat(data, clientId, cb); + } if (cmd === 'CREATE_TEAM') { return void createTeam(ctx, data, clientId, cb); } diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 57571b6b0..8f83c4443 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -212,6 +212,15 @@ define([ }); }; + // Team Chat + var teamChatChannel; + funcs.setTeamChat = function (channel) { + teamChatChannel = channel; + }; + funcs.getTeamChat = function () { + return teamChatChannel; + }; + var cursorChannel; // common-ui-elements needs to be able to get the cursor channel to put it in metadata when // importing a template diff --git a/www/team/app-team.less b/www/team/app-team.less index 4a25cefe3..c1412b4dc 100644 --- a/www/team/app-team.less +++ b/www/team/app-team.less @@ -1,5 +1,6 @@ @import (reference) '../../customize/src/less2/include/framework.less'; @import (reference) '../../customize/src/less2/include/drive.less'; +@import (reference) '../../customize/src/less2/include/messenger.less'; @import (reference) '../../customize/src/less2/include/sidebar-layout.less'; &.cp-app-team { @@ -10,11 +11,20 @@ ); .drive_main(); + .messenger_main(); .sidebar-layout_main(); #cp-sidebarlayout-container { div#cp-sidebarlayout-rightside.cp-rightside-drive { padding: 0; + & > .cp-team-chat { + display: flex; + height: 100%; + margin: 0; + .cp-app-contacts-container { + height: 100%; + } + } & > .cp-team-drive { display: flex; height: 100%; diff --git a/www/team/inner.js b/www/team/inner.js index d39bc2fc7..1c99bd11f 100644 --- a/www/team/inner.js +++ b/www/team/inner.js @@ -10,6 +10,7 @@ define([ '/common/proxy-manager.js', '/common/hyperscript.js', '/customize/application_config.js', + '/common/messenger-ui.js', '/customize/messages.js', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', @@ -27,6 +28,7 @@ define([ ProxyManager, h, AppConfig, + MessengerUI, Messages) { var APP = {}; @@ -101,6 +103,9 @@ define([ 'members': [ 'cp-team-roster' ], + 'chat': [ + 'cp-team-chat' + ], }; var create = {}; @@ -131,6 +136,7 @@ define([ if (key === 'create') { $category.append($('', {'class': 'fa fa-plus-circle'})); } if (key === 'back') { $category.append($('', {'class': 'fa fa-arrow-left'})); } if (key === 'members') { $category.append($('', {'class': 'fa fa-users'})); } + if (key === 'chat') { $category.append($('', {'class': 'fa fa-comments'})); } if (key === 'drive') { $category.append($('', {'class': 'fa fa-hdd-o'})); } if (key === active) { @@ -144,7 +150,7 @@ define([ } if (active === key) { return; } active = key; - if (key === 'drive') { + if (key === 'drive' || key === 'chat') { APP.$rightside.addClass('cp-rightside-drive'); } else { APP.$rightside.removeClass('cp-rightside-drive'); @@ -326,6 +332,18 @@ define([ loadTeam(common, APP.team, false); }); + makeBlock('chat', function (common, cb) { + var container = h('div#cp-app-contacts-container.cp-app-contacts-inapp'); + var content = [container]; + APP.module.execCommand('OPEN_TEAM_CHAT', { + teamId: APP.team + }, function (obj) { + console.warn(obj); + common.setTeamChat(obj.channel); + MessengerUI.create($(container), common, true); + cb(content); + }); + }); var onEvent = function (obj) { var ev = obj.ev;