From 5afd1ca62a73d4bc9a143e7e7b93494fcca95ea8 Mon Sep 17 00:00:00 2001 From: lucaronin Date: Mon, 4 May 2026 00:42:40 +0200 Subject: [PATCH] refactor: separate mobile note projection --- apps/mobile/src/demoData.ts | 27 ++---------- apps/mobile/src/mobileNoteProjection.test.ts | 44 ++++++++++++++++++++ apps/mobile/src/mobileNoteProjection.ts | 42 +++++++++++++++++++ docs/MOBILE_PROGRESS.md | 7 +++- 4 files changed, 96 insertions(+), 24 deletions(-) create mode 100644 apps/mobile/src/mobileNoteProjection.test.ts create mode 100644 apps/mobile/src/mobileNoteProjection.ts diff --git a/apps/mobile/src/demoData.ts b/apps/mobile/src/demoData.ts index 911c34ad..31270846 100644 --- a/apps/mobile/src/demoData.ts +++ b/apps/mobile/src/demoData.ts @@ -1,19 +1,8 @@ -import { countWords, deriveDisplayTitleState, extractSnippet } from '@tolaria/markdown' +import { projectMobileNotes, type MobileNote, type MobileNoteSource } from './mobileNoteProjection' -export interface MobileNote { - id: string - type: string - icon: string - date: string - modified: string - content: string - title: string - snippet: string - words: number - tags: string[] -} +export type { MobileNote } from './mobileNoteProjection' -const noteContent = [ +const noteContent: MobileNoteSource[] = [ { id: 'workflow', type: 'Essay', @@ -76,15 +65,7 @@ const noteContent = [ }, ] -export const notes: MobileNote[] = noteContent.map((note) => { - const titleState = deriveDisplayTitleState({ content: note.content, filename: note.filename }) - return { - ...note, - title: titleState.title, - snippet: extractSnippet(note.content), - words: countWords(note.content), - } -}) +export const notes: MobileNote[] = projectMobileNotes(noteContent) export const sidebarSections = [ { diff --git a/apps/mobile/src/mobileNoteProjection.test.ts b/apps/mobile/src/mobileNoteProjection.test.ts new file mode 100644 index 00000000..0549f46f --- /dev/null +++ b/apps/mobile/src/mobileNoteProjection.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, it } from 'vitest' +import { projectMobileNote, projectMobileNotes, type MobileNoteSource } from './mobileNoteProjection' + +const sourceNote: MobileNoteSource = { + id: 'workflow', + type: 'Essay', + icon: 'pen-nib', + date: 'May 13, 2026', + modified: '6h ago', + filename: 'workflow-orchestration-kestra.md', + tags: ['Design Inspiration', 'Tolaria MVP'], + content: [ + '---', + 'title: Workflow Orchestration Essay', + 'type: Essay', + '---', + '', + '# Workflow Orchestration Essay', + '', + 'The current narrative / temptation is everything routed through an LLM.', + ].join('\n'), +} + +describe('mobile note projection', () => { + it('derives list and editor metadata from markdown content', () => { + const note = projectMobileNote(sourceNote) + + expect(note).toMatchObject({ + id: 'workflow', + title: 'Workflow Orchestration Essay', + snippet: 'The current narrative / temptation is everything routed through an LLM.', + words: 11, + }) + }) + + it('keeps projection order stable for note lists', () => { + const notes = projectMobileNotes([ + sourceNote, + { ...sourceNote, id: 'release', filename: 'release.md' }, + ]) + + expect(notes.map((note) => note.id)).toEqual(['workflow', 'release']) + }) +}) diff --git a/apps/mobile/src/mobileNoteProjection.ts b/apps/mobile/src/mobileNoteProjection.ts new file mode 100644 index 00000000..f1c61f2e --- /dev/null +++ b/apps/mobile/src/mobileNoteProjection.ts @@ -0,0 +1,42 @@ +import { countWords, deriveDisplayTitleState, extractSnippet } from '@tolaria/markdown' + +export type MobileNoteSource = { + id: string + type: string + icon: string + date: string + modified: string + filename: string + content: string + tags: string[] +} + +export type MobileNote = Omit & { + title: string + snippet: string + words: number +} + +export function projectMobileNote(source: MobileNoteSource): MobileNote { + const titleState = deriveDisplayTitleState({ + content: source.content, + filename: source.filename, + }) + + return { + id: source.id, + type: source.type, + icon: source.icon, + date: source.date, + modified: source.modified, + content: source.content, + tags: source.tags, + title: titleState.title, + snippet: extractSnippet(source.content), + words: countWords(source.content), + } +} + +export function projectMobileNotes(sources: MobileNoteSource[]): MobileNote[] { + return sources.map(projectMobileNote) +} diff --git a/docs/MOBILE_PROGRESS.md b/docs/MOBILE_PROGRESS.md index 4a1152f7..71d74c4d 100644 --- a/docs/MOBILE_PROGRESS.md +++ b/docs/MOBILE_PROGRESS.md @@ -8,7 +8,7 @@ This file is the resumable working log for Tolaria mobile. The strategy and road - Branch: `codex/mobile` - Active phase: Phase 2 - Mobile Shell -- Active slice: Prepare compact navigation for gesture-driven phone shell +- Active slice: Separate mobile note projection from fixtures - Push policy: commit locally; do not push unless explicitly requested - Validation target: iPad/iOS simulator first @@ -35,6 +35,7 @@ This file is the resumable working log for Tolaria mobile. The strategy and road - Split mobile styles into small panel-specific StyleSheet modules so new mobile code starts at CodeScene `10.0`. - Pinned Expo runtime dependencies to the versions expected by Expo 55 after the initial generated versions failed iOS bundle export (`react-native@0.83.6`, `react@19.2.0`, matching safe-area/svg versions). - Extracted compact phone navigation into a pure tested state machine so future swipe gestures can dispatch explicit events instead of mutating panels ad hoc. +- Extracted mobile note projection into a pure module that turns raw vault-like note records into the list/editor shape used by the mobile shell. ## Next Action @@ -85,6 +86,10 @@ Continue Phase 2 with the next mobile shell slice: - `pnpm --filter @tolaria/mobile typecheck` passed after compact navigation extraction. - CodeScene after compact navigation extraction: `apps/mobile/src/compactNavigation.ts`, `apps/mobile/src/compactNavigation.test.ts`, and the touched `apps/mobile/src/MobileApp.tsx` scored `10`. - `pnpm --filter @tolaria/mobile exec expo export --platform ios --output-dir /tmp/tolaria-mobile-export` passed after compact navigation extraction. +- `pnpm --filter @tolaria/mobile test` passed after note projection extraction: 3 files / 8 tests. +- `pnpm --filter @tolaria/mobile typecheck` passed after note projection extraction. +- CodeScene after note projection extraction: `apps/mobile/src/mobileNoteProjection.ts` and `apps/mobile/src/mobileNoteProjection.test.ts` scored `10`; `apps/mobile/src/demoData.ts` returned no scorable code and no findings. +- `pnpm --filter @tolaria/mobile exec expo export --platform ios --output-dir /tmp/tolaria-mobile-export` passed after note projection extraction. ## Risks / Watch Items