Fossil SCM
Some flicker reduction when batch loading chat messages. Minor chat layout tweaks.
Commit
5e046b64c73fced861b93dce559545e8ea9e9f2df5188b8aa1e09773056c03d7
Parent
23d6b4570a8752b…
2 files changed
+19
-11
+5
-1
+19
-11
| --- src/chat.js | ||
| +++ src/chat.js | ||
| @@ -206,20 +206,20 @@ | ||
| 206 | 206 | else D.append(mip.parentNode, e); |
| 207 | 207 | }else{ |
| 208 | 208 | D.append(holder,e); |
| 209 | 209 | this.e.newestMessage = e; |
| 210 | 210 | } |
| 211 | - if(!atEnd && !this.isMassLoading | |
| 211 | + if(!atEnd && !this.isBatchLoading | |
| 212 | 212 | && e.dataset.xfrom!==this.me && !isInViewport(e)){ |
| 213 | 213 | /* If a new non-history message arrives while the user is |
| 214 | 214 | scrolled elsewhere, do not scroll to the latest |
| 215 | 215 | message, but gently alert the user that a new message |
| 216 | 216 | has arrived. */ |
| 217 | 217 | F.toast.message("New message has arrived."); |
| 218 | - }else if(!this.isMassLoading && e.dataset.xfrom===Chat.me){ | |
| 218 | + }else if(!this.isBatchLoading && e.dataset.xfrom===Chat.me){ | |
| 219 | 219 | this.scheduleScrollOfMsg(e); |
| 220 | - }else if(!this.isMassLoading){ | |
| 220 | + }else if(!this.isBatchLoading){ | |
| 221 | 221 | /* When a message from someone else arrives, we have to |
| 222 | 222 | figure out whether or not to scroll it into view. Ideally |
| 223 | 223 | we'd just stuff it in the UI and let the flexbox layout |
| 224 | 224 | DTRT, but Safari has expressed, in no uncertain terms, |
| 225 | 225 | some disappointment with that approach, so we'll |
| @@ -289,11 +289,11 @@ | ||
| 289 | 289 | if(where<0){ |
| 290 | 290 | Chat.e.messagesWrapper.scrollTop = 0; |
| 291 | 291 | }else if(where>0){ |
| 292 | 292 | Chat.e.messagesWrapper.scrollTop = Chat.e.messagesWrapper.scrollHeight; |
| 293 | 293 | }else if(Chat.e.newestMessage){ |
| 294 | - Chat.e.newestMessage.scrollIntoView(); | |
| 294 | + Chat.e.newestMessage.scrollIntoView(false); | |
| 295 | 295 | } |
| 296 | 296 | }, |
| 297 | 297 | toggleChatOnlyMode: function(){ |
| 298 | 298 | return this.chatOnlyMode(!this.isChatOnlyMode()); |
| 299 | 299 | }, |
| @@ -918,23 +918,27 @@ | ||
| 918 | 918 | ); |
| 919 | 919 | Chat.disableDuringAjax.push(toolbar); |
| 920 | 920 | /* Loads the next n oldest messages, or all previous history if n is negative. */ |
| 921 | 921 | const loadOldMessages = function(n){ |
| 922 | 922 | Chat.ajaxStart(); |
| 923 | + Chat.e.messagesWrapper.classList.add('loading'); | |
| 924 | + Chat.isBatchLoading = true; | |
| 923 | 925 | var gotMessages = false; |
| 924 | 926 | fetch("chat-poll?before="+Chat.mnMsg+"&n="+n) |
| 925 | 927 | .then(x=>x.json()) |
| 926 | 928 | .then(function(x){ |
| 927 | 929 | gotMessages = x.msgs.length; |
| 928 | 930 | newcontent(x,true); |
| 929 | 931 | }) |
| 930 | 932 | .catch(e=>Chat.reportError(e)) |
| 931 | 933 | .finally(function(){ |
| 934 | + Chat.isBatchLoading = false; | |
| 935 | + Chat.e.messagesWrapper.classList.remove('loading'); | |
| 932 | 936 | if(n<0/*we asked for all history*/ |
| 933 | 937 | || 0===gotMessages/*we found no history*/ |
| 934 | 938 | || (n>0 && gotMessages<n /*we got fewer history entries than requested*/) |
| 935 | - || (false!==gotMessages && n<0 && gotMessages<Chat.loadMessageCount | |
| 939 | + || (false!==gotMessages && n===0 && gotMessages<Chat.loadMessageCount | |
| 936 | 940 | /*we asked for default amount and got fewer than that.*/)){ |
| 937 | 941 | /* We've loaded all history. Permanently disable the |
| 938 | 942 | history-load toolbar and keep it from being re-enabled |
| 939 | 943 | via the ajaxStart()/ajaxEnd() mechanism... */ |
| 940 | 944 | const div = Chat.e.loadOlderToolbar.querySelector('div'); |
| @@ -963,30 +967,34 @@ | ||
| 963 | 967 | })()/*end history loading widget setup*/; |
| 964 | 968 | |
| 965 | 969 | async function poll(isFirstCall){ |
| 966 | 970 | if(poll.running) return; |
| 967 | 971 | poll.running = true; |
| 968 | - if(isFirstCall) Chat.ajaxStart(); | |
| 969 | - Chat.isMassLoading = isFirstCall; | |
| 972 | + if(isFirstCall){ | |
| 973 | + Chat.ajaxStart(); | |
| 974 | + Chat.e.messagesWrapper.classList.add('loading'); | |
| 975 | + } | |
| 976 | + Chat.isBatchLoading = isFirstCall; | |
| 970 | 977 | var p = fetch("chat-poll?name=" + Chat.mxMsg); |
| 971 | 978 | p.then(x=>x.json()) |
| 972 | 979 | .then(y=>newcontent(y)) |
| 973 | 980 | .catch(e=>console.error(e)) |
| 974 | 981 | /* ^^^ we don't use Chat.reportError(e) here b/c the polling |
| 975 | 982 | fails exepectedly when it times out, but is then immediately |
| 976 | 983 | resumed, and reportError() produces a loud error message. */ |
| 977 | 984 | .finally(function(){ |
| 978 | 985 | if(isFirstCall){ |
| 979 | - Chat.isMassLoading = false; | |
| 986 | + Chat.isBatchLoading = false; | |
| 980 | 987 | Chat.ajaxEnd(); |
| 981 | - const m = Chat.e.newestMessage; | |
| 982 | - if(m) Chat.scheduleScrollOfMsg(m); | |
| 983 | - setTimeout(()=>Chat.e.inputWrapper.scrollIntoView(), 0); | |
| 988 | + setTimeout(function(){ | |
| 989 | + Chat.scrollMessagesTo(1); | |
| 990 | + Chat.e.messagesWrapper.classList.remove('loading'); | |
| 991 | + }, 250); | |
| 984 | 992 | } |
| 985 | 993 | poll.running=false; |
| 986 | 994 | }); |
| 987 | 995 | } |
| 988 | 996 | poll.running = false; |
| 989 | 997 | poll(true); |
| 990 | 998 | setInterval(poll, 1000); |
| 991 | 999 | F.page.chat = Chat/* enables testing the APIs via the dev tools */; |
| 992 | 1000 | })(); |
| 993 | 1001 |
| --- src/chat.js | |
| +++ src/chat.js | |
| @@ -206,20 +206,20 @@ | |
| 206 | else D.append(mip.parentNode, e); |
| 207 | }else{ |
| 208 | D.append(holder,e); |
| 209 | this.e.newestMessage = e; |
| 210 | } |
| 211 | if(!atEnd && !this.isMassLoading |
| 212 | && e.dataset.xfrom!==this.me && !isInViewport(e)){ |
| 213 | /* If a new non-history message arrives while the user is |
| 214 | scrolled elsewhere, do not scroll to the latest |
| 215 | message, but gently alert the user that a new message |
| 216 | has arrived. */ |
| 217 | F.toast.message("New message has arrived."); |
| 218 | }else if(!this.isMassLoading && e.dataset.xfrom===Chat.me){ |
| 219 | this.scheduleScrollOfMsg(e); |
| 220 | }else if(!this.isMassLoading){ |
| 221 | /* When a message from someone else arrives, we have to |
| 222 | figure out whether or not to scroll it into view. Ideally |
| 223 | we'd just stuff it in the UI and let the flexbox layout |
| 224 | DTRT, but Safari has expressed, in no uncertain terms, |
| 225 | some disappointment with that approach, so we'll |
| @@ -289,11 +289,11 @@ | |
| 289 | if(where<0){ |
| 290 | Chat.e.messagesWrapper.scrollTop = 0; |
| 291 | }else if(where>0){ |
| 292 | Chat.e.messagesWrapper.scrollTop = Chat.e.messagesWrapper.scrollHeight; |
| 293 | }else if(Chat.e.newestMessage){ |
| 294 | Chat.e.newestMessage.scrollIntoView(); |
| 295 | } |
| 296 | }, |
| 297 | toggleChatOnlyMode: function(){ |
| 298 | return this.chatOnlyMode(!this.isChatOnlyMode()); |
| 299 | }, |
| @@ -918,23 +918,27 @@ | |
| 918 | ); |
| 919 | Chat.disableDuringAjax.push(toolbar); |
| 920 | /* Loads the next n oldest messages, or all previous history if n is negative. */ |
| 921 | const loadOldMessages = function(n){ |
| 922 | Chat.ajaxStart(); |
| 923 | var gotMessages = false; |
| 924 | fetch("chat-poll?before="+Chat.mnMsg+"&n="+n) |
| 925 | .then(x=>x.json()) |
| 926 | .then(function(x){ |
| 927 | gotMessages = x.msgs.length; |
| 928 | newcontent(x,true); |
| 929 | }) |
| 930 | .catch(e=>Chat.reportError(e)) |
| 931 | .finally(function(){ |
| 932 | if(n<0/*we asked for all history*/ |
| 933 | || 0===gotMessages/*we found no history*/ |
| 934 | || (n>0 && gotMessages<n /*we got fewer history entries than requested*/) |
| 935 | || (false!==gotMessages && n<0 && gotMessages<Chat.loadMessageCount |
| 936 | /*we asked for default amount and got fewer than that.*/)){ |
| 937 | /* We've loaded all history. Permanently disable the |
| 938 | history-load toolbar and keep it from being re-enabled |
| 939 | via the ajaxStart()/ajaxEnd() mechanism... */ |
| 940 | const div = Chat.e.loadOlderToolbar.querySelector('div'); |
| @@ -963,30 +967,34 @@ | |
| 963 | })()/*end history loading widget setup*/; |
| 964 | |
| 965 | async function poll(isFirstCall){ |
| 966 | if(poll.running) return; |
| 967 | poll.running = true; |
| 968 | if(isFirstCall) Chat.ajaxStart(); |
| 969 | Chat.isMassLoading = isFirstCall; |
| 970 | var p = fetch("chat-poll?name=" + Chat.mxMsg); |
| 971 | p.then(x=>x.json()) |
| 972 | .then(y=>newcontent(y)) |
| 973 | .catch(e=>console.error(e)) |
| 974 | /* ^^^ we don't use Chat.reportError(e) here b/c the polling |
| 975 | fails exepectedly when it times out, but is then immediately |
| 976 | resumed, and reportError() produces a loud error message. */ |
| 977 | .finally(function(){ |
| 978 | if(isFirstCall){ |
| 979 | Chat.isMassLoading = false; |
| 980 | Chat.ajaxEnd(); |
| 981 | const m = Chat.e.newestMessage; |
| 982 | if(m) Chat.scheduleScrollOfMsg(m); |
| 983 | setTimeout(()=>Chat.e.inputWrapper.scrollIntoView(), 0); |
| 984 | } |
| 985 | poll.running=false; |
| 986 | }); |
| 987 | } |
| 988 | poll.running = false; |
| 989 | poll(true); |
| 990 | setInterval(poll, 1000); |
| 991 | F.page.chat = Chat/* enables testing the APIs via the dev tools */; |
| 992 | })(); |
| 993 |
| --- src/chat.js | |
| +++ src/chat.js | |
| @@ -206,20 +206,20 @@ | |
| 206 | else D.append(mip.parentNode, e); |
| 207 | }else{ |
| 208 | D.append(holder,e); |
| 209 | this.e.newestMessage = e; |
| 210 | } |
| 211 | if(!atEnd && !this.isBatchLoading |
| 212 | && e.dataset.xfrom!==this.me && !isInViewport(e)){ |
| 213 | /* If a new non-history message arrives while the user is |
| 214 | scrolled elsewhere, do not scroll to the latest |
| 215 | message, but gently alert the user that a new message |
| 216 | has arrived. */ |
| 217 | F.toast.message("New message has arrived."); |
| 218 | }else if(!this.isBatchLoading && e.dataset.xfrom===Chat.me){ |
| 219 | this.scheduleScrollOfMsg(e); |
| 220 | }else if(!this.isBatchLoading){ |
| 221 | /* When a message from someone else arrives, we have to |
| 222 | figure out whether or not to scroll it into view. Ideally |
| 223 | we'd just stuff it in the UI and let the flexbox layout |
| 224 | DTRT, but Safari has expressed, in no uncertain terms, |
| 225 | some disappointment with that approach, so we'll |
| @@ -289,11 +289,11 @@ | |
| 289 | if(where<0){ |
| 290 | Chat.e.messagesWrapper.scrollTop = 0; |
| 291 | }else if(where>0){ |
| 292 | Chat.e.messagesWrapper.scrollTop = Chat.e.messagesWrapper.scrollHeight; |
| 293 | }else if(Chat.e.newestMessage){ |
| 294 | Chat.e.newestMessage.scrollIntoView(false); |
| 295 | } |
| 296 | }, |
| 297 | toggleChatOnlyMode: function(){ |
| 298 | return this.chatOnlyMode(!this.isChatOnlyMode()); |
| 299 | }, |
| @@ -918,23 +918,27 @@ | |
| 918 | ); |
| 919 | Chat.disableDuringAjax.push(toolbar); |
| 920 | /* Loads the next n oldest messages, or all previous history if n is negative. */ |
| 921 | const loadOldMessages = function(n){ |
| 922 | Chat.ajaxStart(); |
| 923 | Chat.e.messagesWrapper.classList.add('loading'); |
| 924 | Chat.isBatchLoading = true; |
| 925 | var gotMessages = false; |
| 926 | fetch("chat-poll?before="+Chat.mnMsg+"&n="+n) |
| 927 | .then(x=>x.json()) |
| 928 | .then(function(x){ |
| 929 | gotMessages = x.msgs.length; |
| 930 | newcontent(x,true); |
| 931 | }) |
| 932 | .catch(e=>Chat.reportError(e)) |
| 933 | .finally(function(){ |
| 934 | Chat.isBatchLoading = false; |
| 935 | Chat.e.messagesWrapper.classList.remove('loading'); |
| 936 | if(n<0/*we asked for all history*/ |
| 937 | || 0===gotMessages/*we found no history*/ |
| 938 | || (n>0 && gotMessages<n /*we got fewer history entries than requested*/) |
| 939 | || (false!==gotMessages && n===0 && gotMessages<Chat.loadMessageCount |
| 940 | /*we asked for default amount and got fewer than that.*/)){ |
| 941 | /* We've loaded all history. Permanently disable the |
| 942 | history-load toolbar and keep it from being re-enabled |
| 943 | via the ajaxStart()/ajaxEnd() mechanism... */ |
| 944 | const div = Chat.e.loadOlderToolbar.querySelector('div'); |
| @@ -963,30 +967,34 @@ | |
| 967 | })()/*end history loading widget setup*/; |
| 968 | |
| 969 | async function poll(isFirstCall){ |
| 970 | if(poll.running) return; |
| 971 | poll.running = true; |
| 972 | if(isFirstCall){ |
| 973 | Chat.ajaxStart(); |
| 974 | Chat.e.messagesWrapper.classList.add('loading'); |
| 975 | } |
| 976 | Chat.isBatchLoading = isFirstCall; |
| 977 | var p = fetch("chat-poll?name=" + Chat.mxMsg); |
| 978 | p.then(x=>x.json()) |
| 979 | .then(y=>newcontent(y)) |
| 980 | .catch(e=>console.error(e)) |
| 981 | /* ^^^ we don't use Chat.reportError(e) here b/c the polling |
| 982 | fails exepectedly when it times out, but is then immediately |
| 983 | resumed, and reportError() produces a loud error message. */ |
| 984 | .finally(function(){ |
| 985 | if(isFirstCall){ |
| 986 | Chat.isBatchLoading = false; |
| 987 | Chat.ajaxEnd(); |
| 988 | setTimeout(function(){ |
| 989 | Chat.scrollMessagesTo(1); |
| 990 | Chat.e.messagesWrapper.classList.remove('loading'); |
| 991 | }, 250); |
| 992 | } |
| 993 | poll.running=false; |
| 994 | }); |
| 995 | } |
| 996 | poll.running = false; |
| 997 | poll(true); |
| 998 | setInterval(poll, 1000); |
| 999 | F.page.chat = Chat/* enables testing the APIs via the dev tools */; |
| 1000 | })(); |
| 1001 |
+5
-1
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -1639,10 +1639,14 @@ | ||
| 1639 | 1639 | /** Container for the list of /chat messages. */ |
| 1640 | 1640 | body.chat #chat-messages-wrapper { |
| 1641 | 1641 | overflow: auto; |
| 1642 | 1642 | /*max-height: 800em*//*will be re-calc'd in JS*/; |
| 1643 | 1643 | flex: 2 1 auto; |
| 1644 | +} | |
| 1645 | +body.chat #chat-messages-wrapper.loading > * { | |
| 1646 | + /* An attempt at reducing flicker when loading lots of messages. */ | |
| 1647 | + visibility: hidden; | |
| 1644 | 1648 | } |
| 1645 | 1649 | body.chat div.content { |
| 1646 | 1650 | margin: 0; |
| 1647 | 1651 | padding: 0; |
| 1648 | 1652 | display: flex; |
| @@ -1678,11 +1682,11 @@ | ||
| 1678 | 1682 | settings button. */ |
| 1679 | 1683 | body.chat #chat-input-line { |
| 1680 | 1684 | display: flex; |
| 1681 | 1685 | flex-direction: row; |
| 1682 | 1686 | margin-bottom: 0.25em; |
| 1683 | - align-items: flex-start; | |
| 1687 | + align-items: center; | |
| 1684 | 1688 | } |
| 1685 | 1689 | body.chat #chat-input-line > input[type=submit], |
| 1686 | 1690 | body.chat #chat-input-line > #chat-settings-button, |
| 1687 | 1691 | body.chat #chat-input-line > button { |
| 1688 | 1692 | flex: 1 5 auto; |
| 1689 | 1693 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -1639,10 +1639,14 @@ | |
| 1639 | /** Container for the list of /chat messages. */ |
| 1640 | body.chat #chat-messages-wrapper { |
| 1641 | overflow: auto; |
| 1642 | /*max-height: 800em*//*will be re-calc'd in JS*/; |
| 1643 | flex: 2 1 auto; |
| 1644 | } |
| 1645 | body.chat div.content { |
| 1646 | margin: 0; |
| 1647 | padding: 0; |
| 1648 | display: flex; |
| @@ -1678,11 +1682,11 @@ | |
| 1678 | settings button. */ |
| 1679 | body.chat #chat-input-line { |
| 1680 | display: flex; |
| 1681 | flex-direction: row; |
| 1682 | margin-bottom: 0.25em; |
| 1683 | align-items: flex-start; |
| 1684 | } |
| 1685 | body.chat #chat-input-line > input[type=submit], |
| 1686 | body.chat #chat-input-line > #chat-settings-button, |
| 1687 | body.chat #chat-input-line > button { |
| 1688 | flex: 1 5 auto; |
| 1689 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -1639,10 +1639,14 @@ | |
| 1639 | /** Container for the list of /chat messages. */ |
| 1640 | body.chat #chat-messages-wrapper { |
| 1641 | overflow: auto; |
| 1642 | /*max-height: 800em*//*will be re-calc'd in JS*/; |
| 1643 | flex: 2 1 auto; |
| 1644 | } |
| 1645 | body.chat #chat-messages-wrapper.loading > * { |
| 1646 | /* An attempt at reducing flicker when loading lots of messages. */ |
| 1647 | visibility: hidden; |
| 1648 | } |
| 1649 | body.chat div.content { |
| 1650 | margin: 0; |
| 1651 | padding: 0; |
| 1652 | display: flex; |
| @@ -1678,11 +1682,11 @@ | |
| 1682 | settings button. */ |
| 1683 | body.chat #chat-input-line { |
| 1684 | display: flex; |
| 1685 | flex-direction: row; |
| 1686 | margin-bottom: 0.25em; |
| 1687 | align-items: center; |
| 1688 | } |
| 1689 | body.chat #chat-input-line > input[type=submit], |
| 1690 | body.chat #chat-input-line > #chat-settings-button, |
| 1691 | body.chat #chat-input-line > button { |
| 1692 | flex: 1 5 auto; |
| 1693 |