WIP PWA for Grain

docs: add optimistic favoriting implementation plan

+82
+82
docs/plans/2025-12-29-optimistic-favoriting.md
··· 1 + # Optimistic Favoriting Implementation Plan 2 + 3 + > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. 4 + 5 + **Goal:** Make the favorite button respond instantly by updating UI before API completes, rolling back on failure. 6 + 7 + **Architecture:** Store previous state before updating, apply optimistic update immediately, restore on API error with toast notification. 8 + 9 + **Tech Stack:** Lit, existing mutations service, existing toast component. 10 + 11 + --- 12 + 13 + ### Task 1: Implement Optimistic Update 14 + 15 + **Files:** 16 + - Modify: `src/components/organisms/grain-engagement-bar.js:72-92` 17 + 18 + **Step 1: Replace `#handleFavoriteClick` with optimistic version** 19 + 20 + Replace the entire `#handleFavoriteClick` method (lines 72-92) with: 21 + 22 + ```javascript 23 + async #handleFavoriteClick() { 24 + if (!auth.isAuthenticated || this._loading || !this.galleryUri) return; 25 + 26 + this._loading = true; 27 + 28 + // Store previous state for rollback 29 + const previousState = { 30 + viewerHasFavorited: this.viewerHasFavorited, 31 + viewerFavoriteUri: this.viewerFavoriteUri, 32 + favoriteCount: this.favoriteCount 33 + }; 34 + 35 + // Optimistic update - apply immediately 36 + this.viewerHasFavorited = !this.viewerHasFavorited; 37 + this.favoriteCount += this.viewerHasFavorited ? 1 : -1; 38 + if (!this.viewerHasFavorited) { 39 + this.viewerFavoriteUri = null; 40 + } 41 + 42 + try { 43 + const update = await mutations.toggleFavorite( 44 + this.galleryUri, 45 + previousState.viewerHasFavorited, 46 + previousState.viewerFavoriteUri, 47 + previousState.favoriteCount 48 + ); 49 + // Update with real URI from server (needed for future deletes) 50 + this.viewerFavoriteUri = update.viewerFavoriteUri; 51 + } catch (err) { 52 + // Rollback on failure 53 + console.error('Failed to toggle favorite:', err); 54 + this.viewerHasFavorited = previousState.viewerHasFavorited; 55 + this.viewerFavoriteUri = previousState.viewerFavoriteUri; 56 + this.favoriteCount = previousState.favoriteCount; 57 + this.shadowRoot.querySelector('grain-toast').show('Failed to update'); 58 + } finally { 59 + this._loading = false; 60 + } 61 + } 62 + ``` 63 + 64 + **Step 2: Verify in browser** 65 + 66 + 1. Open the app and navigate to a post 67 + 2. Click the heart - it should fill immediately 68 + 3. Check Network tab - request still goes out 69 + 4. Test error case: disable network, click heart, verify rollback + toast 70 + 71 + **Step 3: Commit** 72 + 73 + ```bash 74 + git add src/components/organisms/grain-engagement-bar.js 75 + git commit -m "feat: add optimistic UI for favoriting" 76 + ``` 77 + 78 + --- 79 + 80 + ## Done 81 + 82 + Single task - the change is small and self-contained.