added different page for detailed cache view

This commit is contained in:
Daniel Shleifman
2021-05-28 10:53:37 +03:00
parent 981f668da5
commit 461fc9014b
4 changed files with 257 additions and 33 deletions

View File

@@ -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 (
<AppBar className={classes.appBar}>
<Toolbar>
<IconButton edge='start' color='inherit' onClick={onBack || onClose} aria-label='close'>
{onBack ? <ArrowBack /> : <CloseIcon />}
</IconButton>
<Typography variant='h6' className={classes.title}>
{title}
</Typography>
{onBack && (
<Button autoFocus color='inherit' onClick={onClose}>
close
</Button>
)}
</Toolbar>
</AppBar>
)
}

View File

@@ -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 (
<Rect
x={x}
y={y}
stroke={strokeColor}
strokeWidth={strokeWidth}
height={boxHeight}
width={boxHeight}
fillAfterStrokeEnabled
preventDefault={false}
// eslint-disable-next-line react/jsx-props-no-spreading
{...(isComplete
? { fill: processCompletedColor }
: inProgress && {
fillLinearGradientStartPointY: boxHeight,
fillLinearGradientEndPointY: 0,
fillLinearGradientColorStops: [
0,
percentageProgressColor,
percentage,
percentageProgressColor,
percentage,
backgroundColor,
],
})}
/>
)
}

View File

@@ -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 (
<Measure bounds onResize={contentRect => setDimensions(contentRect.bounds)}>
{({ measureRef }) => (
<div ref={measureRef}>
<DialogContent style={{ padding: 0 }}>
<Stage
style={{ display: 'flex', justifyContent: 'center' }}
offset={{ x: -stageOffset, y: -stageOffset }}
width={stageOffset + blockSizeWithMargin * piecesInOneRow || 0}
height={stageOffset + blockSizeWithMargin * amountOfRows || 0}
>
<Layer>
{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 && (
<SingleBlock
key={id}
x={((id - activeId) % piecesInOneRow) * blockSizeWithMargin}
y={currentRow * blockSizeWithMargin}
percentage={percentage}
inProgress={inProgress}
isComplete={isComplete}
isReaderRange={isReaderRange}
isActive={isActive}
boxHeight={boxHeight}
strokeWidth={strokeWidth}
/>
)
) : (
<SingleBlock
key={id}
x={(id % piecesInOneRow) * blockSizeWithMargin}
y={currentRow * blockSizeWithMargin}
percentage={percentage}
inProgress={inProgress}
isComplete={isComplete}
isReaderRange={isReaderRange}
isActive={isActive}
boxHeight={boxHeight}
strokeWidth={strokeWidth}
/>
)
})}
</Layer>
</Stage>
</DialogContent>
</div>
)}
</Measure>
)
}

View File

