Skip to content

feat(dashnote): split settings into tab, menu, and focused login modal#80

Merged
thephez merged 2 commits into
mainfrom
dashnote/settings-split
May 13, 2026
Merged

feat(dashnote): split settings into tab, menu, and focused login modal#80
thephez merged 2 commits into
mainfrom
dashnote/settings-split

Conversation

@thephez
Copy link
Copy Markdown
Collaborator

@thephez thephez commented May 13, 2026

LoginModal stripped to the unauthenticated flow only. Account management moves to a Settings tab (Identity, Contract, Data, Appearance, Danger zone) and an IdentityCard popover (Settings, Switch identity, Log out when authed; one-click login when readonly).

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

Summary by CodeRabbit

Release Notes

  • New Features
    • Added a Settings tab for managing app preferences and identity settings
    • Added a Settings dropdown menu accessible from the user identity card, with options to switch identity or log out
    • Added theme toggle in Settings to customize app appearance
    • Added ability to clear cached note data from Settings
    • Moved contract registration and management to the Settings section

Review Change Stack

LoginModal stripped to the unauthenticated flow only. Account management
moves to a Settings tab (Identity, Contract, Data, Appearance, Danger
zone) and an IdentityCard popover (Settings, Switch identity, Log out
when authed; one-click login when readonly).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

Warning

Rate limit exceeded

@thephez has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 43 minutes and 15 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 74ab5c70-f843-4adb-83d2-3638adc80583

📥 Commits

Reviewing files that changed from the base of the PR and between f533b3d and 0df5882.

📒 Files selected for processing (2)
  • example-apps/dashnote/src/hooks/useContractRegistration.ts
  • example-apps/dashnote/test/SettingsPanel.test.tsx
📝 Walkthrough

Walkthrough

This PR introduces a Settings feature to the Dashnote example app. A new "settings" tab is added to navigation alongside notes and how-it-works. The SettingsPanel component provides identity display, contract management, local cache clearing, theme toggling, and device memory controls. LoginModal is simplified from dual-mode (login/settings) to login-only, with contract registration moved to SettingsPanel via a new useContractRegistration hook. IdentityCard is refactored to include a dropdown menu with Settings, Switch identity, and Log out actions. Component callbacks are separated: onOpenLogin for authentication flows and onOpenSettings for settings navigation.

Changes

Settings Tab and Infrastructure

Layer / File(s) Summary
Tab type and contract registration hook
src/components/Tabs.tsx, src/hooks/useContractRegistration.ts
Extends TopTab union to include "settings" option. Adds useContractRegistration hook that manages registering state, error formatting, and persists registered contract ID via session.setContractId.
SettingsPanel component with UI sections
src/components/SettingsPanel.tsx
Implements full SettingsPanel with internal helpers (CopyButton with temporary "Copied!" feedback, ThemeToggleControl, Section wrapper). Renders identity section with clipboard copy, contract section with input/apply/register controls, data section for cache clearing, appearance section with theme toggle, and conditional danger zone for device forgetting. Synchronizes editable contract input during render and manages timed state resets.

IdentityCard Menu and Navigation

Layer / File(s) Summary
IdentityCard dropdown menu and state
src/components/IdentityCard.tsx
Extends IdentityCardProps with onOpenSettings callback. Adds menuOpen state, containerRef, and effect that installs outside-click/Escape listeners. Treats readonly mode as distinct early return (no menu; direct onLoginClick). Non-readonly modes render button that toggles menu, displaying identity/contract status and menu items for Settings (via onOpenSettings), Switch identity (via onLoginClick), and Log out (calling session.logout when authenticated).
AppShell navigation and IdentityCard wiring
src/components/AppShell.tsx
Adds Settings NavButton to mobile/overlay navigation that calls onTabChange("settings") and closes drawer. Passes onOpenSettings handler to IdentityCard that routes to settings tab and closes drawer.

LoginModal Simplification and App Routing

Layer / File(s) Summary
LoginModal contract registration removal
src/components/LoginModal.tsx
Removes registerContract import and handleRegisterContract handler. Simplifies advanced settings UI to contract-reuse-only ("Use this ID"), removing previous "register new" button and registration messaging. Fixes Modal title to "Login" instead of switching between "Login" (unauthenticated) and "Settings" (authenticated).
App tab routing and SettingsPanel mounting
src/App.tsx
Adds SettingsPanel import. Extends screenCopy with settings title/subtitle. Updates NotesWorkspace to accept separate onOpenLogin and onOpenSettings handlers wired to different actions (login vs settings tab switch). Adds conditional render of SettingsPanel when tab === "settings".

