import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbSeparator, VStack, Text, Divider, HStack, Icon, useColorMode, } from "@hope-ui/solid" import { Motion } from "@motionone/solid" import { batch, createEffect, createMemo, createSignal, For, Match, Show, Switch, } 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 { ListTitle } from "~/pages/home/folder/List" import { cols } from "~/pages/home/folder/ListItem" import { Error, MaybeLoading } from "~/components" import { bus, encodePath, formatDate, fsArchiveList, fsArchiveMeta, getFileSize, hoverColor, } from "~/utils" import naturalSort from "typescript-natural-sort" import Password from "~/pages/home/Password" import { useSelectWithMouse } from "~/pages/home/folder/helper" import { getIconByObj } from "~/utils/icon" import createMutex from "~/utils/mutex" import { Item, Menu, useContextMenu } from "solid-contextmenu" import { TbCopy, TbLink } from "solid-icons/tb" import { AiOutlineCloudDownload } from "solid-icons/ai" import { Operations } from "~/pages/home/toolbar/operations" import "solid-contextmenu/dist/style.css" const download = (url: string) => { window.open(url, "_blank") } type ListItemProps = { obj: Obj index: number jumpCallback: () => void innerPath: string url?: string pass: string } const ListItem = (props: ListItemProps) => { const { show } = useContextMenu({ id: 2 }) const { isMouseSupported } = useSelectWithMouse() const filenameStyle = () => local["list_item_filename_overflow"] return ( { if (props.obj.is_dir) { props.jumpCallback() } else if (props.url) { download(props.url) } }} onContextMenu={(e: MouseEvent) => { show(e, { props: props }) }} > {props.obj.name} {getFileSize(props.obj.size)} {formatDate(props.obj.modified)} ) } const operations: Operations = { extract: { icon: TbCopy, color: "$success9" }, copy_link: { icon: TbLink, color: "$info9" }, download: { icon: AiOutlineCloudDownload, color: "$primary9" }, } const ContextMenu = () => { const { copy } = useUtil() const { colorMode } = useColorMode() return ( ) } const ItemContent = (props: { name: string }) => { const t = useT() return ( {t(`home.toolbar.${props.name}`)} ) } type List = { [name: string]: Obj & { children: List | null } } const Preview = () => { const t = useT() const { pathname } = useRouter() const [metaLoading, fetchMeta] = useFetch(fsArchiveMeta) const [listLoading, fetchList] = useFetch(fsArchiveList) const loading = createMemo(() => { return metaLoading() || listLoading() }) let archive_pass = "" let raw_url = "" let sign = "" let list: List | null = null const [error, setError] = createSignal("") const [wrongPassword, setWrongPassword] = createSignal(false) const [requiringPassword, setRequiringPassword] = createSignal(false) const [comment, setComment] = createSignal("") const [innerPaths, setInnerPaths] = createSignal([]) const [orderBy, setOrderBy] = createSignal() const [reverse, setReverse] = createSignal(false) const [extractFolder, setExtractFolder] = createSignal<"" | "front" | "back">( "", ) const getObjsMutex = createMutex() const toList = (tree: ObjTree[] | Obj[]): List => { let l: List = {} tree.forEach((item: any) => { l[item.name] = { ...item, children: item.children ? toList(item.children) : null, } }) return l } const dealWithError = (resp: { code: number; message: string }): boolean => { if (resp.code === 200) return false if (resp.code === 202) { batch(() => { if (archive_pass !== "") { setWrongPassword(true) } setRequiringPassword(true) setError("") }) } else { setError(resp.message) } return true } const getObjs = async (innerPath: string[]) => { await getObjsMutex.acquire() if (requiringPassword() && archive_pass === "") { getObjsMutex.release() return [] } if (raw_url === "") { const resp = await fetchMeta(pathname(), password(), archive_pass) if (dealWithError(resp)) { getObjsMutex.release() return [] } if (resp.data.content !== null) { list = toList(resp.data.content) } raw_url = resp.data.raw_url sign = resp.data.sign setComment(resp.data.comment) if (resp.data.sort !== undefined) { let order: OrderBy | undefined = undefined if (resp.data.sort.order_by !== "") { order = resp.data.sort.order_by } let re = resp.data.sort.order_direction === "desc" let ef = resp.data.sort.extract_folder batch(() => { setOrderBy(order) setReverse(re) setExtractFolder(ef) }) } if (resp.data.encrypted && archive_pass === "") { batch(() => { setRequiringPassword(true) setError("") }) getObjsMutex.release() return [] } } if (list === null) { const resp = await fetchList(pathname(), password(), archive_pass, "/") if (dealWithError(resp)) { getObjsMutex.release() return [] } list = toList(resp.data.content) } let l = list for (let i = 0; i < innerPath.length; i++) { if (l[innerPath[i]].children === null) { const resp = await fetchList( pathname(), password(), archive_pass, "/" + innerPath.slice(0, i + 1).join("/"), ) if (dealWithError(resp)) { getObjsMutex.release() return [] } l[innerPath[i]].children = toList(resp.data.content) } l = l[innerPath[i]].children! } batch(() => { setRequiringPassword(false) setWrongPassword(false) setError("") }) getObjsMutex.release() return Object.values(l) } const [objs, setObjs] = createSignal([]) createEffect(() => { getObjs(innerPaths()).then((ret) => setObjs(ret)) }) const refresh = () => { getObjs(innerPaths()).then((ret) => setObjs(ret)) } refresh() const sortedObjs = () => { let ret = objs() if (orderBy()) { ret = ret.sort((a, b) => { return (reverse() ? -1 : 1) * naturalSort(a[orderBy()!], b[orderBy()!]) }) } let ef = extractFolder() if (ef !== "") { let dir: Obj[] = [] let file: Obj[] = [] ret.forEach((o) => (o.is_dir ? dir : file).push(o)) ret = ef === "front" ? dir.concat(file) : file.concat(dir) } return ret } const sortObjs = (orderBy: OrderBy, reverse?: boolean) => { batch(() => { setExtractFolder("") setOrderBy(orderBy) if (reverse !== undefined) { setReverse(reverse) } }) } return ( setInnerPaths([])} > . {(name, i) => ( setInnerPaths(innerPaths().slice(0, i() + 1))} > {name} )} archive_pass} setPassword={(s) => (archive_pass = s)} enterCallback={() => refresh()} > {t("home.toolbar.archive.incorrect_password")} {(obj, i) => { let url = undefined let innerPath = (innerPaths().length > 0 ? "/" + innerPaths().join("/") : "") + "/" + obj.name if (!obj.is_dir) { url = raw_url + "?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)) } innerPath={innerPath} url={url} pass={archive_pass} /> ) }} {comment()} ) } export default Preview