style: setup prettier & husky (#23)

This commit is contained in:
BoYanZh
2022-11-29 12:42:07 +08:00
committed by GitHub
parent 1e6d489acb
commit 436ab40334
121 changed files with 10603 additions and 3066 deletions

View File

@@ -73,4 +73,4 @@ jobs:
branch: dev branch: dev
directory: web-dist directory: web-dist
repository: alist-org/web-dist repository: alist-org/web-dist
force: true force: true

View File

@@ -3,8 +3,7 @@ name: release
on: on:
push: push:
tags: tags:
- '*' - "*"
jobs: jobs:
changelog: changelog:
@@ -15,7 +14,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Generate changelog - name: Generate changelog
run: npx changelogithub # or changelogithub@0.12 if ensure the stable result run: npx changelogithub # or changelogithub@0.12 if ensure the stable result
env: env:
@@ -88,4 +87,4 @@ jobs:
- name: Release - name: Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
with: with:
files: compress/* files: compress/*

1
.husky/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
_

4
.husky/pre-commit Executable file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged

5
.prettierignore Normal file
View File

@@ -0,0 +1,5 @@
dist/
solid-router/
pnpm-lock.yaml
.husky
.prettierignore

View File

@@ -27,7 +27,8 @@
"start": "vite", "start": "vite",
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",
"serve": "vite preview" "serve": "vite preview",
"prepare": "husky install"
}, },
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
@@ -35,6 +36,9 @@
"@types/node": "^18.7.5", "@types/node": "^18.7.5",
"@types/streamsaver": "^2.0.1", "@types/streamsaver": "^2.0.1",
"@vitejs/plugin-legacy": "^2.0.1", "@vitejs/plugin-legacy": "^2.0.1",
"husky": "^8.0.2",
"lint-staged": "^13.0.4",
"prettier": "2.8.0",
"terser": "^5.14.2", "terser": "^5.14.2",
"typescript": "^4.7.4", "typescript": "^4.7.4",
"vite": "^3.0.8", "vite": "^3.0.8",
@@ -65,5 +69,8 @@
"solid-markdown": "^1.2.0", "solid-markdown": "^1.2.0",
"solid-transition-group": "^0.0.12", "solid-transition-group": "^0.0.12",
"streamsaver": "^2.0.6" "streamsaver": "^2.0.6"
},
"lint-staged": {
"**/*": "prettier --write"
} }
} }

