mirror of
https://github.com/kmc7468/arkvault.git
synced 2025-12-14 22:08:45 +00:00
파일을 업로드하는 중에, 해당되는 디렉터리에 업로드 Progress를 표시하도록 구현
This commit is contained in:
@@ -1,11 +1,18 @@
|
||||
<script lang="ts">
|
||||
import { untrack } from "svelte";
|
||||
import type { Writable } from "svelte/store";
|
||||
import { get, type Writable } from "svelte/store";
|
||||
import { getDirectoryInfo, getFileInfo } from "$lib/modules/file";
|
||||
import { masterKeyStore, type DirectoryInfo, type FileInfo } from "$lib/stores";
|
||||
import {
|
||||
fileUploadStatusStore,
|
||||
masterKeyStore,
|
||||
type DirectoryInfo,
|
||||
type FileInfo,
|
||||
type FileUploadStatus,
|
||||
} from "$lib/stores";
|
||||
import File from "./File.svelte";
|
||||
import SubDirectory from "./SubDirectory.svelte";
|
||||
import { SortBy, sortEntries } from "./service";
|
||||
import UploadingFile from "./UploadingFile.svelte";
|
||||
import type { SelectedDirectoryEntry } from "../service";
|
||||
|
||||
interface Props {
|
||||
@@ -17,37 +24,102 @@
|
||||
|
||||
let { info, onEntryClick, onEntryMenuClick, sortBy = SortBy.NAME_ASC }: Props = $props();
|
||||
|
||||
let subDirectoryInfos: Writable<DirectoryInfo | null>[] = $state([]);
|
||||
let fileInfos: Writable<FileInfo | null>[] = $state([]);
|
||||
interface DirectoryEntry {
|
||||
name?: string;
|
||||
info: Writable<DirectoryInfo | null>;
|
||||
}
|
||||
|
||||
type FileEntry =
|
||||
| {
|
||||
type: "file";
|
||||
name?: string;
|
||||
info: Writable<FileInfo | null>;
|
||||
}
|
||||
| {
|
||||
type: "uploading-file";
|
||||
name: string;
|
||||
info: Writable<FileUploadStatus>;
|
||||
};
|
||||
|
||||
let subDirectories: DirectoryEntry[] = $state([]);
|
||||
let files: FileEntry[] = $state([]);
|
||||
|
||||
$effect(() => {
|
||||
// TODO: Fix duplicated requests
|
||||
|
||||
subDirectoryInfos = info.subDirectoryIds.map((id) =>
|
||||
getDirectoryInfo(id, $masterKeyStore?.get(1)?.key!),
|
||||
subDirectories = info.subDirectoryIds.map((id) => {
|
||||
const info = getDirectoryInfo(id, $masterKeyStore?.get(1)?.key!);
|
||||
return { name: get(info)?.name, info };
|
||||
});
|
||||
files = info.fileIds
|
||||
.map((id): FileEntry => {
|
||||
const info = getFileInfo(id, $masterKeyStore?.get(1)?.key!);
|
||||
return {
|
||||
type: "file",
|
||||
name: get(info)?.name,
|
||||
info,
|
||||
};
|
||||
})
|
||||
.concat(
|
||||
$fileUploadStatusStore
|
||||
.filter((statusStore) => {
|
||||
const status = get(statusStore);
|
||||
return (
|
||||
status.parentId === info.id &&
|
||||
status.status !== "uploaded" &&
|
||||
status.status !== "canceled" &&
|
||||
status.status !== "error"
|
||||
);
|
||||
})
|
||||
.map(
|
||||
(status): FileEntry => ({
|
||||
type: "uploading-file",
|
||||
name: get(status).name,
|
||||
info: status,
|
||||
}),
|
||||
),
|
||||
);
|
||||
fileInfos = info.fileIds.map((id) => getFileInfo(id, $masterKeyStore?.get(1)?.key!));
|
||||
|
||||
const sort = () => {
|
||||
sortEntries(subDirectoryInfos, sortBy);
|
||||
sortEntries(fileInfos, sortBy);
|
||||
sortEntries(subDirectories, sortBy);
|
||||
sortEntries(files, sortBy);
|
||||
};
|
||||
sort();
|
||||
|
||||
return untrack(() => {
|
||||
const unsubscribes = subDirectoryInfos
|
||||
.map((subDirectoryInfo) => subDirectoryInfo.subscribe(sort))
|
||||
.concat(fileInfos.map((fileInfo) => fileInfo.subscribe(sort)));
|
||||
const unsubscribes = subDirectories
|
||||
.map((subDirectory) =>
|
||||
subDirectory.info.subscribe((value) => {
|
||||
if (subDirectory.name === value?.name) return;
|
||||
subDirectory.name = value?.name;
|
||||
sort();
|
||||
}),
|
||||
)
|
||||
.concat(
|
||||
files.map((file) =>
|
||||
file.info.subscribe((value) => {
|
||||
if (file.name === value?.name) return;
|
||||
file.name = value?.name;
|
||||
sort();
|
||||
}),
|
||||
),
|
||||
);
|
||||
return () => unsubscribes.forEach((unsubscribe) => unsubscribe());
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if info.subDirectoryIds.length + info.fileIds.length > 0}
|
||||
{#if subDirectories.length + files.length > 0}
|
||||
<div class="pb-[4.5rem]">
|
||||
{#each subDirectoryInfos as subDirectory}
|
||||
<SubDirectory info={subDirectory} onclick={onEntryClick} onOpenMenuClick={onEntryMenuClick} />
|
||||
{#each subDirectories as { info }}
|
||||
<SubDirectory {info} onclick={onEntryClick} onOpenMenuClick={onEntryMenuClick} />
|
||||
{/each}
|
||||
{#each fileInfos as file}
|
||||
<File info={file} onclick={onEntryClick} onOpenMenuClick={onEntryMenuClick} />
|
||||
{#each files as file}
|
||||
{#if file.type === "file"}
|
||||
<File info={file.info} onclick={onEntryClick} onOpenMenuClick={onEntryMenuClick} />
|
||||
{:else}
|
||||
<UploadingFile info={file.info} />
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<script lang="ts">
|
||||
import type { Writable } from "svelte/store";
|
||||
import type { FileUploadStatus } from "$lib/stores";
|
||||
import { formatUploadProgress, formatUploadRate } from "./service";
|
||||
|
||||
import IconDraft from "~icons/material-symbols/draft";
|
||||
|
||||
interface Props {
|
||||
info: Writable<FileUploadStatus>;
|
||||
}
|
||||
|
||||
let { info }: Props = $props();
|
||||
</script>
|
||||
|
||||
{#if $info.status !== "uploaded" && $info.status !== "canceled" && $info.status !== "error"}
|
||||
<div class="flex h-14 items-center gap-x-4 p-2">
|
||||
<div class="flex-shrink-0 text-lg">
|
||||
<IconDraft class="text-gray-600" />
|
||||
</div>
|
||||
<div class="flex flex-grow flex-col overflow-hidden">
|
||||
<p title={$info.name} class="truncate font-medium text-gray-800">
|
||||
{$info.name}
|
||||
</p>
|
||||
<p class="text-xs text-gray-800">
|
||||
{#if $info.status === "encryption-pending"}
|
||||
준비 중
|
||||
{:else if $info.status === "encrypting"}
|
||||
암호화하는 중
|
||||
{:else if $info.status === "upload-pending"}
|
||||
업로드를 기다리는 중
|
||||
{:else if $info.status === "uploading"}
|
||||
전송됨 {formatUploadProgress($info.progress)} · {formatUploadRate($info.rate)}
|
||||
{/if}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -1,5 +1,4 @@
|
||||
import { get, type Writable } from "svelte/store";
|
||||
import type { DirectoryInfo, FileInfo } from "$lib/stores";
|
||||
import { formatFileSize } from "$lib/modules/util";
|
||||
|
||||
export { formatDateTime } from "$lib/modules/util";
|
||||
|
||||
@@ -8,17 +7,19 @@ export enum SortBy {
|
||||
NAME_DESC,
|
||||
}
|
||||
|
||||
type SortFunc = (a: DirectoryInfo | FileInfo | null, b: DirectoryInfo | FileInfo | null) => number;
|
||||
type SortFunc = (a?: string, b?: string) => number;
|
||||
|
||||
const sortByNameAsc: SortFunc = (a, b) => {
|
||||
if (a && b) return a.name!.localeCompare(b.name!);
|
||||
if (a && b) return a.localeCompare(b);
|
||||
if (a) return -1;
|
||||
if (b) return 1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
const sortByNameDesc: SortFunc = (a, b) => -sortByNameAsc(a, b);
|
||||
|
||||
export const sortEntries = <T extends DirectoryInfo | FileInfo>(
|
||||
entries: Writable<T | null>[],
|
||||
export const sortEntries = <T extends { name?: string }>(
|
||||
entries: T[],
|
||||
sortBy: SortBy = SortBy.NAME_ASC,
|
||||
) => {
|
||||
let sortFunc: SortFunc;
|
||||
@@ -28,5 +29,13 @@ export const sortEntries = <T extends DirectoryInfo | FileInfo>(
|
||||
sortFunc = sortByNameDesc;
|
||||
}
|
||||
|
||||
entries.sort((a, b) => sortFunc(get(a), get(b)));
|
||||
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`;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user