Fossil SCM
Found what seems to be a more or less viable solution for the chat layout in which the input area is effectively sticky while not actually being so. New messages do not scroll to the start of the list except for when a user locally posts a message, but instead, if a new message arrives and is scrolled out of view, a toast is shown to gently alert the user that a new message has arrived.
Commit
0a00a103125e1cd9e9cb7a4219601c76ad0dc3fbe1b31fb9e8090fa68621f821
Parent
b75ce86581982b9…
2 files changed
+21
-47
+13
-10
+21
-47
| --- src/chat.js | ||
| +++ src/chat.js | ||
| @@ -6,10 +6,19 @@ | ||
| 6 | 6 | const F = window.fossil, D = F.dom; |
| 7 | 7 | const E1 = function(selector){ |
| 8 | 8 | const e = document.querySelector(selector); |
| 9 | 9 | if(!e) throw new Error("missing required DOM element: "+selector); |
| 10 | 10 | return e; |
| 11 | + }; | |
| 12 | + const isInViewport = function(e) { | |
| 13 | + const rect = e.getBoundingClientRect(); | |
| 14 | + return ( | |
| 15 | + rect.top >= 0 && | |
| 16 | + rect.left >= 0 && | |
| 17 | + rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && | |
| 18 | + rect.right <= (window.innerWidth || document.documentElement.clientWidth) | |
| 19 | + ); | |
| 11 | 20 | }; |
| 12 | 21 | //document.body.classList.add('chat-only-mode'); |
| 13 | 22 | const Chat = (function(){ |
| 14 | 23 | const cs = { |
| 15 | 24 | e:{/*map of certain DOM elements.*/ |
| @@ -117,40 +126,19 @@ | ||
| 117 | 126 | const mip = atEnd ? this.e.loadToolbar : this.e.messageInjectPoint; |
| 118 | 127 | if(atEnd){ |
| 119 | 128 | mip.parentNode.insertBefore(e, mip); |
| 120 | 129 | }else{ |
| 121 | 130 | const self = this; |
| 122 | - if(false && this.isUiFlipped()){ | |
| 123 | - /* When UI is flipped, new messages start out under the | |
| 124 | - text input area because of its position:sticky | |
| 125 | - style. We have to scroll them up. When the page footer | |
| 126 | - is not hidden but is not on-screen, this causes a | |
| 127 | - slight amount of UI jarring as the footer is *also* | |
| 128 | - scrolled into view (for whatever reason). | |
| 129 | - | |
| 130 | - The remaining problem here is messages with IMG tags. | |
| 131 | - At this point in the process their IMG.src has not yet | |
| 132 | - been loaded - that's async. We scroll the message into | |
| 133 | - view, but then the downstream loading of IMG.src pushes | |
| 134 | - the message content back down, sliding the message | |
| 135 | - behind the input field. This can be verified by delaying the | |
| 136 | - message scroll by a second or so to give the image time | |
| 137 | - to load (from a local server instance). | |
| 138 | - */ | |
| 139 | - D.addClass(self.e.inputWrapper,'unsticky'); | |
| 140 | - } | |
| 141 | 131 | if(mip.nextSibling) mip.parentNode.insertBefore(e, mip.nextSibling); |
| 142 | 132 | else mip.parentNode.appendChild(e); |
| 143 | - if(false && this.isUiFlipped()){ | |
| 144 | - //e.scrollIntoView(); | |
| 145 | - setTimeout(function(){ | |
| 146 | - //self.e.inputWrapper.scrollIntoView(); | |
| 147 | - //self.e.fileSelectWrapper.scrollIntoView(); | |
| 148 | - //e.scrollIntoView(); | |
| 149 | - //D.removeClass(self.e.inputWrapper,'unsticky'); | |
| 150 | - self.e.inputWrapper.scrollIntoView(); | |
| 151 | - },0); | |
| 133 | + if(!atEnd && !this.isMassLoading | |
| 134 | + && e.dataset.xfrom!==Chat.me && !isInViewport(e)){ | |
| 135 | + /* If a new non-history message arrives while the user is | |
| 136 | + scrolled elsewhere, do not scroll to the latest | |
| 137 | + message, but gently alert the user that a new message | |
| 138 | + has arrived. */ | |
| 139 | + F.toast.message("New message has arrived."); | |
| 152 | 140 | } |
| 153 | 141 | } |
| 154 | 142 | }, |
| 155 | 143 | /** Returns true if chat-only mode is enabled. */ |
| 156 | 144 | isChatOnlyMode: ()=>document.body.classList.contains('chat-only-mode'), |
| @@ -167,11 +155,11 @@ | ||
| 167 | 155 | */ |
| 168 | 156 | chatOnlyMode: function f(yes){ |
| 169 | 157 | if(undefined === f.elemsToToggle){ |
| 170 | 158 | f.elemsToToggle = []; |
| 171 | 159 | document.querySelectorAll( |
| 172 | - "body > div.header, body > div.footer" | |
| 160 | + "body > div.header, body > div.mainmenu, body > div.footer" | |
| 173 | 161 | ).forEach((e)=>f.elemsToToggle.push(e)); |
| 174 | 162 | } |
| 175 | 163 | if(!arguments.length) yes = true; |
| 176 | 164 | if(yes === this.isChatOnlyMode()) return this; |
| 177 | 165 | if(yes){ |
| @@ -179,17 +167,10 @@ | ||
| 179 | 167 | D.addClass(document.body, 'chat-only-mode'); |
| 180 | 168 | document.body.scroll(0,document.body.height); |
| 181 | 169 | }else{ |
| 182 | 170 | D.removeClass(f.elemsToToggle, 'hidden'); |
| 183 | 171 | D.removeClass(document.body, 'chat-only-mode'); |
| 184 | - if(false){ | |
| 185 | - setTimeout(()=>document.body.scrollIntoView( | |
| 186 | - /*moves to (0,0), whereas scrollTo(0,0) does not! | |
| 187 | - setTimeout() is unfortunately necessary to get the scroll | |
| 188 | - placement correct.*/ | |
| 189 | - ), 0); | |
| 190 | - } | |
| 191 | 172 | } |
| 192 | 173 | const msg = document.querySelector('.message-widget'); |
| 193 | 174 | if(msg) setTimeout(()=>msg.scrollIntoView(),0); |
| 194 | 175 | return this; |
| 195 | 176 | }, |
| @@ -525,10 +506,12 @@ | ||
| 525 | 506 | body: fd |
| 526 | 507 | }); |
| 527 | 508 | } |
| 528 | 509 | BlobXferState.clear(); |
| 529 | 510 | Chat.inputValue("").inputFocus(); |
| 511 | + Chat.e.messagesWrapper.scrollTo( | |
| 512 | + 0,0/*scrolls to top or bottom, depending on flex direction!*/); | |
| 530 | 513 | }; |
| 531 | 514 | |
| 532 | 515 | Chat.e.inputSingle.addEventListener('keydown',function(ev){ |
| 533 | 516 | if(13===ev.keyCode/*ENTER*/){ |
| 534 | 517 | ev.preventDefault(); |
| @@ -678,21 +661,10 @@ | ||
| 678 | 661 | boolValue: ()=>document.body.classList.contains('chat-bottom-up'), |
| 679 | 662 | callback: function(){ |
| 680 | 663 | document.body.classList.toggle('chat-bottom-up'); |
| 681 | 664 | Chat.settings.set('bottom-up', |
| 682 | 665 | document.body.classList.contains('chat-bottom-up')); |
| 683 | - if(false){ | |
| 684 | - /* Reminder: in order to get a good scrolling effect when | |
| 685 | - sticky mode is enabled for Chat.e.inputWrapper, BOTH of | |
| 686 | - these scrollIntoView() calls are needed. */ | |
| 687 | - const e = document.querySelector( | |
| 688 | - '.message-widget'/*this is always the most recent message, | |
| 689 | - even if flexbox placed it at the end of | |
| 690 | - the page!*/ | |
| 691 | - ); | |
| 692 | - if(e) e.scrollIntoView(); | |
| 693 | - } | |
| 694 | 666 | setTimeout(()=>Chat.e.inputWrapper.scrollIntoView(), 0); |
| 695 | 667 | } |
| 696 | 668 | },{ |
| 697 | 669 | label: "Images inline", |
| 698 | 670 | boolValue: ()=>Chat.settings.getBool('images-inline'), |
| @@ -862,10 +834,11 @@ | ||
| 862 | 834 | })()/*end history loading widget setup*/; |
| 863 | 835 | |
| 864 | 836 | async function poll(isFirstCall){ |
| 865 | 837 | if(poll.running) return; |
| 866 | 838 | poll.running = true; |
| 839 | + Chat.isMassLoading = isFirstCall; | |
| 867 | 840 | if(isFirstCall) Chat.ajaxStart(); |
| 868 | 841 | var p = fetch("chat-poll?name=" + Chat.mxMsg); |
| 869 | 842 | p.then(x=>x.json()) |
| 870 | 843 | .then(y=>newcontent(y)) |
| 871 | 844 | .catch(e=>console.error(e)) |
| @@ -872,10 +845,11 @@ | ||
| 872 | 845 | /* ^^^ we don't use Chat.reportError(e) here b/c the polling |
| 873 | 846 | fails exepectedly when it times out, but is then immediately |
| 874 | 847 | resumed, and reportError() produces a loud error message. */ |
| 875 | 848 | .finally(function(x){ |
| 876 | 849 | if(isFirstCall){ |
| 850 | + Chat.isMassLoading = false; | |
| 877 | 851 | Chat.ajaxEnd(); |
| 878 | 852 | Chat.e.inputWrapper.scrollIntoView(); |
| 879 | 853 | } |
| 880 | 854 | poll.running=false; |
| 881 | 855 | }); |
| 882 | 856 |
| --- src/chat.js | |
| +++ src/chat.js | |
| @@ -6,10 +6,19 @@ | |
| 6 | const F = window.fossil, D = F.dom; |
| 7 | const E1 = function(selector){ |
| 8 | const e = document.querySelector(selector); |
| 9 | if(!e) throw new Error("missing required DOM element: "+selector); |
| 10 | return e; |
| 11 | }; |
| 12 | //document.body.classList.add('chat-only-mode'); |
| 13 | const Chat = (function(){ |
| 14 | const cs = { |
| 15 | e:{/*map of certain DOM elements.*/ |
| @@ -117,40 +126,19 @@ | |
| 117 | const mip = atEnd ? this.e.loadToolbar : this.e.messageInjectPoint; |
| 118 | if(atEnd){ |
| 119 | mip.parentNode.insertBefore(e, mip); |
| 120 | }else{ |
| 121 | const self = this; |
| 122 | if(false && this.isUiFlipped()){ |
| 123 | /* When UI is flipped, new messages start out under the |
| 124 | text input area because of its position:sticky |
| 125 | style. We have to scroll them up. When the page footer |
| 126 | is not hidden but is not on-screen, this causes a |
| 127 | slight amount of UI jarring as the footer is *also* |
| 128 | scrolled into view (for whatever reason). |
| 129 | |
| 130 | The remaining problem here is messages with IMG tags. |
| 131 | At this point in the process their IMG.src has not yet |
| 132 | been loaded - that's async. We scroll the message into |
| 133 | view, but then the downstream loading of IMG.src pushes |
| 134 | the message content back down, sliding the message |
| 135 | behind the input field. This can be verified by delaying the |
| 136 | message scroll by a second or so to give the image time |
| 137 | to load (from a local server instance). |
| 138 | */ |
| 139 | D.addClass(self.e.inputWrapper,'unsticky'); |
| 140 | } |
| 141 | if(mip.nextSibling) mip.parentNode.insertBefore(e, mip.nextSibling); |
| 142 | else mip.parentNode.appendChild(e); |
| 143 | if(false && this.isUiFlipped()){ |
| 144 | //e.scrollIntoView(); |
| 145 | setTimeout(function(){ |
| 146 | //self.e.inputWrapper.scrollIntoView(); |
| 147 | //self.e.fileSelectWrapper.scrollIntoView(); |
| 148 | //e.scrollIntoView(); |
| 149 | //D.removeClass(self.e.inputWrapper,'unsticky'); |
| 150 | self.e.inputWrapper.scrollIntoView(); |
| 151 | },0); |
| 152 | } |
| 153 | } |
| 154 | }, |
| 155 | /** Returns true if chat-only mode is enabled. */ |
| 156 | isChatOnlyMode: ()=>document.body.classList.contains('chat-only-mode'), |
| @@ -167,11 +155,11 @@ | |
| 167 | */ |
| 168 | chatOnlyMode: function f(yes){ |
| 169 | if(undefined === f.elemsToToggle){ |
| 170 | f.elemsToToggle = []; |
| 171 | document.querySelectorAll( |
| 172 | "body > div.header, body > div.footer" |
| 173 | ).forEach((e)=>f.elemsToToggle.push(e)); |
| 174 | } |
| 175 | if(!arguments.length) yes = true; |
| 176 | if(yes === this.isChatOnlyMode()) return this; |
| 177 | if(yes){ |
| @@ -179,17 +167,10 @@ | |
| 179 | D.addClass(document.body, 'chat-only-mode'); |
| 180 | document.body.scroll(0,document.body.height); |
| 181 | }else{ |
| 182 | D.removeClass(f.elemsToToggle, 'hidden'); |
| 183 | D.removeClass(document.body, 'chat-only-mode'); |
| 184 | if(false){ |
| 185 | setTimeout(()=>document.body.scrollIntoView( |
| 186 | /*moves to (0,0), whereas scrollTo(0,0) does not! |
| 187 | setTimeout() is unfortunately necessary to get the scroll |
| 188 | placement correct.*/ |
| 189 | ), 0); |
| 190 | } |
| 191 | } |
| 192 | const msg = document.querySelector('.message-widget'); |
| 193 | if(msg) setTimeout(()=>msg.scrollIntoView(),0); |
| 194 | return this; |
| 195 | }, |
| @@ -525,10 +506,12 @@ | |
| 525 | body: fd |
| 526 | }); |
| 527 | } |
| 528 | BlobXferState.clear(); |
| 529 | Chat.inputValue("").inputFocus(); |
| 530 | }; |
| 531 | |
| 532 | Chat.e.inputSingle.addEventListener('keydown',function(ev){ |
| 533 | if(13===ev.keyCode/*ENTER*/){ |
| 534 | ev.preventDefault(); |
| @@ -678,21 +661,10 @@ | |
| 678 | boolValue: ()=>document.body.classList.contains('chat-bottom-up'), |
| 679 | callback: function(){ |
| 680 | document.body.classList.toggle('chat-bottom-up'); |
| 681 | Chat.settings.set('bottom-up', |
| 682 | document.body.classList.contains('chat-bottom-up')); |
| 683 | if(false){ |
| 684 | /* Reminder: in order to get a good scrolling effect when |
| 685 | sticky mode is enabled for Chat.e.inputWrapper, BOTH of |
| 686 | these scrollIntoView() calls are needed. */ |
| 687 | const e = document.querySelector( |
| 688 | '.message-widget'/*this is always the most recent message, |
| 689 | even if flexbox placed it at the end of |
| 690 | the page!*/ |
| 691 | ); |
| 692 | if(e) e.scrollIntoView(); |
| 693 | } |
| 694 | setTimeout(()=>Chat.e.inputWrapper.scrollIntoView(), 0); |
| 695 | } |
| 696 | },{ |
| 697 | label: "Images inline", |
| 698 | boolValue: ()=>Chat.settings.getBool('images-inline'), |
| @@ -862,10 +834,11 @@ | |
| 862 | })()/*end history loading widget setup*/; |
| 863 | |
| 864 | async function poll(isFirstCall){ |
| 865 | if(poll.running) return; |
| 866 | poll.running = true; |
| 867 | if(isFirstCall) Chat.ajaxStart(); |
| 868 | var p = fetch("chat-poll?name=" + Chat.mxMsg); |
| 869 | p.then(x=>x.json()) |
| 870 | .then(y=>newcontent(y)) |
| 871 | .catch(e=>console.error(e)) |
| @@ -872,10 +845,11 @@ | |
| 872 | /* ^^^ we don't use Chat.reportError(e) here b/c the polling |
| 873 | fails exepectedly when it times out, but is then immediately |
| 874 | resumed, and reportError() produces a loud error message. */ |
| 875 | .finally(function(x){ |
| 876 | if(isFirstCall){ |
| 877 | Chat.ajaxEnd(); |
| 878 | Chat.e.inputWrapper.scrollIntoView(); |
| 879 | } |
| 880 | poll.running=false; |
| 881 | }); |
| 882 |
| --- src/chat.js | |
| +++ src/chat.js | |
| @@ -6,10 +6,19 @@ | |
| 6 | const F = window.fossil, D = F.dom; |
| 7 | const E1 = function(selector){ |
| 8 | const e = document.querySelector(selector); |
| 9 | if(!e) throw new Error("missing required DOM element: "+selector); |
| 10 | return e; |
| 11 | }; |
| 12 | const isInViewport = function(e) { |
| 13 | const rect = e.getBoundingClientRect(); |
| 14 | return ( |
| 15 | rect.top >= 0 && |
| 16 | rect.left >= 0 && |
| 17 | rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && |
| 18 | rect.right <= (window.innerWidth || document.documentElement.clientWidth) |
| 19 | ); |
| 20 | }; |
| 21 | //document.body.classList.add('chat-only-mode'); |
| 22 | const Chat = (function(){ |
| 23 | const cs = { |
| 24 | e:{/*map of certain DOM elements.*/ |
| @@ -117,40 +126,19 @@ | |
| 126 | const mip = atEnd ? this.e.loadToolbar : this.e.messageInjectPoint; |
| 127 | if(atEnd){ |
| 128 | mip.parentNode.insertBefore(e, mip); |
| 129 | }else{ |
| 130 | const self = this; |
| 131 | if(mip.nextSibling) mip.parentNode.insertBefore(e, mip.nextSibling); |
| 132 | else mip.parentNode.appendChild(e); |
| 133 | if(!atEnd && !this.isMassLoading |
| 134 | && e.dataset.xfrom!==Chat.me && !isInViewport(e)){ |
| 135 | /* If a new non-history message arrives while the user is |
| 136 | scrolled elsewhere, do not scroll to the latest |
| 137 | message, but gently alert the user that a new message |
| 138 | has arrived. */ |
| 139 | F.toast.message("New message has arrived."); |
| 140 | } |
| 141 | } |
| 142 | }, |
| 143 | /** Returns true if chat-only mode is enabled. */ |
| 144 | isChatOnlyMode: ()=>document.body.classList.contains('chat-only-mode'), |
| @@ -167,11 +155,11 @@ | |
| 155 | */ |
| 156 | chatOnlyMode: function f(yes){ |
| 157 | if(undefined === f.elemsToToggle){ |
| 158 | f.elemsToToggle = []; |
| 159 | document.querySelectorAll( |
| 160 | "body > div.header, body > div.mainmenu, body > div.footer" |
| 161 | ).forEach((e)=>f.elemsToToggle.push(e)); |
| 162 | } |
| 163 | if(!arguments.length) yes = true; |
| 164 | if(yes === this.isChatOnlyMode()) return this; |
| 165 | if(yes){ |
| @@ -179,17 +167,10 @@ | |
| 167 | D.addClass(document.body, 'chat-only-mode'); |
| 168 | document.body.scroll(0,document.body.height); |
| 169 | }else{ |
| 170 | D.removeClass(f.elemsToToggle, 'hidden'); |
| 171 | D.removeClass(document.body, 'chat-only-mode'); |
| 172 | } |
| 173 | const msg = document.querySelector('.message-widget'); |
| 174 | if(msg) setTimeout(()=>msg.scrollIntoView(),0); |
| 175 | return this; |
| 176 | }, |
| @@ -525,10 +506,12 @@ | |
| 506 | body: fd |
| 507 | }); |
| 508 | } |
| 509 | BlobXferState.clear(); |
| 510 | Chat.inputValue("").inputFocus(); |
| 511 | Chat.e.messagesWrapper.scrollTo( |
| 512 | 0,0/*scrolls to top or bottom, depending on flex direction!*/); |
| 513 | }; |
| 514 | |
| 515 | Chat.e.inputSingle.addEventListener('keydown',function(ev){ |
| 516 | if(13===ev.keyCode/*ENTER*/){ |
| 517 | ev.preventDefault(); |
| @@ -678,21 +661,10 @@ | |
| 661 | boolValue: ()=>document.body.classList.contains('chat-bottom-up'), |
| 662 | callback: function(){ |
| 663 | document.body.classList.toggle('chat-bottom-up'); |
| 664 | Chat.settings.set('bottom-up', |
| 665 | document.body.classList.contains('chat-bottom-up')); |
| 666 | setTimeout(()=>Chat.e.inputWrapper.scrollIntoView(), 0); |
| 667 | } |
| 668 | },{ |
| 669 | label: "Images inline", |
| 670 | boolValue: ()=>Chat.settings.getBool('images-inline'), |
| @@ -862,10 +834,11 @@ | |
| 834 | })()/*end history loading widget setup*/; |
| 835 | |
| 836 | async function poll(isFirstCall){ |
| 837 | if(poll.running) return; |
| 838 | poll.running = true; |
| 839 | Chat.isMassLoading = isFirstCall; |
| 840 | if(isFirstCall) Chat.ajaxStart(); |
| 841 | var p = fetch("chat-poll?name=" + Chat.mxMsg); |
| 842 | p.then(x=>x.json()) |
| 843 | .then(y=>newcontent(y)) |
| 844 | .catch(e=>console.error(e)) |
| @@ -872,10 +845,11 @@ | |
| 845 | /* ^^^ we don't use Chat.reportError(e) here b/c the polling |
| 846 | fails exepectedly when it times out, but is then immediately |
| 847 | resumed, and reportError() produces a loud error message. */ |
| 848 | .finally(function(x){ |
| 849 | if(isFirstCall){ |
| 850 | Chat.isMassLoading = false; |
| 851 | Chat.ajaxEnd(); |
| 852 | Chat.e.inputWrapper.scrollIntoView(); |
| 853 | } |
| 854 | poll.running=false; |
| 855 | }); |
| 856 |
+13
-10
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -1638,52 +1638,55 @@ | ||
| 1638 | 1638 | } |
| 1639 | 1639 | /** Container for the list of /chat messages. */ |
| 1640 | 1640 | body.chat #chat-messages-wrapper { |
| 1641 | 1641 | display: flex; |
| 1642 | 1642 | flex-direction: column; |
| 1643 | + overflow: auto; | |
| 1644 | + width: 100%; | |
| 1645 | + flex: 1 1 auto; | |
| 1643 | 1646 | } |
| 1644 | 1647 | body.chat.chat-bottom-up #chat-messages-wrapper { |
| 1645 | 1648 | flex-direction: column-reverse; |
| 1646 | 1649 | z-index: 99 /* so that it scrolls under input area. If it's |
| 1647 | 1650 | lower than div.content then mouse events to it |
| 1648 | 1651 | are blocked!*/; |
| 1649 | 1652 | } |
| 1653 | +body.chat.chat-only-mode #chat-message-wrapper { | |
| 1654 | +} | |
| 1655 | + | |
| 1650 | 1656 | body.chat div.content { |
| 1651 | 1657 | margin: 0; |
| 1652 | 1658 | padding: 0; |
| 1659 | + position: relative; | |
| 1653 | 1660 | display: flex; |
| 1654 | 1661 | flex-direction: column; |
| 1655 | 1662 | align-items: stretch; |
| 1663 | + max-height: 85vh /* rough approximate */; | |
| 1656 | 1664 | } |
| 1657 | 1665 | body.chat.chat-bottom-up div.content { |
| 1658 | 1666 | flex-direction: column-reverse; |
| 1659 | 1667 | } |
| 1668 | +body.chat.chat-only-mode div.content { | |
| 1669 | + max-height: 95vh/*larger than approx. this is too big for Firefox on Android*/; | |
| 1670 | +} | |
| 1660 | 1671 | /* Wrapper for /chat user input controls */ |
| 1661 | 1672 | body.chat #chat-input-area { |
| 1662 | 1673 | display: flex; |
| 1663 | 1674 | flex-direction: column; |
| 1664 | 1675 | border-bottom: 1px solid black; |
| 1665 | - padding: 0.5em 1em; | |
| 1676 | + padding: 0.5em 1em 0 0.5em; | |
| 1666 | 1677 | margin-bottom: 0.5em; |
| 1667 | - /*position: sticky; top: 0; disabled for the time being because of | |
| 1668 | - scroll-related quirks which are still unresolved. */ | |
| 1669 | 1678 | z-index: 100 |
| 1670 | 1679 | /* see notes in #chat-messages-wrapper. The various popups require a |
| 1671 | 1680 | z-index higher than this one. */; |
| 1681 | + flex: 0 0 auto; | |
| 1672 | 1682 | } |
| 1673 | 1683 | body.chat.chat-bottom-up #chat-input-area { |
| 1674 | 1684 | border-bottom: none; |
| 1675 | 1685 | border-top: 1px solid black; |
| 1676 | 1686 | margin-bottom: 0; |
| 1677 | 1687 | margin-top: 0.5em; |
| 1678 | - position: initial /*sticky currently disabled due to scrolling-related issues*/; | |
| 1679 | - bottom: 0; | |
| 1680 | -} | |
| 1681 | -/* An internal hack to try to help resolve a message-scrolling quirk | |
| 1682 | - when #chat-input-area is sticky on the bottom of the screen. */ | |
| 1683 | -body.chat.chat-bottom-up #chat-input-area.unsticky { | |
| 1684 | - position: initial; | |
| 1685 | 1688 | } |
| 1686 | 1689 | /* Widget holding the chat message input field, send button, and |
| 1687 | 1690 | settings button. */ |
| 1688 | 1691 | body.chat #chat-input-line { |
| 1689 | 1692 | display: flex; |
| 1690 | 1693 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -1638,52 +1638,55 @@ | |
| 1638 | } |
| 1639 | /** Container for the list of /chat messages. */ |
| 1640 | body.chat #chat-messages-wrapper { |
| 1641 | display: flex; |
| 1642 | flex-direction: column; |
| 1643 | } |
| 1644 | body.chat.chat-bottom-up #chat-messages-wrapper { |
| 1645 | flex-direction: column-reverse; |
| 1646 | z-index: 99 /* so that it scrolls under input area. If it's |
| 1647 | lower than div.content then mouse events to it |
| 1648 | are blocked!*/; |
| 1649 | } |
| 1650 | body.chat div.content { |
| 1651 | margin: 0; |
| 1652 | padding: 0; |
| 1653 | display: flex; |
| 1654 | flex-direction: column; |
| 1655 | align-items: stretch; |
| 1656 | } |
| 1657 | body.chat.chat-bottom-up div.content { |
| 1658 | flex-direction: column-reverse; |
| 1659 | } |
| 1660 | /* Wrapper for /chat user input controls */ |
| 1661 | body.chat #chat-input-area { |
| 1662 | display: flex; |
| 1663 | flex-direction: column; |
| 1664 | border-bottom: 1px solid black; |
| 1665 | padding: 0.5em 1em; |
| 1666 | margin-bottom: 0.5em; |
| 1667 | /*position: sticky; top: 0; disabled for the time being because of |
| 1668 | scroll-related quirks which are still unresolved. */ |
| 1669 | z-index: 100 |
| 1670 | /* see notes in #chat-messages-wrapper. The various popups require a |
| 1671 | z-index higher than this one. */; |
| 1672 | } |
| 1673 | body.chat.chat-bottom-up #chat-input-area { |
| 1674 | border-bottom: none; |
| 1675 | border-top: 1px solid black; |
| 1676 | margin-bottom: 0; |
| 1677 | margin-top: 0.5em; |
| 1678 | position: initial /*sticky currently disabled due to scrolling-related issues*/; |
| 1679 | bottom: 0; |
| 1680 | } |
| 1681 | /* An internal hack to try to help resolve a message-scrolling quirk |
| 1682 | when #chat-input-area is sticky on the bottom of the screen. */ |
| 1683 | body.chat.chat-bottom-up #chat-input-area.unsticky { |
| 1684 | position: initial; |
| 1685 | } |
| 1686 | /* Widget holding the chat message input field, send button, and |
| 1687 | settings button. */ |
| 1688 | body.chat #chat-input-line { |
| 1689 | display: flex; |
| 1690 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -1638,52 +1638,55 @@ | |
| 1638 | } |
| 1639 | /** Container for the list of /chat messages. */ |
| 1640 | body.chat #chat-messages-wrapper { |
| 1641 | display: flex; |
| 1642 | flex-direction: column; |
| 1643 | overflow: auto; |
| 1644 | width: 100%; |
| 1645 | flex: 1 1 auto; |
| 1646 | } |
| 1647 | body.chat.chat-bottom-up #chat-messages-wrapper { |
| 1648 | flex-direction: column-reverse; |
| 1649 | z-index: 99 /* so that it scrolls under input area. If it's |
| 1650 | lower than div.content then mouse events to it |
| 1651 | are blocked!*/; |
| 1652 | } |
| 1653 | body.chat.chat-only-mode #chat-message-wrapper { |
| 1654 | } |
| 1655 | |
| 1656 | body.chat div.content { |
| 1657 | margin: 0; |
| 1658 | padding: 0; |
| 1659 | position: relative; |
| 1660 | display: flex; |
| 1661 | flex-direction: column; |
| 1662 | align-items: stretch; |
| 1663 | max-height: 85vh /* rough approximate */; |
| 1664 | } |
| 1665 | body.chat.chat-bottom-up div.content { |
| 1666 | flex-direction: column-reverse; |
| 1667 | } |
| 1668 | body.chat.chat-only-mode div.content { |
| 1669 | max-height: 95vh/*larger than approx. this is too big for Firefox on Android*/; |
| 1670 | } |
| 1671 | /* Wrapper for /chat user input controls */ |
| 1672 | body.chat #chat-input-area { |
| 1673 | display: flex; |
| 1674 | flex-direction: column; |
| 1675 | border-bottom: 1px solid black; |
| 1676 | padding: 0.5em 1em 0 0.5em; |
| 1677 | margin-bottom: 0.5em; |
| 1678 | z-index: 100 |
| 1679 | /* see notes in #chat-messages-wrapper. The various popups require a |
| 1680 | z-index higher than this one. */; |
| 1681 | flex: 0 0 auto; |
| 1682 | } |
| 1683 | body.chat.chat-bottom-up #chat-input-area { |
| 1684 | border-bottom: none; |
| 1685 | border-top: 1px solid black; |
| 1686 | margin-bottom: 0; |
| 1687 | margin-top: 0.5em; |
| 1688 | } |
| 1689 | /* Widget holding the chat message input field, send button, and |
| 1690 | settings button. */ |
| 1691 | body.chat #chat-input-line { |
| 1692 | display: flex; |
| 1693 |