diff --git a/frontend/src/api/api.ts b/frontend/src/api/api.ts index d7fb7c17..cb145a04 100644 --- a/frontend/src/api/api.ts +++ b/frontend/src/api/api.ts @@ -587,6 +587,23 @@ export function sendDeleteShare(id: string): ThunkResponse { }; } +export function sendDeleteShares(ids: string[]): ThunkResponse { + return async (dispatch, _getState) => { + return await dispatch( + send( + "/share", + { + method: "DELETE", + data: { ids }, + }, + { + ...defaultOpts, + }, + ), + ); + }; +} + export function getShareInfo( id: string, password?: string, diff --git a/frontend/src/component/Pages/Shares/ShareCard.tsx b/frontend/src/component/Pages/Shares/ShareCard.tsx index 194d0aed..25dc3709 100644 --- a/frontend/src/component/Pages/Shares/ShareCard.tsx +++ b/frontend/src/component/Pages/Shares/ShareCard.tsx @@ -1,5 +1,6 @@ import { Box, + Checkbox, Chip, Grid, ListItemIcon, @@ -42,6 +43,9 @@ export interface ShareCardProps { onLoad?: () => void; loading?: boolean; onShareDeleted: (id: string) => void; + selecting?: boolean; + selected?: boolean; + onToggleSelect?: (id: string) => void; } interface ActionMenuProps extends MenuProps { @@ -127,7 +131,7 @@ const ActionMenu = ({ share, onShareDeleted, onClose, ...rest }: ActionMenuProps ); }; -const ShareCard = ({ share, onShareDeleted, onLoad, loading }: ShareCardProps) => { +const ShareCard = ({ share, onShareDeleted, onLoad, loading, selecting, selected, onToggleSelect }: ShareCardProps) => { const { t } = useTranslation(); const navigate = useNavigate(); const { ref, inView } = useInView({ @@ -162,18 +166,27 @@ const ShareCard = ({ share, onShareDeleted, onLoad, loading }: ShareCardProps) = expanded={false} onContextMenu={(e) => { e.preventDefault(); + if (selecting) { + return; + } if (share?.owner?.id === user?.user.id) { popupState.open(e); } }} sx={{ p: 0, minHeight: 0, width: "100%", textAlign: "left" }} - {...(share?.owner?.id != user?.user.id + {...(selecting && share?.owner?.id == user?.user.id ? { onClick: () => { - window.open(share?.url ?? "#", "_blank"); + share && onToggleSelect?.(share.id); }, } - : bindTrigger(popupState))} + : share?.owner?.id != user?.user.id + ? { + onClick: () => { + window.open(share?.url ?? "#", "_blank"); + }, + } + : bindTrigger(popupState))} > - {share ? ( + {selecting ? ( + + ) : share ? ( { const { t } = useTranslation(); const dispatch = useAppDispatch(); + const { enqueueSnackbar } = useSnackbar(); const [nextPageToken, setNextPageToken] = useState(""); const [shares, setShares] = useState([]); const [loading, setLoading] = useState(false); const [orderDirection, setOrderDirection] = useState("desc"); + const [selecting, setSelecting] = useState(false); + const [selected, setSelectedIds] = useState>(new Set()); const loadNextPage = useCallback( (originShares: Share[], token?: string, direction?: string) => () => { @@ -62,6 +68,40 @@ const ShareList = () => { [setShares], ); + const onToggleSelect = useCallback( + (id: string) => { + setSelectedIds((prev) => { + const next = new Set(prev); + if (next.has(id)) { + next.delete(id); + } else { + next.add(id); + } + return next; + }); + }, + [setSelectedIds], + ); + + const exitSelecting = useCallback(() => { + setSelecting(false); + setSelectedIds(new Set()); + }, []); + + const deleteSelected = useCallback(() => { + dispatch(confirmOperation(t("fileManager.deleteShareWarning"))).then(() => { + dispatch(sendDeleteShares([...selected])).then(() => { + enqueueSnackbar({ + message: t("application:share.shareCanceled"), + variant: "success", + action: DefaultCloseAction, + }); + setShares((shares) => shares.filter((share) => !selected.has(share.id))); + exitSelecting(); + }); + }); + }, [dispatch, selected, t, enqueueSnackbar, exitSelecting]); + const onSelectChange = useCallback( (e: SelectChangeEvent) => { setOrderDirection(e.target.value as string); @@ -75,28 +115,53 @@ const ShareList = () => { - - - + {selecting && ( + <> + + + )} + + + + + + {t("application:share.createdAtDesc")} + + + + + {t("application:share.createdAtAsc")} + + + + + } onRefresh={() => refresh()} loading={loading} @@ -105,7 +170,14 @@ const ShareList = () => { {shares.map((share) => ( - + ))} {nextPageToken != undefined && ( <>