@@ -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 styled, { css } from 'styled-components'
import { NoImageIcon } from 'icons' import { NoImageIcon } from 'icons'
import { getPeerString, humanizeSize } from 'utils/Utils' import { getPeerString, humanizeSize } from 'utils/Utils'
import { viewedHost } from 'utils/Hosts' import { viewedHost } from 'utils/Hosts'
import { CopyToClipboard } from 'react-copy-to-clipboard' import { CopyToClipboard } from 'react-copy-to-clipboard'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { Button } from '@material-ui/core'
import { useUpdateCache, useCreateCacheMap } from './customHooks' import { useUpdateCache, useCreateCacheMap } from './customHooks'
import DialogHeader from './DialogHeader'
import TorrentCache from './TorrentCache'
const useStyles = makeStyles(theme => ({ const DialogContentGrid = styled.div`
appBar: { position: 'relative' },
title: { marginLeft: theme.spacing(2), flex: 1 },
}))
const DialogContent = styled.div`
display: grid; display: grid;
grid-template-rows: min-content 200px 80px 70px; grid-template-rows: min-content min-content 80px min-content;
` `
const Poster = styled.div` const Poster = styled.div`
${({ poster }) => css` ${({ poster }) => css`
@@ -44,11 +39,12 @@ const Poster = styled.div`
`} `}
`} `}
` `
const HeaderSection = styled.section` const TorrentMainSection = styled.section`
padding: 40px; padding: 40px;
display: grid; display: grid;
grid-template-columns: min-content 1fr; grid-template-columns: min-content 1fr;
gap: 30px; gap: 30px;
background: lightgray;
` `
const TorrentData = styled.div` const TorrentData = styled.div`
@@ -59,7 +55,8 @@ const TorrentData = styled.div`
const CacheSection = styled.section` const CacheSection = styled.section`
padding: 40px; padding: 40px;
background: lightgray; display: flex;
flex-direction: column;
` `
const ButtonSection = styled.section` const ButtonSection = styled.section`
@@ -86,6 +83,16 @@ const ButtonSectionButton = styled.div`
:hover { :hover {
background: red; background: red;
} }
.hash-group {
display: grid;
place-items: center;
}
.hash-text {
font-size: 10px;
color: #7c7b7c;
}
` `
const TorrentFilesSection = styled.div`` const TorrentFilesSection = styled.div``
@@ -99,11 +106,22 @@ const TorrentSubName = styled.div`
color: #7c7b7c; 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 ? '...' : '') const shortenText = (text, count) => text.slice(0, count) + (text.length > count ? '...' : '')
export default function DialogTorrentDetailsContent({ closeDialog, torrent }) { export default function DialogTorrentDetailsContent({ closeDialog, torrent }) {
const classes = useStyles()
const [isLoading, setIsLoading] = useState(true) const [isLoading, setIsLoading] = useState(true)
const [isDetailedCacheView, setIsDetailedCacheView] = useState(false)
const { const {
poster, poster,
hash, hash,
@@ -118,31 +136,31 @@ export default function DialogTorrentDetailsContent({ closeDialog, torrent }) {
const cache = useUpdateCache(hash) const cache = useUpdateCache(hash)
const cacheMap = useCreateCacheMap(cache) 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 const { Capacity, PiecesCount, PiecesLength } = cache
return ( return (
<> <>
<AppBar className={classes.appBar}> <DialogHeader
<Toolbar> onClose={closeDialog}
<IconButton edge='start' color='inherit' onClick={closeDialog} aria-label='close'> title={isDetailedCacheView ? 'Detailed Cache View' : 'Torrent Details'}
<CloseIcon /> // eslint-disable-next-line react/jsx-props-no-spreading
</IconButton> {...(isDetailedCacheView && { onBack: () => setIsDetailedCacheView(false) })}
<Typography variant='h6' className={classes.title}> />
Torrent Details
</Typography>
<Button autoFocus color='inherit' onClick={closeDialog}>
close
</Button>
</Toolbar>
</AppBar>
{isLoading ? ( {isLoading ? (
'loading' 'loading'
) : isDetailedCacheView ? (
<DetailedTorrentCacheViewWrapper>
<TorrentCache cache={cache} cacheMap={cacheMap} />
</DetailedTorrentCacheViewWrapper>
) : ( ) : (
<DialogContent> <DialogContentGrid>
<HeaderSection> <TorrentMainSection>
<Poster poster={poster}>{poster ? <img alt='poster' src={poster} /> : <NoImageIcon />}</Poster> <Poster poster={poster}>{poster ? <img alt='poster' src={poster} /> : <NoImageIcon />}</Poster>
<TorrentData> <TorrentData>
@@ -168,13 +186,32 @@ export default function DialogTorrentDetailsContent({ closeDialog, torrent }) {
<div>PiecesCount: {PiecesCount}</div> <div>PiecesCount: {PiecesCount}</div>
<div>PiecesLength: {humanizeSize(PiecesLength)}</div> <div>PiecesLength: {humanizeSize(PiecesLength)}</div>
</TorrentData> </TorrentData>
</HeaderSection> </TorrentMainSection>
<CacheSection /> <CacheSection>
<SectionTitle>Cache</SectionTitle>
<TorrentCache isMini cache={cache} cacheMap={cacheMap} />
<Button
style={{ alignSelf: 'flex-end', marginTop: '30px' }}
variant='contained'
color='primary'
size='large'
onClick={() => setIsDetailedCacheView(true)}
>
Detailed cache view
</Button>
</CacheSection>
<ButtonSection> <ButtonSection>
<CopyToClipboard text={hash}> <CopyToClipboard text={hash}>
<ButtonSectionButton>copy hash</ButtonSectionButton> <ButtonSectionButton>
<div className='hash-group'>
<div>copy hash</div>
<div className='hash-text'>{hash}</div>
</div>
</ButtonSectionButton>
</CopyToClipboard> </CopyToClipboard>
<ButtonSectionButton>remove views</ButtonSectionButton> <ButtonSectionButton>remove views</ButtonSectionButton>
@@ -187,7 +224,7 @@ export default function DialogTorrentDetailsContent({ closeDialog, torrent }) {
</ButtonSection> </ButtonSection>
<TorrentFilesSection /> <TorrentFilesSection />
</DialogContent> </DialogContentGrid>
)} )}
</> </>
) )