Various UI fixes

Fixed: Manual Import not working if no episodes are Missing
Fixed: Allow spaces in Must/Must not contain fields
Fixed: Show loading indicator when managing episodes and fetching data from the server
Fixed: Series images not loading for all search results
New: Auto focus search input when navigating to Add Series page
This commit is contained in:
Mark McDowall 2019-02-06 19:22:21 -08:00
parent 561fdef815
commit 102b8afd66
8 changed files with 114 additions and 56 deletions

View File

@ -100,6 +100,7 @@ class AddNewSeries extends Component {
name="seriesLookup"
value={term}
placeholder="eg. Breaking Bad, tvdb:####"
autoFocus={true}
onChange={this.onSearchInputChange}
/>

View File

@ -78,12 +78,14 @@ class AddNewSeriesSearchResult extends Component {
{...linkProps}
>
{
!isSmallScreen &&
<SeriesPoster
className={styles.poster}
images={images}
size={250}
/>
isSmallScreen ?
null :
<SeriesPoster
className={styles.poster}
images={images}
size={250}
overflow={true}
/>
}
<div>
@ -91,18 +93,22 @@ class AddNewSeriesSearchResult extends Component {
{title}
{
!title.contains(year) && !!year &&
<span className={styles.year}>({year})</span>
!title.contains(year) && year ?
<span className={styles.year}>
({year})
</span> :
null
}
{
isExistingSeries &&
<Icon
className={styles.alreadyExistsIcon}
name={icons.CHECK_CIRCLE}
size={36}
title="Already in your library"
/>
isExistingSeries ?
<Icon
className={styles.alreadyExistsIcon}
name={icons.CHECK_CIRCLE}
size={36}
title="Already in your library"
/> :
null
}
</div>
@ -115,27 +121,30 @@ class AddNewSeriesSearchResult extends Component {
</Label>
{
!!network &&
<Label size={sizes.LARGE}>
{network}
</Label>
network ?
<Label size={sizes.LARGE}>
{network}
</Label> :
null
}
{
!!seasonCount &&
<Label size={sizes.LARGE}>
{seasons}
</Label>
seasonCount ?
<Label size={sizes.LARGE}>
{seasons}
</Label> :
null
}
{
status === 'ended' &&
<Label
kind={kinds.DANGER}
size={sizes.LARGE}
>
status === 'ended' ?
<Label
kind={kinds.DANGER}
size={sizes.LARGE}
>
Ended
</Label>
</Label> :
null
}
</div>

View File

@ -5,10 +5,11 @@ import getSelectedIds from 'Utilities/Table/getSelectedIds';
import selectAll from 'Utilities/Table/selectAll';
import toggleSelected from 'Utilities/Table/toggleSelected';
import { kinds } from 'Helpers/Props';
import ConfirmModal from 'Components/Modal/ConfirmModal';
import SelectInput from 'Components/Form/SelectInput';
import Button from 'Components/Link/Button';
import SpinnerButton from 'Components/Link/SpinnerButton';
import SelectInput from 'Components/Form/SelectInput';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import ConfirmModal from 'Components/Modal/ConfirmModal';
import ModalContent from 'Components/Modal/ModalContent';
import ModalHeader from 'Components/Modal/ModalHeader';
import ModalBody from 'Components/Modal/ModalBody';
@ -140,6 +141,9 @@ class EpisodeFileEditorModalContent extends Component {
const {
seasonNumber,
isDeleting,
isFetching,
isPopulated,
error,
items,
languages,
qualities,
@ -182,14 +186,27 @@ class EpisodeFileEditorModalContent extends Component {
<ModalBody>
{
!items.length &&
<div>
No episode files to manage.
</div>
isFetching && !isPopulated ?
<LoadingIndicator /> :
null
}
{
!!items.length &&
!isFetching && error ?
<div>{error}</div> :
null
}
{
isPopulated && !items.length ?
<div>
No episode files to manage.
</div>:
null
}
{
isPopulated && items.length ?
<Table
columns={columns}
selectAll={true}
@ -212,7 +229,8 @@ class EpisodeFileEditorModalContent extends Component {
})
}
</TableBody>
</Table>
</Table> :
null
}
</ModalBody>
@ -272,6 +290,9 @@ class EpisodeFileEditorModalContent extends Component {
EpisodeFileEditorModalContent.propTypes = {
seasonNumber: PropTypes.number,
isDeleting: PropTypes.bool.isRequired,
isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired,
error: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
qualities: PropTypes.arrayOf(PropTypes.object).isRequired,

View File

@ -10,20 +10,45 @@ import { deleteEpisodeFiles, updateEpisodeFiles } from 'Store/Actions/episodeFil
import { fetchLanguageProfileSchema, fetchQualityProfileSchema } from 'Store/Actions/settingsActions';
import EpisodeFileEditorModalContent from './EpisodeFileEditorModalContent';
function createSchemaSelector() {
return createSelector(
(state) => state.settings.languageProfiles,
(state) => state.settings.qualityProfiles,
(languageProfiles, qualityProfiles) => {
const languages = _.map(languageProfiles.schema.languages, 'language');
const qualities = getQualities(qualityProfiles.schema.items);
let error = null;
if (languageProfiles.schemaError) {
error = 'Unable to load languages';
} else if (qualityProfiles.schemaError) {
error = 'Unable to load qualities';
}
return {
isFetching: languageProfiles.isSchemaFetching || qualityProfiles.isSchemaFetching,
isPopulated: languageProfiles.isSchemaPopulated && qualityProfiles.isSchemaPopulated,
error,
languages,
qualities
};
}
);
}
function createMapStateToProps() {
return createSelector(
(state, { seasonNumber }) => seasonNumber,
(state) => state.episodes,
(state) => state.episodeFiles,
(state) => state.settings.languageProfiles.schema,
(state) => state.settings.qualityProfiles.schema,
createSchemaSelector(),
createSeriesSelector(),
(
seasonNumber,
episodes,
episodeFiles,
languageProfilesSchema,
qualityProfileSchema,
schema,
series
) => {
const filtered = _.filter(episodes.items, (episode) => {
@ -51,16 +76,12 @@ function createMapStateToProps() {
};
});
const languages = _.map(languageProfilesSchema.languages, 'language');
const qualities = getQualities(qualityProfileSchema.items);
return {
...schema,
items,
seriesType: series.seriesType,
isDeleting: episodeFiles.isDeleting,
isSaving: episodeFiles.isSaving,
languages,
qualities
isSaving: episodeFiles.isSaving
};
}
);
@ -96,9 +117,6 @@ class EpisodeFileEditorModalContentConnector extends Component {
this.props.dispatchFetchQualityProfileSchema();
}
//
// Render
//
// Listeners
@ -120,6 +138,9 @@ class EpisodeFileEditorModalContentConnector extends Component {
this.props.dispatchUpdateEpisodeFiles({ episodeFileIds, quality });
}
//
// Render
render() {
const {
dispatchFetchLanguageProfileSchema,

View File

@ -159,6 +159,7 @@ class SeriesImage extends Component {
style={style}
src={url}
onError={this.onError}
onLoad={this.onLoad}
/>
</LazyLoad>
);

View File

@ -31,7 +31,7 @@ class AddDownloadClientModalContent extends Component {
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
Add DownloadClient
Add Download Client
</ModalHeader>
<ModalBody>

View File

@ -13,6 +13,9 @@ import FormLabel from 'Components/Form/FormLabel';
import FormInputGroup from 'Components/Form/FormInputGroup';
import styles from './EditReleaseProfileModalContent.css';
// Tab, enter, and comma
const tagInputDelimiters = [9, 13, 188];
function EditReleaseProfileModalContent(props) {
const {
isSaving,
@ -51,6 +54,7 @@ function EditReleaseProfileModalContent(props) {
helpText="The release must contain at least one of these terms (case insensitive)"
kind={kinds.SUCCESS}
placeholder="Add new restriction"
delimiters={tagInputDelimiters}
{...required}
onChange={onInputChange}
/>
@ -65,6 +69,7 @@ function EditReleaseProfileModalContent(props) {
helpText="The release will be rejected if it contains one or more of terms (case insensitive)"
kind={kinds.DANGER}
placeholder="Add new restriction"
delimiters={tagInputDelimiters}
{...ignored}
onChange={onInputChange}
/>

View File

@ -265,13 +265,13 @@ class Missing extends Component {
onConfirm={this.onSearchAllMissingConfirmed}
onCancel={this.onConfirmSearchAllMissingModalClose}
/>
<InteractiveImportModal
isOpen={isInteractiveImportModalOpen}
onModalClose={this.onInteractiveImportModalClose}
/>
</div>
}
<InteractiveImportModal
isOpen={isInteractiveImportModalOpen}
onModalClose={this.onInteractiveImportModalClose}
/>
</PageContentBodyConnector>
</PageContent>
);