Skip to content

feat: add saved post folders#112

Open
AlejandroAkbal wants to merge 1 commit intomainfrom
auto-triage/252-save-folders
Open

feat: add saved post folders#112
AlejandroAkbal wants to merge 1 commit intomainfrom
auto-triage/252-save-folders

Conversation

@AlejandroAkbal
Copy link
Copy Markdown
Member

@AlejandroAkbal AlejandroAkbal commented Mar 16, 2026

Summary

  • add local saved-post folders so premium users can organize saved posts without backend schema changes
  • let users create, delete, assign, and clear folders directly from the saved posts page
  • keep folder assignments in sync when a saved post or folder is removed

Validation

  • \pnpm exec nuxi typecheck\\ (fails with many pre-existing repo type/module issues unrelated to this change, plus a local vue-router volar dependency resolution problem in this environment)
  • \pnpm test\\ (fails because existing repo tests import missing \@nuxt/test-utils\\)

Feedback

Summary by CodeRabbit

Release Notes

  • New Features
    • Added folder management for saved posts: create custom folders, assign posts to specific folders, and delete folders as needed
    • Filter saved posts by folder selection for streamlined browsing and improved content organization

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 16, 2026

Walkthrough

A folder management system for saved posts is introduced. The usePocketbase composable now provides folder state and APIs including creation, deletion, and assignment of posts to folders with persistent storage. The PostSave component invokes folder cleanup on post deletion, and the saved posts page implements filtering, folder selection UI, and folder management controls.

Changes

Cohort / File(s) Summary
Folder Management Composable
composables/usePocketbase.ts
Added SavedPostFolderAssignments type, folder state (savedPostFolders, savedPostFolderAssignments), and API methods (getSavedPostFolder, setSavedPostFolder, removeSavedPostFolder, createSavedPostFolder, deleteSavedPostFolder, pruneSavedPostFolderAssignments) with helper utilities for key building and name normalization.
Post Deletion with Folder Cleanup
components/pages/posts/post/PostSave.vue
Updated to expose and destructure removeSavedPostFolder from composable; calls this method to clean up folder assignments when a saved post is deleted.
Saved Posts Page with Folder UI
pages/premium/saved-posts/[domain].vue
Expanded hook destructuring to include folder-related exports; added folder filter state and derived filtered rows; implemented folder management handlers (create, delete, assign); integrated folder controls in header and per-post folder select UI; updated virtualization and pagination logic to use filtered data.

Sequence Diagram

sequenceDiagram
    actor User
    participant Page as [domain].vue Page
    participant PostSave as PostSave Component
    participant Composable as usePocketbase Composable
    participant Storage as Local Storage

    rect rgba(100, 150, 200, 0.5)
    Note over User,Storage: Delete Post Flow
    User->>PostSave: Click Delete
    PostSave->>Composable: removeSavedPostFolder(domain, id)
    Composable->>Storage: Update savedPostFolderAssignments
    Composable->>Composable: Filter post from savedPostList
    Composable-->>PostSave: Folder cleaned up
    PostSave-->>Page: Post removed from list
    end

    rect rgba(150, 200, 100, 0.5)
    Note over User,Storage: Create Folder Flow
    User->>Page: Click Create Folder
    Page->>Composable: createSavedPostFolder(folderName)
    Composable->>Storage: Add to savedPostFolders
    Composable-->>Page: Returns true
    Page->>Page: Update selectedFolderFilter
    end

    rect rgba(200, 150, 100, 0.5)
    Note over User,Storage: Assign Post to Folder Flow
    User->>Page: Select folder for post
    Page->>Composable: setSavedPostFolder(domain, id, folderName)
    Composable->>Storage: Update savedPostFolderAssignments
    Composable-->>Page: Returns true
    Page->>Page: Recompute filteredRows
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat: add saved post folders' directly and clearly reflects the main change: introducing a new feature for organizing saved posts with local folders.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch auto-triage/252-save-folders
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch auto-triage/252-save-folders
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can suggest fixes for GitHub Check annotations.

Configure the reviews.tools.github-checks setting to adjust the time to wait for GitHub Checks to complete.

@AlejandroAkbal
Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 16, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
composables/usePocketbase.ts (1)

146-161: 🧹 Nitpick | 🔵 Trivial

Consider whether savedPostFolderAssignments needs to be exported.

The savedPostFolderAssignments state is exported but doesn't appear to be used directly by consumers—they interact through getSavedPostFolder and setSavedPostFolder. If it's only needed internally, consider removing it from the return object to keep the API surface minimal.

