mirror of
https://github.com/kmc7468/arkvault.git
synced 2025-12-14 22:08:45 +00:00
카테고리 페이지에 파일 제거 버튼 구현
This commit is contained in:
@@ -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}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
};
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user