Authenticated Media Support (#51)
* Fix AvatarField to work with authenticated media * Fix ViewMediaButton to work for user's media tab and reported events * remove console.log * cleanup AvatarField * use correct thumbnail size * fix AvatarField.test * ignore postgres data for watchman * fix new avatar preview * watchman should ignore testdata completely, instead of specific subdirs * update README * change user's media icon in sidebar - use the same icon as the media tab * Add preview for user media files if mimeType is image/* * Add new line in user media Dialog
This commit is contained in:
committed by
GitHub
parent
470f1b5455
commit
a79c3597d6
@@ -1,6 +1,7 @@
|
||||
import { get } from "lodash";
|
||||
import { useState } from "react";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import BlockIcon from "@mui/icons-material/Block";
|
||||
import IconCancel from "@mui/icons-material/Cancel";
|
||||
import ClearIcon from "@mui/icons-material/Clear";
|
||||
@@ -8,7 +9,10 @@ import DeleteSweepIcon from "@mui/icons-material/DeleteSweep";
|
||||
import FileOpenIcon from "@mui/icons-material/FileOpen";
|
||||
import LockIcon from "@mui/icons-material/Lock";
|
||||
import LockOpenIcon from "@mui/icons-material/LockOpen";
|
||||
import { Box, Dialog, DialogContent, DialogContentText, DialogTitle, Tooltip } from "@mui/material";
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import ZoomInIcon from '@mui/icons-material/ZoomIn';
|
||||
import { Box, Dialog, DialogContent, DialogContentText, DialogTitle, Tooltip, Link } from "@mui/material";
|
||||
import { alpha, useTheme } from "@mui/material/styles";
|
||||
import {
|
||||
BooleanInput,
|
||||
@@ -29,12 +33,11 @@ import {
|
||||
useTranslate,
|
||||
} from "react-admin";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { dateParser } from "./date";
|
||||
import { DeleteMediaParams, SynapseDataProvider } from "../synapse/dataProvider";
|
||||
import { getMediaUrl } from "../synapse/synapse";
|
||||
import storage from "../storage";
|
||||
import { fetchAuthenticatedMedia } from "../utils/fetchMedia";
|
||||
|
||||
const DeleteMediaDialog = ({ open, onClose, onSubmit }) => {
|
||||
const translate = useTranslate();
|
||||
@@ -311,48 +314,125 @@ export const QuarantineMediaButton = (props: ButtonProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const ViewMediaButton = ({ media_id, label }) => {
|
||||
export const ViewMediaButton = ({ mxcURL, uploadName, label }) => {
|
||||
const translate = useTranslate();
|
||||
const url = getMediaUrl(media_id);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [blobURL, setBlobURL] = useState("");
|
||||
|
||||
const handleOpen = () => setOpen(true);
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
if (blobURL) {
|
||||
URL.revokeObjectURL(blobURL);
|
||||
}
|
||||
};
|
||||
|
||||
const forceDownload = (url: string, filename: string) => {
|
||||
const anchorElement = document.createElement("a");
|
||||
anchorElement.href = url;
|
||||
anchorElement.download = filename;
|
||||
document.body.appendChild(anchorElement);
|
||||
anchorElement.click();
|
||||
document.body.removeChild(anchorElement);
|
||||
URL.revokeObjectURL(blobURL);
|
||||
};
|
||||
|
||||
const handleFile = async () => {
|
||||
const response = await fetchAuthenticatedMedia(mxcURL, "original");
|
||||
const blob = await response.blob();
|
||||
const blobURL = URL.createObjectURL(blob);
|
||||
setBlobURL(blobURL);
|
||||
|
||||
const mimeType = blob.type;
|
||||
if (!mimeType.startsWith("image/")) {
|
||||
forceDownload(blobURL, uploadName);
|
||||
} else {
|
||||
handleOpen();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box style={{ whiteSpace: "pre" }}>
|
||||
<Tooltip title={translate("resources.users_media.action.open")}>
|
||||
<span>
|
||||
<>
|
||||
<Box style={{ whiteSpace: "pre" }}>
|
||||
<Tooltip title={translate("resources.users_media.action.open")}>
|
||||
<span>
|
||||
<Button
|
||||
component={Link}
|
||||
to={url}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
style={{ minWidth: 0, paddingLeft: 0, paddingRight: 0 }}
|
||||
onClick={() => handleFile()}
|
||||
style={{ minWidth: 0, paddingLeft: 0, paddingRight: 0 }}
|
||||
>
|
||||
<FileOpenIcon />
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
{label}
|
||||
</Box>
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
aria-labelledby="image-modal-title"
|
||||
aria-describedby="image-modal-description"
|
||||
style={{ maxWidth: "100%", maxHeight: "100%" }}
|
||||
>
|
||||
<DialogTitle id="image-modal-title">
|
||||
<Typography>{uploadName}</Typography>
|
||||
<IconButton
|
||||
aria-label="close"
|
||||
onClick={handleClose}
|
||||
sx={(theme) => ({
|
||||
position: 'absolute',
|
||||
right: 8,
|
||||
top: 8,
|
||||
color: theme.palette.grey[500],
|
||||
})}
|
||||
>
|
||||
<FileOpenIcon />
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
{label}
|
||||
</Box>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<Link href={blobURL} target="_blank">
|
||||
<img src={blobURL} alt={uploadName}
|
||||
style={{ maxWidth: "100%", maxHeight: "/calc(100vh - 64px)", objectFit: "contain" }}
|
||||
/>
|
||||
<br />
|
||||
<ZoomInIcon />
|
||||
</Link>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const MediaIDField = ({ source }) => {
|
||||
const record = useRecordContext();
|
||||
if (!record) {
|
||||
return null;
|
||||
}
|
||||
const homeserver = storage.getItem("home_server");
|
||||
const record = useRecordContext();
|
||||
if (!record) return null;
|
||||
|
||||
const src = get(record, source)?.toString();
|
||||
if (!src) return null;
|
||||
const mediaID = get(record, source)?.toString();
|
||||
if (!mediaID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <ViewMediaButton media_id={`${homeserver}/${src}`} label={src} />;
|
||||
const mxcURL = `mxc://${homeserver}/${mediaID}`;
|
||||
const uploadName = decodeURIComponent(get(record, "upload_name")?.toString());
|
||||
|
||||
return <ViewMediaButton mxcURL={mxcURL} uploadName={uploadName} label={mediaID} />;
|
||||
};
|
||||
|
||||
export const MXCField = ({ source }) => {
|
||||
export const ReportMediaContent = ({ source }) => {
|
||||
const record = useRecordContext();
|
||||
if (!record) return null;
|
||||
if (!record) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const src = get(record, source)?.toString();
|
||||
if (!src) return null;
|
||||
const mxcURL = get(record, source)?.toString();
|
||||
if (!mxcURL) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const media_id = src.replace("mxc://", "");
|
||||
const uploadName = decodeURIComponent(record.event_json.content.body);
|
||||
|
||||
return <ViewMediaButton media_id={media_id} label={src} />;
|
||||
return <ViewMediaButton mxcURL={mxcURL} uploadName={uploadName} label={mxcURL} />;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user