| | @@ -38,19 +38,33 @@ |
| 38 | 38 | |
| 39 | 39 | opt.draftKey[string=undefined]: if set then this object's state |
| 40 | 40 | will be stored in fossil.storage when the relevant input fields |
| 41 | 41 | lose focus. If old state is found, the form is pre-populated |
| 42 | 42 | from it. The state is cleared on a successful submit. |
| 43 | + |
| 44 | + opt.ondiscard[=function]: if set, a Discard button is added |
| 45 | + which, when activated, clears the current draft and removes |
| 46 | + this object's widget from the DOM. After that, opt.ondiscard() |
| 47 | + is called and passed no arguments. |
| 48 | + |
| 49 | + TODO: |
| 50 | + |
| 51 | + opt.inReplyTo=uuid: if this is a new response to a post, this |
| 52 | + is the full forum post uuid of the being-replied-to post. |
| 53 | + |
| 54 | + opt.edit=artifactObject: if this is an edit of an existing |
| 55 | + post, this is the full JSON-format artifact of the forum post |
| 56 | + the being-edit post. |
| 43 | 57 | */ |
| 44 | 58 | constructor(opt){ |
| 45 | 59 | opt = this.#opt = F.nu({ |
| 46 | 60 | // todo: defaults once we determine the options |
| 47 | | - // replyTo: hash |
| 61 | + // inReplyTo: hash |
| 48 | 62 | // fpid: hash |
| 49 | 63 | draftKey: undefined |
| 50 | 64 | }, opt); |
| 51 | | - opt.isNewThread = !opt.replyTo && !opt.edit; |
| 65 | + opt.isNewThread = !opt.inReplyTo && !opt.edit; |
| 52 | 66 | if( opt.draftKey ){ |
| 53 | 67 | this.#draft = F.nu(F.storage.getJSON(opt.draftKey, {})); |
| 54 | 68 | } |
| 55 | 69 | const e = this.#e = F.nu({ |
| 56 | 70 | mimetype: F.nu(), |
| | @@ -75,13 +89,13 @@ |
| 75 | 89 | if( this.#draft ){ |
| 76 | 90 | e.title.addEventListener('blur', ()=>{ |
| 77 | 91 | this.#draft.title = e.title.value; |
| 78 | 92 | this.#storeDraft(); |
| 79 | 93 | }); |
| 80 | | - e.title.value = opt.title || this.#draft.title || ''; |
| 81 | | - }else if( opt.title ){ |
| 82 | | - e.title.value = opt.title; |
| 94 | + e.title.value = opt.edit?.H || this.#draft.title || ''; |
| 95 | + }else if( opt.edit?.H ){ |
| 96 | + e.title.value = opt.edit.H; |
| 83 | 97 | } |
| 84 | 98 | wrapper.append(e.titleBar); |
| 85 | 99 | } |
| 86 | 100 | |
| 87 | 101 | { /* Mimetype... */ |
| | @@ -191,17 +205,19 @@ |
| 191 | 205 | e.tabEdit.append(e.editor); |
| 192 | 206 | e.tabEdit.dataset.tabLabel = 'Edit'; |
| 193 | 207 | this.#tabs.addTab( e.tabEdit ); |
| 194 | 208 | this.#tabs.switchToTab( e.tabEdit ); |
| 195 | 209 | if( this.#draft ){ |
| 196 | | - this.editorContent = this.#draft.content || ''; |
| 210 | + this.editorContent = opt.edit?.W || this.#draft.content || ''; |
| 197 | 211 | e.editor.addEventListener( |
| 198 | 212 | 'blur', ()=>{ |
| 199 | 213 | this.#draft.content = this.editorContent; |
| 200 | 214 | this.#storeDraft(); |
| 201 | 215 | } |
| 202 | 216 | ); |
| 217 | + }else if( opt.edit?.W ){ |
| 218 | + this.editorContent = opt.artifact.W; |
| 203 | 219 | } |
| 204 | 220 | e.preview = D.addClass(D.div(), 'preview'); |
| 205 | 221 | e.preview.dataset.tabLabel = 'Preview'; |
| 206 | 222 | this.#tabs.addTab( e.preview ); |
| 207 | 223 | } |
| | @@ -472,11 +488,15 @@ |
| 472 | 488 | async #fetchPreview(content){ |
| 473 | 489 | /* TODO: fetch preview */ |
| 474 | 490 | const e = this.#e; |
| 475 | 491 | const fd = this.#newFormData(content); |
| 476 | 492 | return window |
| 477 | | - .fetch(F.repoUrl('wikiajax/preview'), { |
| 493 | + .fetch(F.repoUrl( |
| 494 | + 'wikiajax/preview' |
| 495 | + /* ^^^ Maybe change to /ajax/preview-text, but it's |
| 496 | + ** got a more complicated interface */ |
| 497 | + ), { |
| 478 | 498 | method: 'POST', |
| 479 | 499 | body: fd |
| 480 | 500 | }) |
| 481 | 501 | .then(r=>r.text()) |
| 482 | 502 | .then(t=>{ |
| | @@ -799,10 +819,11 @@ |
| 799 | 819 | }); |
| 800 | 820 | }); |
| 801 | 821 | }); |
| 802 | 822 | } |
| 803 | 823 | |
| 824 | + const userIsIndividual = ['anonymous','guest'].indexOf(F.user.name)<0; |
| 804 | 825 | const eForumNew = document.body.classList.contains('cpage-forumnew') |
| 805 | 826 | ? document.querySelector('#forumnew-placeholder') |
| 806 | 827 | : null; |
| 807 | 828 | if( eForumNew ){ |
| 808 | 829 | /* /forumnew */ |
| | @@ -817,42 +838,116 @@ |
| 817 | 838 | }); |
| 818 | 839 | eForumNew.parentElement.insertBefore(fpe.widget, eForumNew); |
| 819 | 840 | eForumNew.remove(); |
| 820 | 841 | fossil.page.fpe = fpe /* for testing via the console */; |
| 821 | 842 | }/*eForumNew*/ |
| 822 | | - else if( 0 && (document.body.classList.contains('cpage-forumpost') |
| 823 | | - || document.body.classList.contains('cpage-forumthread'))){ |
| 843 | + else if( userIsIndividual |
| 844 | + && (document.body.classList.contains('cpage-forumpost') |
| 845 | + || document.body.classList.contains('cpage-forumthread'))){ |
| 824 | 846 | /* /forumpost and /forumthread */ |
| 825 | | - const replyClicked = (replyButton, fpid)=>{ |
| 847 | + |
| 848 | + const fetchPost = async (fpid)=>{ |
| 849 | + return window.fetch(F.repoUrl('ajax/artifact.json?uuid='+fpid)) |
| 850 | + .then(r=>r.json()) |
| 851 | + .then(j=>{ |
| 852 | + if( j.error ) throw new Error(j.error); |
| 853 | + return j; |
| 854 | + }); |
| 855 | + }; |
| 856 | + |
| 857 | + const setupEditReplyElement = (ePost, eButton)=>{ |
| 858 | + const fpid = ePost.dataset.fpid; |
| 859 | + ePost.dataset.originalMarginLeft = ePost.style.marginLeft; |
| 860 | + ePost.style.marginLeft = 'initial'; |
| 861 | + eButton.disabled = true; |
| 862 | + eButton.dataset.originalLabel = eButton.value; |
| 863 | + return fpid; |
| 864 | + }; |
| 865 | + |
| 866 | + const restoreEditReplyElement = (ePost, eButton)=>{ |
| 867 | + if( ePost.dataset.originalMarginLeft ){ |
| 868 | + ePost.style.marginLeft = ePost.dataset.originalMarginLeft; |
| 869 | + delete ePost.dataset.originalMarginLeft; |
| 870 | + } |
| 871 | + eButton.disabled = false; |
| 872 | + if( eButton.dataset.originalLabel ){ |
| 873 | + eButton.innerText = eButton.dataset.originalLabel; |
| 874 | + delete eButton.dataset.originalLabel; |
| 875 | + } |
| 876 | + }; |
| 877 | + |
| 878 | + const replyClicked = (ePost, eBtnReply)=>{ |
| 879 | + const fpid = setupEditReplyElement(ePost, eBtnReply); |
| 880 | + eBtnReply.innerText = "Replying..."; |
| 826 | 881 | F.toast.error("Reply is TODO. fpid="+fpid); |
| 827 | 882 | /* |
| 828 | 883 | TODOs include: |
| 829 | 884 | |
| 830 | 885 | - Hide replyButton |
| 831 | 886 | |
| 832 | | - - Pop up a ForumPostEditor. It needs a Cancel button. |
| 887 | + - Shift ePost to the left edge. |
| 888 | + |
| 889 | + - Fetch /ajax/artifact.json?uuid=fpid |
| 890 | + |
| 891 | + - Pop up a ForumPostEditor immediately under ePost. It needs |
| 892 | + a Cancel button. |
| 893 | + |
| 894 | + - When cancelled or submitted, restore the reply button and |
| 895 | + ePost position. |
| 896 | + */ |
| 897 | + if( 0 ) restoreEditReplyElement(ePost, eBtnReply); |
| 898 | + }/*replyClicked()*/; |
| 899 | + |
| 900 | + const editClicked = (ePost, eBtnEdit)=>{ |
| 901 | + const fpid = setupEditReplyElement(ePost, eBtnEdit); |
| 902 | + eBtnEdit.innerText = "Editing..."; |
| 903 | + F.toast.error("Edit is TODO. fpid="+fpid); |
| 904 | + /* |
| 905 | + TODOs include: |
| 906 | + |
| 907 | + - Disable editButton. |
| 908 | + |
| 909 | + - Shift ePost to the left edge. |
| 910 | + |
| 911 | + - Pop up a ForumPostEditor immediately under ePost. It needs |
| 912 | + a Cancel button. |
| 833 | 913 | |
| 834 | | - - When cancelled or submitted, restore the reply button. |
| 914 | + - When cancelled or submitted, restore the edit button and |
| 915 | + ePost position. |
| 835 | 916 | */ |
| 836 | | - }; |
| 917 | + fetchPost(fpid) |
| 918 | + .then(j=>{ |
| 919 | + console.debug("Got post... now what?", j); |
| 920 | + if( 0 ) restoreEditReplyElement(ePost, eBtnEdit); |
| 921 | + }); |
| 922 | + }/*editClicked()*/; |
| 923 | + |
| 837 | 924 | document.body.querySelectorAll( |
| 838 | 925 | '.forumpost-single-controls > form' |
| 839 | 926 | ).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); |
| 927 | + //console.debug("Checking form",form); |
| 928 | + const eThePost = form.parentElement.parentElement; |
| 929 | + if( !eThePost?.dataset?.fpid ){ |
| 930 | + console.warn("Unexpected missing fpid", eThePost); |
| 844 | 931 | return; |
| 845 | 932 | } |
| 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)); |
| 933 | + const btnReply = form.querySelector('input[type=submit][name=reply]'); |
| 934 | + if( btnReply ){ |
| 935 | + //console.debug("hacking Reply button", btnReply); |
| 936 | + const b = D.button("Reply", ()=>replyClicked(eThePost, b)); |
| 850 | 937 | b.type = 'button'/*keep container form from submitting*/; |
| 851 | | - rb.parentElement.insertBefore(b, rb); |
| 852 | | - rb.remove(); |
| 938 | + btnReply.parentElement.insertBefore(b, btnReply); |
| 939 | + btnReply.remove(); |
| 940 | + } |
| 941 | + const btnEdit = form.querySelector('input[type=submit][name=edit]'); |
| 942 | + if( btnEdit ){ |
| 943 | + //console.debug("hacking Edit button", btnEdit); |
| 944 | + const b = D.button("Edit", ()=>editClicked(eThePost, b)); |
| 945 | + b.type = 'button'; |
| 946 | + btnEdit.parentElement.insertBefore(b, btnEdit); |
| 947 | + btnEdit.remove(); |
| 853 | 948 | } |
| 854 | | - }); |
| 855 | | - } |
| 949 | + })/*for-each form*/; |
| 950 | + }/* /forumpost and /forumthread */ |
| 856 | 951 | |
| 857 | 952 | })/*F.onPageLoad callback*/; |
| 858 | 953 | })(window.fossil); |
| 859 | 954 | |