diff --git a/src/lib/modules/filesystem.ts b/src/lib/modules/filesystem.ts index 40e8cd4..6009b60 100644 --- a/src/lib/modules/filesystem.ts +++ b/src/lib/modules/filesystem.ts @@ -40,6 +40,7 @@ export type DirectoryInfo = export interface FileInfo { id: number; + parentId: DirectoryId; dataKey?: CryptoKey; dataKeyVersion?: Date; contentType: string; @@ -199,6 +200,7 @@ const fetchFileInfoFromServer = async ( info.set({ id, + parentId: metadata.parent, dataKey, dataKeyVersion: new Date(metadata.dekVersion), contentType: metadata.contentType, diff --git a/src/routes/(fullscreen)/file/[id]/+page.svelte b/src/routes/(fullscreen)/file/[id]/+page.svelte index 4047e7c..3249bf2 100644 --- a/src/routes/(fullscreen)/file/[id]/+page.svelte +++ b/src/routes/(fullscreen)/file/[id]/+page.svelte @@ -3,6 +3,7 @@ import { untrack } from "svelte"; import { get, type Writable } from "svelte/store"; import { goto } from "$app/navigation"; + import { page } from "$app/state"; import { FullscreenDiv } from "$lib/components/atoms"; import { Categories, IconEntryButton, TopBar } from "$lib/components/molecules"; import { @@ -21,7 +22,9 @@ requestThumbnailUpload, requestFileAdditionToCategory, } from "./service"; + import TopBarMenu from "./TopBarMenu.svelte"; + import IconMoreVert from "~icons/material-symbols/more-vert"; import IconCamera from "~icons/material-symbols/camera"; import IconClose from "~icons/material-symbols/close"; import IconAddCircle from "~icons/material-symbols/add-circle"; @@ -31,6 +34,7 @@ let info: Writable | undefined = $state(); let categories: Writable[] = $state([]); + let isMenuOpen = $state(false); let isAddToCategoryBottomSheetOpen = $state(false); let downloadStatus = $derived( @@ -42,30 +46,26 @@ let isDownloadRequested = $state(false); let viewerType: "image" | "video" | undefined = $state(); + let fileBlob: Blob | undefined = $state(); let fileBlobUrl: string | undefined = $state(); - let heicBlob: Blob | undefined = $state(); let videoElement: HTMLVideoElement | undefined = $state(); const updateViewer = async (buffer: ArrayBuffer, contentType: string) => { - const fileBlob = new Blob([buffer], { type: contentType }); - if (viewerType) { - fileBlobUrl = URL.createObjectURL(fileBlob); - heicBlob = contentType === "image/heic" ? fileBlob : undefined; - } + fileBlob = new Blob([buffer], { type: contentType }); + fileBlobUrl = URL.createObjectURL(fileBlob); return fileBlob; }; const convertHeicToJpeg = async () => { - if (!heicBlob) return; + if (fileBlob?.type !== "image/heic") return; URL.revokeObjectURL(fileBlobUrl!); fileBlobUrl = undefined; const { default: heic2any } = await import("heic2any"); fileBlobUrl = URL.createObjectURL( - (await heic2any({ blob: heicBlob, toType: "image/jpeg" })) as Blob, + (await heic2any({ blob: fileBlob, toType: "image/jpeg" })) as Blob, ); - heicBlob = undefined; }; const updateThumbnail = async (dataKey: CryptoKey, dataKeyVersion: Date) => { @@ -133,7 +133,24 @@ 파일 - + + + +
e.stopPropagation()}> + + +
+
diff --git a/src/routes/(fullscreen)/file/[id]/TopBarMenu.svelte b/src/routes/(fullscreen)/file/[id]/TopBarMenu.svelte new file mode 100644 index 0000000..8c92986 --- /dev/null +++ b/src/routes/(fullscreen)/file/[id]/TopBarMenu.svelte @@ -0,0 +1,58 @@ + + + (isOpen = false)} /> + +{#if isOpen && (directoryId || fileBlob)} +
+

더보기

+
+ {#snippet menuButton( + Icon: Component, + text: string, + onclick: () => void, + )} + + {/snippet} + + {#if directoryId} + {@render menuButton(IconFolderOpen, "폴더에서 보기", () => + goto(directoryId === "root" ? "/directory" : `/directory/${directoryId}`), + )} + {/if} + {#if fileBlob} + {@render menuButton(IconCloudDownload, "다운로드", () => { + console.log(filename); + FileSaver.saveAs(fileBlob, filename); + })} + {/if} +
+
+{/if} diff --git a/src/routes/(main)/category/[[id]]/+page.svelte b/src/routes/(main)/category/[[id]]/+page.svelte index 9cbc41c..9b3e195 100644 --- a/src/routes/(main)/category/[[id]]/+page.svelte +++ b/src/routes/(main)/category/[[id]]/+page.svelte @@ -58,7 +58,7 @@ goto(`/file/${id}`)} + onFileClick={({ id }) => goto(`/file/${id}?from=category`)} onFileRemoveClick={async ({ id }) => { await requestFileRemovalFromCategory(id, data.id as number); info = getCategoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME