mirror of
https://github.com/kmc7468/arkvault.git
synced 2025-12-14 22:08:45 +00:00
정렬 관련 코드를 derived를 사용하도록 재작성
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { untrack } from "svelte";
|
||||
import { get, type Writable } from "svelte/store";
|
||||
import { derived } from "svelte/store";
|
||||
import { CheckBox } from "$lib/components/atoms";
|
||||
import { SubCategories, type SelectedCategory } from "$lib/components/molecules";
|
||||
import type { CategoryInfo } from "$lib/modules/filesystem";
|
||||
import { getFileInfo, type FileInfoStore } from "$lib/modules/filesystem2";
|
||||
import { getFileInfo } from "$lib/modules/filesystem2";
|
||||
import { SortBy, sortEntries } from "$lib/modules/util";
|
||||
import { masterKeyStore } from "$lib/stores";
|
||||
import File from "./File.svelte";
|
||||
@@ -34,37 +33,35 @@
|
||||
isFileRecursive = $bindable(),
|
||||
}: Props = $props();
|
||||
|
||||
let files: { name?: string; info: FileInfoStore; isRecursive: boolean }[] = $state([]);
|
||||
|
||||
$effect(() => {
|
||||
files =
|
||||
info.files
|
||||
?.filter(({ isRecursive }) => isFileRecursive || !isRecursive)
|
||||
.map(({ id, isRecursive }) => {
|
||||
const info = getFileInfo(id, $masterKeyStore?.get(1)?.key!);
|
||||
return {
|
||||
name: get(info).data?.name,
|
||||
info,
|
||||
isRecursive,
|
||||
};
|
||||
}) ?? [];
|
||||
|
||||
const sort = () => {
|
||||
sortEntries(files, sortBy);
|
||||
};
|
||||
return untrack(() => {
|
||||
sort();
|
||||
|
||||
const unsubscribes = files.map((file) =>
|
||||
file.info.subscribe((value) => {
|
||||
if (file.name === value.data?.name) return;
|
||||
file.name = value.data?.name;
|
||||
sort();
|
||||
}),
|
||||
);
|
||||
return () => unsubscribes.forEach((unsubscribe) => unsubscribe());
|
||||
});
|
||||
});
|
||||
let fileInfos = $derived(
|
||||
info.files
|
||||
?.filter(({ isRecursive }) => isFileRecursive || !isRecursive)
|
||||
.map(({ id, isRecursive }) => ({
|
||||
info: getFileInfo(id, $masterKeyStore?.get(1)?.key!),
|
||||
isRecursive,
|
||||
})) ?? [],
|
||||
);
|
||||
let files = $derived(
|
||||
derived(
|
||||
fileInfos.map(({ info }) => info),
|
||||
(infos) => {
|
||||
const files = infos
|
||||
.map(($info, i) => {
|
||||
if ($info.status === "success") {
|
||||
return {
|
||||
name: $info.data.name,
|
||||
isRecursive: fileInfos[i]!.isRecursive,
|
||||
info: $info.data,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
})
|
||||
.filter((info) => info !== undefined);
|
||||
sortEntries(files, sortBy);
|
||||
return files;
|
||||
},
|
||||
),
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="space-y-4">
|
||||
@@ -90,7 +87,7 @@
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
{#key info}
|
||||
{#each files as { info, isRecursive }}
|
||||
{#each $files as { info, isRecursive }}
|
||||
<File
|
||||
{info}
|
||||
onclick={onFileClick}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
<script lang="ts">
|
||||
import type { Writable } from "svelte/store";
|
||||
import { ActionEntryButton } from "$lib/components/atoms";
|
||||
import { DirectoryEntryLabel } from "$lib/components/molecules";
|
||||
import type { FileInfo, FileInfoStore } from "$lib/modules/filesystem2";
|
||||
import type { FileInfo } from "$lib/modules/filesystem2";
|
||||
import { requestFileThumbnailDownload, type SelectedFile } from "./service";
|
||||
|
||||
import IconClose from "~icons/material-symbols/close";
|
||||
|
||||
interface Props {
|
||||
info: FileInfoStore;
|
||||
info: FileInfo;
|
||||
onclick: (selectedFile: SelectedFile) => void;
|
||||
onRemoveClick?: (selectedFile: SelectedFile) => void;
|
||||
}
|
||||
@@ -18,22 +17,22 @@
|
||||
let thumbnail: string | undefined = $state();
|
||||
|
||||
const openFile = () => {
|
||||
const { id, dataKey, dataKeyVersion, name } = $info.data as FileInfo;
|
||||
const { id, dataKey, dataKeyVersion, name } = info;
|
||||
if (!dataKey || !dataKeyVersion) return; // TODO: Error handling
|
||||
|
||||
onclick({ id, dataKey, dataKeyVersion, name });
|
||||
};
|
||||
|
||||
const removeFile = () => {
|
||||
const { id, dataKey, dataKeyVersion, name } = $info.data as FileInfo;
|
||||
const { id, dataKey, dataKeyVersion, name } = info;
|
||||
if (!dataKey || !dataKeyVersion) return; // TODO: Error handling
|
||||
|
||||
onRemoveClick!({ id, dataKey, dataKeyVersion, name });
|
||||
};
|
||||
|
||||
$effect(() => {
|
||||
if ($info.data?.dataKey) {
|
||||
requestFileThumbnailDownload($info.data.id, $info.data.dataKey)
|
||||
if (info.dataKey) {
|
||||
requestFileThumbnailDownload(info.id, info.dataKey)
|
||||
.then((thumbnailUrl) => {
|
||||
thumbnail = thumbnailUrl ?? undefined;
|
||||
})
|
||||
@@ -47,13 +46,11 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if $info.status === "success"}
|
||||
<ActionEntryButton
|
||||
class="h-12"
|
||||
onclick={openFile}
|
||||
actionButtonIcon={onRemoveClick && IconClose}
|
||||
onActionButtonClick={removeFile}
|
||||
>
|
||||
<DirectoryEntryLabel type="file" {thumbnail} name={$info.data.name} />
|
||||
</ActionEntryButton>
|
||||
{/if}
|
||||
<ActionEntryButton
|
||||
class="h-12"
|
||||
onclick={openFile}
|
||||
actionButtonIcon={onRemoveClick && IconClose}
|
||||
onActionButtonClick={removeFile}
|
||||
>
|
||||
<DirectoryEntryLabel type="file" {thumbnail} name={info.name} />
|
||||
</ActionEntryButton>
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
export const callGetApi = async (input: RequestInfo, fetchInternal = fetch) => {
|
||||
return await fetchInternal(input);
|
||||
interface FetchOptions {
|
||||
fetch?: typeof fetch;
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
|
||||
export const callGetApi = async (
|
||||
input: RequestInfo,
|
||||
{ fetch = globalThis.fetch, signal }: FetchOptions = {},
|
||||
) => {
|
||||
return await fetch(input, { method: "GET", signal });
|
||||
};
|
||||
|
||||
export const callPostApi = async <T>(input: RequestInfo, payload?: T, fetchInternal = fetch) => {
|
||||
return await fetchInternal(input, {
|
||||
export const callPostApi = async <T>(
|
||||
input: RequestInfo,
|
||||
payload?: T,
|
||||
{ fetch = globalThis.fetch, signal }: FetchOptions = {},
|
||||
) => {
|
||||
return await fetch(input, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: payload ? JSON.stringify(payload) : undefined,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useQueryClient, createQuery, createMutation } from "@tanstack/svelte-query";
|
||||
import { browser } from "$app/environment";
|
||||
import { callGetApi, callPostApi } from "$lib/hooks";
|
||||
import {
|
||||
getDirectoryInfos as getDirectoryInfosFromIndexedDB,
|
||||
@@ -44,17 +43,11 @@ export type DirectoryInfo =
|
||||
subDirectoryIds: number[];
|
||||
fileIds: number[];
|
||||
};
|
||||
export type SubDirectoryInfo = DirectoryInfo & { id: number };
|
||||
|
||||
const initializedIds = new Set<DirectoryId>();
|
||||
let temporaryIdCounter = -1;
|
||||
|
||||
const getInitialDirectoryInfo = async (id: DirectoryId) => {
|
||||
if (!browser || initializedIds.has(id)) {
|
||||
return undefined;
|
||||
} else {
|
||||
initializedIds.add(id);
|
||||
}
|
||||
|
||||
const [directory, subDirectories, files] = await Promise.all([
|
||||
id !== "root" ? getDirectoryInfoFromIndexedDB(id) : undefined,
|
||||
getDirectoryInfosFromIndexedDB(id),
|
||||
@@ -72,16 +65,18 @@ const getInitialDirectoryInfo = async (id: DirectoryId) => {
|
||||
};
|
||||
|
||||
export const getDirectoryInfo = (id: DirectoryId, masterKey: CryptoKey) => {
|
||||
const queryClient = useQueryClient();
|
||||
getInitialDirectoryInfo(id).then((info) => {
|
||||
if (info && !queryClient.getQueryData(["directory", id])) {
|
||||
queryClient.setQueryData<DirectoryInfo>(["directory", id], info);
|
||||
}
|
||||
}); // Intended
|
||||
return createQuery<DirectoryInfo>({
|
||||
queryKey: ["directory", id],
|
||||
queryFn: async () => {
|
||||
const res = await callGetApi(`/api/directory/${id}`); // TODO: 404
|
||||
queryFn: async ({ client, signal }) => {
|
||||
if (!client.getQueryData(["directory", id])) {
|
||||
const initialInfo = await getInitialDirectoryInfo(id);
|
||||
if (initialInfo) {
|
||||
setTimeout(() => client.invalidateQueries({ queryKey: ["directory", id] }), 0);
|
||||
return initialInfo;
|
||||
}
|
||||
}
|
||||
|
||||
const res = await callGetApi(`/api/directory/${id}`, { signal }); // TODO: 404
|
||||
const {
|
||||
metadata,
|
||||
subDirectories: subDirectoryIds,
|
||||
@@ -202,7 +197,7 @@ export const useDirectoryRename = () => {
|
||||
onMutate: async ({ id, newName }) => {
|
||||
await queryClient.cancelQueries({ queryKey: ["directory", id] });
|
||||
|
||||
const prevInfo = queryClient.getQueryData<DirectoryInfo & { id: number }>(["directory", id]);
|
||||
const prevInfo = queryClient.getQueryData<SubDirectoryInfo>(["directory", id]);
|
||||
if (prevInfo) {
|
||||
queryClient.setQueryData<DirectoryInfo>(["directory", id], {
|
||||
...prevInfo,
|
||||
@@ -214,7 +209,7 @@ export const useDirectoryRename = () => {
|
||||
},
|
||||
onError: (_error, { id }, context) => {
|
||||
if (context?.oldName) {
|
||||
queryClient.setQueryData<DirectoryInfo & { id: number }>(["directory", id], (prevInfo) => {
|
||||
queryClient.setQueryData<SubDirectoryInfo>(["directory", id], (prevInfo) => {
|
||||
if (!prevInfo) return undefined;
|
||||
return { ...prevInfo, name: context.oldName! };
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useQueryClient, createQuery, createMutation } from "@tanstack/svelte-query";
|
||||
import { browser } from "$app/environment";
|
||||
import { callGetApi, callPostApi } from "$lib/hooks";
|
||||
import {
|
||||
getFileInfo as getFileInfoFromIndexedDB,
|
||||
@@ -26,31 +25,23 @@ export interface FileInfo {
|
||||
categoryIds: number[];
|
||||
}
|
||||
|
||||
const initializedFileIds = new Set<number>();
|
||||
|
||||
const getInitialFileInfo = async (id: number) => {
|
||||
if (!browser || initializedFileIds.has(id)) {
|
||||
return undefined;
|
||||
}
|
||||
initializedFileIds.add(id);
|
||||
return await getFileInfoFromIndexedDB(id);
|
||||
};
|
||||
|
||||
const decryptDate = async (ciphertext: string, iv: string, dataKey: CryptoKey) => {
|
||||
return new Date(parseInt(await decryptString(ciphertext, iv, dataKey), 10));
|
||||
};
|
||||
|
||||
export const getFileInfo = (id: number, masterKey: CryptoKey) => {
|
||||
const queryClient = useQueryClient();
|
||||
getInitialFileInfo(id).then((info) => {
|
||||
if (info && !queryClient.getQueryData(["file", id])) {
|
||||
queryClient.setQueryData<FileInfo>(["file", id], info);
|
||||
}
|
||||
}); // Intended
|
||||
return createQuery<FileInfo>({
|
||||
queryKey: ["file", id],
|
||||
queryFn: async () => {
|
||||
const res = await callGetApi(`/api/file/${id}`); // TODO: 404
|
||||
queryFn: async ({ client, signal }) => {
|
||||
if (!client.getQueryData(["file", id])) {
|
||||
const initialInfo = await getFileInfoFromIndexedDB(id);
|
||||
if (initialInfo) {
|
||||
setTimeout(() => client.invalidateQueries({ queryKey: ["file", id] }), 0);
|
||||
return initialInfo;
|
||||
}
|
||||
}
|
||||
|
||||
const res = await callGetApi(`/api/file/${id}`, { signal }); // TODO: 404
|
||||
const metadata: FileInfoResponse = await res.json();
|
||||
|
||||
const { dataKey } = await unwrapDataKey(metadata.dek, masterKey);
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { MissingThumbnailFileScanResponse } from "$lib/server/schemas";
|
||||
import type { PageLoad } from "./$types";
|
||||
|
||||
export const load: PageLoad = async ({ fetch }) => {
|
||||
const res = await callPostApi("/api/file/scanMissingThumbnails", undefined, fetch);
|
||||
const res = await callPostApi("/api/file/scanMissingThumbnails", undefined, { fetch });
|
||||
if (!res.ok) {
|
||||
error(500, "Internal server error");
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
<UploadStatusCard onclick={() => goto("/file/uploads")} />
|
||||
<DownloadStatusCard onclick={() => goto("/file/downloads")} />
|
||||
</div>
|
||||
{#key $info}
|
||||
{#key $info.data.id}
|
||||
<DirectoryEntries
|
||||
info={$info.data}
|
||||
onEntryClick={({ type, id }) => goto(`/${type}/${id}`)}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { untrack } from "svelte";
|
||||
import { get, type Writable } from "svelte/store";
|
||||
import { derived } from "svelte/store";
|
||||
import {
|
||||
getDirectoryInfo,
|
||||
getFileInfo,
|
||||
type DirectoryInfo,
|
||||
type DirectoryInfoStore,
|
||||
type FileInfoStore,
|
||||
type SubDirectoryInfo,
|
||||
type FileInfo,
|
||||
} from "$lib/modules/filesystem2";
|
||||
import { SortBy, sortEntries } from "$lib/modules/util";
|
||||
import {
|
||||
@@ -31,96 +30,84 @@
|
||||
|
||||
interface DirectoryEntry {
|
||||
name?: string;
|
||||
info: DirectoryInfoStore;
|
||||
info: SubDirectoryInfo;
|
||||
}
|
||||
|
||||
type FileEntry =
|
||||
| {
|
||||
type: "file";
|
||||
name?: string;
|
||||
info: FileInfoStore;
|
||||
info: FileInfo;
|
||||
}
|
||||
| {
|
||||
type: "uploading-file";
|
||||
name: string;
|
||||
info: Writable<FileUploadStatus>;
|
||||
info: FileUploadStatus;
|
||||
};
|
||||
|
||||
let subDirectories: DirectoryEntry[] = $state([]);
|
||||
let files: FileEntry[] = $state([]);
|
||||
|
||||
$effect(() => {
|
||||
// TODO: Fix duplicated requests
|
||||
|
||||
subDirectories = info.subDirectoryIds.map((id) => {
|
||||
const info = getDirectoryInfo(id, $masterKeyStore?.get(1)?.key!);
|
||||
return { name: get(info).data?.name, info };
|
||||
});
|
||||
files = info.fileIds
|
||||
.map((id): FileEntry => {
|
||||
const info = getFileInfo(id, $masterKeyStore?.get(1)?.key!);
|
||||
return {
|
||||
type: "file",
|
||||
name: get(info).data?.name,
|
||||
info,
|
||||
};
|
||||
})
|
||||
.concat(
|
||||
$fileUploadStatusStore
|
||||
.filter((statusStore) => {
|
||||
const { parentId, status } = get(statusStore);
|
||||
return parentId === info.id && isFileUploading(status);
|
||||
})
|
||||
.map((status) => ({
|
||||
type: "uploading-file",
|
||||
name: get(status).name,
|
||||
info: status,
|
||||
})),
|
||||
);
|
||||
|
||||
const sort = () => {
|
||||
sortEntries(subDirectories, sortBy);
|
||||
sortEntries(files, sortBy);
|
||||
};
|
||||
return untrack(() => {
|
||||
sort();
|
||||
|
||||
const unsubscribes = subDirectories
|
||||
.map((subDirectory) =>
|
||||
subDirectory.info.subscribe((value) => {
|
||||
if (subDirectory.name === value.data?.name) return;
|
||||
subDirectory.name = value.data?.name;
|
||||
sort();
|
||||
}),
|
||||
)
|
||||
.concat(
|
||||
files.map((file) => {
|
||||
if (file.type === "file") {
|
||||
return file.info.subscribe((value) => {
|
||||
if (file.name === value.data?.name) return;
|
||||
file.name = value.data?.name;
|
||||
sort();
|
||||
});
|
||||
} else {
|
||||
return file.info.subscribe((value) => {
|
||||
if (file.name === value.name) return;
|
||||
file.name = value.name;
|
||||
sort();
|
||||
});
|
||||
}
|
||||
}),
|
||||
);
|
||||
return () => unsubscribes.forEach((unsubscribe) => unsubscribe());
|
||||
});
|
||||
});
|
||||
let subDirectories = $derived(
|
||||
derived(
|
||||
info.subDirectoryIds.map((id) => getDirectoryInfo(id, $masterKeyStore?.get(1)?.key!)),
|
||||
(infos) => {
|
||||
const subDirectories = infos
|
||||
.filter(($info) => $info.status === "success")
|
||||
.map(
|
||||
($info) =>
|
||||
({
|
||||
name: $info.data.name,
|
||||
info: $info.data as SubDirectoryInfo,
|
||||
}) satisfies DirectoryEntry,
|
||||
);
|
||||
sortEntries(subDirectories, sortBy);
|
||||
return subDirectories;
|
||||
},
|
||||
),
|
||||
);
|
||||
let files = $derived(
|
||||
derived(
|
||||
info.fileIds.map((id) => getFileInfo(id, $masterKeyStore?.get(1)?.key!)),
|
||||
(infos) =>
|
||||
infos
|
||||
.filter(($info) => $info.status === "success")
|
||||
.map(
|
||||
($info) =>
|
||||
({
|
||||
type: "file",
|
||||
name: $info.data.name,
|
||||
info: $info.data,
|
||||
}) satisfies FileEntry,
|
||||
),
|
||||
),
|
||||
);
|
||||
let uploadingFiles = $derived(
|
||||
derived($fileUploadStatusStore, (statuses) =>
|
||||
statuses
|
||||
.filter(({ parentId, status }) => parentId === info.id && isFileUploading(status))
|
||||
.map(
|
||||
($status) =>
|
||||
({
|
||||
type: "uploading-file",
|
||||
name: $status.name,
|
||||
info: $status,
|
||||
}) satisfies FileEntry,
|
||||
),
|
||||
),
|
||||
);
|
||||
let everyFiles = $derived(
|
||||
derived([files, uploadingFiles], ([$files, $uploadingFiles]) => {
|
||||
const allFiles = [...$files, ...$uploadingFiles];
|
||||
sortEntries(allFiles, sortBy);
|
||||
return allFiles;
|
||||
}),
|
||||
);
|
||||
</script>
|
||||
|
||||
{#if subDirectories.length + files.length > 0}
|
||||
{#if $subDirectories.length + $everyFiles.length > 0}
|
||||
<div class="space-y-1 pb-[4.5rem]">
|
||||
{#each subDirectories as { info }}
|
||||
{#each $subDirectories as { info }}
|
||||
<SubDirectory {info} onclick={onEntryClick} onOpenMenuClick={onEntryMenuClick} />
|
||||
{/each}
|
||||
{#each files as file}
|
||||
{#each $everyFiles as file}
|
||||
{#if file.type === "file"}
|
||||
<File info={file.info} onclick={onEntryClick} onOpenMenuClick={onEntryMenuClick} />
|
||||
{:else}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { ActionEntryButton } from "$lib/components/atoms";
|
||||
import { DirectoryEntryLabel } from "$lib/components/molecules";
|
||||
import type { FileInfo, FileInfoStore } from "$lib/modules/filesystem2";
|
||||
import type { FileInfo } from "$lib/modules/filesystem2";
|
||||
import { formatDateTime } from "$lib/modules/util";
|
||||
import { requestFileThumbnailDownload } from "./service";
|
||||
import type { SelectedEntry } from "../service.svelte";
|
||||
@@ -9,7 +9,7 @@
|
||||
import IconMoreVert from "~icons/material-symbols/more-vert";
|
||||
|
||||
interface Props {
|
||||
info: FileInfoStore;
|
||||
info: FileInfo;
|
||||
onclick: (selectedEntry: SelectedEntry) => void;
|
||||
onOpenMenuClick: (selectedEntry: SelectedEntry) => void;
|
||||
}
|
||||
@@ -19,22 +19,22 @@
|
||||
let thumbnail: string | undefined = $state();
|
||||
|
||||
const openFile = () => {
|
||||
const { id, dataKey, dataKeyVersion, name } = $info.data as FileInfo;
|
||||
const { id, dataKey, dataKeyVersion, name } = info;
|
||||
if (!dataKey || !dataKeyVersion) return; // TODO: Error handling
|
||||
|
||||
onclick({ type: "file", id, dataKey, dataKeyVersion, name });
|
||||
};
|
||||
|
||||
const openMenu = () => {
|
||||
const { id, dataKey, dataKeyVersion, name } = $info.data as FileInfo;
|
||||
const { id, dataKey, dataKeyVersion, name } = info;
|
||||
if (!dataKey || !dataKeyVersion) return; // TODO: Error handling
|
||||
|
||||
onOpenMenuClick({ type: "file", id, dataKey, dataKeyVersion, name });
|
||||
};
|
||||
|
||||
$effect(() => {
|
||||
if ($info.data?.dataKey) {
|
||||
requestFileThumbnailDownload($info.data.id, $info.data.dataKey)
|
||||
if (info.dataKey) {
|
||||
requestFileThumbnailDownload(info.id, info.dataKey)
|
||||
.then((thumbnailUrl) => {
|
||||
thumbnail = thumbnailUrl ?? undefined;
|
||||
})
|
||||
@@ -48,18 +48,16 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if $info.status === "success"}
|
||||
<ActionEntryButton
|
||||
class="h-14"
|
||||
onclick={openFile}
|
||||
actionButtonIcon={IconMoreVert}
|
||||
onActionButtonClick={openMenu}
|
||||
>
|
||||
<DirectoryEntryLabel
|
||||
type="file"
|
||||
{thumbnail}
|
||||
name={$info.data.name}
|
||||
subtext={formatDateTime($info.data.createdAt ?? $info.data.lastModifiedAt)}
|
||||
/>
|
||||
</ActionEntryButton>
|
||||
{/if}
|
||||
<ActionEntryButton
|
||||
class="h-14"
|
||||
onclick={openFile}
|
||||
actionButtonIcon={IconMoreVert}
|
||||
onActionButtonClick={openMenu}
|
||||
>
|
||||
<DirectoryEntryLabel
|
||||
type="file"
|
||||
{thumbnail}
|
||||
name={info.name}
|
||||
subtext={formatDateTime(info.createdAt ?? info.lastModifiedAt)}
|
||||
/>
|
||||
</ActionEntryButton>
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { ActionEntryButton } from "$lib/components/atoms";
|
||||
import { DirectoryEntryLabel } from "$lib/components/molecules";
|
||||
import type { DirectoryInfo, DirectoryInfoStore } from "$lib/modules/filesystem2";
|
||||
import type { SubDirectoryInfo } from "$lib/modules/filesystem2";
|
||||
import type { SelectedEntry } from "../service.svelte";
|
||||
|
||||
import IconMoreVert from "~icons/material-symbols/more-vert";
|
||||
|
||||
type SubDirectoryInfo = DirectoryInfo & { id: number };
|
||||
|
||||
interface Props {
|
||||
info: DirectoryInfoStore;
|
||||
info: SubDirectoryInfo;
|
||||
onclick: (selectedEntry: SelectedEntry) => void;
|
||||
onOpenMenuClick: (selectedEntry: SelectedEntry) => void;
|
||||
}
|
||||
@@ -17,27 +15,25 @@
|
||||
let { info, onclick, onOpenMenuClick }: Props = $props();
|
||||
|
||||
const openDirectory = () => {
|
||||
const { id, dataKey, dataKeyVersion, name } = $info.data as SubDirectoryInfo;
|
||||
const { id, dataKey, dataKeyVersion, name } = info;
|
||||
if (!dataKey || !dataKeyVersion) return; // TODO: Error handling
|
||||
|
||||
onclick({ type: "directory", id, dataKey, dataKeyVersion, name });
|
||||
};
|
||||
|
||||
const openMenu = () => {
|
||||
const { id, dataKey, dataKeyVersion, name } = $info.data as SubDirectoryInfo;
|
||||
const { id, dataKey, dataKeyVersion, name } = info;
|
||||
if (!dataKey || !dataKeyVersion) return; // TODO: Error handling
|
||||
|
||||
onOpenMenuClick({ type: "directory", id, dataKey, dataKeyVersion, name });
|
||||
};
|
||||
</script>
|
||||
|
||||
{#if $info}
|
||||
<ActionEntryButton
|
||||
class="h-14"
|
||||
onclick={openDirectory}
|
||||
actionButtonIcon={IconMoreVert}
|
||||
onActionButtonClick={openMenu}
|
||||
>
|
||||
<DirectoryEntryLabel type="directory" name={$info.data?.name!} />
|
||||
</ActionEntryButton>
|
||||
{/if}
|
||||
<ActionEntryButton
|
||||
class="h-14"
|
||||
onclick={openDirectory}
|
||||
actionButtonIcon={IconMoreVert}
|
||||
onActionButtonClick={openMenu}
|
||||
>
|
||||
<DirectoryEntryLabel type="directory" name={info.name} />
|
||||
</ActionEntryButton>
|
||||
|
||||
@@ -1,36 +1,35 @@
|
||||
<script lang="ts">
|
||||
import type { Writable } from "svelte/store";
|
||||
import { formatNetworkSpeed } from "$lib/modules/util";
|
||||
import { isFileUploading, type FileUploadStatus } from "$lib/stores";
|
||||
|
||||
import IconDraft from "~icons/material-symbols/draft";
|
||||
|
||||
interface Props {
|
||||
status: Writable<FileUploadStatus>;
|
||||
status: FileUploadStatus;
|
||||
}
|
||||
|
||||
let { status }: Props = $props();
|
||||
</script>
|
||||
|
||||
{#if isFileUploading($status.status)}
|
||||
{#if isFileUploading(status.status)}
|
||||
<div class="flex h-14 gap-x-4 p-2">
|
||||
<div class="flex h-10 w-10 flex-shrink-0 items-center justify-center text-xl">
|
||||
<IconDraft class="text-gray-600" />
|
||||
</div>
|
||||
<div class="flex flex-grow flex-col overflow-hidden text-gray-800">
|
||||
<p title={$status.name} class="truncate font-medium">
|
||||
{$status.name}
|
||||
<p title={status.name} class="truncate font-medium">
|
||||
{status.name}
|
||||
</p>
|
||||
<p class="text-xs">
|
||||
{#if $status.status === "encryption-pending"}
|
||||
{#if status.status === "encryption-pending"}
|
||||
준비 중
|
||||
{:else if $status.status === "encrypting"}
|
||||
{:else if status.status === "encrypting"}
|
||||
암호화하는 중
|
||||
{:else if $status.status === "upload-pending"}
|
||||
{:else if status.status === "upload-pending"}
|
||||
업로드를 기다리는 중
|
||||
{:else if $status.status === "uploading"}
|
||||
전송됨 {Math.floor(($status.progress ?? 0) * 100)}% ·
|
||||
{formatNetworkSpeed(($status.rate ?? 0) * 8)}
|
||||
{:else if status.status === "uploading"}
|
||||
전송됨 {Math.floor((status.progress ?? 0) * 100)}% ·
|
||||
{formatNetworkSpeed((status.rate ?? 0) * 8)}
|
||||
{/if}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { UserInfoResponse } from "$lib/server/schemas";
|
||||
import type { PageLoad } from "./$types";
|
||||
|
||||
export const load: PageLoad = async ({ fetch }) => {
|
||||
const res = await callGetApi("/api/user", fetch);
|
||||
const res = await callGetApi("/api/user", { fetch });
|
||||
if (!res.ok) {
|
||||
error(500, "Internal server error");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user