Skip to content

fix: subfolder navigation after reload with E2E coverage#70

Merged
FSM1 merged 11 commits into
mainfrom
fix/quick-003-subfolder-navigation
Feb 9, 2026
Merged

fix: subfolder navigation after reload with E2E coverage#70
FSM1 merged 11 commits into
mainfrom
fix/quick-003-subfolder-navigation

Conversation

@FSM1

@FSM1 FSM1 commented Feb 9, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fix subfolder navigation: Implement loadFolder with IPNS resolution and metadata decryption so navigateTo works from a cold Zustand store (after page reload or direct URL access)
  • Add E2E coverage: New Phase 3.5 tests (3.7-3.10) reload the page mid-workflow and verify subfolder navigation, breadcrumb nav, and upload all work from a cold state
  • Fix ESLint hanging: Ignore src-tauri/target/ (1,755 generated Rust build files) — lint drops from hanging indefinitely to ~5 seconds
  • Docs: Add acknowledgements to README, fix pre-existing markdownlint errors

Test plan

  • Existing E2E tests (Phases 1-8) still pass
  • New Phase 3.5 tests pass — specifically that subfolder navigation after page.reload() correctly loads content via IPNS resolve + key unwrapping
  • pnpm lint completes in <10 seconds (was hanging before)
  • CI passes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Subfolder navigation now loads real folder data, displays loading placeholders, and supports uploads inside subfolders with updated breadcrumbs.
  • Bug Fixes

    • Navigation race conditions fixed; session and navigation state restore reliably after page reloads.
    • Missing remote records handled gracefully with empty-folder fallback.
  • Documentation

    • README reorganized and expanded.
  • Tests

    • New end-to-end tests covering post-reload and subfolder workflows.
  • Chores

    • Lint/build ignore patterns updated; added planning note for client-side IPNS signature validation.

FSM1 and others added 5 commits February 9, 2026 13:30
Two-task plan: implement loadFolder stub in folder.service.ts and
wire navigateTo in useFolderNavigation.ts to unwrap keys, resolve
IPNS, decrypt metadata, and populate FolderNode in Zustand store.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ta decryption

- Replace TODO stub with real implementation that resolves IPNS, fetches
  and decrypts folder metadata, and returns complete FolderNode
- Add parentId and name parameters for correct FolderNode construction
- Import resolveIpnsRecord from ipns.service
- Gracefully handle IPNS-not-found (returns empty folder with warning)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace setTimeout stub with real subfolder loading pipeline
- Unwrap ECIES-encrypted folderKey and ipnsPrivateKey from FolderEntry
- Call loadFolder to resolve IPNS, fetch and decrypt metadata
- Set loading placeholder in store while async operation runs
- Use getState() pattern throughout to avoid stale Zustand closures
- Gracefully handle errors by removing placeholder on failure
- Make navigateTo async, update return type to Promise<void>

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tasks completed: 2/2
- Implement loadFolder with IPNS resolution and metadata decryption
- Wire navigateTo to load subfolders with key unwrapping

SUMMARY: .planning/quick/003-fix-subfolder-navigation-and-upload/003-SUMMARY.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implemented real subfolder loading: ECIES key unwrapping, IPNS resolution,
and metadata decryption when navigating into subfolders. Fixes breadcrumbs
not updating and "parent folder not found" error on upload.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 9, 2026 12:47
@coderabbitai

coderabbitai Bot commented Feb 9, 2026

Copy link
Copy Markdown

Walkthrough

Implements a real subfolder load pipeline: ECIES key unwrapping, IPNS resolution, encrypted metadata fetch/decrypt, and population of the Zustand store with complete FolderNode data. The navigateTo hook becomes asynchronous and planning docs updated to mark Quick Task 003 complete.

Changes

