Fossil

Check-in [deb9963ac6]
Login

Check-in [deb9963ac6]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Chat: hide message home/end buttons by default in portrait mode and add a menu toggle for them, and swapped the button positions (seems more natural). Minor tweak to the div.content resize algo to make use of CSS calc().
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: deb9963ac6daca869b43a48ad266e2514b867de8741ad82e2b0682b0a3225f38
User & Date: stephan 2020-12-27 17:42:37.859
Context
2020-12-27
17:44
Doc typo fix reported in the forum. ... (check-in: 988c599810 user: stephan tags: trunk)
17:42
Chat: hide message home/end buttons by default in portrait mode and add a menu toggle for them, and swapped the button positions (seems more natural). Minor tweak to the div.content resize algo to make use of CSS calc(). ... (check-in: deb9963ac6 user: stephan tags: trunk)
09:56
Some flicker reduction when batch loading chat messages. Minor chat layout tweaks. ... (check-in: 5e046b64c7 user: stephan tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/chat.c.
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  @ <form accept-encoding="utf-8" id="chat-form" autocomplete="off">
  @   <div id='chat-input-line'>
  @     <input type="text" name="msg" id="chat-input-single" \
  @      placeholder="Type message here." autocomplete="off">
  @     <textarea rows="8" id="chat-input-multi" \
  @      placeholder="Type message here" class="hidden"></textarea>
  @     <input type="submit" value="Send" id="chat-message-submit">
  @     <button id="chat-scroll-bottom">&darr;</button>
  @     <button id="chat-scroll-top">&uarr;</button>
  @     <span id="chat-settings-button" class="settings-icon" \
  @       aria-label="Settings..." aria-haspopup="true" ></span>
  @   </div>
  @   <div id='chat-input-file-area'>
  @     <div class='file-selection-wrapper'>
  @       <div class='help-buttonlet'>
  @        Select a file to upload, drag/drop a file into this spot,







|
|







118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  @ <form accept-encoding="utf-8" id="chat-form" autocomplete="off">
  @   <div id='chat-input-line'>
  @     <input type="text" name="msg" id="chat-input-single" \
  @      placeholder="Type message here." autocomplete="off">
  @     <textarea rows="8" id="chat-input-multi" \
  @      placeholder="Type message here" class="hidden"></textarea>
  @     <input type="submit" value="Send" id="chat-message-submit">
  @     <button id="chat-scroll-top">&uarr;</button>
  @     <button id="chat-scroll-bottom">&darr;</button>
  @     <span id="chat-settings-button" class="settings-icon" \
  @       aria-label="Settings..." aria-haspopup="true" ></span>
  @   </div>
  @   <div id='chat-input-file-area'>
  @     <div class='file-selection-wrapper'>
  @       <div class='help-buttonlet'>
  @        Select a file to upload, drag/drop a file into this spot,
Changes to src/chat.js.
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63



64
65
66
67
68
69
70
    }
    const bcl = document.body.classList;
    const resized = function(){
      const wh = window.innerHeight,
            com = bcl.contains('chat-only-mode');
      var ht;
      if(com){
        ht = wh - 10/*fudge value*/;
      }else{
        f.extra = 0;
        [f.eHead, f.eMenu, f.eFoot].forEach(f.measure);
        ht = wh - f.extra - 10/*fudge value*/;
      }
      f.contentArea.style.height =
        f.contentArea.style.maxHeight = (ht>=100 ? ht : 100)+"px";



      if(false){
        console.debug("resized.",wh, f.extra, ht,
                      window.getComputedStyle(f.contentArea).maxHeight,
                      f.contentArea);
      }
    };
    var doit;







|



|


|
>
>
>







49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
    }
    const bcl = document.body.classList;
    const resized = function(){
      const wh = window.innerHeight,
            com = bcl.contains('chat-only-mode');
      var ht;
      if(com){
        ht = wh;
      }else{
        f.extra = 0;
        [f.eHead, f.eMenu, f.eFoot].forEach(f.measure);
        ht = wh - f.extra;
      }
      f.contentArea.style.height =
        f.contentArea.style.maxHeight = [
          "calc(", (ht>=100 ? ht : 100), "px",
          " - 1em"/*fudge value*/,")"
        ].join('');
      if(false){
        console.debug("resized.",wh, f.extra, ht,
                      window.getComputedStyle(f.contentArea).maxHeight,
                      f.contentArea);
      }
    };
    var doit;
