import { Action as LrcAction, ActionType as LrcActionType } from "../hooks/useLrc.js"; import { assignRepo, createRepo, getFils, getRepos, GistInfo, IGistFile, IGistRepo, Ratelimit, } from "../utils/gistapi.js"; import { appContext } from "./app.context.js"; import { AkariNotFound, AkariOangoLoading } from "./svg.img.js"; import { EditorSVG, GithubSVG, SynchronizerSVG } from "./svg.js"; import { toastPubSub } from "./toast.js"; const { useCallback, useContext, useEffect, useMemo, useState } = React; const newTokenUrl = "https://github.com/settings/tokens/new?scopes=gist&description=https://lrc-maker.github.io"; const disableCheck = { autoCapitalize: "none", autoComplete: "off", autoCorrect: "off", spellCheck: false, }; interface IGistProps { lrcDispatch: React.Dispatch; langName: string; } export const Gist: React.FC = ({ lrcDispatch, langName }) => { const { lang, trimOptions } = useContext(appContext); const [token, setToken] = useState(() => localStorage.getItem(LSK.token)); const [gistId, setGistId] = useState(() => localStorage.getItem(LSK.gistId)); const [gistIdList, setGistIdList] = useState(undefined); const [fileList, setFileList] = useState(() => JSON.parse(localStorage.getItem(LSK.gistFile)!)); const ratelimit: Ratelimit | null = useMemo(() => { return JSON.parse(sessionStorage.getItem(SSK.ratelimit)!); }, []); const onSubmitToken = useCallback((ev: React.FormEvent) => { ev.preventDefault(); const form = ev.target as HTMLFormElement; const tokenInput = form.elements.namedItem("token") as HTMLInputElement; const value = tokenInput.value; localStorage.setItem(LSK.token, value); setToken(value); }, []); const onCreateNewGist = useCallback(() => { createRepo() .then((json: IGistRepo) => { localStorage.setItem(LSK.gistId, json.id); setGistId(json.id); }) .catch((error: Error) => { toastPubSub.pub({ type: "warning", text: error.message, }); }); }, []); const onSubmitGistId = useCallback((ev: React.FormEvent) => { ev.preventDefault(); const form = ev.target as HTMLFormElement; const gistIdInput = form.elements.namedItem("gist-id") as HTMLInputElement; const value = gistIdInput.value; localStorage.setItem(LSK.gistId, value); setGistId(value); assignRepo().catch((error: Error) => { toastPubSub.pub({ type: "warning", text: error.message, }); }); }, []); useEffect(() => { if (gistId !== null || token === null) { return; } if (!("HTMLDataListElement" in window)) { return; } getRepos() .then((result) => { setGistIdList( result .filter((gist) => { return gist.description === GistInfo.description && GistInfo.fileName in gist.files; }) .map(({ id }) => id), ); }) .catch((error: Error) => { toastPubSub.pub({ type: "warning", text: error.message, }); }); }, [token, gistId]); useEffect(() => { if (gistId === null) { return; } getFils() .then((result) => { if (result === null) { return; } const files = Object.values(result.files).filter((file) => file.filename.endsWith(".lrc")); localStorage.setItem( LSK.gistFile, JSON.stringify(files, ["filename", "content", "truncated", "raw_url"]), ); setFileList(files); }) .catch((error: Error) => { toastPubSub.pub({ type: "warning", text: error.message, }); }); }, [gistId]); const onFileLoad = useCallback( (ev: React.MouseEvent) => { const target = ev.target as HTMLElement; if (!("key" in target.dataset)) { return; } const key = Number.parseInt(target.dataset.key!, 10); const file = fileList?.[key]; if (!file) { return; } if (file.truncated) { fetch(file.raw_url) .then((res) => res.text()) .then((text) => { lrcDispatch({ type: LrcActionType.parse, payload: { text, options: trimOptions }, }); }) .catch((error: Error) => { toastPubSub.pub({ type: "warning", text: error.message, }); }); } else { lrcDispatch({ type: LrcActionType.parse, payload: { text: file.content, options: trimOptions }, }); } }, [fileList, lrcDispatch, trimOptions], ); const onClear = useCallback(() => { setGistId(null); setToken(null); }, []); const RateLimit = useMemo(() => { if (ratelimit === null) { return false; } const RatelimitReset = new Intl.DateTimeFormat(langName, { year: "numeric", month: "short", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric", hour12: false, }).format(new Date(Number.parseInt(ratelimit["x-ratelimit-reset"], 10) * 1000)); return (

{"ratelimit-limit: "} {ratelimit["x-ratelimit-limit"]}

{"ratelimit-remaining: "} {ratelimit["x-ratelimit-remaining"]}

{"ratelimit-reset: "} {RatelimitReset}

); }, [ratelimit, langName]); const GistDetails: React.FC = useCallback(() => { if (gistId !== null && token !== null) { return (
{lang.gist.info}

{"Gist id: "} {gistId}

{RateLimit}
); } return null; }, [gistId, token, lang.gist.info, lang.gist.clearTokenAndGist, onClear, RateLimit]); const NewToken = useMemo(() => { if (token === null) { return (

{lang.gist.newTokenTip}

{lang.gist.newTokenButton}
); } }, [token, lang.gist.newTokenTip, lang.gist.newTokenButton, onSubmitToken]); const NewGistID = useMemo(() => { if (gistId === null) { const option = (id: string): JSX.Element => { return