Skip to content

Commit

Permalink
Add storage component to side menu
Browse files Browse the repository at this point in the history
  • Loading branch information
derneuere committed Oct 7, 2023
1 parent e6d3d12 commit 9bfee51
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 3 deletions.
9 changes: 8 additions & 1 deletion src/api_client/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import type { IUploadOptions, IUploadResponse } from "../store/upload/upload.zod
import { UploadExistResponse, UploadResponse } from "../store/upload/upload.zod";
import type { IApiUserListResponse, IManageUser, IUser } from "../store/user/user.zod";
import { ManageUser, UserSchema } from "../store/user/user.zod";
import type { ServerStatsResponseType } from "../store/util/util.zod";
import type { ServerStatsResponseType, StorageStatsResponseType } from "../store/util/util.zod";
import type { IWorkerAvailabilityResponse } from "../store/worker/worker.zod";
// eslint-disable-next-line import/no-cycle
import { Server } from "./apiClient";
Expand Down Expand Up @@ -61,6 +61,7 @@ export enum Endpoints {
notThisPerson = "notThisPerson",
setFacesPersonLabel = "setFacesPersonLabel",
fetchServerStats = "fetchServerStats",
fetchStorageStats = "fetchStorageStats",
}

const baseQuery = fetchBaseQuery({
Expand Down Expand Up @@ -247,6 +248,11 @@ export const api = createApi({
url: `serverstats`,
}),
}),
[Endpoints.fetchStorageStats]: builder.query<StorageStatsResponseType, void>({
query: () => ({
url: `storagestats`,
}),
}),
}),
});

Expand All @@ -271,4 +277,5 @@ export const {
useManageUpdateUserMutation,
useIsFirstTimeSetupQuery,
useFetchServerStatsQuery,
useFetchStorageStatsQuery,
} = api;
46 changes: 44 additions & 2 deletions src/components/menubars/SideMenuNarrow.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,38 @@
import { ActionIcon, Menu, Navbar } from "@mantine/core";
import { ActionIcon, Center, Loader, Menu, Navbar, Progress, Text, Tooltip } from "@mantine/core";
import { useMediaQuery } from "@mantine/hooks";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { push } from "redux-first-history";
import { Book, ChevronRight, Heart } from "tabler-icons-react";
import storage from "redux-persist/lib/storage";
import { Book, ChevronRight, Cloud, Heart } from "tabler-icons-react";

import { useFetchStorageStatsQuery } from "../../api_client/api";
import { selectAuthAccess, selectIsAuthenticated } from "../../store/auth/authSelectors";
import { useAppDispatch, useAppSelector } from "../../store/store";
import { DOCUMENTATION_LINK, LEFT_MENU_WIDTH, SUPPORT_LINK } from "../../ui-constants";
import { getNavigationItems, navigationStyles } from "./navigation";

function formatBytes(bytes, decimals = 2) {
if (!+bytes) return "0 Bytes";

const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ["Bytes", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];

const i = Math.floor(Math.log(bytes) / Math.log(k));

return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`;
}

export function SideMenuNarrow(): JSX.Element {
const isAuthenticated = useAppSelector(selectIsAuthenticated);
const canAccess = useAppSelector(selectAuthAccess);
const dispatch = useAppDispatch();
const { classes, cx } = navigationStyles();
const [active, setActive] = useState("/");

const { data: storageStats, isLoading } = useFetchStorageStatsQuery();

const { t } = useTranslation();
const matches = useMediaQuery("(min-width: 700px)");

Expand Down Expand Up @@ -93,6 +109,32 @@ export function SideMenuNarrow(): JSX.Element {
<Navbar.Section grow>{links}</Navbar.Section>

<Navbar.Section mb="lg">
<div className={classes.hover}>
<div className={classes.text} style={{ paddingBottom: 0 }}>
<ActionIcon className={classes.linkIcon} variant="light">
<Cloud />
</ActionIcon>
<span style={{ flexGrow: 2 }}>{t("storage")}</span>
</div>
{isLoading && (
<Center>
<Loader size="xs"></Loader>
</Center>
)}
{!isLoading && (
<Tooltip
label={t("storagetooltip", {
usedstorage: formatBytes(storageStats.used_storage),
totalstorage: formatBytes(storageStats.total_storage),
})}
>
<Progress
style={{ margin: 10 }}
sections={[{ value: storageStats.used_storage / storageStats.total_storage, color: "grey" }]}
/>
</Tooltip>
)}
</div>
<a href={DOCUMENTATION_LINK} target="_blank" rel="noreferrer" className={classes.link}>
<ActionIcon className={classes.linkIcon} variant="light">
<Book />
Expand Down
19 changes: 19 additions & 0 deletions src/components/menubars/navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,25 @@ export const navigationStyles = createStyles((theme, _params, getRef) => {
display: "block",
},

text: {
...theme.fn.focusStyles(),
display: "flex",
alignItems: "center",
textDecoration: "none",
fontSize: theme.fontSizes.sm,
color: theme.colorScheme === "dark" ? theme.colors.dark[1] : theme.colors.gray[7],
padding: `${theme.spacing.xs}px ${theme.spacing.sm}px`,
borderRadius: theme.radius.sm,
fontWeight: 500,
},

hover: {
"&:hover": {
backgroundColor: theme.colorScheme === "dark" ? theme.colors.dark[6] : theme.colors.gray[0],
color: theme.colorScheme === "dark" ? theme.white : theme.black,
},
},

link: {
...theme.fn.focusStyles(),
display: "flex",
Expand Down
2 changes: 2 additions & 0 deletions src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,8 @@
"save": "Save",
"modify": "Modify",
"supportus": "Support us",
"storage": "Storage",
"storagetooltip": "{{usedstorage}} of {{totalstorage}} used",
"docs": "Documentation",
"deletefaceexplanation": "This action will permanently delete the faces and all its associated data. This action cannot be undone.",
"deletealbumexplanation": "This action will permanently delete the album. This action cannot be undone.",
Expand Down
8 changes: 8 additions & 0 deletions src/store/util/util.zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,11 @@ export type ServerStatsResponseType = z.infer<typeof ServerStatsResponse>;

// To-Do: Add a type for this
export const ServerStatsResponse = z.any();

export type StorageStatsResponseType = z.infer<typeof StorageStatsResponse>;

export const StorageStatsResponse = z.object({
used_storage: z.number(),
total_storage: z.number(),
free_storage: z.number(),
});
10 changes: 10 additions & 0 deletions src/store/util/utilSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import type { PayloadAction } from "@reduxjs/toolkit";
import { createSlice } from "@reduxjs/toolkit";

import { api } from "../../api_client/api";
import type { StorageStatsResponseType } from "./util.zod";

const initialState = {
serverStats: null,
storageStats: {} as StorageStatsResponseType,
};

const utilSlice = createSlice({
Expand All @@ -21,6 +23,14 @@ const utilSlice = createSlice({
...state,
error: payload,
}))
.addMatcher(api.endpoints.fetchStorageStats.matchFulfilled, (state, { payload }) => ({
...state,
storageStats: payload,
}))
.addMatcher(api.endpoints.fetchStorageStats.matchRejected, (state, { payload }) => ({
...state,
error: payload,
}))
.addDefaultCase(state => state);
},
});
Expand Down

0 comments on commit 9bfee51

Please sign in to comment.