Skip to content

feat: add alerts when note is updated or created and won't be visible#358

Merged
chmurson merged 3 commits intomainfrom
fix-adding-new-note-when-filtering-hides-it
Dec 30, 2025
Merged

feat: add alerts when note is updated or created and won't be visible#358
chmurson merged 3 commits intomainfrom
fix-adding-new-note-when-filtering-hides-it

Conversation

@chmurson
Copy link
Collaborator

@chmurson chmurson commented Dec 30, 2025

What?

Fixes weird situation when note is added or updated and it disappears because it no longer matches selected labels.

filtered labels:
Screenshot 2025-12-30 at 22 51 07

When such thing happens, there is a warning alert that appears on the top of notes container..

Screenshot 2025-12-30 at 22 50 06

Summary by CodeRabbit

  • New Features
    • Alert notifications when newly created or updated notes are hidden by active label filters.
  • Bug Fixes / Behavioral Changes
    • Notes selection is automatically cleared when a note becomes hidden by filters.
    • New-note form and notes list rendering adjusted to accommodate alert display and improve readiness handling.

✏️ Tip: You can customize this high-level summary in your review settings.

@netlify
Copy link

netlify bot commented Dec 30, 2025

Deploy Preview for graypaper-reader ready!

Name Link
🔨 Latest commit aff797c
🔍 Latest deploy log https://app.netlify.com/projects/graypaper-reader/deploys/69544f3105d6f80008baf11f
😎 Deploy Preview https://deploy-preview-358--graypaper-reader.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link

coderabbitai bot commented Dec 30, 2025

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Refactors note add/update to return visibility info, adds a filtered-note alert hook and UI, exposes a wrapped addNote in place of latestHandleAddNote, and extends label utilities with a visibility checker used across provider and manager flows.

Changes

Cohort / File(s) Summary
Visibility & Label Utilities
src/components/NotesProvider/NotesProvider.tsx, src/components/NotesProvider/hooks/useLabels.ts
handleAddNote and handleUpdateNote now return { isVisible: boolean }; useLabels adds and exports isVisibleByActiveLabelsLatest (RefObject callback) used to compute note visibility against active labels.
Alert Hook
src/components/NoteManager/hooks/useFilteredNoteAlert.ts
New exported hook useFilteredNoteAlert() managing noteAlertVisibilityState with triggerFilteredNoteAlert and closeNoteAlert, auto-hiding after 6s.
NoteManager Flow & UI
src/components/NoteManager/NoteManager.tsx, src/components/NoteManager/useNoteManagerNotes.ts
useNoteManagerNotes now exposes addNote wrapper (replacing latestHandleAddNote) and returns update results; NoteManager uses alert hook to show "visibleForCreated"/"visibleForUpdated" alerts, clears selection when created/updated notes are not visible, and reorganizes rendering to include the Alert.
Component API Signatures
src/components/NoteManager/components/Note.tsx, src/components/NoteManager/components/NoteContext.tsx
onEditNote signature changed to explicit (noteToReplace: IDecoratedNote, newNote: IStorageNote) => void, removing the dependency on INotesContext type alias.

Sequence Diagram(s)

sequenceDiagram
    participant User as User/UI
    participant NoteManager
    participant useNotes as useNoteManagerNotes
    participant Provider as NotesProvider
    participant Labels as useLabels\n(isVisibleByActiveLabelsLatest)
    participant Alert as useFilteredNoteAlert

    User->>NoteManager: create/update note
    NoteManager->>useNotes: addNote / updateNote
    useNotes->>Provider: handleAddNote / handleUpdateNote
    Provider->>Labels: isVisibleByActiveLabelsLatest(note)
    Labels-->>Provider: boolean (isVisible)
    Provider-->>useNotes: { isVisible }
    useNotes-->>NoteManager: { isVisible }

    alt isVisible == true
        NoteManager->>User: render note list / selection kept
    else isVisible == false
        NoteManager->>Alert: triggerFilteredNoteAlert("visibleForCreated"|"visibleForUpdated")
        Alert-->>NoteManager: alert visible (auto-dismiss after 6s)
        NoteManager->>NoteManager: clear selection
        NoteManager->>User: render alert + updated UI
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through labels, soft and spry,

When notes hide, I gave a cry.
Six seconds flash — a helpful wink,
Selection clears, the UI syncs.
A rabbit's refactor — quick as pie! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 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: adding alerts when notes are created or updated but become hidden due to active filters.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d76d934 and aff797c.

📒 Files selected for processing (1)
  • src/components/NoteManager/hooks/useFilteredNoteAlert.ts

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.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 30, 2025

Visual Regression Test Report ✅ Passed

Github run id: 20607065733

🔗 Artifacts: Download

