Fossil SCM

Apply the connection-recovery reporting to the other AJAX commands, so that they can trigger the polling to continue if they determine that the connection is back up. Internal cleanups.

stephan 2025-04-10 00:08 chat-backoff-timer
Commit fd36f8490beb74a33c298651c787d7179204c355ebd6f60a2e6529db96ab6fd5
1 file changed +64 -39
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -192,23 +192,28 @@
192192
The timeing of resetting the delay when service returns is,
193193
because of the long-poll connection and our lack of low-level
194194
insight into the connection at this level, a bit wonky.
195195
*/
196196
timer:{
197
- id: undefined,
197
+ tidPoller: undefined /* poller timer */,
198198
$initialDelay: 1000 /* initial polling interval (ms) */,
199199
currentDelay: 1000 /* current polling interval */,
200200
maxDelay: 60000 /* max interval when backing off for
201201
connection errors */,
202
- minDelay: 7000 /* minimum delay time */,
203
- idReconnect: undefined /*timer id for reconnection determination*/,
202
+ minDelay: 5000 /* minimum delay time */,
203
+ tidReconnect: undefined /*timer id for reconnection determination*/,
204
+ randomInterval: function(){
205
+ return Math.floor(Math.random() * this.minDelay);
206
+ },
204207
incrDelay: function(){
205208
if( this.maxDelay > this.currentDelay ){
206
- this.currentDelay =
207
- (this.currentDelay < this.minDelay)
208
- ? this.minDelay
209
- : (this.currentDelay * 3);
209
+ if(this.currentDelay < this.minDelay){
210
+ this.currentDelay = this.minDelay;
211
+ }else{
212
+ this.currentDelay *= 2;
213
+ }
214
+ this.currentDelay += this.randomInterval();
210215
}
211216
return this.currentDelay;
212217
},
213218
resetDelay: function(){
214219
return this.currentDelay = this.$initialDelay;
@@ -268,10 +273,11 @@
268273
*/
269274
ajaxStart: function(){
270275
if(1===++this.ajaxInflight){
271276
this.enableAjaxComponents(false);
272277
}
278
+ setupConnectionReestablished();
273279
},
274280
/* Must be called after any ajax-related call for which
275281
ajaxStart() was called, regardless of success or failure. If
276282
it was the last such call (as measured by calls to
277283
ajaxStart() and ajaxEnd()), elements disabled by a prior call
@@ -836,11 +842,15 @@
836842
// We need to fetch the plain-text version...
837843
const self = this;
838844
F.fetch('chat-fetch-one',{
839845
urlParams:{ name: id, raw: true},
840846
responseType: 'json',
847
+ function(){
848
+ Chat.ajaxStart();
849
+ },
841850
onload: function(msg){
851
+ reportConnectionReestablished();
842852
content.$elems[1] = D.append(D.pre(),msg.xmsg);
843853
content.$elems[1]._xmsgRaw = msg.xmsg/*used for copy-to-clipboard feature*/;
844854
self.toggleTextMode(e);
845855
},
846856
aftersend:function(){
@@ -898,11 +908,14 @@
898908
if(!(e instanceof HTMLElement)) return;
899909
if(this.userMayDelete(e)){
900910
this.ajaxStart();
901911
F.fetch("chat-delete/" + id, {
902912
responseType: 'json',
903
- onload:(r)=>this.deleteMessageElem(r),
913
+ onload:(r)=>{
914
+ reportConnectionReestablished();
915
+ this.deleteMessageElem(r);
916
+ },
904917
onerror:(err)=>this.reportErrorAsMessage(err)
905918
});
906919
}else{
907920
this.deleteMessageElem(id);
908921
}
@@ -1518,10 +1531,11 @@
15181531
n: nFetch,
15191532
i: iFirst
15201533
},
15211534
responseType: "json",
15221535
onload:function(jx){
1536
+ reportConnectionReestablished();
15231537
if( bDown ) jx.msgs.reverse();
15241538
jx.msgs.forEach((m) => {
15251539
m.isSearchResult = true;
15261540
var mw = new Chat.MessageWidget(m);
15271541
if( bDown ){
@@ -1685,43 +1699,49 @@
16851699
const theMsg = findMessageWidgetParent(w);
16861700
if(theMsg) Chat.deleteMessageElem(theMsg);
16871701
}));
16881702
Chat.reportErrorAsMessage(w);
16891703
};
1704
+
1705
+ const removeConnectionErrors = function() {
1706
+ D.remove(Chat.e.viewMessages.querySelectorAll(
1707
+ '.message-widget.error-connection'));
1708
+ };
16901709
16911710
/* Assume the connection has been established, reset
1692
- the Chat.timer.idReconnect, and alert the user
1711
+ the Chat.timer.tidReconnect, and alert the user
16931712
that the outage appears to be over. */
16941713
const reportConnectionReestablished = function(){
1695
- if( Chat.timer.idReconnect ){
1696
- clearTimeout(Chat.timer.idReconnect);
1697
- Chat.timer.idReconnect = 0;
1714
+ if( Chat.timer.tidReconnect ){
1715
+ clearTimeout(Chat.timer.tidReconnect);
1716
+ Chat.timer.tidReconnect = 0;
16981717
}
16991718
if( Chat.timer.isDelayed() ){
1719
+ removeConnectionErrors();
17001720
Chat.timer.resetDelay();
17011721
Chat.reportReconnection(
17021722
"Connection restored after outage."
17031723
);
17041724
setTimeout( Chat.poll, 0 );
17051725
}
17061726
};
17071727
1708
- /* If we're currently in delayed-retry mode, try to reset the delay
1709
- if we're waiting for a while for the connection to complete,
1710
- as that's an indication (not a guaranty) that we're connected
1711
- and long-polling. */
1728
+ /* To be called from F.fetch() beforesend() handlers. If we're
1729
+ currently in delayed-retry mode and a connection is start, try to
1730
+ reset the delay after N time waiting on that connection. The fact
1731
+ that the connection is waiting to respond, rather than outright
1732
+ failing, is a good hint that the outage is over and we can reset
1733
+ the back-off timer. */
17121734
const setupConnectionReestablished = function(){
1713
- if( !Chat.timer.idReconnect && Chat.timer.isDelayed() ){
1714
- Chat.timer.idReconnect = setTimeout(()=>{
1715
- Chat.timer.idReconnect = 0;
1735
+ if( !Chat.timer.tidReconnect && Chat.timer.isDelayed() ){
1736
+ Chat.timer.tidReconnect = setTimeout(()=>{
1737
+ Chat.timer.tidReconnect = 0;
17161738
if( poll.running ){
17171739
reportConnectionReestablished();
17181740
}
17191741
}, Chat.timer.$initialDelay * 5 );
1720
- Chat.e.viewMessages.querySelectorAll(
1721
- '.message-widget.error-connection'
1722
- ).forEach(e=>D.remove(e));
1742
+ removeConnectionErrors();
17231743
}
17241744
};
17251745
17261746
/**
17271747
Submits the contents of the message input field (if not empty)
@@ -1777,14 +1797,10 @@
17771797
const self = this;
17781798
fd.set("lmtime", localTime8601(new Date()));
17791799
F.fetch("chat-send",{
17801800
payload: fd,
17811801
responseType: 'text',
1782
- beforesend: function(){
1783
- Chat.ajaxStart();
1784
- setupConnectionReestablished();
1785
- },
17861802
onerror:function(err){
17871803
self.reportErrorAsMessage(err);
17881804
recoverFailedMessage(fallback);
17891805
},
17901806
onload:function(txt){
@@ -2286,10 +2302,11 @@
22862302
/*filename needed for mimetype determination*/);
22872303
fd.append('render_mode',F.page.previewModes.wiki);
22882304
F.fetch('ajax/preview-text',{
22892305
payload: fd,
22902306
onload: function(html){
2307
+ reportConnectionReestablished();
22912308
Chat.setPreviewText(html);
22922309
F.pikchr.addSrcView(Chat.e.viewPreview.querySelectorAll('svg.pikchr'));
22932310
},
22942311
onerror: function(e){
22952312
F.fetch.onerror(e);
@@ -2423,10 +2440,11 @@
24232440
onerror:function(err){
24242441
Chat.reportErrorAsMessage(err);
24252442
Chat._isBatchLoading = false;
24262443
},
24272444
onload:function(x){
2445
+ reportConnectionReestablished();
24282446
let gotMessages = x.msgs.length;
24292447
newcontent(x,true);
24302448
Chat._isBatchLoading = false;
24312449
Chat.updateActiveUserList();
24322450
if(Chat._gotServerError){
@@ -2512,10 +2530,11 @@
25122530
onerror:function(err){
25132531
Chat.setCurrentView(Chat.e.viewMessages);
25142532
Chat.reportErrorAsMessage(err);
25152533
},
25162534
onload:function(jx){
2535
+ reportConnectionReestablished();
25172536
let previd = 0;
25182537
D.clearElement(eMsgTgt);
25192538
jx.msgs.forEach((m)=>{
25202539
m.isSearchResult = true;
25212540
const mw = new Chat.MessageWidget(m);
@@ -2548,44 +2567,50 @@
25482567
}/*Chat.submitSearch()*/;
25492568
25502569
/**
25512570
Deal with the last poll() response and maybe re-start poll().
25522571
*/
2553
- const afterPollFetch = function f(isOkay = true){
2572
+ const afterPollFetch = function f(err){
25542573
if(true===f.isFirstCall){
25552574
f.isFirstCall = false;
25562575
Chat.ajaxEnd();
25572576
Chat.e.viewMessages.classList.remove('loading');
25582577
setTimeout(function(){
25592578
Chat.scrollMessagesTo(1);
25602579
}, 250);
25612580
}
2562
- if(Chat.timer.id) {
2563
- clearTimeout(Chat.timer.id);
2564
- Chat.timer.id = 0;
2581
+ if(Chat.timer.tidPoller) {
2582
+ clearTimeout(Chat.timer.tidPoller);
2583
+ Chat.timer.tidPoller = 0;
25652584
}
25662585
if(Chat._gotServerError){
25672586
Chat.reportErrorAsMessage(
25682587
"Shutting down chat poller due to server-side error. ",
2569
- "Reload this page to reactivate it.");
2570
- Chat.timer.id = undefined;
2588
+ "Reload this page to reactivate it."
2589
+ );
2590
+ Chat.timer.tidPoller = undefined;
25712591
poll.running = false;
25722592
} else {
25732593
poll.running = false;
2574
- if( isOkay ){
2575
- Chat.timer.id = setTimeout(
2594
+ if( !err ){
2595
+ /* Restart the poller. */
2596
+ Chat.timer.tidPoller = setTimeout(
25762597
poll, Chat.timer.resetDelay()
25772598
);
25782599
}else{
2600
+ /* Delay a while before trying again, noting that other Chat
2601
+ APIs may try and succeed at connections before this timer
2602
+ resolves. */
25792603
const delay = Chat.timer.incrDelay();
25802604
const msg = D.addClass(
25812605
Chat.reportErrorAsMessage(
25822606
"Connection error. Retrying in ",
2583
- delay, " ms.").e.body,
2607
+ delay, " ms."
2608
+ ).e.body,
25842609
'error-connection'
25852610
);
2586
- Chat.timer.id = setTimeout(()=>{
2611
+ Chat.timer.tidPoller = setTimeout(()=>{
25872612
D.remove(msg);
25882613
poll();
25892614
}, delay);
25902615
}
25912616
//console.log("isOkay =",isOkay,"currentDelay =",Chat.timer.currentDelay);
@@ -2634,31 +2659,31 @@
26342659
Chat._isBatchLoading = false;
26352660
if(Chat.verboseErrors) console.error("poll onerror:",err);
26362661
/* ^^^ we don't use Chat.reportError() here b/c the polling
26372662
fails exepectedly when it times out, but is then immediately
26382663
resumed, and reportError() produces a loud error message. */
2639
- afterPollFetch(false);
2664
+ afterPollFetch(err);
26402665
},
26412666
onload:function(y){
26422667
reportConnectionReestablished();
26432668
newcontent(y);
26442669
if(Chat._isBatchLoading){
26452670
Chat._isBatchLoading = false;
26462671
Chat.updateActiveUserList();
26472672
}
2648
- afterPollFetch(true);
2673
+ afterPollFetch();
26492674
}
26502675
});
26512676
};
26522677
poll.isFirstCall = true;
26532678
Chat.poll = poll;
26542679
Chat._gotServerError = poll.running = false;
26552680
if( window.fossil.config.chat.fromcli ){
26562681
Chat.chatOnlyMode(true);
26572682
}
2658
- Chat.timer.id = setTimeout(poll, Chat.timer.resetDelay());
2683
+ Chat.timer.tidPoller = setTimeout(poll, Chat.timer.resetDelay());
26592684
delete ForceResizeKludge.$disabled;
26602685
ForceResizeKludge();
26612686
Chat.animate.$disabled = false;
26622687
setTimeout( ()=>Chat.inputFocus(), 0 );
26632688
F.page.chat = Chat/* enables testing the APIs via the dev tools */;
26642689
});
26652690
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -192,23 +192,28 @@
192 The timeing of resetting the delay when service returns is,
193 because of the long-poll connection and our lack of low-level
194 insight into the connection at this level, a bit wonky.
195 */
196 timer:{
197 id: undefined,
198 $initialDelay: 1000 /* initial polling interval (ms) */,
199 currentDelay: 1000 /* current polling interval */,
200 maxDelay: 60000 /* max interval when backing off for
201 connection errors */,
202 minDelay: 7000 /* minimum delay time */,
203 idReconnect: undefined /*timer id for reconnection determination*/,
 
 
 
204 incrDelay: function(){
205 if( this.maxDelay > this.currentDelay ){
206 this.currentDelay =
207 (this.currentDelay < this.minDelay)
208 ? this.minDelay
209 : (this.currentDelay * 3);
 
 
210 }
211 return this.currentDelay;
212 },
213 resetDelay: function(){
214 return this.currentDelay = this.$initialDelay;
@@ -268,10 +273,11 @@
268 */
269 ajaxStart: function(){
270 if(1===++this.ajaxInflight){
271 this.enableAjaxComponents(false);
272 }
 
273 },
274 /* Must be called after any ajax-related call for which
275 ajaxStart() was called, regardless of success or failure. If
276 it was the last such call (as measured by calls to
277 ajaxStart() and ajaxEnd()), elements disabled by a prior call
@@ -836,11 +842,15 @@
836 // We need to fetch the plain-text version...
837 const self = this;
838 F.fetch('chat-fetch-one',{
839 urlParams:{ name: id, raw: true},
840 responseType: 'json',
 
 
 
841 onload: function(msg){
 
842 content.$elems[1] = D.append(D.pre(),msg.xmsg);
843 content.$elems[1]._xmsgRaw = msg.xmsg/*used for copy-to-clipboard feature*/;
844 self.toggleTextMode(e);
845 },
846 aftersend:function(){
@@ -898,11 +908,14 @@
898 if(!(e instanceof HTMLElement)) return;
899 if(this.userMayDelete(e)){
900 this.ajaxStart();
901 F.fetch("chat-delete/" + id, {
902 responseType: 'json',
903 onload:(r)=>this.deleteMessageElem(r),
 
 
 
904 onerror:(err)=>this.reportErrorAsMessage(err)
905 });
906 }else{
907 this.deleteMessageElem(id);
908 }
@@ -1518,10 +1531,11 @@
1518 n: nFetch,
1519 i: iFirst
1520 },
1521 responseType: "json",
1522 onload:function(jx){
 
1523 if( bDown ) jx.msgs.reverse();
1524 jx.msgs.forEach((m) => {
1525 m.isSearchResult = true;
1526 var mw = new Chat.MessageWidget(m);
1527 if( bDown ){
@@ -1685,43 +1699,49 @@
1685 const theMsg = findMessageWidgetParent(w);
1686 if(theMsg) Chat.deleteMessageElem(theMsg);
1687 }));
1688 Chat.reportErrorAsMessage(w);
1689 };
 
 
 
 
 
1690
1691 /* Assume the connection has been established, reset
1692 the Chat.timer.idReconnect, and alert the user
1693 that the outage appears to be over. */
1694 const reportConnectionReestablished = function(){
1695 if( Chat.timer.idReconnect ){
1696 clearTimeout(Chat.timer.idReconnect);
1697 Chat.timer.idReconnect = 0;
1698 }
1699 if( Chat.timer.isDelayed() ){
 
1700 Chat.timer.resetDelay();
1701 Chat.reportReconnection(
1702 "Connection restored after outage."
1703 );
1704 setTimeout( Chat.poll, 0 );
1705 }
1706 };
1707
1708 /* If we're currently in delayed-retry mode, try to reset the delay
1709 if we're waiting for a while for the connection to complete,
1710 as that's an indication (not a guaranty) that we're connected
1711 and long-polling. */
 
 
1712 const setupConnectionReestablished = function(){
1713 if( !Chat.timer.idReconnect && Chat.timer.isDelayed() ){
1714 Chat.timer.idReconnect = setTimeout(()=>{
1715 Chat.timer.idReconnect = 0;
1716 if( poll.running ){
1717 reportConnectionReestablished();
1718 }
1719 }, Chat.timer.$initialDelay * 5 );
1720 Chat.e.viewMessages.querySelectorAll(
1721 '.message-widget.error-connection'
1722 ).forEach(e=>D.remove(e));
1723 }
1724 };
1725
1726 /**
1727 Submits the contents of the message input field (if not empty)
@@ -1777,14 +1797,10 @@
1777 const self = this;
1778 fd.set("lmtime", localTime8601(new Date()));
1779 F.fetch("chat-send",{
1780 payload: fd,
1781 responseType: 'text',
1782 beforesend: function(){
1783 Chat.ajaxStart();
1784 setupConnectionReestablished();
1785 },
1786 onerror:function(err){
1787 self.reportErrorAsMessage(err);
1788 recoverFailedMessage(fallback);
1789 },
1790 onload:function(txt){
@@ -2286,10 +2302,11 @@
2286 /*filename needed for mimetype determination*/);
2287 fd.append('render_mode',F.page.previewModes.wiki);
2288 F.fetch('ajax/preview-text',{
2289 payload: fd,
2290 onload: function(html){
 
2291 Chat.setPreviewText(html);
2292 F.pikchr.addSrcView(Chat.e.viewPreview.querySelectorAll('svg.pikchr'));
2293 },
2294 onerror: function(e){
2295 F.fetch.onerror(e);
@@ -2423,10 +2440,11 @@
2423 onerror:function(err){
2424 Chat.reportErrorAsMessage(err);
2425 Chat._isBatchLoading = false;
2426 },
2427 onload:function(x){
 
2428 let gotMessages = x.msgs.length;
2429 newcontent(x,true);
2430 Chat._isBatchLoading = false;
2431 Chat.updateActiveUserList();
2432 if(Chat._gotServerError){
@@ -2512,10 +2530,11 @@
2512 onerror:function(err){
2513 Chat.setCurrentView(Chat.e.viewMessages);
2514 Chat.reportErrorAsMessage(err);
2515 },
2516 onload:function(jx){
 
2517 let previd = 0;
2518 D.clearElement(eMsgTgt);
2519 jx.msgs.forEach((m)=>{
2520 m.isSearchResult = true;
2521 const mw = new Chat.MessageWidget(m);
@@ -2548,44 +2567,50 @@
2548 }/*Chat.submitSearch()*/;
2549
2550 /**
2551 Deal with the last poll() response and maybe re-start poll().
2552 */
2553 const afterPollFetch = function f(isOkay = true){
2554 if(true===f.isFirstCall){
2555 f.isFirstCall = false;
2556 Chat.ajaxEnd();
2557 Chat.e.viewMessages.classList.remove('loading');
2558 setTimeout(function(){
2559 Chat.scrollMessagesTo(1);
2560 }, 250);
2561 }
2562 if(Chat.timer.id) {
2563 clearTimeout(Chat.timer.id);
2564 Chat.timer.id = 0;
2565 }
2566 if(Chat._gotServerError){
2567 Chat.reportErrorAsMessage(
2568 "Shutting down chat poller due to server-side error. ",
2569 "Reload this page to reactivate it.");
2570 Chat.timer.id = undefined;
 
2571 poll.running = false;
2572 } else {
2573 poll.running = false;
2574 if( isOkay ){
2575 Chat.timer.id = setTimeout(
 
2576 poll, Chat.timer.resetDelay()
2577 );
2578 }else{
 
 
 
2579 const delay = Chat.timer.incrDelay();
2580 const msg = D.addClass(
2581 Chat.reportErrorAsMessage(
2582 "Connection error. Retrying in ",
2583 delay, " ms.").e.body,
 
2584 'error-connection'
2585 );
2586 Chat.timer.id = setTimeout(()=>{
2587 D.remove(msg);
2588 poll();
2589 }, delay);
2590 }
2591 //console.log("isOkay =",isOkay,"currentDelay =",Chat.timer.currentDelay);
@@ -2634,31 +2659,31 @@
2634 Chat._isBatchLoading = false;
2635 if(Chat.verboseErrors) console.error("poll onerror:",err);
2636 /* ^^^ we don't use Chat.reportError() here b/c the polling
2637 fails exepectedly when it times out, but is then immediately
2638 resumed, and reportError() produces a loud error message. */
2639 afterPollFetch(false);
2640 },
2641 onload:function(y){
2642 reportConnectionReestablished();
2643 newcontent(y);
2644 if(Chat._isBatchLoading){
2645 Chat._isBatchLoading = false;
2646 Chat.updateActiveUserList();
2647 }
2648 afterPollFetch(true);
2649 }
2650 });
2651 };
2652 poll.isFirstCall = true;
2653 Chat.poll = poll;
2654 Chat._gotServerError = poll.running = false;
2655 if( window.fossil.config.chat.fromcli ){
2656 Chat.chatOnlyMode(true);
2657 }
2658 Chat.timer.id = setTimeout(poll, Chat.timer.resetDelay());
2659 delete ForceResizeKludge.$disabled;
2660 ForceResizeKludge();
2661 Chat.animate.$disabled = false;
2662 setTimeout( ()=>Chat.inputFocus(), 0 );
2663 F.page.chat = Chat/* enables testing the APIs via the dev tools */;
2664 });
2665
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -192,23 +192,28 @@
192 The timeing of resetting the delay when service returns is,
193 because of the long-poll connection and our lack of low-level
194 insight into the connection at this level, a bit wonky.
195 */
196 timer:{
197 tidPoller: undefined /* poller timer */,
198 $initialDelay: 1000 /* initial polling interval (ms) */,
199 currentDelay: 1000 /* current polling interval */,
200 maxDelay: 60000 /* max interval when backing off for
201 connection errors */,
202 minDelay: 5000 /* minimum delay time */,
203 tidReconnect: undefined /*timer id for reconnection determination*/,
204 randomInterval: function(){
205 return Math.floor(Math.random() * this.minDelay);
206 },
207 incrDelay: function(){
208 if( this.maxDelay > this.currentDelay ){
209 if(this.currentDelay < this.minDelay){
210 this.currentDelay = this.minDelay;
211 }else{
212 this.currentDelay *= 2;
213 }
214 this.currentDelay += this.randomInterval();
215 }
216 return this.currentDelay;
217 },
218 resetDelay: function(){
219 return this.currentDelay = this.$initialDelay;
@@ -268,10 +273,11 @@
273 */
274 ajaxStart: function(){
275 if(1===++this.ajaxInflight){
276 this.enableAjaxComponents(false);
277 }
278 setupConnectionReestablished();
279 },
280 /* Must be called after any ajax-related call for which
281 ajaxStart() was called, regardless of success or failure. If
282 it was the last such call (as measured by calls to
283 ajaxStart() and ajaxEnd()), elements disabled by a prior call
@@ -836,11 +842,15 @@
842 // We need to fetch the plain-text version...
843 const self = this;
844 F.fetch('chat-fetch-one',{
845 urlParams:{ name: id, raw: true},
846 responseType: 'json',
847 function(){
848 Chat.ajaxStart();
849 },
850 onload: function(msg){
851 reportConnectionReestablished();
852 content.$elems[1] = D.append(D.pre(),msg.xmsg);
853 content.$elems[1]._xmsgRaw = msg.xmsg/*used for copy-to-clipboard feature*/;
854 self.toggleTextMode(e);
855 },
856 aftersend:function(){
@@ -898,11 +908,14 @@
908 if(!(e instanceof HTMLElement)) return;
909 if(this.userMayDelete(e)){
910 this.ajaxStart();
911 F.fetch("chat-delete/" + id, {
912 responseType: 'json',
913 onload:(r)=>{
914 reportConnectionReestablished();
915 this.deleteMessageElem(r);
916 },
917 onerror:(err)=>this.reportErrorAsMessage(err)
918 });
919 }else{
920 this.deleteMessageElem(id);
921 }
@@ -1518,10 +1531,11 @@
1531 n: nFetch,
1532 i: iFirst
1533 },
1534 responseType: "json",
1535 onload:function(jx){
1536 reportConnectionReestablished();
1537 if( bDown ) jx.msgs.reverse();
1538 jx.msgs.forEach((m) => {
1539 m.isSearchResult = true;
1540 var mw = new Chat.MessageWidget(m);
1541 if( bDown ){
@@ -1685,43 +1699,49 @@
1699 const theMsg = findMessageWidgetParent(w);
1700 if(theMsg) Chat.deleteMessageElem(theMsg);
1701 }));
1702 Chat.reportErrorAsMessage(w);
1703 };
1704
1705 const removeConnectionErrors = function() {
1706 D.remove(Chat.e.viewMessages.querySelectorAll(
1707 '.message-widget.error-connection'));
1708 };
1709
1710 /* Assume the connection has been established, reset
1711 the Chat.timer.tidReconnect, and alert the user
1712 that the outage appears to be over. */
1713 const reportConnectionReestablished = function(){
1714 if( Chat.timer.tidReconnect ){
1715 clearTimeout(Chat.timer.tidReconnect);
1716 Chat.timer.tidReconnect = 0;
1717 }
1718 if( Chat.timer.isDelayed() ){
1719 removeConnectionErrors();
1720 Chat.timer.resetDelay();
1721 Chat.reportReconnection(
1722 "Connection restored after outage."
1723 );
1724 setTimeout( Chat.poll, 0 );
1725 }
1726 };
1727
1728 /* To be called from F.fetch() beforesend() handlers. If we're
1729 currently in delayed-retry mode and a connection is start, try to
1730 reset the delay after N time waiting on that connection. The fact
1731 that the connection is waiting to respond, rather than outright
1732 failing, is a good hint that the outage is over and we can reset
1733 the back-off timer. */
1734 const setupConnectionReestablished = function(){
1735 if( !Chat.timer.tidReconnect && Chat.timer.isDelayed() ){
1736 Chat.timer.tidReconnect = setTimeout(()=>{
1737 Chat.timer.tidReconnect = 0;
1738 if( poll.running ){
1739 reportConnectionReestablished();
1740 }
1741 }, Chat.timer.$initialDelay * 5 );
1742 removeConnectionErrors();
 
 
1743 }
1744 };
1745
1746 /**
1747 Submits the contents of the message input field (if not empty)
@@ -1777,14 +1797,10 @@
1797 const self = this;
1798 fd.set("lmtime", localTime8601(new Date()));
1799 F.fetch("chat-send",{
1800 payload: fd,
1801 responseType: 'text',
 
 
 
 
1802 onerror:function(err){
1803 self.reportErrorAsMessage(err);
1804 recoverFailedMessage(fallback);
1805 },
1806 onload:function(txt){
@@ -2286,10 +2302,11 @@
2302 /*filename needed for mimetype determination*/);
2303 fd.append('render_mode',F.page.previewModes.wiki);
2304 F.fetch('ajax/preview-text',{
2305 payload: fd,
2306 onload: function(html){
2307 reportConnectionReestablished();
2308 Chat.setPreviewText(html);
2309 F.pikchr.addSrcView(Chat.e.viewPreview.querySelectorAll('svg.pikchr'));
2310 },
2311 onerror: function(e){
2312 F.fetch.onerror(e);
@@ -2423,10 +2440,11 @@
2440 onerror:function(err){
2441 Chat.reportErrorAsMessage(err);
2442 Chat._isBatchLoading = false;
2443 },
2444 onload:function(x){
2445 reportConnectionReestablished();
2446 let gotMessages = x.msgs.length;
2447 newcontent(x,true);
2448 Chat._isBatchLoading = false;
2449 Chat.updateActiveUserList();
2450 if(Chat._gotServerError){
@@ -2512,10 +2530,11 @@
2530 onerror:function(err){
2531 Chat.setCurrentView(Chat.e.viewMessages);
2532 Chat.reportErrorAsMessage(err);
2533 },
2534 onload:function(jx){
2535 reportConnectionReestablished();
2536 let previd = 0;
2537 D.clearElement(eMsgTgt);
2538 jx.msgs.forEach((m)=>{
2539 m.isSearchResult = true;
2540 const mw = new Chat.MessageWidget(m);
@@ -2548,44 +2567,50 @@
2567 }/*Chat.submitSearch()*/;
2568
2569 /**
2570 Deal with the last poll() response and maybe re-start poll().
2571 */
2572 const afterPollFetch = function f(err){
2573 if(true===f.isFirstCall){
2574 f.isFirstCall = false;
2575 Chat.ajaxEnd();
2576 Chat.e.viewMessages.classList.remove('loading');
2577 setTimeout(function(){
2578 Chat.scrollMessagesTo(1);
2579 }, 250);
2580 }
2581 if(Chat.timer.tidPoller) {
2582 clearTimeout(Chat.timer.tidPoller);
2583 Chat.timer.tidPoller = 0;
2584 }
2585 if(Chat._gotServerError){
2586 Chat.reportErrorAsMessage(
2587 "Shutting down chat poller due to server-side error. ",
2588 "Reload this page to reactivate it."
2589 );
2590 Chat.timer.tidPoller = undefined;
2591 poll.running = false;
2592 } else {
2593 poll.running = false;
2594 if( !err ){
2595 /* Restart the poller. */
2596 Chat.timer.tidPoller = setTimeout(
2597 poll, Chat.timer.resetDelay()
2598 );
2599 }else{
2600 /* Delay a while before trying again, noting that other Chat
2601 APIs may try and succeed at connections before this timer
2602 resolves. */
2603 const delay = Chat.timer.incrDelay();
2604 const msg = D.addClass(
2605 Chat.reportErrorAsMessage(
2606 "Connection error. Retrying in ",
2607 delay, " ms."
2608 ).e.body,
2609 'error-connection'
2610 );
2611 Chat.timer.tidPoller = setTimeout(()=>{
2612 D.remove(msg);
2613 poll();
2614 }, delay);
2615 }
2616 //console.log("isOkay =",isOkay,"currentDelay =",Chat.timer.currentDelay);
@@ -2634,31 +2659,31 @@
2659 Chat._isBatchLoading = false;
2660 if(Chat.verboseErrors) console.error("poll onerror:",err);
2661 /* ^^^ we don't use Chat.reportError() here b/c the polling
2662 fails exepectedly when it times out, but is then immediately
2663 resumed, and reportError() produces a loud error message. */
2664 afterPollFetch(err);
2665 },
2666 onload:function(y){
2667 reportConnectionReestablished();
2668 newcontent(y);
2669 if(Chat._isBatchLoading){
2670 Chat._isBatchLoading = false;
2671 Chat.updateActiveUserList();
2672 }
2673 afterPollFetch();
2674 }
2675 });
2676 };
2677 poll.isFirstCall = true;
2678 Chat.poll = poll;
2679 Chat._gotServerError = poll.running = false;
2680 if( window.fossil.config.chat.fromcli ){
2681 Chat.chatOnlyMode(true);
2682 }
2683 Chat.timer.tidPoller = setTimeout(poll, Chat.timer.resetDelay());
2684 delete ForceResizeKludge.$disabled;
2685 ForceResizeKludge();
2686 Chat.animate.$disabled = false;
2687 setTimeout( ()=>Chat.inputFocus(), 0 );
2688 F.page.chat = Chat/* enables testing the APIs via the dev tools */;
2689 });
2690

Keyboard Shortcuts

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