87
88
89
90
91
92
93
94


95
96
97
98
99
100
101
        messagesWrapper: E1('#chat-messages-wrapper'),
        inputForm: E1('#chat-form'),
        btnSubmit: E1('#chat-message-submit'),
        inputSingle: E1('#chat-input-single'),
        inputMulti: E1('#chat-input-multi'),
        inputCurrent: undefined/*one of inputSingle or inputMulti*/,
        inputFile: E1('#chat-input-file'),
        contentDiv: E1('div.content')


      },
      me: F.user.name,
      mxMsg: F.config.chat.initSize ? -F.config.chat.initSize : -50,
      mnMsg: undefined/*lowest message ID we've seen so far (for history loading)*/,
      pageIsActive: 'visible'===document.visibilityState,
      changesSincePageHidden: 0,
      notificationBubbleColor: 'white',







|
>
>







90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
        messagesWrapper: E1('#chat-messages-wrapper'),
        inputForm: E1('#chat-form'),
        btnSubmit: E1('#chat-message-submit'),
        inputSingle: E1('#chat-input-single'),
        inputMulti: E1('#chat-input-multi'),
        inputCurrent: undefined/*one of inputSingle or inputMulti*/,
        inputFile: E1('#chat-input-file'),
        contentDiv: E1('div.content'),
        btnMsgHome: E1('#chat-scroll-top'),
        btnMsgEnd: E1('#chat-scroll-bottom')
      },
      me: F.user.name,
      mxMsg: F.config.chat.initSize ? -F.config.chat.initSize : -50,
      mnMsg: undefined/*lowest message ID we've seen so far (for history loading)*/,
      pageIsActive: 'visible'===document.visibilityState,
      changesSincePageHidden: 0,
      notificationBubbleColor: 'white',
293
294
295
296
297
298
299








300
301
302
303
304
305
306
        }else if(Chat.e.newestMessage){
          Chat.e.newestMessage.scrollIntoView(false);
        }
      },
      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







>
>
>
>
>
>
>
>







298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
        }else if(Chat.e.newestMessage){
          Chat.e.newestMessage.scrollIntoView(false);
        }
      },
      toggleChatOnlyMode: function(){
        return this.chatOnlyMode(!this.isChatOnlyMode());
      },
      /* Turn the message area top/bottom buttons on (yes===true), off
         (yes==false), or toggle them (no arguments). Returns this. */
      toggleNavButtons: function(yes){
        const e = [this.e.btnMsgHome, this.e.btnMsgEnd], c = 'hidden';
        if(0===arguments.length) D.toggleClass(e, c);
        else if(!arguments[0]) D.addClass(e, c);
        else D.removeClass(e, c);
      },
      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
315
316
317
318
319
320
321