Copy link

@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: 0

🧹 Nitpick comments (2)
src/components/NoteManager/hooks/useFilteredNoteAlert.ts (2)

29-31: Consider clearing the timeout when manually closing the alert.

When closeNoteAlert is called, the existing timeout continues running. While this is harmless (it will just set an already-hidden alert to hidden), clearing the timeout would be cleaner and avoid unnecessary callback execution.

🔎 Proposed fix
 const closeNoteAlert = useCallback(() => {
+  if (alertTimeoutRef.current) {
+    window.clearTimeout(alertTimeoutRef.current);
+    alertTimeoutRef.current = null;
+  }
   setNoteAlertVisibiltyState("hidden");
 }, []);

6-6: Minor typo in state setter name.

setNoteAlertVisibiltyState is missing an 'i' — should be setNoteAlertVisibilityState.

🔎 Proposed fix
-const [noteAlertVisibilityState, setNoteAlertVisibiltyState] = useState<
+const [noteAlertVisibilityState, setNoteAlertVisibilityState] = useState<
   "hidden" | "visibleForCreated" | "visibleForUpdated"
 >("hidden");

Then update all usages of setNoteAlertVisibiltyState to setNoteAlertVisibilityState.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5846bd7 and d76d934.

📒 Files selected for processing (7)
  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/components/Note.tsx
  • src/components/NoteManager/components/NoteContext.tsx
  • src/components/NoteManager/hooks/useFilteredNoteAlert.ts
  • src/components/NoteManager/useNoteManagerNotes.ts
  • src/components/NotesProvider/NotesProvider.tsx
  • src/components/NotesProvider/hooks/useLabels.ts
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Feature folders should keep components, hooks, and styles together in src/
Prefer hooks over Higher-Order Components (HOCs)
Colocate helper functions with their consumer components
Use PascalCase for component names
Favor semantic wrappers over long Tailwind utility class strings; complement Tailwind with @fluffylabs/shared-ui components

Files:

  • src/components/NoteManager/hooks/useFilteredNoteAlert.ts
  • src/components/NotesProvider/NotesProvider.tsx
  • src/components/NoteManager/components/Note.tsx
  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/components/NoteContext.tsx
  • src/components/NotesProvider/hooks/useLabels.ts
  • src/components/NoteManager/useNoteManagerNotes.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript as the primary language with functional React components
Use camelCase for function and variable names
Use SCREAMING_SNAKE_CASE for constants

Files:

  • src/components/NoteManager/hooks/useFilteredNoteAlert.ts
  • src/components/NotesProvider/NotesProvider.tsx
  • src/components/NoteManager/components/Note.tsx
  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/components/NoteContext.tsx
  • src/components/NotesProvider/hooks/useLabels.ts
  • src/components/NoteManager/useNoteManagerNotes.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use 2-space indentation

Files:

  • src/components/NoteManager/hooks/useFilteredNoteAlert.ts
  • src/components/NotesProvider/NotesProvider.tsx
  • src/components/NoteManager/components/Note.tsx
  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/components/NoteContext.tsx
  • src/components/NotesProvider/hooks/useLabels.ts
  • src/components/NoteManager/useNoteManagerNotes.ts
