Fixed: UI Fixes

This commit is contained in:
Qstick 2018-03-15 22:26:49 -04:00
parent 718e8d7a9e
commit 94cca69e92
17 changed files with 287 additions and 261 deletions

View File

@ -71,7 +71,6 @@ class AddNewArtistSearchResult extends Component {
artistType,
status,
overview,
albumCount,
ratings,
images,
isExistingArtist,
@ -83,11 +82,6 @@ class AddNewArtistSearchResult extends Component {
} = this.state;
const linkProps = isExistingArtist ? { to: `/artist/${foreignArtistId}` } : { onPress: this.onPress };
let albums = '1 Album';
if (albumCount > 1) {
albums = `${albumCount} Albums`;
}
const height = calculateHeight(230, isSmallScreen);
@ -146,13 +140,6 @@ class AddNewArtistSearchResult extends Component {
</Label>
}
{
!!albumCount &&
<Label size={sizes.LARGE}>
{albums}
</Label>
}
{
status === 'ended' &&
<Label
@ -201,7 +188,6 @@ AddNewArtistSearchResult.propTypes = {
artistType: PropTypes.string,
status: PropTypes.string.isRequired,
overview: PropTypes.string,
albumCount: PropTypes.number,
ratings: PropTypes.object.isRequired,
images: PropTypes.arrayOf(PropTypes.object).isRequired,
isExistingArtist: PropTypes.bool.isRequired,

View File

@ -110,22 +110,6 @@ class InteractiveAlbumSearchModalContent extends Component {
<ModalBody>
{
isFetching &&
<LoadingIndicator />
}
{
!isFetching && !!error &&
<div>Unable to load releases.</div>
}
{
isPopulated && !hasItems && !error &&
<div>No results.</div>
}
{
isPopulated && hasItems && !error &&
<div>
<div className={styles.filterMenuContainer}>
<FilterMenu
@ -140,10 +124,29 @@ class InteractiveAlbumSearchModalContent extends Component {
</div>
{
!!totalReleasesCount && !items.length &&
<div>
All results are hidden by {filters.length > 1 ? 'filters' : 'a filter'}.
</div>
isFetching &&
<LoadingIndicator />
}
{
!isFetching && !!error &&
<div>
Unable to load results for this album search. Try again later.
</div>
}
{
!isFetching && isPopulated && !totalReleasesCount &&
<div>
No results found.
</div>
}
{
!!totalReleasesCount && isPopulated && !items.length &&
<div>
All results are hidden by {filters.length > 1 ? 'filters' : 'a filter'}.
</div>
}
{

View File

@ -79,6 +79,8 @@ class ArtistIndexBanner extends Component {
} = this.props;
const {
albumCount,
sizeOnDisk,
trackCount,
trackFileCount,
totalTrackCount
@ -189,12 +191,13 @@ class ArtistIndexBanner extends Component {
}
<ArtistIndexBannerInfo
albumCount={albumCount}
sizeOnDisk={sizeOnDisk}
qualityProfile={qualityProfile}
showQualityProfile={showQualityProfile}
showRelativeDates={showRelativeDates}
shortDateFormat={shortDateFormat}
timeFormat={timeFormat}
statistics={statistics}
{...otherProps}
/>
@ -242,8 +245,7 @@ ArtistIndexBanner.propTypes = {
ArtistIndexBanner.defaultProps = {
trackCount: 0,
trackFileCount: 0,
albumCount: 0
trackFileCount: 0
};
export default ArtistIndexBanner;

View File

@ -10,19 +10,15 @@ function ArtistIndexBannerInfo(props) {
showQualityProfile,
previousAiring,
added,
statistics,
albumCount,
path,
sizeOnDisk,
sortKey,
showRelativeDates,
shortDateFormat,
timeFormat
} = props;
const {
albumCount,
sizeOnDisk
} = statistics;
if (sortKey === 'qualityProfileId' && !showQualityProfile) {
return (
<div className={styles.info}>
@ -107,8 +103,9 @@ ArtistIndexBannerInfo.propTypes = {
showQualityProfile: PropTypes.bool.isRequired,
previousAiring: PropTypes.string,
added: PropTypes.string,
statistics: PropTypes.object.isRequired,
albumCount: PropTypes.number.isRequired,
path: PropTypes.string.isRequired,
sizeOnDisk: PropTypes.number,
sortKey: PropTypes.string.isRequired,
showRelativeDates: PropTypes.bool.isRequired,
shortDateFormat: PropTypes.string.isRequired,

View File

@ -95,6 +95,8 @@ class ArtistIndexOverview extends Component {
} = this.props;
const {
albumCount,
sizeOnDisk,
trackCount,
trackFileCount,
totalTrackCount
@ -196,11 +198,12 @@ class ArtistIndexOverview extends Component {
height={overviewHeight}
monitored={monitored}
nextAiring={nextAiring}
albumCount={albumCount}
sizeOnDisk={sizeOnDisk}
qualityProfile={qualityProfile}
showRelativeDates={showRelativeDates}
shortDateFormat={shortDateFormat}
timeFormat={timeFormat}
statistics={statistics}
{...overviewOptions}
{...otherProps}
/>
@ -251,8 +254,7 @@ ArtistIndexOverview.propTypes = {
ArtistIndexOverview.defaultProps = {
trackCount: 0,
trackFileCount: 0,
albumCount: 0
trackFileCount: 0
};
export default ArtistIndexOverview;

View File

@ -5,17 +5,6 @@
margin-left: 10px;
}
.info {
flex: 0 0 $artistIndexOverviewInfoRowHeight;
margin: 2px 0;
}
.icon {
margin-right: 5px;
width: 25px;
text-align: center;
}
@media only screen and (max-width: $breakpointSmall) {
.infos {
margin-left: 0;

View File

@ -5,190 +5,191 @@ import getRelativeDate from 'Utilities/Date/getRelativeDate';
import formatBytes from 'Utilities/Number/formatBytes';
import { icons } from 'Helpers/Props';
import dimensions from 'Styles/Variables/dimensions';
import Icon from 'Components/Icon';
import ArtistIndexOverviewInfoRow from './ArtistIndexOverviewInfoRow';
import styles from './ArtistIndexOverviewInfo.css';
const infoRowHeight = parseInt(dimensions.artistIndexOverviewInfoRowHeight);
function isVisible(name, show, value, sortKey, index) {
if (value == null) {
const rows = [
{
name: 'monitored',
showProp: 'showMonitored',
valueProp: 'monitored'
},
{
name: 'qualityProfileId',
showProp: 'showQualityProfile',
valueProp: 'qualityProfileId'
},
{
name: 'added',
showProp: 'showAdded',
valueProp: 'added'
},
{
name: 'albumCount',
showProp: 'showAlbumCount',
valueProp: 'albumCount'
},
{
name: 'path',
showProp: 'showPath',
valueProp: 'path'
},
{
name: 'sizeOnDisk',
showProp: 'showSizeOnDisk',
valueProp: 'sizeOnDisk'
}
];
function isVisible(row, props) {
const {
name,
showProp,
valueProp
} = row;
if (props[valueProp] == null) {
return false;
}
return show || sortKey === name;
return props[showProp] || props.sortKey === name;
}
function getInfoRowProps(row, props) {
const { name } = row;
if (name === 'monitored') {
const monitoredText = props.monitored ? 'Monitored' : 'Unmonitored';
return {
title: monitoredText,
iconName: props.monitored ? icons.MONITORED : icons.UNMONITORED,
label: monitoredText
};
}
if (name === 'qualityProfileId') {
return {
title: 'Quality PROFILE',
iconName: icons.PROFILE,
label: props.qualityProfile.name
};
}
if (name === 'added') {
const {
added,
shortDateFormat,
showRelativeDates,
timeFormat
} = props;
return {
title: 'Added',
iconName: icons.ADD,
label: getRelativeDate(
added,
shortDateFormat,
showRelativeDates,
{
timeFormat,
timeForToday: true
}
)
};
}
if (name === 'albumCount') {
const { albumCount } = props;
let albums = '1 album';
if (albumCount === 0) {
albums = 'No albums';
} else if (albumCount > 1) {
albums = `${albumCount} albums`;
}
return {
title: 'Album Count',
iconName: icons.CIRCLE,
label: albums
};
}
if (name === 'path') {
return {
title: 'Path',
iconName: icons.FOLDER,
label: props.path
};
}
if (name === 'sizeOnDisk') {
return {
title: 'Size on Disk',
iconName: icons.DRIVE,
label: formatBytes(props.sizeOnDisk)
};
}
}
function ArtistIndexOverviewInfo(props) {
const {
height,
showMonitored,
showQualityProfile,
showAdded,
showAlbumCount,
showPath,
showSizeOnDisk,
monitored,
nextAiring,
qualityProfile,
added,
statistics,
path,
sortKey,
showRelativeDates,
shortDateFormat,
timeFormat
} = props;
const {
albumCount,
sizeOnDisk
} = statistics;
let albums = '1 album';
if (albumCount === 0) {
albums = 'No albums';
} else if (albumCount > 1) {
albums = `${albumCount} albums`;
}
let shownRows = 1;
const maxRows = Math.floor(height / (infoRowHeight + 4));
const monitoredText = monitored ? 'Monitored' : 'Unmonitored';
return (
<div className={styles.infos}>
{
!!nextAiring &&
<div
className={styles.info}
title="Next Airing"
>
<Icon
className={styles.icon}
name={icons.SCHEDULED}
size={14}
/>
<ArtistIndexOverviewInfoRow
title={nextAiring}
iconName={icons.SCHEDULED}
label={getRelativeDate(
nextAiring,
shortDateFormat,
showRelativeDates,
{
getRelativeDate(
nextAiring,
shortDateFormat,
showRelativeDates,
{
timeFormat,
timeForToday: true
}
)
timeFormat,
timeForToday: true
}
</div>
)}
/>
}
{
isVisible('monitored', showMonitored, monitored, sortKey) && maxRows > 1 &&
<div
className={styles.info}
title={monitoredText}
>
<Icon
className={styles.icon}
name={monitored ? icons.MONITORED : icons.UNMONITORED}
size={14}
rows.map((row) => {
if (!isVisible(row, props)) {
return null;
}
if (shownRows >= maxRows) {
return null;
}
shownRows++;
const infoRowProps = getInfoRowProps(row, props);
return (
<ArtistIndexOverviewInfoRow
key={row.name}
{...infoRowProps}
/>
{monitoredText}
</div>
);
})
}
{
isVisible('qualityProfileId', showQualityProfile, qualityProfile, sortKey) && maxRows > 2 &&
<div
className={styles.info}
title="Quality Profile"
>
<Icon
className={styles.icon}
name={icons.PROFILE}
size={14}
/>
{qualityProfile.name}
</div>
}
{
isVisible('added', showAdded, added, sortKey) && maxRows > 3 &&
<div
className={styles.info}
title="Date Added"
>
<Icon
className={styles.icon}
name={icons.ADD}
size={14}
/>
{
getRelativeDate(
added,
shortDateFormat,
showRelativeDates,
{
timeFormat,
timeForToday: true
}
)
}
</div>
}
{
isVisible('albumCount', showAlbumCount, albumCount, sortKey) && maxRows > 4 &&
<div
className={styles.info}
title="Album Count"
>
<Icon
className={styles.icon}
name={icons.CIRCLE}
size={14}
/>
{albums}
</div>
}
{
isVisible('path', showPath, path, sortKey) && maxRows > 5 &&
<div
className={styles.info}
title="Path"
>
<Icon
className={styles.icon}
name={icons.FOLDER}
size={14}
/>
{path}
</div>
}
{
isVisible('sizeOnDisk', showSizeOnDisk, sizeOnDisk, sortKey) && maxRows > 6 &&
<div
className={styles.info}
title="Size on Disk"
>
<Icon
className={styles.icon}
name={icons.DRIVE}
size={14}
/>
{formatBytes(sizeOnDisk)}
</div>
}
</div>
);
}
@ -207,8 +208,9 @@ ArtistIndexOverviewInfo.propTypes = {
qualityProfile: PropTypes.object.isRequired,
previousAiring: PropTypes.string,
added: PropTypes.string,
statistics: PropTypes.object.isRequired,
albumCount: PropTypes.number.isRequired,
path: PropTypes.string.isRequired,
sizeOnDisk: PropTypes.number,
sortKey: PropTypes.string.isRequired,
showRelativeDates: PropTypes.bool.isRequired,
shortDateFormat: PropTypes.string.isRequired,

View File

@ -0,0 +1,10 @@
.infoRow {
flex: 0 0 $artistIndexOverviewInfoRowHeight;
margin: 2px 0;
}
.icon {
margin-right: 5px;
width: 25px !important;
text-align: center;
}

View File

@ -0,0 +1,35 @@
import PropTypes from 'prop-types';
import React from 'react';
import Icon from 'Components/Icon';
import styles from './ArtistIndexOverviewInfoRow.css';
function ArtistIndexOverviewInfoRow(props) {
const {
title,
iconName,
label
} = props;
return (
<div
className={styles.infoRow}
title={title}
>
<Icon
className={styles.icon}
name={iconName}
size={14}
/>
{label}
</div>
);
}
ArtistIndexOverviewInfoRow.propTypes = {
title: PropTypes.string,
iconName: PropTypes.object.isRequired,
label: PropTypes.string.isRequired
};
export default ArtistIndexOverviewInfoRow;

View File

@ -79,6 +79,8 @@ class ArtistIndexPoster extends Component {
} = this.props;
const {
albumCount,
sizeOnDisk,
trackCount,
trackFileCount,
totalTrackCount
@ -188,11 +190,12 @@ class ArtistIndexPoster extends Component {
</div>
}
<ArtistIndexPosterInfo
albumCount={albumCount}
sizeOnDisk={sizeOnDisk}
qualityProfile={qualityProfile}
showQualityProfile={showQualityProfile}
showRelativeDates={showRelativeDates}
shortDateFormat={shortDateFormat}
statistics={statistics}
timeFormat={timeFormat}
{...otherProps}
/>
@ -241,8 +244,7 @@ ArtistIndexPoster.propTypes = {
ArtistIndexPoster.defaultProps = {
trackCount: 0,
trackFileCount: 0,
albumCount: 0
trackFileCount: 0
};
export default ArtistIndexPoster;

View File

@ -10,19 +10,15 @@ function ArtistIndexPosterInfo(props) {
showQualityProfile,
previousAiring,
added,
statistics,
albumCount,
path,
sizeOnDisk,
sortKey,
showRelativeDates,
shortDateFormat,
timeFormat
} = props;
const {
albumCount,
sizeOnDisk
} = statistics;
if (sortKey === 'qualityProfileId' && !showQualityProfile) {
return (
<div className={styles.info}>
@ -107,8 +103,9 @@ ArtistIndexPosterInfo.propTypes = {
showQualityProfile: PropTypes.bool.isRequired,
previousAiring: PropTypes.string,
added: PropTypes.string,
statistics: PropTypes.object.isRequired,
albumCount: PropTypes.number.isRequired,
path: PropTypes.string.isRequired,
sizeOnDisk: PropTypes.number,
sortKey: PropTypes.string.isRequired,
showRelativeDates: PropTypes.bool.isRequired,
shortDateFormat: PropTypes.string.isRequired,

View File

@ -43,7 +43,7 @@
.trackCount {
composes: headerCell from 'Components/Table/VirtualTableHeaderCell.css';
flex: 0 0 120px;
flex: 0 0 130px;
}
.path {
@ -55,7 +55,7 @@
.sizeOnDisk {
composes: headerCell from 'Components/Table/VirtualTableHeaderCell.css';
flex: 0 0 115px;
flex: 0 0 120px;
}
.tags {

View File

@ -45,7 +45,7 @@
.trackCount {
composes: cell from 'Components/Table/Cells/VirtualTableRowCell.css';
flex: 0 0 120px;
flex: 0 0 130px;
}
.path {
@ -57,7 +57,7 @@
.sizeOnDisk {
composes: cell from 'Components/Table/Cells/VirtualTableRowCell.css';
flex: 0 0 110px;
flex: 0 0 120px;
}
.tags {

View File

@ -382,8 +382,7 @@ ArtistIndexRow.propTypes = {
ArtistIndexRow.defaultProps = {
trackCount: 0,
trackFileCount: 0,
albumCount: 0
trackFileCount: 0
};
export default ArtistIndexRow;

View File

@ -2,7 +2,6 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Measure from 'react-measure';
import { align, icons } from 'Helpers/Props';
import getFilterValue from 'Utilities/Filter/getFilterValue';
import PageContent from 'Components/Page/PageContent';
import PageContentBodyConnector from 'Components/Page/PageContentBodyConnector';
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
@ -41,6 +40,10 @@ class CalendarPage extends Component {
this.props.onDaysCountChange(days);
}
onFilterSelect = (selectedFilterKey) => {
this.props.onUnmonitoredChange(selectedFilterKey === 'unmonitored');
}
onGetCalendarLinkPress = () => {
this.setState({ isCalendarLinkModalOpen: true });
}
@ -57,8 +60,7 @@ class CalendarPage extends Component {
selectedFilterKey,
filters,
hasArtist,
colorImpairedMode,
onFilterSelect
colorImpairedMode
} = this.props;
const isMeasured = this.state.width > 0;
@ -83,10 +85,11 @@ class CalendarPage extends Component {
<PageToolbarSection alignContent={align.RIGHT}>
<FilterMenu
alignMenu={align.RIGHT}
isDisabled={!hasArtist}
selectedFilterKey={selectedFilterKey}
filters={filters}
customFilters={[]}
onFilterSelect={onFilterSelect}
onFilterSelect={this.onFilterSelect}
/>
</PageToolbarSection>
</PageToolbar>
@ -123,7 +126,7 @@ CalendarPage.propTypes = {
hasArtist: PropTypes.bool.isRequired,
colorImpairedMode: PropTypes.bool.isRequired,
onDaysCountChange: PropTypes.func.isRequired,
onFilterSelect: PropTypes.func.isRequired
onUnmonitoredChange: PropTypes.func.isRequired
};
export default CalendarPage;

View File

@ -1,6 +1,6 @@
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { setCalendarDaysCount, setCalendarFilter } from 'Store/Actions/calendarActions';
import { setCalendarDaysCount, setCalendarIncludeUnmonitored } from 'Store/Actions/calendarActions';
import createArtistCountSelector from 'Store/Selectors/createArtistCountSelector';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
import CalendarPage from './CalendarPage';
@ -12,8 +12,8 @@ function createMapStateToProps() {
createUISettingsSelector(),
(calendar, artistCount, uiSettings) => {
return {
filters: calendar.filters,
selectedFilterKey: calendar.selectedFilterKey,
filters: calendar.filters,
showUpcoming: calendar.showUpcoming,
colorImpairedMode: uiSettings.enableColorImpairedMode,
hasArtist: !!artistCount
@ -28,8 +28,8 @@ function createMapDispatchToProps(dispatch, props) {
dispatch(setCalendarDaysCount({ dayCount }));
},
onFilterSelect(selectedFilterKey) {
dispatch(setCalendarFilter({ selectedFilterKey }));
onUnmonitoredChange(unmonitored) {
dispatch(setCalendarIncludeUnmonitored({ unmonitored }));
}
};
}

View File

@ -36,27 +36,27 @@ export const defaultState = {
error: null,
items: [],
selectedFilterKey: 'all',
selectedFilterKey: 'monitored',
filters: [
{
key: 'all',
label: 'All',
key: 'monitored',
label: 'Monitored Only',
filters: [
{
key: 'unmonitored',
value: false,
key: 'monitored',
value: true,
type: filterTypes.EQUAL
}
]
},
{
key: 'unmonitored',
label: 'Unmonitored',
label: 'Include Unmonitored',
filters: [
{
key: 'unmonitored',
value: true,
key: 'monitored',
value: false,
type: filterTypes.EQUAL
}
]
@ -66,7 +66,7 @@ export const defaultState = {
export const persistState = [
'calendar.view',
'calendar.showUpcoming',
'calendar.unmonitored',
'calendar.selectedFilterKey'
];
@ -75,8 +75,8 @@ export const persistState = [
export const FETCH_CALENDAR = 'calendar/fetchCalendar';
export const SET_CALENDAR_DAYS_COUNT = 'calendar/setCalendarDaysCount';
export const SET_CALENDAR_INCLUDE_UNMONITORED = 'calendar/setCalendarIncludeUnmonitored';
export const SET_CALENDAR_VIEW = 'calendar/setCalendarView';
export const SET_CALENDAR_FILTER = 'calendar/setCalendarFilter';
export const GOTO_CALENDAR_TODAY = 'calendar/gotoCalendarToday';
export const GOTO_CALENDAR_PREVIOUS_RANGE = 'calendar/gotoCalendarPreviousRange';
export const GOTO_CALENDAR_NEXT_RANGE = 'calendar/gotoCalendarNextRange';
@ -182,8 +182,8 @@ function isRangePopulated(start, end, state) {
export const fetchCalendar = createThunk(FETCH_CALENDAR);
export const setCalendarDaysCount = createThunk(SET_CALENDAR_DAYS_COUNT);
export const setCalendarIncludeUnmonitored = createThunk(SET_CALENDAR_INCLUDE_UNMONITORED);
export const setCalendarView = createThunk(SET_CALENDAR_VIEW);
export const setCalendarFilter = createThunk(SET_CALENDAR_FILTER);
export const gotoCalendarToday = createThunk(GOTO_CALENDAR_TODAY);
export const gotoCalendarPreviousRange = createThunk(GOTO_CALENDAR_PREVIOUS_RANGE);
export const gotoCalendarNextRange = createThunk(GOTO_CALENDAR_NEXT_RANGE);
@ -196,8 +196,7 @@ export const actionHandlers = handleThunks({
[FETCH_CALENDAR]: function(getState, payload, dispatch) {
const state = getState();
const selectedFilter = state.calendar.selectedFilterKey;
const unmonitored = state.calendar.filters.find((f) => f.key === selectedFilter).filters[0].value;
const unmonitored = state.calendar.unmonitored;
const {
time,
@ -274,6 +273,18 @@ export const actionHandlers = handleThunks({
dispatch(fetchCalendar({ time, view }));
},
[SET_CALENDAR_INCLUDE_UNMONITORED]: function(getState, payload, dispatch) {
dispatch(set({
section,
unmonitored: payload.unmonitored
}));
const state = getState();
const { time, view } = state.calendar;
dispatch(fetchCalendar({ time, view }));
},
[SET_CALENDAR_VIEW]: function(getState, payload, dispatch) {
const state = getState();
const view = payload.view;
@ -317,18 +328,6 @@ export const actionHandlers = handleThunks({
const amount = view === calendarViews.FORECAST ? dayCount : 1;
const time = moment(state.calendar.time).add(amount, viewRanges[view]);
dispatch(fetchCalendar({ time, view }));
},
[SET_CALENDAR_FILTER]: function(getState, payload, dispatch) {
dispatch(set({
section,
selectedFilterKey: payload.selectedFilterKey
}));
const state = getState();
const { time, view } = state.calendar;
dispatch(fetchCalendar({ time, view }));
}
});