Fossil SCM
chat: re-integrated JS-based div.content resizer to do approximately what the preferred 'vh' CSS units would, but upon which Safari apparently chokes. Message area now gets a scrollbar. This works reasonably well on FF/Chrome on both Linux and Android. The jury is still out on Safari.
Commit
d488f5c66c10b351045a27bcae64351091dd2f4c60b007dda1cf57c5625bc337
Parent
421d6570785de52…
2 files changed
+34
-4
+4
+34
-4
| --- src/chat.js | ||
| +++ src/chat.js | ||
| @@ -16,10 +16,35 @@ | ||
| 16 | 16 | rect.left >= 0 && |
| 17 | 17 | rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && |
| 18 | 18 | rect.right <= (window.innerWidth || document.documentElement.clientWidth) |
| 19 | 19 | ); |
| 20 | 20 | }; |
| 21 | + | |
| 22 | + const ForceResizeKludge = 0 ? function(){} : (function(){ | |
| 23 | + /* Workaround for Safari mayhem regarding use of vh CSS units.... | |
| 24 | + We tried to use vh units to set the content area size for the | |
| 25 | + chat layout, but Safari chokes on that, so we calculate that | |
| 26 | + height here: 85% when in "normal" mode and 95% in chat-only | |
| 27 | + mode. Larger than ~95% is too big for Firefox on Android, | |
| 28 | + causing the input area to move off-screen. */ | |
| 29 | + const contentArea = E1('div.content'), | |
| 30 | + bcl = document.body.classList; | |
| 31 | + const resized = function(){ | |
| 32 | + const wh = window.innerHeight, | |
| 33 | + mult = bcl.contains('chat-only-mode') ? 0.95 : 0.85; | |
| 34 | + contentArea.style.maxHeight = (wh * mult)+"px"; | |
| 35 | + console.debug("resized.",wh, mult, window.getComputedStyle(contentArea).maxHeight); | |
| 36 | + }; | |
| 37 | + var doit; | |
| 38 | + window.addEventListener('resize',function(ev){ | |
| 39 | + clearTimeout(doit); | |
| 40 | + doit = setTimeout(resized, 100); | |
| 41 | + }, false); | |
| 42 | + resized(); | |
| 43 | + return resized; | |
| 44 | + })(); | |
| 45 | + | |
| 21 | 46 | const Chat = (function(){ |
| 22 | 47 | const cs = { |
| 23 | 48 | e:{/*map of certain DOM elements.*/ |
| 24 | 49 | messageInjectPoint: E1('#message-inject-point'), |
| 25 | 50 | pageTitle: E1('head title'), |
| @@ -120,26 +145,30 @@ | ||
| 120 | 145 | ], |
| 121 | 146 | /* Injects element e as a new row in the chat, at the top of the |
| 122 | 147 | list if atEnd is falsy, else at the end of the list, before |
| 123 | 148 | the load-history widget. */ |
| 124 | 149 | injectMessageElem: function f(e, atEnd){ |
| 125 | - const mip = atEnd ? this.e.loadOlderToolbar : this.e.messageInjectPoint, | |
| 126 | - holder = this.e.messagesWrapper; | |
| 150 | + const mip = atEnd ? this.e.loadOlderToolbar : this.e.messageInjectPoint; | |
| 151 | + /* Reminder: this placement is kinda odd because of the | |
| 152 | + flex-direction:column-reverse in this.e.messagesWrapper, | |
| 153 | + which reverses our directions. */ | |
| 127 | 154 | if(atEnd){ |
| 155 | + mip.parentNode.insertBefore(e, mip); | |
| 156 | + }else{ | |
| 128 | 157 | const fe = mip.nextElementSibling; |
| 129 | 158 | if(fe) mip.parentNode.insertBefore(e, fe); |
| 130 | 159 | else D.append(mip.parentNode, e); |
| 131 | - }else{ | |
| 132 | - D.append(holder,e); | |
| 133 | 160 | } |
| 134 | 161 | if(!atEnd && !this.isMassLoading |
| 135 | 162 | && e.dataset.xfrom!==Chat.me && !isInViewport(e)){ |
| 136 | 163 | /* If a new non-history message arrives while the user is |
| 137 | 164 | scrolled elsewhere, do not scroll to the latest |
| 138 | 165 | message, but gently alert the user that a new message |
| 139 | 166 | has arrived. */ |
| 140 | 167 | F.toast.message("New message has arrived."); |
| 168 | + }else if(e.dataset.xfrom===Chat.me){ | |
| 169 | + e.scrollIntoView(); | |
| 141 | 170 | } |
| 142 | 171 | }, |
| 143 | 172 | /** Returns true if chat-only mode is enabled. */ |
| 144 | 173 | isChatOnlyMode: ()=>document.body.classList.contains('chat-only-mode'), |
| 145 | 174 | /** |
| @@ -165,10 +194,11 @@ | ||
| 165 | 194 | D.removeClass(f.elemsToToggle, 'hidden'); |
| 166 | 195 | D.removeClass(document.body, 'chat-only-mode'); |
| 167 | 196 | } |
| 168 | 197 | const msg = document.querySelector('.message-widget'); |
| 169 | 198 | if(msg) setTimeout(()=>msg.scrollIntoView(),0); |
| 199 | + ForceResizeKludge(); | |
| 170 | 200 | return this; |
| 171 | 201 | }, |
| 172 | 202 | toggleChatOnlyMode: function(){ |
| 173 | 203 | return this.chatOnlyMode(!this.isChatOnlyMode()); |
| 174 | 204 | }, |
| 175 | 205 |
| --- src/chat.js | |
| +++ src/chat.js | |
| @@ -16,10 +16,35 @@ | |
| 16 | rect.left >= 0 && |
| 17 | rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && |
| 18 | rect.right <= (window.innerWidth || document.documentElement.clientWidth) |
| 19 | ); |
| 20 | }; |
| 21 | const Chat = (function(){ |
| 22 | const cs = { |
| 23 | e:{/*map of certain DOM elements.*/ |
| 24 | messageInjectPoint: E1('#message-inject-point'), |
| 25 | pageTitle: E1('head title'), |
| @@ -120,26 +145,30 @@ | |
| 120 | ], |
| 121 | /* Injects element e as a new row in the chat, at the top of the |
| 122 | list if atEnd is falsy, else at the end of the list, before |
| 123 | the load-history widget. */ |
| 124 | injectMessageElem: function f(e, atEnd){ |
| 125 | const mip = atEnd ? this.e.loadOlderToolbar : this.e.messageInjectPoint, |
| 126 | holder = this.e.messagesWrapper; |
| 127 | if(atEnd){ |
| 128 | const fe = mip.nextElementSibling; |
| 129 | if(fe) mip.parentNode.insertBefore(e, fe); |
| 130 | else D.append(mip.parentNode, e); |
| 131 | }else{ |
| 132 | D.append(holder,e); |
| 133 | } |
| 134 | if(!atEnd && !this.isMassLoading |
| 135 | && e.dataset.xfrom!==Chat.me && !isInViewport(e)){ |
| 136 | /* If a new non-history message arrives while the user is |
| 137 | scrolled elsewhere, do not scroll to the latest |
| 138 | message, but gently alert the user that a new message |
| 139 | has arrived. */ |
| 140 | F.toast.message("New message has arrived."); |
| 141 | } |
| 142 | }, |
| 143 | /** Returns true if chat-only mode is enabled. */ |
| 144 | isChatOnlyMode: ()=>document.body.classList.contains('chat-only-mode'), |
| 145 | /** |
| @@ -165,10 +194,11 @@ | |
| 165 | D.removeClass(f.elemsToToggle, 'hidden'); |
| 166 | D.removeClass(document.body, 'chat-only-mode'); |
| 167 | } |
| 168 | const msg = document.querySelector('.message-widget'); |
| 169 | if(msg) setTimeout(()=>msg.scrollIntoView(),0); |
| 170 | return this; |
| 171 | }, |
| 172 | toggleChatOnlyMode: function(){ |
| 173 | return this.chatOnlyMode(!this.isChatOnlyMode()); |
| 174 | }, |
| 175 |
| --- src/chat.js | |
| +++ src/chat.js | |
| @@ -16,10 +16,35 @@ | |
| 16 | rect.left >= 0 && |
| 17 | rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && |
| 18 | rect.right <= (window.innerWidth || document.documentElement.clientWidth) |
| 19 | ); |
| 20 | }; |
| 21 | |
| 22 | const ForceResizeKludge = 0 ? function(){} : (function(){ |
| 23 | /* Workaround for Safari mayhem regarding use of vh CSS units.... |
| 24 | We tried to use vh units to set the content area size for the |
| 25 | chat layout, but Safari chokes on that, so we calculate that |
| 26 | height here: 85% when in "normal" mode and 95% in chat-only |
| 27 | mode. Larger than ~95% is too big for Firefox on Android, |
| 28 | causing the input area to move off-screen. */ |
| 29 | const contentArea = E1('div.content'), |
| 30 | bcl = document.body.classList; |
| 31 | const resized = function(){ |
| 32 | const wh = window.innerHeight, |
| 33 | mult = bcl.contains('chat-only-mode') ? 0.95 : 0.85; |
| 34 | contentArea.style.maxHeight = (wh * mult)+"px"; |
| 35 | console.debug("resized.",wh, mult, window.getComputedStyle(contentArea).maxHeight); |
| 36 | }; |
| 37 | var doit; |
| 38 | window.addEventListener('resize',function(ev){ |
| 39 | clearTimeout(doit); |
| 40 | doit = setTimeout(resized, 100); |
| 41 | }, false); |
| 42 | resized(); |
| 43 | return resized; |
| 44 | })(); |
| 45 | |
| 46 | const Chat = (function(){ |
| 47 | const cs = { |
| 48 | e:{/*map of certain DOM elements.*/ |
| 49 | messageInjectPoint: E1('#message-inject-point'), |
| 50 | pageTitle: E1('head title'), |
| @@ -120,26 +145,30 @@ | |
| 145 | ], |
| 146 | /* Injects element e as a new row in the chat, at the top of the |
| 147 | list if atEnd is falsy, else at the end of the list, before |
| 148 | the load-history widget. */ |
| 149 | injectMessageElem: function f(e, atEnd){ |
| 150 | const mip = atEnd ? this.e.loadOlderToolbar : this.e.messageInjectPoint; |
| 151 | /* Reminder: this placement is kinda odd because of the |
| 152 | flex-direction:column-reverse in this.e.messagesWrapper, |
| 153 | which reverses our directions. */ |
| 154 | if(atEnd){ |
| 155 | mip.parentNode.insertBefore(e, mip); |
| 156 | }else{ |
| 157 | const fe = mip.nextElementSibling; |
| 158 | if(fe) mip.parentNode.insertBefore(e, fe); |
| 159 | else D.append(mip.parentNode, e); |
| 160 | } |
| 161 | if(!atEnd && !this.isMassLoading |
| 162 | && e.dataset.xfrom!==Chat.me && !isInViewport(e)){ |
| 163 | /* If a new non-history message arrives while the user is |
| 164 | scrolled elsewhere, do not scroll to the latest |
| 165 | message, but gently alert the user that a new message |
| 166 | has arrived. */ |
| 167 | F.toast.message("New message has arrived."); |
| 168 | }else if(e.dataset.xfrom===Chat.me){ |
| 169 | e.scrollIntoView(); |
| 170 | } |
| 171 | }, |
| 172 | /** Returns true if chat-only mode is enabled. */ |
| 173 | isChatOnlyMode: ()=>document.body.classList.contains('chat-only-mode'), |
| 174 | /** |
| @@ -165,10 +194,11 @@ | |
| 194 | D.removeClass(f.elemsToToggle, 'hidden'); |
| 195 | D.removeClass(document.body, 'chat-only-mode'); |
| 196 | } |
| 197 | const msg = document.querySelector('.message-widget'); |
| 198 | if(msg) setTimeout(()=>msg.scrollIntoView(),0); |
| 199 | ForceResizeKludge(); |
| 200 | return this; |
| 201 | }, |
| 202 | toggleChatOnlyMode: function(){ |
| 203 | return this.chatOnlyMode(!this.isChatOnlyMode()); |
| 204 | }, |
| 205 |
+4
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -1636,10 +1636,14 @@ | ||
| 1636 | 1636 | body.chat .chat-settings-popup > span.menu-entry > input[type=checkbox] { |
| 1637 | 1637 | cursor: inherit; |
| 1638 | 1638 | } |
| 1639 | 1639 | /** Container for the list of /chat messages. */ |
| 1640 | 1640 | body.chat #chat-messages-wrapper { |
| 1641 | + overflow: auto; | |
| 1642 | + max-height: 200em/*we *really* want approx. 85vh*/; | |
| 1643 | + display: flex; | |
| 1644 | + flex-direction: column-reverse/*necessary for scrolling gravity!*/; | |
| 1641 | 1645 | } |
| 1642 | 1646 | body.chat div.content { |
| 1643 | 1647 | margin: 0; |
| 1644 | 1648 | padding: 0; |
| 1645 | 1649 | display: flex; |
| 1646 | 1650 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -1636,10 +1636,14 @@ | |
| 1636 | body.chat .chat-settings-popup > span.menu-entry > input[type=checkbox] { |
| 1637 | cursor: inherit; |
| 1638 | } |
| 1639 | /** Container for the list of /chat messages. */ |
| 1640 | body.chat #chat-messages-wrapper { |
| 1641 | } |
| 1642 | body.chat div.content { |
| 1643 | margin: 0; |
| 1644 | padding: 0; |
| 1645 | display: flex; |
| 1646 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -1636,10 +1636,14 @@ | |
| 1636 | body.chat .chat-settings-popup > span.menu-entry > input[type=checkbox] { |
| 1637 | cursor: inherit; |
| 1638 | } |
| 1639 | /** Container for the list of /chat messages. */ |
| 1640 | body.chat #chat-messages-wrapper { |
| 1641 | overflow: auto; |
| 1642 | max-height: 200em/*we *really* want approx. 85vh*/; |
| 1643 | display: flex; |
| 1644 | flex-direction: column-reverse/*necessary for scrolling gravity!*/; |
| 1645 | } |
| 1646 | body.chat div.content { |
| 1647 | margin: 0; |
| 1648 | padding: 0; |
| 1649 | display: flex; |
| 1650 |