Repository navigation
Implement full mobile settings menu #72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
1cae5ae
Implement full mobile settings menu
koreyba 42ca5a4
fix(mobile): preserve settings tab state
koreyba 6a177d7
fix(mobile): address PR review feedback
koreyba 9e31c01
refactor(mobile): address sonar code smells
koreyba 69aeb5e
Update mobile adb connection scripts
koreyba 868bee6
Обсудить поведение импорта экспорта
koreyba 98a43f2
Fix mobile ENEX import
koreyba b8a852e
Fix mobile settings interactions
koreyba f3c299f
Add mobile duplicate handling
koreyba d58195b
Handle null title map lookup
koreyba 293d3e1
Merge remote-tracking branch 'origin/main' into features/settings-men…
koreyba 9747f33
Resolve PR 72 review comments
koreyba 332409f
Handle duplicate lookup errors
koreyba File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | ||
|
|
||
| - 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 }>` | ||
|
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. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.