diff --git a/web/src/components/DialogTorrentDetailsContent/DialogHeader.jsx b/web/src/components/DialogTorrentDetailsContent/DialogHeader.jsx new file mode 100644 index 0000000..520ed60 --- /dev/null +++ b/web/src/components/DialogTorrentDetailsContent/DialogHeader.jsx @@ -0,0 +1,33 @@ +import Button from '@material-ui/core/Button' +import { AppBar, IconButton, makeStyles, Toolbar, Typography } from '@material-ui/core' +import CloseIcon from '@material-ui/icons/Close' +import { ArrowBack } from '@material-ui/icons' + +const useStyles = makeStyles(theme => ({ + appBar: { position: 'relative' }, + title: { marginLeft: theme.spacing(2), flex: 1 }, +})) + +export default function DialogHeader({ title, onClose, onBack }) { + const classes = useStyles() + + return ( + + + + {onBack ? : } + + + + {title} + + + {onBack && ( + + )} + + + ) +} diff --git a/web/src/components/DialogTorrentDetailsContent/SingleBlock.jsx b/web/src/components/DialogTorrentDetailsContent/SingleBlock.jsx new file mode 100644 index 0000000..07c75b4 --- /dev/null +++ b/web/src/components/DialogTorrentDetailsContent/SingleBlock.jsx @@ -0,0 +1,54 @@ +import { Rect } from 'react-konva' + +export default function SingleBlock({ + x, + y, + percentage, + isActive = false, + inProgress = false, + isReaderRange = false, + isComplete = false, + boxHeight, + strokeWidth, +}) { + const strokeColor = isActive + ? '#000' + : isComplete + ? '#3fb57a' + : inProgress + ? '#00d0d0' + : isReaderRange + ? '#9a9aff' + : '#eef2f4' + const backgroundColor = inProgress ? '#00d0d0' : '#eef2f4' + const percentageProgressColor = '#3fb57a' + const processCompletedColor = '#3fb57a' + + return ( + + ) +} diff --git a/web/src/components/DialogTorrentDetailsContent/TorrentCache.jsx b/web/src/components/DialogTorrentDetailsContent/TorrentCache.jsx new file mode 100644 index 0000000..d46d259 --- /dev/null +++ b/web/src/components/DialogTorrentDetailsContent/TorrentCache.jsx @@ -0,0 +1,100 @@ +import { useEffect, useState } from 'react' +import DialogContent from '@material-ui/core/DialogContent' +import { Stage, Layer } from 'react-konva' +import Measure from 'react-measure' + +import SingleBlock from './SingleBlock' + +export default function TorrentCache({ cache, cacheMap, isMini }) { + const [dimensions, setDimensions] = useState({ width: -1, height: -1 }) + const [stageSettings, setStageSettings] = useState({ + boxHeight: null, + strokeWidth: null, + marginBetweenBlocks: null, + stageOffset: null, + }) + + const updateStageSettings = (boxHeight, strokeWidth) => { + setStageSettings({ + boxHeight, + strokeWidth, + marginBetweenBlocks: strokeWidth, + stageOffset: strokeWidth * 2, + }) + } + + useEffect(() => { + // initializing stageSettings + isMini ? updateStageSettings(24, 4) : updateStageSettings(12, 2) + }, [isMini]) + + const { boxHeight, strokeWidth, marginBetweenBlocks, stageOffset } = stageSettings + const preloadPiecesAmount = Math.round(cache.Capacity / cache.PiecesLength - 1) + const blockSizeWithMargin = boxHeight + strokeWidth + marginBetweenBlocks + const piecesInOneRow = Math.floor((dimensions.width * 0.9) / blockSizeWithMargin) + const amountOfBlocksToRenderInShortView = + preloadPiecesAmount === piecesInOneRow + ? preloadPiecesAmount - 1 + : preloadPiecesAmount + piecesInOneRow - (preloadPiecesAmount % piecesInOneRow) - 1 + const amountOfRows = Math.ceil((isMini ? amountOfBlocksToRenderInShortView : cacheMap.length) / piecesInOneRow) + let activeId = null + + return ( + setDimensions(contentRect.bounds)}> + {({ measureRef }) => ( +
+ + + + {cacheMap.map(({ id, percentage, isComplete, inProgress, isActive, isReaderRange }) => { + const currentRow = Math.floor((isMini ? id - activeId : id) / piecesInOneRow) + + // -------- related only for short view ------- + if (isActive) activeId = id + const shouldBeRendered = + isActive || (id - activeId <= amountOfBlocksToRenderInShortView && id - activeId >= 0) + // -------------------------------------------- + + return isMini ? ( + shouldBeRendered && ( + + ) + ) : ( + + ) + })} + + + +
+ )} +
+ ) +} diff --git a/web/src/components/DialogTorrentDetailsContent/index.jsx b/web/src/components/DialogTorrentDetailsContent/index.jsx index b303e9f..c0ef80a 100644 --- a/web/src/components/DialogTorrentDetailsContent/index.jsx +++ b/web/src/components/DialogTorrentDetailsContent/index.jsx @@ -1,23 +1,18 @@ -import Button from '@material-ui/core/Button' -import { AppBar, IconButton, makeStyles, Toolbar, Typography } from '@material-ui/core' -import CloseIcon from '@material-ui/icons/Close' import styled, { css } from 'styled-components' import { NoImageIcon } from 'icons' import { getPeerString, humanizeSize } from 'utils/Utils' import { viewedHost } from 'utils/Hosts' import { CopyToClipboard } from 'react-copy-to-clipboard' import { useEffect, useState } from 'react' +import { Button } from '@material-ui/core' import { useUpdateCache, useCreateCacheMap } from './customHooks' +import DialogHeader from './DialogHeader' +import TorrentCache from './TorrentCache' -const useStyles = makeStyles(theme => ({ - appBar: { position: 'relative' }, - title: { marginLeft: theme.spacing(2), flex: 1 }, -})) - -const DialogContent = styled.div` +const DialogContentGrid = styled.div` display: grid; - grid-template-rows: min-content 200px 80px 70px; + grid-template-rows: min-content min-content 80px min-content; ` const Poster = styled.div` ${({ poster }) => css` @@ -44,11 +39,12 @@ const Poster = styled.div` `} `} ` -const HeaderSection = styled.section` +const TorrentMainSection = styled.section` padding: 40px; display: grid; grid-template-columns: min-content 1fr; gap: 30px; + background: lightgray; ` const TorrentData = styled.div` @@ -59,7 +55,8 @@ const TorrentData = styled.div` const CacheSection = styled.section` padding: 40px; - background: lightgray; + display: flex; + flex-direction: column; ` const ButtonSection = styled.section` @@ -86,6 +83,16 @@ const ButtonSectionButton = styled.div` :hover { background: red; } + + .hash-group { + display: grid; + place-items: center; + } + + .hash-text { + font-size: 10px; + color: #7c7b7c; + } ` const TorrentFilesSection = styled.div`` @@ -99,11 +106,22 @@ const TorrentSubName = styled.div` color: #7c7b7c; ` +const SectionTitle = styled.div` + font-size: 35px; + font-weight: 200; + line-height: 1; + margin-bottom: 20px; +` + +const DetailedTorrentCacheViewWrapper = styled.div` + padding-top: 50px; +` + const shortenText = (text, count) => text.slice(0, count) + (text.length > count ? '...' : '') export default function DialogTorrentDetailsContent({ closeDialog, torrent }) { - const classes = useStyles() const [isLoading, setIsLoading] = useState(true) + const [isDetailedCacheView, setIsDetailedCacheView] = useState(false) const { poster, hash, @@ -118,31 +136,31 @@ export default function DialogTorrentDetailsContent({ closeDialog, torrent }) { const cache = useUpdateCache(hash) const cacheMap = useCreateCacheMap(cache) - useEffect(() => setIsLoading(false), [cacheMap]) + useEffect(() => { + const torrentLoaded = torrent.stat_string !== 'Torrent in db' && torrent.stat_string !== 'Torrent getting info' + torrentLoaded && isLoading && setIsLoading(false) + }, [torrent, isLoading]) const { Capacity, PiecesCount, PiecesLength } = cache return ( <> - - - - - - - Torrent Details - - - - + setIsDetailedCacheView(false) })} + /> {isLoading ? ( 'loading' + ) : isDetailedCacheView ? ( + + + ) : ( - - + + {poster ? poster : } @@ -168,13 +186,32 @@ export default function DialogTorrentDetailsContent({ closeDialog, torrent }) {
PiecesCount: {PiecesCount}
PiecesLength: {humanizeSize(PiecesLength)}
-
+ - + + Cache + + + + + - copy hash + +
+
copy hash
+
{hash}
+
+
remove views @@ -187,7 +224,7 @@ export default function DialogTorrentDetailsContent({ closeDialog, torrent }) {
-
+ )} )