diff --git a/src/hooks/useLink.ts b/src/hooks/useLink.ts index 8c2d3d12..4bc32b76 100644 --- a/src/hooks/useLink.ts +++ b/src/hooks/useLink.ts @@ -1,5 +1,5 @@ import { objStore, selectedObjs, State, me } from "~/store" -import { Obj } from "~/types" +import { Obj, ArchiveObj } from "~/types" import { base_path, api, @@ -36,6 +36,12 @@ export const getLinkByDirAndObj = ( if (!api.startsWith(location.origin + base_path)) host = location.origin + base_path } + const { inner_path, archive } = obj as ArchiveObj + if (archive) { + prefix = "/ae" + path = `${dir}/${archive.name}` + path = encodePath(path, encodeAll) + } let ans = `${host}${prefix}${path}` if (type !== "preview" && !isShare && obj.sign) { ans += `?sign=${obj.sign}` @@ -46,6 +52,10 @@ export const getLinkByDirAndObj = ( ans += `?pwd=${pwd}` } } + if (archive) { + let inner = `${inner_path}/${obj.name}` + ans += `${ans.includes("?") ? "&" : "?"}inner=${encodePath(inner, encodeAll)}` + } return ans } diff --git a/src/pages/home/previews/archive.tsx b/src/pages/home/previews/archive.tsx index b1f74199..3ba53d52 100644 --- a/src/pages/home/previews/archive.tsx +++ b/src/pages/home/previews/archive.tsx @@ -20,16 +20,35 @@ import { Match, Show, Switch, + Suspense, + onCleanup, } from "solid-js" -import { getMainColor, local, me, OrderBy, password } from "~/store" -import { Obj, ObjTree, UserMethods, UserPermissions } from "~/types" -import { useFetch, useRouter, useT, useUtil } from "~/hooks" +import { Dynamic } from "solid-js/web" +import { + getMainColor, + local, + me, + OrderBy, + password, + objStore, + ObjStore, +} from "~/store" +import { + Obj, + ObjTree, + UserMethods, + UserPermissions, + ObjType, + ArchiveObj, +} from "~/types" +import { useFetch, useRouter, useT, useUtil, useLink } from "~/hooks" import { ListTitle } from "~/pages/home/folder/List" import { cols } from "~/pages/home/folder/ListItem" -import { Error, MaybeLoading } from "~/components" +import { Error, MaybeLoading, FullLoading, SelectWrapper } from "~/components" +import { OpenWith } from "../file/open-with" +import { getPreviews } from "." import { bus, - encodePath, formatDate, fsArchiveList, fsArchiveMeta, @@ -58,6 +77,7 @@ type ListItemProps = { innerPath: string url?: string pass: string + onFileClick?: () => void } const ListItem = (props: ListItemProps) => { @@ -88,8 +108,8 @@ const ListItem = (props: ListItemProps) => { on:click={(_: MouseEvent) => { if (props.obj.is_dir) { props.jumpCallback() - } else if (props.url) { - download(props.url) + } else if (!props.obj.is_dir && props.onFileClick) { + props.onFileClick() } }} onContextMenu={(e: MouseEvent) => { @@ -219,6 +239,7 @@ type List = { const Preview = () => { const t = useT() const { pathname } = useRouter() + const { rawLink } = useLink() const [metaLoading, fetchMeta] = useFetch(fsArchiveMeta) const [listLoading, fetchList] = useFetch(fsArchiveList) const loading = createMemo(() => { @@ -238,6 +259,8 @@ const Preview = () => { const [extractFolder, setExtractFolder] = createSignal<"" | "front" | "back">( "", ) + const [selectedFile, setSelectedFile] = createSignal("") + const [selectedPreviewName, setSelectedPreviewName] = createSignal("") const getObjsMutex = createMutex() const toList = (tree: ObjTree[] | Obj[]): List => { let l: List = {} @@ -361,6 +384,20 @@ const Preview = () => { } return ret } + // Build inner file url for current path by filename + const buildInnerUrl = (name: string) => { + const innerPath = + (innerPaths().length > 0 ? "/" + innerPaths().join("/") : "") + "/" + name + return innerPath + } + // Build obj with inner property + const buildObjWithInner = (obj: Obj): ArchiveObj => { + const innerPath = + innerPaths().length > 0 ? "/" + innerPaths().join("/") : "" + + return { ...obj, sign: sign, inner_path: innerPath, archive: originalObj } + } + const sortObjs = (orderBy: OrderBy, reverse?: boolean) => { batch(() => { setExtractFolder("") @@ -370,13 +407,79 @@ const Preview = () => { } }) } + + // Get all files for navigation + const files = createMemo(() => + sortedObjs() + .filter((obj) => !obj.is_dir) + .map((f) => buildObjWithInner(f)), + ) + + const previews = createMemo(() => { + const file = files().find((f) => f.name === selectedFile()) + if (!file) return [] + + return getPreviews({ ...file, provider: objStore.provider }) + }) + + const currentPreview = createMemo(() => { + const p = previews() + if (p.length === 0) return null + if (selectedPreviewName()) { + const found = p.find((item) => item.name === selectedPreviewName()) + if (found) return found + } + return p[0] + }) + + // Cast to ArchiveObj to make sure onCleanup can delete archive property correctly + const originalObj: ArchiveObj = { + ...objStore.obj, + inner_path: undefined, + archive: undefined, + } + const originalRawUrl = objStore.raw_url + + const changeFile = (name: string) => { + batch(() => { + if (name === "") { + // Restore + ObjStore.setObj(originalObj) + ObjStore.setRawUrl(originalRawUrl) + setSelectedFile("") + } else { + // Set new + const file = files().find((f) => f.name === name) + if (file) { + const innerUrl = rawLink(file) + ObjStore.setObj(file) + ObjStore.setRawUrl(innerUrl) + setSelectedFile(name) + } + } + }) + } + + onCleanup(() => { + // Restore original values + ObjStore.setObj(originalObj) + ObjStore.setRawUrl(originalRawUrl) + }) + + createEffect(() => { + selectedFile() + setSelectedPreviewName("") + }) return ( setInnerPaths([])} + currentPage={innerPaths().length === 0 && !selectedFile()} + on:click={() => { + setInnerPaths([]) + changeFile("") + }} > . @@ -386,14 +489,23 @@ const Preview = () => { setInnerPaths(innerPaths().slice(0, i() + 1))} + currentPage={innerPaths().length === i() + 1 && !selectedFile()} + on:click={() => { + setInnerPaths(innerPaths().slice(0, i() + 1)) + changeFile("") + }} > {name} )} + + + + {selectedFile()} + + @@ -414,47 +526,75 @@ const Preview = () => { - - - - - {(obj, i) => { - let url = undefined - let innerPath = - (innerPaths().length > 0 - ? "/" + innerPaths().join("/") - : "") + - "/" + - obj.name - if (!obj.is_dir) { - const hasQuery = raw_url.includes("?") - url = - raw_url + - `${hasQuery ? "&" : "?"}inner=${encodePath(innerPath, true)}` - if (archive_pass !== "") { - url = url + `&pass=${encodeURIComponent(archive_pass)}` - } - if (sign !== "") { - url = url + `&sign=${sign}` - } - } - return ( - - setInnerPaths(innerPaths().concat(obj.name)) + + + + + {(obj, i) => { + const objWithInner = buildObjWithInner(obj) + // Use rawLink to construct the URL for the object + let url = !obj.is_dir ? rawLink(objWithInner) : undefined + let innerPath = buildInnerUrl(obj.name) + return ( + + setInnerPaths(innerPaths().concat(obj.name)) + } + innerPath={innerPath} + url={url} + pass={archive_pass} + onFileClick={() => changeFile(obj.name)} + /> + ) + }} + + + + + } + > + }> + + + }> + f.type === ObjType.IMAGE)} + navigate={(name) => { + changeFile(name) + }} + /> + + + + 1}> + + setSelectedPreviewName(String(value)) } - innerPath={innerPath} - url={url} - pass={archive_pass} + options={previews().map((p) => ({ + value: p.name, + label: p.name, + }))} /> - ) - }} - - - - + + + + + + diff --git a/src/pages/home/previews/image.tsx b/src/pages/home/previews/image.tsx index f49651a6..44bf9a21 100644 --- a/src/pages/home/previews/image.tsx +++ b/src/pages/home/previews/image.tsx @@ -1,22 +1,50 @@ import { Error, FullLoading, ImageWithError } from "~/components" import { useRouter, useT } from "~/hooks" import { objStore } from "~/store" -import { ObjType } from "~/types" +import { Obj, ObjType } from "~/types" import { onCleanup, onMount } from "solid-js" -const Preview = () => { +interface PreviewProps { + images?: Obj[] + navigate?: (name: string) => void +} + +const Preview = (props: PreviewProps) => { const t = useT() const { replace } = useRouter() - let images = objStore.objs.filter((obj) => obj.type === ObjType.IMAGE) + let images = + props.images || objStore.objs.filter((obj) => obj.type === ObjType.IMAGE) if (images.length === 0) { images = [objStore.obj] } - const onKeydown = (e: KeyboardEvent) => { + + const prev = () => { + const index = images.findIndex((f) => f.name === objStore.obj.name) + if (index > 0) { + if (props.navigate) { + props.navigate(images[index - 1].name) + } else { + replace(images[index - 1].name) + } + } + } + + const next = () => { const index = images.findIndex((f) => f.name === objStore.obj.name) - if (e.key === "ArrowLeft" && index > 0) { - replace(images[index - 1].name) - } else if (e.key === "ArrowRight" && index < images.length - 1) { - replace(images[index + 1].name) + if (index < images.length - 1) { + if (props.navigate) { + props.navigate(images[index + 1].name) + } else { + replace(images[index + 1].name) + } + } + } + + const onKeydown = (e: KeyboardEvent) => { + if (e.key === "ArrowLeft") { + prev() + } else if (e.key === "ArrowRight") { + next() } } onMount(() => { diff --git a/src/pages/home/previews/index.ts b/src/pages/home/previews/index.ts index efa27e2a..c39d6323 100644 --- a/src/pages/home/previews/index.ts +++ b/src/pages/home/previews/index.ts @@ -1,6 +1,6 @@ import { Component, lazy } from "solid-js" import { getIframePreviews, me, getSettingBool, isArchive } from "~/store" -import { Obj, ObjType, UserMethods, UserPermissions } from "~/types" +import { Obj, ObjType, UserMethods, UserPermissions, ArchiveObj } from "~/types" import { ext } from "~/utils" import { generateIframePreview } from "./iframe" import { useRouter } from "~/hooks" @@ -34,6 +34,7 @@ export interface Preview { provider?: RegExp component: Component prior: Prior + availableInArchive?: boolean } export type PreviewComponent = Pick @@ -82,6 +83,7 @@ const previews: Preview[] = [ exts: ["url"], component: lazy(() => import("./text-editor")), prior: true, + availableInArchive: false, }, { name: "Image", @@ -172,6 +174,7 @@ const previews: Preview[] = [ !getSettingBool("share_preview_download_by_default")) ) }, + availableInArchive: false, }, ] @@ -186,6 +189,7 @@ export const getPreviews = ( const downloadPrior = (!isShare() && getSettingBool("preview_download_by_default")) || (isShare() && getSettingBool("share_preview_download_by_default")) + const isInArchive = !!(file as ArchiveObj).archive // internal previews if (!isShare() || getSettingBool("share_preview")) { previews.forEach((preview) => { @@ -198,6 +202,10 @@ export const getPreviews = ( extsContains(preview.exts, file.name) ) { const r = { name: preview.name, component: preview.component } + // Skip previews that are not available in archive when file is in archive + if (isInArchive && preview.availableInArchive === false) { + return + } if (!downloadPrior && isPrior(preview.prior)) { res.push(r) } else { diff --git a/src/types/obj.ts b/src/types/obj.ts index c6d8a5d8..c5235309 100644 --- a/src/types/obj.ts +++ b/src/types/obj.ts @@ -26,6 +26,11 @@ export type StoreObj = Obj & { selected?: boolean } +export type ArchiveObj = Obj & { + inner_path?: string + archive?: Obj +} + export type RenameObj = { src_name: string new_name: string