Fossil SCM

/chat experiment, per chat discussion: when a given user posts multiple messages in a row, indent the 2nd and subsequent messages.

stephan 2021-04-07 14:46 trunk
Commit 609bcd32c85fd10f5492a515ff7835c7f67c6d96ab6426983656e84660302da2
2 files changed +43 -4 +17
+43 -4
--- src/chat.js
+++ src/chat.js
@@ -226,17 +226,54 @@
226226
}else{
227227
eMsg.scrollIntoView(false);
228228
}
229229
return this;
230230
},
231
- /* Injects element e as a new row in the chat, at the top of the
232
- list if atEnd is falsy, else at the end of the list, before
233
- the load-history widget. */
231
+ /**
232
+ Recalculates the ".subsequent" CSS class tag for all
233
+ messages. When a user posts multiple messages in a row, the
234
+ 2nd and subsequent ones get the "subsequent" tag added to
235
+ them for additional styling. We cannot do this easily as
236
+ messages arrive in batch mode because they can arrive out of
237
+ order (they arrive, and are processed, in reverse order for
238
+ the "load older messages" buttons). We do this handling in
239
+ injectMessageElem() for "interactive" use but have to
240
+ recalculate them en mass after receiving a batch of messages
241
+ right after this app loads or via the "load older messages"
242
+ buttons.
243
+ */
244
+ recalcMessageIndents: function(){
245
+ var prevXFrom;
246
+ this.e.messagesWrapper.querySelectorAll('.message-widget').forEach(function(e,ndx){
247
+ if(e.classList.contains('notification')){
248
+ // Obligatory special case.
249
+ prevXFrom = undefined;
250
+ D.removeClass(e, 'subsequent');
251
+ return;
252
+ }
253
+ const xfrom = e.dataset.xfrom;
254
+ if(ndx && xfrom === prevXFrom){
255
+ D.addClass(e,'subsequent');
256
+ }else{
257
+ D.removeClass(e, 'subsequent');
258
+ }
259
+ prevXFrom = xfrom;
260
+ });
261
+ },
262
+ /* Injects element e as a new row in the chat, at the oldest end
263
+ of the list if atEnd is truthy, else at the newest end of the
264
+ list. */
234265
injectMessageElem: function f(e, atEnd){
235266
const mip = atEnd ? this.e.loadOlderToolbar : this.e.messageInjectPoint,
236267
holder = this.e.messagesWrapper,
237268
prevMessage = this.e.newestMessage;
269
+ if(!atEnd && !this._isBatchLoading
270
+ && e.dataset.xfrom && !e.classList.contains('notification')){
271
+ if(prevMessage && prevMessage.dataset.xfrom===e.dataset.xfrom){
272
+ D.addClass(e, 'subsequent');
273
+ }
274
+ }
238275
if(atEnd){
239276
const fe = mip.nextElementSibling;
240277
if(fe) mip.parentNode.insertBefore(e, fe);
241278
else D.append(mip.parentNode, e);
242279
}else{
@@ -1147,10 +1184,11 @@
11471184
Chat._isBatchLoading = false;
11481185
if(Chat._gotServerError){
11491186
Chat._gotServerError = false;
11501187
return;
11511188
}
1189
+ Chat.recalcMessageIndents();
11521190
if(n<0/*we asked for all history*/
11531191
|| 0===gotMessages/*we found no history*/
11541192
|| (n>0 && gotMessages<n /*we got fewer history entries than requested*/)
11551193
|| (n===0 && gotMessages<Chat.loadMessageCount
11561194
/*we asked for default amount and got fewer than that.*/)){
@@ -1194,10 +1232,11 @@
11941232
const afterFetch = function f(){
11951233
if(true===f.isFirstCall){
11961234
f.isFirstCall = false;
11971235
Chat.ajaxEnd();
11981236
Chat.e.messagesWrapper.classList.remove('loading');
1237
+ Chat.recalcMessageIndents();
11991238
setTimeout(function(){
12001239
Chat.scrollMessagesTo(1);
12011240
}, 250);
12021241
}
12031242
if(Chat._gotServerError && Chat.intervalTimer){
@@ -1235,11 +1274,11 @@
12351274
fails exepectedly when it times out, but is then immediately
12361275
resumed, and reportError() produces a loud error message. */
12371276
afterFetch();
12381277
},
12391278
onload:function(y){
1240
- newcontent(y);
1279
+ newcontent(y);
12411280
Chat._isBatchLoading = false;
12421281
afterFetch();
12431282
}
12441283
});
12451284
};
12461285
--- src/chat.js
+++ src/chat.js
@@ -226,17 +226,54 @@
226 }else{
227 eMsg.scrollIntoView(false);
228 }
229 return this;
230 },
231 /* Injects element e as a new row in the chat, at the top of the
232 list if atEnd is falsy, else at the end of the list, before
233 the load-history widget. */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234 injectMessageElem: function f(e, atEnd){
235 const mip = atEnd ? this.e.loadOlderToolbar : this.e.messageInjectPoint,
236 holder = this.e.messagesWrapper,
237 prevMessage = this.e.newestMessage;
 
 
 
 
 
 
238 if(atEnd){
239 const fe = mip.nextElementSibling;
240 if(fe) mip.parentNode.insertBefore(e, fe);
241 else D.append(mip.parentNode, e);
242 }else{
@@ -1147,10 +1184,11 @@
1147 Chat._isBatchLoading = false;
1148 if(Chat._gotServerError){
1149 Chat._gotServerError = false;
1150 return;
1151 }
 
1152 if(n<0/*we asked for all history*/
1153 || 0===gotMessages/*we found no history*/
1154 || (n>0 && gotMessages<n /*we got fewer history entries than requested*/)
1155 || (n===0 && gotMessages<Chat.loadMessageCount
1156 /*we asked for default amount and got fewer than that.*/)){
@@ -1194,10 +1232,11 @@
1194 const afterFetch = function f(){
1195 if(true===f.isFirstCall){
1196 f.isFirstCall = false;
1197 Chat.ajaxEnd();
1198 Chat.e.messagesWrapper.classList.remove('loading');
 
1199 setTimeout(function(){
1200 Chat.scrollMessagesTo(1);
1201 }, 250);
1202 }
1203 if(Chat._gotServerError && Chat.intervalTimer){
@@ -1235,11 +1274,11 @@
1235 fails exepectedly when it times out, but is then immediately
1236 resumed, and reportError() produces a loud error message. */
1237 afterFetch();
1238 },
1239 onload:function(y){
1240 newcontent(y);
1241 Chat._isBatchLoading = false;
1242 afterFetch();
1243 }
1244 });
1245 };
1246
--- src/chat.js
+++ src/chat.js
@@ -226,17 +226,54 @@
226 }else{
227 eMsg.scrollIntoView(false);
228 }
229 return this;
230 },
231 /**
232 Recalculates the ".subsequent" CSS class tag for all
233 messages. When a user posts multiple messages in a row, the
234 2nd and subsequent ones get the "subsequent" tag added to
235 them for additional styling. We cannot do this easily as
236 messages arrive in batch mode because they can arrive out of
237 order (they arrive, and are processed, in reverse order for
238 the "load older messages" buttons). We do this handling in
239 injectMessageElem() for "interactive" use but have to
240 recalculate them en mass after receiving a batch of messages
241 right after this app loads or via the "load older messages"
242 buttons.
243 */
244 recalcMessageIndents: function(){
245 var prevXFrom;
246 this.e.messagesWrapper.querySelectorAll('.message-widget').forEach(function(e,ndx){
247 if(e.classList.contains('notification')){
248 // Obligatory special case.
249 prevXFrom = undefined;
250 D.removeClass(e, 'subsequent');
251 return;
252 }
253 const xfrom = e.dataset.xfrom;
254 if(ndx && xfrom === prevXFrom){
255 D.addClass(e,'subsequent');
256 }else{
257 D.removeClass(e, 'subsequent');
258 }
259 prevXFrom = xfrom;
260 });
261 },
262 /* Injects element e as a new row in the chat, at the oldest end
263 of the list if atEnd is truthy, else at the newest end of the
264 list. */
265 injectMessageElem: function f(e, atEnd){
266 const mip = atEnd ? this.e.loadOlderToolbar : this.e.messageInjectPoint,
267 holder = this.e.messagesWrapper,
268 prevMessage = this.e.newestMessage;
269 if(!atEnd && !this._isBatchLoading
270 && e.dataset.xfrom && !e.classList.contains('notification')){
271 if(prevMessage && prevMessage.dataset.xfrom===e.dataset.xfrom){
272 D.addClass(e, 'subsequent');
273 }
274 }
275 if(atEnd){
276 const fe = mip.nextElementSibling;
277 if(fe) mip.parentNode.insertBefore(e, fe);
278 else D.append(mip.parentNode, e);
279 }else{
@@ -1147,10 +1184,11 @@
1184 Chat._isBatchLoading = false;
1185 if(Chat._gotServerError){
1186 Chat._gotServerError = false;
1187 return;
1188 }
1189 Chat.recalcMessageIndents();
1190 if(n<0/*we asked for all history*/
1191 || 0===gotMessages/*we found no history*/
1192 || (n>0 && gotMessages<n /*we got fewer history entries than requested*/)
1193 || (n===0 && gotMessages<Chat.loadMessageCount
1194 /*we asked for default amount and got fewer than that.*/)){
@@ -1194,10 +1232,11 @@
1232 const afterFetch = function f(){
1233 if(true===f.isFirstCall){
1234 f.isFirstCall = false;
1235 Chat.ajaxEnd();
1236 Chat.e.messagesWrapper.classList.remove('loading');
1237 Chat.recalcMessageIndents();
1238 setTimeout(function(){
1239 Chat.scrollMessagesTo(1);
1240 }, 250);
1241 }
1242 if(Chat._gotServerError && Chat.intervalTimer){
@@ -1235,11 +1274,11 @@
1274 fails exepectedly when it times out, but is then immediately
1275 resumed, and reportError() produces a loud error message. */
1276 afterFetch();
1277 },
1278 onload:function(y){
1279 newcontent(y);
1280 Chat._isBatchLoading = false;
1281 afterFetch();
1282 }
1283 });
1284 };
1285
--- src/default.css
+++ src/default.css
@@ -1518,10 +1518,27 @@
15181518
white-space: nowrap;
15191519
}
15201520
body.chat .fossil-tooltip.help-buttonlet-content {
15211521
font-size: 80%;
15221522
}
1523
+body.chat .message-widget.subsequent {
1524
+ /* When a single user posts multiple messages in a row,
1525
+ the 2nd and subsequent ones get the 'subsequent' class
1526
+ added to them. */
1527
+ margin-left: 2.5em;
1528
+ margin-right: 2.5em;
1529
+}
1530
+body.chat .message-widget.subsequent .message-widget-tab:before {
1531
+ content: "↳ ";
1532
+}
1533
+body.chat.my-messages-right .message-widget.subsequent.mine .message-widget-tab:before {
1534
+ content: revert;
1535
+}
1536
+body.chat.my-messages-right .message-widget.subsequent.mine .message-widget-tab:after {
1537
+ content: " ↲";
1538
+}
1539
+
15231540
/* The popup element for displaying message timestamps
15241541
and deletion controls. */
15251542
body.chat .chat-message-popup {
15261543
font-family: monospace;
15271544
font-size: 0.8em;
15281545
--- src/default.css
+++ src/default.css
@@ -1518,10 +1518,27 @@
1518 white-space: nowrap;
1519 }
1520 body.chat .fossil-tooltip.help-buttonlet-content {
1521 font-size: 80%;
1522 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1523 /* The popup element for displaying message timestamps
1524 and deletion controls. */
1525 body.chat .chat-message-popup {
1526 font-family: monospace;
1527 font-size: 0.8em;
1528
--- src/default.css
+++ src/default.css
@@ -1518,10 +1518,27 @@
1518 white-space: nowrap;
1519 }
1520 body.chat .fossil-tooltip.help-buttonlet-content {
1521 font-size: 80%;
1522 }
1523 body.chat .message-widget.subsequent {
1524 /* When a single user posts multiple messages in a row,
1525 the 2nd and subsequent ones get the 'subsequent' class
1526 added to them. */
1527 margin-left: 2.5em;
1528 margin-right: 2.5em;
1529 }
1530 body.chat .message-widget.subsequent .message-widget-tab:before {
1531 content: "↳ ";
1532 }
1533 body.chat.my-messages-right .message-widget.subsequent.mine .message-widget-tab:before {
1534 content: revert;
1535 }
1536 body.chat.my-messages-right .message-widget.subsequent.mine .message-widget-tab:after {
1537 content: " ↲";
1538 }
1539
1540 /* The popup element for displaying message timestamps
1541 and deletion controls. */
1542 body.chat .chat-message-popup {
1543 font-family: monospace;
1544 font-size: 0.8em;
1545

Keyboard Shortcuts

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