Fossil SCM
Add a Discard button to the post editor. Start hooking into Reply/Edit buttons in thread views but it's currently disabled.
Commit
44aafa6117f4c08ddbf1edd1d10f44c53bba6a0b017a9b68742b0983cfd6a7a8
Parent
4bf65a5a9038f17…
2 files changed
+2
-3
+74
-2
+2
-3
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -1122,14 +1122,13 @@ | ||
| 1122 | 1122 | @ <div id='forum%d(p->fpid)' class='forumTime\ |
| 1123 | 1123 | @ %s(bSelect ? " forumSel" : "")\ |
| 1124 | 1124 | @ %s(iClosed ? " forumClosed" : "")\ |
| 1125 | 1125 | @ %s(p->pEditTail ? " forumObs" : "")' \ |
| 1126 | 1126 | if( iIndent && iIndentScale ){ |
| 1127 | - @ style='margin-left:%d(iIndent*iIndentScale)ex;'> | |
| 1128 | - }else{ | |
| 1129 | - @ > | |
| 1127 | + @ style='margin-left:%d(iIndent*iIndentScale)ex;' \ | |
| 1130 | 1128 | } |
| 1129 | + @ data-fpid="%s(p->zUuid)"> | |
| 1131 | 1130 | |
| 1132 | 1131 | /* If this is the first post (or an edit thereof), emit the thread title. */ |
| 1133 | 1132 | if( pManifest->zThreadTitle ){ |
| 1134 | 1133 | @ <h1>%h(pManifest->zThreadTitle)</h1> |
| 1135 | 1134 | } |
| 1136 | 1135 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -1122,14 +1122,13 @@ | |
| 1122 | @ <div id='forum%d(p->fpid)' class='forumTime\ |
| 1123 | @ %s(bSelect ? " forumSel" : "")\ |
| 1124 | @ %s(iClosed ? " forumClosed" : "")\ |
| 1125 | @ %s(p->pEditTail ? " forumObs" : "")' \ |
| 1126 | if( iIndent && iIndentScale ){ |
| 1127 | @ style='margin-left:%d(iIndent*iIndentScale)ex;'> |
| 1128 | }else{ |
| 1129 | @ > |
| 1130 | } |
| 1131 | |
| 1132 | /* If this is the first post (or an edit thereof), emit the thread title. */ |
| 1133 | if( pManifest->zThreadTitle ){ |
| 1134 | @ <h1>%h(pManifest->zThreadTitle)</h1> |
| 1135 | } |
| 1136 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -1122,14 +1122,13 @@ | |
| 1122 | @ <div id='forum%d(p->fpid)' class='forumTime\ |
| 1123 | @ %s(bSelect ? " forumSel" : "")\ |
| 1124 | @ %s(iClosed ? " forumClosed" : "")\ |
| 1125 | @ %s(p->pEditTail ? " forumObs" : "")' \ |
| 1126 | if( iIndent && iIndentScale ){ |
| 1127 | @ style='margin-left:%d(iIndent*iIndentScale)ex;' \ |
| 1128 | } |
| 1129 | @ data-fpid="%s(p->zUuid)"> |
| 1130 | |
| 1131 | /* If this is the first post (or an edit thereof), emit the thread title. */ |
| 1132 | if( pManifest->zThreadTitle ){ |
| 1133 | @ <h1>%h(pManifest->zThreadTitle)</h1> |
| 1134 | } |
| 1135 |
+74
-2
| --- src/fossil.page.forumpost.js | ||
| +++ src/fossil.page.forumpost.js | ||
| @@ -43,11 +43,11 @@ | ||
| 43 | 43 | */ |
| 44 | 44 | constructor(opt){ |
| 45 | 45 | opt = this.#opt = F.nu({ |
| 46 | 46 | // todo: defaults once we determine the options |
| 47 | 47 | // replyTo: hash |
| 48 | - // edit: hash | |
| 48 | + // fpid: hash | |
| 49 | 49 | draftKey: undefined |
| 50 | 50 | }, opt); |
| 51 | 51 | opt.isNewThread = !opt.replyTo && !opt.edit; |
| 52 | 52 | if( opt.draftKey ){ |
| 53 | 53 | this.#draft = F.nu(F.storage.getJSON(opt.draftKey, {})); |
| @@ -113,15 +113,24 @@ | ||
| 113 | 113 | |
| 114 | 114 | e.buttons = D.addClass(D.div(), 'buttons'); |
| 115 | 115 | { /* Preview/submit buttons... */ |
| 116 | 116 | e.button.preview = D.button("Preview", e=>this.#preview()); |
| 117 | 117 | e.button.submit = D.button("Submit"); |
| 118 | + if( opt.ondiscard instanceof Function ){ | |
| 119 | + e.button.discard = D.button('Discard'); | |
| 120 | + } | |
| 118 | 121 | if( 1 ){ |
| 119 | 122 | F.confirmer(e.button.submit, { |
| 120 | 123 | confirmText: "Confirm submit...", |
| 121 | 124 | onconfirm: ()=>this.#submit() |
| 122 | 125 | }); |
| 126 | + if( e.button.discard ){ | |
| 127 | + F.confirmer(e.button.discard, { | |
| 128 | + confirmText: "Really discard?", | |
| 129 | + onconfirm: ()=>this.discard() | |
| 130 | + }); | |
| 131 | + } | |
| 123 | 132 | }else{ |
| 124 | 133 | e.button.submit.addEventListener('click', ()=>this.#submit()); |
| 125 | 134 | } |
| 126 | 135 | e.button.submit.setAttribute('disabled', ''); |
| 127 | 136 | wrapper.append(e.buttons); |
| @@ -253,10 +262,14 @@ | ||
| 253 | 262 | this.#tabs.addTab(e.tabAttach); |
| 254 | 263 | /* Reminder: we don't currently have a way to disable/enable |
| 255 | 264 | an Attacher's controls during ajax traffic. */ |
| 256 | 265 | } |
| 257 | 266 | e.buttons.append(e.button.preview, e.button.submit); |
| 267 | + if( e.button.discard ){ | |
| 268 | + e.buttons.append(e.button.discard); | |
| 269 | + this.#toDisable.push(e.button.discard); | |
| 270 | + } | |
| 258 | 271 | this.#toDisable.push(e.button.preview); |
| 259 | 272 | |
| 260 | 273 | e.help = D.attr(D.div(), 'id', idPrefix+'-help'); |
| 261 | 274 | e.help.$needsInit = true; |
| 262 | 275 | e.help.dataset.tabLabel = 'Help'; |
| @@ -331,10 +344,21 @@ | ||
| 331 | 344 | }); |
| 332 | 345 | e.buttons.append(e.button.toggleHeader); |
| 333 | 346 | } |
| 334 | 347 | |
| 335 | 348 | }/*constructor*/ |
| 349 | + | |
| 350 | + discard(){ | |
| 351 | + this.#clearDraft(); | |
| 352 | + const e = this.#e.widget; | |
| 353 | + if( e.parentNode ){ | |
| 354 | + e.remove(); | |
| 355 | + if( this.#opt.ondiscard instanceof Function ){ | |
| 356 | + this.#opt.ondiscard(); | |
| 357 | + } | |
| 358 | + } | |
| 359 | + } | |
| 336 | 360 | |
| 337 | 361 | /** This widget's top-most DOM element. */ |
| 338 | 362 | get widget(){ |
| 339 | 363 | return this.#e.widget; |
| 340 | 364 | } |
| @@ -416,10 +440,20 @@ | ||
| 416 | 440 | |
| 417 | 441 | #initAttacherTab(){ |
| 418 | 442 | this.#att = new F.Attacher({ |
| 419 | 443 | reverse: true |
| 420 | 444 | }); |
| 445 | + if( this.#opt.fpid ){ | |
| 446 | + const eNote = D.append( | |
| 447 | + D.div(), | |
| 448 | + "Tip: attachments can be added to posts without editing them", | |
| 449 | + "by visiting ", | |
| 450 | + D.a(F.repoUrl('attachadd?target='+this.#opt.fpid), '/attachadd'), | |
| 451 | + ".", | |
| 452 | + ); | |
| 453 | + this.#e.tabAttach.append(eNote); | |
| 454 | + } | |
| 421 | 455 | this.#e.tabAttach.append(this.#att.widget); |
| 422 | 456 | } |
| 423 | 457 | |
| 424 | 458 | #newFormData(addThisContent){ |
| 425 | 459 | const fd = new FormData; |
| @@ -773,14 +807,52 @@ | ||
| 773 | 807 | if( eForumNew ){ |
| 774 | 808 | /* /forumnew */ |
| 775 | 809 | const fpe = new fossil.ForumPostEditor({ |
| 776 | 810 | draftKey: 'forumnew', |
| 777 | 811 | hiddenFields: eForumNew.querySelectorAll('input[type=hidden]'), |
| 778 | - captcha: eForumNew.querySelector('.captcha-for-js') | |
| 812 | + captcha: eForumNew.querySelector('.captcha-for-js'), | |
| 813 | + ondiscard: ()=>{ | |
| 814 | + window.location = F.repoUrl('forum'); | |
| 815 | + } | |
| 779 | 816 | //mimetype: 'text/plain' |
| 780 | 817 | }); |
| 781 | 818 | eForumNew.parentElement.insertBefore(fpe.widget, eForumNew); |
| 782 | 819 | eForumNew.remove(); |
| 783 | 820 | fossil.page.fpe = fpe /* for testing via the console */; |
| 784 | 821 | }/*eForumNew*/ |
| 822 | + else if( 0 && (document.body.classList.contains('cpage-forumpost') | |
| 823 | + || document.body.classList.contains('cpage-forumthread'))){ | |
| 824 | + /* /forumpost and /forumthread */ | |
| 825 | + const replyClicked = (replyButton, fpid)=>{ | |
| 826 | + F.toast.error("Reply is TODO. fpid="+fpid); | |
| 827 | + /* | |
| 828 | + TODOs include: | |
| 829 | + | |
| 830 | + - Hide replyButton | |
| 831 | + | |
| 832 | + - Pop up a ForumPostEditor. It needs a Cancel button. | |
| 833 | + | |
| 834 | + - When cancelled or submitted, restore the reply button. | |
| 835 | + */ | |
| 836 | + }; | |
| 837 | + document.body.querySelectorAll( | |
| 838 | + '.forumpost-single-controls > form' | |
| 839 | + ).forEach(form=>{ | |
| 840 | + const eReplyTo = form.parentElement.parentElement; | |
| 841 | + const fpid = eReplyTo?.dataset?.fpid; | |
| 842 | + if( !fpid ){ | |
| 843 | + console.warn("Unexpected non-fpid", form, eReplyTo); | |
| 844 | + return; | |
| 845 | + } | |
| 846 | + const rb = form.querySelector('input[type=submit][name=reply]'); | |
| 847 | + if( rb ){ | |
| 848 | + console.debug("hacking Reply button", rb); | |
| 849 | + const b = D.button("Reply", ()=>replyClicked(b, fpid)); | |
| 850 | + b.type = 'button'/*keep container form from submitting*/; | |
| 851 | + rb.parentElement.insertBefore(b, rb); | |
| 852 | + rb.remove(); | |
| 853 | + } | |
| 854 | + }); | |
| 855 | + } | |
| 856 | + | |
| 785 | 857 | })/*F.onPageLoad callback*/; |
| 786 | 858 | })(window.fossil); |
| 787 | 859 |
| --- src/fossil.page.forumpost.js | |
| +++ src/fossil.page.forumpost.js | |
| @@ -43,11 +43,11 @@ | |
| 43 | */ |
| 44 | constructor(opt){ |
| 45 | opt = this.#opt = F.nu({ |
| 46 | // todo: defaults once we determine the options |
| 47 | // replyTo: hash |
| 48 | // edit: hash |
| 49 | draftKey: undefined |
| 50 | }, opt); |
| 51 | opt.isNewThread = !opt.replyTo && !opt.edit; |
| 52 | if( opt.draftKey ){ |
| 53 | this.#draft = F.nu(F.storage.getJSON(opt.draftKey, {})); |
| @@ -113,15 +113,24 @@ | |
| 113 | |
| 114 | e.buttons = D.addClass(D.div(), 'buttons'); |
| 115 | { /* Preview/submit buttons... */ |
| 116 | e.button.preview = D.button("Preview", e=>this.#preview()); |
| 117 | e.button.submit = D.button("Submit"); |
| 118 | if( 1 ){ |
| 119 | F.confirmer(e.button.submit, { |
| 120 | confirmText: "Confirm submit...", |
| 121 | onconfirm: ()=>this.#submit() |
| 122 | }); |
| 123 | }else{ |
| 124 | e.button.submit.addEventListener('click', ()=>this.#submit()); |
| 125 | } |
| 126 | e.button.submit.setAttribute('disabled', ''); |
| 127 | wrapper.append(e.buttons); |
| @@ -253,10 +262,14 @@ | |
| 253 | this.#tabs.addTab(e.tabAttach); |
| 254 | /* Reminder: we don't currently have a way to disable/enable |
| 255 | an Attacher's controls during ajax traffic. */ |
| 256 | } |
| 257 | e.buttons.append(e.button.preview, e.button.submit); |
| 258 | this.#toDisable.push(e.button.preview); |
| 259 | |
| 260 | e.help = D.attr(D.div(), 'id', idPrefix+'-help'); |
| 261 | e.help.$needsInit = true; |
| 262 | e.help.dataset.tabLabel = 'Help'; |
| @@ -331,10 +344,21 @@ | |
| 331 | }); |
| 332 | e.buttons.append(e.button.toggleHeader); |
| 333 | } |
| 334 | |
| 335 | }/*constructor*/ |
| 336 | |
| 337 | /** This widget's top-most DOM element. */ |
| 338 | get widget(){ |
| 339 | return this.#e.widget; |
| 340 | } |
| @@ -416,10 +440,20 @@ | |
| 416 | |
| 417 | #initAttacherTab(){ |
| 418 | this.#att = new F.Attacher({ |
| 419 | reverse: true |
| 420 | }); |
| 421 | this.#e.tabAttach.append(this.#att.widget); |
| 422 | } |
| 423 | |
| 424 | #newFormData(addThisContent){ |
| 425 | const fd = new FormData; |
| @@ -773,14 +807,52 @@ | |
| 773 | if( eForumNew ){ |
| 774 | /* /forumnew */ |
| 775 | const fpe = new fossil.ForumPostEditor({ |
| 776 | draftKey: 'forumnew', |
| 777 | hiddenFields: eForumNew.querySelectorAll('input[type=hidden]'), |
| 778 | captcha: eForumNew.querySelector('.captcha-for-js') |
| 779 | //mimetype: 'text/plain' |
| 780 | }); |
| 781 | eForumNew.parentElement.insertBefore(fpe.widget, eForumNew); |
| 782 | eForumNew.remove(); |
| 783 | fossil.page.fpe = fpe /* for testing via the console */; |
| 784 | }/*eForumNew*/ |
| 785 | })/*F.onPageLoad callback*/; |
| 786 | })(window.fossil); |
| 787 |
| --- src/fossil.page.forumpost.js | |
| +++ src/fossil.page.forumpost.js | |
| @@ -43,11 +43,11 @@ | |
| 43 | */ |
| 44 | constructor(opt){ |
| 45 | opt = this.#opt = F.nu({ |
| 46 | // todo: defaults once we determine the options |
| 47 | // replyTo: hash |
| 48 | // fpid: hash |
| 49 | draftKey: undefined |
| 50 | }, opt); |
| 51 | opt.isNewThread = !opt.replyTo && !opt.edit; |
| 52 | if( opt.draftKey ){ |
| 53 | this.#draft = F.nu(F.storage.getJSON(opt.draftKey, {})); |
| @@ -113,15 +113,24 @@ | |
| 113 | |
| 114 | e.buttons = D.addClass(D.div(), 'buttons'); |
| 115 | { /* Preview/submit buttons... */ |
| 116 | e.button.preview = D.button("Preview", e=>this.#preview()); |
| 117 | e.button.submit = D.button("Submit"); |
| 118 | if( opt.ondiscard instanceof Function ){ |
| 119 | e.button.discard = D.button('Discard'); |
| 120 | } |
| 121 | if( 1 ){ |
| 122 | F.confirmer(e.button.submit, { |
| 123 | confirmText: "Confirm submit...", |
| 124 | onconfirm: ()=>this.#submit() |
| 125 | }); |
| 126 | if( e.button.discard ){ |
| 127 | F.confirmer(e.button.discard, { |
| 128 | confirmText: "Really discard?", |
| 129 | onconfirm: ()=>this.discard() |
| 130 | }); |
| 131 | } |
| 132 | }else{ |
| 133 | e.button.submit.addEventListener('click', ()=>this.#submit()); |
| 134 | } |
| 135 | e.button.submit.setAttribute('disabled', ''); |
| 136 | wrapper.append(e.buttons); |
| @@ -253,10 +262,14 @@ | |
| 262 | this.#tabs.addTab(e.tabAttach); |
| 263 | /* Reminder: we don't currently have a way to disable/enable |
| 264 | an Attacher's controls during ajax traffic. */ |
| 265 | } |
| 266 | e.buttons.append(e.button.preview, e.button.submit); |
| 267 | if( e.button.discard ){ |
| 268 | e.buttons.append(e.button.discard); |
| 269 | this.#toDisable.push(e.button.discard); |
| 270 | } |
| 271 | this.#toDisable.push(e.button.preview); |
| 272 | |
| 273 | e.help = D.attr(D.div(), 'id', idPrefix+'-help'); |
| 274 | e.help.$needsInit = true; |
| 275 | e.help.dataset.tabLabel = 'Help'; |
| @@ -331,10 +344,21 @@ | |
| 344 | }); |
| 345 | e.buttons.append(e.button.toggleHeader); |
| 346 | } |
| 347 | |
| 348 | }/*constructor*/ |
| 349 | |
| 350 | discard(){ |
| 351 | this.#clearDraft(); |
| 352 | const e = this.#e.widget; |
| 353 | if( e.parentNode ){ |
| 354 | e.remove(); |
| 355 | if( this.#opt.ondiscard instanceof Function ){ |
| 356 | this.#opt.ondiscard(); |
| 357 | } |
| 358 | } |
| 359 | } |
| 360 | |
| 361 | /** This widget's top-most DOM element. */ |
| 362 | get widget(){ |
| 363 | return this.#e.widget; |
| 364 | } |
| @@ -416,10 +440,20 @@ | |
| 440 | |
| 441 | #initAttacherTab(){ |
| 442 | this.#att = new F.Attacher({ |
| 443 | reverse: true |
| 444 | }); |
| 445 | if( this.#opt.fpid ){ |
| 446 | const eNote = D.append( |
| 447 | D.div(), |
| 448 | "Tip: attachments can be added to posts without editing them", |
| 449 | "by visiting ", |
| 450 | D.a(F.repoUrl('attachadd?target='+this.#opt.fpid), '/attachadd'), |
| 451 | ".", |
| 452 | ); |
| 453 | this.#e.tabAttach.append(eNote); |
| 454 | } |
| 455 | this.#e.tabAttach.append(this.#att.widget); |
| 456 | } |
| 457 | |
| 458 | #newFormData(addThisContent){ |
| 459 | const fd = new FormData; |
| @@ -773,14 +807,52 @@ | |
| 807 | if( eForumNew ){ |
| 808 | /* /forumnew */ |
| 809 | const fpe = new fossil.ForumPostEditor({ |
| 810 | draftKey: 'forumnew', |
| 811 | hiddenFields: eForumNew.querySelectorAll('input[type=hidden]'), |
| 812 | captcha: eForumNew.querySelector('.captcha-for-js'), |
| 813 | ondiscard: ()=>{ |
| 814 | window.location = F.repoUrl('forum'); |
| 815 | } |
| 816 | //mimetype: 'text/plain' |
| 817 | }); |
| 818 | eForumNew.parentElement.insertBefore(fpe.widget, eForumNew); |
| 819 | eForumNew.remove(); |
| 820 | fossil.page.fpe = fpe /* for testing via the console */; |
| 821 | }/*eForumNew*/ |
| 822 | else if( 0 && (document.body.classList.contains('cpage-forumpost') |
| 823 | || document.body.classList.contains('cpage-forumthread'))){ |
| 824 | /* /forumpost and /forumthread */ |
| 825 | const replyClicked = (replyButton, fpid)=>{ |
| 826 | F.toast.error("Reply is TODO. fpid="+fpid); |
| 827 | /* |
| 828 | TODOs include: |
| 829 | |
| 830 | - Hide replyButton |
| 831 | |
| 832 | - Pop up a ForumPostEditor. It needs a Cancel button. |
| 833 | |
| 834 | - When cancelled or submitted, restore the reply button. |
| 835 | */ |
| 836 | }; |
| 837 | document.body.querySelectorAll( |
| 838 | '.forumpost-single-controls > form' |
| 839 | ).forEach(form=>{ |
| 840 | const eReplyTo = form.parentElement.parentElement; |
| 841 | const fpid = eReplyTo?.dataset?.fpid; |
| 842 | if( !fpid ){ |
| 843 | console.warn("Unexpected non-fpid", form, eReplyTo); |
| 844 | return; |
| 845 | } |
| 846 | const rb = form.querySelector('input[type=submit][name=reply]'); |
| 847 | if( rb ){ |
| 848 | console.debug("hacking Reply button", rb); |
| 849 | const b = D.button("Reply", ()=>replyClicked(b, fpid)); |
| 850 | b.type = 'button'/*keep container form from submitting*/; |
| 851 | rb.parentElement.insertBefore(b, rb); |
| 852 | rb.remove(); |
| 853 | } |
| 854 | }); |
| 855 | } |
| 856 | |
| 857 | })/*F.onPageLoad callback*/; |
| 858 | })(window.fossil); |
| 859 |