파일 업로드/다운로드 현황을 모두 볼 수 있는 페이지 구현

This commit is contained in:
static
2025-01-18 10:26:35 +09:00
parent bde090c464
commit 811713cd03
21 changed files with 322 additions and 52 deletions

View File

@@ -11,8 +11,10 @@
import DeleteDirectoryEntryModal from "./DeleteDirectoryEntryModal.svelte";
import DirectoryEntries from "./DirectoryEntries";
import DirectoryEntryMenuBottomSheet from "./DirectoryEntryMenuBottomSheet.svelte";
import DownloadStatusCard from "./DownloadStatusCard.svelte";
import DuplicateFileModal from "./DuplicateFileModal.svelte";
import RenameDirectoryEntryModal from "./RenameDirectoryEntryModal.svelte";
import UploadStatusCard from "./UploadStatusCard.svelte";
import {
requestHmacSecretDownload,
requestDirectoryCreation,
@@ -99,6 +101,10 @@
{#if $info}
{@const topMargin = data.id === "root" ? "mt-4" : ""}
<div class="mb-4 flex flex-grow flex-col {topMargin}">
<div class="flex gap-x-2">
<UploadStatusCard onclick={() => goto("/file/uploads")} />
<DownloadStatusCard onclick={() => goto("/file/downloads")} />
</div>
{#key $info}
<DirectoryEntries
info={$info}

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import type { Writable } from "svelte/store";
import type { FileInfo } from "$lib/modules/filesystem";
import { formatDateTime } from "./service";
import { formatDateTime } from "$lib/modules/util";
import type { SelectedDirectoryEntry } from "../service";
import IconDraft from "~icons/material-symbols/draft";

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import type { Writable } from "svelte/store";
import { formatNetworkSpeed } from "$lib/modules/util";
import type { FileUploadStatus } from "$lib/stores";
import { formatUploadProgress, formatUploadRate } from "./service";
import IconDraft from "~icons/material-symbols/draft";
@@ -29,7 +29,7 @@
{:else if $info.status === "upload-pending"}
업로드를 기다리는 중
{:else if $info.status === "uploading"}
전송됨 {formatUploadProgress($info.progress)} · {formatUploadRate($info.rate)}
전송됨 {Math.floor(($info.progress ?? 0) * 100)}% · {formatNetworkSpeed($info.rate ?? 0)}
{/if}
</p>
</div>

View File

@@ -1,7 +1,3 @@
import { formatFileSize } from "$lib/modules/util";
export { formatDateTime } from "$lib/modules/util";
export enum SortBy {
NAME_ASC,
NAME_DESC,
@@ -31,11 +27,3 @@ export const sortEntries = <T extends { name?: string }>(
entries.sort((a, b) => sortFunc(a.name, b.name));
};
export const formatUploadProgress = (progress?: number) => {
return `${Math.floor((progress ?? 0) * 100)}%`;
};
export const formatUploadRate = (rate?: number) => {
return `${formatFileSize((rate ?? 0) / 8)}/s`;
};

View File

@@ -0,0 +1,41 @@
<script lang="ts">
import { untrack } from "svelte";
import { get, type Writable } from "svelte/store";
import { fileDownloadStatusStore, isFileDownloading, type FileDownloadStatus } from "$lib/stores";
interface Props {
onclick: () => void;
}
let { onclick }: Props = $props();
let downloadingFiles: Writable<FileDownloadStatus>[] = $state([]);
$effect(() => {
downloadingFiles = $fileDownloadStatusStore.filter((status) =>
isFileDownloading(get(status).status),
);
return untrack(() => {
const unsubscribes = downloadingFiles.map((downloadingFile) =>
downloadingFile.subscribe(({ status }) => {
if (!isFileDownloading(status)) {
downloadingFiles = downloadingFiles.filter((file) => file !== downloadingFile);
}
}),
);
return () => unsubscribes.forEach((unsubscribe) => unsubscribe());
});
});
</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="flex h-full w-full flex-col 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}

View File

@@ -0,0 +1,39 @@
<script lang="ts">
import { untrack } from "svelte";
import { get, type Writable } from "svelte/store";
import { fileUploadStatusStore, isFileUploading, type FileUploadStatus } from "$lib/stores";
interface Props {
onclick: () => void;
}
let { onclick }: Props = $props();
let uploadingFiles: Writable<FileUploadStatus>[] = $state([]);
$effect(() => {
uploadingFiles = $fileUploadStatusStore.filter((status) => isFileUploading(get(status).status));
return untrack(() => {
const unsubscribes = uploadingFiles.map((uploadingFile) =>
uploadingFile.subscribe(({ status }) => {
if (!isFileUploading(status)) {
uploadingFiles = uploadingFiles.filter((file) => file !== uploadingFile);
}
}),
);
return () => unsubscribes.forEach((unsubscribe) => unsubscribe());
});
});
</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="flex h-full w-full flex-col 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}