diff --git a/src/components/organisms/grain-profile-header.js b/src/components/organisms/grain-profile-header.js index c99a21c..47c35e6 100644 --- a/src/components/organisms/grain-profile-header.js +++ b/src/components/organisms/grain-profile-header.js @@ -238,22 +238,45 @@ export class GrainProfileHeader extends LitElement { if (!this._user || this._followLoading || !this.profile) return; this._followLoading = true; + + // Store previous state for rollback + const previousState = { + viewerIsFollowing: this.profile.viewerIsFollowing, + viewerFollowUri: this.profile.viewerFollowUri, + followerCount: this.profile.followerCount || 0 + }; + + // Optimistic update - apply immediately + const newIsFollowing = !previousState.viewerIsFollowing; + this.profile = { + ...this.profile, + viewerIsFollowing: newIsFollowing, + viewerFollowUri: newIsFollowing ? this.profile.viewerFollowUri : null, + followerCount: previousState.followerCount + (newIsFollowing ? 1 : -1) + }; + try { const update = await mutations.toggleFollow( this.profile.handle, this.profile.did, - this.profile.viewerIsFollowing, - this.profile.viewerFollowUri, - this.profile.followerCount || 0 + previousState.viewerIsFollowing, + previousState.viewerFollowUri, + previousState.followerCount ); + // Update with real URI from server (needed for future unfollows) this.profile = { ...this.profile, - viewerIsFollowing: update.viewerIsFollowing, - viewerFollowUri: update.viewerFollowUri, - followerCount: update.followerCount + viewerFollowUri: update.viewerFollowUri }; } catch (err) { + // Rollback on failure console.error('Failed to toggle follow:', err); + this.profile = { + ...this.profile, + viewerIsFollowing: previousState.viewerIsFollowing, + viewerFollowUri: previousState.viewerFollowUri, + followerCount: previousState.followerCount + }; this.shadowRoot.querySelector('grain-toast')?.show('Failed to update'); } finally { this._followLoading = false;