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.
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
or left the current group. The `onchat` callback indicates that a chat
message has been posted to the group, and `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.
or left the current group, or that their attributes have changed; the
user's state can be found in the `users` dictionary. The `onchat`
callback indicates that a chat message has been posted to the group, and
`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.
......
......@@ -115,9 +115,10 @@ users a `user` message:
```javascript
{
type: 'user',
kind: 'add' or 'delete',
kind: 'add' or 'change' or 'delete',
id: id,
username: username
username: username,
permissions: permissions
}
```
......
......@@ -71,7 +71,7 @@ func (client *Client) Permissions() 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
}
......
......@@ -100,6 +100,6 @@ type Client interface {
SetPermissions(ClientPermissions)
OverridePermissions(*Group) bool
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
}
......@@ -485,11 +485,13 @@ func AddClient(group string, c Client) (*Group, error) {
g.clients[c.Id()] = c
g.timestamp = time.Now()
id := c.Id()
u := c.Username()
c.PushClient(c.Id(), u, true)
p := c.Permissions()
c.PushClient(c.Id(), u, p, "add")
for _, cc := range clients {
c.PushClient(cc.Id(), cc.Username(), true)
cc.PushClient(c.Id(), u, true)
c.PushClient(cc.Id(), cc.Username(), cc.Permissions(), "add")
cc.PushClient(id, u, p, "add")
}
return g, nil
......@@ -535,7 +537,7 @@ func DelClient(c Client) {
go func(clients []Client) {
for _, cc := range clients {
cc.PushClient(c.Id(), c.Username(), false)
cc.PushClient(c.Id(), c.Username(), c.Permissions(), "delete")
}
}(clients)
......
......@@ -106,16 +106,13 @@ func (c *webClient) OverridePermissions(g *group.Group) bool {
return false
}
func (c *webClient) PushClient(id, username string, add bool) error {
kind := "add"
if !add {
kind = "delete"
}
func (c *webClient) PushClient(id, username string, permissions group.ClientPermissions, kind string) error {
return c.write(clientMessage{
Type: "user",
Kind: kind,
Id: id,
Username: username,
Type: "user",
Kind: kind,
Id: id,
Username: username,
Permissions: &permissions,
})
}
......@@ -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:
return group.KickError{
a.id, a.username, a.message,
......
......@@ -293,13 +293,11 @@ function setConnected(connected) {
let userbox = document.getElementById('profile');
let connectionbox = document.getElementById('login-container');
if(connected) {
resetUsers();
clearChat();
userbox.classList.remove('invisible');
connectionbox.classList.add('invisible');
displayUsername();
} else {
resetUsers();
fillLogin();
userbox.classList.add('invisible');
connectionbox.classList.remove('invisible');
......@@ -1654,9 +1652,6 @@ function resizePeers() {
}
}
/** @type{Object<string,string>} */
let users = {};
/**
* Lexicographic order, with case differences secondary.
* @param{string} a
......@@ -1683,9 +1678,6 @@ function stringCompare(a, b) {
function addUser(id, name) {
if(!name)
name = null;
if(id in users)
throw new Error('Duplicate user id');
users[id] = name;
let div = document.getElementById('users');
let user = document.createElement('div');
......@@ -1697,7 +1689,9 @@ function addUser(id, name) {
let us = div.children;
for(let i = 0; i < us.length; 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) {
div.insertBefore(user, child);
return;
......@@ -1711,36 +1705,38 @@ function addUser(id, name) {
* @param {string} id
* @param {string} name
*/
function delUser(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');
function changeUser(id, name) {
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)
delUser(id, users[id]);
/**
* @param {string} id
*/
function delUser(id) {
let div = document.getElementById('users');
let user = document.getElementById('user-' + id);
div.removeChild(user);
}
/**
* @param {string} id
* @param {string} kind
* @param {string} name
*/
function gotUser(id, kind, name) {
function gotUser(id, kind) {
switch(kind) {
case 'add':
addUser(id, name);
addUser(id, serverConnection.users[id].username);
break;
case 'delete':
delUser(id, name);
delUser(id);
break;
case 'change':
changeUser(id, serverConnection.users[id].username);
break;
default:
console.warn('Unknown user kind', kind);
......@@ -1986,8 +1982,10 @@ function addToChatbox(peerId, dest, nick, time, privileged, kind, message) {
let header = document.createElement('p');
if(peerId || nick || dest) {
let user = document.createElement('span');
let u = serverConnection.users[dest];
let name = (u && u.username);
user.textContent = dest ?
`${nick||'(anon)'} \u2192 ${users[dest]||'(anon)'}` :
`${nick||'(anon)'} \u2192 ${name || '(anon)'}` :
(nick || '(anon)');
user.classList.add('message-user');
header.appendChild(user);
......@@ -2246,11 +2244,12 @@ function parseCommand(line) {
* @param {string} user
*/
function findUserId(user) {
if(user in users)
if(user in serverConnection.users)
return user;
for(let id in users) {
if(users[id] === user)
for(let id in serverConnection.users) {
let u = serverConnection.users[id];
if(u && u.username === user)
return id;
}
return null;
......
......@@ -60,6 +60,12 @@ function newLocalId() {
return id;
}
/**
* @typedef {Object} user
* @property {string} username
* @property {Object<string,boolean>} permissions
*/
/**
* ServerConnection encapsulates a websocket connection to the server and
* all the associated streams.
......@@ -81,8 +87,16 @@ function ServerConnection() {
this.group = null;
/**
* The username we joined as.
*
* @type {string}
*/
this.username = null;
/**
* The set of users in this group, including ourself.
*
* @type {Object<string,user>}
*/
this.users = {};
/**
* The underlying websocket.
*
......@@ -136,9 +150,10 @@ function ServerConnection() {
*/
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;
/**
......@@ -260,6 +275,11 @@ ServerConnection.prototype.connect = async function(url) {
let c = sc.down[id];
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)
sc.onjoined.call(sc, 'leave', sc.group, {}, '');
sc.group = null;
......@@ -303,14 +323,51 @@ ServerConnection.prototype.connect = async function(url) {
sc.username = m.username;
sc.permissions = m.permissions || [];
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)
sc.onjoined.call(sc, m.kind, m.group,
m.permissions || {},
m.value || null);
break;
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)
sc.onuser.call(sc, m.id, m.kind, m.username);
sc.onuser.call(sc, m.id, m.kind);
break;
case 'chat':
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