11$ ( ( ) => {
2- $ ( ' .js-more-comments' ) . on ( 'click ', async ( evt ) => {
2+ $ ( document ) . on ( 'click' , ' .js-more-comments', async ( evt ) => {
33 evt . preventDefault ( ) ;
44 const $tgt = $ ( evt . target ) ;
55 const $anchor = $tgt . is ( 'a' ) ? $tgt : $tgt . parents ( 'a' ) ;
66 const postId = $anchor . attr ( 'data-post-id' ) ;
77
8- const resp = await fetch ( `/comments/post/${ postId } ` , {
9- headers : { 'Accept' : 'text/html' }
10- } ) ;
11- const data = await resp . text ( ) ;
8+ const data = await QPixel . getThreadsListContent ( postId ) ;
9+
1210 $tgt . parents ( '.post--comments' ) . find ( '.post--comments-container' ) . html ( data ) . trigger ( 'ajax:success' ) ;
1311 $tgt . parents ( '.post--comments' ) . find ( '.js-more-comments' ) . remove ( ) ;
1412 } ) ;
1513
14+ /**
15+ * @param {JQuery<HTMLElement> } $tgt
16+ * @returns {HTMLElement | null }
17+ */
18+ const getCommentThreadWrapper = ( $tgt ) => {
19+ return $tgt . closest ( '.js-comment-thread-wrapper' ) [ 0 ] ?? null ;
20+ } ;
21+
1622 $ ( document ) . on ( 'click' , '.post--comments-thread.is-inline a' , async ( evt ) => {
1723 if ( evt . ctrlKey ) { return ; }
1824
1925 evt . preventDefault ( ) ;
2026
2127 const $tgt = $ ( evt . target ) ;
22- openThread ( $ tgt. closest ( '.post--comments- thread-wrapper' ) [ 0 ] , $tgt . attr ( "href" ) ) ;
23- } ) ;
28+ const $threadId = $ tgt. data ( ' thread' ) ;
29+ const wrapper = getCommentThreadWrapper ( $tgt ) ;
2430
25- async function openThread ( wrapper , targetUrl , showDeleted = false ) {
26- const resp = await fetch ( `${ targetUrl } ?inline=true&show_deleted_comments=${ showDeleted ? 1 : 0 } ` , {
27- headers : { 'Accept' : 'text/html' }
28- } ) ;
29- let data = await resp . text ( ) ;
31+ openThread ( wrapper , $threadId ) ;
32+ } ) ;
3033
31- data = data . split ( "<!-- THREAD STARTS BELOW -->" ) [ 1 ] ;
32- data = data . split ( "<!-- THREAD ENDS ABOVE -->" ) [ 0 ] ;
34+ /**
35+ * @param {HTMLElement } wrapper
36+ * @param {string } threadId
37+ * @param {GetThreadContentOptions } [options]
38+ */
39+ async function openThread ( wrapper , threadId , options ) {
40+ const data = await QPixel . getThreadContent ( threadId , options ) ;
3341
3442 wrapper . innerHTML = data ;
3543
36- $ ( 'a.show-deleted-comments' ) . click ( async ( evt ) => {
37- if ( evt . ctrlKey ) { return ; }
38- evt . preventDefault ( ) ;
39- openThread ( wrapper , targetUrl , true ) ;
40- } ) ;
41-
4244 window . MathJax && MathJax . typeset ( ) ;
4345 window . hljs && hljs . highlightAll ( ) ;
4446 }
4547
48+ $ ( document ) . on ( 'click' , '.js-show-deleted-comments' , ( ev ) => {
49+ if ( ev . ctrlKey ) { return ; } // do we really need it?
50+
51+ ev . preventDefault ( ) ;
52+
53+ const $tgt = $ ( ev . target ) ;
54+ const $inline = $tgt . data ( 'inline' ) ;
55+ const $threadId = $tgt . data ( 'thread' ) ;
56+ const wrapper = getCommentThreadWrapper ( $tgt ) ;
57+
58+ openThread ( wrapper , $threadId , { inline : $inline , showDeleted : true } ) ;
59+ } ) ;
60+
4661 $ ( document ) . on ( 'click' , '.js-collapse-thread' , async ( ev ) => {
4762 const $tgt = $ ( ev . target ) ;
4863 const $widget = $tgt . parents ( '.widget' ) ;
4964 const $embed = $tgt . parents ( '.post--comments-thread' ) ;
5065
5166 const threadId = $widget . data ( 'thread' ) ;
67+ const isLocked = $widget . data ( 'locked' ) ;
5268 const isDeleted = $widget . data ( 'deleted' ) ;
5369 const isArchived = $widget . data ( 'archived' ) ;
5470 const threadTitle = $widget . find ( '.js-thread-title' ) . text ( ) ;
@@ -58,14 +74,21 @@ $(() => {
5874 const $link = $ ( `<a href="/comments/thread/${ threadId } " class="js--comment-link" data-thread=${ threadId } ></a>` ) ;
5975 $link . text ( threadTitle ) ;
6076
77+ if ( isLocked ) {
78+ $container . append ( `<i class="fas fa-lock fa-fw" title="Locked thread" aria-label="Locked thread"></i>` ) ;
79+ $container . addClass ( 'is-locked' ) ;
80+ }
81+
6182 if ( isDeleted ) {
6283 $container . append ( `<i class="fas fa-trash h-c-red-600 fa-fw" title="Deleted thread" aria-label="Deleted thread"></i>` ) ;
6384 $container . addClass ( 'is-deleted' ) ;
6485 }
86+
6587 if ( isArchived ) {
6688 $container . append ( `<i class="fas fa-archive fa-fw" title="Archived thread" aria-label="Archived thread"></i>` ) ;
6789 $container . addClass ( 'is-archived' ) ;
6890 }
91+
6992 $container . append ( $link ) ;
7093 $container . append ( `(${ replyCount } comment${ replyCount !== 1 ? 's' : '' } )` ) ;
7194 $embed [ 0 ] . outerHTML = $container [ 0 ] . outerHTML ;
@@ -78,35 +101,48 @@ $(() => {
78101 const $comment = $tgt . parents ( '.comment' ) ;
79102 const $commentBody = $comment . find ( '.comment--body' ) ;
80103 const $thread = $comment . parents ( '.thread' ) ;
104+
81105 const commentId = $comment . attr ( 'data-id' ) ;
82106 const postId = $thread . attr ( 'data-post' ) ;
83107 const threadId = $thread . attr ( 'data-thread' ) ;
108+
109+ // if this matches, this means we are already in edit mode
110+ if ( $ ( `.js-discard-edit[data-comment-id="${ commentId } "]` ) . length ) {
111+ return ;
112+ }
113+
84114 const originalComment = $commentBody . clone ( ) ;
85115
86- const resp = await fetch ( `/comments/${ commentId } ` , {
87- credentials : 'include' ,
88- headers : { 'Accept' : 'application/json' }
89- } ) ;
90- const data = await resp . json ( ) ;
91- const content = data . content ;
116+ const data = await QPixel . getComment ( commentId ) ;
92117
93118 const formTemplate = `<form action="/comments/${ commentId } /edit" method="POST" class="comment-edit-form" data-remote="true">
94119 <label for="comment-content" class="form-element">Comment body:</label>
95- <textarea id="comment-content" rows="6" class="form-element is-small" data-thread="${ threadId } " data-post="${ postId } " data-character-count=".js-character-count-comment-body" name="comment[content]">${ content } </textarea>
120+ <textarea id="comment-content"
121+ class="form-element is-small"
122+ data-character-count=".js-character-count-comment-body"
123+ data-post="${ postId } "
124+ data-thread="${ threadId } "
125+ name="comment[content]"
126+ rows="6">${ data . content } </textarea>
96127 <input type="submit" class="button is-muted is-filled" value="Update comment" />
97- <input type="button" name="js-discard-edit" data-comment-id="${ commentId } " value="Discard Edit" class="button is-danger is-outlined js-discard-edit" />
128+ <input type="button"
129+ class="button is-danger is-outlined js-discard-edit"
130+ data-comment-id="${ commentId } "
131+ name="js-discard-edit"
132+ value="Discard Edit" />
98133 <span class="has-float-right has-font-size-caption js-character-count-comment-body"
99134 data-max="1000" data-min="15">
100135 <i class="fas fa-ellipsis-h js-character-count__icon"></i>
101- <span class="js-character-count__count">${ content . length } / 1000</span>
136+ <span class="js-character-count__count">${ data . content . length } / 1000</span>
102137 </span>
103138 </form>` ;
104139
105140 $commentBody . html ( formTemplate ) ;
141+ $commentBody . find ( 'textarea#comment-content' ) . trigger ( 'focus' ) ;
106142
107143 $commentBody . find ( `#comment-content` ) . on ( 'keyup' , pingable_popup ) ;
108144
109- $ ( `.js-discard-edit[data-comment-id="${ commentId } "]` ) . click ( ( ) => {
145+ $ ( `.js-discard-edit[data-comment-id="${ commentId } "]` ) . on ( 'click' , ( ) => {
110146 $commentBody . html ( originalComment . html ( ) ) ;
111147 } ) ;
112148 } ) ;
@@ -115,13 +151,10 @@ $(() => {
115151 const $tgt = $ ( evt . target ) ;
116152 const $comment = $tgt . parents ( '.comment' ) ;
117153
118- if ( data . status === 'success' ) {
154+ QPixel . handleJSONResponse ( data , ( data ) => {
119155 const newComment = $ ( data . comment ) ;
120156 $comment . html ( newComment [ 0 ] . innerHTML ) ;
121- }
122- else {
123- QPixel . createNotification ( 'danger' , data . message ) ;
124- }
157+ } ) ;
125158 } ) ;
126159
127160 $ ( document ) . on ( 'click' , '.js-comment-delete, .js-comment-undelete' , async ( evt ) => {
@@ -132,23 +165,18 @@ $(() => {
132165 const commentId = $comment . attr ( 'data-id' ) ;
133166 const isDelete = ! $comment . hasClass ( 'deleted-content' ) ;
134167
135- const resp = await QPixel . fetchJSON ( `/comments/${ commentId } /delete` , { } , { method : isDelete ? 'DELETE' : 'PATCH' } ) ;
136-
137- const data = await resp . json ( ) ;
168+ const data = await ( isDelete ? QPixel . deleteComment ( commentId ) : QPixel . undeleteComment ( commentId ) ) ;
138169
139- if ( data . status === 'success' ) {
170+ QPixel . handleJSONResponse ( data , ( ) => {
140171 if ( isDelete ) {
141172 $comment . addClass ( 'deleted-content' ) ;
142- $tgt . removeClass ( 'js-comment-delete' ) . addClass ( 'js-comment-undelete' ) . text ( 'undelete' ) ;
173+ $tgt . removeClass ( 'js-comment-delete' ) . addClass ( 'js-comment-undelete' ) . val ( 'undelete' ) ;
143174 }
144175 else {
145176 $comment . removeClass ( 'deleted-content' ) ;
146- $tgt . removeClass ( 'js-comment-undelete' ) . addClass ( 'js-comment-delete' ) . text ( 'delete' ) ;
177+ $tgt . removeClass ( 'js-comment-undelete' ) . addClass ( 'js-comment-delete' ) . val ( 'delete' ) ;
147178 }
148- }
149- else {
150- QPixel . createNotification ( 'danger' , data . message ) ;
151- }
179+ } ) ;
152180 } ) ;
153181
154182 $ ( document ) . on ( 'click' , '.js--show-followers' , async ( evt ) => {
@@ -163,38 +191,56 @@ $(() => {
163191 credentials : 'include' ,
164192 headers : { 'Accept' : 'text/html' }
165193 } ) ;
194+
166195 const data = await resp . text ( ) ;
196+
167197 $modal . find ( '.js-follower-display' ) . html ( data ) ;
168198 } ) ;
169199
200+ $ ( document ) . on ( 'click' , '[class*=js--lock-thread] form' , async ( evt ) => {
201+ evt . preventDefault ( ) ;
202+
203+ const $tgt = $ ( evt . target ) ;
204+ const threadID = $tgt . data ( "thread" ) ;
205+
206+ const data = await QPixel . lockThread ( threadID ) ;
207+
208+ QPixel . handleJSONResponse ( data , ( ) => {
209+ window . location . reload ( ) ;
210+ } ) ;
211+ } ) ;
212+
170213 $ ( document ) . on ( 'click' , '.js--restrict-thread, .js--unrestrict-thread' , async ( evt ) => {
171214 evt . preventDefault ( ) ;
172215
173216 const $tgt = $ ( evt . target ) ;
174- const threadID = $tgt . data ( "thread" )
175- const action = $tgt . data ( "action" )
217+ const threadID = $tgt . data ( "thread" ) ;
218+ const action = $tgt . data ( "action" ) ;
176219 const route = $tgt . hasClass ( "js--restrict-thread" ) ? 'restrict' : 'unrestrict' ;
177220
178221 const resp = await QPixel . fetchJSON ( `/comments/thread/${ threadID } /${ route } ` , { type : action } ) ;
179222
180223 const data = await resp . json ( ) ;
181224
182- if ( data . status === 'success' ) {
225+ QPixel . handleJSONResponse ( data , ( ) => {
183226 window . location . reload ( ) ;
184- }
185- else {
186- QPixel . createNotification ( 'danger' , data . message ) ;
187- }
227+ } ) ;
188228 } ) ;
189229
190230 $ ( document ) . on ( 'click' , '.comment-form input[type="submit"]' , async ( evt ) => {
191231 // Comment posting has been clicked.
192232 $ ( evt . target ) . attr ( 'data-disable-with' , 'Posting...' ) ;
193233 } ) ;
194234
235+ /**
236+ * @type {Record<`${number}-${number}`, Record<string, number>> }
237+ */
195238 const pingable = { } ;
196239 $ ( document ) . on ( 'keyup' , '.js-comment-field' , pingable_popup ) ;
197240
241+ /**
242+ * @type {QPixelPingablePopupCallback }
243+ */
198244 async function pingable_popup ( ev ) {
199245 if ( QPixel . Popup . isSpecialKey ( ev . keyCode ) ) {
200246 return ;
@@ -248,20 +294,36 @@ $(() => {
248294 }
249295 }
250296
251- $ ( ' .js-new-thread-link' ) . on ( 'click ', async ( ev ) => {
297+ $ ( document ) . on ( 'click' , ' .js-new-thread-link', async ( ev ) => {
252298 ev . preventDefault ( ) ;
253299 const $tgt = $ ( ev . target ) ;
254300 const postId = $tgt . attr ( 'data-post' ) ;
255301 const $thread = $ ( `#new-thread-modal-${ postId } ` ) ;
256302
257303 if ( $thread . is ( ':hidden' ) ) {
258304 $thread . show ( ) ;
305+ $thread . find ( '.js-comment-field' ) . trigger ( 'focus' ) ;
259306 }
260307 else {
261308 $thread . hide ( ) ;
262309 }
263310 } ) ;
264311
312+ $ ( document ) . on ( 'click' , '.js-reply-to-thread-link' , async ( ev ) => {
313+ ev . preventDefault ( ) ;
314+ const $tgt = $ ( ev . target ) ;
315+ const postId = $tgt . attr ( 'data-post' ) ;
316+ const $reply = $ ( `#reply-to-thread-form-${ postId } ` ) ;
317+
318+ if ( $reply . is ( ':hidden' ) ) {
319+ $reply . show ( ) ;
320+ $reply . find ( '.js-comment-field' ) . trigger ( 'focus' ) ;
321+ }
322+ else {
323+ $reply . hide ( ) ;
324+ }
325+ } ) ;
326+
265327 $ ( '.js-comment-permalink > .js-text' ) . text ( 'copy link' ) ;
266328 $ ( document ) . on ( 'click' , '.js-comment-permalink' , ( ev ) => {
267329 ev . preventDefault ( ) ;
0 commit comments