Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 203 additions & 0 deletions docs/ai/design/feature-mobile-settings-menu.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
---
phase: design
title: System Design & Architecture - Mobile Settings Menu
description: Technical design for the fully interactive mobile settings screen with horizontal tabs and native file actions.
---

# System Design & Architecture

## Architecture Overview
**What is the high-level system structure?**

```mermaid
graph TD
User["Mobile user"] --> Screen["ui/mobile/app/(tabs)/settings.tsx"]
Screen --> Tabs["Horizontal tab rail"]
Screen --> Panels["Settings content panels"]

Panels --> Account["My Account panel"]
Panels --> Import["ENEX import panel"]
Panels --> Export["ENEX export panel"]
Panels --> Wordpress["WordPress settings panel"]
Panels --> ApiKeys["API keys panel"]

Account --> Auth["useAuth / SupabaseProvider"]
Wordpress --> WPService["core/services/wordpressSettings.ts"]
ApiKeys --> KeyService["core/services/apiKeysSettings.ts"]
Import --> ImportService["ui/mobile/services/enexImport.ts"]
Export --> ExportService["ui/mobile/services/enexExport.ts"]

ImportService --> Picker["Expo Document Picker"]
ImportService --> FileRead["Expo FileSystem"]
ImportService --> NotesWrite["core/enex/note-creator.ts"]
ExportService --> NotesRead["core/services/notes.ts"]
ExportService --> Builder["core/enex/enex-builder.ts"]
ExportService --> Share["Expo Sharing"]
```

- Key components and responsibilities
- `settings.tsx`
- Owns selected tab state and page-level styling.
- Hosts the horizontal scrollable tab bar and shared page shell.
- `My Account panel`
- Shows authenticated email.
- Handles sign out, theme mode, and destructive account deletion confirmation.
- Preserves existing settings responsibilities that currently live on the screen outside the requested tab redesign.
- `ENEX import panel`
- Launches native file picker.
- Reads `.enex` content from device storage.
- Parses notes into a RN-safe structure and writes them using existing note creation logic.
- `ENEX export panel`
- Loads notes for the signed-in user.
- Builds `.enex` XML using the shared builder.
- Persists to a temporary file and opens native share/save flow.
- `WordPress settings panel`
- Reuses `WordPressSettingsService`.
- Mirrors existing validation and success/error behavior from web.
- `API keys panel`
- Reuses `ApiKeysSettingsService`.
- Mirrors existing validation and success/error behavior from web.

## Data Models
**What data do we need to manage?**

- Core UI model
- `SettingsTabKey = "account" | "import" | "export" | "wordpress" | "apiKeys"`
- Each tab has:
- label
- icon
- description
- render function / component
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

- Account panel state
- `email: string`
- `themeMode: "system" | "light" | "dark"`
- `confirmDelete: boolean`
- `isDeleting: boolean`
- `errorMessage?: string`

- Import panel state
- `selectedFileName?: string`
- `isImporting: boolean`
- `importSummary?: { success: number; errors: number; message: string }`
- `errorMessage?: string`

- Export panel state
- `isExporting: boolean`
- `lastExportFileName?: string`
- `exportedNotesCount?: number`
- `errorMessage?: string`

- WordPress/API key panel state
- Existing status payloads from shared service classes.
- Local form input values.
- Loading/saving/error/success state.

- ENEX mobile import structure
- `ParsedNote`
- `title: string`
- `content: string`
- `created: Date`
- `updated: Date`
- `tags: string[]`
- `resources: []`
- Resources are not expanded in the first mobile-native implementation path; raw note HTML is preserved where possible.

- ENEX mobile export structure
- Shared `ExportNote` from `core/enex/export-types.ts`.
- Mobile export emits note title, HTML description, tags, timestamps, and an empty `resources` array.

## API Design
**How do components communicate?**

- Existing internal service interfaces
- `ApiKeysSettingsService.getStatus()`
- `ApiKeysSettingsService.upsert(geminiApiKey)`
- `WordPressSettingsService.getStatus()`
- `WordPressSettingsService.upsert({ siteUrl, wpUsername, applicationPassword, enabled })`
- `AuthService.deleteAccount()` via `useAuth()`
- `NoteService.getNotes()` / `getNotesByIds()`
- `NoteCreator.create()`

- New mobile-only service interfaces
- `MobileEnexImportService.importDocument(options): Promise<ImportResult>`
- `MobileEnexExportService.exportAllNotes(userId): Promise<{ fileUri: string; fileName: string; noteCount: number }>`
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

