Fixed: UI and Command manager updates

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
This commit is contained in:
Qstick 2018-08-28 23:01:02 -04:00
parent d9a51a1d02
commit ba96dad8c7
40 changed files with 301 additions and 255 deletions

View file

@ -116,13 +116,12 @@ class AddNewArtistSearchResult extends Component {
{
isExistingArtist &&
<span title="Already in your library">
<Icon
className={styles.alreadyExistsIcon}
name={icons.CHECK_CIRCLE}
size={36}
/>
</span>
<Icon
className={styles.alreadyExistsIcon}
name={icons.CHECK_CIRCLE}
size={36}
title="Already in your library"
/>
}
</div>

View file

@ -134,13 +134,12 @@ class AlbumDetailsMedium extends Component {
className={styles.expandButton}
onPress={this.onExpandPress}
>
<span title={isExpanded ? 'Hide tracks' : 'Show tracks'}>
<Icon
className={styles.expandButtonIcon}
name={isExpanded ? icons.COLLAPSE : icons.EXPAND}
size={24}
/>
</span>
<Icon
className={styles.expandButtonIcon}
name={isExpanded ? icons.COLLAPSE : icons.EXPAND}
title={isExpanded ? 'Hide tracks' : 'Show tracks'}
size={24}
/>
{
!isSmallScreen &&
<span>&nbsp;</span>

View file

@ -48,9 +48,10 @@ function EpisodeStatus(props) {
if (grabbed) {
return (
<div className={styles.center} title="Album is downloading">
<div className={styles.center}>
<Icon
name={icons.DOWNLOADING}
title="Album is downloading"
/>
</div>
);
@ -74,9 +75,10 @@ function EpisodeStatus(props) {
if (!airDateUtc) {
return (
<div className={styles.center} title="TBA">
<div className={styles.center}>
<Icon
name={icons.TBA}
title="TBA"
/>
</div>
);
@ -84,9 +86,10 @@ function EpisodeStatus(props) {
if (!monitored) {
return (
<div className={styles.center} title="Album is not monitored">
<div className={styles.center}>
<Icon
name={icons.UNMONITORED}
title="Album is not monitored"
/>
</div>
);
@ -94,18 +97,20 @@ function EpisodeStatus(props) {
if (hasAired) {
return (
<div className={styles.center} title="Track missing from disk">
<div className={styles.center}>
<Icon
name={icons.MISSING}
title="Track missing from disk"
/>
</div>
);
}
return (
<div className={styles.center} title="Album has not aired">
<div className={styles.center}>
<Icon
name={icons.NOT_AIRED}
title="Album has not aired"
/>
</div>
);

View file

@ -39,12 +39,11 @@ class AlbumStudioRow extends Component {
/>
<TableRowCell className={styles.status}>
<span title={status === 'ended' ? 'Ended' : 'Continuing'}>
<Icon
className={styles.statusIcon}
name={status === 'ended' ? icons.ARTIST_ENDED : icons.ARTIST_CONTINUING}
/>
</span>
<Icon
className={styles.statusIcon}
name={status === 'ended' ? icons.ARTIST_ENDED : icons.ARTIST_CONTINUING}
title={status === 'ended' ? 'Ended' : 'Continuing'}
/>
</TableRowCell>
<TableRowCell className={styles.title}>

View file

@ -580,7 +580,9 @@ class ArtistDetails extends Component {
<InteractiveImportModal
isOpen={isInteractiveImportModalOpen}
folder={path}
allowArtistChange={false}
showFilterExistingFiles={true}
showImportMode={false}
onModalClose={this.onInteractiveImportModalClose}
/>
</PageContentBodyConnector>

View file

@ -150,13 +150,12 @@ class ArtistDetailsSeason extends Component {
</div>
<span title={isExpanded ? 'Hide albums' : 'Show albums'}>
<Icon
className={styles.expandButtonIcon}
name={isExpanded ? icons.COLLAPSE : icons.EXPAND}
size={24}
/>
</span>
<Icon
className={styles.expandButtonIcon}
name={isExpanded ? icons.COLLAPSE : icons.EXPAND}
title={isExpanded ? 'Hide albums' : 'Show albums'}
size={24}
/>
{
!isSmallScreen &&

View file

@ -19,19 +19,17 @@ function ArtistStatusCell(props) {
className={className}
{...otherProps}
>
<span title={monitored ? 'Artist is monitored' : 'Artist is unmonitored'}>
<Icon
className={styles.statusIcon}
name={monitored ? icons.MONITORED : icons.UNMONITORED}
/>
</span>
<Icon
className={styles.statusIcon}
name={monitored ? icons.MONITORED : icons.UNMONITORED}
title={monitored ? 'Artist is monitored' : 'Artist is unmonitored'}
/>
<span title={status === 'ended' ? 'Ended' : 'Continuing'}>
<Icon
className={styles.statusIcon}
name={status === 'ended' ? icons.ARTIST_ENDED : icons.ARTIST_CONTINUING}
/>
</span>
<Icon
className={styles.statusIcon}
name={status === 'ended' ? icons.ARTIST_ENDED : icons.ARTIST_CONTINUING}
title={status === 'ended' ? 'Ended' : 'Continuing'}
/>
</Component>
);
}

View file

@ -107,11 +107,10 @@ class AgendaEvent extends Component {
{
!queueItem && grabbed &&
<span title="Album is downloading">
<Icon
name={icons.DOWNLOADING}
/>
</span>
<Icon
name={icons.DOWNLOADING}
title="Album is downloading"
/>
}
</Link>
</div>

View file

@ -4,15 +4,14 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
import CalendarDay from './CalendarDay';
function createCalendarEventsConnector() {
return createSelector(
(state, { date }) => date,
createClientSideCollectionSelector('calendar'),
(date, calendar) => {
const filtered = _.filter(calendar.items, (item) => {
(state) => state.calendar.items,
(date, items) => {
const filtered = _.filter(items, (item) => {
return moment(date).isSame(moment(item.releaseDate), 'day');
});

View file

@ -91,12 +91,11 @@ class CalendarEvent extends Component {
{
!queueItem && grabbed &&
<span title="Album is downloading">
<Icon
className={styles.statusIcon}
name={icons.DOWNLOADING}
/>
</span>
<Icon
className={styles.statusIcon}
name={icons.DOWNLOADING}
title="Album is downloading"
/>
}
</div>

View file

@ -7,6 +7,7 @@ import styles from './Icon.css';
function Icon(props) {
const {
containerClassName,
className,
name,
kind,
@ -16,11 +17,7 @@ function Icon(props) {
...otherProps
} = props;
if (title && !window.Lidarr.isProduction) {
console.error('Icons cannot have a title');
}
return (
const icon = (
<FontAwesomeIcon
className={classNames(
className,
@ -34,9 +31,23 @@ function Icon(props) {
{...otherProps}
/>
);
if (title) {
return (
<span
className={containerClassName}
title={title}
>
{icon}
</span>
);
}
return icon;
}
Icon.propTypes = {
containerClassName: PropTypes.string,
className: PropTypes.string,
name: PropTypes.object.isRequired,
kind: PropTypes.string.isRequired,

View file

@ -45,9 +45,10 @@ function Message(props) {
styles[type]
)}
>
<div className={styles.iconContainer} title={name}>
<div className={styles.iconContainer}>
<Icon
name={getIconName(name)}
title={name}
/>
</div>

View file

@ -6,7 +6,6 @@
.filterText {
margin-left: 5px;
font-size: $largeFontSize;
}
.footer {

View file

@ -169,7 +169,9 @@ class InteractiveImportModalContent extends Component {
render() {
const {
downloadId,
allowArtistChange,
showFilterExistingFiles,
showImportMode,
filterExistingFiles,
title,
folder,
@ -211,17 +213,7 @@ class InteractiveImportModalContent extends Component {
<ModalBody>
{
isFetching &&
<LoadingIndicator />
}
{
error &&
<div>{errorMessage}</div>
}
{
isPopulated && showFilterExistingFiles && !isFetching &&
showFilterExistingFiles &&
<div className={styles.filterContainer}>
<Menu alignMenu={align.RIGHT}>
<MenuButton>
@ -258,6 +250,16 @@ class InteractiveImportModalContent extends Component {
</div>
}
{
isFetching &&
<LoadingIndicator />
}
{
error &&
<div>{errorMessage}</div>
}
{
isPopulated && !!items.length && !isFetching && !isFetching &&
<Table
@ -278,6 +280,7 @@ class InteractiveImportModalContent extends Component {
key={item.id}
isSelected={selectedState[item.id]}
{...item}
allowArtistChange={allowArtistChange}
onSelectedChange={this.onSelectedChange}
onValidRowChange={this.onValidRowChange}
/>
@ -295,9 +298,9 @@ class InteractiveImportModalContent extends Component {
</ModalBody>
<ModalFooter className={styles.footer}>
{
!downloadId &&
<div className={styles.leftButtons}>
<div className={styles.leftButtons}>
{
!downloadId && showImportMode &&
<SelectInput
className={styles.importMode}
name="importMode"
@ -305,13 +308,16 @@ class InteractiveImportModalContent extends Component {
values={importModeOptions}
onChange={this.onImportModeChange}
/>
</div>
}
}
</div>
<div className={downloadId ? styles.leftButtons : styles.centerButtons}>
<Button onPress={this.onSelectArtistPress}>
Select Artist
</Button>
<div className={styles.centerButtons}>
{
allowArtistChange &&
<Button onPress={this.onSelectArtistPress}>
Select Artist
</Button>
}
<Button onPress={this.onSelectAlbumPress}>
Select Album
@ -357,6 +363,8 @@ class InteractiveImportModalContent extends Component {
InteractiveImportModalContent.propTypes = {
downloadId: PropTypes.string,
allowArtistChange: PropTypes.bool.isRequired,
showImportMode: PropTypes.bool.isRequired,
showFilterExistingFiles: PropTypes.bool.isRequired,
filterExistingFiles: PropTypes.bool.isRequired,
importMode: PropTypes.string.isRequired,
@ -377,7 +385,9 @@ InteractiveImportModalContent.propTypes = {
};
InteractiveImportModalContent.defaultProps = {
allowArtistChange: true,
showFilterExistingFiles: false,
showImportMode: true,
importMode: 'move'
};

View file

@ -163,6 +163,7 @@ class InteractiveImportRow extends Component {
render() {
const {
id,
allowArtistChange,
relativePath,
artist,
album,
@ -210,6 +211,7 @@ class InteractiveImportRow extends Component {
</TableRowCell>
<TableRowCellButton
isDisabled={!allowArtistChange}
onPress={this.onSelectArtistPress}
>
{
@ -348,6 +350,7 @@ class InteractiveImportRow extends Component {
InteractiveImportRow.propTypes = {
id: PropTypes.number.isRequired,
allowArtistChange: PropTypes.bool.isRequired,
relativePath: PropTypes.string.isRequired,
artist: PropTypes.object,
album: PropTypes.object,

View file

@ -92,9 +92,10 @@ class QualityProfileItem extends Component {
{
connectDragSource(
<div className={styles.dragHandle} title="Create group">
<div className={styles.dragHandle}>
<Icon
className={styles.dragIcon}
title="Create group"
name={icons.REORDER}
/>
</div>

View file

@ -129,10 +129,11 @@ class QualityProfileItemGroup extends Component {
{
connectDragSource(
<div className={styles.dragHandle} title="Reorder">
<div className={styles.dragHandle}>
<Icon
className={styles.dragIcon}
name={icons.REORDER}
title="Reorder"
/>
</div>
)

View file

@ -45,7 +45,7 @@ export const defaultState = {
filters: [
{
key: 'monitored',
value: false || true,
value: false,
type: filterTypes.EQUAL
}
]
@ -66,7 +66,8 @@ export const defaultState = {
export const persistState = [
'calendar.view',
'calendar.selectedFilterKey'
'calendar.selectedFilterKey',
'calendar.showUpcoming'
];
//

View file

@ -57,7 +57,7 @@ function showCommandMessage(payload, dispatch) {
const {
id,
name,
manual,
trigger,
message,
body = {},
state
@ -80,7 +80,7 @@ function showCommandMessage(payload, dispatch) {
hideAfter = 4;
} else if (state === 'failed') {
type = messageTypes.ERROR;
hideAfter = manual ? 10 : 4;
hideAfter = trigger === 'manual' ? 10 : 4;
}
dispatch(showMessage({
@ -95,10 +95,11 @@ function showCommandMessage(payload, dispatch) {
function scheduleRemoveCommand(command, dispatch) {
const {
id,
state
status,
body
} = command;
if (state === 'queued') {
if (status === 'queued') {
return;
}
@ -108,6 +109,12 @@ function scheduleRemoveCommand(command, dispatch) {
clearTimeout(timeoutId);
}
// 5 minute timeout for executing disk access commands and
// 30 seconds for all other commands.
const timeout = body.requiresDiskAccess && status === 'started' ?
60000 * 5 :
30000;
removeCommandTimeoutIds[id] = setTimeout(() => {
dispatch(batchActions([
removeCommand({ section: 'commands', id }),
@ -115,7 +122,7 @@ function scheduleRemoveCommand(command, dispatch) {
]));
delete removeCommandTimeoutIds[id];
}, 30000);
}, timeout);
}
//

View file

@ -87,11 +87,10 @@ class BackupRow extends Component {
<TableRow key={id}>
<TableRowCell className={styles.type}>
{
<span title={iconTooltip}>
<Icon
name={iconClassName}
/>
</span>
<Icon
name={iconClassName}
title={iconTooltip}
/>
}
</TableRowCell>

View file

@ -125,12 +125,11 @@ class Health extends Component {
return (
<TableRow key={`health${item.message}`}>
<TableRowCell>
<span title={titleCase(item.type)}>
<Icon
name={icons.DANGER}
kind={item.type.toLowerCase() === 'error' ? kinds.DANGER : kinds.WARNING}
/>
</span>
<Icon
name={icons.DANGER}
kind={item.type.toLowerCase() === 'error' ? kinds.DANGER : kinds.WARNING}
title={titleCase(item.type)}
/>
</TableRowCell>
<TableRowCell>{item.message}</TableRowCell>

View file

@ -105,7 +105,6 @@ class CutoffUnmet extends Component {
filters,
columns,
totalRecords,
isSearchingForAlbums,
isSearchingForCutoffUnmetAlbums,
isSaving,
onFilterSelect,
@ -129,8 +128,7 @@ class CutoffUnmet extends Component {
<PageToolbarButton
label="Search Selected"
iconName={icons.SEARCH}
isDisabled={!itemsSelected}
isSpinning={isSearchingForAlbums}
isDisabled={!itemsSelected || isSearchingForCutoffUnmetAlbums}
onPress={this.onSearchSelectedPress}
/>
@ -255,7 +253,6 @@ CutoffUnmet.propTypes = {
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
totalRecords: PropTypes.number,
isSearchingForAlbums: PropTypes.bool.isRequired,
isSearchingForCutoffUnmetAlbums: PropTypes.bool.isRequired,
isSaving: PropTypes.bool.isRequired,
onFilterSelect: PropTypes.func.isRequired,

View file

@ -20,11 +20,9 @@ function createMapStateToProps() {
(state) => state.wanted.cutoffUnmet,
createCommandsSelector(),
(cutoffUnmet, commands) => {
const isSearchingForAlbums = _.some(commands, { name: commandNames.ALBUM_SEARCH });
const isSearchingForCutoffUnmetAlbums = _.some(commands, { name: commandNames.CUTOFF_UNMET_ALBUM_SEARCH });
return {
isSearchingForAlbums,
isSearchingForCutoffUnmetAlbums,
isSaving: _.some(cutoffUnmet.items, { isSaving: true }),
...cutoffUnmet

View file

@ -114,7 +114,6 @@ class Missing extends Component {
filters,
columns,
totalRecords,
isSearchingForAlbums,
isSearchingForMissingAlbums,
isSaving,
onFilterSelect,
@ -139,8 +138,7 @@ class Missing extends Component {
<PageToolbarButton
label="Search Selected"
iconName={icons.SEARCH}
isDisabled={!itemsSelected}
isSpinning={isSearchingForAlbums}
isDisabled={!itemsSelected || isSearchingForMissingAlbums}
onPress={this.onSearchSelectedPress}
/>
@ -277,7 +275,6 @@ Missing.propTypes = {
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
totalRecords: PropTypes.number,
isSearchingForAlbums: PropTypes.bool.isRequired,
isSearchingForMissingAlbums: PropTypes.bool.isRequired,
isSaving: PropTypes.bool.isRequired,
onFilterSelect: PropTypes.func.isRequired,

View file

@ -19,11 +19,9 @@ function createMapStateToProps() {
(state) => state.wanted.missing,
createCommandsSelector(),
(missing, commands) => {
const isSearchingForAlbums = _.some(commands, { name: commandNames.ALBUM_SEARCH });
const isSearchingForMissingAlbums = _.some(commands, { name: commandNames.MISSING_ALBUM_SEARCH });
return {
isSearchingForAlbums,
isSearchingForMissingAlbums,
isSaving: _.some(missing.items, { isSaving: true }),
...missing