Commit f0a39fca authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Send user permissions to client.

We now maintain the user list in the serverConnection.
parent d0ef6a2c
...@@ -47,10 +47,12 @@ from the server; the field `kind` indicates the kind of message. ...@@ -47,10 +47,12 @@ from the server; the field `kind` indicates the kind of message.
Once you have joined a group (see below), the remaining callbacks may Once you have joined a group (see below), the remaining callbacks may
trigger. The `onuser` callback is used to indicate that a user has joined trigger. The `onuser` callback is used to indicate that a user has joined
or left the current group. The `onchat` callback indicates that a chat or left the current group, or that their attributes have changed; the
message has been posted to the group, and `onclearchat` indicates that the user's state can be found in the `users` dictionary. The `onchat`
chat history has been cleared. Finally, `ondownstream` is called when the callback indicates that a chat message has been posted to the group, and
server pushes a stream to the client; see the section below about streams. `onclearchat` indicates that the chat history has been cleared. Finally,
`ondownstream` is called when the server pushes a stream to the client;
see the section below about streams.
You may now connect to the server. You may now connect to the server.
......
...@@ -115,9 +115,10 @@ users a `user` message: ...@@ -115,9 +115,10 @@ users a `user` message:
```javascript ```javascript
{ {
type: 'user', type: 'user',
kind: 'add' or 'delete', kind: 'add' or 'change' or 'delete',
id: id, id: id,
username: username username: username,
permissions: permissions
} }
``` ```
......
...@@ -71,7 +71,7 @@ func (client *Client) Permissions() group.ClientPermissions { ...@@ -71,7 +71,7 @@ func (client *Client) Permissions() group.ClientPermissions {
return group.ClientPermissions{} return group.ClientPermissions{}
} }
func (client *Client) PushClient(id, username string, add bool) error { func (client *Client) PushClient(id, username string, permissions group.ClientPermissions, kind string) error {
return nil return nil
} }
......
...@@ -100,6 +100,6 @@ type Client interface { ...@@ -100,6 +100,6 @@ type Client interface {
SetPermissions(ClientPermissions) SetPermissions(ClientPermissions)
OverridePermissions(*Group) bool OverridePermissions(*Group) bool
PushConn(g *Group, id string, conn conn.Up, tracks []conn.UpTrack, replace string) error PushConn(g *Group, id string, conn conn.Up, tracks []conn.UpTrack, replace string) error
PushClient(id, username string, add bool) error PushClient(id, username string, permissions ClientPermissions, kind string) error
Kick(id, user, message string) error Kick(id, user, message string) error
} }
...@@ -485,11 +485,13 @@ func AddClient(group string, c Client) (*Group, error) { ...@@ -485,11 +485,13 @@ func AddClient(group string, c Client) (*Group, error) {
g.clients[c.Id()] = c g.clients[c.Id()] = c
g.timestamp = time.Now() g.timestamp = time.Now()
id := c.Id()
u := c.Username() u := c.Username()
c.PushClient(c.Id(), u, true) p := c.Permissions()
c.PushClient(c.Id(), u, p, "add")
for _, cc := range clients { for _, cc := range clients {
c.PushClient(cc.Id(), cc.Username(), true) c.PushClient(cc.Id(), cc.Username(), cc.Permissions(), "add")
cc.PushClient(c.Id(), u, true) cc.PushClient(id, u, p, "add")
} }
return g, nil return g, nil
...@@ -535,7 +537,7 @@ func DelClient(c Client) { ...@@ -535,7 +537,7 @@ func DelClient(c Client) {
go func(clients []Client) { go func(clients []Client) {
for _, cc := range clients { for _, cc := range clients {
cc.PushClient(c.Id(), c.Username(), false) cc.PushClient(c.Id(), c.Username(), c.Permissions(), "delete")
} }
}(clients) }(clients)
......
...@@ -106,16 +106,13 @@ func (c *webClient) OverridePermissions(g *group.Group) bool { ...@@ -106,16 +106,13 @@ func (c *webClient) OverridePermissions(g *group.Group) bool {
return false return false
} }
func (c *webClient) PushClient(id, username string, add bool) error { func (c *webClient) PushClient(id, username string, permissions group.ClientPermissions, kind string) error {
kind := "add"
if !add {
kind = "delete"
}
return c.write(clientMessage{ return c.write(clientMessage{
Type: "user", Type: "user",
Kind: kind, Kind: kind,
Id: id, Id: id,
Username: username, Username: username,
Permissions: &permissions,
}) })
} }
...@@ -989,6 +986,14 @@ func handleAction(c *webClient, a interface{}) error { ...@@ -989,6 +986,14 @@ func handleAction(c *webClient, a interface{}) error {
} }
} }
} }
id := c.Id()
user := c.Username()
clients := g.GetClients(nil)
go func(clients []group.Client) {
for _, cc := range clients {
cc.PushClient(id, user, perms, "change")
}
}(clients)
case kickAction: case kickAction:
return group.KickError{ return group.KickError{
a.id, a.username, a.message, a.id, a.username, a.message,
......
...@@ -293,13 +293,11 @@ function setConnected(connected) { ...@@ -293,13 +293,11 @@ function setConnected(connected) {
let userbox = document.getElementById('profile'); let userbox = document.getElementById('profile');
let connectionbox = document.getElementById('login-container'); let connectionbox = document.getElementById('login-container');
if(connected) { if(connected) {
resetUsers();
clearChat(); clearChat();
userbox.classList.remove('invisible'); userbox.classList.remove('invisible');
connectionbox.classList.add('invisible'); connectionbox.classList.add('invisible');
displayUsername(); displayUsername();
} else { } else {
resetUsers();
fillLogin(); fillLogin();
userbox.classList.add('invisible'); userbox.classList.add('invisible');
connectionbox.classList.remove('invisible'); connectionbox.classList.remove('invisible');
...@@ -1654,9 +1652,6 @@ function resizePeers() { ...@@ -1654,9 +1652,6 @@ function resizePeers() {
} }
} }
/** @type{Object<string,string>} */
let users = {};
/** /**
* Lexicographic order, with case differences secondary. * Lexicographic order, with case differences secondary.
* @param{string} a * @param{string} a
...@@ -1683,9 +1678,6 @@ function stringCompare(a, b) { ...@@ -1683,9 +1678,6 @@ function stringCompare(a, b) {
function addUser(id, name) { function addUser(id, name) {
if(!name) if(!name)
name = null; name = null;
if(id in users)
throw new Error('Duplicate user id');
users[id] = name;
let div = document.getElementById('users'); let div = document.getElementById('users');
let user = document.createElement('div'); let user = document.createElement('div');
...@@ -1697,7 +1689,9 @@ function addUser(id, name) { ...@@ -1697,7 +1689,9 @@ function addUser(id, name) {
let us = div.children; let us = div.children;
for(let i = 0; i < us.length; i++) { for(let i = 0; i < us.length; i++) {
let child = us[i]; let child = us[i];
let childname = users[child.id.slice('user-'.length)] || null; let childuser =
serverConnection.users[child.id.slice('user-'.length)] || null;
let childname = (childuser && childuser.username) || null;
if(!childname || stringCompare(childname, name) > 0) { if(!childname || stringCompare(childname, name) > 0) {
div.insertBefore(user, child); div.insertBefore(user, child);
return; return;
...@@ -1711,36 +1705,38 @@ function addUser(id, name) { ...@@ -1711,36 +1705,38 @@ function addUser(id, name) {
* @param {string} id * @param {string} id
* @param {string} name * @param {string} name
*/ */
function delUser(id, name) { function changeUser(id, name) {
if(!name)
name = null;
if(!(id in users))
throw new Error('Unknown user id');
if(users[id] !== name)
throw new Error('Inconsistent user name');
delete(users[id]);
let div = document.getElementById('users');
let user = document.getElementById('user-' + id); let user = document.getElementById('user-' + id);
div.removeChild(user); if(!user) {
console.warn('Unknown user ' + id);
return;
}
user.textContent = name ? name : '(anon)';
} }
function resetUsers() { /**
for(let id in users) * @param {string} id
delUser(id, users[id]); */
function delUser(id) {
let div = document.getElementById('users');
let user = document.getElementById('user-' + id);
div.removeChild(user);
} }
/** /**
* @param {string} id * @param {string} id
* @param {string} kind * @param {string} kind
* @param {string} name
*/ */
function gotUser(id, kind, name) { function gotUser(id, kind) {
switch(kind) { switch(kind) {
case 'add': case 'add':
addUser(id, name); addUser(id, serverConnection.users[id].username);
break; break;
case 'delete': case 'delete':
delUser(id, name); delUser(id);
break;
case 'change':
changeUser(id, serverConnection.users[id].username);
break; break;
default: default:
console.warn('Unknown user kind', kind); console.warn('Unknown user kind', kind);
...@@ -1986,8 +1982,10 @@ function addToChatbox(peerId, dest, nick, time, privileged, kind, message) { ...@@ -1986,8 +1982,10 @@ function addToChatbox(peerId, dest, nick, time, privileged, kind, message) {
let header = document.createElement('p'); let header = document.createElement('p');
if(peerId || nick || dest) { if(peerId || nick || dest) {
let user = document.createElement('span'); let user = document.createElement('span');
let u = serverConnection.users[dest];
let name = (u && u.username);
user.textContent = dest ? user.textContent = dest ?
`${nick||'(anon)'} \u2192 ${users[dest]||'(anon)'}` : `${nick||'(anon)'} \u2192 ${name || '(anon)'}` :
(nick || '(anon)'); (nick || '(anon)');
user.classList.add('message-user'); user.classList.add('message-user');
header.appendChild(user); header.appendChild(user);
...@@ -2246,11 +2244,12 @@ function parseCommand(line) { ...@@ -2246,11 +2244,12 @@ function parseCommand(line) {
* @param {string} user * @param {string} user
*/ */
function findUserId(user) { function findUserId(user) {
if(user in users) if(user in serverConnection.users)
return user; return user;
for(let id in users) { for(let id in serverConnection.users) {
if(users[id] === user) let u = serverConnection.users[id];
if(u && u.username === user)
return id; return id;
} }
return null; return null;
......
...@@ -60,6 +60,12 @@ function newLocalId() { ...@@ -60,6 +60,12 @@ function newLocalId() {
return id; return id;
} }
/**
* @typedef {Object} user
* @property {string} username
* @property {Object<string,boolean>} permissions
*/
/** /**
* ServerConnection encapsulates a websocket connection to the server and * ServerConnection encapsulates a websocket connection to the server and
* all the associated streams. * all the associated streams.
...@@ -81,8 +87,16 @@ function ServerConnection() { ...@@ -81,8 +87,16 @@ function ServerConnection() {
this.group = null; this.group = null;
/** /**
* The username we joined as. * The username we joined as.
*
* @type {string}
*/ */
this.username = null; this.username = null;
/**
* The set of users in this group, including ourself.
*
* @type {Object<string,user>}
*/
this.users = {};
/** /**
* The underlying websocket. * The underlying websocket.
* *
...@@ -136,9 +150,10 @@ function ServerConnection() { ...@@ -136,9 +150,10 @@ function ServerConnection() {
*/ */
this.onclose = null; this.onclose = null;
/** /**
* onuser is called whenever a user is added or removed from the group * onuser is called whenever a user in the group changes. The users
* array has already been updated.
* *
* @type{(this: ServerConnection, id: string, kind: string, username: string) => void} * @type{(this: ServerConnection, id: string, kind: string) => void}
*/ */
this.onuser = null; this.onuser = null;
/** /**
...@@ -260,6 +275,11 @@ ServerConnection.prototype.connect = async function(url) { ...@@ -260,6 +275,11 @@ ServerConnection.prototype.connect = async function(url) {
let c = sc.down[id]; let c = sc.down[id];
c.close(); c.close();
} }
for(let id in sc.users) {
delete(sc.users[id]);
if(sc.onuser)
sc.onuser.call(sc, id, 'delete');
}
if(sc.group && sc.onjoined) if(sc.group && sc.onjoined)
sc.onjoined.call(sc, 'leave', sc.group, {}, ''); sc.onjoined.call(sc, 'leave', sc.group, {}, '');
sc.group = null; sc.group = null;
...@@ -303,14 +323,51 @@ ServerConnection.prototype.connect = async function(url) { ...@@ -303,14 +323,51 @@ ServerConnection.prototype.connect = async function(url) {
sc.username = m.username; sc.username = m.username;
sc.permissions = m.permissions || []; sc.permissions = m.permissions || [];
sc.rtcConfiguration = m.rtcConfiguration || null; sc.rtcConfiguration = m.rtcConfiguration || null;
if(m.kind == 'leave') {
for(let id in sc.users) {
delete(sc.users[id]);
if(sc.onuser)
sc.onuser.call(sc, id, 'delete');
}
}
if(sc.onjoined) if(sc.onjoined)
sc.onjoined.call(sc, m.kind, m.group, sc.onjoined.call(sc, m.kind, m.group,
m.permissions || {}, m.permissions || {},
m.value || null); m.value || null);
break; break;
case 'user': case 'user':
switch(m.kind) {
case 'add':
if(m.id in sc.users)
console.warn(`Duplicate user ${m.id} ${m.username}`);
sc.users[m.id] = {
username: m.username,
permissions: m.permissions,
};
break;
case 'change':
if(!(m.id in sc.users)) {
console.warn(`Unknown user ${m.id} ${m.username}`);
sc.users[m.id] = {
username: m.username,
permissions: m.permissions,
};
} else {
sc.users[m.id].username = m.username;
sc.users[m.id].permissions = m.permissions;
}
break;
case 'delete':
if(!(m.id in sc.users))
console.warn(`Unknown user ${m.id} ${m.username}`);
delete(sc.users[m.id]);
break;
default:
console.warn(`Unknown user action ${m.kind}`);
return;
}
if(sc.onuser) if(sc.onuser)
sc.onuser.call(sc, m.id, m.kind, m.username); sc.onuser.call(sc, m.id, m.kind);
break; break;
case 'chat': case 'chat':
if(sc.onchat) if(sc.onchat)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment