Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Initial go at a "bottom-up" (mobile-like) layout for chat, but it is only active in chat-only mode where we have more control over the layout. The default mode works like before, top-down. There are still minor usability/scrolling issues left to resolve but it seems to essentially work. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | chat-mode-bottom-up |
| Files: | files | file ages | folders |
| SHA3-256: |
cffd66ffe9ed970d831383df282397ac |
| User & Date: | stephan 2020-12-26 01:31:05.977 |
Context
|
2020-12-26
| ||
| 01:37 | Settings menu now closes if a click or ESC happens outside of the menu. ... (check-in: 1f00036884 user: stephan tags: chat-mode-bottom-up) | |
| 01:31 | Initial go at a "bottom-up" (mobile-like) layout for chat, but it is only active in chat-only mode where we have more control over the layout. The default mode works like before, top-down. There are still minor usability/scrolling issues left to resolve but it seems to essentially work. ... (check-in: cffd66ffe9 user: stephan tags: chat-mode-bottom-up) | |
|
2020-12-25
| ||
| 23:38 | CSS docs and line-wrapped the new settings icon data URL. No functional changes. ... (check-in: ca42098af0 user: stephan tags: trunk) | |
Changes
Changes to src/chat.js.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/**
This file contains the client-side implementation of fossil's /chat
application.
*/
(function(){
const F = window.fossil, D = F.dom;
const E1 = function(selector){
const e = document.querySelector(selector);
if(!e) throw new Error("missing required DOM element: "+selector);
return e;
};
const Chat = (function(){
const cs = {
e:{/*map of certain DOM elements.*/
messageInjectPoint: E1('#message-inject-point'),
pageTitle: E1('head title'),
loadToolbar: undefined /* the load-posts toolbar (dynamically created) */,
inputWrapper: E1("#chat-input-area"),
| > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/**
This file contains the client-side implementation of fossil's /chat
application.
*/
(function(){
const F = window.fossil, D = F.dom;
const E1 = function(selector){
const e = document.querySelector(selector);
if(!e) throw new Error("missing required DOM element: "+selector);
return e;
};
//document.body.classList.add('chat-only-mode');
const Chat = (function(){
const cs = {
e:{/*map of certain DOM elements.*/
messageInjectPoint: E1('#message-inject-point'),
pageTitle: E1('head title'),
loadToolbar: undefined /* the load-posts toolbar (dynamically created) */,
inputWrapper: E1("#chat-input-area"),
|
| ︙ | ︙ | |||
110 111 112 113 114 115 116 |
list if atEnd is falsy, else at the end of the list, before
the load-history widget. */
injectMessageElem: function f(e, atEnd){
const mip = atEnd ? this.e.loadToolbar : this.e.messageInjectPoint;
if(atEnd){
mip.parentNode.insertBefore(e, mip);
}else{
| | | > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > | 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
list if atEnd is falsy, else at the end of the list, before
the load-history widget. */
injectMessageElem: function f(e, atEnd){
const mip = atEnd ? this.e.loadToolbar : this.e.messageInjectPoint;
if(atEnd){
mip.parentNode.insertBefore(e, mip);
}else{
if(mip.nextSibling) mip.parentNode.insertBefore(e, mip.nextSibling);
else mip.parentNode.appendChild(e);
if(this.isChatOnlyMode()){
e.scrollIntoView();
}else{
//const rect = e.getBoundingClientRect();
//const rect = this.e.inputWrapper.getBoundingClientRect();
//window.scrollBy(0, -cs.height);
//console.debug("rect =",rect);
//window.scrollBy(0,rect.height);
//window.scrollTo(0,rect.top);
//e.querySelector('.message-widget-tab').scrollIntoView();
}
}
},
isChatOnlyMode: function(){
return document.body.classList.contains('chat-only-mode');
},
chatOnlyMode: function f(yes){
if(undefined === f.elemsToToggle){
f.elemsToToggle = [];
document.body.childNodes.forEach(function(e){
if(!e.classList) return/*TEXT nodes and such*/;
else if(!e.classList.contains('content')
&& !e.classList.contains('fossil-PopupWidget')
/*kludge^^^ for settingsPopup click handling!*/){
f.elemsToToggle.push(e);
}
});
}
if(!arguments.length) yes = true;
if(yes === this.isChatOnlyMode()) return this;
if(yes){
D.addClass(f.elemsToToggle, 'hidden');
D.addClass(document.body, 'chat-only-mode');
document.body.scroll(0,document.body.height);
}else{
D.removeClass(f.elemsToToggle, 'hidden');
D.removeClass(document.body, 'chat-only-mode');
setTimeout(()=>document.body.scrollIntoView(
/*moves to (0,0), whereas scrollTo(0,0) does not!*/
), 0);
}
const msg = document.querySelector('.message-widget');
if(msg) msg.scrollIntoView();
return this;
},
toggleChatOnlyMode: function(){
return this.chatOnlyMode(!this.isChatOnlyMode());
},
settings:{
get: (k,dflt)=>F.storage.get(k,dflt),
getBool: (k,dflt)=>F.storage.getBool(k,dflt),
set: (k,v)=>F.storage.set(k,v),
defaults:{
"images-inline": !!F.config.chat.imagesInline,
"monospace-messages": false
|
| ︙ | ︙ | |||
143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
document.body.classList.add('my-messages-right');
}
if(cs.settings.getBool('monospace-messages',false)){
document.body.classList.add('monospace-messages');
}
cs.e.inputCurrent = cs.e.inputSingle;
cs.pageTitleOrig = cs.e.pageTitle.innerText;
const qs = (e)=>document.querySelector(e);
const argsToArray = function(args){
return Array.prototype.slice.call(args,0);
};
cs.reportError = function(/*msg args*/){
const args = argsToArray(arguments);
console.error("chat error:",args);
| > > > > > > > > > > > > > > > > > > | 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
document.body.classList.add('my-messages-right');
}
if(cs.settings.getBool('monospace-messages',false)){
document.body.classList.add('monospace-messages');
}
cs.e.inputCurrent = cs.e.inputSingle;
cs.pageTitleOrig = cs.e.pageTitle.innerText;
if(true){
/* In order to make the input area opaque, such that the message
list scrolls under it without being visible, we have to
ensure that the input area has a non-transparent background
color. Ideally we'd select the color of div.content, but that
is not necessarily set, so we fall back to using the body's
background color and hope it's been explicitly set
somewhere. If we rely on the input area having its own color
specified in CSS then all skins would have to define an
appropriate color. Thus our selection of the body color,
while slightly unfortunate, is in the interest of keeping
skins from being forced to define an opaque bg color.
*/
const bodyStyle = window.getComputedStyle(document.body);
cs.e.inputWrapper.style.backgroundColor = bodyStyle.backgroundColor;
}
const qs = (e)=>document.querySelector(e);
const argsToArray = function(args){
return Array.prototype.slice.call(args,0);
};
cs.reportError = function(/*msg args*/){
const args = argsToArray(arguments);
console.error("chat error:",args);
|
| ︙ | ︙ | |||
515 516 517 518 519 520 521 |
f.popup.show(x, y);
}/*handleLegendClicked()*/;
(function(){/*Set up #chat-settings-button */
const settingsButton = document.querySelector('#chat-settings-button');
var popupSize = undefined/*placement workaround*/;
const settingsPopup = new F.PopupWidget({
| | < < < < | | < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 |
f.popup.show(x, y);
}/*handleLegendClicked()*/;
(function(){/*Set up #chat-settings-button */
const settingsButton = document.querySelector('#chat-settings-button');
var popupSize = undefined/*placement workaround*/;
const settingsPopup = new F.PopupWidget({
cssClass: ['fossil-tooltip', 'chat-settings-popup']
});
/* Settings menu entries... */
const settingsOps = [{
label: "Multi-line input",
boolValue: ()=>Chat.inputElement()===Chat.e.inputMulti,
callback: function(){
Chat.inputToggleSingleMulti();
}
},{
label: "Monospace message font",
boolValue: ()=>document.body.classList.contains('monospace-messages'),
callback: function(){
document.body.classList.toggle('monospace-messages');
Chat.settings.set('monospace-messages',
document.body.classList.contains('monospace-messages'));
}
},{
label: "Chat-only mode",
boolValue: ()=>Chat.isChatOnlyMode(),
callback: function(){
Chat.toggleChatOnlyMode();
}
},{
label: "Left-align my posts",
boolValue: ()=>!document.body.classList.contains('my-messages-right'),
callback: function f(){
document.body.classList.toggle('my-messages-right');
}
|
| ︙ | ︙ | |||
633 634 635 636 637 638 639 |
//ev.preventDefault();
if(settingsPopup.isShown()) settingsPopup.hide();
else settingsPopup.show(settingsButton);
/* Reminder: we cannot toggle the visibility from her
*/
}, false);
| | > > > > > > > > | 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 |
//ev.preventDefault();
if(settingsPopup.isShown()) settingsPopup.hide();
else settingsPopup.show(settingsButton);
/* Reminder: we cannot toggle the visibility from her
*/
}, false);
/* Find an ideal X/Y position for the popup, directly above the settings
button, based on the size of the popup... */
settingsPopup.show(document.body);
popupSize = settingsPopup.e.getBoundingClientRect();
settingsPopup.hide();
settingsPopup.options.adjustX = function(x){
const rect = settingsButton.getBoundingClientRect();
return rect.right - popupSize.width;
};
settingsPopup.options.adjustY = function(y){
const rect = settingsButton.getBoundingClientRect();
if(Chat.isChatOnlyMode()){
return rect.top - popupSize.height -2;
}else{
return rect.bottom + 2;
}
};
})()/*#chat-settings-button setup*/;
/** Callback for poll() to inject new content into the page. jx ==
the response from /chat-poll. If atEnd is true, the message is
appended to the end of the chat list, else the beginning (the
|
| ︙ | ︙ |
Changes to src/default.css.
| ︙ | ︙ | |||
1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 |
border-radius: 0.25em;
}
.settings-icon:hover {
border: 1px outset rgba(127,127,127,1);
}
body.fossil-dark-style .settings-icon {
filter: invert(100%);
}
body.chat #chat-settings-button {
}
/** Popup widget for the /chat settings. */
body.chat .chat-settings-popup {
font-size: 0.8em;
text-align: left;
| > > > > | 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 |
border-radius: 0.25em;
}
.settings-icon:hover {
border: 1px outset rgba(127,127,127,1);
}
body.fossil-dark-style .settings-icon {
filter: invert(100%);
}
/* "Chat-only mode" hides the site header/footer, showing only
the chat app. */
body.chat.chat-only-mode{
}
body.chat #chat-settings-button {
}
/** Popup widget for the /chat settings. */
body.chat .chat-settings-popup {
font-size: 0.8em;
text-align: left;
|
| ︙ | ︙ | |||
1638 1639 1640 1641 1642 1643 1644 |
cursor: inherit;
}
/** Container for the list of /chat messages. */
body.chat #chat-messages-wrapper {
display: flex;
flex-direction: column;
}
| < < | > > > > > > | < < < < < | < < < < < < < < < < < < < > > > > > > > > > > > > > > > | 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 |
cursor: inherit;
}
/** Container for the list of /chat messages. */
body.chat #chat-messages-wrapper {
display: flex;
flex-direction: column;
}
body.chat.chat-only-mode #chat-messages-wrapper {
flex-direction: column-reverse;
position: relative;
top: 0;
z-index: 99 /* so that it scrolls under input area. If it's
lower than div.content then mouse events to it
are blocked!*/;
}
body.chat.chat-only-mode > div.content {
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: stretch;
}
body.chat.chat-only-mode > div.content {
flex-direction: column-reverse;
}
/* Wrapper for /chat user input controls */
body.chat #chat-input-area {
display: flex;
flex-direction: column;
border-bottom: 1px solid black;
padding: 0.5em 1em;
margin-bottom: 0.5em;
/*position: sticky; top: 0;*/
/*position: -webkit-sticky*/ /* supposedly some versions of Safari */;
}
body.chat.chat-only-mode #chat-input-area {
z-index: 100
/* see notes in #chat-messages-wrapper. The various popups require a
z-index higher than this one. */;
border-bottom: none;
border-top: 1px solid black;
margin-bottom: 0;
margin-top: 0.5em;
position: sticky;
position: -webkit-sticky/* supposedly some versions of Safari */;
bottom: 0;
}
body.chat #chat-input-line {
display: flex;
flex-direction: row;
margin-bottom: 0.25em;
align-items: flex-start;
}
|
| ︙ | ︙ |