Fossil SCM

Initial impl for chat message deletion. The ajax bits are in place and message deletion propagates to other connected clients (if the message is owned by the poster or the user is an admin) but there's not currently a user interface. TODO: add related controls to the same popup used for the message timestamps.

stephan 2020-12-24 05:03 trunk
Commit 247276113c0a0dc475dddf64fe54d613db13f3a467c6184c84dc25d301591253
+2 -1
--- src/builtin.c
+++ src/builtin.c
@@ -637,11 +637,12 @@
637637
"/*true if the current skin has the 'white-foreground' detail*/",
638638
skin_detail_boolean("white-foreground") ? "true" : "false");
639639
CX("}\n"/*fossil.config.skin*/);
640640
CX("};\n"/* fossil.config */);
641641
CX("window.fossil.user = {");
642
- CX("name: %!j", (g.zLogin&&*g.zLogin) ? g.zLogin : "guest");
642
+ CX("name: %!j,", (g.zLogin&&*g.zLogin) ? g.zLogin : "guest");
643
+ CX("isAdmin: %s", (g.perm.Admin || g.perm.Setup) ? "true" : "false");
643644
CX("};\n"/*fossil.user*/);
644645
CX("if(fossil.config.skin.isDark) "
645646
"document.body.classList.add('fossil-dark-style');\n");
646647
#if 0
647648
/* Is it safe to emit the CSRF token here? Some pages add it
648649
--- src/builtin.c
+++ src/builtin.c
@@ -637,11 +637,12 @@
637 "/*true if the current skin has the 'white-foreground' detail*/",
638 skin_detail_boolean("white-foreground") ? "true" : "false");
639 CX("}\n"/*fossil.config.skin*/);
640 CX("};\n"/* fossil.config */);
641 CX("window.fossil.user = {");
642 CX("name: %!j", (g.zLogin&&*g.zLogin) ? g.zLogin : "guest");
 
