feat: create local mobile notes
This commit is contained in:
@@ -18,7 +18,7 @@ import {
|
||||
SlidersHorizontal,
|
||||
} from 'phosphor-react-native'
|
||||
import { MobileNote, notes as fallbackNotes, sidebarSections } from './demoData'
|
||||
import { loadDemoVaultNotes, saveDemoVaultDraft } from './mobileDemoVault'
|
||||
import { createDemoVaultNote, loadDemoVaultNotes, saveDemoVaultDraft } from './mobileDemoVault'
|
||||
import { createMobileAutosaveQueue } from './mobileAutosaveQueue'
|
||||
import type { MobileEditorDraft } from './mobileEditorDraft'
|
||||
import {
|
||||
@@ -88,6 +88,18 @@ export function MobileApp() {
|
||||
setCompactNavigation((state) => transitionCompactNavigation(state, { type: 'selectNote', noteId: note.id }))
|
||||
}
|
||||
const saveDraft = useCallback((draft: MobileEditorDraft) => autosaveQueue.enqueue(draft), [autosaveQueue])
|
||||
const createNote = useCallback(() => {
|
||||
void createDemoVaultNote()
|
||||
.then((note) => {
|
||||
if (!note) {
|
||||
return
|
||||
}
|
||||
|
||||
setAvailableNotes((notes) => [note, ...notes.filter((item) => item.id !== note.id)])
|
||||
setCompactNavigation((state) => transitionCompactNavigation(state, { type: 'selectNote', noteId: note.id }))
|
||||
})
|
||||
.catch(() => {})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<SafeAreaProvider>
|
||||
@@ -98,6 +110,7 @@ export function MobileApp() {
|
||||
<NoteListPanel
|
||||
notes={availableNotes}
|
||||
selectedNoteId={compactNavigation.selectedNoteId}
|
||||
onCreateNote={createNote}
|
||||
onSelectNote={selectNote}
|
||||
/>
|
||||
<EditorPanel note={selectedNote} saveState={selectedSaveState} onDraftChange={saveDraft} />
|
||||
@@ -112,6 +125,7 @@ export function MobileApp() {
|
||||
selectedNoteId={compactNavigation.selectedNoteId}
|
||||
onNavigate={(event) => setCompactNavigation((state) => transitionCompactNavigation(state, event))}
|
||||
onDraftChange={saveDraft}
|
||||
onCreateNote={createNote}
|
||||
onSelectNote={selectNote}
|
||||
/>
|
||||
)}
|
||||
@@ -127,6 +141,7 @@ function CompactShell({
|
||||
saveState,
|
||||
onNavigate,
|
||||
onDraftChange,
|
||||
onCreateNote,
|
||||
onSelectNote,
|
||||
selectedNoteId,
|
||||
}: {
|
||||
@@ -136,6 +151,7 @@ function CompactShell({
|
||||
saveState: MobileEditorSaveState
|
||||
onNavigate: (event: CompactNavigationEvent) => void
|
||||
onDraftChange: (draft: MobileEditorDraft) => void
|
||||
onCreateNote: () => void
|
||||
onSelectNote: (note: MobileNote) => void
|
||||
selectedNoteId: string
|
||||
}) {
|
||||
@@ -174,6 +190,7 @@ function CompactShell({
|
||||
<NoteListPanel
|
||||
notes={notes}
|
||||
selectedNoteId={selectedNoteId}
|
||||
onCreateNote={onCreateNote}
|
||||
onOpenSidebar={() => onNavigate({ type: 'openSidebar' })}
|
||||
onSelectNote={onSelectNote}
|
||||
/>
|
||||
@@ -216,11 +233,13 @@ function SidebarPanel({ onClose }: { onClose?: () => void }) {
|
||||
|
||||
function NoteListPanel({
|
||||
notes,
|
||||
onCreateNote,
|
||||
onOpenSidebar,
|
||||
onSelectNote,
|
||||
selectedNoteId,
|
||||
}: {
|
||||
notes: MobileNote[]
|
||||
onCreateNote: () => void
|
||||
onOpenSidebar?: () => void
|
||||
onSelectNote: (note: MobileNote) => void
|
||||
selectedNoteId: string
|
||||
@@ -261,7 +280,7 @@ function NoteListPanel({
|
||||
</Pressable>
|
||||
)}
|
||||
/>
|
||||
<Pressable style={styles.composeButton}>
|
||||
<Pressable onPress={onCreateNote} style={styles.composeButton}>
|
||||
<PencilSimple size={28} color="#ffffff" />
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { demoNoteSources } from './demoData'
|
||||
import type { MobileEditorDraft } from './mobileEditorDraft'
|
||||
import { saveMobileEditorDraft } from './mobileEditorDraftSave'
|
||||
import { createMobileNoteFile } from './mobileNoteCreate'
|
||||
import { createMobileVaultConfig } from './mobileVaultConfig'
|
||||
import { createNativeMobileVaultStorage } from './mobileNativeVaultStorage'
|
||||
import { createStoredMobileVaultRepository } from './mobileVaultRepository'
|
||||
@@ -24,6 +25,14 @@ export function saveDemoVaultDraft(draft: MobileEditorDraft) {
|
||||
})
|
||||
}
|
||||
|
||||
export async function createDemoVaultNote() {
|
||||
const storage = createNativeMobileVaultStorage()
|
||||
const file = createMobileNoteFile()
|
||||
await storage.writeMarkdownFile(demoVault, file.path, file.content)
|
||||
|
||||
return createStoredMobileVaultRepository({ storage, vault: demoVault }).readNote(file.path.replace(/\.md$/, ''))
|
||||
}
|
||||
|
||||
function demoVaultFiles(): MobileVaultFile[] {
|
||||
return demoNoteSources.map((source) => ({
|
||||
path: source.filename,
|
||||
|
||||
34
apps/mobile/src/mobileNoteCreate.test.ts
Normal file
34
apps/mobile/src/mobileNoteCreate.test.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { createMobileNoteFile } from './mobileNoteCreate'
|
||||
|
||||
describe('mobile note create', () => {
|
||||
it('creates a deterministic markdown file from the current time', () => {
|
||||
const file = createMobileNoteFile({
|
||||
now: new Date('2026-05-04T12:34:56.000Z'),
|
||||
})
|
||||
|
||||
expect(file).toEqual({
|
||||
path: 'note-mor6mem8.md',
|
||||
content: [
|
||||
'---',
|
||||
'title: Untitled',
|
||||
'type: Note',
|
||||
'created: 2026-05-04T12:34:56.000Z',
|
||||
'---',
|
||||
'',
|
||||
'# Untitled',
|
||||
'',
|
||||
].join('\n'),
|
||||
})
|
||||
})
|
||||
|
||||
it('supports a custom title for future create flows', () => {
|
||||
const file = createMobileNoteFile({
|
||||
now: new Date('2026-05-04T12:34:56.000Z'),
|
||||
title: 'Meeting notes',
|
||||
})
|
||||
|
||||
expect(file.content).toContain('title: Meeting notes')
|
||||
expect(file.content).toContain('# Meeting notes')
|
||||
})
|
||||
})
|
||||
25
apps/mobile/src/mobileNoteCreate.ts
Normal file
25
apps/mobile/src/mobileNoteCreate.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { MobileVaultFile } from './mobileVaultStorage'
|
||||
|
||||
export function createMobileNoteFile({
|
||||
now = new Date(),
|
||||
title = 'Untitled',
|
||||
}: {
|
||||
now?: Date
|
||||
title?: string
|
||||
} = {}): MobileVaultFile {
|
||||
const id = `note-${now.getTime().toString(36)}`
|
||||
|
||||
return {
|
||||
path: `${id}.md`,
|
||||
content: [
|
||||
'---',
|
||||
`title: ${title}`,
|
||||
'type: Note',
|
||||
`created: ${now.toISOString()}`,
|
||||
'---',
|
||||
'',
|
||||
`# ${title}`,
|
||||
'',
|
||||
].join('\n'),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user