Fossil

Check-in [247276113c]
Login

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

Overview
Comment: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.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 247276113c0a0dc475dddf64fe54d613db13f3a467c6184c84dc25d301591253
User & Date: stephan 2020-12-24 05:03:27.127
Context
2020-12-24
05:58
Added UI to delete chat posts (tap on the message header). Made a change to the semantics of when fossil.PopupWidget's refresh() callback is triggered to account for the common case of having to show() the popup twice in a row without a hide() in between. ... (check-in: b7f106da8a user: stephan tags: trunk)
05:03
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. ... (check-in: 247276113c user: stephan tags: trunk)
03:34
The /chat page now redirects to the login page if needed. ... (check-in: 77d3058600 user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/builtin.c.
635
636
637
638
639
640
641
642

643
644
645
646
647
648
649
       CX("name: %!j,", skin_in_use());*/
    CX("isDark: %s"
       "/*true if the current skin has the 'white-foreground' detail*/",
       skin_detail_boolean("white-foreground") ? "true" : "false");
    CX("}\n"/*fossil.config.skin*/);
    CX("};\n"/* fossil.config */);
    CX("window.fossil.user = {");
    CX("name: %!j", (g.zLogin&&*g.zLogin) ? g.zLogin : "guest");

    CX("};\n"/*fossil.user*/);
    CX("if(fossil.config.skin.isDark) "
       "document.body.classList.add('fossil-dark-style');\n");
#if 0
    /* Is it safe to emit the CSRF token here? Some pages add it
    ** as a hidden form field. */
    if(g.zCsrfToken[0]!=0){







|
>







635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
       CX("name: %!j,", skin_in_use());*/
    CX("isDark: %s"
       "/*true if the current skin has the 'white-foreground' detail*/",
       skin_detail_boolean("white-foreground") ? "true" : "false");
    CX("}\n"/*fossil.config.skin*/);
    CX("};\n"/* fossil.config */);
    CX("window.fossil.user = {");
    CX("name: %!j,", (g.zLogin&&*g.zLogin) ? g.zLogin : "guest");
    CX("isAdmin: %s", (g.perm.Admin || g.perm.Setup) ? "true" : "false");
    CX("};\n"/*fossil.user*/);
    CX("if(fossil.config.skin.isDark) "
       "document.body.classList.add('fossil-dark-style');\n");
#if 0
    /* Is it safe to emit the CSRF token here? Some pages add it
    ** as a hidden form field. */
    if(g.zCsrfToken[0]!=0){
Changes to src/chat.js.
10
11
12
13
14
15
16









































17
18
19
20
21
22
23
      onPageInactive: function(){console.debug("Page inactive.")} //override below
    };
    document.addEventListener('visibilitychange', function(ev){
      cs.pageIsActive = !document.hidden;
      if(cs.pageIsActive) cs.onPageActive();
      else cs.onPageInactive();
    }, true);









































    return cs;
  })();
  /* State for paste and drag/drop */
  const BlobXferState = {
    dropDetails: document.querySelector('#chat-drop-details'),
    blob: undefined
  };







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







10
11
12
13
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
53
54
55
56
57
58
59
60
61
62
63
64
      onPageInactive: function(){console.debug("Page inactive.")} //override below
    };
    document.addEventListener('visibilitychange', function(ev){
      cs.pageIsActive = !document.hidden;
      if(cs.pageIsActive) cs.onPageActive();
      else cs.onPageInactive();
    }, true);

    const qs = (e)=>document.querySelector(e);
    const argsToArray = function(args){
      return Array.prototype.slice.call(args,0);
    };
    cs.reportError = function(/*msg args*/){
      const args = argsToArray(arguments);
      console.error("chat error:",args);
      F.toast.error.apply(F.toast, args);
    };

    cs.getMessageElemById = function(id){
      return qs('[data-msgid="'+id+'"]');
    };
    cs.deleteMessageElemById = function(id){
      const e = this.getMessageElemById(id);
      if(e) D.remove(e);
      return !!e;
    };

    /**
       Removes the given message ID from the local chat record and, if
       the message was posted by this user OR this user in an
       admin/setup, also submits it for removal on the remote.
    */
    cs.deleteMessageById = function(id){
      const e = this.getMessageElemById(id);
      if(!e) return;
      if(this.me === e.dataset.xfrom
         || F.user.isAdmin/*will be confirmed server-side*/
        ){
        fetch("chat-delete?name=" + id)
          .then(()=>D.remove(e))
          .then(()=>F.toast.message("Deleted message "+id+"."))
          .catch(err=>this.reportError(err))
      }else{
        D.remove(e);
        F.toast.message("Locally removed message "+id+".");
      }
    };

    return cs;
  })();
  /* State for paste and drag/drop */
  const BlobXferState = {
    dropDetails: document.querySelector('#chat-drop-details'),
    blob: undefined
  };
