Skip to content

Commit ceef0dd

Browse files
committed
fix: refresh code and notes after vault sync
1 parent f22f6ad commit ceef0dd

5 files changed

Lines changed: 180 additions & 2 deletions

File tree

src/renderer/components/notes/NotesEditorPane.vue

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
Pencil,
1414
Presentation,
1515
} from 'lucide-vue-next'
16+
import { shouldSyncSelectedNoteContent } from './editorSync'
1617
import { getTextStats } from './textStats'
1718
1819
const {
@@ -88,8 +89,12 @@ const name = computed({
8889
const editorContent = ref('')
8990
9091
watch(
91-
() => selectedNote.value?.id,
92-
() => {
92+
selectedNote,
93+
(nextNote, previousNote) => {
94+
if (!shouldSyncSelectedNoteContent(previousNote, nextNote)) {
95+
return
96+
}
97+
9398
editorContent.value = selectedNote.value?.content ?? ''
9499
},
95100
{ immediate: true },
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { shouldSyncSelectedNoteContent } from '../editorSync'
3+
4+
describe('shouldSyncSelectedNoteContent', () => {
5+
it('returns true when selected note content changes for the same note', () => {
6+
expect(
7+
shouldSyncSelectedNoteContent(
8+
{ id: 1, content: 'before' },
9+
{ id: 1, content: 'after' },
10+
),
11+
).toBe(true)
12+
})
13+
14+
it('returns true when selected note changes', () => {
15+
expect(
16+
shouldSyncSelectedNoteContent(
17+
{ id: 1, content: 'same' },
18+
{ id: 2, content: 'same' },
19+
),
20+
).toBe(true)
21+
})
22+
23+
it('returns false when note id and content are unchanged', () => {
24+
expect(
25+
shouldSyncSelectedNoteContent(
26+
{ id: 1, content: 'same' },
27+
{ id: 1, content: 'same' },
28+
),
29+
).toBe(false)
30+
})
31+
})
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
interface SyncableSelectedNote {
2+
id: number
3+
content: string
4+
}
5+
6+
export function shouldSyncSelectedNoteContent(
7+
previousNote: SyncableSelectedNote | null | undefined,
8+
nextNote: SyncableSelectedNote | null | undefined,
9+
): boolean {
10+
return (
11+
previousNote?.id !== nextNote?.id
12+
|| previousNote?.content !== nextNote?.content
13+
)
14+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
2+
import { ref } from 'vue'
3+
4+
async function setup(activeSpace: 'code' | 'notes' | 'tools' | null) {
5+
vi.resetModules()
6+
vi.useFakeTimers()
7+
8+
const ipcHandlers = new Map<string, (...args: any[]) => void>()
9+
const state = {
10+
snippetId: undefined,
11+
}
12+
const isCodeSpaceInitialized = ref(true)
13+
const isNotesSpaceInitialized = ref(true)
14+
const displayedSnippets = ref<{ id: number }[]>([])
15+
const getFolders = vi.fn(async () => undefined)
16+
const getSnippets = vi.fn(async () => undefined)
17+
const reloadMathFromDisk = vi.fn(async () => undefined)
18+
const getNoteFolders = vi.fn(async () => undefined)
19+
const getNotes = vi.fn(async () => undefined)
20+
const getNoteTags = vi.fn(async () => undefined)
21+
22+
vi.doMock('@/composables', () => ({
23+
useApp: () => ({
24+
state,
25+
highlightedFolderIds: ref(new Set<number>()),
26+
highlightedSnippetIds: ref(new Set<number>()),
27+
focusedSnippetId: ref<number | undefined>(),
28+
focusedFolderId: ref<number | undefined>(),
29+
isCodeSpaceInitialized,
30+
}),
31+
useFolders: () => ({
32+
selectFolder: vi.fn(),
33+
getFolders,
34+
}),
35+
useMathNotebook: () => ({
36+
reloadFromDisk: reloadMathFromDisk,
37+
}),
38+
useNoteFolders: () => ({
39+
getNoteFolders,
40+
}),
41+
useNotes: () => ({
42+
getNotes,
43+
hasBusyNoteContentUpdates: vi.fn(() => false),
44+
}),
45+
useNotesApp: () => ({
46+
isNotesSpaceInitialized,
47+
}),
48+
useNoteTags: () => ({
49+
getNoteTags,
50+
}),
51+
useSnippets: () => ({
52+
selectSnippet: vi.fn(),
53+
getSnippets,
54+
selectFirstSnippet: vi.fn(),
55+
displayedSnippets,
56+
}),
57+
useSnippetUpdate: () => ({
58+
hasBusyContentUpdates: vi.fn(() => false),
59+
}),
60+
useSonner: () => ({
61+
sonner: vi.fn(),
62+
}),
63+
useStorageMutation: () => ({
64+
shouldSkipStorageSyncRefresh: vi.fn(() => false),
65+
}),
66+
}))
67+
68+
vi.doMock('@/electron', () => ({
69+
i18n: {
70+
t: vi.fn((key: string) => key),
71+
},
72+
ipc: {
73+
on: vi.fn((channel: string, handler: (...args: any[]) => void) => {
74+
ipcHandlers.set(channel, handler)
75+
}),
76+
invoke: vi.fn(),
77+
},
78+
}))
79+
80+
vi.doMock('@/spaceDefinitions', () => ({
81+
getActiveSpaceId: vi.fn(() => activeSpace),
82+
}))
83+
84+
const { registerSystemListeners } = await import('../system')
85+
86+
registerSystemListeners()
87+
88+
return {
89+
getFolders,
90+
getNoteFolders,
91+
getNotes,
92+
getNoteTags,
93+
getSnippets,
94+
ipcHandlers,
95+
isCodeSpaceInitialized,
96+
isNotesSpaceInitialized,
97+
}
98+
}
99+
100+
beforeEach(() => {
101+
vi.clearAllMocks()
102+
})
103+
104+
afterEach(() => {
105+
vi.useRealTimers()
106+
})
107+
108+
describe('registerSystemListeners', () => {
109+
it('invalidates code and notes initialization after storage sync', async () => {
110+
const context = await setup('tools')
111+
112+
context.ipcHandlers.get('system:storage-synced')?.(undefined)
113+
await vi.advanceTimersByTimeAsync(300)
114+
115+
expect(context.isCodeSpaceInitialized.value).toBe(false)
116+
expect(context.isNotesSpaceInitialized.value).toBe(false)
117+
expect(context.getFolders).not.toHaveBeenCalled()
118+
expect(context.getSnippets).not.toHaveBeenCalled()
119+
expect(context.getNoteFolders).not.toHaveBeenCalled()
120+
expect(context.getNotes).not.toHaveBeenCalled()
121+
expect(context.getNoteTags).not.toHaveBeenCalled()
122+
})
123+
})

src/renderer/ipc/listeners/system.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
useMathNotebook,
55
useNoteFolders,
66
useNotes,
7+
useNotesApp,
78
useNoteTags,
89
useSnippets,
910
useSnippetUpdate,
@@ -16,6 +17,7 @@ import { repository } from '../../../../package.json'
1617

1718
const {
1819
state,
20+
isCodeSpaceInitialized,
1921
highlightedFolderIds,
2022
highlightedSnippetIds,
2123
focusedSnippetId,
@@ -27,6 +29,7 @@ const { selectSnippet, getSnippets, selectFirstSnippet, displayedSnippets }
2729
const { hasBusyContentUpdates } = useSnippetUpdate()
2830
const { shouldSkipStorageSyncRefresh } = useStorageMutation()
2931
const { reloadFromDisk: reloadMathFromDisk } = useMathNotebook()
32+
const { isNotesSpaceInitialized } = useNotesApp()
3033
const { getNoteFolders } = useNoteFolders()
3134
const { getNotes, hasBusyNoteContentUpdates } = useNotes()
3235
const { getNoteTags } = useNoteTags()
@@ -54,6 +57,8 @@ async function refreshCodeSpace() {
5457

5558
async function refreshAfterStorageSync() {
5659
const activeSpace = getActiveSpaceId()
60+
isCodeSpaceInitialized.value = false
61+
isNotesSpaceInitialized.value = false
5762

5863
switch (activeSpace) {
5964
case 'math':

0 commit comments

Comments
 (0)