Fossil

Check-in [a1161fa9bd]
Login

Check-in [a1161fa9bd]

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

Overview
Comment:chat: next round of Safari-friendly baby steps, developed in conjunction with Safari user mgagnon via chat session.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: a1161fa9bd6587f81e30dd4f0b0170eb83396f8519550e5a2367230b4b62efa3
User & Date: stephan 2020-12-27 06:48:24.808
Context
2020-12-27
07:03
chat: fixed mis-scroll when toggling chat-only mode. Re-re-introduced the img preview size cap. ... (check-in: b09f7e990d user: stephan tags: trunk)
06:48
chat: next round of Safari-friendly baby steps, developed in conjunction with Safari user mgagnon via chat session. ... (check-in: a1161fa9bd user: stephan tags: trunk)
03:39
Eliminated top-down chat mode altogether in an attempt to eliminate some complexity and cruft. Re-added the toast-on-new-invisible-message from [0a00a103]. ... (check-in: 421d657078 user: stephan tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/chat.js.
14
15
16
17
18
19
20

























21
22
23
24
25
26
27
    return (
      rect.top >= 0 &&
      rect.left >= 0 &&
      rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
      rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );
  };

























  const Chat = (function(){
    const cs = {
      e:{/*map of certain DOM elements.*/
        messageInjectPoint: E1('#message-inject-point'),
        pageTitle: E1('head title'),
        loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */,
        inputWrapper: E1("#chat-input-area"),







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
    return (
      rect.top >= 0 &&
      rect.left >= 0 &&
      rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
      rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );
  };

  const ForceResizeKludge = 0 ? function(){} : (function(){
    /* Workaround for Safari mayhem regarding use of vh CSS units....
       We tried to use vh units to set the content area size for the
       chat layout, but Safari chokes on that, so we calculate that
       height here: 85% when in "normal" mode and 95% in chat-only
       mode. Larger than ~95% is too big for Firefox on Android,
       causing the input area to move off-screen. */
    const contentArea = E1('div.content'),
          bcl = document.body.classList;
    const resized = function(){
      const wh = window.innerHeight,
            mult = bcl.contains('chat-only-mode') ? 0.95 : 0.85;
      contentArea.style.height = contentArea.style.maxHeight = (wh * mult)+"px";
      //console.debug("resized.",wh, mult, window.getComputedStyle(contentArea).maxHeight);
    };
    var doit;
    window.addEventListener('resize',function(ev){
      clearTimeout(doit);
      doit = setTimeout(resized, 100);
    }, false);
    resized();
    return resized;
  })();

  const Chat = (function(){
    const cs = {
      e:{/*map of certain DOM elements.*/
        messageInjectPoint: E1('#message-inject-point'),
        pageTitle: E1('head title'),
        loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */,
        inputWrapper: E1("#chat-input-area"),
126
127
128
129
130
131
132

133
134
135
136
137
138
139
140


141
142
143
144
145
146
147
              holder = this.e.messagesWrapper;
        if(atEnd){
          const fe = mip.nextElementSibling;
          if(fe) mip.parentNode.insertBefore(e, fe);
          else D.append(mip.parentNode, e);
        }else{
          D.append(holder,e);

        }
        if(!atEnd && !this.isMassLoading
           && e.dataset.xfrom!==Chat.me && !isInViewport(e)){
          /* If a new non-history message arrives while the user is
             scrolled elsewhere, do not scroll to the latest
             message, but gently alert the user that a new message
             has arrived. */
          F.toast.message("New message has arrived.");


        }
      },
      /** Returns true if chat-only mode is enabled. */
      isChatOnlyMode: ()=>document.body.classList.contains('chat-only-mode'),
      /**
         Enters (if passed a truthy value or no arguments) or leaves
         "chat-only" mode. That mode hides the page's header and







>








>
>







151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
              holder = this.e.messagesWrapper;
        if(atEnd){
          const fe = mip.nextElementSibling;
          if(fe) mip.parentNode.insertBefore(e, fe);
          else D.append(mip.parentNode, e);
        }else{
          D.append(holder,e);
          Chat.newestMessageElem = e;
        }
        if(!atEnd && !this.isMassLoading
           && e.dataset.xfrom!==Chat.me && !isInViewport(e)){
          /* If a new non-history message arrives while the user is
             scrolled elsewhere, do not scroll to the latest
             message, but gently alert the user that a new message
             has arrived. */
          F.toast.message("New message has arrived.");
        }else if(e.dataset.xfrom===Chat.me){
          e.scrollIntoView();
        }
      },
      /** Returns true if chat-only mode is enabled. */
      isChatOnlyMode: ()=>document.body.classList.contains('chat-only-mode'),
      /**
         Enters (if passed a truthy value or no arguments) or leaves
         "chat-only" mode. That mode hides the page's header and
163
164
165
166
167
168
169

170
171
172
173
174
175
176
          document.body.scroll(0,document.body.height);
        }else{
          D.removeClass(f.elemsToToggle, 'hidden');
          D.removeClass(document.body, 'chat-only-mode');
        }
        const msg = document.querySelector('.message-widget');
        if(msg) setTimeout(()=>msg.scrollIntoView(),0);

        return this;
      },
      toggleChatOnlyMode: function(){
        return this.chatOnlyMode(!this.isChatOnlyMode());
      },
      settings:{
        get: (k,dflt)=>F.storage.get(k,dflt),







>







191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
          document.body.scroll(0,document.body.height);
        }else{
          D.removeClass(f.elemsToToggle, 'hidden');
          D.removeClass(document.body, 'chat-only-mode');
        }
        const msg = document.querySelector('.message-widget');
        if(msg) setTimeout(()=>msg.scrollIntoView(),0);
        ForceResizeKludge();
        return this;
      },
      toggleChatOnlyMode: function(){
        return this.chatOnlyMode(!this.isChatOnlyMode());
      },
      settings:{
        get: (k,dflt)=>F.storage.get(k,dflt),
227
228
229
230
231
232
233








234
235
236
237
238
239
240
241
242
243
244
245
246
247
248



249
250
251
252
253
254
255
      console.error("chat error:",args);
      F.toast.error.apply(F.toast, args);
    };

    cs.getMessageElemById = function(id){
      return qs('[data-msgid="'+id+'"]');
    };








    /**
       LOCALLY deletes a message element by the message ID or passing
       the .message-row element. Returns true if it removes an element,
       else false.
    */
    cs.deleteMessageElem = function(id){
      var e;
      if(id instanceof HTMLElement){
        e = id;
        id = e.dataset.msgid;
      }else{
        e = this.getMessageElemById(id);
      }
      if(e && id){
        D.remove(e);



        F.toast.message("Deleted message "+id+".");
      }
      return !!e;
    };

    /** Given a .message-row element, this function returns whethe the
        current user may, at least hypothetically, delete the message







>
>
>
>
>
>
>
>















>
>
>







256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
      console.error("chat error:",args);
      F.toast.error.apply(F.toast, args);
    };

    cs.getMessageElemById = function(id){
      return qs('[data-msgid="'+id+'"]');
    };

    /** Finds the last .message-widget element and returns it or
        the undefined value if none are found. */
    cs.fetchLastMessageElem = function(){
      const msgs = document.querySelectorAll('.message-widget');
      return msgs.length ? msgs[msgs.length-1] : undefined;
    };

    /**
       LOCALLY deletes a message element by the message ID or passing
       the .message-row element. Returns true if it removes an element,
       else false.
    */
    cs.deleteMessageElem = function(id){
      var e;
      if(id instanceof HTMLElement){
        e = id;
        id = e.dataset.msgid;
      }else{
        e = this.getMessageElemById(id);
      }
      if(e && id){
        D.remove(e);
        if(e===this.newestMessageElem){
          Chat.newestMessageElem = Chat.fetchLastMessageElem();
        }
        F.toast.message("Deleted message "+id+".");
      }
      return !!e;
    };

    /** Given a .message-row element, this function returns whethe the
        current user may, at least hypothetically, delete the message
817
818
819
820
821
822
823
824
825
826
827






828

829
830
831
832
833
834
835
836
837
    var p = fetch("chat-poll?name=" + Chat.mxMsg);
    p.then(x=>x.json())
      .then(y=>newcontent(y))
      .catch(e=>console.error(e))
    /* ^^^ we don't use Chat.reportError(e) here b/c the polling
       fails exepectedly when it times out, but is then immediately
       resumed, and reportError() produces a loud error message. */
      .finally(function(x){
        if(isFirstCall){
          Chat.isMassLoading = false;
          Chat.ajaxEnd();






          Chat.e.inputWrapper.scrollIntoView();

        }
        poll.running=false;
      });
  }
  poll.running = false;
  poll(true);
  setInterval(poll, 1000);
  F.page.chat = Chat/* enables testing the APIs via the dev tools */;
})();







|



>
>
>
>
>
>
|
>









857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
    var p = fetch("chat-poll?name=" + Chat.mxMsg);
    p.then(x=>x.json())
      .then(y=>newcontent(y))
      .catch(e=>console.error(e))
    /* ^^^ we don't use Chat.reportError(e) here b/c the polling
       fails exepectedly when it times out, but is then immediately
       resumed, and reportError() produces a loud error message. */
      .finally(function(){
        if(isFirstCall){
          Chat.isMassLoading = false;
          Chat.ajaxEnd();
          setTimeout(function(){
            const m = Chat.newestMessageElem;
            if(m){
              m.scrollIntoView();
              //console.debug("Scrolling into view...",msgs[msgs.length-1]);
            }
            Chat.e.inputWrapper.scrollIntoView()
          }, 0);
        }
        poll.running=false;
      });
  }
  poll.running = false;
  poll(true);
  setInterval(poll, 1000);
  F.page.chat = Chat/* enables testing the APIs via the dev tools */;
})();
Changes to src/default.css.
1634
1635
1636
1637
1638
1639
1640



1641
1642
1643
1644
1645
1646
1647
  flex: 1 1 auto/*eliminates dead no-click zones on the right*/;
}
body.chat .chat-settings-popup > span.menu-entry > input[type=checkbox] {
  cursor: inherit;
}
/** Container for the list of /chat messages. */
body.chat #chat-messages-wrapper {



}
body.chat div.content {
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column-reverse;
  /* ^^^^ In order to get good automatic scrolling of new messages on







>
>
>







1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
  flex: 1 1 auto/*eliminates dead no-click zones on the right*/;
}
body.chat .chat-settings-popup > span.menu-entry > input[type=checkbox] {
  cursor: inherit;
}
/** Container for the list of /chat messages. */
body.chat #chat-messages-wrapper {
  overflow: auto;
  /*max-height: 800em*//*will be re-calc'd in JS*/;
  flex: 2 1 auto;
}
body.chat div.content {
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column-reverse;
  /* ^^^^ In order to get good automatic scrolling of new messages on