WIP PWA for Grain
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add ability to delete own comments

+61 -2
+30 -2
src/components/molecules/grain-comment.js
··· 10 10 avatarUrl: { type: String }, 11 11 text: { type: String }, 12 12 createdAt: { type: String }, 13 - isReply: { type: Boolean } 13 + isReply: { type: Boolean }, 14 + isOwner: { type: Boolean } 14 15 }; 15 16 16 17 static styles = css` ··· 65 66 font-family: inherit; 66 67 font-weight: var(--font-weight-semibold); 67 68 } 68 - .reply-btn:hover { 69 + .reply-btn:hover, 70 + .delete-btn:hover { 69 71 color: var(--color-text-primary); 70 72 } 73 + .delete-btn { 74 + font-size: var(--font-size-xs); 75 + color: var(--color-text-secondary); 76 + background: none; 77 + border: none; 78 + padding: 0; 79 + cursor: pointer; 80 + font-family: inherit; 81 + font-weight: var(--font-weight-semibold); 82 + } 83 + .delete-btn:hover { 84 + color: var(--color-error, #e53935); 85 + } 71 86 `; 72 87 73 88 constructor() { ··· 79 94 this.text = ''; 80 95 this.createdAt = ''; 81 96 this.isReply = false; 97 + this.isOwner = false; 82 98 } 83 99 84 100 #handleProfileClick(e) { ··· 90 106 e.stopPropagation(); 91 107 this.dispatchEvent(new CustomEvent('reply', { 92 108 detail: { uri: this.uri, handle: this.handle }, 109 + bubbles: true, 110 + composed: true 111 + })); 112 + } 113 + 114 + #handleDeleteClick(e) { 115 + e.stopPropagation(); 116 + this.dispatchEvent(new CustomEvent('delete', { 117 + detail: { uri: this.uri }, 93 118 bubbles: true, 94 119 composed: true 95 120 })); ··· 128 153 <div class="meta"> 129 154 <span class="time">${this.#formatTime(this.createdAt)}</span> 130 155 <button class="reply-btn" @click=${this.#handleReplyClick}>Reply</button> 156 + ${this.isOwner ? html` 157 + <button class="delete-btn" @click=${this.#handleDeleteClick}>Delete</button> 158 + ` : ''} 131 159 </div> 132 160 </div> 133 161 </div>
+21
src/components/organisms/grain-comment-sheet.js
··· 297 297 this.shadowRoot.querySelector('grain-comment-input')?.focus(); 298 298 } 299 299 300 + async #handleDelete(e) { 301 + const { uri } = e.detail; 302 + 303 + try { 304 + await mutations.deleteComment(uri); 305 + 306 + // Remove from list 307 + this._comments = this._comments.filter(c => c.uri !== uri); 308 + this._totalCount--; 309 + 310 + // Update comment count in cache 311 + recordCache.set(this.galleryUri, { 312 + commentCount: this._totalCount 313 + }); 314 + } catch (err) { 315 + console.error('Failed to delete comment:', err); 316 + } 317 + } 318 + 300 319 render() { 301 320 const userAvatarUrl = auth.user?.avatar?.url || ''; 302 321 ··· 337 356 text=${comment.text} 338 357 createdAt=${comment.createdAt} 339 358 ?is-reply=${comment.isReply} 359 + ?isOwner=${comment.handle === auth.user?.handle} 340 360 @reply=${this.#handleReply} 361 + @delete=${this.#handleDelete} 341 362 ></grain-comment> 342 363 `)} 343 364 `}
+10
src/services/mutations.js
··· 120 120 121 121 return result.createSocialGrainComment.uri; 122 122 } 123 + 124 + async deleteComment(commentUri) { 125 + const client = auth.getClient(); 126 + const rkey = commentUri.split('/').pop(); 127 + await client.mutate(` 128 + mutation DeleteComment($rkey: String!) { 129 + deleteSocialGrainComment(rkey: $rkey) { uri } 130 + } 131 + `, { rkey }); 132 + } 123 133 } 124 134 125 135 export const mutations = new MutationsService();