♻️ Suggested change
   return {
     email,
     license,
     subscription_expires_at,

     savedPostList,
     savedPostFolders,
-    savedPostFolderAssignments,

     getSavedPostFolder,
     setSavedPostFolder,
     removeSavedPostFolder,

     createSavedPostFolder,
     deleteSavedPostFolder
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@composables/usePocketbase.ts` around lines 146 - 161, Remove
savedPostFolderAssignments from the module's public API: if consumers only use
getSavedPostFolder and setSavedPostFolder, stop exporting
savedPostFolderAssignments from the return object and keep it as an internal
reactive state referenced by getSavedPostFolder and setSavedPostFolder; update
the returned object to omit savedPostFolderAssignments, verify
getSavedPostFolder and setSavedPostFolder still access the internal variable,
and run a project-wide search for savedPostFolderAssignments to ensure no
external code expects it to be exported.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pages/premium/saved-posts/`[domain].vue:
- Around line 314-331: The onCreateFolderClick handler duplicates normalization
logic instead of reusing the composable's normalizeSavedPostFolderName which
risks divergence; update so createSavedPostFolder either returns the normalized
folder name on success (instead of boolean) or export and call
normalizeSavedPostFolderName from the composable, then set
selectedFolderFilter.value to the returned/normalized name; adjust
onCreateFolderClick to use the normalized value and handle falsy return as
failure, and update createSavedPostFolder and any callers to match the new
return contract.

---

Outside diff comments:
In `@composables/usePocketbase.ts`:
- Around line 146-161: Remove savedPostFolderAssignments from the module's
public API: if consumers only use getSavedPostFolder and setSavedPostFolder,
stop exporting savedPostFolderAssignments from the return object and keep it as
an internal reactive state referenced by getSavedPostFolder and
setSavedPostFolder; update the returned object to omit
savedPostFolderAssignments, verify getSavedPostFolder and setSavedPostFolder
still access the internal variable, and run a project-wide search for
savedPostFolderAssignments to ensure no external code expects it to be exported.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: fe77eb13-d873-4a61-8f35-6373338aa4b0

📥 Commits

Reviewing files that changed from the base of the PR and between df24fbc and 7ada833.

📒 Files selected for processing (3)
  • components/pages/posts/post/PostSave.vue
  • composables/usePocketbase.ts
  • pages/premium/saved-posts/[domain].vue

Comment on lines +314 to +331
function onCreateFolderClick() {
const folderNamePrompt = prompt('Folder name')

if (folderNamePrompt == null) {
return
}

const wasFolderCreated = createSavedPostFolder(folderNamePrompt)

if (!wasFolderCreated) {
toast.error('Invalid or duplicate folder name')
return
}

const normalizedFolderName = folderNamePrompt.trim().replace(/\s+/g, ' ')

selectedFolderFilter.value = normalizedFolderName
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Duplicated normalization logic could diverge.

Line 328 duplicates the folder name normalization that exists in normalizeSavedPostFolderName. If the normalization logic changes in the composable, this inline version could produce mismatched folder names.

♻️ Suggested refactor: Export and reuse the normalizer

Consider exporting normalizeSavedPostFolderName from the composable or having createSavedPostFolder return the normalized name:

- const wasFolderCreated = createSavedPostFolder(folderNamePrompt)
-
- if (!wasFolderCreated) {
-   toast.error('Invalid or duplicate folder name')
-   return
- }
-
- const normalizedFolderName = folderNamePrompt.trim().replace(/\s+/g, ' ')
-
- selectedFolderFilter.value = normalizedFolderName
+ const createdFolderName = createSavedPostFolder(folderNamePrompt)
+
+ if (!createdFolderName) {
+   toast.error('Invalid or duplicate folder name')
+   return
+ }
+
+ selectedFolderFilter.value = createdFolderName

This would require updating createSavedPostFolder to return the normalized name on success instead of true.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pages/premium/saved-posts/`[domain].vue around lines 314 - 331, The
onCreateFolderClick handler duplicates normalization logic instead of reusing
the composable's normalizeSavedPostFolderName which risks divergence; update so
createSavedPostFolder either returns the normalized folder name on success
(instead of boolean) or export and call normalizeSavedPostFolderName from the
composable, then set selectedFolderFilter.value to the returned/normalized name;
adjust onCreateFolderClick to use the normalized value and handle falsy return
as failure, and update createSavedPostFolder and any callers to match the new
return contract.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant