[UI Work] Add Artist, Import Artist, Calendar

This commit is contained in:
Qstick 2017-09-07 23:09:52 -04:00
parent a747c5f135
commit 77f1d2e64c
109 changed files with 891 additions and 1082 deletions

View file

@ -13,14 +13,14 @@ function Agenda(props) {
<div className={styles.agenda}>
{
items.map((item, index) => {
const momentDate = moment(item.airDateUtc);
const momentDate = moment(item.releaseDate);
const showDate = index === 0 ||
!moment(items[index - 1].airDateUtc).isSame(momentDate, 'day');
!moment(items[index - 1].releaseDate).isSame(momentDate, 'day');
return (
<AgendaEventConnector
key={item.id}
episodeId={item.id}
albumId={item.id}
showDate={showDate}
{...item}
/>

View file

@ -26,15 +26,15 @@
margin-right: 10px;
}
.seriesTitle,
.episodeTitle {
.artistName,
.albumTitle {
composes: truncate from 'Styles/Mixins/truncate.css';
flex: 0 1 300px;
margin-right: 10px;
}
.episodeTitle {
.albumTitle {
flex: 1 1 1px;
}
@ -66,18 +66,10 @@
composes: unmonitored from 'Calendar/Events/CalendarEvent.css';
}
.onAir {
composes: onAir from 'Calendar/Events/CalendarEvent.css';
}
.missing {
composes: missing from 'Calendar/Events/CalendarEvent.css';
}
.premiere {
composes: premiere from 'Calendar/Events/CalendarEvent.css';
}
.unaired {
composes: unaired from 'Calendar/Events/CalendarEvent.css';
}
@ -98,7 +90,7 @@
.date,
.time,
.seriesTitle {
.artistName {
flex: 0 0 100%;
}

View file

@ -42,12 +42,12 @@ class AgendaEvent extends Component {
render() {
const {
id,
series,
artist,
title,
seasonNumber,
episodeNumber,
absoluteEpisodeNumber,
airDateUtc,
// seasonNumber,
// episodeNumber,
// absoluteEpisodeNumber,
releaseDate,
monitored,
hasFile,
grabbed,
@ -57,11 +57,11 @@ class AgendaEvent extends Component {
longDateFormat
} = this.props;
const startTime = moment(airDateUtc);
const endTime = startTime.add(series.runtime, 'minutes');
const startTime = moment(releaseDate);
// const endTime = startTime.add(artist.runtime, 'minutes');
const downloading = !!(queueItem || grabbed);
const isMonitored = series.monitored && monitored;
const statusStyle = getStatusStyle(episodeNumber, hasFile, downloading, startTime, endTime, isMonitored);
const isMonitored = artist.monitored && monitored;
const statusStyle = getStatusStyle(id, hasFile, downloading, startTime, isMonitored);
return (
<div>
@ -85,34 +85,25 @@ class AgendaEvent extends Component {
/>
<div className={styles.time}>
{formatTime(airDateUtc, timeFormat)} - {formatTime(endTime.toISOString(), timeFormat, { includeMinuteZero: true })}
{formatTime(releaseDate, timeFormat)}
</div>
<div className={styles.seriesTitle}>
{series.title}
<div className={styles.artistName}>
{artist.artistName}
</div>
<div className={styles.seasonEpisodeNumber}>
{seasonNumber}x{padNumber(episodeNumber, 2)}
<div className={styles.episodeSeparator}> - </div>
{
series.seriesType === 'anime' && absoluteEpisodeNumber &&
<span className={styles.absoluteEpisodeNumber}>({absoluteEpisodeNumber})</span>
}
<div className={styles.episodeSeparator}> - </div>
</div>
<div className={styles.episodeTitle}>
<div className={styles.albumTitle}>
{title}
</div>
{
!!queueItem &&
<CalendarEventQueueDetails
seriesType={series.seriesType}
seasonNumber={seasonNumber}
absoluteEpisodeNumber={absoluteEpisodeNumber}
seriesType={artist.seriesType}
// seasonNumber={seasonNumber}
// absoluteEpisodeNumber={absoluteEpisodeNumber}
{...queueItem}
/>
}
@ -121,7 +112,7 @@ class AgendaEvent extends Component {
!queueItem && grabbed &&
<Icon
name={icons.DOWNLOADING}
title="Episode is downloading"
title="Album is downloading"
/>
}
</Link>
@ -130,7 +121,7 @@ class AgendaEvent extends Component {
isOpen={this.state.isDetailsModalOpen}
episodeId={id}
episodeEntity={episodeEntities.CALENDAR}
artistId={series.id}
artistId={artist.id}
episodeTitle={title}
showOpenSeriesButton={true}
onModalClose={this.onDetailsModalClose}
@ -142,12 +133,12 @@ class AgendaEvent extends Component {
AgendaEvent.propTypes = {
id: PropTypes.number.isRequired,
series: PropTypes.object.isRequired,
artist: PropTypes.object.isRequired,
title: PropTypes.string.isRequired,
seasonNumber: PropTypes.number.isRequired,
episodeNumber: PropTypes.number.isRequired,
absoluteEpisodeNumber: PropTypes.number,
airDateUtc: PropTypes.string.isRequired,
// seasonNumber: PropTypes.number.isRequired,
// episodeNumber: PropTypes.number.isRequired,
// absoluteEpisodeNumber: PropTypes.number,
releaseDate: PropTypes.string.isRequired,
monitored: PropTypes.bool.isRequired,
hasFile: PropTypes.bool.isRequired,
grabbed: PropTypes.bool,

View file

@ -10,9 +10,9 @@ function createMapStateToProps() {
createArtistSelector(),
createQueueItemSelector(),
createUISettingsSelector(),
(series, queueItem, uiSettings) => {
(artist, queueItem, uiSettings) => {
return {
series,
artist,
queueItem,
timeFormat: uiSettings.timeFormat,
longDateFormat: uiSettings.longDateFormat

View file

@ -12,10 +12,10 @@ function createCalendarEventsConnector() {
(state) => state.calendar,
(date, calendar) => {
const filtered = _.filter(calendar.items, (item) => {
return moment(date).isSame(moment(item.airDateUtc), 'day');
return moment(date).isSame(moment(item.releaseDate), 'day');
});
return _.sortBy(filtered, (item) => moment(item.airDateUtc).unix());
return _.sortBy(filtered, (item) => moment(item.releaseDate).unix());
}
);
}

View file

@ -8,19 +8,19 @@
}
.info,
.episodeInfo {
.albumInfo {
display: flex;
}
.seriesTitle,
.episodeTitle {
.artistName,
.albumTitle {
composes: truncate from 'Styles/Mixins/truncate.css';
flex: 1 0 1px;
margin-right: 10px;
}
.seriesTitle {
.artistName {
color: #3a3f51;
font-size: 14px;
}
@ -53,14 +53,6 @@
}
}
.onAir {
border-left-color: $warningColor;
&:global(.colorImpaired) {
background: repeating-linear-gradient(90deg, transparent, transparent 5px, #eee 5px, #eee 10px);
}
}
.missing {
border-left-color: $dangerColor;
@ -69,14 +61,6 @@
}
}
.premiere {
border-left-color: $sonarrBlue;
&:global(.colorImpaired) {
background: repeating-linear-gradient(90deg, transparent, transparent 5px, #eee 5px, #eee 10px);
}
}
.unaired {
border-left-color: $primaryColor;

View file

@ -47,26 +47,26 @@ class CalendarEvent extends Component {
render() {
const {
id,
series,
artist,
title,
seasonNumber,
episodeNumber,
absoluteEpisodeNumber,
airDateUtc,
// seasonNumber,
// episodeNumber,
// absoluteEpisodeNumber,
releaseDate,
monitored,
hasFile,
grabbed,
queueItem,
timeFormat,
// timeFormat,
colorImpairedMode
} = this.props;
const startTime = moment(airDateUtc);
const endTime = startTime.add(series.runtime, 'minutes');
const startTime = moment(releaseDate);
// const endTime = startTime.add(artist.runtime, 'minutes');
const downloading = !!(queueItem || grabbed);
const isMonitored = series.monitored && monitored;
const statusStyle = getStatusStyle(episodeNumber, hasFile, downloading, startTime, endTime, isMonitored);
const missingAbsoluteNumber = series.seriesType === 'anime' && seasonNumber > 0 && !absoluteEpisodeNumber;
const isMonitored = artist.monitored && monitored;
const statusStyle = getStatusStyle(id, hasFile, downloading, startTime, isMonitored);
// const missingAbsoluteNumber = artist.seriesType === 'anime' && seasonNumber > 0 && !absoluteEpisodeNumber;
return (
<div>
@ -80,18 +80,10 @@ class CalendarEvent extends Component {
onPress={this.onPress}
>
<div className={styles.info}>
<div className={styles.seriesTitle}>
{series.title}
<div className={styles.artistName}>
{artist.artistName}
</div>
{
missingAbsoluteNumber &&
<Icon
name={icons.WARNING}
title="Episode does not have an absolute episode number"
/>
}
{
!!queueItem &&
<span className={styles.statusIcon}>
@ -106,28 +98,15 @@ class CalendarEvent extends Component {
<Icon
className={styles.statusIcon}
name={icons.DOWNLOADING}
title="Episode is downloading"
title="Album is downloading"
/>
}
</div>
<div className={styles.episodeInfo}>
<div className={styles.episodeTitle}>
<div className={styles.albumInfo}>
<div className={styles.albumTitle}>
{title}
</div>
<div>
{seasonNumber}x{padNumber(episodeNumber, 2)}
{
series.seriesType === 'anime' && absoluteEpisodeNumber &&
<span className={styles.absoluteEpisodeNumber}>({absoluteEpisodeNumber})</span>
}
</div>
</div>
<div>
{formatTime(airDateUtc, timeFormat)} - {formatTime(endTime.toISOString(), timeFormat, { includeMinuteZero: true })}
</div>
</Link>
@ -135,7 +114,7 @@ class CalendarEvent extends Component {
isOpen={this.state.isDetailsModalOpen}
episodeId={id}
episodeEntity={episodeEntities.CALENDAR}
artistId={series.id}
artistId={artist.id}
episodeTitle={title}
showOpenSeriesButton={true}
onModalClose={this.onDetailsModalClose}
@ -147,17 +126,17 @@ class CalendarEvent extends Component {
CalendarEvent.propTypes = {
id: PropTypes.number.isRequired,
series: PropTypes.object.isRequired,
artist: PropTypes.object.isRequired,
title: PropTypes.string.isRequired,
seasonNumber: PropTypes.number.isRequired,
episodeNumber: PropTypes.number.isRequired,
absoluteEpisodeNumber: PropTypes.number,
airDateUtc: PropTypes.string.isRequired,
// seasonNumber: PropTypes.number.isRequired,
// episodeNumber: PropTypes.number.isRequired,
// absoluteEpisodeNumber: PropTypes.number,
releaseDate: PropTypes.string.isRequired,
monitored: PropTypes.bool.isRequired,
hasFile: PropTypes.bool.isRequired,
grabbed: PropTypes.bool,
queueItem: PropTypes.object,
timeFormat: PropTypes.string.isRequired,
// timeFormat: PropTypes.string.isRequired,
colorImpairedMode: PropTypes.bool.isRequired,
onEventModalOpenToggle: PropTypes.func.isRequired
};

View file

@ -10,9 +10,9 @@ function createMapStateToProps() {
createArtistSelector(),
createQueueItemSelector(),
createUISettingsSelector(),
(series, queueItem, uiSettings) => {
(artist, queueItem, uiSettings) => {
return {
series,
artist,
queueItem,
timeFormat: uiSettings.timeFormat,
colorImpairedMode: uiSettings.enableColorImpairedMode

View file

@ -6,46 +6,30 @@ import styles from './Legend.css';
function Legend({ colorImpairedMode }) {
return (
<div className={styles.legend}>
<div>
<LegendItem
name="Unaired Premiere"
status="premiere"
tooltip="Premiere episode hasn't aired yet"
colorImpairedMode={colorImpairedMode}
/>
<LegendItem
status="unaired"
tooltip="Episode hasn't aired yet"
colorImpairedMode={colorImpairedMode}
/>
</div>
<div>
<LegendItem
status="downloading"
tooltip="Episode is currently downloading"
tooltip="Album is currently downloading"
colorImpairedMode={colorImpairedMode}
/>
<LegendItem
status="downloaded"
tooltip="Episode was downloaded and sorted"
tooltip="Album was downloaded and sorted"
colorImpairedMode={colorImpairedMode}
/>
</div>
<div>
<LegendItem
name="On Air"
status="onAir"
tooltip="Episode is currently airing"
status="unaired"
tooltip="Album hasn't released yet"
colorImpairedMode={colorImpairedMode}
/>
<LegendItem
status="missing"
tooltip="Episode file has not been found"
tooltip="Track file has not been found"
colorImpairedMode={colorImpairedMode}
/>
</div>
@ -53,7 +37,7 @@ function Legend({ colorImpairedMode }) {
<div>
<LegendItem
status="unmonitored"
tooltip="Episode is unmonitored"
tooltip="Album is unmonitored"
colorImpairedMode={colorImpairedMode}
/>
</div>

View file

@ -32,10 +32,6 @@
composes: missing from 'Calendar/Events/CalendarEvent.css';
}
.premiere {
composes: premiere from 'Calendar/Events/CalendarEvent.css';
}
.unaired {
composes: unaired from 'Calendar/Events/CalendarEvent.css';
}

View file

@ -1,6 +1,6 @@
import moment from 'moment';
function getStatusStyle(episodeNumber, hasFile, downloading, startTime, endTime, isMonitored) {
function getStatusStyle(episodeNumber, hasFile, downloading, startTime, isMonitored) {
const currentTime = moment();
if (hasFile) {
@ -15,18 +15,10 @@ function getStatusStyle(episodeNumber, hasFile, downloading, startTime, endTime,
return 'unmonitored';
}
if (currentTime.isAfter(startTime) && currentTime.isBefore(endTime)) {
return 'onAir';
}
if (endTime.isBefore(currentTime) && !hasFile) {
if (currentTime.isAfter(startTime)) {
return 'missing';
}
if (episodeNumber === 1) {
return 'premiere';
}
return 'unaired';
}

View file

@ -22,7 +22,7 @@ function getUrls(state) {
tags
} = state;
let icalUrl = `${window.location.host}${window.Sonarr.urlBase}/feed/calendar/Sonarr.ics?`;
let icalUrl = `${window.location.host}${window.Sonarr.urlBase}/feed/calendar/Lidarr.ics?`;
if (unmonitored) {
icalUrl += 'unmonitored=true&';
@ -115,7 +115,7 @@ class CalendarLinkModalContent extends Component {
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
Sonarr Calendar Feed
Lidarr Calendar Feed
</ModalHeader>
<ModalBody>
@ -127,7 +127,7 @@ class CalendarLinkModalContent extends Component {
type={inputTypes.CHECK}
name="unmonitored"
value={unmonitored}
helpText="Include unmonitored episodes in the iCal feed"
helpText="Include unmonitored albums in the iCal feed"
onChange={this.onInputChange}
/>
</FormGroup>
@ -139,7 +139,7 @@ class CalendarLinkModalContent extends Component {
type={inputTypes.CHECK}
name="premieresOnly"
value={premieresOnly}
helpText="Only the first episode in a season will be in the feed"
helpText="Only the first album from an artist will be in the feed"
onChange={this.onInputChange}
/>
</FormGroup>
@ -163,7 +163,7 @@ class CalendarLinkModalContent extends Component {
type={inputTypes.TAG}
name="tags"
value={tags}
helpText="Feed will only contain series with at least one matching tag"
helpText="Feed will only contain artists with at least one matching tag"
onChange={this.onInputChange}
/>
</FormGroup>