diff --git a/frontend/src/Components/Table/VirtualTable.js b/frontend/src/Components/Table/VirtualTable.js index d877b8208..b80619411 100644 --- a/frontend/src/Components/Table/VirtualTable.js +++ b/frontend/src/Components/Table/VirtualTable.js @@ -39,7 +39,8 @@ class VirtualTable extends Component { super(props, context); this.state = { - width: 0 + width: 0, + scrollRestored: false }; this._grid = null; @@ -48,20 +49,25 @@ class VirtualTable extends Component { componentDidUpdate(prevProps, prevState) { const { items, - scrollIndex + scrollIndex, + scrollTop } = this.props; const { - width + width, + scrollRestored } = this.state; - if (this._grid && - (prevState.width !== width || - hasDifferentItemsOrOrder(prevProps.items, items))) { + if (this._grid && (prevState.width !== width || hasDifferentItemsOrOrder(prevProps.items, items))) { // recomputeGridSize also forces Grid to discard its cache of rendered cells this._grid.recomputeGridSize(); } + if (this._grid && scrollTop !== undefined && scrollTop !== 0 && !scrollRestored) { + this.setState({ scrollRestored: true }); + this._grid.scrollToPosition({ scrollTop }); + } + if (scrollIndex != null && scrollIndex !== prevProps.scrollIndex) { this._grid.scrollToCell({ rowIndex: scrollIndex, @@ -98,6 +104,7 @@ class VirtualTable extends Component { focusScroller, header, headerHeight, + rowHeight, rowRenderer, ...otherProps } = this.props; @@ -141,6 +148,7 @@ class VirtualTable extends Component { {header}
@@ -180,16 +187,19 @@ VirtualTable.propTypes = { className: PropTypes.string.isRequired, items: PropTypes.arrayOf(PropTypes.object).isRequired, scrollIndex: PropTypes.number, + scrollTop: PropTypes.number, scroller: PropTypes.instanceOf(Element).isRequired, focusScroller: PropTypes.bool.isRequired, header: PropTypes.node.isRequired, headerHeight: PropTypes.number.isRequired, - rowRenderer: PropTypes.func.isRequired + rowRenderer: PropTypes.func.isRequired, + rowHeight: PropTypes.number.isRequired }; VirtualTable.defaultProps = { className: styles.tableContainer, headerHeight: 38, + rowHeight: ROW_HEIGHT, focusScroller: true }; diff --git a/frontend/src/Indexer/Index/IndexerIndex.tsx b/frontend/src/Indexer/Index/IndexerIndex.tsx index 212edbd27..587206066 100644 --- a/frontend/src/Indexer/Index/IndexerIndex.tsx +++ b/frontend/src/Indexer/Index/IndexerIndex.tsx @@ -158,7 +158,7 @@ const IndexerIndex = withScrollPosition((props: IndexerIndexProps) => { const characters = items.reduce((acc, item) => { let char = item.sortName.charAt(0); - if (!isNaN(char)) { + if (!isNaN(Number(char))) { char = '#'; } diff --git a/frontend/src/Indexer/Index/Table/IndexerIndexTable.tsx b/frontend/src/Indexer/Index/Table/IndexerIndexTable.tsx index bfa7e3d38..939c02abb 100644 --- a/frontend/src/Indexer/Index/Table/IndexerIndexTable.tsx +++ b/frontend/src/Indexer/Index/Table/IndexerIndexTable.tsx @@ -92,11 +92,9 @@ function IndexerIndexTable(props: IndexerIndexTableProps) { const columns = useSelector(columnsSelector); const { showBanners } = useSelector(selectTableOptions); - const listRef: React.MutableRefObject = useRef(); + const listRef = useRef(null); const [measureRef, bounds] = useMeasure(); const [size, setSize] = useState({ width: 0, height: 0 }); - const windowWidth = window.innerWidth; - const windowHeight = window.innerHeight; const rowHeight = useMemo(() => { return showBanners ? 70 : 38; @@ -107,8 +105,8 @@ function IndexerIndexTable(props: IndexerIndexTableProps) { if (isSmallScreen) { setSize({ - width: windowWidth, - height: windowHeight, + width: window.innerWidth, + height: window.innerHeight, }); return; @@ -121,14 +119,14 @@ function IndexerIndexTable(props: IndexerIndexTableProps) { setSize({ width: width - padding * 2, - height: windowHeight, + height: window.innerHeight, }); } - }, [isSmallScreen, windowWidth, windowHeight, scrollerRef, bounds]); + }, [isSmallScreen, scrollerRef, bounds]); useEffect(() => { - const currentScrollListener = isSmallScreen ? window : scrollerRef.current; - const currentScrollerRef = scrollerRef.current; + const currentScrollerRef = scrollerRef.current as HTMLElement; + const currentScrollListener = isSmallScreen ? window : currentScrollerRef; const handleScroll = throttle(() => { const { offsetTop = 0 } = currentScrollerRef; @@ -137,7 +135,7 @@ function IndexerIndexTable(props: IndexerIndexTableProps) { ? getWindowScrollTopPosition() : currentScrollerRef.scrollTop) - offsetTop; - listRef.current.scrollTo(scrollTop); + listRef.current?.scrollTo(scrollTop); }, 10); currentScrollListener.addEventListener('scroll', handleScroll); @@ -166,8 +164,8 @@ function IndexerIndexTable(props: IndexerIndexTableProps) { scrollTop += offset; } - listRef.current.scrollTo(scrollTop); - scrollerRef.current.scrollTo(0, scrollTop); + listRef.current?.scrollTo(scrollTop); + scrollerRef.current?.scrollTo(0, scrollTop); } } }, [jumpToCharacter, rowHeight, items, scrollerRef, listRef]); diff --git a/frontend/src/Search/SearchIndex.js b/frontend/src/Search/SearchIndex.js index 62748f641..ba25b0912 100644 --- a/frontend/src/Search/SearchIndex.js +++ b/frontend/src/Search/SearchIndex.js @@ -154,7 +154,7 @@ class SearchIndex extends Component { const characters = _.reduce(items, (acc, item) => { let char = item.sortTitle.charAt(0); - if (!isNaN(char)) { + if (!isNaN(Number(char))) { char = '#'; } diff --git a/frontend/src/Store/Selectors/createIndexerClientSideCollectionItemsSelector.js b/frontend/src/Store/Selectors/createIndexerClientSideCollectionItemsSelector.js index 098562877..c0edaa6dd 100644 --- a/frontend/src/Store/Selectors/createIndexerClientSideCollectionItemsSelector.js +++ b/frontend/src/Store/Selectors/createIndexerClientSideCollectionItemsSelector.js @@ -9,12 +9,12 @@ function createUnoptimizedSelector(uiSection) { const items = indexers.items.map((s) => { const { id, - name + sortName } = s; return { id, - sortName: name + sortName }; }); diff --git a/frontend/src/Store/Selectors/createReleaseClientSideCollectionItemsSelector.js b/frontend/src/Store/Selectors/createReleaseClientSideCollectionItemsSelector.js index c76ba4236..4bc195aa5 100644 --- a/frontend/src/Store/Selectors/createReleaseClientSideCollectionItemsSelector.js +++ b/frontend/src/Store/Selectors/createReleaseClientSideCollectionItemsSelector.js @@ -9,13 +9,13 @@ function createUnoptimizedSelector(uiSection) { const items = releases.items.map((s) => { const { guid, - title, + sortTitle, indexerId } = s; return { guid, - sortTitle: title, + sortTitle, indexerId }; }); @@ -40,7 +40,7 @@ const createMovieEqualSelector = createSelectorCreator( function createReleaseClientSideCollectionItemsSelector(uiSection) { return createMovieEqualSelector( createUnoptimizedSelector(uiSection), - (movies) => movies + (releases) => releases ); } diff --git a/frontend/src/Utilities/Array/getIndexOfFirstCharacter.js b/frontend/src/Utilities/Array/getIndexOfFirstCharacter.js index 5cbb30085..a0dbc4d0d 100644 --- a/frontend/src/Utilities/Array/getIndexOfFirstCharacter.js +++ b/frontend/src/Utilities/Array/getIndexOfFirstCharacter.js @@ -1,9 +1,9 @@ export default function getIndexOfFirstCharacter(items, character) { return items.findIndex((item) => { - const firstCharacter = item.sortTitle.charAt(0); + const firstCharacter = 'sortName' in item ? item.sortName.charAt(0) : item.sortTitle.charAt(0); if (character === '#') { - return !isNaN(firstCharacter); + return !isNaN(Number(firstCharacter)); } return firstCharacter === character; diff --git a/package.json b/package.json index 9d8d54aff..b3ce34388 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "react-router-dom": "5.2.0", "react-text-truncate": "0.19.0", "react-use-measure": "2.1.1", - "react-virtualized": "9.22.3", + "react-virtualized": "9.21.1", "react-window": "1.8.8", "redux": "4.2.1", "redux-actions": "2.6.5", diff --git a/yarn.lock b/yarn.lock index a21da4ac6..2a0332158 100644 --- a/yarn.lock +++ b/yarn.lock @@ -980,7 +980,7 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.21.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200" integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q== @@ -2077,6 +2077,14 @@ babel-plugin-transform-react-remove-prop-types@0.4.24: resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a" integrity sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA== +babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g== + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + balanced-match@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.1.0.tgz#b504bd05869b39259dd0c5efc35d843176dccc4a" @@ -2326,7 +2334,7 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== -clsx@^1.0.4: +clsx@^1.0.1: version "1.2.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== @@ -2461,6 +2469,11 @@ core-js@3.30.2: resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.30.2.tgz#6528abfda65e5ad728143ea23f7a14f0dcf503fc" integrity sha512-uBJiDmwqsbJCWHAwjrx3cvjbMXP7xD72Dmsn5LOJpiRmE3WbBbN5rCqQ2Qh6Ek6/eOrjlWngEynBWo4VxerQhg== +core-js@^2.4.0: + version "2.6.12" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" + integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== + core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" @@ -2730,13 +2743,12 @@ dom-css@^2.0.0: prefix-style "2.0.1" to-camel-case "1.0.0" -dom-helpers@^5.1.3: - version "5.2.1" - resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" - integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== +"dom-helpers@^2.4.0 || ^3.0.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8" + integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA== dependencies: - "@babel/runtime" "^7.8.7" - csstype "^3.0.2" + "@babel/runtime" "^7.1.2" dom-serializer@^1.0.1: version "1.4.1" @@ -4179,6 +4191,11 @@ line-diff@^2.0.1: dependencies: levdist "^1.0.0" +linear-layout-vector@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/linear-layout-vector/-/linear-layout-vector-0.0.1.tgz#398114d7303b6ecc7fd6b273af7b8401d8ba9c70" + integrity sha512-w+nr1ZOVFGyMhwr8JKo0YzqDc8C2Z7pc9UbTuJA4VG/ezlSFEx+7kNrfCYvq7JQ/jHKR+FWy6reNrkVVzm0hSA== + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -4308,7 +4325,7 @@ lodash@4.17.21, lodash@^4.17.14, lodash@^4.17.20, lodash@^4.17.21: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -5099,7 +5116,7 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -prop-types@15.8.1, prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@15.8.1, prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -5394,16 +5411,17 @@ react-use-measure@2.1.1: dependencies: debounce "^1.2.1" -react-virtualized@9.22.3: - version "9.22.3" - resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.22.3.tgz#f430f16beb0a42db420dbd4d340403c0de334421" - integrity sha512-MKovKMxWTcwPSxE1kK1HcheQTWfuCxAuBoSTf2gwyMM21NdX/PXUhnoP8Uc5dRKd+nKm8v41R36OellhdCpkrw== - dependencies: - "@babel/runtime" "^7.7.2" - clsx "^1.0.4" - dom-helpers "^5.1.3" - loose-envify "^1.4.0" - prop-types "^15.7.2" +react-virtualized@9.21.1: + version "9.21.1" + resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.21.1.tgz#4dbbf8f0a1420e2de3abf28fbb77120815277b3a" + integrity sha512-E53vFjRRMCyUTEKuDLuGH1ld/9TFzjf/fFW816PE4HFXWZorESbSTYtiZz1oAjra0MminaUU1EnvUxoGuEFFPA== + dependencies: + babel-runtime "^6.26.0" + clsx "^1.0.1" + dom-helpers "^2.4.0 || ^3.0.0" + linear-layout-vector "0.0.1" + loose-envify "^1.3.0" + prop-types "^15.6.0" react-lifecycles-compat "^3.0.4" react-window@1.8.8: @@ -5542,6 +5560,11 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== + regenerator-runtime@^0.13.11: version "0.13.11" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"