Skip to content

Commit 9ad324d

Browse files
authored
feat(context): delete actions (#19)
1 parent 1ac229d commit 9ad324d

File tree

13 files changed

+689
-96
lines changed

13 files changed

+689
-96
lines changed

src/app/src/App.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ function detectActiveDocuments() {
2323
})
2424
}
2525
26-
function onContentSelect(id: string) {
27-
documentTree.selectItemById(id)
26+
async function onContentSelect(id: string) {
27+
await documentTree.selectItemById(id)
2828
ui.openPanel(StudioFeature.Content)
2929
}
3030
@@ -70,7 +70,7 @@ host.on.mounted(() => {
7070
size="lg"
7171
variant="outline"
7272
label="Edit This Page"
73-
class="shadow-lg"
73+
class="shadow-lg bg-white hover:bg-gray-100"
7474
@click="onContentSelect(activeDocuments[0].id)"
7575
/>
7676
</div>

src/app/src/components/modal/ModalConfirmAction.vue

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,27 @@ const emit = defineEmits<{ close: [] }>()
2727
2828
const titleMap = {
2929
[StudioItemActionId.RevertItem]: `Reverting ${name.value}`,
30+
[StudioItemActionId.DeleteItem]: `Deleting ${name.value}`,
3031
} as Record<StudioItemActionId, string>
3132
3233
const descriptionMap = {
3334
[StudioItemActionId.RevertItem]: `Are you sure you want to revert ${name.value} back to its original version?`,
35+
[StudioItemActionId.DeleteItem]: `Are you sure you want to delete ${name.value}?`,
3436
} as Record<StudioItemActionId, string>
3537
3638
const successLabelMap = {
3739
[StudioItemActionId.RevertItem]: 'Revert changes',
40+
[StudioItemActionId.DeleteItem]: 'Delete',
3841
} as Record<StudioItemActionId, string>
3942
4043
const successMessageMap = {
41-
[StudioItemActionId.RevertItem]: 'Changes reverted successfully!',
44+
[StudioItemActionId.RevertItem]: 'Revert successful!',
45+
[StudioItemActionId.DeleteItem]: 'Deletion successful!',
4246
} as Record<StudioItemActionId, string>
4347
4448
const errorMessageMap = {
4549
[StudioItemActionId.RevertItem]: 'Something went wrong while reverting your file.',
50+
[StudioItemActionId.DeleteItem]: 'Something went wrong while deleting your file.',
4651
} as Record<StudioItemActionId, string>
4752
4853
const handleConfirm = async () => {

src/app/src/composables/useContext.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type { useDraftDocuments } from './useDraftDocuments'
77
import { useModal } from './useModal'
88
import type { useTree } from './useTree'
99
import type { useDraftMedias } from './useDraftMedias'
10+
import { findDescendantsFileItemsFromId } from '../utils/tree'
1011

1112
export const useContext = createSharedComposable((
1213
host: StudioHost,
@@ -58,15 +59,14 @@ export const useContext = createSharedComposable((
5859
[StudioItemActionId.CreateDocument]: async ({ fsPath, routePath, content }: CreateFileParams) => {
5960
const document = await host.document.create(fsPath, routePath, content)
6061
const draftItem = await draft.value.create(document)
61-
tree.selectItemById(draftItem.id)
62+
await tree.selectItemById(draftItem.id)
6263
},
6364
[StudioItemActionId.UploadMedia]: async ({ directory, files }: UploadMediaParams) => {
6465
for (const file of files) {
6566
await (draft.value as ReturnType<typeof useDraftMedias>).upload(directory, file)
6667
}
6768
},
6869
[StudioItemActionId.RevertItem]: async (id: string) => {
69-
console.log('revert item', id)
7070
modal.openConfirmActionModal(id, StudioItemActionId.RevertItem, async () => {
7171
await draft.value.revert(id)
7272
})
@@ -75,7 +75,11 @@ export const useContext = createSharedComposable((
7575
alert(`rename file ${path} ${file.name}`)
7676
},
7777
[StudioItemActionId.DeleteItem]: async (id: string) => {
78-
alert(`delete file ${id}`)
78+
modal.openConfirmActionModal(id, StudioItemActionId.DeleteItem, async () => {
79+
const ids: string[] = findDescendantsFileItemsFromId(tree.root.value, id).map(item => item.id)
80+
await draft.value.remove(ids)
81+
await tree.selectParentById(id)
82+
})
7983
},
8084
[StudioItemActionId.DuplicateItem]: async (id: string) => {
8185
alert(`duplicate file ${id}`)

src/app/src/composables/useDraftDocuments.ts

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -88,56 +88,62 @@ export const useDraftDocuments = createSharedComposable((host: StudioHost, git:
8888
return existingItem
8989
}
9090

91-
async function remove(id: string) {
92-
const item = await storage.getItem(id) as DraftItem<DatabaseItem>
93-
const fsPath = host.document.getFileSystemPath(id)
94-
95-
if (item) {
96-
if (item.status === DraftStatus.Deleted) return
91+
async function remove(ids: string[]) {
92+
for (const id of ids) {
93+
const existingDraftItem = list.value.find(item => item.id === id)
94+
const fsPath = host.document.getFileSystemPath(id)
95+
const originalDbItem = await host.document.get(id)
9796

9897
await storage.removeItem(id)
9998
await host.document.delete(id)
10099

101-
if (item.original) {
102-
const deleteDraft: DraftItem<DatabaseItem> = {
100+
let deleteDraftItem: DraftItem<DatabaseItem> | null = null
101+
if (existingDraftItem) {
102+
if (existingDraftItem.status === DraftStatus.Deleted) return
103+
104+
if (existingDraftItem.status === DraftStatus.Created) {
105+
list.value = list.value.filter(item => item.id !== id)
106+
}
107+
else {
108+
deleteDraftItem = {
109+
id,
110+
fsPath: existingDraftItem.fsPath,
111+
status: DraftStatus.Deleted,
112+
original: existingDraftItem.original,
113+
githubFile: existingDraftItem.githubFile,
114+
}
115+
116+
list.value = list.value.map(item => item.id === id ? deleteDraftItem! : item)
117+
}
118+
}
119+
else {
120+
// TODO: check if gh file has been updated
121+
const githubFile = await git.fetchFile(fsPath, { cached: true }) as GithubFile
122+
123+
deleteDraftItem = {
103124
id,
104-
fsPath: item.fsPath,
125+
fsPath,
105126
status: DraftStatus.Deleted,
106-
original: item.original,
107-
githubFile: item.githubFile,
127+
original: originalDbItem,
128+
githubFile,
108129
}
109130

110-
await storage.setItem(id, deleteDraft)
111-
await host.document.upsert(id, item.original!)
131+
list.value.push(deleteDraftItem)
112132
}
113-
}
114-
else {
115-
// Fetch github file before creating draft to detect non deployed changes
116-
const githubFile = await git.fetchFile(fsPath, { cached: true }) as GithubFile
117-
const original = await host.document.get(id)
118-
119-
const deleteItem: DraftItem = {
120-
id,
121-
fsPath,
122-
status: DraftStatus.Deleted,
123-
original,
124-
githubFile,
133+
134+
if (deleteDraftItem) {
135+
await storage.setItem(id, deleteDraftItem)
125136
}
126137

127-
await storage.setItem(id, deleteItem)
138+
host.app.requestRerender()
128139

129-
await host.document.delete(id)
140+
await hooks.callHook('studio:draft:document:updated')
130141
}
131-
132-
list.value = list.value.filter(item => item.id !== id)
133-
host.app.requestRerender()
134142
}
135143

136144
async function revert(id: string) {
137145
const draftItems = findDescendantsFromId(list.value, id)
138146

139-
console.log('draftItems', draftItems)
140-
141147
for (const draftItem of draftItems) {
142148
const existingItem = list.value.find(item => item.id === draftItem.id)
143149
if (!existingItem) {
@@ -206,6 +212,11 @@ export const useDraftDocuments = createSharedComposable((host: StudioHost, git:
206212
}
207213

208214
function select(draftItem: DraftItem<DatabaseItem> | null) {
215+
// TODO: Handle editor with deleted file
216+
if (draftItem?.status === DraftStatus.Deleted) {
217+
return
218+
}
219+
209220
current.value = draftItem
210221
}
211222

src/app/src/composables/useDraftMedias.ts

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -82,36 +82,38 @@ export const useDraftMedias = createSharedComposable((host: StudioHost, git: Ret
8282
return existingItem
8383
}
8484

85-
async function remove(id: string) {
86-
const item = await storage.getItem(id) as DraftItem
87-
const fsPath = host.media.getFileSystemPath(id)
85+
async function remove(ids: string[]) {
86+
for (const id of ids) {
87+
const item = await storage.getItem(id) as DraftItem
88+
const fsPath = host.media.getFileSystemPath(id)
8889

89-
if (item) {
90-
if (item.status === DraftStatus.Deleted) return
90+
if (item) {
91+
if (item.status === DraftStatus.Deleted) return
9192

92-
await storage.removeItem(id)
93-
await host.media.delete(id)
94-
}
95-
else {
93+
await storage.removeItem(id)
94+
await host.media.delete(id)
95+
}
96+
else {
9697
// Fetch github file before creating draft to detect non deployed changes
97-
const githubFile = await git.fetchFile(fsPath, { cached: true }) as GithubFile
98-
const original = await host.media.get(id)
98+
const githubFile = await git.fetchFile(fsPath, { cached: true }) as GithubFile
99+
const original = await host.media.get(id)
100+
101+
const deleteItem: DraftItem = {
102+
id,
103+
fsPath,
104+
status: DraftStatus.Deleted,
105+
original,
106+
githubFile,
107+
}
99108

100-
const deleteItem: DraftItem = {
101-
id,
102-
fsPath,
103-
status: DraftStatus.Deleted,
104-
original,
105-
githubFile,
106-
}
109+
await storage.setItem(id, deleteItem)
107110

108-
await storage.setItem(id, deleteItem)
111+
await host.media.delete(id)
112+
}
109113

110-
await host.media.delete(id)
114+
list.value = list.value.filter(item => item.id !== id)
115+
host.app.requestRerender()
111116
}
112-
113-
list.value = list.value.filter(item => item.id !== id)
114-
host.app.requestRerender()
115117
}
116118

117119
async function revert(id: string) {

src/app/src/composables/useStudio.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ export const useStudio = createSharedComposable(() => {
3737
host.app.requestRerender()
3838
isReady.value = true
3939

40-
host.on.routeChange((to: RouteLocationNormalized, _from: RouteLocationNormalized) => {
40+
host.on.routeChange(async (to: RouteLocationNormalized, _from: RouteLocationNormalized) => {
4141
if (ui.isPanelOpen.value && ui.config.value.syncEditorAndRoute) {
42-
documentTree.selectByRoute(to)
42+
await documentTree.selectByRoute(to)
4343
}
4444
// setTimeout(() => {
4545
// host.document.detectActives()

src/app/src/composables/useTree.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { StudioFeature, type StudioHost, type TreeItem } from '../types'
22
import { ref, computed } from 'vue'
33
import type { useDraftDocuments } from './useDraftDocuments'
44
import type { useDraftMedias } from './useDraftMedias'
5-
import { buildTree, findItemFromId, findItemFromRoute, ROOT_ITEM } from '../utils/tree'
5+
import { buildTree, findItemFromId, findItemFromRoute, ROOT_ITEM, findParentFromId } from '../utils/tree'
66
import type { RouteLocationNormalized } from 'vue-router'
77
import { useHooks } from './useHooks'
88

@@ -56,15 +56,22 @@ export const useTree = (type: StudioFeature, host: StudioHost, draft: ReturnType
5656

5757
if (!item || item.id === currentItem.value.id) return
5858

59-
select(item)
59+
await select(item)
6060
}
6161

6262
async function selectItemById(id: string) {
6363
const treeItem = findItemFromId(tree.value, id)
6464

6565
if (!treeItem || treeItem.id === currentItem.value.id) return
6666

67-
select(treeItem)
67+
await select(treeItem)
68+
}
69+
70+
async function selectParentById(id: string) {
71+
const parent = findParentFromId(tree.value, id)
72+
if (parent) {
73+
await select(parent)
74+
}
6875
}
6976

7077
async function handleDraftUpdate() {
@@ -102,5 +109,6 @@ export const useTree = (type: StudioFeature, host: StudioHost, draft: ReturnType
102109
select,
103110
selectByRoute,
104111
selectItemById,
112+
selectParentById,
105113
}
106114
}

src/app/src/utils/context.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,18 @@ export const STUDIO_ITEM_ACTION_DEFINITIONS: StudioAction[] = [
3636
icon: 'i-lucide-pencil',
3737
tooltip: 'Rename file',
3838
},
39-
{
40-
id: StudioItemActionId.DeleteItem,
41-
label: 'Delete',
42-
icon: 'i-lucide-trash',
43-
tooltip: 'Delete file',
44-
},
4539
{
4640
id: StudioItemActionId.DuplicateItem,
4741
label: 'Duplicate',
4842
icon: 'i-lucide-copy',
4943
tooltip: 'Duplicate file',
5044
},
45+
{
46+
id: StudioItemActionId.DeleteItem,
47+
label: 'Delete',
48+
icon: 'i-lucide-trash',
49+
tooltip: 'Delete file',
50+
},
5151
] as const
5252

5353
export function computeActionItems(itemActions: StudioAction[], item?: TreeItem | null): StudioAction[] {
@@ -93,6 +93,7 @@ export function computeActionItems(itemActions: StudioAction[], item?: TreeItem
9393
export function computeActionParams(action: StudioItemActionId, { item }: { item: TreeItem }): ActionHandlerParams[typeof action] {
9494
switch (action) {
9595
case StudioItemActionId.RevertItem:
96+
case StudioItemActionId.DeleteItem:
9697
return item.id
9798
default:
9899
return {}

src/app/src/utils/draft.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export const COLOR_STATUS_MAP: { [key in DraftStatus]?: string } = {
1313
export const COLOR_UI_STATUS_MAP: { [key in DraftStatus]?: string } = {
1414
[DraftStatus.Created]: 'success',
1515
[DraftStatus.Updated]: 'warning',
16-
[DraftStatus.Deleted]: 'danger',
16+
[DraftStatus.Deleted]: 'error',
1717
[DraftStatus.Renamed]: 'info',
1818
[DraftStatus.Opened]: 'neutral',
1919
} as const

0 commit comments

Comments
 (0)