Fossil

Check-in [fb9026e264]
Login

Check-in [fb9026e264]

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

Overview
Comment:Reimplemented chat message operations popup as an inlined DOM element to enable a confirmation option on the global delete button, per request from drh (and it's also more platform-portable).
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: fb9026e2648cec21d6ad4081e304dd633481202d1fce18d585d1a7d6dacbed9b
User & Date: stephan 2021-09-21 16:10:28.206
Context
2021-09-21
16:28
Removed some extraneous console debug output. ... (check-in: 8663dde1df user: stephan tags: trunk)
16:10
Reimplemented chat message operations popup as an inlined DOM element to enable a confirmation option on the global delete button, per request from drh (and it's also more platform-portable). ... (check-in: fb9026e264 user: stephan tags: trunk)
15:02
Updated changelog for chat preview. Hyperlinks in preview mode now have target=_blank. Removed extraneous FORM element around chat input area, as it caused only grief with regards to Enter key handling (always activating the first button in the list, which is now the preview button). ... (check-in: 60ed1ff951 user: stephan tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/chat.c.
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
  @ <button>Close Settings</button>
  @ </div>
  @ <div id='chat-messages-wrapper'>
  /* New chat messages get inserted immediately after this element */
  @ <span id='message-inject-point'></span>
  @ </div>
  fossil_free(zProjectName);
  builtin_fossil_js_bundle_or("popupwidget", "storage",
                              "fetch", "pikchr", NULL);
  /* Always in-line the javascript for the chat page */
  @ <script nonce="%h(style_nonce())">/* chat.c:%d(__LINE__) */
  /* We need an onload handler to ensure that window.fossil is
     initialized before the chat init code runs. */
  @ window.addEventListener('load', function(){
  @ document.body.classList.add('chat')
  @ /*^^^for skins which add their own BODY tag */;







|
|







194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
  @ <button>Close Settings</button>
  @ </div>
  @ <div id='chat-messages-wrapper'>
  /* New chat messages get inserted immediately after this element */
  @ <span id='message-inject-point'></span>
  @ </div>
  fossil_free(zProjectName);
  builtin_fossil_js_bundle_or("popupwidget", "storage", "fetch",
                              "pikchr", "confirmer", NULL);
  /* Always in-line the javascript for the chat page */
  @ <script nonce="%h(style_nonce())">/* chat.c:%d(__LINE__) */
  /* We need an onload handler to ensure that window.fossil is
     initialized before the chat init code runs. */
  @ window.addEventListener('load', function(){
  @ document.body.classList.add('chat')
  @ /*^^^for skins which add their own BODY tag */;
Changes to src/chat.js.
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
    /**
       Constructor. If passed an argument, it is passed to
       this.setMessage() after initialization.
    */
    const cf = function(){
      this.e = {
        body: D.addClass(D.div(), 'message-widget'),
        tab: D.addClass(D.span(), 'message-widget-tab'),
        content: D.addClass(D.div(), 'message-widget-content')
      };
      D.append(this.e.body, this.e.tab, this.e.content);
      this.e.tab.setAttribute('role', 'button');
      if(arguments.length){
        this.setMessage(arguments[0]);
      }







|







628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
    /**
       Constructor. If passed an argument, it is passed to
       this.setMessage() after initialization.
    */
    const cf = function(){
      this.e = {
        body: D.addClass(D.div(), 'message-widget'),
        tab: D.addClass(D.div(), 'message-widget-tab'),
        content: D.addClass(D.div(), 'message-widget-content')
      };
      D.append(this.e.body, this.e.tab, this.e.content);
      this.e.tab.setAttribute('role', 'button');
      if(arguments.length){
        this.setMessage(arguments[0]);
      }
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
        }
        const d = new Date(m.mtime);
        D.clearElement(this.e.tab);
        var contentTarget = this.e.content;
        var eXFrom /* element holding xfrom name */;
        if(m.xfrom){
          eXFrom = D.append(D.addClass(D.span(), 'xfrom'), m.xfrom);
          D.append(
            this.e.tab, eXFrom,
            D.text(" #",(m.msgid||'???'),' @ ',theTime(d))
          );
        }else{/*notification*/
          D.addClass(this.e.body, 'notification');
          if(m.isError){
            D.addClass([contentTarget, this.e.tab], 'error');
          }
          D.append(
            this.e.tab,







|
|
|
|







690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
        }
        const d = new Date(m.mtime);
        D.clearElement(this.e.tab);
        var contentTarget = this.e.content;
        var eXFrom /* element holding xfrom name */;
        if(m.xfrom){
          eXFrom = D.append(D.addClass(D.span(), 'xfrom'), m.xfrom);
          const wrapper = D.append(
            D.span(), eXFrom,
            D.text(" #",(m.msgid||'???'),' @ ',theTime(d)))
          D.append(this.e.tab, wrapper);
        }else{/*notification*/
          D.addClass(this.e.body, 'notification');
          if(m.isError){
            D.addClass([contentTarget, this.e.tab], 'error');
          }
          D.append(
            this.e.tab,
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
            contentTarget.innerHTML = m.xmsg;
            contentTarget.querySelectorAll('a').forEach(addAnchorTargetBlank);
            if(F.pikchr){
              F.pikchr.addSrcView(contentTarget.querySelectorAll('svg.pikchr'));
            }
          }
        }
        this.e.tab.addEventListener('click', this._handleLegendClicked, false);
        if(eXFrom){
          eXFrom.addEventListener('click', ()=>this.e.tab.click(), false);
        }
        return this;
      },
      /* Event handler for clicking .message-user elements to show their
         timestamps. */
      _handleLegendClicked: function f(ev){
        if(!f.popup){
          /* Timestamp popup widget */
          f.popup = new F.PopupWidget({
            cssClass: ['fossil-tooltip', 'chat-message-popup'],
            refresh:function(){
              const eMsg = this._eMsg;
              if(!eMsg) return;
              D.clearElement(this.e);
              const d = new Date(eMsg.dataset.timestamp);
              if(d.getMinutes().toString()!=="NaN"){
                // Date works, render informative timestamps
                const xfrom = eMsg.dataset.xfrom || 'server';
                D.append(this.e,







|
|

|







|
|

|







749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
            contentTarget.innerHTML = m.xmsg;
            contentTarget.querySelectorAll('a').forEach(addAnchorTargetBlank);
            if(F.pikchr){
              F.pikchr.addSrcView(contentTarget.querySelectorAll('svg.pikchr'));
            }
          }
        }
        this.e.tab.firstElementChild.addEventListener('click', this._handleLegendClicked, false);
        /*if(eXFrom){
          eXFrom.addEventListener('click', ()=>this.e.tab.click(), false);
        }*/
        return this;
      },
      /* Event handler for clicking .message-user elements to show their
         timestamps. */
      _handleLegendClicked: function f(ev){
        if(!f.popup){
          /* Timestamp popup widget */
          f.popup = {
            e: D.addClass(D.div(), 'chat-message-popup'),
            refresh:function(){
              const eMsg = this.$eMsg/*.message-widget element*/;
              if(!eMsg) return;
              D.clearElement(this.e);
              const d = new Date(eMsg.dataset.timestamp);
              if(d.getMinutes().toString()!=="NaN"){
                // Date works, render informative timestamps
                const xfrom = eMsg.dataset.xfrom || 'server';
                D.append(this.e,
800
801
802
803
804
805
806
807




808
809

810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830



831
832
833
834

835



836
837




838
839
840
841

842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
              btnDeleteLocal.addEventListener('click', function(){
                self.hide();
                Chat.deleteMessageElem(eMsg);
              });
              if(Chat.userMayDelete(eMsg)){
                const btnDeleteGlobal = D.button("Delete globally");
                D.append(toolbar, btnDeleteGlobal);
                btnDeleteGlobal.addEventListener('click', function(){




                  self.hide();
                  Chat.deleteMessage(eMsg);

                });
              }
              const toolbar2 = D.addClass(D.div(), 'toolbar');
              D.append(this.e, toolbar2);
              const btnToggleText = D.button("Toggle text mode");
              btnToggleText.addEventListener('click', function(){
                self.hide();
                Chat.toggleTextMode(eMsg);
              });
              D.append(toolbar2, btnToggleText);
              if(eMsg.dataset.xfrom){
                /* Add a link to the /timeline filtered on this user. */
                const timelineLink = D.attr(
                  D.a(F.repoUrl('timeline',{
                    u: eMsg.dataset.xfrom,
                    y: 'a'
                  }), "User's Timeline"),
                  'target', '_blank'
                );
                D.append(toolbar2, timelineLink);
              }



            }/*refresh()*/
          });
          f.popup.installHideHandlers();
          f.popup.hide = function(){

            delete this._eMsg;



            D.clearElement(this.e);
            return this.show(false);




          };
        }/*end static init*/
        const rect = ev.target.getBoundingClientRect();
        const eMsg = ev.target.parentNode/*the owning .message-widget element*/;

        f.popup._eMsg = eMsg;
        let x = rect.left, y = rect.topm;
        f.popup.show(ev.target)/*so we can get its computed size*/;
        if(eMsg.dataset.xfrom===Chat.me
           && document.body.classList.contains('my-messages-right')){
          // Shift popup to the left for right-aligned messages to avoid
          // truncation off the right edge of the page.
          const pRect = f.popup.e.getBoundingClientRect();
          x = rect.right - pRect.width;
        }
        f.popup.show(x, y);
      }/*_handleLegendClicked()*/
    };
    return cf;
  })()/*MessageWidget*/;

  const BlobXferState = (function(){/*drag/drop bits...*/
    /* State for paste and drag/drop */







|
>
>
>
>
|
|
>








|












>
>
>
|
<
<
|
>
|
>
>
>
|
|
>
>
>
>
|

|
<
>
|
<
<
<
|
<
<
<
|

|







800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839


840
841
842
843
844
845
846
847
848
849
850
851
852
853
854

855
856



857



858
859
860
861
862
863
864
865
866
867
              btnDeleteLocal.addEventListener('click', function(){
                self.hide();
                Chat.deleteMessageElem(eMsg);
              });
              if(Chat.userMayDelete(eMsg)){
                const btnDeleteGlobal = D.button("Delete globally");
                D.append(toolbar, btnDeleteGlobal);
                F.confirmer(btnDeleteGlobal,{
                  pinSize: true,
                  ticks: F.config.confirmerButtonTicks,
                  confirmText: "Confirm delete?",
                  onconfirm:function(){
                    self.hide();
                    Chat.deleteMessage(eMsg);
                  }
                });
              }
              const toolbar2 = D.addClass(D.div(), 'toolbar');
              D.append(this.e, toolbar2);
              const btnToggleText = D.button("Toggle text mode");
              btnToggleText.addEventListener('click', function(){
                self.hide();
                Chat.toggleTextMode(eMsg);
              },false);
              D.append(toolbar2, btnToggleText);
              if(eMsg.dataset.xfrom){
                /* Add a link to the /timeline filtered on this user. */
                const timelineLink = D.attr(
                  D.a(F.repoUrl('timeline',{
                    u: eMsg.dataset.xfrom,
                    y: 'a'
                  }), "User's Timeline"),
                  'target', '_blank'
                );
                D.append(toolbar2, timelineLink);
              }
              const tab = eMsg.querySelector('.message-widget-tab');
              D.append(tab, this.e);
              D.removeClass(this.e, 'hidden');
            }/*refresh()*/,


            hide: function(){
              D.addClass(D.clearElement(this.e), 'hidden');
              delete this.$eMsg;
            },
            show: function(tgtMsg){
              if(tgtMsg === this.$eMsg){
                this.hide();
                return;
              }
              this.$eMsg = tgtMsg;
              this.refresh();
            }
          }/*f.popup*/;
        }/*end static init*/
        console.debug("event =",ev);

        console.debug("event.target =",ev.target);
        let theMsg = ev.target;



        while( theMsg && !theMsg.classList.contains('message-widget')){



          theMsg = theMsg.parentNode;
        }
        if(theMsg) f.popup.show(theMsg);
      }/*_handleLegendClicked()*/
    };
    return cf;
  })()/*MessageWidget*/;

  const BlobXferState = (function(){/*drag/drop bits...*/
    /* State for paste and drag/drop */
Changes to src/style.chat.css.
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  font-style: italic;
  font-weight: bold;
}
/* The popup element for displaying message timestamps
   and deletion controls. */
body.chat .chat-message-popup {
  font-family: monospace;
  font-size: 0.8em;
  text-align: left;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  padding: 0.25em;
  z-index: 200;
}
/* Full message timestamps. */
body.chat .chat-message-popup > span { white-space: nowrap; }
/* Container for the message deletion buttons. */
body.chat .chat-message-popup > .toolbar {
  padding: 0;
  margin: 0;







|





|







68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  font-style: italic;
  font-weight: bold;
}
/* The popup element for displaying message timestamps
   and deletion controls. */
body.chat .chat-message-popup {
  font-family: monospace;
  font-size: 0.9em;
  text-align: left;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  padding: 0.25em;
  margin-top: 0.25em;
}
/* Full message timestamps. */
body.chat .chat-message-popup > span { white-space: nowrap; }
/* Container for the message deletion buttons. */
body.chat .chat-message-popup > .toolbar {
  padding: 0;
  margin: 0;