카테고리 페이지에 파일 제거 버튼 구현

This commit is contained in:
static
2025-01-22 15:48:46 +09:00
parent 368868910d
commit 8f8bad6d10
7 changed files with 37 additions and 24 deletions

View File

@@ -12,6 +12,7 @@
interface Props { interface Props {
info: CategoryInfo; info: CategoryInfo;
onFileClick: (file: SelectedFile) => void; onFileClick: (file: SelectedFile) => void;
onFileRemoveClick: (file: SelectedFile) => void;
onSubCategoryClick: (subCategory: SelectedCategory) => void; onSubCategoryClick: (subCategory: SelectedCategory) => void;
onSubCategoryCreateClick: () => void; onSubCategoryCreateClick: () => void;
onSubCategoryMenuClick: (subCategory: SelectedCategory) => void; onSubCategoryMenuClick: (subCategory: SelectedCategory) => void;
@@ -20,6 +21,7 @@
let { let {
info, info,
onFileClick, onFileClick,
onFileRemoveClick,
onSubCategoryClick, onSubCategoryClick,
onSubCategoryCreateClick, onSubCategoryCreateClick,
onSubCategoryMenuClick, onSubCategoryMenuClick,
@@ -53,7 +55,7 @@
<div class="space-y-1"> <div class="space-y-1">
{#key info} {#key info}
{#each files as file} {#each files as file}
<File info={file} onclick={onFileClick} /> <File info={file} onclick={onFileClick} onRemoveClick={onFileRemoveClick} />
{:else} {:else}
<p>이 카테고리에 추가된 파일이 없어요.</p> <p>이 카테고리에 추가된 파일이 없어요.</p>
{/each} {/each}

View File

@@ -4,14 +4,15 @@
import type { SelectedFile } from "./service"; import type { SelectedFile } from "./service";
import IconDraft from "~icons/material-symbols/draft"; import IconDraft from "~icons/material-symbols/draft";
import IconMoreVert from "~icons/material-symbols/more-vert"; import IconClose from "~icons/material-symbols/close";
interface Props { interface Props {
info: Writable<FileInfo | null>; info: Writable<FileInfo | null>;
onclick: (selectedFile: SelectedFile) => void; onclick: (selectedFile: SelectedFile) => void;
onRemoveClick: (selectedFile: SelectedFile) => void;
} }
let { info, onclick }: Props = $props(); let { info, onclick, onRemoveClick }: Props = $props();
const openFile = () => { const openFile = () => {
const { id, dataKey, dataKeyVersion, name } = $info as FileInfo; const { id, dataKey, dataKeyVersion, name } = $info as FileInfo;
@@ -22,10 +23,15 @@
}, 100); }, 100);
}; };
const openMenu = (e: Event) => { const removeFile = (e: Event) => {
e.stopPropagation(); e.stopPropagation();
// TODO const { id, dataKey, dataKeyVersion, name } = $info as FileInfo;
if (!dataKey || !dataKeyVersion) return; // TODO: Error handling
setTimeout(() => {
onRemoveClick({ id, dataKey, dataKeyVersion, name });
}, 100);
}; };
</script> </script>
@@ -41,21 +47,21 @@
{$info.name} {$info.name}
</p> </p>
<button <button
id="open-menu" id="remove-file"
onclick={openMenu} onclick={removeFile}
class="flex-shrink-0 rounded-full p-1 active:bg-gray-100" class="flex-shrink-0 rounded-full p-1 active:bg-gray-100"
> >
<IconMoreVert class="text-lg" /> <IconClose class="text-lg" />
</button> </button>
</div> </div>
</div> </div>
{/if} {/if}
<style> <style>
#button:active:not(:has(#open-menu:active)) { #button:active:not(:has(#remove-file:active)) {
@apply bg-gray-100; @apply bg-gray-100;
} }
#button-content:active:not(:has(#open-menu:active)) { #button-content:active:not(:has(#remove-file:active)) {
@apply scale-95; @apply scale-95;
} }
</style> </style>

View File

@@ -1,6 +1,6 @@
import { callPostApi } from "$lib/hooks"; import { callPostApi } from "$lib/hooks";
import { generateDataKey, wrapDataKey, encryptString } from "$lib/modules/crypto"; import { generateDataKey, wrapDataKey, encryptString } from "$lib/modules/crypto";
import type { CategoryCreateRequest } from "$lib/server/schemas"; import type { CategoryCreateRequest, CategoryFileRemoveRequest } from "$lib/server/schemas";
import type { MasterKey } from "$lib/stores"; import type { MasterKey } from "$lib/stores";
export const requestCategoryCreation = async ( export const requestCategoryCreation = async (
@@ -19,3 +19,11 @@ export const requestCategoryCreation = async (
nameIv: nameEncrypted.iv, nameIv: nameEncrypted.iv,
}); });
}; };
export const requestFileRemovalFromCategory = async (fileId: number, categoryId: number) => {
const res = await callPostApi<CategoryFileRemoveRequest>(
`/api/category/${categoryId}/file/remove`,
{ file: fileId },
);
return res.ok;
};

