diff --git a/VoidCat/spa/src/app/src/Admin/Admin.tsx b/VoidCat/spa/src/app/src/Admin/Admin.tsx
index 8485396..112583a 100644
--- a/VoidCat/spa/src/app/src/Admin/Admin.tsx
+++ b/VoidCat/spa/src/app/src/Admin/Admin.tsx
@@ -4,7 +4,6 @@ import {useSelector} from "react-redux";
import {Navigate} from "react-router-dom";
import {AdminProfile} from "@void-cat/api";
-import {FileList} from "../Components/Shared/FileList";
import {UserList} from "./UserList";
import {VoidButton} from "../Components/Shared/VoidButton";
import VoidModal from "../Components/Shared/VoidModal";
@@ -12,6 +11,7 @@ import EditUser from "./EditUser";
import useApi from "Hooks/UseApi";
import {RootState} from "Store";
+import ImageGrid from "../Components/Shared/ImageGrid";
export function Admin() {
const auth = useSelector((state: RootState) => state.login.jwt);
@@ -41,7 +41,7 @@ export function Admin() {
]}/>
Files
- AdminApi.adminListFiles(r)} actions={(i) => {
+ AdminApi.adminListFiles(r)} actions={(i) => {
return
deleteFile(i.id)}>Delete
|
diff --git a/VoidCat/spa/src/app/src/Components/Shared/FileList.tsx b/VoidCat/spa/src/app/src/Components/Shared/FileList.tsx
index 96adace..e7daae1 100644
--- a/VoidCat/spa/src/app/src/Components/Shared/FileList.tsx
+++ b/VoidCat/spa/src/app/src/Components/Shared/FileList.tsx
@@ -65,7 +65,7 @@ export function FileList(props: FileListProps) {
loadFileList().catch(console.error)
}, [page]);
- if (accessDenied === true) {
+ if (accessDenied) {
return Access Denied
;
}
diff --git a/VoidCat/spa/src/app/src/Components/Shared/ImageGrid.css b/VoidCat/spa/src/app/src/Components/Shared/ImageGrid.css
new file mode 100644
index 0000000..4ec764d
--- /dev/null
+++ b/VoidCat/spa/src/app/src/Components/Shared/ImageGrid.css
@@ -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;
+}
\ No newline at end of file
diff --git a/VoidCat/spa/src/app/src/Components/Shared/ImageGrid.tsx b/VoidCat/spa/src/app/src/Components/Shared/ImageGrid.tsx
new file mode 100644
index 0000000..1bc23e4
--- /dev/null
+++ b/VoidCat/spa/src/app/src/Components/Shared/ImageGrid.tsx
@@ -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>
+}
+
+export default function ImageGrid(props: ImageGridProps) {
+ const navigate = useNavigate();
+ const loadPage = props.loadPage;
+ const actions = props.actions;
+ const dispatch = useDispatch();
+ const [files, setFiles] = useState>();
+ const [page, setPage] = useState(0);
+ const pageSize = 20;
+ const [accessDenied, setAccessDenied] = useState();
+
+ 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 ;
+ }
+ case "audio/aac":
+ case "audio/opus":
+ case "audio/wav":
+ case "audio/webm":
+ case "audio/midi":
+ case "audio/mpeg":
+ case "audio/ogg": {
+ return ;
+ }
+ 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