import BlockIcon from "@mui/icons-material/Block"; import IconCancel from "@mui/icons-material/Cancel"; import ClearIcon from "@mui/icons-material/Clear"; import DeleteSweepIcon from "@mui/icons-material/DeleteSweep"; import DownloadIcon from "@mui/icons-material/Download"; import DownloadingIcon from "@mui/icons-material/Downloading"; import FileOpenIcon from "@mui/icons-material/FileOpen"; import LockIcon from "@mui/icons-material/Lock"; import LockOpenIcon from "@mui/icons-material/LockOpen"; import { Grid2 as Grid, Box, Dialog, DialogContent, DialogContentText, DialogTitle, Tooltip, Link, } from "@mui/material"; import { alpha, useTheme } from "@mui/material/styles"; import { useMutation } from "@tanstack/react-query"; import { get } from "lodash"; import { useState } from "react"; import { BooleanInput, Button, ButtonProps, DateTimeInput, NumberInput, SaveButton, SimpleForm, Toolbar, ToolbarProps, useCreate, useDataProvider, useDelete, useNotify, useRecordContext, useRefresh, useTranslate, } from "react-admin"; import { DeleteMediaParams, SynapseDataProvider } from "../synapse/dataProvider"; import { dateParser } from "../utils/date"; import decodeURLComponent from "../utils/decodeURLComponent"; import { fetchAuthenticatedMedia } from "../utils/fetchMedia"; const DeleteMediaDialog = ({ open, onClose, onSubmit }) => { const translate = useTranslate(); const DeleteMediaToolbar = (props: ToolbarProps) => ( } /> ); return ( {translate("delete_media.action.send")} {translate("delete_media.helper.send")} } onSubmit={onSubmit}> ); }; export const DeleteMediaButton = (props: ButtonProps) => { const theme = useTheme(); const [open, setOpen] = useState(false); const notify = useNotify(); const translate = useTranslate(); const dataProvider = useDataProvider(); const { mutate: deleteMedia, isPending } = useMutation({ mutationFn: (values: DeleteMediaParams) => dataProvider.deleteMedia(values), onSuccess: () => { notify("delete_media.action.send_success"); closeDialog(); }, onError: () => { notify("delete_media.action.send_failure", { type: "error", }); }, }); const openDialog = () => setOpen(true); const closeDialog = () => setOpen(false); return ( <> ); }; const PurgeRemoteMediaDialog = ({ open, onClose, onSubmit }) => { const translate = useTranslate(); const PurgeRemoteMediaToolbar = (props: ToolbarProps) => ( } /> ); return ( {translate("purge_remote_media.action.send")} {translate("purge_remote_media.helper.send")} } onSubmit={onSubmit}> ); }; export const PurgeRemoteMediaButton = (props: ButtonProps) => { const theme = useTheme(); const [open, setOpen] = useState(false); const notify = useNotify(); const translate = useTranslate(); const dataProvider = useDataProvider(); const { mutate: purgeRemoteMedia, isPending } = useMutation({ mutationFn: (values: DeleteMediaParams) => dataProvider.purgeRemoteMedia(values), onSuccess: () => { notify("purge_remote_media.action.send_success"); closeDialog(); }, onError: () => { notify("purge_remote_media.action.send_failure", { type: "error", }); }, }); const openDialog = () => setOpen(true); const closeDialog = () => setOpen(false); return ( <> ); }; export const ProtectMediaButton = (props: ButtonProps) => { const record = useRecordContext(); const translate = useTranslate(); const refresh = useRefresh(); const notify = useNotify(); const [create, { isLoading }] = useCreate(); const [deleteOne] = useDelete(); if (!record) return null; const handleProtect = () => { create( "protect_media", { data: record }, { onSuccess: () => { notify("resources.protect_media.action.send_success"); refresh(); }, onError: () => notify("resources.protect_media.action.send_failure", { type: "error", }), } ); }; const handleUnprotect = () => { deleteOne( "protect_media", { id: record.id }, { onSuccess: () => { notify("resources.protect_media.action.send_success"); refresh(); }, onError: () => notify("resources.protect_media.action.send_failure", { type: "error", }), } ); }; return ( /* Wrapping Tooltip with
https://github.com/marmelab/react-admin/issues/4349#issuecomment-578594735 */ <> {record.quarantined_by && (
{/* Button instead BooleanField for consistent appearance and position in the column */}
)} {record.safe_from_quarantine && (
)} {!record.safe_from_quarantine && !record.quarantined_by && (
)} ); }; export const QuarantineMediaButton = (props: ButtonProps) => { const record = useRecordContext(); const translate = useTranslate(); const refresh = useRefresh(); const notify = useNotify(); const [create, { isLoading }] = useCreate(); const [deleteOne] = useDelete(); if (!record) return null; const handleQuarantaine = () => { create( "quarantine_media", { data: record }, { onSuccess: () => { notify("resources.quarantine_media.action.send_success"); refresh(); }, onError: () => notify("resources.quarantine_media.action.send_failure", { type: "error", }), } ); }; const handleRemoveQuarantaine = () => { deleteOne( "quarantine_media", { id: record.id, previousData: record }, { onSuccess: () => { notify("resources.quarantine_media.action.send_success"); refresh(); }, onError: () => notify("resources.quarantine_media.action.send_failure", { type: "error", }), } ); }; return ( <> {record.safe_from_quarantine && (
)} {record.quarantined_by && (
)} {!record.safe_from_quarantine && !record.quarantined_by && (
)} ); }; export const ViewMediaButton = ({ mxcURL, label, uploadName, mimetype }) => { const translate = useTranslate(); const [loading, setLoading] = useState(false); const notify = useNotify(); const isImage = mimetype && mimetype.startsWith("image/"); const openFileInNewTab = (blobURL: string) => { const anchorElement = document.createElement("a"); anchorElement.href = blobURL; anchorElement.target = "_blank"; document.body.appendChild(anchorElement); anchorElement.click(); document.body.removeChild(anchorElement); setTimeout(() => URL.revokeObjectURL(blobURL), 10); }; const downloadFile = async (blobURL: string) => { console.log("downloadFile", blobURL, uploadName); const anchorElement = document.createElement("a"); anchorElement.href = blobURL; anchorElement.download = uploadName; document.body.appendChild(anchorElement); anchorElement.click(); document.body.removeChild(anchorElement); setTimeout(() => URL.revokeObjectURL(blobURL), 10); }; const handleFile = async (preview: boolean) => { setLoading(true); const response = await fetchAuthenticatedMedia(mxcURL, "original"); if (response.ok) { const blob = await response.blob(); const blobURL = URL.createObjectURL(blob); if (preview) { openFileInNewTab(blobURL); } else { downloadFile(blobURL); } } else { const body = await response.json(); notify("resources.room_media.action.error", { messageArgs: { errcode: body.errcode, errstatus: response.status, error: body.error, }, type: "error", }); } setLoading(false); }; return ( <> {isImage && ( )} {label} ); }; export const MediaIDField = ({ source }) => { const translate = useTranslate(); const record = useRecordContext(); if (!record) { return null; } const homeserver = localStorage.getItem("home_server"); const mediaID = get(record, source)?.toString(); if (!mediaID) { return null; } let uploadName = mediaID; if (get(record, "upload_name")) { uploadName = decodeURLComponent(get(record, "upload_name")?.toString()); } let mxcURL = mediaID; if (!mediaID.startsWith(`mxc://${homeserver}`)) { // this is user's media, where mediaID doesn't have the mxc://home_server/ prefix as it has in the rooms mxcURL = `mxc://${homeserver}/${mediaID}`; } return ( ); }; export const ReportMediaContent = ({ source }) => { const translate = useTranslate(); const record = useRecordContext(); if (!record) { return null; } const mxcURL = get(record, source)?.toString(); if (!mxcURL) { return null; } let uploadName = ""; if (get(record, "event_json.content.body")) { uploadName = decodeURLComponent(get(record, "event_json.content.body")?.toString()); } return ( ); };