Fossil SCM
Get replying basically working. Attachments to responses are being saved but are not showing up in the attachment list later, which is weird. Just now noticing that forum threads can be rendered under /info, in which case the Edit/Reply buttons do not get hijacked for the new editor because this JS isn't loaded in that page. Whether that's a feature or bug is TBD.
Commit
6269e5d70004f6f9b090757d41fd10973b1886631c2f577d4d2f3305cb6bc579
Parent
14abbc4d469c18e…
2 files changed
+40
-52
+3
+40
-52
| --- src/fossil.page.forumpost.js | ||
| +++ src/fossil.page.forumpost.js | ||
| @@ -77,14 +77,15 @@ | ||
| 77 | 77 | } |
| 78 | 78 | const e = this.#e = F.nu({ |
| 79 | 79 | mimetype: F.nu(), |
| 80 | 80 | button: F.nu() |
| 81 | 81 | }); |
| 82 | + console.debug("Setting up FPE opt =",opt); | |
| 82 | 83 | const wrapper = e.widget = D.addClass(D.div(), 'ForumPostEditor'); |
| 83 | 84 | D.clearElement(wrapper); |
| 84 | 85 | |
| 85 | - if( !opt.inReplyTo && !opt.hideTitle ){ | |
| 86 | + if( !opt.inReplyTo ){ | |
| 86 | 87 | /* Title... */ |
| 87 | 88 | e.titleBar = D.addClass(D.div(),'titlebar'); |
| 88 | 89 | e.title = D.attr( |
| 89 | 90 | D.addClass(D.input('text'), 'title'), |
| 90 | 91 | 'placeholder', |
| @@ -279,11 +280,11 @@ | ||
| 279 | 280 | this.#draft.status = v; |
| 280 | 281 | this.#storeDraft(); |
| 281 | 282 | } |
| 282 | 283 | }); |
| 283 | 284 | } |
| 284 | - } | |
| 285 | + }/*e.status*/ | |
| 285 | 286 | |
| 286 | 287 | if( F.user.mayAttachForum ){ |
| 287 | 288 | //e.buttons.append( e.button.addAttach = this.#att.takeAddButton() ); |
| 288 | 289 | e.tabAttach = D.div(); |
| 289 | 290 | e.tabAttach.setAttribute('id', idPrefix+'-attach'); |
| @@ -426,10 +427,11 @@ | ||
| 426 | 427 | collecting, e.g., the CSRF token and an initial page title. |
| 427 | 428 | */ |
| 428 | 429 | addHiddenFields(list){ |
| 429 | 430 | this.#extraFields ??= []; |
| 430 | 431 | for( const f of list ){ |
| 432 | + if( !f ) continue; | |
| 431 | 433 | if( 'title'===f.name && this.#e.title ){ |
| 432 | 434 | if( f.value && this.#opt.isNewThread && !this.#e.title.value ){ |
| 433 | 435 | this.#e.title.value = f.value; |
| 434 | 436 | } |
| 435 | 437 | }else{ |
| @@ -604,11 +606,11 @@ | ||
| 604 | 606 | } |
| 605 | 607 | if( e.debug ){ |
| 606 | 608 | e.debug.querySelectorAll('input[type=checkbox]').forEach(cb=>{ |
| 607 | 609 | if( cb.checked ){ |
| 608 | 610 | fd.append(cb.value, 1); |
| 609 | - console.debug("Debug option:",cb); | |
| 611 | + //console.debug("Forum post debug option:",cb); | |
| 610 | 612 | } |
| 611 | 613 | }); |
| 612 | 614 | } |
| 613 | 615 | if( this.#att ){ |
| 614 | 616 | this.#att.populateFormData(fd); |
| @@ -616,16 +618,10 @@ | ||
| 616 | 618 | console.warn("Ready to submit",fd); |
| 617 | 619 | if( 0 ){ |
| 618 | 620 | this.#isWaiting = false; |
| 619 | 621 | return; |
| 620 | 622 | } |
| 621 | - /* | |
| 622 | - TODO: save it, set #isWaiting=false, then handle error or | |
| 623 | - redirect to the post (if this is a new post) or, if replying | |
| 624 | - inline, replace this object with a static rendering from the | |
| 625 | - response. | |
| 626 | - */ | |
| 627 | 623 | const resp = window.fetch(F.repoUrl('forumajax_save'), { |
| 628 | 624 | method: 'POST', |
| 629 | 625 | body: fd |
| 630 | 626 | }).then(r=>r.json()) |
| 631 | 627 | .then(j=>{ |
| @@ -939,55 +935,45 @@ | ||
| 939 | 935 | D.enable(eToDisable); |
| 940 | 936 | }; |
| 941 | 937 | |
| 942 | 938 | const replyClicked = (form, ePost, eBtnReply, eToDisable)=>{ |
| 943 | 939 | const fpid = setupEditReplyElement(ePost, eBtnReply, eToDisable); |
| 944 | - const firt = ePost.dataset.firt; | |
| 945 | 940 | const fEditHead = ePost.dataset.fedithead; |
| 946 | 941 | eBtnReply.innerText = "Replying..."; |
| 947 | - F.toast.error("Reply is TODO. fpid="+fpid); | |
| 948 | - /* | |
| 949 | - TODOs include: | |
| 950 | - | |
| 951 | - - Hide replyButton | |
| 952 | - | |
| 953 | - - Shift ePost to the left edge. | |
| 954 | - | |
| 955 | - - Fetch /ajax/artifact.json?uuid=fpid | |
| 956 | - | |
| 957 | - - Pop up a ForumPostEditor immediately under ePost. It needs | |
| 958 | - a Cancel button. | |
| 959 | - | |
| 960 | - - When cancelled or submitted, restore the reply button and | |
| 961 | - ePost position. | |
| 962 | - */ | |
| 963 | - fetchPost(fpid) | |
| 964 | - .then(artifact=>{ | |
| 965 | - const ondone = (fpe)=>{ | |
| 966 | - restoreEditReplyElement(ePost, eBtnReply, eToDisable); | |
| 967 | - //console.debug("ondiscard/onsubmit", fpe, eToDisable); | |
| 968 | - if( fpe/*onsubmit*/ ){ | |
| 969 | - if( fpe.widget.parentNode ){ | |
| 970 | - fpe.widget.remove(); | |
| 971 | - } | |
| 972 | - } | |
| 973 | - }; | |
| 974 | - const fpe = new F.ForumPostEditor({ | |
| 975 | - hiddenFields: form.querySelectorAll('input[type=hidden]'), | |
| 976 | - ondiscard: ondone, | |
| 977 | - onsubmit: ondone, | |
| 978 | - draftKey: 'draft-forumedit-'+(fEditHead || fpid).substr(0,12), | |
| 979 | - hideTitle: true/*fixme: only show if this is the root post*/, | |
| 980 | - edit: artifact, | |
| 981 | - inReplyTo: firt | |
| 982 | - }); | |
| 983 | - const w = fpe.widget; | |
| 984 | - w.style.borderTop = '2px dotted'; | |
| 985 | - /* Adding an "Editing..." <h3> here adds way too much space */ | |
| 986 | - ePost.append(w); | |
| 987 | - w.scrollIntoView(); | |
| 988 | - }); | |
| 942 | + const ondone = (fpe)=>{ | |
| 943 | + restoreEditReplyElement(ePost, eBtnReply, eToDisable); | |
| 944 | + //console.debug("ondiscard/onsubmit", fpe, eToDisable); | |
| 945 | + if( fpe/*onsubmit*/ ){ | |
| 946 | + if( fpe.widget.parentNode ){ | |
| 947 | + fpe.widget.remove(); | |
| 948 | + } | |
| 949 | + } | |
| 950 | + }; | |
| 951 | + const fpe = new F.ForumPostEditor({ | |
| 952 | + hiddenFields: form.querySelectorAll( | |
| 953 | + 'input[type=hidden][name=csrf]' | |
| 954 | + /* Do not inherit the fpid field, else this will become | |
| 955 | + an edit to that post rather than a response. */ | |
| 956 | + ), | |
| 957 | + ondiscard: ondone, | |
| 958 | + onsubmit: ondone, | |
| 959 | + inReplyTo: fpid, | |
| 960 | + draftKey: 'draft-reply-'+( | |
| 961 | + fEditHead | |
| 962 | + /* The problem with firt as a key is that firt is not | |
| 963 | + necessarily the root edit of that post, which is | |
| 964 | + what we really want as a draft key so that the | |
| 965 | + draft does not disapper if firt is later edited | |
| 966 | + (giving us a new firt value here). */ | |
| 967 | + || fpid | |
| 968 | + ).substr(0,12) | |
| 969 | + }); | |
| 970 | + const w = fpe.widget; | |
| 971 | + w.style.borderTop = '2px dotted'; | |
| 972 | + /* Adding an "Editing..." <h3> here adds way too much space */ | |
| 973 | + ePost.append(w); | |
| 974 | + w.scrollIntoView(); | |
| 989 | 975 | }/*replyClicked()*/; |
| 990 | 976 | |
| 991 | 977 | const editClicked = (form, ePost, eBtnEdit, eToDisable)=>{ |
| 992 | 978 | const fpid = setupEditReplyElement(ePost, eBtnEdit, eToDisable); |
| 993 | 979 | const firt = ePost.dataset.firt; |
| @@ -1017,13 +1003,15 @@ | ||
| 1017 | 1003 | status: eStatusSelect?.value, |
| 1018 | 1004 | inReplyTo: firt |
| 1019 | 1005 | }); |
| 1020 | 1006 | const w = fpe.widget; |
| 1021 | 1007 | w.style.borderTop = '2px dotted'; |
| 1008 | + //w.style.height = '0px'; | |
| 1022 | 1009 | /* Adding an "Editing..." <h3> here adds way too much space */ |
| 1023 | 1010 | ePost.append(w); |
| 1024 | 1011 | w.scrollIntoView(); |
| 1012 | + //w.style.height = ''; | |
| 1025 | 1013 | }); |
| 1026 | 1014 | }/*editClicked()*/; |
| 1027 | 1015 | |
| 1028 | 1016 | document.body.querySelectorAll( |
| 1029 | 1017 | '.forumpost-single-controls > form' |
| 1030 | 1018 |
| --- src/fossil.page.forumpost.js | |
| +++ src/fossil.page.forumpost.js | |
| @@ -77,14 +77,15 @@ | |
| 77 | } |
| 78 | const e = this.#e = F.nu({ |
| 79 | mimetype: F.nu(), |
| 80 | button: F.nu() |
| 81 | }); |
| 82 | const wrapper = e.widget = D.addClass(D.div(), 'ForumPostEditor'); |
| 83 | D.clearElement(wrapper); |
| 84 | |
| 85 | if( !opt.inReplyTo && !opt.hideTitle ){ |
| 86 | /* Title... */ |
| 87 | e.titleBar = D.addClass(D.div(),'titlebar'); |
| 88 | e.title = D.attr( |
| 89 | D.addClass(D.input('text'), 'title'), |
| 90 | 'placeholder', |
| @@ -279,11 +280,11 @@ | |
| 279 | this.#draft.status = v; |
| 280 | this.#storeDraft(); |
| 281 | } |
| 282 | }); |
| 283 | } |
| 284 | } |
| 285 | |
| 286 | if( F.user.mayAttachForum ){ |
| 287 | //e.buttons.append( e.button.addAttach = this.#att.takeAddButton() ); |
| 288 | e.tabAttach = D.div(); |
| 289 | e.tabAttach.setAttribute('id', idPrefix+'-attach'); |
| @@ -426,10 +427,11 @@ | |
| 426 | collecting, e.g., the CSRF token and an initial page title. |
| 427 | */ |
| 428 | addHiddenFields(list){ |
| 429 | this.#extraFields ??= []; |
| 430 | for( const f of list ){ |
| 431 | if( 'title'===f.name && this.#e.title ){ |
| 432 | if( f.value && this.#opt.isNewThread && !this.#e.title.value ){ |
| 433 | this.#e.title.value = f.value; |
| 434 | } |
| 435 | }else{ |
| @@ -604,11 +606,11 @@ | |
| 604 | } |
| 605 | if( e.debug ){ |
| 606 | e.debug.querySelectorAll('input[type=checkbox]').forEach(cb=>{ |
| 607 | if( cb.checked ){ |
| 608 | fd.append(cb.value, 1); |
| 609 | console.debug("Debug option:",cb); |
| 610 | } |
| 611 | }); |
| 612 | } |
| 613 | if( this.#att ){ |
| 614 | this.#att.populateFormData(fd); |
| @@ -616,16 +618,10 @@ | |
| 616 | console.warn("Ready to submit",fd); |
| 617 | if( 0 ){ |
| 618 | this.#isWaiting = false; |
| 619 | return; |
| 620 | } |
| 621 | /* |
| 622 | TODO: save it, set #isWaiting=false, then handle error or |
| 623 | redirect to the post (if this is a new post) or, if replying |
| 624 | inline, replace this object with a static rendering from the |
| 625 | response. |
| 626 | */ |
| 627 | const resp = window.fetch(F.repoUrl('forumajax_save'), { |
| 628 | method: 'POST', |
| 629 | body: fd |
| 630 | }).then(r=>r.json()) |
| 631 | .then(j=>{ |
| @@ -939,55 +935,45 @@ | |
| 939 | D.enable(eToDisable); |
| 940 | }; |
| 941 | |
| 942 | const replyClicked = (form, ePost, eBtnReply, eToDisable)=>{ |
| 943 | const fpid = setupEditReplyElement(ePost, eBtnReply, eToDisable); |
| 944 | const firt = ePost.dataset.firt; |
| 945 | const fEditHead = ePost.dataset.fedithead; |
| 946 | eBtnReply.innerText = "Replying..."; |
| 947 | F.toast.error("Reply is TODO. fpid="+fpid); |
| 948 | /* |
| 949 | TODOs include: |
| 950 | |
| 951 | - Hide replyButton |
| 952 | |
| 953 | - Shift ePost to the left edge. |
| 954 | |
| 955 | - Fetch /ajax/artifact.json?uuid=fpid |
| 956 | |
| 957 | - Pop up a ForumPostEditor immediately under ePost. It needs |
| 958 | a Cancel button. |
| 959 | |
| 960 | - When cancelled or submitted, restore the reply button and |
| 961 | ePost position. |
| 962 | */ |
| 963 | fetchPost(fpid) |
| 964 | .then(artifact=>{ |
| 965 | const ondone = (fpe)=>{ |
| 966 | restoreEditReplyElement(ePost, eBtnReply, eToDisable); |
| 967 | //console.debug("ondiscard/onsubmit", fpe, eToDisable); |
| 968 | if( fpe/*onsubmit*/ ){ |
| 969 | if( fpe.widget.parentNode ){ |
| 970 | fpe.widget.remove(); |
| 971 | } |
| 972 | } |
| 973 | }; |
| 974 | const fpe = new F.ForumPostEditor({ |
| 975 | hiddenFields: form.querySelectorAll('input[type=hidden]'), |
| 976 | ondiscard: ondone, |
| 977 | onsubmit: ondone, |
| 978 | draftKey: 'draft-forumedit-'+(fEditHead || fpid).substr(0,12), |
| 979 | hideTitle: true/*fixme: only show if this is the root post*/, |
| 980 | edit: artifact, |
| 981 | inReplyTo: firt |
| 982 | }); |
| 983 | const w = fpe.widget; |
| 984 | w.style.borderTop = '2px dotted'; |
| 985 | /* Adding an "Editing..." <h3> here adds way too much space */ |
| 986 | ePost.append(w); |
| 987 | w.scrollIntoView(); |
| 988 | }); |
| 989 | }/*replyClicked()*/; |
| 990 | |
| 991 | const editClicked = (form, ePost, eBtnEdit, eToDisable)=>{ |
| 992 | const fpid = setupEditReplyElement(ePost, eBtnEdit, eToDisable); |
| 993 | const firt = ePost.dataset.firt; |
| @@ -1017,13 +1003,15 @@ | |
| 1017 | status: eStatusSelect?.value, |
| 1018 | inReplyTo: firt |
| 1019 | }); |
| 1020 | const w = fpe.widget; |
| 1021 | w.style.borderTop = '2px dotted'; |
| 1022 | /* Adding an "Editing..." <h3> here adds way too much space */ |
| 1023 | ePost.append(w); |
| 1024 | w.scrollIntoView(); |
| 1025 | }); |
| 1026 | }/*editClicked()*/; |
| 1027 | |
| 1028 | document.body.querySelectorAll( |
| 1029 | '.forumpost-single-controls > form' |
| 1030 |
| --- src/fossil.page.forumpost.js | |
| +++ src/fossil.page.forumpost.js | |
| @@ -77,14 +77,15 @@ | |
| 77 | } |
| 78 | const e = this.#e = F.nu({ |
| 79 | mimetype: F.nu(), |
| 80 | button: F.nu() |
| 81 | }); |
| 82 | console.debug("Setting up FPE opt =",opt); |
| 83 | const wrapper = e.widget = D.addClass(D.div(), 'ForumPostEditor'); |
| 84 | D.clearElement(wrapper); |
| 85 | |
| 86 | if( !opt.inReplyTo ){ |
| 87 | /* Title... */ |
| 88 | e.titleBar = D.addClass(D.div(),'titlebar'); |
| 89 | e.title = D.attr( |
| 90 | D.addClass(D.input('text'), 'title'), |
| 91 | 'placeholder', |
| @@ -279,11 +280,11 @@ | |
| 280 | this.#draft.status = v; |
| 281 | this.#storeDraft(); |
| 282 | } |
| 283 | }); |
| 284 | } |
| 285 | }/*e.status*/ |
| 286 | |
| 287 | if( F.user.mayAttachForum ){ |
| 288 | //e.buttons.append( e.button.addAttach = this.#att.takeAddButton() ); |
| 289 | e.tabAttach = D.div(); |
| 290 | e.tabAttach.setAttribute('id', idPrefix+'-attach'); |
| @@ -426,10 +427,11 @@ | |
| 427 | collecting, e.g., the CSRF token and an initial page title. |
| 428 | */ |
| 429 | addHiddenFields(list){ |
| 430 | this.#extraFields ??= []; |
| 431 | for( const f of list ){ |
| 432 | if( !f ) continue; |
| 433 | if( 'title'===f.name && this.#e.title ){ |
| 434 | if( f.value && this.#opt.isNewThread && !this.#e.title.value ){ |
| 435 | this.#e.title.value = f.value; |
| 436 | } |
| 437 | }else{ |
| @@ -604,11 +606,11 @@ | |
| 606 | } |
| 607 | if( e.debug ){ |
| 608 | e.debug.querySelectorAll('input[type=checkbox]').forEach(cb=>{ |
| 609 | if( cb.checked ){ |
| 610 | fd.append(cb.value, 1); |
| 611 | //console.debug("Forum post debug option:",cb); |
| 612 | } |
| 613 | }); |
| 614 | } |
| 615 | if( this.#att ){ |
| 616 | this.#att.populateFormData(fd); |
| @@ -616,16 +618,10 @@ | |
| 618 | console.warn("Ready to submit",fd); |
| 619 | if( 0 ){ |
| 620 | this.#isWaiting = false; |
| 621 | return; |
| 622 | } |
| 623 | const resp = window.fetch(F.repoUrl('forumajax_save'), { |
| 624 | method: 'POST', |
| 625 | body: fd |
| 626 | }).then(r=>r.json()) |
| 627 | .then(j=>{ |
| @@ -939,55 +935,45 @@ | |
| 935 | D.enable(eToDisable); |
| 936 | }; |
| 937 | |
| 938 | const replyClicked = (form, ePost, eBtnReply, eToDisable)=>{ |
| 939 | const fpid = setupEditReplyElement(ePost, eBtnReply, eToDisable); |
| 940 | const fEditHead = ePost.dataset.fedithead; |
| 941 | eBtnReply.innerText = "Replying..."; |
| 942 | const ondone = (fpe)=>{ |
| 943 | restoreEditReplyElement(ePost, eBtnReply, eToDisable); |
| 944 | //console.debug("ondiscard/onsubmit", fpe, eToDisable); |
| 945 | if( fpe/*onsubmit*/ ){ |
| 946 | if( fpe.widget.parentNode ){ |
| 947 | fpe.widget.remove(); |
| 948 | } |
| 949 | } |
| 950 | }; |
| 951 | const fpe = new F.ForumPostEditor({ |
| 952 | hiddenFields: form.querySelectorAll( |
| 953 | 'input[type=hidden][name=csrf]' |
| 954 | /* Do not inherit the fpid field, else this will become |
| 955 | an edit to that post rather than a response. */ |
| 956 | ), |
| 957 | ondiscard: ondone, |
| 958 | onsubmit: ondone, |
| 959 | inReplyTo: fpid, |
| 960 | draftKey: 'draft-reply-'+( |
| 961 | fEditHead |
| 962 | /* The problem with firt as a key is that firt is not |
| 963 | necessarily the root edit of that post, which is |
| 964 | what we really want as a draft key so that the |
| 965 | draft does not disapper if firt is later edited |
| 966 | (giving us a new firt value here). */ |
| 967 | || fpid |
| 968 | ).substr(0,12) |
| 969 | }); |
| 970 | const w = fpe.widget; |
| 971 | w.style.borderTop = '2px dotted'; |
| 972 | /* Adding an "Editing..." <h3> here adds way too much space */ |
| 973 | ePost.append(w); |
| 974 | w.scrollIntoView(); |
| 975 | }/*replyClicked()*/; |
| 976 | |
| 977 | const editClicked = (form, ePost, eBtnEdit, eToDisable)=>{ |
| 978 | const fpid = setupEditReplyElement(ePost, eBtnEdit, eToDisable); |
| 979 | const firt = ePost.dataset.firt; |
| @@ -1017,13 +1003,15 @@ | |
| 1003 | status: eStatusSelect?.value, |
| 1004 | inReplyTo: firt |
| 1005 | }); |
| 1006 | const w = fpe.widget; |
| 1007 | w.style.borderTop = '2px dotted'; |
| 1008 | //w.style.height = '0px'; |
| 1009 | /* Adding an "Editing..." <h3> here adds way too much space */ |
| 1010 | ePost.append(w); |
| 1011 | w.scrollIntoView(); |
| 1012 | //w.style.height = ''; |
| 1013 | }); |
| 1014 | }/*editClicked()*/; |
| 1015 | |
| 1016 | document.body.querySelectorAll( |
| 1017 | '.forumpost-single-controls > form' |
| 1018 |
+3
| --- src/style.forum.css | ||
| +++ src/style.forum.css | ||
| @@ -9,10 +9,13 @@ | ||
| 9 | 9 | .ForumPostEditor { |
| 10 | 10 | display: flex; |
| 11 | 11 | flex-direction: column; |
| 12 | 12 | gap: 1em; |
| 13 | 13 | padding: 0.5em; |
| 14 | + /* Anyone know how to make these fade/slide in gracefully? | |
| 15 | + transition: display 0.5s ease-in-out; | |
| 16 | + transition: height 0.5s ease-in-out; */ | |
| 14 | 17 | } |
| 15 | 18 | |
| 16 | 19 | .ForumPostEditor > .tab-bar{ |
| 17 | 20 | } |
| 18 | 21 | |
| 19 | 22 |
| --- src/style.forum.css | |
| +++ src/style.forum.css | |
| @@ -9,10 +9,13 @@ | |
| 9 | .ForumPostEditor { |
| 10 | display: flex; |
| 11 | flex-direction: column; |
| 12 | gap: 1em; |
| 13 | padding: 0.5em; |
| 14 | } |
| 15 | |
| 16 | .ForumPostEditor > .tab-bar{ |
| 17 | } |
| 18 | |
| 19 |
| --- src/style.forum.css | |
| +++ src/style.forum.css | |
| @@ -9,10 +9,13 @@ | |
| 9 | .ForumPostEditor { |
| 10 | display: flex; |
| 11 | flex-direction: column; |
| 12 | gap: 1em; |
| 13 | padding: 0.5em; |
| 14 | /* Anyone know how to make these fade/slide in gracefully? |
| 15 | transition: display 0.5s ease-in-out; |
| 16 | transition: height 0.5s ease-in-out; */ |
| 17 | } |
| 18 | |
| 19 | .ForumPostEditor > .tab-bar{ |
| 20 | } |
| 21 | |
| 22 |