- Native integration contracts
- File import:
- `DocumentPicker.getDocumentAsync(...)` returns a picked `.enex` file URI.
- `FileSystem.readAsStringAsync(uri)` loads XML text.
- File export:
- `FileSystem.writeAsStringAsync(tempFileUri, xml)`
- `Sharing.shareAsync(tempFileUri, { mimeType: "application/xml" })`

- Authentication/authorization approach
- All settings and note operations continue to use the authenticated Supabase session supplied by `SupabaseProvider`.
- Import writes and export reads operate only on the current user's notes.

## Component Breakdown
**What are the major building blocks?**

- Frontend components
- `ui/mobile/app/(tabs)/settings.tsx`
- `ui/mobile/components/settings/SettingsTabBar.tsx`
- `ui/mobile/components/settings/SettingsTabButton.tsx`
- `ui/mobile/components/settings/SettingsPanelCard.tsx`
- `ui/mobile/components/settings/AccountSettingsPanel.tsx`
- `ui/mobile/components/settings/EnexImportPanel.tsx`
- `ui/mobile/components/settings/EnexExportPanel.tsx`
- `ui/mobile/components/settings/WordPressSettingsPanel.tsx`
- Updated `GeminiApiKeySection` or replacement inline `ApiKeysSettingsPanel.tsx`

- Services/modules
- `ui/mobile/services/enexImport.ts`
- `ui/mobile/services/enexExport.ts`
- Existing shared services for account/API/WordPress.

- Third-party/native dependencies
- Expo document picker for choosing import files.
- Expo file system for reading and writing ENEX files.
- Expo sharing for handing export files to the native share sheet.

## Design Decisions
**Why did we choose this approach?**

- Decision: Replace modal-first settings interactions with inline tab panels.
- Pros: closer to the provided design, fewer context switches, easier to compare sections.
- Cons: more state lives on the screen instead of being isolated in modals.

- Decision: Keep horizontal tabs scrollable instead of compressing labels.
- Pros: preserves readability of long labels like `Import .enex file`.
- Cons: requires careful spacing and active-state styling.

- Decision: Reuse backend/service contracts for WordPress and API keys.
- Pros: consistent validation and data model between web and mobile.
- Cons: mobile UI must mirror service error handling carefully.

- Decision: Build mobile-native ENEX import/export services instead of reusing browser-specific `core/enex` orchestrators.
- Pros: avoids DOM/File API mismatch in React Native.
- Cons: mobile service logic is partially duplicated around parsing/orchestration.

- Alternatives considered
- Keep current sectioned list and only remove `Soon` badges.
- Open separate modal screens for each setting.
- Send import/export to an external web page instead of supporting native file actions.

## Non-Functional Requirements
**How should the system perform?**

- Performance targets
- Tab switching should feel immediate (<100ms local state updates).
- Settings status loads should show feedback without blocking the whole screen.
- Export should keep the UI responsive while the archive is generated.
- Existing sign-out and theme toggling should remain immediate local interactions.

- Security requirements
- Do not expose stored API keys or WordPress passwords in plaintext.
- Preserve existing encrypted-storage behavior for remote credentials.
- Require explicit destructive confirmation before account deletion.

- Reliability/availability needs
- Errors from remote services should render inline and preserve user input.
- Import/export failures should produce actionable feedback instead of silent failure.
- If native sharing is unavailable, the user should receive a clear error message.
144 changes: 144 additions & 0 deletions docs/ai/implementation/feature-mobile-settings-menu.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
---
phase: implementation
title: Implementation Guide - Mobile Settings Menu
description: Implementation notes for the redesigned mobile settings screen with native settings actions.
---

# Implementation Guide

## Development Setup
**How do we get started?**

- Prerequisites and dependencies
- Root dependencies installed with `npm ci`.
- Mobile dependencies installed in `ui/mobile/`.
- Working Supabase configuration for the mobile app.
- Environment setup steps
- Confirm mobile app auth/session is working through `SupabaseProvider`.
- Add Expo-native file dependencies before implementing ENEX actions.
- Use the existing mobile theme provider and typography tokens.

## Code Structure
**How is the code organized?**

