Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
221 changes: 140 additions & 81 deletions frontend/src/components/DownloadCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ import StopCircleIcon from '@mui/icons-material/StopCircle'
import OpenInBrowserIcon from '@mui/icons-material/OpenInBrowser'
import SaveAltIcon from '@mui/icons-material/SaveAlt'

import ShareIcon from '@mui/icons-material/Share'
import ContentCopyIcon from '@mui/icons-material/ContentCopy'
import { useState } from 'react'
import { Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions } from '@mui/material'


type Props = {
download: RPCResult
onStop: () => void
Expand Down Expand Up @@ -55,97 +61,150 @@ const DownloadCard: React.FC<Props> = ({ download, onStop, onCopy }) => {
window.open(`${serverAddr}/filebrowser/d/${encoded}?token=${localStorage.getItem('token')}`)
}

const handleShare = () => {
const encoded = base64URLEncode(download.output.savedFilePath)
const link = `${serverAddr}/public/${encoded}`
setShareLink(link)
setShareOpen(true)
}

const [shareOpen, setShareOpen] = useState(false)
const [shareLink, setShareLink] = useState('')


return (
<Card>
<CardActionArea onClick={() => {
navigator.clipboard.writeText(download.info.url)
onCopy()
}}>
{download.info.thumbnail !== '' ?
<CardMedia
component="img"
height={180}
image={download.info.thumbnail}
/> :
<Skeleton variant="rectangular" height={180} />
}
{download.progress.percentage ?
<LinearProgress
variant="determinate"
value={percentageToNumber()}
color={isCompleted() ? "success" : "primary"}
/> :
null
}
<CardContent>
{download.info.title !== '' ?
<Typography gutterBottom variant="h6" component="div">
{ellipsis(download.info.title, 100)}
</Typography> :
<Skeleton />
<>
<Card>
<CardActionArea onClick={() => {
navigator.clipboard.writeText(download.info.url)
onCopy()
}}>
{download.info.thumbnail !== '' ?
<CardMedia
component="img"
height={180}
image={download.info.thumbnail}
/> :
<Skeleton variant="rectangular" height={180} />
}
<Stack direction="row" spacing={0.5} py={1}>
<Chip
label={
isCompleted()
? 'Completed'
: mapProcessStatus(download.progress.process_status)
}
color="primary"
size="small"
/>
<Typography>
{!isCompleted() ? download.progress.percentage : ''}
</Typography>
<Typography>
&nbsp;
{!isCompleted() ? formatSpeedMiB(download.progress.speed) : ''}
</Typography>
<Typography>
{formatSize(download.info.filesize_approx ?? 0)}
</Typography>
<ResolutionBadge resolution={download.info.resolution} />
</Stack>
</CardContent>
</CardActionArea>
<CardActions>
{isCompleted() ?
<Tooltip title="Clear from the view">
<IconButton
onClick={onStop}
>
<ClearIcon />
</IconButton>
</Tooltip>
:
<Tooltip title="Stop this download">
<IconButton
onClick={onStop}
>
<StopCircleIcon />
</IconButton>
</Tooltip>
}
{isCompleted() &&
<>
<Tooltip title="Download this file">
{download.progress.percentage ?
<LinearProgress
variant="determinate"
value={percentageToNumber()}
color={isCompleted() ? "success" : "primary"}
/> :
null
}
<CardContent>
{download.info.title !== '' ?
<Typography gutterBottom variant="h6" component="div">
{ellipsis(download.info.title, 100)}
</Typography> :
<Skeleton />
}
<Stack direction="row" spacing={0.5} py={1}>
<Chip
label={
isCompleted()
? 'Completed'
: mapProcessStatus(download.progress.process_status)
}
color="primary"
size="small"
/>
<Typography>
{!isCompleted() ? download.progress.percentage : ''}
</Typography>
<Typography>
&nbsp;
{!isCompleted() ? formatSpeedMiB(download.progress.speed) : ''}
</Typography>
<Typography>
{formatSize(download.info.filesize_approx ?? 0)}
</Typography>
<ResolutionBadge resolution={download.info.resolution} />
</Stack>
</CardContent>
</CardActionArea>
<CardActions>
{isCompleted() ?
<Tooltip title="Clear from the view">
<IconButton
onClick={() => downloadFile(download.output.savedFilePath)}
onClick={onStop}
>
<SaveAltIcon />
<ClearIcon />
</IconButton>
</Tooltip>
<Tooltip title="Open in a new tab">
:
<Tooltip title="Stop this download">
<IconButton
onClick={() => viewFile(download.output.savedFilePath)}
onClick={onStop}
>
<OpenInBrowserIcon />
<StopCircleIcon />
</IconButton>
</Tooltip>
</>
}
</CardActions>
</Card>
}
{isCompleted() &&
<>
<Tooltip title="Download this file">
<IconButton
onClick={() => downloadFile(download.output.savedFilePath)}
>
<SaveAltIcon />
</IconButton>
</Tooltip>
<Tooltip title="Open in a new tab">
<IconButton
onClick={() => viewFile(download.output.savedFilePath)}
>
<OpenInBrowserIcon />
</IconButton>
</Tooltip>
<Tooltip title="Share this file">
<IconButton onClick={handleShare}>
<ShareIcon />
</IconButton>
</Tooltip>
</>
}
</CardActions>
</Card>
<Dialog
open={shareOpen}
onClose={() => setShareOpen(false)}
>
<DialogTitle>Share this file</DialogTitle>
<DialogContent>
<DialogContentText>
Copy the link below and share it.
</DialogContentText>
<Stack direction="row" spacing={1} alignItems="center" mt={2}>
<Typography
sx={{
fontFamily: 'monospace',
fontSize: '0.8rem',
overflowWrap: 'anywhere',
flex: 1,
}}
>
{shareLink}
</Typography>
<Button
variant="outlined"
onClick={() => {
navigator.clipboard.writeText(shareLink)
}}
>
<ContentCopyIcon fontSize="small" sx={{ mr: 1 }} />
Copy
</Button>
</Stack>
</DialogContent>
<DialogActions>
<Button onClick={() => setShareOpen(false)}>Close</Button>
</DialogActions>
</Dialog>
</>
)
}

Expand Down
73 changes: 65 additions & 8 deletions frontend/src/views/Filebrowser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ import { DirectoryEntry } from '../types'
import { base64URLEncode, formatSize } from '../utils'
import { useAtomValue } from 'jotai'

import ShareIcon from '@mui/icons-material/Share'
import ContentCopyIcon from '@mui/icons-material/ContentCopy'


export default function Downloaded() {
const [menuPos, setMenuPos] = useState({ x: 0, y: 0 })
const [showMenu, setShowMenu] = useState(false)
Expand Down Expand Up @@ -178,6 +182,9 @@ export default function Downloaded() {
fetcherSubfolder(path)
})

const [shareOpen, setShareOpen] = useState(false)
const [shareLink, setShareLink] = useState('')

return (
<Container
maxWidth="xl"
Expand All @@ -200,6 +207,15 @@ export default function Downloaded() {
setCurrentFile(undefined)
}
}}
onShare={() => {
if (currentFile) {
const encoded = base64URLEncode(currentFile.path)
const link = `${serverAddr}/public/${encoded}`
setShareLink(link)
setShareOpen(true)
setShowMenu(false)
}
}}
/>
<Backdrop
sx={{ color: '#fff', zIndex: (theme) => theme.zIndex.drawer + 1 }}
Expand Down Expand Up @@ -316,8 +332,46 @@ export default function Downloaded() {
</Button>
</DialogActions>
</Dialog>
<Dialog
open={shareOpen}
onClose={() => setShareOpen(false)}
>
<DialogTitle>Share this file</DialogTitle>
<DialogContent>
<DialogContentText>
Copy the link below and share it.
</DialogContentText>
<Paper sx={{ display: 'flex', mt: 2, p: 1 }}>
<input
style={{
flex: 1,
border: 'none',
background: 'transparent',
fontFamily: 'monospace',
fontSize: '0.9rem',
}}
value={shareLink}
readOnly
/>
<Button
variant="outlined"
onClick={() => {
navigator.clipboard.writeText(shareLink)
pushMessage('Link copied to clipboard', 'success')
}}
>
<ContentCopyIcon fontSize="small" sx={{ mr: 1 }} />
Copy
</Button>
</Paper>
</DialogContent>
<DialogActions>
<Button onClick={() => setShareOpen(false)}>Close</Button>
</DialogActions>
</Dialog>
</Container>
)

}

const IconMenu: React.FC<{
Expand All @@ -326,7 +380,8 @@ const IconMenu: React.FC<{
hide: boolean
onDownload: () => void
onDelete: () => void
}> = ({ posX, posY, hide, onDelete, onDownload }) => {
onShare: () => void
}> = ({ posX, posY, hide, onDelete, onDownload, onShare }) => {
return (
<Paper sx={{
width: 320,
Expand All @@ -342,19 +397,21 @@ const IconMenu: React.FC<{
<ListItemIcon>
<DownloadIcon fontSize="small" />
</ListItemIcon>
<ListItemText>
Download
</ListItemText>
<ListItemText>Download</ListItemText>
</MenuItem>
<MenuItem onClick={onDelete}>
<ListItemIcon>
<DeleteForeverIcon fontSize="small" />
</ListItemIcon>
<ListItemText>
Delete
</ListItemText>
<ListItemText>Delete</ListItemText>
</MenuItem>
<MenuItem onClick={onShare}>
<ListItemIcon>
<ShareIcon fontSize="small" />
</ListItemIcon>
<ListItemText>Share</ListItemText>
</MenuItem>
</MenuList>
</Paper>
)
}
}
Loading