Fossil SCM

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).

stephan 2021-09-21 16:10 trunk
Commit fb9026e2648cec21d6ad4081e304dd633481202d1fce18d585d1a7d6dacbed9b
3 files changed +2 -2 +44 -36 +2 -2
+2 -2
--- src/chat.c
+++ src/chat.c
@@ -196,12 +196,12 @@
196196
@ <div id='chat-messages-wrapper'>
197197
/* New chat messages get inserted immediately after this element */
198198
@ <span id='message-inject-point'></span>
199199
@ </div>
200200
fossil_free(zProjectName);
201
- builtin_fossil_js_bundle_or("popupwidget", "storage",
202
- "fetch", "pikchr", NULL);
201
+ builtin_fossil_js_bundle_or("popupwidget", "storage", "fetch",
202
+ "pikchr", "confirmer", NULL);
203203
/* Always in-line the javascript for the chat page */
204204
@ <script nonce="%h(style_nonce())">/* chat.c:%d(__LINE__) */
205205
/* We need an onload handler to ensure that window.fossil is
206206
initialized before the chat init code runs. */
207207
@ window.addEventListener('load', function(){
208208
--- src/chat.c
+++ src/chat.c
@@ -196,12 +196,12 @@
196 @ <div id='chat-messages-wrapper'>
197 /* New chat messages get inserted immediately after this element */
198 @ <span id='message-inject-point'></span>
199 @ </div>
200 fossil_free(zProjectName);
201 builtin_fossil_js_bundle_or("popupwidget", "storage",
202 "fetch", "pikchr", NULL);
203 /* Always in-line the javascript for the chat page */
204 @ <script nonce="%h(style_nonce())">/* chat.c:%d(__LINE__) */
205 /* We need an onload handler to ensure that window.fossil is
206 initialized before the chat init code runs. */
207 @ window.addEventListener('load', function(){
208
--- src/chat.c
+++ src/chat.c
@@ -196,12 +196,12 @@
196 @ <div id='chat-messages-wrapper'>
197 /* New chat messages get inserted immediately after this element */
198 @ <span id='message-inject-point'></span>
199 @ </div>
200 fossil_free(zProjectName);
201 builtin_fossil_js_bundle_or("popupwidget", "storage", "fetch",
202 "pikchr", "confirmer", NULL);
203 /* Always in-line the javascript for the chat page */
204 @ <script nonce="%h(style_nonce())">/* chat.c:%d(__LINE__) */
205 /* We need an onload handler to ensure that window.fossil is
206 initialized before the chat init code runs. */
207 @ window.addEventListener('load', function(){
208
+44 -36
--- src/chat.js
+++ src/chat.js
@@ -630,11 +630,11 @@
630630
this.setMessage() after initialization.
631631
*/
632632
const cf = function(){
633633
this.e = {
634634
body: D.addClass(D.div(), 'message-widget'),
635
- tab: D.addClass(D.span(), 'message-widget-tab'),
635
+ tab: D.addClass(D.div(), 'message-widget-tab'),
636636
content: D.addClass(D.div(), 'message-widget-content')
637637
};
638638
D.append(this.e.body, this.e.tab, this.e.content);
639639
this.e.tab.setAttribute('role', 'button');
640640
if(arguments.length){
@@ -692,14 +692,14 @@
692692
D.clearElement(this.e.tab);
693693
var contentTarget = this.e.content;
694694
var eXFrom /* element holding xfrom name */;
695695
if(m.xfrom){
696696
eXFrom = D.append(D.addClass(D.span(), 'xfrom'), m.xfrom);
697
- D.append(
698
- this.e.tab, eXFrom,
699
- D.text(" #",(m.msgid||'???'),' @ ',theTime(d))
700
- );
697
+ const wrapper = D.append(
698
+ D.span(), eXFrom,
699
+ D.text(" #",(m.msgid||'???'),' @ ',theTime(d)))
700
+ D.append(this.e.tab, wrapper);
701701
}else{/*notification*/
702702
D.addClass(this.e.body, 'notification');
703703
if(m.isError){
704704
D.addClass([contentTarget, this.e.tab], 'error');
705705
}
@@ -751,25 +751,25 @@
751751
if(F.pikchr){
752752
F.pikchr.addSrcView(contentTarget.querySelectorAll('svg.pikchr'));
753753
}
754754
}
755755
}
756
- this.e.tab.addEventListener('click', this._handleLegendClicked, false);
757
- if(eXFrom){
756
+ this.e.tab.firstElementChild.addEventListener('click', this._handleLegendClicked, false);
757
+ /*if(eXFrom){
758758
eXFrom.addEventListener('click', ()=>this.e.tab.click(), false);
759
- }
759
+ }*/
760760
return this;
761761
},
762762
/* Event handler for clicking .message-user elements to show their
763763
timestamps. */
764764
_handleLegendClicked: function f(ev){
765765
if(!f.popup){
766766
/* Timestamp popup widget */
767
- f.popup = new F.PopupWidget({
768
- cssClass: ['fossil-tooltip', 'chat-message-popup'],
767
+ f.popup = {
768
+ e: D.addClass(D.div(), 'chat-message-popup'),
769769
refresh:function(){
770
- const eMsg = this._eMsg;
770
+ const eMsg = this.$eMsg/*.message-widget element*/;
771771
if(!eMsg) return;
772772
D.clearElement(this.e);
773773
const d = new Date(eMsg.dataset.timestamp);
774774
if(d.getMinutes().toString()!=="NaN"){
775775
// Date works, render informative timestamps
@@ -802,22 +802,27 @@
802802
Chat.deleteMessageElem(eMsg);
803803
});
804804
if(Chat.userMayDelete(eMsg)){
805805
const btnDeleteGlobal = D.button("Delete globally");
806806
D.append(toolbar, btnDeleteGlobal);
807
- btnDeleteGlobal.addEventListener('click', function(){
808
- self.hide();
809
- Chat.deleteMessage(eMsg);
807
+ F.confirmer(btnDeleteGlobal,{
808
+ pinSize: true,
809
+ ticks: F.config.confirmerButtonTicks,
810
+ confirmText: "Confirm delete?",
811
+ onconfirm:function(){
812
+ self.hide();
813
+ Chat.deleteMessage(eMsg);
814
+ }
810815
});
811816
}
812817
const toolbar2 = D.addClass(D.div(), 'toolbar');
813818
D.append(this.e, toolbar2);
814819
const btnToggleText = D.button("Toggle text mode");
815820
btnToggleText.addEventListener('click', function(){
816821
self.hide();
817822
Chat.toggleTextMode(eMsg);
818
- });
823
+ },false);
819824
D.append(toolbar2, btnToggleText);
820825
if(eMsg.dataset.xfrom){
821826
/* Add a link to the /timeline filtered on this user. */
822827
const timelineLink = D.attr(
823828
D.a(F.repoUrl('timeline',{
@@ -826,32 +831,35 @@
826831
}), "User's Timeline"),
827832
'target', '_blank'
828833
);
829834
D.append(toolbar2, timelineLink);
830835
}
831
- }/*refresh()*/
832
- });
833
- f.popup.installHideHandlers();
834
- f.popup.hide = function(){
835
- delete this._eMsg;
836
- D.clearElement(this.e);
837
- return this.show(false);
838
- };
836
+ const tab = eMsg.querySelector('.message-widget-tab');
837
+ D.append(tab, this.e);
838
+ D.removeClass(this.e, 'hidden');
839
+ }/*refresh()*/,
840
+ hide: function(){
841
+ D.addClass(D.clearElement(this.e), 'hidden');
842
+ delete this.$eMsg;
843
+ },
844
+ show: function(tgtMsg){
845
+ if(tgtMsg === this.$eMsg){
846
+ this.hide();
847
+ return;
848
+ }
849
+ this.$eMsg = tgtMsg;
850
+ this.refresh();
851
+ }
852
+ }/*f.popup*/;
839853
}/*end static init*/
840
- const rect = ev.target.getBoundingClientRect();
841
- const eMsg = ev.target.parentNode/*the owning .message-widget element*/;
842
- f.popup._eMsg = eMsg;
843
- let x = rect.left, y = rect.topm;
844
- f.popup.show(ev.target)/*so we can get its computed size*/;
845
- if(eMsg.dataset.xfrom===Chat.me
846
- && document.body.classList.contains('my-messages-right')){
847
- // Shift popup to the left for right-aligned messages to avoid
848
- // truncation off the right edge of the page.
849
- const pRect = f.popup.e.getBoundingClientRect();
850
- x = rect.right - pRect.width;
851
- }
852
- f.popup.show(x, y);
854
+ console.debug("event =",ev);
855
+ console.debug("event.target =",ev.target);
856
+ let theMsg = ev.target;
857
+ while( theMsg && !theMsg.classList.contains('message-widget')){
858
+ theMsg = theMsg.parentNode;
859
+ }
860
+ if(theMsg) f.popup.show(theMsg);
853861
}/*_handleLegendClicked()*/
854862
};
855863
return cf;
856864
})()/*MessageWidget*/;
857865
858866
--- src/chat.js
+++ src/chat.js
@@ -630,11 +630,11 @@
630 this.setMessage() after initialization.
631 */
632 const cf = function(){
633 this.e = {
634 body: D.addClass(D.div(), 'message-widget'),
635 tab: D.addClass(D.span(), 'message-widget-tab'),
636 content: D.addClass(D.div(), 'message-widget-content')
637 };
638 D.append(this.e.body, this.e.tab, this.e.content);
639 this.e.tab.setAttribute('role', 'button');
640 if(arguments.length){
@@ -692,14 +692,14 @@
692 D.clearElement(this.e.tab);
693 var contentTarget = this.e.content;
694 var eXFrom /* element holding xfrom name */;
695 if(m.xfrom){
696 eXFrom = D.append(D.addClass(D.span(), 'xfrom'), m.xfrom);
697 D.append(
698 this.e.tab, eXFrom,
699 D.text(" #",(m.msgid||'???'),' @ ',theTime(d))
700 );
701 }else{/*notification*/
702 D.addClass(this.e.body, 'notification');
703 if(m.isError){
704 D.addClass([contentTarget, this.e.tab], 'error');
705 }
@@ -751,25 +751,25 @@
751 if(F.pikchr){
752 F.pikchr.addSrcView(contentTarget.querySelectorAll('svg.pikchr'));
753 }
754 }
755 }
756 this.e.tab.addEventListener('click', this._handleLegendClicked, false);
757 if(eXFrom){
758 eXFrom.addEventListener('click', ()=>this.e.tab.click(), false);
759 }
760 return this;
761 },
762 /* Event handler for clicking .message-user elements to show their
763 timestamps. */
764 _handleLegendClicked: function f(ev){
765 if(!f.popup){
766 /* Timestamp popup widget */
767 f.popup = new F.PopupWidget({
768 cssClass: ['fossil-tooltip', 'chat-message-popup'],
769 refresh:function(){
770 const eMsg = this._eMsg;
771 if(!eMsg) return;
772 D.clearElement(this.e);
773 const d = new Date(eMsg.dataset.timestamp);
774 if(d.getMinutes().toString()!=="NaN"){
775 // Date works, render informative timestamps
@@ -802,22 +802,27 @@
802 Chat.deleteMessageElem(eMsg);
803 });
804 if(Chat.userMayDelete(eMsg)){
805 const btnDeleteGlobal = D.button("Delete globally");
806 D.append(toolbar, btnDeleteGlobal);
807 btnDeleteGlobal.addEventListener('click', function(){
808 self.hide();
809 Chat.deleteMessage(eMsg);
 
 
 
 
 
810 });
811 }
812 const toolbar2 = D.addClass(D.div(), 'toolbar');
813 D.append(this.e, toolbar2);
814 const btnToggleText = D.button("Toggle text mode");
815 btnToggleText.addEventListener('click', function(){
816 self.hide();
817 Chat.toggleTextMode(eMsg);
818 });
819 D.append(toolbar2, btnToggleText);
820 if(eMsg.dataset.xfrom){
821 /* Add a link to the /timeline filtered on this user. */
822 const timelineLink = D.attr(
823 D.a(F.repoUrl('timeline',{
@@ -826,32 +831,35 @@
826 }), "User's Timeline"),
827 'target', '_blank'
828 );
829 D.append(toolbar2, timelineLink);
830 }
831 }/*refresh()*/
832 });
833 f.popup.installHideHandlers();
834 f.popup.hide = function(){
835 delete this._eMsg;
836 D.clearElement(this.e);
837 return this.show(false);
838 };
 
 
 
 
 
 
 
 
 
839 }/*end static init*/
840 const rect = ev.target.getBoundingClientRect();
841 const eMsg = ev.target.parentNode/*the owning .message-widget element*/;
842 f.popup._eMsg = eMsg;
843 let x = rect.left, y = rect.topm;
844 f.popup.show(ev.target)/*so we can get its computed size*/;
845 if(eMsg.dataset.xfrom===Chat.me
846 && document.body.classList.contains('my-messages-right')){
847 // Shift popup to the left for right-aligned messages to avoid
848 // truncation off the right edge of the page.
849 const pRect = f.popup.e.getBoundingClientRect();
850 x = rect.right - pRect.width;
851 }
852 f.popup.show(x, y);
853 }/*_handleLegendClicked()*/
854 };
855 return cf;
856 })()/*MessageWidget*/;
857
858
--- src/chat.js
+++ src/chat.js
@@ -630,11 +630,11 @@
630 this.setMessage() after initialization.
631 */
632 const cf = function(){
633 this.e = {
634 body: D.addClass(D.div(), 'message-widget'),
635 tab: D.addClass(D.div(), 'message-widget-tab'),
636 content: D.addClass(D.div(), 'message-widget-content')
637 };
638 D.append(this.e.body, this.e.tab, this.e.content);
639 this.e.tab.setAttribute('role', 'button');
640 if(arguments.length){
@@ -692,14 +692,14 @@
692 D.clearElement(this.e.tab);
693 var contentTarget = this.e.content;
694 var eXFrom /* element holding xfrom name */;
695 if(m.xfrom){
696 eXFrom = D.append(D.addClass(D.span(), 'xfrom'), m.xfrom);
697 const wrapper = D.append(
698 D.span(), eXFrom,
699 D.text(" #",(m.msgid||'???'),' @ ',theTime(d)))
700 D.append(this.e.tab, wrapper);
701 }else{/*notification*/
702 D.addClass(this.e.body, 'notification');
703 if(m.isError){
704 D.addClass([contentTarget, this.e.tab], 'error');
705 }
@@ -751,25 +751,25 @@
751 if(F.pikchr){
752 F.pikchr.addSrcView(contentTarget.querySelectorAll('svg.pikchr'));
753 }
754 }
755 }
756 this.e.tab.firstElementChild.addEventListener('click', this._handleLegendClicked, false);
757 /*if(eXFrom){
758 eXFrom.addEventListener('click', ()=>this.e.tab.click(), false);
759 }*/
760 return this;
761 },
762 /* Event handler for clicking .message-user elements to show their
763 timestamps. */
764 _handleLegendClicked: function f(ev){
765 if(!f.popup){
766 /* Timestamp popup widget */
767 f.popup = {
768 e: D.addClass(D.div(), 'chat-message-popup'),
769 refresh:function(){
770 const eMsg = this.$eMsg/*.message-widget element*/;
771 if(!eMsg) return;
772 D.clearElement(this.e);
773 const d = new Date(eMsg.dataset.timestamp);
774 if(d.getMinutes().toString()!=="NaN"){
775 // Date works, render informative timestamps
@@ -802,22 +802,27 @@
802 Chat.deleteMessageElem(eMsg);
803 });
804 if(Chat.userMayDelete(eMsg)){
805 const btnDeleteGlobal = D.button("Delete globally");
806 D.append(toolbar, btnDeleteGlobal);
807 F.confirmer(btnDeleteGlobal,{
808 pinSize: true,
809 ticks: F.config.confirmerButtonTicks,
810 confirmText: "Confirm delete?",
811 onconfirm:function(){
812 self.hide();
813 Chat.deleteMessage(eMsg);
814 }
815 });
816 }
817 const toolbar2 = D.addClass(D.div(), 'toolbar');
818 D.append(this.e, toolbar2);
819 const btnToggleText = D.button("Toggle text mode");
820 btnToggleText.addEventListener('click', function(){
821 self.hide();
822 Chat.toggleTextMode(eMsg);
823 },false);
824 D.append(toolbar2, btnToggleText);
825 if(eMsg.dataset.xfrom){
826 /* Add a link to the /timeline filtered on this user. */
827 const timelineLink = D.attr(
828 D.a(F.repoUrl('timeline',{
@@ -826,32 +831,35 @@
831 }), "User's Timeline"),
832 'target', '_blank'
833 );
834 D.append(toolbar2, timelineLink);
835 }
836 const tab = eMsg.querySelector('.message-widget-tab');
837 D.append(tab, this.e);
838 D.removeClass(this.e, 'hidden');
839 }/*refresh()*/,
840 hide: function(){
841 D.addClass(D.clearElement(this.e), 'hidden');
842 delete this.$eMsg;
843 },
844 show: function(tgtMsg){
845 if(tgtMsg === this.$eMsg){
846 this.hide();
847 return;
848 }
849 this.$eMsg = tgtMsg;
850 this.refresh();
851 }
852 }/*f.popup*/;
853 }/*end static init*/
854 console.debug("event =",ev);
855 console.debug("event.target =",ev.target);
856 let theMsg = ev.target;
857 while( theMsg && !theMsg.classList.contains('message-widget')){
858 theMsg = theMsg.parentNode;
859 }
860 if(theMsg) f.popup.show(theMsg);
 
 
 
 
 
 
861 }/*_handleLegendClicked()*/
862 };
863 return cf;
864 })()/*MessageWidget*/;
865
866
--- src/style.chat.css
+++ src/style.chat.css
@@ -70,17 +70,17 @@
7070
}
7171
/* The popup element for displaying message timestamps
7272
and deletion controls. */
7373
body.chat .chat-message-popup {
7474
font-family: monospace;
75
- font-size: 0.8em;
75
+ font-size: 0.9em;
7676
text-align: left;
7777
display: flex;
7878
flex-direction: column;
7979
align-items: stretch;
8080
padding: 0.25em;
81
- z-index: 200;
81
+ margin-top: 0.25em;
8282
}
8383
/* Full message timestamps. */
8484
body.chat .chat-message-popup > span { white-space: nowrap; }
8585
/* Container for the message deletion buttons. */
8686
body.chat .chat-message-popup > .toolbar {
8787
--- src/style.chat.css
+++ src/style.chat.css
@@ -70,17 +70,17 @@
70 }
71 /* The popup element for displaying message timestamps
72 and deletion controls. */
73 body.chat .chat-message-popup {
74 font-family: monospace;
75 font-size: 0.8em;
76 text-align: left;
77 display: flex;
78 flex-direction: column;
79 align-items: stretch;
80 padding: 0.25em;
81 z-index: 200;
82 }
83 /* Full message timestamps. */
84 body.chat .chat-message-popup > span { white-space: nowrap; }
85 /* Container for the message deletion buttons. */
86 body.chat .chat-message-popup > .toolbar {
87
--- src/style.chat.css
+++ src/style.chat.css
@@ -70,17 +70,17 @@
70 }
71 /* The popup element for displaying message timestamps
72 and deletion controls. */
73 body.chat .chat-message-popup {
74 font-family: monospace;
75 font-size: 0.9em;
76 text-align: left;
77 display: flex;
78 flex-direction: column;
79 align-items: stretch;
80 padding: 0.25em;
81 margin-top: 0.25em;
82 }
83 /* Full message timestamps. */
84 body.chat .chat-message-popup > span { white-space: nowrap; }
85 /* Container for the message deletion buttons. */
86 body.chat .chat-message-popup > .toolbar {
87

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button