feat: save mobile note frontmatter
This commit is contained in:
@@ -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,
|
||||
|
||||
65
apps/mobile/src/mobileNoteFrontmatterSave.test.ts
Normal file
65
apps/mobile/src/mobileNoteFrontmatterSave.test.ts
Normal 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
|
||||
}
|
||||
41
apps/mobile/src/mobileNoteFrontmatterSave.ts
Normal file
41
apps/mobile/src/mobileNoteFrontmatterSave.ts
Normal 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`
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user