- {#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;