Browse Source

[add][Refs #3] Add/remove reaction for a message already containing reactions

isundil 8 years ago
parent
commit
342bc86c12
7 changed files with 133 additions and 14 deletions
  1. 1 1
      cli/data.js
  2. 24 2
      cli/ui.js
  3. 24 0
      cli/workflow.js
  4. 12 11
      srv/public/slack.min.js
  5. 23 0
      srv/src/httpServ.js
  6. 28 0
      srv/src/slack.js
  7. 21 0
      srv/src/slackHistory.js

+ 1 - 1
cli/data.js

@@ -1,7 +1,7 @@
 
 var
     /**
-     * @type SlackWrapper
+     * @type {SlackWrapper}
     **/
     SLACK
 

+ 24 - 2
cli/ui.js

@@ -140,11 +140,13 @@ function onReplyingToUpdated() {
 }
 
 /**
+ * @param {string} chanId
+ * @param {string} msgId
  * @param {string} reaction
  * @param {Array.<string>} users
  * @return {Element|null}
 **/
-function createReactionDom(reaction, users) {
+function createReactionDom(chanId, msgId, reaction, users) {
     var emojiDom = makeEmojiDom(reaction);
     if (emojiDom) {
         var dom = document.createElement("li")
@@ -162,6 +164,7 @@ function createReactionDom(reaction, users) {
         userList.textContent = userNames.join(", ");
         emojiContainer.appendChild(emojiDom);
         emojiContainer.className = R.klass.emoji.small;
+        a.href = "javascript:toggleReaction('" +chanId +"', '" +msgId +"', '" +reaction +"')";
         a.appendChild(emojiContainer);
         a.appendChild(userList);
         dom.className = R.klass.msg.reactions.item;
@@ -171,6 +174,25 @@ function createReactionDom(reaction, users) {
     return null;
 }
 
+/**
+ * @param {string} chanId
+ * @param {string} msgId
+ * @param {string} reaction
+**/
+window['toggleReaction'] = function(chanId, msgId, reaction) {
+    var hist = SLACK.history[chanId];
+    if (!hist)
+        return;
+    var msg = hist.getMessageById(msgId);
+    if (msg) {
+        if (msg.hasReactionForUser(reaction, SLACK.context.self.id)) {
+            removeReaction(chanId, msgId, reaction);
+        } else {
+            addReaction(chanId, msgId, reaction);
+        }
+    }
+};
+
 /**
  * @param {string} channelId
  * @param {SlackMessage} msg
@@ -219,7 +241,7 @@ function doCreateMessageDom(channelId, msg, skipAttachment) {
     reactions.className = R.klass.msg.reactions.container;
     if (skipAttachment !== true) {
         if (msg.reactions) for (var reaction in msg.reactions) {
-            var reac = createReactionDom(reaction, msg.reactions[reaction]);
+            var reac = createReactionDom(channelId, msg.id, reaction, msg.reactions[reaction]);
             reac && reactions.appendChild(reac);
         }
         if (msg.raw["attachments"]) {

+ 24 - 0
cli/workflow.js

@@ -160,3 +160,27 @@ function sendMsg(chan, msg, replyTo) {
     xhr.send(null);
 }
 
+/**
+ * @param {string} channelId
+ * @param {string} msgId
+ * @param {string} reaction
+**/
+function addReaction(channelId, msgId, reaction) {
+    var xhr = new XMLHttpRequest();
+    var url = 'api/reaction?room=' +channelId +"&msg=" +msgId +"&reaction=" +encodeURIComponent(reaction);
+    xhr.open('POST', url, true);
+    xhr.send(null);
+}
+
+/**
+ * @param {string} channelId
+ * @param {string} msgId
+ * @param {string} reaction
+**/
+function removeReaction(channelId, msgId, reaction) {
+    var xhr = new XMLHttpRequest();
+    var url = 'api/reaction?room=' +channelId +"&msg=" +msgId +"&reaction=" +encodeURIComponent(reaction);
+    xhr.open('DELETE', url, true);
+    xhr.send(null);
+}
+

+ 12 - 11
srv/public/slack.min.js

@@ -1,19 +1,20 @@
 function h(a){this.id=a.id;this.name=a.name}function aa(a,b){this.id=a.id;this.name=a.name;this.b=parseFloat(a.last_read);this.a={};if(a.members)for(var d=0,c=a.members.length;d<c;d++){var f=l(b,a.members[d]);this.a[f.id]=f;f.f[this.id]=this}}function ba(a,b){var d=[];this.id=b.id;this.a={};for(var c=0,f=b.members.length;c<f;c++){var e=l(a,b.members[c]);this.a[b.members[c]]=e;e.f[this.id]=this;d.push(e.name)}this.name=d.join(", ");this.b=parseFloat(b.last_read)}
 function ca(a,b){this.id=b.id;this.c=a;this.b=parseFloat(b.last_read)}function da(a){this.id=a.id;this.name=a.name;this.status=a.status;this.b={I:a.profile.image_24,J:a.profile.image_32,l:a.profile.image_48,D:a.profile.image_72,H:a.profile.image_192,L:a.profile.image_512};this.f={};this.a=null}function ea(a){this.id=a.id;this.name=a.name;this.b={K:a.icons.image_36,l:a.icons.image_48,D:a.icons.image_72};this.f={};this.a=null}
-function t(){this.s=null;this.f={};this.b={};this.i={};this.a={};this.c=null;this.g={};this.o={}}function l(a,b){return a.a[b]||a.g[b]||null}function u(a,b){return a.f[b]||a.i[b]||a.b[b]||null}"undefined"!==typeof module&&(module.C.F=t);function fa(a,b){this.i=a.user;this.c=b||parseFloat(a.ts);this.type=a.type;this.g=a.subtype;this.b=a;this.a={};var d=this;a.reactions&&a.reactions.forEach(function(a){d.a[a.name]=[];a.users.forEach(function(b){d.a[a.name].push(b)})})}function v(a,b,d){this.id="string"===typeof a?a:a.id;this.a=[];this.b=b;d&&x(this,d)}function ga(a,b,d){a.a[b]&&(1===a.a[b].length?delete a.a[b]:a.a[b]=a.a[b].filter(function(a){return a!==d}))}
-function x(a,b){var d=0;b.forEach(function(a){d=Math.max(this.push(a),d)}.bind(a));ha(a)}v.prototype.push=function(a){for(var b=parseFloat(a.ts),d=0,c=this.a.length;d<c;d++)if(this.a[d].c===b)return b;for(this.a.push(new fa(a,b));this.a.length>this.b;)this.a.shift();if("reaction_added"===a.type){if(console.log(a),d=y(this,parseFloat(a.item.ts)))c=a.reaction,a=a.user,d.a[c]||(d.a[c]=[]),d.a[c].push(a)}else"reaction_removed"===a.type&&(d=y(this,parseFloat(a.item.ts)))&&ga(d,a.reaction,a.user);return b};
+function t(){this.s=null;this.f={};this.c={};this.i={};this.a={};this.b=null;this.g={};this.o={}}function l(a,b){return a.a[b]||a.g[b]||null}function u(a,b){return a.f[b]||a.i[b]||a.c[b]||null}"undefined"!==typeof module&&(module.C.F=t);function fa(a,b){this.i=a.user;this.id=a.ts;this.c=b||parseFloat(a.ts);this.type=a.type;this.g=a.subtype;this.b=a;this.a={};var d=this;a.reactions&&a.reactions.forEach(function(a){d.a[a.name]=[];a.users.forEach(function(b){d.a[a.name].push(b)})})}function v(a,b,d){this.id="string"===typeof a?a:a.id;this.a=[];this.b=b;d&&x(this,d)}function ga(a,b,d){a.a[b]&&(1===a.a[b].length?delete a.a[b]:a.a[b]=a.a[b].filter(function(a){return a!==d}))}
+function x(a,b){var d=0;b.forEach(function(a){d=Math.max(this.push(a),d)}.bind(a));ha(a)}v.prototype.push=function(a){for(var b=parseFloat(a.ts),d=0,c=this.a.length;d<c;d++)if(this.a[d].c===b)return b;for(this.a.push(new fa(a,b));this.a.length>this.b;)this.a.shift();if("reaction_added"===a.type){if(d=y(this,parseFloat(a.item.ts)))c=a.reaction,a=a.user,d.a[c]||(d.a[c]=[]),d.a[c].push(a)}else"reaction_removed"===a.type&&(d=y(this,parseFloat(a.item.ts)))&&ga(d,a.reaction,a.user);return b};
 function y(a,b){for(var d=0,c=a.a.length;d<c&&b>=a.a[d].c;d++)if(a.a[d].c===b)return a.a[d];return null}function ha(a){a.a.sort(function(a,d){return a.c-d.c})}"undefined"!==typeof module&&(module.C.G=v);var A={},D;function ia(){var a;if(!a){for(var b=0,d=navigator.languages.length;b<d;b++)if(A.hasOwnProperty(navigator.languages[b])){a=navigator.languages[b];break}a||(a="en")}D=A[a];console.log("Loading language pack: "+a);if(D.h)for(b in D.h)document.getElementById(b).textContent=D.h[b]};A.fr={B:"Utilisateur inconnu",A:"Channel inconnu",w:"Nouveau message",u:"Reseau",h:{fileUploadCancel:"Annuler",neterror:"Impossible de se connecter au chat !"}};A.en={B:"Unknown member",A:"Unknown channel",w:"New message",u:"Network",h:{fileUploadCancel:"Cancel",neterror:"Cannot connect to chat !"}};var E=null,F=0;
-function ja(){var a=document.createDocumentFragment(),b=H.a.c?Object.keys(H.a.c.f):[];b.sort(function(a,b){return a[0]!==b[0]?a[0]-b[0]:(H.a.f[a]||H.a.b[a]).name.localeCompare((H.a.f[b]||H.a.b[b]).name)});b.forEach(function(b){b=H.a.f[b]||H.a.b[b];var c=document.createElement("li"),d=document.createElement("a");c.id=b.id;d.href="#"+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");d.textContent=
+function ja(){var a=document.createDocumentFragment(),b=H.a.b?Object.keys(H.a.b.f):[];b.sort(function(a,b){return a[0]!==b[0]?a[0]-b[0]:(H.a.f[a]||H.a.c[a]).name.localeCompare((H.a.f[b]||H.a.c[b]).name)});b.forEach(function(b){b=H.a.f[b]||H.a.c[b];var c=document.createElement("li"),d=document.createElement("a");c.id=b.id;d.href="#"+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");d.textContent=
 b.name;c.appendChild(d);c&&a.appendChild(c)});b=H.a.a?Object.keys(H.a.a):[];b.sort(function(a,b){return H.a.a[a].name.localeCompare(H.a.a[b].name)});b.forEach(function(b){b=H.a.a[b].a;var c=document.createElement("li"),d=document.createElement("a");c.id=b.id;d.href="#"+b.id;c.className="slack-context-room";d.textContent=b.c.name;c.appendChild(d);c&&a.appendChild(c)});document.getElementById("chanList").textContent="";document.getElementById("chanList").appendChild(a);I();J()}
 function K(a){a?document.body.classList.remove("no-network"):document.body.classList.add("no-network");J()}function L(){if(E){document.body.classList.add("replyingTo");var a=document.getElementById("replyToContainer"),b=document.createElement("a");b.addEventListener("click",function(){E=null;L()});b.className="replyto-close";b.textContent="x";a.textContent="";a.appendChild(b);a.appendChild(M("reply_"+N.id,E,!0))}else document.body.classList.remove("replyingTo")}
-function ka(a,b){var d=O(a);if(d){for(var c=document.createElement("li"),f=document.createElement("a"),e=document.createElement("span"),k=document.createElement("span"),g=[],m=0,p=b.length;m<p;m++){var q=l(H.a,b[m]);q&&g.push(q.name)}g.sort();k.textContent=g.join(", ");e.appendChild(d);e.className="emoji-small";f.appendChild(e);f.appendChild(k);c.className="slackmsg-reaction-item";c.appendChild(f);return c}return null}
+function ka(a,b,d,c){var f=O(d);if(f){for(var e=document.createElement("li"),k=document.createElement("a"),g=document.createElement("span"),m=document.createElement("span"),p=[],q=0,n=c.length;q<n;q++){var r=l(H.a,c[q]);r&&p.push(r.name)}p.sort();m.textContent=p.join(", ");g.appendChild(f);g.className="emoji-small";k.href="javascript:toggleReaction('"+a+"', '"+b+"', '"+d+"')";k.appendChild(g);k.appendChild(m);e.className="slackmsg-reaction-item";e.appendChild(k);return e}return null}
+window.toggleReaction=function(a,b,d){var c=H.b[a];if(c){a:{for(var f=0,e=c.a.length;f<e;f++)if(c.a[f].id==b){c=c.a[f];break a}c=null}c&&(f=H.a.b.id,c.a[d]&&-1!==c.a[d].indexOf(f)?(c=new XMLHttpRequest,c.open("DELETE","api/reaction?room="+a+"&msg="+b+"&reaction="+encodeURIComponent(d),!0)):(c=new XMLHttpRequest,c.open("POST","api/reaction?room="+a+"&msg="+b+"&reaction="+encodeURIComponent(d),!0)),c.send(null))}};
 function P(a,b,d){var c=document.createElement("div"),f=document.createElement("div"),e=document.createElement("div"),k=document.createElement("div"),g=document.createElement("img"),m=document.createElement("span"),p=document.createElement("ul"),q=document.createElement("li"),n=document.createElement("ul"),r=document.createElement("ul"),w=b.b.user?H.a.a[b.b.user]:H.a.g[b.b.bot_id];c.id=a+"_"+b.c;c.className="slackmsg-item";f.className="slackmsg-ts";e.className="slackmsg-msg";k.className="slackmsg-author";
 g.className="slackmsg-author-img";m.className="slackmsg-author-name";p.className="slackmsg-hover";q.className="slackmsg-hover-reply";f.innerHTML=Q(b.c);e.innerHTML=R(b.b.text||"");m.textContent=w?w.name:b.b.username||"?";w||b.b.username||(e.textContent=b.b.subtype||JSON.stringify(b.b));g.src=w?w.b.l:"";k.appendChild(g);k.appendChild(m);p.appendChild(q);c.appendChild(k);c.appendChild(e);c.appendChild(f);c.appendChild(n);c.appendChild(r);n.className="slackmsg-attachments";r.className="slackmsg-reactions";
-if(!0!==d){if(b.a)for(var z in b.a)(a=ka(z,b.a[z]))&&r.appendChild(a);b.b.attachments&&b.b.attachments.forEach(function(a){var b=document.createElement("li"),c=document.createElement("div"),d=document.createElement("div"),e=document.createElement("a"),f=document.createElement("div"),g=document.createElement("img"),B=document.createElement("a"),k=document.createElement("div"),w=document.createElement("div"),m=document.createElement("img"),p=document.createElement("img"),z=document.createElement("div"),
-q=document.createElement("img"),r=document.createElement("span"),G=document.createElement("span");b.className="slackmsg-attachment";var C="#e3e4e6";a.color&&("#"===a.color[0]?C=a.color[0]:"good"===a.color?C="#2fa44f":"warning"===a.color?C="#de9e31":"danger"===a.color&&(C="#d50200"));c.style.borderColor=C;d.className="slackmsg-attachment-pretext";a.pretext?d.innerHTML=R(a.pretext):d.classList.add("hidden");e.target="_blank";a.title?(e.innerHTML=R(a.title),a.title_link&&(e.href=a.title_link),e.className=
+if(!0!==d){if(b.a)for(var z in b.a)(d=ka(a,b.id,z,b.a[z]))&&r.appendChild(d);b.b.attachments&&b.b.attachments.forEach(function(a){var b=document.createElement("li"),c=document.createElement("div"),d=document.createElement("div"),e=document.createElement("a"),f=document.createElement("div"),g=document.createElement("img"),B=document.createElement("a"),k=document.createElement("div"),w=document.createElement("div"),m=document.createElement("img"),p=document.createElement("img"),q=document.createElement("div"),
+r=document.createElement("img"),z=document.createElement("span"),G=document.createElement("span");b.className="slackmsg-attachment";var C="#e3e4e6";a.color&&("#"===a.color[0]?C=a.color[0]:"good"===a.color?C="#2fa44f":"warning"===a.color?C="#de9e31":"danger"===a.color&&(C="#d50200"));c.style.borderColor=C;d.className="slackmsg-attachment-pretext";a.pretext?d.innerHTML=R(a.pretext):d.classList.add("hidden");e.target="_blank";a.title?(e.innerHTML=R(a.title),a.title_link&&(e.href=a.title_link),e.className=
 "slackmsg-attachment-title"):e.className="hidden slackmsg-attachment-title";B.target="_blank";f.className="slackmsg-author";a.author_name?(B.innerHTML=R(a.author_name),B.href=a.author_link||"",B.className="slackmsg-author-name",g.className="slackmsg-author-img",a.author_icon?g.src=a.author_icon:g.classList.add("hidden")):f.classList.add("hidden");w.innerHTML=R(a.text||"");w.a="slackmsg-attachment-text";m.className="slackmsg-attachment-thumb";a.thumb_url?m.src=a.thumb_url:m.classList.add("hidden");
-p.className="slackmsg-attachment-img";a.image_url?p.src=a.image_url:p.classList.add("hidden");z.className="slackmsg-attachment-footer";r.className="slackmsg-attachment-footer-text";q.className="slackmsg-attachment-footer-icon";a.footer?(r.innerHTML=R(a.footer),a.footer_icon?q.src=a.footer_icon:q.classList.add("hidden")):(q.classList.add("hidden"),r.classList.add("hidden"));G.className="slackmsg-ts";a.ts?G.innerHTML=Q(a.ts):G.classList.add("hidden");f.appendChild(g);f.appendChild(B);k.appendChild(w);
-k.appendChild(m);z.appendChild(q);z.appendChild(r);z.appendChild(G);c.appendChild(e);c.appendChild(f);c.appendChild(k);c.appendChild(p);c.appendChild(z);b.appendChild(d);b.appendChild(c);b&&n.appendChild(b)})}c.appendChild(p);return c}function Q(a){"string"!==typeof a&&(a=parseFloat(a));return(new Date(1E3*a)).toLocaleTimeString()}
+p.className="slackmsg-attachment-img";a.image_url?p.src=a.image_url:p.classList.add("hidden");q.className="slackmsg-attachment-footer";z.className="slackmsg-attachment-footer-text";r.className="slackmsg-attachment-footer-icon";a.footer?(z.innerHTML=R(a.footer),a.footer_icon?r.src=a.footer_icon:r.classList.add("hidden")):(r.classList.add("hidden"),z.classList.add("hidden"));G.className="slackmsg-ts";a.ts?G.innerHTML=Q(a.ts):G.classList.add("hidden");f.appendChild(g);f.appendChild(B);k.appendChild(w);
+k.appendChild(m);q.appendChild(r);q.appendChild(z);q.appendChild(G);c.appendChild(e);c.appendChild(f);c.appendChild(k);c.appendChild(p);c.appendChild(q);b.appendChild(d);b.appendChild(c);b&&n.appendChild(b)})}c.appendChild(p);return c}function Q(a){"string"!==typeof a&&(a=parseFloat(a));return(new Date(1E3*a)).toLocaleTimeString()}
 function O(a){a:{for(var b=a,d={};!d[b];){if(a=H.a.o[b])if("alias:"==a.substr(0,6))d[b]=!0,b=a.substr(6);else{b=document.createElement("span");b.className="emoji-custom emoji";b.style.backgroundImage="url('"+a+"')";a=b;break a}break}a=b}"string"===typeof a&&"makeEmoji"in window&&(a=window.makeEmoji(a));return"string"===typeof a?null:a}
 function la(a){return a.replace(/:(\w+):/g,function(a,d){var b=O(d);if(b){var f=document.createElement("span");f.className="emoji-small";f.appendChild(b);return f.outerHTML}return a})}
 function R(a){a=a.split(/\r?\n/g);for(var b=0,d=a.length;b<d;b++){for(var c=a[b],f="",e={},k=!1,g=0,c=c.replace(RegExp("<([@#]?)([^>]*)>","g"),function(a,b,c){c=c.split("|");if("@"===b)c[1]?"@"!==c[1][0]&&(c[1]="@"+c[1]):(a=l(H.a,c[0]),c[1]=a?"@"+a.name:D.B),c[0]="#"+c[0],c[2]="slackmsg-link slackmsg-link-user";else if("#"===b)c[1]?"#"!==c[1][0]&&(c[1]="#"+c[1]):(a=u(H.a,c[0]),c[1]=a?"#"+a.name:D.A),c[0]="#"+c[0],c[2]="slackmsg-link slackmsg-link-chan";else if(-1!==c[0].indexOf("://"))c[1]||(c[1]=
@@ -28,9 +29,9 @@ function I(){var a=document.location.hash.substr(1),b=u(H.a,a),a=l(H.a,a);b&&b!=
 document.addEventListener("DOMContentLoaded",function(){ia();document.getElementById("chatWindow").addEventListener("click",na);window.addEventListener("hashchange",function(){document.location.hash&&"#"===document.location.hash[0]&&I()});document.getElementById("fileUploadCancel").addEventListener("click",function(a){a.preventDefault();document.getElementById("fileUploadError").classList.add("hidden");document.getElementById("fileUploadContainer").classList.add("hidden");document.getElementById("fileUploadInput").value=
 "";return!1});document.getElementById("fileUploadForm").addEventListener("submit",function(a){a.preventDefault();a=document.getElementById("fileUploadInput");var b=a.value;b&&(b=b.substr(b.lastIndexOf("\\")+1),oa(b,a.files[0],function(a){var b=document.getElementById("fileUploadError");a?(b.textContent=a,b.classList.remove("hidden")):(b.classList.add("hidden"),document.getElementById("fileUploadInput").value="",document.getElementById("fileUploadContainer").classList.add("hidden"))}));return!1});
 document.getElementById("attachFile").addEventListener("click",function(a){a.preventDefault();N&&document.getElementById("fileUploadContainer").classList.remove("hidden");return!1});document.getElementById("msgForm").addEventListener("submit",function(a){a.preventDefault();a=document.getElementById("msgInput");if(N&&a.value){var b=N,d=E,c=new XMLHttpRequest,f="api/msg?room="+b.id+"&text="+encodeURIComponent(a.value);if(d){var e=l(H.a,d.i),k="Message";"C"===b.id[0]?k="Channel message":"D"===b.id[0]?
-k="Direct message":"G"===b.id[0]&&(k="Group message");f+="&attachments="+encodeURIComponent(JSON.stringify([{fallback:d.b.text||"",author_name:"<@"+e.id+"|"+e.name+">",author_icon:e.b.l,text:d.b.text||"",footer:k,ts:d.c}]))}c.open("POST",f,!0);c.send(null);a.value="";E&&(E=null,L())}V();return!1});window.addEventListener("blur",function(){window.hasFocus=!1});window.addEventListener("focus",function(){window.hasFocus=!0;F=0;N&&X();V()});window.hasFocus=!0;Y()});var H,T={};function pa(a,b){if(a&&(a!==N||!window.hasFocus)){var d=new RegExp("<@"+H.a.c.id),c=!1,f=!1;T[a.id]||(T[a.id]={j:0,m:0});b.forEach(function(b){"message"===b.type&&b.text&&("D"===a.id[0]||b.text.match(d)?(f|=!T[a.id].j,T[a.id].j++,c=!0):T[a.id].m++)});J();document.getElementById(a.id).classList.add("unread");c&&document.getElementById(a.id).classList.add("unreadHi");f&&!window.hasFocus&&ma()}}
+k="Direct message":"G"===b.id[0]&&(k="Group message");f+="&attachments="+encodeURIComponent(JSON.stringify([{fallback:d.b.text||"",author_name:"<@"+e.id+"|"+e.name+">",author_icon:e.b.l,text:d.b.text||"",footer:k,ts:d.c}]))}c.open("POST",f,!0);c.send(null);a.value="";E&&(E=null,L())}V();return!1});window.addEventListener("blur",function(){window.hasFocus=!1});window.addEventListener("focus",function(){window.hasFocus=!0;F=0;N&&X();V()});window.hasFocus=!0;Y()});var H,T={};function pa(a,b){if(a&&(a!==N||!window.hasFocus)){var d=new RegExp("<@"+H.a.b.id),c=!1,f=!1;T[a.id]||(T[a.id]={j:0,m:0});b.forEach(function(b){"message"===b.type&&b.text&&("D"===a.id[0]||b.text.match(d)?(f|=!T[a.id].j,T[a.id].j++,c=!0):T[a.id].m++)});J();document.getElementById(a.id).classList.add("unread");c&&document.getElementById(a.id).classList.add("unreadHi");f&&!window.hasFocus&&ma()}}
 function X(){var a=N;T[a.id]&&(T[a.id]={j:0,m:0},J());a=document.getElementById(a.id);a.classList.remove("unread");a.classList.remove("unreadHi")}H=new function(){this.c=0;this.a=new t;this.b={}};var S=0,N=null;function Z(a){var b=new XMLHttpRequest;b.timeout=6E4;b.onreadystatechange=function(){if(4===b.readyState)if(b.status){var d=null,c=2===Math.floor(b.status/100);if(c){S&&(S=0,K(!0));d=b.response;try{d=JSON.parse(d)}catch(f){d=null}}else S?(S+=Math.floor((S||5)/2),S=Math.min(60,S)):(S=5,K(!1));a(c,d)}else S&&(S=0,K(!0)),Z(a)};b.open("GET","api?v="+H.c,!0);b.send(null)}
-function qa(a,b){if(a){if(b){var d=H;b.v&&(d.c=b.v);if(b["static"]){for(var c=d.a,f=b["static"],e=0,k=f.bots.length;e<k;e++)c.g[f.bots[e].id]=new ea(f.bots[e]);e=0;for(k=f.users.length;e<k;e++)c.a[f.users[e].id]=new da(f.users[e]);e=0;for(k=f.ims.length;e<k;e++){var g=l(c,f.ims[e].user);g&&(g.a=new ca(g,f.ims[e]),c.i[g.a.id]=g.a)}e=0;for(k=f.channels.length;e<k;e++)c.f[f.channels[e].id]=new aa(f.channels[e],c);e=0;for(k=f.groups.length;e<k;e++)c.b[f.groups[e].id]=new ba(c,f.groups[e]);c.o=f.emojis;
-c.s=new h(f.team);c.c=l(c,f.self.id);ja()}if(b.live){for(var m in b.live)(c=d.b[m])?x(c,b.live[m]):d.b[m]=new v(m,250,b.live[m]);for(var p in b.live)pa(u(d.a,p),b.live[p]),N&&b.live[N.id]&&U()}}Y()}else setTimeout(Y,1E3*S)}function Y(){Z(qa)}
+function qa(a,b){if(a){if(b){var d=H;b.v&&(d.c=b.v);if(b["static"]){for(var c=d.a,f=b["static"],e=0,k=f.bots.length;e<k;e++)c.g[f.bots[e].id]=new ea(f.bots[e]);e=0;for(k=f.users.length;e<k;e++)c.a[f.users[e].id]=new da(f.users[e]);e=0;for(k=f.ims.length;e<k;e++){var g=l(c,f.ims[e].user);g&&(g.a=new ca(g,f.ims[e]),c.i[g.a.id]=g.a)}e=0;for(k=f.channels.length;e<k;e++)c.f[f.channels[e].id]=new aa(f.channels[e],c);e=0;for(k=f.groups.length;e<k;e++)c.c[f.groups[e].id]=new ba(c,f.groups[e]);c.o=f.emojis;
+c.s=new h(f.team);c.b=l(c,f.self.id);ja()}if(b.live){for(var m in b.live)(c=d.b[m])?x(c,b.live[m]):d.b[m]=new v(m,250,b.live[m]);for(var p in b.live)pa(u(d.a,p),b.live[p]),N&&b.live[N.id]&&U()}}Y()}else setTimeout(Y,1E3*S)}function Y(){Z(qa)}
 function W(a){N&&document.getElementById(N.id).classList.remove("selected");document.getElementById(a.id).classList.add("selected");document.body.classList.remove("no-room-selected");N=a;a=N.name||(N.c?N.c.name:void 0);if(!a){a=[];for(var b in N.a)a.push(N.a[b].name);a=a.join(", ")}document.getElementById("currentRoomTitle").textContent=a;U();V();document.getElementById("fileUploadContainer").classList.add("hidden");X();E&&(E=null,L());N.b&&!H.b[N.id]&&(b=new XMLHttpRequest,b.open("GET","api/hist?room="+
 N.id,!0),b.send(null))}function oa(a,b,d){var c=N;new FileReader;var f=new FormData,e=new XMLHttpRequest;f.append("file",b);f.append("filename",a);e.onreadystatechange=function(){4===e.readyState&&(204===e.status?d(null):d(e.statusText))};e.open("POST","api/file?room="+c.id);e.send(f)};

+ 23 - 0
srv/src/httpServ.js

@@ -161,6 +161,29 @@ Server.prototype.onRequest = function(req, res) {
             }
             sessionManager.saveSession(req.session);
             res.end();
+        } else if (req.urlObj.match(["api", "reaction"])) {
+            var chanId = req.urlObj.queryTokens["room"] ? req.urlObj.queryTokens["room"][0] : undefined
+                ,msgId = req.urlObj.queryTokens["msg"] ? req.urlObj.queryTokens["msg"][0] : undefined
+                ,reaction = req.urlObj.queryTokens["reaction"] ? req.urlObj.queryTokens["reaction"][0] : undefined;
+
+            if (chanId && msgId && reaction) {
+                var chan = res.slack.data.getChannel(chanId);
+                if (!chan) {
+                    res.writeHeader("404", "Channel Not Found");
+                } else if (req.method === 'POST') {
+                    res.writeHeader("204", "No Content");
+                    res.slack.addReaction(chan, msgId, reaction);
+                } else if (req.method === 'DELETE') {
+                    res.writeHeader("204", "No Content");
+                    res.slack.removeReaction(chan, msgId, reaction);
+                } else {
+                    res.writeHeader("405", "Method not allowed");
+                }
+            } else {
+                res.writeHeader("400", "Missing Parameter");
+            }
+            sessionManager.saveSession(req.session);
+            res.end();
         } else if (req.urlObj.match(["api", "file"])) {
             sessionManager.saveSession(req.session);
             if (req.urlObj.queryTokens["room"]) {

+ 28 - 0
srv/src/slack.js

@@ -21,6 +21,8 @@ const SLACK_ENDPOINT = "https://slack.com/api/"
         ,postMsg: "chat.postMessage"
         ,postFile: "files.upload"
         ,emojiList: "emoji.list"
+        ,addReaction: "reactions.add"
+        ,removeReaction: "reactions.remove"
     }
     ,HISTORY_LENGTH = 35
 
@@ -279,6 +281,32 @@ Slack.prototype.openUploadFileStream = function(channel, contentType, callback)
     return req;
 };
 
+/**
+ * @param {SlackChan|SlackGroup|SlackIms} channel
+ * @param {string} msgId
+ * @param {string} reaction
+**/
+Slack.prototype.addReaction = function(channel, msgId, reaction) {
+    httpsRequest(SLACK_ENDPOINT +GETAPI.addReaction
+        +"?token=" +this.token
+        +"&name=" +reaction
+        +"&channel="+channel.id
+        +"&timestamp="+msgId);
+}
+
+/**
+ * @param {SlackChan|SlackGroup|SlackIms} channel
+ * @param {string} msgId
+ * @param {string} reaction
+**/
+Slack.prototype.removeReaction = function(channel, msgId, reaction) {
+    httpsRequest(SLACK_ENDPOINT +GETAPI.removeReaction
+        +"?token=" +this.token
+        +"&name=" +reaction
+        +"&channel="+channel.id
+        +"&timestamp="+msgId);
+}
+
 /**
  * @param {SlackChan|SlackGroup|SlackIms} channel
  * @param {Array.<string>} text

+ 21 - 0
srv/src/slackHistory.js

@@ -8,6 +8,9 @@ function SlackMessage(e, ts) {
     /** @type {string} **/
     this.userId = e["user"];
 
+    /** @type {string} **/
+    this.id = e["ts"];
+
     /** @type {number} **/
     this.ts = ts || parseFloat(e["ts"]);
 
@@ -92,6 +95,12 @@ SlackMessage.prototype.removeReaction = function(reaction, userId) {
     }
 }
 
+SlackMessage.prototype.hasReactionForUser = function(reaction, userId) {
+    if (this.reactions[reaction])
+        return this.reactions[reaction].indexOf(userId) !== -1;
+    return false;
+}
+
 /**
  * @param {Array} evts
  * @return {number} biggest ts
@@ -162,6 +171,18 @@ SlackHistory.prototype.getMessage = function(ts) {
     return null;
 }
 
+/**
+ * @param {string} id
+ * @return {SlackMessage|null}
+**/
+SlackHistory.prototype.getMessageById = function(id) {
+    for (var i =0, nbMessages = this.messages.length; i < nbMessages; i++) {
+        if (this.messages[i].id == id)
+            return this.messages[i];
+    }
+    return null;
+}
+
 /**
  * @return {Array.<*>}
 **/