643 CX("};\n"/*fossil.user*/);
644 CX("if(fossil.config.skin.isDark) "
645 "document.body.classList.add('fossil-dark-style');\n");
646 #if 0
647 /* Is it safe to emit the CSRF token here? Some pages add it
648
--- src/builtin.c
+++ src/builtin.c
@@ -637,11 +637,12 @@
637 "/*true if the current skin has the 'white-foreground' detail*/",
638 skin_detail_boolean("white-foreground") ? "true" : "false");
639 CX("}\n"/*fossil.config.skin*/);
640 CX("};\n"/* fossil.config */);
641 CX("window.fossil.user = {");
642 CX("name: %!j,", (g.zLogin&&*g.zLogin) ? g.zLogin : "guest");
643 CX("isAdmin: %s", (g.perm.Admin || g.perm.Setup) ? "true" : "false");
644 CX("};\n"/*fossil.user*/);
645 CX("if(fossil.config.skin.isDark) "
646 "document.body.classList.add('fossil-dark-style');\n");
647 #if 0
648 /* Is it safe to emit the CSRF token here? Some pages add it
649
+49
--- src/chat.js
+++ src/chat.js
@@ -12,10 +12,51 @@
1212
document.addEventListener('visibilitychange', function(ev){
1313
cs.pageIsActive = !document.hidden;
1414
if(cs.pageIsActive) cs.onPageActive();
1515
else cs.onPageInactive();
1616
}, true);
17
+
18
+ const qs = (e)=>document.querySelector(e);
19
+ const argsToArray = function(args){
20
+ return Array.prototype.slice.call(args,0);
21
+ };
22
+ cs.reportError = function(/*msg args*/){
23
+ const args = argsToArray(arguments);
24
+ console.error("chat error:",args);
25
+ F.toast.error.apply(F.toast, args);
26
+ };
27
+
28
+ cs.getMessageElemById = function(id){
29
+ return qs('[data-msgid="'+id+'"]');
30
+ };
31
+ cs.deleteMessageElemById = function(id){
32
+ const e = this.getMessageElemById(id);
33
+ if(e) D.remove(e);
34
+ return !!e;
35
+ };
36
+
37
+ /**
38
+ Removes the given message ID from the local chat record and, if
39
+ the message was posted by this user OR this user in an
40
+ admin/setup, also submits it for removal on the remote.
41
+ */
42
+ cs.deleteMessageById = function(id){
43
+ const e = this.getMessageElemById(id);
44
+ if(!e) return;
45
+ if(this.me === e.dataset.xfrom
46
+ || F.user.isAdmin/*will be confirmed server-side*/
47
+ ){
48
+ fetch("chat-delete?name=" + id)
49
+ .then(()=>D.remove(e))
50
+ .then(()=>F.toast.message("Deleted message "+id+"."))
51
+ .catch(err=>this.reportError(err))
52
+ }else{
53
+ D.remove(e);
54
+ F.toast.message("Locally removed message "+id+".");
55
+ }
56
+ };
57
+
1758
return cs;
1859
})();
1960
/* State for paste and drag/drop */
2061
const BlobXferState = {
2162
dropDetails: document.querySelector('#chat-drop-details'),
@@ -192,12 +233,19 @@
192233
function newcontent(jx){
193234
var i;
194235
for(i=0; i<jx.msgs.length; ++i){
195236
const m = jx.msgs[i];
196237
if( m.msgid>Chat.mxMsg ) Chat.mxMsg = m.msgid;
238
+ if( m.mdel ){
239
+ /* A record deletion notice. */
240
+ Chat.deleteMessageElemById(m.mdel);
241
+ continue;
242
+ }
197243
const eWho = D.create('legend'),
198244
row = D.addClass(D.fieldset(eWho), 'message-row');
245
+ row.dataset.msgid = m.msgid;
246
+ row.dataset.xfrom = m.xfrom;
199247
injectMessage(row);
200248
eWho.dataset.timestamp = m.mtime;
201249
eWho.addEventListener('click', handleLegendClicked, false);
202250
if( m.xfrom==Chat.me && window.outerWidth<1000 ){
203251
eWho.setAttribute('align', 'right');
@@ -252,6 +300,7 @@
252300
.catch(e=>console.error(e))
253301
.finally(()=>poll.running=false)
254302
}
255303
poll();
256304
setInterval(poll, 1000);
305
+ F.page.chat = Chat;
257306
})();
258307
--- src/chat.js
+++ src/chat.js
@@ -12,10 +12,51 @@
12 document.addEventListener('visibilitychange', function(ev){
13 cs.pageIsActive = !document.hidden;
14 if(cs.pageIsActive) cs.onPageActive();
15 else cs.onPageInactive();
16 }, true);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17 return cs;
18 })();
19 /* State for paste and drag/drop */
20 const BlobXferState = {
21 dropDetails: document.querySelector('#chat-drop-details'),
@@ -192,12 +233,19 @@
192 function newcontent(jx){
193 var i;
194 for(i=0; i<jx.msgs.length; ++i){
195 const m = jx.msgs[i];
196 if( m.msgid>Chat.mxMsg ) Chat.mxMsg = m.msgid;
 
 
 
 
 
197 const eWho = D.create('legend'),
198 row = D.addClass(D.fieldset(eWho), 'message-row');
 
 
199 injectMessage(row);
200 eWho.dataset.timestamp = m.mtime;
201 eWho.addEventListener('click', handleLegendClicked, false);
202 if( m.xfrom==Chat.me && window.outerWidth<1000 ){
203 eWho.setAttribute('align', 'right');
@@ -252,6 +300,7 @@
252 .catch(e=>console.error(e))
253 .finally(()=>poll.running=false)
254 }
255 poll();
256 setInterval(poll, 1000);
 
257 })();
258
--- src/chat.js
+++ src/chat.js
@@ -12,10 +12,51 @@
12 document.addEventListener('visibilitychange', function(ev){
13 cs.pageIsActive = !document.hidden;
14 if(cs.pageIsActive) cs.onPageActive();
15 else cs.onPageInactive();
16 }, true);
17
18 const qs = (e)=>document.querySelector(e);
19 const argsToArray = function(args){
20 return Array.prototype.slice.call(args,0);
21 };
22 cs.reportError = function(/*msg args*/){
23 const args = argsToArray(arguments);
24 console.error("chat error:",args);
25 F.toast.error.apply(F.toast, args);
26 };
27
28 cs.getMessageElemById = function(id){
29 return qs('[data-msgid="'+id+'"]');
30 };
31 cs.deleteMessageElemById = function(id){
32 const e = this.getMessageElemById(id);
33 if(e) D.remove(e);
34 return !!e;
35 };
36
37 /**
38 Removes the given message ID from the local chat record and, if
39 the message was posted by this user OR this user in an
40 admin/setup, also submits it for removal on the remote.
41 */
42 cs.deleteMessageById = function(id){
43 const e = this.getMessageElemById(id);
44 if(!e) return;
45 if(this.me === e.dataset.xfrom
46 || F.user.isAdmin/*will be confirmed server-side*/
47 ){
48 fetch("chat-delete?name=" + id)
49 .then(()=>D.remove(e))
50 .then(()=>F.toast.message("Deleted message "+id+"."))
51 .catch(err=>this.reportError(err))
52 }else{
53 D.remove(e);
54 F.toast.message("Locally removed message "+id+".");
55 }
56 };
57
58 return cs;
59 })();
60 /* State for paste and drag/drop */
61 const BlobXferState = {
62 dropDetails: document.querySelector('#chat-drop-details'),
@@ -192,12 +233,19 @@
233 function newcontent(jx){
234 var i;
235 for(i=0; i<jx.msgs.length; ++i){
236 const m = jx.msgs[i];
237 if( m.msgid>Chat.mxMsg ) Chat.mxMsg = m.msgid;
238 if( m.mdel ){
239 /* A record deletion notice. */
240 Chat.deleteMessageElemById(m.mdel);
241 continue;
242 }
243 const eWho = D.create('legend'),
244 row = D.addClass(D.fieldset(eWho), 'message-row');
245 row.dataset.msgid = m.msgid;
246 row.dataset.xfrom = m.xfrom;
247 injectMessage(row);
248 eWho.dataset.timestamp = m.mtime;
249 eWho.addEventListener('click', handleLegendClicked, false);
250 if( m.xfrom==Chat.me && window.outerWidth<1000 ){
251 eWho.setAttribute('align', 'right');
@@ -252,6 +300,7 @@
300 .catch(e=>console.error(e))
301 .finally(()=>poll.running=false)
302 }
303 poll();
304 setInterval(poll, 1000);
305 F.page.chat = Chat;
306 })();
307
--- src/fossil.dom.js
+++ src/fossil.dom.js
@@ -289,11 +289,12 @@
289289
e.forEach((x)=>f.call(this, parent,x));
290290
continue;
291291
}
292292
if('string'===typeof e
293293
|| 'number'===typeof e
294
- || 'boolean'===typeof e) e = this.text(e);
294
+ || 'boolean'===typeof e
295
+ || e instanceof Error) e = this.text(e);
295296
parent.appendChild(e);
296297
}
297298
return parent;
298299
};
299300
300301
--- src/fossil.dom.js
+++ src/fossil.dom.js
@@ -289,11 +289,12 @@
289 e.forEach((x)=>f.call(this, parent,x));
290 continue;
291 }
292 if('string'===typeof e
293 || 'number'===typeof e
294 || 'boolean'===typeof e) e = this.text(e);
 
