mirror of
https://github.com/Radarr/Radarr.git
synced 2026-04-22 22:15:17 -04:00
Fix: Aphrodite UI enhancements
* New: Display UI before movies have loaded * Revised webpack bundling * New: Option for production build with profiling * Fixed: Faster hasDifferentItems and specialized OrOrder version * Fixed: Faster movie selector * Fixed: Speed up release processing, add indices (migration 161) * Fixed: Use a worker for UI fuzzy search * Fixed: Don't loop over all movies if we know none selected * Fixed: Strip UrlBase from UI events before sending to sentry Should mean that source maps are picked up correctly. * Better selection of jump bar items Show first, last and most common items * Fixed: Don't repeatedly re-render cells * Rework Movie Index and virtualTable * Corresponding improvements for AddListMovie and ImportMovie
This commit is contained in:
@@ -1,13 +1,5 @@
|
||||
$hoverScale: 1.05;
|
||||
|
||||
.container {
|
||||
&:hover {
|
||||
.content {
|
||||
background-color: $tableRowHoverBackgroundColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
|
||||
@@ -80,7 +80,6 @@ class MovieIndexOverview extends Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
style,
|
||||
id,
|
||||
title,
|
||||
overview,
|
||||
@@ -126,7 +125,7 @@ class MovieIndexOverview extends Component {
|
||||
const overviewHeight = contentHeight - titleRowHeight;
|
||||
|
||||
return (
|
||||
<div className={styles.container} style={style}>
|
||||
<div className={styles.container}>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.poster}>
|
||||
<div className={styles.posterContainer}>
|
||||
@@ -247,7 +246,6 @@ class MovieIndexOverview extends Component {
|
||||
}
|
||||
|
||||
MovieIndexOverview.propTypes = {
|
||||
style: PropTypes.object.isRequired,
|
||||
id: PropTypes.number.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
overview: PropTypes.string.isRequired,
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
.grid {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
.container {
|
||||
&:hover {
|
||||
.content {
|
||||
background-color: $tableRowHoverBackgroundColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Grid, WindowScroller } from 'react-virtualized';
|
||||
import getIndexOfFirstCharacter from 'Utilities/Array/getIndexOfFirstCharacter';
|
||||
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
||||
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
|
||||
import dimensions from 'Styles/Variables/dimensions';
|
||||
import { sortDirections } from 'Helpers/Props';
|
||||
import Measure from 'Components/Measure';
|
||||
import MovieIndexItemConnector from 'Movie/Index/MovieIndexItemConnector';
|
||||
import MovieIndexOverview from './MovieIndexOverview';
|
||||
@@ -66,56 +63,44 @@ class MovieIndexOverviews extends Component {
|
||||
rowHeight: calculateRowHeight(238, null, props.isSmallScreen, {})
|
||||
};
|
||||
|
||||
this._isInitialized = false;
|
||||
this._grid = null;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._contentBodyNode = ReactDOM.findDOMNode(this.props.contentBody);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
const {
|
||||
items,
|
||||
filters,
|
||||
sortKey,
|
||||
sortDirection,
|
||||
overviewOptions,
|
||||
jumpToCharacter
|
||||
} = this.props;
|
||||
|
||||
const itemsChanged = hasDifferentItems(prevProps.items, items);
|
||||
const overviewOptionsChanged = !_.isMatch(prevProps.overviewOptions, overviewOptions);
|
||||
const {
|
||||
width,
|
||||
rowHeight
|
||||
} = this.state;
|
||||
|
||||
if (
|
||||
prevProps.sortKey !== sortKey ||
|
||||
prevProps.overviewOptions !== overviewOptions ||
|
||||
itemsChanged
|
||||
) {
|
||||
if (prevProps.sortKey !== sortKey ||
|
||||
prevProps.overviewOptions !== overviewOptions) {
|
||||
this.calculateGrid();
|
||||
}
|
||||
|
||||
if (
|
||||
prevProps.filters !== filters ||
|
||||
prevProps.sortKey !== sortKey ||
|
||||
prevProps.sortDirection !== sortDirection ||
|
||||
itemsChanged ||
|
||||
overviewOptionsChanged
|
||||
) {
|
||||
if (this._grid &&
|
||||
(prevState.width !== width ||
|
||||
prevState.rowHeight !== rowHeight ||
|
||||
hasDifferentItemsOrOrder(prevProps.items, items))) {
|
||||
// recomputeGridSize also forces Grid to discard its cache of rendered cells
|
||||
this._grid.recomputeGridSize();
|
||||
}
|
||||
|
||||
if (jumpToCharacter != null && jumpToCharacter !== prevProps.jumpToCharacter) {
|
||||
const index = getIndexOfFirstCharacter(items, jumpToCharacter);
|
||||
|
||||
if (index != null) {
|
||||
const {
|
||||
rowHeight
|
||||
} = this.state;
|
||||
if (this._grid && index != null) {
|
||||
|
||||
const scrollTop = rowHeight * index;
|
||||
|
||||
this.props.onScroll({ scrollTop });
|
||||
this._grid.scrollToCell({
|
||||
rowIndex: index,
|
||||
columnIndex: 0
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,21 +108,6 @@ class MovieIndexOverviews extends Component {
|
||||
//
|
||||
// Control
|
||||
|
||||
scrollToFirstCharacter(character) {
|
||||
const items = this.props.items;
|
||||
const {
|
||||
rowHeight
|
||||
} = this.state;
|
||||
|
||||
const index = getIndexOfFirstCharacter(items, character);
|
||||
|
||||
if (index != null) {
|
||||
const scrollTop = rowHeight * index;
|
||||
|
||||
this.props.onScroll({ scrollTop });
|
||||
}
|
||||
}
|
||||
|
||||
setGridRef = (ref) => {
|
||||
this._grid = ref;
|
||||
}
|
||||
@@ -188,26 +158,31 @@ class MovieIndexOverviews extends Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<MovieIndexItemConnector
|
||||
<div
|
||||
className={styles.container}
|
||||
key={key}
|
||||
component={MovieIndexOverview}
|
||||
sortKey={sortKey}
|
||||
posterWidth={posterWidth}
|
||||
posterHeight={posterHeight}
|
||||
rowHeight={rowHeight}
|
||||
overviewOptions={overviewOptions}
|
||||
showRelativeDates={showRelativeDates}
|
||||
shortDateFormat={shortDateFormat}
|
||||
longDateFormat={longDateFormat}
|
||||
timeFormat={timeFormat}
|
||||
isSmallScreen={isSmallScreen}
|
||||
style={style}
|
||||
movieId={movie.id}
|
||||
qualityProfileId={movie.qualityProfileId}
|
||||
isSelected={selectedState[movie.id]}
|
||||
onSelectedChange={onSelectedChange}
|
||||
isMovieEditorActive={isMovieEditorActive}
|
||||
/>
|
||||
>
|
||||
<MovieIndexItemConnector
|
||||
key={movie.id}
|
||||
component={MovieIndexOverview}
|
||||
sortKey={sortKey}
|
||||
posterWidth={posterWidth}
|
||||
posterHeight={posterHeight}
|
||||
rowHeight={rowHeight}
|
||||
overviewOptions={overviewOptions}
|
||||
showRelativeDates={showRelativeDates}
|
||||
shortDateFormat={shortDateFormat}
|
||||
longDateFormat={longDateFormat}
|
||||
timeFormat={timeFormat}
|
||||
isSmallScreen={isSmallScreen}
|
||||
movieId={movie.id}
|
||||
qualityProfileId={movie.qualityProfileId}
|
||||
isSelected={selectedState[movie.id]}
|
||||
onSelectedChange={onSelectedChange}
|
||||
isMovieEditorActive={isMovieEditorActive}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -218,22 +193,14 @@ class MovieIndexOverviews extends Component {
|
||||
this.calculateGrid(width, this.props.isSmallScreen);
|
||||
}
|
||||
|
||||
onSectionRendered = () => {
|
||||
if (!this._isInitialized && this._contentBodyNode) {
|
||||
this.props.onRender();
|
||||
this._isInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
items,
|
||||
scrollTop,
|
||||
isSmallScreen,
|
||||
onScroll,
|
||||
scroller,
|
||||
items,
|
||||
selectedState
|
||||
} = this.props;
|
||||
|
||||
@@ -243,29 +210,39 @@ class MovieIndexOverviews extends Component {
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<Measure onMeasure={this.onMeasure}>
|
||||
<Measure
|
||||
whitelist={['width']}
|
||||
onMeasure={this.onMeasure}
|
||||
>
|
||||
<WindowScroller
|
||||
scrollElement={isSmallScreen ? undefined : this._contentBodyNode}
|
||||
onScroll={onScroll}
|
||||
scrollElement={isSmallScreen ? undefined : scroller}
|
||||
>
|
||||
{({ height, isScrolling }) => {
|
||||
{({ height, registerChild, onChildScroll, scrollTop }) => {
|
||||
if (!height) {
|
||||
return <div />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid
|
||||
ref={this.setGridRef}
|
||||
className={styles.grid}
|
||||
autoHeight={true}
|
||||
height={height}
|
||||
columnCount={1}
|
||||
columnWidth={width}
|
||||
rowCount={items.length}
|
||||
rowHeight={rowHeight}
|
||||
width={width}
|
||||
scrollTop={scrollTop}
|
||||
overscanRowCount={2}
|
||||
cellRenderer={this.cellRenderer}
|
||||
onSectionRendered={this.onSectionRendered}
|
||||
selectedState={selectedState}
|
||||
/>
|
||||
<div ref={registerChild}>
|
||||
<Grid
|
||||
ref={this.setGridRef}
|
||||
className={styles.grid}
|
||||
autoHeight={true}
|
||||
height={height}
|
||||
columnCount={1}
|
||||
columnWidth={width}
|
||||
rowCount={items.length}
|
||||
rowHeight={rowHeight}
|
||||
width={width}
|
||||
onScroll={onChildScroll}
|
||||
scrollTop={scrollTop}
|
||||
overscanRowCount={2}
|
||||
cellRenderer={this.cellRenderer}
|
||||
selectedState={selectedState}
|
||||
scrollToAlignment={'start'}
|
||||
isScrollingOptout={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -277,20 +254,15 @@ class MovieIndexOverviews extends Component {
|
||||
|
||||
MovieIndexOverviews.propTypes = {
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
sortKey: PropTypes.string,
|
||||
sortDirection: PropTypes.oneOf(sortDirections.all),
|
||||
overviewOptions: PropTypes.object.isRequired,
|
||||
scrollTop: PropTypes.number.isRequired,
|
||||
jumpToCharacter: PropTypes.string,
|
||||
contentBody: PropTypes.object.isRequired,
|
||||
scroller: PropTypes.instanceOf(Element).isRequired,
|
||||
showRelativeDates: PropTypes.bool.isRequired,
|
||||
shortDateFormat: PropTypes.string.isRequired,
|
||||
longDateFormat: PropTypes.string.isRequired,
|
||||
isSmallScreen: PropTypes.bool.isRequired,
|
||||
timeFormat: PropTypes.string.isRequired,
|
||||
onRender: PropTypes.func.isRequired,
|
||||
onScroll: PropTypes.func.isRequired,
|
||||
selectedState: PropTypes.object.isRequired,
|
||||
onSelectedChange: PropTypes.func.isRequired,
|
||||
isMovieEditorActive: PropTypes.bool.isRequired
|
||||
|
||||
Reference in New Issue
Block a user