export default class ContentController{ /* (1) Construct default attributes * ---------------------------------------------------------*/ constructor(){} /* (2) Channel bindings * ---------------------------------------------------------*/ get cid(){ return gs.get.channel.current; } get cbuf(){ return gs.get.channel._buffer; } /* (3) Room ID binding * ---------------------------------------------------------*/ 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 User id * * @return user 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 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; // update username in channel for( let u in this.cbuf.users ) if( this.cbuf.users[u].uid == this.uid ) this.cbuf.users[u].username = username; }.bind(this), auth.token); /* (4) Error */ return true; } /* (7) Change password * * @password New password * ---------------------------------------------------------*/ change_password(password=null){ /* (1) Error: if invalid user_id */ 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 */ window.csock.send({ buffer: { rid: this.rid, mid: null, message: _msg }}); return true; } /* (9) Websocket connection / reconnection * ---------------------------------------------------------*/ ws_connect(){ console.warn(`new ws(/channel/${this.cid})`); // 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.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 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 in 500ms ---------------------------------------------------------*/ if( this.event === 'close' ) return setTimeout(gs.get.content.ws_connect.bind(gs.get.content), 500); /* (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){ /* (1) Manage rooms DELETE ---------------------------------------------------------*/ /* (1) Extract ids */ let room_ids = Object.keys(_dat.room).map( (v) => parseInt(v) ); let current_list = gs.get.room; /* (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 ) 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; } } } // 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; } // 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; // 7. Push new messages for( let m of room.messages ){ current_list[room.type].list[existing_index].messages.push({ uid: m.uid, mid: m.mid, msg: m.content, ts: m.ts }); } // 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 }); } } /* (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 ) gs.get.channel.list.splice(ci, 1); } } /* (4) Manage channels CREATE ---------------------------------------------------------*/ for( let c of _dat.channels.add ){ console.log(c); 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; } } } } }