기존에 제작된 모달들을 ActionModal 컴포넌트 기반으로 재구성

This commit is contained in:
static
2025-01-25 22:30:06 +09:00
parent 38a1c8d7e0
commit 7dba1cf4c6
26 changed files with 367 additions and 387 deletions

View File

@@ -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<CategoryInfo | null> | 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 @@
<CategoryCreateModal
bind:isOpen={isCategoryCreateModalOpen}
oncreate={async (name: string) => {
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 @@
/>
<CategoryMenuBottomSheet
bind:isOpen={isSubCategoryMenuBottomSheetOpen}
bind:selectedCategory={selectedSubCategory}
bind:isOpen={isCategoryMenuBottomSheetOpen}
onRenameClick={() => {
isSubCategoryMenuBottomSheetOpen = false;
isCategoryMenuBottomSheetOpen = false;
isCategoryRenameModalOpen = true;
}}
onDeleteClick={() => {
isSubCategoryMenuBottomSheetOpen = false;
isDeleteCategoryModalOpen = true;
isCategoryMenuBottomSheetOpen = false;
isCategoryDeleteModalOpen = true;
}}
/>
<RenameModal
<CategoryRenameModal
bind:isOpen={isCategoryRenameModalOpen}
onbeforeclose={() => (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;
}}
/>
<DeleteCategoryModal
bind:isOpen={isDeleteCategoryModalOpen}
bind:selectedCategory={selectedSubCategory}
<CategoryDeleteModal
bind:isOpen={isCategoryDeleteModalOpen}
onDeleteClick={async () => {
if (selectedSubCategory) {
await requestCategoryDeletion(selectedSubCategory);
if (await requestCategoryDeletion(context.selectedCategory!)) {
info = getCategoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME
return true;
}

View File

@@ -0,0 +1,29 @@
<script lang="ts">
import { ActionModal } from "$lib/components/molecules";
import { truncateString } from "$lib/modules/util";
import { useContext } from "./service.svelte";
interface Props {
isOpen: boolean;
onDeleteClick: () => Promise<boolean>;
}
let { isOpen = $bindable(), onDeleteClick }: Props = $props();
let context = useContext();
</script>
{#if context.selectedCategory}
{@const { name } = context.selectedCategory}
<ActionModal
bind:isOpen
title="'{truncateString(name)}' 카테고리를 삭제할까요?"
cancelText="아니요"
confirmText="삭제할게요"
onConfirmClick={onDeleteClick}
>
<p>
모든 하위 카테고리도 함께 삭제돼요. <br />
하지만 카테고리에 추가된 파일들은 삭제되지 않아요.
</p>
</ActionModal>
{/if}

View File

@@ -1,36 +1,26 @@
<script lang="ts">
import { BottomSheet } from "$lib/components";
import { EntryButton } from "$lib/components/atoms";
import type { SelectedCategory } from "$lib/molecules/Categories";
import { useContext } from "./service.svelte";
import IconCategory from "~icons/material-symbols/category";
import IconEdit from "~icons/material-symbols/edit";
import IconDelete from "~icons/material-symbols/delete";
interface Props {
onRenameClick: () => void;
onDeleteClick: () => void;
isOpen: boolean;
selectedCategory: SelectedCategory | undefined;
onDeleteClick: () => void;
onRenameClick: () => void;
}
let {
onRenameClick,
onDeleteClick,
isOpen = $bindable(),
selectedCategory = $bindable(),
}: Props = $props();
const closeBottomSheet = () => {
isOpen = false;
selectedCategory = undefined;
};
let { isOpen = $bindable(), onDeleteClick, onRenameClick }: Props = $props();
let context = useContext();
</script>
<BottomSheet bind:isOpen onclose={closeBottomSheet}>
<div class="w-full py-4">
{#if selectedCategory}
{@const { name } = selectedCategory}
{#if context.selectedCategory}
{@const { name } = context.selectedCategory}
<BottomSheet bind:isOpen>
<div class="w-full py-4">
<div class="flex h-12 items-center gap-x-4 p-2">
<div class="flex-shrink-0 text-lg">
<IconCategory />
@@ -40,18 +30,18 @@
</p>
</div>
<div class="my-2 h-px w-full bg-gray-200"></div>
{/if}
<EntryButton onclick={onRenameClick} class="w-full">
<div class="flex h-8 items-center gap-x-4">
<IconEdit class="text-lg" />
<p class="font-medium">이름 바꾸기</p>
</div>
</EntryButton>
<EntryButton onclick={onDeleteClick} class="w-full">
<div class="flex h-8 items-center gap-x-4 text-red-500">
<IconDelete class="text-lg" />
<p class="font-medium">삭제하기</p>
</div>
</EntryButton>
</div>
</BottomSheet>
<EntryButton onclick={onRenameClick} class="w-full">
<div class="flex h-8 items-center gap-x-4">
<IconEdit class="text-lg" />
<p class="font-medium">이름 바꾸기</p>
</div>
</EntryButton>
<EntryButton onclick={onDeleteClick} class="w-full">
<div class="flex h-8 items-center gap-x-4 text-red-500">
<IconDelete class="text-lg" />
<p class="font-medium">삭제하기</p>
</div>
</EntryButton>
</div>
</BottomSheet>
{/if}

View File

@@ -0,0 +1,17 @@
<script lang="ts">
import { RenameModal } from "$lib/components/organisms";
import { useContext } from "./service.svelte";
interface Props {
isOpen: boolean;
onRenameClick: (newName: string) => Promise<boolean>;
}
let { isOpen = $bindable(), onRenameClick }: Props = $props();
let context = useContext();
</script>
{#if context.selectedCategory}
{@const { name } = context.selectedCategory}
<RenameModal bind:isOpen originalName={name} {onRenameClick} />
{/if}

View File

@@ -1,47 +0,0 @@
<script lang="ts">
import { Button, Modal } from "$lib/components/atoms";
import type { SelectedCategory } from "$lib/molecules/Categories";
interface Props {
onDeleteClick: () => Promise<boolean>;
isOpen: boolean;
selectedCategory: SelectedCategory | undefined;
}
let { onDeleteClick, isOpen = $bindable(), selectedCategory = $bindable() }: Props = $props();
const closeModal = () => {
isOpen = false;
selectedCategory = undefined;
};
const deleteEntry = async () => {
// TODO: Validation
if (await onDeleteClick()) {
closeModal();
}
};
</script>
<Modal bind:isOpen onclose={closeModal}>
{#if selectedCategory}
{@const { name } = selectedCategory}
{@const nameShort = name.length > 20 ? `${name.slice(0, 20)}...` : name}
<div class="space-y-4">
<div class="space-y-2 break-keep">
<p class="text-xl font-bold">
'{nameShort}' 카테고리를 삭제할까요?
</p>
<p>
모든 하위 카테고리도 함께 삭제돼요. <br />
하지만 카테고리에 추가된 파일들은 삭제되지 않아요.
</p>
</div>
<div class="flex gap-x-2">
<Button color="gray" onclick={closeModal} class="flex-1">아니요</Button>
<Button onclick={deleteEntry} class="flex-1">삭제할게요</Button>
</div>
</div>
{/if}
</Modal>

View File

@@ -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<ReturnType<typeof createContext>>("context");
};
export const requestCategoryRename = async (category: SelectedCategory, newName: string) => {
const newNameEncrypted = await encryptString(newName, category.dataKey);