**/*.{js,jsx,ts,tsx}

⚙️ CodeRabbit configuration file

When reviewing Tailwind CSS classes, ensure they follow Tailwind 4.x conventions and suggest modern 4.x alternatives for deprecated patterns.

Files:

  • src/components/NoteManager/hooks/useFilteredNoteAlert.ts
  • src/components/NotesProvider/NotesProvider.tsx
  • src/components/NoteManager/components/Note.tsx
  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/components/NoteContext.tsx
  • src/components/NotesProvider/hooks/useLabels.ts
  • src/components/NoteManager/useNoteManagerNotes.ts
🧠 Learnings (8)
📓 Common learnings
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 357
File: src/components/NoteManager/NoteManager.tsx:52-58
Timestamp: 2025-12-28T21:10:04.074Z
Learning: In `src/components/NoteManager/NoteManager.tsx`, the direct mutation of `note.original.content` in `memoizedHandleUpdateNote` before calling `updateNote` is intentional and used for optimistic UI updates.
📚 Learning: 2025-12-28T21:10:33.529Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 357
File: src/components/NoteManager/NoteManager.tsx:52-58
Timestamp: 2025-12-28T21:10:33.529Z
Learning: In src/components/NoteManager/NoteManager.tsx, direct mutation of `note.original.content` before calling `updateNote` is intentional for optimistic UI updates. This pattern is documented with an explicit comment in the code.

Applied to files:

  • src/components/NoteManager/hooks/useFilteredNoteAlert.ts
  • src/components/NotesProvider/NotesProvider.tsx
  • src/components/NoteManager/components/Note.tsx
  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/components/NoteContext.tsx
  • src/components/NotesProvider/hooks/useLabels.ts
  • src/components/NoteManager/useNoteManagerNotes.ts
📚 Learning: 2025-12-28T21:10:04.074Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 357
File: src/components/NoteManager/NoteManager.tsx:52-58
Timestamp: 2025-12-28T21:10:04.074Z
Learning: In `src/components/NoteManager/NoteManager.tsx`, the direct mutation of `note.original.content` in `memoizedHandleUpdateNote` before calling `updateNote` is intentional and used for optimistic UI updates.

Applied to files:

  • src/components/NoteManager/hooks/useFilteredNoteAlert.ts
  • src/components/NotesProvider/NotesProvider.tsx
  • src/components/NoteManager/components/Note.tsx
  • src/components/NoteManager/components/NoteContext.tsx
  • src/components/NotesProvider/hooks/useLabels.ts
  • src/components/NoteManager/useNoteManagerNotes.ts
📚 Learning: 2025-12-28T13:25:05.152Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 355
File: src/components/NoteManager/components/NoteLayout.tsx:39-39
Timestamp: 2025-12-28T13:25:05.152Z
Learning: In Tailwind CSS 4.x, max-h-68 is a valid utility that sets max-height: 17rem (272px) and is part of the extended spacing scale. When reviewing TSX files, verify components use this class consistently and that tailwind.config.js includes the 68 spacing key in the extended spacing scale if you rely on it.

Applied to files:

  • src/components/NotesProvider/NotesProvider.tsx
  • src/components/NoteManager/components/Note.tsx
  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/components/NoteContext.tsx
📚 Learning: 2025-10-22T20:36:10.440Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: Project targets React 19+; prefer passing refs as a regular prop in function components and avoid React.forwardRef in new code. File: src/components/Outline/OutlineLink.tsx.

Applied to files:

  • src/components/NoteManager/components/Note.tsx
  • src/components/NoteManager/NoteManager.tsx
  • src/components/NotesProvider/hooks/useLabels.ts
📚 Learning: 2025-12-28T21:09:55.992Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 357
File: src/components/NoteManager/NoteManager.tsx:52-58
Timestamp: 2025-12-28T21:09:55.992Z
Learning: In src/components/NoteManager/NoteManager.tsx, note.original.content is mutated directly within memoizedHandleUpdateNote before updateNote is called to support optimistic UI updates. This is intentional for this component; during reviews, don't flag this as an improper mutation. Ensure the mutation is isolated, safe, and revertible, and consider documenting the rationale in code comments.

Applied to files:

  • src/components/NoteManager/NoteManager.tsx
📚 Learning: 2025-10-22T20:36:10.440Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: This repo targets React 19+; prefer passing refs as a normal prop to function components and avoid React.forwardRef in new code. File context: src/components/Outline/OutlineLink.tsx.

Applied to files:

  • src/components/NoteManager/NoteManager.tsx
  • src/components/NoteManager/components/NoteContext.tsx
  • src/components/NotesProvider/hooks/useLabels.ts
📚 Learning: 2025-10-28T20:41:06.887Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 333
File: src/components/PdfProvider/PdfProvider.tsx:34-37
Timestamp: 2025-10-28T20:41:06.887Z
Learning: In the graypaper-reader codebase, for text layer render tracking in PdfProvider, use RefObject<number[]> approach (textLayerRenderedRef) without adding reactive signals or version numbers. Consumers should check the ref imperatively when needed rather than reacting to changes.

Applied to files:

  • src/components/NotesProvider/hooks/useLabels.ts
🧬 Code graph analysis (6)
src/components/NotesProvider/NotesProvider.tsx (3)
src/components/NotesProvider/types/StorageNote.ts (1)
  • IStorageNote (5-5)
src/components/NotesProvider/types/DecoratedNote.ts (1)
  • IDecoratedNote (4-19)
src/components/NotesProvider/hooks/useLabels.ts (1)
  • useLabels (119-247)
src/components/NoteManager/components/Note.tsx (2)
src/components/NotesProvider/types/DecoratedNote.ts (1)
  • IDecoratedNote (4-19)
src/components/NotesProvider/types/StorageNote.ts (1)
  • IStorageNote (5-5)
src/components/NoteManager/NoteManager.tsx (4)
src/components/NoteManager/hooks/useFilteredNoteAlert.ts (1)
  • useFilteredNoteAlert (5-34)
src/components/NotesProvider/utils/areSelectionsEqual.ts (1)
  • areSelectionsEqual (8-13)
src/components/NoteManager/components/NewNote.tsx (1)
  • NewNote (21-99)
src/components/NoteManager/components/InactiveNoteSkeleton.tsx (1)
  • InactiveNoteSkeleton (6-26)
src/components/NoteManager/components/NoteContext.tsx (2)
src/components/NotesProvider/types/DecoratedNote.ts (1)
  • IDecoratedNote (4-19)
src/components/NotesProvider/types/StorageNote.ts (1)
  • IStorageNote (5-5)
src/components/NotesProvider/hooks/useLabels.ts (3)
src/components/NotesProvider/types/DecoratedNote.ts (1)
  • IDecoratedNote (4-19)
src/components/NotesProvider/types/StorageNote.ts (1)
  • IStorageNote (5-5)
src/hooks/useLatestCallback.ts (1)
  • useLatestCallback (4-8)
src/components/NoteManager/useNoteManagerNotes.ts (1)
src/components/NotesProvider/NotesProvider.tsx (1)
  • INotesContext (20-40)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: visual-tests
🔇 Additional comments (13)
src/components/NoteManager/useNoteManagerNotes.ts (3)

30-37: LGTM!

The addNote wrapper correctly delegates to latestHandleAddNote.current and propagates the isVisible return value. The type annotation using INotesContext["handleAddNote"] ensures type safety with the provider's interface.


39-45: LGTM!

The updateNote function now correctly returns the result from the underlying handler, maintaining consistency with the updated INotesContext["handleUpdateNote"] return type.


118-118: LGTM!

Exposing addNote instead of latestHandleAddNote provides a cleaner API surface that properly encapsulates the ref-based callback pattern.

src/components/NotesProvider/hooks/useLabels.ts (2)

231-244: LGTM!

The activeLabels memo is a good refactor that avoids duplicate computation. The isVisibleByActiveLabelsLatest using useLatestCallback provides a stable reference to check note visibility against current active labels without triggering re-renders — essential for the add/update flow to determine if a note should trigger an alert.


119-124: LGTM!

The updated return type signature correctly reflects the new isVisibleByActiveLabelsLatest member as a RefObject, aligning with the useLatestCallback pattern used elsewhere in this codebase.

src/components/NoteManager/components/Note.tsx (1)

20-28: LGTM!

The explicit onEditNote signature is cleaner and appropriately decoupled from INotesContext. Since Note.tsx doesn't need the { isVisible } return value (handled upstream in NoteManager.tsx), the void return type is correct here.

src/components/NoteManager/components/NoteContext.tsx (1)

5-21: LGTM!

The explicit onEditNote signature in ISingleNoteContext properly aligns with Note.tsx and removes the coupling to INotesContext. This makes the context interface self-documenting and consistent with the component layer above it.

src/components/NotesProvider/NotesProvider.tsx (2)

150-161: LGTM!

The handleAddNote implementation correctly computes visibility before adding the note and returns the result. Using isVisibleByActiveLabelsLatest.current(note) properly leverages the ref-based pattern to get the latest visibility check.


163-181: LGTM!

The handleUpdateNote implementation correctly:

  1. Returns { isVisible: true } when refusing to edit remote notes (appropriate since no action was taken)
  2. Computes visibility of the newNote against active labels
  3. Returns the visibility result for the caller to handle alerts

The dependency array correctly includes isVisibleByActiveLabelsLatest.

src/components/NoteManager/NoteManager.tsx (4)

54-66: LGTM!

The optimistic mutation of note.original.labels on line 58 follows the established pattern for note.original.content (documented in the existing comment on line 56). The visibility check and alert triggering logic is correctly implemented — clearing selection and showing the alert only when the updated note won't be visible due to label filtering.

Based on learnings, this optimistic UI mutation pattern is intentional for this component.


72-107: LGTM!

The handleAddNoteClick correctly:

  1. Creates the new note with all required fields
  2. Calls addNote and destructures the isVisible result
  3. Triggers the alert and clears selection when the note won't be visible
  4. Maintains the keepShowingNewNote ref for the transitional UI state

The dependency array is complete.


149-168: LGTM!

The Alert UI is well-implemented:

  • Clear conditional rendering based on noteAlertVisibilityState
  • Distinct messages for "created" vs "updated" scenarios
  • Close button properly wired to closeNoteAlert
  • Uses semantic Alert component with warning intent from shared-ui

169-207: LGTM!

The restructured rendering correctly:

  • Wraps content in a fragment to accommodate the Alert at the top level
  • Maintains proper conditional rendering for NewNote, skeletons, empty state, and notes list
  • Preserves the cn() utility for conditional classes on the note-manager container

@chmurson chmurson merged commit 9f81668 into main Dec 30, 2025
7 of 9 checks passed
@chmurson chmurson deleted the fix-adding-new-note-when-filtering-hides-it branch December 30, 2025 22:16
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