- Screen entry
- `ui/mobile/app/(tabs)/settings.tsx`
- Settings UI components
- `ui/mobile/components/settings/*`
- Mobile ENEX services
- `ui/mobile/services/enexImport.ts`
- `ui/mobile/services/enexExport.ts`
- Shared services reused
- `core/services/apiKeysSettings.ts`
- `core/services/wordpressSettings.ts`
- `core/services/notes.ts`
- `core/enex/enex-builder.ts`
- `core/enex/note-creator.ts`

## Implementation Notes
**Key technical details to remember:**

### Delivered implementation
- `ui/mobile/app/(tabs)/settings.tsx` now renders a card-based settings shell with a horizontally scrollable tab rail.
- New tab panels were added for:
- `My Account`
- `Import .enex file`
- `Export .enex file`
- `WordPress settings`
- `API Keys`
- The old `ComingSoon`-driven mobile settings entries are no longer used by this screen.
- Mobile-native ENEX helpers/services were added in:
- `ui/mobile/lib/enexMobile.ts`
- `ui/mobile/services/enexImport.ts`
- `ui/mobile/services/enexExport.ts`
- Shared UI primitives were extended so the common mobile `Button` now exposes `accessibilityRole="button"` for better accessibility/testability.

### Core Features
- Horizontal tabs
- Use a horizontally scrolling `ScrollView` for the tab rail.
- Keep labels readable; do not shrink text to fit the screen width.
- Active tab should render as a pill/card state similar to the screenshot.

- Account panel
- Show current user email in a dedicated card.
- Keep theme mode switching accessible inside this panel.
- Replace the old modal-only delete flow with an inline destructive confirmation section that matches the screenshot.

- API keys panel
- Reuse `ApiKeysSettingsService`.
- Keep current validation rules:
- empty key + unconfigured => error;
- empty key + configured => no-op/success message;
- non-empty key => save and clear field.

- WordPress panel
- Reuse `WordPressSettingsService`.
- Mirror current web validation:
- site URL and username required;
- initial setup requires application password;
- trailing slash trimmed from site URL;
- stored password remains valid when the field is left empty.

- ENEX import
- Pick a single `.enex` document from the device.
- Read file content as UTF-8 text.
- Parse note entries with a RN-safe XML parser.
- Preserve title, tags, timestamps, and note body HTML where possible.
- Create notes through `NoteCreator` using duplicate strategy `prefix`.

- ENEX export
- Read user notes through `NoteService`.
- Convert notes into `ExportNote[]` and build XML through `EnexBuilder`.
- Write the result to a temporary file and trigger native share/save flow.

### Patterns & Best Practices
- Keep panel state localized to each feature component.
- Prefer explicit `loading`, `saving`, `successMessage`, and `errorMessage` states over implicit status logic.
- Add small comments only where mobile/native behavior is non-obvious.
- Keep remote settings loading inside panel components so unopened tabs do not fetch on first render.

## Integration Points
**How do pieces connect?**

- `settings.tsx` owns selected tab state and composes all panels.
- Each remote settings panel uses the existing Supabase-backed service classes.
- ENEX services use the active mobile Supabase client and authenticated user from the provider layer.

## Error Handling
**How do we handle failures?**

- Render inline error banners inside each panel.
- Preserve user-entered form values after failed saves.
- Show a clear import/export error if:
- no file is selected;
- file parsing fails;
- native share/save is unavailable;
- note creation/export generation fails.

## Implementation Validation
**What was verified during implementation?**

- `npm --prefix ui/mobile run type-check`
- `npm --prefix ui/mobile run lint`
- `npx jest --runInBand tests/unit/mobileEnexHelpers.test.ts tests/integration/settingsScreen.test.tsx` in `ui/mobile/`
- `npx ai-devkit@latest lint --feature mobile-settings-menu`

## Environment Notes
**What setup details mattered in this workspace?**

- Root `npm ci` completed successfully.
- `ui/mobile` dependency bootstrap required a fallback from `npm ci` to `npm install` on Windows because the existing `node_modules` directory produced an `ENOTEMPTY` error during cleanup.

## Performance Considerations
**How do we keep it fast?**

- Do not eagerly load every remote panel on initial screen render.
- Load WordPress/API key status when their panel is shown or on explicit user action.
- Avoid re-rendering every panel on every input change by keeping panel logic isolated.

## Security Notes
**What security measures are in place?**

- Continue using existing encrypted backend storage for API keys and WordPress passwords.
- Do not echo stored secrets back into input fields.
- Guard destructive account deletion behind an explicit acknowledgement checkbox.
Loading
Loading