feat: 4K Requests (#559)
parent
79629645aa
commit
6b2df24a2e
@ -0,0 +1,91 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
export class Add4kStatusFields1610370640747 implements MigrationInterface {
|
||||||
|
name = 'Add4kStatusFields1610370640747';
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE TABLE "temporary_season" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "seasonNumber" integer NOT NULL, "status" integer NOT NULL DEFAULT (1), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "mediaId" integer, "status4k" integer NOT NULL DEFAULT (1), CONSTRAINT "FK_087099b39600be695591da9a49c" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`INSERT INTO "temporary_season"("id", "seasonNumber", "status", "createdAt", "updatedAt", "mediaId") SELECT "id", "seasonNumber", "status", "createdAt", "updatedAt", "mediaId" FROM "season"`
|
||||||
|
);
|
||||||
|
await queryRunner.query(`DROP TABLE "season"`);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "temporary_season" RENAME TO "season"`
|
||||||
|
);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_7157aad07c73f6a6ae3bbd5ef5"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_41a289eb1fa489c1bc6f38d9c3"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_7ff2d11f6a83cb52386eaebe74"`);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE TABLE "temporary_media" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "mediaType" varchar NOT NULL, "tmdbId" integer NOT NULL, "tvdbId" integer, "imdbId" varchar, "status" integer NOT NULL DEFAULT (1), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "lastSeasonChange" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "status4k" integer NOT NULL DEFAULT (1), CONSTRAINT "UQ_41a289eb1fa489c1bc6f38d9c3c" UNIQUE ("tvdbId"))`
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`INSERT INTO "temporary_media"("id", "mediaType", "tmdbId", "tvdbId", "imdbId", "status", "createdAt", "updatedAt", "lastSeasonChange") SELECT "id", "mediaType", "tmdbId", "tvdbId", "imdbId", "status", "createdAt", "updatedAt", "lastSeasonChange" FROM "media"`
|
||||||
|
);
|
||||||
|
await queryRunner.query(`DROP TABLE "media"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "temporary_media" RENAME TO "media"`);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_7157aad07c73f6a6ae3bbd5ef5" ON "media" ("tmdbId") `
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_41a289eb1fa489c1bc6f38d9c3" ON "media" ("tvdbId") `
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_7ff2d11f6a83cb52386eaebe74" ON "media" ("imdbId") `
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE TABLE "temporary_media_request" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "status" integer NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "type" varchar NOT NULL, "mediaId" integer, "requestedById" integer, "modifiedById" integer, "is4k" boolean NOT NULL DEFAULT (0), "serverId" integer, "profileId" integer, "rootFolder" varchar, CONSTRAINT "FK_f4fc4efa14c3ba2b29c4525fa15" FOREIGN KEY ("modifiedById") REFERENCES "user" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_6997bee94720f1ecb7f31137095" FOREIGN KEY ("requestedById") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_a1aa713f41c99e9d10c48da75a0" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`INSERT INTO "temporary_media_request"("id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById") SELECT "id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById" FROM "media_request"`
|
||||||
|
);
|
||||||
|
await queryRunner.query(`DROP TABLE "media_request"`);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "temporary_media_request" RENAME TO "media_request"`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "media_request" RENAME TO "temporary_media_request"`
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE TABLE "media_request" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "status" integer NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "type" varchar NOT NULL, "mediaId" integer, "requestedById" integer, "modifiedById" integer, CONSTRAINT "FK_f4fc4efa14c3ba2b29c4525fa15" FOREIGN KEY ("modifiedById") REFERENCES "user" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_6997bee94720f1ecb7f31137095" FOREIGN KEY ("requestedById") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_a1aa713f41c99e9d10c48da75a0" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`INSERT INTO "media_request"("id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById") SELECT "id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById" FROM "temporary_media_request"`
|
||||||
|
);
|
||||||
|
await queryRunner.query(`DROP TABLE "temporary_media_request"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_7ff2d11f6a83cb52386eaebe74"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_41a289eb1fa489c1bc6f38d9c3"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_7157aad07c73f6a6ae3bbd5ef5"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "media" RENAME TO "temporary_media"`);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE TABLE "media" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "mediaType" varchar NOT NULL, "tmdbId" integer NOT NULL, "tvdbId" integer, "imdbId" varchar, "status" integer NOT NULL DEFAULT (1), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "lastSeasonChange" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), CONSTRAINT "UQ_41a289eb1fa489c1bc6f38d9c3c" UNIQUE ("tvdbId"))`
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`INSERT INTO "media"("id", "mediaType", "tmdbId", "tvdbId", "imdbId", "status", "createdAt", "updatedAt", "lastSeasonChange") SELECT "id", "mediaType", "tmdbId", "tvdbId", "imdbId", "status", "createdAt", "updatedAt", "lastSeasonChange" FROM "temporary_media"`
|
||||||
|
);
|
||||||
|
await queryRunner.query(`DROP TABLE "temporary_media"`);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_7ff2d11f6a83cb52386eaebe74" ON "media" ("imdbId") `
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_41a289eb1fa489c1bc6f38d9c3" ON "media" ("tvdbId") `
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_7157aad07c73f6a6ae3bbd5ef5" ON "media" ("tmdbId") `
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "season" RENAME TO "temporary_season"`
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE TABLE "season" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "seasonNumber" integer NOT NULL, "status" integer NOT NULL DEFAULT (1), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "mediaId" integer, CONSTRAINT "FK_087099b39600be695591da9a49c" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`INSERT INTO "season"("id", "seasonNumber", "status", "createdAt", "updatedAt", "mediaId") SELECT "id", "seasonNumber", "status", "createdAt", "updatedAt", "mediaId" FROM "temporary_season"`
|
||||||
|
);
|
||||||
|
await queryRunner.query(`DROP TABLE "temporary_season"`);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,582 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import React, { useContext, useState } from 'react';
|
||||||
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
import {
|
||||||
|
MediaRequestStatus,
|
||||||
|
MediaStatus,
|
||||||
|
} from '../../../../server/constants/media';
|
||||||
|
import Media from '../../../../server/entity/Media';
|
||||||
|
import { MediaRequest } from '../../../../server/entity/MediaRequest';
|
||||||
|
import { SettingsContext } from '../../../context/SettingsContext';
|
||||||
|
import { Permission, useUser } from '../../../hooks/useUser';
|
||||||
|
import ButtonWithDropdown from '../../Common/ButtonWithDropdown';
|
||||||
|
import RequestModal from '../../RequestModal';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
viewrequest: 'View Request',
|
||||||
|
viewrequest4k: 'View 4K Request',
|
||||||
|
request: 'Request',
|
||||||
|
request4k: 'Request 4K',
|
||||||
|
requestmore: 'Request More',
|
||||||
|
requestmore4k: 'Request More 4K',
|
||||||
|
approverequest: 'Approve Request',
|
||||||
|
approverequest4k: 'Approve 4K Request',
|
||||||
|
declinerequest: 'Decline Request',
|
||||||
|
declinerequest4k: 'Decline 4K Request',
|
||||||
|
approverequests:
|
||||||
|
'Approve {requestCount} {requestCount, plural, one {Request} other {Requests}}',
|
||||||
|
declinerequests:
|
||||||
|
'Decline {requestCount} {requestCount, plural, one {Request} other {Requests}}',
|
||||||
|
approve4krequests:
|
||||||
|
'Approve {requestCount} 4K {requestCount, plural, one {Request} other {Requests}}',
|
||||||
|
decline4krequests:
|
||||||
|
'Decline {requestCount} 4K {requestCount, plural, one {Request} other {Requests}}',
|
||||||
|
});
|
||||||
|
|
||||||
|
interface ButtonOption {
|
||||||
|
id: string;
|
||||||
|
text: string;
|
||||||
|
action: () => void;
|
||||||
|
svg?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RequestButtonProps {
|
||||||
|
mediaType: 'movie' | 'tv';
|
||||||
|
onUpdate: () => void;
|
||||||
|
tmdbId: number;
|
||||||
|
media?: Media;
|
||||||
|
isShowComplete?: boolean;
|
||||||
|
is4kShowComplete?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RequestButton: React.FC<RequestButtonProps> = ({
|
||||||
|
tmdbId,
|
||||||
|
onUpdate,
|
||||||
|
media,
|
||||||
|
mediaType,
|
||||||
|
isShowComplete = false,
|
||||||
|
is4kShowComplete = false,
|
||||||
|
}) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const settings = useContext(SettingsContext);
|
||||||
|
const { hasPermission } = useUser();
|
||||||
|
const [showRequestModal, setShowRequestModal] = useState(false);
|
||||||
|
const [showRequest4kModal, setShowRequest4kModal] = useState(false);
|
||||||
|
|
||||||
|
const activeRequest = media?.requests.find(
|
||||||
|
(request) => request.status === MediaRequestStatus.PENDING && !request.is4k
|
||||||
|
);
|
||||||
|
const active4kRequest = media?.requests.find(
|
||||||
|
(request) => request.status === MediaRequestStatus.PENDING && request.is4k
|
||||||
|
);
|
||||||
|
|
||||||
|
// All pending
|
||||||
|
const activeRequests = media?.requests.filter(
|
||||||
|
(request) => request.status === MediaRequestStatus.PENDING && !request.is4k
|
||||||
|
);
|
||||||
|
|
||||||
|
const active4kRequests = media?.requests.filter(
|
||||||
|
(request) => request.status === MediaRequestStatus.PENDING && request.is4k
|
||||||
|
);
|
||||||
|
|
||||||
|
const modifyRequest = async (
|
||||||
|
request: MediaRequest,
|
||||||
|
type: 'approve' | 'decline'
|
||||||
|
) => {
|
||||||
|
const response = await axios.get(`/api/v1/request/${request.id}/${type}`);
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const modifyRequests = async (
|
||||||
|
requests: MediaRequest[],
|
||||||
|
type: 'approve' | 'decline'
|
||||||
|
): Promise<void> => {
|
||||||
|
if (!requests) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
requests.map(async (request) => {
|
||||||
|
return axios.get(`/api/v1/request/${request.id}/${type}`);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
onUpdate();
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttons: ButtonOption[] = [];
|
||||||
|
if (
|
||||||
|
(!media || media.status === MediaStatus.UNKNOWN) &&
|
||||||
|
hasPermission(Permission.REQUEST)
|
||||||
|
) {
|
||||||
|
buttons.push({
|
||||||
|
id: 'request',
|
||||||
|
text: intl.formatMessage(messages.request),
|
||||||
|
action: () => {
|
||||||
|
setShowRequestModal(true);
|
||||||
|
},
|
||||||
|
svg: (
|
||||||
|
<svg
|
||||||
|
className="w-4 mr-1"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
hasPermission(Permission.REQUEST) &&
|
||||||
|
mediaType === 'tv' &&
|
||||||
|
media &&
|
||||||
|
media.status !== MediaStatus.AVAILABLE &&
|
||||||
|
media.status !== MediaStatus.UNKNOWN &&
|
||||||
|
!isShowComplete
|
||||||
|
) {
|
||||||
|
buttons.push({
|
||||||
|
id: 'request-more',
|
||||||
|
text: intl.formatMessage(messages.requestmore),
|
||||||
|
action: () => {
|
||||||
|
setShowRequestModal(true);
|
||||||
|
},
|
||||||
|
svg: (
|
||||||
|
<svg
|
||||||
|
className="w-4 mr-1"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(!media || media.status4k === MediaStatus.UNKNOWN) &&
|
||||||
|
(hasPermission(Permission.REQUEST_4K) ||
|
||||||
|
(mediaType === 'movie' && hasPermission(Permission.REQUEST_4K_MOVIE)) ||
|
||||||
|
(mediaType === 'tv' && hasPermission(Permission.REQUEST_4K_TV))) &&
|
||||||
|
((settings.currentSettings.movie4kEnabled && mediaType === 'movie') ||
|
||||||
|
(settings.currentSettings.series4kEnabled && mediaType === 'tv'))
|
||||||
|
) {
|
||||||
|
buttons.push({
|
||||||
|
id: 'request4k',
|
||||||
|
text: intl.formatMessage(messages.request4k),
|
||||||
|
action: () => {
|
||||||
|
setShowRequest4kModal(true);
|
||||||
|
},
|
||||||
|
svg: (
|
||||||
|
<svg
|
||||||
|
className="w-4 mr-1"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
mediaType === 'tv' &&
|
||||||
|
(hasPermission(Permission.REQUEST_4K) ||
|
||||||
|
(mediaType === 'tv' && hasPermission(Permission.REQUEST_4K_TV))) &&
|
||||||
|
media &&
|
||||||
|
media.status4k !== MediaStatus.AVAILABLE &&
|
||||||
|
media.status4k !== MediaStatus.UNKNOWN &&
|
||||||
|
!is4kShowComplete &&
|
||||||
|
settings.currentSettings.series4kEnabled
|
||||||
|
) {
|
||||||
|
buttons.push({
|
||||||
|
id: 'request-more-4k',
|
||||||
|
text: intl.formatMessage(messages.requestmore4k),
|
||||||
|
action: () => {
|
||||||
|
setShowRequest4kModal(true);
|
||||||
|
},
|
||||||
|
svg: (
|
||||||
|
<svg
|
||||||
|
className="w-4 mr-1"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
activeRequest &&
|
||||||
|
mediaType === 'movie' &&
|
||||||
|
hasPermission(Permission.REQUEST)
|
||||||
|
) {
|
||||||
|
buttons.push({
|
||||||
|
id: 'active-request',
|
||||||
|
text: intl.formatMessage(messages.viewrequest),
|
||||||
|
action: () => setShowRequestModal(true),
|
||||||
|
svg: (
|
||||||
|
<svg
|
||||||
|
className="w-4 mr-1"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
active4kRequest &&
|
||||||
|
mediaType === 'movie' &&
|
||||||
|
(hasPermission(Permission.REQUEST_4K) ||
|
||||||
|
hasPermission(Permission.REQUEST_4K_MOVIE))
|
||||||
|
) {
|
||||||
|
buttons.push({
|
||||||
|
id: 'active-4k-request',
|
||||||
|
text: intl.formatMessage(messages.viewrequest4k),
|
||||||
|
action: () => setShowRequest4kModal(true),
|
||||||
|
svg: (
|
||||||
|
<svg
|
||||||
|
className="w-4 mr-1"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
activeRequest &&
|
||||||
|
hasPermission(Permission.MANAGE_REQUESTS) &&
|
||||||
|
mediaType === 'movie'
|
||||||
|
) {
|
||||||
|
buttons.push(
|
||||||
|
{
|
||||||
|
id: 'approve-request',
|
||||||
|
text: intl.formatMessage(messages.approverequest),
|
||||||
|
action: () => {
|
||||||
|
modifyRequest(activeRequest, 'approve');
|
||||||
|
},
|
||||||
|
svg: (
|
||||||
|
<svg
|
||||||
|
className="w-4 mr-1"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'decline-request',
|
||||||
|
text: intl.formatMessage(messages.declinerequest),
|
||||||
|
action: () => {
|
||||||
|
modifyRequest(activeRequest, 'decline');
|
||||||
|
},
|
||||||
|
svg: (
|
||||||
|
<svg
|
||||||
|
className="w-4 mr-1"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
activeRequests &&
|
||||||
|
activeRequests.length > 0 &&
|
||||||
|
hasPermission(Permission.MANAGE_REQUESTS) &&
|
||||||
|
mediaType === 'tv'
|
||||||
|
) {
|
||||||
|
buttons.push(
|
||||||
|
{
|
||||||
|
id: 'approve-request-batch',
|
||||||
|
text: intl.formatMessage(messages.approverequests, {
|
||||||
|
requestCount: activeRequests.length,
|
||||||
|
}),
|
||||||
|
action: () => {
|
||||||
|
modifyRequests(activeRequests, 'approve');
|
||||||
|
},
|
||||||
|
svg: (
|
||||||
|
<svg
|
||||||
|
className="w-4 mr-1"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'decline-request-batch',
|
||||||
|
text: intl.formatMessage(messages.declinerequests, {
|
||||||
|
requestCount: activeRequests.length,
|
||||||
|
}),
|
||||||
|
action: () => {
|
||||||
|
modifyRequests(activeRequests, 'decline');
|
||||||
|
},
|
||||||
|
svg: (
|
||||||
|
<svg
|
||||||
|
className="w-4 mr-1"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
active4kRequest &&
|
||||||
|
hasPermission(Permission.MANAGE_REQUESTS) &&
|
||||||
|
mediaType === 'movie'
|
||||||
|
) {
|
||||||
|
buttons.push(
|
||||||
|
{
|
||||||
|
id: 'approve-4k-request',
|
||||||
|
text: intl.formatMessage(messages.approverequest4k),
|
||||||
|
action: () => {
|
||||||
|
modifyRequest(active4kRequest, 'approve');
|
||||||
|
},
|
||||||
|
svg: (
|
||||||
|
<svg
|
||||||
|
className="w-4 mr-1"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'decline-4k-request',
|
||||||
|
text: intl.formatMessage(messages.declinerequest4k),
|
||||||
|
action: () => {
|
||||||
|
modifyRequest(active4kRequest, 'decline');
|
||||||
|
},
|
||||||
|
svg: (
|
||||||
|
<svg
|
||||||
|
className="w-4 mr-1"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
active4kRequests &&
|
||||||
|
active4kRequests.length > 0 &&
|
||||||
|
hasPermission(Permission.MANAGE_REQUESTS) &&
|
||||||
|
mediaType === 'tv'
|
||||||
|
) {
|
||||||
|
buttons.push(
|
||||||
|
{
|
||||||
|
id: 'approve-request-batch',
|
||||||
|
text: intl.formatMessage(messages.approve4krequests, {
|
||||||
|
requestCount: active4kRequests.length,
|
||||||
|
}),
|
||||||
|
action: () => {
|
||||||
|
modifyRequests(active4kRequests, 'approve');
|
||||||
|
},
|
||||||
|
svg: (
|
||||||
|
<svg
|
||||||
|
className="w-4 mr-1"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'decline-request-batch',
|
||||||
|
text: intl.formatMessage(messages.decline4krequests, {
|
||||||
|
requestCount: active4kRequests.length,
|
||||||
|
}),
|
||||||
|
action: () => {
|
||||||
|
modifyRequests(active4kRequests, 'decline');
|
||||||
|
},
|
||||||
|
svg: (
|
||||||
|
<svg
|
||||||
|
className="w-4 mr-1"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [buttonOne, ...others] = buttons;
|
||||||
|
|
||||||
|
if (!buttonOne) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<RequestModal
|
||||||
|
tmdbId={tmdbId}
|
||||||
|
show={showRequestModal}
|
||||||
|
type={mediaType}
|
||||||
|
onComplete={() => {
|
||||||
|
onUpdate();
|
||||||
|
setShowRequestModal(false);
|
||||||
|
}}
|
||||||
|
onCancel={() => setShowRequestModal(false)}
|
||||||
|
/>
|
||||||
|
<RequestModal
|
||||||
|
tmdbId={tmdbId}
|
||||||
|
show={showRequest4kModal}
|
||||||
|
type={mediaType}
|
||||||
|
is4k
|
||||||
|
onComplete={() => {
|
||||||
|
onUpdate();
|
||||||
|
setShowRequest4kModal(false);
|
||||||
|
}}
|
||||||
|
onCancel={() => setShowRequest4kModal(false)}
|
||||||
|
/>
|
||||||
|
<ButtonWithDropdown
|
||||||
|
text={
|
||||||
|
<>
|
||||||
|
{buttonOne.svg ?? null}
|
||||||
|
{buttonOne.text}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
onClick={buttonOne.action}
|
||||||
|
className="ml-2"
|
||||||
|
>
|
||||||
|
{others && others.length > 0
|
||||||
|
? others.map((button) => (
|
||||||
|
<ButtonWithDropdown.Item
|
||||||
|
onClick={button.action}
|
||||||
|
key={`request-option-${button.id}`}
|
||||||
|
>
|
||||||
|
{button.svg}
|
||||||
|
{button.text}
|
||||||
|
</ButtonWithDropdown.Item>
|
||||||
|
))
|
||||||
|
: null}
|
||||||
|
{/* {hasPermission(Permission.MANAGE_REQUESTS) && (
|
||||||
|
<>
|
||||||
|
<ButtonWithDropdown.Item onClick={() => modifyRequest('approve')}>
|
||||||
|
<svg
|
||||||
|
className="w-4 mr-1"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{intl.formatMessage(messages.approve)}
|
||||||
|
</ButtonWithDropdown.Item>
|
||||||
|
</>
|
||||||
|
)} */}
|
||||||
|
</ButtonWithDropdown>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RequestButton;
|
@ -0,0 +1,39 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { PublicSettingsResponse } from '../../server/interfaces/api/settingsInterfaces';
|
||||||
|
import useSWR from 'swr';
|
||||||
|
|
||||||
|
interface SettingsContextProps {
|
||||||
|
currentSettings: PublicSettingsResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultSettings = {
|
||||||
|
initialized: false,
|
||||||
|
movie4kEnabled: false,
|
||||||
|
series4kEnabled: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SettingsContext = React.createContext<SettingsContextProps>({
|
||||||
|
currentSettings: defaultSettings,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SettingsProvider: React.FC<SettingsContextProps> = ({
|
||||||
|
children,
|
||||||
|
currentSettings,
|
||||||
|
}) => {
|
||||||
|
const { data, error } = useSWR<PublicSettingsResponse>(
|
||||||
|
'/api/v1/settings/public',
|
||||||
|
{ initialData: currentSettings }
|
||||||
|
);
|
||||||
|
|
||||||
|
let newSettings = defaultSettings;
|
||||||
|
|
||||||
|
if (data && !error) {
|
||||||
|
newSettings = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsContext.Provider value={{ currentSettings: newSettings }}>
|
||||||
|
{children}
|
||||||
|
</SettingsContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
Loading…
Reference in new issue