diff options
Diffstat (limited to 'webclient/client.html')
-rw-r--r-- | webclient/client.html | 123 |
1 files changed, 108 insertions, 15 deletions
diff --git a/webclient/client.html b/webclient/client.html index 28a91fd..f88ee07 100644 --- a/webclient/client.html +++ b/webclient/client.html @@ -4,12 +4,16 @@ <meta charset="utf-8"> <title>tomsg webclient</title> <script> -var PROTOCOL_VERSION=1; +var PROTOCOL_VERSION=2; + +// Note: message id's are considered strings in this code. This is to not have to deal with full 64-bit integers. var sock=null,negotiated_version=false,username=null; +var messagecache=new Map(); // msgid => [user,message] var roomlist=[":console"]; var currentroom=":console"; -var roomlogs=new Map([[":console",[]]]); +var currentreply=null; // [msgid, table row] or null +var roomlogs=new Map([[":console",[]]]); // see drawRoomEntry for entry types var commandlist=[ {cmd:"register",usage:"/register <username> <password>"}, @@ -93,11 +97,17 @@ function net_historyCollectionCallback(id,list,count,cb, item,err){ } } -function reconnect(){ +function resetState() { if(sock)sock.close(); - - net_callbacks={}; negotiated_version=false; + username=null; + messagecache.clear(); + net_callbacks={}; + cancelReply(); +} + +function reconnect(){ + resetState(); var url; if(location.hostname!="")url="wss://"+location.hostname; @@ -112,8 +122,10 @@ function reconnect(){ if(id=="_push"){ if(type=="message"){ var r=spl.word[2],u=spl.word[3],t=new Date(+spl.word[4]/1000); - // Ignore msgid at word[5] - addRoomEntry(r,"message",[u,t,spl.rest[6]]); + var msgid=spl.word[5],replyid=spl.word[6]; + var text=spl.rest[7]; + messagecache.set(msgid,[u,text]); + addRoomEntry(r,"message",[u,t,msgid,replyid,text]); } else if(type=="invite"){ var r=spl.word[2],inviter=spl.word[3]; roomlist.push(r); @@ -137,13 +149,14 @@ function reconnect(){ var obj; if(type=="ok")fn(true); else if(type=="error")fn(null,spl.rest[2]); - else if(type=="number")fn(+spl.rest[2]); + else if(type=="number")fn(spl.rest[2]); // Note: no int parse! else if(type=="name")fn(spl.rest[2]); else if(type=="list")fn(spl.word.slice(3)); else if(type=="history"){ var count=+spl.word[2]; net_callbacks[id]=net_historyCollectionCallback.bind(this,id,[],count,fn); - } else if(type=="history_message")fn([spl.word[4],+spl.word[5]/1000,spl.rest[7]]); + } else if(type=="history_message")fn([spl.word[4],+spl.word[5]/1000,spl.word[6],spl.word[7],spl.rest[8]]); + else if(type=="message")fn([spl.word[3],+spl.word[4]/1000,spl.word[5],spl.word[6],spl.rest[7]]); else alert("Unknown server response message type '"+type+"'!"); } else { alert("No callback for server message id '"+id+"'!"); @@ -206,6 +219,18 @@ function fetchRoomHistory(roomid){ }); } +function getMessage(msgid,callback){ + if(messagecache.has(msgid)){ + callback(null,messagecache.get(msgid)); + return; + } + + net_send("get_message "+msgid,function(item,err){ + if(err)callback(err,null); + else callback(null,item); + }); +} + function updateStatus(){ var str=null; if(!sock||sock.readyState>=2){ @@ -284,21 +309,44 @@ function drawRoomEntry(type,args){ tr.appendChild(td2); tbody.appendChild(tr); switch(type){ - case "message": + case "message": // [username, timestamp_ms, msgid, replyid, text] tr.classList.add("message"); node0.nodeValue=formatTime(new Date(args[1])); node1.nodeValue="<"+args[0]+">"; - node2.nodeValue=args[2]; + node2.nodeValue=args[4]; + if(args[3]!="-1"){ + var replyNode=document.createElement("span"); + replyNode.classList.add("reply"); + replyNode.classList.add("pending"); + getMessage(args[3],function(err,item){ + replyNode.classList.remove("pending"); + var text; + if(err){ + replyNode.classList.add("failed"); + text="Failed to fetch original message!"; + } else { + text="> <"+item[0]+"> "+item[4]; + } + replyNode.appendChild(document.createTextNode(text)); + }); + td2.insertBefore(replyNode,node2); + td2.insertBefore(document.createElement("br"),node2); + } + + tr.addEventListener("click",function(){ + if(currentreply&¤treply[0]==args[2])cancelReply(); + else startReply(args[2],tr); + }); break; - case "error": + case "error": // [timestamp_ms, text] tr.classList.add("error"); node0.nodeValue=formatTime(new Date(args[0])); node1.nodeValue="--"; node2.nodeValue=args[1]; break; - case "notice": + case "notice": // [timestamp_ms, text] tr.classList.add("notice"); node0.nodeValue=formatTime(new Date(args[0])); node1.nodeValue="--"; @@ -323,6 +371,21 @@ function addRoomEntry(roomid,type,args){ } } +function cancelReply(){ + if(!currentreply)return; + currentreply[1].classList.remove("replying"); + document.getElementById("roominput_td").classList.remove("replying"); + currentreply=null; +} + +function startReply(msgid,tr){ + cancelReply(); + currentreply=[msgid,tr]; + tr.classList.add("replying"); + document.getElementById("roominput_td").classList.add("replying"); + document.getElementById("roominput").focus(); +} + function showUsage(roomid,cmd){ for(var i=0;i<commandlist.length;i++){ if(commandlist[i].cmd==cmd){ @@ -423,10 +486,15 @@ function sendMessage(roomid,text){ addRoomEntry(roomid,"error",[now(),"Cannot send a message here"]); return; } + var sentAs=username,msg=text.replace(/\n/g,""); - net_send("send "+roomid+" "+msg,function(msgid,err){ + var replyid=currentreply?currentreply[0]:"-1"; + cancelReply(); + + net_send("send "+roomid+" "+replyid+" "+msg,function(msgid,err){ if(msgid!=null){ - addRoomEntry(roomid,"message",[sentAs,new Date().getTime(),msg]); + msgid=msgid.toString(); + addRoomEntry(roomid,"message",[sentAs,new Date().getTime(),msgid,replyid,msg]); return; } addRoomEntry(roomid,"error",[now(),"Unable to send message: "+err]); @@ -478,6 +546,10 @@ table{ height:100%; } +.invisible{ + display:none; +} + /* SIDEBAR */ #sidebar{ width:150px; @@ -557,6 +629,13 @@ table{ } #roomlog > tr.message{ color:inherit; + cursor:pointer; +} +#roomlog > tr.message:hover{ + background-color:#202030; +} +#roomlog > tr.replying{ + background-color:#090 !important; } #roomlog > tr.error{ color:#f44; @@ -564,6 +643,17 @@ table{ #roomlog > tr.notice{ color:#a8f; } +#roomlog > tr > td > span.reply{ + color:#8f8; + font-style:italic; +} +#roomlog > tr > td > span.reply.pending:after{ + content:"(...)"; +} +#roomlog > tr > td > span.reply.failed{ + color:#f22; + font-style:normal; +} #roombar_tr{ height:30px; @@ -574,6 +664,9 @@ table{ vertical-align:top; padding:0px 1px 0px 10px; } +#roominput_td.replying{ + background-color:#090; +} #roominput{ position:absolute; background-color:rgba(0,0,0,0); |