리다이렉트 경로 처리, 파일 정보 변환, 상태 표시 UI 등 중복된 코드를 통합하고 모듈 간 순환 의존성 및 계층 위반 해소

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
staticandClaude Opus 5.5 committed 2026-10-12 08:51:07 +09:00
1 parent 9119c21881
commit 5b8621e524
96 files changed
+452 -548

No files matched your search

@@ -0,0 +1,5 @@
import type { PageLoad } from "./$types";
export const load: PageLoad = async ({ params }) => {
return { id: params.id ?? ("root" as const) };
};
@@ -1,17 +0,0 @@
import { error } from "@sveltejs/kit";
import { z } from "zod";
import type { PageLoad } from "./$types";
export const load: PageLoad = async ({ params }) => {
const zodRes = z
.object({
id: z.coerce.number().int().positive().optional(),
})
.safeParse(params);
if (!zodRes.success) error(404, "Not found");
const { id } = zodRes.data;
return {
id: id ? id : ("root" as const),
};
};
@@ -1,18 +1,18 @@
<script lang="ts">
import { goto } from "$app/navigation";
import { page } from "$app/state";
import { getDownloadingFiles, getUploadingFiles } from "#lib/client/transfer/index.ts";
import { tryAction } from "#lib/features/errors.ts";
import { createDirectory, uploadFile, useDirectory } from "#lib/features/filesystem/index.ts";
import { FloatingButton, OverlayGroup, TopBar } from "#lib/ui/index.ts";
import DirectoryCreateModal from "./DirectoryCreateModal.svelte";
import DirectoryEntries from "./directory-entries/index.ts";
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 TransferStatusCard from "./TransferStatusCard.svelte";
import {
createContext,
deleteEntry,
@@ -89,8 +89,18 @@
{#if info?.exists}
<div class="flex grow flex-col px-4 pb-4">
<div class="flex gap-x-2">
<UploadStatusCard onclick={() => goto("/file/uploads")} />
<DownloadStatusCard onclick={() => goto("/file/downloads")} />
<TransferStatusCard
label="진행 중인 업로드"
count={getUploadingFiles().length}
color="blue"
onclick={() => goto("/file/uploads")}
/>
<TransferStatusCard
label="진행 중인 다운로드"
count={getDownloadingFiles().length}
color="green"
onclick={() => goto("/file/downloads")}
/>
</div>
{#key info.id}
<DirectoryEntries
@@ -0,0 +1,5 @@
import type { PageLoad } from "./$types";
export const load: PageLoad = async ({ params }) => {
return { id: params.id ?? ("root" as const) };
};
@@ -0,0 +1,29 @@
<script lang="ts">
import { afterPress } from "#lib/ui/index.ts";
interface Props {
color: "blue" | "green";
count: number;
label: string;
onclick: () => void;
}
let { color, count, label, onclick }: Props = $props();
const colorClasses = {
blue: { button: "bg-blue-100 active:bg-blue-200", count: "text-blue-800" },
green: { button: "bg-green-100 active:bg-green-200", count: "text-green-800" },
};
</script>
{#if count > 0}
<button
onclick={afterPress(onclick)}
class={["mb-4 max-w-[50%] flex-1 rounded-xl p-3", colorClasses[color].button]}
>
<div class="text-left transition active:scale-95">
<p class="text-xs text-gray-800">{label}</p>
<p class={["font-medium", colorClasses[color].count]}>{count}개</p>
</div>
</button>
{/if}
@@ -49,7 +49,7 @@
...sortEntries(info.subDirectories.map(toEntry("directory"))),
...sortEntries([
...info.files.map(toEntry("file")),
...(getUploadingFiles(info.id) as LiveFileUploadState[]).map(toEntry("uploading-file")),
...getUploadingFiles(info.id).map(toEntry("uploading-file")),
]),
]);
</script>
@@ -1,6 +1,6 @@
<script lang="ts">
import type { LiveFileUploadState } from "#lib/client/transfer/index.ts";
import { formatNetworkSpeed } from "#lib/utils/index.ts";
import { UploadStatusText } from "#lib/features/transfer/components/index.ts";
import IconDraft from "~icons/material-symbols/draft";
@@ -20,18 +20,7 @@
{state.name}
</p>
<p class="text-xs">
{#if state.status === "queued"}
대기 중
{:else if state.status === "encryption-pending"}
준비 중
{:else if state.status === "encrypting"}
암호화하는 중
{:else if state.status === "upload-pending"}
업로드를 기다리는 중
{:else if state.status === "uploading"}
전송됨 {Math.floor((state.progress ?? 0) * 100)}% ·
{formatNetworkSpeed((state.rate ?? 0) * 8)}
{/if}
<UploadStatusText {state} />
</p>
</div>
</div>
@@ -1,17 +0,0 @@
import { error } from "@sveltejs/kit";
import { z } from "zod";
import type { PageLoad } from "./$types";
export const load: PageLoad = async ({ params }) => {
const zodRes = z
.object({
id: z.coerce.number().int().positive().optional(),
})
.safeParse(params);
if (!zodRes.success) error(404, "Not found");
const { id } = zodRes.data;
return {
id: id ? id : ("root" as const),
};
};
@@ -1,23 +0,0 @@
<script lang="ts">
import { getDownloadingFiles } from "#lib/client/transfer/index.ts";
interface Props {
onclick: () => void;
}
let { onclick }: Props = $props();
let downloadingFiles = $derived(getDownloadingFiles());
</script>
{#if downloadingFiles.length > 0}
<button
onclick={() => setTimeout(onclick, 100)}
class="mb-4 max-w-[50%] flex-1 rounded-xl bg-green-100 p-3 active:bg-green-200"
>
<div class="text-left transition active:scale-95">
<p class="text-xs text-gray-800">진행 중인 다운로드</p>
<p class="font-medium text-green-800">{downloadingFiles.length}개</p>
</div>
</button>
{/if}
@@ -1,23 +0,0 @@
<script lang="ts">
import { getUploadingFiles } from "#lib/client/transfer/index.ts";
interface Props {
onclick: () => void;
}
let { onclick }: Props = $props();
let uploadingFiles = $derived(getUploadingFiles());
</script>
{#if uploadingFiles.length > 0}
<button
onclick={() => setTimeout(onclick, 100)}
class="mb-4 max-w-[50%] flex-1 rounded-xl bg-blue-100 p-3 active:bg-blue-200"
>
<div class="text-left transition active:scale-95">
<p class="text-xs text-gray-800">진행 중인 업로드</p>
<p class="font-medium text-blue-800">{uploadingFiles.length}개</p>
</div>
</button>
{/if}
+5 -3
View File
@@ -2,14 +2,16 @@
import { onMount } from "svelte";
import { goto } from "$app/navigation";
import { FileThumbnailButton } from "#lib/features/filesystem/components/index.ts";
import { type SummarizedFileInfo } from "#lib/features/filesystem/index.ts";
import {
getRecentMediaFileInfos,
type SummarizedFileInfo,
} from "#lib/features/filesystem/index.ts";
import { EntryButton } from "#lib/ui/index.ts";
import { requestFreshMediaFilesRetrieval } from "./service.ts";
let mediaFiles: SummarizedFileInfo[] = $state([]);
onMount(async () => {
mediaFiles = await requestFreshMediaFilesRetrieval();
mediaFiles = await getRecentMediaFileInfos(8);
});
</script>
-14
View File
@@ -1,14 +0,0 @@
import { getAllFileInfos } from "#lib/client/storage/indexed-db/index.ts";
export const requestFreshMediaFilesRetrieval = async (limit = 8) => {
const files = await getAllFileInfos();
files.sort(
(a, b) =>
(b.createdAt ?? b.lastModifiedAt).getTime() - (a.createdAt ?? a.lastModifiedAt).getTime(),
);
return files
.filter(
({ contentType }) => contentType.startsWith("image/") || contentType.startsWith("video/"),
)
.slice(0, limit);
};