From a6f4f6ff7738599dd25f74b8d3079c5e4f94d2ab Mon Sep 17 00:00:00 2001 From: Marcus Noble Date: Sun, 26 Dec 2021 11:48:56 +0000 Subject: [PATCH 1/5] Support bulk delete of user media Signed-off-by: Marcus Noble --- src/components/media.js | 39 +++++++++++ src/components/users.js | 152 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 189 insertions(+), 2 deletions(-) diff --git a/src/components/media.js b/src/components/media.js index a523fa2..604f19d 100644 --- a/src/components/media.js +++ b/src/components/media.js @@ -13,6 +13,7 @@ import { Toolbar, useCreate, useDelete, + useDeleteMany, useNotify, useRefresh, useTranslate, @@ -325,3 +326,41 @@ export const QuarantineMediaButton = props => { ); }; + +export const DeleteMediaBulkButton = ({ selectedIds }) => { + const classes = useStyles(false); + const notify = useNotify(); + const refresh = useRefresh(); + const [deleteMany, { loading }] = useDeleteMany("delete_media"); + + const handleSend = values => { + deleteMany( + { + type: "deleteMany", + resource: "users_media", + payload: { ids: selectedIds }, + }, + { + onSuccess: () => { + notify("resources.delete_media.action.send_success"); + refresh(); + }, + onFailure: () => + notify("resources.delete_media.action.send_failure", "error"), + } + ); + }; + + return ( + + + + ); +}; diff --git a/src/components/users.js b/src/components/users.js index 83694f9..48ba73c 100644 --- a/src/components/users.js +++ b/src/components/users.js @@ -46,12 +46,19 @@ import { TopToolbar, sanitizeListRestProps, NumberField, + BulkActionsToolbar, + DatagridHeaderCell, + useListContext, + FunctionField, + ImageField, } from "react-admin"; import { Link } from "react-router-dom"; import { ServerNoticeButton, ServerNoticeBulkButton } from "./ServerNotices"; import { DeviceRemoveButton } from "./devices"; -import { ProtectMediaButton, QuarantineMediaButton } from "./media"; +import { ProtectMediaButton, QuarantineMediaButton, DeleteMediaBulkButton } from "./media"; import { makeStyles } from "@material-ui/core/styles"; +import { TableHead, TableRow, TableCell, Checkbox } from '@material-ui/core'; +import classnames from 'classnames'; const redirect = () => { return { @@ -313,6 +320,128 @@ const UserTitle = ({ record }) => { ); }; + + +const UserMediaDatagridHeader = (props) => { + const { + children, + classes, + className, + hasExpand = false, + hasBulkActions = false, + isRowSelectable, + } = props; + const translate = useTranslate(); + const { + currentSort, + data, + ids, + onSelect, + selectedIds, + setSort, + } = useListContext(props); + + const updateSortCallback = React.useCallback( + event => { + event.stopPropagation(); + const newField = event.currentTarget.dataset.field; + const newOrder = + currentSort.field === newField + ? currentSort.order === 'ASC' + ? 'DESC' + : 'ASC' + : event.currentTarget.dataset.order; + + setSort(newField, newOrder); + }, + [currentSort.field, currentSort.order, setSort] + ); + + const updateSort = setSort ? updateSortCallback : null; + + const handleSelectAll = React.useCallback( + event => { + onSelect( + event.target.checked + ? ids + .filter(id => + isRowSelectable ? isRowSelectable(data[id]) : true + ) + .concat(selectedIds.filter(id => !ids.includes(id))) + : [] + ); + }, + [data, ids, onSelect, isRowSelectable, selectedIds] + ); + + const selectableIds = isRowSelectable + ? ids.filter(id => isRowSelectable(data[id])) + : ids; + + + return ( + + + + + + + + + + {hasExpand && ( + + )} + {hasBulkActions && selectedIds && ( + + 0 && + selectableIds.length > 0 && + selectableIds.every(id => + selectedIds.includes(id) + ) + } + onChange={handleSelectAll} + /> + + )} + {React.Children.map(children, (field, index) => + React.isValidElement(field) ? ( + + ) : null + )} + + + ); +} + export const UserEdit = props => { const classes = useStyles(); const translate = useTranslate(); @@ -472,7 +601,26 @@ export const UserEdit = props => { perPage={50} sort={{ field: "created_ts", order: "DESC" }} > - + } + > + { + let data = { + title: record.upload_name, + imgURL: `${localStorage.getItem("base_url")}/_matrix/media/v1/thumbnail/${localStorage.getItem("home_server")}/${record.media_id}?width=50&height=50&method=crop`, + downloadURL: `${localStorage.getItem("base_url")}/_matrix/media/r0/download/${localStorage.getItem("home_server")}/${record.media_id}`, + } + if (record.media_type.startsWith("image")) { + return window.open(data.downloadURL)} /> + } else { + return "Preview unavailable"; + } + }} + /> Date: Sun, 26 Dec 2021 12:11:15 +0000 Subject: [PATCH 2/5] Fixed lint Signed-off-by: Marcus Noble --- src/components/users.js | 204 ++++++++++++++++++++-------------------- 1 file changed, 102 insertions(+), 102 deletions(-) diff --git a/src/components/users.js b/src/components/users.js index 48ba73c..e056b0c 100644 --- a/src/components/users.js +++ b/src/components/users.js @@ -55,10 +55,14 @@ import { import { Link } from "react-router-dom"; import { ServerNoticeButton, ServerNoticeBulkButton } from "./ServerNotices"; import { DeviceRemoveButton } from "./devices"; -import { ProtectMediaButton, QuarantineMediaButton, DeleteMediaBulkButton } from "./media"; +import { + ProtectMediaButton, + QuarantineMediaButton, + DeleteMediaBulkButton, +} from "./media"; import { makeStyles } from "@material-ui/core/styles"; -import { TableHead, TableRow, TableCell, Checkbox } from '@material-ui/core'; -import classnames from 'classnames'; +import { TableHead, TableRow, TableCell, Checkbox } from "@material-ui/core"; +import classnames from "classnames"; const redirect = () => { return { @@ -321,8 +325,7 @@ const UserTitle = ({ record }) => { ); }; - -const UserMediaDatagridHeader = (props) => { +const UserMediaDatagridHeader = props => { const { children, classes, @@ -332,115 +335,98 @@ const UserMediaDatagridHeader = (props) => { isRowSelectable, } = props; const translate = useTranslate(); - const { - currentSort, - data, - ids, - onSelect, - selectedIds, - setSort, - } = useListContext(props); + const { currentSort, data, ids, onSelect, selectedIds, setSort } = + useListContext(props); const updateSortCallback = React.useCallback( - event => { - event.stopPropagation(); - const newField = event.currentTarget.dataset.field; - const newOrder = - currentSort.field === newField - ? currentSort.order === 'ASC' - ? 'DESC' - : 'ASC' - : event.currentTarget.dataset.order; + event => { + event.stopPropagation(); + const newField = event.currentTarget.dataset.field; + const newOrder = + currentSort.field === newField + ? currentSort.order === "ASC" + ? "DESC" + : "ASC" + : event.currentTarget.dataset.order; - setSort(newField, newOrder); - }, - [currentSort.field, currentSort.order, setSort] + setSort(newField, newOrder); + }, + [currentSort.field, currentSort.order, setSort] ); const updateSort = setSort ? updateSortCallback : null; const handleSelectAll = React.useCallback( - event => { - onSelect( - event.target.checked - ? ids - .filter(id => - isRowSelectable ? isRowSelectable(data[id]) : true - ) - .concat(selectedIds.filter(id => !ids.includes(id))) - : [] - ); - }, - [data, ids, onSelect, isRowSelectable, selectedIds] + event => { + onSelect( + event.target.checked + ? ids + .filter(id => + isRowSelectable ? isRowSelectable(data[id]) : true + ) + .concat(selectedIds.filter(id => !ids.includes(id))) + : [] + ); + }, + [data, ids, onSelect, isRowSelectable, selectedIds] ); const selectableIds = isRowSelectable - ? ids.filter(id => isRowSelectable(data[id])) - : ids; - + ? ids.filter(id => isRowSelectable(data[id])) + : ids; return ( - - - - - + + + + + + + + + {hasExpand && ( + + )} + {hasBulkActions && selectedIds && ( + + 0 && + selectableIds.length > 0 && + selectableIds.every(id => selectedIds.includes(id)) + } + onChange={handleSelectAll} + /> - - - {hasExpand && ( - - )} - {hasBulkActions && selectedIds && ( - - 0 && - selectableIds.length > 0 && - selectableIds.every(id => - selectedIds.includes(id) - ) - } - onChange={handleSelectAll} - /> - - )} - {React.Children.map(children, (field, index) => - React.isValidElement(field) ? ( - - ) : null - )} - + )} + {React.Children.map(children, (field, index) => + React.isValidElement(field) ? ( + + ) : null + )} + ); -} +}; export const UserEdit = props => { const classes = useStyles(); @@ -604,18 +590,32 @@ export const UserEdit = props => { } + header={} > { let data = { title: record.upload_name, - imgURL: `${localStorage.getItem("base_url")}/_matrix/media/v1/thumbnail/${localStorage.getItem("home_server")}/${record.media_id}?width=50&height=50&method=crop`, - downloadURL: `${localStorage.getItem("base_url")}/_matrix/media/r0/download/${localStorage.getItem("home_server")}/${record.media_id}`, - } + imgURL: `${localStorage.getItem( + "base_url" + )}/_matrix/media/v1/thumbnail/${localStorage.getItem( + "home_server" + )}/${record.media_id}?width=50&height=50&method=crop`, + downloadURL: `${localStorage.getItem( + "base_url" + )}/_matrix/media/r0/download/${localStorage.getItem( + "home_server" + )}/${record.media_id}`, + }; if (record.media_type.startsWith("image")) { - return window.open(data.downloadURL)} /> + return ( + window.open(data.downloadURL)} + /> + ); } else { return "Preview unavailable"; } From ad0e73284c3439a4b0b0cc9e08baf81ce4233ab7 Mon Sep 17 00:00:00 2001 From: Marcus Noble Date: Tue, 28 Dec 2021 11:22:05 +0000 Subject: [PATCH 3/5] Update src/components/media.js Co-authored-by: Dirk Klimpel <5740567+dklimpel@users.noreply.github.com> --- src/components/media.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/media.js b/src/components/media.js index 604f19d..e7e933b 100644 --- a/src/components/media.js +++ b/src/components/media.js @@ -354,7 +354,7 @@ export const DeleteMediaBulkButton = ({ selectedIds }) => { return (