feat: save mobile note frontmatter

This commit is contained in:
lucaronin
2026-05-05 10:32:10 +02:00
parent 6cf7b4b118
commit d08da1c7bf
4 changed files with 132 additions and 1 deletions

View File

@@ -1,6 +1,8 @@
import { demoNoteSources } from './demoData'
import type { MobileEditorDraft } from './mobileEditorDraft'
import { saveMobileEditorDraft } from './mobileEditorDraftSave'
import { saveMobileNoteFrontmatter } from './mobileNoteFrontmatterSave'
import type { WritableMobileNoteFrontmatter } from './mobileNoteFrontmatterWrite'
import { createMobileNoteFile } from './mobileNoteCreate'
import {
createMobileVaultConfigFromMetadata,
@@ -51,6 +53,23 @@ export async function deleteDemoVaultNote(noteId: string, vaultMetadata = defaul
}).deleteNote(noteId)
}
export function saveDemoVaultNoteFrontmatter({
metadata,
noteId,
vaultMetadata = defaultMobileVaultMetadata,
}: {
metadata: WritableMobileNoteFrontmatter
noteId: string
vaultMetadata?: MobileVaultMetadata
}) {
return saveMobileNoteFrontmatter({
metadata,
noteId,
storage: createNativeMobileVaultStorage(),
vault: createDemoVaultConfig(vaultMetadata),
})
}
function demoVaultFiles(): MobileVaultFile[] {
return demoNoteSources.map((source) => ({
path: source.filename,

View File

@@ -0,0 +1,65 @@
import { describe, expect, it } from 'vitest'
import { saveMobileNoteFrontmatter } from './mobileNoteFrontmatterSave'
import { createMobileVaultConfig } from './mobileVaultConfig'
import { createMemoryMobileVaultStorage } from './mobileVaultStorage'
const vault = createVault()
describe('mobile note frontmatter save', () => {
it('updates note frontmatter through vault storage', async () => {
const storage = createMemoryMobileVaultStorage([
{
path: 'notes/workflow.md',
content: '---\ntitle: Workflow\nprivate: true\n---\n# Workflow',
},
])
await expect(saveMobileNoteFrontmatter({
metadata: {
status: 'Draft',
tags: ['mobile'],
type: 'Project',
},
noteId: 'notes/workflow',
storage,
vault,
})).resolves.toEqual({
status: 'saved',
path: 'notes/workflow.md',
})
await expect(storage.readMarkdownFile(vault, 'notes/workflow.md')).resolves.toBe([
'---',
'title: Workflow',
'private: true',
'type: Project',
'status: Draft',
'tags: [mobile]',
'---',
'# Workflow',
].join('\n'))
})
it('returns missing when the note file does not exist', async () => {
const storage = createMemoryMobileVaultStorage([])
await expect(saveMobileNoteFrontmatter({
metadata: { type: 'Essay' },
noteId: 'missing',
storage,
vault,
})).resolves.toEqual({
status: 'missing',
path: 'missing.md',
})
})
})
function createVault() {
const result = createMobileVaultConfig({ id: 'personal', name: 'Personal Journal' })
if (!result.ok) {
throw new Error(result.error)
}
return result.config
}

View File

@@ -0,0 +1,41 @@
import type { MobileVaultConfig } from './mobileVaultConfig'
import type { MobileVaultStorageDriver } from './mobileVaultStorage'
import {
writeMobileNoteFrontmatter,
type WritableMobileNoteFrontmatter,
} from './mobileNoteFrontmatterWrite'
export type MobileNoteFrontmatterSaveResult =
| {
status: 'saved'
path: string
}
| {
status: 'missing'
path: string
}
export async function saveMobileNoteFrontmatter({
metadata,
noteId,
storage,
vault,
}: {
metadata: WritableMobileNoteFrontmatter
noteId: string
storage: MobileVaultStorageDriver
vault: MobileVaultConfig
}): Promise<MobileNoteFrontmatterSaveResult> {
const path = notePath(noteId)
const content = await storage.readMarkdownFile(vault, path)
if (content === null) {
return { status: 'missing', path }
}
await storage.writeMarkdownFile(vault, path, writeMobileNoteFrontmatter({ content, metadata }))
return { status: 'saved', path }
}
function notePath(noteId: string) {
return `${noteId}.md`
}

View File

@@ -77,12 +77,13 @@ This file is the resumable working log for Tolaria mobile. The strategy and road
- Deferred archive as a first-class mobile note state until the mobile vault schema/frontmatter model is explicit; implementing archive now as file movement would create throwaway semantics that may conflict with desktop-compatible metadata.
- Added mobile frontmatter metadata parsing for stored markdown notes, including type, icon, date, and inline tags, so app-local vault scans can project note metadata instead of hardcoding every stored note as a generic file.
- Added mobile frontmatter serialization helpers that can create/update supported type/status/date/icon/tags fields while preserving unknown metadata lines.
- Added a mobile frontmatter save boundary that updates persisted note metadata through the vault storage driver and exposes the same path through the demo vault facade for future properties UI calls.
## Next Action
Continue Phase 4 with editor durability:
1. Wire mobile property edits to the frontmatter read/write helpers for type/date/status/icon/tags.
1. Wire the mobile properties panel to the frontmatter save boundary for type/date/status/icon/tags.
2. Expand TenTap Markdown serialization coverage for common writing constructs and preserve unsupported blocks without corrupting files.
3. Add simulator interaction coverage for create/open/edit/autosave/delete using a development-client path or another route that avoids Expo Go's overlay controls.
@@ -271,6 +272,11 @@ Continue Phase 4 with editor durability:
- CodeScene after mobile frontmatter serialization helpers: `apps/mobile/src/mobileNoteFrontmatterWrite.ts` and `apps/mobile/src/mobileNoteFrontmatterWrite.test.ts` scored `10`.
- `pnpm --filter @tolaria/mobile test` passed after mobile frontmatter serialization helpers: 24 files / 78 tests.
- `pnpm --filter @tolaria/mobile exec expo export --platform ios --output-dir /tmp/tolaria-mobile-export` passed after mobile frontmatter serialization helpers.
- `pnpm --filter @tolaria/mobile test -- src/mobileNoteFrontmatterSave.test.ts src/mobileNoteFrontmatterWrite.test.ts` passed after mobile frontmatter save boundary: 25 files / 80 tests.
- `pnpm --filter @tolaria/mobile typecheck` passed after mobile frontmatter save boundary.
- CodeScene after mobile frontmatter save boundary: `apps/mobile/src/mobileNoteFrontmatterSave.ts`, `apps/mobile/src/mobileNoteFrontmatterSave.test.ts`, and `apps/mobile/src/mobileDemoVault.ts` scored `10`.
- `pnpm --filter @tolaria/mobile test` passed after mobile frontmatter save boundary: 25 files / 80 tests.
- `pnpm --filter @tolaria/mobile exec expo export --platform ios --output-dir /tmp/tolaria-mobile-export` passed after mobile frontmatter save boundary.
## Risks / Watch Items