From 09b0430c985b43fcb1f203cd490f1a67ef73fabc Mon Sep 17 00:00:00 2001 From: DerpgonCz Date: Thu, 14 Apr 2016 23:59:44 +0200 Subject: [PATCH 01/14] Public chat logging --- socketserver/db_level.js | 35 ++++++++++++++++- socketserver/db_mysql.js | 73 +++++++++++++++++++++++++++--------- socketserver/room.js | 73 ++++++++++++++++++------------------ socketserver/socketserver.js | 14 ++++--- 4 files changed, 135 insertions(+), 60 deletions(-) diff --git a/socketserver/db_level.js b/socketserver/db_level.js index b73004c..b084a1f 100644 --- a/socketserver/db_level.js +++ b/socketserver/db_level.js @@ -16,6 +16,7 @@ var DBUtils = require('./database_util'); //Variables var currentPID = 0; var currentUID = 0; +var currentCID = 0; var expires = 1000 * 60 * 60 * 24 * config.loginExpire; var usernames = []; @@ -118,6 +119,32 @@ function LevelDB(callback) { return false; }); }); + + //ChatDB + if(!this.ChatDB) + this.ChatDB = setupDB(dbdir + '/chat', + + //If new DB is created + function(newdb) { + currentCID = 1; + log.debug('CIDCOUNTER set to 1'); + newdb.put('CIDCOUNTER', 1); + }, + + //Callback + function(err, newdb) { + if (err) { + throw new Error('Could not open ChatDB: ' + err); + } + if (currentCID != 0) return; + + newdb.get('CIDCOUNTER', function(err, val) { + if (err) { + throw new Error('Cannot get CIDCOUNTER from PmDB. Might be corrupt'); + } + currentCID = parseInt(val); + }); + }); } function setupDB(dir, setup, callback){ @@ -583,4 +610,10 @@ LevelDB.prototype.userEmailExists = function(key, callback) { }); }; -module.exports = new LevelDB(); +//ChatDB +LevelDB.prototype.logChat = function(uid, msg, special, callback) { + this.putJSON(this.ChatDB, currentCID, { uid: uid, msg: msg, special: special }); + callback(currentCID++); +}; + +module.exports = new LevelDB(); \ No newline at end of file diff --git a/socketserver/db_mysql.js b/socketserver/db_mysql.js index e4bc8a5..139a1f5 100644 --- a/socketserver/db_mysql.js +++ b/socketserver/db_mysql.js @@ -76,7 +76,7 @@ var MysqlDB = function(){ PRIMARY KEY (`id`)\ );\ \ - CREATE TABLE IF NOT EXISTS `history` (\ + CREATE TABLE IF NOT EXISTS `history_dj` (\ `slug` VARCHAR(32) NOT NULL,\ `dj` int(10) unsigned DEFAULT NULL,\ `cid` varchar(32) DEFAULT NULL,\ @@ -88,10 +88,12 @@ var MysqlDB = function(){ `dislike` int(10) unsigned NOT NULL DEFAULT '0'\ );\ \ - CREATE TABLE IF NOT EXISTS `chat` (\ + CREATE TABLE IF NOT EXISTS `history_chat` (\ `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,\ - `msg` VARCHAR(256) NOT NULL DEFAULT 'NULL',\ + `msg` VARCHAR(256) NOT NULL,\ `uid` INTEGER UNSIGNED NULL DEFAULT NULL,\ + `special` VARCHAR(16) NULL DEFAULT NULL,\ + `time` DATETIME,\ PRIMARY KEY (`id`)\ );\ \ @@ -139,7 +141,7 @@ var MysqlDB = function(){ } } -MysqlDB.prototype.execute = function(query, vars, callback) { +MysqlDB.prototype.execute = function(query, vars, callback, trans) { callback = callback || function(){}; pool.getConnection(function(err, con){ @@ -148,10 +150,26 @@ MysqlDB.prototype.execute = function(query, vars, callback) { return; } - con.query(query, vars, function(err, rows){ - callback(err, rows); - con.release(); - }); + if(trans){ + con.beginTransaction(function(err) { + if (err) { callback(err); return } + + con.query(query, vars, function(err, rows) { + if(err){ + callback(err); + return con.rollback(); + } else { + callback(null, rows); + return con.commit(); + } + }); + }); + } else { + con.query(query, vars, function(err, rows){ + callback(err, rows); + con.release(); + }); + } }); }; @@ -262,14 +280,14 @@ MysqlDB.prototype.getRoom = function(slug, callback) { that.execute("\ SELECT\ - `history`.`cid`, UNIX_TIMESTAMP(`history`.`start`) as `start`, `history`.`title`, `history`.`duration`, `history`.`like`, `history`.`grab`, `history`.`dislike`,\ + `h`.`cid`, UNIX_TIMESTAMP(`h`.`start`) as `start`, `h`.`title`, `h`.`duration`, `h`.`like`, `h`.`grab`, `h`.`dislike`,\ `users`.`badge_top`, `users`.`badge_bottom`, IFNULL((SELECT `role` FROM `roles` WHERE `roles`.`uid` = `users`.`id` AND ?), 'default') as `role`, `users`.`un`, `users`.`id`\ FROM\ - `history`\ + `history_dj` AS `h`\ LEFT OUTER JOIN\ `users`\ ON\ - `users`.`id` = `history`.`dj`\ + `users`.`id` = `h`.`dj`\ WHERE\ ?\ ORDER BY `start` ASC;\ @@ -311,6 +329,8 @@ MysqlDB.prototype.getRoom = function(slug, callback) { MysqlDB.prototype.setRoom = function(slug, val, callback) { var that = this; + + callback = callback || function(){}; var outRoles = [], outBans = [], outHistory = []; @@ -327,7 +347,7 @@ MysqlDB.prototype.setRoom = function(slug, val, callback) { outHistory.push([ slug, obj.user.uid, obj.song.cid, new Date(obj.start), obj.song.duration, obj.song.title, obj.votes.like, obj.votes.grab, obj.votes.dislike ]); } - var query = "DELETE FROM `roles` WHERE ?; DELETE FROM `bans` WHERE ?;DELETE FROM `history` WHERE ?;"; + var query = "DELETE FROM `roles` WHERE ?; DELETE FROM `bans` WHERE ?;DELETE FROM `history_dj` WHERE ?;"; var params = [{ slug: slug, }, { slug: slug, }, { slug: slug, }]; if(outRoles.length){ @@ -339,11 +359,18 @@ MysqlDB.prototype.setRoom = function(slug, val, callback) { params.push([ 'slug', 'uid', 'uid_by', 'reason', 'start', 'end' ], outBans); } if(outHistory.length){ - query += "INSERT INTO `history`(??) VALUES ?;"; + query += "INSERT INTO `history_dj`(??) VALUES ?;"; params.push([ 'slug', 'dj', 'cid', 'start', 'duration', 'title', 'like', 'grab', 'dislike' ], outHistory); } - that.execute(query, params, callback); + that.execute(query, params, function(err, res){ + if(err){ + callback(err); + } else { + callback(null, res); + } + + }, true); return that; }; @@ -356,7 +383,7 @@ MysqlDB.prototype.deleteToken = function(tok) { MysqlDB.prototype.createToken = function(email) { var tok = DBUtils.makePass(email, Date.now()); - this.execute("DELETE FROM `tokens` WHERE ?; INSERT INTO `tokens` SET ?, `created` = FROM_UNIXTIME(?);", [ { email: email, }, { token: tok, email: email, }, Date.now() / 1000]); + this.execute("DELETE FROM `tokens` WHERE ?; INSERT INTO `tokens` SET ?;", [ { email: email, }, { token: tok, email: email, created: new Date() } ]); return tok; }; @@ -575,8 +602,8 @@ MysqlDB.prototype.putUser = function(email, data, callback) { delete newData.badge; delete newData.playlists; this.execute("INSERT INTO `users`(??) VALUES(?) ON DUPLICATE KEY UPDATE ?;", [ Object.keys(newData), _.values(newData), newData ], function(err, res) { - if(res.insertId) callback(null, res.insertId); - else callback(err, res); + if(err) callback(err); + else callback(null, res.insertId); }); }; @@ -693,4 +720,16 @@ MysqlDB.prototype.userEmailExists = function(key, callback) { }) }; +//ChatDB +MysqlDB.prototype.logChat = function(uid, msg, special, callback) { + this.execute("INSERT INTO `history_chat` SET ?;", { msg: msg, uid: uid, time: new Date(), special: special }, function(err, res){ + if(err){ + log.error("Error logging chat message"); + callback(err); + } else{ + callback(null, res.insertId); + } + }); +}; + module.exports = new MysqlDB; \ No newline at end of file diff --git a/socketserver/room.js b/socketserver/room.js index 9ece955..646b7c0 100644 --- a/socketserver/room.js +++ b/socketserver/room.js @@ -28,7 +28,6 @@ var Room = function(socketServer, options){ ownerEmail: "", // Owner email for owner promotion guestCanSeeChat: true, // Whether guests can see the chat or not bannedCanSeeChat: true, // Whether banned users can see the chat - chatID: 0, // Default CID roomOwnerUN: null, // Username of the room owner to use with lobby API }, options); @@ -443,47 +442,49 @@ Room.prototype.sendBroadcastMessage = function(message) { this.sendAll({type:'broadcastMessage', data:message}); }; -Room.prototype.sendMessage = function( sock, message, ext, specdata ){ +Room.prototype.sendMessage = function( sock, message, ext, specdata, callback ){ var that = this; - log.debug('Number of users: ' + this.attendeeList.length); message = message.substring(0,255).replace(//g, '>'); - this.roomInfo.chatID++; - - this.sendAll({ - type: 'chat', - data: { - uid: sock.user.uid, // Will always be present. Unauthd can't send messages - message: message, - time: Date.now(), - cid: this.roomInfo.chatID, - special: specdata - } - }, function(obj){ - // Guests can't see chat with config variable set - if (!that.roomInfo.guestCanSeeChat && !obj.user) return false; - - // Banned users can't see chat with config variable set - if (!that.roomInfo.bannedCanSeeChat && obj.user && that.isUserBanned(obj.user.uid)) return false; - - // Check for extensive function - if("function" === typeof ext) if(!ext(obj)) return false; - return true; - }); + callback = callback || function(){}; - //Save last X messages to show newly connected users - if(!specdata){ - this.lastChat.push({ - user: sock.user.getClientObj(), - message: message, - time: Date.now(), - cid: this.roomInfo.chatID + DB.logChat(sock.user.uid, message, specdata, function(cid){ + that.sendAll({ + type: 'chat', + data: { + uid: sock.user.uid, // Will always be present. Unauthd can't send messages + message: message, + time: Date.now(), + cid: cid, + special: specdata + } + }, function(obj){ + // Guests can't see chat with config variable set + if (!that.roomInfo.guestCanSeeChat && !obj.user) return false; + + // Banned users can't see chat with config variable set + if (!that.roomInfo.bannedCanSeeChat && obj.user && that.isUserBanned(obj.user.uid)) return false; + + // Check for extensive function + if("function" === typeof ext) if(!ext(obj)) return false; + + return true; }); - if(this.lastChat.length > config.room.lastmsglimit) this.lastChat.shift(); - } - - return this.roomInfo.chatID; + + //Save last X messages to show newly connected users + if(!specdata){ + that.lastChat.push({ + user: sock.user.getClientObj(), + message: message, + time: Date.now(), + cid: cid, + }); + if(that.lastChat.length > config.room.lastmsglimit) that.lastChat.shift(); + } + + callback(cid); + }); }; Room.prototype.makePrevChatObj = function(){ diff --git a/socketserver/socketserver.js b/socketserver/socketserver.js index 283e27a..1eb3bec 100644 --- a/socketserver/socketserver.js +++ b/socketserver/socketserver.js @@ -1400,12 +1400,14 @@ var SocketServer = function(server){ break; } - returnObj.data = { - success: true, - cid: that.room.sendMessage(socket, data.data.message) - }; - - socket.sendJSON(returnObj); + that.room.sendMessage(socket, data.data.message, null, null, function(cid){ + returnObj.data = { + success: true, + cid: cid, + }; + + socket.sendJSON(returnObj); + }); break; case 'staffchat': /* From 61500156fd7e0a8d67db4385ff2d21b8787e93de Mon Sep 17 00:00:00 2001 From: Christopher Nelson Date: Mon, 18 Apr 2016 17:18:19 +0100 Subject: [PATCH 02/14] Initial Commit of PM Update --- socketserver/db_mysql.js | 87 +++++++- socketserver/socketserver.js | 153 ++++++++++++- webserver/public/index.html | 50 ++++- webserver/public/lib/css/chat.css | 61 ++++- webserver/public/lib/js/app.js | 354 ++++++++++++++++++++++++++++-- 5 files changed, 663 insertions(+), 42 deletions(-) diff --git a/socketserver/db_mysql.js b/socketserver/db_mysql.js index 2138f8f..b34b72d 100644 --- a/socketserver/db_mysql.js +++ b/socketserver/db_mysql.js @@ -97,6 +97,16 @@ var MysqlDB = function(){ PRIMARY KEY (`id`)\ );\ \ + CREATE TABLE IF NOT EXISTS `history_pm` (\ + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,\ + `msg` VARCHAR(256) NOT NULL DEFAULT 'NULL',\ + `from` INTEGER UNSIGNED NOT NULL,\ + `to` INTEGER UNSIGNED NOT NULL,\ + `time` DATETIME,\ + `unread` INTEGER UNSIGNED NOT NULL DEFAULT 1,\ + PRIMARY KEY (`id`)\ + );\ + \ CREATE TABLE IF NOT EXISTS `tokens` (\ `email` VARCHAR(254) NOT NULL,\ `token` VARCHAR(32) NOT NULL,\ @@ -725,11 +735,84 @@ MysqlDB.prototype.logChat = function(uid, msg, special, callback) { this.execute("INSERT INTO `history_chat` SET ?;", { msg: msg, uid: uid, time: new Date(), special: special }, function(err, res){ if(err){ log.error("Error logging chat message"); - callback(err); + if (callback) callback(err); + } else{ + if (callback) callback(null, res.insertId); + } + }); +}; + +//PmDB +MysqlDB.prototype.logPM = function(from, to, msg, callback) { + this.execute("INSERT INTO `history_pm` SET ?;", { msg: msg, from: from, to: to, time: new Date() }, function(err, res){ + if(err){ + log.error("Error logging chat message"); + if (callback) callback(err); } else{ - callback(null, res.insertId); + if (callback) callback(null, res.insertId); } }); }; +MysqlDB.prototype.getConversation = function(from, to, callback) { + this.execute("SELECT * FROM `history_pm` WHERE (? AND ?) OR (? AND ?) ORDER BY `time` ASC LIMIT 512;", [ { from: from}, { to: to }, { from: to }, { to: from } ], function(err, res) { + if(err){ + callback(err); + } else { + var out = []; + for(var key in res){ + out.push({message:res[key].msg,time:res[key].time,from:res[key].from}); + } + callback(null, out); + } + }); +}; + +MysqlDB.prototype.getConversations = function(uid, callback) { + var that = this; + this.execute("SELECT *, SUM(IF(?, unread, 0)) as `unread_total` FROM (SELECT `history_pm`.*, LEAST(`from`, `to`) as `from_r`, GREATEST(`from`, `to`) as `to_r` FROM `history_pm` ORDER BY `time` DESC) as `h` WHERE ? OR ? GROUP BY `h`.`from_r`, `h`.`to_r`;", [ { to: uid }, { from_r: uid }, { to_r: uid } ], function(err, res) { + if (err){ + callback(err); + } else { + var out = {}; + var uids = []; + for (var key in res) { + var otherUid = res[key].to == uid ? res[key].from : res[key].to; + + if (out[otherUid] === undefined) { + uids.push(otherUid); + out[otherUid] = { + user: null, + messages: [], + unread: res[key].unread_total + }; + } + out[otherUid].messages.push({ message: res[key].msg, time: res[key].time, from: res[key].from }); + } + + if (uids.length > 0) { + that.getUserByUid(uids, function(err, result){ + if (err) { + callback(err); + } else { + for (var id in result) { + if (out[id]) { + out[id].user = result[id].getClientObj(); + } + } + callback(null, out); + } + }); + } + else { + callback(null, out); + } + } + }); +}; + +MysqlDB.prototype.markConversationRead = function(uid, uid2, time) { + this.execute("UPDATE history_pm SET `unread` = 0 WHERE time < ? AND `to` = ? AND `from` = ?;", [time, uid, uid2]) +}; + module.exports = new MysqlDB; \ No newline at end of file diff --git a/socketserver/socketserver.js b/socketserver/socketserver.js index cc0b6b8..6e43a83 100644 --- a/socketserver/socketserver.js +++ b/socketserver/socketserver.js @@ -1480,14 +1480,6 @@ var SocketServer = function(server){ socket.sendJSON(returnObj); break; } - var userSock = that.room.findSocketByUid(data.data.uid); - if (userSock == null) { - returnObj.data = { - error: 'UserNotInPad' - }; - socket.sendJSON(returnObj); - break; - } if (typeof data.data.message != 'string' || !data.data.message){ returnObj.data = { error: 'emptyMessage' @@ -1505,14 +1497,151 @@ var SocketServer = function(server){ }; socket.sendJSON(returnObj); - userSock.sendJSON({ - type: 'privateMessage', + var userSock = that.room.findSocketByUid(data.data.uid); + if (userSock != null) { + userSock.sendJSON({ + type: 'privateMessage', + data: { + uid: socket.user.uid, + message: msg + } + }); + } + DB.logPM(socket.user.uid, data.data.uid, msg); + break; + + case 'getConversations': + /* + Expected input object: + { + type: 'getConversations', data: { - uid: socket.user.uid, - message: msg } + } + */ + if (!socket.room) { + returnObj.data = { + error: 'NotInPad' + }; + socket.sendJSON(returnObj); + break; + } + + if (!Roles.checkPermission(socket.user.role, 'chat.private')){ + returnObj.data = { + error: 'InsufficientPermissions' + }; + socket.sendJSON(returnObj); + break; + } + DB.getConversations(socket.user.uid, function(err, res) { + if (err) { + + } else { + for (var i in res) { + if (res[i].user && res[i].user.uid > 0){ + res[i].user.role = that.room.findRole(res[i].user.uid); + } + } + returnObj.data = { + conversations: res + }; + socket.sendJSON(returnObj); + } + }); + break; + + case 'getPrivateConversation': + /* + Expected input object: + { + type: 'getPrivateConversation', + data: { + uid: uid + } + } + */ + + if (!socket.room) { + returnObj.data = { + error: 'NotInPad' + }; + socket.sendJSON(returnObj); + break; + } + + if (!Roles.checkPermission(socket.user.role, 'chat.private')){ + returnObj.data = { + error: 'InsufficientPermissions' + }; + socket.sendJSON(returnObj); + break; + } + + if (!data.data.uid) { + break; + } + DB.getUserByUid(data.data.uid, function(err, user) { + if (err) { + returnObj.data = { + error: 'UserNotFound' + }; + socket.sendJSON(returnObj); + return; + } + DB.getConversation(socket.user.uid, data.data.uid, function(err, res) { + if (err) { + returnObj.data = { + error: 'FailedRetrievingPMs' + }; + socket.sendJSON(returnObj); + return; + } + + returnObj.data = { + user: user.getClientObj(), + messages: res + } + socket.sendJSON(returnObj); + }); }); break; + + case 'markConversationRead': + /* + Expected input object: + { + type: 'markConversationRead', + data: { + uid: uid, + date: date + } + } + */ + if (!socket.room) { + returnObj.data = { + error: 'NotInPad' + }; + socket.sendJSON(returnObj); + break; + } + + if (!Roles.checkPermission(socket.user.role, 'chat.private')){ + returnObj.data = { + error: 'InsufficientPermissions' + }; + socket.sendJSON(returnObj); + break; + } + if (!data.data.uid || ! data.data.date) { + returnObj.data = { + error: 'PropsMissing' + }; + socket.sendJSON(returnObj); + break; + } + DB.markConversationRead(socket.user.uid, data.data.uid, new Date(data.data.date)); + break; case 'broadcastMessage': if (!Roles.checkPermission(socket.user.role, 'chat.broadcast')){ diff --git a/webserver/public/index.html b/webserver/public/index.html index d2b27fb..81dd9dc 100644 --- a/webserver/public/index.html +++ b/webserver/public/index.html @@ -562,11 +562,53 @@
-
-
+
+
+
+
+
+
+
+
+
+ +
-
- +
+
+
+ +
+ + + {{(pmFuncs.makeMessageTime(pmFuncs.getPMGroupInfo(pmgroup).lastPM.time))}} +
+
+
New Message
+
+
+
Back to PM list
+
+
+
+
+ {{(pmFuncs.makeMessageTime(msg.time))}} +
+ {{(user.uid == msg.from ? user.un : activepm.user.un)}} + +
+
+
+
+
+
+ +
+
diff --git a/webserver/public/lib/css/chat.css b/webserver/public/lib/css/chat.css index a2346ea..abfbc3d 100644 --- a/webserver/public/lib/css/chat.css +++ b/webserver/public/lib/css/chat.css @@ -32,12 +32,13 @@ display: -webkit-flex; display: flex; } -#chat { +#chat, #pm-chat { position: absolute; z-index: 9; overflow-x: hidden; overflow-y: auto; - height: calc(100% - 41px); + height: calc(100% - 81px); + top: 40px; width: 100%; font-size: 14px; -webkit-touch-callout: text; @@ -47,7 +48,7 @@ -ms-user-select: text; user-select: text; } -#messages { +#messages, #pm-messages { margin: 0; padding: 0; } @@ -75,7 +76,7 @@ .greet-umsg { color: #A77DC2; } -.uname { +.uname, .username { font-size: 14px; font-weight: bold; cursor: pointer; @@ -229,14 +230,31 @@ position: relative; background: rgba(28, 28, 31, 0.4); } -.waitlist-user, .people-user { +.waitlist-user, .people-user, .pm-user { font-size: 14px; vertical-align: middle; line-height: 40px; height: 40px; padding: 0 6px; } -.waitlist-user .uname, .people-user .uname, .user-menu .uname { +.pm-user { + height: 60px; + cursor: pointer; +} +.pm-user-info { + line-height: 25px; +} +.pm-latest-info { + /*margin-left: 32px;*/ +} +.pm-latest-info .time { + float: right; + color: #585858; +} +.pm-user .bdg { + margin: 4px 5px; +} +.waitlist-user .uname, .people-user .uname, .user-menu .uname, .pm-user .uname, .pm-user .username { margin: 0 5px; width: 300px; } @@ -246,7 +264,7 @@ .waitlist-user:first-child { background: rgba(167,125,194,0.2); } -.waitlist-user:nth-child(even), .people-user:nth-child(even) { +.waitlist-user:nth-child(even), .people-user:nth-child(even), .pm-user:nth-child(even) { background: rgba(28, 28, 31, 0.4); } .waitlist-user .nav { @@ -255,6 +273,11 @@ .waitlist-user.self { background-color: #31160C; } +.pm-info { + color: #88898C; + margin: 0 5px; + font-size: 12px; +} .people-info { float: right; color: #88898C; @@ -275,6 +298,30 @@ font-weight: bold; float: right; } +.btn-new-pm { + z-index:10; + position: absolute; + bottom:0; + left: 0; + right: 0; +} +.pms, .single-pm { + top: 40px; + position: absolute; + width: 100%; + height: calc(100% - 41px); +} +.pm-group { + width: 100%; + top: 40px; + position: absolute; + height: calc(100% - 41px); +} +.pm-user-list{ + max-height: 200px; + overflow-y: scroll; + overflow-x: hidden; +} @keyframes flash-background { diff --git a/webserver/public/lib/js/app.js b/webserver/public/lib/js/app.js index 7e09bcf..c9c0079 100644 --- a/webserver/public/lib/js/app.js +++ b/webserver/public/lib/js/app.js @@ -48,6 +48,7 @@ allowemojis: MP.session.allowemojis, lastdj: MP.session.lastdj, description: MP.session.description, + pms: MP.pms }; var $scope = angular.element( $("body") ).scope(); @@ -224,8 +225,10 @@ queue: {}, secondsLeftInSong: 0, songDuration: 0, - songProgress: 0 + songProgress: 0, + pms: {} }, + pms: {}, session: { // Used for temp variables specific to current session roomInfo: {}, viewedPl: null, @@ -927,9 +930,20 @@ isLoggedIn: function(){ return MP.isLoggedIn(); }, - getUser: function(uid){ - if (!uid) return MP.user; - return MP.findUser(uid); + getUser: function(uid, callback){ + if(callback) { + var obj = { + type: 'getUser', + data: { + uid: uid, + }, + }; + obj.id = MP.addCallback(obj.type, function(err, data){ callback(err, err ? null : data.user); }); + socket.sendJSON(obj); + } else { + if (!uid) return MP.user; + return MP.findUser(uid); + } }, getUsers: function(arr){ if (typeof arr == 'undefined') arr = false; @@ -1060,6 +1074,30 @@ } }, chat: { + getConversations: function(callback) { + MP.getConversations(function (err, data) { + if (callback) { + if (callback.length == 1) { + callback(data); + } + else { + callback(err, data); + } + } + }); + }, + getPrivateConversation: function(uid, callback) { + MP.getPrivateConversation(uid, function (err, data) { + if (callback) { + if (callback.length == 1) { + callback(data); + } + else { + callback(err, data); + } + } + }); + }, log: function(a,b){ MP.addMessage({msg:a,user:{un:b}}, 'log'); }, @@ -2446,6 +2484,47 @@ } }); }, + getPrivateConversation: function(uid, callback) { + if (!MP.checkPerm('chat.private')) return; + + var obj = { + type: 'getPrivateConversation', + data: { + uid: uid + } + }; + + obj.id = MP.addCallback(obj.type, callback); + socket.sendJSON(obj); + }, + getConversations: function(callback) { + if (!MP.checkPerm('chat.private')) return; + + var obj = { + type: 'getConversations', + data: { } + }; + + obj.id = MP.addCallback(obj.type, callback); + socket.sendJSON(obj); + }, + markConversationRead: function(uid, date) { + if (!MP.checkPerm('chat.private')) return; + + if (!date) { + date = Date.now(); + } + + var obj = { + type: 'markConversationRead', + data: { + uid: uid, + date: date + } + }; + + socket.sendJSON(obj); + }, deleteChat: function(cid, callback){ if (!MP.checkPerm('chat.delete')) return; @@ -3410,10 +3489,49 @@ if (err){ if (callback) callback(err); console.log('Could not send private message: ' + err); return;} if (callback) callback(err, data); + + MP.api.room.getUser(uid, function(err,user) { + if (err) { + + } else { + MP.addPrivateMessage(user, message, MP.user.uid); + } + }); }); socket.sendJSON(obj); }, + addPrivateMessage: function(user, message, fromUid) { + var messageObj = { + message: message, + time: Date.now(), + from: fromUid + }; + var scope = angular.element($('body')).scope(); + var messageUnread = 1; + if (scope.activepm && scope.activepm.user.uid == user.uid) { + messageUnread = 0; + } + if (!MP.pms[user.un]) { + MP.pms[user.un] = { + user: user, + messages: [ + messageObj + ], + unread: messageUnread + }; + } + else { + MP.pms[user.un].messages.push(messageObj); + MP.pms[user.un].unread += messageUnread; + } + if (messageUnread == 0) { + MP.markConversationRead(user.uid); + } + MP.applyModels(); + var $chat = $('#pm-chat'); + $chat.scrollTop( $chat[0].scrollHeight ); + }, getCurrentVideoTime: function(callback){ var obj = { type: 'getCurrentSongTime' @@ -4114,18 +4232,7 @@ getInfo: MP.api.room.getInfo, isLoggedIn: MP.api.room.isLoggedIn, getUser: function(uid, callback) { - if(callback) { - var obj = { - type: 'getUser', - data: { - uid: uid, - }, - }; - obj.id = MP.addCallback(obj.type, function(err, data){ callback(err, err ? null : data.user); }); - socket.sendJSON(obj); - } else { - return MP.api.room.getUser(uid); - } + return MP.api.room.getUser(uid, callback); }, getUsers: function(arr) { return MP.copyObject(MP.api.room.getUsers(arr)); }, getRoles: function(arr) { return MP.copyObject(MP.api.room.getRoles(arr)); }, @@ -4159,6 +4266,7 @@ } }, chat: { + getConversations: MP.api.chat.getConversations, log: MP.api.chat.log, system: MP.api.chat.system, broadcast: MP.api.chat.broadcast, @@ -4649,6 +4757,23 @@ if (data.token){ MP.cookie.setCookie(MP.getTokenName(), data.token, 7); } + + MP.api.chat.getConversations(onLoadConversations); + }; + + var onLoadConversations = function(err, data) { + if (err) { + + } else { + if (!data.conversations) return; + MP.pms = {}; + for (var i in data.conversations) { + var convo = data.conversations[i]; + convo.__init = false; + MP.pms[convo.user.un] = convo; + } + MP.applyModels(); + } }; var socketPort = config.serverPort; @@ -5139,6 +5264,7 @@ if(settings.roomSettings.notifications.sound.pm) mentionSound.play(); + MP.addPrivateMessage(user, data.data.message, data.data.uid); break; case API.DATA.EVENTS.SERVER_RESPONSE: @@ -5204,6 +5330,35 @@ return mentionVal.length + 1; } }; + + $('#pm-msg-in').on('keydown', function(e){ + if (e.which == 13) { + e.preventDefault(); + var $input = $(this); + var $chat = $('#pm-chat'); + + if (!$input.val()) return; + + //MP.session.lastMessage = $input.val(); + //MP.sendMessage($input.val()); + var activepm = angular.element($('body')).scope().activepm; + if (!activepm) { + return; + } + MP.api.room.getUser(activepm.user.uid, function(err, user) { + if (!user) { + return; + } + var msg = $input.val(); + MP.privateMessage(user.uid, msg, function(err, data){ + + }); + $chat.scrollTop( $chat[0].scrollHeight ); + $input.val(''); + }); + return true; + } + }); // Chat text box $('#msg-in') @@ -5395,6 +5550,89 @@ $ul.find('li.active').removeClass('active'); $(this).addClass('active'); + }) + // Create a new Private Message + .on('click', '.btn-new-pm', function() { + console.log('New PM Modal'); + var users = MP.getUsersInRoom(); + var userHtml = ""; + for (var uid in users) { + userHtml += '
  • \ +
    \ +
    ' + MP.makeBadgeStyle({user: users[uid], type: 'pmList' }) + '
    ' + users[uid].un + '
    \ +
    \ +
  • '; + } + MP.makeCustomModal({ + content: '
    \ +

    Select User

    \ +
      ' + + userHtml + + '
    \ +
    \ +
    \ +
    ', + buttons: [ + { + icon: 'mdi-close', + handler: function(){ + $('.modal-bg').remove(); + }, + classes: 'modal-ctrl modal-no' + }, + { + icon: 'mdi-check', + handler: function(){ + var pmuid = $('.pm-user-list li.selected div').attr('data-pmuid'); + console.log('Selected User' + pmuid); + + $('.modal-bg').remove(); + + MP.makeCustomModal({ + content: '
    \ +

    Sending PM to ' + users[pmuid].un + '

    \ + \ +
    ', + buttons: [{ + icon: 'mdi-close', + handler: function(){ + $('.modal-bg').remove(); + }, + classes: 'modal-ctrl modal-no' + }, + { + icon: 'mdi-check', + handler: function(){ + var pmmessage = $('#pm-in').val(); + console.log('Selected Message "' + pmmessage + '" sending to ID "' + pmuid + '" with UserName "' + users[pmuid].un + '"'); + MP.api.chat.sendPrivate(pmuid, pmmessage, function(err, data){ + if (err == undefined) + { + console.log(data); + if (data.success == true) + { + $('.modal-bg').remove(); + } + } + else + { + console.log(err); + } + }); + }, + classes: 'modal-ctrl modal-yes' + }], + dismissable: true + }); + }, + classes: 'modal-ctrl modal-yes' + } + ], + dismissable: true + }); + }) + .on('click', '.pm-user-list > li', function(){ + $(this).addClass("selected").siblings().removeClass("selected"); }) .on('click', '.autocomplete li.active', function(){ var newPos = acceptAutocomplete(); @@ -6576,9 +6814,84 @@ t: 3, // Logo menu c: 1, // Right view (Chat, Waitlist, Userlist) p: 1, // People tabs inside of Userlist + ci: 1, // Chat list internal chatScroll: 0, // Chat scroll memory leaveAfterPlay: false, }; + + + $scope.activepm = null; + + $scope.getPMUnread = function() { + var total = 0; + for (var i in MP.pms) { + total += MP.pms[i].unread; + } + return total; + }; + + $scope.pmFuncs = { + setPM: function(pmGroup) { + console.log(pmGroup); + $scope.activepm = pmGroup; + if (pmGroup && !pmGroup.__init) { + MP.api.chat.getPrivateConversation(pmGroup.user.uid, function(data) { + if (data) { + console.log(data); + MP.pms[pmGroup.user.un].messages = data.messages; + MP.pms[pmGroup.user.un].__init = true; + if (MP.pms[pmGroup.user.un].unread > 0) { + MP.markConversationRead(pmGroup.user.uid, Date.now()); + MP.pms[pmGroup.user.un].unread = 0; + } + MP.applyModels(); + var $chat = $('#pm-chat'); + $chat.scrollTop( $chat[0].scrollHeight ); + } + }); + } + else if (pmGroup) { + if (pmGroup.unread > 0) { + MP.markConversationRead(pmGroup.user.uid, Date.now()); + MP.pms[pmGroup.user.un].unread = 0; + MP.applyModels(); + } + } + }, + getPMGroupInfo: function(pmGroup) { + if (!pmGroup) return { lastPM: { time: null } }; + var returnObj = { + lastPM: null, + unreadCount: pmGroup.unread ? pmGroup.unread : 0 + }; + if (pmGroup.messages.length > 0) { + returnObj.lastPM = pmGroup.messages[pmGroup.messages.length - 1]; + } + + return returnObj; + }, + makeMessageTime: function(time) { + if (time) { + if (Number(time) || typeof(time) === "string") { + time = new Date(time); + } + return MP.makeTime(time); + } + return ""; + }, + getOrderedPMs: function() { + var out = []; + for (var i in MP.pms) { + if ($scope.pmFuncs.getPMGroupInfo(MP.pms[i]).lastPM.time != null) { + out.push(MP.pms[i]); + } + } + out.sort(function(a,b){ + return (new Date($scope.pmFuncs.getPMGroupInfo(b).lastPM.time).getTime()) - (new Date($scope.pmFuncs.getPMGroupInfo(a).lastPM.time).getTime()); + }); + return out; + } + }; $scope.customSettings = { theme: 'bootstrap', @@ -6764,7 +7077,14 @@ return MP.makeUsernameStyle(user.role); }; - + + $scope.emojiReplace = function(text) { + if (text) { + return MP.emojiReplace(text); + } + return ""; + }; + $scope.getRole = function(role){ if (MP.getRole(role)) return MP.getRole(role); From 49761504649bd4b192ee22b4da95cf77b05437cb Mon Sep 17 00:00:00 2001 From: musiqpad Date: Mon, 18 Apr 2016 20:02:46 +0000 Subject: [PATCH 03/14] LevelDB support for PMs --- socketserver/db_level.js | 117 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/socketserver/db_level.js b/socketserver/db_level.js index b084a1f..af49478 100644 --- a/socketserver/db_level.js +++ b/socketserver/db_level.js @@ -145,6 +145,20 @@ function LevelDB(callback) { currentCID = parseInt(val); }); }); + + //PmDB + if(!this.PmDB) + this.PmDB = setupDB(dbdir + '/pm', + + //If new DB is created + function(newdb) {}, + + //Callback + function(err, newdb) { + if (err) { + throw new Error('Could not open PmDB: ' + err); + } + }); } function setupDB(dir, setup, callback){ @@ -616,4 +630,107 @@ LevelDB.prototype.logChat = function(uid, msg, special, callback) { callback(currentCID++); }; +//PmDB +LevelDB.prototype.logPM = function(from, to, msg, callback) { + var that = this; + var key = Math.min(from, to) + ":" + Math.max(from, to); + + this.getJSON(this.PmDB, key, function(err, res){ + var out = []; + + if(!err) out = res; + + out.push({ + message: msg, + time: new Date(), + from: from, + unread: true, + }); + + that.putJSON(that.PmDB, key, out); + }); +}; + +LevelDB.prototype.getConversation = function(from, to, callback) { + var key = Math.min(from, to) + ":" + Math.max(from, to); + + this.getJSON(this.PmDB, key, function(err, res){ + if(err){ + callback(null, []); + } else { + callback(null, res); + } + }); +}; + +LevelDB.prototype.getConversations = function(uid, callback) { + var that = this; + + var out = {}; + var uids; + uid = uid.toString(); + + this.PmDB.createReadStream() + .on('data', function(data) { + if (data.key.indexOf(':') == -1 || (uids = data.key.split(':')).indexOf(uid) == -1) return; + + try { + var convo = JSON.parse(data.value); + } catch (e) { + return; + } + + var unread = 0; + convo.map(function(e){ + if(e.unread && e.from != uid) unread++; + return { + messages: e.messages, + time: e.time, + from: e.from, + }; + }); + + out[uids[(uids.indexOf(uid) + 1) % 2]] = { + user: null, + messages: [ convo.pop() ], + unread: unread, + }; + }) + .on('end', function() { + var uids = Object.keys(out).map(function(e){ return parseInt(e); }); + + if (uids.length > 0) { + that.getUserByUid(uids, function(err, result){ + if (err) { + callback(err); + } else { + for (var id in result) { + out[id].user = result[id].getClientObj(); + } + callback(null, out); + } + }); + } else { + callback(null, out); + } + return false; + }); +}; + +LevelDB.prototype.markConversationRead = function(uid, uid2, time) { + var that = this; + var key = Math.min(uid, uid2) + ":" + Math.max(uid, uid2); + + this.getJSON(this.PmDB, key, function(err, res) { + if(err) return; + + res.map(function(e){ + if(e.from == uid2 && new Date(e.time) < new Date(time)) e.unread = false; + return e; + }); + + that.putJSON(that.PmDB, key, res); + }); +}; + module.exports = new LevelDB(); \ No newline at end of file From 9f465e1190666d6f5b4361197c5f3c6eaba8f9cc Mon Sep 17 00:00:00 2001 From: Christopher Nelson Date: Tue, 19 Apr 2016 13:48:42 +0100 Subject: [PATCH 04/14] Changed PM Chat notification to default to 'Off' Changed due to new PM interface. --- webserver/public/lib/js/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webserver/public/lib/js/app.js b/webserver/public/lib/js/app.js index 22b91e1..621b0c1 100644 --- a/webserver/public/lib/js/app.js +++ b/webserver/public/lib/js/app.js @@ -6948,7 +6948,7 @@ like: false, grab: false, chat: false, - pm: true, + pm: false, }, desktop: { advance_last: false, From 05ddc85f5489833fd7cff3a75ef9419456dfa846 Mon Sep 17 00:00:00 2001 From: Christopher Nelson Date: Tue, 19 Apr 2016 14:01:39 +0100 Subject: [PATCH 05/14] Fix for PM counter not increasing in certain situations --- webserver/public/lib/js/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webserver/public/lib/js/app.js b/webserver/public/lib/js/app.js index 621b0c1..831b8dd 100644 --- a/webserver/public/lib/js/app.js +++ b/webserver/public/lib/js/app.js @@ -3513,7 +3513,7 @@ }; var scope = angular.element($('body')).scope(); var messageUnread = 1; - if (scope.activepm && scope.activepm.user.uid == user.uid) { + if (scope.activepm && scope.activepm.user.uid == user.uid && scope.props.ci == 2) { messageUnread = 0; } if (!MP.pms[user.un]) { From e0980e5f032a1ef1468547c4b21e824dd30b33c6 Mon Sep 17 00:00:00 2001 From: Christopher Nelson Date: Tue, 19 Apr 2016 14:06:31 +0100 Subject: [PATCH 06/14] Fix for PM unread counter not increasing in certain situations --- webserver/public/index.html | 2 +- webserver/public/lib/js/app.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/webserver/public/index.html b/webserver/public/index.html index 8f91dfd..6eb59a7 100644 --- a/webserver/public/index.html +++ b/webserver/public/index.html @@ -580,7 +580,7 @@
    -
    +
    diff --git a/webserver/public/lib/js/app.js b/webserver/public/lib/js/app.js index 831b8dd..0e437c2 100644 --- a/webserver/public/lib/js/app.js +++ b/webserver/public/lib/js/app.js @@ -6875,6 +6875,12 @@ return returnObj; }, + changeToPMTab: function() { + if ($scope.activepm != null && $scope.activepm.unread > 0) { + MP.markConversationRead($scope.activepm.user.uid); + } + $scope.prop.ci = 2; + }, makeMessageTime: function(time) { if (time) { if (Number(time) || typeof(time) === "string") { From bbd29448ed5059eccad791ea7bf6d6f32c646073 Mon Sep 17 00:00:00 2001 From: Christopher Nelson Date: Tue, 19 Apr 2016 14:23:38 +0100 Subject: [PATCH 07/14] Fix for client not marking read messages --- webserver/public/lib/js/app.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/webserver/public/lib/js/app.js b/webserver/public/lib/js/app.js index 0e437c2..53e5d74 100644 --- a/webserver/public/lib/js/app.js +++ b/webserver/public/lib/js/app.js @@ -3513,7 +3513,7 @@ }; var scope = angular.element($('body')).scope(); var messageUnread = 1; - if (scope.activepm && scope.activepm.user.uid == user.uid && scope.props.ci == 2) { + if (scope.activepm && scope.activepm.user.uid == user.uid && scope.prop.ci == 2) { messageUnread = 0; } if (!MP.pms[user.un]) { @@ -6878,6 +6878,8 @@ changeToPMTab: function() { if ($scope.activepm != null && $scope.activepm.unread > 0) { MP.markConversationRead($scope.activepm.user.uid); + MP.pms[$scope.activepm.user.un].unread = 0; + MP.applyModels(); } $scope.prop.ci = 2; }, From e32853e7de51ce1d05033cb4a09972e6d44a248c Mon Sep 17 00:00:00 2001 From: Christopher Nelson Date: Tue, 19 Apr 2016 14:26:35 +0100 Subject: [PATCH 08/14] Update app.js --- webserver/public/lib/js/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webserver/public/lib/js/app.js b/webserver/public/lib/js/app.js index 53e5d74..943a7b3 100644 --- a/webserver/public/lib/js/app.js +++ b/webserver/public/lib/js/app.js @@ -6876,12 +6876,12 @@ return returnObj; }, changeToPMTab: function() { + $scope.prop.ci = 2; if ($scope.activepm != null && $scope.activepm.unread > 0) { MP.markConversationRead($scope.activepm.user.uid); MP.pms[$scope.activepm.user.un].unread = 0; MP.applyModels(); } - $scope.prop.ci = 2; }, makeMessageTime: function(time) { if (time) { From af269f3a4104fec744ef0a4a8a7614069634fa31 Mon Sep 17 00:00:00 2001 From: musiqpad Date: Wed, 20 Apr 2016 22:35:59 +0000 Subject: [PATCH 09/14] Added ability to send a PM to an offline user --- socketserver/socketserver.js | 35 +++++++ webserver/public/lib/css/chat.css | 11 ++- webserver/public/lib/js/app.js | 146 ++++++++++++++++++------------ 3 files changed, 131 insertions(+), 61 deletions(-) diff --git a/socketserver/socketserver.js b/socketserver/socketserver.js index 6e43a83..4c6c985 100644 --- a/socketserver/socketserver.js +++ b/socketserver/socketserver.js @@ -2780,6 +2780,41 @@ var SocketServer = function(server){ socket.sendJSON(returnObj); }); break; + case 'getUserByName': + /* + Expects { + type: 'getUserByName', + data: { + un: un + } + } + */ + if (!data.data.un){ + returnObj.data = { + error: 'PropsMissing' + }; + socket.sendJSON(returnObj); + break; + } + + DB.getUserByName(data.data.un, { getPlaylists: false }, function(err, user){ + //Handle error + if (err){ + returnObj.data = { + error: err + }; + socket.sendJSON(returnObj); + return; + } + + //Execute and return data + returnObj.data = { + user: user.getClientObj(), + }; + returnObj.data.user.role = that.room.findRole(returnObj.data.user.uid); + socket.sendJSON(returnObj); + }); + break; case 'whois': /* Expects { diff --git a/webserver/public/lib/css/chat.css b/webserver/public/lib/css/chat.css index abfbc3d..6497722 100644 --- a/webserver/public/lib/css/chat.css +++ b/webserver/public/lib/css/chat.css @@ -230,13 +230,22 @@ position: relative; background: rgba(28, 28, 31, 0.4); } -.waitlist-user, .people-user, .pm-user { +.waitlist-user, .people-user, .pm-user, .new-pm-user { font-size: 14px; vertical-align: middle; line-height: 40px; height: 40px; padding: 0 6px; } +.new-pm-user .bdg { + margin: 6px; +} +.new-pm-user { + line-height: 28px; +} +#offline-pm-user { + width: 365px; +} .pm-user { height: 60px; cursor: pointer; diff --git a/webserver/public/lib/js/app.js b/webserver/public/lib/js/app.js index 943a7b3..9c80045 100644 --- a/webserver/public/lib/js/app.js +++ b/webserver/public/lib/js/app.js @@ -943,6 +943,22 @@ } else { if (!uid) return MP.user; return MP.findUser(uid); + } + }, + getUserByName: function(un, callback){ + if(callback) { + var obj = { + type: 'getUserByName', + data: { + un: un, + }, + }; + obj.id = MP.addCallback(obj.type, function(err, data){ callback(err, err ? null : data.user); }); + socket.sendJSON(obj); + } else { + if (!un) return MP.user; + var users = MP.api.util.objectToArray(MP.getUsersInRoom()); + return users.filter(function(a){ return a.un == un; })[0]; } }, getUsers: function(arr){ @@ -2208,8 +2224,7 @@ arr.shift(); var usernick = arr.shift().replace(/[@:]/g,''); - var users = MP.api.util.objectToArray(MP.getUsersInRoom()); - var user = users.filter(function(a){ return a.un == usernick; })[0]; + var user = MP.api.room.getUserByName(usernick); if (!user) return; @@ -2232,8 +2247,7 @@ return API.chat.log('
    Try /ban @username', 'Ban user'); } - var users = MP.api.room.getUsers(true); - var user = users.filter(function(a){return a.un == arr[0].substring(1);})[0]; + var user = MP.api.room.getUserByName(arr[0].substring(1)); if (!user) return; @@ -2252,8 +2266,7 @@ return API.chat.log('
    Try /role @username', 'Set user role'); } - var users = MP.api.room.getUsers(true); - var user = users.filter(function(a){return a.un == arr[0].substring(1);})[0]; + var user = MP.api.room.getUserByName(arr[0].substring(1)); if (!user) return; @@ -2270,8 +2283,7 @@ return API.chat.log('
    Try /mute @username', 'Ignore user'); } - var users = MP.api.room.getUsers(true); - var user = users.filter(function(a){return a.un == arr[0].substring(1);})[0]; + var user = MP.api.room.getUserByName(arr[0].substring(1)); if (!user) return; @@ -2290,8 +2302,7 @@ return API.chat.log('
    Try /add @username', 'Add user to queue'); } - var users = MP.api.room.getUsers(true); - var user = users.filter(function(a){return a.un == arr[0].substring(1);})[0]; + var user = MP.api.room.getUserByName(arr[0].substring(1)); if (!user) return; var position = parseInt(arr[1]); @@ -2312,8 +2323,7 @@ return API.chat.log('
    Try /rem @username', 'Remove user from queue'); } - var users = MP.api.room.getUsers(true); - var user = users.filter(function(a){return a.un == arr[0].substring(1);})[0]; + var user = MP.api.room.getUserByName(arr[0].substring(1)); if (!user) return; MP.djQueueModRemove(user.uid); @@ -2331,8 +2341,7 @@ return API.chat.log('
    Try /move @username 1', 'Move user in queue'); } - var users = MP.api.room.getUsers(true); - var user = users.filter(function(a){return a.un == arr[0].substring(1);})[0]; + var user = MP.api.room.getUserByName(arr[0].substring(1)); if (!user) return; var pos = parseInt(arr[1]); @@ -2351,9 +2360,8 @@ return API.chat.log('
    Try /swap @username1 @username2', 'Swap users in queue'); } - var users = MP.api.room.getUsers(true); - var user1 = users.filter(function(a){return a.un == arr[0].substring(1);})[0]; - var user2 = users.filter(function(a){return a.un == arr[1].substring(1);})[0]; + var user1 = MP.api.room.getUserByName(arr[0].substring(1)); + var user2 = MP.api.room.getUserByName(arr[1].substring(1)); if (!user1 || !user2 || user1.uid == user2.uid) return; MP.djQueueModSwap(user1.uid,user2.uid); @@ -3476,11 +3484,6 @@ return; } - if (!MP.findUser(uid)){ - if (callback) callback('userNotFound'); - return; - } - var obj = { type: 'privateMessage', data: { @@ -5563,7 +5566,7 @@ var userHtml = ""; for (var uid in users) { userHtml += '
  • \ -
    \ +
    \
    ' + MP.makeBadgeStyle({user: users[uid], type: 'pmList' }) + '
    ' + users[uid].un + '
    \
    \
  • '; @@ -5576,6 +5579,9 @@ '\
    \
    \ +
    \ +
    \ + \
    ', buttons: [ { @@ -5589,46 +5595,66 @@ icon: 'mdi-check', handler: function(){ var pmuid = $('.pm-user-list li.selected div').attr('data-pmuid'); - console.log('Selected User' + pmuid); - $('.modal-bg').remove(); + var offlineUsername = $('#offline-pm-user').val().replace(" ", ""); - MP.makeCustomModal({ - content: '
    \ -

    Sending PM to ' + users[pmuid].un + '

    \ - \ -
    ', - buttons: [{ - icon: 'mdi-close', - handler: function(){ - $('.modal-bg').remove(); - }, - classes: 'modal-ctrl modal-no' - }, + function userCallback(user) { + + if (user) { - icon: 'mdi-check', - handler: function(){ - var pmmessage = $('#pm-in').val(); - console.log('Selected Message "' + pmmessage + '" sending to ID "' + pmuid + '" with UserName "' + users[pmuid].un + '"'); - MP.api.chat.sendPrivate(pmuid, pmmessage, function(err, data){ - if (err == undefined) - { - console.log(data); - if (data.success == true) - { - $('.modal-bg').remove(); - } - } - else - { - console.log(err); - } - }); - }, - classes: 'modal-ctrl modal-yes' - }], - dismissable: true - }); + console.log('Selected User ' + user.uid); + $('.modal-bg').remove(); + + MP.makeCustomModal({ + content: '
    \ +

    Sending PM to ' + user.un + '

    \ + \ +
    ', + buttons: [{ + icon: 'mdi-close', + handler: function(){ + $('.modal-bg').remove(); + }, + classes: 'modal-ctrl modal-no' + }, + { + icon: 'mdi-check', + handler: function(){ + var pmmessage = $('#pm-in').val(); + + console.log('Selected Message "' + pmmessage + '" sending to ID "' + user.uid + '" with UserName "' + user.un + '"'); + MP.api.chat.sendPrivate(user.uid, pmmessage, function(err, data){ + if (err) { + console.log(err); + } else { + console.log(data); + if (data.success == true) + { + $('.modal-bg').remove(); + } + } + }); + }, + classes: 'modal-ctrl modal-yes' + }], + dismissable: true + }); + } + } + + if (offlineUsername && offlineUsername.length > 0) + { + var user = MP.api.room.getUserByName(offlineUsername, function(err, data){ + if (err) { + // Display User Not Found/Whatever error + } else { + userCallback(data); + } + }); + } + else { + userCallback(MP.getUser(pmuid)); + } }, classes: 'modal-ctrl modal-yes' } From cc25b828a3716935bc94b523f38287bbd535fe89 Mon Sep 17 00:00:00 2001 From: musiqpad Date: Wed, 20 Apr 2016 22:58:02 +0000 Subject: [PATCH 10/14] Added mention filter option. --- webserver/public/index.html | 5 +++-- webserver/public/lib/css/chat.css | 10 ++++++++++ webserver/public/lib/js/app.js | 12 ++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/webserver/public/index.html b/webserver/public/index.html index 6eb59a7..949a947 100644 --- a/webserver/public/index.html +++ b/webserver/public/index.html @@ -579,10 +579,11 @@
    -
    +
    +
    @
    -
    +
    diff --git a/webserver/public/lib/css/chat.css b/webserver/public/lib/css/chat.css index 6497722..9ee3859 100644 --- a/webserver/public/lib/css/chat.css +++ b/webserver/public/lib/css/chat.css @@ -331,6 +331,16 @@ overflow-y: scroll; overflow-x: hidden; } +.tab.btn-mentions { + flex: 0.5; + -webkit-flex: 0.5; + -ms-flex: 0.5; +} +.tab.btn-mentions .icon-info { + font-size: 16px; + border-radius: 16px; + padding-bottom: 4px; +} @keyframes flash-background { diff --git a/webserver/public/lib/js/app.js b/webserver/public/lib/js/app.js index 9c80045..052bece 100644 --- a/webserver/public/lib/js/app.js +++ b/webserver/public/lib/js/app.js @@ -6931,6 +6931,18 @@ return out; } }; + + $scope.filterChat = function(type) { + type = type ? type : ''; + switch (type) { + case 'mentions': + $('#messages .cm.message:not(.mention)').hide(); + break; + default: + $('#messages .cm.message:not(.mention)').show(); + break; + } + } $scope.customSettings = { theme: 'bootstrap', From 205c639818678d1ee2f345b6d3370cd147b403ba Mon Sep 17 00:00:00 2001 From: Christopher Nelson Date: Thu, 21 Apr 2016 18:47:07 +0100 Subject: [PATCH 11/14] Improved look of the New PM Modal --- webserver/public/lib/css/chat.css | 14 +++++++++++--- webserver/public/lib/js/app.js | 22 ++++++++++++++++++---- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/webserver/public/lib/css/chat.css b/webserver/public/lib/css/chat.css index 9ee3859..7572af3 100644 --- a/webserver/public/lib/css/chat.css +++ b/webserver/public/lib/css/chat.css @@ -242,9 +242,14 @@ } .new-pm-user { line-height: 28px; + height: 28px; +} +.pm-user-list li.selected { + background: rgba(167,125,194,0.2); + border-radius: 3px; } #offline-pm-user { - width: 365px; + width: calc(100% - 20px); } .pm-user { height: 60px; @@ -327,9 +332,12 @@ height: calc(100% - 41px); } .pm-user-list{ - max-height: 200px; - overflow-y: scroll; + height: 200px; + overflow-y: auto; overflow-x: hidden; + border-bottom: 1px solid #444A59; + border-top: 1px solid #444A59; + padding: 5px 0; } .tab.btn-mentions { flex: 0.5; diff --git a/webserver/public/lib/js/app.js b/webserver/public/lib/js/app.js index 052bece..0bc0af2 100644 --- a/webserver/public/lib/js/app.js +++ b/webserver/public/lib/js/app.js @@ -5578,11 +5578,10 @@ userHtml + '\
    \ + \
    \
    \ -
    \ - \ -
    ', +
    ', buttons: [ { icon: 'mdi-close', @@ -5646,7 +5645,15 @@ { var user = MP.api.room.getUserByName(offlineUsername, function(err, data){ if (err) { - // Display User Not Found/Whatever error + if (err == "UserNotFound") { + MP.makeAlertModal({ + content: 'The username you entered does not match a user from this server.' + }); + } else { + MP.makeAlertModal({ + content: 'An error occurred. Please try again later.' + }); + } } else { userCallback(data); } @@ -5664,6 +5671,13 @@ }) .on('click', '.pm-user-list > li', function(){ $(this).addClass("selected").siblings().removeClass("selected"); + $('#offline-pm-user').val(''); + }) + .on('input', '#offline-pm-user', function() { + var val = $(this).val(); + if (val && val.length > 0) { + $('.pm-user-list > li').removeClass("selected"); + } }) .on('click', '.autocomplete li.active', function(){ var newPos = acceptAutocomplete(); From cc8da73ed035e6c48c632222d9436eadaeff41ca Mon Sep 17 00:00:00 2001 From: musiqpad Date: Fri, 22 Apr 2016 11:49:23 +0000 Subject: [PATCH 12/14] Removed logging and undefined error, added loading animation --- webserver/public/index.html | 3 ++- webserver/public/lib/css/back.css | 40 +++++++++++++++++++++++++++++++ webserver/public/lib/js/app.js | 4 +--- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/webserver/public/index.html b/webserver/public/index.html index 949a947..f72a334 100644 --- a/webserver/public/index.html +++ b/webserver/public/index.html @@ -611,7 +611,8 @@
    Back to PM list
    -
    +
    +
    {{(pmFuncs.makeMessageTime(msg.time))}}
    diff --git a/webserver/public/lib/css/back.css b/webserver/public/lib/css/back.css index 0fcd6c1..7ec90b8 100644 --- a/webserver/public/lib/css/back.css +++ b/webserver/public/lib/css/back.css @@ -288,6 +288,46 @@ a { border-radius: 50%; animation: spin-back 3s linear infinite; } +.loader-small { + display: block; + position: relative; + left: 50%; + top: 50%; + width: 40px; + height: 40px; + border: 2px solid transparent; + border-top-color: #A77DC2; + border-bottom-color: #A77DC2; + border-radius: 50%; + animation: spin-back 4s linear infinite; + margin: 0 0 0 -20px; +} +.loader-small:before { + content: ""; + position: absolute; + top: 5px; + left: 5px; + right: 5px; + bottom: 5px; + border: 2px solid transparent; + border-top-color: #F46B40; + border-bottom-color: #F46B40; + border-radius: 50%; + animation: spin 2s linear infinite; +} +.loader-small:after { + content: ""; + position: absolute; + top: 11px; + left: 11px; + right: 11px; + bottom: 11px; + border: 2px solid transparent; + border-top-color: #89BE6C; + border-bottom-color: #89BE6C; + border-radius: 50%; + animation: spin-back 3s linear infinite; +} .icon-info { background: #94959A; border-radius: 10px; diff --git a/webserver/public/lib/js/app.js b/webserver/public/lib/js/app.js index 0bc0af2..fa92f1e 100644 --- a/webserver/public/lib/js/app.js +++ b/webserver/public/lib/js/app.js @@ -5660,7 +5660,7 @@ }); } else { - userCallback(MP.getUser(pmuid)); + userCallback(MP.findUser(pmuid)); } }, classes: 'modal-ctrl modal-yes' @@ -6877,12 +6877,10 @@ $scope.pmFuncs = { setPM: function(pmGroup) { - console.log(pmGroup); $scope.activepm = pmGroup; if (pmGroup && !pmGroup.__init) { MP.api.chat.getPrivateConversation(pmGroup.user.uid, function(data) { if (data) { - console.log(data); MP.pms[pmGroup.user.un].messages = data.messages; MP.pms[pmGroup.user.un].__init = true; if (MP.pms[pmGroup.user.un].unread > 0) { From d93c37a8dda781b67040418f39774a4fd141bf8d Mon Sep 17 00:00:00 2001 From: Christopher Nelson Date: Fri, 22 Apr 2016 14:32:13 +0100 Subject: [PATCH 13/14] Cleaned up look of PM buttons --- webserver/public/index.html | 4 ++-- webserver/public/lib/css/chat.css | 22 ++++++++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/webserver/public/index.html b/webserver/public/index.html index f72a334..d81fb2a 100644 --- a/webserver/public/index.html +++ b/webserver/public/index.html @@ -605,10 +605,10 @@ {{(pmFuncs.makeMessageTime(pmFuncs.getPMGroupInfo(pmgroup).lastPM.time))}}
    -
    New Message
    +
    New Message
    -
    Back to PM list
    +
    Back to PM list
    diff --git a/webserver/public/lib/css/chat.css b/webserver/public/lib/css/chat.css index 7572af3..33864dc 100644 --- a/webserver/public/lib/css/chat.css +++ b/webserver/public/lib/css/chat.css @@ -312,12 +312,30 @@ font-weight: bold; float: right; } -.btn-new-pm { +.btn-inner-pm { z-index:10; position: absolute; - bottom:0; left: 0; right: 0; + width: 100%; + background: rgb(28, 28, 31) none repeat scroll 0% 0%; + border-radius: 0px; + line-height: 40px; + height: 40px; + padding: 0px; +} +.btn-inner-pm:hover { + background: rgba(146, 90, 255, .25); + color: white; +} +.btn-new-pm { + border-top: 1px solid rgb(68, 74, 89); + bottom: -2px; +} +.btn-back-pm-list { + top: 0px; + border-bottom: 1px solid rgb(68, 74, 89); + border-top: 1px solid rgb(68, 74, 89); } .pms, .single-pm { top: 40px; From 6de01e2aa101febf62fd0b92b42a212705a3c263 Mon Sep 17 00:00:00 2001 From: Christopher Nelson Date: Fri, 22 Apr 2016 14:37:44 +0100 Subject: [PATCH 14/14] Fix for buttons being in the wrong place on new-pm modal --- webserver/public/lib/js/app.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/webserver/public/lib/js/app.js b/webserver/public/lib/js/app.js index fa92f1e..583c90b 100644 --- a/webserver/public/lib/js/app.js +++ b/webserver/public/lib/js/app.js @@ -5580,8 +5580,7 @@
    \ \
    \ -
    \ -
    ', +
    ', buttons: [ { icon: 'mdi-close',