mirror of
https://github.com/OpenListTeam/OpenList-Frontend.git
synced 2026-03-13 11:20:24 +00:00
feat: setting: add Load Default Settings button; home: display loaded folder info (#100)
* fix: resolve all prompt errors * feat(home): display loaded folder info * feat(setting): add `Load Default Settings` button
This commit is contained in:
@@ -19,7 +19,7 @@ import { MustUser } from "./MustUser"
|
|||||||
import "./index.css"
|
import "./index.css"
|
||||||
import { useI18n } from "@solid-primitives/i18n"
|
import { useI18n } from "@solid-primitives/i18n"
|
||||||
import { initialLang, langMap, loadedLangs } from "./i18n"
|
import { initialLang, langMap, loadedLangs } from "./i18n"
|
||||||
import { Resp } from "~/types"
|
import { PEmptyResp, PResp, Resp } from "~/types"
|
||||||
import { setArchiveExtensions } from "~/store/archive"
|
import { setArchiveExtensions } from "~/store/archive"
|
||||||
|
|
||||||
const Home = lazy(() => import("~/pages/home/Layout"))
|
const Home = lazy(() => import("~/pages/home/Layout"))
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export const FullLoading = (props: {
|
|||||||
|
|
||||||
export const MaybeLoading = (props: {
|
export const MaybeLoading = (props: {
|
||||||
children?: JSXElement
|
children?: JSXElement
|
||||||
loading: boolean
|
loading?: boolean
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Show when={!props.loading} fallback={<FullLoading />}>
|
<Show when={!props.loading} fallback={<FullLoading />}>
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const [isTocDisabled, setTocDisabled] = createStorageSignal(
|
|||||||
|
|
||||||
export { isTocVisible, setTocDisabled }
|
export { isTocVisible, setTocDisabled }
|
||||||
|
|
||||||
|
type PluginType = typeof remarkGfm | typeof rehypeRaw
|
||||||
function MarkdownToc(props: {
|
function MarkdownToc(props: {
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
markdownRef: HTMLDivElement
|
markdownRef: HTMLDivElement
|
||||||
@@ -214,10 +215,10 @@ export function Markdown(props: {
|
|||||||
})
|
})
|
||||||
return content
|
return content
|
||||||
})
|
})
|
||||||
const [remarkPlugins, setRemarkPlugins] = createSignal<Function[]>([
|
const [remarkPlugins, setRemarkPlugins] = createSignal<PluginType[]>([
|
||||||
remarkGfm,
|
remarkGfm,
|
||||||
])
|
])
|
||||||
const [rehypePlugins, setRehypePlugins] = createSignal<Function[]>([
|
const [rehypePlugins, setRehypePlugins] = createSignal<PluginType[]>([
|
||||||
rehypeRaw,
|
rehypeRaw,
|
||||||
])
|
])
|
||||||
createEffect(
|
createEffect(
|
||||||
@@ -228,8 +229,8 @@ export function Markdown(props: {
|
|||||||
const { default: reMarkMath } = await import("remark-math")
|
const { default: reMarkMath } = await import("remark-math")
|
||||||
const { default: rehypeKatex } = await import("rehype-katex")
|
const { default: rehypeKatex } = await import("rehype-katex")
|
||||||
insertKatexCSS()
|
insertKatexCSS()
|
||||||
setRemarkPlugins([...remarkPlugins(), reMarkMath])
|
setRemarkPlugins([...remarkPlugins(), reMarkMath as PluginType])
|
||||||
setRehypePlugins([...rehypePlugins(), rehypeKatex])
|
setRehypePlugins([...rehypePlugins(), rehypeKatex as PluginType])
|
||||||
}
|
}
|
||||||
insertMermaidJS()
|
insertMermaidJS()
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ export const ModalInput = (props: ModalInputProps) => {
|
|||||||
initialFocus="#modal-input"
|
initialFocus="#modal-input"
|
||||||
>
|
>
|
||||||
<ModalOverlay />
|
<ModalOverlay />
|
||||||
<ModalContent onDrop={(e) => props.onDrop?.(e, setValue)}>
|
<ModalContent onDrop={(e: DragEvent) => props.onDrop?.(e, setValue)}>
|
||||||
{/* <ModalCloseButton /> */}
|
{/* <ModalCloseButton /> */}
|
||||||
<ModalHeader>{t(props.title)}</ModalHeader>
|
<ModalHeader>{t(props.title)}</ModalHeader>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
|
|||||||
@@ -5,17 +5,14 @@ export const useLoading = <T>(
|
|||||||
p: (...arg: any[]) => Promise<T>,
|
p: (...arg: any[]) => Promise<T>,
|
||||||
fetch?: boolean,
|
fetch?: boolean,
|
||||||
t?: boolean, // initial loading true
|
t?: boolean, // initial loading true
|
||||||
): [
|
): [Accessor<typeof t>, typeof p] => {
|
||||||
Accessor<boolean>,
|
const [loading, setLoading] = createSignal(t)
|
||||||
(...arg: any[]) => Promise<unknown extends T ? any : T>,
|
|
||||||
] => {
|
|
||||||
const [loading, setLoading] = createSignal<boolean>(t ?? false)
|
|
||||||
return [
|
return [
|
||||||
loading,
|
loading,
|
||||||
async (...arg: any[]) => {
|
async (...arg: any[]) => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
const data = await p(...arg)
|
const data = await p(...arg)
|
||||||
if (!fetch || (data as unknown as EmptyResp).code !== 401) {
|
if (!fetch || (data as EmptyResp).code !== 401) {
|
||||||
// why?
|
// why?
|
||||||
// because if setLoading(false) here will rerender before navigate
|
// because if setLoading(false) here will rerender before navigate
|
||||||
// maybe cause some bugs
|
// maybe cause some bugs
|
||||||
@@ -29,10 +26,7 @@ export const useLoading = <T>(
|
|||||||
export const useFetch = <T>(
|
export const useFetch = <T>(
|
||||||
p: (...arg: any[]) => PResp<T>,
|
p: (...arg: any[]) => PResp<T>,
|
||||||
loading?: boolean,
|
loading?: boolean,
|
||||||
): [
|
): [Accessor<typeof loading>, typeof p] => {
|
||||||
Accessor<boolean>,
|
|
||||||
(...arg: Parameters<typeof p>) => PResp<unknown extends T ? any : T>,
|
|
||||||
] => {
|
|
||||||
return useLoading(p, true, loading)
|
return useLoading(p, true, loading)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,13 +34,13 @@ const useListLoading = <T, K>(
|
|||||||
p: (key: K, ...arg: any[]) => Promise<T>,
|
p: (key: K, ...arg: any[]) => Promise<T>,
|
||||||
fetch?: boolean,
|
fetch?: boolean,
|
||||||
initial?: K,
|
initial?: K,
|
||||||
): [Accessor<K | undefined>, (key: K, ...arg: any[]) => Promise<any>] => {
|
): [Accessor<typeof initial>, typeof p] => {
|
||||||
const [loading, setLoading] = createSignal<K | undefined>(initial)
|
const [loading, setLoading] = createSignal(initial)
|
||||||
return [
|
return [
|
||||||
loading,
|
loading,
|
||||||
async (key: K, ...arg: any[]) => {
|
async (key: K, ...arg: any[]) => {
|
||||||
setLoading(() => key)
|
setLoading(() => key)
|
||||||
const data: unknown = await p(key, ...arg)
|
const data = await p(key, ...arg)
|
||||||
if (!fetch || (data as EmptyResp).code !== 401) {
|
if (!fetch || (data as EmptyResp).code !== 401) {
|
||||||
setLoading(undefined)
|
setLoading(undefined)
|
||||||
}
|
}
|
||||||
@@ -58,6 +52,6 @@ const useListLoading = <T, K>(
|
|||||||
export const useListFetch = <T, K>(
|
export const useListFetch = <T, K>(
|
||||||
p: (key: K, ...arg: any[]) => PResp<T>,
|
p: (key: K, ...arg: any[]) => PResp<T>,
|
||||||
initial?: K,
|
initial?: K,
|
||||||
): [Accessor<K | undefined>, (key: K, ...arg: any[]) => Promise<any>] => {
|
): [Accessor<typeof initial>, typeof p] => {
|
||||||
return useListLoading(p, true, initial)
|
return useListLoading(p, true, initial)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"obj": {
|
"obj": {
|
||||||
|
"selected": "Selected",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"size": "Size",
|
"size": "Size",
|
||||||
"modified": "Modified"
|
"modified": "Modified"
|
||||||
|
|||||||
@@ -124,5 +124,7 @@
|
|||||||
"version": "Version",
|
"version": "Version",
|
||||||
"video_autoplay": "Video autoplay",
|
"video_autoplay": "Video autoplay",
|
||||||
"video_types": "Video types",
|
"video_types": "Video types",
|
||||||
|
"load_default_setting": "Load default settings",
|
||||||
|
"load_default_setting_success": "Loaded successfully, Not saved",
|
||||||
"webauthn_login_enabled": "Webauthn login enabled"
|
"webauthn_login_enabled": "Webauthn login enabled"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const Index = () => {
|
|||||||
useTitle(getSetting("site_title"))
|
useTitle(getSetting("site_title"))
|
||||||
const announcement = getSetting("announcement")
|
const announcement = getSetting("announcement")
|
||||||
if (announcement) {
|
if (announcement) {
|
||||||
notify.render(() => <Markdown children={announcement} />)
|
notify.render(<Markdown children={announcement} />)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import {
|
|||||||
BreadcrumbLink,
|
BreadcrumbLink,
|
||||||
BreadcrumbProps,
|
BreadcrumbProps,
|
||||||
BreadcrumbSeparator,
|
BreadcrumbSeparator,
|
||||||
|
HStack,
|
||||||
|
Text,
|
||||||
} from "@hope-ui/solid"
|
} from "@hope-ui/solid"
|
||||||
import { Link } from "@solidjs/router"
|
import { Link } from "@solidjs/router"
|
||||||
import { createMemo, For, Show } from "solid-js"
|
import { createMemo, For, Show } from "solid-js"
|
||||||
import { usePath, useRouter, useT } from "~/hooks"
|
import { usePath, useRouter, useT } from "~/hooks"
|
||||||
import { getSetting, local } from "~/store"
|
import { getSetting, local, objStore, State } from "~/store"
|
||||||
import { encodePath, hoverColor, joinBase } from "~/utils"
|
import { encodePath, hoverColor, joinBase } from "~/utils"
|
||||||
|
|
||||||
export const Nav = () => {
|
export const Nav = () => {
|
||||||
@@ -17,6 +19,38 @@ export const Nav = () => {
|
|||||||
const t = useT()
|
const t = useT()
|
||||||
const { setPathAs } = usePath()
|
const { setPathAs } = usePath()
|
||||||
|
|
||||||
|
const folderInfo = createMemo(() => {
|
||||||
|
const { folder, file } = objStore.objs.reduce(
|
||||||
|
(acc, item) => {
|
||||||
|
if (item.is_dir) {
|
||||||
|
acc.folder++
|
||||||
|
} else {
|
||||||
|
acc.file++
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
},
|
||||||
|
{ folder: 0, file: 0 },
|
||||||
|
)
|
||||||
|
|
||||||
|
const parts: string[] = []
|
||||||
|
if (folder) parts.push(`${t("home.search.scopes.folder")}:${folder}`)
|
||||||
|
if (file) parts.push(`${t("home.search.scopes.file")}:${file}`)
|
||||||
|
return parts.join(" ")
|
||||||
|
})
|
||||||
|
const selectInfo = createMemo(() => {
|
||||||
|
const { selected } = objStore.objs.reduce(
|
||||||
|
(acc, item) => {
|
||||||
|
if (item.selected) acc.selected++
|
||||||
|
return acc
|
||||||
|
},
|
||||||
|
{ selected: 0 },
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!selected) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return ` ${t("home.obj.selected")}:${selected}`
|
||||||
|
})
|
||||||
const stickyProps = createMemo<BreadcrumbProps>(() => {
|
const stickyProps = createMemo<BreadcrumbProps>(() => {
|
||||||
const mask: BreadcrumbProps = {
|
const mask: BreadcrumbProps = {
|
||||||
_after: {
|
_after: {
|
||||||
@@ -48,45 +82,58 @@ export const Nav = () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Breadcrumb {...stickyProps} background="$background" class="nav" w="$full">
|
<HStack background="$background" class="nav" w="$full">
|
||||||
<For each={paths()}>
|
<Breadcrumb {...stickyProps} w="$full">
|
||||||
{(name, i) => {
|
<For each={paths()}>
|
||||||
const isLast = createMemo(() => i() === paths().length - 1)
|
{(name, i) => {
|
||||||
const path = paths()
|
const isLast = createMemo(() => i() === paths().length - 1)
|
||||||
.slice(0, i() + 1)
|
const path = paths()
|
||||||
.join("/")
|
.slice(0, i() + 1)
|
||||||
const href = encodePath(path)
|
.join("/")
|
||||||
let text = () => name
|
const href = encodePath(path)
|
||||||
if (text() === "") {
|
let text = () => name
|
||||||
text = () => getSetting("home_icon") + t("manage.sidemenu.home")
|
if (text() === "") {
|
||||||
}
|
text = () => getSetting("home_icon") + t("manage.sidemenu.home")
|
||||||
return (
|
}
|
||||||
<BreadcrumbItem class="nav-item">
|
return (
|
||||||
<BreadcrumbLink
|
<BreadcrumbItem class="nav-item">
|
||||||
class="nav-link"
|
<BreadcrumbLink
|
||||||
css={{
|
class="nav-link"
|
||||||
wordBreak: "break-all",
|
css={{
|
||||||
}}
|
wordBreak: "break-all",
|
||||||
color="unset"
|
}}
|
||||||
_hover={{ bgColor: hoverColor(), color: "unset" }}
|
color="unset"
|
||||||
_active={{ transform: "scale(.95)", transition: "0.1s" }}
|
_hover={{ bgColor: hoverColor(), color: "unset" }}
|
||||||
cursor="pointer"
|
_active={{ transform: "scale(.95)", transition: "0.1s" }}
|
||||||
p="$1"
|
cursor="pointer"
|
||||||
rounded="$lg"
|
p="$1"
|
||||||
currentPage={isLast()}
|
rounded="$lg"
|
||||||
as={isLast() ? undefined : Link}
|
currentPage={isLast()}
|
||||||
href={joinBase(href)}
|
as={isLast() ? undefined : Link}
|
||||||
onMouseEnter={() => setPathAs(path)}
|
href={joinBase(href)}
|
||||||
>
|
onMouseEnter={() => setPathAs(path)}
|
||||||
{text}
|
>
|
||||||
</BreadcrumbLink>
|
{text()}
|
||||||
<Show when={!isLast()}>
|
</BreadcrumbLink>
|
||||||
<BreadcrumbSeparator class="nav-separator" />
|
<Show when={!isLast()}>
|
||||||
</Show>
|
<BreadcrumbSeparator class="nav-separator" />
|
||||||
</BreadcrumbItem>
|
</Show>
|
||||||
)
|
</BreadcrumbItem>
|
||||||
}}
|
)
|
||||||
</For>
|
}}
|
||||||
</Breadcrumb>
|
</For>
|
||||||
|
</Breadcrumb>
|
||||||
|
<Show when={objStore.state == State.Folder}>
|
||||||
|
<Text
|
||||||
|
css={{
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
}}
|
||||||
|
p="$1"
|
||||||
|
>
|
||||||
|
{folderInfo()}
|
||||||
|
{selectInfo()}
|
||||||
|
</Text>
|
||||||
|
</Show>
|
||||||
|
</HStack>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Box } from "@hope-ui/solid"
|
import { Box } from "@hope-ui/solid"
|
||||||
|
// @ts-ignore
|
||||||
import * as AsciinemaPlayer from "asciinema-player"
|
import * as AsciinemaPlayer from "asciinema-player"
|
||||||
import "asciinema-player/dist/bundle/asciinema-player.css"
|
import "asciinema-player/dist/bundle/asciinema-player.css"
|
||||||
import { onMount } from "solid-js"
|
import { onMount } from "solid-js"
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const Preview = () => {
|
|||||||
|
|
||||||
let libheif: any
|
let libheif: any
|
||||||
let decoder: any
|
let decoder: any
|
||||||
let canvas: HTMLCanvasElement | null = null
|
let canvas: HTMLCanvasElement | undefined
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
window.addEventListener("keydown", onKeydown)
|
window.addEventListener("keydown", onKeydown)
|
||||||
@@ -74,21 +74,20 @@ const Preview = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 加载脚本
|
// 加载脚本
|
||||||
const loadScript = (src: string, id: string) => {
|
const loadScript = (src: string, id: string) =>
|
||||||
return new Promise<void>((resolve, reject) => {
|
new Promise<void>((resolve, reject) => {
|
||||||
const script = document.createElement("script")
|
const script = document.createElement("script")
|
||||||
script.src = src
|
script.src = src
|
||||||
script.id = id
|
script.id = id
|
||||||
script.onload = () => resolve()
|
script.onload = () => resolve()
|
||||||
script.onerror = () => reject(new Error(`脚本加载失败: ${src}`))
|
script.onerror = () => reject(`脚本加载失败: ${src}`)
|
||||||
document.head.appendChild(script)
|
document.head.appendChild(script)
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// 获取WASM文件
|
// 获取WASM文件
|
||||||
const fetchWasm = async (url: string) => {
|
const fetchWasm = async (url: string) => {
|
||||||
const response = await fetch(url)
|
const response = await fetch(url)
|
||||||
if (!response.ok) throw new Error(`WASM加载失败: ${url}`)
|
if (!response.ok) throw `WASM加载失败: ${url}`
|
||||||
return await response.arrayBuffer()
|
return await response.arrayBuffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,13 +99,13 @@ const Preview = () => {
|
|||||||
|
|
||||||
// 获取HEIC文件
|
// 获取HEIC文件
|
||||||
const response = await fetch(url)
|
const response = await fetch(url)
|
||||||
if (!response.ok) throw new Error("文件获取失败")
|
if (!response.ok) throw "文件获取失败"
|
||||||
const buffer = await response.arrayBuffer()
|
const buffer = await response.arrayBuffer()
|
||||||
|
|
||||||
// 解码HEIC文件
|
// 解码HEIC文件
|
||||||
const images = decoder.decode(buffer)
|
const images = decoder.decode(buffer)
|
||||||
if (!images || images.length === 0) {
|
if (!images || images.length === 0) {
|
||||||
throw new Error("没有可解码的图像")
|
throw "没有可解码的图像"
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示第一张图像
|
// 显示第一张图像
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ export const VideoBox = (props: {
|
|||||||
whiteSpace: "nowrap",
|
whiteSpace: "nowrap",
|
||||||
}}
|
}}
|
||||||
defaultChecked={autoNext === "true"}
|
defaultChecked={autoNext === "true"}
|
||||||
onChange={(e) => {
|
onChange={(e: { currentTarget: HTMLInputElement }) => {
|
||||||
props.onAutoNextChange(e.currentTarget.checked)
|
props.onAutoNextChange(e.currentTarget.checked)
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
"video_auto_next",
|
"video_auto_next",
|
||||||
|
|||||||
@@ -58,13 +58,7 @@ function LocalSettingEdit(props: LocalSetting) {
|
|||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectListbox>
|
<SelectListbox>
|
||||||
<For
|
<For each={props.options}>
|
||||||
each={
|
|
||||||
typeof props.options === "function"
|
|
||||||
? props.options()
|
|
||||||
: props.options
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{(item) => (
|
{(item) => (
|
||||||
<SelectOption value={item}>
|
<SelectOption value={item}>
|
||||||
<SelectOptionText>
|
<SelectOptionText>
|
||||||
@@ -81,7 +75,7 @@ function LocalSettingEdit(props: LocalSetting) {
|
|||||||
<Match when={props.type === "boolean"}>
|
<Match when={props.type === "boolean"}>
|
||||||
<HopeSwitch
|
<HopeSwitch
|
||||||
defaultChecked={local[props.key] === "true"}
|
defaultChecked={local[props.key] === "true"}
|
||||||
onChange={(e) => {
|
onChange={(e: { currentTarget: HTMLInputElement }) => {
|
||||||
setLocal(props.key, e.currentTarget.checked.toString())
|
setLocal(props.key, e.currentTarget.checked.toString())
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -36,11 +36,7 @@ const CornerBottom = (props: Props) => {
|
|||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
<g opacity="1">
|
<g opacity="1">
|
||||||
<use
|
<use href="#path-2" fill="url(#linearGradient-3)" fill-opacity="1" />
|
||||||
xlink:href="#path-2"
|
|
||||||
fill="url(#linearGradient-3)"
|
|
||||||
fill-opacity="1"
|
|
||||||
/>
|
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -36,11 +36,7 @@ const CornerTop = (props: Props) => {
|
|||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
<g opacity="1">
|
<g opacity="1">
|
||||||
<use
|
<use href="#path-1" fill="url(#linearGradient-2)" fill-opacity="1" />
|
||||||
xlink:href="#path-1"
|
|
||||||
fill="url(#linearGradient-2)"
|
|
||||||
fill-opacity="1"
|
|
||||||
/>
|
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { useT } from "~/hooks"
|
|||||||
|
|
||||||
export interface DeletePopoverProps {
|
export interface DeletePopoverProps {
|
||||||
name: string
|
name: string
|
||||||
loading: boolean
|
loading?: boolean
|
||||||
onClick: () => void
|
onClick: () => void
|
||||||
}
|
}
|
||||||
export const DeletePopover = (props: DeletePopoverProps) => {
|
export const DeletePopover = (props: DeletePopoverProps) => {
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ const AddOrEdit = () => {
|
|||||||
|
|
||||||
const initEdit = async () => {
|
const initEdit = async () => {
|
||||||
const resp = await loadMeta()
|
const resp = await loadMeta()
|
||||||
handleResp(resp, setMeta)
|
handleResp<Meta>(resp, setMeta)
|
||||||
}
|
}
|
||||||
if (id) {
|
if (id) {
|
||||||
initEdit()
|
initEdit()
|
||||||
@@ -154,14 +154,12 @@ const AddOrEdit = () => {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
<Item
|
<Item
|
||||||
name={item.name}
|
name={item.name}
|
||||||
type={item.type as "string" | "bool" | "text"}
|
type={item.type}
|
||||||
value={meta[item.name as keyof Meta] as string | boolean}
|
value={meta[item.name]}
|
||||||
onChange={(val: any): void =>
|
onChange={(val: any): void => setMeta(item.name, val)}
|
||||||
setMeta(item.name as keyof Meta, val)
|
sub={meta[item.sub]}
|
||||||
}
|
onSub={(val): void => setMeta(item.sub, val)}
|
||||||
sub={meta[item.sub] as boolean}
|
help={(item as { help: boolean }).help}
|
||||||
onSub={(val: boolean): void => setMeta(item.sub, val)}
|
|
||||||
help={item.help}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
useT,
|
useT,
|
||||||
} from "~/hooks"
|
} from "~/hooks"
|
||||||
import { handleResp, notify, r } from "~/utils"
|
import { handleResp, notify, r } from "~/utils"
|
||||||
import { Meta, PageResp } from "~/types"
|
import { Meta, PEmptyResp, PPageResp } from "~/types"
|
||||||
import { DeletePopover } from "../common/DeletePopover"
|
import { DeletePopover } from "../common/DeletePopover"
|
||||||
import { Wether } from "~/components"
|
import { Wether } from "~/components"
|
||||||
|
|
||||||
@@ -27,16 +27,18 @@ const Metas = () => {
|
|||||||
const t = useT()
|
const t = useT()
|
||||||
useManageTitle("manage.sidemenu.metas")
|
useManageTitle("manage.sidemenu.metas")
|
||||||
const { to } = useRouter()
|
const { to } = useRouter()
|
||||||
const [getMetasLoading, getMetas] = useFetch(() => r.get("/admin/meta/list"))
|
const [getMetasLoading, getMetas] = useFetch(
|
||||||
|
(): PPageResp<Meta> => r.get("/admin/meta/list"),
|
||||||
|
)
|
||||||
const [metas, setMetas] = createSignal<Meta[]>([])
|
const [metas, setMetas] = createSignal<Meta[]>([])
|
||||||
const refresh = async () => {
|
const refresh = async () => {
|
||||||
const resp: PageResp<Meta> = await getMetas()
|
const resp = await getMetas()
|
||||||
handleResp(resp, (data) => setMetas(data.content))
|
handleResp(resp, (data) => setMetas(data.content))
|
||||||
}
|
}
|
||||||
refresh()
|
refresh()
|
||||||
|
|
||||||
const [deleting, deleteMeta] = useListFetch((id: number) =>
|
const [deleting, deleteMeta] = useListFetch(
|
||||||
r.post(`/admin/meta/delete?id=${id}`),
|
(id: number): PEmptyResp => r.post(`/admin/meta/delete?id=${id}`),
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
<VStack spacing="$2" alignItems="start" w="$full">
|
<VStack spacing="$2" alignItems="start" w="$full">
|
||||||
|
|||||||
@@ -21,18 +21,22 @@ const CommonSettings = (props: CommonSettingsProps) => {
|
|||||||
const [settings, setSettings] = createStore<SettingItem[]>([])
|
const [settings, setSettings] = createStore<SettingItem[]>([])
|
||||||
const refresh = async () => {
|
const refresh = async () => {
|
||||||
const resp = await getSettings()
|
const resp = await getSettings()
|
||||||
handleResp(resp, setSettings)
|
handleResp<SettingItem[]>(resp, setSettings)
|
||||||
}
|
}
|
||||||
refresh()
|
refresh()
|
||||||
const [saveLoading, saveSettings] = useFetch(
|
const [saveLoading, saveSettings] = useFetch(
|
||||||
(): PEmptyResp => r.post("/admin/setting/save", getTarget(settings)),
|
(): PEmptyResp => r.post("/admin/setting/save", getTarget(settings)),
|
||||||
)
|
)
|
||||||
|
const [defaultLoading, defaultSettings] = useFetch(
|
||||||
|
(): PResp<SettingItem[]> =>
|
||||||
|
r.post(`/admin/setting/default?group=${props.group}`),
|
||||||
|
)
|
||||||
const [loading, setLoading] = createSignal(false)
|
const [loading, setLoading] = createSignal(false)
|
||||||
return (
|
return (
|
||||||
<VStack w="$full" alignItems="start" spacing="$2">
|
<VStack w="$full" alignItems="start" spacing="$2">
|
||||||
<ResponsiveGrid>
|
<ResponsiveGrid>
|
||||||
<Index each={settings}>
|
<Index each={settings}>
|
||||||
{(item, _) => (
|
{(item) => (
|
||||||
<Item
|
<Item
|
||||||
{...item()}
|
{...item()}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
@@ -57,7 +61,7 @@ const CommonSettings = (props: CommonSettingsProps) => {
|
|||||||
<Button
|
<Button
|
||||||
colorScheme="accent"
|
colorScheme="accent"
|
||||||
onClick={refresh}
|
onClick={refresh}
|
||||||
loading={settingsLoading() || loading()}
|
loading={settingsLoading() || loading() || defaultLoading()}
|
||||||
>
|
>
|
||||||
{t("global.refresh")}
|
{t("global.refresh")}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -70,6 +74,19 @@ const CommonSettings = (props: CommonSettingsProps) => {
|
|||||||
>
|
>
|
||||||
{t("global.save")}
|
{t("global.save")}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
colorScheme="warning"
|
||||||
|
loading={settingsLoading() || loading() || defaultLoading()}
|
||||||
|
onClick={async () => {
|
||||||
|
const resp = await defaultSettings()
|
||||||
|
handleResp(resp, (data) => {
|
||||||
|
notify.info(t("settings.load_default_setting_success"))
|
||||||
|
setSettings(data)
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("settings.load_default_setting")}
|
||||||
|
</Button>
|
||||||
</HStack>
|
</HStack>
|
||||||
</VStack>
|
</VStack>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const S3Settings = () => {
|
|||||||
const [settings, setSettings] = createStore<SettingItem[]>([])
|
const [settings, setSettings] = createStore<SettingItem[]>([])
|
||||||
const refresh = async () => {
|
const refresh = async () => {
|
||||||
const resp = await getSettings()
|
const resp = await getSettings()
|
||||||
handleResp(resp, setSettings)
|
handleResp<SettingItem[]>(resp, setSettings)
|
||||||
}
|
}
|
||||||
refresh()
|
refresh()
|
||||||
const [saveLoading, saveSettings] = useFetch(
|
const [saveLoading, saveSettings] = useFetch(
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import { TiDelete } from "solid-icons/ti"
|
|||||||
export type ItemProps = SettingItem & {
|
export type ItemProps = SettingItem & {
|
||||||
onChange?: (value: string) => void
|
onChange?: (value: string) => void
|
||||||
onDelete?: () => void
|
onDelete?: () => void
|
||||||
// value: () => string;
|
|
||||||
hideLabel?: boolean
|
hideLabel?: boolean
|
||||||
w?: string
|
w?: string
|
||||||
}
|
}
|
||||||
@@ -58,7 +57,6 @@ const Item = (props: ItemProps) => {
|
|||||||
<Input
|
<Input
|
||||||
type={props.type === Type.Number ? "number" : ""}
|
type={props.type === Type.Number ? "number" : ""}
|
||||||
id={props.key}
|
id={props.key}
|
||||||
// value={props.value()}
|
|
||||||
value={props.value}
|
value={props.value}
|
||||||
onInput={(e) => props.onChange?.(e.currentTarget.value)}
|
onInput={(e) => props.onChange?.(e.currentTarget.value)}
|
||||||
readOnly={props.flag === Flag.READONLY}
|
readOnly={props.flag === Flag.READONLY}
|
||||||
@@ -67,11 +65,9 @@ const Item = (props: ItemProps) => {
|
|||||||
<Match when={props.type === Type.Bool}>
|
<Match when={props.type === Type.Bool}>
|
||||||
<HopeSwitch
|
<HopeSwitch
|
||||||
id={props.key}
|
id={props.key}
|
||||||
defaultChecked={props.value === "true"}
|
checked={props.value === "true"}
|
||||||
// checked={props.value() === "true"}
|
onChange={(e: { currentTarget: HTMLInputElement }) =>
|
||||||
onChange={(e: any) =>
|
props.onChange?.(e.currentTarget?.checked ? "true" : "false")
|
||||||
// props.onChange?.(props.value() === "true" ? "false" : "true")
|
|
||||||
props.onChange?.(e.currentTarget.checked ? "true" : "false")
|
|
||||||
}
|
}
|
||||||
readOnly={props.flag === Flag.READONLY}
|
readOnly={props.flag === Flag.READONLY}
|
||||||
/>
|
/>
|
||||||
@@ -80,7 +76,6 @@ const Item = (props: ItemProps) => {
|
|||||||
<Textarea
|
<Textarea
|
||||||
id={props.key}
|
id={props.key}
|
||||||
value={props.value}
|
value={props.value}
|
||||||
// value={props.value()}
|
|
||||||
onChange={(e) => props.onChange?.(e.currentTarget.value)}
|
onChange={(e) => props.onChange?.(e.currentTarget.value)}
|
||||||
readOnly={props.flag === Flag.READONLY}
|
readOnly={props.flag === Flag.READONLY}
|
||||||
/>
|
/>
|
||||||
@@ -88,8 +83,7 @@ const Item = (props: ItemProps) => {
|
|||||||
<Match when={props.type === Type.Select}>
|
<Match when={props.type === Type.Select}>
|
||||||
<Select
|
<Select
|
||||||
id={props.key}
|
id={props.key}
|
||||||
defaultValue={props.value}
|
value={props.value}
|
||||||
// value={props.value()}
|
|
||||||
onChange={(e) => props.onChange?.(e)}
|
onChange={(e) => props.onChange?.(e)}
|
||||||
readOnly={props.flag === Flag.READONLY}
|
readOnly={props.flag === Flag.READONLY}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -130,8 +130,10 @@ const Storages = () => {
|
|||||||
</Show>
|
</Show>
|
||||||
<HopeSwitch
|
<HopeSwitch
|
||||||
checked={layout() === "table"}
|
checked={layout() === "table"}
|
||||||
onChange={(e) => {
|
onChange={(e: Event) => {
|
||||||
setLayout(e.currentTarget.checked ? "table" : "grid")
|
setLayout(
|
||||||
|
(e.currentTarget as HTMLInputElement).checked ? "table" : "grid",
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t("storages.other.table_layout")}
|
{t("storages.other.table_layout")}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { handleResp, notify, r } from "~/utils"
|
|||||||
import { PEmptyResp, PResp, User, UserMethods, UserPermissions } from "~/types"
|
import { PEmptyResp, PResp, User, UserMethods, UserPermissions } from "~/types"
|
||||||
import { createStore } from "solid-js/store"
|
import { createStore } from "solid-js/store"
|
||||||
import { For, Show } from "solid-js"
|
import { For, Show } from "solid-js"
|
||||||
import { me, setMe } from "~/store"
|
import { Me, me, setMe } from "~/store"
|
||||||
import { PublicKeys } from "./PublicKeys"
|
import { PublicKeys } from "./PublicKeys"
|
||||||
|
|
||||||
const Permission = (props: {
|
const Permission = (props: {
|
||||||
@@ -63,7 +63,7 @@ const AddOrEdit = () => {
|
|||||||
|
|
||||||
const initEdit = async () => {
|
const initEdit = async () => {
|
||||||
const resp = await loadUser()
|
const resp = await loadUser()
|
||||||
handleResp(resp, setUser)
|
handleResp<User>(resp, setUser)
|
||||||
}
|
}
|
||||||
if (id) {
|
if (id) {
|
||||||
initEdit()
|
initEdit()
|
||||||
@@ -153,7 +153,7 @@ const AddOrEdit = () => {
|
|||||||
handleResp(resp, async () => {
|
handleResp(resp, async () => {
|
||||||
notify.success(t("global.save_success"))
|
notify.success(t("global.save_success"))
|
||||||
if (user.username === me().username)
|
if (user.username === me().username)
|
||||||
handleResp(await r.get("/me"), setMe)
|
handleResp(await (r.get("/me") as PResp<Me>), setMe)
|
||||||
back()
|
back()
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ export const initialLocalSettings = [
|
|||||||
default: "direct",
|
default: "direct",
|
||||||
type: "select",
|
type: "select",
|
||||||
options: ["direct", "dblclick", "disable_while_checked"],
|
options: ["direct", "dblclick", "disable_while_checked"],
|
||||||
|
hidden: false,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
export type LocalSetting = (typeof initialLocalSettings)[number]
|
export type LocalSetting = (typeof initialLocalSettings)[number]
|
||||||
|
|||||||
@@ -16,39 +16,25 @@ export enum State {
|
|||||||
File, // File state
|
File, // File state
|
||||||
NeedPassword,
|
NeedPassword,
|
||||||
}
|
}
|
||||||
|
const initialObjStore = {
|
||||||
const [objStore, setObjStore] = createStore<{
|
|
||||||
obj: Obj
|
|
||||||
raw_url: string
|
|
||||||
related: Obj[]
|
|
||||||
|
|
||||||
objs: StoreObj[]
|
|
||||||
total: number
|
|
||||||
write?: boolean
|
|
||||||
|
|
||||||
readme: string
|
|
||||||
header: string
|
|
||||||
provider: string
|
|
||||||
// pageIndex: number;
|
|
||||||
// pageSize: number;
|
|
||||||
state: State
|
|
||||||
err: string
|
|
||||||
}>({
|
|
||||||
obj: {} as Obj,
|
obj: {} as Obj,
|
||||||
raw_url: "",
|
raw_url: "",
|
||||||
related: [],
|
related: [] as Obj[],
|
||||||
|
|
||||||
objs: [],
|
objs: [] as StoreObj[],
|
||||||
total: 0,
|
total: 0,
|
||||||
|
|
||||||
readme: "",
|
readme: "",
|
||||||
header: "",
|
header: "",
|
||||||
provider: "",
|
provider: "",
|
||||||
// pageIndex: 1,
|
|
||||||
// pageSize: 50,
|
|
||||||
state: State.Initial,
|
state: State.Initial,
|
||||||
err: "",
|
err: "",
|
||||||
})
|
}
|
||||||
|
const [objStore, setObjStore] = createStore<
|
||||||
|
typeof initialObjStore & {
|
||||||
|
write?: boolean
|
||||||
|
}
|
||||||
|
>(initialObjStore)
|
||||||
|
|
||||||
const setObjs = (objs: Obj[]) => {
|
const setObjs = (objs: Obj[]) => {
|
||||||
lastChecked.start = -1
|
lastChecked.start = -1
|
||||||
@@ -96,7 +82,6 @@ export type OrderBy = "name" | "size" | "modified"
|
|||||||
|
|
||||||
export const sortObjs = (orderBy: OrderBy, reverse?: boolean) => {
|
export const sortObjs = (orderBy: OrderBy, reverse?: boolean) => {
|
||||||
log("sort:", orderBy, reverse)
|
log("sort:", orderBy, reverse)
|
||||||
naturalSort.insensitive = true
|
|
||||||
setObjStore(
|
setObjStore(
|
||||||
"objs",
|
"objs",
|
||||||
produce((objs) =>
|
produce((objs) =>
|
||||||
|
|||||||
@@ -26,7 +26,13 @@ const notify = {
|
|||||||
padding: "$3",
|
padding: "$3",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ flexGrow: 1, display: "flex", alignItems: "center" }}>
|
<div
|
||||||
|
style={{
|
||||||
|
"flex-grow": 1,
|
||||||
|
display: "flex",
|
||||||
|
"align-items": "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div style={{ margin: "auto" }}>{element}</div>
|
<div style={{ margin: "auto" }}>{element}</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: "inline-block", padding: "5px" }}>
|
<div style={{ display: "inline-block", padding: "5px" }}>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"~/*": ["./src/*"]
|
"~/*": ["./src/*"]
|
||||||
// "@solidjs/router": ["./solid-router/src/index.tsx"]
|
// "@solidjs/router": ["./solid-router/src/index.tsx"]
|
||||||
},
|
},
|
||||||
|
"skipLibCheck": true,
|
||||||
"resolveJsonModule": true
|
"resolveJsonModule": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user