diff --git a/web/package.json b/web/package.json index d6ecd14..1dbb14a 100644 --- a/web/package.json +++ b/web/package.json @@ -10,7 +10,6 @@ "fontsource-roboto": "^4.0.0", "i18next": "^20.3.1", "i18next-browser-languagedetector": "^6.1.1", - "konva": "^8.0.1", "lodash": "^4.17.21", "material-ui-image": "^3.3.2", "parse-torrent": "^9.1.3", @@ -21,7 +20,6 @@ "react-dom": "^17.0.2", "react-dropzone": "^11.3.2", "react-i18next": "^11.10.0", - "react-konva": "^17.0.2-4", "react-measure": "^2.5.2", "react-query": "^3.17.0", "react-scripts": "4.0.3", diff --git a/web/src/components/DialogTorrentDetailsContent/TorrentCache/DefaultSnake.jsx b/web/src/components/DialogTorrentDetailsContent/TorrentCache/DefaultSnake.jsx deleted file mode 100644 index ada28a9..0000000 --- a/web/src/components/DialogTorrentDetailsContent/TorrentCache/DefaultSnake.jsx +++ /dev/null @@ -1,127 +0,0 @@ -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 { useTranslation } from 'react-i18next' - -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 { t } = useTranslation() - 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 ( - setDimensions(bounds)}> - {({ measureRef }) => ( -
- - - - {isMini - ? shortCacheMap.map(({ percentage, isComplete, inProgress, isActive, isReaderRange }, i) => { - const { x, y } = getItemCoordinates(i) - - return ( - - ) - }) - : cacheMap.map(({ id, percentage, isComplete, inProgress, isActive, isReaderRange }) => { - const { x, y } = getItemCoordinates(id) - - return ( - - ) - })} - - - - - {isMini && - (stageOffset + blockSizeWithMargin * amountOfRows || 0) >= miniCacheMaxHeight && - dimensions.height >= miniCacheMaxHeight && {t('ScrollDown')}} -
- )} -
- ) -} diff --git a/web/src/components/DialogTorrentDetailsContent/TorrentCache/LargeSnake.jsx b/web/src/components/DialogTorrentDetailsContent/TorrentCache/LargeSnake.jsx deleted file mode 100644 index 05cb2bc..0000000 --- a/web/src/components/DialogTorrentDetailsContent/TorrentCache/LargeSnake.jsx +++ /dev/null @@ -1,114 +0,0 @@ -import styled, { css } from 'styled-components' -import Measure from 'react-measure' -import { useState } from 'react' -import { v4 as uuidv4 } from 'uuid' -import { useTranslation } from 'react-i18next' - -import { - defaultBackgroundColor, - defaultBorderColor, - progressColor, - completeColor, - activeColor, - rangeColor, -} from './colors' -import getShortCacheMap from './getShortCacheMap' - -const borderWidth = 1 -const defaultPieceSize = 14 -const pieceSizeForMiniMap = 23 -const gapBetweenPieces = 3 -const miniCacheMaxHeight = 340 - -const ScrollNotification = styled.div` - margin-top: 10px; - text-transform: uppercase; - color: rgba(0, 0, 0, 0.5); - align-self: center; -` - -const SnakeWrapper = styled.div` - ${({ pieceSize, piecesInOneRow }) => css` - display: grid; - gap: ${gapBetweenPieces}px; - grid-template-columns: repeat(${piecesInOneRow || 'auto-fit'}, ${pieceSize}px); - grid-auto-rows: max-content; - justify-content: center; - - ${piecesInOneRow && - css` - max-height: ${miniCacheMaxHeight}px; - overflow: auto; - `} - - .piece { - width: ${pieceSize}px; - height: ${pieceSize}px; - background: ${defaultBackgroundColor}; - border: ${borderWidth}px solid ${defaultBorderColor}; - display: grid; - align-items: end; - - &-loading { - background: ${progressColor}; - border-color: ${progressColor}; - } - &-complete { - background: ${completeColor}; - border-color: ${completeColor}; - } - &-reader { - border-color: ${activeColor}; - } - } - - .reader-range { - border-color: ${rangeColor}; - } - `} -` - -const PercentagePiece = styled.div` - background: ${completeColor}; - height: ${({ percentage }) => (percentage / 100) * 12}px; -` - -export default function LargeSnake({ cacheMap, isMini, preloadPiecesAmount }) { - const { t } = useTranslation() - const [dimensions, setDimensions] = useState({ width: 0, height: 0 }) - const pieceSize = isMini ? pieceSizeForMiniMap : defaultPieceSize - - let piecesInOneRow - let shotCacheMap - if (isMini) { - const pieceSizeWithGap = pieceSize + gapBetweenPieces - piecesInOneRow = Math.floor((dimensions.width * 0.95) / pieceSizeWithGap) - shotCacheMap = isMini && getShortCacheMap({ cacheMap, preloadPiecesAmount, piecesInOneRow }) - } - - return isMini ? ( - setDimensions(bounds)}> - {({ measureRef }) => ( -
- - {shotCacheMap.map(({ className, id, percentage }) => ( - - {percentage > 0 && percentage <= 100 && } - - ))} - - - {dimensions.height >= miniCacheMaxHeight && {t('ScrollDown')}} -
- )} -
- ) : ( - - {cacheMap.map(({ className, id, percentage }) => ( - - {percentage > 0 && percentage <= 100 && } - - ))} - - ) -} diff --git a/web/src/components/DialogTorrentDetailsContent/TorrentCache/SingleBlock.jsx b/web/src/components/DialogTorrentDetailsContent/TorrentCache/SingleBlock.jsx deleted file mode 100644 index 3951532..0000000 --- a/web/src/components/DialogTorrentDetailsContent/TorrentCache/SingleBlock.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import { Rect } from 'react-konva' - -import { activeColor, completeColor, defaultBorderColor, progressColor, rangeColor } from './colors' - -export default function SingleBlock({ - x, - y, - percentage, - isActive = false, - inProgress = false, - isReaderRange = false, - isComplete = false, - boxHeight, - strokeWidth, -}) { - const strokeColor = isActive - ? activeColor - : isComplete - ? completeColor - : inProgress - ? progressColor - : isReaderRange - ? rangeColor - : defaultBorderColor - const backgroundColor = inProgress ? progressColor : defaultBorderColor - const percentageProgressColor = completeColor - const processCompletedColor = completeColor - - return ( - - ) -} diff --git a/web/src/components/DialogTorrentDetailsContent/TorrentCache/colors.js b/web/src/components/DialogTorrentDetailsContent/TorrentCache/colors.js deleted file mode 100644 index 235be1d..0000000 --- a/web/src/components/DialogTorrentDetailsContent/TorrentCache/colors.js +++ /dev/null @@ -1,26 +0,0 @@ -export const defaultBorderColor = '#eef2f4' -export const defaultBackgroundColor = '#fff' -export const completeColor = '#00a572' -export const progressColor = '#ffa724' -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 } -} diff --git a/web/src/components/DialogTorrentDetailsContent/TorrentCache/index.jsx b/web/src/components/DialogTorrentDetailsContent/TorrentCache/index.jsx index 0c70c18..6a4903f 100644 --- a/web/src/components/DialogTorrentDetailsContent/TorrentCache/index.jsx +++ b/web/src/components/DialogTorrentDetailsContent/TorrentCache/index.jsx @@ -1,18 +1,59 @@ -import { memo } from 'react' +import Measure from 'react-measure' +import { useState, memo } from 'react' +import { v4 as uuidv4 } from 'uuid' +import { useTranslation } from 'react-i18next' import isEqual from 'lodash/isEqual' import { useCreateCacheMap } from '../customHooks' -import LargeSnake from './LargeSnake' +import { gapBetweenPieces, miniCacheMaxHeight, pieceSizeForMiniMap, defaultPieceSize } from './snakeSettings' +import getShortCacheMap from './getShortCacheMap' +import { SnakeWrapper, PercentagePiece, ScrollNotification } from './style' -const TorrentCache = memo( - ({ cache, isMini }) => { - const cacheMap = useCreateCacheMap(cache) +const TorrentCache = ({ cache, isMini }) => { + const { t } = useTranslation() + const [dimensions, setDimensions] = useState({ width: 0, height: 0 }) + const cacheMap = useCreateCacheMap(cache) - const preloadPiecesAmount = Math.round(cache.Capacity / cache.PiecesLength - 1) + const preloadPiecesAmount = Math.round(cache.Capacity / cache.PiecesLength - 1) - return - }, + const pieceSize = isMini ? pieceSizeForMiniMap : defaultPieceSize + + let piecesInOneRow + let shotCacheMap + if (isMini) { + const pieceSizeWithGap = pieceSize + gapBetweenPieces + piecesInOneRow = Math.floor((dimensions.width * 0.95) / pieceSizeWithGap) + shotCacheMap = isMini && getShortCacheMap({ cacheMap, preloadPiecesAmount, piecesInOneRow }) + } + + return isMini ? ( + setDimensions(bounds)}> + {({ measureRef }) => ( +
+ + {shotCacheMap.map(({ className, id, percentage }) => ( + + {percentage > 0 && percentage <= 100 && } + + ))} + + + {dimensions.height >= miniCacheMaxHeight && {t('ScrollDown')}} +
+ )} +
+ ) : ( + + {cacheMap.map(({ className, id, percentage }) => ( + + {percentage > 0 && percentage <= 100 && } + + ))} + + ) +} + +export default memo( + TorrentCache, (prev, next) => isEqual(prev.cache.Pieces, next.cache.Pieces) && isEqual(prev.cache.Readers, next.cache.Readers), ) - -export default TorrentCache diff --git a/web/src/components/DialogTorrentDetailsContent/TorrentCache/snakeSettings.js b/web/src/components/DialogTorrentDetailsContent/TorrentCache/snakeSettings.js new file mode 100644 index 0000000..82c4463 --- /dev/null +++ b/web/src/components/DialogTorrentDetailsContent/TorrentCache/snakeSettings.js @@ -0,0 +1,12 @@ +export const borderWidth = 1 +export const defaultPieceSize = 14 +export const pieceSizeForMiniMap = 23 +export const gapBetweenPieces = 3 +export const miniCacheMaxHeight = 340 + +export const defaultBorderColor = '#eef2f4' +export const defaultBackgroundColor = '#fff' +export const completeColor = '#00a572' +export const progressColor = '#ffa724' +export const activeColor = '#000' +export const rangeColor = '#9a9aff' diff --git a/web/src/components/DialogTorrentDetailsContent/TorrentCache/style.js b/web/src/components/DialogTorrentDetailsContent/TorrentCache/style.js new file mode 100644 index 0000000..532a6d3 --- /dev/null +++ b/web/src/components/DialogTorrentDetailsContent/TorrentCache/style.js @@ -0,0 +1,66 @@ +import styled, { css } from 'styled-components' + +import { + defaultBackgroundColor, + defaultBorderColor, + progressColor, + completeColor, + activeColor, + rangeColor, + gapBetweenPieces, + miniCacheMaxHeight, + borderWidth, +} from './snakeSettings' + +export const ScrollNotification = styled.div` + margin-top: 10px; + text-transform: uppercase; + color: rgba(0, 0, 0, 0.5); + align-self: center; +` + +export const SnakeWrapper = styled.div` + ${({ pieceSize, piecesInOneRow }) => css` + display: grid; + gap: ${gapBetweenPieces}px; + grid-template-columns: repeat(${piecesInOneRow || 'auto-fit'}, ${pieceSize}px); + grid-auto-rows: max-content; + justify-content: center; + + ${piecesInOneRow && + css` + max-height: ${miniCacheMaxHeight}px; + overflow: auto; + `} + + .piece { + width: ${pieceSize}px; + height: ${pieceSize}px; + background: ${defaultBackgroundColor}; + border: ${borderWidth}px solid ${defaultBorderColor}; + display: grid; + align-items: end; + + &-loading { + background: ${progressColor}; + border-color: ${progressColor}; + } + &-complete { + background: ${completeColor}; + border-color: ${completeColor}; + } + &-reader { + border-color: ${activeColor}; + } + } + + .reader-range { + border-color: ${rangeColor}; + } + `} +` + +export const PercentagePiece = styled.div` + background: ${completeColor}; + height: ${({ percentage }) => (percentage / 100) * 12}px; +` diff --git a/web/yarn.lock b/web/yarn.lock index 96fd161..34ff6b6 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -7938,11 +7938,6 @@ klona@^2.0.4: resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== -konva@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/konva/-/konva-8.0.1.tgz#f34f483cdf62c36f966addc1a7484ed694313c2b" - integrity sha512-QDppGS1L5Dhod1zjwy9GVVjeyfPBHnPncL5oRh1NyjR1mEvhrLjzflrkdW+p73uFIW9hwCDZVLGxzzjQre9izw== - language-subtag-registry@~0.3.2: version "0.3.21" resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a" @@ -10472,14 +10467,6 @@ react-is@^16.7.0, react-is@^16.8.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-konva@^17.0.2-4: - version "17.0.2-4" - resolved "https://registry.yarnpkg.com/react-konva/-/react-konva-17.0.2-4.tgz#afd0968e1295b624bf2a7a154ba294e0d5be55cd" - integrity sha512-YvRVPT81y8sMQV1SY1/tIDetGxBK+7Rk86u4LmiyDBLLE17vD78F01b8EC3AuP3nI3hUaTblfBugUF35cm6Etg== - dependencies: - react-reconciler "~0.26.2" - scheduler "^0.20.2" - react-measure@^2.5.2: version "2.5.2" resolved "https://registry.yarnpkg.com/react-measure/-/react-measure-2.5.2.tgz#4ffc410e8b9cb836d9455a9ff18fc1f0fca67f89" @@ -10499,15 +10486,6 @@ react-query@^3.17.0: broadcast-channel "^3.4.1" match-sorter "^6.0.2" -react-reconciler@~0.26.2: - version "0.26.2" - resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.26.2.tgz#bbad0e2d1309423f76cf3c3309ac6c96e05e9d91" - integrity sha512-nK6kgY28HwrMNwDnMui3dvm3rCFjZrcGiuwLc5COUipBK5hWHLOxMJhSnSomirqWwjPBJKV1QcbkI0VJr7Gl1Q== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler "^0.20.2" - react-refresh@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"