From 30d9cf31c3a62e14d89d42f65c9b27e3345d5f0a Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 19 Sep 2019 13:30:31 +0200 Subject: [PATCH] Display roster in the team app --- www/common/outer/team.js | 49 ++++++++++++++ www/team/inner.js | 141 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+) diff --git a/www/common/outer/team.js b/www/common/outer/team.js index 6f22ae046..2785ee806 100644 --- a/www/common/outer/team.js +++ b/www/common/outer/team.js @@ -448,6 +448,16 @@ define([ }); }; + var getTeamRoster = function (ctx, data, cId, cb) { + var teamId = data.teamId; + if (!teamId) { return void cb({error: 'EINVAL'}); } + var team = ctx.teams[teamId]; + if (!team) { return void cb ({error: 'ENOENT'}); } + if (!team.roster) { return void cb({error: 'NO_ROSTER'}); } + var state = team.roster.getState() || {}; + cb(state.members || {}); + }; + var getTeamMetadata = function (ctx, data, cId, cb) { var teamId = data.teamId; if (!teamId) { return void cb({error: 'EINVAL'}); } @@ -474,6 +484,36 @@ define([ }); }; + var describeUser = function (ctx, data, cId, cb) { + var teamId = data.teamId; + if (!teamId) { return void cb({error: 'EINVAL'}); } + var team = ctx.teams[teamId]; + if (!team) { return void cb ({error: 'ENOENT'}); } + if (!team.roster) { return void cb({error: 'NO_ROSTER'}); } + if (!data.curvePublic || !data.data) { return void cb({error: 'MISSING_DATA'}); } + var obj = {}; + obj[data.curvePublic] = data.data; + team.roster.describe(obj, function (err) { + if (err) { return void cb({error: err}); } + cb(); + }); + }; + + // XXX Listen for changes to the roster pad to know if you've been removed + // XXX onReady, if you've been removed, leave the team + var removeUser = function (ctx, data, cId, cb) { + var teamId = data.teamId; + if (!teamId) { return void cb({error: 'EINVAL'}); } + var team = ctx.teams[teamId]; + if (!team) { return void cb ({error: 'ENOENT'}); } + if (!team.roster) { return void cb({error: 'NO_ROSTER'}); } + if (!data.curvePublic) { return void cb({error: 'MISSING_DATA'}); } + team.roster.remove([data.curvePublic], function (err) { + if (err) { return void cb({error: err}); } + cb(); + }); + }; + // Remove a client from all the team they're subscribed to var removeClient = function (ctx, cId) { Object.keys(ctx.onReadyHandlers).forEach(function (teamId) { @@ -592,12 +632,21 @@ define([ if (cmd === 'OPEN_TEAM_CHAT') { return void openTeamChat(ctx, data, clientId, cb); } + if (cmd === 'GET_TEAM_ROSTER') { + return void getTeamRoster(ctx, data, clientId, cb); + } if (cmd === 'GET_TEAM_METADATA') { return void getTeamMetadata(ctx, data, clientId, cb); } if (cmd === 'SET_TEAM_METADATA') { return void setTeamMetadata(ctx, data, clientId, cb); } + if (cmd === 'DESCRIBE_USER') { + return void describeUser(ctx, data, clientId, cb); + } + if (cmd === 'REMOVE_USER') { + return void removeUser(ctx, data, clientId, cb); + } if (cmd === 'CREATE_TEAM') { return void createTeam(ctx, data, clientId, cb); } diff --git a/www/team/inner.js b/www/team/inner.js index 9b9080fd1..d550eae35 100644 --- a/www/team/inner.js +++ b/www/team/inner.js @@ -354,6 +354,147 @@ define([ loadTeam(common, APP.team, false); }); + var redrawRoster = function (common, _$roster) { + var $roster = _$roster || $('#cp-team-roster-container'); + if (!$roster.length) { return; } + APP.module.execCommand('GET_TEAM_ROSTER', { + teamId: APP.team + }, function (obj) { + if (obj && obj.error) { + return void UI.warn(Messages.error); + } + var roster = APP.refreshRoster(common, obj); + $roster.empty().append(roster); + }); + }; + + var ROLES = ['MEMBER', 'ADMIN', 'OWNER']; + var describeUser = function (data, icon) { + APP.module.execCommand('DESCRIBE_USER', { + teamId: APP.team, + curvePublic: data.curvePublic, + data: data + }, function (obj) { + if (obj && obj.error) { + $(icon).show(); + return void UI.alert(Messages.error); + } + redrawRoster(); + }); + }; + var makeMember = function (common, data, me) { + if (!data.curvePublic) { return; } + // Avatar + var avatar = h('span.cp-avatar.cp-team-member-avatar'); + common.displayAvatar($(avatar), data.avatar, data.displayName); + // Name + var name = h('span.cp-team-member-name', data.displayName); + // Actions + var actions = h('span.cp-team-member-actions'); + var $actions = $(actions); + var isMe = me && me.curvePublic === data.curvePublic; + var myRole = me ? (ROLES.indexOf(me.role) || 0) : -1; + var theirRole = ROLES.indexOf(data.role) || 0; + // If they're a member and I have a higher role than them, I can promote them to admin + if (!isMe && myRole > theirRole && theirRole === 0) { + var promote = h('fa.fa-angle-double-up', { + title: 'Promote' // XXX + }); + $(promote).click(function () { + data.role = 'ADMIN'; + $(promote).hide(); + describeUser(data, promote); + }); + $actions.append(promote); + } + // If I'm not a member and I have an equal or higher role than them, I can demote them + // (if they're not already a MEMBER) + if (!isMe && myRole >= theirRole && theirRole > 0) { + var demote = h('fa.fa-angle-double-down', { + title: 'Demote' // XXX + }); + $(demote).click(function () { + data.role = ROLES[theirRole - 1] || 'MEMBER'; + $(demote).hide(); + describeUser(data, demote); + }); + $actions.append(demote); + } + // If I'm not a member and I have an equal or higher role than them, I can remove them + if (!isMe && myRole > 0 && myRole >= theirRole) { + var remove = h('fa.fa-times', { + title: 'Remove' // XXX + }); + $(remove).click(function () { + $(remove).hide(); + APP.module.execCommand('REMOVE_USER', { + teamId: APP.team, + curvePublic: data.curvePublic, + }, function (obj) { + if (obj && obj.error) { + $(remove).show(); + return void UI.alert(Messages.error); + } + redrawRoster(); + }); + }); + $actions.append(remove); + } + + // User + var content = [ + avatar, + name, + actions + ]; + var div = h('div.cp-team-roster-member', content); + if (data.profile) { + $(div).click(function (e) { + e.preventDefault(); + e.stopPropagation(); + common.openURL('/profile/#' + data.profile); + }); + } + return div; + }; + var refreshRoster = APP.refreshRoster = function (common, roster) { + if (!roster || typeof(roster) !== "object" || Object.keys(roster) === 0) { return; } + var metadataMgr = common.getMetadataMgr(); + var privateData = metadataMgr.getPrivateData(); + var me = roster[privateData.curvePublic]; + var owner = Object.keys(roster).filter(function (k) { + return roster[k].role === "OWNER"; + }).map(function (k) { + return makeMember(common, roster[k], me); + }); + var admins = Object.keys(roster).filter(function (k) { + return roster[k].role === "ADMIN"; + }).map(function (k) { + return makeMember(common, roster[k], me); + }); + var members = Object.keys(roster).filter(function (k) { + return roster[k].role === "MEMBER" || !roster[k].role; + }).map(function (k) { + return makeMember(common, roster[k], me); + }); + // XXX LEAVE the team button + // XXX INVITE to the team button + return [ + h('h3', 'OWNER'), // XXX + h('div', owner), + h('h3', 'ADMINS'), // XXX + h('div', admins), + h('h3', 'MEMBERS'), // XXX + h('div', members) + ]; + }; + makeBlock('roster', function (common, cb) { + var container = h('div#cp-team-roster-container'); + var content = [container]; + redrawRoster(common, $(container)); + cb(content); + }); + makeBlock('chat', function (common, cb) { var container = h('div#cp-app-contacts-container.cp-app-contacts-inapp'); var content = [container];