From 27a21cc2d3638f6f147311eb68512c1368ac0a42 Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Fri, 30 May 2014 16:56:22 -0700 Subject: [PATCH] New debug console. Add sender_message_id to delivered messages for code simplicity. --- lib/models.py | 26 +++-- static/debug.css | 219 +++++++++++++++++++++++++++++++++++-- static/debug.html | 58 +++++++++- static/debug.js | 273 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 555 insertions(+), 21 deletions(-) create mode 100644 static/debug.js diff --git a/lib/models.py b/lib/models.py index 48bd90a..647e218 100644 --- a/lib/models.py +++ b/lib/models.py @@ -354,12 +354,13 @@ class Message(db.Model): def ToEvent(self): return { - 'event_type': 'message', - 'id': self.id_, - 'sender': str(Message.sender.get_value_for_datastore(self)), - 'subject': self.parent().ToDict(), - 'created': self.created, - 'message': self.message, + 'event_type': 'message', + 'id': self.id_, + 'sender': str(Message.sender.get_value_for_datastore(self)), + 'subject': self.parent().ToDict(), + 'created': self.created, + 'sender_message_id': self.sender_message_id, + 'message': self.message, } @@ -374,12 +375,13 @@ class Pin(db.Model): def ToEvent(self, event_type='pin'): return { - 'event_type': event_type, - 'id': str(self.key()), - 'sender': str(Pin.sender.get_value_for_datastore(self)), - 'subject': self.parent().ToDict(), - 'created': self.created, - 'message': self.message, + 'event_type': event_type, + 'id': str(self.key()), + 'sender': str(Pin.sender.get_value_for_datastore(self)), + 'subject': self.parent().ToDict(), + 'created': self.created, + 'sender_message_id': self.sender_message_id, + 'message': self.message, } def Delete(self): diff --git a/static/debug.css b/static/debug.css index 841dc01..691d3e9 100644 --- a/static/debug.css +++ b/static/debug.css @@ -1,4 +1,8 @@ -@import url(http://fonts.googleapis.com/css?family=Roboto); +@import url(http://fonts.googleapis.com/css?family=Roboto:300,400|Source+Code+Pro); + +body { + overflow: hidden; +} container { position: fixed; @@ -13,15 +17,15 @@ container { } subjectcontainer { - flex-grow: 1; + flex-basis: 20%; } messagecontainer { - flex-grow: 2; + flex-basis: 40%; } pincontainer { - flex-grow: 2; + flex-basis: 40%; } .verticalcontainer { @@ -30,6 +34,19 @@ pincontainer { align-items: stretch; } +.panel { + overflow: hidden; +} + +status { + border-color: #ebccd1 !important; +} + +status panelheading { + background-image: linear-gradient(to bottom, #f2dede 0, #ebcccc 100%) !important; + color: #4d1f1e !important; +} + subjectcontainer .panel { border-color: #bce8f1; } @@ -44,7 +61,7 @@ messagecontainer .panel { } messagecontainer panelheading { - background-image: linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%); + background-image: linear-gradient(to bottom, #dff0d8 0, #d0e9c6 100%); color: #274d05; } @@ -53,7 +70,7 @@ pincontainer .panel { } pincontainer panelheading { - background-image: linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%); + background-image: linear-gradient(to bottom, #fcf8e3 0, #faf2cc 100%); color: #4d3d21; } @@ -63,8 +80,10 @@ pincontainer panelheading { background-color: #fff; border: 1px solid transparent; border-radius: 4px; - box-shadow: 0 1px 2px rgba(0, 0, 0, .05); - min-height: 60px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + display: flex; + flex-direction: column; + align-items: stretch; } panelheading { @@ -75,8 +94,192 @@ panelheading { border-top-right-radius: 3px; font-size: 18px; font-variant: small-caps; + -webkit-user-select: none; + cursor: default; + flex-grow: 0; + flex-shrink: 0; +} + +panelbody { + flex-grow: 1; + overflow-x: hidden; + overflow-y: scroll; } .fixed { flex-grow: 0; + flex-shrink: 0; +} + +row { + white-space: pre-wrap; + display: flex; + flex-direction: row; + align-items: stretch; + justify-content: center; +} + +status row { + justify-content: flex-start; +} + +subjectlist row { + justify-content: flex-start; +} + +messagelist row { + justify-content: flex-start; +} + +pinlist row { + justify-content: flex-start; +} + +input[type=text] { + font-family: Roboto; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 3px; + background: linear-gradient(rgba(0, 0, 0, 0.025), white); + box-shadow: inset 1px 1px rgba(0, 0, 0, 0.05); + transition: all 0.1s ease-out; + padding: 5px; + color: #555; + flex-grow: 1; +} + +input[type=text]:focus { + outline: none; + border: 1px solid rgba(0,0,0,0.5); +} + +button { + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); + color: #fff; + border-radius: 6px; + border: 1px solid transparent; + font-size: 14px; + font-variant: small-caps; + padding: 4px 4px; + outline: none; + background-repeat: repeat-x; + flex-grow: 0; + flex-shrink: 0; +} + +button:hover { + background-position: 0 -10px; +} + +button:active { + background-image: none; +} + +messagecontainer button { + background-image: linear-gradient(to bottom, #5cb85c 0, #419641 100%); + border-color: #3e8f3e; +} + +messagecontainer button:hover { + background-color: #419641; +} + +messagecontainer button:active { + background-color: #419641; + border-color: #3e8f3e; +} + +pincontainer button { + background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); + border-color: #e38d13; +} + +pincontainer button:hover { + background-color: #eb9316; +} + +pincontainer button:active { + background-color: #eb9316; + border-color: #e38d13; +} + +subjectcontainer button { + background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); + border-color: #28a4c9; +} + +subjectcontainer button:hover { + background-color: #2aabd2; +} + +subjectcontainer button:active { + background-color: #2aabd2; + border-color: #28a4c9; +} + +label { + padding: 4px 4px; + font-variant: small-caps; +} + +value { + padding: 4px 0; + font-weight: 300; +} + +select { + margin-top: 4px; + font-family: Roboto; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 3px; + background: linear-gradient(rgba(0, 0, 0, 0.025), white); + box-shadow: inset 1px 1px rgba(0, 0, 0, 0.05); + transition: all 0.1s ease-out; + padding: 5px; + color: #555; +} + +item { + font-family: 'Source Code Pro'; + display: block; + width: 100%; + padding: 5px; + border-bottom: 1px solid; + cursor: default; +} + +item.selected { + background-color: #eeeeee; +} + +subjectcontainer item { + border-color: #bce8f1; +} + +subjectcontainer row { + color: #235066; +} + +messagecontainer item { + border-color: #d6e9c6; +} + +messagecontainer row { + color: #336606; +} + +pincontainer item { + border-color: #faebcc; +} + +pincontainer row { + color: #66512c; +} + +error item { + background-color: #ebccd1; +} + +error item.selected { + background-color: #ccb1b6; } diff --git a/static/debug.html b/static/debug.html index c493dbb..2c8ce7c 100644 --- a/static/debug.html +++ b/static/debug.html @@ -1,32 +1,88 @@ - + + + + Status + + + + + + + + + + + + + + + + + Subjects + Subscribe + + + + + + + + + + + + + + + Messages + Send Message + + + + + + Pins + Pin + + + + + + diff --git a/static/debug.js b/static/debug.js new file mode 100644 index 0000000..c752d7c --- /dev/null +++ b/static/debug.js @@ -0,0 +1,273 @@ +var cosmo; +var elements = {}; +var selectedSubject = null; +var pins = {}; +var senders = {}; + +var onReady = function() { + var elementIDs = [ + 'connectionStatus', + 'loginAction', + 'loginStatus', + 'messageList', + 'messageText', + 'pinList', + 'pinText', + 'readID', + 'subjectList', + 'subjectName', + 'username', + 'writeID' + ]; + for (var i = 0; i < elementIDs.length; i++) { + elements[elementIDs[i]] = document.getElementById(elementIDs[i]); + } + + var callbacks = { + 'onConnect': onConnect, + 'onDisconnect': onDisconnect, + 'onLogin': onLogin, + 'onLogout': onLogout, + 'onMessage': onMessage, + 'onPin': onPin, + 'onUnpin': onUnpin, + } + cosmo = new Cosmopolite(callbacks); + + document.getElementById('pin').addEventListener('click', pin); + document.getElementById('sendMessage').addEventListener('click', sendMessage); + document.getElementById('subscribe').addEventListener('click', subscribe); +}; + +document.addEventListener('DOMContentLoaded', onReady); + +var onConnect = function() { + elements['connectionStatus'].innerHTML = ''; + elements['connectionStatus'].appendChild( + document.createTextNode('Connected')); +}; + +var onDisconnect = function() { + elements['connectionStatus'].innerHTML = ''; + elements['connectionStatus'].appendChild( + document.createTextNode('Disconnected')); +}; + +var onLogin = function(username, logout_url) { + elements['loginStatus'].innerHTML = ''; + elements['loginStatus'].appendChild(document.createTextNode('Logged in')); + + elements['username'].innerHTML = ''; + elements['username'].appendChild(document.createTextNode(username)); + + elements['loginAction'].innerHTML = ''; + var link = document.createElement('a'); + link.href = logout_url; + link.target = '_blank'; + link.appendChild(document.createTextNode('Log out')); + elements['loginAction'].appendChild(link); +}; + +var onLogout = function(login_url) { + elements['loginStatus'].innerHTML = ''; + elements['loginStatus'].appendChild( + document.createTextNode('Not logged in')); + + elements['username'].innerHTML = ''; + + elements['loginAction'].innerHTML = ''; + var link = document.createElement('a'); + link.href = login_url; + link.target = '_blank'; + link.appendChild(document.createTextNode('Log in')); + elements['loginAction'].appendChild(link); +}; + +var onMessage = function(msg) { + addToList(msg, elements['messageList']); +}; + +var onPin = function(msg) { + var item = addToList(msg, elements['pinList'], pins); + if (msg['sender'] == cosmo.currentProfile()) { + item.addEventListener('contextmenu', deletePin); + } +}; + +var onUnpin = function(msg) { + var item = pins[msg['id']]; + item.parentNode.removeChild(item); +}; + +var selectSubject = function() { + if (selectedSubject == this) { + return; + } + if (selectedSubject) { + selectedSubject.className = ''; + } + this.className = 'selected'; + selectedSubject = this; + + elements['messageList'].innerHTML = ''; + cosmo.getMessages(this.subject).forEach(onMessage); + + elements['pinList'].innerHTML = ''; + cosmo.getPins(this.subject).forEach(onPin); +}; + +var addToList = function(msg, list, trackobj) { + if (selectedSubject && ( + msg['subject']['name'] != selectedSubject.subject['name'] || + msg['subject']['readable_only_by'] != + selectedSubject.subject['readable_only_by'] || + msg['subject']['writable_only_by'] != + selectedSubject.subject['writable_only_by'])) { + return; + } + + var item = document.createElement('item'); + item.message = msg; + + { + var row = document.createElement('row'); + row.appendChild(document.createTextNode('Sender: ')); + row.appendChild(document.createTextNode(senderID(msg))); + item.appendChild(row); + } + { + var row = document.createElement('row'); + row.appendChild(document.createTextNode('Created: ')); + row.appendChild(document.createTextNode( + (new Date(msg['created'] * 1000)).toString())); + item.appendChild(row); + } + item.appendChild(document.createTextNode(msg['message'])); + + list.insertBefore(item, list.firstChild); + + if (trackobj) { + trackobj[msg['id']] = item; + } + + return item; +}; + +var deletePin = function(e) { + cosmo.unpin(this.message['sender_message_id']); + e.preventDefault(); +}; + +var deleteSubject = function(e) { + cosmo.unsubscribe(this.subject); + if (selectedSubject == this) { + selectedSubject = null; + elements['messageList'].innerHTML = ''; + elements['pinList'].innerHTML = ''; + } + this.parentNode.removeChild(this); + e.preventDefault(); +}; + +var addSubject = function(subject, error) { + var item = document.createElement('item'); + item.subject = subject; + + item.appendChild(document.createTextNode(subject['name'])); + + { + var row = document.createElement('row'); + row.appendChild(document.createTextNode('Read: ')); + row.appendChild( + document.createTextNode(elements['readID'].selectedOptions[0].text)); + item.appendChild(row); + } + + { + var row = document.createElement('row'); + row.appendChild(document.createTextNode('Write: ')); + row.appendChild( + document.createTextNode(elements['writeID'].selectedOptions[0].text)); + item.appendChild(row); + } + + item.addEventListener('click', selectSubject); + item.addEventListener('contextmenu', deleteSubject); + if (error) { + var error = document.createElement('error'); + error.appendChild(item); + elements['subjectList'].appendChild(error); + } else { + elements['subjectList'].appendChild(item); + } + + if (!selectedSubject) { + selectSubject.bind(item)(); + } +}; + +var subscribe = function() { + var subject = { + 'name': elements['subjectName'].value + }; + if (elements['readID'].value != '(all)') { + var value = elements['readID'].value; + if (value == 'me') { + value = cosmo.currentProfile(); + } + subject['readable_only_by'] = value; + } + if (elements['writeID'].value != '(all)') { + var value = elements['writeID'].value; + if (value == 'me') { + value = cosmo.currentProfile(); + } + subject['writable_only_by'] = value; + } + cosmo.subscribe(subject, -1).then(function() { + addSubject(subject); + }, function() { + addSubject(subject, true); + }); +}; + +var sendMessage = function() { + if (!selectedSubject) { + alert('Please select a subject.'); + return; + } + cosmo.sendMessage(selectedSubject.subject, elements['messageText'].value); + elements['messageText'].value = ''; +}; + +var pin = function() { + if (!selectedSubject) { + alert('Please select a subject.'); + return; + } + cosmo.pin(selectedSubject.subject, elements['pinText'].value); + elements['pinText'].value = ''; +}; + +var senderID = function(msg) { + var id = Math.abs(msg['sender'].hashCode() % 1000000); + if (msg['sender'] == cosmo.currentProfile()) { + return 'me'; + } + if (!senders[id]) { + senders[id] = msg['sender']; + { + var option = document.createElement('option'); + option.value = msg['sender']; + option.appendChild(document.createTextNode(id)); + elements['readID'].appendChild(option); + } + { + var option = document.createElement('option'); + option.value = msg['sender']; + option.appendChild(document.createTextNode(id)); + elements['writeID'].appendChild(option); + } + } + return id; +};