From 7dba1cf4c6e886c3341429cca62724c7f94fa39a Mon Sep 17 00:00:00 2001 From: static Date: Sat, 25 Jan 2025 22:30:06 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B8=B0=EC=A1=B4=EC=97=90=20=EC=A0=9C?= =?UTF-8?q?=EC=9E=91=EB=90=9C=20=EB=AA=A8=EB=8B=AC=EB=93=A4=EC=9D=84=20Act?= =?UTF-8?q?ionModal=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B8=B0?= =?UTF-8?q?=EB=B0=98=EC=9C=BC=EB=A1=9C=20=EC=9E=AC=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/components/BottomSheet.svelte | 7 +- src/lib/components/atoms/Modal.svelte | 2 +- .../modals/CategoryCreateModal.svelte | 6 +- src/lib/modules/util.ts | 5 + .../file/[id]/AddToCategoryBottomSheet.svelte | 2 +- .../key/export/BeforeContinueModal.svelte | 27 ++-- .../(main)/category/[[id]]/+page.svelte | 44 +++--- .../[[id]]/CategoryDeleteModal.svelte | 29 ++++ .../[[id]]/CategoryMenuBottomSheet.svelte | 58 +++---- .../[[id]]/CategoryRenameModal.svelte | 17 ++ .../[[id]]/DeleteCategoryModal.svelte | 47 ------ .../[[id]]/{service.ts => service.svelte.ts} | 12 ++ .../(main)/directory/[[id]]/+page.svelte | 148 +++++++++--------- .../[[id]]/CreateDirectoryModal.svelte | 28 ---- .../[[id]]/DeleteDirectoryEntryModal.svelte | 55 ------- .../[[id]]/DirectoryCreateModal.svelte | 18 +++ .../DirectoryEntries/DirectoryEntries.svelte | 6 +- .../[[id]]/DirectoryEntries/File.svelte | 6 +- .../DirectoryEntries/SubDirectory.svelte | 6 +- .../DirectoryEntryMenuBottomSheet.svelte | 62 -------- .../[[id]]/DuplicateFileModal.svelte | 38 +++-- ...t.svelte => EntryCreateBottomSheet.svelte} | 4 +- .../directory/[[id]]/EntryDeleteModal.svelte | 33 ++++ .../[[id]]/EntryMenuBottomSheet.svelte | 52 ++++++ .../directory/[[id]]/EntryRenameModal.svelte | 17 ++ .../[[id]]/{service.ts => service.svelte.ts} | 25 ++- 26 files changed, 367 insertions(+), 387 deletions(-) create mode 100644 src/routes/(main)/category/[[id]]/CategoryDeleteModal.svelte create mode 100644 src/routes/(main)/category/[[id]]/CategoryRenameModal.svelte delete mode 100644 src/routes/(main)/category/[[id]]/DeleteCategoryModal.svelte rename src/routes/(main)/category/[[id]]/{service.ts => service.svelte.ts} (73%) delete mode 100644 src/routes/(main)/directory/[[id]]/CreateDirectoryModal.svelte delete mode 100644 src/routes/(main)/directory/[[id]]/DeleteDirectoryEntryModal.svelte create mode 100644 src/routes/(main)/directory/[[id]]/DirectoryCreateModal.svelte delete mode 100644 src/routes/(main)/directory/[[id]]/DirectoryEntryMenuBottomSheet.svelte rename src/routes/(main)/directory/[[id]]/{CreateBottomSheet.svelte => EntryCreateBottomSheet.svelte} (91%) create mode 100644 src/routes/(main)/directory/[[id]]/EntryDeleteModal.svelte create mode 100644 src/routes/(main)/directory/[[id]]/EntryMenuBottomSheet.svelte create mode 100644 src/routes/(main)/directory/[[id]]/EntryRenameModal.svelte rename src/routes/(main)/directory/[[id]]/{service.ts => service.svelte.ts} (83%) diff --git a/src/lib/components/BottomSheet.svelte b/src/lib/components/BottomSheet.svelte index 0ee943a..9fdfe4b 100644 --- a/src/lib/components/BottomSheet.svelte +++ b/src/lib/components/BottomSheet.svelte @@ -23,13 +23,16 @@
-
+
e.stopPropagation()} class="flex max-h-[70vh] min-h-[30vh] overflow-y-auto rounded-t-2xl bg-white px-4" - transition:fly={{ y: 100, duration: 200 }} + transition:fly|global={{ y: 100, duration: 200 }} > {@render children?.()}
diff --git a/src/lib/components/atoms/Modal.svelte b/src/lib/components/atoms/Modal.svelte index edfce1f..2d100d4 100644 --- a/src/lib/components/atoms/Modal.svelte +++ b/src/lib/components/atoms/Modal.svelte @@ -20,7 +20,7 @@
(isOpen = false))} class="fixed inset-0 z-10 bg-black bg-opacity-50" - transition:fade={{ duration: 100 }} + transition:fade|global={{ duration: 100 }} >
diff --git a/src/lib/components/organisms/modals/CategoryCreateModal.svelte b/src/lib/components/organisms/modals/CategoryCreateModal.svelte index 1e2ed55..279c1a1 100644 --- a/src/lib/components/organisms/modals/CategoryCreateModal.svelte +++ b/src/lib/components/organisms/modals/CategoryCreateModal.svelte @@ -3,10 +3,10 @@ interface Props { isOpen: boolean; - oncreate: (name: string) => Promise; + onCreateClick: (name: string) => Promise; } - let { isOpen = $bindable(), oncreate }: Props = $props(); + let { isOpen = $bindable(), onCreateClick }: Props = $props(); diff --git a/src/lib/modules/util.ts b/src/lib/modules/util.ts index 0048e9e..3fff89d 100644 --- a/src/lib/modules/util.ts +++ b/src/lib/modules/util.ts @@ -28,6 +28,11 @@ export const formatNetworkSpeed = (speed: number) => { return `${(speed / 1000 / 1000 / 1000).toFixed(1)} Gbps`; }; +export const truncateString = (str: string, maxLength = 20) => { + if (str.length <= maxLength) return str; + return `${str.slice(0, maxLength)}...`; +}; + export enum SortBy { NAME_ASC, NAME_DESC, diff --git a/src/routes/(fullscreen)/file/[id]/AddToCategoryBottomSheet.svelte b/src/routes/(fullscreen)/file/[id]/AddToCategoryBottomSheet.svelte index ad3ecb4..619bf8e 100644 --- a/src/routes/(fullscreen)/file/[id]/AddToCategoryBottomSheet.svelte +++ b/src/routes/(fullscreen)/file/[id]/AddToCategoryBottomSheet.svelte @@ -50,7 +50,7 @@ { + onCreateClick={async (name: string) => { if (await requestCategoryCreation(name, $category!.id, $masterKeyStore?.get(1)!)) { category = getCategoryInfo($category!.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME return true; diff --git a/src/routes/(fullscreen)/key/export/BeforeContinueModal.svelte b/src/routes/(fullscreen)/key/export/BeforeContinueModal.svelte index d1ea9c5..2f00b32 100644 --- a/src/routes/(fullscreen)/key/export/BeforeContinueModal.svelte +++ b/src/routes/(fullscreen)/key/export/BeforeContinueModal.svelte @@ -1,23 +1,20 @@ - -
-
-

내보내지 않고 계속할까요?

-

암호 키 파일은 유출 방지를 위해 이 화면에서만 저장할 수 있어요.

-
-
- - -
-
-
+ +

암호 키 파일은 유출 방지를 위해 이 화면에서만 저장할 수 있어요.

+
diff --git a/src/routes/(main)/category/[[id]]/+page.svelte b/src/routes/(main)/category/[[id]]/+page.svelte index dc701ba..81d7524 100644 --- a/src/routes/(main)/category/[[id]]/+page.svelte +++ b/src/routes/(main)/category/[[id]]/+page.svelte @@ -2,31 +2,32 @@ import type { Writable } from "svelte/store"; import { goto } from "$app/navigation"; import { TopBar } from "$lib/components"; - import { CategoryCreateModal, RenameModal } from "$lib/components/organisms"; + import { CategoryCreateModal } from "$lib/components/organisms"; import { getCategoryInfo, type CategoryInfo } from "$lib/modules/filesystem"; - import type { SelectedCategory } from "$lib/molecules/Categories"; import Category from "$lib/organisms/Category"; import { masterKeyStore } from "$lib/stores"; + import CategoryDeleteModal from "./CategoryDeleteModal.svelte"; import CategoryMenuBottomSheet from "./CategoryMenuBottomSheet.svelte"; - import DeleteCategoryModal from "./DeleteCategoryModal.svelte"; + import CategoryRenameModal from "./CategoryRenameModal.svelte"; import { + createContext, requestCategoryCreation, requestFileRemovalFromCategory, requestCategoryRename, requestCategoryDeletion, - } from "./service"; + } from "./service.svelte"; let { data } = $props(); + let context = createContext(); let info: Writable | undefined = $state(); - let selectedSubCategory: SelectedCategory | undefined = $state(); let isFileRecursive = $state(false); let isCategoryCreateModalOpen = $state(false); - let isSubCategoryMenuBottomSheetOpen = $state(false); + let isCategoryMenuBottomSheetOpen = $state(false); let isCategoryRenameModalOpen = $state(false); - let isDeleteCategoryModalOpen = $state(false); + let isCategoryDeleteModalOpen = $state(false); $effect(() => { info = getCategoryInfo(data.id, $masterKeyStore?.get(1)?.key!); @@ -54,8 +55,8 @@ onSubCategoryClick={({ id }) => goto(`/category/${id}`)} onSubCategoryCreateClick={() => (isCategoryCreateModalOpen = true)} onSubCategoryMenuClick={(subCategory) => { - selectedSubCategory = subCategory; - isSubCategoryMenuBottomSheetOpen = true; + context.selectedCategory = subCategory; + isCategoryMenuBottomSheetOpen = true; }} /> {/if} @@ -64,7 +65,7 @@ { + onCreateClick={async (name: string) => { if (await requestCategoryCreation(name, data.id, $masterKeyStore?.get(1)!)) { info = getCategoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME return true; @@ -74,35 +75,30 @@ /> { - isSubCategoryMenuBottomSheetOpen = false; + isCategoryMenuBottomSheetOpen = false; isCategoryRenameModalOpen = true; }} onDeleteClick={() => { - isSubCategoryMenuBottomSheetOpen = false; - isDeleteCategoryModalOpen = true; + isCategoryMenuBottomSheetOpen = false; + isCategoryDeleteModalOpen = true; }} /> - (selectedSubCategory = undefined)} - originalName={selectedSubCategory?.name} onRenameClick={async (newName: string) => { - if (await requestCategoryRename(selectedSubCategory!, newName)) { + if (await requestCategoryRename(context.selectedCategory!, newName)) { info = getCategoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME return true; } return false; }} /> - { - if (selectedSubCategory) { - await requestCategoryDeletion(selectedSubCategory); + if (await requestCategoryDeletion(context.selectedCategory!)) { info = getCategoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME return true; } diff --git a/src/routes/(main)/category/[[id]]/CategoryDeleteModal.svelte b/src/routes/(main)/category/[[id]]/CategoryDeleteModal.svelte new file mode 100644 index 0000000..5e792d1 --- /dev/null +++ b/src/routes/(main)/category/[[id]]/CategoryDeleteModal.svelte @@ -0,0 +1,29 @@ + + +{#if context.selectedCategory} + {@const { name } = context.selectedCategory} + +

+ 모든 하위 카테고리도 함께 삭제돼요.
+ 하지만 카테고리에 추가된 파일들은 삭제되지 않아요. +

+
+{/if} diff --git a/src/routes/(main)/category/[[id]]/CategoryMenuBottomSheet.svelte b/src/routes/(main)/category/[[id]]/CategoryMenuBottomSheet.svelte index c71fdff..9e96fba 100644 --- a/src/routes/(main)/category/[[id]]/CategoryMenuBottomSheet.svelte +++ b/src/routes/(main)/category/[[id]]/CategoryMenuBottomSheet.svelte @@ -1,36 +1,26 @@ - -
- {#if selectedCategory} - {@const { name } = selectedCategory} +{#if context.selectedCategory} + {@const { name } = context.selectedCategory} + +
@@ -40,18 +30,18 @@

- {/if} - -
- -

이름 바꾸기

-
-
- -
- -

삭제하기

-
-
-
- + +
+ +

이름 바꾸기

+
+
+ +
+ +

삭제하기

+
+
+
+
+{/if} diff --git a/src/routes/(main)/category/[[id]]/CategoryRenameModal.svelte b/src/routes/(main)/category/[[id]]/CategoryRenameModal.svelte new file mode 100644 index 0000000..a3ce045 --- /dev/null +++ b/src/routes/(main)/category/[[id]]/CategoryRenameModal.svelte @@ -0,0 +1,17 @@ + + +{#if context.selectedCategory} + {@const { name } = context.selectedCategory} + +{/if} diff --git a/src/routes/(main)/category/[[id]]/DeleteCategoryModal.svelte b/src/routes/(main)/category/[[id]]/DeleteCategoryModal.svelte deleted file mode 100644 index a10af62..0000000 --- a/src/routes/(main)/category/[[id]]/DeleteCategoryModal.svelte +++ /dev/null @@ -1,47 +0,0 @@ - - - - {#if selectedCategory} - {@const { name } = selectedCategory} - {@const nameShort = name.length > 20 ? `${name.slice(0, 20)}...` : name} -
-
-

- '{nameShort}' 카테고리를 삭제할까요? -

-

- 모든 하위 카테고리도 함께 삭제돼요.
- 하지만 카테고리에 추가된 파일들은 삭제되지 않아요. -

-
-
- - -
-
- {/if} -
diff --git a/src/routes/(main)/category/[[id]]/service.ts b/src/routes/(main)/category/[[id]]/service.svelte.ts similarity index 73% rename from src/routes/(main)/category/[[id]]/service.ts rename to src/routes/(main)/category/[[id]]/service.svelte.ts index a4ebe57..275fe80 100644 --- a/src/routes/(main)/category/[[id]]/service.ts +++ b/src/routes/(main)/category/[[id]]/service.svelte.ts @@ -1,3 +1,4 @@ +import { getContext, setContext } from "svelte"; import { callPostApi } from "$lib/hooks"; import { encryptString } from "$lib/modules/crypto"; import type { SelectedCategory } from "$lib/molecules/Categories"; @@ -5,6 +6,17 @@ import type { CategoryRenameRequest } from "$lib/server/schemas"; export { requestCategoryCreation, requestFileRemovalFromCategory } from "$lib/services/category"; +export const createContext = () => { + const context = $state({ + selectedCategory: undefined as SelectedCategory | undefined, + }); + return setContext("context", context); +}; + +export const useContext = () => { + return getContext>("context"); +}; + export const requestCategoryRename = async (category: SelectedCategory, newName: string) => { const newNameEncrypted = await encryptString(newName, category.dataKey); diff --git a/src/routes/(main)/directory/[[id]]/+page.svelte b/src/routes/(main)/directory/[[id]]/+page.svelte index 6e43b73..30cc33f 100644 --- a/src/routes/(main)/directory/[[id]]/+page.svelte +++ b/src/routes/(main)/directory/[[id]]/+page.svelte @@ -4,49 +4,43 @@ import { goto } from "$app/navigation"; import { TopBar } from "$lib/components"; import { FloatingButton } from "$lib/components/atoms"; - import { RenameModal } from "$lib/components/organisms"; import { getDirectoryInfo, type DirectoryInfo } from "$lib/modules/filesystem"; import { masterKeyStore, hmacSecretStore } from "$lib/stores"; - import CreateBottomSheet from "./CreateBottomSheet.svelte"; - import CreateDirectoryModal from "./CreateDirectoryModal.svelte"; - import DeleteDirectoryEntryModal from "./DeleteDirectoryEntryModal.svelte"; + import DirectoryCreateModal from "./DirectoryCreateModal.svelte"; import DirectoryEntries from "./DirectoryEntries"; - import DirectoryEntryMenuBottomSheet from "./DirectoryEntryMenuBottomSheet.svelte"; import DownloadStatusCard from "./DownloadStatusCard.svelte"; import DuplicateFileModal from "./DuplicateFileModal.svelte"; + import EntryCreateBottomSheet from "./EntryCreateBottomSheet.svelte"; + import EntryDeleteModal from "./EntryDeleteModal.svelte"; + import EntryMenuBottomSheet from "./EntryMenuBottomSheet.svelte"; + import EntryRenameModal from "./EntryRenameModal.svelte"; import UploadStatusCard from "./UploadStatusCard.svelte"; import { + createContext, requestHmacSecretDownload, requestDirectoryCreation, requestFileUpload, - requestDirectoryEntryRename, - requestDirectoryEntryDeletion, - type SelectedDirectoryEntry, - } from "./service"; + requestEntryRename, + requestEntryDeletion, + } from "./service.svelte"; import IconAdd from "~icons/material-symbols/add"; let { data } = $props(); + let context = createContext(); let info: Writable | undefined = $state(); let fileInput: HTMLInputElement | undefined = $state(); - let resolveForDuplicateFileModal: ((res: boolean) => void) | undefined = $state(); let duplicatedFile: File | undefined = $state(); - let selectedEntry: SelectedDirectoryEntry | undefined = $state(); + let resolveForDuplicateFileModal: ((res: boolean) => void) | undefined = $state(); - let isCreateBottomSheetOpen = $state(false); - let isCreateDirectoryModalOpen = $state(false); + let isEntryCreateBottomSheetOpen = $state(false); + let isDirectoryCreateModalOpen = $state(false); let isDuplicateFileModalOpen = $state(false); - let isDirectoryEntryMenuBottomSheetOpen = $state(false); - let isDirectoryEntryRenameModalOpen = $state(false); - let isDeleteDirectoryEntryModalOpen = $state(false); - - const createDirectory = async (name: string) => { - await requestDirectoryCreation(name, data.id, $masterKeyStore?.get(1)!); - isCreateDirectoryModalOpen = false; - info = getDirectoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME - }; + let isEntryMenuBottomSheetOpen = $state(false); + let isEntryRenameModalOpen = $state(false); + let isEntryDeleteModalOpen = $state(false); const uploadFile = () => { const files = fileInput?.files; @@ -55,22 +49,19 @@ for (const file of files) { requestFileUpload(file, data.id, $hmacSecretStore?.get(1)!, $masterKeyStore?.get(1)!, () => { return new Promise((resolve) => { - resolveForDuplicateFileModal = resolve; duplicatedFile = file; + resolveForDuplicateFileModal = resolve; isDuplicateFileModalOpen = true; }); }) .then((res) => { if (!res) return; - // TODO: FIXME info = getDirectoryInfo(data.id, $masterKeyStore?.get(1)?.key!); - window.alert(`'${file.name}' 파일이 업로드되었어요.`); }) .catch((e: Error) => { // TODO: FIXME console.error(e); - window.alert(`'${file.name}' 파일 업로드에 실패했어요.\n${e.message}`); }); } @@ -110,8 +101,8 @@ info={$info} onEntryClick={({ type, id }) => goto(`/${type}/${id}`)} onEntryMenuClick={(entry) => { - selectedEntry = entry; - isDirectoryEntryMenuBottomSheetOpen = true; + context.selectedEntry = entry; + isEntryMenuBottomSheetOpen = true; }} /> {/key} @@ -122,68 +113,71 @@ { - isCreateBottomSheetOpen = true; + isEntryCreateBottomSheetOpen = true; }} /> - { - isCreateBottomSheetOpen = false; - isCreateDirectoryModalOpen = true; + isEntryCreateBottomSheetOpen = false; + isDirectoryCreateModalOpen = true; }} onFileUploadClick={() => { - isCreateBottomSheetOpen = false; + isEntryCreateBottomSheetOpen = false; fileInput?.click(); }} /> - - { - resolveForDuplicateFileModal?.(false); - resolveForDuplicateFileModal = undefined; - duplicatedFile = undefined; - isDuplicateFileModalOpen = false; - }} - onDuplicateClick={() => { - resolveForDuplicateFileModal?.(true); - resolveForDuplicateFileModal = undefined; - duplicatedFile = undefined; - isDuplicateFileModalOpen = false; - }} -/> - - { - isDirectoryEntryMenuBottomSheetOpen = false; - isDirectoryEntryRenameModalOpen = true; - }} - onDeleteClick={() => { - isDirectoryEntryMenuBottomSheetOpen = false; - isDeleteDirectoryEntryModalOpen = true; - }} -/> - (selectedEntry = undefined)} - originalName={selectedEntry?.name} - onRenameClick={async (newName: string) => { - if (await requestDirectoryEntryRename(selectedEntry!, newName)) { + { + if (await requestDirectoryCreation(name, data.id, $masterKeyStore?.get(1)!)) { info = getDirectoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME return true; } return false; }} /> - { - await requestDirectoryEntryDeletion(selectedEntry!); - info = getDirectoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME - return true; + { + resolveForDuplicateFileModal?.(false); + isDuplicateFileModalOpen = false; + }} + onUploadClick={() => { + resolveForDuplicateFileModal?.(true); + isDuplicateFileModalOpen = false; + }} +/> + + { + isEntryMenuBottomSheetOpen = false; + isEntryRenameModalOpen = true; + }} + onDeleteClick={() => { + isEntryMenuBottomSheetOpen = false; + isEntryDeleteModalOpen = true; + }} +/> + { + if (await requestEntryRename(context.selectedEntry!, newName)) { + info = getDirectoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME + return true; + } + return false; + }} +/> + { + if (await requestEntryDeletion(context.selectedEntry!)) { + info = getDirectoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME + return true; + } + return false; }} /> diff --git a/src/routes/(main)/directory/[[id]]/CreateDirectoryModal.svelte b/src/routes/(main)/directory/[[id]]/CreateDirectoryModal.svelte deleted file mode 100644 index 987e45e..0000000 --- a/src/routes/(main)/directory/[[id]]/CreateDirectoryModal.svelte +++ /dev/null @@ -1,28 +0,0 @@ - - - -

새 폴더

-
- -
-
- - -
-
diff --git a/src/routes/(main)/directory/[[id]]/DeleteDirectoryEntryModal.svelte b/src/routes/(main)/directory/[[id]]/DeleteDirectoryEntryModal.svelte deleted file mode 100644 index 81eced9..0000000 --- a/src/routes/(main)/directory/[[id]]/DeleteDirectoryEntryModal.svelte +++ /dev/null @@ -1,55 +0,0 @@ - - - - {#if selectedEntry} - {@const { type, name } = selectedEntry} - {@const nameShort = name.length > 20 ? `${name.slice(0, 20)}...` : name} -
-
-

- {#if type === "directory"} - '{nameShort}' 폴더를 삭제할까요? - {:else} - '{nameShort}' 파일을 삭제할까요? - {/if} -

-

- {#if type === "directory"} - 삭제한 폴더는 복구할 수 없어요.
- 폴더 안의 모든 파일과 폴더도 함께 삭제돼요. - {:else} - 삭제한 파일은 복구할 수 없어요. - {/if} -

-
-
- - -
-
- {/if} -
diff --git a/src/routes/(main)/directory/[[id]]/DirectoryCreateModal.svelte b/src/routes/(main)/directory/[[id]]/DirectoryCreateModal.svelte new file mode 100644 index 0000000..e43b569 --- /dev/null +++ b/src/routes/(main)/directory/[[id]]/DirectoryCreateModal.svelte @@ -0,0 +1,18 @@ + + + diff --git a/src/routes/(main)/directory/[[id]]/DirectoryEntries/DirectoryEntries.svelte b/src/routes/(main)/directory/[[id]]/DirectoryEntries/DirectoryEntries.svelte index baa9760..7ad3af7 100644 --- a/src/routes/(main)/directory/[[id]]/DirectoryEntries/DirectoryEntries.svelte +++ b/src/routes/(main)/directory/[[id]]/DirectoryEntries/DirectoryEntries.svelte @@ -17,12 +17,12 @@ import File from "./File.svelte"; import SubDirectory from "./SubDirectory.svelte"; import UploadingFile from "./UploadingFile.svelte"; - import type { SelectedDirectoryEntry } from "../service"; + import type { SelectedEntry } from "../service.svelte"; interface Props { info: DirectoryInfo; - onEntryClick: (entry: SelectedDirectoryEntry) => void; - onEntryMenuClick: (entry: SelectedDirectoryEntry) => void; + onEntryClick: (entry: SelectedEntry) => void; + onEntryMenuClick: (entry: SelectedEntry) => void; sortBy?: SortBy; } diff --git a/src/routes/(main)/directory/[[id]]/DirectoryEntries/File.svelte b/src/routes/(main)/directory/[[id]]/DirectoryEntries/File.svelte index 0dad51b..b25bd2f 100644 --- a/src/routes/(main)/directory/[[id]]/DirectoryEntries/File.svelte +++ b/src/routes/(main)/directory/[[id]]/DirectoryEntries/File.svelte @@ -2,15 +2,15 @@ import type { Writable } from "svelte/store"; import type { FileInfo } from "$lib/modules/filesystem"; import { formatDateTime } from "$lib/modules/util"; - import type { SelectedDirectoryEntry } from "../service"; + import type { SelectedEntry } from "../service.svelte"; import IconDraft from "~icons/material-symbols/draft"; import IconMoreVert from "~icons/material-symbols/more-vert"; interface Props { info: Writable; - onclick: (selectedEntry: SelectedDirectoryEntry) => void; - onOpenMenuClick: (selectedEntry: SelectedDirectoryEntry) => void; + onclick: (selectedEntry: SelectedEntry) => void; + onOpenMenuClick: (selectedEntry: SelectedEntry) => void; } let { info, onclick, onOpenMenuClick }: Props = $props(); diff --git a/src/routes/(main)/directory/[[id]]/DirectoryEntries/SubDirectory.svelte b/src/routes/(main)/directory/[[id]]/DirectoryEntries/SubDirectory.svelte index 11a788a..bea9e73 100644 --- a/src/routes/(main)/directory/[[id]]/DirectoryEntries/SubDirectory.svelte +++ b/src/routes/(main)/directory/[[id]]/DirectoryEntries/SubDirectory.svelte @@ -1,7 +1,7 @@ - - -
- {#if selectedEntry} - {@const { type, name } = selectedEntry} -
-
- {#if type === "directory"} - - {:else} - - {/if} -
-

- {name} -

-
-
- {/if} - -
- -

이름 바꾸기

-
-
- -
- -

삭제하기

-
-
-
-
diff --git a/src/routes/(main)/directory/[[id]]/DuplicateFileModal.svelte b/src/routes/(main)/directory/[[id]]/DuplicateFileModal.svelte index 1f97a75..28b8d6b 100644 --- a/src/routes/(main)/directory/[[id]]/DuplicateFileModal.svelte +++ b/src/routes/(main)/directory/[[id]]/DuplicateFileModal.svelte @@ -1,29 +1,27 @@ - - {#if file} - {@const { name } = file} - {@const nameShort = name.length > 20 ? `${name.slice(0, 20)}...` : name} -
-
-

'{nameShort}' 파일이 있어요.

-

예전에 이미 업로드된 파일이에요. 그래도 업로드할까요?

-
-
- - -
-
- {/if} -
+{#if file} + {@const { name } = file} + +

예전에 이미 업로드된 파일이에요. 그래도 업로드할까요?

+
+{/if} diff --git a/src/routes/(main)/directory/[[id]]/CreateBottomSheet.svelte b/src/routes/(main)/directory/[[id]]/EntryCreateBottomSheet.svelte similarity index 91% rename from src/routes/(main)/directory/[[id]]/CreateBottomSheet.svelte rename to src/routes/(main)/directory/[[id]]/EntryCreateBottomSheet.svelte index d0b2280..7d262dd 100644 --- a/src/routes/(main)/directory/[[id]]/CreateBottomSheet.svelte +++ b/src/routes/(main)/directory/[[id]]/EntryCreateBottomSheet.svelte @@ -6,12 +6,12 @@ import IconUploadFile from "~icons/material-symbols/upload-file"; interface Props { + isOpen: boolean; onDirectoryCreateClick: () => void; onFileUploadClick: () => void; - isOpen: boolean; } - let { onDirectoryCreateClick, onFileUploadClick, isOpen = $bindable() }: Props = $props(); + let { isOpen = $bindable(), onDirectoryCreateClick, onFileUploadClick }: Props = $props(); diff --git a/src/routes/(main)/directory/[[id]]/EntryDeleteModal.svelte b/src/routes/(main)/directory/[[id]]/EntryDeleteModal.svelte new file mode 100644 index 0000000..ad9ca2c --- /dev/null +++ b/src/routes/(main)/directory/[[id]]/EntryDeleteModal.svelte @@ -0,0 +1,33 @@ + + +{#if context.selectedEntry} + {@const { name, type } = context.selectedEntry} + +

+ {#if type === "directory"} + 삭제한 폴더는 복구할 수 없어요.
+ 폴더 안의 모든 파일과 폴더도 함께 삭제돼요. + {:else} + 삭제한 파일은 복구할 수 없어요. + {/if} +

+
+{/if} diff --git a/src/routes/(main)/directory/[[id]]/EntryMenuBottomSheet.svelte b/src/routes/(main)/directory/[[id]]/EntryMenuBottomSheet.svelte new file mode 100644 index 0000000..3b83406 --- /dev/null +++ b/src/routes/(main)/directory/[[id]]/EntryMenuBottomSheet.svelte @@ -0,0 +1,52 @@ + + +{#if context.selectedEntry} + {@const { name, type } = context.selectedEntry} + +
+
+
+ {#if type === "directory"} + + {:else} + + {/if} +
+

+ {name} +

+
+
+ +
+ +

이름 바꾸기

+
+
+ +
+ +

삭제하기

+
+
+
+
+{/if} diff --git a/src/routes/(main)/directory/[[id]]/EntryRenameModal.svelte b/src/routes/(main)/directory/[[id]]/EntryRenameModal.svelte new file mode 100644 index 0000000..225884d --- /dev/null +++ b/src/routes/(main)/directory/[[id]]/EntryRenameModal.svelte @@ -0,0 +1,17 @@ + + +{#if context.selectedEntry} + {@const { name } = context.selectedEntry} + +{/if} diff --git a/src/routes/(main)/directory/[[id]]/service.ts b/src/routes/(main)/directory/[[id]]/service.svelte.ts similarity index 83% rename from src/routes/(main)/directory/[[id]]/service.ts rename to src/routes/(main)/directory/[[id]]/service.svelte.ts index 1b0c427..3e2ddfc 100644 --- a/src/routes/(main)/directory/[[id]]/service.ts +++ b/src/routes/(main)/directory/[[id]]/service.svelte.ts @@ -1,3 +1,4 @@ +import { getContext, setContext } from "svelte"; import { callGetApi, callPostApi } from "$lib/hooks"; import { storeHmacSecrets } from "$lib/indexedDB"; import { generateDataKey, wrapDataKey, unwrapHmacSecret, encryptString } from "$lib/modules/crypto"; @@ -11,7 +12,7 @@ import type { } from "$lib/server/schemas"; import { hmacSecretStore, type MasterKey, type HmacSecret } from "$lib/stores"; -export interface SelectedDirectoryEntry { +export interface SelectedEntry { type: "directory" | "file"; id: number; dataKey: CryptoKey; @@ -19,6 +20,17 @@ export interface SelectedDirectoryEntry { name: string; } +export const createContext = () => { + const context = $state({ + selectedEntry: undefined as SelectedEntry | undefined, + }); + return setContext("context", context); +}; + +export const useContext = () => { + return getContext>("context"); +}; + export const requestHmacSecretDownload = async (masterKey: CryptoKey) => { // TODO: MEK rotation @@ -46,7 +58,8 @@ export const requestDirectoryCreation = async ( ) => { const { dataKey, dataKeyVersion } = await generateDataKey(); const nameEncrypted = await encryptString(name, dataKey); - await callPostApi("/api/directory/create", { + + const res = await callPostApi("/api/directory/create", { parent: parentId, mekVersion: masterKey.version, dek: await wrapDataKey(dataKey, masterKey.key), @@ -54,6 +67,7 @@ export const requestDirectoryCreation = async ( name: nameEncrypted.ciphertext, nameIv: nameEncrypted.iv, }); + return res.ok; }; export const requestFileUpload = async ( @@ -66,10 +80,7 @@ export const requestFileUpload = async ( return await uploadFile(file, parentId, hmacSecret, masterKey, onDuplicate); }; -export const requestDirectoryEntryRename = async ( - entry: SelectedDirectoryEntry, - newName: string, -) => { +export const requestEntryRename = async (entry: SelectedEntry, newName: string) => { const newNameEncrypted = await encryptString(newName, entry.dataKey); let res; @@ -89,7 +100,7 @@ export const requestDirectoryEntryRename = async ( return res.ok; }; -export const requestDirectoryEntryDeletion = async (entry: SelectedDirectoryEntry) => { +export const requestEntryDeletion = async (entry: SelectedEntry) => { const res = await callPostApi(`/api/${entry.type}/${entry.id}/delete`); if (!res.ok) return false;