discord-client/webpack/lib/content-controller.js

483 lines
9.9 KiB
JavaScript
Raw Normal View History

export default class ContentController{
2018-03-22 13:57:03 +00:00
/* (1) Construct default attributes
*
---------------------------------------------------------*/
constructor(){
/* (1) Websocket re-connection policy */
this.attempt = {
max: 3,
count: 0,
_default_timeout: 500, // in ms
get timeout(){
// return timeout + increment it for next time
return this._default_timeout * Math.pow(2, this.count++);
}
};
/* (2) Manage updates in connection status (ONLINE - OFFLINE) */
window.addEventListener('online', this.ws_connect.bind(this));
window.addEventListener('offline', function(){ gs.get.connection = 0; }); // connection status bar
2018-03-22 13:57:03 +00:00
}
2018-03-22 13:57:03 +00:00
/* (2) Channel bindings
*
---------------------------------------------------------*/
get cid(){ return gs.get.channel.current; }
2018-03-22 13:57:03 +00:00
get cbuf(){ return gs.get.channel._buffer; }
2018-03-28 13:54:19 +00:00
2018-03-22 13:57:03 +00:00
/* (3) Room ID binding
2018-03-22 13:57:03 +00:00
*
---------------------------------------------------------*/
get rid(){ return gs.get.room.text.current; }
get rbuf(){ return gs.get.room._buffer.text; }
get messages(){ return this.rbuf.messages; }
get members(){ return this.rbuf.members; }
// current user data
get uid(){ return gs.get.auth.user.uid; }
get ubuf(){ return gs.get.auth.user; }
/* (5) User getter
*
* @user_id<int> User id
*
* @return user<array> User data
*
---------------------------------------------------------*/
user(user_id=null){
/* (1) Error: if invalid user_id */
if( isNaN(user_id) )
return {};
/* (2) Error: unknown user */
if( this.cbuf.users == null || this.cbuf.users.length < 1 )
return {};
/* (3) return user data */
for( let u of this.cbuf.users )
if( u.uid === user_id )
return u;
/* (4) Error */
return {};
}
/* (6) Change username
*
* @username<String> New username
---------------------------------------------------------*/
change_username(username=null){
/* (1) Error: if invalid user_id */
if( typeof username !== 'string' )
return false;
/* (2) Error: unknown user */
if( this.uid == null )
return false;
/* (3) Call api UPDATE */
api.call(`PUT /user/${this.uid}`, { username: username }, function(rs){
gs.get.popup.hide();
// manage error
if( rs.error !== 0 )
return;
// update global username
let tmp_user = auth.user;
tmp_user.username = username;
auth.user = tmp_user;
}.bind(this), auth.token);
/* (4) Error */
return true;
}
/* (7) Change password
*
* @password<String> New password
*
---------------------------------------------------------*/
change_password(password=null){
/* (1) Error: if invalid user_id */
2018-03-28 23:45:18 +00:00
if( typeof password !== 'string' )
return false;
/* (2) Error: unknown user */
if( this.uid == null )
return false;
/* (3) Call api UPDATE */
api.call(`PUT /user/${this.uid}`, { password: password }, () => gs.get.popup.hide(), auth.token);
/* (4) Error */
return true;
}
/* (8) Send message
*
---------------------------------------------------------*/
send_message(_msg=null){
/* (1) Manage invalid _msg */
if( typeof _msg !== 'string' || _msg.length <= 0 )
return true;
/* (2) Send message */
2018-04-03 13:37:18 +00:00
window.csock.send({ buffer: {
rid: this.rid,
mid: null,
message: _msg
}});
return true;
}
/* (9) Websocket connection / reconnection
2018-04-03 13:37:18 +00:00
*
---------------------------------------------------------*/
ws_connect(){
console.warn(`new ws(/channel/${this.cid})`);
gs.get.connection = 1;
// 1. Close websocket if exists
if( window.csock instanceof wscd ){
csock.onclose = function(){}; // stop propagating recursive ws_connect()
csock.close();
}
// 2. Create new connection
window.csock = new wscd(`wss://ws.douscord.xdrm.io/channel/${this.cid}`, { token: auth.token });
// 3. Bind events
csock.onconnected = () => {
// show connection status
gs.get.connection = 2;
// reset attempt count
gs.get.content.attempt.count = 0;
setTimeout( () => { gs.get.connection = null; }, 1500);
};
csock.onreceive = gs.get.content.ws_handler.bind({ event: 'receive' });
csock.onclose = gs.get.content.ws_handler.bind({ event: 'close' });
// 4. Start communication
csock.bind();
}
/* (10) Websocket connection manager
*
* @this.event<String> Event type :
* 'close' -> socket closed
* 'receive' -> received message
*
---------------------------------------------------------*/
ws_handler(_response){
console.warn('ws(', this.event, _response || '', ')');
/* (1) Manage error */
if( this.event === null )
return;
/* (2) CLOSE event -> reconnect
---------------------------------------------------------*/
if( this.event === 'close' ){
// 1. update connection status bar
gs.get.connection = 0;
// 2. Do nothing if offline (online trigger will do the job when online again)
if( !navigator.onLine )
return;
// 3. if max attempt exceeded -> logout user
if( gs.get.content.attempt.count >= gs.get.content.attempt.max-1 ){
auth.token = null;
gs.get.refresh();
return;
}
// 4. Try to reconnect
return setTimeout(gs.get.content.ws_connect.bind(gs.get.content), gs.get.content.attempt.timeout);
}
/* (3) RECEIVE event
---------------------------------------------------------*/
if( this.event === 'receive' ){
/* (1) Communication error -> reconnect in 500ms */
if( typeof _response !== 'object' )
return setTimeout(gs.get.content.ws_connect.bind(gs.get.content), 500);
/* (2) If message update -> update interface model */
gs.get.content.ws_to_model(_response);
}
}
/* (11) MAIN UPDATER
*
---------------------------------------------------------*/
ws_to_model(_dat){
2018-04-03 13:37:18 +00:00
/* (1) Manage rooms DELETE
---------------------------------------------------------*/
/* (1) Extract ids */
let room_ids = Object.keys(_dat.room).map( (v) => parseInt(v) );
let current_list = gs.get.room;
2018-04-03 13:37:18 +00:00
/* (2) Manage DELETED rooms */
for( let t in current_list ){
for( let ri in current_list[t].list ){
// if existing room is not in received keys -> has been deleted
let to_remove = room_ids.indexOf(current_list[t].list[ri].id) < 0;
// delete room from interface
( to_remove ) && current_list[t].list.splice(ri,1);
}
}
/* (2) Manage rooms CREATE + UPDATE
---------------------------------------------------------*/
for( let ri of room_ids ){
// 1. Extract room data
let room = _dat.room[ri];
// 2. if room data is null -> ignore
if( room === null )
2018-04-03 13:37:18 +00:00
continue;
// 3. Manage room 'type'
room.type = (room.type === 0) ? 'text' : 'voice';
// 4. Check whether room already exists in interface
let existing_index = -1;
main_loop: for( let t in current_list ){
for( let r in current_list[t].list ){
if( current_list[t].list[r].id === ri ){
existing_index = r;
break main_loop;
}
}
}
2018-04-03 13:37:18 +00:00
// 5. Create room
if( existing_index < 0 ){
console.log(`create room#${ri} of type ${room.type}`);
gs.get.room.dump([{
rid: ri,
name: room.name,
messages: room.messages,
members: room.members,
type: room.type
}], true);
continue;
}
2018-04-03 13:37:18 +00:00
// 5. Update room
current_list[room.type].list[existing_index].name = room.name;
current_list[room.type].list[existing_index].members = room.members;
// 6. We are done if VOICE room
if( room.type === 'voice' )
continue;
2018-04-03 13:37:18 +00:00
// 7. Push new messages
for( let m of room.messages ){
2018-04-03 13:37:18 +00:00
current_list[room.type].list[existing_index].messages.push({
uid: m.uid,
mid: m.mid,
msg: m.content,
ts: m.ts
});
2018-04-03 13:37:18 +00:00
}
// 8. Notification API -> if not current channel
if( room.messages.length > 0 && ri !== gs.get.content.rid ){
let title = `Room #${room.name}`;
let body = `${room.messages.length} new messages`;
new Notification(title, { body: body });
}
2018-04-03 13:37:18 +00:00
}
/* (3) Manage channels DELETE
---------------------------------------------------------*/
for( let c of _dat.channels.rem ){
for( let ci in gs.get.channel.list ){
// 1. Local copy channel data
let channel = gs.get.channel.list[ci];
// 2. If id matches -> REMOVE
if( channel.id === c.id ){
// 2.1. If remove CURRENT channel -> nav to channel 1
if( channel.id === gs.get.content.cid )
gs.get.channel.nav(1);
// 2.2. Delete channel
gs.get.channel.list.splice(ci, 1);
}
}
}
/* (4) Manage channels CREATE
---------------------------------------------------------*/
for( let c of _dat.channels.add ){
gs.get.channel.dump([{
id: parseInt(c.id),
label: c.name,
link: c.link
}], true);
}
/* (5) Manage channels UPDATE
---------------------------------------------------------*/
for( let c of _dat.channels.upd ){
for( let ci in gs.get.channel.list ){
// 1. Local copy channel data
let channel = gs.get.channel.list[ci];
// 2. If id matches -> UPDATE
if( channel.id === c.id ){
gs.get.channel.list[ci].label = c.name;
gs.get.channel.list[ci].link = c.link;
}
}
}
let userset = gs.get.content.cbuf.users;
/* (6) Manage users DELETE
---------------------------------------------------------*/
for( let u of _dat.users.rem ){
for( let ui in userset ){
// 1. Local copy user data
let user = userset[ui];
// 2. If id matches -> REMOVE
if( user.uid === u.id )
userset.splice(ui, 1);
}
}
/* (7) Manage users CREATE
---------------------------------------------------------*/
for( let u of _dat.users.add ){
userset.push({
uid: parseInt(u.id),
username: u.name
});
}
/* (8) Manage users UPDATE
---------------------------------------------------------*/
for( let u of _dat.users.upd ){
for( let ui in userset ){
// 1. Local copy user data
let user = userset[ui];
// 2. If id matches -> UPDATE
if( user.uid === u.id )
userset[ui].username = u.name;
}
}
2018-04-03 13:37:18 +00:00
}
2018-03-22 13:57:03 +00:00
}