Fossil SCM

/chat: experimentally render a list of users ordered by most recent activity. Until/unless we can find a useful function for the list, though, it's really just a somewhat pretty screen space hog.

stephan 2021-06-14 13:48 chat-user-last-seen
Commit c7ee6f4ef19a3b1ef94ded459b98bd94c91bf249f0d3b5d1bfd71f808ad588e4
2 files changed +47 -18 +18
+47 -18
--- src/chat.js
+++ src/chat.js
@@ -109,11 +109,12 @@
109109
btnSubmit: E1('#chat-message-submit'),
110110
inputSingle: E1('#chat-input-single'),
111111
inputMulti: E1('#chat-input-multi'),
112112
inputCurrent: undefined/*one of inputSingle or inputMulti*/,
113113
inputFile: E1('#chat-input-file'),
114
- contentDiv: E1('div.content')
114
+ contentDiv: E1('div.content'),
115
+ activeUserList: undefined/*active user list (dynamically created later on)*/
115116
},
116117
me: F.user.name,
117118
mxMsg: F.config.chat.initSize ? -F.config.chat.initSize : -50,
118119
mnMsg: undefined/*lowest message ID we've seen so far (for history loading)*/,
119120
pageIsActive: 'visible'===document.visibilityState,
@@ -394,10 +395,49 @@
394395
setNewMessageSound: function f(uri){
395396
delete this.playNewMessageSound.audio;
396397
this.playNewMessageSound.uri = uri;
397398
this.settings.set('audible-alert', !!uri);
398399
return this;
400
+ },
401
+
402
+ /**
403
+ Updates the "active user list" view.
404
+ */
405
+ updateActiveUserList: function callee(){
406
+ if(!this.e.activeUserList){
407
+ /** Array.sort() callback. Expects an array of user names and
408
+ sorts them in last-received message order (newest first). */
409
+ const usersLastSeen = this.usersLastSeen;
410
+ callee.sortUsersSeen = function(l,r){
411
+ l = usersLastSeen[l];
412
+ r = usersLastSeen[r];
413
+ if(l && r) return r - l;
414
+ else if(l) return -1;
415
+ else if(r) return 1;
416
+ else return 0;
417
+ };
418
+ const content = document.querySelector('body > div.content');
419
+ const ael = this.e.activeUserList =
420
+ D.attr(D.div(),'id','active-user-list');
421
+ D.append(ael, "user list placeholder");
422
+ content.insertBefore(ael, content.firstElementChild)
423
+ /*remember: layout is reversed!*/;
424
+ }
425
+ const self = this,
426
+ users = Object.keys(this.usersLastSeen).sort(callee.sortUsersSeen);
427
+ if(!users.length) return this;
428
+ const ael = this.e.activeUserList;
429
+ D.clearElement(ael);
430
+ users.forEach(function(u){
431
+ const uSpan = D.addClass(D.span(), 'chat-user');
432
+ const uDate = self.usersLastSeen[u];
433
+ D.append(uSpan, u);
434
+ if(uDate.$uColor){
435
+ uSpan.style.backgroundColor = uDate.$uColor;
436
+ }
437
+ D.append(ael, uSpan);
438
+ });
399439
}
400440
};
401441
F.fetch.beforesend = ()=>cs.ajaxStart();
402442
F.fetch.aftersend = ()=>cs.ajaxEnd();
403443
cs.e.inputCurrent = cs.e.inputSingle;
@@ -1086,20 +1126,10 @@
10861126
the response from /chat-poll. If atEnd is true, the message is
10871127
appended to the end of the chat list (for loading older
10881128
messages), else the beginning (the default). */
10891129
const newcontent = function f(jx,atEnd){
10901130
if(!f.processPost){
1091
- /** Array.sort() callback. Expects an array of user names and
1092
- sorts them in last-received message order (newest first). */
1093
- f.sortUsersSeen = function(l,r){
1094
- l = Chat.usersLastSeen[l];
1095
- r = Chat.usersLastSeen[r];
1096
- if(l && r) return r - l;
1097
- else if(l) return -1;
1098
- else if(r) return 1;
1099
- else return 0;
1100
- };
11011131
/** Processes chat message m, placing it either at the start (if
11021132
atEnd is falsy) or end (if atEnd is truthy) of the chat
11031133
history. atEnd should only be true when loading older
11041134
messages. */
11051135
f.processPost = function(m,atEnd){
@@ -1107,11 +1137,14 @@
11071137
if( m.msgid>Chat.mxMsg ) Chat.mxMsg = m.msgid;
11081138
if( !Chat.mnMsg || m.msgid<Chat.mnMsg) Chat.mnMsg = m.msgid;
11091139
if(m.xfrom && m.mtime){
11101140
const d = new Date(m.mtime);
11111141
const uls = Chat.usersLastSeen[m.xfrom];
1112
- if(!uls || uls<d) Chat.usersLastSeen[m.xfrom] = d;
1142
+ if(!uls || uls<d){
1143
+ d.$uColor = m.uclr;
1144
+ Chat.usersLastSeen[m.xfrom] = d;
1145
+ }
11131146
}
11141147
if( m.mdel ){
11151148
/* A record deletion notice. */
11161149
Chat.deleteMessageElem(m.mdel);
11171150
return;
@@ -1121,16 +1154,12 @@
11211154
}
11221155
const row = new Chat.MessageWidget(m);
11231156
Chat.injectMessageElem(row.e.body,atEnd);
11241157
if(m.isError){
11251158
Chat._gotServerError = m;
1126
- }else if(false){
1127
- const users = Object.keys(Chat.usersLastSeen).sort(f.sortUsersSeen);
1128
- console.debug("Users sorted by most recent activity (newest first):", users);
1129
- users.forEach(function(u){
1130
- console.debug(u, Chat.usersLastSeen[u].toISOString());
1131
- });
1159
+ }else{
1160
+ Chat.updateActiveUserList();
11321161
}
11331162
}/*processPost()*/;
11341163
}/*end static init*/
11351164
jx.msgs.forEach((m)=>f.processPost(m,atEnd));
11361165
if('visible'===document.visibilityState){
11371166
--- src/chat.js
+++ src/chat.js
@@ -109,11 +109,12 @@
109 btnSubmit: E1('#chat-message-submit'),
110 inputSingle: E1('#chat-input-single'),
111 inputMulti: E1('#chat-input-multi'),
112 inputCurrent: undefined/*one of inputSingle or inputMulti*/,
113 inputFile: E1('#chat-input-file'),
114 contentDiv: E1('div.content')
 
115 },
116 me: F.user.name,
117 mxMsg: F.config.chat.initSize ? -F.config.chat.initSize : -50,
118 mnMsg: undefined/*lowest message ID we've seen so far (for history loading)*/,
119 pageIsActive: 'visible'===document.visibilityState,
@@ -394,10 +395,49 @@
394 setNewMessageSound: function f(uri){
395 delete this.playNewMessageSound.audio;
396 this.playNewMessageSound.uri = uri;
397 this.settings.set('audible-alert', !!uri);
398 return this;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399 }
400 };
401 F.fetch.beforesend = ()=>cs.ajaxStart();
402 F.fetch.aftersend = ()=>cs.ajaxEnd();
403 cs.e.inputCurrent = cs.e.inputSingle;
@@ -1086,20 +1126,10 @@
1086 the response from /chat-poll. If atEnd is true, the message is
1087 appended to the end of the chat list (for loading older
1088 messages), else the beginning (the default). */
1089 const newcontent = function f(jx,atEnd){
1090 if(!f.processPost){
1091 /** Array.sort() callback. Expects an array of user names and
1092 sorts them in last-received message order (newest first). */
1093 f.sortUsersSeen = function(l,r){
1094 l = Chat.usersLastSeen[l];
1095 r = Chat.usersLastSeen[r];
1096 if(l && r) return r - l;
1097 else if(l) return -1;
1098 else if(r) return 1;
1099 else return 0;
1100 };
1101 /** Processes chat message m, placing it either at the start (if
1102 atEnd is falsy) or end (if atEnd is truthy) of the chat
1103 history. atEnd should only be true when loading older
1104 messages. */
1105 f.processPost = function(m,atEnd){
@@ -1107,11 +1137,14 @@
1107 if( m.msgid>Chat.mxMsg ) Chat.mxMsg = m.msgid;
1108 if( !Chat.mnMsg || m.msgid<Chat.mnMsg) Chat.mnMsg = m.msgid;
1109 if(m.xfrom && m.mtime){
1110 const d = new Date(m.mtime);
1111 const uls = Chat.usersLastSeen[m.xfrom];
1112 if(!uls || uls<d) Chat.usersLastSeen[m.xfrom] = d;
 
 
 
1113 }
1114 if( m.mdel ){
1115 /* A record deletion notice. */
1116 Chat.deleteMessageElem(m.mdel);
1117 return;
@@ -1121,16 +1154,12 @@
1121 }
1122 const row = new Chat.MessageWidget(m);
1123 Chat.injectMessageElem(row.e.body,atEnd);
1124 if(m.isError){
1125 Chat._gotServerError = m;
1126 }else if(false){
1127 const users = Object.keys(Chat.usersLastSeen).sort(f.sortUsersSeen);
1128 console.debug("Users sorted by most recent activity (newest first):", users);
1129 users.forEach(function(u){
1130 console.debug(u, Chat.usersLastSeen[u].toISOString());
1131 });
1132 }
1133 }/*processPost()*/;
1134 }/*end static init*/
1135 jx.msgs.forEach((m)=>f.processPost(m,atEnd));
1136 if('visible'===document.visibilityState){
1137
--- src/chat.js
+++ src/chat.js
@@ -109,11 +109,12 @@
109 btnSubmit: E1('#chat-message-submit'),
110 inputSingle: E1('#chat-input-single'),
111 inputMulti: E1('#chat-input-multi'),
112 inputCurrent: undefined/*one of inputSingle or inputMulti*/,
113 inputFile: E1('#chat-input-file'),
114 contentDiv: E1('div.content'),
115 activeUserList: undefined/*active user list (dynamically created later on)*/
116 },
117 me: F.user.name,
118 mxMsg: F.config.chat.initSize ? -F.config.chat.initSize : -50,
119 mnMsg: undefined/*lowest message ID we've seen so far (for history loading)*/,
120 pageIsActive: 'visible'===document.visibilityState,
@@ -394,10 +395,49 @@
395 setNewMessageSound: function f(uri){
396 delete this.playNewMessageSound.audio;
397 this.playNewMessageSound.uri = uri;
398 this.settings.set('audible-alert', !!uri);
399 return this;
400 },
401
402 /**
403 Updates the "active user list" view.
404 */
405 updateActiveUserList: function callee(){
406 if(!this.e.activeUserList){
407 /** Array.sort() callback. Expects an array of user names and
408 sorts them in last-received message order (newest first). */
409 const usersLastSeen = this.usersLastSeen;
410 callee.sortUsersSeen = function(l,r){
411 l = usersLastSeen[l];
412 r = usersLastSeen[r];
413 if(l && r) return r - l;
414 else if(l) return -1;
415 else if(r) return 1;
416 else return 0;
417 };
418 const content = document.querySelector('body > div.content');
419 const ael = this.e.activeUserList =
420 D.attr(D.div(),'id','active-user-list');
421 D.append(ael, "user list placeholder");
422 content.insertBefore(ael, content.firstElementChild)
423 /*remember: layout is reversed!*/;
424 }
425 const self = this,
426 users = Object.keys(this.usersLastSeen).sort(callee.sortUsersSeen);
427 if(!users.length) return this;
428 const ael = this.e.activeUserList;
429 D.clearElement(ael);
430 users.forEach(function(u){
431 const uSpan = D.addClass(D.span(), 'chat-user');
432 const uDate = self.usersLastSeen[u];
433 D.append(uSpan, u);
434 if(uDate.$uColor){
435 uSpan.style.backgroundColor = uDate.$uColor;
436 }
437 D.append(ael, uSpan);
438 });
439 }
440 };
441 F.fetch.beforesend = ()=>cs.ajaxStart();
442 F.fetch.aftersend = ()=>cs.ajaxEnd();
443 cs.e.inputCurrent = cs.e.inputSingle;
@@ -1086,20 +1126,10 @@
1126 the response from /chat-poll. If atEnd is true, the message is
1127 appended to the end of the chat list (for loading older
1128 messages), else the beginning (the default). */
1129 const newcontent = function f(jx,atEnd){
1130 if(!f.processPost){
 
 
 
 
 
 
 
 
 
 
1131 /** Processes chat message m, placing it either at the start (if
1132 atEnd is falsy) or end (if atEnd is truthy) of the chat
1133 history. atEnd should only be true when loading older
1134 messages. */
1135 f.processPost = function(m,atEnd){
@@ -1107,11 +1137,14 @@
1137 if( m.msgid>Chat.mxMsg ) Chat.mxMsg = m.msgid;
1138 if( !Chat.mnMsg || m.msgid<Chat.mnMsg) Chat.mnMsg = m.msgid;
1139 if(m.xfrom && m.mtime){
1140 const d = new Date(m.mtime);
1141 const uls = Chat.usersLastSeen[m.xfrom];
1142 if(!uls || uls<d){
1143 d.$uColor = m.uclr;
1144 Chat.usersLastSeen[m.xfrom] = d;
1145 }
1146 }
1147 if( m.mdel ){
1148 /* A record deletion notice. */
1149 Chat.deleteMessageElem(m.mdel);
1150 return;
@@ -1121,16 +1154,12 @@
1154 }
1155 const row = new Chat.MessageWidget(m);
1156 Chat.injectMessageElem(row.e.body,atEnd);
1157 if(m.isError){
1158 Chat._gotServerError = m;
1159 }else{
1160 Chat.updateActiveUserList();
 
 
 
 
1161 }
1162 }/*processPost()*/;
1163 }/*end static init*/
1164 jx.msgs.forEach((m)=>f.processPost(m,atEnd));
1165 if('visible'===document.visibilityState){
1166
--- src/default.css
+++ src/default.css
@@ -1767,10 +1767,28 @@
17671767
}
17681768
17691769
body.chat #chat-drop-details img {
17701770
max-width: 45%;
17711771
max-height: 45%;
1772
+}
1773
+body.chat #active-user-list {
1774
+ border: 1px inset;
1775
+ padding: 0.1em 0.2em;
1776
+ border-radius: 0.25em;
1777
+ display: flex;
1778
+ flex-direction: row;
1779
+ flex-wrap: wrap;
1780
+ align-items: center;
1781
+ font-size: 80%;
1782
+}
1783
+body.chat #active-user-list::before {
1784
+ content: "Most recently active:";
1785
+}
1786
+body.chat #active-user-list span.chat-user {
1787
+ margin: 0.2em;
1788
+ padding: 0.15em;
1789
+ border-radius: 0.25em;
17721790
}
17731791
17741792
input[type="checkbox"].diff-toggle {
17751793
float: right;
17761794
}
17771795
--- src/default.css
+++ src/default.css
@@ -1767,10 +1767,28 @@
1767 }
1768
1769 body.chat #chat-drop-details img {
1770 max-width: 45%;
1771 max-height: 45%;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1772 }
1773
1774 input[type="checkbox"].diff-toggle {
1775 float: right;
1776 }
1777
--- src/default.css
+++ src/default.css
@@ -1767,10 +1767,28 @@
1767 }
1768
1769 body.chat #chat-drop-details img {
1770 max-width: 45%;
1771 max-height: 45%;
1772 }
1773 body.chat #active-user-list {
1774 border: 1px inset;
1775 padding: 0.1em 0.2em;
1776 border-radius: 0.25em;
1777 display: flex;
1778 flex-direction: row;
1779 flex-wrap: wrap;
1780 align-items: center;
1781 font-size: 80%;
1782 }
1783 body.chat #active-user-list::before {
1784 content: "Most recently active:";
1785 }
1786 body.chat #active-user-list span.chat-user {
1787 margin: 0.2em;
1788 padding: 0.15em;
1789 border-radius: 0.25em;
1790 }
1791
1792 input[type="checkbox"].diff-toggle {
1793 float: right;
1794 }
1795

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button