From 5454609e2c814ccd636bee593da4f8602bb76eb9 Mon Sep 17 00:00:00 2001 From: Xhofe Date: Mon, 25 Apr 2022 16:37:39 +0800 Subject: [PATCH 1/5] fix: music auto switch back while change volume (close Xhofe/alist#994) --- src/pages/list/preview/audio.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pages/list/preview/audio.tsx b/src/pages/list/preview/audio.tsx index b76c818..fdeddef 100644 --- a/src/pages/list/preview/audio.tsx +++ b/src/pages/list/preview/audio.tsx @@ -37,6 +37,7 @@ const Audio = ({ file }: FileProps) => { getSetting("music cover") || "https://store.heytapimage.com/cdo-portal/feedback/202110/30/d43c41c5d257c9bc36366e310374fb19.png"; const singer = t("unknown"); + const [playIndex, setPlayIndex] = React.useState(0); useEffect(() => { const audio: ReactJkMusicPlayerAudioListProps = { name: file.name, @@ -68,7 +69,7 @@ const Audio = ({ file }: FileProps) => { } else { setAudioLists([audio]); } - // setAudioLists([audio, ...audioList]); + setPlayIndex(audioList.findIndex((item) => item.name === file.name)); }, []); return ( @@ -91,7 +92,8 @@ const Audio = ({ file }: FileProps) => { left: 20, bottom: 20, }} - playIndex={audioLists.findIndex((item) => item.name === file.name)} + playIndex={playIndex} + onPlayIndexChange={setPlayIndex} sortableOptions={{ disabled: mobile }} defaultVolume={volume} onAudioVolumeChange={(v) => setVolume(v)} From 537be54ac05b2980bffce2c848480587379f1092 Mon Sep 17 00:00:00 2001 From: Windman Date: Thu, 28 Apr 2022 17:34:15 +0800 Subject: [PATCH 2/5] feat: add aria2 download support (#42) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add aria2 support 在右键菜单中增加了使用aria2下载的item,可以直接发送选中的文件链接到aria2,aria2的配置在 setting->backend->(Aria2 RPC url, Aria2 RPC secret) * fix: allow set empty aria2 rpcSecret * chore: review pr Co-authored-by: Xhofe --- src/i18n/locales/zh.ts | 4 ++ src/pages/list/context.tsx | 31 ++++++++++ src/pages/list/layout/files/contextmenu.tsx | 67 ++++++++++++++++++++- src/utils/aria2.ts | 30 +++++++++ 4 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 src/utils/aria2.ts diff --git a/src/i18n/locales/zh.ts b/src/i18n/locales/zh.ts index fe3bceb..8b3ec98 100644 --- a/src/i18n/locales/zh.ts +++ b/src/i18n/locales/zh.ts @@ -149,6 +149,10 @@ const zh = { "Backup": "备份", "Restore": "还原", "Virtual path": "虚拟路径", + "Send to Aria2": "发送到Aria2", + "Send {{number}} links to Aria2": "发送 {{number}} 个链接到Aria2", + "Can't download folder with Aria2": "Aria2不能下载文件夹", + "Sent": "已发送", } export default zh diff --git a/src/pages/list/context.tsx b/src/pages/list/context.tsx index 94156ba..b38383f 100644 --- a/src/pages/list/context.tsx +++ b/src/pages/list/context.tsx @@ -82,6 +82,11 @@ interface Page { page_size: number; } +interface Aria2 { + rpcUrl: string; + rpcSecret: string; +} + export interface ContextProps { files: File[]; setFiles: (files: File[]) => void; @@ -113,6 +118,8 @@ export interface ContextProps { page: Page; setPage: (page: Page) => void; hideFiles: RegExp[]; + aria2: Aria2; + setAria2: (aria2: Aria2) => void; } export const IContext = createContext({ @@ -140,6 +147,8 @@ export const IContext = createContext({ page: { page_num: 1, page_size: 30 }, setPage: () => {}, hideFiles: [], + aria2: { rpcUrl: "", rpcSecret: "" }, + setAria2: () => {}, }); const IContextProvider = (props: any) => { @@ -169,6 +178,11 @@ const IContextProvider = (props: any) => { page_size: 30, }); + const [aria2, setAria2] = React.useState({ + rpcUrl: "", + rpcSecret: "", + }); + const [hideFiles, setHideFiles] = React.useState([]); const darkMode = useColorModeValue(false, true); @@ -253,9 +267,25 @@ const IContextProvider = (props: any) => { }); }, []); + const aria2Config = useCallback(() => { + admin.get("settings?group=1").then((resp) => { + let res = resp.data; + if (res.code === 200) { + let setting: Setting[] = res.data; + let url = setting.find((item) => item.key === "Aria2 RPC url"); + let secret = setting.find((item) => item.key === "Aria2 RPC secret"); + setAria2({ + rpcUrl: url?.value || "", + rpcSecret: secret?.value || "", + }); + } + }); + }, []); + useEffect(() => { initialSettings(); login(); + aria2Config(); }, []); const [showUnfold, setShowUnfold] = React.useState(false); @@ -309,6 +339,7 @@ const IContextProvider = (props: any) => { page, setPage, hideFiles, + aria2, }} {...props} /> diff --git a/src/pages/list/layout/files/contextmenu.tsx b/src/pages/list/layout/files/contextmenu.tsx index 7a77eef..b13c2ec 100644 --- a/src/pages/list/layout/files/contextmenu.tsx +++ b/src/pages/list/layout/files/contextmenu.tsx @@ -21,6 +21,7 @@ import { FcNumericalSorting12, FcClock, FcRefresh, + FcDownload, } from "react-icons/fc"; import { MdDeleteForever } from "react-icons/md"; import useFileUrl from "../../../../hooks/useFileUrl"; @@ -34,6 +35,7 @@ import Rename, { RenameInput } from "./menus/rename"; import Move, { MoveSelect } from "./menus/move"; import Copy, { CopySelect } from "./menus/copy"; import Refresh from "./menus/refresh"; +import { downloadWithAria2 } from "~/utils/aria2"; export const MENU_ID = "list-menu"; @@ -43,8 +45,15 @@ interface IsOpenSet { const ContextMenu = () => { const { t } = useTranslation(); - const { sort, setSort, multiSelect, setMultiSelect, selectFiles, loggedIn } = - useContext(IContext); + const { + sort, + setSort, + multiSelect, + setMultiSelect, + selectFiles, + loggedIn, + aria2, + } = useContext(IContext); const menuTheme = useColorModeValue(theme.light, theme.dark); const toast = useToast(); const getFileUrl = useFileUrl(); @@ -158,6 +167,60 @@ const ContextMenu = () => { + {loggedIn && ( + { + let content = ""; + if (multiSelect) { + content = selectFiles + .filter((file) => file.type !== 1) + .map((file) => { + return getFileUrlDecode(file); + }) + .join("\n"); + } else { + const file = props as File; + if (file.type === 1) { + toast({ + title: t("Can't download folder with Aria2"), + status: "warning", + duration: 3000, + isClosable: true, + }); + return; + } + content = getFileUrlDecode(file); + } + if (!aria2.rpcUrl) { + toast({ + title: t("Aria2 is not configured"), + status: "error", + duration: 3000, + isClosable: true, + }); + } else { + downloadWithAria2(content, aria2); + toast({ + title: t("Sent"), + status: "success", + duration: 3000, + isClosable: true, + }); + } + }} + > + + + {multiSelect + ? t("Send {{number}} links to Aria2", { + number: selectFiles.length, + }) + : t("Send to Aria2")} + + + )} + { diff --git a/src/utils/aria2.ts b/src/utils/aria2.ts new file mode 100644 index 0000000..5491f9f --- /dev/null +++ b/src/utils/aria2.ts @@ -0,0 +1,30 @@ +import axios from "axios"; +import { md5_16 } from "./md5"; + +export const downloadWithAria2 = ( + content: string, + aria2: { rpcUrl: string; rpcSecret: string } +) => { + let linkArr: string[] = content.split("\n"); + if (linkArr.length > 0) { + let url = aria2.rpcUrl; + let secret = aria2.rpcSecret; + if (!url) { + console.log("rpcUrl 不能为空"); + } else { + for (let link of linkArr) { + let id = md5_16(link); + let data = { + id: id, + jsonrpc: "2.0", + method: "aria2.addUri", + params: ["token:" + secret, [link]], + }; + axios.post(url, data).then(async (resp) => { + let res = resp.data; + console.log(res); + }); + } + } + } +}; From 26e62bab1bda16b9b4f8c5e4535691f77da491e7 Mon Sep 17 00:00:00 2001 From: ericarena Date: Thu, 5 May 2022 11:18:26 +0800 Subject: [PATCH 3/5] feat: add monaco edit --- package.json | 2 + src/pages/list/index.tsx | 11 ++ src/pages/list/layout/files/contextmenu.tsx | 15 +- src/pages/list/preview/codeeditor.tsx | 160 ++++++++++++++++++++ yarn.lock | 25 +++ 5 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 src/pages/list/preview/codeeditor.tsx diff --git a/package.json b/package.json index bc910f7..39cccc6 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@choc-ui/paginator": "^3.4.0", "@emotion/react": "^11.8.1", "@emotion/styled": "^11.8.1", + "@monaco-editor/react": "^4.4.4", "@xhofe/react-viewer": "^3.2.3", "artplayer": "^4.3.1", "axios": "^0.26.0", @@ -37,6 +38,7 @@ "hls.js": "^1.1.5", "i18next": "^21.6.12", "mitt": "^3.0.0", + "monaco-editor": "^0.33.0", "react": "^17.0.0", "react-contexify": "^5.0.0", "react-dom": "^17.0.0", diff --git a/src/pages/list/index.tsx b/src/pages/list/index.tsx index 06ac247..f266573 100644 --- a/src/pages/list/index.tsx +++ b/src/pages/list/index.tsx @@ -78,6 +78,9 @@ const Do = (props: any) => { setMsg(res.message); if (res.code === 200) { if (res.data.type === "file") { + if (switchToEdit()) { + res.data.files[0].type = 10 + } setFiles(res.data.files); } else { if ( @@ -171,6 +174,14 @@ const Do = (props: any) => { } return false; }; + const switchToEdit = () => { + const query = new URLSearchParams(location.search); + const search = query.get("edit"); + if (search) { + return true + } + return false + } useEffect(() => { if(!switchToSearch()){ allRefresh(); diff --git a/src/pages/list/layout/files/contextmenu.tsx b/src/pages/list/layout/files/contextmenu.tsx index b13c2ec..b414546 100644 --- a/src/pages/list/layout/files/contextmenu.tsx +++ b/src/pages/list/layout/files/contextmenu.tsx @@ -22,13 +22,14 @@ import { FcClock, FcRefresh, FcDownload, + FcEditImage, } from "react-icons/fc"; import { MdDeleteForever } from "react-icons/md"; import useFileUrl from "../../../../hooks/useFileUrl"; import useDownPackage from "../../../../hooks/useDownPackage"; import { copyToClip } from "../../../../utils/copy-clip"; import admin from "../../../../utils/admin"; -import { useLocation } from "react-router-dom"; +import { useLocation, useHistory } from "react-router-dom"; import bus from "../../../../utils/event-bus"; import NewFolder, { NewFolderInput } from "./menus/new-folder"; import Rename, { RenameInput } from "./menus/rename"; @@ -54,6 +55,7 @@ const ContextMenu = () => { loggedIn, aria2, } = useContext(IContext); + const history = useHistory() const menuTheme = useColorModeValue(theme.light, theme.dark); const toast = useToast(); const getFileUrl = useFileUrl(); @@ -127,6 +129,17 @@ const ContextMenu = () => { } > + { + const file = props as File + history.push(file.name + "?edit=true") + }}> + + + {t("Edit")} + + { setIsOpen({ ...isOpen, rename: true }); diff --git a/src/pages/list/preview/codeeditor.tsx b/src/pages/list/preview/codeeditor.tsx new file mode 100644 index 0000000..eb7e8db --- /dev/null +++ b/src/pages/list/preview/codeeditor.tsx @@ -0,0 +1,160 @@ +// Integrating the ESM version of the Monaco Editor +// https://github.com/microsoft/monaco-editor/blob/main/docs/integrate-esm.md#using-vite + +// import * as monaco from 'monaco-editor'; +// self.MonacoEnvironment = { +// getWorker: function (workerId, label) { +// const getWorkerModule = (moduleUrl, label) => { +// return new Worker(self.MonacoEnvironment.getWorkerUrl(moduleUrl), { +// name: label, +// type: 'module' +// }); +// }; + +// switch (label) { +// case 'json': +// return getWorkerModule('/monaco-editor/esm/vs/language/json/json.worker?worker', label); +// case 'css': +// case 'scss': +// case 'less': +// return getWorkerModule('/monaco-editor/esm/vs/language/css/css.worker?worker', label); +// case 'html': +// case 'handlebars': +// case 'razor': +// return getWorkerModule('/monaco-editor/esm/vs/language/html/html.worker?worker', label); +// case 'typescript': +// case 'javascript': +// return getWorkerModule('/monaco-editor/esm/vs/language/typescript/ts.worker?worker', label); +// default: +// return getWorkerModule('/monaco-editor/esm/vs/editor/editor.worker?worker', label); +// } +// } +// }; + +import Editor, { loader } from "@monaco-editor/react"; +import { Box, Select, Button, Flex, Spacer, useColorModeValue, useToast } from "@chakra-ui/react"; +import React from "react"; +import axios from "axios"; +import { useLocation } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import useFileUrl from "../../../hooks/useFileUrl"; +import { FileProps, IContext } from "../context"; +import request from "../../../utils/public"; +import { useHistory } from "react-router-dom"; + +export const type = 10; +export const exts = []; + +const CodeEditor = ({ file, readme }: FileProps) => { + const theme = useColorModeValue("light", "dark"); + const { t } = useTranslation(); + const toast = useToast(); + const { pathname } = useLocation() + + const [content, setContent] = React.useState("") + const [ext, setExt] = React.useState("") + + const fileTypes: { [key: string]: string } = { + ts: "TypeScript", + js: "JavaScript", + css: "CSS", + less: "LESS", + scss: "SCSS", + json: "JSON", + html: "HTML", + dockerfile: "DOCKERFILE", + php: "PHP", + py: "Python", + cs: "C#", + go: "GO", + cpp: "C++", + h: "C++", + ps: "Powershell", + md: "Markdown", + diff: "Diff", + java: "Java", + xml: "XML", + vb: "VB", + coffee: "CoffeeScript", + bat: "Batch", + lua: "Lua", + ruby: "Ruby", + sass: "SASS", + r: "R", + obc: "Objective-C", + } + + const fileopts = Array.from( + new Set(Object.keys(fileTypes).map((key) => {return fileTypes[key]})) + ).map((f, index) => + + ) + + let link = useFileUrl(true)(file) + let fileext = file.name.split(".").pop() as string + const getContent = () => { + axios.get(link).then( + async (resp) => { + setExt(fileext) + setContent(resp.data) + } + ) + } + + const handelOptsChange: React.ChangeEventHandler = (event) => { + console.log(event.target.value) + setExt(event.target.value) + } + const handleSaveButton: React.MouseEventHandler = async (e) => { + const folder = pathname.substring(0, pathname.lastIndexOf("/") || 1) + const form = new FormData(); + form.append("files", new Blob([content as string]), file.name); + form.append("path", folder); + request.post("upload", form, { + headers: { + "Content-Type": "multipart/form-data", + } + }).then((resp) => { + const res = resp.data + toast({ + title: t(res.message), + status: res.code === 200 ? "success" : "error", + duration: 3000, + isClosable: true, + }); + }) + } + React.useEffect(() =>{ + getContent() + }, []) + + return ( + + + + {file.name} + + + + + setContent(value)} + /> + + + + + + ) +} + +export default CodeEditor \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index b857e76..e4e114b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -960,6 +960,21 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== +"@monaco-editor/loader@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.3.1.tgz#35d198fb13a983dd49e9e448a172b6a9da309411" + integrity sha512-LfpAO6e54SZQW0nnI1sWKO4XtAlNx3WHXYZixeVy0HhZ4txGPOok4rs2u4dSi2+iRWeL198cZ2FlFQKr8mH+cw== + dependencies: + state-local "^1.0.6" + +"@monaco-editor/react@^4.4.4": + version "4.4.4" + resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-4.4.4.tgz#8d6b0ac144e2b673f85408e30f1facafff9b7aab" + integrity sha512-yQsYnVkgP5RC5ZMoRVCXSBn4D4hLUOgoQK+AZJpVY57NDXmEb57OVaaYKh8/RTzxkpuLV278hKNw5DnuzlgQwg== + dependencies: + "@monaco-editor/loader" "^1.3.1" + prop-types "^15.7.2" + "@popperjs/core@^2.9.3": version "2.11.2" resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz#830beaec4b4091a9e9398ac50f865ddea52186b9" @@ -2508,6 +2523,11 @@ mitt@^3.0.0: resolved "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz#69ef9bd5c80ff6f57473e8d89326d01c414be0bd" integrity sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ== +monaco-editor@^0.33.0: + version "0.33.0" + resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.33.0.tgz#842e244f3750a2482f8a29c676b5684e75ff34af" + integrity sha512-VcRWPSLIUEgQJQIE0pVT8FcGBIgFoxz7jtqctE+IiCxWugD0DwgyQBcZBhdSrdMC84eumoqMZsGl2GTreOzwqw== + mri@^1.1.0: version "1.2.0" resolved "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" @@ -3108,6 +3128,11 @@ space-separated-tokens@^2.0.0: resolved "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.1.tgz#43193cec4fb858a2ce934b7f98b7f2c18107098b" integrity sha512-ekwEbFp5aqSPKaqeY1PGrlGQxPNaq+Cnx4+bE2D8sciBQrHpbwoBbawqTN2+6jPs9IdWxxiUcN0K2pkczD3zmw== +state-local@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/state-local/-/state-local-1.0.7.tgz#da50211d07f05748d53009bee46307a37db386d5" + integrity sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w== + streamsaver@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/streamsaver/-/streamsaver-2.0.6.tgz#869d2347dd70191e0ac888d52296956a8cba2ed9" From daee559db252287cbce7760fc49f75b02fde9073 Mon Sep 17 00:00:00 2001 From: Xhofe Date: Thu, 5 May 2022 16:22:38 +0800 Subject: [PATCH 4/5] fix: the way to `change to edit` --- src/pages/list/index.tsx | 11 -- src/pages/list/layout/files/contextmenu.tsx | 14 +- src/pages/list/preview/codeeditor.tsx | 181 ++++++++------------ src/pages/list/preview/markdown.tsx | 66 +++---- 4 files changed, 106 insertions(+), 166 deletions(-) diff --git a/src/pages/list/index.tsx b/src/pages/list/index.tsx index f266573..06ac247 100644 --- a/src/pages/list/index.tsx +++ b/src/pages/list/index.tsx @@ -78,9 +78,6 @@ const Do = (props: any) => { setMsg(res.message); if (res.code === 200) { if (res.data.type === "file") { - if (switchToEdit()) { - res.data.files[0].type = 10 - } setFiles(res.data.files); } else { if ( @@ -174,14 +171,6 @@ const Do = (props: any) => { } return false; }; - const switchToEdit = () => { - const query = new URLSearchParams(location.search); - const search = query.get("edit"); - if (search) { - return true - } - return false - } useEffect(() => { if(!switchToSearch()){ allRefresh(); diff --git a/src/pages/list/layout/files/contextmenu.tsx b/src/pages/list/layout/files/contextmenu.tsx index b414546..d9d691b 100644 --- a/src/pages/list/layout/files/contextmenu.tsx +++ b/src/pages/list/layout/files/contextmenu.tsx @@ -22,7 +22,6 @@ import { FcClock, FcRefresh, FcDownload, - FcEditImage, } from "react-icons/fc"; import { MdDeleteForever } from "react-icons/md"; import useFileUrl from "../../../../hooks/useFileUrl"; @@ -55,7 +54,7 @@ const ContextMenu = () => { loggedIn, aria2, } = useContext(IContext); - const history = useHistory() + const history = useHistory(); const menuTheme = useColorModeValue(theme.light, theme.dark); const toast = useToast(); const getFileUrl = useFileUrl(); @@ -129,17 +128,6 @@ const ContextMenu = () => { } > - { - const file = props as File - history.push(file.name + "?edit=true") - }}> - - - {t("Edit")} - - { setIsOpen({ ...isOpen, rename: true }); diff --git a/src/pages/list/preview/codeeditor.tsx b/src/pages/list/preview/codeeditor.tsx index eb7e8db..b8a0244 100644 --- a/src/pages/list/preview/codeeditor.tsx +++ b/src/pages/list/preview/codeeditor.tsx @@ -31,130 +31,85 @@ // } // }; -import Editor, { loader } from "@monaco-editor/react"; -import { Box, Select, Button, Flex, Spacer, useColorModeValue, useToast } from "@chakra-ui/react"; +import Editor from "@monaco-editor/react"; +import { + Box, + Select, + Button, + Flex, + Spacer, + useColorModeValue, + useToast, + Heading, +} from "@chakra-ui/react"; import React from "react"; import axios from "axios"; import { useLocation } from "react-router-dom"; import { useTranslation } from "react-i18next"; import useFileUrl from "../../../hooks/useFileUrl"; -import { FileProps, IContext } from "../context"; +import { FileProps } from "../context"; import request from "../../../utils/public"; -import { useHistory } from "react-router-dom"; -export const type = 10; +export const type = -1; export const exts = []; -const CodeEditor = ({ file, readme }: FileProps) => { - const theme = useColorModeValue("light", "dark"); - const { t } = useTranslation(); - const toast = useToast(); - const { pathname } = useLocation() +const CodeEditor = ({ file }: FileProps) => { + const theme = useColorModeValue("light", "dark"); + const { t } = useTranslation(); + const toast = useToast(); + const { pathname } = useLocation(); - const [content, setContent] = React.useState("") - const [ext, setExt] = React.useState("") + const [content, setContent] = React.useState(""); - const fileTypes: { [key: string]: string } = { - ts: "TypeScript", - js: "JavaScript", - css: "CSS", - less: "LESS", - scss: "SCSS", - json: "JSON", - html: "HTML", - dockerfile: "DOCKERFILE", - php: "PHP", - py: "Python", - cs: "C#", - go: "GO", - cpp: "C++", - h: "C++", - ps: "Powershell", - md: "Markdown", - diff: "Diff", - java: "Java", - xml: "XML", - vb: "VB", - coffee: "CoffeeScript", - bat: "Batch", - lua: "Lua", - ruby: "Ruby", - sass: "SASS", - r: "R", - obc: "Objective-C", - } + let link = useFileUrl(true)(file); + const getContent = () => { + axios.get(link).then(async (resp) => { + setContent(resp.data); + }); + }; - const fileopts = Array.from( - new Set(Object.keys(fileTypes).map((key) => {return fileTypes[key]})) - ).map((f, index) => - - ) + const handleSaveButton: React.MouseEventHandler = async () => { + const folder = pathname.substring(0, pathname.lastIndexOf("/") || 1); + const form = new FormData(); + form.append("files", new Blob([content as string]), file.name); + form.append("path", folder); + request + .post("upload", form, { + headers: { + "Content-Type": "multipart/form-data", + }, + }) + .then((resp) => { + const res = resp.data; + toast({ + title: t(res.message), + status: res.code === 200 ? "success" : "error", + duration: 3000, + isClosable: true, + }); + }); + }; + React.useEffect(() => { + getContent(); + }, []); - let link = useFileUrl(true)(file) - let fileext = file.name.split(".").pop() as string - const getContent = () => { - axios.get(link).then( - async (resp) => { - setExt(fileext) - setContent(resp.data) - } - ) - } + return ( + + setContent(value)} + /> + + + + + + ); +}; - const handelOptsChange: React.ChangeEventHandler = (event) => { - console.log(event.target.value) - setExt(event.target.value) - } - const handleSaveButton: React.MouseEventHandler = async (e) => { - const folder = pathname.substring(0, pathname.lastIndexOf("/") || 1) - const form = new FormData(); - form.append("files", new Blob([content as string]), file.name); - form.append("path", folder); - request.post("upload", form, { - headers: { - "Content-Type": "multipart/form-data", - } - }).then((resp) => { - const res = resp.data - toast({ - title: t(res.message), - status: res.code === 200 ? "success" : "error", - duration: 3000, - isClosable: true, - }); - }) - } - React.useEffect(() =>{ - getContent() - }, []) - - return ( - - - - {file.name} - - - - - setContent(value)} - /> - - - - - - ) -} - -export default CodeEditor \ No newline at end of file +export default CodeEditor; diff --git a/src/pages/list/preview/markdown.tsx b/src/pages/list/preview/markdown.tsx index 3378040..f8bd6fa 100644 --- a/src/pages/list/preview/markdown.tsx +++ b/src/pages/list/preview/markdown.tsx @@ -1,26 +1,24 @@ import React, { useContext, useEffect } from "react"; import { FileProps, IContext } from "../context"; import axios from "axios"; -import { Spinner, useColorModeValue } from "@chakra-ui/react"; +import { HStack, Spinner } from "@chakra-ui/react"; import { Box, Center } from "@chakra-ui/react"; -import { useTranslation } from "react-i18next"; import useFileUrl from "../../../hooks/useFileUrl"; import { FormControl, FormLabel, Switch } from "@chakra-ui/react"; import Markdown from "~/components/markdown"; +import CodeEditor from "./codeeditor"; // import jschardet from "jschardet"; export const type = 5; export const exts = []; const MarkdownPreview = ({ file, readme }: FileProps) => { - const theme = useColorModeValue("light", "dark"); const [content, setContent] = React.useState(""); const [srcDoc, setSrcDoc] = React.useState(""); - const { getSetting } = useContext(IContext); + const { getSetting,loggedIn } = useContext(IContext); let link = useFileUrl(true)(file); - const { i18n } = useTranslation(); const html = file.name.endsWith(".html"); - const [render, setRender] = React.useState(false); + const [show, setShow] = React.useState("preview"); const refresh = () => { if (readme) { if (file.type === -1) { @@ -29,19 +27,11 @@ const MarkdownPreview = ({ file, readme }: FileProps) => { } axios .get(link, { - // transformResponse: [ - // (data) => { - // return data; - // }, - // ], responseType: "blob", }) .then(async (resp) => { const blob = resp.data; let res = await blob.text(); - // const encoding = jschardet.detect(res).encoding; - // console.log(encoding); - // if (encoding === "windows-1252") { if (res.includes("�")) { const decoder = new TextDecoder("gbk"); res = decoder.decode(await blob.arrayBuffer()); @@ -67,21 +57,37 @@ const MarkdownPreview = ({ file, readme }: FileProps) => { if (content) { return ( - {html && ( - - - Render? - - { - setRender(!render); - }} - /> - - )} - {render ? ( + + {html && ( + + + Render? + + { + setShow(show === "render" ? "preview" : "render"); + }} + /> + + )} + {!readme && loggedIn && ( + + + Edit? + + { + setShow(show === "edit" ? "preview" : "edit"); + }} + /> + + )} + + {show === "render" ? ( + ) : show === "edit" ? ( + ) : ( {content} From 3f3722ecac873fb41e4b296c72e5dac0695180f6 Mon Sep 17 00:00:00 2001 From: Xhofe Date: Sat, 7 May 2022 16:22:18 +0800 Subject: [PATCH 5/5] fix: hide search icon while disable search --- src/pages/list/layout/search.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/list/layout/search.tsx b/src/pages/list/layout/search.tsx index f0f481c..90ac76d 100644 --- a/src/pages/list/layout/search.tsx +++ b/src/pages/list/layout/search.tsx @@ -25,12 +25,12 @@ const Search = (props: SearchProps) => { inputRef.current?.focus(); } const switchSearch = (e: KeyboardEvent) => { - if (["f", "F"].includes(e.key) && e.ctrlKey) { + if (["k", "K"].includes(e.key) && e.ctrlKey) { props.setIsSearch(!props.isSearch); e.preventDefault(); } }; - document.addEventListener("keydown", switchSearch); + if(getSetting("enable search") === "true"){document.addEventListener("keydown", switchSearch);} const cancelSearch = (e: MouseEvent) => { props.setIsSearch(false); }; @@ -40,7 +40,7 @@ const Search = (props: SearchProps) => { document.removeEventListener("click", cancelSearch); }; }, [props.isSearch]); - // if (!getSetting("enable search")) return null; + if (getSetting("enable search") !== "true") return null; return (