Component Prop Threading for Login and Settings

Layer / File(s) Summary
NoteEditor onOpenLogin prop
src/components/NoteEditor.tsx
Adds onOpenLogin callback to NoteEditorProps. Updates destructuring to include new prop. Changes read-only "Sign in to edit" button to call onOpenLogin instead of onOpenSettings.
NotesWorkspace onOpenLogin prop and threading
src/components/NotesWorkspace.tsx
Adds onOpenLogin to NotesWorkspace props alongside onOpenSettings. Changes unauthenticated "Sign in to see your notes" empty state to use onOpenLogin. Passes onOpenLogin into NoteEditor component.

Test Coverage for Settings Feature

Layer / File(s) Summary
App settings tab and SettingsPanel rendering tests
test/App.test.tsx
Updates AppShell mock to accept "settings" tab option. Adds Settings tab button to mock UI. Adds test verifying authenticated session renders SettingsPanel with identity display.
IdentityCard menu and interaction tests
test/IdentityCard.test.tsx
Adds hoisted mockUseSession for logout control. Expands renderCard helper to accept onLoginClick/onOpenSettings callbacks. Sets up per-test mock reset. Adds regression test for readonly mode and comprehensive menu interaction tests (opening menu, Settings/Switch identity/Log out item selection).
LoginModal mock setup and test updates
test/LoginModal.test.tsx
Removes hoisted mockRegisterContract and contract module mocking. Removes beforeEach mockRegisterContract reset. Adds regression test for modal remaining open during authenticated flow. Removes tests for authenticated settings view and DPNS caption handling.
NotesWorkspace prop threading test updates
test/NotesWorkspace.test.tsx
Updates all NotesWorkspace test renders to include onOpenLogin prop. Changes unauthenticated gating test to assert onOpenLogin is called for login button action instead of onOpenSettings. Propagates onOpenLogin through all desktop, mobile, cache hydration, and identity-switching test scenarios.
SettingsPanel comprehensive test suite
test/SettingsPanel.test.tsx
Introduces full Vitest/JSDOM test suite with mocked useSession, registerContract, and clearCachedNotes. Adds makeSession factory. Tests UI rendering (authenticated identity, DPNS caption, network indicator, empty state), user interactions (copy to clipboard, contract input/apply, successful/failed registration flows), cache clearing, and device forgetting.

🎯 3 (Moderate) | ⏱️ ~20 minutes

🐰 A rabbit's tale of settings now told,
With menus that open and contracts to hold,
LoginModal's simpler, for login alone,
Identity card has a dropdown full-grown,
Dashnote settings shine, let the features unfold!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(dashnote): split settings into tab, menu, and focused login modal' accurately captures the main changes: settings moved to a tab, identity menu added, and LoginModal refocused to unauthenticated flow.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dashnote/settings-split

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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.

Copy link
Copy Markdown

@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

🧹 Nitpick comments (2)
example-apps/dashnote/src/components/NotesWorkspace.tsx (1)

34-40: ⚡ Quick win

Use an interface for NotesWorkspace props instead of an inline object type.

This changed prop contract is currently defined inline; convert it to an interface to match project TypeScript conventions.

