import copy from "copy-to-clipboard"; import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import * as utils from "../helpers/utils"; import useLoading from "../hooks/useLoading"; import { resourceService } from "../services"; import { useAppSelector } from "../store"; import Icon from "./Icon"; import toastHelper from "./Toast"; import Dropdown from "./common/Dropdown"; import { generateDialog } from "./Dialog"; import { showCommonDialog } from "./Dialog/CommonDialog"; import showPreviewImageDialog from "./PreviewImageDialog"; import showChangeResourceFilenameDialog from "./ChangeResourceFilenameDialog"; import "../less/resources-dialog.less"; type Props = DialogProps; interface State { isUploadingResource: boolean; } const ResourcesDialog: React.FC = (props: Props) => { const { destroy } = props; const { t } = useTranslation(); const loadingState = useLoading(); const { resources } = useAppSelector((state) => state.resource); const [state, setState] = useState({ isUploadingResource: false, }); useEffect(() => { resourceService .fetchResourceList() .catch((error) => { console.error(error); toastHelper.error(error.response.data.message); }) .finally(() => { loadingState.setFinish(); }); }, []); const handleUploadFileBtnClick = async () => { if (state.isUploadingResource) { return; } const inputEl = document.createElement("input"); inputEl.style.position = "fixed"; inputEl.style.top = "-100vh"; inputEl.style.left = "-100vw"; document.body.appendChild(inputEl); inputEl.type = "file"; inputEl.multiple = true; inputEl.accept = "*"; inputEl.onchange = async () => { if (!inputEl.files || inputEl.files.length === 0) { return; } setState({ ...state, isUploadingResource: true, }); for (const file of inputEl.files) { try { await resourceService.upload(file); } catch (error: any) { console.error(error); toastHelper.error(error.response.data.message); } finally { setState({ ...state, isUploadingResource: false, }); } } document.body.removeChild(inputEl); }; inputEl.click(); }; const getResourceUrl = useCallback((resource: Resource) => { return `${window.location.origin}/o/r/${resource.id}/${resource.filename}`; }, []); const handlePreviewBtnClick = (resource: Resource) => { const resourceUrl = getResourceUrl(resource); if (resource.type.startsWith("image")) { showPreviewImageDialog( resources.filter((r) => r.type.startsWith("image")).map((r) => getResourceUrl(r)), resources.findIndex((r) => r.id === resource.id) ); } else { window.open(resourceUrl); } }; const handleRenameBtnClick = (resource: Resource) => { showChangeResourceFilenameDialog(resource.id, resource.filename); }; const handleCopyResourceLinkBtnClick = (resource: Resource) => { copy(`${window.location.origin}/o/r/${resource.id}/${resource.filename}`); toastHelper.success("Succeed to copy resource link to clipboard"); }; const handleDeleteUnusedResourcesBtnClick = () => { let warningText = t("resources.warning-text-unused"); const unusedResources = resources.filter((resource) => { if (resource.linkedMemoAmount === 0) { warningText = warningText + `\n- ${resource.filename}`; return true; } return false; }); if (unusedResources.length === 0) { toastHelper.success(t("resources.no-unused-resources")); return; } showCommonDialog({ title: t("resources.delete-resource"), content: warningText, style: "warning", onConfirm: async () => { for (const resource of unusedResources) { await resourceService.deleteResourceById(resource.id); } }, }); }; const handleDeleteResourceBtnClick = (resource: Resource) => { let warningText = t("resources.warning-text"); if (resource.linkedMemoAmount > 0) { warningText = warningText + `\n${t("resources.linked-amount")}: ${resource.linkedMemoAmount}`; } showCommonDialog({ title: t("resources.delete-resource"), content: warningText, style: "warning", onConfirm: async () => { await resourceService.deleteResourceById(resource.id); }, }); }; const handleResourceNameOrTypeMouseEnter = useCallback((event: React.MouseEvent, nameOrType: string) => { const tempDiv = document.createElement("div"); tempDiv.className = "usage-detail-container pop-up"; const bounding = utils.getElementBounding(event.target as HTMLElement); tempDiv.style.left = bounding.left + "px"; tempDiv.style.top = bounding.top - 2 + "px"; tempDiv.innerHTML = `${nameOrType}`; document.body.appendChild(tempDiv); }, []); const handleResourceNameOrTypeMouseLeave = useCallback(() => { document.body.querySelectorAll("div.usage-detail-container.pop-up").forEach((node) => node.remove()); }, []); return ( <>

🌄 {t("sidebar.resources")}

handleUploadFileBtnClick()}> {t("resources.upload")}
{t("resources.clear-unused-resources")}
{loadingState.isLoading ? (

{t("resources.fetching-data")}

) : (
ID NAME
{resources.length === 0 ? (

{t("resources.no-resources")}

) : ( resources.map((resource) => (
{resource.id} handleResourceNameOrTypeMouseEnter(e, resource.filename)} onMouseLeave={handleResourceNameOrTypeMouseLeave} > {resource.filename}
} />
)) )}
)}
); }; export default function showResourcesDialog() { generateDialog( { className: "resources-dialog", }, ResourcesDialog, {} ); }