mirror of
https://github.com/Ernous/TorrServerJellyfin.git
synced 2025-12-20 05:56:10 +05:00
File diff suppressed because one or more lines are too long
22981
web/package-lock.json
generated
22981
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,8 @@
|
|||||||
"material-ui-image": "^3.3.0",
|
"material-ui-image": "^3.3.0",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
"react-scripts": "4.0.1"
|
"react-scripts": "4.0.1",
|
||||||
|
"styled-components": "^5.3.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
@@ -47,5 +48,10 @@
|
|||||||
"gulp-inline-source": "^4.0.0",
|
"gulp-inline-source": "^4.0.0",
|
||||||
"gulp-replace": "^1.0.0",
|
"gulp-replace": "^1.0.0",
|
||||||
"prettier": "2.2.1"
|
"prettier": "2.2.1"
|
||||||
}
|
},
|
||||||
|
"description": "",
|
||||||
|
"main": "gulpfile.js",
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React from 'react'
|
|
||||||
import CssBaseline from '@material-ui/core/CssBaseline'
|
import CssBaseline from '@material-ui/core/CssBaseline'
|
||||||
import Appbar from './components/Appbar.js'
|
import Appbar from './components/Appbar/index.js'
|
||||||
import { createMuiTheme, MuiThemeProvider } from '@material-ui/core'
|
import { createMuiTheme, MuiThemeProvider } from '@material-ui/core'
|
||||||
|
|
||||||
const baseTheme = createMuiTheme({
|
const baseTheme = createMuiTheme({
|
||||||
@@ -26,11 +25,9 @@ const baseTheme = createMuiTheme({
|
|||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
|
||||||
<MuiThemeProvider theme={baseTheme}>
|
<MuiThemeProvider theme={baseTheme}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<Appbar />
|
<Appbar />
|
||||||
</MuiThemeProvider>
|
</MuiThemeProvider>
|
||||||
</React.Fragment>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import Button from '@material-ui/core/Button'
|
|
||||||
import TextField from '@material-ui/core/TextField'
|
|
||||||
import Dialog from '@material-ui/core/Dialog'
|
|
||||||
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 ListItemIcon from '@material-ui/core/ListItemIcon'
|
|
||||||
import LibraryAddIcon from '@material-ui/icons/LibraryAdd'
|
|
||||||
import ListItemText from '@material-ui/core/ListItemText'
|
|
||||||
import ListItem from '@material-ui/core/ListItem'
|
|
||||||
import { torrentsHost } from '../utils/Hosts'
|
|
||||||
|
|
||||||
export default function AddDialog() {
|
|
||||||
const [open, setOpen] = React.useState(false)
|
|
||||||
|
|
||||||
const [magnet, setMagnet] = React.useState('')
|
|
||||||
const [title, setTitle] = React.useState('')
|
|
||||||
const [poster, setPoster] = React.useState('')
|
|
||||||
|
|
||||||
const handleClickOpen = () => {
|
|
||||||
setOpen(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
const inputMagnet = (event) => {
|
|
||||||
setMagnet(event.target.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
const inputTitle = (event) => {
|
|
||||||
setTitle(event.target.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
const inputPoster = (event) => {
|
|
||||||
setPoster(event.target.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCloseSave = () => {
|
|
||||||
try {
|
|
||||||
if (!magnet) return
|
|
||||||
|
|
||||||
fetch(torrentsHost(), {
|
|
||||||
method: 'post',
|
|
||||||
body: JSON.stringify({
|
|
||||||
action: 'add',
|
|
||||||
link: magnet,
|
|
||||||
title: title,
|
|
||||||
poster: poster,
|
|
||||||
save_to_db: true,
|
|
||||||
}),
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json, text/plain, */*',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
setOpen(false)
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
setOpen(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<ListItem button key="Add" onClick={handleClickOpen}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<LibraryAddIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Add" />
|
|
||||||
</ListItem>
|
|
||||||
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title" fullWidth={true}>
|
|
||||||
<DialogTitle id="form-dialog-title">Add Magnet</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<DialogContentText>Add magnet or link to torrent file:</DialogContentText>
|
|
||||||
<TextField onChange={inputTitle} margin="dense" id="title" label="Title" type="text" fullWidth />
|
|
||||||
<TextField onChange={inputPoster} margin="dense" id="poster" label="Poster" type="url" fullWidth />
|
|
||||||
<TextField onChange={inputMagnet} autoFocus margin="dense" id="magnet" label="Magnet" type="text" fullWidth />
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={handleClose} color="primary" variant="outlined">
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button onClick={handleCloseSave} color="primary" variant="outlined">
|
|
||||||
Add
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
64
web/src/components/Add/AddDialog.js
Normal file
64
web/src/components/Add/AddDialog.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
import Button from '@material-ui/core/Button'
|
||||||
|
import TextField from '@material-ui/core/TextField'
|
||||||
|
import Dialog from '@material-ui/core/Dialog'
|
||||||
|
import DialogActions from '@material-ui/core/DialogActions'
|
||||||
|
import DialogContent from '@material-ui/core/DialogContent'
|
||||||
|
import DialogTitle from '@material-ui/core/DialogTitle'
|
||||||
|
import { torrentsHost } from '../../utils/Hosts'
|
||||||
|
|
||||||
|
export default function AddDialog({ handleClose }) {
|
||||||
|
const [magnet, setMagnet] = useState('')
|
||||||
|
const [title, setTitle] = useState('')
|
||||||
|
const [poster, setPoster] = useState('')
|
||||||
|
|
||||||
|
const inputMagnet = ({ target: { value } }) => setMagnet(value)
|
||||||
|
const inputTitle = ({ target: { value } }) => setTitle(value)
|
||||||
|
const inputPoster = ({ target: { value } }) => setPoster(value)
|
||||||
|
|
||||||
|
const handleCloseSave = () => {
|
||||||
|
try {
|
||||||
|
if (!magnet) return
|
||||||
|
|
||||||
|
fetch(torrentsHost(), {
|
||||||
|
method: 'post',
|
||||||
|
body: JSON.stringify({
|
||||||
|
action: 'add',
|
||||||
|
link: magnet,
|
||||||
|
title: title,
|
||||||
|
poster: poster,
|
||||||
|
save_to_db: true,
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json, text/plain, */*',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
handleClose()
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open onClose={handleClose} aria-labelledby="form-dialog-title" fullWidth>
|
||||||
|
<DialogTitle id="form-dialog-title">Add magnet or link to torrent file</DialogTitle>
|
||||||
|
|
||||||
|
<DialogContent>
|
||||||
|
<TextField onChange={inputTitle} margin="dense" id="title" label="Title" type="text" fullWidth />
|
||||||
|
<TextField onChange={inputPoster} margin="dense" id="poster" label="Poster" type="url" fullWidth />
|
||||||
|
<TextField onChange={inputMagnet} autoFocus margin="dense" id="magnet" label="Magnet or torrent file link" type="text" fullWidth />
|
||||||
|
</DialogContent>
|
||||||
|
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={handleClose} color="primary" variant="outlined">
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button disabled={!magnet} onClick={handleCloseSave} color="primary" variant="outlined">
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
26
web/src/components/Add/index.js
Normal file
26
web/src/components/Add/index.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
import ListItemIcon from '@material-ui/core/ListItemIcon'
|
||||||
|
import LibraryAddIcon from '@material-ui/icons/LibraryAdd'
|
||||||
|
import ListItemText from '@material-ui/core/ListItemText'
|
||||||
|
import ListItem from '@material-ui/core/ListItem'
|
||||||
|
import AddDialog from './AddDialog'
|
||||||
|
|
||||||
|
export default function AddDialogButton() {
|
||||||
|
const [isDialogOpen, setIsDialogOpen] = useState(false)
|
||||||
|
|
||||||
|
const handleClickOpen = () => setIsDialogOpen(true)
|
||||||
|
const handleClose = () => setIsDialogOpen(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<ListItem button key="Add" onClick={handleClickOpen}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<LibraryAddIcon />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary="Add from link" />
|
||||||
|
</ListItem>
|
||||||
|
|
||||||
|
{isDialogOpen && <AddDialog handleClose={handleClose} />}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
import React, { useEffect } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import { makeStyles, useTheme } from '@material-ui/core/styles'
|
import { useTheme } from '@material-ui/core/styles'
|
||||||
import Drawer from '@material-ui/core/Drawer'
|
import Drawer from '@material-ui/core/Drawer'
|
||||||
import AppBar from '@material-ui/core/AppBar'
|
import AppBar from '@material-ui/core/AppBar'
|
||||||
import Toolbar from '@material-ui/core/Toolbar'
|
import Toolbar from '@material-ui/core/Toolbar'
|
||||||
import List from '@material-ui/core/List'
|
import List from '@material-ui/core/List'
|
||||||
import CssBaseline from '@material-ui/core/CssBaseline'
|
|
||||||
import Typography from '@material-ui/core/Typography'
|
import Typography from '@material-ui/core/Typography'
|
||||||
import Divider from '@material-ui/core/Divider'
|
import Divider from '@material-ui/core/Divider'
|
||||||
import IconButton from '@material-ui/core/IconButton'
|
import IconButton from '@material-ui/core/IconButton'
|
||||||
@@ -19,86 +18,22 @@ import ListItemText from '@material-ui/core/ListItemText'
|
|||||||
import ListIcon from '@material-ui/icons/List'
|
import ListIcon from '@material-ui/icons/List'
|
||||||
import PowerSettingsNewIcon from '@material-ui/icons/PowerSettingsNew'
|
import PowerSettingsNewIcon from '@material-ui/icons/PowerSettingsNew'
|
||||||
|
|
||||||
import TorrentList from './TorrentList'
|
import TorrentList from '../TorrentList'
|
||||||
import { Box } from '@material-ui/core'
|
|
||||||
|
|
||||||
import AddDialog from './Add'
|
import AddDialogButton from '../Add'
|
||||||
import RemoveAll from './RemoveAll'
|
import RemoveAll from '../RemoveAll'
|
||||||
import SettingsDialog from './Settings'
|
import SettingsDialog from '../Settings'
|
||||||
import AboutDialog from './About'
|
import AboutDialog from '../About'
|
||||||
import { playlistAllHost, shutdownHost, torrserverHost } from '../utils/Hosts'
|
import { playlistAllHost, shutdownHost, torrserverHost } from '../../utils/Hosts'
|
||||||
import DonateDialog from './Donate'
|
import DonateDialog from '../Donate'
|
||||||
import UploadDialog from './Upload'
|
import UploadDialog from '../Upload'
|
||||||
|
import useStyles from './useStyles'
|
||||||
const drawerWidth = 240
|
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
|
||||||
root: {
|
|
||||||
display: 'flex',
|
|
||||||
},
|
|
||||||
appBar: {
|
|
||||||
zIndex: theme.zIndex.drawer + 1,
|
|
||||||
transition: theme.transitions.create(['width', 'margin'], {
|
|
||||||
easing: theme.transitions.easing.sharp,
|
|
||||||
duration: theme.transitions.duration.leavingScreen,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
appBarShift: {
|
|
||||||
marginLeft: drawerWidth,
|
|
||||||
width: `calc(100% - ${drawerWidth}px)`,
|
|
||||||
transition: theme.transitions.create(['width', 'margin'], {
|
|
||||||
easing: theme.transitions.easing.sharp,
|
|
||||||
duration: theme.transitions.duration.enteringScreen,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
menuButton: {
|
|
||||||
marginRight: 36,
|
|
||||||
},
|
|
||||||
hide: {
|
|
||||||
display: 'none',
|
|
||||||
},
|
|
||||||
drawer: {
|
|
||||||
width: drawerWidth,
|
|
||||||
flexShrink: 0,
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
},
|
|
||||||
drawerOpen: {
|
|
||||||
width: drawerWidth,
|
|
||||||
transition: theme.transitions.create('width', {
|
|
||||||
easing: theme.transitions.easing.sharp,
|
|
||||||
duration: theme.transitions.duration.enteringScreen,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
drawerClose: {
|
|
||||||
transition: theme.transitions.create('width', {
|
|
||||||
easing: theme.transitions.easing.sharp,
|
|
||||||
duration: theme.transitions.duration.leavingScreen,
|
|
||||||
}),
|
|
||||||
overflowX: 'hidden',
|
|
||||||
width: theme.spacing(7) + 1,
|
|
||||||
[theme.breakpoints.up('sm')]: {
|
|
||||||
width: theme.spacing(9) + 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
toolbar: {
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'flex-end',
|
|
||||||
padding: theme.spacing(0, 1),
|
|
||||||
// necessary for content to be below app bar
|
|
||||||
...theme.mixins.toolbar,
|
|
||||||
},
|
|
||||||
content: {
|
|
||||||
flexGrow: 1,
|
|
||||||
padding: theme.spacing(3),
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
|
|
||||||
export default function MiniDrawer() {
|
export default function MiniDrawer() {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
const [open, setOpen] = React.useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const [tsVersion, setTSVersion] = React.useState('')
|
const [tsVersion, setTSVersion] = useState('')
|
||||||
|
|
||||||
const handleDrawerOpen = () => {
|
const handleDrawerOpen = () => {
|
||||||
setOpen(true)
|
setOpen(true)
|
||||||
@@ -118,7 +53,6 @@ export default function MiniDrawer() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<CssBaseline />
|
|
||||||
<AppBar
|
<AppBar
|
||||||
position="fixed"
|
position="fixed"
|
||||||
className={clsx(classes.appBar, {
|
className={clsx(classes.appBar, {
|
||||||
@@ -142,6 +76,7 @@ export default function MiniDrawer() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
|
|
||||||
<Drawer
|
<Drawer
|
||||||
variant="permanent"
|
variant="permanent"
|
||||||
className={clsx(classes.drawer, {
|
className={clsx(classes.drawer, {
|
||||||
@@ -156,11 +91,15 @@ export default function MiniDrawer() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={classes.toolbar}>
|
<div className={classes.toolbar}>
|
||||||
<IconButton onClick={handleDrawerClose}>{theme.direction === 'rtl' ? <ChevronRightIcon /> : <ChevronLeftIcon />}</IconButton>
|
<IconButton onClick={handleDrawerClose}>
|
||||||
|
{theme.direction === 'rtl' ? <ChevronRightIcon /> : <ChevronLeftIcon />}
|
||||||
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<List>
|
<List>
|
||||||
<AddDialog />
|
<AddDialogButton />
|
||||||
<UploadDialog />
|
<UploadDialog />
|
||||||
<RemoveAll />
|
<RemoveAll />
|
||||||
<ListItem button component="a" key="Playlist all torrents" target="_blank" href={playlistAllHost()}>
|
<ListItem button component="a" key="Playlist all torrents" target="_blank" href={playlistAllHost()}>
|
||||||
@@ -170,10 +109,11 @@ export default function MiniDrawer() {
|
|||||||
<ListItemText primary="Playlist all torrents" />
|
<ListItemText primary="Playlist all torrents" />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<List>
|
<List>
|
||||||
<SettingsDialog />
|
<SettingsDialog />
|
||||||
<DonateDialog />
|
|
||||||
<AboutDialog />
|
<AboutDialog />
|
||||||
<ListItem button key="Close server" onClick={() => fetch(shutdownHost())}>
|
<ListItem button key="Close server" onClick={() => fetch(shutdownHost())}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
@@ -182,12 +122,15 @@ export default function MiniDrawer() {
|
|||||||
<ListItemText primary="Close server" />
|
<ListItemText primary="Close server" />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
<Divider />
|
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
|
||||||
<main className={classes.content}>
|
<main className={classes.content}>
|
||||||
<Box m="5em" />
|
<div className={classes.toolbar} />
|
||||||
|
|
||||||
<TorrentList />
|
<TorrentList />
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<DonateDialog />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
65
web/src/components/Appbar/useStyles.js
Normal file
65
web/src/components/Appbar/useStyles.js
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
|
|
||||||
|
const drawerWidth = 240
|
||||||
|
|
||||||
|
export default makeStyles((theme) => ({
|
||||||
|
root: {
|
||||||
|
display: 'flex',
|
||||||
|
},
|
||||||
|
appBar: {
|
||||||
|
zIndex: theme.zIndex.drawer + 1,
|
||||||
|
transition: theme.transitions.create(['width', 'margin'], {
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
duration: theme.transitions.duration.leavingScreen,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
appBarShift: {
|
||||||
|
marginLeft: drawerWidth,
|
||||||
|
width: `calc(100% - ${drawerWidth}px)`,
|
||||||
|
transition: theme.transitions.create(['width', 'margin'], {
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
duration: theme.transitions.duration.enteringScreen,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
menuButton: {
|
||||||
|
marginRight: 36,
|
||||||
|
},
|
||||||
|
hide: {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
drawer: {
|
||||||
|
width: drawerWidth,
|
||||||
|
flexShrink: 0,
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
},
|
||||||
|
drawerOpen: {
|
||||||
|
width: drawerWidth,
|
||||||
|
transition: theme.transitions.create('width', {
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
duration: theme.transitions.duration.enteringScreen,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
drawerClose: {
|
||||||
|
transition: theme.transitions.create('width', {
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
duration: theme.transitions.duration.leavingScreen,
|
||||||
|
}),
|
||||||
|
overflowX: 'hidden',
|
||||||
|
width: theme.spacing(7) + 1,
|
||||||
|
[theme.breakpoints.up('sm')]: {
|
||||||
|
width: theme.spacing(9) + 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
toolbar: {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
padding: theme.spacing(0, 1),
|
||||||
|
// necessary for content to be below app bar
|
||||||
|
...theme.mixins.toolbar,
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
flexGrow: 1,
|
||||||
|
padding: theme.spacing(3),
|
||||||
|
},
|
||||||
|
}))
|
||||||
@@ -6,44 +6,44 @@ import DialogTitle from '@material-ui/core/DialogTitle'
|
|||||||
import DialogContent from '@material-ui/core/DialogContent'
|
import DialogContent from '@material-ui/core/DialogContent'
|
||||||
import { cacheHost } from '../utils/Hosts'
|
import { cacheHost } from '../utils/Hosts'
|
||||||
|
|
||||||
const style = {
|
// const style = {
|
||||||
cache: {
|
// cache: {
|
||||||
paddingLeft: "6px",
|
// paddingLeft: "6px",
|
||||||
paddingRight: "2px",
|
// paddingRight: "2px",
|
||||||
lineHeight: "11px",
|
// lineHeight: "11px",
|
||||||
},
|
// },
|
||||||
piece: {
|
// piece: {
|
||||||
width: "12px",
|
// width: "12px",
|
||||||
height: "12px",
|
// height: "12px",
|
||||||
backgroundColor: "#eef2f4",
|
// backgroundColor: "#eef2f4",
|
||||||
border: "1px solid #eef2f4",
|
// border: "1px solid #eef2f4",
|
||||||
display: "inline-block",
|
// display: "inline-block",
|
||||||
marginRight: "1px",
|
// marginRight: "1px",
|
||||||
},
|
// },
|
||||||
pieceComplete: {
|
// pieceComplete: {
|
||||||
backgroundColor: "#3fb57a",
|
// backgroundColor: "#3fb57a",
|
||||||
borderColor: "#3fb57a",
|
// borderColor: "#3fb57a",
|
||||||
},
|
// },
|
||||||
pieceLoading: {
|
// pieceLoading: {
|
||||||
backgroundColor: "#00d0d0",
|
// backgroundColor: "#00d0d0",
|
||||||
borderColor: "#00d0d0",
|
// borderColor: "#00d0d0",
|
||||||
},
|
// },
|
||||||
readerRange: {
|
// readerRange: {
|
||||||
borderColor: "#9a9aff !important",
|
// borderColor: "#9a9aff !important",
|
||||||
},
|
// },
|
||||||
pieceReader: {
|
// pieceReader: {
|
||||||
borderColor: "#000000 !important",
|
// borderColor: "#000000 !important",
|
||||||
},
|
// },
|
||||||
pieceProgress: {
|
// pieceProgress: {
|
||||||
position: "relative",
|
// position: "relative",
|
||||||
zIndex: "1",
|
// zIndex: "1",
|
||||||
backgroundColor: "#009090",
|
// backgroundColor: "#009090",
|
||||||
|
|
||||||
left: "-1px",
|
// left: "-1px",
|
||||||
top: "-1px",
|
// top: "-1px",
|
||||||
width: "12px",
|
// width: "12px",
|
||||||
},
|
// },
|
||||||
}
|
// }
|
||||||
|
|
||||||
export default function DialogCacheInfo(props) {
|
export default function DialogCacheInfo(props) {
|
||||||
const [hash] = React.useState(props.hash)
|
const [hash] = React.useState(props.hash)
|
||||||
@@ -80,7 +80,7 @@ export default function DialogCacheInfo(props) {
|
|||||||
prc = (cache.Pieces[i].Size / cache.Pieces[i].Length * 100).toFixed(2)
|
prc = (cache.Pieces[i].Size / cache.Pieces[i].Length * 100).toFixed(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
cache.Readers.forEach((r, k) => {
|
cache.Readers.forEach(r => {
|
||||||
if (i >= r.Start && i <= r.End && i !== r.Reader)
|
if (i >= r.Start && i <= r.End && i !== r.Reader)
|
||||||
cls += " reader-range"
|
cls += " reader-range"
|
||||||
if (i === r.Reader) {
|
if (i === r.Reader) {
|
||||||
@@ -101,7 +101,7 @@ export default function DialogCacheInfo(props) {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<DialogTitle id="form-dialog-title">
|
<DialogTitle id="form-dialog-title">
|
||||||
<Typography fullWidth>
|
<Typography>
|
||||||
<b>Hash </b> {cache.Hash}
|
<b>Hash </b> {cache.Hash}
|
||||||
<br />
|
<br />
|
||||||
<b>Capacity </b> {humanizeSize(cache.Capacity)}
|
<b>Capacity </b> {humanizeSize(cache.Capacity)}
|
||||||
@@ -123,48 +123,55 @@ export default function DialogCacheInfo(props) {
|
|||||||
<b>Status </b> {cache.Torrent && cache.Torrent.stat_string && cache.Torrent.stat_string}
|
<b>Status </b> {cache.Torrent && cache.Torrent.stat_string && cache.Torrent.stat_string}
|
||||||
</Typography>
|
</Typography>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<div className="cache">
|
<div className="cache">
|
||||||
{pMap.map((itm) => <span className={itm.class} title={itm.info}>{itm.prc>0 && itm.prc<100 && (<div className="piece-progress" style={{height: itm.prc/100*12+"px"}}></div>)}</span>)}
|
{pMap.map(itm => (
|
||||||
|
<span key={itm.info} className={itm.class} title={itm.info}>
|
||||||
|
{itm.prc > 0 && itm.prc < 100 && (
|
||||||
|
<div className="piece-progress" style={{ height: itm.prc / 100 * 12 + "px" }}></div>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCacheMap(cache) {
|
// function getCacheMap(cache) {
|
||||||
if (!cache || !cache.PiecesCount) return ''
|
// if (!cache || !cache.PiecesCount) return ''
|
||||||
var html = ''
|
// var html = ''
|
||||||
for (let i = 0; i < cache.PiecesCount; i++) {
|
// for (let i = 0; i < cache.PiecesCount; i++) {
|
||||||
html += "<span class='piece"
|
// html += "<span class='piece"
|
||||||
let info = i
|
// let info = i
|
||||||
var prcDiv = ""
|
// var prcDiv = ""
|
||||||
if (cache.Pieces && cache.Pieces[i]) {
|
// if (cache.Pieces && cache.Pieces[i]) {
|
||||||
let prc = (cache.Pieces[i].Size/cache.Pieces[i].Length*100).toFixed(2)
|
// let prc = (cache.Pieces[i].Size / cache.Pieces[i].Length * 100).toFixed(2)
|
||||||
let piece = cache.Pieces[i]
|
// let piece = cache.Pieces[i]
|
||||||
if (piece.Completed && piece.Size >= piece.Length) {
|
// if (piece.Completed && piece.Size >= piece.Length) {
|
||||||
html += ' piece-complete'
|
// html += ' piece-complete'
|
||||||
info += ' 100%'
|
// info += ' 100%'
|
||||||
}else {
|
// } else {
|
||||||
html += ' piece-loading'
|
// html += ' piece-loading'
|
||||||
info += ' ' + prc + '%'
|
// info += ' ' + prc + '%'
|
||||||
prcDiv = "<div class='piece-progress' style='height: "+prc+"%;'></div>"
|
// prcDiv = "<div class='piece-progress' style='height: " + prc + "%;'></div>"
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
cache.Readers.forEach((r,k)=> {
|
// cache.Readers.forEach((r, k) => {
|
||||||
if (i >= r.Start && i <= r.End && i !== r.Reader)
|
// if (i >= r.Start && i <= r.End && i !== r.Reader)
|
||||||
html += ' reader-range'
|
// html += ' reader-range'
|
||||||
if (i === r.Reader) {
|
// if (i === r.Reader) {
|
||||||
html += ' piece-reader'
|
// html += ' piece-reader'
|
||||||
info += ' reader'
|
// info += ' reader'
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
html += "' title='" + info + "'>"
|
// html += "' title='" + info + "'>"
|
||||||
html += prcDiv
|
// html += prcDiv
|
||||||
html += "</span>"
|
// html += "</span>"
|
||||||
}
|
// }
|
||||||
return html
|
// return html
|
||||||
}
|
// }
|
||||||
|
|
||||||
function getCache(hash, callback) {
|
function getCache(hash, callback) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -20,21 +20,25 @@ export default function DonateDialog() {
|
|||||||
const [open, setOpen] = React.useState(false)
|
const [open, setOpen] = React.useState(false)
|
||||||
const [snakeOpen, setSnakeOpen] = React.useState(true)
|
const [snakeOpen, setSnakeOpen] = React.useState(true)
|
||||||
|
|
||||||
const handleClickOpen = () => {
|
// NOT USED FOR NOW
|
||||||
setOpen(true)
|
// const handleClickOpen = () => {
|
||||||
}
|
// setOpen(true)
|
||||||
|
// }
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ListItem button key="Donate" onClick={handleClickOpen}>
|
{/* !!!!!!!!!!! Should be removed or moved to sidebar because it is not visible. It is hiddent behind header */}
|
||||||
|
{/* <ListItem button key="Donate" onClick={handleClickOpen}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<CreditCardIcon />
|
<CreditCardIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText primary="Donate" />
|
<ListItemText primary="Donate" />
|
||||||
</ListItem>
|
</ListItem> */}
|
||||||
|
{/* !!!!!!!!!!!!!!!!!!!! */}
|
||||||
|
|
||||||
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title" fullWidth>
|
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title" fullWidth>
|
||||||
<DialogTitle id="form-dialog-title">Donate</DialogTitle>
|
<DialogTitle id="form-dialog-title">Donate</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import ListItemText from '@material-ui/core/ListItemText'
|
|||||||
import DeleteIcon from '@material-ui/icons/Delete'
|
import DeleteIcon from '@material-ui/icons/Delete'
|
||||||
import { torrentsHost } from '../utils/Hosts'
|
import { torrentsHost } from '../utils/Hosts'
|
||||||
|
|
||||||
export default function RemoveAll() {
|
|
||||||
const fnRemoveAll = () => {
|
const fnRemoveAll = () => {
|
||||||
fetch(torrentsHost(), {
|
fetch(torrentsHost(), {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
@@ -29,6 +28,8 @@ export default function RemoveAll() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function RemoveAll() {
|
||||||
return (
|
return (
|
||||||
<ListItem button key="Remove all" onClick={fnRemoveAll}>
|
<ListItem button key="Remove all" onClick={fnRemoveAll}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
import React, { useEffect, useRef } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import ButtonGroup from '@material-ui/core/ButtonGroup'
|
|
||||||
import Button from '@material-ui/core/Button'
|
import Button from '@material-ui/core/Button'
|
||||||
|
|
||||||
import 'fontsource-roboto'
|
import 'fontsource-roboto'
|
||||||
|
|
||||||
|
import HeightIcon from '@material-ui/icons/Height';
|
||||||
import CloseIcon from '@material-ui/icons/Close';
|
import CloseIcon from '@material-ui/icons/Close';
|
||||||
import DeleteIcon from '@material-ui/icons/Delete'
|
import DeleteIcon from '@material-ui/icons/Delete'
|
||||||
import Typography from '@material-ui/core/Typography'
|
|
||||||
import ListItem from '@material-ui/core/ListItem'
|
|
||||||
import DialogActions from '@material-ui/core/DialogActions'
|
import DialogActions from '@material-ui/core/DialogActions'
|
||||||
import Dialog from '@material-ui/core/Dialog'
|
import Dialog from '@material-ui/core/Dialog'
|
||||||
|
|
||||||
import { getPeerString, humanizeSize } from '../utils/Utils'
|
import { getPeerString, humanizeSize } from '../../utils/Utils'
|
||||||
|
|
||||||
import DialogTorrentInfo from './DialogTorrentInfo'
|
import DialogTorrentInfo from '../DialogTorrentInfo'
|
||||||
import { torrentsHost } from '../utils/Hosts'
|
import { torrentsHost } from '../../utils/Hosts'
|
||||||
import DialogCacheInfo from './DialogCacheInfo'
|
import DialogCacheInfo from '../DialogCacheInfo'
|
||||||
import DataUsageIcon from '@material-ui/icons/DataUsage'
|
import DataUsageIcon from '@material-ui/icons/DataUsage'
|
||||||
|
import { NoImageIcon } from '../../icons';
|
||||||
|
import { StyledButton, TorrentCard, TorrentCardButtons, TorrentCardDescription, TorrentCardDescriptionContent, TorrentCardDescriptionLabel, TorrentCardPoster } from './style';
|
||||||
|
|
||||||
export default function Torrent(props) {
|
export default function Torrent(props) {
|
||||||
const [open, setOpen] = React.useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const [showCache, setShowCache] = React.useState(false)
|
const [showCache, setShowCache] = useState(false)
|
||||||
const [torrent, setTorrent] = React.useState(props.torrent)
|
const [torrent, setTorrent] = useState(props.torrent)
|
||||||
const timerID = useRef(-1)
|
const timerID = useRef(-1)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -43,61 +43,74 @@ export default function Torrent(props) {
|
|||||||
}
|
}
|
||||||
}, [torrent.hash, open])
|
}, [torrent.hash, open])
|
||||||
|
|
||||||
|
const { title, name, poster, torrent_size, download_speed } = torrent
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
<ListItem>
|
|
||||||
<ButtonGroup style={{width:'100%',boxShadow:'2px 2px 2px gray'}} disableElevation variant="contained" color="primary">
|
<TorrentCard>
|
||||||
<Button
|
<TorrentCardPoster isPoster={poster}>
|
||||||
style={{width: '100%', justifyContent:'start'}}
|
{poster
|
||||||
onClick={() => {
|
? <img src={poster} alt="poster" />
|
||||||
setShowCache(false)
|
: <NoImageIcon />}
|
||||||
setOpen(true)
|
</TorrentCardPoster>
|
||||||
}}
|
|
||||||
>
|
<TorrentCardButtons>
|
||||||
{torrent.poster &&
|
<StyledButton
|
||||||
<img src={torrent.poster} alt="" align="left" style={{width: 'auto',height:'100px',margin:'0 10px 0 0',borderRadius:'5px'}}/>
|
|
||||||
}
|
|
||||||
<Typography>
|
|
||||||
{torrent.title ? torrent.title : torrent.name}
|
|
||||||
{torrent.torrent_size > 0 ? ' | ' + humanizeSize(torrent.torrent_size) : ''}
|
|
||||||
{torrent.download_speed > 0 ? ' | ' + humanizeSize(torrent.download_speed) + '/sec' : ''}
|
|
||||||
{getPeerString(torrent) ? ' | ' + getPeerString(torrent) : '' }
|
|
||||||
</Typography>
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowCache(true)
|
setShowCache(true)
|
||||||
setOpen(true)
|
setOpen(true)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DataUsageIcon />
|
<DataUsageIcon />
|
||||||
<Typography>Cache</Typography>
|
Cache
|
||||||
</Button>
|
</StyledButton>
|
||||||
<Button
|
|
||||||
onClick={() => {
|
<StyledButton
|
||||||
dropTorrent(torrent)
|
onClick={() => dropTorrent(torrent)}
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
<Typography>Drop</Typography>
|
Drop
|
||||||
</Button>
|
</StyledButton>
|
||||||
<Button
|
|
||||||
onClick={() => {
|
<StyledButton
|
||||||
deleteTorrent(torrent)
|
onClick={() => deleteTorrent(torrent)}
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
<Typography>Delete</Typography>
|
Delete
|
||||||
</Button>
|
</StyledButton>
|
||||||
</ButtonGroup>
|
|
||||||
</ListItem>
|
<StyledButton
|
||||||
|
onClick={() => {
|
||||||
|
setShowCache(false)
|
||||||
|
setOpen(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<HeightIcon />
|
||||||
|
Details
|
||||||
|
</StyledButton>
|
||||||
|
</TorrentCardButtons>
|
||||||
|
|
||||||
|
<TorrentCardDescription>
|
||||||
|
<TorrentCardDescriptionLabel>Name</TorrentCardDescriptionLabel>
|
||||||
|
<TorrentCardDescriptionContent>{title || name}</TorrentCardDescriptionContent>
|
||||||
|
|
||||||
|
<TorrentCardDescriptionLabel>Size</TorrentCardDescriptionLabel>
|
||||||
|
<TorrentCardDescriptionContent>{torrent_size > 0 && humanizeSize(torrent_size)}</TorrentCardDescriptionContent>
|
||||||
|
|
||||||
|
<TorrentCardDescriptionLabel>Download speed</TorrentCardDescriptionLabel>
|
||||||
|
<TorrentCardDescriptionContent>{download_speed > 0 ? humanizeSize(download_speed) : '---'}</TorrentCardDescriptionContent>
|
||||||
|
|
||||||
|
<TorrentCardDescriptionLabel>Peers</TorrentCardDescriptionLabel>
|
||||||
|
<TorrentCardDescriptionContent>{getPeerString(torrent) || '---'}</TorrentCardDescriptionContent>
|
||||||
|
</TorrentCardDescription>
|
||||||
|
</TorrentCard>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
open={open}
|
open={open}
|
||||||
onClose={() => {
|
onClose={() => setOpen(false)}
|
||||||
setOpen(false)
|
|
||||||
}}
|
|
||||||
aria-labelledby="form-dialog-title"
|
aria-labelledby="form-dialog-title"
|
||||||
fullWidth={true}
|
fullWidth
|
||||||
maxWidth={'lg'}
|
maxWidth={'lg'}
|
||||||
>
|
>
|
||||||
{!showCache ? <DialogTorrentInfo torrent={(open, torrent)} /> : <DialogCacheInfo hash={(open, torrent.hash)} />}
|
{!showCache ? <DialogTorrentInfo torrent={(open, torrent)} /> : <DialogCacheInfo hash={(open, torrent.hash)} />}
|
||||||
@@ -105,15 +118,13 @@ export default function Torrent(props) {
|
|||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => {
|
onClick={() => setOpen(false)}
|
||||||
setOpen(false)
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
OK
|
OK
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
98
web/src/components/Torrent/style.js
Normal file
98
web/src/components/Torrent/style.js
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
|
export const TorrentCard = styled.div`
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: 5px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
grid-template-rows: 175px minmax(min-content, 1fr);
|
||||||
|
grid-template-areas:
|
||||||
|
"poster buttons"
|
||||||
|
"description description";
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
background: #3fb57a;
|
||||||
|
box-shadow:
|
||||||
|
0px 2px 4px -1px rgb(0 0 0 / 20%),
|
||||||
|
0px 4px 5px 0px rgb(0 0 0 / 14%),
|
||||||
|
0px 1px 10px 0px rgb(0 0 0 / 12%);
|
||||||
|
`
|
||||||
|
|
||||||
|
export const TorrentCardPoster = styled.div`
|
||||||
|
grid-area: poster;
|
||||||
|
border-radius: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
${({ isPoster }) => isPoster ? css`
|
||||||
|
img {
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
`: css`
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
background: #74c39c;
|
||||||
|
border: 1px solid;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
}
|
||||||
|
`};
|
||||||
|
`
|
||||||
|
export const TorrentCardButtons = styled.div`
|
||||||
|
grid-area: buttons;
|
||||||
|
display: grid;
|
||||||
|
gap: 5px;
|
||||||
|
`
|
||||||
|
export const TorrentCardDescription = styled.div`
|
||||||
|
grid-area: description;
|
||||||
|
background: #74c39c;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 5px;
|
||||||
|
`
|
||||||
|
|
||||||
|
export const TorrentCardDescriptionLabel = styled.div`
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.4px;
|
||||||
|
color: #216e47;
|
||||||
|
`
|
||||||
|
|
||||||
|
export const TorrentCardDescriptionContent = styled.div`
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
`
|
||||||
|
|
||||||
|
export const StyledButton = styled.button`
|
||||||
|
border-radius: 5px;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 0.2s;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
text-transform: uppercase;
|
||||||
|
background: #216e47;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-family: "Roboto", "Helvetica", "Arial", sans-serif;
|
||||||
|
letter-spacing: 0.009em;
|
||||||
|
|
||||||
|
> :first-child {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
|
||||||
|
> :first-child {
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
:hover {
|
||||||
|
background: #2a7e54;
|
||||||
|
}
|
||||||
|
`
|
||||||
@@ -1,13 +1,18 @@
|
|||||||
import React, { useEffect, useRef } from 'react'
|
import styled from 'styled-components';
|
||||||
import Container from '@material-ui/core/Container'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import Torrent from './Torrent'
|
import Torrent from './Torrent'
|
||||||
import List from '@material-ui/core/List'
|
|
||||||
import { Typography } from '@material-ui/core'
|
import { Typography } from '@material-ui/core'
|
||||||
import { torrentsHost } from '../utils/Hosts'
|
import { torrentsHost } from '../utils/Hosts'
|
||||||
|
|
||||||
export default function TorrentList(props, onChange) {
|
const TorrentListWrapper = styled.div`
|
||||||
const [torrents, setTorrents] = React.useState([])
|
display: grid;
|
||||||
const [offline, setOffline] = React.useState(true)
|
grid-template-columns: repeat(auto-fit, minmax(250px, 350px));
|
||||||
|
gap: 30px;
|
||||||
|
`
|
||||||
|
|
||||||
|
export default function TorrentList() {
|
||||||
|
const [torrents, setTorrents] = useState([])
|
||||||
|
const [offline, setOffline] = useState(true)
|
||||||
const timerID = useRef(-1)
|
const timerID = useRef(-1)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -25,9 +30,11 @@ export default function TorrentList(props, onChange) {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<TorrentListWrapper>
|
||||||
<Container maxWidth="lg">{!offline ? <List>{torrents && torrents.map((torrent) => <Torrent key={torrent.hash} torrent={torrent} />)}</List> : <Typography>Offline</Typography>}</Container>
|
{offline ? <Typography>Offline</Typography> : (
|
||||||
</React.Fragment>
|
torrents && torrents.map(torrent => <Torrent key={torrent.hash} torrent={torrent} />)
|
||||||
|
)}
|
||||||
|
</TorrentListWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
11
web/src/icons/index.js
Normal file
11
web/src/icons/index.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export const NoImageIcon = () => (
|
||||||
|
<svg height='80px' width='80px' fill="#248a57"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlnsXlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 100" enableBackground="new 0 0 100 100" xmlSpace="preserve">
|
||||||
|
<g>
|
||||||
|
<path d="M18.293,93.801c0.066,0.376,0.284,0.718,0.597,0.937c0.313,0.219,0.708,0.307,1.085,0.241l70.058-12.353 c0.376-0.066,0.718-0.284,0.937-0.597c0.219-0.313,0.307-0.708,0.24-1.085l-9.502-53.891c-0.139-0.79-0.892-1.317-1.682-1.178 l-19.402,3.421L47.997,14.16c0.241-0.706,0.375-1.456,0.375-2.229c0-0.399-0.035-0.804-0.106-1.209C47.671,7.363,44.757,5,41.455,5 c-0.4,0-0.804,0.035-1.209,0.106h0c-3.359,0.595-5.723,3.509-5.723,6.812c0,0.4,0.035,0.804,0.106,1.209 c0.178,1.005,0.567,1.918,1.109,2.709l-6.875,19.061L9.968,38.228c-0.79,0.139-1.317,0.892-1.177,1.682L18.293,93.801z M40.75,7.966L40.75,7.966c0.239-0.042,0.474-0.062,0.705-0.062c1.909,0,3.612,1.373,3.953,3.324v0 c0.042,0.238,0.062,0.473,0.062,0.704c0,1.908-1.373,3.612-3.323,3.953h0.001c-0.238,0.042-0.473,0.062-0.705,0.062 c-1.908,0-3.612-1.373-3.953-3.323c-0.042-0.238-0.062-0.473-0.062-0.705C37.427,10.01,38.799,8.306,40.75,7.966z M38.059,17.96 c1.012,0.569,2.17,0.89,3.383,0.89c0.399,0,0.804-0.034,1.208-0.106h0.001c1.48-0.263,2.766-0.976,3.743-1.974l10.935,13.108 L32.16,34.315L38.059,17.96z M29.978,37.648c0.136-0.004,0.268-0.029,0.396-0.07l29.75-5.246c0.134-0.006,0.266-0.027,0.395-0.07 l18.582-3.277l8.998,51.031L20.9,91.867l-8.998-51.032L29.978,37.648z"></path>
|
||||||
|
<path d="M49.984,75.561c0.809,0,1.627-0.065,2.449-0.199l0.001,0c7.425-1.213,12.701-7.627,12.701-14.919 c0-0.809-0.065-1.627-0.199-2.449c-1.213-7.425-7.626-12.701-14.919-12.701c-0.808,0-1.627,0.065-2.45,0.199 c-7.425,1.213-12.701,7.626-12.701,14.918c0,0.808,0.065,1.627,0.199,2.449C36.278,70.284,42.692,75.561,49.984,75.561z M51.967,72.496c-0.668,0.109-1.33,0.161-1.983,0.161c-5.883,0-11.079-4.265-12.053-10.265c-0.109-0.668-0.161-1.33-0.161-1.983 c0-2.108,0.555-4.123,1.534-5.892l19.693,14.176C57.206,70.645,54.782,72.039,51.967,72.496z M48.034,48.357L48.034,48.357 c0.668-0.109,1.329-0.161,1.983-0.161c5.882,0,11.079,4.265,12.053,10.265c0.109,0.667,0.161,1.329,0.161,1.983 c0,2.109-0.556,4.127-1.536,5.897L41.001,52.163C42.791,50.21,45.217,48.814,48.034,48.357z"></path>
|
||||||
|
<polygon points="47.567,45.492 47.567,45.492 47.568,45.491 "></polygon>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user