fix: remove daily note command surfaces
This commit is contained in:
@@ -134,24 +134,6 @@ export function resolveNewType({ typeName, vaultPath }: NewTypeParams): { entry:
|
||||
return { entry, content: `---\ntype: Type\n---\n` }
|
||||
}
|
||||
|
||||
export function todayDateString(): string {
|
||||
return new Date().toISOString().split('T')[0]
|
||||
}
|
||||
|
||||
export function buildDailyNoteContent(date: string): string {
|
||||
const lines = ['---', `title: ${date}`, 'type: Journal', `date: ${date}`, '---']
|
||||
return `${lines.join('\n')}\n\n## Intentions\n\n\n\n## Reflections\n\n`
|
||||
}
|
||||
|
||||
export function resolveDailyNote(date: string, vaultPath: string): { entry: VaultEntry; content: string } {
|
||||
const entry = buildNewEntry({ path: `${vaultPath}/${date}.md`, slug: date, title: date, type: 'Journal', status: null })
|
||||
return { entry, content: buildDailyNoteContent(date) }
|
||||
}
|
||||
|
||||
export function findDailyNote(entries: VaultEntry[], date: string): VaultEntry | undefined {
|
||||
return entries.find(e => e.filename === `${date}.md` && e.isA === 'Journal')
|
||||
}
|
||||
|
||||
/** Persist a newly created note to disk. Returns a Promise for error handling. */
|
||||
export function persistNewNote(path: string, content: string): Promise<void> {
|
||||
if (!isTauri()) return Promise.resolve()
|
||||
@@ -190,11 +172,12 @@ function persistOptimistic(path: string, content: string, cbs: PersistCallbacks)
|
||||
.catch(() => { cbs.onEnd?.(path); cbs.onFail(path) })
|
||||
}
|
||||
|
||||
type PersistFn = (resolved: { entry: VaultEntry; content: string }) => void
|
||||
type ResolvedNote = { entry: VaultEntry; content: string }
|
||||
type PersistFn = (resolved: ResolvedNote) => void
|
||||
|
||||
/** Optimistically open note, add entry to vault, and persist to disk. */
|
||||
function createAndPersist(
|
||||
resolved: { entry: VaultEntry; content: string },
|
||||
resolved: ResolvedNote,
|
||||
addFn: (e: VaultEntry) => void,
|
||||
openTab: (e: VaultEntry, c: string) => void,
|
||||
cbs: PersistCallbacks,
|
||||
@@ -204,16 +187,6 @@ function createAndPersist(
|
||||
persistOptimistic(resolved.entry.path, resolved.content, cbs)
|
||||
}
|
||||
|
||||
/** Open today's daily note: navigate to it if it exists, or create + persist a new one. */
|
||||
function openDailyNote(entries: VaultEntry[], selectNote: (e: VaultEntry) => void, persist: PersistFn, vaultPath: string): void {
|
||||
const date = todayDateString()
|
||||
const existing = findDailyNote(entries, date)
|
||||
const targetPath = existing?.path ?? `${vaultPath}/${date}.md`
|
||||
if (existing) selectNote(existing)
|
||||
else persist(resolveDailyNote(date, vaultPath))
|
||||
signalFocusEditor({ path: targetPath })
|
||||
}
|
||||
|
||||
interface ImmediateCreateDeps {
|
||||
entries: VaultEntry[]
|
||||
vaultPath: string
|
||||
@@ -228,6 +201,15 @@ interface ImmediateCreateRequest {
|
||||
type?: string
|
||||
}
|
||||
|
||||
interface ImmediateCreateQueueConfig {
|
||||
entries: VaultEntry[]
|
||||
vaultPath: string
|
||||
addEntry: (entry: VaultEntry) => void
|
||||
openTabWithContent: (entry: VaultEntry, content: string) => void
|
||||
trackUnsaved?: (path: string) => void
|
||||
markContentPending?: (path: string, content: string) => void
|
||||
}
|
||||
|
||||
/** Generate a unique untitled filename using a timestamp. */
|
||||
function generateUntitledFilename(entries: VaultEntry[], type: string, pendingSlugs?: Set<string>): string {
|
||||
const ts = Math.floor(Date.now() / 1000)
|
||||
@@ -262,6 +244,82 @@ function createNoteImmediate(deps: ImmediateCreateDeps, type?: string): void {
|
||||
signalFocusEditor({ path: entry.path, selectTitle: true })
|
||||
}
|
||||
|
||||
function useImmediateCreateQueue(config: ImmediateCreateQueueConfig): (type?: string) => void {
|
||||
const pendingSlugsRef = useRef<Set<string>>(new Set())
|
||||
const queuedImmediateCreatesRef = useRef<ImmediateCreateRequest[]>([])
|
||||
const immediateCreateLockedRef = useRef(false)
|
||||
const immediateCreateTimerRef = useRef<number | null>(null)
|
||||
const latestDepsRef = useRef<ImmediateCreateDeps | null>(null)
|
||||
|
||||
const syncDeps = useCallback(() => {
|
||||
latestDepsRef.current = {
|
||||
entries: config.entries,
|
||||
vaultPath: config.vaultPath,
|
||||
pendingSlugs: pendingSlugsRef.current,
|
||||
openTabWithContent: config.openTabWithContent,
|
||||
addEntry: config.addEntry,
|
||||
trackUnsaved: config.trackUnsaved,
|
||||
markContentPending: config.markContentPending,
|
||||
}
|
||||
}, [
|
||||
config.entries,
|
||||
config.vaultPath,
|
||||
config.openTabWithContent,
|
||||
config.addEntry,
|
||||
config.trackUnsaved,
|
||||
config.markContentPending,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
syncDeps()
|
||||
}, [syncDeps])
|
||||
|
||||
const executeRequest = useCallback((request: ImmediateCreateRequest) => {
|
||||
const deps = latestDepsRef.current
|
||||
if (!deps) return
|
||||
createNoteImmediate(deps, request.type)
|
||||
trackEvent('note_created', {
|
||||
has_type: request.type ? 1 : 0,
|
||||
creation_path: request.type ? 'type_section' : 'cmd_n',
|
||||
})
|
||||
}, [])
|
||||
|
||||
const scheduleQueuedBurst = useCallback(function scheduleQueuedBurst() {
|
||||
if (immediateCreateTimerRef.current !== null) return
|
||||
|
||||
immediateCreateTimerRef.current = window.setTimeout(() => {
|
||||
immediateCreateTimerRef.current = null
|
||||
const next = queuedImmediateCreatesRef.current.shift()
|
||||
if (!next) {
|
||||
immediateCreateLockedRef.current = false
|
||||
return
|
||||
}
|
||||
|
||||
executeRequest(next)
|
||||
scheduleQueuedBurst()
|
||||
}, RAPID_CREATE_NOTE_SETTLE_MS)
|
||||
}, [executeRequest])
|
||||
|
||||
useEffect(() => () => {
|
||||
if (immediateCreateTimerRef.current !== null) {
|
||||
window.clearTimeout(immediateCreateTimerRef.current)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return useCallback((type?: string) => {
|
||||
syncDeps()
|
||||
const request = { type }
|
||||
if (immediateCreateLockedRef.current) {
|
||||
queuedImmediateCreatesRef.current.push(request)
|
||||
return
|
||||
}
|
||||
|
||||
immediateCreateLockedRef.current = true
|
||||
executeRequest(request)
|
||||
scheduleQueuedBurst()
|
||||
}, [syncDeps, executeRequest, scheduleQueuedBurst])
|
||||
}
|
||||
|
||||
interface RelationshipCreateDeps {
|
||||
entries: VaultEntry[]
|
||||
vaultPath: string
|
||||
@@ -303,40 +361,17 @@ export interface NoteCreationConfig {
|
||||
|
||||
interface CreationTabDeps {
|
||||
openTabWithContent: (entry: VaultEntry, content: string) => void
|
||||
handleSelectNote: (entry: VaultEntry) => void
|
||||
}
|
||||
|
||||
export function useNoteCreation(config: NoteCreationConfig, tabDeps: CreationTabDeps) {
|
||||
const { addEntry, removeEntry, entries, setToastMessage, addPendingSave, removePendingSave } = config
|
||||
const { openTabWithContent, handleSelectNote } = tabDeps
|
||||
const { openTabWithContent } = tabDeps
|
||||
|
||||
const revertOptimisticNote = useCallback((path: string) => {
|
||||
removeEntry(path)
|
||||
setToastMessage('Failed to create note — disk write error')
|
||||
}, [removeEntry, setToastMessage])
|
||||
|
||||
const pendingSlugsRef = useRef<Set<string>>(new Set())
|
||||
const queuedImmediateCreatesRef = useRef<ImmediateCreateRequest[]>([])
|
||||
const immediateCreateLockedRef = useRef(false)
|
||||
const immediateCreateTimerRef = useRef<number | null>(null)
|
||||
const latestImmediateCreateDepsRef = useRef<ImmediateCreateDeps | null>(null)
|
||||
|
||||
const syncImmediateCreateDeps = useCallback(() => {
|
||||
latestImmediateCreateDepsRef.current = {
|
||||
entries,
|
||||
vaultPath: config.vaultPath,
|
||||
pendingSlugs: pendingSlugsRef.current,
|
||||
openTabWithContent,
|
||||
addEntry,
|
||||
trackUnsaved: config.trackUnsaved,
|
||||
markContentPending: config.markContentPending,
|
||||
}
|
||||
}, [entries, config.vaultPath, openTabWithContent, addEntry, config.trackUnsaved, config.markContentPending])
|
||||
|
||||
useEffect(() => {
|
||||
syncImmediateCreateDeps()
|
||||
}, [syncImmediateCreateDeps])
|
||||
|
||||
const persistNew: PersistFn = useCallback(
|
||||
(resolved) => createAndPersist(resolved, addEntry, openTabWithContent, {
|
||||
onFail: revertOptimisticNote,
|
||||
@@ -347,54 +382,21 @@ export function useNoteCreation(config: NoteCreationConfig, tabDeps: CreationTab
|
||||
[openTabWithContent, addEntry, revertOptimisticNote, addPendingSave, removePendingSave, config.onNewNotePersisted],
|
||||
)
|
||||
|
||||
const handleCreateNoteImmediate = useImmediateCreateQueue({
|
||||
entries,
|
||||
vaultPath: config.vaultPath,
|
||||
addEntry,
|
||||
openTabWithContent,
|
||||
trackUnsaved: config.trackUnsaved,
|
||||
markContentPending: config.markContentPending,
|
||||
})
|
||||
|
||||
const handleCreateNote = useCallback((title: string, type: string) => {
|
||||
const template = resolveTemplate({ entries, typeName: type })
|
||||
persistNew(resolveNewNote({ title, type, vaultPath: config.vaultPath, template }))
|
||||
trackEvent('note_created', { has_type: type !== 'Note' ? 1 : 0, creation_path: 'plus_button' })
|
||||
}, [entries, persistNew, config.vaultPath])
|
||||
|
||||
const executeImmediateCreateRequest = useCallback((request: ImmediateCreateRequest) => {
|
||||
const deps = latestImmediateCreateDepsRef.current
|
||||
if (!deps) return
|
||||
createNoteImmediate(deps, request.type)
|
||||
trackEvent('note_created', { has_type: request.type ? 1 : 0, creation_path: request.type ? 'type_section' : 'cmd_n' })
|
||||
}, [])
|
||||
|
||||
const continueImmediateCreateBurst = useCallback(function scheduleImmediateCreateBurst() {
|
||||
if (immediateCreateTimerRef.current !== null) return
|
||||
|
||||
immediateCreateTimerRef.current = window.setTimeout(() => {
|
||||
immediateCreateTimerRef.current = null
|
||||
const next = queuedImmediateCreatesRef.current.shift()
|
||||
if (!next) {
|
||||
immediateCreateLockedRef.current = false
|
||||
return
|
||||
}
|
||||
|
||||
executeImmediateCreateRequest(next)
|
||||
scheduleImmediateCreateBurst()
|
||||
}, RAPID_CREATE_NOTE_SETTLE_MS)
|
||||
}, [executeImmediateCreateRequest])
|
||||
|
||||
const handleCreateNoteImmediate = useCallback((type?: string) => {
|
||||
syncImmediateCreateDeps()
|
||||
const request = { type }
|
||||
if (immediateCreateLockedRef.current) {
|
||||
queuedImmediateCreatesRef.current.push(request)
|
||||
return
|
||||
}
|
||||
|
||||
immediateCreateLockedRef.current = true
|
||||
executeImmediateCreateRequest(request)
|
||||
continueImmediateCreateBurst()
|
||||
}, [syncImmediateCreateDeps, executeImmediateCreateRequest, continueImmediateCreateBurst])
|
||||
|
||||
useEffect(() => () => {
|
||||
if (immediateCreateTimerRef.current !== null) {
|
||||
window.clearTimeout(immediateCreateTimerRef.current)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleCreateNoteForRelationship = useCallback((title: string): Promise<boolean> => {
|
||||
createNoteForRelationship({
|
||||
entries, vaultPath: config.vaultPath, openTabWithContent, addEntry,
|
||||
@@ -403,8 +405,6 @@ export function useNoteCreation(config: NoteCreationConfig, tabDeps: CreationTab
|
||||
return Promise.resolve(true)
|
||||
}, [entries, openTabWithContent, addEntry, removeEntry, setToastMessage, config.vaultPath, config.onNewNotePersisted])
|
||||
|
||||
const handleOpenDailyNote = useCallback(() => openDailyNote(entries, handleSelectNote, persistNew, config.vaultPath), [entries, handleSelectNote, persistNew, config.vaultPath])
|
||||
|
||||
const handleCreateType = useCallback((typeName: string) => {
|
||||
persistNew(resolveNewType({ typeName, vaultPath: config.vaultPath }))
|
||||
trackEvent('type_created')
|
||||
@@ -422,7 +422,6 @@ export function useNoteCreation(config: NoteCreationConfig, tabDeps: CreationTab
|
||||
handleCreateNote,
|
||||
handleCreateNoteImmediate,
|
||||
handleCreateNoteForRelationship,
|
||||
handleOpenDailyNote,
|
||||
handleCreateType,
|
||||
createTypeEntrySilent,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user