diff --git a/frontend/src/Collection/Overview/CollectionOverviews.js b/frontend/src/Collection/Overview/CollectionOverviews.js index c2faffb18..a64adceb7 100644 --- a/frontend/src/Collection/Overview/CollectionOverviews.js +++ b/frontend/src/Collection/Overview/CollectionOverviews.js @@ -53,7 +53,8 @@ class CollectionOverviews extends Component { columnCount: 1, posterWidth: 162, posterHeight: 238, - rowHeight: calculateRowHeight(238, null, props.isSmallScreen, {}) + rowHeight: calculateRowHeight(238, null, props.isSmallScreen, {}), + navigateToId: props.location.state?.navigateToId ?? 0 }; this._grid = null; @@ -72,7 +73,8 @@ class CollectionOverviews extends Component { const { width, rowHeight, - scrollRestored + scrollRestored, + navigateToId } = this.state; if (prevProps.sortKey !== sortKey || @@ -106,6 +108,10 @@ class CollectionOverviews extends Component { }); } } + + if (navigateToId) { + this.scrollToItem(navigateToId); + } } // @@ -186,6 +192,25 @@ class CollectionOverviews extends Component { ); }; + scrollToItem = (itemId) => { + const index = this.props.items.findIndex((item) => item.tmdbId === itemId); + + if (index !== -1 && this._grid) { + this._grid.scrollToCell({ + columnIndex: 0, + rowIndex: index, + align: 'start' + }); + } + + // Replacing the history to prevent navigating back to this item on re-renders + window.history.replaceState({}, ''); + + this.setState({ + navigateToId: 0 + }); + }; + // // Listeners @@ -265,7 +290,8 @@ CollectionOverviews.propTypes = { isSmallScreen: PropTypes.bool.isRequired, timeFormat: PropTypes.string.isRequired, selectedState: PropTypes.object.isRequired, - onSelectedChange: PropTypes.func.isRequired + onSelectedChange: PropTypes.func.isRequired, + location: PropTypes.object.isRequired }; export default CollectionOverviews; diff --git a/frontend/src/Components/Link/Link.tsx b/frontend/src/Components/Link/Link.tsx index a7a7cd9b6..7e304584c 100644 --- a/frontend/src/Components/Link/Link.tsx +++ b/frontend/src/Components/Link/Link.tsx @@ -8,17 +8,13 @@ import React, { import { Link as RouterLink } from 'react-router-dom'; import styles from './Link.css'; -interface ReactRouterLinkProps { - to?: string; -} - export interface LinkProps extends React.HTMLProps { className?: string; component?: | string | FunctionComponent | ComponentClass; - to?: string; + to?: string | { pathname: string; state?: object }; target?: string; isDisabled?: boolean; noRouter?: boolean; @@ -46,26 +42,38 @@ function Link(props: LinkProps) { [isDisabled, onPress] ); - const linkProps: React.HTMLProps & ReactRouterLinkProps = { + const linkProps: React.HTMLProps & LinkProps = { target, }; let el = component; if (to) { - if (/\w+?:\/\//.test(to)) { - el = 'a'; - linkProps.href = to; - linkProps.target = target || '_blank'; - linkProps.rel = 'noreferrer'; - } else if (noRouter) { - el = 'a'; - linkProps.href = to; - linkProps.target = target || '_self'; + if (typeof to === 'string') { + if (/\w+?:\/\//.test(to)) { + el = 'a'; + linkProps.href = to; + linkProps.target = target || '_blank'; + linkProps.rel = 'noreferrer'; + } else if (noRouter) { + el = 'a'; + linkProps.href = to; + linkProps.target = target || '_self'; + } else { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + el = RouterLink; + linkProps.to = `${window.Radarr.urlBase}/${to.replace(/^\//, '')}`; + linkProps.target = target; + } } else { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore el = RouterLink; - linkProps.to = `${window.Radarr.urlBase}/${to.replace(/^\//, '')}`; + const url = `${window.Radarr.urlBase}/${to.pathname.replace(/^\//, '')}`; + linkProps.to = { + pathname: url, + ...(to.state && { state: to.state }), + }; linkProps.target = target; } } diff --git a/frontend/src/Movie/MovieCollectionLabel.css b/frontend/src/Movie/MovieCollectionLabel.css index 7a75a4a1c..041c1ead4 100644 --- a/frontend/src/Movie/MovieCollectionLabel.css +++ b/frontend/src/Movie/MovieCollectionLabel.css @@ -7,3 +7,7 @@ color: var(--iconButtonHoverLightColor); } } + +.titleLink { + color: inherit; +} diff --git a/frontend/src/Movie/MovieCollectionLabel.css.d.ts b/frontend/src/Movie/MovieCollectionLabel.css.d.ts index 0e1588bee..0b1225152 100644 --- a/frontend/src/Movie/MovieCollectionLabel.css.d.ts +++ b/frontend/src/Movie/MovieCollectionLabel.css.d.ts @@ -2,6 +2,7 @@ // Please do not change this file! interface CssExports { 'monitorToggleButton': string; + 'titleLink': string; } export const cssExports: CssExports; export default cssExports; diff --git a/frontend/src/Movie/MovieCollectionLabel.js b/frontend/src/Movie/MovieCollectionLabel.js index fb071f91c..8789adb43 100644 --- a/frontend/src/Movie/MovieCollectionLabel.js +++ b/frontend/src/Movie/MovieCollectionLabel.js @@ -1,5 +1,6 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; +import Link from 'Components/Link/Link'; import MonitorToggleButton from 'Components/MonitorToggleButton'; import styles from './MovieCollectionLabel.css'; @@ -18,6 +19,7 @@ class MovieCollectionLabel extends Component { render() { const { + tmdbId, title, monitored, onMonitorTogglePress @@ -31,7 +33,15 @@ class MovieCollectionLabel extends Component { size={15} onPress={onMonitorTogglePress} /> - {title} + + {title} + ); } @@ -40,7 +50,8 @@ class MovieCollectionLabel extends Component { MovieCollectionLabel.propTypes = { title: PropTypes.string.isRequired, monitored: PropTypes.bool.isRequired, - onMonitorTogglePress: PropTypes.func.isRequired + onMonitorTogglePress: PropTypes.func.isRequired, + tmdbId: PropTypes.number.isRequired }; export default MovieCollectionLabel; diff --git a/frontend/src/Store/Selectors/createCollectionClientSideCollectionItemsSelector.js b/frontend/src/Store/Selectors/createCollectionClientSideCollectionItemsSelector.js index fd925a81c..0ff24f289 100644 --- a/frontend/src/Store/Selectors/createCollectionClientSideCollectionItemsSelector.js +++ b/frontend/src/Store/Selectors/createCollectionClientSideCollectionItemsSelector.js @@ -9,11 +9,13 @@ function createUnoptimizedSelector(uiSection) { const items = movies.items.map((s) => { const { id, + tmdbId, sortTitle } = s; return { id, + tmdbId, sortTitle }; });