This commit is contained in:
Aria Moradi
2021-10-30 16:10:34 +03:30
parent 92ae5c8cd0
commit 091d97c914
47 changed files with 76 additions and 132 deletions

View File

@@ -20,10 +20,10 @@ import GetAppIcon from '@mui/icons-material/GetApp';
import SettingsIcon from '@mui/icons-material/Settings';
import ArrowBack from '@mui/icons-material/ArrowBack';
import { useHistory } from 'react-router-dom';
import NavBarContext from 'context/NavbarContext';
import DarkTheme from 'context/DarkTheme';
import PermanentSideBar from './PermanentSideBar';
import BottomNavigationBar from './BottomNavigationBar';
import NavBarContext from 'components/context/NavbarContext';
import DarkTheme from 'components/context/DarkTheme';
import DesktopSideBar from './navigation/DesktopSideBar';
import MobileBottomBar from './navigation/MobileBottomBar';
const useStyles = makeStyles((theme) => ({
root: {
@@ -62,26 +62,27 @@ const navbarItems: Array<NavbarItem> = [
},
];
export default function NavBar() {
export default function DefaultNavBar() {
const classes = useStyles();
const { title, action, override } = useContext(NavBarContext);
const theme = useTheme();
const isMobileWidth = useMediaQuery(theme.breakpoints.down('sm'));
const history = useHistory();
const isMainRoute = navbarItems.some(({ path }) => path === history.location.pathname);
const { darkTheme } = useContext(DarkTheme);
// Allow default navbar to be overrided with `NavBarContext`
const theme = useTheme();
const history = useHistory();
const isMobileWidth = useMediaQuery(theme.breakpoints.down('sm'));
const isMainRoute = navbarItems.some(({ path }) => path === history.location.pathname);
// Allow default navbar to be overrided
if (override.status) return override.value;
let navbar = <></>;
if (isMobileWidth) {
if (!isMainRoute) {
navbar = <BottomNavigationBar navBarItems={navbarItems} />;
navbar = <MobileBottomBar navBarItems={navbarItems} />;
}
} else {
navbar = <PermanentSideBar navBarItems={navbarItems} />;
navbar = <DesktopSideBar navBarItems={navbarItems} />;
}
return (

View File

@@ -27,7 +27,7 @@ import ListItemText from '@mui/material/ListItemText';
import ListItemSecondaryAction from '@mui/material/ListItemSecondaryAction';
import Collapse from '@mui/material/Collapse';
import Button from '@mui/material/Button';
import NavBarContext from '../../context/NavbarContext';
import NavBarContext from 'components/context/NavbarContext';
const useStyles = (settings: IReaderSettings) => makeStyles((theme) => ({
// main container and root div need to change classes...

View File

@@ -1,62 +0,0 @@
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import React from 'react';
import makeStyles from '@mui/styles/makeStyles';
import Drawer from '@mui/material/Drawer';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import { Link } from 'react-router-dom';
const useStyles = makeStyles({
list: {
width: 250,
},
});
interface IProps {
drawerOpen: boolean
setDrawerOpen: React.Dispatch<React.SetStateAction<boolean>>
navBarItems: Array<NavbarItem>
}
export default function TemporaryDrawer({ drawerOpen, setDrawerOpen, navBarItems }: IProps) {
const classes = useStyles();
return (
<div>
<Drawer
open={drawerOpen}
anchor="left"
onClose={() => setDrawerOpen(false)}
>
<div
className={classes.list}
role="presentation"
onClick={() => setDrawerOpen(false)}
onKeyDown={() => setDrawerOpen(false)}
>
<List>
{navBarItems.map((({ path, title, IconComponent }: NavbarItem) => (
<Link to={path} style={{ color: 'inherit', textDecoration: 'none' }}>
<ListItem button key={title}>
<ListItemIcon>
<IconComponent />
</ListItemIcon>
<ListItemText primary={title} />
</ListItem>
</Link>
)))}
</List>
</div>
</Drawer>
</div>
);
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import React, { useEffect, useState } from 'react';
import makeStyles from '@mui/styles/makeStyles';
import createStyles from '@mui/styles/createStyles';
import Button from '@mui/material/Button';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import DialogActions from '@mui/material/DialogActions';
import Dialog from '@mui/material/Dialog';
import Checkbox from '@mui/material/Checkbox';
import FormControlLabel from '@mui/material/FormControlLabel';
import FormGroup from '@mui/material/FormGroup';
import client from 'util/client';
const useStyles = makeStyles(() => createStyles({
paper: {
maxHeight: 435,
width: '80%',
},
}));
interface IProps {
open: boolean
setOpen: (value: boolean) => void
mangaId: number
}
interface ICategoryInfo {
category: ICategory
selected: boolean
}
export default function CategorySelect(props: IProps) {
const classes = useStyles();
const { open, setOpen, mangaId } = props;
const [categoryInfos, setCategoryInfos] = useState<ICategoryInfo[]>([]);
const [updateTriggerHolder, setUpdateTriggerHolder] = useState(0); // just a hack
const triggerUpdate = () => setUpdateTriggerHolder(updateTriggerHolder + 1); // just a hack
useEffect(() => {
let tmpCategoryInfos: ICategoryInfo[] = [];
client.get('/api/v1/category/')
.then((response) => response.data)
.then((data: ICategory[]) => {
if (data.length > 0 && data[0].name === 'Default') { data.shift(); }
tmpCategoryInfos = data.map((category) => ({ category, selected: false }));
})
.then(() => {
client.get(`/api/v1/manga/${mangaId}/category/`)
.then((response) => response.data)
.then((data: ICategory[]) => {
data.forEach((category) => {
tmpCategoryInfos[category.order - 1].selected = true;
});
setCategoryInfos(tmpCategoryInfos);
});
});
}, [updateTriggerHolder, open]);
const handleCancel = () => {
setOpen(false);
};
const handleOk = () => {
setOpen(false);
};
const handleChange = (event: React.ChangeEvent<HTMLInputElement>, categoryId: number) => {
const { checked } = event.target as HTMLInputElement;
const method = checked ? client.get : client.delete;
method(`/api/v1/manga/${mangaId}/category/${categoryId}`)
.then(() => triggerUpdate());
};
return (
<Dialog
classes={classes}
maxWidth="xs"
open={open}
>
<DialogTitle>Set categories</DialogTitle>
<DialogContent dividers>
<FormGroup>
{categoryInfos.length === 0
&& (
<span>
No categories found!
<br />
You should make some from settings.
</span>
)}
{categoryInfos.map((categoryInfo) => (
<FormControlLabel
control={(
<Checkbox
checked={categoryInfo.selected}
onChange={(e) => handleChange(e, categoryInfo.category.id)}
color="default"
/>
)}
label={categoryInfo.category.name}
key={categoryInfo.category.id}
/>
))}
</FormGroup>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleCancel} color="primary">
Cancel
</Button>
<Button onClick={handleOk} color="primary">
Ok
</Button>
</DialogActions>
</Dialog>
);
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import React, { useState } from 'react';
import makeStyles from '@mui/styles/makeStyles';
import createStyles from '@mui/styles/createStyles';
import Button from '@mui/material/Button';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import DialogActions from '@mui/material/DialogActions';
import Dialog from '@mui/material/Dialog';
import Switch from '@mui/material/Switch';
import IconButton from '@mui/material/IconButton';
import FilterListIcon from '@mui/icons-material/FilterList';
import { List, ListItemSecondaryAction, ListItemText } from '@mui/material';
import ListItem from '@mui/material/ListItem';
import { langCodeToName } from 'util/language';
import cloneObject from 'util/cloneObject';
const useStyles = makeStyles(() => createStyles({
paper: {
maxHeight: 435,
width: '80%',
},
}));
function removeAll(firstList: any[], secondList: any[]) {
secondList.forEach((item) => {
const index = firstList.indexOf(item);
if (index !== -1) {
firstList.splice(index, 1);
}
});
return firstList;
}
interface IProps {
shownLangs: string[]
setShownLangs: (arg0: string[]) => void
allLangs: string[]
forcedLangs?: string[]
}
export default function LangSelect(props: IProps) {
const {
shownLangs, setShownLangs, allLangs, forcedLangs,
} = props;
// hold a copy and only sate state on parent when OK pressed, improves performance
const [mShownLangs, setMShownLangs] = useState(
removeAll(cloneObject(shownLangs), forcedLangs!),
);
const classes = useStyles();
const [open, setOpen] = useState<boolean>(false);
const handleCancel = () => {
setOpen(false);
};
const handleOk = () => {
setOpen(false);
setShownLangs(mShownLangs);
};
const handleChange = (event: React.ChangeEvent<HTMLInputElement>, lang: string) => {
const { checked } = event.target as HTMLInputElement;
if (checked) {
setMShownLangs([...mShownLangs, lang]);
} else {
const clone = cloneObject(mShownLangs);
clone.splice(clone.indexOf(lang), 1);
setMShownLangs(clone);
}
};
return (
<>
<IconButton
onClick={() => setOpen(true)}
aria-label="display more actions"
edge="end"
color="inherit"
size="large"
>
<FilterListIcon />
</IconButton>
<Dialog
classes={classes}
maxWidth="xs"
open={open}
>
<DialogTitle>Enabled Languages</DialogTitle>
<DialogContent dividers style={{ padding: 0 }}>
<List>
{allLangs.map((lang) => (
<ListItem key={lang}>
<ListItemText primary={langCodeToName(lang)} />
<ListItemSecondaryAction>
<Switch
checked={mShownLangs.indexOf(lang) !== -1}
onChange={(e) => handleChange(e, lang)}
/>
</ListItemSecondaryAction>
</ListItem>
))}
</List>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleCancel} color="primary">
Cancel
</Button>
<Button onClick={handleOk} color="primary">
Ok
</Button>
</DialogActions>
</Dialog>
</>
);
}
LangSelect.defaultProps = {
forcedLangs: [] as string[],
};

View File

@@ -26,7 +26,7 @@ interface IProps {
navBarItems: Array<NavbarItem>
}
export default function PermanentSideBar({ navBarItems }: IProps) {
export default function DesktopSideBar({ navBarItems }: IProps) {
const location = useLocation();
return (
<SideNavBarContainer>

View File

@@ -32,7 +32,7 @@ interface IProps {
navBarItems: Array<NavbarItem>
}
export default function BottomNavigationBar({ navBarItems }: IProps) {
export default function MobileBottomBar({ navBarItems }: IProps) {
const location = useLocation();
return (
<BottomNavContainer>