Added more settings to /settings #5

main
Guus van Meerveld 3 years ago
parent 6c5b9f7932
commit c4b9ba011d
Signed by: Guusvanmeerveld
GPG Key ID: 2BA7D7912771966E

@ -10,7 +10,7 @@ module.exports = {
ignoreDuringBuilds: true
},
env: {
NEXT_PUBLIC_GITHUB_URL: packageInfo.repository.url,
NEXT_PUBLIC_GITHUB_URL: process.env.GIT_URL ?? packageInfo.repository.url,
NEXT_PUBLIC_APP_NAME: process.env.APP_NAME ?? packageInfo.displayName,
NEXT_PUBLIC_DEFAULT_SERVER:
process.env.DEFAULT_SERVER ?? "invidious.privacy.gd"

@ -0,0 +1,20 @@
import Box from "@mui/material/Box";
import { styled } from "@mui/material/styles";
interface ColorBoxProps {
color: string;
}
const ColorBox = styled(Box, {
shouldForwardProp: (prop) => prop !== "color"
})<ColorBoxProps>(({ theme, color }) => ({
width: 24,
height: 24,
backgroundColor: color,
borderRadius: "50%",
borderColor: theme.palette.text.primary,
borderWidth: 2,
borderStyle: "solid"
}));
export default ColorBox;

@ -0,0 +1,103 @@
import { FC } from "react";
import Box from "@mui/material/Box";
import Grid from "@mui/material/Grid";
import Modal from "@mui/material/Modal";
import Typography from "@mui/material/Typography";
import {
blue,
green,
amber,
red,
cyan,
teal,
deepOrange,
indigo,
yellow,
lightBlue,
orange,
lime,
deepPurple,
lightGreen,
pink,
purple
} from "@mui/material/colors";
import { useTheme } from "@mui/material/styles";
import ModalBox from "@components/ModalBox";
const colors = [
red,
deepOrange,
orange,
amber,
yellow,
lime,
lightGreen,
green,
teal,
cyan,
lightBlue,
blue,
indigo,
deepPurple,
purple,
pink
];
const MaterialColorPicker: FC<{
isOpen: boolean;
setState: (isOpen: boolean) => void;
selectedColor: string;
setColor: (color: string) => void;
}> = ({ setState, isOpen, selectedColor, setColor }) => {
const theme = useTheme();
return (
<Modal
open={isOpen}
onClose={() => setState(false)}
aria-labelledby="color-picker"
aria-describedby="Pick a material color"
component="div"
>
<ModalBox>
<Typography gutterBottom variant="h4">
Pick a color
</Typography>
<Grid container spacing={1} columns={12}>
{colors.map((color, i) => (
<Grid item key={i}>
{Object.values(color)
.slice(0, 10)
.map((shade, i) => (
<Box
onClick={() => setColor(shade)}
key={i}
sx={{
width: 24,
height: 24,
backgroundColor: shade,
borderRadius: "50%",
border:
shade == selectedColor
? {
borderColor: theme.palette.text.primary,
borderWidth: 2,
borderStyle: "solid"
}
: null,
cursor: "pointer",
mb: 1
}}
></Box>
))}
</Grid>
))}
</Grid>
</ModalBox>
</Modal>
);
};
export default MaterialColorPicker;

@ -0,0 +1,17 @@
import Box from "@mui/material/Box";
import styled from "@mui/system/styled";
const ModalBox = styled(Box)(({ theme }) => ({
padding: "2rem",
position: "absolute",
left: "50%",
top: "50%",
transform: "translate(-50%, -50%)",
minWidth: "20rem",
backgroundColor: theme.palette.background.paper,
borderRadius: 5,
outline: "none"
}));
export default ModalBox;

@ -95,6 +95,7 @@ const Navbar: FC = () => {
</IconButton>
<Link href="/" passHref>
<a>
<Typography
variant="h6"
component="div"
@ -108,6 +109,7 @@ const Navbar: FC = () => {
<PlayCircleOutline sx={{ mr: 1 }} />
{process.env.NEXT_PUBLIC_APP_NAME}
</Typography>
</a>
</Link>
<Box

@ -3,6 +3,16 @@ interface Settings {
primaryColor: string;
accentColor: string;
invidiousServer: string;
invidiousUsername?: string;
storageType: StorageType;
customServer?: string;
password?: string;
}
export enum StorageType {
Local = "local",
Invidious = "invidious",
RemoteServer = "remoteserver"
}
export default Settings;

@ -1,11 +1,11 @@
import axios from "axios";
import { NextPage } from "next";
import { NextSeo } from "next-seo";
import { FC, useState } from "react";
import { useQuery } from "react-query";
import { useMutation, useQuery } from "react-query";
import axios from "axios";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
@ -18,104 +18,35 @@ import InputLabel from "@mui/material/InputLabel";
import MenuItem from "@mui/material/MenuItem";
import Modal from "@mui/material/Modal";
import Select from "@mui/material/Select";
import TextField from "@mui/material/TextField";
import Typography from "@mui/material/Typography";
import {
blue,
red,
green,
cyan,
purple,
yellow,
orange
} from "@mui/material/colors/";
import { red, green } from "@mui/material/colors/";
import { useTheme } from "@mui/material/styles/";
import Done from "@mui/icons-material/Done";
import Error from "@mui/icons-material/Error";
import { toCamelCase } from "@src/utils";
import Refresh from "@mui/icons-material/Refresh";
import {
ServerInstance,
ServerInstanceType,
Stats
} from "@interfaces/api/instances";
import { StorageType } from "@interfaces/settings";
import { useSettings } from "@utils/hooks";
import Layout from "@components/Layout";
import MaterialColorPicker from "@components/MaterialColorPicker";
import ColorBox from "@components/MaterialColorPicker/ColorBox";
import ModalBox from "@components/ModalBox";
const Settings: NextPage = () => {
const [settings, setSettings] = useSettings();
const setSetting = (key: string, value?: string): void => {
setSettings({ ...settings, [key]: value });
};
const [modalIsOpen, setModalState] = useState(false);
const theme = useTheme();
const instances = useQuery<[string, ServerInstance][]>(
"invidiousInstances",
() =>
axios
.get("https://api.invidious.io/instances.json")
.then((res) => res.data),
{ retry: false }
);
const invidiousServerResponse = useQuery<Stats>("invidiousInstance", () =>
axios
.get(`https://${settings.invidiousServer}/api/v1/stats`)
.then((res) => res.data)
);
const ColorSetting: FC<{ type: string }> = ({ type }) => {
const camelCase = toCamelCase(type) as "primaryColor" | "accentColor";
return (
<Box sx={{ my: 3, display: "flex" }}>
<Typography variant="h5" sx={{ flexGrow: 1 }}>
{type}
</Typography>
{[
blue[800],
red[800],
green[800],
cyan[800],
purple[800],
yellow[800],
orange[800]
].map((color) => (
<Box
onClick={() => setSetting(camelCase, color)}
key={color}
component="span"
sx={{
width: 24,
height: 24,
backgroundColor: color,
borderRadius: "50%",
border:
color == settings[camelCase]
? {
borderColor: theme.palette.text.primary,
borderWidth: 1,
borderStyle: "solid"
}
: null,
cursor: "pointer",
ml: 1.5
}}
/>
))}
</Box>
);
};
const InfoModal: FC = () => {
const data = invidiousServerResponse.data!;
const InfoModal: FC<{
modalIsOpen: boolean;
setModalState: (isOpen: boolean) => void;
data: Stats;
}> = ({ modalIsOpen, setModalState, data }) => {
const [settings] = useSettings();
const lastUpdated = new Date(data.metadata.updatedAt * 1000);
@ -123,21 +54,10 @@ const Settings: NextPage = () => {
<Modal
open={modalIsOpen}
onClose={() => setModalState(false)}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box
sx={{
p: 4,
position: "absolute",
left: "50%",
top: "50%",
transform: "translate(-50%, -50%)",
backgroundColor: theme.palette.background.paper,
borderRadius: 1,
outline: "none"
}}
aria-labelledby="stats-modal"
aria-describedby="Shows server stats"
>
<ModalBox>
<Typography id="modal-modal-title" variant="h4">
Stats for {settings.invidiousServer}
</Typography>
@ -146,23 +66,72 @@ const Settings: NextPage = () => {
Software name: {data.software.name} <br />
Software version: {data.software.version} <br />
Software branch: {data.software.branch} <br /> <br />
Is accepting registrations: {data.openRegistrations
? "Yes"
: "No"}{" "}
Is accepting registrations: {data.openRegistrations ? "Yes" : "No"}
<br /> <br />
Total users: {data.usage.users.total} <br />
Active in the past half year: {data.usage.users.activeHalfyear}
<br />
Active in the past month: {data.usage.users.activeMonth} <br />{" "}
<br />
Active in the past month: {data.usage.users.activeMonth} <br /> <br />
Stats updated at: {lastUpdated.toLocaleDateString()} -{" "}
{lastUpdated.toLocaleTimeString()}
</Typography>
</Box>
</ModalBox>
</Modal>
);
};
const Setting: FC<{ title: string; description?: string }> = ({
title,
children,
description
}) => {
const theme = useTheme();
return (
<Box sx={{ my: 3, display: "flex", alignItems: "center" }}>
<Box sx={{ flexGrow: 1 }}>
<Typography variant="h5">{title}</Typography>
{description && (
<Typography variant="subtitle1" color={theme.palette.text.secondary}>
{description}
</Typography>
)}
</Box>
{children}
</Box>
);
};
const Settings: NextPage = () => {
const [settings, setSettings] = useSettings();
const setSetting = (key: string, value?: string): void =>
setSettings({ ...settings, [key]: value });
const theme = useTheme();
const [primaryColorModalIsOpen, setPrimaryColorModal] = useState(false);
const [accentColorModalIsOpen, setAccentColorModal] = useState(false);
const [modalIsOpen, setModalState] = useState(false);
const instances = useQuery<[string, ServerInstance][]>(
"invidiousInstances",
() =>
axios
.get("https://api.invidious.io/instances.json")
.then((res) => res.data),
{ retry: false }
);
const invidiousServerResponse = useMutation<Stats, unknown, string>(
"invidiousInstance",
(server) =>
axios.get(`https://${server}/api/v1/stats`).then((res) => res.data)
);
const allowsRegistrations = invidiousServerResponse.data?.openRegistrations;
return (
<>
<NextSeo
@ -179,10 +148,10 @@ const Settings: NextPage = () => {
<Typography variant="h4">Theme</Typography>
<Box sx={{ my: 3, display: "flex" }}>
<Typography variant="h5" sx={{ flexGrow: 1 }}>
General theme
</Typography>
<Setting
title="General Theme"
description="Sets the background color"
>
<ButtonGroup
variant="contained"
color="primary"
@ -194,11 +163,39 @@ const Settings: NextPage = () => {
<Button onClick={() => setSetting("theme")}>System</Button>
<Button onClick={() => setSetting("theme", "dark")}>Dark</Button>
</ButtonGroup>
</Box>
</Setting>
<ColorSetting type="Primary Color" />
<Setting title="Primary Color">
<ColorBox marginRight={1} color={settings.primaryColor} />
<Button
onClick={() => setPrimaryColorModal(true)}
variant="contained"
>
Pick Color
</Button>
<MaterialColorPicker
setState={setPrimaryColorModal}
isOpen={primaryColorModalIsOpen}
setColor={(color) => setSetting("primaryColor", color)}
selectedColor={settings.primaryColor}
/>
</Setting>
<ColorSetting type="Accent Color" />
<Setting title="Accent Color">
<ColorBox marginRight={1} color={settings.accentColor} />
<Button
onClick={() => setAccentColorModal(true)}
variant="contained"
>
Pick Color
</Button>
<MaterialColorPicker
setState={setAccentColorModal}
isOpen={accentColorModalIsOpen}
setColor={(color) => setSetting("accentColor", color)}
selectedColor={settings.accentColor}
/>
</Setting>
<Divider sx={{ my: 4 }} />
@ -206,13 +203,28 @@ const Settings: NextPage = () => {
<Divider sx={{ my: 4 }} />
<Typography variant="h4">Miscellaneous</Typography>
<Typography variant="h4">Data</Typography>
<Box sx={{ my: 3, display: "flex", alignItems: "center" }}>
<Typography variant="h5" sx={{ flexGrow: 1 }}>
Invidious Server
</Typography>
<Setting
title="Invidious Server"
description={`Where to fetch data from ${
settings.storageType == "invidious" ? "and login into" : ""
}`}
>
<Box sx={{ mr: 2 }}>
{!invidiousServerResponse.data &&
!invidiousServerResponse.error &&
!invidiousServerResponse.isLoading && (
<Refresh
sx={{
cursor: "pointer",
color: theme.palette.text.secondary
}}
onClick={() =>
invidiousServerResponse.mutate(settings.invidiousServer)
}
/>
)}
{invidiousServerResponse.data &&
!invidiousServerResponse.isLoading && (
<>
@ -220,9 +232,14 @@ const Settings: NextPage = () => {
onClick={() => setModalState(true)}
sx={{ color: green[800], cursor: "pointer" }}
/>
<InfoModal />
<InfoModal
modalIsOpen={modalIsOpen}
setModalState={setModalState}
data={invidiousServerResponse.data!}
/>
</>
)}
{invidiousServerResponse.error && (
<Error sx={{ color: red[800] }} />
)}
@ -238,6 +255,8 @@ const Settings: NextPage = () => {
onChange={(e) => {
const server = e.target.value;
invidiousServerResponse.mutate(server);
setSetting("invidiousServer", server);
}}
MenuProps={{ sx: { maxHeight: 300 } }}
@ -256,7 +275,89 @@ const Settings: NextPage = () => {
))}
</Select>
</FormControl>
</Box>
</Setting>
<Setting
title="Data Storage Location"
description="Where your personal data will be stored"
>
<FormControl sx={{ minWidth: 200 }}>
<InputLabel id="location-select-label">Location</InputLabel>
<Select
labelId="location-select-label"
id="location-select"
value={settings.storageType}
label="Location"
onChange={(e) => {
const location = e.target.value;
setSetting("storageType", location);
}}
MenuProps={{ sx: { maxHeight: 300 } }}
>
{[
{ name: "Locally", value: StorageType.Local },
{
name: "Invidious server using auth",
value: StorageType.Invidious
},
{
name: `A custom ${process.env.NEXT_PUBLIC_APP_NAME} auth server`,
value: StorageType.RemoteServer
}
].map((location, i) => (
<MenuItem key={i} value={location.value}>
{location.name}
</MenuItem>
))}
</Select>
</FormControl>
</Setting>
{settings.storageType != StorageType.Local && (
<>
{settings.storageType == "invidious" && allowsRegistrations && (
<Setting
title="Username"
description="The username for your Invidious account"
>
<TextField label="Username" color="primary" />
</Setting>
)}
{settings.storageType == StorageType.RemoteServer && (
<Setting
title="Server address"
description={`The address for your ${process.env.NEXT_PUBLIC_APP_NAME} auth server`}
>
<TextField label="Server adress" color="primary" />
</Setting>
)}
<Setting
title={
settings.storageType == StorageType.Invidious
? "Password"
: "Passphrase"
}
description={
settings.storageType == StorageType.Invidious
? "The password for your invidious account"
: "The passphrase for your account"
}
>
<TextField
type="password"
label={
settings.storageType == StorageType.Invidious
? "Password"
: "Passphrase"
}
color="primary"
/>
</Setting>
</>
)}
</Container>
</Layout>
</>

@ -109,7 +109,7 @@ export const getStaticProps: GetStaticProps = async ({}) => {
);
return {
props: { trending }
props: { trending: trending.slice(0, 10) }
};
};

@ -9,7 +9,8 @@ import Settings from "@interfaces/settings";
const defaultSettings: Settings = {
primaryColor: red[800],
accentColor: red[800],
invidiousServer: process.env.NEXT_PUBLIC_DEFAULT_SERVER as string
invidiousServer: process.env.NEXT_PUBLIC_DEFAULT_SERVER as string,
storageType: "local"
};
export const useSettings = (): [

Loading…
Cancel
Save