mirror of
https://github.com/Radarr/Radarr
synced 2025-12-23 08:44:49 +01:00
New: Show detailed queue status on Calendar
(cherry picked from commit 8fff59ff107d9a9fcfc0de1acb6aa635565e5d9b)
This commit is contained in:
parent
69253a4ac4
commit
a798556d32
15 changed files with 308 additions and 266 deletions
5
frontend/src/Activity/Queue/QueueDetails.css
Normal file
5
frontend/src/Activity/Queue/QueueDetails.css
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
.progressBarContainer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
7
frontend/src/Activity/Queue/QueueDetails.css.d.ts
vendored
Normal file
7
frontend/src/Activity/Queue/QueueDetails.css.d.ts
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'progressBarContainer': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
|
|
@ -1,116 +1,71 @@
|
||||||
import moment from 'moment';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
import Popover from 'Components/Tooltip/Popover';
|
||||||
|
import { icons, tooltipPositions } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
|
import QueueStatus from './QueueStatus';
|
||||||
|
import styles from './QueueDetails.css';
|
||||||
|
|
||||||
function QueueDetails(props) {
|
function QueueDetails(props) {
|
||||||
const {
|
const {
|
||||||
title,
|
title,
|
||||||
size,
|
size,
|
||||||
sizeleft,
|
sizeleft,
|
||||||
estimatedCompletionTime,
|
|
||||||
status,
|
status,
|
||||||
trackedDownloadState,
|
trackedDownloadState,
|
||||||
trackedDownloadStatus,
|
trackedDownloadStatus,
|
||||||
|
statusMessages,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
progressBar
|
progressBar
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const progress = size ? (100 - sizeleft / size * 100) : 0;
|
const progress = size ? (100 - sizeleft / size * 100) : 0;
|
||||||
|
const isDownloading = status === 'downloading';
|
||||||
|
const isPaused = status === 'paused';
|
||||||
|
const hasWarning = trackedDownloadStatus === 'warning';
|
||||||
|
const hasError = trackedDownloadStatus === 'error';
|
||||||
|
|
||||||
if (status === 'pending') {
|
if (
|
||||||
return (
|
(isDownloading || isPaused) &&
|
||||||
<Icon
|
!hasWarning &&
|
||||||
name={icons.PENDING}
|
!hasError
|
||||||
title={translate('ReleaseWillBeProcessedInterp', [moment(estimatedCompletionTime).fromNow()])}
|
) {
|
||||||
/>
|
const state = isPaused ? translate('Paused') : translate('Downloading');
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'completed') {
|
if (progress < 5) {
|
||||||
if (errorMessage) {
|
|
||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
name={icons.DOWNLOAD}
|
name={icons.DOWNLOADING}
|
||||||
kind={kinds.DANGER}
|
title={`${state} - ${progress.toFixed(1)}% ${title}`}
|
||||||
title={translate('ImportFailedInterp', { errorMessage })}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackedDownloadStatus === 'warning') {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
name={icons.DOWNLOAD}
|
|
||||||
kind={kinds.WARNING}
|
|
||||||
title={translate('UnableToImportCheckLogs')}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trackedDownloadState === 'importPending') {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
name={icons.DOWNLOAD}
|
|
||||||
kind={kinds.PURPLE}
|
|
||||||
title={`${translate('Downloaded')} - ${translate('WaitingToImport')}`}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trackedDownloadState === 'importing') {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
name={icons.DOWNLOAD}
|
|
||||||
kind={kinds.PURPLE}
|
|
||||||
title={`${translate('Downloaded')} - ${translate('Importing')}`}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage) {
|
|
||||||
return (
|
return (
|
||||||
<Icon
|
<Popover
|
||||||
name={icons.DOWNLOADING}
|
className={styles.progressBarContainer}
|
||||||
kind={kinds.DANGER}
|
anchor={progressBar}
|
||||||
title={translate('DownloadFailedInterp', { errorMessage })}
|
title={`${state} - ${progress.toFixed(1)}%`}
|
||||||
|
body={
|
||||||
|
<div>{title}</div>
|
||||||
|
}
|
||||||
|
position={tooltipPositions.LEFT}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status === 'failed') {
|
return (
|
||||||
return (
|
<QueueStatus
|
||||||
<Icon
|
sourceTitle={title}
|
||||||
name={icons.DOWNLOADING}
|
status={status}
|
||||||
kind={kinds.DANGER}
|
trackedDownloadStatus={trackedDownloadStatus}
|
||||||
title={translate('DownloadFailedCheckDownloadClientForMoreDetails')}
|
trackedDownloadState={trackedDownloadState}
|
||||||
/>
|
statusMessages={statusMessages}
|
||||||
);
|
errorMessage={errorMessage}
|
||||||
}
|
position={tooltipPositions.LEFT}
|
||||||
|
/>
|
||||||
if (status === 'warning') {
|
);
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
name={icons.DOWNLOADING}
|
|
||||||
kind={kinds.WARNING}
|
|
||||||
title={translate('DownloadWarningCheckDownloadClientForMoreDetails')}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (progress < 5) {
|
|
||||||
return (
|
|
||||||
<Icon
|
|
||||||
name={icons.DOWNLOADING}
|
|
||||||
title={translate('MovieIsDownloadingInterp', [progress.toFixed(1), title])}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return progressBar;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QueueDetails.propTypes = {
|
QueueDetails.propTypes = {
|
||||||
|
|
@ -121,6 +76,7 @@ QueueDetails.propTypes = {
|
||||||
status: PropTypes.string.isRequired,
|
status: PropTypes.string.isRequired,
|
||||||
trackedDownloadState: PropTypes.string.isRequired,
|
trackedDownloadState: PropTypes.string.isRequired,
|
||||||
trackedDownloadStatus: PropTypes.string.isRequired,
|
trackedDownloadStatus: PropTypes.string.isRequired,
|
||||||
|
statusMessages: PropTypes.arrayOf(PropTypes.object),
|
||||||
errorMessage: PropTypes.string,
|
errorMessage: PropTypes.string,
|
||||||
progressBar: PropTypes.node.isRequired
|
progressBar: PropTypes.node.isRequired
|
||||||
};
|
};
|
||||||
|
|
|
||||||
3
frontend/src/Activity/Queue/QueueStatus.css
Normal file
3
frontend/src/Activity/Queue/QueueStatus.css
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
.noMessages {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
7
frontend/src/Activity/Queue/QueueStatus.css.d.ts
vendored
Normal file
7
frontend/src/Activity/Queue/QueueStatus.css.d.ts
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'noMessages': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
162
frontend/src/Activity/Queue/QueueStatus.js
Normal file
162
frontend/src/Activity/Queue/QueueStatus.js
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import Icon from 'Components/Icon';
|
||||||
|
import Popover from 'Components/Tooltip/Popover';
|
||||||
|
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import styles from './QueueStatus.css';
|
||||||
|
|
||||||
|
function getDetailedPopoverBody(statusMessages) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
statusMessages.map(({ title, messages }) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={title}
|
||||||
|
className={messages.length ? undefined: styles.noMessages}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
<ul>
|
||||||
|
{
|
||||||
|
messages.map((message) => {
|
||||||
|
return (
|
||||||
|
<li key={message}>
|
||||||
|
{message}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function QueueStatus(props) {
|
||||||
|
const {
|
||||||
|
sourceTitle,
|
||||||
|
status,
|
||||||
|
trackedDownloadStatus,
|
||||||
|
trackedDownloadState,
|
||||||
|
statusMessages,
|
||||||
|
errorMessage,
|
||||||
|
position,
|
||||||
|
canFlip
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const hasWarning = trackedDownloadStatus === 'warning';
|
||||||
|
const hasError = trackedDownloadStatus === 'error';
|
||||||
|
|
||||||
|
// status === 'downloading'
|
||||||
|
let iconName = icons.DOWNLOADING;
|
||||||
|
let iconKind = kinds.DEFAULT;
|
||||||
|
let title = translate('Downloading');
|
||||||
|
|
||||||
|
if (status === 'paused') {
|
||||||
|
iconName = icons.PAUSED;
|
||||||
|
title = translate('Paused');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'queued') {
|
||||||
|
iconName = icons.QUEUED;
|
||||||
|
title = translate('Queued');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'completed') {
|
||||||
|
iconName = icons.DOWNLOADED;
|
||||||
|
title = translate('Downloaded');
|
||||||
|
|
||||||
|
if (trackedDownloadState === 'importPending') {
|
||||||
|
title += ` - ${translate('WaitingToImport')}`;
|
||||||
|
iconKind = kinds.PURPLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackedDownloadState === 'importing') {
|
||||||
|
title += ` - ${translate('Importing')}`;
|
||||||
|
iconKind = kinds.PURPLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackedDownloadState === 'failedPending') {
|
||||||
|
title += ` - ${translate('WaitingToProcess')}`;
|
||||||
|
iconKind = kinds.DANGER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasWarning) {
|
||||||
|
iconKind = kinds.WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'delay') {
|
||||||
|
iconName = icons.PENDING;
|
||||||
|
title = translate('Pending');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'downloadClientUnavailable') {
|
||||||
|
iconName = icons.PENDING;
|
||||||
|
iconKind = kinds.WARNING;
|
||||||
|
title = translate('PendingDownloadClientUnavailable');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'failed') {
|
||||||
|
iconName = icons.DOWNLOADING;
|
||||||
|
iconKind = kinds.DANGER;
|
||||||
|
title = translate('DownloadFailed');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'warning') {
|
||||||
|
iconName = icons.DOWNLOADING;
|
||||||
|
iconKind = kinds.WARNING;
|
||||||
|
const warningMessage = errorMessage || translate('CheckDownloadClientForDetails');
|
||||||
|
title = translate('DownloadWarning', { warningMessage });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasError) {
|
||||||
|
if (status === 'completed') {
|
||||||
|
iconName = icons.DOWNLOAD;
|
||||||
|
iconKind = kinds.DANGER;
|
||||||
|
title = translate('ImportFailed', { sourceTitle });
|
||||||
|
} else {
|
||||||
|
iconName = icons.DOWNLOADING;
|
||||||
|
iconKind = kinds.DANGER;
|
||||||
|
title = translate('DownloadFailed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
anchor={
|
||||||
|
<Icon
|
||||||
|
name={iconName}
|
||||||
|
kind={iconKind}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
title={title}
|
||||||
|
body={hasWarning || hasError ? getDetailedPopoverBody(statusMessages) : sourceTitle}
|
||||||
|
position={position}
|
||||||
|
canFlip={canFlip}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
QueueStatus.propTypes = {
|
||||||
|
sourceTitle: PropTypes.string.isRequired,
|
||||||
|
status: PropTypes.string.isRequired,
|
||||||
|
trackedDownloadStatus: PropTypes.string.isRequired,
|
||||||
|
trackedDownloadState: PropTypes.string.isRequired,
|
||||||
|
statusMessages: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
errorMessage: PropTypes.string,
|
||||||
|
position: PropTypes.oneOf(tooltipPositions.all).isRequired,
|
||||||
|
canFlip: PropTypes.bool.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
QueueStatus.defaultProps = {
|
||||||
|
trackedDownloadStatus: 'ok',
|
||||||
|
trackedDownloadState: 'downloading',
|
||||||
|
canFlip: false
|
||||||
|
};
|
||||||
|
|
||||||
|
export default QueueStatus;
|
||||||
|
|
@ -1,39 +1,11 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import Popover from 'Components/Tooltip/Popover';
|
import { tooltipPositions } from 'Helpers/Props';
|
||||||
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
|
import QueueStatus from './QueueStatus';
|
||||||
import styles from './QueueStatusCell.css';
|
import styles from './QueueStatusCell.css';
|
||||||
|
|
||||||
function getDetailedPopoverBody(statusMessages) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{
|
|
||||||
statusMessages.map(({ title, messages }) => {
|
|
||||||
return (
|
|
||||||
<div key={title}>
|
|
||||||
{title}
|
|
||||||
<ul>
|
|
||||||
{
|
|
||||||
messages.map((message) => {
|
|
||||||
return (
|
|
||||||
<li key={message}>
|
|
||||||
{message}
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function QueueStatusCell(props) {
|
function QueueStatusCell(props) {
|
||||||
const {
|
const {
|
||||||
sourceTitle,
|
sourceTitle,
|
||||||
|
|
@ -44,97 +16,16 @@ function QueueStatusCell(props) {
|
||||||
errorMessage
|
errorMessage
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const hasWarning = trackedDownloadStatus === 'warning';
|
|
||||||
const hasError = trackedDownloadStatus === 'error';
|
|
||||||
|
|
||||||
// status === 'downloading'
|
|
||||||
let iconName = icons.DOWNLOADING;
|
|
||||||
let iconKind = kinds.DEFAULT;
|
|
||||||
let title = translate('Downloading');
|
|
||||||
|
|
||||||
if (status === 'paused') {
|
|
||||||
iconName = icons.PAUSED;
|
|
||||||
title = translate('Paused');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'queued') {
|
|
||||||
iconName = icons.QUEUED;
|
|
||||||
title = translate('Queued');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'completed') {
|
|
||||||
iconName = icons.DOWNLOADED;
|
|
||||||
title = translate('Downloaded');
|
|
||||||
|
|
||||||
if (trackedDownloadState === 'importPending') {
|
|
||||||
title += ` - ${translate('WaitingToImport')}`;
|
|
||||||
iconKind = kinds.PURPLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trackedDownloadState === 'importing') {
|
|
||||||
title += ` - ${translate('Importing')}`;
|
|
||||||
iconKind = kinds.PURPLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trackedDownloadState === 'failedPending') {
|
|
||||||
title += ` - ${translate('WaitingToProcess')}`;
|
|
||||||
iconKind = kinds.DANGER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasWarning) {
|
|
||||||
iconKind = kinds.WARNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'delay') {
|
|
||||||
iconName = icons.PENDING;
|
|
||||||
title = translate('Pending');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'DownloadClientUnavailable') {
|
|
||||||
iconName = icons.PENDING;
|
|
||||||
iconKind = kinds.WARNING;
|
|
||||||
title = `${translate('Pending')} - ${translate('DownloadClientUnavailable')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'failed') {
|
|
||||||
iconName = icons.DOWNLOADING;
|
|
||||||
iconKind = kinds.DANGER;
|
|
||||||
title = translate('DownloadFailed');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 'warning') {
|
|
||||||
iconName = icons.DOWNLOADING;
|
|
||||||
iconKind = kinds.WARNING;
|
|
||||||
const warningMessage = errorMessage || translate('CheckDownloadClientForDetails');
|
|
||||||
title = translate('DownloadWarning', { warningMessage });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasError) {
|
|
||||||
if (status === 'completed') {
|
|
||||||
iconName = icons.DOWNLOAD;
|
|
||||||
iconKind = kinds.DANGER;
|
|
||||||
title = translate('ImportFailed', { sourceTitle });
|
|
||||||
} else {
|
|
||||||
iconName = icons.DOWNLOADING;
|
|
||||||
iconKind = kinds.DANGER;
|
|
||||||
title = translate('DownloadFailed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRowCell className={styles.status}>
|
<TableRowCell className={styles.status}>
|
||||||
<Popover
|
<QueueStatus
|
||||||
anchor={
|
sourceTitle={sourceTitle}
|
||||||
<Icon
|
status={status}
|
||||||
name={iconName}
|
trackedDownloadStatus={trackedDownloadStatus}
|
||||||
kind={iconKind}
|
trackedDownloadState={trackedDownloadState}
|
||||||
/>
|
statusMessages={statusMessages}
|
||||||
}
|
errorMessage={errorMessage}
|
||||||
title={title}
|
|
||||||
body={hasWarning || hasError ? getDetailedPopoverBody(statusMessages) : sourceTitle}
|
|
||||||
position={tooltipPositions.RIGHT}
|
position={tooltipPositions.RIGHT}
|
||||||
canFlip={false}
|
|
||||||
/>
|
/>
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,28 @@
|
||||||
.event {
|
.event {
|
||||||
display: flex;
|
position: relative;
|
||||||
overflow-x: hidden;
|
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
border-bottom: 1px solid var(--borderColor);
|
border-bottom: 1px solid var(--borderColor);
|
||||||
font-size: $defaultFontSize;
|
}
|
||||||
|
|
||||||
|
.underlay {
|
||||||
|
@add-mixin cover;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--tableRowHoverBackgroundColor);
|
background-color: var(--tableRowHoverBackgroundColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.link {
|
.overlay {
|
||||||
composes: link from '~Calendar/Events/CalendarEvent.css';
|
@add-mixin linkOverlay;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
overflow-x: hidden;
|
||||||
|
font-size: $defaultFontSize;
|
||||||
|
|
||||||
|
&:global(.colorImpaired) {
|
||||||
|
border-left-width: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.eventWrapper {
|
.eventWrapper {
|
||||||
|
|
@ -44,6 +55,8 @@
|
||||||
|
|
||||||
.statusIcon {
|
.statusIcon {
|
||||||
margin-left: 3px;
|
margin-left: 3px;
|
||||||
|
cursor: default;
|
||||||
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -95,6 +108,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dateIcon {
|
.releaseIcon {
|
||||||
|
margin-right: 20px;
|
||||||
width: 25px;
|
width: 25px;
|
||||||
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,19 @@
|
||||||
interface CssExports {
|
interface CssExports {
|
||||||
'continuing': string;
|
'continuing': string;
|
||||||
'date': string;
|
'date': string;
|
||||||
'dateIcon': string;
|
|
||||||
'downloaded': string;
|
'downloaded': string;
|
||||||
'event': string;
|
'event': string;
|
||||||
'eventWrapper': string;
|
'eventWrapper': string;
|
||||||
'genres': string;
|
'genres': string;
|
||||||
'link': string;
|
|
||||||
'missingMonitored': string;
|
'missingMonitored': string;
|
||||||
'missingUnmonitored': string;
|
'missingUnmonitored': string;
|
||||||
'movieTitle': string;
|
'movieTitle': string;
|
||||||
|
'overlay': string;
|
||||||
'queue': string;
|
'queue': string;
|
||||||
|
'releaseIcon': string;
|
||||||
'statusIcon': string;
|
'statusIcon': string;
|
||||||
'time': string;
|
'time': string;
|
||||||
|
'underlay': string;
|
||||||
'unmonitored': string;
|
'unmonitored': string;
|
||||||
}
|
}
|
||||||
export const cssExports: CssExports;
|
export const cssExports: CssExports;
|
||||||
|
|
|
||||||
|
|
@ -87,25 +87,24 @@ class AgendaEvent extends Component {
|
||||||
const link = `/movie/${titleSlug}`;
|
const link = `/movie/${titleSlug}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={styles.event}>
|
||||||
<Link
|
<Link
|
||||||
className={classNames(
|
className={styles.underlay}
|
||||||
styles.event,
|
|
||||||
styles.link
|
|
||||||
)}
|
|
||||||
to={link}
|
to={link}
|
||||||
>
|
/>
|
||||||
<div className={styles.dateIcon}>
|
|
||||||
|
<div className={styles.overlay}>
|
||||||
|
<div className={styles.date}>
|
||||||
|
{(showDate) ? startTime.format(longDateFormat) : null}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.releaseIcon}>
|
||||||
<Icon
|
<Icon
|
||||||
name={releaseIcon}
|
name={releaseIcon}
|
||||||
kind={kinds.DEFAULT}
|
kind={kinds.DEFAULT}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.date}>
|
|
||||||
{(showDate) ? startTime.format(longDateFormat) : null}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
styles.eventWrapper,
|
styles.eventWrapper,
|
||||||
|
|
@ -143,9 +142,7 @@ class AgendaEvent extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
showCutoffUnmetIcon &&
|
showCutoffUnmetIcon && !!movieFile && movieFile.qualityCutoffNotMet &&
|
||||||
!!movieFile &&
|
|
||||||
movieFile.qualityCutoffNotMet &&
|
|
||||||
<Icon
|
<Icon
|
||||||
className={styles.statusIcon}
|
className={styles.statusIcon}
|
||||||
name={icons.MOVIE_FILE}
|
name={icons.MOVIE_FILE}
|
||||||
|
|
@ -154,7 +151,7 @@ class AgendaEvent extends Component {
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,22 @@
|
||||||
$fullColorGradient: rgba(244, 245, 246, 0.2);
|
$fullColorGradient: rgba(244, 245, 246, 0.2);
|
||||||
|
|
||||||
.event {
|
.event {
|
||||||
overflow-x: hidden;
|
position: relative;
|
||||||
margin: 4px 2px;
|
margin: 4px 2px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
border-bottom: 1px solid var(--calendarBorderColor);
|
border-bottom: 1px solid var(--calendarBorderColor);
|
||||||
border-left: 4px solid var(--calendarBorderColor);
|
border-left: 4px solid var(--calendarBorderColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
.underlay {
|
||||||
|
@add-mixin cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
@add-mixin linkOverlay;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
overflow-x: hidden;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|
||||||
&:global(.colorImpaired) {
|
&:global(.colorImpaired) {
|
||||||
|
|
@ -13,18 +24,6 @@ $fullColorGradient: rgba(244, 245, 246, 0.2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.link {
|
|
||||||
composes: link from '~Components/Link/Link.css';
|
|
||||||
|
|
||||||
display: block;
|
|
||||||
color: var(--defaultColor);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--defaultColor);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.info,
|
.info,
|
||||||
.movieInfo {
|
.movieInfo {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -53,6 +52,8 @@ $fullColorGradient: rgba(244, 245, 246, 0.2);
|
||||||
|
|
||||||
.statusIcon {
|
.statusIcon {
|
||||||
margin-left: 3px;
|
margin-left: 3px;
|
||||||
|
cursor: default;
|
||||||
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,15 @@ interface CssExports {
|
||||||
'event': string;
|
'event': string;
|
||||||
'genres': string;
|
'genres': string;
|
||||||
'info': string;
|
'info': string;
|
||||||
'link': string;
|
|
||||||
'missingMonitored': string;
|
'missingMonitored': string;
|
||||||
'missingUnmonitored': string;
|
'missingUnmonitored': string;
|
||||||
'movieInfo': string;
|
'movieInfo': string;
|
||||||
'movieTitle': string;
|
'movieTitle': string;
|
||||||
|
'overlay': string;
|
||||||
'queue': string;
|
'queue': string;
|
||||||
'statusContainer': string;
|
'statusContainer': string;
|
||||||
'statusIcon': string;
|
'statusIcon': string;
|
||||||
|
'underlay': string;
|
||||||
'unmonitored': string;
|
'unmonitored': string;
|
||||||
}
|
}
|
||||||
export const cssExports: CssExports;
|
export const cssExports: CssExports;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component, Fragment } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import Link from 'Components/Link/Link';
|
import Link from 'Components/Link/Link';
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
|
|
@ -57,18 +57,20 @@ class CalendarEvent extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<div
|
||||||
|
className={classNames(
|
||||||
|
styles.event,
|
||||||
|
styles[statusStyle],
|
||||||
|
colorImpairedMode && 'colorImpaired',
|
||||||
|
fullColorEvents && 'fullColor'
|
||||||
|
)}
|
||||||
|
>
|
||||||
<Link
|
<Link
|
||||||
className={classNames(
|
className={styles.underlay}
|
||||||
styles.event,
|
|
||||||
styles.link,
|
|
||||||
styles[statusStyle],
|
|
||||||
colorImpairedMode && 'colorImpaired',
|
|
||||||
fullColorEvents && 'fullColor'
|
|
||||||
)}
|
|
||||||
// component="div"
|
|
||||||
to={link}
|
to={link}
|
||||||
>
|
/>
|
||||||
|
|
||||||
|
<div className={styles.overlay} >
|
||||||
<div className={styles.info}>
|
<div className={styles.info}>
|
||||||
<div className={styles.movieTitle}>
|
<div className={styles.movieTitle}>
|
||||||
{title}
|
{title}
|
||||||
|
|
@ -130,8 +132,8 @@ class CalendarEvent extends Component {
|
||||||
</div> :
|
</div> :
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
</Link>
|
</div>
|
||||||
</Fragment>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import QueueDetails from 'Activity/Queue/QueueDetails';
|
import QueueDetails from 'Activity/Queue/QueueDetails';
|
||||||
import CircularProgressBar from 'Components/CircularProgressBar';
|
import CircularProgressBar from 'Components/CircularProgressBar';
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
|
|
||||||
function CalendarEventQueueDetails(props) {
|
function CalendarEventQueueDetails(props) {
|
||||||
const {
|
const {
|
||||||
|
|
@ -13,6 +12,7 @@ function CalendarEventQueueDetails(props) {
|
||||||
status,
|
status,
|
||||||
trackedDownloadState,
|
trackedDownloadState,
|
||||||
trackedDownloadStatus,
|
trackedDownloadStatus,
|
||||||
|
statusMessages,
|
||||||
errorMessage
|
errorMessage
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
|
@ -27,16 +27,15 @@ function CalendarEventQueueDetails(props) {
|
||||||
status={status}
|
status={status}
|
||||||
trackedDownloadState={trackedDownloadState}
|
trackedDownloadState={trackedDownloadState}
|
||||||
trackedDownloadStatus={trackedDownloadStatus}
|
trackedDownloadStatus={trackedDownloadStatus}
|
||||||
|
statusMessages={statusMessages}
|
||||||
errorMessage={errorMessage}
|
errorMessage={errorMessage}
|
||||||
progressBar={
|
progressBar={
|
||||||
<div title={translate('MovieIsDownloadingInterp', [progress.toFixed(1), title])}>
|
<CircularProgressBar
|
||||||
<CircularProgressBar
|
progress={progress}
|
||||||
progress={progress}
|
size={20}
|
||||||
size={20}
|
strokeWidth={2}
|
||||||
strokeWidth={2}
|
strokeColor={'#7a43b6'}
|
||||||
strokeColor={'#7a43b6'}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
@ -50,6 +49,7 @@ CalendarEventQueueDetails.propTypes = {
|
||||||
status: PropTypes.string.isRequired,
|
status: PropTypes.string.isRequired,
|
||||||
trackedDownloadState: PropTypes.string.isRequired,
|
trackedDownloadState: PropTypes.string.isRequired,
|
||||||
trackedDownloadStatus: PropTypes.string.isRequired,
|
trackedDownloadStatus: PropTypes.string.isRequired,
|
||||||
|
statusMessages: PropTypes.arrayOf(PropTypes.object),
|
||||||
errorMessage: PropTypes.string
|
errorMessage: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -314,14 +314,11 @@
|
||||||
"DownloadClientsLoadError": "Unable to load download clients",
|
"DownloadClientsLoadError": "Unable to load download clients",
|
||||||
"DownloadClientsSettingsSummary": "Download clients, download handling and remote path mappings",
|
"DownloadClientsSettingsSummary": "Download clients, download handling and remote path mappings",
|
||||||
"DownloadFailed": "Download failed",
|
"DownloadFailed": "Download failed",
|
||||||
"DownloadFailedCheckDownloadClientForMoreDetails": "Download failed: check download client for more details",
|
|
||||||
"DownloadFailedInterp": "Download failed: {errorMessage}",
|
|
||||||
"DownloadPropersAndRepacks": "Propers and Repacks",
|
"DownloadPropersAndRepacks": "Propers and Repacks",
|
||||||
"DownloadPropersAndRepacksHelpText1": "Whether or not to automatically upgrade to Propers/Repacks",
|
"DownloadPropersAndRepacksHelpText1": "Whether or not to automatically upgrade to Propers/Repacks",
|
||||||
"DownloadPropersAndRepacksHelpText2": "Use 'Do not Prefer' to sort by custom format score over Propers/Repacks",
|
"DownloadPropersAndRepacksHelpText2": "Use 'Do not Prefer' to sort by custom format score over Propers/Repacks",
|
||||||
"DownloadPropersAndRepacksHelpTextWarning": "Use custom formats for automatic upgrades to Propers/Repacks",
|
"DownloadPropersAndRepacksHelpTextWarning": "Use custom formats for automatic upgrades to Propers/Repacks",
|
||||||
"DownloadWarning": "Download warning: {warningMessage}",
|
"DownloadWarning": "Download warning: {warningMessage}",
|
||||||
"DownloadWarningCheckDownloadClientForMoreDetails": "Download warning: check download client for more details",
|
|
||||||
"Downloaded": "Downloaded",
|
"Downloaded": "Downloaded",
|
||||||
"DownloadedAndMonitored": "Downloaded (Monitored)",
|
"DownloadedAndMonitored": "Downloaded (Monitored)",
|
||||||
"DownloadedButNotMonitored": "Downloaded (Unmonitored)",
|
"DownloadedButNotMonitored": "Downloaded (Unmonitored)",
|
||||||
|
|
@ -472,7 +469,6 @@
|
||||||
"ImportExtraFiles": "Import Extra Files",
|
"ImportExtraFiles": "Import Extra Files",
|
||||||
"ImportExtraFilesHelpText": "Import matching extra files (subtitles, nfo, etc) after importing an movie file",
|
"ImportExtraFilesHelpText": "Import matching extra files (subtitles, nfo, etc) after importing an movie file",
|
||||||
"ImportFailed": "Import failed: {sourceTitle}",
|
"ImportFailed": "Import failed: {sourceTitle}",
|
||||||
"ImportFailedInterp": "Import failed: {errorMessage}",
|
|
||||||
"ImportHeader": "Import an existing organized library to add movies to Radarr",
|
"ImportHeader": "Import an existing organized library to add movies to Radarr",
|
||||||
"ImportIncludeQuality": "Make sure that your files include the quality in their filenames. e.g. {0}",
|
"ImportIncludeQuality": "Make sure that your files include the quality in their filenames. e.g. {0}",
|
||||||
"ImportLibrary": "Library Import",
|
"ImportLibrary": "Library Import",
|
||||||
|
|
@ -674,7 +670,6 @@
|
||||||
"MovieInfoLanguageHelpTextWarning": "Browser Reload Required",
|
"MovieInfoLanguageHelpTextWarning": "Browser Reload Required",
|
||||||
"MovieInvalidFormat": "Movie: Invalid Format",
|
"MovieInvalidFormat": "Movie: Invalid Format",
|
||||||
"MovieIsDownloading": "Movie is downloading",
|
"MovieIsDownloading": "Movie is downloading",
|
||||||
"MovieIsDownloadingInterp": "Movie is downloading - {0}% {1}",
|
|
||||||
"MovieIsMonitored": "Movie is monitored",
|
"MovieIsMonitored": "Movie is monitored",
|
||||||
"MovieIsOnImportExclusionList": "Movie is on Import Exclusion List",
|
"MovieIsOnImportExclusionList": "Movie is on Import Exclusion List",
|
||||||
"MovieIsRecommend": "Movie is recommended based on recent addition",
|
"MovieIsRecommend": "Movie is recommended based on recent addition",
|
||||||
|
|
@ -798,6 +793,7 @@
|
||||||
"PendingChangesDiscardChanges": "Discard changes and leave",
|
"PendingChangesDiscardChanges": "Discard changes and leave",
|
||||||
"PendingChangesMessage": "You have unsaved changes, are you sure you want to leave this page?",
|
"PendingChangesMessage": "You have unsaved changes, are you sure you want to leave this page?",
|
||||||
"PendingChangesStayReview": "Stay and review changes",
|
"PendingChangesStayReview": "Stay and review changes",
|
||||||
|
"PendingDownloadClientUnavailable": "Pending - Download client is unavailable",
|
||||||
"Permissions": "Permissions",
|
"Permissions": "Permissions",
|
||||||
"PhysicalRelease": "Physical Release",
|
"PhysicalRelease": "Physical Release",
|
||||||
"PhysicalReleaseDate": "Physical Release Date",
|
"PhysicalReleaseDate": "Physical Release Date",
|
||||||
|
|
@ -902,7 +898,6 @@
|
||||||
"ReleaseRejected": "Release Rejected",
|
"ReleaseRejected": "Release Rejected",
|
||||||
"ReleaseStatus": "Release Status",
|
"ReleaseStatus": "Release Status",
|
||||||
"ReleaseTitle": "Release Title",
|
"ReleaseTitle": "Release Title",
|
||||||
"ReleaseWillBeProcessedInterp": "Release will be processed {0}",
|
|
||||||
"Released": "Released",
|
"Released": "Released",
|
||||||
"ReleasedMsg": "Movie is released",
|
"ReleasedMsg": "Movie is released",
|
||||||
"Reload": "Reload",
|
"Reload": "Reload",
|
||||||
|
|
@ -1208,7 +1203,6 @@
|
||||||
"UnableToAddANewQualityProfilePleaseTryAgain": "Unable to add a new quality profile, please try again.",
|
"UnableToAddANewQualityProfilePleaseTryAgain": "Unable to add a new quality profile, please try again.",
|
||||||
"UnableToAddANewRemotePathMappingPleaseTryAgain": "Unable to add a new remote path mapping, please try again.",
|
"UnableToAddANewRemotePathMappingPleaseTryAgain": "Unable to add a new remote path mapping, please try again.",
|
||||||
"UnableToAddRootFolder": "Unable to add root folder",
|
"UnableToAddRootFolder": "Unable to add root folder",
|
||||||
"UnableToImportCheckLogs": "Downloaded - Unable to Import: check logs for details",
|
|
||||||
"UnableToLoadAltTitle": "Unable to load alternative titles.",
|
"UnableToLoadAltTitle": "Unable to load alternative titles.",
|
||||||
"UnableToLoadAutoTagging": "Unable to load auto tagging",
|
"UnableToLoadAutoTagging": "Unable to load auto tagging",
|
||||||
"UnableToLoadBackups": "Unable to load backups",
|
"UnableToLoadBackups": "Unable to load backups",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue