Skip to content

Commit 965131f

Browse files
committed
refactor getDraftStatus
1 parent b69f2ba commit 965131f

File tree

7 files changed

+107
-101
lines changed

7 files changed

+107
-101
lines changed

src/app/src/composables/useDraftBase.ts

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import type { Storage } from 'unstorage'
22
import { joinURL } from 'ufo'
3-
import type { DraftItem, StudioHost, GithubFile, DatabaseItem, MediaItem } from '../types'
3+
import type { DraftItem, StudioHost, GithubFile, DatabaseItem, MediaItem, BaseItem } from '../types'
4+
import { ContentFileExtension } from '../types'
5+
import { studioFlags } from './useStudio'
46
import { DraftStatus } from '../types/draft'
5-
import { checkConflict, findDescendantsFromFsPath, getDraftStatus } from '../utils/draft'
7+
import { checkConflict, findDescendantsFromFsPath } from '../utils/draft'
68
import type { useGit } from './useGit'
79
import { useHooks } from './useHooks'
810
import { ref } from 'vue'
@@ -20,6 +22,7 @@ export function useDraftBase<T extends DatabaseItem | MediaItem>(
2022
const ghPathPrefix = type === 'media' ? 'public' : 'content'
2123
const hostDb = type === 'media' ? host.media : host.document.db
2224
const hookName = `studio:draft:${type}:updated` as const
25+
const areDocumentsEqual = host.document.utils.areEqual
2326

2427
const hooks = useHooks()
2528

@@ -38,7 +41,7 @@ export function useDraftBase<T extends DatabaseItem | MediaItem>(
3841
const draftItem: DraftItem<T> = {
3942
fsPath,
4043
githubFile,
41-
status: getDraftStatus(item, original!, host.document.utils.areEqual),
44+
status: getStatus(item, original!),
4245
modified: item,
4346
}
4447

@@ -135,7 +138,7 @@ export function useDraftBase<T extends DatabaseItem | MediaItem>(
135138
// @ts-expect-error upsert type is wrong, second param should be DatabaseItem | MediaItem
136139
await hostDb.upsert(draftItem.fsPath, existingItem.original)
137140
existingItem.modified = existingItem.original
138-
existingItem.status = getDraftStatus(existingItem.modified as DatabaseItem, existingItem.original as DatabaseItem, host.document.utils.areEqual)
141+
existingItem.status = getStatus(existingItem.modified as DatabaseItem, existingItem.original as DatabaseItem)
139142
await storage.setItem(draftItem.fsPath, existingItem)
140143
}
141144
}
@@ -211,6 +214,42 @@ export function useDraftBase<T extends DatabaseItem | MediaItem>(
211214
await hooks.callHook(hookName, { caller: 'useDraftBase.load', selectItem: false })
212215
}
213216

217+
function getStatus(modified: BaseItem, original: BaseItem): DraftStatus {
218+
if (studioFlags.dev) {
219+
return DraftStatus.Pristine
220+
}
221+
222+
if (!modified && !original) {
223+
throw new Error('Unconsistent state: both modified and original are undefined')
224+
}
225+
226+
if (!modified) {
227+
return DraftStatus.Deleted
228+
}
229+
230+
if (!original || original.id !== modified.id) {
231+
return DraftStatus.Created
232+
}
233+
234+
if (original.extension === ContentFileExtension.Markdown) {
235+
if (!areDocumentsEqual(original as DatabaseItem, modified as DatabaseItem)) {
236+
return DraftStatus.Updated
237+
}
238+
}
239+
else if (typeof original === 'object' && typeof modified === 'object') {
240+
if (!areDocumentsEqual(original as DatabaseItem, modified as DatabaseItem)) {
241+
return DraftStatus.Updated
242+
}
243+
}
244+
else {
245+
if (JSON.stringify(original) !== JSON.stringify(modified)) {
246+
return DraftStatus.Updated
247+
}
248+
}
249+
250+
return DraftStatus.Pristine
251+
}
252+
214253
return {
215254
isLoading,
216255
list,
@@ -224,5 +263,6 @@ export function useDraftBase<T extends DatabaseItem | MediaItem>(
224263
unselect,
225264
load,
226265
checkConflict,
266+
getStatus,
227267
}
228268
}

src/app/src/composables/useDraftDocuments.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type { DatabaseItem, DraftItem, StudioHost, RawFile } from '../types'
22
import { DraftStatus } from '../types/draft'
33
import type { useGit } from './useGit'
4-
import { getDraftStatus } from '../utils/draft'
54
import { createSharedComposable } from '@vueuse/core'
65
import { useHooks } from './useHooks'
76
import { joinURL } from 'ufo'
@@ -22,6 +21,7 @@ export const useDraftDocuments = createSharedComposable((host: StudioHost, git:
2221
selectByFsPath,
2322
unselect,
2423
load,
24+
getStatus,
2525
} = useDraftBase<DatabaseItem>('document', host, git, storage)
2626

2727
const hooks = useHooks()
@@ -35,7 +35,7 @@ export const useDraftDocuments = createSharedComposable((host: StudioHost, git:
3535
}
3636

3737
const oldStatus = existingItem.status
38-
existingItem.status = getDraftStatus(document, existingItem.original as DatabaseItem, host.document.utils.areEqual)
38+
existingItem.status = getStatus(document, existingItem.original as DatabaseItem)
3939
existingItem.modified = document
4040

4141
await storage.setItem(fsPath, existingItem)
@@ -145,5 +145,6 @@ export const useDraftDocuments = createSharedComposable((host: StudioHost, git:
145145
load,
146146
selectByFsPath,
147147
unselect,
148+
getStatus,
148149
}
149150
})

src/app/src/composables/useDraftMedias.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const useDraftMedias = createSharedComposable((host: StudioHost, git: Ret
2424
selectByFsPath,
2525
unselect,
2626
load,
27+
getStatus,
2728
} = useDraftBase('media', host, git, storage)
2829

2930
async function upload(parentFsPath: string, file: File) {
@@ -131,5 +132,6 @@ export const useDraftMedias = createSharedComposable((host: StudioHost, git: Ret
131132
unselect,
132133
upload,
133134
listAsRawFiles,
135+
getStatus,
134136
}
135137
})

src/app/src/composables/useStudio.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import type { StudioHost, GitOptions, DatabaseItem } from '../types'
1111
import { StudioFeature } from '../types'
1212
import { documentStorage, mediaStorage, nullStorageDriver } from '../utils/storage'
1313
import { useHooks } from './useHooks'
14-
import { getDraftStatus } from '../utils/draft'
1514
import { useStudioState } from './useStudioState'
1615

1716
export const studioFlags = {
@@ -44,7 +43,7 @@ export const useStudio = createSharedComposable(() => {
4443

4544
host.on.mounted(async () => {
4645
if (studioFlags.dev) {
47-
initDevelopmentMode(host, draftDocuments, draftMedias, documentTree, mediaTree)
46+
initDevelopmentMode(host, documentTree, mediaTree)
4847
}
4948

5049
await draftDocuments.load()
@@ -81,20 +80,19 @@ export const useStudio = createSharedComposable(() => {
8180
}
8281
})
8382

84-
function initDevelopmentMode(host: StudioHost, draftDocuments: ReturnType<typeof useDraftDocuments>, draftMedias: ReturnType<typeof useDraftMedias>, documentTree: ReturnType<typeof useTree>, mediaTree: ReturnType<typeof useTree>) {
83+
function initDevelopmentMode(host: StudioHost, documentTree: ReturnType<typeof useTree>, mediaTree: ReturnType<typeof useTree>) {
8584
const hooks = useHooks()
86-
const areDocumentsEqual = host.document.utils.areEqual
8785

8886
// Disable browser storages
8987
documentStorage.mount('/', nullStorageDriver)
9088
mediaStorage.mount('/', nullStorageDriver)
9189

9290
host.on.documentUpdate(async (fsPath: string, type: 'remove' | 'update') => {
93-
const item = draftDocuments.list.value.find(item => item.fsPath === fsPath)
91+
const item = documentTree.draft.list.value.find(item => item.fsPath === fsPath)
9492

9593
if (type === 'remove') {
9694
if (item) {
97-
await draftDocuments.remove([fsPath])
95+
await documentTree.draft.remove([fsPath])
9896
}
9997
}
10098
else if (item) {
@@ -103,7 +101,7 @@ function initDevelopmentMode(host: StudioHost, draftDocuments: ReturnType<typeof
103101
const document = await host.document.db.get(fsPath)
104102
item.modified = document
105103
item.original = document
106-
item.status = getDraftStatus(document as DatabaseItem, item.original as DatabaseItem, areDocumentsEqual)
104+
item.status = mediaTree.draft.getStatus(document as DatabaseItem, item.original as DatabaseItem)
107105
item.version = item.version ? item.version + 1 : 1
108106
}
109107
}
@@ -112,19 +110,19 @@ function initDevelopmentMode(host: StudioHost, draftDocuments: ReturnType<typeof
112110
})
113111

114112
host.on.mediaUpdate(async (fsPath: string, type: 'remove' | 'update') => {
115-
const item = draftMedias.list.value.find(item => item.fsPath === fsPath)
113+
const item = mediaTree.draft.list.value.find(item => item.fsPath === fsPath)
116114

117115
if (type === 'remove') {
118116
if (item) {
119-
await draftMedias.remove([fsPath])
117+
await mediaTree.draft.remove([fsPath])
120118
}
121119
}
122120
else if (item) {
123121
if (!window.document.hasFocus() || mediaTree.currentItem.value?.fsPath !== fsPath) {
124122
const media = await host.media.get(fsPath)
125123
item.modified = media
126124
item.original = media
127-
item.status = getDraftStatus(media, item.original, areDocumentsEqual)
125+
item.status = mediaTree.draft.getStatus(media, item.original)
128126
item.version = item.version ? item.version + 1 : 1
129127
}
130128
}

src/app/src/utils/draft.ts

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import type { DatabaseItem, MediaItem, DraftItem, BaseItem, ContentConflict, StudioHost } from '../types'
2-
import { DraftStatus, ContentFileExtension } from '../types'
3-
import { studioFlags } from '../composables/useStudio'
1+
import type { DatabaseItem, MediaItem, DraftItem, ContentConflict, StudioHost } from '../types'
2+
import { DraftStatus } from '../types'
43
import { fromBase64ToUTF8 } from '../utils/string'
54
import { isMediaFile } from './file'
65

@@ -45,42 +44,6 @@ export async function checkConflict(host: StudioHost, draftItem: DraftItem<Datab
4544
}
4645
}
4746

48-
export function getDraftStatus(modified: BaseItem, original: BaseItem, comparisonMethod: (org: DatabaseItem, mod: DatabaseItem) => boolean): DraftStatus {
49-
if (studioFlags.dev) {
50-
return DraftStatus.Pristine
51-
}
52-
53-
if (!modified && !original) {
54-
throw new Error('Unconsistent state: both modified and original are undefined')
55-
}
56-
57-
if (!modified) {
58-
return DraftStatus.Deleted
59-
}
60-
61-
if (!original || original.id !== modified.id) {
62-
return DraftStatus.Created
63-
}
64-
65-
if (original.extension === ContentFileExtension.Markdown) {
66-
if (!comparisonMethod(original as DatabaseItem, modified as DatabaseItem)) {
67-
return DraftStatus.Updated
68-
}
69-
}
70-
else if (typeof original === 'object' && typeof modified === 'object') {
71-
if (!comparisonMethod(original as DatabaseItem, modified as DatabaseItem)) {
72-
return DraftStatus.Updated
73-
}
74-
}
75-
else {
76-
if (JSON.stringify(original) !== JSON.stringify(modified)) {
77-
return DraftStatus.Updated
78-
}
79-
}
80-
81-
return DraftStatus.Pristine
82-
}
83-
8447
export function findDescendantsFromFsPath(list: DraftItem[], fsPath: string): DraftItem[] {
8548
if (fsPath === '/') {
8649
return list
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { describe, it, expect } from 'vitest'
2+
import { useDraftBase } from '../../../src/composables/useDraftBase'
3+
import { dbItemsList } from '../../mocks/database'
4+
import { DraftStatus } from '../../../src/types'
5+
import { createMockHost } from '../../mocks/host'
6+
7+
const { getStatus } = useDraftBase('document', createMockHost(), null as never, null as never)
8+
9+
describe('getStatus', () => {
10+
it('returns Deleted status when modified item is undefined', () => {
11+
const original = dbItemsList[0] // landing/index.md
12+
13+
expect(getStatus(undefined as never, original)).toBe(DraftStatus.Deleted)
14+
})
15+
16+
it('returns Created status when original is undefined', () => {
17+
const modified = dbItemsList[1] // docs/1.getting-started/2.introduction.md
18+
19+
expect(getStatus(modified, undefined as never)).toBe(DraftStatus.Created)
20+
})
21+
22+
it('returns Created status when original has different id', () => {
23+
const original = dbItemsList[0] // landing/index.md
24+
const modified = dbItemsList[1] // docs/1.getting-started/2.introduction.md
25+
26+
expect(getStatus(modified, original)).toBe(DraftStatus.Created)
27+
})
28+
29+
it('returns Updated status when markdown content is different', () => {
30+
const original = dbItemsList[1] // docs/1.getting-started/2.introduction.md
31+
const modified = {
32+
...original,
33+
body: {
34+
type: 'minimark',
35+
value: ['text', 'Modified'],
36+
},
37+
}
38+
39+
expect(getStatus(modified, original)).toBe(DraftStatus.Updated)
40+
})
41+
42+
it('returns Pristine status when markdown content is identical', () => {
43+
const original = dbItemsList[1] // docs/1.getting-started/2.introduction.md
44+
45+
expect(getStatus(original, original)).toBe(DraftStatus.Pristine)
46+
})
47+
})
Lines changed: 1 addition & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import { describe, it, expect } from 'vitest'
2-
import { findDescendantsFromFsPath, getDraftStatus } from '../../../src/utils/draft'
2+
import { findDescendantsFromFsPath } from '../../../src/utils/draft'
33
import { draftItemsList } from '../../../test/mocks/draft'
4-
import { dbItemsList } from '../../../test/mocks/database'
5-
import type { DatabaseItem } from '../../../src/types'
6-
import { DraftStatus } from '../../../src/types'
74

85
describe('findDescendantsFromFsPath', () => {
96
it('returns exact match for a root level file', () => {
@@ -51,45 +48,3 @@ describe('findDescendantsFromFsPath', () => {
5148
expect(descendants[0].fsPath).toBe('1.getting-started/1.advanced/1.studio.md')
5249
})
5350
})
54-
55-
describe('getDraftStatus', () => {
56-
const areDocumentsEqual = (document1: DatabaseItem, document2: DatabaseItem) => JSON.stringify(document1) === JSON.stringify(document2)
57-
58-
it('returns Deleted status when modified item is undefined', () => {
59-
const original = dbItemsList[0] // landing/index.md
60-
61-
expect(getDraftStatus(undefined as never, original, areDocumentsEqual)).toBe(DraftStatus.Deleted)
62-
})
63-
64-
it('returns Created status when original is undefined', () => {
65-
const modified = dbItemsList[1] // docs/1.getting-started/2.introduction.md
66-
67-
expect(getDraftStatus(modified, undefined as never, areDocumentsEqual)).toBe(DraftStatus.Created)
68-
})
69-
70-
it('returns Created status when original has different id', () => {
71-
const original = dbItemsList[0] // landing/index.md
72-
const modified = dbItemsList[1] // docs/1.getting-started/2.introduction.md
73-
74-
expect(getDraftStatus(modified, original, areDocumentsEqual)).toBe(DraftStatus.Created)
75-
})
76-
77-
it('returns Updated status when markdown content is different', () => {
78-
const original = dbItemsList[1] // docs/1.getting-started/2.introduction.md
79-
const modified = {
80-
...original,
81-
body: {
82-
type: 'minimark',
83-
value: ['text', 'Modified'],
84-
},
85-
}
86-
87-
expect(getDraftStatus(modified, original, () => false)).toBe(DraftStatus.Updated)
88-
})
89-
90-
it('returns Pristine status when markdown content is identical', () => {
91-
const original = dbItemsList[1] // docs/1.getting-started/2.introduction.md
92-
93-
expect(getDraftStatus(original, original, areDocumentsEqual)).toBe(DraftStatus.Pristine)
94-
})
95-
})

0 commit comments

Comments
 (0)