From 20037ffb6071888b70318de3f3514f83ba54da9c Mon Sep 17 00:00:00 2001 From: Frederik Pytlick Date: Sun, 8 Oct 2023 22:41:18 +0200 Subject: [PATCH] #16 - Added actions to context menus --- package-lock.json | 38 +++ package.json | 2 + src/components/base/confirmModal.vue | 68 +++++ .../ContextMenu.vue => base/contextMenu.vue} | 0 src/components/files/FileBrowser.vue | 277 ------------------ src/components/item/Browser.vue | 191 ++++++++++++ src/components/item/file/EditModal.vue | 71 +++++ src/components/{files => item/file}/File.vue | 56 +++- .../{files => item/file}/NoFiles.vue | 0 src/components/item/folder/CreateModal.vue | 100 +++++++ src/components/item/folder/EditModal.vue | 105 +++++++ .../{files => item/folder}/Folder.vue | 58 +++- src/lang/da.ts | 12 + src/lang/en.ts | 12 + src/lib/items/files.ts | 46 +++ src/lib/items/folders.ts | 64 ++-- src/pages/u/folder/[id].astro | 4 +- src/pages/u/index.astro | 4 +- src/stores/items.ts | 16 + tsconfig.json | 3 +- 20 files changed, 806 insertions(+), 321 deletions(-) create mode 100644 src/components/base/confirmModal.vue rename src/components/{files/ContextMenu.vue => base/contextMenu.vue} (100%) delete mode 100644 src/components/files/FileBrowser.vue create mode 100644 src/components/item/Browser.vue create mode 100644 src/components/item/file/EditModal.vue rename src/components/{files => item/file}/File.vue (51%) rename src/components/{files => item/file}/NoFiles.vue (100%) create mode 100644 src/components/item/folder/CreateModal.vue create mode 100644 src/components/item/folder/EditModal.vue rename src/components/{files => item/folder}/Folder.vue (55%) create mode 100644 src/stores/items.ts diff --git a/package-lock.json b/package-lock.json index d18c15e..1a7c207 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@astrojs/ts-plugin": "^1.1.3", "@astrojs/vercel": "^5.0.1", "@astrojs/vue": "^3.0.0", + "@nanostores/vue": "^0.10.0", "@tauri-apps/api": "^1.5.0", "@vercel/blob": "^0.13.0", "@vueuse/core": "^10.4.1", @@ -23,6 +24,7 @@ "flowbite": "^1.8.1", "flowbite-typography": "^1.0.3", "jose": "^4.15.1", + "nanostores": "^0.9.3", "tailwind-scrollbar": "^3.0.5", "tailwindcss": "^3.3.3", "typescript": "^5.2.2", @@ -1247,6 +1249,28 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/@nanostores/vue": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@nanostores/vue/-/vue-0.10.0.tgz", + "integrity": "sha512-832RAUbzRfHPs1CdqVEwfvgB2+RD/INji4Zo8bUSEfRO2pQRMMeq479gydnohGpRaa0oNwlfKo7TGFXCghq/1g==", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@nanostores/logger": ">=0.2.3", + "@vue/devtools-api": ">=6.5.0", + "nanostores": ">=0.9.2", + "vue": ">=3.3.1" + }, + "peerDependenciesMeta": { + "@nanostores/logger": { + "optional": true + }, + "@vue/devtools-api": { + "optional": true + } + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -5965,6 +5989,20 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/nanostores": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/nanostores/-/nanostores-0.9.3.tgz", + "integrity": "sha512-KobZjcVyNndNrb5DAjfs0WG0lRcZu5Q1BOrfTOxokFLi25zFrWPjg+joXC6kuDqNfSt9fQwppyjUBkRPtsL+8w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "engines": { + "node": "^16.0.0 || ^18.0.0 || >=20.0.0" + } + }, "node_modules/napi-build-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", diff --git a/package.json b/package.json index b88e31e..8e23002 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "@astrojs/ts-plugin": "^1.1.3", "@astrojs/vercel": "^5.0.1", "@astrojs/vue": "^3.0.0", + "@nanostores/vue": "^0.10.0", "@tauri-apps/api": "^1.5.0", "@vercel/blob": "^0.13.0", "@vueuse/core": "^10.4.1", @@ -42,6 +43,7 @@ "flowbite": "^1.8.1", "flowbite-typography": "^1.0.3", "jose": "^4.15.1", + "nanostores": "^0.9.3", "tailwind-scrollbar": "^3.0.5", "tailwindcss": "^3.3.3", "typescript": "^5.2.2", diff --git a/src/components/base/confirmModal.vue b/src/components/base/confirmModal.vue new file mode 100644 index 0000000..0ce0c9b --- /dev/null +++ b/src/components/base/confirmModal.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/src/components/files/ContextMenu.vue b/src/components/base/contextMenu.vue similarity index 100% rename from src/components/files/ContextMenu.vue rename to src/components/base/contextMenu.vue diff --git a/src/components/files/FileBrowser.vue b/src/components/files/FileBrowser.vue deleted file mode 100644 index c793b71..0000000 --- a/src/components/files/FileBrowser.vue +++ /dev/null @@ -1,277 +0,0 @@ - - diff --git a/src/components/item/Browser.vue b/src/components/item/Browser.vue new file mode 100644 index 0000000..5f771ad --- /dev/null +++ b/src/components/item/Browser.vue @@ -0,0 +1,191 @@ + + diff --git a/src/components/item/file/EditModal.vue b/src/components/item/file/EditModal.vue new file mode 100644 index 0000000..3ae3f4d --- /dev/null +++ b/src/components/item/file/EditModal.vue @@ -0,0 +1,71 @@ + + + diff --git a/src/components/files/File.vue b/src/components/item/file/File.vue similarity index 51% rename from src/components/files/File.vue rename to src/components/item/file/File.vue index ddd515a..6e13352 100644 --- a/src/components/files/File.vue +++ b/src/components/item/file/File.vue @@ -1,5 +1,6 @@ diff --git a/src/components/files/NoFiles.vue b/src/components/item/file/NoFiles.vue similarity index 100% rename from src/components/files/NoFiles.vue rename to src/components/item/file/NoFiles.vue diff --git a/src/components/item/folder/CreateModal.vue b/src/components/item/folder/CreateModal.vue new file mode 100644 index 0000000..d40c325 --- /dev/null +++ b/src/components/item/folder/CreateModal.vue @@ -0,0 +1,100 @@ + + + diff --git a/src/components/item/folder/EditModal.vue b/src/components/item/folder/EditModal.vue new file mode 100644 index 0000000..6380203 --- /dev/null +++ b/src/components/item/folder/EditModal.vue @@ -0,0 +1,105 @@ + + + diff --git a/src/components/files/Folder.vue b/src/components/item/folder/Folder.vue similarity index 55% rename from src/components/files/Folder.vue rename to src/components/item/folder/Folder.vue index a1f16b3..66151b9 100644 --- a/src/components/files/Folder.vue +++ b/src/components/item/folder/Folder.vue @@ -1,5 +1,6 @@ @@ -53,19 +76,42 @@ import { type PropType, computed, ref } from 'vue'; import { FolderClass } from '@lib/items/folders'; import { url } from '@lib/helpers'; -import ContextMenu from './ContextMenu.vue'; +import ContextMenu from '@components/base/contextMenu.vue'; +import BaseConfirmModal, { ConfirmModalType } from '@components/base/confirmModal.vue'; +import EditFolderModal from './EditModal.vue'; +import { t } from '@lib/i18n'; +import { removeItem } from '@stores/items'; const folderContextMenu = ref>(); +defineEmits(['update:modelValue']); const props = defineProps({ modelValue: { type: Object as PropType, required: true, }, }); -defineEmits(['update:modelValue']); const classes = computed(() => { return props.modelValue.colorClass; }); + +const showDeleteModal = ref(false); +async function deleteFolder() { + try { + await props.modelValue.delete(); + + removeItem(props.modelValue); + + // TODO: Show success toast + } catch (e) { + console.error('Error: ' + e); + + // TODO: Show error toast + } + + showDeleteModal.value = false; +} + +const showEditModal = ref(false); diff --git a/src/lang/da.ts b/src/lang/da.ts index 2029975..fd1134b 100644 --- a/src/lang/da.ts +++ b/src/lang/da.ts @@ -46,11 +46,21 @@ export default { fileBrowser: { file: { uploadFile: 'Upload fil', + renameFile: 'Omdøb fil', + name: 'Navn', + delete: 'Slet', + rename: 'Omdøb', + areYouSureYouWantToDeleteThisFile: 'Er du sikker på, at du vil slette denne fil?', }, folder: { name: 'Navn', createFolder: 'Opret mappe', create: 'Opret', + edit: 'Rediger', + share: 'Del', + delete: 'Slet', + openInNewTab: 'Åben i ny fane', + areYouSureYouWantToDeleteThisFolder: 'Er du sikker på, at du vil slette denne mappe?', color: { name: 'Farve', required: 'Farve er påkrævet', @@ -88,4 +98,6 @@ export default { toggleThemeMode: 'Lys / mørk tilstand', }, }, + yesImSure: 'Ja, jeg er sikker', + noCancel: 'Nej, annuller', }; diff --git a/src/lang/en.ts b/src/lang/en.ts index f0b0230..40275fc 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -46,11 +46,21 @@ export default { fileBrowser: { file: { uploadFile: 'Upload file', + renameFile: 'Rename file', + name: 'Name', + delete: 'Delete', + rename: 'Rename', + areYouSureYouWantToDeleteThisFile: 'Are you sure you want to delete this file?', }, folder: { name: 'Name', createFolder: 'Create folder', create: 'Create', + edit: 'Edit', + share: 'Share', + delete: 'Delete', + openInNewTab: 'Open in new tab', + areYouSureYouWantToDeleteThisFolder: 'Are you sure you want to delete this folder?', color: { name: 'Color', required: 'Color is required', @@ -88,4 +98,6 @@ export default { toggleThemeMode: 'Light / dark mode', }, }, + yesImSure: 'Yes, I am sure', + noCancel: 'No, cancel', }; diff --git a/src/lib/items/files.ts b/src/lib/items/files.ts index 3303981..339f766 100644 --- a/src/lib/items/files.ts +++ b/src/lib/items/files.ts @@ -22,6 +22,52 @@ export class FileClass extends ItemClass { }); } + async update(input: { name: string }) { + const response = await fetch(api('blob'), { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + credentials: 'include', + body: JSON.stringify({ + id: this.id, + name: input.name, + }), + }); + + if (!response.ok) { + if (response.status >= 400 && response.status < 500) { + const json = await response.json(); + + throw new Error(json.error); + } + + throw new Error(await response.text()); + } + + return new FileClass(await response.json()); + } + + async delete() { + const response = await fetch(api('blob/' + this.id), { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + credentials: 'include', + }); + + if (!response.ok) { + if (response.status >= 400 && response.status < 500) { + const json = await response.json(); + + throw new Error(json.error); + } + + throw new Error(await response.text()); + } + } + get blobUrl() { return this._blobUrl; } diff --git a/src/lib/items/folders.ts b/src/lib/items/folders.ts index d3000c0..5a1e966 100644 --- a/src/lib/items/folders.ts +++ b/src/lib/items/folders.ts @@ -48,37 +48,51 @@ export class FolderClass extends ItemClass { return new FolderClass(returnedFolder); } - async delete() { - return await fetch(api('folder/' + this.id), { - method: 'DELETE', + async update(input: { name: string; color: FolderColor }) { + const response = await fetch(api('folder'), { + method: 'PUT', headers: { 'Content-Type': 'application/json', }, credentials: 'include', - }) - .then(async (response) => { - if (!response.ok) { - if (response.status >= 400 && response.status < 500) { - const json = await response.json(); + body: JSON.stringify({ + id: this.id, + name: input.name, + color: input.color, + }), + }); - throw new Error(json.error); - } + if (!response.ok) { + if (response.status >= 400 && response.status < 500) { + const json = await response.json(); - throw new Error(await response.text()); - } + throw new Error(json.error); + } - return response.json(); - }) - .then((data) => { - console.log('Success:', data); + throw new Error(await response.text()); + } - return true; - }) - .catch((error) => { - console.error('Error:', error); + return new FolderClass(await response.json()); + } - return false; - }); + async delete() { + const response = await fetch(api('folder/' + this.id), { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + credentials: 'include', + }); + + if (!response.ok) { + if (response.status >= 400 && response.status < 500) { + const json = await response.json(); + + throw new Error(json.error); + } + + throw new Error(await response.text()); + } } get color() { @@ -89,12 +103,6 @@ export class FolderClass extends ItemClass { return FolderColors[this._color]; } - private static randomColor() { - return Object.keys(FolderColors)[ - Math.floor(Math.random() * Object.keys(FolderColors).length) - ] as FolderColor; - } - static isFolder(object: any): object is FolderType { if (!ItemClass.isItem(object)) return false; diff --git a/src/pages/u/folder/[id].astro b/src/pages/u/folder/[id].astro index 13cf3bf..1dc8dea 100644 --- a/src/pages/u/folder/[id].astro +++ b/src/pages/u/folder/[id].astro @@ -1,6 +1,6 @@ --- import LayoutSidebar from '@layouts/LayoutSidebar.astro'; -import FileBrowser from '@components/files/FileBrowser.vue'; +import Browser from '@components/item/Browser.vue'; // @ts-ignore https://github.com/withastro/language-tools/issues/476 import { api, url } from '@lib/helpers'; import { FolderClass } from '@lib/items/folders'; @@ -37,6 +37,6 @@ const user = Astro.locals.user as User;
- +
diff --git a/src/pages/u/index.astro b/src/pages/u/index.astro index 7217976..9050989 100644 --- a/src/pages/u/index.astro +++ b/src/pages/u/index.astro @@ -1,6 +1,6 @@ --- import LayoutSidebar from '@layouts/LayoutSidebar.astro'; -import FileBrowser from '@components/files/FileBrowser.vue'; +import Browser from '@components/item/Browser.vue'; // We can safely cast this as we know that middleware would have already checked for a valid user const user = Astro.locals.user as User; @@ -8,6 +8,6 @@ const user = Astro.locals.user as User;
- +
diff --git a/src/stores/items.ts b/src/stores/items.ts new file mode 100644 index 0000000..f43a2ae --- /dev/null +++ b/src/stores/items.ts @@ -0,0 +1,16 @@ +import { map } from 'nanostores'; +import type { ItemClass } from '@lib/items/items'; + +export const itemsStore = map>({}); + +export function addItem(item: ItemClass) { + itemsStore.setKey(item.id, item); +} + +export function updateItem(item: ItemClass) { + itemsStore.setKey(item.id, item); +} + +export function removeItem(item: ItemClass) { + itemsStore.setKey(item.id, undefined); +} diff --git a/tsconfig.json b/tsconfig.json index a2b7fc2..e8aedb7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,8 @@ "paths": { "@components/*": ["src/components/*"], "@layouts/*": ["src/layouts/*"], - "@lib/*": ["src/lib/*"] + "@lib/*": ["src/lib/*"], + "@stores/*": ["src/stores/*"] }, "strictNullChecks": true, "verbatimModuleSyntax": true,