190
191
192
193
194
195
196





197
198


199
200
201
202
203
204
205
  };
  /** Callback for poll() to inject new content into the page. */
  function newcontent(jx){
    var i;
    for(i=0; i<jx.msgs.length; ++i){
      const m = jx.msgs[i];
      if( m.msgid>Chat.mxMsg ) Chat.mxMsg = m.msgid;





      const eWho = D.create('legend'),
            row = D.addClass(D.fieldset(eWho), 'message-row');


      injectMessage(row);
      eWho.dataset.timestamp = m.mtime;
      eWho.addEventListener('click', handleLegendClicked, false);
      if( m.xfrom==Chat.me && window.outerWidth<1000 ){
        eWho.setAttribute('align', 'right');
        row.style.justifyContent = "flex-end";
      }else{







>
>
>
>
>


>
>







231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
  };
  /** Callback for poll() to inject new content into the page. */
  function newcontent(jx){
    var i;
    for(i=0; i<jx.msgs.length; ++i){
      const m = jx.msgs[i];
      if( m.msgid>Chat.mxMsg ) Chat.mxMsg = m.msgid;
      if( m.mdel ){
        /* A record deletion notice. */
        Chat.deleteMessageElemById(m.mdel);
        continue;
      }
      const eWho = D.create('legend'),
            row = D.addClass(D.fieldset(eWho), 'message-row');
      row.dataset.msgid = m.msgid;
      row.dataset.xfrom = m.xfrom;
      injectMessage(row);
      eWho.dataset.timestamp = m.mtime;
      eWho.addEventListener('click', handleLegendClicked, false);
      if( m.xfrom==Chat.me && window.outerWidth<1000 ){
        eWho.setAttribute('align', 'right');
        row.style.justifyContent = "flex-end";
      }else{
250
251
252
253
254
255
256

257
    .then(x=>x.json())
    .then(y=>newcontent(y))
    .catch(e=>console.error(e))
    .finally(()=>poll.running=false)
  }
  poll();
  setInterval(poll, 1000);

})();







>

298
299
300
301
302
303
304
305
306
    .then(x=>x.json())
    .then(y=>newcontent(y))
    .catch(e=>console.error(e))
    .finally(()=>poll.running=false)
  }
  poll();
  setInterval(poll, 1000);
  F.page.chat = Chat;
})();
Changes to src/fossil.dom.js.
287
288
289
290
291
292
293
294

295
296
297
298
299
300
301
      var e = a[i];
      if(isArray(e) || e.forEach){
        e.forEach((x)=>f.call(this, parent,x));
        continue;
      }
      if('string'===typeof e
         || 'number'===typeof e
         || 'boolean'===typeof e) e = this.text(e);

      parent.appendChild(e);
    }
    return parent;
  };

  dom.input = function(type){
    return this.attr(this.create('input'), 'type', type);







|
>







287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
      var e = a[i];
      if(isArray(e) || e.forEach){
        e.forEach((x)=>f.call(this, parent,x));
        continue;
      }
      if('string'===typeof e
         || 'number'===typeof e
         || 'boolean'===typeof e
         || e instanceof Error) e = this.text(e);
      parent.appendChild(e);
    }
    return parent;
  };

  dom.input = function(type){
    return this.attr(this.create('input'), 'type', type);
Changes to src/fossil.popupwidget.js.
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
       Displays a toast with the 'warning' CSS class assigned to it. It
       displays for 1.5 times as long as a normal toast.
    */
    warning: function(/*...*/){
      return toastImpl('warning',1.5,arguments);
    },
    /**
       Displays a toast with the 'warning' CSS class assigned to it. It
       displays for twice as long as a normal toast.
    */
    error: function(/*...*/){
      return toastImpl('error',2,arguments);
    }
  }/*F.toast*/;








|







270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
       Displays a toast with the 'warning' CSS class assigned to it. It
       displays for 1.5 times as long as a normal toast.
    */
    warning: function(/*...*/){
      return toastImpl('warning',1.5,arguments);
    },
    /**
       Displays a toast with the 'error' CSS class assigned to it. It
       displays for twice as long as a normal toast.
    */
    error: function(/*...*/){
      return toastImpl('error',2,arguments);
    }
  }/*F.toast*/;