파일/폴더 삭제 및 이름 변경 구현 완료

This commit is contained in:
static
2025-01-06 14:30:00 +09:00
parent bd0dd3343a
commit 71f12c942b
6 changed files with 75 additions and 27 deletions

View File

@@ -20,7 +20,7 @@ export const callGetApi = async (input: RequestInfo, fetchInternal?: typeof fetc
export const callPostApi = async <T>(
input: RequestInfo,
payload: T,
payload?: T,
fetchInternal?: typeof fetch,
) => {
return await callApi(
@@ -28,7 +28,7 @@ export const callPostApi = async <T>(
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
body: payload ? JSON.stringify(payload) : undefined,
},
fetchInternal,
);

View File

@@ -1,11 +1,3 @@
<script module lang="ts">
export interface SelectedDirectoryEntry {
type: "directory" | "file";
id: number;
name: string;
}
</script>
<script lang="ts">
import { goto } from "$app/navigation";
import { TopBar } from "$lib/components";
@@ -22,6 +14,9 @@
decryptFileMetadata,
requestDirectoryCreation,
requestFileUpload,
requestDirectoryEntryRename,
requestDirectoryEntryDeletion,
type SelectedDirectoryEntry,
} from "./service";
import IconAdd from "~icons/material-symbols/add";
@@ -114,12 +109,12 @@
<div class="my-4 pb-[4.5rem]">
{#if subDirectories}
{#await subDirectories then subDirectories}
{#each subDirectories as { id, name }}
{#each subDirectories as { id, dataKey, name }}
<DirectoryEntry
{name}
onclick={() => goto(`/directory/${id}`)}
onOpenMenuClick={() => {
selectedEntry = { type: "directory", id, name };
selectedEntry = { type: "directory", id, dataKey, name };
isDirectoryEntryMenuBottomSheetOpen = true;
}}
type="directory"
@@ -129,12 +124,12 @@
{/if}
{#if files}
{#await files then files}
{#each files as { id, name }}
{#each files as { id, dataKey, name }}
<DirectoryEntry
{name}
onclick={() => goto(`/file/${id}`)}
onOpenMenuClick={() => {
selectedEntry = { type: "file", id, name };
selectedEntry = { type: "file", id, dataKey, name };
isDirectoryEntryMenuBottomSheetOpen = true;
}}
type="file"
@@ -177,5 +172,19 @@
isDeleteDirectoryEntryModalOpen = true;
}}
/>
<RenameDirectoryEntryModal bind:isOpen={isRenameDirectoryEntryModalOpen} bind:selectedEntry />
<DeleteDirectoryEntryModal bind:isOpen={isDeleteDirectoryEntryModalOpen} bind:selectedEntry />
<RenameDirectoryEntryModal
bind:isOpen={isRenameDirectoryEntryModalOpen}
bind:selectedEntry
onRenameClick={async (newName) => {
await requestDirectoryEntryRename(selectedEntry!, newName);
return true;
}}
/>
<DeleteDirectoryEntryModal
bind:isOpen={isDeleteDirectoryEntryModalOpen}
bind:selectedEntry
onDeleteClick={async () => {
await requestDirectoryEntryDeletion(selectedEntry!);
return true;
}}
/>

View File

@@ -1,24 +1,27 @@
<script lang="ts">
import { Modal } from "$lib/components";
import { Button } from "$lib/components/buttons";
import type { SelectedDirectoryEntry } from "./+page.svelte";
import type { SelectedDirectoryEntry } from "./service";
interface Props {
onDeleteClick: () => Promise<boolean>;
isOpen: boolean;
selectedEntry: SelectedDirectoryEntry | undefined;
}
let { isOpen = $bindable(), selectedEntry = $bindable() }: Props = $props();
let { onDeleteClick, isOpen = $bindable(), selectedEntry = $bindable() }: Props = $props();
const closeModal = () => {
isOpen = false;
selectedEntry = undefined;
};
const deleteEntry = () => {
// TODO
const deleteEntry = async () => {
// TODO: Validation
closeModal();
if (await onDeleteClick()) {
closeModal();
}
};
</script>

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { BottomSheet } from "$lib/components";
import { EntryButton } from "$lib/components/buttons";
import type { SelectedDirectoryEntry } from "./+page.svelte";
import type { SelectedDirectoryEntry } from "./service";
import IconFolder from "~icons/material-symbols/folder";
import IconDraft from "~icons/material-symbols/draft";

View File

@@ -2,14 +2,15 @@
import { Modal } from "$lib/components";
import { Button } from "$lib/components/buttons";
import { TextInput } from "$lib/components/inputs";
import type { SelectedDirectoryEntry } from "./+page.svelte";
import type { SelectedDirectoryEntry } from "./service";
interface Props {
onRenameClick: (newName: string) => Promise<boolean>;
isOpen: boolean;
selectedEntry: SelectedDirectoryEntry | undefined;
}
let { isOpen = $bindable(), selectedEntry = $bindable() }: Props = $props();
let { onRenameClick, isOpen = $bindable(), selectedEntry = $bindable() }: Props = $props();
let name = $state("");
@@ -19,10 +20,12 @@
selectedEntry = undefined;
};
const renameEntry = () => {
// TODO
const renameEntry = async () => {
// TODO: Validation
closeModal();
if (await onRenameClick(name)) {
closeModal();
}
};
$effect(() => {

View File

@@ -9,20 +9,30 @@ import {
decryptString,
} from "$lib/modules/crypto";
import type {
DirectoryRenameRequest,
DirectoryInfoResponse,
DirectoryCreateRequest,
FileRenameRequest,
FileUploadRequest,
} from "$lib/server/schemas";
import type { MasterKey } from "$lib/stores";
export { decryptFileMetadata } from "$lib/services/file";
export interface SelectedDirectoryEntry {
type: "directory" | "file";
id: number;
dataKey: CryptoKey;
name: string;
}
export const decryptDirectoryMetadata = async (
metadata: NonNullable<DirectoryInfoResponse["metadata"]>,
masterKey: CryptoKey,
) => {
const { dataKey } = await unwrapDataKey(metadata.dek, masterKey);
return {
dataKey,
name: await decryptString(metadata.name, metadata.nameIv, dataKey),
};
};
@@ -71,3 +81,26 @@ export const requestFileUpload = async (
xhr.open("POST", "/api/file/upload");
xhr.send(form);
};
export const requestDirectoryEntryRename = async (
entry: SelectedDirectoryEntry,
newName: string,
) => {
const newNameEncrypted = await encryptString(newName, entry.dataKey);
if (entry.type === "directory") {
await callPostApi<DirectoryRenameRequest>(`/api/directory/${entry.id}/rename`, {
name: newNameEncrypted.ciphertext,
nameIv: newNameEncrypted.iv,
});
} else {
await callPostApi<FileRenameRequest>(`/api/file/${entry.id}/rename`, {
name: newNameEncrypted.ciphertext,
nameIv: newNameEncrypted.iv,
});
}
};
export const requestDirectoryEntryDeletion = async (entry: SelectedDirectoryEntry) => {
await callPostApi(`/api/${entry.type}/${entry.id}/delete`);
};