View File

@@ -16,9 +16,9 @@
import AddToCategoryBottomSheet from "./AddToCategoryBottomSheet.svelte"; import AddToCategoryBottomSheet from "./AddToCategoryBottomSheet.svelte";
import DownloadStatus from "./DownloadStatus.svelte"; import DownloadStatus from "./DownloadStatus.svelte";
import { import {
requestFileRemovalFromCategory,
requestFileDownload, requestFileDownload,
requestFileAdditionToCategory, requestFileAdditionToCategory,
requestFileRemovalFromCategory,
} from "./service"; } from "./service";
import IconClose from "~icons/material-symbols/close"; import IconClose from "~icons/material-symbols/close";

View File

@@ -1,8 +1,8 @@
import { callPostApi } from "$lib/hooks"; import { callPostApi } from "$lib/hooks";
import { getFileCache, storeFileCache, downloadFile } from "$lib/modules/file"; import { getFileCache, storeFileCache, downloadFile } from "$lib/modules/file";
import type { CategoryFileAddRequest, CategoryFileRemoveRequest } from "$lib/server/schemas"; import type { CategoryFileAddRequest } from "$lib/server/schemas";
export { requestCategoryCreation } from "$lib/services/category"; export { requestCategoryCreation, requestFileRemovalFromCategory } from "$lib/services/category";
export const requestFileDownload = async ( export const requestFileDownload = async (
fileId: number, fileId: number,
@@ -23,11 +23,3 @@ export const requestFileAdditionToCategory = async (fileId: number, categoryId:
}); });
return res.ok; return res.ok;
}; };
export const requestFileRemovalFromCategory = async (fileId: number, categoryId: number) => {
const res = await callPostApi<CategoryFileRemoveRequest>(
`/api/category/${categoryId}/file/remove`,
{ file: fileId },
);
return res.ok;
};

View File

@@ -12,6 +12,7 @@
import RenameCategoryModal from "./RenameCategoryModal.svelte"; import RenameCategoryModal from "./RenameCategoryModal.svelte";
import { import {
requestCategoryCreation, requestCategoryCreation,
requestFileRemovalFromCategory,
requestCategoryRename, requestCategoryRename,
requestCategoryDeletion, requestCategoryDeletion,
} from "./service"; } from "./service";
@@ -49,13 +50,17 @@
{#if $info} {#if $info}
<Category <Category
info={$info} info={$info}
onFileClick={({ id }) => goto(`/file/${id}`)}
onFileRemoveClick={({ id }) => {
requestFileRemovalFromCategory(id, data.id as number);
info = getCategoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME
}}
onSubCategoryClick={({ id }) => goto(`/category/${id}`)} onSubCategoryClick={({ id }) => goto(`/category/${id}`)}
onSubCategoryCreateClick={() => (isCreateCategoryModalOpen = true)} onSubCategoryCreateClick={() => (isCreateCategoryModalOpen = true)}
onSubCategoryMenuClick={(subCategory) => { onSubCategoryMenuClick={(subCategory) => {
selectedSubCategory = subCategory; selectedSubCategory = subCategory;
isSubCategoryMenuBottomSheetOpen = true; isSubCategoryMenuBottomSheetOpen = true;
}} }}
onFileClick={({ id }) => goto(`/file/${id}`)}
/> />
{/if} {/if}
</div> </div>

View File

@@ -3,7 +3,7 @@ import { encryptString } from "$lib/modules/crypto";
import type { SelectedCategory } from "$lib/molecules/Categories"; import type { SelectedCategory } from "$lib/molecules/Categories";
import type { CategoryRenameRequest } from "$lib/server/schemas"; import type { CategoryRenameRequest } from "$lib/server/schemas";
export { requestCategoryCreation } from "$lib/services/category"; export { requestCategoryCreation, requestFileRemovalFromCategory } from "$lib/services/category";
export const requestCategoryRename = async (category: SelectedCategory, newName: string) => { export const requestCategoryRename = async (category: SelectedCategory, newName: string) => {
const newNameEncrypted = await encryptString(newName, category.dataKey); const newNameEncrypted = await encryptString(newName, category.dataKey);