forked from Kieran/void.cat
render image grid for admin file list
This commit is contained in:
parent
f724203a93
commit
0786c6830c
@ -4,7 +4,6 @@ import {useSelector} from "react-redux";
|
|||||||
import {Navigate} from "react-router-dom";
|
import {Navigate} from "react-router-dom";
|
||||||
import {AdminProfile} from "@void-cat/api";
|
import {AdminProfile} from "@void-cat/api";
|
||||||
|
|
||||||
import {FileList} from "../Components/Shared/FileList";
|
|
||||||
import {UserList} from "./UserList";
|
import {UserList} from "./UserList";
|
||||||
import {VoidButton} from "../Components/Shared/VoidButton";
|
import {VoidButton} from "../Components/Shared/VoidButton";
|
||||||
import VoidModal from "../Components/Shared/VoidModal";
|
import VoidModal from "../Components/Shared/VoidModal";
|
||||||
@ -12,6 +11,7 @@ import EditUser from "./EditUser";
|
|||||||
|
|
||||||
import useApi from "Hooks/UseApi";
|
import useApi from "Hooks/UseApi";
|
||||||
import {RootState} from "Store";
|
import {RootState} from "Store";
|
||||||
|
import ImageGrid from "../Components/Shared/ImageGrid";
|
||||||
|
|
||||||
export function Admin() {
|
export function Admin() {
|
||||||
const auth = useSelector((state: RootState) => state.login.jwt);
|
const auth = useSelector((state: RootState) => state.login.jwt);
|
||||||
@ -41,7 +41,7 @@ export function Admin() {
|
|||||||
]}/>
|
]}/>
|
||||||
|
|
||||||
<h2>Files</h2>
|
<h2>Files</h2>
|
||||||
<FileList loadPage={r => AdminApi.adminListFiles(r)} actions={(i) => {
|
<ImageGrid loadPage={r => AdminApi.adminListFiles(r)} actions={(i) => {
|
||||||
return <td>
|
return <td>
|
||||||
<VoidButton onClick={() => deleteFile(i.id)}>Delete</VoidButton>
|
<VoidButton onClick={() => deleteFile(i.id)}>Delete</VoidButton>
|
||||||
</td>
|
</td>
|
||||||
|
@ -65,7 +65,7 @@ export function FileList(props: FileListProps) {
|
|||||||
loadFileList().catch(console.error)
|
loadFileList().catch(console.error)
|
||||||
}, [page]);
|
}, [page]);
|
||||||
|
|
||||||
if (accessDenied === true) {
|
if (accessDenied) {
|
||||||
return <h3>Access Denied</h3>;
|
return <h3>Access Denied</h3>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
18
VoidCat/spa/src/app/src/Components/Shared/ImageGrid.css
Normal file
18
VoidCat/spa/src/app/src/Components/Shared/ImageGrid.css
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
.image-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-grid > div {
|
||||||
|
width: 100px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-grid img, .image-grid video, .image-grid audio {
|
||||||
|
width: stretch;
|
||||||
|
}
|
103
VoidCat/spa/src/app/src/Components/Shared/ImageGrid.tsx
Normal file
103
VoidCat/spa/src/app/src/Components/Shared/ImageGrid.tsx
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import "./ImageGrid.css";
|
||||||
|
|
||||||
|
import {ApiError, PagedRequest, PagedResponse, PagedSortBy, PageSortOrder, VoidFileResponse} from "@void-cat/api";
|
||||||
|
import {ReactNode, useEffect, useState} from "react";
|
||||||
|
import {useDispatch} from "react-redux";
|
||||||
|
import {logout} from "../../LoginState";
|
||||||
|
import {PageSelector} from "./PageSelector";
|
||||||
|
import {useNavigate} from "react-router-dom";
|
||||||
|
|
||||||
|
interface ImageGridProps {
|
||||||
|
actions?: (f: VoidFileResponse) => ReactNode
|
||||||
|
loadPage: (req: PagedRequest) => Promise<PagedResponse<any>>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ImageGrid(props: ImageGridProps) {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const loadPage = props.loadPage;
|
||||||
|
const actions = props.actions;
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [files, setFiles] = useState<PagedResponse<VoidFileResponse>>();
|
||||||
|
const [page, setPage] = useState(0);
|
||||||
|
const pageSize = 20;
|
||||||
|
const [accessDenied, setAccessDenied] = useState<boolean>();
|
||||||
|
|
||||||
|
async function loadFileList() {
|
||||||
|
try {
|
||||||
|
const pageReq = {
|
||||||
|
page: page,
|
||||||
|
pageSize,
|
||||||
|
sortBy: PagedSortBy.Date,
|
||||||
|
sortOrder: PageSortOrder.Dsc
|
||||||
|
};
|
||||||
|
const rsp = await loadPage(pageReq);
|
||||||
|
setFiles(rsp);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
if (e instanceof ApiError) {
|
||||||
|
if (e.statusCode === 401) {
|
||||||
|
dispatch(logout());
|
||||||
|
} else if (e.statusCode === 403) {
|
||||||
|
setAccessDenied(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadFileList().catch(console.error)
|
||||||
|
}, [page]);
|
||||||
|
|
||||||
|
function renderPreview(info: VoidFileResponse) {
|
||||||
|
const link = `/d/${info.id}`;
|
||||||
|
|
||||||
|
if (info.metadata) {
|
||||||
|
switch (info.metadata.mimeType) {
|
||||||
|
case "image/avif":
|
||||||
|
case "image/bmp":
|
||||||
|
case "image/gif":
|
||||||
|
case "image/svg+xml":
|
||||||
|
case "image/tiff":
|
||||||
|
case "image/webp":
|
||||||
|
case "image/jpg":
|
||||||
|
case "image/jpeg":
|
||||||
|
case "image/png": {
|
||||||
|
return <img src={link} alt={info.metadata.name}/>;
|
||||||
|
}
|
||||||
|
case "audio/aac":
|
||||||
|
case "audio/opus":
|
||||||
|
case "audio/wav":
|
||||||
|
case "audio/webm":
|
||||||
|
case "audio/midi":
|
||||||
|
case "audio/mpeg":
|
||||||
|
case "audio/ogg": {
|
||||||
|
return <audio src={link} controls/>;
|
||||||
|
}
|
||||||
|
case "video/x-msvideo":
|
||||||
|
case "video/mpeg":
|
||||||
|
case "video/ogg":
|
||||||
|
case "video/mp2t":
|
||||||
|
case "video/mp4":
|
||||||
|
case "video/matroksa":
|
||||||
|
case "video/x-matroska":
|
||||||
|
case "video/webm":
|
||||||
|
case "video/quicktime": {
|
||||||
|
return <video src={link}/>;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return <b>{info.metadata?.name ?? info.id}</b>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<div className="image-grid">
|
||||||
|
{files?.results.map(v => <div key={v.id} onClick={() => navigate(`/${v.id}`)}>
|
||||||
|
{renderPreview(v)}
|
||||||
|
</div>)}
|
||||||
|
</div>
|
||||||
|
<PageSelector onSelectPage={(x) => setPage(x)} page={page} total={files?.totalResults ?? 0}
|
||||||
|
pageSize={pageSize}/>
|
||||||
|
</>
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
grid-auto-flow: column;
|
grid-auto-flow: column;
|
||||||
width: min-content;
|
width: min-content;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-buttons > div {
|
.page-buttons > div {
|
||||||
|
Loading…
Reference in New Issue
Block a user