Skip to content

Commit

Permalink
Implement categories + sidebar (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
aminpaks committed Mar 1, 2021
1 parent c90360a commit cadbf37
Show file tree
Hide file tree
Showing 33 changed files with 1,342 additions and 114 deletions.
74 changes: 74 additions & 0 deletions src/api/category.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { request } from './request';
import { buildEndpointUrl } from './utils';
import { getFormData } from '../utils';

export interface Category {
__internal: string | false;
name: string;
savePath: string;
hashList: string[];
}

export type CategoryCollection = Record<string, Category>;
export type CategoryOperationPayload = {
assign: { category: string; list: string[] };
create: { category: string; savePath: string };
edit: { category: string; savePath: string };
delete: { categories: string[] };
};
export type CategoryOperation = keyof CategoryOperationPayload;
export type CategoryOperationOptions = {
[K in CategoryOperation]: [operation: K, options: CategoryOperationPayload[K]];
}[CategoryOperation];

export type CategoryOperationResponse = [CategoryOperation, boolean];

export const apiV2CreateCategory = ({ category, savePath }: { category: string; savePath: string }) =>
request(buildEndpointUrl(`/api/v2/torrents/createCategory`), {
method: 'POST',
body: getFormData({
category,
savePath,
}),
}).then(() => ['create', true] as CategoryOperationResponse);

export const apiV2EditCategory = ({ category, savePath }: { category: string; savePath: string }) =>
request(buildEndpointUrl(`/api/v2/torrents/editCategory`), {
method: 'POST',
body: getFormData({
category,
savePath,
}),
}).then(() => ['edit', true] as CategoryOperationResponse);

export const apiV2DeleteCategory = ({ categories }: { categories: string[] }) =>
request(buildEndpointUrl(`/api/v2/torrents/removeCategories`), {
method: 'POST',
body: getFormData({
categories: categories.join('\n'),
}),
}).then(() => ['delete', true] as CategoryOperationResponse);

export const apiV2AssignToCategory = ({ category, list }: { category: string; list: string[] }) =>
request(buildEndpointUrl(`/api/v2/torrents/setCategory`), {
method: 'POST',
body: getFormData({
category,
hashes: list.join('|'),
}),
}).then(() => ['assign', true] as CategoryOperationResponse);

export const apiV2OperateCategory = (params: CategoryOperationOptions) => {
switch (params[0]) {
case 'assign':
return apiV2AssignToCategory(params[1]);
case 'create':
return apiV2CreateCategory(params[1]);
case 'edit':
return apiV2EditCategory(params[1]);
case 'delete':
return apiV2DeleteCategory(params[1]);
default:
throw new Error('Invalid operation');
}
};
3 changes: 2 additions & 1 deletion src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './app';
export * from './app-preferences';
export * from './app';
export * from './auth';
export * from './category';
export * from './request';
export * from './sync';
export * from './torrents';
Expand Down
5 changes: 3 additions & 2 deletions src/api/sync.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Category, CategoryCollection } from './category';
import { requestJson } from './request';
import { Torrent } from './torrents';
import { buildEndpointUrl } from './utils';
Expand All @@ -21,8 +22,8 @@ export interface SyncMaindata {
full_update: boolean; // Whether the response contains all the data or partial data
torrents?: Record<string, Partial<Torrent>>; // Property: torrent hash, value: same as torrent list
torrents_removed?: string[]; // List of hashes of torrents removed since last request
categories: unknown; // Info for categories added since last request
categories_removed?: unknown[]; // List of categories removed since last request
categories: CategoryCollection; // Info for categories added since last request
categories_removed?: string[]; // List of categories removed since last request
tags?: unknown[]; // List of tags added since last request
tags_removed?: unknown[]; // List of tags removed since last request
server_state?: ServerState; // Global transfer info
Expand Down
1 change: 1 addition & 0 deletions src/api/torrents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export type TorrentPrimitiveOperationOptions = {
}[TorrentPrimitiveOperations];

export type TorrentKeys = keyof Torrent;
export type TorrentCollection = Record<string, Torrent>;

export interface Torrent {
added_on: number; // Time (Unix Epoch) when the torrent was added to the client
Expand Down
10 changes: 4 additions & 6 deletions src/components/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { FC } from 'react';
import { mStyles } from './common';
import { useAppVersionQuery } from './data';
import { MainLayout } from './layout';
import Sidebar from './sidebar';
import TorrentsContainer from './torrents';

const useStyles = mStyles(() => ({
torrentContainer: {
width: '100%',
flex: '1 0 auto',
height: '100%',
},
}));

Expand All @@ -16,10 +16,8 @@ export const App: FC = () => {
const { data: qbtVersion } = useAppVersionQuery();

return (
<MainLayout qbtVersion={qbtVersion || ''}>
<div className={classes.torrentContainer}>
<TorrentsContainer />
</div>
<MainLayout qbtVersion={qbtVersion || ''} sideBar={<Sidebar />}>
<TorrentsContainer />
</MainLayout>
);
};
6 changes: 5 additions & 1 deletion src/components/auth/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ const LOGIN_PASSWORD = 'loginPassword';
const LOGIN_REMEMBER_ME = 'loginRememberMe';

const useStyles = mStyles(({ spacing }) => ({
container: {
display: 'flex',
justifyContent: 'center',
},
loginRoot: {
display: 'block',
flex: '0 0 auto',
Expand Down Expand Up @@ -72,7 +76,7 @@ export const Login: FC = () => {
}, [isSuccess, appVersion]);

return (
<MainLayout header={<AppHeader qbtVersion="" />} qbtVersion="">
<MainLayout header={<AppHeader qbtVersion="" />} qbtVersion="" className={classes.container}>
<section className={classes.loginRoot}>
<Box marginBottom={2} component="header">
<Typography variant="h4" component="h1">
Expand Down
18 changes: 18 additions & 0 deletions src/components/data/category.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useMutation } from 'react-query';
import { apiV2OperateCategory, CategoryOperationResponse } from '../../api';
import { LazyReason } from '../../types';

export const useCategoryOperationsMutation = (
{ onSuccess, onError } = {} as {
onSuccess?: LazyReason<Promise<void> | void, CategoryOperationResponse>;
onError?: LazyReason<Promise<void> | void>;
}
) => {
const mutationObject = useMutation(apiV2OperateCategory, {
retry: false,
onSuccess,
onError,
});

return mutationObject;
};
1 change: 1 addition & 0 deletions src/components/data/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './app';
export * from './category';
export * from './login';
export * from './torrents';
22 changes: 13 additions & 9 deletions src/components/layout/main-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import AppHeader from '../header';
import { AppStatusBar } from '../app-statusbar';
import { mStyles } from '../common';
import { AppBar } from '../material-ui-core';
import clsx from 'clsx';

const useStyles = mStyles(({ spacing }) => ({
const useStyles = mStyles(({ spacing, zIndex }) => ({
mainLayoutRoot: {
width: '100vw',
height: '100vh',
Expand All @@ -14,27 +15,30 @@ const useStyles = mStyles(({ spacing }) => ({
mainLayoutContainer: {
display: 'flex',
flex: '1 0 auto',
alignItems: 'center',
flexDirection: 'column',
padding: spacing(2),
paddingBottom: 0,
alignItems: 'flex-start',
},
appBarContainer: {
padding: spacing(2),
appChildren: {
height: '100%',
flex: '1 0 auto',
},
}));

export const MainLayout: FC<{
header?: ReactElement;
statusBar?: ReactElement;
sideBar?: ReactElement;
qbtVersion: string;
}> = ({ header, statusBar, qbtVersion, children }) => {
className?: string;
}> = ({ header, statusBar, sideBar = <div />, qbtVersion, children, className }) => {
const classes = useStyles();

return (
<div className={classes.mainLayoutRoot}>
<AppBar position="static">{header || <AppHeader qbtVersion={qbtVersion} />}</AppBar>
<div className={classes.mainLayoutContainer}>{children}</div>
<div className={classes.mainLayoutContainer}>
{sideBar}
<div className={clsx(classes.appChildren, className)}>{children}</div>
</div>
{statusBar || <AppStatusBar />}
</div>
);
Expand Down
10 changes: 10 additions & 0 deletions src/components/material-ui-core.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { PopoverOrigin } from '@material-ui/core/Popover/index';
import { SvgIconTypeMap } from '@material-ui/core/SvgIcon/index';
import Accordion from '@material-ui/core/Accordion';
import AccordionDetails from '@material-ui/core/AccordionDetails';
import AccordionSummary from '@material-ui/core/AccordionSummary';
import Alert from '@material-ui/lab/Alert';
import AppBar from '@material-ui/core/AppBar';
import Box from '@material-ui/core/Box';
Expand All @@ -11,6 +14,8 @@ import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import Divider from '@material-ui/core/Divider';
import Drawer from '@material-ui/core/Drawer';
import FormControl from '@material-ui/core/FormControl';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormGroup from '@material-ui/core/FormGroup';
Expand Down Expand Up @@ -38,6 +43,7 @@ import TextField from '@material-ui/core/TextField';
import Typography from '@material-ui/core/Typography';

export {
Accordion,
Alert,
AppBar,
Box,
Expand All @@ -49,6 +55,8 @@ export {
DialogContent,
DialogContentText,
DialogTitle,
Divider,
Drawer,
FormControl,
FormControlLabel,
FormGroup,
Expand Down Expand Up @@ -76,4 +84,6 @@ export {
SvgIconTypeMap,
TextField,
Typography,
AccordionDetails,
AccordionSummary,
};
24 changes: 22 additions & 2 deletions src/components/material-ui-icons.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import AddCircleIcon from '@material-ui/icons/AddCircle';
import AddIcon from '@material-ui/icons/Add';
import AlarmIcon from '@material-ui/icons/Alarm';
import AllInclusiveIcon from '@material-ui/icons/AllInclusive';
Expand All @@ -12,16 +13,22 @@ import CheckIcon from '@material-ui/icons/Check';
import ChevronLeftIcon from '@material-ui/icons/ChevronLeft';
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
import ClassIcon from '@material-ui/icons/Class';
import CloseIcon from '@material-ui/icons/Close';
import DeleteIcon from '@material-ui/icons/Delete';
import DoneIcon from '@material-ui/icons/Done';
import EditIcon from '@material-ui/icons/Edit';
import FastForwardIcon from '@material-ui/icons/FastForward';
import FeaturedPlayListIcon from '@material-ui/icons/FeaturedPlayList';
import FileCopyIcon from '@material-ui/icons/FileCopy';
import FindInPageIcon from '@material-ui/icons/FindInPage';
import FirstPageIcon from '@material-ui/icons/FirstPage';
import FolderIcon from '@material-ui/icons/Folder';
import FolderOpenIcon from '@material-ui/icons/FolderOpen';
import GetAppIcon from '@material-ui/icons/GetApp';
import GrainIcon from '@material-ui/icons/Grain';
import HelpIcon from '@material-ui/icons/Help';
import HelpOutlineIcon from '@material-ui/icons/HelpOutline';
import HighlightOffIcon from '@material-ui/icons/HighlightOff';
import LabelImportantIcon from '@material-ui/icons/LabelImportant';
import LastPageIcon from '@material-ui/icons/LastPage';
import LinkIcon from '@material-ui/icons/Link';
Expand All @@ -36,20 +43,23 @@ import PowerOffIcon from '@material-ui/icons/PowerOff';
import PublishIcon from '@material-ui/icons/Publish';
import RemoveIcon from '@material-ui/icons/Remove';
import ReorderIcon from '@material-ui/icons/Reorder';
import SearchIcon from '@material-ui/icons/Search';
import SettingsIcon from '@material-ui/icons/Settings';
import ShareIcon from '@material-ui/icons/Share';
import SpeedIcon from '@material-ui/icons/Speed';
import StorageIcon from '@material-ui/icons/Storage';
import SubtitlesIcon from '@material-ui/icons/Subtitles';
import TrackChangesIcon from '@material-ui/icons/TrackChanges';
import TrendingDownIcon from '@material-ui/icons/TrendingDown';
import TrendingUpIcon from '@material-ui/icons/TrendingUp';
import VerticalAlignBottomIcon from '@material-ui/icons/VerticalAlignBottom';
import VerticalAlignTopIcon from '@material-ui/icons/VerticalAlignTop';
import ViewListIcon from '@material-ui/icons/ViewList';
import WifiIcon from '@material-ui/icons/Wifi';
import WifiOffIcon from '@material-ui/icons/WifiOff';
import SearchIcon from '@material-ui/icons/Search';

export {
SearchIcon,
AddCircleIcon,
AddIcon,
AlarmIcon,
AllInclusiveIcon,
Expand All @@ -64,16 +74,22 @@ export {
ChevronLeftIcon,
ChevronRightIcon,
ClassIcon,
CloseIcon,
DeleteIcon,
DoneIcon,
EditIcon,
FastForwardIcon,
FeaturedPlayListIcon,
FileCopyIcon,
FindInPageIcon,
FirstPageIcon,
FolderIcon,
FolderOpenIcon,
GetAppIcon,
GrainIcon,
HelpIcon,
HelpOutlineIcon,
HighlightOffIcon,
LabelImportantIcon,
LastPageIcon,
LinkIcon,
Expand All @@ -88,14 +104,18 @@ export {
PublishIcon,
RemoveIcon,
ReorderIcon,
SearchIcon,
SettingsIcon,
ShareIcon,
SpeedIcon,
StorageIcon,
SubtitlesIcon,
TrackChangesIcon,
TrendingDownIcon,
TrendingUpIcon,
VerticalAlignBottomIcon,
VerticalAlignTopIcon,
ViewListIcon,
WifiIcon,
WifiOffIcon,
};

0 comments on commit cadbf37

Please sign in to comment.