Fixed: Misc Calendar Improvements

pull/2/head
Qstick 4 years ago
parent b2c1dbf3ab
commit 5a5e896eb4

@ -37,22 +37,6 @@
margin-right: 10px;
}
.episodeTitle {
flex: 1 1 1px;
}
.seasonEpisodeNumber {
flex: 0 0 100px;
}
.episodeSeparator {
display: none;
}
.absoluteEpisodeNumber {
margin-left: 3px;
}
.statusIcon {
margin-left: 3px;
}
@ -73,18 +57,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';
}
@media only screen and (max-width: $breakpointSmall) {
.event {
flex-direction: column;
@ -101,16 +77,7 @@
.date,
.time,
.seriesTitle {
.movieTitle {
flex: 0 0 100%;
}
.seasonEpisodeNumber {
flex: 0 0 auto;
}
.episodeSeparator {
display: inline-block;
margin: 0 5px;
}
}

@ -44,18 +44,10 @@
font-size: $defaultFontSize;
}
.absoluteEpisodeNumber {
margin-left: 3px;
}
.statusIcon {
margin-left: 3px;
}
.airTime {
color: $calendarTextDim;
}
/*
* Status
*/
@ -97,7 +89,7 @@
}
}
.unaired {
.unreleased {
border-left-color: $primaryColor !important;
&:global(.colorImpaired) {

@ -11,17 +11,6 @@ import styles from './CalendarEvent.css';
class CalendarEvent extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
// isDetailsModalOpen: false
};
}
//
// Render
@ -138,7 +127,6 @@ CalendarEvent.propTypes = {
timeFormat: PropTypes.string.isRequired,
colorImpairedMode: PropTypes.bool.isRequired,
date: PropTypes.string.isRequired
// onEventModalOpenToggle: PropTypes.func.isRequired
};
CalendarEvent.defaultProps = {

@ -1,6 +1,5 @@
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import createMovieFileSelector from 'Store/Selectors/createMovieFileSelector';
import createMovieSelector from 'Store/Selectors/createMovieSelector';
import createQueueItemSelector from 'Store/Selectors/createQueueItemSelector';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
@ -10,13 +9,11 @@ function createMapStateToProps() {
return createSelector(
(state) => state.calendar.options,
createMovieSelector(),
createMovieFileSelector(),
createQueueItemSelector(),
createUISettingsSelector(),
(calendarOptions, movie, movieFile, queueItem, uiSettings) => {
(calendarOptions, movie, queueItem, uiSettings) => {
return {
movie,
movieFile,
queueItem,
...calendarOptions,
timeFormat: uiSettings.timeFormat,

@ -1,87 +0,0 @@
.eventGroup {
overflow-x: hidden;
margin: 4px 2px;
padding: 5px;
border-bottom: 1px solid $borderColor;
border-left: 4px solid $borderColor;
font-size: 12px;
}
.info,
.airingInfo {
display: flex;
}
.seriesTitle {
@add-mixin truncate;
flex: 1 0 1px;
margin-right: 10px;
color: #3a3f51;
font-size: $defaultFontSize;
}
.airTime {
flex: 1 0 1px;
color: $calendarTextDim;
}
.episodeInfo {
margin-left: 10px;
color: $calendarTextDim;
}
.absoluteEpisodeNumber {
margin-left: 3px;
}
.expandContainerInline {
display: flex;
justify-content: flex-end;
flex: 1 0 20px;
}
.expandContainer,
.collapseContainer {
display: flex;
justify-content: center;
}
.collapseContainer {
margin-bottom: 5px;
}
.statusIcon {
margin-left: 3px;
}
/*
* Status
*/
.downloaded {
composes: downloaded from '~Calendar/Events/CalendarEvent.css';
}
.downloading {
composes: downloading from '~Calendar/Events/CalendarEvent.css';
}
.unmonitored {
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';
}

@ -1,200 +0,0 @@
import moment from 'moment';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import classNames from 'classnames';
import { icons, kinds } from 'Helpers/Props';
import Icon from 'Components/Icon';
import Link from 'Components/Link/Link';
import getStatusStyle from 'Calendar/getStatusStyle';
import CalendarEventConnector from 'Calendar/Events/CalendarEventConnector';
import styles from './CalendarEventGroup.css';
function getEventsInfo(events) {
let files = 0;
let queued = 0;
let monitored = 0;
let absoluteEpisodeNumbers = 0;
events.forEach((event) => {
if (event.episodeFileId) {
files++;
}
if (event.queued) {
queued++;
}
if (event.monitored) {
monitored++;
}
if (event.absoluteEpisodeNumber) {
absoluteEpisodeNumbers++;
}
});
return {
allDownloaded: files === events.length,
anyQueued: queued > 0,
anyMonitored: monitored > 0,
allAbsoluteEpisodeNumbers: absoluteEpisodeNumbers === events.length
};
}
class CalendarEventGroup extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
isExpanded: false
};
}
//
// Listeners
onExpandPress = () => {
this.setState({ isExpanded: !this.state.isExpanded });
}
//
// Render
render() {
const {
series,
events,
isDownloading,
showEpisodeInformation,
showFinaleIcon,
colorImpairedMode,
onEventModalOpenToggle
} = this.props;
const { isExpanded } = this.state;
const {
allDownloaded,
anyQueued,
anyMonitored
} = getEventsInfo(events);
const anyDownloading = isDownloading || anyQueued;
const firstEpisode = events[0];
const lastEpisode = events[events.length -1];
const airDateUtc = firstEpisode.airDateUtc;
const startTime = moment(airDateUtc);
const endTime = moment(lastEpisode.airDateUtc).add(series.runtime, 'minutes');
const seasonNumber = firstEpisode.seasonNumber;
const statusStyle = getStatusStyle(allDownloaded, anyDownloading, startTime, endTime, anyMonitored);
if (isExpanded) {
return (
<div>
{
events.map((event) => {
if (event.isGroup) {
return null;
}
return (
<CalendarEventConnector
key={event.id}
episodeId={event.id}
{...event}
onEventModalOpenToggle={onEventModalOpenToggle}
/>
);
})
}
<Link
className={styles.collapseContainer}
component="div"
onPress={this.onExpandPress}
>
<Icon
name={icons.COLLAPSE}
/>
</Link>
</div>
);
}
return (
<div
className={classNames(
styles.eventGroup,
styles[statusStyle],
colorImpairedMode && 'colorImpaired'
)}
>
<div className={styles.info}>
<div className={styles.seriesTitle}>
{series.title}
</div>
{
anyDownloading &&
<Icon
className={styles.statusIcon}
name={icons.DOWNLOADING}
title="An episode is downloading"
/>
}
{
firstEpisode.episodeNumber === 1 && seasonNumber > 0 &&
<Icon
className={styles.statusIcon}
name={icons.INFO}
kind={kinds.INFO}
title={seasonNumber === 1 ? 'Series Premiere' : 'Season Premiere'}
/>
}
{
showFinaleIcon &&
lastEpisode.episodeNumber !== 1 &&
seasonNumber > 0 &&
lastEpisode.episodeNumber === series.seasons.find((season) => season.seasonNumber === seasonNumber).statistics.totalEpisodeCount &&
<Icon
className={styles.statusIcon}
name={icons.INFO}
kind={kinds.WARNING}
title={series.status === 'ended' ? 'Series finale' : 'Season finale'}
/>
}
</div>
{
showEpisodeInformation &&
<Link
className={styles.expandContainer}
component="div"
onPress={this.onExpandPress}
>
<Icon
name={icons.EXPAND}
/>
</Link>
}
</div>
);
}
}
CalendarEventGroup.propTypes = {
series: PropTypes.object.isRequired,
events: PropTypes.arrayOf(PropTypes.object).isRequired,
isDownloading: PropTypes.bool.isRequired,
showEpisodeInformation: PropTypes.bool.isRequired,
showFinaleIcon: PropTypes.bool.isRequired,
timeFormat: PropTypes.string.isRequired,
colorImpairedMode: PropTypes.bool.isRequired,
onEventModalOpenToggle: PropTypes.func.isRequired
};
export default CalendarEventGroup;

@ -1,37 +0,0 @@
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import createMovieSelector from 'Store/Selectors/createMovieSelector';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
import CalendarEventGroup from './CalendarEventGroup';
function createIsDownloadingSelector() {
return createSelector(
(state, { movieIds }) => movieIds,
(state) => state.queue.details,
(movieIds, details) => {
return details.items.some((item) => {
return item.movie && movieIds.includes(item.movie.id);
});
}
);
}
function createMapStateToProps() {
return createSelector(
(state) => state.calendar.options,
createMovieSelector(),
createIsDownloadingSelector(),
createUISettingsSelector(),
(calendarOptions, movie, isDownloading, uiSettings) => {
return {
movie,
isDownloading,
...calendarOptions,
timeFormat: uiSettings.timeFormat,
colorImpairedMode: uiSettings.enableColorImpairedMode
};
}
);
}
export default connect(createMapStateToProps)(CalendarEventGroup);

@ -7,34 +7,11 @@ import styles from './Legend.css';
function Legend(props) {
const {
showFinaleIcon,
showSpecialIcon,
showCutoffUnmetIcon,
colorImpairedMode
} = props;
const iconsToShow = [];
if (showFinaleIcon) {
iconsToShow.push(
<LegendIconItem
name="Finale"
icon={icons.INFO}
kind={kinds.WARNING}
tooltip="Series or season finale"
/>
);
}
if (showSpecialIcon) {
iconsToShow.push(
<LegendIconItem
name="Special"
icon={icons.INFO}
kind={kinds.PINK}
tooltip="Special episode"
/>
);
}
if (showCutoffUnmetIcon) {
iconsToShow.push(
@ -51,8 +28,8 @@ function Legend(props) {
<div className={styles.legend}>
<div>
<LegendItem
status="unaired"
tooltip="Movie hasn't aired yet"
status="unreleased"
tooltip="Movie hasn't released yet"
colorImpairedMode={colorImpairedMode}
/>
@ -77,22 +54,10 @@ function Legend(props) {
/>
</div>
<div>
<LegendIconItem
name="Premiere"
icon={icons.INFO}
kind={kinds.INFO}
tooltip="Series or season premiere"
/>
{iconsToShow[0]}
</div>
{
iconsToShow.length > 1 &&
iconsToShow.length > 0 &&
<div>
{iconsToShow[1]}
{iconsToShow[2]}
{iconsToShow[0]}
</div>
}
</div>
@ -100,8 +65,6 @@ function Legend(props) {
}
Legend.propTypes = {
showFinaleIcon: PropTypes.bool.isRequired,
showSpecialIcon: PropTypes.bool.isRequired,
showCutoffUnmetIcon: PropTypes.bool.isRequired,
colorImpairedMode: PropTypes.bool.isRequired
};

@ -24,18 +24,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';
.unreleased {
composes: unreleased from '~Calendar/Events/CalendarEvent.css';
}

@ -203,8 +203,6 @@ class CalendarOptionsModalContent extends Component {
CalendarOptionsModalContent.propTypes = {
showMovieInformation: PropTypes.bool.isRequired,
showFinaleIcon: PropTypes.bool.isRequired,
showSpecialIcon: PropTypes.bool.isRequired,
showCutoffUnmetIcon: PropTypes.bool.isRequired,
firstDayOfWeek: PropTypes.number.isRequired,
calendarWeekColumnHeader: PropTypes.string.isRequired,

@ -20,7 +20,7 @@ function getStatusStyle(hasFile, downloading, startTime, isMonitored) {
return 'missing';
}
return 'unaired';
return 'unreleased';
}
export default getStatusStyle;

@ -17,7 +17,6 @@ import ModalFooter from 'Components/Modal/ModalFooter';
function getUrls(state) {
const {
unmonitored,
premieresOnly,
asAllDay,
tags
} = state;
@ -28,10 +27,6 @@ function getUrls(state) {
icalUrl += 'unmonitored=true&';
}
if (premieresOnly) {
icalUrl += 'premieresOnly=true&';
}
if (asAllDay) {
icalUrl += 'asAllDay=true&';
}
@ -61,7 +56,6 @@ class CalendarLinkModalContent extends Component {
const defaultState = {
unmonitored: false,
premieresOnly: false,
asAllDay: false,
tags: []
};
@ -105,7 +99,6 @@ class CalendarLinkModalContent extends Component {
const {
unmonitored,
premieresOnly,
asAllDay,
tags,
iCalHttpUrl,
@ -132,18 +125,6 @@ class CalendarLinkModalContent extends Component {
/>
</FormGroup>
<FormGroup>
<FormLabel>Season Premieres Only</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="premieresOnly"
value={premieresOnly}
helpText="Only the first episode in a season will be in the feed"
onChange={this.onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>Show as All-Day Events</FormLabel>

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Nancy;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Movies;
using NzbDrone.SignalR;
using Radarr.Api.V3.Movies;
@ -11,13 +12,16 @@ namespace Radarr.Api.V3.Calendar
{
public class CalendarModule : RadarrRestModuleWithSignalR<MovieResource, Movie>
{
protected readonly IMovieService _moviesService;
private readonly IMovieService _moviesService;
private readonly IUpgradableSpecification _qualityUpgradableSpecification;
public CalendarModule(IBroadcastSignalRMessage signalR,
IMovieService moviesService)
IMovieService moviesService,
IUpgradableSpecification qualityUpgradableSpecification)
: base(signalR, "calendar")
{
_moviesService = moviesService;
_qualityUpgradableSpecification = qualityUpgradableSpecification;
GetResourceAll = GetCalendar;
}
@ -59,7 +63,7 @@ namespace Radarr.Api.V3.Calendar
return null;
}
var resource = movie.ToResource();
var resource = movie.ToResource(_qualityUpgradableSpecification);
return resource;
}

Loading…
Cancel
Save