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