Proposed diff
+interface NotesWorkspaceProps {
+  onOpenLogin: () => void;
+  onOpenSettings: () => void;
+}
+
 export function NotesWorkspace({
   onOpenLogin,
   onOpenSettings,
-}: {
-  onOpenLogin: () => void;
-  onOpenSettings: () => void;
-}) {
+}: NotesWorkspaceProps) {
As per coding guidelines "Use interface for defining object shapes in TypeScript".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@example-apps/dashnote/src/components/NotesWorkspace.tsx` around lines 34 -
40, Replace the inline object type for the NotesWorkspace component props with a
named interface: create an interface NotesWorkspaceProps that declares
onOpenLogin: () => void and onOpenSettings: () => void, then update the
NotesWorkspace signature to use NotesWorkspaceProps (e.g., export function
NotesWorkspace(props: NotesWorkspaceProps) or export function NotesWorkspace({
onOpenLogin, onOpenSettings }: NotesWorkspaceProps)). Ensure the interface name
(NotesWorkspaceProps) is exported if the props type is used elsewhere.
example-apps/dashnote/src/components/SettingsPanel.tsx (1)

62-68: ⚡ Quick win

Use an interface for Section props.

Replace the inline object shape with an interface to match the project’s TypeScript rule.

Proposed fix
+interface SectionProps {
+  title: string;
+  children: React.ReactNode;
+}
+
 function Section({
   title,
   children,
-}: {
-  title: string;
-  children: React.ReactNode;
-}) {
+}: SectionProps) {
As per coding guidelines: `example-apps/dashnote/**/*.{ts,tsx}`: Use interface for defining object shapes in TypeScript.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@example-apps/dashnote/src/components/SettingsPanel.tsx` around lines 62 - 68,
The Section component currently declares its props with an inline object type;
define a named interface (e.g., SectionProps) that declares title: string and
children: React.ReactNode, then update the function signature to use
SectionProps (function Section(props: SectionProps) or function Section({ title,
children }: SectionProps)). Update any related references to the props type name
so the file conforms to the project's rule requiring interfaces for object
shapes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@example-apps/dashnote/src/hooks/useContractRegistration.ts`:
- Around line 19-37: The register() callback can be called concurrently causing
duplicate contract deployments; add an internal re-entrancy guard (e.g., a
useRef flag like isRegisteringRef) and check it at the top of register() before
proceeding, set the flag true immediately when starting, and clear it in the
finally block to prevent concurrent invocations; keep the existing
setRegistering state updates and error handling but use the ref to short-circuit
early (return null) if an invocation is already in-flight; update the register
callback reference in useCallback so the guard is stable across renders.

---

Nitpick comments:
In `@example-apps/dashnote/src/components/NotesWorkspace.tsx`:
- Around line 34-40: Replace the inline object type for the NotesWorkspace
component props with a named interface: create an interface NotesWorkspaceProps
that declares onOpenLogin: () => void and onOpenSettings: () => void, then
update the NotesWorkspace signature to use NotesWorkspaceProps (e.g., export
function NotesWorkspace(props: NotesWorkspaceProps) or export function
NotesWorkspace({ onOpenLogin, onOpenSettings }: NotesWorkspaceProps)). Ensure
the interface name (NotesWorkspaceProps) is exported if the props type is used
elsewhere.

In `@example-apps/dashnote/src/components/SettingsPanel.tsx`:
- Around line 62-68: The Section component currently declares its props with an
inline object type; define a named interface (e.g., SectionProps) that declares
title: string and children: React.ReactNode, then update the function signature
to use SectionProps (function Section(props: SectionProps) or function Section({
title, children }: SectionProps)). Update any related references to the props
type name so the file conforms to the project's rule requiring interfaces for
object shapes.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: cbeba858-d25b-42da-b23b-71c628946201

📥 Commits

Reviewing files that changed from the base of the PR and between 254da27 and f533b3d.

📒 Files selected for processing (14)
  • example-apps/dashnote/src/App.tsx
  • example-apps/dashnote/src/components/AppShell.tsx
  • example-apps/dashnote/src/components/IdentityCard.tsx
  • example-apps/dashnote/src/components/LoginModal.tsx
  • example-apps/dashnote/src/components/NoteEditor.tsx
  • example-apps/dashnote/src/components/NotesWorkspace.tsx
  • example-apps/dashnote/src/components/SettingsPanel.tsx
  • example-apps/dashnote/src/components/Tabs.tsx
  • example-apps/dashnote/src/hooks/useContractRegistration.ts
  • example-apps/dashnote/test/App.test.tsx
  • example-apps/dashnote/test/IdentityCard.test.tsx
  • example-apps/dashnote/test/LoginModal.test.tsx
  • example-apps/dashnote/test/NotesWorkspace.test.tsx
  • example-apps/dashnote/test/SettingsPanel.test.tsx

Comment thread example-apps/dashnote/src/hooks/useContractRegistration.ts
The Register button disables once setRegistering(true) commits, but a
second click landing in the same task could slip a duplicate publish
through. Add a synchronous useRef guard so the second invocation
short-circuits before the SDK call.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@thephez thephez merged commit a7f935e into main May 13, 2026
3 checks passed
@thephez thephez deleted the dashnote/settings-split branch May 13, 2026 15: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