Fossil SCM

Maintainability cleanups and docs in /chat. No (intended) functional changes.

stephan 2025-04-12 00:07 trunk
Commit 084001c76dba674baa6bc4963e561ec4b500fb07f1122303704bfefd8f7b304c
1 file changed +87 -52
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -196,19 +196,23 @@
196196
The timing of resetting the delay when service returns is,
197197
because of the long-poll connection and our lack of low-level
198198
insight into the connection at this level, a bit wonky.
199199
*/
200200
timer:{
201
- tidPoller: undefined /* setTimeout() poller timer id */,
201
+ /* setTimeout() ID for (delayed) starting a Chat.poll(), so
202
+ that it runs at controlled intervals (which change when a
203
+ connection drops and recovers). */
204
+ tidPendingPoll: undefined,
205
+ tidClearPollErr: undefined /*setTimeout() timer id for
206
+ reconnection determination. See
207
+ clearPollErrOnWait(). */,
202208
$initialDelay: 1000 /* initial polling interval (ms) */,
203209
currentDelay: 1000 /* current polling interval */,
204210
maxDelay: 60000 * 5 /* max interval when backing off for
205211
connection errors */,
206
- minDelay: 5000 /* minimum delay time */,
207
- tidReconnect: undefined /*setTimeout() timer id for
208
- reconnection determination. See
209
- clearPollErrOnWait(). */,
212
+ minDelay: 5000 /* minimum delay time for a back-off/retry
213
+ attempt. */,
210214
errCount: 0 /* Current poller connection error count */,
211215
minErrForNotify: 4 /* Don't warn for connection errors until this
212216
many have occurred */,
213217
pollTimeout: (1 && window.location.hostname.match(
214218
"localhost" /*presumably local dev mode*/
@@ -243,10 +247,40 @@
243247
return this.currentDelay = ms || this.$initialDelay;
244248
},
245249
/** Returns true if the timer is set to delayed mode. */
246250
isDelayed: function(){
247251
return (this.currentDelay > this.$initialDelay) ? this.currentDelay : 0;
252
+ },
253
+ /**
254
+ Cancels any in-progress pending-poll timer and starts a new
255
+ one with the given delay, defaulting to this.resetDelay().
256
+ */
257
+ startPendingPollTimer: function(delay){
258
+ this.cancelPendingPollTimer().tidPendingPoll
259
+ = setTimeout( Chat.poll, delay || Chat.timer.resetDelay() );
260
+ return this;
261
+ },
262
+ /**
263
+ Cancels any still-active timer set to trigger the next
264
+ Chat.poll().
265
+ */
266
+ cancelPendingPollTimer: function(){
267
+ if( this.tidPendingPoll ){
268
+ clearTimeout(this.tidPendingPoll);
269
+ this.tidPendingPoll = 0;
270
+ }
271
+ return this;
272
+ },
273
+ /**
274
+ Cancels any pending reconnection attempt back-off timer..
275
+ */
276
+ cancelReconnectCheckTimer: function(){
277
+ if( this.tidClearPollErr ){
278
+ clearTimeout(this.tidClearPollErr);
279
+ this.tidClearPollErr = 0;
280
+ }
281
+ return this;
248282
}
249283
},
250284
/**
251285
Gets (no args) or sets (1 arg) the current input text field
252286
value, taking into account single- vs multi-line input. The
@@ -1750,31 +1784,27 @@
17501784
}));
17511785
D.addClass(Chat.reportErrorAsMessage(w).e.body, "resend-message");
17521786
};
17531787
17541788
/* Assume the connection has been established, reset the
1755
- Chat.timer.tidReconnect, and (if showMsg and
1789
+ Chat.timer.tidClearPollErr, and (if showMsg and
17561790
!!Chat.e.eMsgPollError) alert the user that the outage appears to
17571791
be over. Also schedule Chat.poll() to run in the very near
17581792
future. */
17591793
const reportConnectionOkay = function(dbgContext, showMsg = true){
17601794
if(Chat.beVerbose){
17611795
console.warn('reportConnectionOkay', dbgContext,
17621796
'Chat.e.pollErrorMarker classes =',
17631797
Chat.e.pollErrorMarker.classList,
1764
- 'Chat.timer.tidReconnect =',Chat.timer.tidReconnect,
1798
+ 'Chat.timer.tidClearPollErr =',Chat.timer.tidClearPollErr,
17651799
'Chat.timer =',Chat.timer);
17661800
}
1767
- setTimeout( Chat.poll, Chat.timer.resetDelay() );
17681801
if( Chat.timer.errCount ){
17691802
D.removeClass(Chat.e.pollErrorMarker, 'connection-error');
17701803
Chat.timer.errCount = 0;
17711804
}
1772
- if( Chat.timer.tidReconnect ){
1773
- clearTimeout(Chat.timer.tidReconnect);
1774
- Chat.timer.tidReconnect = 0;
1775
- }
1805
+ Chat.timer.cancelReconnectCheckTimer().startPendingPollTimer();
17761806
if( Chat.e.eMsgPollError ) {
17771807
const oldErrMsg = Chat.e.eMsgPollError;
17781808
Chat.e.eMsgPollError = undefined;
17791809
if( showMsg ){
17801810
if(Chat.beVerbose){
@@ -2611,36 +2641,44 @@
26112641
}
26122642
}
26132643
);
26142644
}/*Chat.submitSearch()*/;
26152645
2616
- /* To be called from F.fetch('chat-poll') beforesend() handler. If
2617
- we're currently in delayed-retry mode and a connection is
2618
- started, try to reset the delay after N time waiting on that
2619
- connection. The fact that the connection is waiting to respond,
2620
- rather than outright failing, is a good hint that the outage is
2621
- over and we can reset the back-off timer.
2622
-
2623
- Without this, recovery of a connection error won't be reported
2624
- until after the long-poll completes by either receiving new
2625
- messages or times out. Once a long-poll is in progress, though,
2626
- we "know" that it's up and running again, so can update the UI
2627
- and connection timer to reflect that.
2646
+ /*
2647
+ To be called from F.fetch('chat-poll') beforesend() handler. If
2648
+ we're currently in delayed-retry mode and a connection is
2649
+ started, try to reset the delay after N time waiting on that
2650
+ connection. The fact that the connection is waiting to respond,
2651
+ rather than outright failing, is a good hint that the outage is
2652
+ over and we can reset the back-off timer.
2653
+
2654
+ Without this, recovery of a connection error won't be reported
2655
+ until after the long-poll completes by either receiving new
2656
+ messages or timing out. Once a long-poll is in progress, though,
2657
+ we "know" that it's up and running again, so can update the UI and
2658
+ connection timer to reflect that. That's the job this function
2659
+ does.
2660
+
2661
+ Only one of these asynchronous checks will ever be active
2662
+ concurrently and only if Chat.timer.isDelayed() is true. i.e. if
2663
+ this timer is active or Chat.timer.isDelayed() is false, this is a
2664
+ no-op.
26282665
*/
26292666
const chatPollBeforeSend = function(){
2630
- //console.warn('chatPollBeforeSend outer', Chat.timer.tidReconnect, Chat.timer.currentDelay);
2631
- if( !Chat.timer.tidReconnect && Chat.timer.isDelayed() ){
2632
- Chat.timer.tidReconnect = setTimeout(()=>{
2667
+ //console.warn('chatPollBeforeSend outer', Chat.timer.tidClearPollErr, Chat.timer.currentDelay);
2668
+ if( !Chat.timer.tidClearPollErr && Chat.timer.isDelayed() ){
2669
+ Chat.timer.tidClearPollErr = setTimeout(()=>{
26332670
//console.warn('chatPollBeforeSend inner');
2634
- Chat.timer.tidReconnect = 0;
2671
+ Chat.timer.tidClearPollErr = 0;
26352672
if( poll.running ){
26362673
/* This chat-poll F.fetch() is still underway, so let's
26372674
assume the connection is back up until/unless it times
26382675
out or breaks again. */
26392676
reportConnectionOkay('chatPollBeforeSend', true);
26402677
}
2641
- }, Chat.timer.$initialDelay * 3 );
2678
+ }, Chat.timer.$initialDelay * 4/*kinda arbitrary: not too long for UI wait and
2679
+ not too short as to make connection unlikely. */ );
26422680
}
26432681
};
26442682
26452683
/**
26462684
Deal with the last poll() response and maybe re-start poll().
@@ -2652,20 +2690,16 @@
26522690
Chat.e.viewMessages.classList.remove('loading');
26532691
setTimeout(function(){
26542692
Chat.scrollMessagesTo(1);
26552693
}, 250);
26562694
}
2657
- if(Chat.timer.tidPoller) {
2658
- clearTimeout(Chat.timer.tidPoller);
2659
- Chat.timer.tidPoller = 0;
2660
- }
2695
+ Chat.timer.cancelPendingPollTimer();
26612696
if(Chat._gotServerError){
26622697
Chat.reportErrorAsMessage(
26632698
"Shutting down chat poller due to server-side error. ",
26642699
"Reload this page to reactivate it."
26652700
);
2666
- Chat.timer.tidPoller = undefined;
26672701
} else {
26682702
if( err && Chat.beVerbose ){
26692703
console.error("afterPollFetch:",err.name,err.status,err.message);
26702704
}
26712705
if( !err || 'timeout'===err.name/*(probably) long-poll expired*/ ){
@@ -2701,42 +2735,44 @@
27012735
}
27022736
const theMsg = Chat.e.eMsgPollError = Chat.reportErrorAsMessage(msg);
27032737
D.addClass(Chat.e.eMsgPollError.e.body,'poller-connection');
27042738
/* Add a "retry now" button */
27052739
const btnDel = D.addClass(D.button("Retry now"), 'retry-now');
2706
- D.append(Chat.e.eMsgPollError.e.content, " ", btnDel);
2740
+ const eParent = Chat.e.eMsgPollError.e.content;
2741
+ D.append(eParent, " ", btnDel);
27072742
btnDel.addEventListener('click', function(){
27082743
D.remove(btnDel);
2709
- Chat.timer.currentDelay =
2710
- Chat.timer.resetDelay() + 1 /*workaround for showing the "connection restored" message*/;
2711
- if( Chat.timer.tidPoller ){
2712
- clearTimeout(Chat.timer.tidPoller);
2713
- Chat.timer.tidPoller = 0;
2714
- }
2744
+ D.append(eParent, D.text("retrying..."));
2745
+ Chat.timer.cancelPendingPollTimer().currentDelay =
2746
+ Chat.timer.resetDelay() +
2747
+ 1 /*workaround for showing the "connection restored"
2748
+ message, as the +1 will cause
2749
+ Chat.timer.isDelayed() to be true.*/;
27152750
poll();
27162751
});
27172752
//Chat.playNewMessageSound();// browser complains b/c this wasn't via human interaction
27182753
}
2719
- Chat.timer.tidPoller = setTimeout(Chat.poll, delay);
2754
+ Chat.timer.startPendingPollTimer(delay);
27202755
}
27212756
}
27222757
};
27232758
afterPollFetch.isFirstCall = true;
27242759
27252760
/**
27262761
Initiates, if it's not already running, a single long-poll
27272762
request to the /chat-poll endpoint. In the handling of that
27282763
response, it end up will psuedo-recursively calling itself via
2729
- the response-handling process.
2764
+ the response-handling process. Despite being async, the implied
2765
+ returned Promise is meaningless.
27302766
*/
27312767
const poll = Chat.poll = async function f(){
27322768
if(f.running) return;
27332769
f.running = true;
27342770
Chat._isBatchLoading = f.isFirstCall;
27352771
if(true===f.isFirstCall){
27362772
f.isFirstCall = false;
2737
- Chat.pendingOnError = undefined;
2773
+ f.pendingOnError = undefined;
27382774
Chat.ajaxStart();
27392775
Chat.e.viewMessages.classList.add('loading');
27402776
/*
27412777
We manager onerror() results in poll() in a roundabout
27422778
manner: when an onerror() arrives, we stash it aside
@@ -2757,18 +2793,17 @@
27572793
this one is a lot less explicable. (It's almost certainly a
27582794
mis-handling bug in F.fetch(), but it has so far eluded my
27592795
eyes.)
27602796
*/
27612797
f.delayPendingOnError = function(err){
2762
- if( Chat.pendingOnError ){
2763
- const x = Chat.pendingOnError;
2764
- Chat.pendingOnError = undefined;
2798
+ if( f.pendingOnError ){
2799
+ const x = f.pendingOnError;
2800
+ f.pendingOnError = undefined;
27652801
afterPollFetch(x);
27662802
}
27672803
};
27682804
}
2769
- let nErr = 0;
27702805
F.fetch("chat-poll",{
27712806
timeout: Chat.timer.pollTimeout,
27722807
urlParams:{
27732808
name: Chat.mxMsg
27742809
},
@@ -2777,20 +2812,20 @@
27772812
beforesend: chatPollBeforeSend,
27782813
aftersend: function(){
27792814
poll.running = false;
27802815
},
27812816
ontimeout: function(err){
2782
- Chat.pendingOnError = undefined /*strip preceeding non-timeout error, if any*/;
2817
+ f.pendingOnError = undefined /*strip preceeding non-timeout error, if any*/;
27832818
afterPollFetch(err);
27842819
},
27852820
onerror:function(err){
27862821
Chat._isBatchLoading = false;
27872822
if(Chat.beVerbose){
27882823
console.error("poll.onerror:",err.name,err.status,JSON.stringify(err));
27892824
}
2790
- Chat.pendingOnError = err;
2791
- setTimeout(f.delayPendingOnError, 250);
2825
+ f.pendingOnError = err;
2826
+ setTimeout(f.delayPendingOnError, 100);
27922827
},
27932828
onload:function(y){
27942829
reportConnectionOkay('poll.onload', true);
27952830
newcontent(y);
27962831
if(Chat._isBatchLoading){
@@ -2804,12 +2839,12 @@
28042839
poll.isFirstCall = true;
28052840
Chat._gotServerError = poll.running = false;
28062841
if( window.fossil.config.chat.fromcli ){
28072842
Chat.chatOnlyMode(true);
28082843
}
2809
- Chat.timer.tidPoller = setTimeout(poll, Chat.timer.resetDelay());
2844
+ Chat.timer.startPendingPollTimer();
28102845
delete ForceResizeKludge.$disabled;
28112846
ForceResizeKludge();
28122847
Chat.animate.$disabled = false;
28132848
setTimeout( ()=>Chat.inputFocus(), 0 );
28142849
F.page.chat = Chat/* enables testing the APIs via the dev tools */;
28152850
});
28162851
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -196,19 +196,23 @@
196 The timing of resetting the delay when service returns is,
197 because of the long-poll connection and our lack of low-level
198 insight into the connection at this level, a bit wonky.
199 */
200 timer:{
201 tidPoller: undefined /* setTimeout() poller timer id */,
 
 
 
 
 
 
202 $initialDelay: 1000 /* initial polling interval (ms) */,
203 currentDelay: 1000 /* current polling interval */,
204 maxDelay: 60000 * 5 /* max interval when backing off for
205 connection errors */,
206 minDelay: 5000 /* minimum delay time */,
207 tidReconnect: undefined /*setTimeout() timer id for
208 reconnection determination. See
209 clearPollErrOnWait(). */,
210 errCount: 0 /* Current poller connection error count */,
211 minErrForNotify: 4 /* Don't warn for connection errors until this
212 many have occurred */,
213 pollTimeout: (1 && window.location.hostname.match(
214 "localhost" /*presumably local dev mode*/
@@ -243,10 +247,40 @@
243 return this.currentDelay = ms || this.$initialDelay;
244 },
245 /** Returns true if the timer is set to delayed mode. */
246 isDelayed: function(){
247 return (this.currentDelay > this.$initialDelay) ? this.currentDelay : 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248 }
249 },
250 /**
251 Gets (no args) or sets (1 arg) the current input text field
252 value, taking into account single- vs multi-line input. The
@@ -1750,31 +1784,27 @@
1750 }));
1751 D.addClass(Chat.reportErrorAsMessage(w).e.body, "resend-message");
1752 };
1753
1754 /* Assume the connection has been established, reset the
1755 Chat.timer.tidReconnect, and (if showMsg and
1756 !!Chat.e.eMsgPollError) alert the user that the outage appears to
1757 be over. Also schedule Chat.poll() to run in the very near
1758 future. */
1759 const reportConnectionOkay = function(dbgContext, showMsg = true){
1760 if(Chat.beVerbose){
1761 console.warn('reportConnectionOkay', dbgContext,
1762 'Chat.e.pollErrorMarker classes =',
1763 Chat.e.pollErrorMarker.classList,
1764 'Chat.timer.tidReconnect =',Chat.timer.tidReconnect,
1765 'Chat.timer =',Chat.timer);
1766 }
1767 setTimeout( Chat.poll, Chat.timer.resetDelay() );
1768 if( Chat.timer.errCount ){
1769 D.removeClass(Chat.e.pollErrorMarker, 'connection-error');
1770 Chat.timer.errCount = 0;
1771 }
1772 if( Chat.timer.tidReconnect ){
1773 clearTimeout(Chat.timer.tidReconnect);
1774 Chat.timer.tidReconnect = 0;
1775 }
1776 if( Chat.e.eMsgPollError ) {
1777 const oldErrMsg = Chat.e.eMsgPollError;
1778 Chat.e.eMsgPollError = undefined;
1779 if( showMsg ){
1780 if(Chat.beVerbose){
@@ -2611,36 +2641,44 @@
2611 }
2612 }
2613 );
2614 }/*Chat.submitSearch()*/;
2615
2616 /* To be called from F.fetch('chat-poll') beforesend() handler. If
2617 we're currently in delayed-retry mode and a connection is
2618 started, try to reset the delay after N time waiting on that
2619 connection. The fact that the connection is waiting to respond,
2620 rather than outright failing, is a good hint that the outage is
2621 over and we can reset the back-off timer.
2622
2623 Without this, recovery of a connection error won't be reported
2624 until after the long-poll completes by either receiving new
2625 messages or times out. Once a long-poll is in progress, though,
2626 we "know" that it's up and running again, so can update the UI
2627 and connection timer to reflect that.
 
 
 
 
 
 
 
2628 */
2629 const chatPollBeforeSend = function(){
2630 //console.warn('chatPollBeforeSend outer', Chat.timer.tidReconnect, Chat.timer.currentDelay);
2631 if( !Chat.timer.tidReconnect && Chat.timer.isDelayed() ){
2632 Chat.timer.tidReconnect = setTimeout(()=>{
2633 //console.warn('chatPollBeforeSend inner');
2634 Chat.timer.tidReconnect = 0;
2635 if( poll.running ){
2636 /* This chat-poll F.fetch() is still underway, so let's
2637 assume the connection is back up until/unless it times
2638 out or breaks again. */
2639 reportConnectionOkay('chatPollBeforeSend', true);
2640 }
2641 }, Chat.timer.$initialDelay * 3 );
 
2642 }
2643 };
2644
2645 /**
2646 Deal with the last poll() response and maybe re-start poll().
@@ -2652,20 +2690,16 @@
2652 Chat.e.viewMessages.classList.remove('loading');
2653 setTimeout(function(){
2654 Chat.scrollMessagesTo(1);
2655 }, 250);
2656 }
2657 if(Chat.timer.tidPoller) {
2658 clearTimeout(Chat.timer.tidPoller);
2659 Chat.timer.tidPoller = 0;
2660 }
2661 if(Chat._gotServerError){
2662 Chat.reportErrorAsMessage(
2663 "Shutting down chat poller due to server-side error. ",
2664 "Reload this page to reactivate it."
2665 );
2666 Chat.timer.tidPoller = undefined;
2667 } else {
2668 if( err && Chat.beVerbose ){
2669 console.error("afterPollFetch:",err.name,err.status,err.message);
2670 }
2671 if( !err || 'timeout'===err.name/*(probably) long-poll expired*/ ){
@@ -2701,42 +2735,44 @@
2701 }
2702 const theMsg = Chat.e.eMsgPollError = Chat.reportErrorAsMessage(msg);
2703 D.addClass(Chat.e.eMsgPollError.e.body,'poller-connection');
2704 /* Add a "retry now" button */
2705 const btnDel = D.addClass(D.button("Retry now"), 'retry-now');
2706 D.append(Chat.e.eMsgPollError.e.content, " ", btnDel);
 
2707 btnDel.addEventListener('click', function(){
2708 D.remove(btnDel);
2709 Chat.timer.currentDelay =
2710 Chat.timer.resetDelay() + 1 /*workaround for showing the "connection restored" message*/;
2711 if( Chat.timer.tidPoller ){
2712 clearTimeout(Chat.timer.tidPoller);
2713 Chat.timer.tidPoller = 0;
2714 }
2715 poll();
2716 });
2717 //Chat.playNewMessageSound();// browser complains b/c this wasn't via human interaction
2718 }
2719 Chat.timer.tidPoller = setTimeout(Chat.poll, delay);
2720 }
2721 }
2722 };
2723 afterPollFetch.isFirstCall = true;
2724
2725 /**
2726 Initiates, if it's not already running, a single long-poll
2727 request to the /chat-poll endpoint. In the handling of that
2728 response, it end up will psuedo-recursively calling itself via
2729 the response-handling process.
 
2730 */
2731 const poll = Chat.poll = async function f(){
2732 if(f.running) return;
2733 f.running = true;
2734 Chat._isBatchLoading = f.isFirstCall;
2735 if(true===f.isFirstCall){
2736 f.isFirstCall = false;
2737 Chat.pendingOnError = undefined;
2738 Chat.ajaxStart();
2739 Chat.e.viewMessages.classList.add('loading');
2740 /*
2741 We manager onerror() results in poll() in a roundabout
2742 manner: when an onerror() arrives, we stash it aside
@@ -2757,18 +2793,17 @@
2757 this one is a lot less explicable. (It's almost certainly a
2758 mis-handling bug in F.fetch(), but it has so far eluded my
2759 eyes.)
2760 */
2761 f.delayPendingOnError = function(err){
2762 if( Chat.pendingOnError ){
2763 const x = Chat.pendingOnError;
2764 Chat.pendingOnError = undefined;
2765 afterPollFetch(x);
2766 }
2767 };
2768 }
2769 let nErr = 0;
2770 F.fetch("chat-poll",{
2771 timeout: Chat.timer.pollTimeout,
2772 urlParams:{
2773 name: Chat.mxMsg
2774 },
@@ -2777,20 +2812,20 @@
2777 beforesend: chatPollBeforeSend,
2778 aftersend: function(){
2779 poll.running = false;
2780 },
2781 ontimeout: function(err){
2782 Chat.pendingOnError = undefined /*strip preceeding non-timeout error, if any*/;
2783 afterPollFetch(err);
2784 },
2785 onerror:function(err){
2786 Chat._isBatchLoading = false;
2787 if(Chat.beVerbose){
2788 console.error("poll.onerror:",err.name,err.status,JSON.stringify(err));
2789 }
2790 Chat.pendingOnError = err;
2791 setTimeout(f.delayPendingOnError, 250);
2792 },
2793 onload:function(y){
2794 reportConnectionOkay('poll.onload', true);
2795 newcontent(y);
2796 if(Chat._isBatchLoading){
@@ -2804,12 +2839,12 @@
2804 poll.isFirstCall = true;
2805 Chat._gotServerError = poll.running = false;
2806 if( window.fossil.config.chat.fromcli ){
2807 Chat.chatOnlyMode(true);
2808 }
2809 Chat.timer.tidPoller = setTimeout(poll, Chat.timer.resetDelay());
2810 delete ForceResizeKludge.$disabled;
2811 ForceResizeKludge();
2812 Chat.animate.$disabled = false;
2813 setTimeout( ()=>Chat.inputFocus(), 0 );
2814 F.page.chat = Chat/* enables testing the APIs via the dev tools */;
2815 });
2816
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -196,19 +196,23 @@
196 The timing of resetting the delay when service returns is,
197 because of the long-poll connection and our lack of low-level
198 insight into the connection at this level, a bit wonky.
199 */
200 timer:{
201 /* setTimeout() ID for (delayed) starting a Chat.poll(), so
202 that it runs at controlled intervals (which change when a
203 connection drops and recovers). */
204 tidPendingPoll: undefined,
205 tidClearPollErr: undefined /*setTimeout() timer id for
206 reconnection determination. See
207 clearPollErrOnWait(). */,
208 $initialDelay: 1000 /* initial polling interval (ms) */,
209 currentDelay: 1000 /* current polling interval */,
210 maxDelay: 60000 * 5 /* max interval when backing off for
211 connection errors */,
212 minDelay: 5000 /* minimum delay time for a back-off/retry
213 attempt. */,
 
 
214 errCount: 0 /* Current poller connection error count */,
215 minErrForNotify: 4 /* Don't warn for connection errors until this
216 many have occurred */,
217 pollTimeout: (1 && window.location.hostname.match(
218 "localhost" /*presumably local dev mode*/
@@ -243,10 +247,40 @@
247 return this.currentDelay = ms || this.$initialDelay;
248 },
249 /** Returns true if the timer is set to delayed mode. */
250 isDelayed: function(){
251 return (this.currentDelay > this.$initialDelay) ? this.currentDelay : 0;
252 },
253 /**
254 Cancels any in-progress pending-poll timer and starts a new
255 one with the given delay, defaulting to this.resetDelay().
256 */
257 startPendingPollTimer: function(delay){
258 this.cancelPendingPollTimer().tidPendingPoll
259 = setTimeout( Chat.poll, delay || Chat.timer.resetDelay() );
260 return this;
261 },
262 /**
263 Cancels any still-active timer set to trigger the next
264 Chat.poll().
265 */
266 cancelPendingPollTimer: function(){
267 if( this.tidPendingPoll ){
268 clearTimeout(this.tidPendingPoll);
269 this.tidPendingPoll = 0;
270 }
271 return this;
272 },
273 /**
274 Cancels any pending reconnection attempt back-off timer..
275 */
276 cancelReconnectCheckTimer: function(){
277 if( this.tidClearPollErr ){
278 clearTimeout(this.tidClearPollErr);
279 this.tidClearPollErr = 0;
280 }
281 return this;
282 }
283 },
284 /**
285 Gets (no args) or sets (1 arg) the current input text field
286 value, taking into account single- vs multi-line input. The
@@ -1750,31 +1784,27 @@
1784 }));
1785 D.addClass(Chat.reportErrorAsMessage(w).e.body, "resend-message");
1786 };
1787
1788 /* Assume the connection has been established, reset the
1789 Chat.timer.tidClearPollErr, and (if showMsg and
1790 !!Chat.e.eMsgPollError) alert the user that the outage appears to
1791 be over. Also schedule Chat.poll() to run in the very near
1792 future. */
1793 const reportConnectionOkay = function(dbgContext, showMsg = true){
1794 if(Chat.beVerbose){
1795 console.warn('reportConnectionOkay', dbgContext,
1796 'Chat.e.pollErrorMarker classes =',
1797 Chat.e.pollErrorMarker.classList,
1798 'Chat.timer.tidClearPollErr =',Chat.timer.tidClearPollErr,
1799 'Chat.timer =',Chat.timer);
1800 }
 
1801 if( Chat.timer.errCount ){
1802 D.removeClass(Chat.e.pollErrorMarker, 'connection-error');
1803 Chat.timer.errCount = 0;
1804 }
1805 Chat.timer.cancelReconnectCheckTimer().startPendingPollTimer();
 
 
 
1806 if( Chat.e.eMsgPollError ) {
1807 const oldErrMsg = Chat.e.eMsgPollError;
1808 Chat.e.eMsgPollError = undefined;
1809 if( showMsg ){
1810 if(Chat.beVerbose){
@@ -2611,36 +2641,44 @@
2641 }
2642 }
2643 );
2644 }/*Chat.submitSearch()*/;
2645
2646 /*
2647 To be called from F.fetch('chat-poll') beforesend() handler. If
2648 we're currently in delayed-retry mode and a connection is
2649 started, try to reset the delay after N time waiting on that
2650 connection. The fact that the connection is waiting to respond,
2651 rather than outright failing, is a good hint that the outage is
2652 over and we can reset the back-off timer.
2653
2654 Without this, recovery of a connection error won't be reported
2655 until after the long-poll completes by either receiving new
2656 messages or timing out. Once a long-poll is in progress, though,
2657 we "know" that it's up and running again, so can update the UI and
2658 connection timer to reflect that. That's the job this function
2659 does.
2660
2661 Only one of these asynchronous checks will ever be active
2662 concurrently and only if Chat.timer.isDelayed() is true. i.e. if
2663 this timer is active or Chat.timer.isDelayed() is false, this is a
2664 no-op.
2665 */
2666 const chatPollBeforeSend = function(){
2667 //console.warn('chatPollBeforeSend outer', Chat.timer.tidClearPollErr, Chat.timer.currentDelay);
2668 if( !Chat.timer.tidClearPollErr && Chat.timer.isDelayed() ){
2669 Chat.timer.tidClearPollErr = setTimeout(()=>{
2670 //console.warn('chatPollBeforeSend inner');
2671 Chat.timer.tidClearPollErr = 0;
2672 if( poll.running ){
2673 /* This chat-poll F.fetch() is still underway, so let's
2674 assume the connection is back up until/unless it times
2675 out or breaks again. */
2676 reportConnectionOkay('chatPollBeforeSend', true);
2677 }
2678 }, Chat.timer.$initialDelay * 4/*kinda arbitrary: not too long for UI wait and
2679 not too short as to make connection unlikely. */ );
2680 }
2681 };
2682
2683 /**
2684 Deal with the last poll() response and maybe re-start poll().
@@ -2652,20 +2690,16 @@
2690 Chat.e.viewMessages.classList.remove('loading');
2691 setTimeout(function(){
2692 Chat.scrollMessagesTo(1);
2693 }, 250);
2694 }
2695 Chat.timer.cancelPendingPollTimer();
 
 
 
2696 if(Chat._gotServerError){
2697 Chat.reportErrorAsMessage(
2698 "Shutting down chat poller due to server-side error. ",
2699 "Reload this page to reactivate it."
2700 );
 
2701 } else {
2702 if( err && Chat.beVerbose ){
2703 console.error("afterPollFetch:",err.name,err.status,err.message);
2704 }
2705 if( !err || 'timeout'===err.name/*(probably) long-poll expired*/ ){
@@ -2701,42 +2735,44 @@
2735 }
2736 const theMsg = Chat.e.eMsgPollError = Chat.reportErrorAsMessage(msg);
2737 D.addClass(Chat.e.eMsgPollError.e.body,'poller-connection');
2738 /* Add a "retry now" button */
2739 const btnDel = D.addClass(D.button("Retry now"), 'retry-now');
2740 const eParent = Chat.e.eMsgPollError.e.content;
2741 D.append(eParent, " ", btnDel);
2742 btnDel.addEventListener('click', function(){
2743 D.remove(btnDel);
2744 D.append(eParent, D.text("retrying..."));
2745 Chat.timer.cancelPendingPollTimer().currentDelay =
2746 Chat.timer.resetDelay() +
2747 1 /*workaround for showing the "connection restored"
2748 message, as the +1 will cause
2749 Chat.timer.isDelayed() to be true.*/;
2750 poll();
2751 });
2752 //Chat.playNewMessageSound();// browser complains b/c this wasn't via human interaction
2753 }
2754 Chat.timer.startPendingPollTimer(delay);
2755 }
2756 }
2757 };
2758 afterPollFetch.isFirstCall = true;
2759
2760 /**
2761 Initiates, if it's not already running, a single long-poll
2762 request to the /chat-poll endpoint. In the handling of that
2763 response, it end up will psuedo-recursively calling itself via
2764 the response-handling process. Despite being async, the implied
2765 returned Promise is meaningless.
2766 */
2767 const poll = Chat.poll = async function f(){
2768 if(f.running) return;
2769 f.running = true;
2770 Chat._isBatchLoading = f.isFirstCall;
2771 if(true===f.isFirstCall){
2772 f.isFirstCall = false;
2773 f.pendingOnError = undefined;
2774 Chat.ajaxStart();
2775 Chat.e.viewMessages.classList.add('loading');
2776 /*
2777 We manager onerror() results in poll() in a roundabout
2778 manner: when an onerror() arrives, we stash it aside
@@ -2757,18 +2793,17 @@
2793 this one is a lot less explicable. (It's almost certainly a
2794 mis-handling bug in F.fetch(), but it has so far eluded my
2795 eyes.)
2796 */
2797 f.delayPendingOnError = function(err){
2798 if( f.pendingOnError ){
2799 const x = f.pendingOnError;
2800 f.pendingOnError = undefined;
2801 afterPollFetch(x);
2802 }
2803 };
2804 }
 
2805 F.fetch("chat-poll",{
2806 timeout: Chat.timer.pollTimeout,
2807 urlParams:{
2808 name: Chat.mxMsg
2809 },
@@ -2777,20 +2812,20 @@
2812 beforesend: chatPollBeforeSend,
2813 aftersend: function(){
2814 poll.running = false;
2815 },
2816 ontimeout: function(err){
2817 f.pendingOnError = undefined /*strip preceeding non-timeout error, if any*/;
2818 afterPollFetch(err);
2819 },
2820 onerror:function(err){
2821 Chat._isBatchLoading = false;
2822 if(Chat.beVerbose){
2823 console.error("poll.onerror:",err.name,err.status,JSON.stringify(err));
2824 }
2825 f.pendingOnError = err;
2826 setTimeout(f.delayPendingOnError, 100);
2827 },
2828 onload:function(y){
2829 reportConnectionOkay('poll.onload', true);
2830 newcontent(y);
2831 if(Chat._isBatchLoading){
@@ -2804,12 +2839,12 @@
2839 poll.isFirstCall = true;
2840 Chat._gotServerError = poll.running = false;
2841 if( window.fossil.config.chat.fromcli ){
2842 Chat.chatOnlyMode(true);
2843 }
2844 Chat.timer.startPendingPollTimer();
2845 delete ForceResizeKludge.$disabled;
2846 ForceResizeKludge();
2847 Chat.animate.$disabled = false;
2848 setTimeout( ()=>Chat.inputFocus(), 0 );
2849 F.page.chat = Chat/* enables testing the APIs via the dev tools */;
2850 });
2851

Keyboard Shortcuts

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