aboutsummaryrefslogtreecommitdiff
path: root/webclient/client.html
diff options
context:
space:
mode:
Diffstat (limited to 'webclient/client.html')
-rw-r--r--webclient/client.html123
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&&currentreply[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);