295 parent.appendChild(e);
296 }
297 return parent;
298 };
299
300
--- src/fossil.dom.js
+++ src/fossil.dom.js
@@ -289,11 +289,12 @@
289 e.forEach((x)=>f.call(this, parent,x));
290 continue;
291 }
292 if('string'===typeof e
293 || 'number'===typeof e
294 || 'boolean'===typeof e
295 || e instanceof Error) e = this.text(e);
296 parent.appendChild(e);
297 }
298 return parent;
299 };
300
301
--- src/fossil.popupwidget.js
+++ src/fossil.popupwidget.js
@@ -272,11 +272,11 @@
272272
*/
273273
warning: function(/*...*/){
274274
return toastImpl('warning',1.5,arguments);
275275
},
276276
/**
277
- Displays a toast with the 'warning' CSS class assigned to it. It
277
+ Displays a toast with the 'error' CSS class assigned to it. It
278278
displays for twice as long as a normal toast.
279279
*/
280280
error: function(/*...*/){
281281
return toastImpl('error',2,arguments);
282282
}
283283
--- src/fossil.popupwidget.js
+++ src/fossil.popupwidget.js
@@ -272,11 +272,11 @@
272 */
273 warning: function(/*...*/){
274 return toastImpl('warning',1.5,arguments);
275 },
276 /**
277 Displays a toast with the 'warning' CSS class assigned to it. It
278 displays for twice as long as a normal toast.
279 */
280 error: function(/*...*/){
281 return toastImpl('error',2,arguments);
282 }
283
--- src/fossil.popupwidget.js
+++ src/fossil.popupwidget.js
@@ -272,11 +272,11 @@
272 */
273 warning: function(/*...*/){
274 return toastImpl('warning',1.5,arguments);
275 },
276 /**
277 Displays a toast with the 'error' CSS class assigned to it. It
278 displays for twice as long as a normal toast.
279 */
280 error: function(/*...*/){
281 return toastImpl('error',2,arguments);
282 }
283

Keyboard Shortcuts

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