1
0
Эх сурвалжийг харах

[add] Manage attachments (can send attachments)
[add][Closes #2] send reply (do not show them yet)
[add][Closes #26] Focus text input on browser focus / chan change / tab focus

B Thibault 8 жил өмнө
parent
commit
a1166f10e5

+ 6 - 0
cli/resources.js

@@ -9,6 +9,7 @@ var R = {
         ,message: {
             form: "msgForm"
             ,input: "msgInput"
+            ,replyTo: "replyToContainer"
         }
     }
     ,klass: {
@@ -17,6 +18,7 @@ var R = {
         ,noNetwork: "no-network"
         ,unread: "unread"
         ,unreadHi: "unreadHi"
+        ,replyingTo: "replyingTo"
         ,chatList: {
             entry: "slack-context-room"
             ,typeChannel: "slack-channel"
@@ -36,6 +38,10 @@ var R = {
                 ,reply: "slackmsg-hover-reply"
             }
 
+            ,replyTo: {
+                close: "replyto-close"
+            }
+
             ,link: "slackmsg-link"
             ,linkuser: "slackmsg-link-user"
             ,linkchan: "slackmsg-link-chan"

+ 48 - 6
cli/ui.js

@@ -1,3 +1,8 @@
+var
+    /** @type {SlackMessage|null} */
+    REPLYING_TO = null
+;
+
 /**
  * @param {SlackChan|SlackGroup} chan
  * @return {Element}
@@ -76,10 +81,38 @@ function onRoomSelected() {
     var roomLi = document.getElementById(SELECTED_ROOM.id);
     document.getElementById(R.id.currentRoom.title).textContent = name;
     onRoomUpdated();
+    focusInput();
 
     markRoomAsRead(SELECTED_ROOM);
+    if (REPLYING_TO) {
+        REPLYING_TO = null;
+        onReplyingToUpdated();
+    }
 }
 
+function onReplyingToUpdated() {
+    if (REPLYING_TO) {
+        document.body.classList.add(R.klass.replyingTo);
+        var domParent = document.getElementById(R.id.message.replyTo)
+            ,closeLink = document.createElement("a");
+        closeLink.addEventListener("click", function() {
+            REPLYING_TO = null;
+            onReplyingToUpdated();
+        });
+        closeLink.className = R.klass.msg.replyTo.close;
+        closeLink.textContent = 'x';
+        domParent.textContent = "";
+        domParent.appendChild(closeLink);
+        domParent.appendChild(createMessageDom("reply_" +SELECTED_ROOM.id, REPLYING_TO));
+    } else {
+        document.body.classList.remove(R.klass.replyingTo);
+    }
+}
+
+/**
+ * @param {string} channelId
+ * @param {SlackMessage} msg
+**/
 function createMessageDom(channelId, msg) {
     var dom = document.createElement("div")
         ,ts = document.createElement("div")
@@ -216,10 +249,6 @@ function onChanClick(e) {
     }
 }
 
-function replyTo(msg) {
-    console.log("Replying to ", msg);
-}
-
 function chatClickDelegate(e) {
     var target = e.target
         ,getMessageId = function(e, target) {
@@ -242,7 +271,10 @@ function chatClickDelegate(e) {
 
                 for (var i =0, histLen =history.length; i < histLen && history[i].ts <= messageId; i++) {
                     if (history[i].ts === messageId) {
-                        replyTo(history[i]);
+                        if (REPLYING_TO !== history[i]) {
+                            REPLYING_TO = history[i];
+                            onReplyingToUpdated();
+                        }
                         return;
                     }
                 }
@@ -253,6 +285,10 @@ function chatClickDelegate(e) {
     }
 }
 
+function focusInput() {
+    document.getElementById(R.id.message.input).focus();
+}
+
 document.addEventListener('DOMContentLoaded', function() {
     document.getElementById(R.id.chatList).addEventListener("click", onChanClick);
     document.getElementById(R.id.currentRoom.content).addEventListener("click", chatClickDelegate);
@@ -260,9 +296,14 @@ document.addEventListener('DOMContentLoaded', function() {
         e.preventDefault();
         var input =document.getElementById(R.id.message.input);
         if (SELECTED_ROOM && input.value) {
-            sendMsg(SELECTED_ROOM, input.value);
+            sendMsg(SELECTED_ROOM, input.value, REPLYING_TO);
             input.value = "";
+            if (REPLYING_TO) {
+                REPLYING_TO = null;
+                onReplyingToUpdated();
+            }
         }
+        focusInput();
         return false;
     });
     window.addEventListener('blur', function() {
@@ -272,6 +313,7 @@ document.addEventListener('DOMContentLoaded', function() {
         window.hasFocus = true;
         if (SELECTED_ROOM)
             markRoomAsRead(SELECTED_ROOM);
+        focusInput();
     });
     window.hasFocus = true;
 

+ 25 - 2
cli/workflow.js

@@ -109,10 +109,33 @@ function unselectRoom() {
 /**
  * @param {SlackChan|SlackGroup|SlackIms} chan
  * @param {string} msg
+ * @param {SlackMessage|null=} replyTo
 **/
-function sendMsg(chan, msg) {
+function sendMsg(chan, msg, replyTo) {
     var xhr = new XMLHttpRequest();
-    xhr.open('POST', 'api/msg?room=' +chan.id +"&text=" +encodeURIComponent(msg), true);
+    var url = 'api/msg?room=' +chan.id +"&text=" +encodeURIComponent(msg);
+    if (replyTo) {
+        var sender = SLACK.context.getMember(replyTo.userId)
+            ,footer = "Message";
+
+        if (chan.id[0] === 'C') {
+            footer = "Channel message";
+        } else if (chan.id[0] === 'D') {
+            footer = "Direct message";
+        } else if (chan.id[0] === 'G') {
+            footer = "Group message";
+        }
+        var attachment = {
+            "fallback": replyTo.raw["text"] || ""
+            ,"author_name": "<@" +sender.id +"|" +sender.name +">"
+            ,"author_icon": sender.icons.image_48
+            ,"text": replyTo.raw["text"] || ""
+            ,"footer": footer
+            ,"ts": replyTo.ts
+        };
+        url += "&attachments=" +encodeURIComponent(JSON.stringify([attachment]));
+    }
+    xhr.open('POST', url, true);
     xhr.send(null);
 }
 

+ 2 - 0
srv/public/index.html

@@ -15,6 +15,8 @@
             <div class="slack-chat-content" id="chatWindow"></div>
             <div class="slack-chat-control">
                 <form id="msgForm">
+                    <div id="replyToContainer" class="replyto-container">
+                    </div>
                     <input type="text" id="msgInput" />
                     <input type="submit" />
                 </form>

+ 1 - 0
srv/public/repl.svg

@@ -0,0 +1 @@
+<?xml version="1.0" ?><svg height="1024" width="768" xmlns="http://www.w3.org/2000/svg"><path d="M384 160l-384 288 384 288v-192s288 0 384 280c0-472-384-472-384-472v-192z"/></svg>

+ 17 - 15
srv/public/slack.min.js

@@ -1,15 +1,17 @@
-function g(a){this.id=a.id;this.name=a.name}function h(a,b){this.id=a.id;this.name=a.name;this.b=parseFloat(a.last_read);this.a={};if(a.members)for(var c=0,d=a.members.length;c<d;c++){var f=m(b,a.members[c]);this.a[f.id]=f;f.c[this.id]=this}}function p(a,b){var c=[];this.id=b.id;this.a={};for(var d=0,f=b.members.length;d<f;d++){var e=m(a,b.members[d]);this.a[b.members[d]]=e;e.c[this.id]=this;c.push(e.name)}this.name=c.join(", ");this.b=parseFloat(b.last_read)}
-function q(a,b){this.id=b.id;this.f=a;this.b=parseFloat(b.last_read)}function u(a){this.id=a.id;this.name=a.name;this.status=a.status;this.b={A:a.profile.image_24,B:a.profile.image_32,l:a.profile.image_48,o:a.profile.image_72,w:a.profile.image_192,D:a.profile.image_512};this.c={};this.a=null}function v(a){this.id=a.id;this.name=a.name;this.b={C:a.icons.image_36,l:a.icons.image_48,o:a.icons.image_72};this.c={};this.a=null}
-function x(){this.j=null;this.c={};this.b={};this.f={};this.a={};this.h=null;this.i={}}function m(a,b){return a.a[b]||a.i[b]||null}function y(a,b){return a.c[b]||a.f[b]||a.b[b]||null}"undefined"!==typeof module&&(module.m.s=x);function z(a){this.g=parseFloat(a.ts);this.raw=a}function A(a,b,c){this.id="string"===typeof a?a:a.id;this.a=[];this.b=b;c&&B(this,c)}function B(a,b){var c=0;b.forEach(function(a){c=Math.max(this.push(a),c)}.bind(a))}A.prototype.push=function(a){for(var b=parseFloat(a.ts),c=0,d=this.a.length;c<d;c++)if(this.a[c].g===b)return b;for(this.a.push(new z(a));this.a.length>this.b;)this.a.shift();return b};"undefined"!==typeof module&&(module.m.u=A);function C(){var a=document.createDocumentFragment(),b=D.a.h?Object.keys(D.a.h.c):[];b.sort(function(a,b){return a[0]!==b[0]?a[0]-b[0]:(D.a.c[a]||D.a.b[a]).name.localeCompare((D.a.c[b]||D.a.b[b]).name)});b.forEach(function(b){b=D.a.c[b]||D.a.b[b];var c=document.createElement("li");c.id=b.id;"D"===b.id[0]?c.className="slack-context-room slack-ims":"G"===b.id[0]?c.className="slack-context-room slack-group":"C"===b.id[0]&&(c.className="slack-context-room slack-channel");c.textContent=b.name;c&&a.appendChild(c)});
-b=D.a.a?Object.keys(D.a.a):[];b.sort(function(a,b){return D.a.a[a].name.localeCompare(D.a.a[b].name)});b.forEach(function(b){b=D.a.a[b].a;var c=document.createElement("li");c.id=b.id;c.className="slack-context-room";c.textContent=b.f.name;c&&a.appendChild(c)});document.getElementById("chanList").textContent="";document.getElementById("chanList").appendChild(a)}function E(a){a?document.body.classList.remove("no-network"):document.body.classList.add("no-network")}
-function F(a,b){var c=document.createElement("div"),d=document.createElement("div"),f=document.createElement("div"),e=document.createElement("div"),l=document.createElement("img"),n=document.createElement("span"),k=document.createElement("ul"),r=document.createElement("li"),t=b.raw.user?D.a.a[b.raw.user]:D.a.i[b.raw.bot_id];c.id=a+"_"+b.g;c.className="slackmsg-item";d.className="slackmsg-ts";f.className="slackmsg-msg";e.className="slackmsg-author";l.className="slackmsg-author-img";n.className="slackmsg-author-name";
-k.className="slackmsg-hover";r.className="slackmsg-hover-reply";d.textContent=(new Date(1E3*b.g)).toLocaleTimeString();var w=b.raw.text||"",w=w.replace(RegExp("<([@#]?)([^>]*)>","g"),function(a,b,c){a=c.split("|");"@"===b?(a[1]||(c=m(D.a,a[0]),a[1]=c?c.name:"Unknown member"),a[0]="#"+a[0],a[2]="slackmsg-link slackmsg-link-user"):"#"===b?(a[1]?"#"!==a[1][0]&&(a[1]="#"+a[1]):(c=y(D.a,a[0]),a[1]=c?"#"+c.name:"Unknown channel"),a[0]="#"+a[0],a[2]="slackmsg-link slackmsg-link-chan"):(a[1]||(a[1]=a[0]),
-a[2]="slackmsg-link");return'<a href="'+a[0]+'" class="'+a[2]+'"'+(b?"":' target="_blank"')+">"+a[1]+"</a>"});f.innerHTML=w;n.textContent=t?t.name:b.raw.username||"?";l.src=t?t.b.l:"";e.appendChild(l);e.appendChild(n);k.appendChild(r);c.appendChild(e);c.appendChild(f);c.appendChild(d);c.appendChild(k);return c}function G(){var a=0,b;for(b in H)H.hasOwnProperty(b)&&(a+=H[b]);document.title=(a?"("+a+") - ":"")+D.a.j.name}
-function I(){var a=J;H[a.id]&&(H[a.id]=0,G());a=document.getElementById(a.id);a.classList.remove("unread");a.classList.remove("unreadHi")}function K(){var a=document.createDocumentFragment(),b=J.id;document.getElementById("chatWindow").textContent="";D.b[b]&&D.b[b].a.forEach(function(c){a.appendChild(F(b,c))});var c=document.getElementById("chatWindow");c.appendChild(a);c.scrollTop=c.scrollHeight-c.clientHeight}
-function L(a){for(;a.target!==a.currentTarget&&a.target;){if(a.target.classList.contains("slack-context-room")){if((a=D.a.c[a.target.id]||D.a.f[a.target.id]||D.a.b[a.target.id])&&a!==J){J&&document.getElementById(J.id).classList.remove("selected");document.getElementById(a.id).classList.add("selected");document.body.classList.remove("no-room-selected");J=a;a=void 0;var b=J.name||(J.f?J.f.name:void 0);if(!b){b=[];for(a in J.a)b.push(J.a[a].name);b=b.join(", ")}document.getElementById("currentRoomTitle").textContent=
-b;K();I();J.b&&!D.b[J.id]&&(a=new XMLHttpRequest,a.open("GET","api/hist?room="+J.id,!0),a.send(null))}break}a.target=a.target.parentElement}}
-function M(a){for(var b=a.target;b!==a.currentTarget&&b&&!b.classList.contains("slackmsg-hover");){if(b.classList.contains("slackmsg-hover-reply")){a:{for(b=b||a.target;b!==a.currentTarget&&b;){if(b.classList.contains("slackmsg-item")){a=b.id;break a}b=b.parentElement}a=void 0}if(a){a=parseFloat(a.split("_")[1]);for(var b=D.b[J.id].a,c=0,d=b.length;c<d&&b[c].g<=a;c++)if(b[c].g===a){console.log("Replying to ",b[c]);break}}break}b=b.parentElement}}
-document.addEventListener("DOMContentLoaded",function(){document.getElementById("chatList").addEventListener("click",L);document.getElementById("chatWindow").addEventListener("click",M);document.getElementById("msgForm").addEventListener("submit",function(a){a.preventDefault();a=document.getElementById("msgInput");if(J&&a.value){var b=new XMLHttpRequest;b.open("POST","api/msg?room="+J.id+"&text="+encodeURIComponent(a.value),!0);b.send(null);a.value=""}return!1});window.addEventListener("blur",function(){window.hasFocus=
-!1});window.addEventListener("focus",function(){window.hasFocus=!0;J&&I()});window.hasFocus=!0;N()});var D,H={};D=new function(){this.f=0;this.a=new x;this.b={}};var O=0,J=null;function P(a){var b=new XMLHttpRequest;b.timeout=6E4;b.onreadystatechange=function(){if(4===b.readyState)if(b.status){var c=null,d=2===Math.floor(b.status/100);if(d){O&&(O=0,E(!0));c=b.response;try{c=JSON.parse(c)}catch(f){c=null}}else O?(O+=Math.floor((O||5)/2),O=Math.min(60,O)):(O=5,E(!1));a(d,c)}else O&&(O=0,E(!0)),P(a)};b.open("GET","api?v="+D.f,!0);b.send(null)}
-function Q(a,b){if(a){if(b){var c=D;b.v&&(c.f=b.v);if(b["static"]){for(var d=c.a,f=b["static"],e=0,l=f.bots.length;e<l;e++)d.i[f.bots[e].id]=new v(f.bots[e]);e=0;for(l=f.users.length;e<l;e++)d.a[f.users[e].id]=new u(f.users[e]);e=0;for(l=f.ims.length;e<l;e++){var n=m(d,f.ims[e].user);n&&(n.a=new q(n,f.ims[e]),d.f[n.a.id]=n.a)}e=0;for(l=f.channels.length;e<l;e++)d.c[f.channels[e].id]=new h(f.channels[e],d);e=0;for(l=f.groups.length;e<l;e++)d.b[f.groups[e].id]=new p(d,f.groups[e]);d.j=new g(f.team);
-d.h=m(d,f.self.id);C()}if(b.live){for(var k in b.live)(d=c.b[k])?B(d,b.live[k]):c.b[k]=new A(k,500,b.live[k]);for(var r in b.live)k=y(c.a,r),d=b.live[r],!k||k===J&&window.hasFocus||(document.getElementById(k.id).classList.add("unread"),H[k.id]=(H[k.id]||0)+d.length,G()),J&&b.live[J.id]&&K()}}N()}else setTimeout(N,1E3*O)}function N(){P(Q)};
+function g(b){this.id=b.id;this.name=b.name}function h(b,a){this.id=b.id;this.name=b.name;this.b=parseFloat(b.last_read);this.a={};if(b.members)for(var c=0,e=b.members.length;c<e;c++){var f=m(a,b.members[c]);this.a[f.id]=f;f.f[this.id]=this}}function n(b,a){var c=[];this.id=a.id;this.a={};for(var e=0,f=a.members.length;e<f;e++){var d=m(b,a.members[e]);this.a[a.members[e]]=d;d.f[this.id]=this;c.push(d.name)}this.name=c.join(", ");this.b=parseFloat(a.last_read)}
+function q(b,a){this.id=a.id;this.c=b;this.b=parseFloat(a.last_read)}function r(b){this.id=b.id;this.name=b.name;this.status=b.status;this.b={w:b.profile.image_24,A:b.profile.image_32,i:b.profile.image_48,m:b.profile.image_72,u:b.profile.image_192,C:b.profile.image_512};this.f={};this.a=null}function u(b){this.id=b.id;this.name=b.name;this.b={B:b.icons.image_36,i:b.icons.image_48,m:b.icons.image_72};this.f={};this.a=null}
+function w(){this.j=null;this.f={};this.b={};this.c={};this.a={};this.g=null;this.h={}}function m(b,a){return b.a[a]||b.h[a]||null}function x(b,a){return b.f[a]||b.c[a]||b.b[a]||null}"undefined"!==typeof module&&(module.l.o=w);function z(b){this.c=b.user;this.b=parseFloat(b.ts);this.a=b}function A(b,a,c){this.id="string"===typeof b?b:b.id;this.a=[];this.b=a;c&&B(this,c)}function B(b,a){var c=0;a.forEach(function(b){c=Math.max(this.push(b),c)}.bind(b))}A.prototype.push=function(b){for(var a=parseFloat(b.ts),c=0,e=this.a.length;c<e;c++)if(this.a[c].b===a)return a;for(this.a.push(new z(b));this.a.length>this.b;)this.a.shift();return a};"undefined"!==typeof module&&(module.l.s=A);var C=null;
+function D(){var b=document.createDocumentFragment(),a=E.a.g?Object.keys(E.a.g.f):[];a.sort(function(b,a){return b[0]!==a[0]?b[0]-a[0]:(E.a.f[b]||E.a.b[b]).name.localeCompare((E.a.f[a]||E.a.b[a]).name)});a.forEach(function(a){a=E.a.f[a]||E.a.b[a];var c=document.createElement("li");c.id=a.id;"D"===a.id[0]?c.className="slack-context-room slack-ims":"G"===a.id[0]?c.className="slack-context-room slack-group":"C"===a.id[0]&&(c.className="slack-context-room slack-channel");c.textContent=a.name;c&&b.appendChild(c)});
+a=E.a.a?Object.keys(E.a.a):[];a.sort(function(b,a){return E.a.a[b].name.localeCompare(E.a.a[a].name)});a.forEach(function(a){a=E.a.a[a].a;var c=document.createElement("li");c.id=a.id;c.className="slack-context-room";c.textContent=a.c.name;c&&b.appendChild(c)});document.getElementById("chanList").textContent="";document.getElementById("chanList").appendChild(b)}function F(b){b?document.body.classList.remove("no-network"):document.body.classList.add("no-network")}
+function G(){if(C){document.body.classList.add("replyingTo");var b=document.getElementById("replyToContainer"),a=document.createElement("a");a.addEventListener("click",function(){C=null;G()});a.className="replyto-close";a.textContent="x";b.textContent="";b.appendChild(a);b.appendChild(H("reply_"+I.id,C))}else document.body.classList.remove("replyingTo")}
+function H(b,a){var c=document.createElement("div"),e=document.createElement("div"),f=document.createElement("div"),d=document.createElement("div"),k=document.createElement("img"),p=document.createElement("span"),l=document.createElement("ul"),t=document.createElement("li"),v=a.a.user?E.a.a[a.a.user]:E.a.h[a.a.bot_id];c.id=b+"_"+a.b;c.className="slackmsg-item";e.className="slackmsg-ts";f.className="slackmsg-msg";d.className="slackmsg-author";k.className="slackmsg-author-img";p.className="slackmsg-author-name";
+l.className="slackmsg-hover";t.className="slackmsg-hover-reply";e.textContent=(new Date(1E3*a.b)).toLocaleTimeString();var y=a.a.text||"",y=y.replace(RegExp("<([@#]?)([^>]*)>","g"),function(a,b,c){a=c.split("|");"@"===b?(a[1]?"@"!==a[1][0]&&(a[1]="@"+a[1]):(c=m(E.a,a[0]),a[1]=c?"@"+c.name:"Unknown member"),a[0]="#"+a[0],a[2]="slackmsg-link slackmsg-link-user"):"#"===b?(a[1]?"#"!==a[1][0]&&(a[1]="#"+a[1]):(c=x(E.a,a[0]),a[1]=c?"#"+c.name:"Unknown channel"),a[0]="#"+a[0],a[2]="slackmsg-link slackmsg-link-chan"):
+(a[1]||(a[1]=a[0]),a[2]="slackmsg-link");return'<a href="'+a[0]+'" class="'+a[2]+'"'+(b?"":' target="_blank"')+">"+a[1]+"</a>"});f.innerHTML=y;p.textContent=v?v.name:a.a.username||"?";k.src=v?v.b.i:"";d.appendChild(k);d.appendChild(p);l.appendChild(t);c.appendChild(d);c.appendChild(f);c.appendChild(e);c.appendChild(l);return c}function J(){var b=0,a;for(a in K)K.hasOwnProperty(a)&&(b+=K[a]);document.title=(b?"("+b+") - ":"")+E.a.j.name}
+function L(){var b=I;K[b.id]&&(K[b.id]=0,J());b=document.getElementById(b.id);b.classList.remove("unread");b.classList.remove("unreadHi")}function M(){var b=document.createDocumentFragment(),a=I.id;document.getElementById("chatWindow").textContent="";E.b[a]&&E.b[a].a.forEach(function(c){b.appendChild(H(a,c))});var c=document.getElementById("chatWindow");c.appendChild(b);c.scrollTop=c.scrollHeight-c.clientHeight}
+function N(b){for(;b.target!==b.currentTarget&&b.target;){if(b.target.classList.contains("slack-context-room")){if((b=E.a.f[b.target.id]||E.a.c[b.target.id]||E.a.b[b.target.id])&&b!==I){I&&document.getElementById(I.id).classList.remove("selected");document.getElementById(b.id).classList.add("selected");document.body.classList.remove("no-room-selected");I=b;b=void 0;var a=I.name||(I.c?I.c.name:void 0);if(!a){a=[];for(b in I.a)a.push(I.a[b].name);a=a.join(", ")}document.getElementById("currentRoomTitle").textContent=
+a;M();O();L();C&&(C=null,G());I.b&&!E.b[I.id]&&(b=new XMLHttpRequest,b.open("GET","api/hist?room="+I.id,!0),b.send(null))}break}b.target=b.target.parentElement}}
+function P(b){for(var a=b.target;a!==b.currentTarget&&a&&!a.classList.contains("slackmsg-hover");){if(a.classList.contains("slackmsg-hover-reply")){a:{for(a=a||b.target;a!==b.currentTarget&&a;){if(a.classList.contains("slackmsg-item")){b=a.id;break a}a=a.parentElement}b=void 0}if(b){b=parseFloat(b.split("_")[1]);for(var a=E.b[I.id].a,c=0,e=a.length;c<e&&a[c].b<=b;c++)if(a[c].b===b){C!==a[c]&&(C=a[c],G());break}}break}a=a.parentElement}}function O(){document.getElementById("msgInput").focus()}
+document.addEventListener("DOMContentLoaded",function(){document.getElementById("chatList").addEventListener("click",N);document.getElementById("chatWindow").addEventListener("click",P);document.getElementById("msgForm").addEventListener("submit",function(b){b.preventDefault();b=document.getElementById("msgInput");if(I&&b.value){var a=I,c=C,e=new XMLHttpRequest,f="api/msg?room="+a.id+"&text="+encodeURIComponent(b.value);if(c){var d=m(E.a,c.c),k="Message";"C"===a.id[0]?k="Channel message":"D"===a.id[0]?
+k="Direct message":"G"===a.id[0]&&(k="Group message");f+="&attachments="+encodeURIComponent(JSON.stringify([{fallback:c.a.text||"",author_name:"<@"+d.id+"|"+d.name+">",author_icon:d.b.i,text:c.a.text||"",footer:k,ts:c.b}]))}e.open("POST",f,!0);e.send(null);b.value="";C&&(C=null,G())}O();return!1});window.addEventListener("blur",function(){window.hasFocus=!1});window.addEventListener("focus",function(){window.hasFocus=!0;I&&L();O()});window.hasFocus=!0;Q()});var E,K={};E=new function(){this.c=0;this.a=new w;this.b={}};var R=0,I=null;function S(b){var a=new XMLHttpRequest;a.timeout=6E4;a.onreadystatechange=function(){if(4===a.readyState)if(a.status){var c=null,e=2===Math.floor(a.status/100);if(e){R&&(R=0,F(!0));c=a.response;try{c=JSON.parse(c)}catch(f){c=null}}else R?(R+=Math.floor((R||5)/2),R=Math.min(60,R)):(R=5,F(!1));b(e,c)}else R&&(R=0,F(!0)),S(b)};a.open("GET","api?v="+E.c,!0);a.send(null)}
+function T(b,a){if(b){if(a){var c=E;a.v&&(c.c=a.v);if(a["static"]){for(var e=c.a,f=a["static"],d=0,k=f.bots.length;d<k;d++)e.h[f.bots[d].id]=new u(f.bots[d]);d=0;for(k=f.users.length;d<k;d++)e.a[f.users[d].id]=new r(f.users[d]);d=0;for(k=f.ims.length;d<k;d++){var p=m(e,f.ims[d].user);p&&(p.a=new q(p,f.ims[d]),e.c[p.a.id]=p.a)}d=0;for(k=f.channels.length;d<k;d++)e.f[f.channels[d].id]=new h(f.channels[d],e);d=0;for(k=f.groups.length;d<k;d++)e.b[f.groups[d].id]=new n(e,f.groups[d]);e.j=new g(f.team);
+e.g=m(e,f.self.id);D()}if(a.live){for(var l in a.live)(e=c.b[l])?B(e,a.live[l]):c.b[l]=new A(l,500,a.live[l]);for(var t in a.live)l=x(c.a,t),e=a.live[t],!l||l===I&&window.hasFocus||(document.getElementById(l.id).classList.add("unread"),K[l.id]=(K[l.id]||0)+e.length,J()),I&&a.live[I.id]&&M()}}Q()}else setTimeout(Q,1E3*R)}function Q(){S(T)};

+ 9 - 7
srv/public/style.css

@@ -33,8 +33,8 @@ body {
 .no-network .error { display: inline-block; }
 .slack-chat-content { height: calc(100vh - 8em); overflow: auto; }
 .slack-chat-title { display: inline-block; height: 1.75em; font-size: 1.75em; font-style: italic; }
-.slack-chat-control { display: inline-block; width: 100%; height: 2em; padding: 1.5em 0; }
-.slack-chat-control * { display: inline-block; height: 100%; }
+.slack-chat-control { display: inline-block; position: relative; width: 100%; height: 2em; padding: 1.5em 0; }
+.slack-chat-control > * { display: inline-block; height: 100%; }
 .slack-context-room.unread { font-weight: bold; }
 .slack-context-room.slack-channel + .slack-context-room:not(.slack-channel), .slack-context-room.slack-group + .slack-context-room:not(.slack-group) { margin-top: 1em; border-top: 1px solid grey; }
 .slackmsg-item { position: relative; background: #eee; }
@@ -46,11 +46,13 @@ body {
 .slackmsg-ts {}
 
 .slackmsg-hover { display: none; position: absolute; top: 0; right: 0; margin: 0; list-style: none; padding: 0; }
-.slackmsg-hover > li { display: inline-block; width: 1.5em; height: 1.5em; background: white; border: 1px solid black; cursor: pointer; }
+.slackmsg-hover > li { display: inline-block; width: 1.5em; height: 1.5em; background: white; border: 1px solid black; cursor: pointer; background-size: contain; background-repeat: no-repeat; background-position: center center; }
 .slackmsg-hover > li:first-child { border-bottom-left-radius: 5px; }
 .slackmsg-hover > li:last-child { border-top-right-radius: 5px; }
-/*
- * DEBUG
-.slackmsg-item:hover .slackmsg-hover { display: block; }
-*/
+.slackmsg-hover .slackmsg-hover-reply { background-image: url("repl.svg"); }
+.slack-chat-content .slackmsg-item:hover .slackmsg-hover { display: block; }
+.replyto-container { display: none; position: relative; height: 5em; width: 100%; }
+.replyingTo .replyto-container { display: inline-block; }
+.replyingTo .slack-chat-content { height: calc(100vh - 13em); }
+.replyto-container .replyto-close { position: absolute; display: inline-block; top: 15px; right: 0; height: 1.5em; width: 1.5em; text-align: center; cursor: pointer; z-index: 500; }
 

+ 6 - 1
srv/src/httpServ.js

@@ -132,7 +132,12 @@ Server.prototype.onRequest = function(req, res) {
                 } else {
                     var chan = res.slack.data.getChannel(req.urlObj.queryTokens.room[0]);
                     if (chan) {
-                        res.slack.sendMsg(chan, req.urlObj.queryTokens.text);
+                        var attachments = null;
+                        if (req.urlObj.queryTokens.attachments) {
+                            try { attachments = JSON.parse(decodeURIComponent(req.urlObj.queryTokens.attachments[0])); }
+                            catch (e) {}
+                        }
+                        res.slack.sendMsg(chan, req.urlObj.queryTokens.text, attachments);
                         res.writeHeader("204", "No Content");
                     } else {
                         res.writeHeader("404", "Channel not found");

+ 3 - 1
srv/src/slack.js

@@ -201,12 +201,14 @@ Slack.getOauthToken = function(code, cb) {
 /**
  * @param {SlackChan|SlackGroup|SlackIms} channel
  * @param {Array.<string>} text
+ * @param {Array.<Object>=} attachments
 **/
-Slack.prototype.sendMsg = function(channel, text) {
+Slack.prototype.sendMsg = function(channel, text, attachments) {
     httpsRequest(SLACK_ENDPOINT +GETAPI.postMsg
         +"?token=" +this.token
         +"&channel=" +channel.id
         +"&text=" +text.join("\n")
+        + (attachments ? ("&attachments=" +encodeURIComponent(JSON.stringify(attachments))) : "")
         +"&as_user=true");
 };