mirror of
https://github.com/Ernous/TorrServerJellyfin.git
synced 2025-12-19 21:46:11 +05:00
snake replaced with canvas
This commit is contained in:
@@ -1,55 +1,113 @@
|
||||
import Measure from 'react-measure'
|
||||
import { useState, memo } from 'react'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { useState, memo, useRef, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import isEqual from 'lodash/isEqual'
|
||||
|
||||
import { useCreateCacheMap } from '../customHooks'
|
||||
import { gapBetweenPieces, miniCacheMaxHeight, pieceSizeForMiniMap, defaultPieceSize } from './snakeSettings'
|
||||
import getShortCacheMap from './getShortCacheMap'
|
||||
import { SnakeWrapper, PercentagePiece, ScrollNotification } from './style'
|
||||
import { SnakeWrapper, ScrollNotification } from './style'
|
||||
import {
|
||||
defaultBorderWidth,
|
||||
miniCacheMaxHeight,
|
||||
pieceSizeForMiniMap,
|
||||
defaultPieceSize,
|
||||
defaultBackgroundColor,
|
||||
defaultBorderColor,
|
||||
completeColor,
|
||||
activeColor,
|
||||
rangeColor,
|
||||
defaultGapBetweenPieces,
|
||||
miniBackgroundColor,
|
||||
miniBorderWidth,
|
||||
miniGapBetweenPieces,
|
||||
createGradient,
|
||||
} from './snakeSettings'
|
||||
|
||||
const TorrentCache = ({ cache, isMini }) => {
|
||||
const { t } = useTranslation()
|
||||
const [dimensions, setDimensions] = useState({ width: 0, height: 0 })
|
||||
const { width } = dimensions
|
||||
const canvasRef = useRef(null)
|
||||
const ctxRef = useRef(null)
|
||||
const cacheMap = useCreateCacheMap(cache)
|
||||
|
||||
const preloadPiecesAmount = Math.round(cache.Capacity / cache.PiecesLength - 1)
|
||||
|
||||
const canvasWidth = isMini ? width * 0.93 : width
|
||||
const pieceSize = isMini ? pieceSizeForMiniMap : defaultPieceSize
|
||||
const gapBetweenPieces = isMini ? miniGapBetweenPieces : defaultGapBetweenPieces
|
||||
|
||||
const pieceSizeWithGap = pieceSize + gapBetweenPieces
|
||||
const piecesInOneRow = Math.floor(canvasWidth / pieceSizeWithGap)
|
||||
|
||||
let piecesInOneRow
|
||||
let shotCacheMap
|
||||
if (isMini) {
|
||||
const pieceSizeWithGap = pieceSize + gapBetweenPieces
|
||||
piecesInOneRow = Math.floor((dimensions.width * 0.95) / pieceSizeWithGap)
|
||||
shotCacheMap = isMini && getShortCacheMap({ cacheMap, preloadPiecesAmount, piecesInOneRow })
|
||||
const preloadPiecesAmount = Math.round(cache.Capacity / cache.PiecesLength - 1)
|
||||
shotCacheMap = getShortCacheMap({ cacheMap, preloadPiecesAmount, piecesInOneRow })
|
||||
}
|
||||
const source = isMini ? shotCacheMap : cacheMap
|
||||
const startingXPoint = Math.ceil((canvasWidth - pieceSizeWithGap * piecesInOneRow) / 2) // needed to center grid
|
||||
const height = Math.ceil(source.length / piecesInOneRow) * pieceSizeWithGap
|
||||
|
||||
return isMini ? (
|
||||
useEffect(() => {
|
||||
if (!canvasWidth || !height) return
|
||||
|
||||
const canvas = canvasRef.current
|
||||
canvas.width = canvasWidth
|
||||
canvas.height = height
|
||||
ctxRef.current = canvas.getContext('2d')
|
||||
}, [canvasRef, height, canvasWidth])
|
||||
|
||||
useEffect(() => {
|
||||
const ctx = ctxRef.current
|
||||
if (!ctx) return
|
||||
|
||||
ctx.clearRect(0, 0, canvasWidth, height)
|
||||
|
||||
source.forEach(({ percentage, isReader, isReaderRange }, i) => {
|
||||
const inProgress = percentage > 0 && percentage < 100
|
||||
const isCompleted = percentage === 100
|
||||
const currentRow = i % piecesInOneRow
|
||||
const currentColumn = Math.floor(i / piecesInOneRow)
|
||||
const borderWidth = isMini ? miniBorderWidth : defaultBorderWidth
|
||||
const fixBlurStroke = borderWidth % 2 === 0 ? 0 : 0.5
|
||||
const requiredFix = Math.ceil(borderWidth / 2) + 1 + fixBlurStroke
|
||||
const x = currentRow * pieceSize + currentRow * gapBetweenPieces + startingXPoint + requiredFix
|
||||
const y = currentColumn * pieceSize + currentColumn * gapBetweenPieces + requiredFix
|
||||
|
||||
ctx.lineWidth = borderWidth
|
||||
ctx.fillStyle = inProgress
|
||||
? createGradient(ctx, percentage)
|
||||
: isCompleted
|
||||
? completeColor
|
||||
: isMini
|
||||
? miniBackgroundColor
|
||||
: defaultBackgroundColor
|
||||
ctx.strokeStyle = isReader
|
||||
? activeColor
|
||||
: inProgress || isCompleted
|
||||
? completeColor
|
||||
: isReaderRange
|
||||
? rangeColor
|
||||
: defaultBorderColor
|
||||
|
||||
ctx.translate(x, y)
|
||||
ctx.fillRect(0, 0, pieceSize, pieceSize)
|
||||
ctx.strokeRect(0, 0, pieceSize, pieceSize)
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0)
|
||||
})
|
||||
}, [cacheMap, height, canvasWidth, piecesInOneRow, isMini, startingXPoint, pieceSize, gapBetweenPieces, source])
|
||||
|
||||
return (
|
||||
<Measure bounds onResize={({ bounds }) => setDimensions(bounds)}>
|
||||
{({ measureRef }) => (
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<SnakeWrapper ref={measureRef} pieceSize={pieceSize} piecesInOneRow={piecesInOneRow}>
|
||||
{shotCacheMap.map(({ className, id, percentage }) => (
|
||||
<span key={id || uuidv4()} className={className}>
|
||||
{percentage > 0 && percentage <= 100 && <PercentagePiece percentage={percentage} />}
|
||||
</span>
|
||||
))}
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }} ref={measureRef}>
|
||||
<SnakeWrapper isMini={isMini}>
|
||||
<canvas ref={canvasRef} />
|
||||
</SnakeWrapper>
|
||||
|
||||
{dimensions.height >= miniCacheMaxHeight && <ScrollNotification>{t('ScrollDown')}</ScrollNotification>}
|
||||
{isMini && height >= miniCacheMaxHeight && <ScrollNotification>{t('ScrollDown')}</ScrollNotification>}
|
||||
</div>
|
||||
)}
|
||||
</Measure>
|
||||
) : (
|
||||
<SnakeWrapper pieceSize={pieceSize}>
|
||||
{cacheMap.map(({ className, id, percentage }) => (
|
||||
<span key={id || uuidv4()} className={className}>
|
||||
{percentage > 0 && percentage <= 100 && <PercentagePiece percentage={percentage} />}
|
||||
</span>
|
||||
))}
|
||||
</SnakeWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user