Cohort / File(s) Summary
Planning & State
/.planning/STATE.md, .planning/quick/003-fix-subfolder-navigation-and-upload/003-PLAN.md, .planning/quick/003-fix-subfolder-navigation-and-upload/003-SUMMARY.md, .planning/todos/pending/2026-02-09-client-side-ipns-signature-validation.md
Mark Quick Task 003 complete; add plan/summary for Quick Task 003; add pending todo for client-side IPNS signature validation.
Folder Service Implementation
apps/web/src/services/folder.service.ts
Replace loadFolder placeholder with real implementation that resolves IPNS (resolveIpnsRecord), fetches & decrypts metadata (fetchAndDecryptMetadata), returns full FolderNode; signature extended to accept parentId and name; add exported helper.
Navigation Hook Integration
apps/web/src/hooks/useFolderNavigation.ts
Change navigateTo to async Promise<void>; add ECIES unwrap flow, loading placeholder, retrying IPNS resolution, call loadFolder, guard via latest-target/getState(), and add error/cleanup handling.
Store Update
apps/web/src/stores/folder.store.ts
When updating folder children, also set isLoaded = true and isLoading = false for that folder.
E2E Tests & Docs
tests/e2e/tests/full-workflow.spec.ts, tests/e2e/README.md
Add phases 3.7–3.10 to test reload → subfolder navigation → post-reload upload; minor README formatting.
Linting & Ignore Configs
eslint.config.js, .markdownlintignore
Add ignore patterns (**/.learnings/**, **/src-tauri/target/**, apps/desktop/src-tauri/target/) to lint/markdownlint configs.
Repository README
README.md
Formatting and content updates (overview, vision, MVP scope, acknowledgements); non-functional documentation edits.

Sequence Diagram

sequenceDiagram
    participant User
    participant Hook as useFolderNavigation
    participant Store as Zustand\ Store
    participant Service as folder.service
    participant IPNS as ipns.service
    participant IPFS as IPFS
    participant Crypto as Crypto\ Utils

    User->>Hook: Click subfolder (navigateTo)
    Hook->>Store: Read current folders (getState)
    Hook->>Hook: Find FolderEntry for target
    Hook->>Crypto: Get derived keypair (useAuthStore)
    Hook->>Store: Insert loading placeholder & navigate
    Hook->>Crypto: unwrapKey(folderKey)
    Hook->>Crypto: unwrapKey(ipnsPrivateKey)
    Hook->>Service: loadFolder(id, folderKey, ipnsPrivateKey, ipnsName, parentId, name)
    Service->>IPNS: resolveIpnsRecord(ipnsName)
    alt IPNS found
        IPNS-->>Service: CID & sequenceNumber
        Service->>IPFS: fetch metadata (CID)
        IPFS-->>Service: Encrypted metadata
        Service->>Crypto: decrypt metadata with folderKey
        Crypto-->>Service: Decrypted FolderMetadata
        Service-->>Hook: FolderNode (with children, sequenceNumber)
    else IPNS not found
        IPNS-->>Service: null
        Service-->>Hook: Empty/unloaded FolderNode (fallback)
    end
    Hook->>Store: Update FolderNode (if latest target)
    Hook->>Store: Clear loading state
    Hook-->>User: Subfolder rendered (breadcrumbs updated)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

  • Phase 7 - Multi Device Sync #54 — Directly related: modifies IPNS resolution and metadata pipeline used by the new loadFolder.
  • Phase 6.3 - UI restructure #53 — Related: also updates useFolderNavigation.ts / navigateTo behavior and signature.
  • Restyle #47 — Partially related: touches tests/e2e/tests/full-workflow.spec.ts interactions and selectors used by the new tests.
🚥 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 title accurately describes the main change: implementing subfolder navigation that works after page reload, with corresponding E2E test coverage added.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/quick-003-subfolder-navigation

No actionable comments were generated in the recent review. 🎉


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@apps/web/src/hooks/useFolderNavigation.ts`:
- Around line 156-162: The navigation happens before the folder placeholder is
inserted, causing a brief undefined currentFolder; fix by inserting the store
placeholder for the target folder before calling navigate (or move the
navigate() call to after the placeholder-insertion logic used at the later
block), i.e., in useFolderNavigation ensure the same placeholder creation
routine (the code at the placeholder insertion point) runs for targetFolderId
first (and updates currentFolderId/store), then call navigate('/files' or
`/files/${targetFolderId}`) so the component re-render finds the placeholder
already in the store.
- Around line 155-251: navigateTo currently uses a single hook-level isLoading
flag and sets placeholder keys to empty Uint8Array, which causes
stale-completion races and potential key-read issues; modify navigateTo to (1)
track the latest requested target with a ref (e.g., lastNavigatedRef) or create
an AbortController per navigation and abort previous controllers so that when
async work completes you ignore stale results and only call setIsLoading(false)
or setFolder when the ref/controller matches the current navigation, and (2)
ensure the loadingPlaceholder created in navigateTo still uses safe sentinel
values but update any code paths (especially functions that decrypt or traverse
children such as unwrapKey, loadFolder and any recursive folder readers) to
first check folderNode.isLoaded before reading folderKey/ipnsPrivateKey (or
throw/return early) so empty keys are never used for real decryption; also
removeFolder can remain as-is.

Comment thread apps/web/src/hooks/useFolderNavigation.ts
Comment thread apps/web/src/hooks/useFolderNavigation.ts

Copilot AI left a comment

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.

Pull request overview

Implements real subfolder navigation in the web app by loading subfolder metadata (IPNS resolve → IPFS fetch → decrypt) and unwrapping per-subfolder keys during navigation, so folder state in the Zustand store is actually populated for breadcrumbs and operations like upload.

Changes:

  • Implemented loadFolder to resolve IPNS, fetch encrypted metadata from IPFS, decrypt it with the folder key, and return a fully populated FolderNode.
  • Updated useFolderNavigation.navigateTo to unwrap ECIES-encrypted subfolder keys and call loadFolder when entering unloaded subfolders.
  • Cleaned up README sections and added planning artifacts documenting the quick task.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
apps/web/src/services/folder.service.ts Implements real folder loading via IPNS resolution and metadata decryption, returning populated FolderNodes.
apps/web/src/hooks/useFolderNavigation.ts Makes navigation async and loads/unlocks subfolder state on-demand, storing it in Zustand for breadcrumbs & operations.
README.md Removes outdated PoC/timeline content.
.planning/quick/003-fix-subfolder-navigation-and-upload/003-SUMMARY.md Adds quick-task execution summary and decisions.
.planning/quick/003-fix-subfolder-navigation-and-upload/003-PLAN.md Adds the executed plan used to implement the changes.
.planning/STATE.md Updates project planning state to reflect completion of quick task 003.

Comment thread apps/web/src/hooks/useFolderNavigation.ts Outdated
Comment thread apps/web/src/services/folder.service.ts Outdated
FSM1 and others added 3 commits February 9, 2026 14:03
Credit ChainSafe Files as inspiration for the project.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add `text` language to all bare fenced code blocks in README.md
- Change bold emphasis to heading for subtitle (MD036)
- Add blank line before fenced code block in tests/e2e/README.md (MD031)
- Add `text` language to tree structure block in tests/e2e/README.md (MD040)
- Exclude Rust build artifacts from markdownlint via .markdownlintignore

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Phase 3.5 E2E tests (3.7-3.10) that reload the page and verify
subfolder navigation works from a cold Zustand store, exercising the
full navigateTo path (IPNS resolve + key unwrapping + metadata decrypt).

Also fix ESLint hanging by ignoring src-tauri/target/ (1,755 generated
JS files from Rust build) and .learnings/ directories.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@FSM1 FSM1 changed the title fix: implement subfolder navigation with key unwrapping and IPNS loading fix: subfolder navigation after reload with E2E coverage Feb 9, 2026
- Fix race condition: track latest nav target with useRef, skip stale
  completions when user rapidly navigates between folders
- Fix undefined flash: insert store placeholder before calling navigate()
  so component re-render finds the folder node immediately
- Fix root fallthrough: special-case navigateTo('root') to return early
  instead of falling into the subfolder key-unwrapping path
- Fix IPNS retry: return isLoaded: false when IPNS resolution returns
  null so the folder can be retried on next navigation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@apps/web/src/hooks/useFolderNavigation.ts`:
- Around line 240-252: After calling loadFolder(...) check the returned
FolderNode's isLoaded flag before calling useFolderStore.getState().setFolder:
if folderNode.isLoaded === false (and latestNavTarget.current ===
targetFolderId) either schedule an automatic retry with exponential backoff
(e.g., retry loop with setTimeout, capped attempts, and re-calling loadFolder
using the same parameters) or set a pending UI state on the store (add a
"pending"/"isPendingLoad" flag) so the UI shows a retry message/spinner; ensure
you still respect latestNavTarget.current to avoid races and clear retries when
navigation changes.

In `@apps/web/src/services/folder.service.ts`:
- Around line 78-99: resolveIpnsRecord currently returns only a CID and
fetchAndDecryptMetadata trusts the decrypted payload — add client-side IPNS
signature verification: have the backend include the IPNS record signature and
signer info in the resolve response (so resolveIpnsRecord returns { cid,
signature, signerPubKey, sequence }), then in the folder loading flow (where
resolveIpnsRecord is called in folder.service.ts) verify the IPNS record by
calling a new or existing verifyIpnsRecordSignature(signature, signerPubKey,
cid, sequence) before passing the CID to fetchAndDecryptMetadata; if
verification fails, throw an Error and mark the folder as not loaded/retry (do
not call fetchAndDecryptMetadata). Ensure you reference resolveIpnsRecord,
fetchAndDecryptMetadata, and verifyIpnsRecordSignature when implementing.
🧹 Nitpick comments (1)
apps/web/src/hooks/useFolderNavigation.ts (1)

157-266: setIsLoading is a React state setter — verify no update-after-unmount.

If the user navigates away (unmounts the component) while the async navigateTo is in flight, setIsLoading(false) in the finally block would fire on an unmounted component. While React 18+ no longer warns about this, the state update is wasted. A minor concern, but if you ever add cleanup logic, an isMounted ref or AbortController would be the right pattern.

Comment thread apps/web/src/hooks/useFolderNavigation.ts Outdated
Comment thread apps/web/src/services/folder.service.ts
- updateFolderChildren now sets isLoaded: true — fixes post-reload
  subfolder navigation where root stayed isLoaded: false after sync,
  causing navigateTo to skip it when searching for FolderEntry
- Add retry loop (3 attempts, 2s delay) in navigateTo when loadFolder
  returns isLoaded: false due to IPNS not yet propagated

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

2 participants