322
323
324
325
326
327
328
    if(window.innerWidth<window.innerHeight){
      /* Alignment of 'my' messages: right alignment is conventional
         for mobile chat apps but can be difficult to read in wide
         windows (desktop/tablet landscape mode), so we default to a
         layout based on the apparently "orientation" of the window:
         tall vs wide. Can be toggled via settings popup. */
      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;








>







328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
    if(window.innerWidth<window.innerHeight){
      /* Alignment of 'my' messages: right alignment is conventional
         for mobile chat apps but can be difficult to read in wide
         windows (desktop/tablet landscape mode), so we default to a
         layout based on the apparently "orientation" of the window:
         tall vs wide. Can be toggled via settings popup. */
      document.body.classList.add('my-messages-right');
      cs.toggleNavButtons(false);
    }
    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;

783
784
785
786
787
788
789




790
791
792
793
794
795
796
    },{
      label: "Left-align my posts",
      boolValue: ()=>!document.body.classList.contains('my-messages-right'),
      callback: function f(){
        document.body.classList.toggle('my-messages-right');
      }
    },{




      label: "Images inline",
      boolValue: ()=>Chat.settings.getBool('images-inline'),
      callback: function(){
        const v = Chat.settings.getBool('images-inline',true);
        Chat.settings.set('images-inline', !v);
        F.toast.message("Image mode set to "+(v ? "hyperlink" : "inline")+".");
      }







>
>
>
>







797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
    },{
      label: "Left-align my posts",
      boolValue: ()=>!document.body.classList.contains('my-messages-right'),
      callback: function f(){
        document.body.classList.toggle('my-messages-right');
      }
    },{
      label: "Message home/end buttons",
      boolValue: ()=>!Chat.e.btnMsgHome.classList.contains('hidden'),
      callback: ()=>Chat.toggleNavButtons()
    },{
      label: "Images inline",
      boolValue: ()=>Chat.settings.getBool('images-inline'),
      callback: function(){
        const v = Chat.settings.getBool('images-inline',true);
        Chat.settings.set('images-inline', !v);
        F.toast.message("Image mode set to "+(v ? "hyperlink" : "inline")+".");
      }
Changes to src/default.css.
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626


1627
1628
1629
1630
1631
1632
1633
}
body.chat .chat-settings-popup > span {
  vertical-align: middle;
}
body.chat .chat-settings-popup > span.menu-entry{
  white-space: nowrap;
  cursor: pointer;
  border: 1px outset;
  border-radius: 0.25em;
  padding: 0.25em 0.5em;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;


}
body.chat .chat-settings-popup > span.menu-entry > .help-buttonlet {
  vertical-align: middle;
}
body.chat .chat-settings-popup > span.menu-entry > span.button {
  margin: 0.25em 0.2em;
  padding: 0.5em;







|






>
>







1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
}
body.chat .chat-settings-popup > span {
  vertical-align: middle;
}
body.chat .chat-settings-popup > span.menu-entry{
  white-space: nowrap;
  cursor: pointer;
  border: 1px solid;
  border-radius: 0.25em;
  padding: 0.25em 0.5em;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
}
body.chat .chat-settings-popup > span.menu-entry:hover {
}
body.chat .chat-settings-popup > span.menu-entry > .help-buttonlet {
  vertical-align: middle;
}
body.chat .chat-settings-popup > span.menu-entry > span.button {
  margin: 0.25em 0.2em;
  padding: 0.5em;
1694
1695
1696
1697
1698
1699
1700

1701
1702
1703
1704
1705
1706
1707
  margin: 0 0.25em;
}
body.chat #chat-input-line > button {
  max-width: 4em;
}
body.chat #chat-input-line > #chat-settings-button{
  margin: 0 0 0 0.25em;

}
body.chat #chat-input-line > input[type=text],
body.chat #chat-input-line > textarea {
  flex: 5 1 auto;
}
/* Widget holding the file selection control and preview */
body.chat #chat-input-file-area  {







>







1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
  margin: 0 0.25em;
}
body.chat #chat-input-line > button {
  max-width: 4em;
}
body.chat #chat-input-line > #chat-settings-button{
  margin: 0 0 0 0.25em;
  max-width: 2em;
}
body.chat #chat-input-line > input[type=text],
body.chat #chat-input-line > textarea {
  flex: 5 1 auto;
}
/* Widget holding the file selection control and preview */
body.chat #chat-input-file-area  {
Changes to src/fossil.dom.js.
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
    return e;
  };

  /**
     "Blinks" the given element a single time for the given number of
     milliseconds, defaulting (if the 2nd argument is falsy or not a
     number) to flashOnce.defaultTimeMs. If a 3rd argument is passed
     in, it must be a function, and it gets callback back at the end
     of the asynchronous flashing processes.

     This will only activate once per element during that timeframe -
     further calls will become no-ops until the blink is
     completed. This routine adds a dataset member to the element for
     the duration of the blink, to allow it to block multiple blinks.

     If passed 2 arguments and the 2nd is a function, it behaves as if







|
|







628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
    return e;
  };

  /**
     "Blinks" the given element a single time for the given number of
     milliseconds, defaulting (if the 2nd argument is falsy or not a
     number) to flashOnce.defaultTimeMs. If a 3rd argument is passed
     in, it must be a function, and it gets called at the end of the
     asynchronous flashing processes.

     This will only activate once per element during that timeframe -
     further calls will become no-ops until the blink is
     completed. This routine adds a dataset member to the element for
     the duration of the blink, to allow it to block multiple blinks.

     If passed 2 arguments and the 2nd is a function, it behaves as if