temporary disabled donation button behind the header. All torrents are rendered with card style. Created GRID wrapper for holding all torrent cards. Empty posters will be rendered as NoImageIcon svg

This commit is contained in:
Daniel Shleifman
2021-05-24 17:04:20 +03:00
parent fcf4faff4a
commit af1534787e
11 changed files with 484 additions and 166 deletions

View File

@@ -1,6 +1,5 @@
import React from 'react'
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'
const baseTheme = createMuiTheme({
@@ -26,11 +25,9 @@ const baseTheme = createMuiTheme({
export default function App() {
return (
<React.Fragment>
<MuiThemeProvider theme={baseTheme}>
<CssBaseline />
<Appbar />
</MuiThemeProvider>
</React.Fragment>
<MuiThemeProvider theme={baseTheme}>
<CssBaseline />
<Appbar />
</MuiThemeProvider>
)
}

View File

@@ -1,11 +1,10 @@
import React, { useEffect } from 'react'
import { useEffect, useState } from 'react'
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 AppBar from '@material-ui/core/AppBar'
import Toolbar from '@material-ui/core/Toolbar'
import List from '@material-ui/core/List'
import CssBaseline from '@material-ui/core/CssBaseline'
import Typography from '@material-ui/core/Typography'
import Divider from '@material-ui/core/Divider'
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 PowerSettingsNewIcon from '@material-ui/icons/PowerSettingsNew'
import TorrentList from './TorrentList'
import { Box } from '@material-ui/core'
import TorrentList from '../TorrentList'
import AddDialogButton from './Add'
import RemoveAll from './RemoveAll'
import SettingsDialog from './Settings'
import AboutDialog from './About'
import { playlistAllHost, shutdownHost, torrserverHost } from '../utils/Hosts'
import DonateDialog from './Donate'
import UploadDialog from './Upload'
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),
},
}))
import AddDialogButton from '../Add'
import RemoveAll from '../RemoveAll'
import SettingsDialog from '../Settings'
import AboutDialog from '../About'
import { playlistAllHost, shutdownHost, torrserverHost } from '../../utils/Hosts'
import DonateDialog from '../Donate'
import UploadDialog from '../Upload'
import useStyles from './useStyles'
export default function MiniDrawer() {
const classes = useStyles()
const theme = useTheme()
const [open, setOpen] = React.useState(false)
const [tsVersion, setTSVersion] = React.useState('')
const [open, setOpen] = useState(false)
const [tsVersion, setTSVersion] = useState('')
const handleDrawerOpen = () => {
setOpen(true)
@@ -118,7 +53,6 @@ export default function MiniDrawer() {
return (
<div className={classes.root}>
<CssBaseline />
<AppBar
position="fixed"
className={clsx(classes.appBar, {
@@ -142,6 +76,7 @@ export default function MiniDrawer() {
</Typography>
</Toolbar>
</AppBar>
<Drawer
variant="permanent"
className={clsx(classes.drawer, {
@@ -156,9 +91,13 @@ export default function MiniDrawer() {
}}
>
<div className={classes.toolbar}>
<IconButton onClick={handleDrawerClose}>{theme.direction === 'rtl' ? <ChevronRightIcon /> : <ChevronLeftIcon />}</IconButton>
<IconButton onClick={handleDrawerClose}>
{theme.direction === 'rtl' ? <ChevronRightIcon /> : <ChevronLeftIcon />}
</IconButton>
</div>
<Divider />
<List>
<AddDialogButton />
<UploadDialog />
@@ -170,10 +109,11 @@ export default function MiniDrawer() {
<ListItemText primary="Playlist all torrents" />
</ListItem>
</List>
<Divider />
<List>
<SettingsDialog />
<DonateDialog />
<AboutDialog />
<ListItem button key="Close server" onClick={() => fetch(shutdownHost())}>
<ListItemIcon>
@@ -182,12 +122,15 @@ export default function MiniDrawer() {
<ListItemText primary="Close server" />
</ListItem>
</List>
<Divider />
</Drawer>
<main className={classes.content}>
<Box m="5em" />
<div className={classes.toolbar} />
<TorrentList />
</main>
<DonateDialog />
</div>
)
}

View 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),
},
}))

View File

@@ -20,21 +20,25 @@ export default function DonateDialog() {
const [open, setOpen] = React.useState(false)
const [snakeOpen, setSnakeOpen] = React.useState(true)
const handleClickOpen = () => {
setOpen(true)
}
// NOT USED FOR NOW
// const handleClickOpen = () => {
// setOpen(true)
// }
const handleClose = () => {
setOpen(false)
}
return (
<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>
<CreditCardIcon />
</ListItemIcon>
<ListItemText primary="Donate" />
</ListItem>
</ListItem> */}
{/* !!!!!!!!!!!!!!!!!!!! */}
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title" fullWidth>
<DialogTitle id="form-dialog-title">Donate</DialogTitle>
<DialogContent>
@@ -63,12 +67,12 @@ export default function DonateDialog() {
horizontal: 'center',
}}
open={snakeOpen}
onClose={()=>{setSnakeOpen(false)}}
onClose={() => { setSnakeOpen(false) }}
autoHideDuration={6000}
message="Donate?"
action={
<React.Fragment>
<IconButton size="small" aria-label="close" color="inherit" onClick={()=>{
<IconButton size="small" aria-label="close" color="inherit" onClick={() => {
setSnakeOpen(false)
setOpen(true)
}}>

View File

@@ -1,27 +1,27 @@
import React, { useEffect, useRef } from 'react'
import ButtonGroup from '@material-ui/core/ButtonGroup'
import { useEffect, useRef, useState } from 'react'
import Button from '@material-ui/core/Button'
import 'fontsource-roboto'
import HeightIcon from '@material-ui/icons/Height';
import CloseIcon from '@material-ui/icons/Close';
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 Dialog from '@material-ui/core/Dialog'
import { getPeerString, humanizeSize } from '../utils/Utils'
import { getPeerString, humanizeSize } from '../../utils/Utils'
import DialogTorrentInfo from './DialogTorrentInfo'
import { torrentsHost } from '../utils/Hosts'
import DialogCacheInfo from './DialogCacheInfo'
import DialogTorrentInfo from '../DialogTorrentInfo'
import { torrentsHost } from '../../utils/Hosts'
import DialogCacheInfo from '../DialogCacheInfo'
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) {
const [open, setOpen] = React.useState(false)
const [showCache, setShowCache] = React.useState(false)
const [torrent, setTorrent] = React.useState(props.torrent)
const [open, setOpen] = useState(false)
const [showCache, setShowCache] = useState(false)
const [torrent, setTorrent] = useState(props.torrent)
const timerID = useRef(-1)
useEffect(() => {
@@ -43,61 +43,74 @@ export default function Torrent(props) {
}
}, [torrent.hash, open])
const { title, name, poster, torrent_size, download_speed } = torrent
return (
<div>
<ListItem>
<ButtonGroup style={{width:'100%',boxShadow:'2px 2px 2px gray'}} disableElevation variant="contained" color="primary">
<Button
style={{width: '100%', justifyContent:'start'}}
onClick={() => {
setShowCache(false)
setOpen(true)
}}
>
{torrent.poster &&
<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
<>
<TorrentCard>
<TorrentCardPoster isPoster={poster}>
{poster
? <img src={poster} alt="poster" />
: <NoImageIcon />}
</TorrentCardPoster>
<TorrentCardButtons>
<StyledButton
onClick={() => {
setShowCache(true)
setOpen(true)
}}
>
<DataUsageIcon />
<Typography>Cache</Typography>
</Button>
<Button
onClick={() => {
dropTorrent(torrent)
}}
Cache
</StyledButton>
<StyledButton
onClick={() => dropTorrent(torrent)}
>
<CloseIcon />
<Typography>Drop</Typography>
</Button>
<Button
onClick={() => {
deleteTorrent(torrent)
}}
Drop
</StyledButton>
<StyledButton
onClick={() => deleteTorrent(torrent)}
>
<DeleteIcon />
<Typography>Delete</Typography>
</Button>
</ButtonGroup>
</ListItem>
Delete
</StyledButton>
<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
open={open}
onClose={() => {
setOpen(false)
}}
onClose={() => setOpen(false)}
aria-labelledby="form-dialog-title"
fullWidth={true}
fullWidth
maxWidth={'lg'}
>
{!showCache ? <DialogTorrentInfo torrent={(open, torrent)} /> : <DialogCacheInfo hash={(open, torrent.hash)} />}
@@ -105,15 +118,13 @@ export default function Torrent(props) {
<Button
variant="outlined"
color="primary"
onClick={() => {
setOpen(false)
}}
onClick={() => setOpen(false)}
>
OK
</Button>
</DialogActions>
</Dialog>
</div>
</>
)
}

View 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;
}
`

View File

@@ -1,13 +1,18 @@
import React, { useEffect, useRef } from 'react'
import Container from '@material-ui/core/Container'
import styled from 'styled-components';
import { useEffect, useRef, useState } from 'react'
import Torrent from './Torrent'
import List from '@material-ui/core/List'
import { Typography } from '@material-ui/core'
import { torrentsHost } from '../utils/Hosts'
export default function TorrentList(props, onChange) {
const [torrents, setTorrents] = React.useState([])
const [offline, setOffline] = React.useState(true)
const TorrentListWrapper = styled.div`
display: grid;
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)
useEffect(() => {
@@ -25,9 +30,11 @@ export default function TorrentList(props, onChange) {
}, [])
return (
<React.Fragment>
<Container maxWidth="lg">{!offline ? <List>{torrents && torrents.map((torrent) => <Torrent key={torrent.hash} torrent={torrent} />)}</List> : <Typography>Offline</Typography>}</Container>
</React.Fragment>
<TorrentListWrapper>
{offline ? <Typography>Offline</Typography> : (
torrents && torrents.map(torrent => <Torrent key={torrent.hash} torrent={torrent} />)
)}
</TorrentListWrapper>
)
}

11
web/src/icons/index.js Normal file
View 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>
)