WIP PWA for Grain
0
fork

Configure Feed

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

feat: add mutations service and favorite toggle to engagement bar

+132 -1
+38 -1
src/components/organisms/grain-engagement-bar.js
··· 1 1 import { LitElement, html, css } from 'lit'; 2 2 import { share } from '../../services/share.js'; 3 + import { auth } from '../../services/auth.js'; 4 + import { mutations } from '../../services/mutations.js'; 3 5 import '../molecules/grain-stat-count.js'; 4 6 import '../atoms/grain-icon.js'; 5 7 import '../atoms/grain-toast.js'; ··· 8 10 static properties = { 9 11 favoriteCount: { type: Number }, 10 12 commentCount: { type: Number }, 11 - url: { type: String } 13 + url: { type: String }, 14 + galleryUri: { type: String }, 15 + viewerHasFavorited: { type: Boolean }, 16 + viewerFavoriteUri: { type: String }, 17 + _loading: { state: true } 12 18 }; 13 19 14 20 static styles = css` ··· 50 56 this.favoriteCount = 0; 51 57 this.commentCount = 0; 52 58 this.url = ''; 59 + this.galleryUri = ''; 60 + this.viewerHasFavorited = false; 61 + this.viewerFavoriteUri = null; 62 + this._loading = false; 53 63 } 54 64 55 65 async #handleShare() { ··· 59 69 } 60 70 } 61 71 72 + async #handleFavoriteClick() { 73 + if (!auth.isAuthenticated || this._loading || !this.galleryUri) return; 74 + 75 + this._loading = true; 76 + try { 77 + const update = await mutations.toggleFavorite( 78 + this.galleryUri, 79 + this.viewerHasFavorited, 80 + this.viewerFavoriteUri, 81 + this.favoriteCount 82 + ); 83 + this.viewerHasFavorited = update.viewerHasFavorited; 84 + this.viewerFavoriteUri = update.viewerFavoriteUri; 85 + this.favoriteCount = update.favoriteCount; 86 + } catch (err) { 87 + console.error('Failed to toggle favorite:', err); 88 + this.shadowRoot.querySelector('grain-toast').show('Failed to update'); 89 + } finally { 90 + this._loading = false; 91 + } 92 + } 93 + 62 94 render() { 95 + const isLoggedIn = auth.isAuthenticated; 96 + 63 97 return html` 64 98 <grain-stat-count 65 99 icon="heart" 66 100 count=${this.favoriteCount} 101 + ?filled=${this.viewerHasFavorited} 102 + ?interactive=${isLoggedIn} 103 + @stat-click=${this.#handleFavoriteClick} 67 104 ></grain-stat-count> 68 105 <grain-stat-count 69 106 icon="comment"
+94
src/services/mutations.js
··· 1 + import { auth } from './auth.js'; 2 + import { recordCache } from './record-cache.js'; 3 + 4 + class MutationsService { 5 + async createFavorite(galleryUri) { 6 + const client = auth.getClient(); 7 + const result = await client.mutate(` 8 + mutation CreateFavorite($input: SocialGrainFavoriteInput!) { 9 + createSocialGrainFavorite(input: $input) { uri } 10 + } 11 + `, { input: { subject: { uri: galleryUri } } }); 12 + 13 + return result.createSocialGrainFavorite.uri; 14 + } 15 + 16 + async deleteFavorite(favoriteUri) { 17 + const client = auth.getClient(); 18 + const rkey = favoriteUri.split('/').pop(); 19 + await client.mutate(` 20 + mutation DeleteFavorite($rkey: String!) { 21 + deleteSocialGrainFavorite(rkey: $rkey) { uri } 22 + } 23 + `, { rkey }); 24 + } 25 + 26 + async toggleFavorite(galleryUri, viewerHasFavorited, viewerFavoriteUri, currentCount) { 27 + let newFavoriteUri = null; 28 + let newCount = currentCount; 29 + 30 + if (viewerHasFavorited) { 31 + await this.deleteFavorite(viewerFavoriteUri); 32 + newCount = Math.max(0, currentCount - 1); 33 + } else { 34 + newFavoriteUri = await this.createFavorite(galleryUri); 35 + newCount = currentCount + 1; 36 + } 37 + 38 + const update = { 39 + viewerHasFavorited: !viewerHasFavorited, 40 + viewerFavoriteUri: newFavoriteUri, 41 + favoriteCount: newCount 42 + }; 43 + 44 + recordCache.set(galleryUri, update); 45 + 46 + return update; 47 + } 48 + 49 + async createFollow(did) { 50 + const client = auth.getClient(); 51 + const result = await client.mutate(` 52 + mutation CreateFollow($input: SocialGrainGraphFollowInput!) { 53 + createSocialGrainGraphFollow(input: $input) { uri } 54 + } 55 + `, { input: { subject: did } }); 56 + 57 + return result.createSocialGrainGraphFollow.uri; 58 + } 59 + 60 + async deleteFollow(followUri) { 61 + const client = auth.getClient(); 62 + const rkey = followUri.split('/').pop(); 63 + await client.mutate(` 64 + mutation DeleteFollow($rkey: String!) { 65 + deleteSocialGrainGraphFollow(rkey: $rkey) { uri } 66 + } 67 + `, { rkey }); 68 + } 69 + 70 + async toggleFollow(handle, did, viewerIsFollowing, viewerFollowUri, currentCount) { 71 + let newFollowUri = null; 72 + let newCount = currentCount; 73 + 74 + if (viewerIsFollowing) { 75 + await this.deleteFollow(viewerFollowUri); 76 + newCount = Math.max(0, currentCount - 1); 77 + } else { 78 + newFollowUri = await this.createFollow(did); 79 + newCount = currentCount + 1; 80 + } 81 + 82 + const update = { 83 + viewerIsFollowing: !viewerIsFollowing, 84 + viewerFollowUri: newFollowUri, 85 + followerCount: newCount 86 + }; 87 + 88 + recordCache.set(`profile:${handle}`, update); 89 + 90 + return update; 91 + } 92 + } 93 + 94 + export const mutations = new MutationsService();