import { Action as LrcAction, ActionType as LrcActionType } from "../hooks/useLrc.js"; import { State as LrcState, stringify } from "../lrc-parser.js"; import { createFile } from "../utils/gistapi.js"; import { lrcFileName } from "../utils/lrc-file-name.js"; import { appContext } from "./app.context.js"; import { CloudUploadSVG, CopySVG, DownloadSVG, OpenFileSVG, UtilitySVG } from "./svg.js"; import { toastPubSub } from "./toast.js"; const { useCallback, useContext, useEffect, useMemo, useRef, useState } = React; const disableCheck = { autoCapitalize: "none", autoComplete: "off", autoCorrect: "off", spellCheck: false, }; type HTMLInputLikeElement = HTMLInputElement & HTMLTextAreaElement; type UseDefaultValue> = ( defaultValue: string, ref?: T, ) => { defaultValue: string; ref: T }; const useDefaultValue: UseDefaultValue = (defaultValue, ref) => { const or = (a: T, b: K): NonNullable | K => a ?? b; const $ref = or(ref, useRef(null)); useEffect(() => { if ($ref.current) { $ref.current.value = defaultValue; } }, [defaultValue, $ref]); return { ref: $ref, defaultValue }; }; export const Eidtor: React.FC<{ lrcState: LrcState; lrcDispatch: React.Dispatch; }> = ({ lrcState, lrcDispatch }) => { const { prefState, lang, trimOptions } = useContext(appContext); const parse = useCallback( (ev: React.FocusEvent) => { lrcDispatch({ type: LrcActionType.parse, payload: { text: ev.target.value, options: trimOptions }, }); }, [lrcDispatch, trimOptions], ); const setInfo = useCallback( (ev: React.FocusEvent) => { const { name, value } = ev.target; lrcDispatch({ type: LrcActionType.info, payload: { name, value }, }); }, [lrcDispatch], ); const text = stringify(lrcState, prefState); const details = useRef(null); const onDetailsToggle = useCallback(() => { sessionStorage.setItem(SSK.editorDetailsOpen, details.current!.open.toString()); }, []); const detailsOpened = useMemo(() => { return sessionStorage.getItem(SSK.editorDetailsOpen) !== "false"; }, []); const textarea = useRef(null); const [href, setHref] = useState(undefined); const onDownloadClick = useCallback(() => { setHref((url) => { if (url) { URL.revokeObjectURL(url); } return URL.createObjectURL( new Blob([textarea.current!.value], { type: "text/plain;charset=UTF-8", }), ); }); }, []); const onTextFileUpload = useCallback( (ev: React.ChangeEvent) => { if (ev.target.files === null || ev.target.files.length === 0) { return; } const fileReader = new FileReader(); fileReader.addEventListener("load", () => { lrcDispatch({ type: LrcActionType.parse, payload: { text: fileReader.result as string, options: trimOptions }, }); }); fileReader.readAsText(ev.target.files[0], "UTF-8"); }, [lrcDispatch, trimOptions], ); const onCopyClick = useCallback(() => { textarea.current?.select(); document.execCommand("copy"); }, []); const downloadName = useMemo(() => lrcFileName(lrcState.info), [lrcState.info]); const canSaveToGist = useMemo(() => { return localStorage.getItem(LSK.token) !== null && localStorage.getItem(LSK.gistId) !== null; }, []); const onGistSave = useCallback(() => { setTimeout(() => { const name = prompt(lang.editor.saveFileName, downloadName); if (name) { createFile(name, textarea.current!.value).catch((error: Error) => { toastPubSub.pub({ type: "warning", text: error.message, }); }); } }, 500); }, [downloadName, lang]); return (
{lang.editor.metaInfo}