1188
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,143 +13,166 @@
// This will prevent the sw from restarting // This will prevent the sw from restarting
let keepAlive = () => { let keepAlive = () => {
keepAlive = () => {} keepAlive = () => {}
var ping = location.href.substr(0, location.href.lastIndexOf('/')) + '/ping' var ping = location.href.substr(0, location.href.lastIndexOf("/")) + "/ping"
var interval = setInterval(() => { var interval = setInterval(() => {
if (sw) { if (sw) {
sw.postMessage('ping') sw.postMessage("ping")
} else { } else {
fetch(ping).then(res => res.text(!res.ok && clearInterval(interval))) fetch(ping).then((res) => res.text(!res.ok && clearInterval(interval)))
} }
}, 10000) }, 10000)
} }
// message event is the first thing we need to setup a listener for // message event is the first thing we need to setup a listener for
// don't want the opener to do a random timeout - instead they can listen for // don't want the opener to do a random timeout - instead they can listen for
// the ready event // the ready event
// but since we need to wait for the Service Worker registration, we store the // but since we need to wait for the Service Worker registration, we store the
// message for later // message for later
let messages = [] let messages = []
window.onmessage = evt => messages.push(evt) window.onmessage = (evt) => messages.push(evt)
let sw = null let sw = null
let scope = '' let scope = ""
function registerWorker() { function registerWorker() {
return navigator.serviceWorker.getRegistration('./').then(swReg => { return navigator.serviceWorker
return swReg || navigator.serviceWorker.register('sw.js', { scope: './' }) .getRegistration("./")
}).then(swReg => { .then((swReg) => {
const swRegTmp = swReg.installing || swReg.waiting return (
swReg || navigator.serviceWorker.register("sw.js", { scope: "./" })
scope = swReg.scope )
})
return (sw = swReg.active) || new Promise(resolve => { .then((swReg) => {
swRegTmp.addEventListener('statechange', fn = () => { const swRegTmp = swReg.installing || swReg.waiting
if (swRegTmp.state === 'activated') {
swRegTmp.removeEventListener('statechange', fn) scope = swReg.scope
sw = swReg.active
resolve() return (
} (sw = swReg.active) ||
}) new Promise((resolve) => {
swRegTmp.addEventListener(
"statechange",
(fn = () => {
if (swRegTmp.state === "activated") {
swRegTmp.removeEventListener("statechange", fn)
sw = swReg.active
resolve()
}
})
)
})
)
}) })
})
} }
// Now that we have the Service Worker registered we can process messages // Now that we have the Service Worker registered we can process messages
function onMessage (event) { function onMessage(event) {
let { data, ports, origin } = event let { data, ports, origin } = event
// It's important to have a messageChannel, don't want to interfere // It's important to have a messageChannel, don't want to interfere
// with other simultaneous downloads // with other simultaneous downloads
if (!ports || !ports.length) { if (!ports || !ports.length) {
throw new TypeError("[StreamSaver] You didn't send a messageChannel") throw new TypeError("[StreamSaver] You didn't send a messageChannel")
} }
if (typeof data !== 'object') { if (typeof data !== "object") {
throw new TypeError("[StreamSaver] You didn't send a object") throw new TypeError("[StreamSaver] You didn't send a object")
} }
// the default public service worker for StreamSaver is shared among others. // the default public service worker for StreamSaver is shared among others.
// so all download links needs to be prefixed to avoid any other conflict // so all download links needs to be prefixed to avoid any other conflict
data.origin = origin data.origin = origin
// if we ever (in some feature version of streamsaver) would like to // if we ever (in some feature version of streamsaver) would like to
// redirect back to the page of who initiated a http request // redirect back to the page of who initiated a http request
data.referrer = data.referrer || document.referrer || origin data.referrer = data.referrer || document.referrer || origin
// pass along version for possible backwards compatibility in sw.js // pass along version for possible backwards compatibility in sw.js
data.streamSaverVersion = new URLSearchParams(location.search).get('version') data.streamSaverVersion = new URLSearchParams(location.search).get(
"version"
if (data.streamSaverVersion === '1.2.0') { )
console.warn('[StreamSaver] please update streamsaver')
if (data.streamSaverVersion === "1.2.0") {
console.warn("[StreamSaver] please update streamsaver")
} }
/** @since v2.0.0 */ /** @since v2.0.0 */
if (!data.headers) { if (!data.headers) {
console.warn("[StreamSaver] pass `data.headers` that you would like to pass along to the service worker\nit should be a 2D array or a key/val object that fetch's Headers api accepts") console.warn(
"[StreamSaver] pass `data.headers` that you would like to pass along to the service worker\nit should be a 2D array or a key/val object that fetch's Headers api accepts"
)
} else { } else {
// test if it's correct // test if it's correct
// should thorw a typeError if not // should thorw a typeError if not
new Headers(data.headers) new Headers(data.headers)
} }
/** @since v2.0.0 */ /** @since v2.0.0 */
if (typeof data.filename === 'string') { if (typeof data.filename === "string") {
console.warn("[StreamSaver] You shouldn't send `data.filename` anymore. It should be included in the Content-Disposition header option") console.warn(
"[StreamSaver] You shouldn't send `data.filename` anymore. It should be included in the Content-Disposition header option"
)
// Do what File constructor do with fileNames // Do what File constructor do with fileNames
data.filename = data.filename.replace(/\//g, ':') data.filename = data.filename.replace(/\//g, ":")
} }
/** @since v2.0.0 */ /** @since v2.0.0 */
if (data.size) { if (data.size) {
console.warn("[StreamSaver] You shouldn't send `data.size` anymore. It should be included in the content-length header option") console.warn(
"[StreamSaver] You shouldn't send `data.size` anymore. It should be included in the content-length header option"
)
} }
/** @since v2.0.0 */ /** @since v2.0.0 */
if (data.readableStream) { if (data.readableStream) {
console.warn("[StreamSaver] You should send the readableStream in the messageChannel, not through mitm") console.warn(
"[StreamSaver] You should send the readableStream in the messageChannel, not through mitm"
)
} }
/** @since v2.0.0 */ /** @since v2.0.0 */
if (!data.pathname) { if (!data.pathname) {
console.warn("[StreamSaver] Please send `data.pathname` (eg: /pictures/summer.jpg)") console.warn(
data.pathname = Math.random().toString().slice(-6) + '/' + data.filename "[StreamSaver] Please send `data.pathname` (eg: /pictures/summer.jpg)"
)
data.pathname = Math.random().toString().slice(-6) + "/" + data.filename
} }
// remove all leading slashes // remove all leading slashes
data.pathname = data.pathname.replace(/^\/+/g, '') data.pathname = data.pathname.replace(/^\/+/g, "")
// remove protocol // remove protocol
let org = origin.replace(/(^\w+:|^)\/\//, '') let org = origin.replace(/(^\w+:|^)\/\//, "")
// set the absolute pathname to the download url. // set the absolute pathname to the download url.
data.url = new URL(`${scope + org}/${data.pathname}`).toString() data.url = new URL(`${scope + org}/${data.pathname}`).toString()
if (!data.url.startsWith(`${scope + org}/`)) { if (!data.url.startsWith(`${scope + org}/`)) {
throw new TypeError('[StreamSaver] bad `data.pathname`') throw new TypeError("[StreamSaver] bad `data.pathname`")
} }
// This sends the message data as well as transferring // This sends the message data as well as transferring
// messageChannel.port2 to the service worker. The service worker can // messageChannel.port2 to the service worker. The service worker can
// then use the transferred port to reply via postMessage(), which // then use the transferred port to reply via postMessage(), which
// will in turn trigger the onmessage handler on messageChannel.port1. // will in turn trigger the onmessage handler on messageChannel.port1.
const transferable = data.readableStream const transferable = data.readableStream
? [ ports[0], data.readableStream ] ? [ports[0], data.readableStream]
: [ ports[0] ] : [ports[0]]
if (!(data.readableStream || data.transferringReadable)) { if (!(data.readableStream || data.transferringReadable)) {
keepAlive() keepAlive()
} }
return sw.postMessage(data, transferable) return sw.postMessage(data, transferable)
} }
if (window.opener) { if (window.opener) {
// The opener can't listen to onload event, so we need to help em out! // The opener can't listen to onload event, so we need to help em out!
// (telling them that we are ready to accept postMessage's) // (telling them that we are ready to accept postMessage's)
window.opener.postMessage('StreamSaver::loadedPopup', '*') window.opener.postMessage("StreamSaver::loadedPopup", "*")
} }
if (navigator.serviceWorker) { if (navigator.serviceWorker) {
registerWorker().then(() => { registerWorker().then(() => {
window.onmessage = onMessage window.onmessage = onMessage
@@ -160,5 +183,4 @@
// shouldn't really be possible? // shouldn't really be possible?
keepAlive() keepAlive()
} }
</script>
</script>

View File

@@ -1,10 +1,10 @@
/* global self ReadableStream Response */ /* global self ReadableStream Response */
self.addEventListener('install', () => { self.addEventListener("install", () => {
self.skipWaiting() self.skipWaiting()
}) })
self.addEventListener('activate', event => { self.addEventListener("activate", (event) => {
event.waitUntil(self.clients.claim()) event.waitUntil(self.clients.claim())
}) })
@@ -12,15 +12,20 @@ const map = new Map()
// This should be called once per download // This should be called once per download
// Each event has a dataChannel that the data will be piped through // Each event has a dataChannel that the data will be piped through
self.onmessage = event => { self.onmessage = (event) => {
// We send a heartbeat every x second to keep the // We send a heartbeat every x second to keep the
// service worker alive if a transferable stream is not sent // service worker alive if a transferable stream is not sent
if (event.data === 'ping') { if (event.data === "ping") {
return return
} }
const data = event.data const data = event.data
const downloadUrl = data.url || self.registration.scope + Math.random() + '/' + (typeof data === 'string' ? data : data.filename) const downloadUrl =
data.url ||
self.registration.scope +
Math.random() +
"/" +
(typeof data === "string" ? data : data.filename)
const port = event.ports[0] const port = event.ports[0]
const metadata = new Array(3) // [stream, data, port] const metadata = new Array(3) // [stream, data, port]
@@ -33,7 +38,7 @@ self.onmessage = event => {
if (event.data.readableStream) { if (event.data.readableStream) {
metadata[0] = event.data.readableStream metadata[0] = event.data.readableStream
} else if (event.data.transferringReadable) { } else if (event.data.transferringReadable) {
port.onmessage = evt => { port.onmessage = (evt) => {
port.onmessage = null port.onmessage = null
metadata[0] = evt.data.readableStream metadata[0] = evt.data.readableStream
} }
@@ -45,84 +50,92 @@ self.onmessage = event => {
port.postMessage({ download: downloadUrl }) port.postMessage({ download: downloadUrl })
} }
function createStream (port) { function createStream(port) {
// ReadableStream is only supported by chrome 52 // ReadableStream is only supported by chrome 52
return new ReadableStream({ return new ReadableStream({
start (controller) { start(controller) {
// When we receive data on the messageChannel, we write // When we receive data on the messageChannel, we write
port.onmessage = ({ data }) => { port.onmessage = ({ data }) => {
if (data === 'end') { if (data === "end") {
return controller.close() return controller.close()
} }
if (data === 'abort') { if (data === "abort") {
controller.error('Aborted the download') controller.error("Aborted the download")
return return
} }
controller.enqueue(data) controller.enqueue(data)
} }
}, },
cancel () { cancel() {
console.log('user aborted') console.log("user aborted")
} },
}) })
} }
self.onfetch = event => { self.onfetch = (event) => {
const url = event.request.url const url = event.request.url
// this only works for Firefox // this only works for Firefox
if (url.endsWith('/ping')) { if (url.endsWith("/ping")) {
return event.respondWith(new Response('pong')) return event.respondWith(new Response("pong"))
} }
const hijacke = map.get(url) const hijacke = map.get(url)
if (!hijacke) return null if (!hijacke) return null
const [ stream, data, port ] = hijacke const [stream, data, port] = hijacke
map.delete(url) map.delete(url)
// Not comfortable letting any user control all headers // Not comfortable letting any user control all headers
// so we only copy over the length & disposition // so we only copy over the length & disposition
const responseHeaders = new Headers({ const responseHeaders = new Headers({
'Content-Type': 'application/octet-stream; charset=utf-8', "Content-Type": "application/octet-stream; charset=utf-8",
// To be on the safe side, The link can be opened in a iframe. // To be on the safe side, The link can be opened in a iframe.
// but octet-stream should stop it. // but octet-stream should stop it.
'Content-Security-Policy': "default-src 'none'", "Content-Security-Policy": "default-src 'none'",
'X-Content-Security-Policy': "default-src 'none'", "X-Content-Security-Policy": "default-src 'none'",
'X-WebKit-CSP': "default-src 'none'", "X-WebKit-CSP": "default-src 'none'",
'X-XSS-Protection': '1; mode=block' "X-XSS-Protection": "1; mode=block",
}) })
let headers = new Headers(data.headers || {}) let headers = new Headers(data.headers || {})
if (headers.has('Content-Length')) { if (headers.has("Content-Length")) {
responseHeaders.set('Content-Length', headers.get('Content-Length')) responseHeaders.set("Content-Length", headers.get("Content-Length"))
} }
if (headers.has('Content-Disposition')) { if (headers.has("Content-Disposition")) {
responseHeaders.set('Content-Disposition', headers.get('Content-Disposition')) responseHeaders.set(
"Content-Disposition",
headers.get("Content-Disposition")
)
} }
// data, data.filename and size should not be used anymore // data, data.filename and size should not be used anymore
if (data.size) { if (data.size) {
console.warn('Depricated') console.warn("Depricated")
responseHeaders.set('Content-Length', data.size) responseHeaders.set("Content-Length", data.size)
} }
let fileName = typeof data === 'string' ? data : data.filename let fileName = typeof data === "string" ? data : data.filename
if (fileName) { if (fileName) {
console.warn('Depricated') console.warn("Depricated")
// Make filename RFC5987 compatible // Make filename RFC5987 compatible
fileName = encodeURIComponent(fileName).replace(/['()]/g, escape).replace(/\*/g, '%2A') fileName = encodeURIComponent(fileName)
responseHeaders.set('Content-Disposition', "attachment; filename*=UTF-8''" + fileName) .replace(/['()]/g, escape)
.replace(/\*/g, "%2A")
responseHeaders.set(
"Content-Disposition",
"attachment; filename*=UTF-8''" + fileName
)
} }
event.respondWith(new Response(stream, { headers: responseHeaders })) event.respondWith(new Response(stream, { headers: responseHeaders }))
port.postMessage({ debug: 'Download started' }) port.postMessage({ debug: "Download started" })
} }

View File

@@ -1,6 +1,4 @@
{ {
"$schema": "https://docs.renovatebot.com/renovate-schema.json", "$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [ "extends": ["config:base"]
"config:base"
]
} }

View File

@@ -1,11 +1,11 @@
import fs from "fs"; import fs from "fs"
import path from "path"; import path from "path"
const root = "./src/lang"; const root = "./src/lang"
const entry = "entry.ts"; const entry = "entry.ts"
const langs = fs.readdirSync(root); const langs = fs.readdirSync(root)
langs langs
.filter((lang) => lang !== "en") .filter((lang) => lang !== "en")
.forEach((lang) => { .forEach((lang) => {
fs.copyFileSync(path.join(root, "en", entry), path.join(root, lang, entry)); fs.copyFileSync(path.join(root, "en", entry), path.join(root, lang, entry))
}); })

View File

@@ -1,5 +1,5 @@
import { Progress, ProgressIndicator } from "@hope-ui/solid"; import { Progress, ProgressIndicator } from "@hope-ui/solid"
import { Route, Routes, useIsRouting } from "@solidjs/router"; import { Route, Routes, useIsRouting } from "@solidjs/router"
import { import {
Component, Component,
createSignal, createSignal,
@@ -7,54 +7,54 @@ import {
Match, Match,
onCleanup, onCleanup,
Switch, Switch,
} from "solid-js"; } from "solid-js"
import { Portal } from "solid-js/web"; import { Portal } from "solid-js/web"
import { useLoading, useRouter } from "~/hooks"; import { useLoading, useRouter } from "~/hooks"
import { globalStyles } from "./theme"; import { globalStyles } from "./theme"
import { bus, r, handleRespWithoutAuthAndNotify, base_path } from "~/utils"; import { bus, r, handleRespWithoutAuthAndNotify, base_path } from "~/utils"
import { setSettings } from "~/store"; import { setSettings } from "~/store"
import { Error, FullScreenLoading } from "~/components"; import { Error, FullScreenLoading } from "~/components"
import { MustUser } from "./MustUser"; 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 { Resp } from "~/types"
const Home = lazy(() => import("~/pages/home/Layout")); const Home = lazy(() => import("~/pages/home/Layout"))
const Manage = lazy(() => import("~/pages/manage")); const Manage = lazy(() => import("~/pages/manage"))
const Login = lazy(() => import("~/pages/login")); const Login = lazy(() => import("~/pages/login"))
const Test = lazy(() => import("~/pages/test")); const Test = lazy(() => import("~/pages/test"))
const App: Component = () => { const App: Component = () => {
globalStyles(); globalStyles()
const [, { add }] = useI18n(); const [, { add }] = useI18n()
const isRouting = useIsRouting(); const isRouting = useIsRouting()
const { to } = useRouter(); const { to } = useRouter()
const onTo = (path: string) => { const onTo = (path: string) => {
to(path); to(path)
}; }
bus.on("to", onTo); bus.on("to", onTo)
onCleanup(() => { onCleanup(() => {
bus.off("to", onTo); bus.off("to", onTo)
}); })
const [err, setErr] = createSignal<string>(); const [err, setErr] = createSignal<string>()
const [loading, data] = useLoading(() => const [loading, data] = useLoading(() =>
Promise.all([ Promise.all([
(async () => { (async () => {
add(initialLang, (await langMap[initialLang]()).default); add(initialLang, (await langMap[initialLang]()).default)
loadedLangs.add(initialLang); loadedLangs.add(initialLang)
})(), })(),
(async () => { (async () => {
handleRespWithoutAuthAndNotify( handleRespWithoutAuthAndNotify(
(await r.get("/public/settings")) as Resp<Record<string, string>>, (await r.get("/public/settings")) as Resp<Record<string, string>>,
setSettings, setSettings,
setErr setErr
); )
})(), })(),
]) ])
); )
data(); data()
return ( return (
<> <>
<Portal> <Portal>
@@ -103,7 +103,7 @@ const App: Component = () => {
</Match> </Match>
</Switch> </Switch>
</> </>
); )
}; }
export default App; export default App

View File

@@ -1,22 +1,22 @@
import { createI18nContext } from "@solid-primitives/i18n"; import { createI18nContext } from "@solid-primitives/i18n"
import { createSignal } from "solid-js"; import { createSignal } from "solid-js"
interface Language { interface Language {
code: string; code: string
lang: string; lang: string
} }
const langs = import.meta.glob("~/lang/*/index.json", { const langs = import.meta.glob("~/lang/*/index.json", {
eager: true, eager: true,
import: "lang", import: "lang",
}); })
const languages: Language[] = []; const languages: Language[] = []
for (const path in langs) { for (const path in langs) {
const name = path.split("/")[3]; const name = path.split("/")[3]
languages.push({ languages.push({
code: name, code: name,
lang: langs[path] as string, lang: langs[path] as string,
}); })
} }
const defaultLang = const defaultLang =
languages.find( languages.find(
@@ -27,25 +27,25 @@ const defaultLang =
lang.code.toLowerCase().split("_")[0] === lang.code.toLowerCase().split("_")[0] ===
navigator.language.toLowerCase().split("_")[0] navigator.language.toLowerCase().split("_")[0]
)?.code || )?.code ||
"en"; "en"
export let initialLang = localStorage.getItem("lang") ?? ""; export let initialLang = localStorage.getItem("lang") ?? ""
if (!initialLang || !languages.find((lang) => lang.code === initialLang)) { if (!initialLang || !languages.find((lang) => lang.code === initialLang)) {
initialLang = defaultLang; initialLang = defaultLang
} }
// store lang and import // store lang and import
export const langMap: Record<string, any> = {}; export const langMap: Record<string, any> = {}
const imports = import.meta.glob("~/lang/*/entry.ts"); const imports = import.meta.glob("~/lang/*/entry.ts")
for (const path in imports) { for (const path in imports) {
const name = path.split("/")[3]; const name = path.split("/")[3]
langMap[name] = imports[path]; langMap[name] = imports[path]
} }
export const loadedLangs = new Set<string>(); export const loadedLangs = new Set<string>()
const i18n = createI18nContext({}, initialLang); const i18n = createI18nContext({}, initialLang)
const [currentLang, setLang] = createSignal(initialLang); const [currentLang, setLang] = createSignal(initialLang)
export { languages, i18n, currentLang, setLang }; export { languages, i18n, currentLang, setLang }

View File

@@ -26,7 +26,6 @@
background-color: rgba(0, 0, 0, 0.38); background-color: rgba(0, 0, 0, 0.38);
} }
/* dark */ /* dark */
.hope-ui-dark ::-webkit-scrollbar-corner, .hope-ui-dark ::-webkit-scrollbar-corner,
@@ -44,4 +43,4 @@
.hope-ui-dark ::-webkit-scrollbar-thumb:vertical:active { .hope-ui-dark ::-webkit-scrollbar-thumb:vertical:active {
background-color: rgb(58, 58, 58); background-color: rgb(58, 58, 58);
} }

View File

@@ -1,19 +1,19 @@
import { HopeProvider, NotificationsProvider } from "@hope-ui/solid"; import { HopeProvider, NotificationsProvider } from "@hope-ui/solid"
import { I18nContext } from "@solid-primitives/i18n"; import { I18nContext } from "@solid-primitives/i18n"
import { ErrorBoundary, Suspense } from "solid-js"; import { ErrorBoundary, Suspense } from "solid-js"
import { Error, FullScreenLoading } from "~/components"; import { Error, FullScreenLoading } from "~/components"
import App from "./App"; import App from "./App"
import { i18n } from "./i18n"; import { i18n } from "./i18n"
import { globalStyles, theme } from "./theme"; import { globalStyles, theme } from "./theme"
const Index = () => { const Index = () => {
globalStyles(); globalStyles()
return ( return (
<HopeProvider config={theme}> <HopeProvider config={theme}>
<ErrorBoundary <ErrorBoundary
fallback={(err) => { fallback={(err) => {
console.error("error", err); console.error("error", err)
return <Error msg={`System error: ${err}`} h="100vh" />; return <Error msg={`System error: ${err}`} h="100vh" />
}} }}
> >
<I18nContext.Provider value={i18n}> <I18nContext.Provider value={i18n}>
@@ -25,7 +25,7 @@ const Index = () => {
</I18nContext.Provider> </I18nContext.Provider>
</ErrorBoundary> </ErrorBoundary>
</HopeProvider> </HopeProvider>
); )
}; }
export { Index }; export { Index }

View File

@@ -1,5 +1,5 @@
import { globalCss, HopeThemeConfig } from "@hope-ui/solid"; import { globalCss, HopeThemeConfig } from "@hope-ui/solid"
import { hoverColor } from "~/utils"; import { hoverColor } from "~/utils"
const theme: HopeThemeConfig = { const theme: HopeThemeConfig = {
initialColorMode: "system", initialColorMode: "system",
@@ -157,7 +157,7 @@ const theme: HopeThemeConfig = {
}, },
}, },
}, },
}; }
export const globalStyles = globalCss({ export const globalStyles = globalCss({
"*": { "*": {
@@ -176,6 +176,6 @@ export const globalStyles = globalCss({
flexWrap: "wrap", flexWrap: "wrap",
rowGap: "0 !important", rowGap: "0 !important",
}, },
}); })
export { theme }; export { theme }

View File

@@ -1,6 +1,6 @@
import { Center, ElementType, Spinner, SpinnerProps } from "@hope-ui/solid"; import { Center, ElementType, Spinner, SpinnerProps } from "@hope-ui/solid"
import { JSXElement, mergeProps, Show } from "solid-js"; import { JSXElement, mergeProps, Show } from "solid-js"
import { getMainColor } from "~/store"; import { getMainColor } from "~/store"
export const FullScreenLoading = () => { export const FullScreenLoading = () => {
return ( return (
<Center h="100vh"> <Center h="100vh">
@@ -12,14 +12,14 @@ export const FullScreenLoading = () => {
size="xl" size="xl"
/> />
</Center> </Center>
); )
}; }
export const FullLoading = (props: { export const FullLoading = (props: {
py?: string; py?: string
size?: string; size?: string
thickness?: number; thickness?: number
ref?: any; ref?: any
}) => { }) => {
const merged = mergeProps( const merged = mergeProps(
{ {
@@ -28,7 +28,7 @@ export const FullLoading = (props: {
thickness: 4, thickness: 4,
}, },
props props
); )
return ( return (
<Center ref={props.ref} h="$full" w="$full" py={merged.py}> <Center ref={props.ref} h="$full" w="$full" py={merged.py}>
<Spinner <Spinner
@@ -39,19 +39,19 @@ export const FullLoading = (props: {
size={merged.size as any} size={merged.size as any}
/> />
</Center> </Center>
); )
}; }
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 />}>
{props.children} {props.children}
</Show> </Show>
); )
}; }
export const CenterLoading = <C extends ElementType = "div">( export const CenterLoading = <C extends ElementType = "div">(
props: SpinnerProps<C> props: SpinnerProps<C>
@@ -60,5 +60,5 @@ export const CenterLoading = <C extends ElementType = "div">(
<Center w="$full" h="$full"> <Center w="$full" h="$full">
<Spinner color={getMainColor()} {...props} /> <Spinner color={getMainColor()} {...props} />
</Center> </Center>
); )
}; }

View File

@@ -1,4 +1,4 @@
const Hello = ()=>{ const Hello = () => {
return <h1>Hello, Solidjs</h1> return <h1>Hello, Solidjs</h1>
} }
export default Hello; export default Hello

View File

@@ -1,20 +1,20 @@
import { ElementType, Image, ImageProps } from "@hope-ui/solid"; import { ElementType, Image, ImageProps } from "@hope-ui/solid"
import { createSignal, JSXElement, Show } from "solid-js"; import { createSignal, JSXElement, Show } from "solid-js"
export const ImageWithError = <C extends ElementType = "img">( export const ImageWithError = <C extends ElementType = "img">(
props: ImageProps<C> & { props: ImageProps<C> & {
fallbackErr?: JSXElement; fallbackErr?: JSXElement
} }
) => { ) => {
const [err, setErr] = createSignal(false); const [err, setErr] = createSignal(false)
return ( return (
<Show when={!err()} fallback={props.fallbackErr}> <Show when={!err()} fallback={props.fallbackErr}>
<Image <Image
{...props} {...props}
onError={() => { onError={() => {
setErr(true); setErr(true)
}} }}
/> />
</Show> </Show>
); )
}; }

View File

@@ -1,15 +1,15 @@
import { Link, LinkProps } from "@solidjs/router"; import { Link, LinkProps } from "@solidjs/router"
import { Anchor, AnchorProps, ElementType } from "@hope-ui/solid"; import { Anchor, AnchorProps, ElementType } from "@hope-ui/solid"
import { joinBase } from "~/utils"; import { joinBase } from "~/utils"
import { useRouter } from "~/hooks"; import { useRouter } from "~/hooks"
export const LinkWithBase = (props: LinkProps) => ( export const LinkWithBase = (props: LinkProps) => (
<Link {...props} href={joinBase(props.href)} /> <Link {...props} href={joinBase(props.href)} />
); )
export const AnchorWithBase = <C extends ElementType = "a">( export const AnchorWithBase = <C extends ElementType = "a">(
props: AnchorProps<C> props: AnchorProps<C>
) => <Anchor {...props} href={joinBase(props.href)} />; ) => <Anchor {...props} href={joinBase(props.href)} />
export const LinkWithPush = (props: LinkProps) => { export const LinkWithPush = (props: LinkProps) => {
const { pushHref } = useRouter(); const { pushHref } = useRouter()
return <LinkWithBase {...props} href={pushHref(props.href)} />; return <LinkWithBase {...props} href={pushHref(props.href)} />
}; }

View File

@@ -1,15 +1,15 @@
// @ts-ignore // @ts-ignore
import { hljs } from "./highlight.js"; import { hljs } from "./highlight.js"
import SolidMarkdown from "solid-markdown"; import SolidMarkdown from "solid-markdown"
import remarkGfm from "remark-gfm"; import remarkGfm from "remark-gfm"
import rehypeRaw from "rehype-raw"; import rehypeRaw from "rehype-raw"
import "./markdown.css"; import "./markdown.css"
import { onMount } from "solid-js"; import { onMount } from "solid-js"
export const Markdown = (props: { children?: string }) => { export const Markdown = (props: { children?: string }) => {
onMount(() => { onMount(() => {
hljs.highlightAll(); hljs.highlightAll()
}); })
return ( return (
<SolidMarkdown <SolidMarkdown
class="markdown-body" class="markdown-body"
@@ -17,5 +17,5 @@ export const Markdown = (props: { children?: string }) => {
rehypePlugins={[rehypeRaw]} rehypePlugins={[rehypeRaw]}
children={props.children} children={props.children}
/> />
); )
}; }

View File

@@ -9,31 +9,31 @@ import {
Input, Input,
Textarea, Textarea,
FormHelperText, FormHelperText,
} from "@hope-ui/solid"; } from "@hope-ui/solid"
import { createSignal, Show } from "solid-js"; import { createSignal, Show } from "solid-js"
import { useT } from "~/hooks"; import { useT } from "~/hooks"
import { notify } from "~/utils"; import { notify } from "~/utils"
export type ModalInputProps = { export type ModalInputProps = {
opened: boolean; opened: boolean
onClose: () => void; onClose: () => void
title: string; title: string
onSubmit?: (text: string) => void; onSubmit?: (text: string) => void
type?: string; type?: string
defaultValue?: string; defaultValue?: string
loading?: boolean; loading?: boolean
tips?: string; tips?: string
}; }
export const ModalInput = (props: ModalInputProps) => { export const ModalInput = (props: ModalInputProps) => {
const [value, setValue] = createSignal(props.defaultValue ?? ""); const [value, setValue] = createSignal(props.defaultValue ?? "")
const t = useT(); const t = useT()
const submit = () => { const submit = () => {
if (!value()) { if (!value()) {
notify.warning(t("global.empty_input")); notify.warning(t("global.empty_input"))
return; return
} }
props.onSubmit?.(value()); props.onSubmit?.(value())
setValue(""); setValue("")
}; }
return ( return (
<Modal <Modal
blockScrollOnMount={false} blockScrollOnMount={false}
@@ -54,11 +54,11 @@ export const ModalInput = (props: ModalInputProps) => {
type={props.type} type={props.type}
value={value()} value={value()}
onInput={(e) => { onInput={(e) => {
setValue(e.currentTarget.value); setValue(e.currentTarget.value)
}} }}
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === "Enter") { if (e.key === "Enter") {
submit(); submit()
} }
}} }}
/> />
@@ -68,7 +68,7 @@ export const ModalInput = (props: ModalInputProps) => {
id="modal-input" id="modal-input"
value={value()} value={value()}
onInput={(e) => { onInput={(e) => {
setValue(e.currentTarget.value); setValue(e.currentTarget.value)
}} }}
/> />
</Show> </Show>
@@ -86,5 +86,5 @@ export const ModalInput = (props: ModalInputProps) => {
</ModalFooter> </ModalFooter>
</ModalContent> </ModalContent>
</Modal> </Modal>
); )
}; }

View File

@@ -1,62 +1,62 @@
import { Box } from "@hope-ui/solid"; import { Box } from "@hope-ui/solid"
import { createEffect, createSignal, onCleanup, onMount } from "solid-js"; import { createEffect, createSignal, onCleanup, onMount } from "solid-js"
import { MaybeLoading } from "./FullLoading"; import { MaybeLoading } from "./FullLoading"
import loader from "@monaco-editor/loader"; import loader from "@monaco-editor/loader"
import { monaco_cdn } from "~/utils"; import { monaco_cdn } from "~/utils"
loader.config({ loader.config({
paths: { paths: {
vs: monaco_cdn, vs: monaco_cdn,
}, },
}); })
export interface MonacoEditorProps { export interface MonacoEditorProps {
value: string; value: string
onChange?: (value: string) => void; onChange?: (value: string) => void
theme: "vs" | "vs-dark"; theme: "vs" | "vs-dark"
path?: string; path?: string
language?: string; language?: string
} }
let monaco: any; let monaco: any
export const MonacoEditorLoader = (props: MonacoEditorProps) => { export const MonacoEditorLoader = (props: MonacoEditorProps) => {
const [loading, setLoading] = createSignal(true); const [loading, setLoading] = createSignal(true)
loader.init().then((m) => { loader.init().then((m) => {
monaco = m; monaco = m
setLoading(false); setLoading(false)
}); })
return ( return (
<MaybeLoading loading={loading()}> <MaybeLoading loading={loading()}>
<MonacoEditor {...props} /> <MonacoEditor {...props} />
</MaybeLoading> </MaybeLoading>
); )
}; }
export const MonacoEditor = (props: MonacoEditorProps) => { export const MonacoEditor = (props: MonacoEditorProps) => {
let monacoEditorDiv: HTMLDivElement; let monacoEditorDiv: HTMLDivElement
let monacoEditor: any /*monaco.editor.IStandaloneCodeEditor*/; let monacoEditor: any /*monaco.editor.IStandaloneCodeEditor*/
let model: any /*monaco.editor.ITextModel*/; let model: any /*monaco.editor.ITextModel*/
onMount(() => { onMount(() => {
monacoEditor = monaco.editor.create(monacoEditorDiv!, { monacoEditor = monaco.editor.create(monacoEditorDiv!, {
value: props.value, value: props.value,
theme: props.theme, theme: props.theme,
}); })
model = monaco.editor.createModel( model = monaco.editor.createModel(
props.value, props.value,
props.language, props.language,
props.path ? monaco.Uri.parse(props.path) : undefined props.path ? monaco.Uri.parse(props.path) : undefined
); )
monacoEditor.setModel(model); monacoEditor.setModel(model)
monacoEditor.onDidChangeModelContent(() => { monacoEditor.onDidChangeModelContent(() => {
props.onChange?.(monacoEditor.getValue()); props.onChange?.(monacoEditor.getValue())
}); })
}); })
createEffect(() => { createEffect(() => {
monaco.editor.setTheme(props.theme); monaco.editor.setTheme(props.theme)
}); })
onCleanup(() => { onCleanup(() => {
model && model.dispose(); model && model.dispose()
monacoEditor && monacoEditor.dispose(); monacoEditor && monacoEditor.dispose()
}); })
return <Box w="$full" h="70vh" ref={monacoEditorDiv!} />; return <Box w="$full" h="70vh" ref={monacoEditorDiv!} />
}; }

View File

@@ -1,7 +1,7 @@
import { Button, HStack, IconButton } from "@hope-ui/solid"; import { Button, HStack, IconButton } from "@hope-ui/solid"
import { createMemo, For, mergeProps, Show } from "solid-js"; import { createMemo, For, mergeProps, Show } from "solid-js"
import { createStore } from "solid-js/store"; import { createStore } from "solid-js/store"
import { FaSolidAngleLeft, FaSolidAngleRight } from "solid-icons/fa"; import { FaSolidAngleLeft, FaSolidAngleRight } from "solid-icons/fa"
export interface PaginatorProps { export interface PaginatorProps {
colorScheme?: colorScheme?:
@@ -11,14 +11,14 @@ export interface PaginatorProps {
| "success" | "success"
| "info" | "info"
| "warning" | "warning"
| "danger"; | "danger"
// size?: "xs" | "sm" | "lg" | "xl" | "md"; // size?: "xs" | "sm" | "lg" | "xl" | "md";
defaultCurrent?: number; defaultCurrent?: number
onChange?: (current: number) => void; onChange?: (current: number) => void
hideOnSinglePage?: boolean; hideOnSinglePage?: boolean
total: number; total: number
defaultPageSize?: number; defaultPageSize?: number
maxShowPage?: number; maxShowPage?: number
} }
export const Paginator = (props: PaginatorProps) => { export const Paginator = (props: PaginatorProps) => {
const merged = mergeProps( const merged = mergeProps(
@@ -29,35 +29,35 @@ export const Paginator = (props: PaginatorProps) => {
hideOnSinglePage: true, hideOnSinglePage: true,
}, },
props props
); )
const [store, setStore] = createStore({ const [store, setStore] = createStore({
pageSize: merged.defaultPageSize, pageSize: merged.defaultPageSize,
current: merged.defaultCurrent, current: merged.defaultCurrent,
}); })
const pages = createMemo(() => { const pages = createMemo(() => {
return Math.ceil(merged.total / store.pageSize); return Math.ceil(merged.total / store.pageSize)
}); })
const leftPages = createMemo(() => { const leftPages = createMemo(() => {
const current = store.current; const current = store.current
const min = Math.max(2, current - Math.floor(merged.maxShowPage / 2)); const min = Math.max(2, current - Math.floor(merged.maxShowPage / 2))
return Array.from({ length: current - min }, (_, i) => min + i); return Array.from({ length: current - min }, (_, i) => min + i)
}); })
const rightPages = createMemo(() => { const rightPages = createMemo(() => {
const current = store.current; const current = store.current
const max = Math.min( const max = Math.min(
pages() - 1, pages() - 1,
current + Math.floor(merged.maxShowPage / 2) current + Math.floor(merged.maxShowPage / 2)
); )
return Array.from({ length: max - current }, (_, i) => current + 1 + i); return Array.from({ length: max - current }, (_, i) => current + 1 + i)
}); })
const size = { const size = {
"@initial": "sm", "@initial": "sm",
"@md": "md", "@md": "md",
} as const; } as const
const onPageChange = (page: number) => { const onPageChange = (page: number) => {
setStore("current", page); setStore("current", page)
merged.onChange?.(page); merged.onChange?.(page)
}; }
return ( return (
<Show when={!merged.hideOnSinglePage || pages() > 1}> <Show when={!merged.hideOnSinglePage || pages() > 1}>
<HStack spacing="$1"> <HStack spacing="$1">
@@ -66,7 +66,7 @@ export const Paginator = (props: PaginatorProps) => {
size={size} size={size}
colorScheme={merged.colorScheme} colorScheme={merged.colorScheme}
onClick={() => { onClick={() => {
onPageChange(1); onPageChange(1)
}} }}
px="$3" px="$3"
> >
@@ -78,7 +78,7 @@ export const Paginator = (props: PaginatorProps) => {
aria-label="Previous" aria-label="Previous"
colorScheme={merged.colorScheme} colorScheme={merged.colorScheme}
onClick={() => { onClick={() => {
onPageChange(store.current - 1); onPageChange(store.current - 1)
}} }}
w="2rem !important" w="2rem !important"
/> />
@@ -89,7 +89,7 @@ export const Paginator = (props: PaginatorProps) => {
size={size} size={size}
colorScheme={merged.colorScheme} colorScheme={merged.colorScheme}
onClick={() => { onClick={() => {
onPageChange(page); onPageChange(page)
}} }}
px={page > 10 ? "$2_5" : "$3"} px={page > 10 ? "$2_5" : "$3"}
> >
@@ -111,7 +111,7 @@ export const Paginator = (props: PaginatorProps) => {
size={size} size={size}
colorScheme={merged.colorScheme} colorScheme={merged.colorScheme}
onClick={() => { onClick={() => {
onPageChange(page); onPageChange(page)
}} }}
px={page > 10 ? "$2_5" : "$3"} px={page > 10 ? "$2_5" : "$3"}
> >
@@ -126,7 +126,7 @@ export const Paginator = (props: PaginatorProps) => {
aria-label="Next" aria-label="Next"
colorScheme={merged.colorScheme} colorScheme={merged.colorScheme}
onClick={() => { onClick={() => {
onPageChange(store.current + 1); onPageChange(store.current + 1)
}} }}
w="2rem !important" w="2rem !important"
/> />
@@ -134,7 +134,7 @@ export const Paginator = (props: PaginatorProps) => {
size={size} size={size}
colorScheme={merged.colorScheme} colorScheme={merged.colorScheme}
onClick={() => { onClick={() => {
onPageChange(pages()); onPageChange(pages())
}} }}
px={pages() > 10 ? "$2_5" : "$3"} px={pages() > 10 ? "$2_5" : "$3"}
> >
@@ -143,5 +143,5 @@ export const Paginator = (props: PaginatorProps) => {
</Show> </Show>
</HStack> </HStack>
</Show> </Show>
); )
}; }

View File

@@ -1,10 +1,10 @@
import { Icon, useColorMode, useColorModeValue } from "@hope-ui/solid"; import { Icon, useColorMode, useColorModeValue } from "@hope-ui/solid"
// import { IoMoonOutline as Moon } from "solid-icons/io"; // import { IoMoonOutline as Moon } from "solid-icons/io";
import { FiSun as Sun } from "solid-icons/fi"; import { FiSun as Sun } from "solid-icons/fi"
import { FiMoon as Moon } from "solid-icons/fi"; import { FiMoon as Moon } from "solid-icons/fi"
const SwitchColorMode = () => { const SwitchColorMode = () => {
const { toggleColorMode } = useColorMode(); const { toggleColorMode } = useColorMode()
const icon = useColorModeValue( const icon = useColorModeValue(
{ {
size: "$8", size: "$8",
@@ -16,7 +16,7 @@ const SwitchColorMode = () => {
component: Sun, component: Sun,
p: "$0_5", p: "$0_5",
} }
); )
return ( return (
<Icon <Icon
cursor="pointer" cursor="pointer"
@@ -25,6 +25,6 @@ const SwitchColorMode = () => {
onClick={toggleColorMode} onClick={toggleColorMode}
p={icon().p} p={icon().p}
/> />
); )
}; }
export { SwitchColorMode }; export { SwitchColorMode }

View File

@@ -1,15 +1,15 @@
import { Badge } from "@hope-ui/solid"; import { Badge } from "@hope-ui/solid"
import { useT } from "~/hooks"; import { useT } from "~/hooks"
export interface WetherProps { export interface WetherProps {
yes?: boolean; yes?: boolean
} }
export const Wether = (props: WetherProps) => { export const Wether = (props: WetherProps) => {
const t = useT(); const t = useT()
return ( return (
<Badge colorScheme={props.yes ? "success" : "danger"}> <Badge colorScheme={props.yes ? "success" : "danger"}>
{t(`global.${props.yes ? "yes" : "no"}`)} {t(`global.${props.yes ? "yes" : "no"}`)}
</Badge> </Badge>
); )
}; }

File diff suppressed because one or more lines are too long

View File

@@ -1,12 +1,12 @@
export * from "./FullLoading"; export * from "./FullLoading"
export * from "./SwitchColorMode"; export * from "./SwitchColorMode"
export * from "./SwitchLanguage"; export * from "./SwitchLanguage"
export * from "./Hello"; export * from "./Hello"
export * from "./FolderTree"; export * from "./FolderTree"
export * from "./Wether"; export * from "./Wether"
export * from "./LinkWithBase"; export * from "./LinkWithBase"
export * from "./ImageWithError"; export * from "./ImageWithError"
export * from "./Markdown"; export * from "./Markdown"
export * from "./ModalInput"; export * from "./ModalInput"
export * from "./Base"; export * from "./Base"
export * from "./Paginator"; export * from "./Paginator"

View File

@@ -1,8 +1,8 @@
export * from "./useFetch"; export * from "./useFetch"
export * from "./useRouter"; export * from "./useRouter"
export * from "./useT"; export * from "./useT"
export * from "./useTitle"; export * from "./useTitle"
export * from "./usePath"; export * from "./usePath"
export * from "./useLink"; export * from "./useLink"
export * from "./useUtil"; export * from "./useUtil"
export * from "./useDownload"; export * from "./useDownload"

View File

@@ -1,25 +1,25 @@
import axios from "axios"; import axios from "axios"
import { local, selectedObjs } from "~/store"; import { local, selectedObjs } from "~/store"
import { notify } from "~/utils"; import { notify } from "~/utils"
import { useSelectedLink, useLink, useT } from "."; import { useSelectedLink, useLink, useT } from "."
export const useDownload = () => { export const useDownload = () => {
const { rawLinks } = useSelectedLink(); const { rawLinks } = useSelectedLink()
const { rawLink } = useLink(); const { rawLink } = useLink()
const t = useT(); const t = useT()
return { return {
batchDownloadSelected: () => { batchDownloadSelected: () => {
const urls = rawLinks(true); const urls = rawLinks(true)
urls.forEach((url) => { urls.forEach((url) => {
window.open(url, "_blank"); window.open(url, "_blank")
}); })
}, },
sendToAria2: async () => { sendToAria2: async () => {
const selectedFiles = selectedObjs().filter((obj) => !obj.is_dir); const selectedFiles = selectedObjs().filter((obj) => !obj.is_dir)
const { aria2_rpc_url, aria2_rpc_secret, aria2_dir } = local; const { aria2_rpc_url, aria2_rpc_secret, aria2_dir } = local
if (!aria2_rpc_url) { if (!aria2_rpc_url) {
notify.warning(t("home.toolbar.aria2_not_set")); notify.warning(t("home.toolbar.aria2_not_set"))
return; return
} }
try { try {
for (const file of selectedFiles) { for (const file of selectedFiles) {
@@ -36,14 +36,14 @@ export const useDownload = () => {
"check-certificate": "false", "check-certificate": "false",
}, },
], ],
}); })
console.log(resp); console.log(resp)
} }
notify.success(t("home.toolbar.send_aria2_success")); notify.success(t("home.toolbar.send_aria2_success"))
} catch (e) { } catch (e) {
console.error(e); console.error(e)
notify.error(`failed to send to aria2: ${e}`); notify.error(`failed to send to aria2: ${e}`)
} }
}, },
}; }
}; }

View File

@@ -1,15 +1,9 @@
import { objStore, selectedObjs, State, me } from "~/store"; import { objStore, selectedObjs, State, me } from "~/store"
import { Obj } from "~/types"; import { Obj } from "~/types"
import { import { api, encodePath, pathDir, pathJoin, standardizePath } from "~/utils"
api, import { useRouter, useUtil } from "."
encodePath,
pathDir,
pathJoin,
standardizePath,
} from "~/utils";
import { useRouter, useUtil } from ".";
type URLType = "preview" | "direct" | "proxy"; type URLType = "preview" | "direct" | "proxy"
// get download url by dir and obj // get download url by dir and obj
export const getLinkByDirAndObj = ( export const getLinkByDirAndObj = (
@@ -18,81 +12,81 @@ export const getLinkByDirAndObj = (
type: URLType = "direct", type: URLType = "direct",
encodeAll?: boolean encodeAll?: boolean
) => { ) => {
dir = standardizePath(pathJoin(me().base_path, dir), true); dir = standardizePath(pathJoin(me().base_path, dir), true)
let path = `${dir}/${obj.name}`; let path = `${dir}/${obj.name}`
path = encodePath(path, encodeAll); path = encodePath(path, encodeAll)
let host = api; let host = api
let prefix = type === "direct" ? "/d" : "/p"; let prefix = type === "direct" ? "/d" : "/p"
if (type === "preview") { if (type === "preview") {
host = location.origin; host = location.origin
prefix = ""; prefix = ""
} }
let ans = `${host}${prefix}${path}`; let ans = `${host}${prefix}${path}`
if (type !== "preview" && obj.sign) { if (type !== "preview" && obj.sign) {
ans += `?sign=${obj.sign}`; ans += `?sign=${obj.sign}`
} }
return ans; return ans
}; }
// get download link by current state and pathname // get download link by current state and pathname
export const useLink = () => { export const useLink = () => {
const { pathname } = useRouter(); const { pathname } = useRouter()
const getLinkByObj = (obj: Obj, type?: URLType, encodeAll?: boolean) => { const getLinkByObj = (obj: Obj, type?: URLType, encodeAll?: boolean) => {
const dir = const dir =
objStore.state === State.Folder ? pathname() : pathDir(pathname()); objStore.state === State.Folder ? pathname() : pathDir(pathname())
return getLinkByDirAndObj(dir, obj, type, encodeAll); return getLinkByDirAndObj(dir, obj, type, encodeAll)
}; }
const rawLink = (obj: Obj, encodeAll?: boolean) => { const rawLink = (obj: Obj, encodeAll?: boolean) => {
return getLinkByObj(obj, "direct", encodeAll); return getLinkByObj(obj, "direct", encodeAll)
}; }
return { return {
getLinkByObj: getLinkByObj, getLinkByObj: getLinkByObj,
rawLink: rawLink, rawLink: rawLink,
proxyLink: (obj: Obj, encodeAll?: boolean) => { proxyLink: (obj: Obj, encodeAll?: boolean) => {
return getLinkByObj(obj, "proxy", encodeAll); return getLinkByObj(obj, "proxy", encodeAll)
}, },
previewPage: (obj: Obj, encodeAll?: boolean) => { previewPage: (obj: Obj, encodeAll?: boolean) => {
return getLinkByObj(obj, "preview", encodeAll); return getLinkByObj(obj, "preview", encodeAll)
}, },
currentObjLink: (encodeAll?: boolean) => { currentObjLink: (encodeAll?: boolean) => {
return rawLink(objStore.obj, encodeAll); return rawLink(objStore.obj, encodeAll)
}, },
}; }
}; }
export const useSelectedLink = () => { export const useSelectedLink = () => {
const { previewPage, rawLink: rawUrl } = useLink(); const { previewPage, rawLink: rawUrl } = useLink()
const rawLinks = (encodeAll?: boolean) => { const rawLinks = (encodeAll?: boolean) => {
return selectedObjs() return selectedObjs()
.filter((obj) => !obj.is_dir) .filter((obj) => !obj.is_dir)
.map((obj) => rawUrl(obj, encodeAll)); .map((obj) => rawUrl(obj, encodeAll))
}; }
return { return {
rawLinks: rawLinks, rawLinks: rawLinks,
previewPagesText: () => { previewPagesText: () => {
return selectedObjs() return selectedObjs()
.map((obj) => previewPage(obj, true)) .map((obj) => previewPage(obj, true))
.join("\n"); .join("\n")
}, },
rawLinksText: (encodeAll?: boolean) => { rawLinksText: (encodeAll?: boolean) => {
return rawLinks(encodeAll).join("\n"); return rawLinks(encodeAll).join("\n")
}, },
}; }
}; }
export const useCopyLink = () => { export const useCopyLink = () => {
const { copy } = useUtil(); const { copy } = useUtil()
const { previewPagesText, rawLinksText } = useSelectedLink(); const { previewPagesText, rawLinksText } = useSelectedLink()
const { currentObjLink } = useLink(); const { currentObjLink } = useLink()
return { return {
copySelectedPreviewPage: () => { copySelectedPreviewPage: () => {
copy(previewPagesText()); copy(previewPagesText())
}, },
copySelectedRawLink: (encodeAll?: boolean) => { copySelectedRawLink: (encodeAll?: boolean) => {
copy(rawLinksText(encodeAll)); copy(rawLinksText(encodeAll))
}, },
copyCurrentRawLink: (encodeAll?: boolean) => { copyCurrentRawLink: (encodeAll?: boolean) => {
copy(currentObjLink(encodeAll)); copy(currentObjLink(encodeAll))
}, },
}; }
}; }

View File

@@ -1,25 +1,25 @@
import { useI18n } from "@solid-primitives/i18n"; import { useI18n } from "@solid-primitives/i18n"
import { firstUpperCase } from "~/utils"; import { firstUpperCase } from "~/utils"
const useT = () => { const useT = () => {
const [t] = useI18n(); const [t] = useI18n()
return ( return (
key: string, key: string,
params?: Record<string, string> | undefined, params?: Record<string, string> | undefined,
defaultValue?: string | undefined defaultValue?: string | undefined
) => { ) => {
const value = t(key, params, defaultValue); const value = t(key, params, defaultValue)
if (!value) { if (!value) {
if (import.meta.env.DEV) return key; if (import.meta.env.DEV) return key
let lastDotIndex = key.lastIndexOf("."); let lastDotIndex = key.lastIndexOf(".")
if (lastDotIndex === key.length - 1) { if (lastDotIndex === key.length - 1) {
lastDotIndex = key.lastIndexOf(".", lastDotIndex - 1); lastDotIndex = key.lastIndexOf(".", lastDotIndex - 1)
} }
const last = key.slice(lastDotIndex + 1); const last = key.slice(lastDotIndex + 1)
return firstUpperCase(last).split("_").join(" "); return firstUpperCase(last).split("_").join(" ")
} }
return value; return value
}; }
}; }
export { useT }; export { useT }

View File

@@ -1,55 +1,55 @@
import { createEffect, onCleanup } from "solid-js"; import { createEffect, onCleanup } from "solid-js"
import { getSetting } from "~/store"; import { getSetting } from "~/store"
import { pathBase } from "~/utils"; import { pathBase } from "~/utils"
import { useRouter } from "./useRouter"; import { useRouter } from "./useRouter"
import { useT } from "./useT"; import { useT } from "./useT"
let id = 0; let id = 0
const effects: Record<string, boolean> = {}; const effects: Record<string, boolean> = {}
const useTitle = (title: string | (() => string)) => { const useTitle = (title: string | (() => string)) => {
const cid = (id++).toString(); const cid = (id++).toString()
const valids: string[] = []; const valids: string[] = []
for (const key in effects) { for (const key in effects) {
if (effects[key]) { if (effects[key]) {
valids.push(key); valids.push(key)
effects[key] = false; effects[key] = false
} }
} }
effects[cid] = true; effects[cid] = true
const pre = document.title; const pre = document.title
if (typeof title === "function") { if (typeof title === "function") {
createEffect(() => { createEffect(() => {
if (effects[cid]) { if (effects[cid]) {
document.title = title(); document.title = title()
} }
}); })
} else { } else {
document.title = title; document.title = title
} }
onCleanup(() => { onCleanup(() => {
// document.title = pre; // document.title = pre;
delete effects[cid]; delete effects[cid]
for (const key in valids) { for (const key in valids) {
effects[key] = true; effects[key] = true
} }
}); })
}; }
export const useObjTitle = () => { export const useObjTitle = () => {
const t = useT(); const t = useT()
const { pathname } = useRouter(); const { pathname } = useRouter()
useTitle( useTitle(
() => () =>
`${ `${
pathname() === "/" ? t("manage.sidemenu.home") : pathBase(pathname()) pathname() === "/" ? t("manage.sidemenu.home") : pathBase(pathname())
} | ${getSetting("site_title")}` } | ${getSetting("site_title")}`
); )
}; }
export const useManageTitle = (title: string) => { export const useManageTitle = (title: string) => {
const t = useT(); const t = useT()
useTitle(() => `${t(title)} | ${t("manage.title")}`); useTitle(() => `${t(title)} | ${t("manage.title")}`)
}; }
export { useTitle }; export { useTitle }

View File

@@ -1,34 +1,34 @@
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard"
import { createResource } from "solid-js"; import { createResource } from "solid-js"
import { getHideFiles, objStore } from "~/store"; import { getHideFiles, objStore } from "~/store"
import { Obj } from "~/types"; import { Obj } from "~/types"
import { fetchText, notify, pathJoin } from "~/utils"; import { fetchText, notify, pathJoin } from "~/utils"
import { useT, useLink, useRouter } from "."; import { useT, useLink, useRouter } from "."
export const useUtil = () => { export const useUtil = () => {
const t = useT(); const t = useT()
const { pathname } = useRouter(); const { pathname } = useRouter()
return { return {
copy: (text: string) => { copy: (text: string) => {
copy(text); copy(text)
notify.success(t("global.copied")); notify.success(t("global.copied"))
}, },
isHide: (obj: Obj) => { isHide: (obj: Obj) => {
const hideFiles = getHideFiles(); const hideFiles = getHideFiles()
for (const reg of hideFiles) { for (const reg of hideFiles) {
if (reg.test(pathJoin(pathname(), obj.name))) { if (reg.test(pathJoin(pathname(), obj.name))) {
return true; return true
} }
} }
return false; return false
}, },
}; }
}; }
export const useFetchText = () => { export const useFetchText = () => {
const { proxyLink } = useLink(); const { proxyLink } = useLink()
const fetchContent = async () => { const fetchContent = async () => {
return fetchText(proxyLink(objStore.obj, true)); return fetchText(proxyLink(objStore.obj, true))
}; }
return createResource("", fetchContent); return createResource("", fetchContent)
}; }

8
src/index.d.ts vendored
View File

@@ -1,7 +1,7 @@
declare module "aplayer"; declare module "aplayer"
declare namespace aliyun { declare namespace aliyun {
class Config { class Config {
setToken(token: { token: string }): any; setToken(token: { token: string }): any
} }
function config(options: { mount: Element; url: string }): Config; function config(options: { mount: Element; url: string }): Config
} }

View File

@@ -417,4 +417,4 @@
"chunk_size": "Chunk size", "chunk_size": "Chunk size",
"root_folder_path": "Root folder path" "root_folder_path": "Root folder path"
} }
} }

View File

@@ -1,7 +1,7 @@
const jsons = import.meta.glob("./*.json", { eager: true, import: "default" }); const jsons = import.meta.glob("./*.json", { eager: true, import: "default" })
const langs: any = {}; const langs: any = {}
for (const path in jsons) { for (const path in jsons) {
const name = path.split("/")[1].split(".")[0]; const name = path.split("/")[1].split(".")[0]
langs[name] = jsons[path]; langs[name] = jsons[path]
} }
export default langs; export default langs

View File

@@ -47,4 +47,4 @@
"max_980px": "Max 980px", "max_980px": "Max 980px",
"hope_container": "Hope container" "hope_container": "Hope container"
} }
} }

View File

@@ -1,12 +1,12 @@
/* @refresh reload */ /* @refresh reload */
import { Router } from "@solidjs/router"; import { Router } from "@solidjs/router"
import { render } from "solid-js/web"; import { render } from "solid-js/web"
import { Index } from "./app"; import { Index } from "./app"
declare global { declare global {
interface Window { interface Window {
[key: string]: any; [key: string]: any
} }
} }
render( render(
@@ -16,4 +16,4 @@ render(
</Router> </Router>
), ),
document.getElementById("root") as HTMLElement document.getElementById("root") as HTMLElement
); )

View File

@@ -1,8 +1,8 @@
import { VStack } from "@hope-ui/solid"; import { VStack } from "@hope-ui/solid"
import { Nav } from "./Nav"; import { Nav } from "./Nav"
import { Obj } from "./Obj"; import { Obj } from "./Obj"
import { Readme } from "./Readme"; import { Readme } from "./Readme"
import { Container } from "./Container"; import { Container } from "./Container"
export const Body = () => { export const Body = () => {
return ( return (
@@ -21,5 +21,5 @@ export const Body = () => {
<Readme /> <Readme />
</VStack> </VStack>
</Container> </Container>
); )
}; }

View File

@@ -1,14 +1,14 @@
import { JSXElement, Match, Switch } from "solid-js"; import { JSXElement, Match, Switch } from "solid-js"
import { getSetting } from "~/store"; import { getSetting } from "~/store"
import { Box, Container as HopeContainer } from "@hope-ui/solid"; import { Box, Container as HopeContainer } from "@hope-ui/solid"
export const Container = (props: { children: JSXElement }) => { export const Container = (props: { children: JSXElement }) => {
const container = getSetting("home_container"); const container = getSetting("home_container")
return ( return (
<Switch fallback={<Box w="min(99%, 980px)">{props.children}</Box>}> <Switch fallback={<Box w="min(99%, 980px)">{props.children}</Box>}>
<Match when={container === "hope_container"}> <Match when={container === "hope_container"}>
<HopeContainer>{props.children}</HopeContainer> <HopeContainer>{props.children}</HopeContainer>
</Match> </Match>
</Switch> </Switch>
); )
}; }

View File

@@ -1,12 +1,12 @@
import { Anchor, HStack, VStack } from "@hope-ui/solid"; import { Anchor, HStack, VStack } from "@hope-ui/solid"
import { Link } from "@solidjs/router"; import { Link } from "@solidjs/router"
import { AnchorWithBase } from "~/components"; import { AnchorWithBase } from "~/components"
import { useT } from "~/hooks"; import { useT } from "~/hooks"
import { me } from "~/store"; import { me } from "~/store"
import { UserMethods } from "~/types"; import { UserMethods } from "~/types"
export const Footer = () => { export const Footer = () => {
const t = useT(); const t = useT()
return ( return (
<VStack class="footer" w="$full" py="$4"> <VStack class="footer" w="$full" py="$4">
<HStack spacing="$1"> <HStack spacing="$1">
@@ -18,11 +18,9 @@ export const Footer = () => {
as={Link} as={Link}
href={UserMethods.is_guest(me()) ? "/@login" : "/@manage"} href={UserMethods.is_guest(me()) ? "/@login" : "/@manage"}
> >
{t( {t(UserMethods.is_guest(me()) ? "login.login" : "home.footer.manage")}
UserMethods.is_guest(me()) ? "login.login" : "home.footer.manage"
)}
</AnchorWithBase> </AnchorWithBase>
</HStack> </HStack>
</VStack> </VStack>
); )
}; }

View File

@@ -1,17 +1,17 @@
import { Markdown } from "~/components"; import { Markdown } from "~/components"
import { useTitle } from "~/hooks"; import { useTitle } from "~/hooks"
import { getSetting } from "~/store"; import { getSetting } from "~/store"
import { notify } from "~/utils"; import { notify } from "~/utils"
import { Body } from "./Body"; import { Body } from "./Body"
import { Footer } from "./Footer"; import { Footer } from "./Footer"
import { Header } from "./Header"; import { Header } from "./Header"
import { Toolbar } from "./toolbar/Toolbar"; import { Toolbar } from "./toolbar/Toolbar"
const Index = () => { 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 (
<> <>
@@ -20,7 +20,7 @@ const Index = () => {
<Body /> <Body />
<Footer /> <Footer />
</> </>
); )
}; }
export default Index; export default Index

View File

@@ -3,33 +3,30 @@ import {
BreadcrumbItem, BreadcrumbItem,
BreadcrumbLink, BreadcrumbLink,
BreadcrumbSeparator, BreadcrumbSeparator,
} 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 } from "~/store"; import { getSetting } from "~/store"
import { encodePath, hoverColor, joinBase } from "~/utils"; import { encodePath, hoverColor, joinBase } from "~/utils"
export const Nav = () => { export const Nav = () => {
const { pathname } = useRouter(); const { pathname } = useRouter()
const paths = createMemo(() => [ const paths = createMemo(() => ["", ...pathname().split("/").filter(Boolean)])
"", const t = useT()
...pathname().split("/").filter(Boolean), const { setPathAs } = usePath()
]);
const t = useT();
const { setPathAs } = usePath();
return ( return (
<Breadcrumb class="nav" w="$full"> <Breadcrumb class="nav" w="$full">
<For each={paths()}> <For each={paths()}>
{(name, i) => { {(name, i) => {
const isLast = createMemo(() => i() === paths().length - 1); const isLast = createMemo(() => i() === paths().length - 1)
const path = paths() const path = paths()
.slice(0, i() + 1) .slice(0, i() + 1)
.join("/"); .join("/")
const href = encodePath(path); const href = encodePath(path)
let text = () => name; let text = () => name
if (text() === "") { if (text() === "") {
text = () => getSetting("home_icon") + t("manage.sidemenu.home"); text = () => getSetting("home_icon") + t("manage.sidemenu.home")
} }
return ( return (
<BreadcrumbItem class="nav-item"> <BreadcrumbItem class="nav-item">
@@ -55,9 +52,9 @@ export const Nav = () => {
<BreadcrumbSeparator class="nav-separator" /> <BreadcrumbSeparator class="nav-separator" />
</Show> </Show>
</BreadcrumbItem> </BreadcrumbItem>
); )
}} }}
</For> </For>
</Breadcrumb> </Breadcrumb>
); )
}; }

View File

@@ -7,15 +7,15 @@ import {
Text, Text,
useColorModeValue, useColorModeValue,
VStack, VStack,
} from "@hope-ui/solid"; } from "@hope-ui/solid"
import { LinkWithBase } from "~/components"; import { LinkWithBase } from "~/components"
import { usePath, useRouter, useT } from "~/hooks"; import { usePath, useRouter, useT } from "~/hooks"
import { password, setPassword } from "~/store"; import { password, setPassword } from "~/store"
const Password = () => { const Password = () => {
const t = useT(); const t = useT()
const { refresh } = usePath(); const { refresh } = usePath()
const { back } = useRouter(); const { back } = useRouter()
return ( return (
<VStack <VStack
w={{ w={{
@@ -33,7 +33,7 @@ const Password = () => {
background={useColorModeValue("$neutral3", "$neutral2")()} background={useColorModeValue("$neutral3", "$neutral2")()}
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === "Enter") { if (e.key === "Enter") {
refresh(true); refresh(true)
} }
}} }}
onInput={(e) => setPassword(e.currentTarget.value)} onInput={(e) => setPassword(e.currentTarget.value)}
@@ -62,6 +62,6 @@ const Password = () => {
</HStack> </HStack>
</HStack> </HStack>
</VStack> </VStack>
); )
}; }
export default Password; export default Password

View File

@@ -1,17 +1,17 @@
import { HStack, VStack } from "@hope-ui/solid"; import { HStack, VStack } from "@hope-ui/solid"
import { createMemo, createSignal, Show, Suspense } from "solid-js"; import { createMemo, createSignal, Show, Suspense } from "solid-js"
import { Dynamic } from "solid-js/web"; import { Dynamic } from "solid-js/web"
import { FullLoading, SelectWrapper } from "~/components"; import { FullLoading, SelectWrapper } from "~/components"
import { objStore } from "~/store"; import { objStore } from "~/store"
import { Download } from "../previews/download"; import { Download } from "../previews/download"
import { OpenWith } from "./open-with"; import { OpenWith } from "./open-with"
import { getPreviews } from "../previews"; import { getPreviews } from "../previews"
const File = () => { const File = () => {
const previews = createMemo(() => { const previews = createMemo(() => {
return getPreviews({ ...objStore.obj, provider: objStore.provider }); return getPreviews({ ...objStore.obj, provider: objStore.provider })
}); })
const [cur, setCur] = createSignal(previews()[0]); const [cur, setCur] = createSignal(previews()[0])
return ( return (
<Show when={previews().length > 1} fallback={<Download openWith />}> <Show when={previews().length > 1} fallback={<Download openWith />}>
<VStack w="$full" spacing="$2"> <VStack w="$full" spacing="$2">
@@ -20,7 +20,7 @@ const File = () => {
alwaysShowBorder alwaysShowBorder
value={cur().name} value={cur().name}
onChange={(name) => { onChange={(name) => {
setCur(previews().find((p) => p.name === name)!); setCur(previews().find((p) => p.name === name)!)
}} }}
options={previews().map((item) => ({ value: item.name }))} options={previews().map((item) => ({ value: item.name }))}
/> />
@@ -31,7 +31,7 @@ const File = () => {
</Suspense> </Suspense>
</VStack> </VStack>
</Show> </Show>
); )
}; }
export default File; export default File

View File

@@ -5,18 +5,18 @@ import {
MenuContent, MenuContent,
MenuItem, MenuItem,
MenuTrigger, MenuTrigger,
} from "@hope-ui/solid"; } from "@hope-ui/solid"
import { createMemo, For, Show } from "solid-js"; import { createMemo, For, Show } from "solid-js"
import { useT } from "~/hooks"; import { useT } from "~/hooks"
import { getExternalPreviews, objStore } from "~/store"; import { getExternalPreviews, objStore } from "~/store"
import { FaSolidAngleDown } from "solid-icons/fa"; import { FaSolidAngleDown } from "solid-icons/fa"
import { convertURL } from "~/utils"; import { convertURL } from "~/utils"
export const OpenWith = () => { export const OpenWith = () => {
const t = useT(); const t = useT()
const previews = createMemo(() => { const previews = createMemo(() => {
return getExternalPreviews(objStore.obj.name); return getExternalPreviews(objStore.obj.name)
}); })
return ( return (
<Show when={previews().length}> <Show when={previews().length}>
<Menu> <Menu>
@@ -46,5 +46,5 @@ export const OpenWith = () => {
</MenuContent> </MenuContent>
</Menu> </Menu>
</Show> </Show>
); )
}; }

View File

@@ -1,55 +1,55 @@
import { lazy, Show, createEffect, createMemo, onCleanup } from "solid-js"; import { lazy, Show, createEffect, createMemo, onCleanup } from "solid-js"
import { layout } from "~/store"; import { layout } from "~/store"
import { ContextMenu } from "./context-menu"; import { ContextMenu } from "./context-menu"
import { Pager } from "./Pager"; import { Pager } from "./Pager"
import { useLink } from "~/hooks"; import { useLink } from "~/hooks"
import { objStore } from "~/store"; import { objStore } from "~/store"
import { ObjType } from "~/types"; import { ObjType } from "~/types"
import { bus } from "~/utils"; import { bus } from "~/utils"
import lightGallery from "lightgallery"; import lightGallery from "lightgallery"
import lgThumbnail from "lightgallery/plugins/thumbnail"; import lgThumbnail from "lightgallery/plugins/thumbnail"
import lgZoom from "lightgallery/plugins/zoom"; import lgZoom from "lightgallery/plugins/zoom"
import lgRotate from "lightgallery/plugins/rotate"; import lgRotate from "lightgallery/plugins/rotate"
import lgAutoplay from "lightgallery/plugins/autoplay"; import lgAutoplay from "lightgallery/plugins/autoplay"
import lgFullscreen from "lightgallery/plugins/fullscreen"; import lgFullscreen from "lightgallery/plugins/fullscreen"
import "lightgallery/css/lightgallery-bundle.css"; import "lightgallery/css/lightgallery-bundle.css"
import { LightGallery } from "lightgallery/lightgallery"; import { LightGallery } from "lightgallery/lightgallery"
import { Search } from "./Search"; import { Search } from "./Search"
const ListLayout = lazy(() => import("./List")); const ListLayout = lazy(() => import("./List"))
const GridLayout = lazy(() => import("./Grid")); const GridLayout = lazy(() => import("./Grid"))
const Folder = () => { const Folder = () => {
const { rawLink } = useLink(); const { rawLink } = useLink()
const images = createMemo(() => const images = createMemo(() =>
objStore.objs.filter((obj) => obj.type === ObjType.IMAGE) objStore.objs.filter((obj) => obj.type === ObjType.IMAGE)
); )
let dynamicGallery: LightGallery | undefined; let dynamicGallery: LightGallery | undefined
createEffect(() => { createEffect(() => {
dynamicGallery?.destroy(); dynamicGallery?.destroy()
if (images().length > 0) { if (images().length > 0) {
dynamicGallery = lightGallery(document.createElement("div"), { dynamicGallery = lightGallery(document.createElement("div"), {
dynamic: true, dynamic: true,
thumbnail: true, thumbnail: true,
plugins: [lgZoom, lgThumbnail, lgRotate, lgAutoplay, lgFullscreen], plugins: [lgZoom, lgThumbnail, lgRotate, lgAutoplay, lgFullscreen],
dynamicEl: images().map((obj) => { dynamicEl: images().map((obj) => {
const raw = rawLink(obj, true); const raw = rawLink(obj, true)
return { return {
src: raw, src: raw,
thumb: obj.thumb === "" ? raw : obj.thumb, thumb: obj.thumb === "" ? raw : obj.thumb,
subHtml: `<h4>${obj.name}</h4>`, subHtml: `<h4>${obj.name}</h4>`,
}; }
}), }),
}); })
} }
}); })
bus.on("gallery", (name) => { bus.on("gallery", (name) => {
dynamicGallery?.openGallery(images().findIndex((obj) => obj.name === name)); dynamicGallery?.openGallery(images().findIndex((obj) => obj.name === name))
}); })
onCleanup(() => { onCleanup(() => {
bus.off("gallery"); bus.off("gallery")
dynamicGallery?.destroy(); dynamicGallery?.destroy()
}); })
return ( return (
<> <>
<Show when={layout() === "list"} fallback={<GridLayout />}> <Show when={layout() === "list"} fallback={<GridLayout />}>
@@ -59,7 +59,7 @@ const Folder = () => {
<Search /> <Search />
<ContextMenu /> <ContextMenu />
</> </>
); )
}; }
export default Folder; export default Folder

View File

@@ -1,8 +1,8 @@
import { Grid } from "@hope-ui/solid"; import { Grid } from "@hope-ui/solid"
import { For } from "solid-js"; import { For } from "solid-js"
import { GridItem } from "./GridItem"; import { GridItem } from "./GridItem"
import "lightgallery/css/lightgallery-bundle.css"; import "lightgallery/css/lightgallery-bundle.css"
import { objStore } from "~/store"; import { objStore } from "~/store"
const GridLayout = () => { const GridLayout = () => {
return ( return (
@@ -13,11 +13,11 @@ const GridLayout = () => {
> >
<For each={objStore.objs}> <For each={objStore.objs}>
{(obj, i) => { {(obj, i) => {
return <GridItem obj={obj} index={i()} />; return <GridItem obj={obj} index={i()} />
}} }}
</For> </For>
</Grid> </Grid>
); )
}; }
export default GridLayout; export default GridLayout

View File

@@ -1,5 +1,5 @@
import { Grid, Skeleton } from "@hope-ui/solid"; import { Grid, Skeleton } from "@hope-ui/solid"
import { For } from "solid-js"; import { For } from "solid-js"
const GridSkeleton = () => { const GridSkeleton = () => {
return ( return (
@@ -12,7 +12,7 @@ const GridSkeleton = () => {
{() => <Skeleton w="$full" h="$28" rounded="$lg" />} {() => <Skeleton w="$full" h="$28" rounded="$lg" />}
</For> </For>
</Grid> </Grid>
); )
}; }
export default GridSkeleton; export default GridSkeleton

View File

@@ -1,5 +1,5 @@
import { Skeleton, VStack } from "@hope-ui/solid"; import { Skeleton, VStack } from "@hope-ui/solid"
import { For } from "solid-js"; import { For } from "solid-js"
const ListSkeleton = () => { const ListSkeleton = () => {
return ( return (
@@ -8,7 +8,7 @@ const ListSkeleton = () => {
{() => <Skeleton rounded="$lg" w="$full" h="$8" />} {() => <Skeleton rounded="$lg" w="$full" h="$8" />}
</For> </For>
</VStack> </VStack>
); )
}; }
export default ListSkeleton; export default ListSkeleton

View File

@@ -1,15 +1,15 @@
import { Menu, Item } from "solid-contextmenu"; import { Menu, Item } from "solid-contextmenu"
import { useCopyLink, useDownload, useT } from "~/hooks"; import { useCopyLink, useDownload, useT } from "~/hooks"
import "solid-contextmenu/dist/style.css"; import "solid-contextmenu/dist/style.css"
import { HStack, Icon, Text, useColorMode } from "@hope-ui/solid"; import { HStack, Icon, Text, useColorMode } from "@hope-ui/solid"
import { operations } from "../toolbar/operations"; import { operations } from "../toolbar/operations"
import { For } from "solid-js"; import { For } from "solid-js"
import { bus, notify } from "~/utils"; import { bus, notify } from "~/utils"
import { UserMethods, UserPermissions } from "~/types"; import { UserMethods, UserPermissions } from "~/types"
import { getSettingBool, me } from "~/store"; import { getSettingBool, me } from "~/store"
const ItemContent = (props: { name: string }) => { const ItemContent = (props: { name: string }) => {
const t = useT(); const t = useT()
return ( return (
<HStack spacing="$2"> <HStack spacing="$2">
<Icon <Icon
@@ -20,17 +20,17 @@ const ItemContent = (props: { name: string }) => {
/> />
<Text>{t(`home.toolbar.${props.name}`)}</Text> <Text>{t(`home.toolbar.${props.name}`)}</Text>
</HStack> </HStack>
); )
}; }
export const ContextMenu = () => { export const ContextMenu = () => {
const t = useT(); const t = useT()
const { colorMode } = useColorMode(); const { colorMode } = useColorMode()
const { copySelectedRawLink, copySelectedPreviewPage } = useCopyLink(); const { copySelectedRawLink, copySelectedPreviewPage } = useCopyLink()
const { batchDownloadSelected } = useDownload(); const { batchDownloadSelected } = useDownload()
const canPackageDownload = () => { const canPackageDownload = () => {
return UserMethods.is_admin(me()) || getSettingBool("package_download"); return UserMethods.is_admin(me()) || getSettingBool("package_download")
}; }
return ( return (
<Menu <Menu
id={1} id={1}
@@ -41,11 +41,11 @@ export const ContextMenu = () => {
{(name) => ( {(name) => (
<Item <Item
hidden={() => { hidden={() => {
const index = UserPermissions.findIndex((item) => item === name); const index = UserPermissions.findIndex((item) => item === name)
return !UserMethods.can(me(), index); return !UserMethods.can(me(), index)
}} }}
onClick={() => { onClick={() => {
bus.emit("tool", name); bus.emit("tool", name)
}} }}
> >
<ItemContent name={name} /> <ItemContent name={name} />
@@ -55,9 +55,9 @@ export const ContextMenu = () => {
<Item <Item
onClick={({ props }) => { onClick={({ props }) => {
if (props.is_dir) { if (props.is_dir) {
copySelectedPreviewPage(); copySelectedPreviewPage()
} else { } else {
copySelectedRawLink(true); copySelectedRawLink(true)
} }
}} }}
> >
@@ -67,17 +67,17 @@ export const ContextMenu = () => {
onClick={({ props }) => { onClick={({ props }) => {
if (props.is_dir) { if (props.is_dir) {
if (!canPackageDownload()) { if (!canPackageDownload()) {
notify.warning(t("home.toolbar.package_download_disabled")); notify.warning(t("home.toolbar.package_download_disabled"))
return; return
} }
bus.emit("tool", "package_download"); bus.emit("tool", "package_download")
} else { } else {
batchDownloadSelected(); batchDownloadSelected()
} }
}} }}
> >
<ItemContent name="download" /> <ItemContent name="download" />
</Item> </Item>
</Menu> </Menu>
); )
}; }

View File

@@ -1,27 +1,27 @@
import "aplayer/dist/APlayer.min.css"; import "aplayer/dist/APlayer.min.css"
import "./audio.css"; import "./audio.css"
import APlayer from "aplayer"; import APlayer from "aplayer"
import { Box } from "@hope-ui/solid"; import { Box } from "@hope-ui/solid"
import { onCleanup, onMount } from "solid-js"; import { onCleanup, onMount } from "solid-js"
import { useLink } from "~/hooks"; import { useLink } from "~/hooks"
import { getSetting, getSettingBool, objStore } from "~/store"; import { getSetting, getSettingBool, objStore } from "~/store"
import { ObjType, StoreObj } from "~/types"; import { ObjType, StoreObj } from "~/types"
import { baseName } from "~/utils"; import { baseName } from "~/utils"
const Preview = () => { const Preview = () => {
const { proxyLink, rawLink } = useLink(); const { proxyLink, rawLink } = useLink()
let audios = objStore.objs.filter((obj) => obj.type === ObjType.AUDIO); let audios = objStore.objs.filter((obj) => obj.type === ObjType.AUDIO)
if (audios.length === 0) { if (audios.length === 0) {
audios = [objStore.obj]; audios = [objStore.obj]
} }
let ap: any; let ap: any
const objToAudio = (obj: StoreObj) => { const objToAudio = (obj: StoreObj) => {
let lrc = undefined; let lrc = undefined
const lrcObj = objStore.objs.find((o) => { const lrcObj = objStore.objs.find((o) => {
return baseName(o.name) === baseName(obj.name) && o.name.endsWith(".lrc"); return baseName(o.name) === baseName(obj.name) && o.name.endsWith(".lrc")
}); })
if (lrcObj) { if (lrcObj) {
lrc = proxyLink(lrcObj, true); lrc = proxyLink(lrcObj, true)
} }
return { return {
name: obj.name, name: obj.name,
@@ -31,8 +31,8 @@ const Preview = () => {
getSetting("audio_cover") || getSetting("audio_cover") ||
"https://jsd.nn.ci/gh/alist-org/logo@main/logo.svg", "https://jsd.nn.ci/gh/alist-org/logo@main/logo.svg",
lrc: lrc, lrc: lrc,
}; }
}; }
onMount(() => { onMount(() => {
ap = new APlayer({ ap = new APlayer({
container: document.querySelector("#audio-player"), container: document.querySelector("#audio-player"),
@@ -46,16 +46,16 @@ const Preview = () => {
listFolded: false, listFolded: false,
lrcType: 3, lrcType: 3,
audio: audios.map(objToAudio), audio: audios.map(objToAudio),
}); })
const curIndex = audios.findIndex((obj) => obj.name === objStore.obj.name); const curIndex = audios.findIndex((obj) => obj.name === objStore.obj.name)
if (curIndex !== -1) { if (curIndex !== -1) {
ap.list.switch(curIndex); ap.list.switch(curIndex)
} }
}); })
onCleanup(() => { onCleanup(() => {
ap?.destroy(); ap?.destroy()
}); })
return <Box w="$full" id="audio-player" />; return <Box w="$full" id="audio-player" />
}; }
export default Preview; export default Preview

View File

@@ -1,13 +1,13 @@
import { Button, HStack } from "@hope-ui/solid"; import { Button, HStack } from "@hope-ui/solid"
import { useCopyLink, useT } from "~/hooks"; import { useCopyLink, useT } from "~/hooks"
import { objStore } from "~/store"; import { objStore } from "~/store"
import { FileInfo } from "./info"; import { FileInfo } from "./info"
import { OpenWith } from "../file/open-with"; import { OpenWith } from "../file/open-with"
import { Show } from "solid-js"; import { Show } from "solid-js"
export const Download = (props: { openWith?: boolean }) => { export const Download = (props: { openWith?: boolean }) => {
const t = useT(); const t = useT()
const { copyCurrentRawLink } = useCopyLink(); const { copyCurrentRawLink } = useCopyLink()
return ( return (
<FileInfo> <FileInfo>
<HStack spacing="$2"> <HStack spacing="$2">
@@ -22,7 +22,7 @@ export const Download = (props: { openWith?: boolean }) => {
<OpenWith /> <OpenWith />
</Show> </Show>
</FileInfo> </FileInfo>
); )
}; }
export default Download; export default Download

View File

@@ -1,9 +1,9 @@
import { hope } from "@hope-ui/solid"; import { hope } from "@hope-ui/solid"
import { BoxWithFullScreen, MaybeLoading } from "~/components"; import { BoxWithFullScreen, MaybeLoading } from "~/components"
import { useFetchText } from "~/hooks"; import { useFetchText } from "~/hooks"
const HtmlPreview = () => { const HtmlPreview = () => {
const [content] = useFetchText(); const [content] = useFetchText()
return ( return (
<MaybeLoading loading={content.loading}> <MaybeLoading loading={content.loading}>
<BoxWithFullScreen w="$full" h="70vh"> <BoxWithFullScreen w="$full" h="70vh">
@@ -16,7 +16,7 @@ const HtmlPreview = () => {
/> />
</BoxWithFullScreen> </BoxWithFullScreen>
</MaybeLoading> </MaybeLoading>
); )
}; }
export default HtmlPreview; export default HtmlPreview

View File

@@ -1,8 +1,8 @@
import { BoxWithFullScreen } from "~/components"; import { BoxWithFullScreen } from "~/components"
import { objStore } from "~/store"; import { objStore } from "~/store"
import { hope } from "@hope-ui/solid"; import { hope } from "@hope-ui/solid"
import { convertURL } from "~/utils"; import { convertURL } from "~/utils"
import { Component } from "solid-js"; import { Component } from "solid-js"
const IframePreview = (props: { scheme: string }) => { const IframePreview = (props: { scheme: string }) => {
return ( return (
@@ -13,11 +13,11 @@ const IframePreview = (props: { scheme: string }) => {
src={convertURL(props.scheme, objStore.raw_url, objStore.obj.name)} src={convertURL(props.scheme, objStore.raw_url, objStore.obj.name)}
/> />
</BoxWithFullScreen> </BoxWithFullScreen>
); )
}; }
export const generateIframePreview = (scheme: string): Component => { export const generateIframePreview = (scheme: string): Component => {
return () => { return () => {
return <IframePreview scheme={scheme} />; return <IframePreview scheme={scheme} />
}; }
}; }

View File

@@ -1,9 +1,9 @@
import { Error, FullLoading, ImageWithError } from "~/components"; import { Error, FullLoading, ImageWithError } from "~/components"
import { useT } from "~/hooks"; import { useT } from "~/hooks"
import { objStore } from "~/store"; import { objStore } from "~/store"
const Preview = () => { const Preview = () => {
const t = useT(); const t = useT()
return ( return (
<ImageWithError <ImageWithError
maxH="75vh" maxH="75vh"
@@ -12,7 +12,7 @@ const Preview = () => {
fallback={<FullLoading />} fallback={<FullLoading />}
fallbackErr={<Error msg={t("home.preview.failed_load_img")} />} fallbackErr={<Error msg={t("home.preview.failed_load_img")} />}
/> />
); )
}; }
export default Preview; export default Preview

View File

@@ -1,18 +1,18 @@
import { Component, lazy } from "solid-js"; import { Component, lazy } from "solid-js"
import { getIframePreviews } from "~/store"; import { getIframePreviews } from "~/store"
import { Obj, ObjType } from "~/types"; import { Obj, ObjType } from "~/types"
import { ext } from "~/utils"; import { ext } from "~/utils"
import { generateIframePreview } from "./iframe"; import { generateIframePreview } from "./iframe"
export interface Preview { export interface Preview {
name: string; name: string
type?: ObjType; type?: ObjType
exts?: string[] | "*"; exts?: string[] | "*"
provider?: RegExp; provider?: RegExp
component: Component; component: Component
} }
export type PreviewComponent = Pick<Preview, "name" | "component">; export type PreviewComponent = Pick<Preview, "name" | "component">
const previews: Preview[] = [ const previews: Preview[] = [
{ {
@@ -61,37 +61,37 @@ const previews: Preview[] = [
provider: /Aliyundrive/, provider: /Aliyundrive/,
component: lazy(() => import("./aliyun_office")), component: lazy(() => import("./aliyun_office")),
}, },
]; ]
export const getPreviews = ( export const getPreviews = (
file: Obj & { provider: string } file: Obj & { provider: string }
): PreviewComponent[] => { ): PreviewComponent[] => {
const res: PreviewComponent[] = []; const res: PreviewComponent[] = []
// internal previews // internal previews
previews.forEach((preview) => { previews.forEach((preview) => {
if (preview.provider && !preview.provider.test(file.provider)) { if (preview.provider && !preview.provider.test(file.provider)) {
return; return
} }
if ( if (
preview.type === file.type || preview.type === file.type ||
preview.exts === "*" || preview.exts === "*" ||
preview.exts?.includes(ext(file.name).toLowerCase()) preview.exts?.includes(ext(file.name).toLowerCase())
) { ) {
res.push({ name: preview.name, component: preview.component }); res.push({ name: preview.name, component: preview.component })
} }
}); })
// iframe previews // iframe previews
const iframePreviews = getIframePreviews(file.name); const iframePreviews = getIframePreviews(file.name)
iframePreviews.forEach((preview) => { iframePreviews.forEach((preview) => {
res.push({ res.push({
name: preview.key, name: preview.key,
component: generateIframePreview(preview.value), component: generateIframePreview(preview.value),
}); })
}); })
// download page // download page
res.push({ res.push({
name: "Download", name: "Download",
component: lazy(() => import("./download")), component: lazy(() => import("./download")),
}); })
return res; return res
}; }

View File

@@ -1,8 +1,8 @@
import { Heading, Icon, Text, VStack } from "@hope-ui/solid"; import { Heading, Icon, Text, VStack } from "@hope-ui/solid"
import { JSXElement } from "solid-js"; import { JSXElement } from "solid-js"
import { getMainColor, objStore } from "~/store"; import { getMainColor, objStore } from "~/store"
import { formatDate, getFileSize } from "~/utils"; import { formatDate, getFileSize } from "~/utils"
import { getIconByObj } from "~/utils/icon"; import { getIconByObj } from "~/utils/icon"
export const FileInfo = (props: { children: JSXElement }) => { export const FileInfo = (props: { children: JSXElement }) => {
return ( return (
@@ -27,5 +27,5 @@ export const FileInfo = (props: { children: JSXElement }) => {
</VStack> </VStack>
<VStack spacing="$2">{props.children}</VStack> <VStack spacing="$2">{props.children}</VStack>
</VStack> </VStack>
); )
}; }

View File

@@ -1,13 +1,13 @@
import { Button } from "@hope-ui/solid"; import { Button } from "@hope-ui/solid"
import { createSignal } from "solid-js"; import { createSignal } from "solid-js"
import { useT } from "~/hooks"; import { useT } from "~/hooks"
import { objStore } from "~/store"; import { objStore } from "~/store"
import { api, baseName, safeBtoa } from "~/utils"; import { api, baseName, safeBtoa } from "~/utils"
import { FileInfo } from "./info"; import { FileInfo } from "./info"
const Ipa = () => { const Ipa = () => {
const t = useT(); const t = useT()
const [installing, setInstalling] = createSignal(false); const [installing, setInstalling] = createSignal(false)
return ( return (
<FileInfo> <FileInfo>
<Button <Button
@@ -21,13 +21,13 @@ const Ipa = () => {
)}.plist` )}.plist`
} }
onClick={() => { onClick={() => {
setInstalling(true); setInstalling(true)
}} }}
> >
{t(`home.preview.${installing() ? "installing" : "install"}`)} {t(`home.preview.${installing() ? "installing" : "install"}`)}
</Button> </Button>
</FileInfo> </FileInfo>
); )
}; }
export default Ipa; export default Ipa

View File

@@ -1,12 +1,12 @@
import { Button } from "@hope-ui/solid"; import { Button } from "@hope-ui/solid"
import { createSignal } from "solid-js"; import { createSignal } from "solid-js"
import { useT } from "~/hooks"; import { useT } from "~/hooks"
import { objStore } from "~/store"; import { objStore } from "~/store"
import { FileInfo } from "./info"; import { FileInfo } from "./info"
const Plist = () => { const Plist = () => {
const t = useT(); const t = useT()
const [installing, setInstalling] = createSignal(false); const [installing, setInstalling] = createSignal(false)
return ( return (
<FileInfo> <FileInfo>
<Button <Button
@@ -15,13 +15,13 @@ const Plist = () => {
"itms-services://?action=download-manifest&url=" + objStore.raw_url "itms-services://?action=download-manifest&url=" + objStore.raw_url
} }
onClick={() => { onClick={() => {
setInstalling(true); setInstalling(true)
}} }}
> >
{t(`home.preview.${installing() ? "installing" : "install"}`)} {t(`home.preview.${installing() ? "installing" : "install"}`)}
</Button> </Button>
</FileInfo> </FileInfo>
); )
}; }
export default Plist; export default Plist

View File

@@ -1,23 +1,23 @@
import { createDisclosure } from "@hope-ui/solid"; import { createDisclosure } from "@hope-ui/solid"
import { ModalInput } from "~/components"; import { ModalInput } from "~/components"
import { useFetch, useRouter, useT } from "~/hooks"; import { useFetch, useRouter, useT } from "~/hooks"
import { addAria2, bus, handleRespWithNotifySuccess } from "~/utils"; import { addAria2, bus, handleRespWithNotifySuccess } from "~/utils"
import { onCleanup } from "solid-js"; import { onCleanup } from "solid-js"
export const Aria2 = () => { export const Aria2 = () => {
const t = useT(); const t = useT()
const { isOpen, onOpen, onClose } = createDisclosure(); const { isOpen, onOpen, onClose } = createDisclosure()
const [loading, ok] = useFetch(addAria2); const [loading, ok] = useFetch(addAria2)
const { pathname } = useRouter(); const { pathname } = useRouter()
const handler = (name: string) => { const handler = (name: string) => {
if (name === "offline_download") { if (name === "offline_download") {
onOpen(); onOpen()
} }
}; }
bus.on("tool", handler); bus.on("tool", handler)
onCleanup(() => { onCleanup(() => {
bus.off("tool", handler); bus.off("tool", handler)
}); })
return ( return (
<ModalInput <ModalInput
title="home.toolbar.offline_download" title="home.toolbar.offline_download"
@@ -27,11 +27,11 @@ export const Aria2 = () => {
loading={loading()} loading={loading()}
tips={t("home.toolbar.offline_download-tips")} tips={t("home.toolbar.offline_download-tips")}
onSubmit={async (urls) => { onSubmit={async (urls) => {
const resp = await ok(pathname(), urls.split("\n")); const resp = await ok(pathname(), urls.split("\n"))
handleRespWithNotifySuccess(resp, () => { handleRespWithNotifySuccess(resp, () => {
onClose(); onClose()
}); })
}} }}
/> />
); )
}; }

View File

@@ -1,17 +1,11 @@
import { Box, HStack, useColorModeValue } from "@hope-ui/solid"; import { Box, HStack, useColorModeValue } from "@hope-ui/solid"
import { createMemo, For, Show } from "solid-js"; import { createMemo, For, Show } from "solid-js"
import { import { checkboxOpen, haveSelected, objStore, selectAll, State } from "~/store"
checkboxOpen, import { CopyLink } from "./CopyLink"
haveSelected, import { CenterIcon } from "./Icon"
objStore, import { bus } from "~/utils"
selectAll, import { Download } from "./Download"
State, import { Motion, Presence } from "@motionone/solid"
} from "~/store";
import { CopyLink } from "./CopyLink";
import { CenterIcon } from "./Icon";
import { bus } from "~/utils";
import { Download } from "./Download";
import { Motion, Presence } from "@motionone/solid";
export const Center = () => { export const Center = () => {
const show = createMemo( const show = createMemo(
@@ -19,7 +13,7 @@ export const Center = () => {
[State.Folder, State.FetchingMore].includes(objStore.state) && [State.Folder, State.FetchingMore].includes(objStore.state) &&
checkboxOpen() && checkboxOpen() &&
haveSelected() haveSelected()
); )
return ( return (
<Presence exitBeforeEnter> <Presence exitBeforeEnter>
<Show when={show()}> <Show when={show()}>
@@ -53,10 +47,10 @@ export const Center = () => {
<CenterIcon <CenterIcon
name={name} name={name}
onClick={() => { onClick={() => {
bus.emit("tool", name); bus.emit("tool", name)
}} }}
/> />
); )
}} }}
</For> </For>
<CopyLink /> <CopyLink />
@@ -64,12 +58,12 @@ export const Center = () => {
<CenterIcon <CenterIcon
name="cancel_select" name="cancel_select"
onClick={() => { onClick={() => {
selectAll(false); selectAll(false)
}} }}
/> />
</HStack> </HStack>
</Box> </Box>
</Show> </Show>
</Presence> </Presence>
); )
}; }

View File

@@ -1,11 +1,11 @@
import { Menu, MenuTrigger, MenuContent, MenuItem } from "@hope-ui/solid"; import { Menu, MenuTrigger, MenuContent, MenuItem } from "@hope-ui/solid"
import { useT, useCopyLink } from "~/hooks"; import { useT, useCopyLink } from "~/hooks"
import { CenterIcon } from "./Icon"; import { CenterIcon } from "./Icon"
export const CopyLink = () => { export const CopyLink = () => {
const t = useT(); const t = useT()
const { copySelectedPreviewPage, copySelectedRawLink } = useCopyLink(); const { copySelectedPreviewPage, copySelectedRawLink } = useCopyLink()
const colorScheme = "neutral"; const colorScheme = "neutral"
return ( return (
<Menu placement="top" offset={10}> <Menu placement="top" offset={10}>
<MenuTrigger as={CenterIcon} name="copy_link" /> <MenuTrigger as={CenterIcon} name="copy_link" />
@@ -13,7 +13,7 @@ export const CopyLink = () => {
<MenuItem <MenuItem
colorScheme={colorScheme} colorScheme={colorScheme}
onSelect={() => { onSelect={() => {
copySelectedPreviewPage(); copySelectedPreviewPage()
}} }}
> >
{t("home.toolbar.preview_page")} {t("home.toolbar.preview_page")}
@@ -21,7 +21,7 @@ export const CopyLink = () => {
<MenuItem <MenuItem
colorScheme={colorScheme} colorScheme={colorScheme}
onSelect={() => { onSelect={() => {
copySelectedRawLink(); copySelectedRawLink()
}} }}
> >
{t("home.toolbar.down_link")} {t("home.toolbar.down_link")}
@@ -29,12 +29,12 @@ export const CopyLink = () => {
<MenuItem <MenuItem
colorScheme={colorScheme} colorScheme={colorScheme}
onSelect={() => { onSelect={() => {
copySelectedRawLink(true); copySelectedRawLink(true)
}} }}
> >
{t("home.toolbar.encode_down_link")} {t("home.toolbar.encode_down_link")}
</MenuItem> </MenuItem>
</MenuContent> </MenuContent>
</Menu> </Menu>
); )
}; }

View File

@@ -1,24 +1,24 @@
import { createDisclosure } from "@hope-ui/solid"; import { createDisclosure } from "@hope-ui/solid"
import { onCleanup } from "solid-js"; import { onCleanup } from "solid-js"
import { ModalFolderChoose } from "~/components"; import { ModalFolderChoose } from "~/components"
import { useFetch, usePath, useRouter } from "~/hooks"; import { useFetch, usePath, useRouter } from "~/hooks"
import { selectedObjs } from "~/store"; import { selectedObjs } from "~/store"
import { bus, fsCopy, fsMove, handleRespWithNotifySuccess } from "~/utils"; import { bus, fsCopy, fsMove, handleRespWithNotifySuccess } from "~/utils"
export const Copy = () => { export const Copy = () => {
const { isOpen, onOpen, onClose } = createDisclosure(); const { isOpen, onOpen, onClose } = createDisclosure()
const [loading, ok] = useFetch(fsCopy); const [loading, ok] = useFetch(fsCopy)
const { pathname } = useRouter(); const { pathname } = useRouter()
const { refresh } = usePath(); const { refresh } = usePath()
const handler = (name: string) => { const handler = (name: string) => {
if (name === "copy") { if (name === "copy") {
onOpen(); onOpen()
} }
}; }
bus.on("tool", handler); bus.on("tool", handler)
onCleanup(() => { onCleanup(() => {
bus.off("tool", handler); bus.off("tool", handler)
}); })
return ( return (
<ModalFolderChoose <ModalFolderChoose
opened={isOpen()} opened={isOpen()}
@@ -29,30 +29,30 @@ export const Copy = () => {
pathname(), pathname(),
dst, dst,
selectedObjs().map((obj) => obj.name) selectedObjs().map((obj) => obj.name)
); )
handleRespWithNotifySuccess(resp, () => { handleRespWithNotifySuccess(resp, () => {
refresh(); refresh()
onClose(); onClose()
}); })
}} }}
/> />
); )
}; }
export const Move = () => { export const Move = () => {
const { isOpen, onOpen, onClose } = createDisclosure(); const { isOpen, onOpen, onClose } = createDisclosure()
const [loading, ok] = useFetch(fsMove); const [loading, ok] = useFetch(fsMove)
const { pathname } = useRouter(); const { pathname } = useRouter()
const { refresh } = usePath(); const { refresh } = usePath()
const handler = (name: string) => { const handler = (name: string) => {
if (name === "move") { if (name === "move") {
onOpen(); onOpen()
} }
}; }
bus.on("tool", handler); bus.on("tool", handler)
onCleanup(() => { onCleanup(() => {
bus.off("tool", handler); bus.off("tool", handler)
}); })
return ( return (
<ModalFolderChoose <ModalFolderChoose
opened={isOpen()} opened={isOpen()}
@@ -63,16 +63,16 @@ export const Move = () => {
pathname(), pathname(),
dst, dst,
selectedObjs().map((obj) => obj.name) selectedObjs().map((obj) => obj.name)
); )
handleRespWithNotifySuccess(resp, () => { handleRespWithNotifySuccess(resp, () => {
refresh(); refresh()
onClose(); onClose()
}); })
}} }}
/> />
// <CenterIcon tip="move" viewBox="0 0 1024 1024" fill="currentColor"> // <CenterIcon tip="move" viewBox="0 0 1024 1024" fill="currentColor">
// <path d="M840.704 256h-36.992c-82.624 0-82.496-128-140.864-128H311.232C245.44 128 192 181.44 192 247.296V384h64V247.296C256 216.832 280.832 192 311.232 192h339.456c3.968 6.144 9.024 15.36 12.672 22.208C684.8 253.76 720.704 320 803.712 320h36.992C869.12 320 896 351.104 896 384v392.768c0 30.4-24.832 55.232-55.296 55.232H311.232c-30.4 0-55.232-24.832-55.232-55.232V704h-64v72.768C192 842.624 245.44 896 311.232 896H840.64C906.56 896 960 842.624 960 776.768V384c0-65.856-53.44-128-119.296-128z"></path> // <path d="M840.704 256h-36.992c-82.624 0-82.496-128-140.864-128H311.232C245.44 128 192 181.44 192 247.296V384h64V247.296C256 216.832 280.832 192 311.232 192h339.456c3.968 6.144 9.024 15.36 12.672 22.208C684.8 253.76 720.704 320 803.712 320h36.992C869.12 320 896 351.104 896 384v392.768c0 30.4-24.832 55.232-55.296 55.232H311.232c-30.4 0-55.232-24.832-55.232-55.232V704h-64v72.768C192 842.624 245.44 896 311.232 896H840.64C906.56 896 960 842.624 960 776.768V384c0-65.856-53.44-128-119.296-128z"></path>
// <path d="M497.344 693.824L630.4 563.968c0.128-0.128 0.192-0.32 0.32-0.512 2.816-2.816 5.184-9.536 6.784-13.248 1.344-3.456 1.856-0.64 2.112-4.096 0-0.768 0.384-1.408 0.384-2.112 0-0.512-0.256-0.896-0.256-1.344-0.192-3.84-0.896-5.76-2.24-9.344-1.344-3.264-3.52-6.016-5.76-8.64-0.512-0.64-0.768-1.536-1.344-2.112L497.344 389.632c-12.8-12.864-33.6-6.976-46.4 5.888-12.864 12.8-12.864 33.6 0 46.464l76.864 70.976-429.632 0.064c-18.752 0-33.984 12.8-33.92 30.912-0.064 18.112 15.168 29.696 33.984 29.696h429.632l-76.864 79.552c-12.864 12.864-12.864 33.6 0 46.528 6.4 6.4 14.72 3.776 23.168 3.776s16.832-3.328 23.168-9.664z"></path> // <path d="M497.344 693.824L630.4 563.968c0.128-0.128 0.192-0.32 0.32-0.512 2.816-2.816 5.184-9.536 6.784-13.248 1.344-3.456 1.856-0.64 2.112-4.096 0-0.768 0.384-1.408 0.384-2.112 0-0.512-0.256-0.896-0.256-1.344-0.192-3.84-0.896-5.76-2.24-9.344-1.344-3.264-3.52-6.016-5.76-8.64-0.512-0.64-0.768-1.536-1.344-2.112L497.344 389.632c-12.8-12.864-33.6-6.976-46.4 5.888-12.864 12.8-12.864 33.6 0 46.464l76.864 70.976-429.632 0.064c-18.752 0-33.984 12.8-33.92 30.912-0.064 18.112 15.168 29.696 33.984 29.696h429.632l-76.864 79.552c-12.864 12.864-12.864 33.6 0 46.528 6.4 6.4 14.72 3.776 23.168 3.776s16.832-3.328 23.168-9.664z"></path>
// </CenterIcon> // </CenterIcon>
); )
}; }

View File

@@ -7,27 +7,27 @@ import {
ModalFooter, ModalFooter,
Button, Button,
createDisclosure, createDisclosure,
} from "@hope-ui/solid"; } from "@hope-ui/solid"
import { onCleanup } from "solid-js"; import { onCleanup } from "solid-js"
import { useFetch, usePath, useRouter, useT } from "~/hooks"; import { useFetch, usePath, useRouter, useT } from "~/hooks"
import { selectedObjs } from "~/store"; import { selectedObjs } from "~/store"
import { bus, fsRemove, handleRespWithNotifySuccess } from "~/utils"; import { bus, fsRemove, handleRespWithNotifySuccess } from "~/utils"
export const Delete = () => { export const Delete = () => {
const t = useT(); const t = useT()
const { isOpen, onOpen, onClose } = createDisclosure(); const { isOpen, onOpen, onClose } = createDisclosure()
const [loading, ok] = useFetch(fsRemove); const [loading, ok] = useFetch(fsRemove)
const { refresh } = usePath(); const { refresh } = usePath()
const { pathname } = useRouter(); const { pathname } = useRouter()
const handler = (name: string) => { const handler = (name: string) => {
if (name === "delete") { if (name === "delete") {
onOpen(); onOpen()
} }
}; }
bus.on("tool", handler); bus.on("tool", handler)
onCleanup(() => { onCleanup(() => {
bus.off("tool", handler); bus.off("tool", handler)
}); })
return ( return (
<Modal <Modal
blockScrollOnMount={false} blockScrollOnMount={false}
@@ -55,11 +55,11 @@ export const Delete = () => {
const resp = await ok( const resp = await ok(
pathname(), pathname(),
selectedObjs().map((obj) => obj.name) selectedObjs().map((obj) => obj.name)
); )
handleRespWithNotifySuccess(resp, () => { handleRespWithNotifySuccess(resp, () => {
refresh(); refresh()
onClose(); onClose()
}); })
}} }}
> >
{t("global.confirm")} {t("global.confirm")}
@@ -67,5 +67,5 @@ export const Delete = () => {
</ModalFooter> </ModalFooter>
</ModalContent> </ModalContent>
</Modal> </Modal>
); )
}; }

View File

@@ -11,19 +11,19 @@ import {
ModalHeader, ModalHeader,
ModalOverlay, ModalOverlay,
createDisclosure, createDisclosure,
} from "@hope-ui/solid"; } from "@hope-ui/solid"
import { createSignal, lazy, onCleanup, Show, Suspense } from "solid-js"; import { createSignal, lazy, onCleanup, Show, Suspense } from "solid-js"
import { FullLoading } from "~/components"; import { FullLoading } from "~/components"
import { useT, useDownload } from "~/hooks"; import { useT, useDownload } from "~/hooks"
import { getSettingBool, me } from "~/store"; import { getSettingBool, me } from "~/store"
import { UserMethods } from "~/types"; import { UserMethods } from "~/types"
import { bus } from "~/utils"; import { bus } from "~/utils"
import { CenterIcon } from "./Icon"; import { CenterIcon } from "./Icon"
export const Download = () => { export const Download = () => {
const t = useT(); const t = useT()
const colorScheme = "neutral"; const colorScheme = "neutral"
const { batchDownloadSelected, sendToAria2 } = useDownload(); const { batchDownloadSelected, sendToAria2 } = useDownload()
return ( return (
<Menu placement="top" offset={10}> <Menu placement="top" offset={10}>
<MenuTrigger as={CenterIcon} name="download" /> <MenuTrigger as={CenterIcon} name="download" />
@@ -39,7 +39,7 @@ export const Download = () => {
<MenuItem <MenuItem
colorScheme={colorScheme} colorScheme={colorScheme}
onSelect={() => { onSelect={() => {
bus.emit("tool", "package_download"); bus.emit("tool", "package_download")
}} }}
> >
{t("home.toolbar.package_download")} {t("home.toolbar.package_download")}
@@ -50,24 +50,24 @@ export const Download = () => {
</MenuItem> </MenuItem>
</MenuContent> </MenuContent>
</Menu> </Menu>
); )
}; }
const PackageDownload = lazy(() => import("./PackageDownload")); const PackageDownload = lazy(() => import("./PackageDownload"))
export const PackageDownloadModal = () => { export const PackageDownloadModal = () => {
const t = useT(); const t = useT()
const handler = (name: string) => { const handler = (name: string) => {
if (name === "package_download") { if (name === "package_download") {
onOpen(); onOpen()
} }
}; }
bus.on("tool", handler); bus.on("tool", handler)
onCleanup(() => { onCleanup(() => {
bus.off("tool", handler); bus.off("tool", handler)
}); })
const { isOpen, onOpen, onClose } = createDisclosure(); const { isOpen, onOpen, onClose } = createDisclosure()
const [show, setShow] = createSignal("pre_tips"); const [show, setShow] = createSignal("pre_tips")
return ( return (
<Modal <Modal
blockScrollOnMount={false} blockScrollOnMount={false}
@@ -98,7 +98,7 @@ export const PackageDownloadModal = () => {
<Button <Button
colorScheme="info" colorScheme="info"
onClick={() => { onClick={() => {
setShow("package_download"); setShow("package_download")
}} }}
> >
{t("global.confirm")} {t("global.confirm")}
@@ -108,5 +108,5 @@ export const PackageDownloadModal = () => {
</Suspense> </Suspense>
</ModalContent> </ModalContent>
</Modal> </Modal>
); )
}; }

View File

@@ -1,19 +1,19 @@
import { ElementType, Icon, IconProps, Tooltip } from "@hope-ui/solid"; import { ElementType, Icon, IconProps, Tooltip } from "@hope-ui/solid"
import { IconTypes } from "solid-icons"; import { IconTypes } from "solid-icons"
import { useT } from "~/hooks"; import { useT } from "~/hooks"
import { getMainColor, me } from "~/store"; import { getMainColor, me } from "~/store"
import { UserMethods, UserPermissions } from "~/types"; import { UserMethods, UserPermissions } from "~/types"
import { hoverColor } from "~/utils"; import { hoverColor } from "~/utils"
import { operations } from "./operations"; import { operations } from "./operations"
export const CenterIcon = <C extends ElementType = "svg">( export const CenterIcon = <C extends ElementType = "svg">(
props: IconProps<C> & { props: IconProps<C> & {
name: string; name: string
} }
) => { ) => {
const index = UserPermissions.findIndex((p) => p === props.name); const index = UserPermissions.findIndex((p) => p === props.name)
if (index !== -1 && !UserMethods.can(me(), index)) return null; if (index !== -1 && !UserMethods.can(me(), index)) return null
const t = useT(); const t = useT()
return ( return (
<Tooltip placement="top" withArrow label={t(`home.toolbar.${props.name}`)}> <Tooltip placement="top" withArrow label={t(`home.toolbar.${props.name}`)}>
<Icon <Icon
@@ -37,16 +37,16 @@ export const CenterIcon = <C extends ElementType = "svg">(
{...props} {...props}
/> />
</Tooltip> </Tooltip>
); )
}; }
export const RightIcon = <C extends ElementType = "svg">( export const RightIcon = <C extends ElementType = "svg">(
props: IconProps<C> & { props: IconProps<C> & {
tips?: string; tips?: string
icon?: IconTypes; icon?: IconTypes
} }
) => { ) => {
const t = useT(); const t = useT()
return ( return (
<Tooltip <Tooltip
disabled={!props.tips} disabled={!props.tips}
@@ -76,8 +76,8 @@ export const RightIcon = <C extends ElementType = "svg">(
{...props} {...props}
/> />
</Tooltip> </Tooltip>
); )
}; }
// export const ToolIcon = <C extends ElementType = "button">( // export const ToolIcon = <C extends ElementType = "button">(
// props: IconButtonProps<C> // props: IconButtonProps<C>

View File

@@ -12,40 +12,40 @@ import {
HStack, HStack,
Input, Input,
VStack, VStack,
} from "@hope-ui/solid"; } from "@hope-ui/solid"
import { For, onCleanup } from "solid-js"; import { For, onCleanup } from "solid-js"
import { SwitchLanguageWhite, SwitchColorMode } from "~/components"; import { SwitchLanguageWhite, SwitchColorMode } from "~/components"
import { useT } from "~/hooks"; import { useT } from "~/hooks"
import { initialLocalSettings, local, setLocal } from "~/store"; import { initialLocalSettings, local, setLocal } from "~/store"
import { bus } from "~/utils"; import { bus } from "~/utils"
const LocalSettingsInput = (props: { name: string }) => { const LocalSettingsInput = (props: { name: string }) => {
const t = useT(); const t = useT()
return ( return (
<FormControl> <FormControl>
<FormLabel>{t(`home.local_settings.${props.name}`)}</FormLabel> <FormLabel>{t(`home.local_settings.${props.name}`)}</FormLabel>
<Input <Input
value={local[props.name]} value={local[props.name]}
onInput={(e) => { onInput={(e) => {
setLocal(props.name, e.currentTarget.value); setLocal(props.name, e.currentTarget.value)
}} }}
/> />
</FormControl> </FormControl>
); )
}; }
export const LocalSettings = () => { export const LocalSettings = () => {
const { isOpen, onOpen, onClose } = createDisclosure(); const { isOpen, onOpen, onClose } = createDisclosure()
const t = useT(); const t = useT()
const handler = (name: string) => { const handler = (name: string) => {
if (name === "local_settings") { if (name === "local_settings") {
onOpen(); onOpen()
} }
}; }
bus.on("tool", handler); bus.on("tool", handler)
onCleanup(() => { onCleanup(() => {
bus.off("tool", handler); bus.off("tool", handler)
}); })
return ( return (
<Drawer opened={isOpen()} placement="right" onClose={onClose}> <Drawer opened={isOpen()} placement="right" onClose={onClose}>
<DrawerOverlay /> <DrawerOverlay />
@@ -69,5 +69,5 @@ export const LocalSettings = () => {
</DrawerBody> </DrawerBody>
</DrawerContent> </DrawerContent>
</Drawer> </Drawer>
); )
}; }

View File

@@ -1,23 +1,23 @@
import { createDisclosure } from "@hope-ui/solid"; import { createDisclosure } from "@hope-ui/solid"
import { ModalInput } from "~/components"; import { ModalInput } from "~/components"
import { useFetch, usePath, useRouter } from "~/hooks"; import { useFetch, usePath, useRouter } from "~/hooks"
import { bus, fsMkdir, handleRespWithNotifySuccess, pathJoin } from "~/utils"; import { bus, fsMkdir, handleRespWithNotifySuccess, pathJoin } from "~/utils"
import { onCleanup } from "solid-js"; import { onCleanup } from "solid-js"
export const Mkdir = () => { export const Mkdir = () => {
const { isOpen, onOpen, onClose } = createDisclosure(); const { isOpen, onOpen, onClose } = createDisclosure()
const [loading, ok] = useFetch(fsMkdir); const [loading, ok] = useFetch(fsMkdir)
const { pathname } = useRouter(); const { pathname } = useRouter()
const { refresh } = usePath(); const { refresh } = usePath()
const handler = (name: string) => { const handler = (name: string) => {
if (name === "mkdir") { if (name === "mkdir") {
onOpen(); onOpen()
} }
}; }
bus.on("tool", handler); bus.on("tool", handler)
onCleanup(() => { onCleanup(() => {
bus.off("tool", handler); bus.off("tool", handler)
}); })
return ( return (
<ModalInput <ModalInput
title="home.toolbar.input_dir_name" title="home.toolbar.input_dir_name"
@@ -25,12 +25,12 @@ export const Mkdir = () => {
onClose={onClose} onClose={onClose}
loading={loading()} loading={loading()}
onSubmit={async (name) => { onSubmit={async (name) => {
const resp = await ok(pathJoin(pathname(), name)); const resp = await ok(pathJoin(pathname(), name))
handleRespWithNotifySuccess(resp, () => { handleRespWithNotifySuccess(resp, () => {
refresh(); refresh()
onClose(); onClose()
}); })
}} }}
/> />
); )
}; }

View File

@@ -6,29 +6,29 @@ import {
ModalOverlay, ModalOverlay,
createDisclosure, createDisclosure,
ModalCloseButton, ModalCloseButton,
} from "@hope-ui/solid"; } from "@hope-ui/solid"
import { JSXElement, onCleanup, Show, Suspense } from "solid-js"; import { JSXElement, onCleanup, Show, Suspense } from "solid-js"
import { FullLoading } from "~/components"; import { FullLoading } from "~/components"
import { useT } from "~/hooks"; import { useT } from "~/hooks"
import { bus } from "~/utils"; import { bus } from "~/utils"
export const ModalWrapper = (props: { export const ModalWrapper = (props: {
children: JSXElement; children: JSXElement
name: string; name: string
title: string; title: string
blockScrollOnMount?: boolean; blockScrollOnMount?: boolean
}) => { }) => {
const t = useT(); const t = useT()
const handler = (name: string) => { const handler = (name: string) => {
if (name === props.name) { if (name === props.name) {
onOpen(); onOpen()
} }
}; }
bus.on("tool", handler); bus.on("tool", handler)
onCleanup(() => { onCleanup(() => {
bus.off("tool", handler); bus.off("tool", handler)
}); })
const { isOpen, onOpen, onClose } = createDisclosure(); const { isOpen, onOpen, onClose } = createDisclosure()
return ( return (
<Modal <Modal
blockScrollOnMount={props.blockScrollOnMount} blockScrollOnMount={props.blockScrollOnMount}
@@ -55,5 +55,5 @@ export const ModalWrapper = (props: {
</ModalBody> </ModalBody>
</ModalContent> </ModalContent>
</Modal> </Modal>
); )
}; }

View File

@@ -1,29 +1,24 @@
import { createDisclosure } from "@hope-ui/solid"; import { createDisclosure } from "@hope-ui/solid"
import { onCleanup } from "solid-js"; import { onCleanup } from "solid-js"
import { ModalInput } from "~/components"; import { ModalInput } from "~/components"
import { useFetch, usePath, useRouter } from "~/hooks"; import { useFetch, usePath, useRouter } from "~/hooks"
import { password } from "~/store"; import { password } from "~/store"
import { import { bus, fsNewFile, handleRespWithNotifySuccess, pathJoin } from "~/utils"
bus,
fsNewFile,
handleRespWithNotifySuccess,
pathJoin,
} from "~/utils";
export const NewFile = () => { export const NewFile = () => {
const { isOpen, onOpen, onClose } = createDisclosure(); const { isOpen, onOpen, onClose } = createDisclosure()
const [loading, ok] = useFetch(fsNewFile); const [loading, ok] = useFetch(fsNewFile)
const { refresh } = usePath(); const { refresh } = usePath()
const { pathname } = useRouter(); const { pathname } = useRouter()
const handler = (name: string) => { const handler = (name: string) => {
if (name === "new_file") { if (name === "new_file") {
onOpen(); onOpen()
} }
}; }
bus.on("tool", handler); bus.on("tool", handler)
onCleanup(() => { onCleanup(() => {
bus.off("tool", handler); bus.off("tool", handler)
}); })
return ( return (
<ModalInput <ModalInput
title="home.toolbar.input_filename" title="home.toolbar.input_filename"
@@ -31,12 +26,12 @@ export const NewFile = () => {
onClose={onClose} onClose={onClose}
loading={loading()} loading={loading()}
onSubmit={async (name) => { onSubmit={async (name) => {
const resp = await ok(pathJoin(pathname(), name), password()); const resp = await ok(pathJoin(pathname(), name), password())
handleRespWithNotifySuccess(resp, () => { handleRespWithNotifySuccess(resp, () => {
refresh(); refresh()
onClose(); onClose()
}); })
}} }}
/> />
); )
}; }

View File

@@ -1,35 +1,35 @@
import { createDisclosure } from "@hope-ui/solid"; import { createDisclosure } from "@hope-ui/solid"
import { onCleanup, Show } from "solid-js"; import { onCleanup, Show } from "solid-js"
import { ModalInput } from "~/components"; import { ModalInput } from "~/components"
import { useFetch, usePath, useRouter, useT } from "~/hooks"; import { useFetch, usePath, useRouter, useT } from "~/hooks"
import { oneChecked, selectedObjs } from "~/store"; import { oneChecked, selectedObjs } from "~/store"
import { import {
bus, bus,
fsRename, fsRename,
handleRespWithNotifySuccess, handleRespWithNotifySuccess,
notify, notify,
pathJoin, pathJoin,
} from "~/utils"; } from "~/utils"
export const Rename = () => { export const Rename = () => {
const { isOpen, onOpen, onClose } = createDisclosure(); const { isOpen, onOpen, onClose } = createDisclosure()
const t = useT(); const t = useT()
const [loading, ok] = useFetch(fsRename); const [loading, ok] = useFetch(fsRename)
const { pathname } = useRouter(); const { pathname } = useRouter()
const { refresh } = usePath(); const { refresh } = usePath()
const handler = (name: string) => { const handler = (name: string) => {
if (name === "rename") { if (name === "rename") {
if (!oneChecked()) { if (!oneChecked()) {
notify.warning(t("home.toolbar.only_one-tips")); notify.warning(t("home.toolbar.only_one-tips"))
return; return
} }
onOpen(); onOpen()
} }
}; }
bus.on("tool", handler); bus.on("tool", handler)
onCleanup(() => { onCleanup(() => {
bus.off("tool", handler); bus.off("tool", handler)
}); })
return ( return (
<Show when={isOpen()}> <Show when={isOpen()}>
<ModalInput <ModalInput
@@ -42,13 +42,13 @@ export const Rename = () => {
const resp = await ok( const resp = await ok(
pathJoin(pathname(), selectedObjs()[0].name), pathJoin(pathname(), selectedObjs()[0].name),
name name
); )
handleRespWithNotifySuccess(resp, () => { handleRespWithNotifySuccess(resp, () => {
refresh(); refresh()
onClose(); onClose()
}); })
}} }}
/> />
</Show> </Show>
); )
}; }

View File

@@ -3,29 +3,29 @@ import {
createDisclosure, createDisclosure,
useColorModeValue, useColorModeValue,
VStack, VStack,
} from "@hope-ui/solid"; } from "@hope-ui/solid"
import { createMemo, Show } from "solid-js"; import { createMemo, Show } from "solid-js"
import { RightIcon } from "./Icon"; import { RightIcon } from "./Icon"
import { CgMoreO } from "solid-icons/cg"; import { CgMoreO } from "solid-icons/cg"
import { TbCheckbox } from "solid-icons/tb"; import { TbCheckbox } from "solid-icons/tb"
import { objStore, State, toggleCheckbox, userCan } from "~/store"; import { objStore, State, toggleCheckbox, userCan } from "~/store"
import { bus } from "~/utils"; import { bus } from "~/utils"
import { operations } from "./operations"; import { operations } from "./operations"
import { IoMagnetOutline } from "solid-icons/io"; import { IoMagnetOutline } from "solid-icons/io"
import { AiOutlineCloudUpload, AiOutlineSetting } from "solid-icons/ai"; import { AiOutlineCloudUpload, AiOutlineSetting } from "solid-icons/ai"
import { RiSystemRefreshLine } from "solid-icons/ri"; import { RiSystemRefreshLine } from "solid-icons/ri"
import { usePath } from "~/hooks"; import { usePath } from "~/hooks"
import { Motion } from "@motionone/solid"; import { Motion } from "@motionone/solid"
export const Right = () => { export const Right = () => {
const { isOpen, onToggle } = createDisclosure({ const { isOpen, onToggle } = createDisclosure({
defaultIsOpen: localStorage.getItem("more-open") === "true", defaultIsOpen: localStorage.getItem("more-open") === "true",
onClose: () => localStorage.setItem("more-open", "false"), onClose: () => localStorage.setItem("more-open", "false"),
onOpen: () => localStorage.setItem("more-open", "true"), onOpen: () => localStorage.setItem("more-open", "true"),
}); })
const margin = createMemo(() => (isOpen() ? "$4" : "$5")); const margin = createMemo(() => (isOpen() ? "$4" : "$5"))
const isFolder = createMemo(() => objStore.state === State.Folder); const isFolder = createMemo(() => objStore.state === State.Folder)
const { refresh } = usePath(); const { refresh } = usePath()
return ( return (
<Box <Box
class="left-toolbar-box" class="left-toolbar-box"
@@ -40,7 +40,7 @@ export const Right = () => {
class="toolbar-toggle" class="toolbar-toggle"
as={CgMoreO} as={CgMoreO}
onClick={() => { onClick={() => {
onToggle(); onToggle()
}} }}
/> />
} }
@@ -67,14 +67,14 @@ export const Right = () => {
as={RiSystemRefreshLine} as={RiSystemRefreshLine}
tips="refresh" tips="refresh"
onClick={() => { onClick={() => {
refresh(undefined, true); refresh(undefined, true)
}} }}
/> />
<RightIcon <RightIcon
as={operations.new_file.icon} as={operations.new_file.icon}
tips="new_file" tips="new_file"
onClick={() => { onClick={() => {
bus.emit("tool", "new_file"); bus.emit("tool", "new_file")
}} }}
/> />
<RightIcon <RightIcon
@@ -82,14 +82,14 @@ export const Right = () => {
p="$1_5" p="$1_5"
tips="mkdir" tips="mkdir"
onClick={() => { onClick={() => {
bus.emit("tool", "mkdir"); bus.emit("tool", "mkdir")
}} }}
/> />
<RightIcon <RightIcon
as={AiOutlineCloudUpload} as={AiOutlineCloudUpload}
tips="upload" tips="upload"
onClick={() => { onClick={() => {
bus.emit("tool", "upload"); bus.emit("tool", "upload")
}} }}
/> />
</Show> </Show>
@@ -99,7 +99,7 @@ export const Right = () => {
pl="0" pl="0"
tips="offline_download" tips="offline_download"
onClick={() => { onClick={() => {
bus.emit("tool", "offline_download"); bus.emit("tool", "offline_download")
}} }}
/> />
</Show> </Show>
@@ -112,7 +112,7 @@ export const Right = () => {
as={AiOutlineSetting} as={AiOutlineSetting}
tips="local_settings" tips="local_settings"
onClick={() => { onClick={() => {
bus.emit("tool", "local_settings"); bus.emit("tool", "local_settings")
}} }}
/> />
</VStack> </VStack>
@@ -120,5 +120,5 @@ export const Right = () => {
</VStack> </VStack>
</Show> </Show>
</Box> </Box>
); )
}; }

View File

@@ -1,17 +1,17 @@
import { Portal } from "solid-js/web"; import { Portal } from "solid-js/web"
import { Center } from "./Center"; import { Center } from "./Center"
import { Right } from "./Right"; import { Right } from "./Right"
import { Copy, Move } from "./CopyMove"; import { Copy, Move } from "./CopyMove"
import { Delete } from "./Delete"; import { Delete } from "./Delete"
import { Rename } from "./Rename"; import { Rename } from "./Rename"
import { NewFile } from "./NewFile"; import { NewFile } from "./NewFile"
import { Mkdir } from "./Mkdir"; import { Mkdir } from "./Mkdir"
import { Aria2 } from "./Aria2"; import { Aria2 } from "./Aria2"
import { PackageDownloadModal } from "./Download"; import { PackageDownloadModal } from "./Download"
import { lazy } from "solid-js"; import { lazy } from "solid-js"
import { ModalWrapper } from "./ModalWrapper"; import { ModalWrapper } from "./ModalWrapper"
import { LocalSettings } from "./LocalSettings"; import { LocalSettings } from "./LocalSettings"
const Upload = lazy(() => import("../uploads/Upload")); const Upload = lazy(() => import("../uploads/Upload"))
export const Modal = () => { export const Modal = () => {
return ( return (
@@ -29,8 +29,8 @@ export const Modal = () => {
</ModalWrapper> </ModalWrapper>
<LocalSettings /> <LocalSettings />
</> </>
); )
}; }
export const Toolbar = () => { export const Toolbar = () => {
return ( return (
@@ -39,5 +39,5 @@ export const Toolbar = () => {
<Center /> <Center />
<Modal /> <Modal />
</Portal> </Portal>
); )
}; }

View File

@@ -1,18 +1,18 @@
import { IconTypes } from "solid-icons"; import { IconTypes } from "solid-icons"
import { TiDeleteOutline } from "solid-icons/ti"; import { TiDeleteOutline } from "solid-icons/ti"
import { CgRename } from "solid-icons/cg"; import { CgRename } from "solid-icons/cg"
import { TbFileArrowRight } from "solid-icons/tb"; import { TbFileArrowRight } from "solid-icons/tb"
import { TbCopy, TbLink } from "solid-icons/tb"; import { TbCopy, TbLink } from "solid-icons/tb"
import { AiTwotoneDelete } from "solid-icons/ai"; import { AiTwotoneDelete } from "solid-icons/ai"
import { CgFileAdd, CgFolderAdd } from "solid-icons/cg"; import { CgFileAdd, CgFolderAdd } from "solid-icons/cg"
import { AiOutlineCloudDownload } from "solid-icons/ai"; import { AiOutlineCloudDownload } from "solid-icons/ai"
interface Operations { interface Operations {
[key: string]: { [key: string]: {
icon: IconTypes; icon: IconTypes
color?: string; color?: string
p?: boolean; p?: boolean
}; }
} }
export const operations: Operations = { export const operations: Operations = {
rename: { icon: CgRename, color: "$accent9" }, rename: { icon: CgRename, color: "$accent9" },
@@ -24,7 +24,7 @@ export const operations: Operations = {
new_file: { icon: CgFileAdd, p: true }, new_file: { icon: CgFileAdd, p: true },
cancel_select: { icon: TiDeleteOutline }, cancel_select: { icon: TiDeleteOutline },
download: { icon: AiOutlineCloudDownload, color: "$primary9" }, download: { icon: AiOutlineCloudDownload, color: "$primary9" },
}; }
// interface Operation { // interface Operation {
// label: string; // label: string;
// icon: IconTypes; // icon: IconTypes;

View File

@@ -21,4 +21,4 @@ export type Upload = (
file: File, file: File,
setUpload: SetUpload, setUpload: SetUpload,
asTask: boolean asTask: boolean
) => Promise<Error | undefined> ) => Promise<Error | undefined>

View File

@@ -1,10 +1,10 @@
import { mergeProps } from "solid-js"; import { mergeProps } from "solid-js"
interface Props { interface Props {
/** 过渡的开始颜色 */ /** 过渡的开始颜色 */
startColor?: string; startColor?: string
/** 过渡的结束颜色 */ /** 过渡的结束颜色 */
endColor?: string; endColor?: string
} }
const CornerBottom = (props: Props) => { const CornerBottom = (props: Props) => {
@@ -14,7 +14,7 @@ const CornerBottom = (props: Props) => {
endColor: "#120fc4", endColor: "#120fc4",
}, },
props props
); )
return ( return (
<svg <svg
version="1.1" version="1.1"
@@ -43,7 +43,7 @@ const CornerBottom = (props: Props) => {
/> />
</g> </g>
</svg> </svg>
); )
}; }
export default CornerBottom; export default CornerBottom

View File

@@ -1,10 +1,10 @@
import { mergeProps } from "solid-js"; import { mergeProps } from "solid-js"
interface Props { interface Props {
/** 过渡的开始颜色 */ /** 过渡的开始颜色 */
startColor?: string; startColor?: string
/** 过渡的结束颜色 */ /** 过渡的结束颜色 */
endColor?: string; endColor?: string
} }
const CornerTop = (props: Props) => { const CornerTop = (props: Props) => {
@@ -14,7 +14,7 @@ const CornerTop = (props: Props) => {
endColor: "#120fc4", endColor: "#120fc4",
}, },
props props
); )
return ( return (
<svg height="1337" width="1337"> <svg height="1337" width="1337">
<defs> <defs>
@@ -43,7 +43,7 @@ const CornerTop = (props: Props) => {
/> />
</g> </g>
</svg> </svg>
); )
}; }
export default CornerTop; export default CornerTop

View File

@@ -1,9 +1,9 @@
import { Box, useColorModeValue } from "@hope-ui/solid"; import { Box, useColorModeValue } from "@hope-ui/solid"
import CornerBottom from "./CornerBottom"; import CornerBottom from "./CornerBottom"
import CornerTop from "./CornerTop"; import CornerTop from "./CornerTop"
const LoginBg = () => { const LoginBg = () => {
const bgColor = useColorModeValue("#a9c6ff", "#062b74"); const bgColor = useColorModeValue("#a9c6ff", "#062b74")
return ( return (
<Box <Box
bgColor={bgColor()} bgColor={bgColor()}
@@ -42,7 +42,7 @@ const LoginBg = () => {
<CornerBottom /> <CornerBottom />
</Box> </Box>
</Box> </Box>
); )
}; }
export default LoginBg; export default LoginBg

View File

@@ -1,18 +1,18 @@
import { createResource } from "solid-js"; import { createResource } from "solid-js"
import { Markdown, MaybeLoading } from "~/components"; import { Markdown, MaybeLoading } from "~/components"
const fetchReadme = async () => const fetchReadme = async () =>
await ( await (
await fetch("https://jsd.nn.ci/gh/alist-org/alist@main/README.md") await fetch("https://jsd.nn.ci/gh/alist-org/alist@main/README.md")
).text(); ).text()
const About = () => { const About = () => {
const [readme] = createResource(fetchReadme); const [readme] = createResource(fetchReadme)
return ( return (
<MaybeLoading loading={readme.loading}> <MaybeLoading loading={readme.loading}>
<Markdown children={readme()} /> <Markdown children={readme()} />
</MaybeLoading> </MaybeLoading>
); )
}; }
export default About; export default About

View File

@@ -1,13 +1,13 @@
import { Center, Heading } from "@hope-ui/solid"; import { Center, Heading } from "@hope-ui/solid"
import { useManageTitle } from "~/hooks"; import { useManageTitle } from "~/hooks"
const Dashboard = () => { const Dashboard = () => {
useManageTitle("manage.sidemenu.dashboard"); useManageTitle("manage.sidemenu.dashboard")
return ( return (
<Center h="$full"> <Center h="$full">
<Heading>Dashboard</Heading> <Heading>Dashboard</Heading>
</Center> </Center>
); )
}; }
export default Dashboard; export default Dashboard

View File

@@ -13,19 +13,19 @@ import {
HStack, HStack,
IconButton, IconButton,
useColorModeValue, useColorModeValue,
} from "@hope-ui/solid"; } from "@hope-ui/solid"
import { TiThMenu } from "solid-icons/ti"; import { TiThMenu } from "solid-icons/ti"
import { IoExit } from "solid-icons/io"; import { IoExit } from "solid-icons/io"
import { SwitchColorMode, SwitchLanguageWhite } from "~/components"; import { SwitchColorMode, SwitchLanguageWhite } from "~/components"
import { useRouter, useT } from "~/hooks"; import { useRouter, useT } from "~/hooks"
import { SideMenu } from "./SideMenu"; import { SideMenu } from "./SideMenu"
import { side_menu_items } from "./sidemenu_items"; import { side_menu_items } from "./sidemenu_items"
import { changeToken, notify } from "~/utils"; import { changeToken, notify } from "~/utils"
const { isOpen, onOpen, onClose } = createDisclosure(); const { isOpen, onOpen, onClose } = createDisclosure()
const Header = () => { const Header = () => {
const t = useT(); const t = useT()
const { to } = useRouter(); const { to } = useRouter()
return ( return (
<Box <Box
as="header" as="header"
@@ -54,7 +54,7 @@ const Header = () => {
color="$info9" color="$info9"
cursor="pointer" cursor="pointer"
onClick={() => { onClick={() => {
to("/@manage"); to("/@manage")
}} }}
> >
{t("manage.title")} {t("manage.title")}
@@ -65,9 +65,9 @@ const Header = () => {
aria-label="logout" aria-label="logout"
icon={<IoExit />} icon={<IoExit />}
onClick={() => { onClick={() => {
changeToken(); changeToken()
notify.success(t("manage.logout_success")); notify.success(t("manage.logout_success"))
to(`/@login?redirect=${encodeURIComponent(location.pathname)}`); to(`/@login?redirect=${encodeURIComponent(location.pathname)}`)
}} }}
size="sm" size="sm"
/> />
@@ -90,7 +90,7 @@ const Header = () => {
</DrawerContent> </DrawerContent>
</Drawer> </Drawer>
</Box> </Box>
); )
}; }
export { Header, onClose }; export { Header, onClose }

View File

@@ -1,36 +1,33 @@
import { Box, Flex, Heading, HStack, Icon, VStack } from "@hope-ui/solid"; import { Box, Flex, Heading, HStack, Icon, VStack } from "@hope-ui/solid"
import { createMemo, createSignal, For, Match, Show, Switch } from "solid-js"; import { createMemo, createSignal, For, Match, Show, Switch } from "solid-js"
import { useRouter, useT } from "~/hooks"; import { useRouter, useT } from "~/hooks"
import { BiSolidRightArrow } from "solid-icons/bi"; import { BiSolidRightArrow } from "solid-icons/bi"
import { onClose } from "./Header"; import { onClose } from "./Header"
import { UserMethods, UserRole } from "~/types"; import { UserMethods, UserRole } from "~/types"
import { me } from "~/store"; import { me } from "~/store"
import { AnchorWithBase } from "~/components"; import { AnchorWithBase } from "~/components"
import { Link } from "@solidjs/router"; import { Link } from "@solidjs/router"
import { hoverColor } from "~/utils"; import { hoverColor } from "~/utils"
import { IconTypes } from "solid-icons"; import { IconTypes } from "solid-icons"
export interface SideMenuItemProps { export interface SideMenuItemProps {
title: string; title: string
to: string; to: string
icon?: IconTypes; icon?: IconTypes
children?: SideMenuItemProps[]; children?: SideMenuItemProps[]
role?: number; role?: number
external?: true; external?: true
} }
const SideMenuItem = (props: SideMenuItemProps) => { const SideMenuItem = (props: SideMenuItemProps) => {
const ifShow = createMemo(() => { const ifShow = createMemo(() => {
if (!UserMethods.is_admin(me())) { if (!UserMethods.is_admin(me())) {
if (props.role === undefined) return false; if (props.role === undefined) return false
else if ( else if (props.role === UserRole.GENERAL && !UserMethods.is_general(me()))
props.role === UserRole.GENERAL && return false
!UserMethods.is_general(me())
)
return false;
} }
return true; return true
}); })
return ( return (
<Switch fallback={<SideMenuItemWithTo {...props} />}> <Switch fallback={<SideMenuItemWithTo {...props} />}>
<Match when={!ifShow()}>{null}</Match> <Match when={!ifShow()}>{null}</Match>
@@ -38,13 +35,13 @@ const SideMenuItem = (props: SideMenuItemProps) => {
<SideMenuItemWithChildren {...props} /> <SideMenuItemWithChildren {...props} />
</Match> </Match>
</Switch> </Switch>
); )
}; }
const SideMenuItemWithTo = (props: SideMenuItemProps) => { const SideMenuItemWithTo = (props: SideMenuItemProps) => {
const { pathname } = useRouter(); const { pathname } = useRouter()
const t = useT(); const t = useT()
const isActive = () => pathname() === props.to; const isActive = () => pathname() === props.to
return ( return (
<AnchorWithBase <AnchorWithBase
display="flex" display="flex"
@@ -52,7 +49,7 @@ const SideMenuItemWithTo = (props: SideMenuItemProps) => {
href={props.to} href={props.to}
onClick={() => { onClick={() => {
// to(props.to!); // to(props.to!);
onClose(); onClose()
}} }}
w="$full" w="$full"
alignItems="center" alignItems="center"
@@ -72,19 +69,19 @@ const SideMenuItemWithTo = (props: SideMenuItemProps) => {
<Show when={props.icon}>{<Icon mr="$2" as={props.icon} />}</Show> <Show when={props.icon}>{<Icon mr="$2" as={props.icon} />}</Show>
<Heading>{t(props.title)}</Heading> <Heading>{t(props.title)}</Heading>
</AnchorWithBase> </AnchorWithBase>
); )
}; }
const SideMenuItemWithChildren = (props: SideMenuItemProps) => { const SideMenuItemWithChildren = (props: SideMenuItemProps) => {
const { pathname } = useRouter(); const { pathname } = useRouter()
const [open, setOpen] = createSignal(pathname().includes(props.to)); const [open, setOpen] = createSignal(pathname().includes(props.to))
const t = useT(); const t = useT()
return ( return (
<Box w="$full"> <Box w="$full">
<Flex <Flex
justifyContent="space-between" justifyContent="space-between"
onClick={() => { onClick={() => {
setOpen(!open()); setOpen(!open())
}} }}
w="$full" w="$full"
alignItems="center" alignItems="center"
@@ -112,17 +109,17 @@ const SideMenuItemWithChildren = (props: SideMenuItemProps) => {
</Box> </Box>
</Show> </Show>
</Box> </Box>
); )
}; }
export const SideMenu = (props: { items: SideMenuItemProps[] }) => { export const SideMenu = (props: { items: SideMenuItemProps[] }) => {
return ( return (
<VStack p="$2" w="$full" color="$neutral11" spacing="$1"> <VStack p="$2" w="$full" color="$neutral11" spacing="$1">
<For each={props.items}> <For each={props.items}>
{(item) => { {(item) => {
return <SideMenuItem {...item} />; return <SideMenuItem {...item} />
}} }}
</For> </For>
</VStack> </VStack>
); )
}; }

View File

@@ -7,16 +7,16 @@ import {
PopoverHeader, PopoverHeader,
PopoverBody, PopoverBody,
HStack, HStack,
} from "@hope-ui/solid"; } from "@hope-ui/solid"
import { useT } from "~/hooks"; 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) => {
const t = useT(); const t = useT()
return ( return (
<Popover> <Popover>
{({ onClose }) => ( {({ onClose }) => (
@@ -49,5 +49,5 @@ export const DeletePopover = (props: DeletePopoverProps) => {
</> </>
)} )}
</Popover> </Popover>
); )
}; }

View File

@@ -1,5 +1,5 @@
import { Grid } from "@hope-ui/solid"; import { Grid } from "@hope-ui/solid"
import { JSXElement } from "solid-js"; import { JSXElement } from "solid-js"
export const ResponsiveGrid = (props: { children: JSXElement }) => { export const ResponsiveGrid = (props: { children: JSXElement }) => {
return ( return (
@@ -13,5 +13,5 @@ export const ResponsiveGrid = (props: { children: JSXElement }) => {
> >
{props.children} {props.children}
</Grid> </Grid>
); )
}; }

View File

@@ -1,20 +1,16 @@
import { Box, Center, Flex, HStack, useColorModeValue } from "@hope-ui/solid"; import { Box, Center, Flex, HStack, useColorModeValue } from "@hope-ui/solid"
import { import { FullLoading, SwitchColorMode, SwitchLanguageWhite } from "~/components"
FullLoading, import { useT, useTitle } from "~/hooks"
SwitchColorMode, import { Header } from "./Header"
SwitchLanguageWhite, import { SideMenu } from "./SideMenu"
} from "~/components"; import { side_menu_items } from "./sidemenu_items"
import { useT, useTitle } from "~/hooks"; import { Route, Routes } from "@solidjs/router"
import { Header } from "./Header"; import { For, Suspense } from "solid-js"
import { SideMenu } from "./SideMenu"; import { routes } from "./routes"
import { side_menu_items } from "./sidemenu_items";
import { Route, Routes } from "@solidjs/router";
import { For, Suspense } from "solid-js";
import { routes } from "./routes";
const Manage = () => { const Manage = () => {
const t = useT(); const t = useT()
useTitle(() => t("manage.title")); useTitle(() => t("manage.title"))
return ( return (
<Box <Box
css={{ css={{
@@ -52,14 +48,14 @@ const Manage = () => {
<Routes> <Routes>
<For each={routes}> <For each={routes}>
{(route) => { {(route) => {
return <Route path={route.to!} component={route.component} />; return <Route path={route.to!} component={route.component} />
}} }}
</For> </For>
</Routes> </Routes>
</Box> </Box>
</Flex> </Flex>
</Box> </Box>
); )
}; }
export default Manage; export default Manage

View File

@@ -1,10 +1,10 @@
import { Message } from "./Messenger"; import { Message } from "./Messenger"
import { Heading, Image } from "@hope-ui/solid"; import { Heading, Image } from "@hope-ui/solid"
export const StringShow = (props: Message) => { export const StringShow = (props: Message) => {
return <Heading>{props.content}</Heading>; return <Heading>{props.content}</Heading>
}; }
export const ImageShow = (props: Message) => { export const ImageShow = (props: Message) => {
return <Image src={props.content} />; return <Image src={props.content} />
}; }

View File

@@ -9,35 +9,35 @@ import {
Thead, Thead,
Tr, Tr,
VStack, VStack,
} from "@hope-ui/solid"; } from "@hope-ui/solid"
import { createSignal, For } from "solid-js"; import { createSignal, For } from "solid-js"
import { import {
useFetch, useFetch,
useListFetch, useListFetch,
useManageTitle, useManageTitle,
useRouter, useRouter,
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, PageResp } from "~/types"
import { DeletePopover } from "../common/DeletePopover"; import { DeletePopover } from "../common/DeletePopover"
import { Wether } from "~/components"; import { Wether } from "~/components"
const Metas = () => { 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(() => 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: PageResp<Meta> = 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((id: number) =>
r.post(`/admin/meta/delete?id=${id}`) r.post(`/admin/meta/delete?id=${id}`)
); )
return ( return (
<VStack spacing="$2" alignItems="start" w="$full"> <VStack spacing="$2" alignItems="start" w="$full">
<HStack spacing="$2"> <HStack spacing="$2">
@@ -50,7 +50,7 @@ const Metas = () => {
</Button> </Button>
<Button <Button
onClick={() => { onClick={() => {
to("/@manage/metas/add"); to("/@manage/metas/add")
}} }}
> >
{t("global.add")} {t("global.add")}
@@ -80,7 +80,7 @@ const Metas = () => {
<HStack spacing="$2"> <HStack spacing="$2">
<Button <Button
onClick={() => { onClick={() => {
to(`/@manage/metas/edit/${meta.id}`); to(`/@manage/metas/edit/${meta.id}`)
}} }}
> >
{t("global.edit")} {t("global.edit")}
@@ -89,11 +89,11 @@ const Metas = () => {
name={meta.path} name={meta.path}
loading={deleting() === meta.id} loading={deleting() === meta.id}
onClick={async () => { onClick={async () => {
const resp = await deleteMeta(meta.id); const resp = await deleteMeta(meta.id)
handleResp(resp, () => { handleResp(resp, () => {
notify.success(t("global.delete_success")); notify.success(t("global.delete_success"))
refresh(); refresh()
}); })
}} }}
/> />
</HStack> </HStack>
@@ -105,7 +105,7 @@ const Metas = () => {
</Table> </Table>
</Box> </Box>
</VStack> </VStack>
); )
}; }
export default Metas; export default Metas

View File

@@ -1,10 +1,10 @@
import { lazy } from "solid-js"; import { lazy } from "solid-js"
import { Center, Heading } from "@hope-ui/solid"; import { Center, Heading } from "@hope-ui/solid"
import { trimLeft } from "~/utils"; import { trimLeft } from "~/utils"
import { SideMenuItem, side_menu_items } from "./sidemenu_items"; import { SideMenuItem, side_menu_items } from "./sidemenu_items"
import { useManageTitle } from "~/hooks"; import { useManageTitle } from "~/hooks"
type Route = Pick<SideMenuItem, "to" | "component">; type Route = Pick<SideMenuItem, "to" | "component">
const hide_routes: Route[] = [ const hide_routes: Route[] = [
{ {
@@ -39,32 +39,32 @@ const hide_routes: Route[] = [
to: "/messenger", to: "/messenger",
component: lazy(() => import("./messenger/Messenger")), component: lazy(() => import("./messenger/Messenger")),
}, },
]; ]
const Placeholder = (props: { title: string; to: string }) => { const Placeholder = (props: { title: string; to: string }) => {
useManageTitle(props.title); useManageTitle(props.title)
return ( return (
<Center h="$full"> <Center h="$full">
<Heading>{props.title}</Heading> <Heading>{props.title}</Heading>
</Center> </Center>
); )
}; }
const get_routes = (items: SideMenuItem[], acc: Route[] = []) => { const get_routes = (items: SideMenuItem[], acc: Route[] = []) => {
items.forEach((item) => { items.forEach((item) => {
if (item.children) { if (item.children) {
get_routes(item.children, acc); get_routes(item.children, acc)
} else { } else {
acc.push({ acc.push({
to: trimLeft(item.to!, "/@manage"), to: trimLeft(item.to!, "/@manage"),
component: component:
item.component || item.component ||
(() => <Placeholder title={item.title} to={item.to || "empty"} />), (() => <Placeholder title={item.title} to={item.to || "empty"} />),
}); })
} }
}); })
return acc; return acc
}; }
const routes = get_routes(side_menu_items).concat(hide_routes); const routes = get_routes(side_menu_items).concat(hide_routes)
export { routes }; export { routes }

View File

@@ -16,36 +16,36 @@ import {
SelectValue, SelectValue,
Switch as HopeSwitch, Switch as HopeSwitch,
Textarea, Textarea,
} from "@hope-ui/solid"; } from "@hope-ui/solid"
import { For, Match, Show, Switch } from "solid-js"; import { For, Match, Show, Switch } from "solid-js"
import { useT } from "~/hooks"; import { useT } from "~/hooks"
import { DriverItem, Type } from "~/types"; import { DriverItem, Type } from "~/types"
export type ItemProps = DriverItem & { export type ItemProps = DriverItem & {
readonly?: boolean; readonly?: boolean
full_name_path?: string; full_name_path?: string
options_prefix?: string; options_prefix?: string
driver?: string; driver?: string
} & ( } & (
| { | {
type: Type.Bool; type: Type.Bool
onChange?: (value: boolean) => void; onChange?: (value: boolean) => void
value: boolean; value: boolean
} }
| { | {
type: Type.Number; type: Type.Number
onChange?: (value: number) => void; onChange?: (value: number) => void
value: number; value: number
} }
| { | {
type: Type.String | Type.Text | Type.Select; type: Type.String | Type.Text | Type.Select
onChange?: (value: string) => void; onChange?: (value: string) => void
value: string; value: string
} }
); )
const Item = (props: ItemProps) => { const Item = (props: ItemProps) => {
const t = useT(); const t = useT()
return ( return (
<FormControl <FormControl
w="$full" w="$full"
@@ -159,7 +159,7 @@ const Item = (props: ItemProps) => {
</FormHelperText> </FormHelperText>
</Show> </Show>
</FormControl> </FormControl>
); )
}; }
export { Item }; export { Item }

View File

@@ -1,11 +1,6 @@
import { Button, Grid, HStack, VStack } from "@hope-ui/solid" import { Button, Grid, HStack, VStack } from "@hope-ui/solid"
import { createSignal, For } from "solid-js" import { createSignal, For } from "solid-js"
import { import { useFetch, useManageTitle, useRouter, useT } from "~/hooks"
useFetch,
useManageTitle,
useRouter,
useT,
} from "~/hooks"
import { handleResp, r } from "~/utils" import { handleResp, r } from "~/utils"
import { PageResp, Storage } from "~/types" import { PageResp, Storage } from "~/types"
import { StorageC } from "./Storage" import { StorageC } from "./Storage"

View File

@@ -1,15 +1,15 @@
import { VStack } from "@hope-ui/solid"; import { VStack } from "@hope-ui/solid"
import { useManageTitle } from "~/hooks"; import { useManageTitle } from "~/hooks"
import { TypeTasks } from "./Tasks"; import { TypeTasks } from "./Tasks"
const Aria2 = () => { const Aria2 = () => {
useManageTitle("manage.sidemenu.aria2"); useManageTitle("manage.sidemenu.aria2")
return ( return (
<VStack w="$full" alignItems="start" spacing="$4"> <VStack w="$full" alignItems="start" spacing="$4">
<TypeTasks type="down" /> <TypeTasks type="down" />
<TypeTasks type="transfer" /> <TypeTasks type="transfer" />
</VStack> </VStack>
); )
}; }
export default Aria2; export default Aria2

View File

@@ -1,9 +1,9 @@
import { useManageTitle } from "~/hooks"; import { useManageTitle } from "~/hooks"
import { TypeTasks } from "./Tasks"; import { TypeTasks } from "./Tasks"
const Copy = () => { const Copy = () => {
useManageTitle("manage.sidemenu.copy"); useManageTitle("manage.sidemenu.copy")
return <TypeTasks type="copy" />; return <TypeTasks type="copy" />
}; }
export default Copy; export default Copy

View File

@@ -11,13 +11,7 @@ import {
import { MaybeLoading, FolderChooseInput } from "~/components" import { MaybeLoading, FolderChooseInput } from "~/components"
import { useFetch, useRouter, useT } from "~/hooks" import { useFetch, useRouter, useT } from "~/hooks"
import { handleResp, notify, r } from "~/utils" import { handleResp, notify, r } from "~/utils"
import { import { PEmptyResp, PResp, User, UserMethods, UserPermissions } from "~/types"
PEmptyResp,
PResp,
User,
UserMethods,
UserPermissions,
} from "~/types"
import { createStore } from "solid-js/store" import { createStore } from "solid-js/store"
import { For } from "solid-js" import { For } from "solid-js"

View File

@@ -1,5 +1,5 @@
import { Box, VStack } from "@hope-ui/solid"; import { Box, VStack } from "@hope-ui/solid"
import Upload from "~/pages/home/uploads/Upload"; import Upload from "~/pages/home/uploads/Upload"
const Index = () => { const Index = () => {
return ( return (
@@ -8,7 +8,7 @@ const Index = () => {
<Upload /> <Upload />
</Box> </Box>
</VStack> </VStack>
); )
}; }
export default Index; export default Index

View File

@@ -1,8 +1,8 @@
export const keyPressed: Record<string, boolean> = {}; export const keyPressed: Record<string, boolean> = {}
document.addEventListener("keydown", (e) => { document.addEventListener("keydown", (e) => {
keyPressed[e.key] = true; keyPressed[e.key] = true
}); })
document.addEventListener("keyup", (e) => { document.addEventListener("keyup", (e) => {
// keyPressed[e.key] = false; // keyPressed[e.key] = false;
delete keyPressed[e.key]; delete keyPressed[e.key]
}); })

View File

@@ -1,22 +1,22 @@
import { createLocalStorage } from "@solid-primitives/storage"; import { createLocalStorage } from "@solid-primitives/storage"
const [local, setLocal, { remove, clear, toJSON }] = createLocalStorage(); const [local, setLocal, { remove, clear, toJSON }] = createLocalStorage()
export function isValidKey( export function isValidKey(
key: string | number | symbol, key: string | number | symbol,
object: object object: object
): key is keyof typeof object { ): key is keyof typeof object {
return key in object; return key in object
} }
export const initialLocalSettings = { export const initialLocalSettings = {
aria2_rpc_url: "http://localhost:6800/jsonrpc", aria2_rpc_url: "http://localhost:6800/jsonrpc",
aria2_rpc_secret: "", aria2_rpc_secret: "",
// aria2_dir: "alist", // aria2_dir: "alist",
}; }
for (const key in initialLocalSettings) { for (const key in initialLocalSettings) {
if (!local[key] && isValidKey(key, initialLocalSettings)) { if (!local[key] && isValidKey(key, initialLocalSettings)) {
setLocal(key, initialLocalSettings[key]); setLocal(key, initialLocalSettings[key])
} }
} }
export { local, setLocal, remove, clear, toJSON }; export { local, setLocal, remove, clear, toJSON }

View File

@@ -1,9 +1,9 @@
import { cookieStorage, createStorageSignal } from "@solid-primitives/storage"; import { cookieStorage, createStorageSignal } from "@solid-primitives/storage"
import { createSignal } from "solid-js"; import { createSignal } from "solid-js"
import { createStore, produce } from "solid-js/store"; import { createStore, produce } from "solid-js/store"
import { Obj, StoreObj } from "~/types"; import { Obj, StoreObj } from "~/types"
import { log } from "~/utils"; import { log } from "~/utils"
import { keyPressed } from "./key-event"; import { keyPressed } from "./key-event"
export enum State { export enum State {
Initial, // Initial state Initial, // Initial state
@@ -16,20 +16,20 @@ export enum State {
} }
const [objStore, setObjStore] = createStore<{ const [objStore, setObjStore] = createStore<{
obj: Obj; obj: Obj
raw_url: string; raw_url: string
related: Obj[]; related: Obj[]
objs: StoreObj[]; objs: StoreObj[]
total: number; total: number
write?: boolean; write?: boolean
readme: string; readme: string
provider: string; provider: string
// pageIndex: number; // pageIndex: number;
// pageSize: number; // pageSize: number;
state: State; state: State
err: string; err: string
}>({ }>({
obj: {} as Obj, obj: {} as Obj,
raw_url: "", raw_url: "",
@@ -44,30 +44,30 @@ const [objStore, setObjStore] = createStore<{
// pageSize: 50, // pageSize: 50,
state: State.Initial, state: State.Initial,
err: "", err: "",
}); })
const [selectedNum, setSelectedNum] = createSignal(0); const [selectedNum, setSelectedNum] = createSignal(0)
const setObjs = (objs: Obj[]) => { const setObjs = (objs: Obj[]) => {
setSelectedNum(0); setSelectedNum(0)
lastChecked = { index: -1, selected: false }; lastChecked = { index: -1, selected: false }
setObjStore("objs", objs); setObjStore("objs", objs)
setObjStore("obj", "is_dir", true); setObjStore("obj", "is_dir", true)
}; }
export const ObjStore = { export const ObjStore = {
setObj: (obj: Obj) => { setObj: (obj: Obj) => {
setObjStore("obj", obj); setObjStore("obj", obj)
}, },
setRawUrl: (raw_url: string) => { setRawUrl: (raw_url: string) => {
setObjStore("raw_url", raw_url); setObjStore("raw_url", raw_url)
}, },
setProvider: (provider: string) => { setProvider: (provider: string) => {
setObjStore("provider", provider); setObjStore("provider", provider)
}, },
setObjs: setObjs, setObjs: setObjs,
setTotal: (total: number) => { setTotal: (total: number) => {
setObjStore("total", total); setObjStore("total", total)
}, },
setReadme: (readme: string) => setObjStore("readme", readme), setReadme: (readme: string) => setObjStore("readme", readme),
setRelated: (related: Obj[]) => setObjStore("related", related), setRelated: (related: Obj[]) => setObjStore("related", related),
@@ -84,35 +84,35 @@ export const ObjStore = {
// }, // },
setState: (state: State) => setObjStore("state", state), setState: (state: State) => setObjStore("state", state),
setErr: (err: string) => setObjStore("err", err), setErr: (err: string) => setObjStore("err", err),
}; }
export type OrderBy = "name" | "size" | "modified"; 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)
setObjStore( setObjStore(
"objs", "objs",
produce((objs) => produce((objs) =>
objs.sort((a, b) => { objs.sort((a, b) => {
if (a[orderBy] < b[orderBy]) return reverse ? 1 : -1; if (a[orderBy] < b[orderBy]) return reverse ? 1 : -1
if (a[orderBy] > b[orderBy]) return reverse ? -1 : 1; if (a[orderBy] > b[orderBy]) return reverse ? -1 : 1
return 0; return 0
}) })
) )
); )
}; }
export const appendObjs = (objs: Obj[]) => { export const appendObjs = (objs: Obj[]) => {
setObjStore( setObjStore(
"objs", "objs",
produce((prev) => prev.push(...objs)) produce((prev) => prev.push(...objs))
); )
}; }
let lastChecked = { let lastChecked = {
index: -1, index: -1,
selected: false, selected: false,
}; }
export const selectIndex = (index: number, checked: boolean, one?: boolean) => { export const selectIndex = (index: number, checked: boolean, one?: boolean) => {
if ( if (
@@ -120,80 +120,80 @@ export const selectIndex = (index: number, checked: boolean, one?: boolean) => {
lastChecked.index !== -1 && lastChecked.index !== -1 &&
lastChecked.selected === checked lastChecked.selected === checked
) { ) {
const start = Math.min(lastChecked.index, index); const start = Math.min(lastChecked.index, index)
const end = Math.max(lastChecked.index, index); const end = Math.max(lastChecked.index, index)
const curCheckedNum = objStore.objs const curCheckedNum = objStore.objs
.slice(start, end + 1) .slice(start, end + 1)
.filter((o) => o.selected).length; .filter((o) => o.selected).length
setObjStore("objs", { from: start, to: end }, () => ({ setObjStore("objs", { from: start, to: end }, () => ({
selected: checked, selected: checked,
})); }))
// update selected num // update selected num
const newSelectedNum = const newSelectedNum =
selectedNum() - curCheckedNum + (checked ? end - start + 1 : 0); selectedNum() - curCheckedNum + (checked ? end - start + 1 : 0)
setSelectedNum(newSelectedNum); setSelectedNum(newSelectedNum)
} else { } else {
setObjStore( setObjStore(
"objs", "objs",
index, index,
produce((obj) => { produce((obj) => {
if (obj.selected !== checked) { if (obj.selected !== checked) {
setSelectedNum(checked ? selectedNum() + 1 : selectedNum() - 1); setSelectedNum(checked ? selectedNum() + 1 : selectedNum() - 1)
} }
obj.selected = checked; obj.selected = checked
}) })
); )
} }
lastChecked = { index, selected: checked }; lastChecked = { index, selected: checked }
one && setSelectedNum(checked ? 1 : 0); one && setSelectedNum(checked ? 1 : 0)
}; }
export const selectAll = (checked: boolean) => { export const selectAll = (checked: boolean) => {
setSelectedNum(checked ? objStore.objs.length : 0); setSelectedNum(checked ? objStore.objs.length : 0)
setObjStore("objs", {}, (obj) => ({ selected: checked })); setObjStore("objs", {}, (obj) => ({ selected: checked }))
}; }
export const selectedObjs = () => { export const selectedObjs = () => {
return objStore.objs.filter((obj) => obj.selected); return objStore.objs.filter((obj) => obj.selected)
}; }
export const allChecked = () => { export const allChecked = () => {
return objStore.objs.length === selectedNum(); return objStore.objs.length === selectedNum()
}; }
export const oneChecked = () => { export const oneChecked = () => {
return selectedNum() === 1; return selectedNum() === 1
}; }
export const haveSelected = () => { export const haveSelected = () => {
return selectedNum() > 0; return selectedNum() > 0
}; }
export const isIndeterminate = () => { export const isIndeterminate = () => {
return selectedNum() > 0 && selectedNum() < objStore.objs.length; return selectedNum() > 0 && selectedNum() < objStore.objs.length
}; }
export type Layout = "list" | "grid"; export type Layout = "list" | "grid"
const [layout, setLayout] = createStorageSignal<Layout>("layout", "list"); const [layout, setLayout] = createStorageSignal<Layout>("layout", "list")
const [_checkboxOpen, setCheckboxOpen] = createStorageSignal<string>( const [_checkboxOpen, setCheckboxOpen] = createStorageSignal<string>(
"checkbox-open", "checkbox-open",
"false" "false"
); )
export const checkboxOpen = () => _checkboxOpen() === "true"; export const checkboxOpen = () => _checkboxOpen() === "true"
export const toggleCheckbox = () => { export const toggleCheckbox = () => {
setCheckboxOpen(checkboxOpen() ? "false" : "true"); setCheckboxOpen(checkboxOpen() ? "false" : "true")
}; }
export { objStore, layout, setLayout }; export { objStore, layout, setLayout }
// browser password // browser password
const [_password, _setPassword] = createSignal<string>( const [_password, _setPassword] = createSignal<string>(
cookieStorage.getItem("browser-password") || "" cookieStorage.getItem("browser-password") || ""
); )
export { _password as password }; export { _password as password }
export const setPassword = (password: string) => { export const setPassword = (password: string) => {
_setPassword(password); _setPassword(password)
cookieStorage.setItem("browser-password", password); cookieStorage.setItem("browser-password", password)
}; }

View File

@@ -1,21 +1,21 @@
import { Type } from "."; import { Type } from "."
export interface DriverItem { export interface DriverItem {
name: string; name: string
type: Type; type: Type
default: string; default: string
options: string; options: string
required?: boolean; required?: boolean
help?: string; help?: string
} }
export interface DriverConfig { export interface DriverConfig {
name: string; name: string
local_sort: boolean; local_sort: boolean
only_local: boolean; only_local: boolean
only_proxy: boolean; only_proxy: boolean
no_cache: boolean; no_cache: boolean
no_upload: boolean; no_upload: boolean
need_ms: boolean; need_ms: boolean
default_root: string; default_root: string
} }

Some files were not shown because too many files have changed in this diff Show More