mirror of
https://github.com/Ernous/TorrServerJellyfin.git
synced 2025-12-19 13:36:09 +05:00
Merge branch 'master' of https://github.com/YouROK/TorrServer
This commit is contained in:
@@ -23,6 +23,8 @@
|
|||||||
"react-konva": "^17.0.2-4",
|
"react-konva": "^17.0.2-4",
|
||||||
"react-measure": "^2.5.2",
|
"react-measure": "^2.5.2",
|
||||||
"react-scripts": "4.0.3",
|
"react-scripts": "4.0.3",
|
||||||
|
"react-virtualized-auto-sizer": "^1.0.5",
|
||||||
|
"react-window": "^1.8.6",
|
||||||
"styled-components": "^5.3.0",
|
"styled-components": "^5.3.0",
|
||||||
"uuid": "^8.3.2"
|
"uuid": "^8.3.2"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,138 +0,0 @@
|
|||||||
import { memo, useEffect, useState } from 'react'
|
|
||||||
import DialogContent from '@material-ui/core/DialogContent'
|
|
||||||
import { Stage, Layer } from 'react-konva'
|
|
||||||
import Measure from 'react-measure'
|
|
||||||
import isEqual from 'lodash/isEqual'
|
|
||||||
import styled from 'styled-components'
|
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
|
||||||
|
|
||||||
import SingleBlock from './SingleBlock'
|
|
||||||
import { useCreateCacheMap } from './customHooks'
|
|
||||||
|
|
||||||
const ScrollNotification = styled.div`
|
|
||||||
margin-top: 10px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: rgba(0, 0, 0, 0.5);
|
|
||||||
align-self: center;
|
|
||||||
`
|
|
||||||
|
|
||||||
const TorrentCache = memo(
|
|
||||||
({ cache, isMini }) => {
|
|
||||||
const [dimensions, setDimensions] = useState({ width: 0, height: 0 })
|
|
||||||
const [stageSettings, setStageSettings] = useState({
|
|
||||||
boxHeight: null,
|
|
||||||
strokeWidth: null,
|
|
||||||
marginBetweenBlocks: null,
|
|
||||||
stageOffset: null,
|
|
||||||
})
|
|
||||||
|
|
||||||
const cacheMap = useCreateCacheMap(cache)
|
|
||||||
|
|
||||||
const updateStageSettings = (boxHeight, strokeWidth) => {
|
|
||||||
setStageSettings({
|
|
||||||
boxHeight,
|
|
||||||
strokeWidth,
|
|
||||||
marginBetweenBlocks: strokeWidth,
|
|
||||||
stageOffset: strokeWidth * 2,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// initializing stageSettings
|
|
||||||
if (isMini) return dimensions.width < 500 ? updateStageSettings(20, 3) : updateStageSettings(24, 4)
|
|
||||||
updateStageSettings(12, 2)
|
|
||||||
}, [isMini, dimensions.width])
|
|
||||||
|
|
||||||
const miniCacheMaxHeight = 340
|
|
||||||
|
|
||||||
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 || 0
|
|
||||||
const amountOfRows = Math.ceil((isMini ? amountOfBlocksToRenderInShortView : cacheMap.length) / piecesInOneRow)
|
|
||||||
const activeId = null
|
|
||||||
|
|
||||||
const cacheMapWithoutEmptyBlocks = cacheMap.filter(({ isComplete, inProgress }) => inProgress || isComplete)
|
|
||||||
const extraEmptyBlocksForFillingLine =
|
|
||||||
cacheMapWithoutEmptyBlocks.length < amountOfBlocksToRenderInShortView
|
|
||||||
? new Array(amountOfBlocksToRenderInShortView - cacheMapWithoutEmptyBlocks.length + 1).fill({})
|
|
||||||
: []
|
|
||||||
const shortCacheMap = [...cacheMapWithoutEmptyBlocks, ...extraEmptyBlocksForFillingLine]
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Measure bounds onResize={({ bounds }) => setDimensions(bounds)}>
|
|
||||||
{({ measureRef }) => (
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
|
||||||
<DialogContent
|
|
||||||
ref={measureRef}
|
|
||||||
{...(isMini
|
|
||||||
? { style: { padding: 0, maxHeight: `${miniCacheMaxHeight}px`, overflow: 'auto' } }
|
|
||||||
: { 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>
|
|
||||||
{isMini
|
|
||||||
? shortCacheMap.map(({ percentage, isComplete, inProgress, isActive, isReaderRange }, i) => {
|
|
||||||
const currentRow = Math.floor(i / piecesInOneRow)
|
|
||||||
const shouldBeRendered = inProgress || isComplete || i <= amountOfBlocksToRenderInShortView
|
|
||||||
|
|
||||||
return (
|
|
||||||
shouldBeRendered && (
|
|
||||||
<SingleBlock
|
|
||||||
key={uuidv4()}
|
|
||||||
x={(i % piecesInOneRow) * blockSizeWithMargin}
|
|
||||||
y={currentRow * blockSizeWithMargin}
|
|
||||||
percentage={percentage}
|
|
||||||
inProgress={inProgress}
|
|
||||||
isComplete={isComplete}
|
|
||||||
isReaderRange={isReaderRange}
|
|
||||||
isActive={isActive}
|
|
||||||
boxHeight={boxHeight}
|
|
||||||
strokeWidth={strokeWidth}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
: cacheMap.map(({ id, percentage, isComplete, inProgress, isActive, isReaderRange }) => {
|
|
||||||
const currentRow = Math.floor((isMini ? id - activeId : id) / piecesInOneRow)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SingleBlock
|
|
||||||
key={uuidv4()}
|
|
||||||
x={(id % piecesInOneRow) * blockSizeWithMargin}
|
|
||||||
y={currentRow * blockSizeWithMargin}
|
|
||||||
percentage={percentage}
|
|
||||||
inProgress={inProgress}
|
|
||||||
isComplete={isComplete}
|
|
||||||
isReaderRange={isReaderRange}
|
|
||||||
isActive={isActive}
|
|
||||||
boxHeight={boxHeight}
|
|
||||||
strokeWidth={strokeWidth}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</Layer>
|
|
||||||
</Stage>
|
|
||||||
</DialogContent>
|
|
||||||
|
|
||||||
{isMini &&
|
|
||||||
(stageOffset + blockSizeWithMargin * amountOfRows || 0) >= miniCacheMaxHeight &&
|
|
||||||
dimensions.height >= miniCacheMaxHeight && <ScrollNotification>scroll down</ScrollNotification>}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Measure>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
(prev, next) => isEqual(prev.cache.Pieces, next.cache.Pieces) && isEqual(prev.cache.Readers, next.cache.Readers),
|
|
||||||
)
|
|
||||||
|
|
||||||
export default TorrentCache
|
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import DialogContent from '@material-ui/core/DialogContent'
|
||||||
|
import { Stage, Layer } from 'react-konva'
|
||||||
|
import Measure from 'react-measure'
|
||||||
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
import SingleBlock from './SingleBlock'
|
||||||
|
import getShortCacheMap from './getShortCacheMap'
|
||||||
|
|
||||||
|
const ScrollNotification = styled.div`
|
||||||
|
margin-top: 10px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(0, 0, 0, 0.5);
|
||||||
|
align-self: center;
|
||||||
|
`
|
||||||
|
|
||||||
|
export default function DefaultSnake({ isMini, cacheMap, preloadPiecesAmount }) {
|
||||||
|
const [dimensions, setDimensions] = useState({ width: 0, height: 0 })
|
||||||
|
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
|
||||||
|
if (isMini) return dimensions.width < 500 ? updateStageSettings(20, 3) : updateStageSettings(24, 4)
|
||||||
|
updateStageSettings(12, 2)
|
||||||
|
}, [isMini, dimensions.width])
|
||||||
|
|
||||||
|
const miniCacheMaxHeight = 340
|
||||||
|
|
||||||
|
const { boxHeight, strokeWidth, marginBetweenBlocks, stageOffset } = stageSettings
|
||||||
|
|
||||||
|
const blockSizeWithMargin = boxHeight + strokeWidth + marginBetweenBlocks
|
||||||
|
const piecesInOneRow = Math.floor((dimensions.width * 0.9) / blockSizeWithMargin)
|
||||||
|
|
||||||
|
const shortCacheMap = isMini ? getShortCacheMap({ cacheMap, preloadPiecesAmount, piecesInOneRow }) : []
|
||||||
|
|
||||||
|
const amountOfRows = Math.ceil((isMini ? shortCacheMap.length : cacheMap.length) / piecesInOneRow)
|
||||||
|
|
||||||
|
const getItemCoordinates = blockOrder => {
|
||||||
|
const currentRow = Math.floor(blockOrder / piecesInOneRow)
|
||||||
|
const x = (blockOrder % piecesInOneRow) * blockSizeWithMargin || 0
|
||||||
|
const y = currentRow * blockSizeWithMargin || 0
|
||||||
|
|
||||||
|
return { x, y }
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Measure bounds onResize={({ bounds }) => setDimensions(bounds)}>
|
||||||
|
{({ measureRef }) => (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||||
|
<DialogContent
|
||||||
|
ref={measureRef}
|
||||||
|
{...(isMini
|
||||||
|
? { style: { padding: 0, maxHeight: `${miniCacheMaxHeight}px`, overflow: 'auto' } }
|
||||||
|
: { 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>
|
||||||
|
{isMini
|
||||||
|
? shortCacheMap.map(({ percentage, isComplete, inProgress, isActive, isReaderRange }, i) => {
|
||||||
|
const { x, y } = getItemCoordinates(i)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SingleBlock
|
||||||
|
key={uuidv4()}
|
||||||
|
x={x}
|
||||||
|
y={y}
|
||||||
|
percentage={percentage}
|
||||||
|
inProgress={inProgress}
|
||||||
|
isComplete={isComplete}
|
||||||
|
isReaderRange={isReaderRange}
|
||||||
|
isActive={isActive}
|
||||||
|
boxHeight={boxHeight}
|
||||||
|
strokeWidth={strokeWidth}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
: cacheMap.map(({ id, percentage, isComplete, inProgress, isActive, isReaderRange }) => {
|
||||||
|
const { x, y } = getItemCoordinates(id)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SingleBlock
|
||||||
|
key={uuidv4()}
|
||||||
|
x={x}
|
||||||
|
y={y}
|
||||||
|
percentage={percentage}
|
||||||
|
inProgress={inProgress}
|
||||||
|
isComplete={isComplete}
|
||||||
|
isReaderRange={isReaderRange}
|
||||||
|
isActive={isActive}
|
||||||
|
boxHeight={boxHeight}
|
||||||
|
strokeWidth={strokeWidth}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Layer>
|
||||||
|
</Stage>
|
||||||
|
</DialogContent>
|
||||||
|
|
||||||
|
{isMini &&
|
||||||
|
(stageOffset + blockSizeWithMargin * amountOfRows || 0) >= miniCacheMaxHeight &&
|
||||||
|
dimensions.height >= miniCacheMaxHeight && <ScrollNotification>scroll down</ScrollNotification>}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Measure>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { FixedSizeGrid as Grid } from 'react-window'
|
||||||
|
import AutoSizer from 'react-virtualized-auto-sizer'
|
||||||
|
|
||||||
|
import { getLargeSnakeColors } from './colors'
|
||||||
|
|
||||||
|
const Cell = ({ columnIndex, rowIndex, style, data }) => {
|
||||||
|
const { columnCount, cacheMap, gutterSize, borderSize, pieces } = data
|
||||||
|
const itemIndex = rowIndex * columnCount + columnIndex
|
||||||
|
|
||||||
|
const { borderColor, backgroundColor } = getLargeSnakeColors(cacheMap[itemIndex] || {})
|
||||||
|
|
||||||
|
const newStyle = {
|
||||||
|
...style,
|
||||||
|
left: style.left + gutterSize,
|
||||||
|
top: style.top + gutterSize,
|
||||||
|
width: style.width - gutterSize,
|
||||||
|
height: style.height - gutterSize,
|
||||||
|
border: `${borderSize}px solid ${borderColor}`,
|
||||||
|
display: itemIndex >= pieces ? 'none' : null,
|
||||||
|
background: backgroundColor,
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div style={newStyle} />
|
||||||
|
}
|
||||||
|
|
||||||
|
const gutterSize = 2
|
||||||
|
const borderSize = 1
|
||||||
|
const pieceSize = 12
|
||||||
|
const pieceSizeWithSpacing = pieceSize + gutterSize
|
||||||
|
|
||||||
|
export default function LargeSnake({ cacheMap }) {
|
||||||
|
const pieces = cacheMap.length
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: '60vh', overflow: 'hidden' }}>
|
||||||
|
<AutoSizer>
|
||||||
|
{({ height, width }) => {
|
||||||
|
const columnCount = Math.floor(width / (gutterSize + pieceSize)) - 1
|
||||||
|
const rowCount = pieces / columnCount + 1
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid
|
||||||
|
columnCount={columnCount}
|
||||||
|
rowCount={rowCount}
|
||||||
|
columnWidth={pieceSizeWithSpacing}
|
||||||
|
rowHeight={pieceSizeWithSpacing}
|
||||||
|
height={height}
|
||||||
|
width={width}
|
||||||
|
itemData={{ columnCount, cacheMap, gutterSize, borderSize, pieces }}
|
||||||
|
>
|
||||||
|
{Cell}
|
||||||
|
</Grid>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</AutoSizer>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import { Rect } from 'react-konva'
|
import { Rect } from 'react-konva'
|
||||||
|
|
||||||
|
import { activeColor, completeColor, defaultBorderColor, progressColor, rangeColor } from './colors'
|
||||||
|
|
||||||
export default function SingleBlock({
|
export default function SingleBlock({
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
@@ -12,17 +14,17 @@ export default function SingleBlock({
|
|||||||
strokeWidth,
|
strokeWidth,
|
||||||
}) {
|
}) {
|
||||||
const strokeColor = isActive
|
const strokeColor = isActive
|
||||||
? '#000'
|
? activeColor
|
||||||
: isComplete
|
: isComplete
|
||||||
? '#3fb57a'
|
? completeColor
|
||||||
: inProgress
|
: inProgress
|
||||||
? '#00d0d0'
|
? progressColor
|
||||||
: isReaderRange
|
: isReaderRange
|
||||||
? '#9a9aff'
|
? rangeColor
|
||||||
: '#eef2f4'
|
: defaultBorderColor
|
||||||
const backgroundColor = inProgress ? '#00d0d0' : '#eef2f4'
|
const backgroundColor = inProgress ? progressColor : defaultBorderColor
|
||||||
const percentageProgressColor = '#3fb57a'
|
const percentageProgressColor = completeColor
|
||||||
const processCompletedColor = '#3fb57a'
|
const processCompletedColor = completeColor
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Rect
|
<Rect
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
export const defaultBorderColor = '#eef2f4'
|
||||||
|
export const defaultBackgroundColor = '#fff'
|
||||||
|
export const completeColor = '#3fb57a'
|
||||||
|
export const progressColor = '#00d0d0'
|
||||||
|
export const activeColor = '#000'
|
||||||
|
export const rangeColor = '#9a9aff'
|
||||||
|
|
||||||
|
export const getLargeSnakeColors = ({ isActive, isComplete, inProgress, isReaderRange, percentage }) => {
|
||||||
|
const gradientBackgroundColor = inProgress ? progressColor : defaultBackgroundColor
|
||||||
|
const gradient = `linear-gradient(to top, ${completeColor} 0%, ${completeColor} ${
|
||||||
|
percentage * 100
|
||||||
|
}%, ${gradientBackgroundColor} ${percentage * 100}%, ${gradientBackgroundColor} 100%)`
|
||||||
|
|
||||||
|
const borderColor = isActive
|
||||||
|
? activeColor
|
||||||
|
: isComplete
|
||||||
|
? completeColor
|
||||||
|
: inProgress
|
||||||
|
? progressColor
|
||||||
|
: isReaderRange
|
||||||
|
? rangeColor
|
||||||
|
: defaultBorderColor
|
||||||
|
const backgroundColor = isComplete ? completeColor : inProgress ? gradient : defaultBackgroundColor
|
||||||
|
|
||||||
|
return { borderColor, backgroundColor }
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
export default ({ cacheMap, preloadPiecesAmount, piecesInOneRow }) => {
|
||||||
|
const cacheMapWithoutEmptyBlocks = cacheMap.filter(({ isComplete, inProgress }) => inProgress || isComplete)
|
||||||
|
|
||||||
|
const getFullAmountOfBlocks = amountOfBlocks =>
|
||||||
|
// this function counts existed amount of blocks with extra "empty blocks" to fill the row till the end
|
||||||
|
amountOfBlocks % piecesInOneRow === 0
|
||||||
|
? amountOfBlocks - 1
|
||||||
|
: amountOfBlocks + piecesInOneRow - (amountOfBlocks % piecesInOneRow) - 1 || 0
|
||||||
|
|
||||||
|
const amountOfBlocksToRenderInShortView = getFullAmountOfBlocks(preloadPiecesAmount)
|
||||||
|
// preloadPiecesAmount is counted from "cache.Capacity / cache.PiecesLength". We always show at least this amount of blocks
|
||||||
|
const scalableAmountOfBlocksToRenderInShortView = getFullAmountOfBlocks(cacheMapWithoutEmptyBlocks.length)
|
||||||
|
// cacheMap can become bigger than preloadPiecesAmount counted before. In that case we count blocks dynamically
|
||||||
|
|
||||||
|
const finalAmountOfBlocksToRenderInShortView = Math.max(
|
||||||
|
// this check is needed to decide which is the biggest amount of blocks and take it to render
|
||||||
|
scalableAmountOfBlocksToRenderInShortView,
|
||||||
|
amountOfBlocksToRenderInShortView,
|
||||||
|
)
|
||||||
|
|
||||||
|
const extraBlocksAmount = finalAmountOfBlocksToRenderInShortView - cacheMapWithoutEmptyBlocks.length + 1
|
||||||
|
// amount of blocks needed to fill the line till the end
|
||||||
|
|
||||||
|
const extraEmptyBlocksForFillingLine = extraBlocksAmount ? new Array(extraBlocksAmount).fill({}) : []
|
||||||
|
|
||||||
|
return [...cacheMapWithoutEmptyBlocks, ...extraEmptyBlocksForFillingLine]
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { memo } from 'react'
|
||||||
|
import isEqual from 'lodash/isEqual'
|
||||||
|
|
||||||
|
import { useCreateCacheMap } from '../customHooks'
|
||||||
|
import LargeSnake from './LargeSnake'
|
||||||
|
import DefaultSnake from './DefaultSnake'
|
||||||
|
|
||||||
|
const TorrentCache = memo(
|
||||||
|
({ cache, isMini }) => {
|
||||||
|
const cacheMap = useCreateCacheMap(cache)
|
||||||
|
|
||||||
|
const preloadPiecesAmount = Math.round(cache.Capacity / cache.PiecesLength - 1)
|
||||||
|
const isSnakeLarge = cacheMap.length > 7000
|
||||||
|
|
||||||
|
return isMini ? (
|
||||||
|
<DefaultSnake isMini cacheMap={cacheMap} preloadPiecesAmount={preloadPiecesAmount} />
|
||||||
|
) : isSnakeLarge ? (
|
||||||
|
<LargeSnake cacheMap={cacheMap} />
|
||||||
|
) : (
|
||||||
|
<DefaultSnake cacheMap={cacheMap} preloadPiecesAmount={preloadPiecesAmount} />
|
||||||
|
)
|
||||||
|
},
|
||||||
|
(prev, next) => isEqual(prev.cache.Pieces, next.cache.Pieces) && isEqual(prev.cache.Readers, next.cache.Readers),
|
||||||
|
)
|
||||||
|
|
||||||
|
export default TorrentCache
|
||||||
@@ -47,15 +47,13 @@ export const Poster = styled.div`
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 840px) {
|
@media (max-width: 840px) {
|
||||||
height: 200px;
|
${poster
|
||||||
|
? css`
|
||||||
${!poster &&
|
height: 200px;
|
||||||
css`
|
`
|
||||||
width: 150px;
|
: css`
|
||||||
svg {
|
display: none;
|
||||||
transform: translateY(-3px);
|
`}
|
||||||
}
|
|
||||||
`}
|
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export const UploadSpeedWidget = ({ data }) => (
|
|||||||
export const PeersWidget = ({ data }) => (
|
export const PeersWidget = ({ data }) => (
|
||||||
<StatisticsField
|
<StatisticsField
|
||||||
title='Peers'
|
title='Peers'
|
||||||
value={getPeerString(data)}
|
value={getPeerString(data) || '[0] 0 / 0'}
|
||||||
iconBg='#cdc118'
|
iconBg='#cdc118'
|
||||||
valueBg='#d8cb18'
|
valueBg='#d8cb18'
|
||||||
icon={SwapVerticalCircleIcon}
|
icon={SwapVerticalCircleIcon}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export function humanizeSize(size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getPeerString(torrent) {
|
export function getPeerString(torrent) {
|
||||||
if (!torrent || !torrent.connected_seeders) return ''
|
if (!torrent || !torrent.connected_seeders) return null
|
||||||
return `[${torrent.connected_seeders}] ${torrent.active_peers} / ${torrent.total_peers}`
|
return `[${torrent.connected_seeders}] ${torrent.active_peers} / ${torrent.total_peers}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1174,7 +1174,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
|
"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
|
||||||
version "7.14.0"
|
version "7.14.0"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6"
|
||||||
integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==
|
integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==
|
||||||
@@ -8197,6 +8197,11 @@ media-typer@0.3.0:
|
|||||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||||
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
|
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
|
||||||
|
|
||||||
|
"memoize-one@>=3.1.1 <6":
|
||||||
|
version "5.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
|
||||||
|
integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
|
||||||
|
|
||||||
memory-fs@^0.4.1:
|
memory-fs@^0.4.1:
|
||||||
version "0.4.1"
|
version "0.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
|
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
|
||||||
@@ -10419,6 +10424,19 @@ react-transition-group@^4.4.0:
|
|||||||
loose-envify "^1.4.0"
|
loose-envify "^1.4.0"
|
||||||
prop-types "^15.6.2"
|
prop-types "^15.6.2"
|
||||||
|
|
||||||
|
react-virtualized-auto-sizer@^1.0.5:
|
||||||
|
version "1.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.5.tgz#9eeeb8302022de56fbd7a860b08513120ce36509"
|
||||||
|
integrity sha512-kivjYVWX15TX2IUrm8F1jaCEX8EXrpy3DD+u41WGqJ1ZqbljWpiwscV+VxOM1l7sSIM1jwi2LADjhhAJkJ9dxA==
|
||||||
|
|
||||||
|
react-window@^1.8.6:
|
||||||
|
version "1.8.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.6.tgz#d011950ac643a994118632665aad0c6382e2a112"
|
||||||
|
integrity sha512-8VwEEYyjz6DCnGBsd+MgkD0KJ2/OXFULyDtorIiTz+QzwoP94tBoA7CnbtyXMm+cCeAUER5KJcPtWl9cpKbOBg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.0.0"
|
||||||
|
memoize-one ">=3.1.1 <6"
|
||||||
|
|
||||||
react@^17.0.2:
|
react@^17.0.2:
|
||||||
version "17.0.2"
|
version "17.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||||
|
|||||||
Reference in New Issue
Block a user