feat: added the ability to request 4k media on the titlecard

pull/3636/head
Brandon Cohen 1 year ago
parent 7bdd25e5a4
commit 76f57550fa
No known key found for this signature in database
GPG Key ID: 3E759476348594C9

@ -26,6 +26,8 @@ export interface PublicSettingsResponse {
applicationUrl: string; applicationUrl: string;
hideAvailable: boolean; hideAvailable: boolean;
localLogin: boolean; localLogin: boolean;
movieEnabled: boolean;
seriesEnabled: boolean;
movie4kEnabled: boolean; movie4kEnabled: boolean;
series4kEnabled: boolean; series4kEnabled: boolean;
region: string; region: string;

@ -115,6 +115,8 @@ interface FullPublicSettings extends PublicSettings {
applicationUrl: string; applicationUrl: string;
hideAvailable: boolean; hideAvailable: boolean;
localLogin: boolean; localLogin: boolean;
movieEnabled: boolean;
seriesEnabled: boolean;
movie4kEnabled: boolean; movie4kEnabled: boolean;
series4kEnabled: boolean; series4kEnabled: boolean;
region: string; region: string;
@ -491,6 +493,12 @@ class Settings {
applicationUrl: this.data.main.applicationUrl, applicationUrl: this.data.main.applicationUrl,
hideAvailable: this.data.main.hideAvailable, hideAvailable: this.data.main.hideAvailable,
localLogin: this.data.main.localLogin, localLogin: this.data.main.localLogin,
movieEnabled: this.data.radarr.some(
(radarr) => !radarr.is4k && radarr.isDefault
),
seriesEnabled: this.data.sonarr.some(
(sonarr) => !sonarr.is4k && sonarr.isDefault
),
movie4kEnabled: this.data.radarr.some( movie4kEnabled: this.data.radarr.some(
(radarr) => radarr.is4k && radarr.isDefault (radarr) => radarr.is4k && radarr.isDefault
), ),

@ -98,6 +98,7 @@ const MediaSlider = ({
id={title.id} id={title.id}
image={title.posterPath} image={title.posterPath}
status={title.mediaInfo?.status} status={title.mediaInfo?.status}
status4k={title.mediaInfo?.status4k}
summary={title.overview} summary={title.overview}
title={title.title} title={title.title}
userScore={title.voteAverage} userScore={title.voteAverage}
@ -112,6 +113,7 @@ const MediaSlider = ({
id={title.id} id={title.id}
image={title.posterPath} image={title.posterPath}
status={title.mediaInfo?.status} status={title.mediaInfo?.status}
status4k={title.mediaInfo?.status4k}
summary={title.overview} summary={title.overview}
title={title.name} title={title.name}
userScore={title.voteAverage} userScore={title.voteAverage}

@ -185,6 +185,7 @@ const PersonDetails = () => {
summary={media.overview} summary={media.overview}
mediaType={media.mediaType as 'movie' | 'tv'} mediaType={media.mediaType as 'movie' | 'tv'}
status={media.mediaInfo?.status} status={media.mediaInfo?.status}
status4k={media.mediaInfo?.status}
canExpand canExpand
/> />
{media.job && ( {media.job && (

@ -59,6 +59,7 @@ const TmdbTitleCard = ({
id={title.id} id={title.id}
image={title.posterPath} image={title.posterPath}
status={title.mediaInfo?.status} status={title.mediaInfo?.status}
status4k={title.mediaInfo?.status4k}
summary={title.overview} summary={title.overview}
title={title.title} title={title.title}
userScore={title.voteAverage} userScore={title.voteAverage}
@ -71,6 +72,7 @@ const TmdbTitleCard = ({
id={title.id} id={title.id}
image={title.posterPath} image={title.posterPath}
status={title.mediaInfo?.status} status={title.mediaInfo?.status}
status4k={title.mediaInfo?.status4k}
summary={title.overview} summary={title.overview}
title={title.name} title={title.name}
userScore={title.voteAverage} userScore={title.voteAverage}

@ -6,6 +6,7 @@ import RequestModal from '@app/components/RequestModal';
import ErrorCard from '@app/components/TitleCard/ErrorCard'; import ErrorCard from '@app/components/TitleCard/ErrorCard';
import Placeholder from '@app/components/TitleCard/Placeholder'; import Placeholder from '@app/components/TitleCard/Placeholder';
import { useIsTouch } from '@app/hooks/useIsTouch'; import { useIsTouch } from '@app/hooks/useIsTouch';
import useSettings from '@app/hooks/useSettings';
import { Permission, useUser } from '@app/hooks/useUser'; import { Permission, useUser } from '@app/hooks/useUser';
import globalMessages from '@app/i18n/globalMessages'; import globalMessages from '@app/i18n/globalMessages';
import { withProperties } from '@app/utils/typeHelpers'; import { withProperties } from '@app/utils/typeHelpers';
@ -26,6 +27,7 @@ interface TitleCardProps {
userScore?: number; userScore?: number;
mediaType: MediaType; mediaType: MediaType;
status?: MediaStatus; status?: MediaStatus;
status4k?: MediaStatus;
canExpand?: boolean; canExpand?: boolean;
inProgress?: boolean; inProgress?: boolean;
} }
@ -37,6 +39,7 @@ const TitleCard = ({
year, year,
title, title,
status, status,
status4k,
mediaType, mediaType,
inProgress = false, inProgress = false,
canExpand = false, canExpand = false,
@ -46,8 +49,11 @@ const TitleCard = ({
const { hasPermission } = useUser(); const { hasPermission } = useUser();
const [isUpdating, setIsUpdating] = useState(false); const [isUpdating, setIsUpdating] = useState(false);
const [currentStatus, setCurrentStatus] = useState(status); const [currentStatus, setCurrentStatus] = useState(status);
const [currentStatus4k, setCurrentStatus4k] = useState(status4k);
const [showDetail, setShowDetail] = useState(false); const [showDetail, setShowDetail] = useState(false);
const [showRequestModal, setShowRequestModal] = useState(false); const [showRequestModal, setShowRequestModal] = useState(false);
const [showRequestModal4k, setShowRequestModal4k] = useState(false);
const settings = useSettings();
// Just to get the year from the date // Just to get the year from the date
if (year) { if (year) {
@ -56,7 +62,8 @@ const TitleCard = ({
useEffect(() => { useEffect(() => {
setCurrentStatus(status); setCurrentStatus(status);
}, [status]); setCurrentStatus4k(status4k);
}, [status, status4k]);
const requestComplete = useCallback((newStatus: MediaStatus) => { const requestComplete = useCallback((newStatus: MediaStatus) => {
setCurrentStatus(newStatus); setCurrentStatus(newStatus);
@ -70,15 +77,44 @@ const TitleCard = ({
const closeModal = useCallback(() => setShowRequestModal(false), []); const closeModal = useCallback(() => setShowRequestModal(false), []);
const showRequestButton = hasPermission( const requestComplete4k = useCallback((newStatus: MediaStatus) => {
[ setCurrentStatus4k(newStatus);
Permission.REQUEST, setShowRequestModal4k(false);
mediaType === 'movie' || mediaType === 'collection' }, []);
? Permission.REQUEST_MOVIE
: Permission.REQUEST_TV, const closeModal4k = useCallback(() => setShowRequestModal4k(false), []);
],
{ type: 'or' } const showRequestButton =
); hasPermission(
[
Permission.REQUEST,
mediaType === 'movie' || mediaType === 'collection'
? Permission.REQUEST_MOVIE
: Permission.REQUEST_TV,
],
{ type: 'or' }
) &&
((settings.currentSettings.movieEnabled && mediaType === 'movie') ||
(settings.currentSettings.seriesEnabled && mediaType === 'tv'));
const showRequestButton4k =
hasPermission(
[
Permission.REQUEST_4K,
mediaType === 'movie' || mediaType === 'collection'
? Permission.REQUEST_4K_MOVIE
: Permission.REQUEST_4K_TV,
],
{ type: 'or' }
) &&
((settings.currentSettings.movie4kEnabled && mediaType === 'movie') ||
(settings.currentSettings.series4kEnabled && mediaType === 'tv'));
const isRequestable =
(showRequestButton &&
(!currentStatus || currentStatus === MediaStatus.UNKNOWN)) ||
(showRequestButton4k &&
(!currentStatus4k || currentStatus4k === MediaStatus.UNKNOWN));
return ( return (
<div <div
@ -99,6 +135,21 @@ const TitleCard = ({
onUpdating={requestUpdating} onUpdating={requestUpdating}
onCancel={closeModal} onCancel={closeModal}
/> />
<RequestModal
tmdbId={id}
show={showRequestModal4k}
type={
mediaType === 'movie'
? 'movie'
: mediaType === 'collection'
? 'collection'
: 'tv'
}
onComplete={requestComplete4k}
onUpdating={requestUpdating}
onCancel={closeModal4k}
is4k
/>
<div <div
className={`relative transform-gpu cursor-default overflow-hidden rounded-xl bg-gray-800 bg-cover outline-none ring-1 transition duration-300 ${ className={`relative transform-gpu cursor-default overflow-hidden rounded-xl bg-gray-800 bg-cover outline-none ring-1 transition duration-300 ${
showDetail showDetail
@ -151,15 +202,24 @@ const TitleCard = ({
: intl.formatMessage(globalMessages.tvshow)} : intl.formatMessage(globalMessages.tvshow)}
</div> </div>
</div> </div>
{currentStatus && currentStatus !== MediaStatus.UNKNOWN && ( <div className="pointer-events-none z-40 flex items-center">
<div className="pointer-events-none z-40 flex items-center"> {currentStatus && currentStatus !== MediaStatus.UNKNOWN ? (
<StatusBadgeMini <StatusBadgeMini
status={currentStatus} status={currentStatus}
inProgress={inProgress} inProgress={inProgress}
shrink shrink
/> />
</div> ) : (
)} currentStatus4k &&
currentStatus4k !== MediaStatus.UNKNOWN && (
<StatusBadgeMini
status={currentStatus4k}
inProgress={inProgress}
shrink
/>
)
)}
</div>
</div> </div>
<Transition <Transition
as={Fragment} as={Fragment}
@ -178,7 +238,9 @@ const TitleCard = ({
<Transition <Transition
as={Fragment} as={Fragment}
show={!image || showDetail || showRequestModal} show={
!image || showDetail || showRequestModal || showRequestModal4k
}
enter="transition-opacity" enter="transition-opacity"
enterFrom="opacity-0" enterFrom="opacity-0"
enterTo="opacity-100" enterTo="opacity-100"
@ -206,10 +268,7 @@ const TitleCard = ({
<div className="flex h-full w-full items-end"> <div className="flex h-full w-full items-end">
<div <div
className={`px-2 text-white ${ className={`px-2 text-white ${
!showRequestButton || !isRequestable ? 'pb-2' : 'pb-11'
(currentStatus && currentStatus !== MediaStatus.UNKNOWN)
? 'pb-2'
: 'pb-11'
}`} }`}
> >
{year && ( {year && (
@ -232,12 +291,7 @@ const TitleCard = ({
<div <div
className="whitespace-normal text-xs" className="whitespace-normal text-xs"
style={{ style={{
WebkitLineClamp: WebkitLineClamp: !isRequestable ? 5 : 3,
!showRequestButton ||
(currentStatus &&
currentStatus !== MediaStatus.UNKNOWN)
? 5
: 3,
display: '-webkit-box', display: '-webkit-box',
overflow: 'hidden', overflow: 'hidden',
WebkitBoxOrient: 'vertical', WebkitBoxOrient: 'vertical',
@ -253,20 +307,39 @@ const TitleCard = ({
<div className="absolute bottom-0 left-0 right-0 flex justify-between px-2 py-2"> <div className="absolute bottom-0 left-0 right-0 flex justify-between px-2 py-2">
{showRequestButton && {showRequestButton &&
(!currentStatus || currentStatus === MediaStatus.UNKNOWN) && ( (!currentStatus || currentStatus === MediaStatus.UNKNOWN) ? (
<Button
buttonType="primary"
buttonSize="sm"
onClick={(e) => {
e.preventDefault();
setShowRequestModal(true);
}}
className="h-7 w-full"
>
<ArrowDownTrayIcon />
<span>{intl.formatMessage(globalMessages.request)}</span>
</Button>
) : (
showRequestButton4k &&
(!currentStatus4k ||
currentStatus4k === MediaStatus.UNKNOWN) && (
<Button <Button
buttonType="primary" buttonType="primary"
buttonSize="sm" buttonSize="sm"
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
setShowRequestModal(true); setShowRequestModal4k(true);
}} }}
className="h-7 w-full" className="h-7 w-full"
> >
<ArrowDownTrayIcon /> <ArrowDownTrayIcon />
<span>{intl.formatMessage(globalMessages.request)}</span> <span>
{intl.formatMessage(globalMessages.request4k)}
</span>
</Button> </Button>
)} )
)}
</div> </div>
</div> </div>
</Transition> </Transition>

@ -13,6 +13,8 @@ const defaultSettings = {
applicationUrl: '', applicationUrl: '',
hideAvailable: false, hideAvailable: false,
localLogin: true, localLogin: true,
movieEnabled: false,
seriesEnabled: false,
movie4kEnabled: false, movie4kEnabled: false,
series4kEnabled: false, series4kEnabled: false,
region: '', region: '',

@ -174,6 +174,8 @@ CoreApp.getInitialProps = async (initialProps) => {
applicationTitle: '', applicationTitle: '',
applicationUrl: '', applicationUrl: '',
hideAvailable: false, hideAvailable: false,
movieEnabled: false,
seriesEnabled: false,
movie4kEnabled: false, movie4kEnabled: false,
series4kEnabled: false, series4kEnabled: false,
localLogin: true, localLogin: true,

Loading…
Cancel
Save