···1515- [x] [8. Saved Posts — Accessible from Profile](#8-saved-posts--accessible-from-profile)
1616- [x] [9. Saved Posts — Long Press for Local, Tap for Menu](#9-saved-posts--long-press-for-local-tap-for-menu)
1717- [x] [10. Saved Posts — Show Save Counts](#10-saved-posts--show-save-counts)
1818-- [ ] [11. Failed Action Snackbar with Revert](#11-failed-action-snackbar-with-revert)
1919-- [ ] [12. Delete Post — Remove from Feed](#12-delete-post--remove-from-feed)
1818+- [ ] [11. Saved Posts — Cloud Save via AT Protocol](#11-saved-posts--cloud-save-via-at-protocol)
1919+- [ ] [12. Failed Action Snackbar with Revert](#12-failed-action-snackbar-with-revert)
2020+- [ ] [13. Delete Post — Remove from Feed](#13-delete-post--remove-from-feed)
20212122## 1. Post Thread Screen
2223···253254- Edit: `lib/features/feed/presentation/widgets/post_action_bar.dart` — use the passed
254255 count instead of hardcoded `0`
255256256256-## 11. Failed Action Snackbar with Revert
257257+## 11. Saved Posts — Cloud Save via AT Protocol
258258+259259+**Status:** Not implemented — "Save to Bluesky" option is disabled with "Coming soon" placeholder.
260260+261261+**Problem:** The save menu in `PostActionBar._showSaveOptions()` has a disabled "Save to
262262+Bluesky" option. The `bluesky` package already exposes a bookmark API
263263+(`app.bsky.bookmark.*`) but it is not wired up. Currently all saves are local-only.
264264+265265+**Fix:**
266266+267267+- Add bookmark methods to `PostActionRepository` using the existing `_bluesky.bookmark`
268268+ service:
269269+ - `createBookmark({uri, cid})` → `_bluesky.bookmark.createBookmark(uri, cid)`
270270+ - `deleteBookmark({uri})` → `_bluesky.bookmark.deleteBookmark(uri)`
271271+ - `getBookmarks({limit, cursor})` → `_bluesky.bookmark.getBookmarks(limit, cursor)`
272272+- Add `cloudSave` and `cloudUnsave` methods to `SavedPostsCubit`:
273273+ - Call `PostActionRepository.createBookmark` / `deleteBookmark`.
274274+ - On success, upsert the local DB row with `saveType: 'cloud'` (or `'both'` if already
275275+ saved locally). On cloud unsave, downgrade `saveType` to `'local'` if a local save
276276+ exists, or delete the row entirely.
277277+ - Use optimistic UI: update the icon immediately, revert on failure.
278278+- Enable the "Save to Bluesky" / "Remove from Bluesky" option in
279279+ `PostActionBar._showSaveOptions()` and wire it to `SavedPostsCubit.cloudSave` /
280280+ `cloudUnsave` via a new callback.
281281+- Distinguish cloud vs local saves visually:
282282+ - Local-only: amber/gold bookmark icon.
283283+ - Cloud (or both): primary/blue bookmark icon.
284284+ - `PostActionBar` already receives `isSaved`; extend it with a `saveType` parameter
285285+ (or similar) so the icon color reflects the save type.
286286+- Add a one-time sync on login: call `getBookmarks` (paginated) and merge results into
287287+ the local DB so cloud saves made on other clients appear. Mark these as `saveType:
288288+ 'cloud'`.
289289+290290+**Files:**
291291+292292+- Edit: `lib/features/feed/data/post_action_repository.dart` — add `createBookmark`,
293293+ `deleteBookmark`, `getBookmarks` methods
294294+- Edit: `lib/features/feed/cubit/saved_posts_cubit.dart` — add `cloudSave`,
295295+ `cloudUnsave`, `syncCloudBookmarks` methods; handle `saveType` transitions
296296+- Edit: `lib/features/feed/presentation/widgets/post_action_bar.dart` — enable cloud
297297+ save option, accept `saveType` parameter, update icon color logic
298298+- Edit: `lib/features/feed/presentation/widgets/post_card_with_actions.dart` — pass
299299+ `saveType` and cloud save/unsave callbacks to `PostActionBar`
300300+301301+## 12. Failed Action Snackbar with Revert
257302258303**Status:** Partially implemented — rollback works but snackbar is basic.
259304···278323- Edit: `lib/features/feed/presentation/widgets/post_action_bar.dart` — show loading
279324 state visually on like/repost buttons
280325281281-## 12. Delete Post — Remove from Feed
326326+## 13. Delete Post — Remove from Feed
282327283328**Status:** Incomplete — post is deleted on the server but remains visible in the feed.
284329
···103103104104 await tester.pumpWidget(buildSubject(post, onTap: () => tapped = true));
105105106106- // Tap the author handle which is in the content InkWell (not the action bar).
107106 await tester.tap(find.text('test.bsky.social', findRichText: true).first);
108107 expect(tapped, isTrue);
109108 });
···111110 testWidgets('does not call onTap when onTap is null', (tester) async {
112111 final post = _makePost();
113112 await tester.pumpWidget(buildSubject(post));
114114- // Should not throw when tapping without a callback.
115113 await tester.tap(find.text('test.bsky.social', findRichText: true).first);
116114 await tester.pump();
117115 });