Compare commits
2 Commits
master
...
feat/basep
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
abef384196 | ||
|
|
7de426f483 |
15
public/sw.js
15
public/sw.js
@@ -3,10 +3,13 @@
|
|||||||
// previously cached resources to be updated from the network.
|
// previously cached resources to be updated from the network.
|
||||||
// This variable is intentionally declared and unused.
|
// This variable is intentionally declared and unused.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const OFFLINE_VERSION = 3;
|
const OFFLINE_VERSION = 4;
|
||||||
const CACHE_NAME = "offline";
|
const CACHE_NAME = "offline";
|
||||||
|
|
||||||
|
// use scope path to determine if we are running in basePath
|
||||||
|
const BASE_PATH = (new URL(self.registration.scope)).pathname.replace(/\/$/, '')
|
||||||
// Customize this with a different URL if needed.
|
// Customize this with a different URL if needed.
|
||||||
const OFFLINE_URL = "/offline.html";
|
const OFFLINE_URL = BASE_PATH + "/offline.html";
|
||||||
|
|
||||||
self.addEventListener("install", (event) => {
|
self.addEventListener("install", (event) => {
|
||||||
event.waitUntil(
|
event.waitUntil(
|
||||||
@@ -120,16 +123,16 @@ self.addEventListener('notificationclick', (event) => {
|
|||||||
event.notification.close();
|
event.notification.close();
|
||||||
|
|
||||||
if (event.action === 'approve') {
|
if (event.action === 'approve') {
|
||||||
fetch(`/api/v1/request/${notificationData.requestId}/approve`, {
|
fetch(`${BASE_PATH}/api/v1/request/${notificationData.requestId}/approve`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
});
|
});
|
||||||
} else if (event.action === 'decline') {
|
} else if (event.action === 'decline') {
|
||||||
fetch(`/api/v1/request/${notificationData.requestId}/decline`, {
|
fetch(`${BASE_PATH}/api/v1/request/${notificationData.requestId}/decline`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notificationData.actionUrl) {
|
if (notificationData.actionUrl) {
|
||||||
clients.openWindow(notificationData.actionUrl);
|
clients.openWindow(BASE_PATH + notificationData.actionUrl);
|
||||||
}
|
}
|
||||||
}, false);
|
}, false);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { getClientIp } from '@supercharge/request-ip';
|
|||||||
import { TypeormStore } from 'connect-typeorm/out';
|
import { TypeormStore } from 'connect-typeorm/out';
|
||||||
import cookieParser from 'cookie-parser';
|
import cookieParser from 'cookie-parser';
|
||||||
import csurf from 'csurf';
|
import csurf from 'csurf';
|
||||||
import express, { NextFunction, Request, Response } from 'express';
|
import express, { NextFunction, Request, Response, Router } from 'express';
|
||||||
import * as OpenApiValidator from 'express-openapi-validator';
|
import * as OpenApiValidator from 'express-openapi-validator';
|
||||||
import session, { Store } from 'express-session';
|
import session, { Store } from 'express-session';
|
||||||
import next from 'next';
|
import next from 'next';
|
||||||
@@ -30,17 +30,13 @@ import routes from './routes';
|
|||||||
import { getAppVersion } from './utils/appVersion';
|
import { getAppVersion } from './utils/appVersion';
|
||||||
|
|
||||||
const API_SPEC_PATH = path.join(__dirname, '../overseerr-api.yml');
|
const API_SPEC_PATH = path.join(__dirname, '../overseerr-api.yml');
|
||||||
|
const NEXT_CONFIG_PATH = path.join(__dirname, '../next.config.js');
|
||||||
|
|
||||||
logger.info(`Starting Overseerr version ${getAppVersion()}`);
|
logger.info(`Starting Overseerr version ${getAppVersion()}`);
|
||||||
const dev = process.env.NODE_ENV !== 'production';
|
const dev = process.env.NODE_ENV !== 'production';
|
||||||
const app = next({ dev });
|
|
||||||
const handle = app.getRequestHandler();
|
|
||||||
|
|
||||||
app
|
|
||||||
.prepare()
|
|
||||||
.then(async () => {
|
|
||||||
const dbConnection = await createConnection();
|
|
||||||
|
|
||||||
|
createConnection()
|
||||||
|
.then(async (dbConnection) => {
|
||||||
// Run migrations in production
|
// Run migrations in production
|
||||||
if (process.env.NODE_ENV === 'production') {
|
if (process.env.NODE_ENV === 'production') {
|
||||||
await dbConnection.query('PRAGMA foreign_keys=OFF');
|
await dbConnection.query('PRAGMA foreign_keys=OFF');
|
||||||
@@ -51,6 +47,34 @@ app
|
|||||||
// Load Settings
|
// Load Settings
|
||||||
const settings = getSettings().load();
|
const settings = getSettings().load();
|
||||||
|
|
||||||
|
const { default: nextConfig } = await import(NEXT_CONFIG_PATH);
|
||||||
|
|
||||||
|
let basePath = settings.main.basePath;
|
||||||
|
if (basePath) {
|
||||||
|
// Ensure base path is free of invalid characters
|
||||||
|
basePath = encodeURI(basePath);
|
||||||
|
logger.debug(`Serving from base path ${basePath}`);
|
||||||
|
|
||||||
|
nextConfig.rewrites = async () => ({
|
||||||
|
beforeFiles: [
|
||||||
|
/* Strip off base path for serving pages */
|
||||||
|
{
|
||||||
|
source: basePath + '/:path*',
|
||||||
|
destination: '/:path*',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
nextConfig.publicRuntimeConfig = { basePath };
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = next({ dev, conf: nextConfig });
|
||||||
|
const handle = app.getRequestHandler();
|
||||||
|
|
||||||
|
if (basePath) {
|
||||||
|
app.setAssetPrefix(basePath + '/');
|
||||||
|
}
|
||||||
|
await app.prepare();
|
||||||
|
|
||||||
// Migrate library types
|
// Migrate library types
|
||||||
if (
|
if (
|
||||||
settings.plex.libraries.length > 1 &&
|
settings.plex.libraries.length > 1 &&
|
||||||
@@ -131,7 +155,9 @@ app
|
|||||||
|
|
||||||
// Set up sessions
|
// Set up sessions
|
||||||
const sessionRespository = getRepository(Session);
|
const sessionRespository = getRepository(Session);
|
||||||
server.use(
|
const apiRoutes = Router();
|
||||||
|
server.use(basePath || '/', apiRoutes);
|
||||||
|
apiRoutes.use(
|
||||||
'/api',
|
'/api',
|
||||||
session({
|
session({
|
||||||
secret: settings.clientId,
|
secret: settings.clientId,
|
||||||
@@ -150,8 +176,8 @@ app
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
const apiDocs = YAML.load(API_SPEC_PATH);
|
const apiDocs = YAML.load(API_SPEC_PATH);
|
||||||
server.use('/api-docs', swaggerUi.serve, swaggerUi.setup(apiDocs));
|
apiRoutes.use('/api-docs', swaggerUi.serve, swaggerUi.setup(apiDocs));
|
||||||
server.use(
|
apiRoutes.use(
|
||||||
OpenApiValidator.middleware({
|
OpenApiValidator.middleware({
|
||||||
apiSpec: API_SPEC_PATH,
|
apiSpec: API_SPEC_PATH,
|
||||||
validateRequests: true,
|
validateRequests: true,
|
||||||
@@ -162,15 +188,15 @@ app
|
|||||||
* OpenAPI validator. Otherwise, they are treated as objects instead of strings
|
* OpenAPI validator. Otherwise, they are treated as objects instead of strings
|
||||||
* and response validation will fail
|
* and response validation will fail
|
||||||
*/
|
*/
|
||||||
server.use((_req, res, next) => {
|
apiRoutes.use((_req, res, next) => {
|
||||||
const original = res.json;
|
const original = res.json;
|
||||||
res.json = function jsonp(json) {
|
res.json = function jsonp(json) {
|
||||||
return original.call(this, JSON.parse(JSON.stringify(json)));
|
return original.call(this, JSON.parse(JSON.stringify(json)));
|
||||||
};
|
};
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
server.use('/api/v1', routes);
|
apiRoutes.use('/api/v1', routes);
|
||||||
server.get('*', (req, res) => handle(req, res));
|
apiRoutes.get('*', (req, res) => handle(req, res));
|
||||||
server.use(
|
server.use(
|
||||||
(
|
(
|
||||||
err: { status: number; message: string; errors: string[] },
|
err: { status: number; message: string; errors: string[] },
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ export interface MainSettings {
|
|||||||
trustProxy: boolean;
|
trustProxy: boolean;
|
||||||
partialRequestsEnabled: boolean;
|
partialRequestsEnabled: boolean;
|
||||||
locale: string;
|
locale: string;
|
||||||
|
basePath: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PublicSettings {
|
interface PublicSettings {
|
||||||
@@ -271,6 +272,7 @@ class Settings {
|
|||||||
trustProxy: false,
|
trustProxy: false,
|
||||||
partialRequestsEnabled: true,
|
partialRequestsEnabled: true,
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
|
basePath: '',
|
||||||
},
|
},
|
||||||
plex: {
|
plex: {
|
||||||
name: '',
|
name: '',
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { DownloadIcon } from '@heroicons/react/outline';
|
import { DownloadIcon } from '@heroicons/react/outline';
|
||||||
import { uniq } from 'lodash';
|
import { uniq } from 'lodash';
|
||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
@@ -15,6 +14,7 @@ import ButtonWithDropdown from '../Common/ButtonWithDropdown';
|
|||||||
import CachedImage from '../Common/CachedImage';
|
import CachedImage from '../Common/CachedImage';
|
||||||
import LoadingSpinner from '../Common/LoadingSpinner';
|
import LoadingSpinner from '../Common/LoadingSpinner';
|
||||||
import PageTitle from '../Common/PageTitle';
|
import PageTitle from '../Common/PageTitle';
|
||||||
|
import Link from '../Link';
|
||||||
import RequestModal from '../RequestModal';
|
import RequestModal from '../RequestModal';
|
||||||
import Slider from '../Slider';
|
import Slider from '../Slider';
|
||||||
import StatusBadge from '../StatusBadge';
|
import StatusBadge from '../StatusBadge';
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import Image, { ImageProps } from 'next/image';
|
import Image, { ImageProps } from 'next/image';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import useSettings from '../../../hooks/useSettings';
|
import useSettings from '../../../hooks/useSettings';
|
||||||
|
import addBasePath from '../../../utils/addBasePath';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The CachedImage component should be used wherever
|
* The CachedImage component should be used wherever
|
||||||
@@ -12,6 +13,10 @@ import useSettings from '../../../hooks/useSettings';
|
|||||||
const CachedImage: React.FC<ImageProps> = (props) => {
|
const CachedImage: React.FC<ImageProps> = (props) => {
|
||||||
const { currentSettings } = useSettings();
|
const { currentSettings } = useSettings();
|
||||||
|
|
||||||
|
/* Check for internal urls and add base path */
|
||||||
|
if (typeof props.src === 'string' && !props.src.includes('://')) {
|
||||||
|
props = { ...props, src: addBasePath(props.src) };
|
||||||
|
}
|
||||||
return <Image unoptimized={!currentSettings.cacheImages} {...props} />;
|
return <Image unoptimized={!currentSettings.cacheImages} {...props} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { hasPermission, Permission } from '../../../../server/lib/permissions';
|
import { hasPermission, Permission } from '../../../../server/lib/permissions';
|
||||||
|
import { useRouter } from '../../../hooks/useRouter';
|
||||||
import { useUser } from '../../../hooks/useUser';
|
import { useUser } from '../../../hooks/useUser';
|
||||||
|
import Link from '../../Link';
|
||||||
|
|
||||||
export interface SettingsRoute {
|
export interface SettingsRoute {
|
||||||
text: string;
|
text: string;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { MovieResult } from '../../../../server/models/Search';
|
import type { MovieResult } from '../../../../server/models/Search';
|
||||||
|
import { useRouter } from '../../../hooks/useRouter';
|
||||||
import ListView from '../../Common/ListView';
|
import ListView from '../../Common/ListView';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import Header from '../../Common/Header';
|
import Header from '../../Common/Header';
|
||||||
import PageTitle from '../../Common/PageTitle';
|
import PageTitle from '../../Common/PageTitle';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import globalMessages from '../../../i18n/globalMessages';
|
import globalMessages from '../../../i18n/globalMessages';
|
||||||
import useDiscover from '../../../hooks/useDiscover';
|
import useDiscover from '../../../hooks/useDiscover';
|
||||||
import Error from '../../../pages/_error';
|
import Error from '../../../pages/_error';
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
import { useRouter } from 'next/router';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { TvResult } from '../../../../server/models/Search';
|
import type { TvResult } from '../../../../server/models/Search';
|
||||||
import ListView from '../../Common/ListView';
|
import ListView from '../../Common/ListView';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import Header from '../../Common/Header';
|
import Header from '../../Common/Header';
|
||||||
import PageTitle from '../../Common/PageTitle';
|
import PageTitle from '../../Common/PageTitle';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import globalMessages from '../../../i18n/globalMessages';
|
import globalMessages from '../../../i18n/globalMessages';
|
||||||
import useDiscover from '../../../hooks/useDiscover';
|
import useDiscover from '../../../hooks/useDiscover';
|
||||||
import Error from '../../../pages/_error';
|
import Error from '../../../pages/_error';
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { MovieResult } from '../../../../server/models/Search';
|
import type { MovieResult } from '../../../../server/models/Search';
|
||||||
|
import { useRouter } from '../../../hooks/useRouter';
|
||||||
import ListView from '../../Common/ListView';
|
import ListView from '../../Common/ListView';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import Header from '../../Common/Header';
|
import Header from '../../Common/Header';
|
||||||
import PageTitle from '../../Common/PageTitle';
|
import PageTitle from '../../Common/PageTitle';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import globalMessages from '../../../i18n/globalMessages';
|
import globalMessages from '../../../i18n/globalMessages';
|
||||||
import useDiscover from '../../../hooks/useDiscover';
|
import useDiscover from '../../../hooks/useDiscover';
|
||||||
import Error from '../../../pages/_error';
|
import Error from '../../../pages/_error';
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import ListView from '../../Common/ListView';
|
|||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import Header from '../../Common/Header';
|
import Header from '../../Common/Header';
|
||||||
import PageTitle from '../../Common/PageTitle';
|
import PageTitle from '../../Common/PageTitle';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import globalMessages from '../../../i18n/globalMessages';
|
import globalMessages from '../../../i18n/globalMessages';
|
||||||
import useDiscover from '../../../hooks/useDiscover';
|
import useDiscover from '../../../hooks/useDiscover';
|
||||||
import Error from '../../../pages/_error';
|
import Error from '../../../pages/_error';
|
||||||
|
import { useRouter } from '../../../hooks/useRouter';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
genreSeries: '{genre} Series',
|
genreSeries: '{genre} Series',
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { TvResult } from '../../../../server/models/Search';
|
import type { TvResult } from '../../../../server/models/Search';
|
||||||
|
import { useRouter } from '../../../hooks/useRouter';
|
||||||
import ListView from '../../Common/ListView';
|
import ListView from '../../Common/ListView';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import Header from '../../Common/Header';
|
import Header from '../../Common/Header';
|
||||||
import PageTitle from '../../Common/PageTitle';
|
import PageTitle from '../../Common/PageTitle';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import globalMessages from '../../../i18n/globalMessages';
|
import globalMessages from '../../../i18n/globalMessages';
|
||||||
import useDiscover from '../../../hooks/useDiscover';
|
import useDiscover from '../../../hooks/useDiscover';
|
||||||
import Error from '../../../pages/_error';
|
import Error from '../../../pages/_error';
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
||||||
import Link from 'next/link';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { GenreSliderItem } from '../../../../server/interfaces/api/discoverInterfaces';
|
import { GenreSliderItem } from '../../../../server/interfaces/api/discoverInterfaces';
|
||||||
import GenreCard from '../../GenreCard';
|
import GenreCard from '../../GenreCard';
|
||||||
|
import Link from '../../Link';
|
||||||
import Slider from '../../Slider';
|
import Slider from '../../Slider';
|
||||||
import { genreColorMap } from '../constants';
|
import { genreColorMap } from '../constants';
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
||||||
import Link from 'next/link';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { GenreSliderItem } from '../../../../server/interfaces/api/discoverInterfaces';
|
import { GenreSliderItem } from '../../../../server/interfaces/api/discoverInterfaces';
|
||||||
import GenreCard from '../../GenreCard';
|
import GenreCard from '../../GenreCard';
|
||||||
|
import Link from '../../Link';
|
||||||
import Slider from '../../Slider';
|
import Slider from '../../Slider';
|
||||||
import { genreColorMap } from '../constants';
|
import { genreColorMap } from '../constants';
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
||||||
import Link from 'next/link';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import type { MediaResultsResponse } from '../../../server/interfaces/api/mediaInterfaces';
|
import type { MediaResultsResponse } from '../../../server/interfaces/api/mediaInterfaces';
|
||||||
import type { RequestResultsResponse } from '../../../server/interfaces/api/requestInterfaces';
|
import type { RequestResultsResponse } from '../../../server/interfaces/api/requestInterfaces';
|
||||||
import PageTitle from '../Common/PageTitle';
|
import PageTitle from '../Common/PageTitle';
|
||||||
|
import Link from '../Link';
|
||||||
import MediaSlider from '../MediaSlider';
|
import MediaSlider from '../MediaSlider';
|
||||||
import RequestCard from '../RequestCard';
|
import RequestCard from '../RequestCard';
|
||||||
import Slider from '../Slider';
|
import Slider from '../Slider';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Link from 'next/link';
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { withProperties } from '../../utils/typeHelpers';
|
import { withProperties } from '../../utils/typeHelpers';
|
||||||
import CachedImage from '../Common/CachedImage';
|
import CachedImage from '../Common/CachedImage';
|
||||||
|
import Link from '../Link';
|
||||||
|
|
||||||
interface GenreCardProps {
|
interface GenreCardProps {
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import {
|
|||||||
EyeIcon,
|
EyeIcon,
|
||||||
UserIcon,
|
UserIcon,
|
||||||
} from '@heroicons/react/solid';
|
} from '@heroicons/react/solid';
|
||||||
import Link from 'next/link';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import type Issue from '../../../server/entity/Issue';
|
import type Issue from '../../../server/entity/Issue';
|
||||||
import globalMessages from '../../i18n/globalMessages';
|
import globalMessages from '../../i18n/globalMessages';
|
||||||
import Button from '../Common/Button';
|
import Button from '../Common/Button';
|
||||||
import { issueOptions } from '../IssueModal/constants';
|
import { issueOptions } from '../IssueModal/constants';
|
||||||
|
import Link from '../Link';
|
||||||
|
|
||||||
interface IssueBlockProps {
|
interface IssueBlockProps {
|
||||||
issue: Issue;
|
issue: Issue;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { ExclamationIcon } from '@heroicons/react/outline';
|
|||||||
import { DotsVerticalIcon } from '@heroicons/react/solid';
|
import { DotsVerticalIcon } from '@heroicons/react/solid';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Field, Form, Formik } from 'formik';
|
import { Field, Form, Formik } from 'formik';
|
||||||
import Link from 'next/link';
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
|
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
|
||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from 'react-markdown';
|
||||||
@@ -12,6 +11,7 @@ import type { default as IssueCommentType } from '../../../../server/entity/Issu
|
|||||||
import { Permission, useUser } from '../../../hooks/useUser';
|
import { Permission, useUser } from '../../../hooks/useUser';
|
||||||
import Button from '../../Common/Button';
|
import Button from '../../Common/Button';
|
||||||
import Modal from '../../Common/Modal';
|
import Modal from '../../Common/Modal';
|
||||||
|
import Link from '../../Link';
|
||||||
import Transition from '../../Transition';
|
import Transition from '../../Transition';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import {
|
|||||||
import { RefreshIcon } from '@heroicons/react/solid';
|
import { RefreshIcon } from '@heroicons/react/solid';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Field, Form, Formik } from 'formik';
|
import { Field, Form, Formik } from 'formik';
|
||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
|
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
@@ -20,6 +18,7 @@ import { MediaType } from '../../../server/constants/media';
|
|||||||
import type Issue from '../../../server/entity/Issue';
|
import type Issue from '../../../server/entity/Issue';
|
||||||
import type { MovieDetails } from '../../../server/models/Movie';
|
import type { MovieDetails } from '../../../server/models/Movie';
|
||||||
import type { TvDetails } from '../../../server/models/Tv';
|
import type { TvDetails } from '../../../server/models/Tv';
|
||||||
|
import { useRouter } from '../../hooks/useRouter';
|
||||||
import { Permission, useUser } from '../../hooks/useUser';
|
import { Permission, useUser } from '../../hooks/useUser';
|
||||||
import globalMessages from '../../i18n/globalMessages';
|
import globalMessages from '../../i18n/globalMessages';
|
||||||
import Error from '../../pages/_error';
|
import Error from '../../pages/_error';
|
||||||
@@ -30,6 +29,7 @@ import LoadingSpinner from '../Common/LoadingSpinner';
|
|||||||
import Modal from '../Common/Modal';
|
import Modal from '../Common/Modal';
|
||||||
import PageTitle from '../Common/PageTitle';
|
import PageTitle from '../Common/PageTitle';
|
||||||
import { issueOptions } from '../IssueModal/constants';
|
import { issueOptions } from '../IssueModal/constants';
|
||||||
|
import Link from '../Link';
|
||||||
import Transition from '../Transition';
|
import Transition from '../Transition';
|
||||||
import IssueComment from './IssueComment';
|
import IssueComment from './IssueComment';
|
||||||
import IssueDescription from './IssueDescription';
|
import IssueDescription from './IssueDescription';
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { EyeIcon } from '@heroicons/react/solid';
|
import { EyeIcon } from '@heroicons/react/solid';
|
||||||
import Link from 'next/link';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useInView } from 'react-intersection-observer';
|
import { useInView } from 'react-intersection-observer';
|
||||||
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
|
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
|
||||||
@@ -15,6 +14,7 @@ import Badge from '../../Common/Badge';
|
|||||||
import Button from '../../Common/Button';
|
import Button from '../../Common/Button';
|
||||||
import CachedImage from '../../Common/CachedImage';
|
import CachedImage from '../../Common/CachedImage';
|
||||||
import { issueOptions } from '../../IssueModal/constants';
|
import { issueOptions } from '../../IssueModal/constants';
|
||||||
|
import Link from '../../Link';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
openeduserdate: '{date} by {user}',
|
openeduserdate: '{date} by {user}',
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import {
|
|||||||
FilterIcon,
|
FilterIcon,
|
||||||
SortDescendingIcon,
|
SortDescendingIcon,
|
||||||
} from '@heroicons/react/solid';
|
} from '@heroicons/react/solid';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { IssueResultsResponse } from '../../../server/interfaces/api/issueInterfaces';
|
import { IssueResultsResponse } from '../../../server/interfaces/api/issueInterfaces';
|
||||||
import Button from '../../components/Common/Button';
|
import Button from '../../components/Common/Button';
|
||||||
|
import { useRouter } from '../../hooks/useRouter';
|
||||||
import { useUpdateQueryParams } from '../../hooks/useUpdateQueryParams';
|
import { useUpdateQueryParams } from '../../hooks/useUpdateQueryParams';
|
||||||
import globalMessages from '../../i18n/globalMessages';
|
import globalMessages from '../../i18n/globalMessages';
|
||||||
import Header from '../Common/Header';
|
import Header from '../Common/Header';
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { ExclamationIcon } from '@heroicons/react/outline';
|
|||||||
import { ArrowCircleRightIcon } from '@heroicons/react/solid';
|
import { ArrowCircleRightIcon } from '@heroicons/react/solid';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Field, Formik } from 'formik';
|
import { Field, Formik } from 'formik';
|
||||||
import Link from 'next/link';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
@@ -18,6 +17,7 @@ import { Permission, useUser } from '../../../hooks/useUser';
|
|||||||
import globalMessages from '../../../i18n/globalMessages';
|
import globalMessages from '../../../i18n/globalMessages';
|
||||||
import Button from '../../Common/Button';
|
import Button from '../../Common/Button';
|
||||||
import Modal from '../../Common/Modal';
|
import Modal from '../../Common/Modal';
|
||||||
|
import Link from '../../Link';
|
||||||
import { issueOptions } from '../constants';
|
import { issueOptions } from '../constants';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ import {
|
|||||||
UsersIcon,
|
UsersIcon,
|
||||||
XIcon,
|
XIcon,
|
||||||
} from '@heroicons/react/outline';
|
} from '@heroicons/react/outline';
|
||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React, { ReactNode, useRef } from 'react';
|
import React, { ReactNode, useRef } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useClickOutside from '../../../hooks/useClickOutside';
|
import useClickOutside from '../../../hooks/useClickOutside';
|
||||||
|
import { useRouter } from '../../../hooks/useRouter';
|
||||||
import { Permission, useUser } from '../../../hooks/useUser';
|
import { Permission, useUser } from '../../../hooks/useUser';
|
||||||
|
import addBasePath from '../../../utils/addBasePath';
|
||||||
|
import Link from '../../Link';
|
||||||
import Transition from '../../Transition';
|
import Transition from '../../Transition';
|
||||||
import VersionStatus from '../VersionStatus';
|
import VersionStatus from '../VersionStatus';
|
||||||
|
|
||||||
@@ -130,8 +131,8 @@ const Sidebar: React.FC<SidebarProps> = ({ open, setClosed }) => {
|
|||||||
>
|
>
|
||||||
<div className="flex items-center flex-shrink-0 px-2">
|
<div className="flex items-center flex-shrink-0 px-2">
|
||||||
<span className="px-4 text-xl text-gray-50">
|
<span className="px-4 text-xl text-gray-50">
|
||||||
<a href="/">
|
<a href={addBasePath('/')}>
|
||||||
<img src="/logo_full.svg" alt="Logo" />
|
<img src={addBasePath('/logo_full.svg')} alt="Logo" />
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -199,8 +200,8 @@ const Sidebar: React.FC<SidebarProps> = ({ open, setClosed }) => {
|
|||||||
<div className="flex flex-col flex-1 pt-8 pb-4 overflow-y-auto">
|
<div className="flex flex-col flex-1 pt-8 pb-4 overflow-y-auto">
|
||||||
<div className="flex items-center flex-shrink-0">
|
<div className="flex items-center flex-shrink-0">
|
||||||
<span className="px-4 text-2xl text-gray-50">
|
<span className="px-4 text-2xl text-gray-50">
|
||||||
<a href="/">
|
<a href={addBasePath('/')}>
|
||||||
<img src="/logo_full.svg" alt="Logo" />
|
<img src={addBasePath('/logo_full.svg')} alt="Logo" />
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { LogoutIcon } from '@heroicons/react/outline';
|
import { LogoutIcon } from '@heroicons/react/outline';
|
||||||
import { CogIcon, UserIcon } from '@heroicons/react/solid';
|
import { CogIcon, UserIcon } from '@heroicons/react/solid';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import Link from 'next/link';
|
|
||||||
import React, { useRef, useState } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useClickOutside from '../../../hooks/useClickOutside';
|
import useClickOutside from '../../../hooks/useClickOutside';
|
||||||
import { useUser } from '../../../hooks/useUser';
|
import { useUser } from '../../../hooks/useUser';
|
||||||
|
import Link from '../../Link';
|
||||||
import Transition from '../../Transition';
|
import Transition from '../../Transition';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import {
|
|||||||
CodeIcon,
|
CodeIcon,
|
||||||
ServerIcon,
|
ServerIcon,
|
||||||
} from '@heroicons/react/outline';
|
} from '@heroicons/react/outline';
|
||||||
import Link from 'next/link';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { StatusResponse } from '../../../../server/interfaces/api/settingsInterfaces';
|
import { StatusResponse } from '../../../../server/interfaces/api/settingsInterfaces';
|
||||||
|
import Link from '../../Link';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
streamdevelop: 'Overseerr Develop',
|
streamdevelop: 'Overseerr Develop',
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { MenuAlt2Icon } from '@heroicons/react/outline';
|
import { MenuAlt2Icon } from '@heroicons/react/outline';
|
||||||
import { ArrowLeftIcon } from '@heroicons/react/solid';
|
import { ArrowLeftIcon } from '@heroicons/react/solid';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { AvailableLocale } from '../../context/LanguageContext';
|
import { AvailableLocale } from '../../context/LanguageContext';
|
||||||
import useLocale from '../../hooks/useLocale';
|
import useLocale from '../../hooks/useLocale';
|
||||||
|
import { useRouter } from '../../hooks/useRouter';
|
||||||
import useSettings from '../../hooks/useSettings';
|
import useSettings from '../../hooks/useSettings';
|
||||||
import { useUser } from '../../hooks/useUser';
|
import { useUser } from '../../hooks/useUser';
|
||||||
import SearchInput from './SearchInput';
|
import SearchInput from './SearchInput';
|
||||||
|
|||||||
12
src/components/Link/index.tsx
Normal file
12
src/components/Link/index.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import NextLink, { LinkProps } from 'next/link';
|
||||||
|
import React from 'react';
|
||||||
|
import addBasePath from '../../utils/addBasePath';
|
||||||
|
|
||||||
|
const Link: React.FC<LinkProps> = ({ href, ...props }) => {
|
||||||
|
if (props.as) {
|
||||||
|
props.as = addBasePath(props.as);
|
||||||
|
}
|
||||||
|
return <NextLink href={addBasePath(href)} {...props} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Link;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { NProgress } from '@tanem/react-nprogress';
|
import { NProgress } from '@tanem/react-nprogress';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from '../../hooks/useRouter';
|
||||||
|
|
||||||
interface BarProps {
|
interface BarProps {
|
||||||
progress: number;
|
progress: number;
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { LoginIcon, SupportIcon } from '@heroicons/react/outline';
|
import { LoginIcon, SupportIcon } from '@heroicons/react/outline';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Field, Form, Formik } from 'formik';
|
import { Field, Form, Formik } from 'formik';
|
||||||
import Link from 'next/link';
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import useSettings from '../../hooks/useSettings';
|
import useSettings from '../../hooks/useSettings';
|
||||||
import Button from '../Common/Button';
|
import Button from '../Common/Button';
|
||||||
import SensitiveInput from '../Common/SensitiveInput';
|
import SensitiveInput from '../Common/SensitiveInput';
|
||||||
|
import Link from '../Link';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
email: 'Email Address',
|
email: 'Email Address',
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { XCircleIcon } from '@heroicons/react/solid';
|
import { XCircleIcon } from '@heroicons/react/solid';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useRouter } from 'next/dist/client/router';
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
import { useRouter } from '../../hooks/useRouter';
|
||||||
import useSettings from '../../hooks/useSettings';
|
import useSettings from '../../hooks/useSettings';
|
||||||
import { useUser } from '../../hooks/useUser';
|
import { useUser } from '../../hooks/useUser';
|
||||||
|
import addBasePath from '../../utils/addBasePath';
|
||||||
import Accordion from '../Common/Accordion';
|
import Accordion from '../Common/Accordion';
|
||||||
import ImageFader from '../Common/ImageFader';
|
import ImageFader from '../Common/ImageFader';
|
||||||
import PageTitle from '../Common/PageTitle';
|
import PageTitle from '../Common/PageTitle';
|
||||||
@@ -81,7 +82,11 @@ const Login: React.FC = () => {
|
|||||||
<LanguagePicker />
|
<LanguagePicker />
|
||||||
</div>
|
</div>
|
||||||
<div className="relative z-40 flex flex-col items-center px-4 mt-10 sm:mx-auto sm:w-full sm:max-w-md">
|
<div className="relative z-40 flex flex-col items-center px-4 mt-10 sm:mx-auto sm:w-full sm:max-w-md">
|
||||||
<img src="/logo_stacked.svg" className="max-w-full mb-10" alt="Logo" />
|
<img
|
||||||
|
src={addBasePath('/logo_stacked.svg')}
|
||||||
|
className="max-w-full mb-10"
|
||||||
|
alt="Logo"
|
||||||
|
/>
|
||||||
<h2 className="mt-2 text-3xl font-extrabold leading-9 text-center text-gray-100">
|
<h2 className="mt-2 text-3xl font-extrabold leading-9 text-center text-gray-100">
|
||||||
{intl.formatMessage(messages.signinheader)}
|
{intl.formatMessage(messages.signinheader)}
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ArrowCircleRightIcon } from '@heroicons/react/solid';
|
import { ArrowCircleRightIcon } from '@heroicons/react/solid';
|
||||||
import Link from 'next/link';
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
import Link from '../../Link';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
seemore: 'See More',
|
seemore: 'See More',
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
||||||
import Link from 'next/link';
|
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { useSWRInfinite } from 'swr';
|
import { useSWRInfinite } from 'swr';
|
||||||
import { MediaStatus } from '../../../server/constants/media';
|
import { MediaStatus } from '../../../server/constants/media';
|
||||||
@@ -9,6 +8,7 @@ import type {
|
|||||||
TvResult,
|
TvResult,
|
||||||
} from '../../../server/models/Search';
|
} from '../../../server/models/Search';
|
||||||
import useSettings from '../../hooks/useSettings';
|
import useSettings from '../../hooks/useSettings';
|
||||||
|
import Link from '../Link';
|
||||||
import PersonCard from '../PersonCard';
|
import PersonCard from '../PersonCard';
|
||||||
import Slider from '../Slider';
|
import Slider from '../Slider';
|
||||||
import TitleCard from '../TitleCard';
|
import TitleCard from '../TitleCard';
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { MovieDetails } from '../../../../server/models/Movie';
|
import { MovieDetails } from '../../../../server/models/Movie';
|
||||||
|
import { useRouter } from '../../../hooks/useRouter';
|
||||||
import Error from '../../../pages/_error';
|
import Error from '../../../pages/_error';
|
||||||
import Header from '../../Common/Header';
|
import Header from '../../Common/Header';
|
||||||
import LoadingSpinner from '../../Common/LoadingSpinner';
|
import LoadingSpinner from '../../Common/LoadingSpinner';
|
||||||
import PageTitle from '../../Common/PageTitle';
|
import PageTitle from '../../Common/PageTitle';
|
||||||
|
import Link from '../../Link';
|
||||||
import PersonCard from '../../PersonCard';
|
import PersonCard from '../../PersonCard';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { MovieDetails } from '../../../../server/models/Movie';
|
import { MovieDetails } from '../../../../server/models/Movie';
|
||||||
|
import { useRouter } from '../../../hooks/useRouter';
|
||||||
import Error from '../../../pages/_error';
|
import Error from '../../../pages/_error';
|
||||||
import Header from '../../Common/Header';
|
import Header from '../../Common/Header';
|
||||||
import LoadingSpinner from '../../Common/LoadingSpinner';
|
import LoadingSpinner from '../../Common/LoadingSpinner';
|
||||||
import PageTitle from '../../Common/PageTitle';
|
import PageTitle from '../../Common/PageTitle';
|
||||||
|
import Link from '../../Link';
|
||||||
import PersonCard from '../../PersonCard';
|
import PersonCard from '../../PersonCard';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
@@ -10,6 +9,7 @@ import Error from '../../pages/_error';
|
|||||||
import Header from '../Common/Header';
|
import Header from '../Common/Header';
|
||||||
import ListView from '../Common/ListView';
|
import ListView from '../Common/ListView';
|
||||||
import PageTitle from '../Common/PageTitle';
|
import PageTitle from '../Common/PageTitle';
|
||||||
|
import Link from '../Link';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
recommendations: 'Recommendations',
|
recommendations: 'Recommendations',
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import type { MovieDetails } from '../../../server/models/Movie';
|
import type { MovieDetails } from '../../../server/models/Movie';
|
||||||
import type { MovieResult } from '../../../server/models/Search';
|
import type { MovieResult } from '../../../server/models/Search';
|
||||||
import useDiscover from '../../hooks/useDiscover';
|
import useDiscover from '../../hooks/useDiscover';
|
||||||
|
import { useRouter } from '../../hooks/useRouter';
|
||||||
import Error from '../../pages/_error';
|
import Error from '../../pages/_error';
|
||||||
import Header from '../Common/Header';
|
import Header from '../Common/Header';
|
||||||
import ListView from '../Common/ListView';
|
import ListView from '../Common/ListView';
|
||||||
import PageTitle from '../Common/PageTitle';
|
import PageTitle from '../Common/PageTitle';
|
||||||
|
import Link from '../Link';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
similar: 'Similar Titles',
|
similar: 'Similar Titles',
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ import {
|
|||||||
import { hasFlag } from 'country-flag-icons';
|
import { hasFlag } from 'country-flag-icons';
|
||||||
import 'country-flag-icons/3x2/flags.css';
|
import 'country-flag-icons/3x2/flags.css';
|
||||||
import { uniqBy } from 'lodash';
|
import { uniqBy } from 'lodash';
|
||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
@@ -29,6 +27,7 @@ import RTFresh from '../../assets/rt_fresh.svg';
|
|||||||
import RTRotten from '../../assets/rt_rotten.svg';
|
import RTRotten from '../../assets/rt_rotten.svg';
|
||||||
import TmdbLogo from '../../assets/tmdb_logo.svg';
|
import TmdbLogo from '../../assets/tmdb_logo.svg';
|
||||||
import useLocale from '../../hooks/useLocale';
|
import useLocale from '../../hooks/useLocale';
|
||||||
|
import { useRouter } from '../../hooks/useRouter';
|
||||||
import useSettings from '../../hooks/useSettings';
|
import useSettings from '../../hooks/useSettings';
|
||||||
import { Permission, useUser } from '../../hooks/useUser';
|
import { Permission, useUser } from '../../hooks/useUser';
|
||||||
import globalMessages from '../../i18n/globalMessages';
|
import globalMessages from '../../i18n/globalMessages';
|
||||||
@@ -41,6 +40,7 @@ import PageTitle from '../Common/PageTitle';
|
|||||||
import PlayButton, { PlayButtonLink } from '../Common/PlayButton';
|
import PlayButton, { PlayButtonLink } from '../Common/PlayButton';
|
||||||
import ExternalLinkBlock from '../ExternalLinkBlock';
|
import ExternalLinkBlock from '../ExternalLinkBlock';
|
||||||
import IssueModal from '../IssueModal';
|
import IssueModal from '../IssueModal';
|
||||||
|
import Link from '../Link';
|
||||||
import ManageSlideOver from '../ManageSlideOver';
|
import ManageSlideOver from '../ManageSlideOver';
|
||||||
import MediaSlider from '../MediaSlider';
|
import MediaSlider from '../MediaSlider';
|
||||||
import PersonCard from '../PersonCard';
|
import PersonCard from '../PersonCard';
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import addBasePath from '../../utils/addBasePath';
|
||||||
|
|
||||||
interface PWAHeaderProps {
|
interface PWAHeaderProps {
|
||||||
applicationTitle?: string;
|
applicationTitle?: string;
|
||||||
@@ -10,148 +11,148 @@ const PWAHeader: React.FC<PWAHeaderProps> = ({ applicationTitle }) => {
|
|||||||
<link
|
<link
|
||||||
rel="apple-touch-icon"
|
rel="apple-touch-icon"
|
||||||
sizes="180x180"
|
sizes="180x180"
|
||||||
href="/apple-touch-icon.png"
|
href={addBasePath('/apple-touch-icon.png')}
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="icon"
|
rel="icon"
|
||||||
type="image/png"
|
type="image/png"
|
||||||
sizes="32x32"
|
sizes="32x32"
|
||||||
href="/favicon-32x32.png"
|
href={addBasePath('/favicon-32x32.png')}
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="icon"
|
rel="icon"
|
||||||
type="image/png"
|
type="image/png"
|
||||||
sizes="16x16"
|
sizes="16x16"
|
||||||
href="/favicon-16x16.png"
|
href={addBasePath('/favicon-16x16.png')}
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-2048-2732.jpg"
|
href={addBasePath('/apple-splash-2048-2732.jpg')}
|
||||||
media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
|
media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-2732-2048.jpg"
|
href={addBasePath('/apple-splash-2732-2048.jpg')}
|
||||||
media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
|
media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-1668-2388.jpg"
|
href={addBasePath('/apple-splash-1668-2388.jpg')}
|
||||||
media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
|
media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-2388-1668.jpg"
|
href={addBasePath('/apple-splash-2388-1668.jpg')}
|
||||||
media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
|
media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-1536-2048.jpg"
|
href={addBasePath('/apple-splash-1536-2048.jpg')}
|
||||||
media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
|
media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-2048-1536.jpg"
|
href={addBasePath('/apple-splash-2048-1536.jpg')}
|
||||||
media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
|
media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-1668-2224.jpg"
|
href={addBasePath('/apple-splash-1668-2224.jpg')}
|
||||||
media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
|
media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-2224-1668.jpg"
|
href={addBasePath('/apple-splash-2224-1668.jpg')}
|
||||||
media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
|
media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-1620-2160.jpg"
|
href={addBasePath('/apple-splash-1620-2160.jpg')}
|
||||||
media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
|
media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-2160-1620.jpg"
|
href={addBasePath('/apple-splash-2160-1620.jpg')}
|
||||||
media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
|
media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-1284-2778.jpg"
|
href={addBasePath('/apple-splash-1284-2778.jpg')}
|
||||||
media="(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
|
media="(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-2778-1284.jpg"
|
href={addBasePath('/apple-splash-2778-1284.jpg')}
|
||||||
media="(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
|
media="(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-1170-2532.jpg"
|
href={addBasePath('/apple-splash-1170-2532.jpg')}
|
||||||
media="(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
|
media="(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-2532-1170.jpg"
|
href={addBasePath('/apple-splash-2532-1170.jpg')}
|
||||||
media="(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
|
media="(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-1125-2436.jpg"
|
href={addBasePath('/apple-splash-1125-2436.jpg')}
|
||||||
media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
|
media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-2436-1125.jpg"
|
href={addBasePath('/apple-splash-2436-1125.jpg')}
|
||||||
media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
|
media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-1242-2688.jpg"
|
href={addBasePath('/apple-splash-1242-2688.jpg')}
|
||||||
media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
|
media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-2688-1242.jpg"
|
href={addBasePath('/apple-splash-2688-1242.jpg')}
|
||||||
media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
|
media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-828-1792.jpg"
|
href={addBasePath('/apple-splash-828-1792.jpg')}
|
||||||
media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
|
media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-1792-828.jpg"
|
href={addBasePath('/apple-splash-1792-828.jpg')}
|
||||||
media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
|
media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-1242-2208.jpg"
|
href={addBasePath('/apple-splash-1242-2208.jpg')}
|
||||||
media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
|
media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-2208-1242.jpg"
|
href={addBasePath('/apple-splash-2208-1242.jpg')}
|
||||||
media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
|
media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-750-1334.jpg"
|
href={addBasePath('/apple-splash-750-1334.jpg')}
|
||||||
media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
|
media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-1334-750.jpg"
|
href={addBasePath('/apple-splash-1334-750.jpg')}
|
||||||
media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
|
media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-640-1136.jpg"
|
href={addBasePath('/apple-splash-640-1136.jpg')}
|
||||||
media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
|
media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="/apple-splash-1136-640.jpg"
|
href={addBasePath('/apple-splash-1136-640.jpg')}
|
||||||
media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
|
media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
|
||||||
/>
|
/>
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
@@ -161,7 +162,7 @@ const PWAHeader: React.FC<PWAHeaderProps> = ({ applicationTitle }) => {
|
|||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="manifest"
|
rel="manifest"
|
||||||
href="/site.webmanifest"
|
href={addBasePath('/site.webmanifest')}
|
||||||
crossOrigin="use-credentials"
|
crossOrigin="use-credentials"
|
||||||
/>
|
/>
|
||||||
<meta name="application-name" content={applicationTitle ?? 'Overseerr'} />
|
<meta name="application-name" content={applicationTitle ?? 'Overseerr'} />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { UserCircleIcon } from '@heroicons/react/solid';
|
import { UserCircleIcon } from '@heroicons/react/solid';
|
||||||
import Link from 'next/link';
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import CachedImage from '../Common/CachedImage';
|
import CachedImage from '../Common/CachedImage';
|
||||||
|
import Link from '../Link';
|
||||||
|
|
||||||
interface PersonCardProps {
|
interface PersonCardProps {
|
||||||
personId: number;
|
personId: number;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { groupBy } from 'lodash';
|
import { groupBy } from 'lodash';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import TruncateMarkup from 'react-truncate-markup';
|
import TruncateMarkup from 'react-truncate-markup';
|
||||||
@@ -7,6 +6,7 @@ import useSWR from 'swr';
|
|||||||
import type { PersonCombinedCreditsResponse } from '../../../server/interfaces/api/personInterfaces';
|
import type { PersonCombinedCreditsResponse } from '../../../server/interfaces/api/personInterfaces';
|
||||||
import type { PersonDetail } from '../../../server/models/Person';
|
import type { PersonDetail } from '../../../server/models/Person';
|
||||||
import Ellipsis from '../../assets/ellipsis.svg';
|
import Ellipsis from '../../assets/ellipsis.svg';
|
||||||
|
import { useRouter } from '../../hooks/useRouter';
|
||||||
import globalMessages from '../../i18n/globalMessages';
|
import globalMessages from '../../i18n/globalMessages';
|
||||||
import Error from '../../pages/_error';
|
import Error from '../../pages/_error';
|
||||||
import CachedImage from '../Common/CachedImage';
|
import CachedImage from '../Common/CachedImage';
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
XIcon,
|
XIcon,
|
||||||
} from '@heroicons/react/solid';
|
} from '@heroicons/react/solid';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import Link from 'next/link';
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { MediaRequestStatus } from '../../../server/constants/media';
|
import { MediaRequestStatus } from '../../../server/constants/media';
|
||||||
@@ -17,6 +16,7 @@ import useRequestOverride from '../../hooks/useRequestOverride';
|
|||||||
import globalMessages from '../../i18n/globalMessages';
|
import globalMessages from '../../i18n/globalMessages';
|
||||||
import Badge from '../Common/Badge';
|
import Badge from '../Common/Badge';
|
||||||
import Button from '../Common/Button';
|
import Button from '../Common/Button';
|
||||||
|
import Link from '../Link';
|
||||||
import RequestModal from '../RequestModal';
|
import RequestModal from '../RequestModal';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
XIcon,
|
XIcon,
|
||||||
} from '@heroicons/react/solid';
|
} from '@heroicons/react/solid';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import Link from 'next/link';
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useInView } from 'react-intersection-observer';
|
import { useInView } from 'react-intersection-observer';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
@@ -25,6 +24,7 @@ import { withProperties } from '../../utils/typeHelpers';
|
|||||||
import Badge from '../Common/Badge';
|
import Badge from '../Common/Badge';
|
||||||
import Button from '../Common/Button';
|
import Button from '../Common/Button';
|
||||||
import CachedImage from '../Common/CachedImage';
|
import CachedImage from '../Common/CachedImage';
|
||||||
|
import Link from '../Link';
|
||||||
import RequestModal from '../RequestModal';
|
import RequestModal from '../RequestModal';
|
||||||
import StatusBadge from '../StatusBadge';
|
import StatusBadge from '../StatusBadge';
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
XIcon,
|
XIcon,
|
||||||
} from '@heroicons/react/solid';
|
} from '@heroicons/react/solid';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import Link from 'next/link';
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useInView } from 'react-intersection-observer';
|
import { useInView } from 'react-intersection-observer';
|
||||||
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
|
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
|
||||||
@@ -25,6 +24,7 @@ import Badge from '../../Common/Badge';
|
|||||||
import Button from '../../Common/Button';
|
import Button from '../../Common/Button';
|
||||||
import CachedImage from '../../Common/CachedImage';
|
import CachedImage from '../../Common/CachedImage';
|
||||||
import ConfirmButton from '../../Common/ConfirmButton';
|
import ConfirmButton from '../../Common/ConfirmButton';
|
||||||
|
import Link from '../../Link';
|
||||||
import RequestModal from '../../RequestModal';
|
import RequestModal from '../../RequestModal';
|
||||||
import StatusBadge from '../../StatusBadge';
|
import StatusBadge from '../../StatusBadge';
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,11 @@ import {
|
|||||||
FilterIcon,
|
FilterIcon,
|
||||||
SortDescendingIcon,
|
SortDescendingIcon,
|
||||||
} from '@heroicons/react/solid';
|
} from '@heroicons/react/solid';
|
||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import type { RequestResultsResponse } from '../../../server/interfaces/api/requestInterfaces';
|
import type { RequestResultsResponse } from '../../../server/interfaces/api/requestInterfaces';
|
||||||
|
import { useRouter } from '../../hooks/useRouter';
|
||||||
import { useUpdateQueryParams } from '../../hooks/useUpdateQueryParams';
|
import { useUpdateQueryParams } from '../../hooks/useUpdateQueryParams';
|
||||||
import { useUser } from '../../hooks/useUser';
|
import { useUser } from '../../hooks/useUser';
|
||||||
import globalMessages from '../../i18n/globalMessages';
|
import globalMessages from '../../i18n/globalMessages';
|
||||||
@@ -17,6 +16,7 @@ import Button from '../Common/Button';
|
|||||||
import Header from '../Common/Header';
|
import Header from '../Common/Header';
|
||||||
import LoadingSpinner from '../Common/LoadingSpinner';
|
import LoadingSpinner from '../Common/LoadingSpinner';
|
||||||
import PageTitle from '../Common/PageTitle';
|
import PageTitle from '../Common/PageTitle';
|
||||||
|
import Link from '../Link';
|
||||||
import RequestItem from './RequestItem';
|
import RequestItem from './RequestItem';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/solid';
|
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/solid';
|
||||||
import Link from 'next/link';
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { QuotaStatus } from '../../../../server/interfaces/api/userInterfaces';
|
import { QuotaStatus } from '../../../../server/interfaces/api/userInterfaces';
|
||||||
import ProgressCircle from '../../Common/ProgressCircle';
|
import ProgressCircle from '../../Common/ProgressCircle';
|
||||||
|
import Link from '../../Link';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
requestsremaining:
|
requestsremaining:
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import useSWR from 'swr';
|
|||||||
import { SonarrSeries } from '../../../../server/api/servarr/sonarr';
|
import { SonarrSeries } from '../../../../server/api/servarr/sonarr';
|
||||||
import globalMessages from '../../../i18n/globalMessages';
|
import globalMessages from '../../../i18n/globalMessages';
|
||||||
import Alert from '../../Common/Alert';
|
import Alert from '../../Common/Alert';
|
||||||
|
import CachedImage from '../../Common/CachedImage';
|
||||||
import { SmallLoadingSpinner } from '../../Common/LoadingSpinner';
|
import { SmallLoadingSpinner } from '../../Common/LoadingSpinner';
|
||||||
import Modal from '../../Common/Modal';
|
import Modal from '../../Common/Modal';
|
||||||
|
|
||||||
@@ -72,7 +73,7 @@ const SearchByNameModal: React.FC<SearchByNameModalProps> = ({
|
|||||||
} `}
|
} `}
|
||||||
>
|
>
|
||||||
<div className="flex items-center flex-none w-24 space-x-4">
|
<div className="flex items-center flex-none w-24 space-x-4">
|
||||||
<img
|
<CachedImage
|
||||||
src={
|
src={
|
||||||
item.remotePoster ??
|
item.remotePoster ??
|
||||||
'/images/overseerr_poster_not_found.png'
|
'/images/overseerr_poster_not_found.png'
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import { ArrowLeftIcon, MailIcon } from '@heroicons/react/solid';
|
import { ArrowLeftIcon, MailIcon } from '@heroicons/react/solid';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Field, Form, Formik } from 'formik';
|
import { Field, Form, Formik } from 'formik';
|
||||||
import Link from 'next/link';
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
|
import addBasePath from '../../utils/addBasePath';
|
||||||
import Button from '../Common/Button';
|
import Button from '../Common/Button';
|
||||||
import ImageFader from '../Common/ImageFader';
|
import ImageFader from '../Common/ImageFader';
|
||||||
import PageTitle from '../Common/PageTitle';
|
import PageTitle from '../Common/PageTitle';
|
||||||
import LanguagePicker from '../Layout/LanguagePicker';
|
import LanguagePicker from '../Layout/LanguagePicker';
|
||||||
|
import Link from '../Link';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
passwordreset: 'Password Reset',
|
passwordreset: 'Password Reset',
|
||||||
@@ -49,7 +50,11 @@ const ResetPassword: React.FC = () => {
|
|||||||
<LanguagePicker />
|
<LanguagePicker />
|
||||||
</div>
|
</div>
|
||||||
<div className="relative z-40 flex flex-col items-center px-4 mt-10 sm:mx-auto sm:w-full sm:max-w-md">
|
<div className="relative z-40 flex flex-col items-center px-4 mt-10 sm:mx-auto sm:w-full sm:max-w-md">
|
||||||
<img src="/logo_stacked.svg" className="max-w-full mb-10" alt="Logo" />
|
<img
|
||||||
|
src={addBasePath('/logo_stacked.svg')}
|
||||||
|
className="max-w-full mb-10"
|
||||||
|
alt="Logo"
|
||||||
|
/>
|
||||||
<h2 className="mt-2 text-3xl font-extrabold leading-9 text-center text-gray-100">
|
<h2 className="mt-2 text-3xl font-extrabold leading-9 text-center text-gray-100">
|
||||||
{intl.formatMessage(messages.resetpassword)}
|
{intl.formatMessage(messages.resetpassword)}
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
import { SupportIcon } from '@heroicons/react/outline';
|
import { SupportIcon } from '@heroicons/react/outline';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Form, Formik } from 'formik';
|
import { Form, Formik } from 'formik';
|
||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
|
import { useRouter } from '../../hooks/useRouter';
|
||||||
import globalMessages from '../../i18n/globalMessages';
|
import globalMessages from '../../i18n/globalMessages';
|
||||||
|
import addBasePath from '../../utils/addBasePath';
|
||||||
import Button from '../Common/Button';
|
import Button from '../Common/Button';
|
||||||
import ImageFader from '../Common/ImageFader';
|
import ImageFader from '../Common/ImageFader';
|
||||||
import SensitiveInput from '../Common/SensitiveInput';
|
import SensitiveInput from '../Common/SensitiveInput';
|
||||||
import LanguagePicker from '../Layout/LanguagePicker';
|
import LanguagePicker from '../Layout/LanguagePicker';
|
||||||
|
import Link from '../Link';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
passwordreset: 'Password Reset',
|
passwordreset: 'Password Reset',
|
||||||
@@ -64,7 +65,11 @@ const ResetPassword: React.FC = () => {
|
|||||||
<LanguagePicker />
|
<LanguagePicker />
|
||||||
</div>
|
</div>
|
||||||
<div className="relative z-40 flex flex-col items-center px-4 mt-10 sm:mx-auto sm:w-full sm:max-w-md">
|
<div className="relative z-40 flex flex-col items-center px-4 mt-10 sm:mx-auto sm:w-full sm:max-w-md">
|
||||||
<img src="/logo_stacked.svg" className="max-w-full mb-10" alt="Logo" />
|
<img
|
||||||
|
src={addBasePath('/logo_stacked.svg')}
|
||||||
|
className="max-w-full mb-10"
|
||||||
|
alt="Logo"
|
||||||
|
/>
|
||||||
<h2 className="mt-2 text-3xl font-extrabold leading-9 text-center text-gray-100">
|
<h2 className="mt-2 text-3xl font-extrabold leading-9 text-center text-gray-100">
|
||||||
{intl.formatMessage(messages.resetpassword)}
|
{intl.formatMessage(messages.resetpassword)}
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import {
|
import {
|
||||||
TvResult,
|
TvResult,
|
||||||
MovieResult,
|
MovieResult,
|
||||||
PersonResult,
|
PersonResult,
|
||||||
} from '../../../server/models/Search';
|
} from '../../../server/models/Search';
|
||||||
|
import { useRouter } from '../../hooks/useRouter';
|
||||||
import ListView from '../Common/ListView';
|
import ListView from '../Common/ListView';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import Header from '../Common/Header';
|
import Header from '../Common/Header';
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import axios from 'axios';
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import useSettings from '../../hooks/useSettings';
|
import useSettings from '../../hooks/useSettings';
|
||||||
import { useUser } from '../../hooks/useUser';
|
import { useUser } from '../../hooks/useUser';
|
||||||
|
import addBasePath from '../../utils/addBasePath';
|
||||||
|
|
||||||
const ServiceWorkerSetup: React.FC = () => {
|
const ServiceWorkerSetup: React.FC = () => {
|
||||||
const { currentSettings } = useSettings();
|
const { currentSettings } = useSettings();
|
||||||
@@ -10,7 +11,7 @@ const ServiceWorkerSetup: React.FC = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if ('serviceWorker' in navigator && user?.id) {
|
if ('serviceWorker' in navigator && user?.id) {
|
||||||
navigator.serviceWorker
|
navigator.serviceWorker
|
||||||
.register('/sw.js')
|
.register(addBasePath('/sw.js'))
|
||||||
.then(async (registration) => {
|
.then(async (registration) => {
|
||||||
console.log(
|
console.log(
|
||||||
'[SW] Registration successful, scope is:',
|
'[SW] Registration successful, scope is:',
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { QuestionMarkCircleIcon, RefreshIcon } from '@heroicons/react/solid';
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Field, Form, Formik } from 'formik';
|
import { Field, Form, Formik } from 'formik';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import Link from 'next/link';
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
@@ -12,6 +11,7 @@ import * as Yup from 'yup';
|
|||||||
import globalMessages from '../../../../i18n/globalMessages';
|
import globalMessages from '../../../../i18n/globalMessages';
|
||||||
import Button from '../../../Common/Button';
|
import Button from '../../../Common/Button';
|
||||||
import LoadingSpinner from '../../../Common/LoadingSpinner';
|
import LoadingSpinner from '../../../Common/LoadingSpinner';
|
||||||
|
import Link from '../../../Link';
|
||||||
import NotificationTypeSelector from '../../../NotificationTypeSelector';
|
import NotificationTypeSelector from '../../../NotificationTypeSelector';
|
||||||
|
|
||||||
const JSONEditor = dynamic(() => import('../../../JSONEditor'), { ssr: false });
|
const JSONEditor = dynamic(() => import('../../../JSONEditor'), { ssr: false });
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
PlayIcon,
|
PlayIcon,
|
||||||
} from '@heroicons/react/solid';
|
} from '@heroicons/react/solid';
|
||||||
import copy from 'copy-to-clipboard';
|
import copy from 'copy-to-clipboard';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
@@ -17,6 +16,7 @@ import {
|
|||||||
LogMessage,
|
LogMessage,
|
||||||
LogsResultsResponse,
|
LogsResultsResponse,
|
||||||
} from '../../../../server/interfaces/api/settingsInterfaces';
|
} from '../../../../server/interfaces/api/settingsInterfaces';
|
||||||
|
import { useRouter } from '../../../hooks/useRouter';
|
||||||
import { useUpdateQueryParams } from '../../../hooks/useUpdateQueryParams';
|
import { useUpdateQueryParams } from '../../../hooks/useUpdateQueryParams';
|
||||||
import globalMessages from '../../../i18n/globalMessages';
|
import globalMessages from '../../../i18n/globalMessages';
|
||||||
import Error from '../../../pages/_error';
|
import Error from '../../../pages/_error';
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ const messages = defineMessages({
|
|||||||
apikey: 'API Key',
|
apikey: 'API Key',
|
||||||
applicationTitle: 'Application Title',
|
applicationTitle: 'Application Title',
|
||||||
applicationurl: 'Application URL',
|
applicationurl: 'Application URL',
|
||||||
|
basePath: 'Base Path',
|
||||||
|
basePathTip:
|
||||||
|
'Configure Overseerr to run under a sub-path of a domain (Overseerr must be restarted for changes to take effect)',
|
||||||
region: 'Discover Region',
|
region: 'Discover Region',
|
||||||
regionTip: 'Filter content by regional availability',
|
regionTip: 'Filter content by regional availability',
|
||||||
originallanguage: 'Discover Language',
|
originallanguage: 'Discover Language',
|
||||||
@@ -44,7 +47,7 @@ const messages = defineMessages({
|
|||||||
hideAvailable: 'Hide Available Media',
|
hideAvailable: 'Hide Available Media',
|
||||||
csrfProtection: 'Enable CSRF Protection',
|
csrfProtection: 'Enable CSRF Protection',
|
||||||
csrfProtectionTip:
|
csrfProtectionTip:
|
||||||
'Set external API access to read-only (requires HTTPS, and Overseerr must be reloaded for changes to take effect)',
|
'Set external API access to read-only (requires HTTPS, and Overseerr must be restarted for changes to take effect)',
|
||||||
csrfProtectionHoverTip:
|
csrfProtectionHoverTip:
|
||||||
'Do NOT enable this setting unless you understand what you are doing!',
|
'Do NOT enable this setting unless you understand what you are doing!',
|
||||||
cacheImages: 'Enable Image Caching',
|
cacheImages: 'Enable Image Caching',
|
||||||
@@ -52,10 +55,12 @@ const messages = defineMessages({
|
|||||||
'Optimize and store all images locally (consumes a significant amount of disk space)',
|
'Optimize and store all images locally (consumes a significant amount of disk space)',
|
||||||
trustProxy: 'Enable Proxy Support',
|
trustProxy: 'Enable Proxy Support',
|
||||||
trustProxyTip:
|
trustProxyTip:
|
||||||
'Allow Overseerr to correctly register client IP addresses behind a proxy (Overseerr must be reloaded for changes to take effect)',
|
'Allow Overseerr to correctly register client IP addresses behind a proxy (Overseerr must be restarted for changes to take effect)',
|
||||||
validationApplicationTitle: 'You must provide an application title',
|
validationApplicationTitle: 'You must provide an application title',
|
||||||
validationApplicationUrl: 'You must provide a valid URL',
|
validationApplicationUrl: 'You must provide a valid URL',
|
||||||
validationApplicationUrlTrailingSlash: 'URL must not end in a trailing slash',
|
validationApplicationUrlTrailingSlash: 'URL must not end in a trailing slash',
|
||||||
|
validationBasePathLeadingSlash: 'Path must begin with a leading slash',
|
||||||
|
validationBasePathTrailingSlash: 'Path must not end in a trailing slash',
|
||||||
partialRequestsEnabled: 'Allow Partial Series Requests',
|
partialRequestsEnabled: 'Allow Partial Series Requests',
|
||||||
locale: 'Display Language',
|
locale: 'Display Language',
|
||||||
});
|
});
|
||||||
@@ -81,12 +86,18 @@ const SettingsMain: React.FC = () => {
|
|||||||
.test(
|
.test(
|
||||||
'no-trailing-slash',
|
'no-trailing-slash',
|
||||||
intl.formatMessage(messages.validationApplicationUrlTrailingSlash),
|
intl.formatMessage(messages.validationApplicationUrlTrailingSlash),
|
||||||
(value) => {
|
(value) => !value || !value.endsWith('/')
|
||||||
if (value?.substr(value.length - 1) === '/') {
|
),
|
||||||
return false;
|
basePath: Yup.string()
|
||||||
}
|
.test(
|
||||||
return true;
|
'leading-slash',
|
||||||
}
|
intl.formatMessage(messages.validationBasePathLeadingSlash),
|
||||||
|
(value) => !value || value.startsWith('/')
|
||||||
|
)
|
||||||
|
.test(
|
||||||
|
'no-trailing-slash',
|
||||||
|
intl.formatMessage(messages.validationBasePathTrailingSlash),
|
||||||
|
(value) => !value || !value.endsWith('/')
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -132,6 +143,7 @@ const SettingsMain: React.FC = () => {
|
|||||||
initialValues={{
|
initialValues={{
|
||||||
applicationTitle: data?.applicationTitle,
|
applicationTitle: data?.applicationTitle,
|
||||||
applicationUrl: data?.applicationUrl,
|
applicationUrl: data?.applicationUrl,
|
||||||
|
basePath: data?.basePath,
|
||||||
csrfProtection: data?.csrfProtection,
|
csrfProtection: data?.csrfProtection,
|
||||||
hideAvailable: data?.hideAvailable,
|
hideAvailable: data?.hideAvailable,
|
||||||
locale: data?.locale ?? 'en',
|
locale: data?.locale ?? 'en',
|
||||||
@@ -147,6 +159,7 @@ const SettingsMain: React.FC = () => {
|
|||||||
await axios.post('/api/v1/settings/main', {
|
await axios.post('/api/v1/settings/main', {
|
||||||
applicationTitle: values.applicationTitle,
|
applicationTitle: values.applicationTitle,
|
||||||
applicationUrl: values.applicationUrl,
|
applicationUrl: values.applicationUrl,
|
||||||
|
basePath: values.basePath,
|
||||||
csrfProtection: values.csrfProtection,
|
csrfProtection: values.csrfProtection,
|
||||||
hideAvailable: values.hideAvailable,
|
hideAvailable: values.hideAvailable,
|
||||||
locale: values.locale,
|
locale: values.locale,
|
||||||
@@ -255,6 +268,27 @@ const SettingsMain: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="form-row">
|
||||||
|
<label htmlFor="basePath" className="text-label">
|
||||||
|
<span>{intl.formatMessage(messages.basePath)}</span>
|
||||||
|
<span className="label-tip">
|
||||||
|
{intl.formatMessage(messages.basePathTip)}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<div className="form-input">
|
||||||
|
<div className="form-input-field">
|
||||||
|
<Field
|
||||||
|
id="basePath"
|
||||||
|
name="basePath"
|
||||||
|
type="text"
|
||||||
|
inputMode="url"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{errors.basePath && touched.basePath && (
|
||||||
|
<div className="error">{errors.basePath}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="form-row">
|
<div className="form-row">
|
||||||
<label htmlFor="trustProxy" className="checkbox-label">
|
<label htmlFor="trustProxy" className="checkbox-label">
|
||||||
<span>{intl.formatMessage(messages.trustProxy)}</span>
|
<span>{intl.formatMessage(messages.trustProxy)}</span>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR, { mutate } from 'swr';
|
import useSWR, { mutate } from 'swr';
|
||||||
import useLocale from '../../hooks/useLocale';
|
import useLocale from '../../hooks/useLocale';
|
||||||
|
import { useRouter } from '../../hooks/useRouter';
|
||||||
|
import addBasePath from '../../utils/addBasePath';
|
||||||
import AppDataWarning from '../AppDataWarning';
|
import AppDataWarning from '../AppDataWarning';
|
||||||
import Badge from '../Common/Badge';
|
import Badge from '../Common/Badge';
|
||||||
import Button from '../Common/Button';
|
import Button from '../Common/Button';
|
||||||
@@ -72,7 +73,7 @@ const Setup: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="relative z-40 px-4 sm:mx-auto sm:w-full sm:max-w-4xl">
|
<div className="relative z-40 px-4 sm:mx-auto sm:w-full sm:max-w-4xl">
|
||||||
<img
|
<img
|
||||||
src="/logo_stacked.svg"
|
src={addBasePath('/logo_stacked.svg')}
|
||||||
className="max-w-full mb-10 sm:max-w-md sm:mx-auto"
|
className="max-w-full mb-10 sm:max-w-md sm:mx-auto"
|
||||||
alt="Logo"
|
alt="Logo"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { DownloadIcon } from '@heroicons/react/outline';
|
import { DownloadIcon } from '@heroicons/react/outline';
|
||||||
import { BellIcon, CheckIcon, ClockIcon } from '@heroicons/react/solid';
|
import { BellIcon, CheckIcon, ClockIcon } from '@heroicons/react/solid';
|
||||||
import Link from 'next/link';
|
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { MediaStatus } from '../../../server/constants/media';
|
import { MediaStatus } from '../../../server/constants/media';
|
||||||
@@ -12,6 +11,7 @@ import globalMessages from '../../i18n/globalMessages';
|
|||||||
import { withProperties } from '../../utils/typeHelpers';
|
import { withProperties } from '../../utils/typeHelpers';
|
||||||
import Button from '../Common/Button';
|
import Button from '../Common/Button';
|
||||||
import CachedImage from '../Common/CachedImage';
|
import CachedImage from '../Common/CachedImage';
|
||||||
|
import Link from '../Link';
|
||||||
import RequestModal from '../RequestModal';
|
import RequestModal from '../RequestModal';
|
||||||
import Transition from '../Transition';
|
import Transition from '../Transition';
|
||||||
import Placeholder from './Placeholder';
|
import Placeholder from './Placeholder';
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import type { TvDetails } from '../../../../server/models/Tv';
|
import type { TvDetails } from '../../../../server/models/Tv';
|
||||||
|
import { useRouter } from '../../../hooks/useRouter';
|
||||||
import Error from '../../../pages/_error';
|
import Error from '../../../pages/_error';
|
||||||
import Header from '../../Common/Header';
|
import Header from '../../Common/Header';
|
||||||
import LoadingSpinner from '../../Common/LoadingSpinner';
|
import LoadingSpinner from '../../Common/LoadingSpinner';
|
||||||
import PageTitle from '../../Common/PageTitle';
|
import PageTitle from '../../Common/PageTitle';
|
||||||
|
import Link from '../../Link';
|
||||||
import PersonCard from '../../PersonCard';
|
import PersonCard from '../../PersonCard';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import type { TvDetails } from '../../../../server/models/Tv';
|
import type { TvDetails } from '../../../../server/models/Tv';
|
||||||
|
import { useRouter } from '../../../hooks/useRouter';
|
||||||
import Error from '../../../pages/_error';
|
import Error from '../../../pages/_error';
|
||||||
import Header from '../../Common/Header';
|
import Header from '../../Common/Header';
|
||||||
import LoadingSpinner from '../../Common/LoadingSpinner';
|
import LoadingSpinner from '../../Common/LoadingSpinner';
|
||||||
import PageTitle from '../../Common/PageTitle';
|
import PageTitle from '../../Common/PageTitle';
|
||||||
|
import Link from '../../Link';
|
||||||
import PersonCard from '../../PersonCard';
|
import PersonCard from '../../PersonCard';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import type { TvResult } from '../../../server/models/Search';
|
import type { TvResult } from '../../../server/models/Search';
|
||||||
import { TvDetails } from '../../../server/models/Tv';
|
import { TvDetails } from '../../../server/models/Tv';
|
||||||
import useDiscover from '../../hooks/useDiscover';
|
import useDiscover from '../../hooks/useDiscover';
|
||||||
|
import { useRouter } from '../../hooks/useRouter';
|
||||||
import Error from '../../pages/_error';
|
import Error from '../../pages/_error';
|
||||||
import Header from '../Common/Header';
|
import Header from '../Common/Header';
|
||||||
import ListView from '../Common/ListView';
|
import ListView from '../Common/ListView';
|
||||||
import PageTitle from '../Common/PageTitle';
|
import PageTitle from '../Common/PageTitle';
|
||||||
|
import Link from '../Link';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
recommendations: 'Recommendations',
|
recommendations: 'Recommendations',
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import type { TvResult } from '../../../server/models/Search';
|
import type { TvResult } from '../../../server/models/Search';
|
||||||
import type { TvDetails } from '../../../server/models/Tv';
|
import type { TvDetails } from '../../../server/models/Tv';
|
||||||
import useDiscover from '../../hooks/useDiscover';
|
import useDiscover from '../../hooks/useDiscover';
|
||||||
|
import { useRouter } from '../../hooks/useRouter';
|
||||||
import Error from '../../pages/_error';
|
import Error from '../../pages/_error';
|
||||||
import Header from '../Common/Header';
|
import Header from '../Common/Header';
|
||||||
import ListView from '../Common/ListView';
|
import ListView from '../Common/ListView';
|
||||||
import PageTitle from '../Common/PageTitle';
|
import PageTitle from '../Common/PageTitle';
|
||||||
|
import Link from '../Link';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
similar: 'Similar Series',
|
similar: 'Similar Series',
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import {
|
|||||||
} from '@heroicons/react/outline';
|
} from '@heroicons/react/outline';
|
||||||
import { hasFlag } from 'country-flag-icons';
|
import { hasFlag } from 'country-flag-icons';
|
||||||
import 'country-flag-icons/3x2/flags.css';
|
import 'country-flag-icons/3x2/flags.css';
|
||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
@@ -24,6 +22,7 @@ import RTFresh from '../../assets/rt_fresh.svg';
|
|||||||
import RTRotten from '../../assets/rt_rotten.svg';
|
import RTRotten from '../../assets/rt_rotten.svg';
|
||||||
import TmdbLogo from '../../assets/tmdb_logo.svg';
|
import TmdbLogo from '../../assets/tmdb_logo.svg';
|
||||||
import useLocale from '../../hooks/useLocale';
|
import useLocale from '../../hooks/useLocale';
|
||||||
|
import { useRouter } from '../../hooks/useRouter';
|
||||||
import useSettings from '../../hooks/useSettings';
|
import useSettings from '../../hooks/useSettings';
|
||||||
import { Permission, useUser } from '../../hooks/useUser';
|
import { Permission, useUser } from '../../hooks/useUser';
|
||||||
import globalMessages from '../../i18n/globalMessages';
|
import globalMessages from '../../i18n/globalMessages';
|
||||||
@@ -36,6 +35,7 @@ import PageTitle from '../Common/PageTitle';
|
|||||||
import PlayButton, { PlayButtonLink } from '../Common/PlayButton';
|
import PlayButton, { PlayButtonLink } from '../Common/PlayButton';
|
||||||
import ExternalLinkBlock from '../ExternalLinkBlock';
|
import ExternalLinkBlock from '../ExternalLinkBlock';
|
||||||
import IssueModal from '../IssueModal';
|
import IssueModal from '../IssueModal';
|
||||||
|
import Link from '../Link';
|
||||||
import ManageSlideOver from '../ManageSlideOver';
|
import ManageSlideOver from '../ManageSlideOver';
|
||||||
import MediaSlider from '../MediaSlider';
|
import MediaSlider from '../MediaSlider';
|
||||||
import PersonCard from '../PersonCard';
|
import PersonCard from '../PersonCard';
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import {
|
|||||||
} from '@heroicons/react/solid';
|
} from '@heroicons/react/solid';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Field, Form, Formik } from 'formik';
|
import { Field, Form, Formik } from 'formik';
|
||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
@@ -18,6 +16,7 @@ import useSWR from 'swr';
|
|||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import type { UserResultsResponse } from '../../../server/interfaces/api/userInterfaces';
|
import type { UserResultsResponse } from '../../../server/interfaces/api/userInterfaces';
|
||||||
import { hasPermission } from '../../../server/lib/permissions';
|
import { hasPermission } from '../../../server/lib/permissions';
|
||||||
|
import { useRouter } from '../../hooks/useRouter';
|
||||||
import useSettings from '../../hooks/useSettings';
|
import useSettings from '../../hooks/useSettings';
|
||||||
import { useUpdateQueryParams } from '../../hooks/useUpdateQueryParams';
|
import { useUpdateQueryParams } from '../../hooks/useUpdateQueryParams';
|
||||||
import { Permission, User, UserType, useUser } from '../../hooks/useUser';
|
import { Permission, User, UserType, useUser } from '../../hooks/useUser';
|
||||||
@@ -31,6 +30,7 @@ import Modal from '../Common/Modal';
|
|||||||
import PageTitle from '../Common/PageTitle';
|
import PageTitle from '../Common/PageTitle';
|
||||||
import SensitiveInput from '../Common/SensitiveInput';
|
import SensitiveInput from '../Common/SensitiveInput';
|
||||||
import Table from '../Common/Table';
|
import Table from '../Common/Table';
|
||||||
|
import Link from '../Link';
|
||||||
import Transition from '../Transition';
|
import Transition from '../Transition';
|
||||||
import BulkEditModal from './BulkEditModal';
|
import BulkEditModal from './BulkEditModal';
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { CogIcon, UserIcon } from '@heroicons/react/solid';
|
import { CogIcon, UserIcon } from '@heroicons/react/solid';
|
||||||
import Link from 'next/link';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { Permission, User, useUser } from '../../../hooks/useUser';
|
import { Permission, User, useUser } from '../../../hooks/useUser';
|
||||||
import Button from '../../Common/Button';
|
import Button from '../../Common/Button';
|
||||||
|
import Link from '../../Link';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
settings: 'Edit Settings',
|
settings: 'Edit Settings',
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { SaveIcon } from '@heroicons/react/outline';
|
import { SaveIcon } from '@heroicons/react/outline';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Field, Form, Formik } from 'formik';
|
import { Field, Form, Formik } from 'formik';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
@@ -12,6 +11,7 @@ import {
|
|||||||
AvailableLocale,
|
AvailableLocale,
|
||||||
} from '../../../../context/LanguageContext';
|
} from '../../../../context/LanguageContext';
|
||||||
import useLocale from '../../../../hooks/useLocale';
|
import useLocale from '../../../../hooks/useLocale';
|
||||||
|
import { useRouter } from '../../../../hooks/useRouter';
|
||||||
import useSettings from '../../../../hooks/useSettings';
|
import useSettings from '../../../../hooks/useSettings';
|
||||||
import { Permission, UserType, useUser } from '../../../../hooks/useUser';
|
import { Permission, UserType, useUser } from '../../../../hooks/useUser';
|
||||||
import globalMessages from '../../../../i18n/globalMessages';
|
import globalMessages from '../../../../i18n/globalMessages';
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { SaveIcon } from '@heroicons/react/outline';
|
import { SaveIcon } from '@heroicons/react/outline';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Field, Form, Formik } from 'formik';
|
import { Field, Form, Formik } from 'formik';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
|
import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
|
||||||
|
import { useRouter } from '../../../../hooks/useRouter';
|
||||||
import { useUser } from '../../../../hooks/useUser';
|
import { useUser } from '../../../../hooks/useUser';
|
||||||
import globalMessages from '../../../../i18n/globalMessages';
|
import globalMessages from '../../../../i18n/globalMessages';
|
||||||
import Button from '../../../Common/Button';
|
import Button from '../../../Common/Button';
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { SaveIcon } from '@heroicons/react/outline';
|
import { SaveIcon } from '@heroicons/react/outline';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Form, Formik } from 'formik';
|
import { Form, Formik } from 'formik';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
|
import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
|
||||||
|
import { useRouter } from '../../../../hooks/useRouter';
|
||||||
import { useUser } from '../../../../hooks/useUser';
|
import { useUser } from '../../../../hooks/useUser';
|
||||||
import globalMessages from '../../../../i18n/globalMessages';
|
import globalMessages from '../../../../i18n/globalMessages';
|
||||||
import Badge from '../../../Common/Badge';
|
import Badge from '../../../Common/Badge';
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Form, Formik } from 'formik';
|
import { Form, Formik } from 'formik';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
|
import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
|
||||||
|
import { useRouter } from '../../../../hooks/useRouter';
|
||||||
import { useUser } from '../../../../hooks/useUser';
|
import { useUser } from '../../../../hooks/useUser';
|
||||||
import globalMessages from '../../../../i18n/globalMessages';
|
import globalMessages from '../../../../i18n/globalMessages';
|
||||||
import Button from '../../../Common/Button';
|
import Button from '../../../Common/Button';
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Field, Form, Formik } from 'formik';
|
import { Field, Form, Formik } from 'formik';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
|
import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
|
||||||
|
import { useRouter } from '../../../../hooks/useRouter';
|
||||||
import useSettings from '../../../../hooks/useSettings';
|
import useSettings from '../../../../hooks/useSettings';
|
||||||
import { useUser } from '../../../../hooks/useUser';
|
import { useUser } from '../../../../hooks/useUser';
|
||||||
import globalMessages from '../../../../i18n/globalMessages';
|
import globalMessages from '../../../../i18n/globalMessages';
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { SaveIcon } from '@heroicons/react/outline';
|
import { SaveIcon } from '@heroicons/react/outline';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Field, Form, Formik } from 'formik';
|
import { Field, Form, Formik } from 'formik';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
|
import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
|
||||||
|
import { useRouter } from '../../../../hooks/useRouter';
|
||||||
import { useUser } from '../../../../hooks/useUser';
|
import { useUser } from '../../../../hooks/useUser';
|
||||||
import globalMessages from '../../../../i18n/globalMessages';
|
import globalMessages from '../../../../i18n/globalMessages';
|
||||||
import Button from '../../../Common/Button';
|
import Button from '../../../Common/Button';
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { SaveIcon } from '@heroicons/react/outline';
|
import { SaveIcon } from '@heroicons/react/outline';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Form, Formik } from 'formik';
|
import { Form, Formik } from 'formik';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
import useSWR, { mutate } from 'swr';
|
import useSWR, { mutate } from 'swr';
|
||||||
import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
|
import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
|
||||||
|
import { useRouter } from '../../../../hooks/useRouter';
|
||||||
import { useUser } from '../../../../hooks/useUser';
|
import { useUser } from '../../../../hooks/useUser';
|
||||||
import globalMessages from '../../../../i18n/globalMessages';
|
import globalMessages from '../../../../i18n/globalMessages';
|
||||||
import Button from '../../../Common/Button';
|
import Button from '../../../Common/Button';
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { CloudIcon, MailIcon } from '@heroicons/react/solid';
|
import { CloudIcon, MailIcon } from '@heroicons/react/solid';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
@@ -8,6 +7,7 @@ import DiscordLogo from '../../../../assets/extlogos/discord.svg';
|
|||||||
import PushbulletLogo from '../../../../assets/extlogos/pushbullet.svg';
|
import PushbulletLogo from '../../../../assets/extlogos/pushbullet.svg';
|
||||||
import PushoverLogo from '../../../../assets/extlogos/pushover.svg';
|
import PushoverLogo from '../../../../assets/extlogos/pushover.svg';
|
||||||
import TelegramLogo from '../../../../assets/extlogos/telegram.svg';
|
import TelegramLogo from '../../../../assets/extlogos/telegram.svg';
|
||||||
|
import { useRouter } from '../../../../hooks/useRouter';
|
||||||
import { useUser } from '../../../../hooks/useUser';
|
import { useUser } from '../../../../hooks/useUser';
|
||||||
import globalMessages from '../../../../i18n/globalMessages';
|
import globalMessages from '../../../../i18n/globalMessages';
|
||||||
import Error from '../../../../pages/_error';
|
import Error from '../../../../pages/_error';
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { SaveIcon } from '@heroicons/react/outline';
|
import { SaveIcon } from '@heroicons/react/outline';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Form, Formik } from 'formik';
|
import { Form, Formik } from 'formik';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
|
import { useRouter } from '../../../../hooks/useRouter';
|
||||||
import { Permission, useUser } from '../../../../hooks/useUser';
|
import { Permission, useUser } from '../../../../hooks/useUser';
|
||||||
import globalMessages from '../../../../i18n/globalMessages';
|
import globalMessages from '../../../../i18n/globalMessages';
|
||||||
import Error from '../../../../pages/_error';
|
import Error from '../../../../pages/_error';
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { SaveIcon } from '@heroicons/react/outline';
|
import { SaveIcon } from '@heroicons/react/outline';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Form, Formik } from 'formik';
|
import { Form, Formik } from 'formik';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
import { useRouter } from '../../../../hooks/useRouter';
|
||||||
import { useUser } from '../../../../hooks/useUser';
|
import { useUser } from '../../../../hooks/useUser';
|
||||||
import globalMessages from '../../../../i18n/globalMessages';
|
import globalMessages from '../../../../i18n/globalMessages';
|
||||||
import Error from '../../../../pages/_error';
|
import Error from '../../../../pages/_error';
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { useRouter } from 'next/router';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { UserSettingsNotificationsResponse } from '../../../../server/interfaces/api/userSettingsInterfaces';
|
import { UserSettingsNotificationsResponse } from '../../../../server/interfaces/api/userSettingsInterfaces';
|
||||||
import { hasPermission, Permission } from '../../../../server/lib/permissions';
|
import { hasPermission, Permission } from '../../../../server/lib/permissions';
|
||||||
|
import { useRouter } from '../../../hooks/useRouter';
|
||||||
import useSettings from '../../../hooks/useSettings';
|
import useSettings from '../../../hooks/useSettings';
|
||||||
import { useUser } from '../../../hooks/useUser';
|
import { useUser } from '../../../hooks/useUser';
|
||||||
import globalMessages from '../../../i18n/globalMessages';
|
import globalMessages from '../../../i18n/globalMessages';
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
@@ -10,12 +8,14 @@ import {
|
|||||||
} from '../../../server/interfaces/api/userInterfaces';
|
} from '../../../server/interfaces/api/userInterfaces';
|
||||||
import { MovieDetails } from '../../../server/models/Movie';
|
import { MovieDetails } from '../../../server/models/Movie';
|
||||||
import { TvDetails } from '../../../server/models/Tv';
|
import { TvDetails } from '../../../server/models/Tv';
|
||||||
|
import { useRouter } from '../../hooks/useRouter';
|
||||||
import { Permission, useUser } from '../../hooks/useUser';
|
import { Permission, useUser } from '../../hooks/useUser';
|
||||||
import Error from '../../pages/_error';
|
import Error from '../../pages/_error';
|
||||||
import ImageFader from '../Common/ImageFader';
|
import ImageFader from '../Common/ImageFader';
|
||||||
import LoadingSpinner from '../Common/LoadingSpinner';
|
import LoadingSpinner from '../Common/LoadingSpinner';
|
||||||
import PageTitle from '../Common/PageTitle';
|
import PageTitle from '../Common/PageTitle';
|
||||||
import ProgressCircle from '../Common/ProgressCircle';
|
import ProgressCircle from '../Common/ProgressCircle';
|
||||||
|
import Link from '../Link';
|
||||||
import RequestCard from '../RequestCard';
|
import RequestCard from '../RequestCard';
|
||||||
import Slider from '../Slider';
|
import Slider from '../Slider';
|
||||||
import ProfileHeader from './ProfileHeader';
|
import ProfileHeader from './ProfileHeader';
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import { useUser, User } from '../hooks/useUser';
|
import { useUser, User } from '../hooks/useUser';
|
||||||
import { useRouter } from 'next/dist/client/router';
|
import { useRouter } from 'next/dist/client/router';
|
||||||
|
import addBasePath from '../utils/addBasePath';
|
||||||
|
|
||||||
interface UserContextProps {
|
interface UserContextProps {
|
||||||
initialUser: User;
|
initialUser: User;
|
||||||
@@ -30,7 +31,7 @@ export const UserContext: React.FC<UserContextProps> = ({
|
|||||||
!routing.current
|
!routing.current
|
||||||
) {
|
) {
|
||||||
routing.current = true;
|
routing.current = true;
|
||||||
location.href = '/login';
|
location.href = addBasePath('/login');
|
||||||
}
|
}
|
||||||
}, [router, user, error]);
|
}, [router, user, error]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useRouter } from 'next/router';
|
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
import { useRouter } from './useRouter';
|
||||||
import { Permission, PermissionCheckOptions, useUser } from './useUser';
|
import { Permission, PermissionCheckOptions, useUser } from './useUser';
|
||||||
|
|
||||||
const useRouteGuard = (
|
const useRouteGuard = (
|
||||||
|
|||||||
67
src/hooks/useRouter.ts
Normal file
67
src/hooks/useRouter.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import { NextRouter, useRouter as useNextRouter } from 'next/router';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import addBasePath from '../utils/addBasePath';
|
||||||
|
|
||||||
|
const basePath = addBasePath('');
|
||||||
|
|
||||||
|
const urlPropertyFields = [
|
||||||
|
'pathname',
|
||||||
|
'events',
|
||||||
|
'route',
|
||||||
|
'query',
|
||||||
|
'asPath',
|
||||||
|
'components',
|
||||||
|
'isFallback',
|
||||||
|
'basePath',
|
||||||
|
'locale',
|
||||||
|
'locales',
|
||||||
|
'defaultLocale',
|
||||||
|
'isReady',
|
||||||
|
'isPreview',
|
||||||
|
'isLocaleDomain',
|
||||||
|
'domainLocales',
|
||||||
|
];
|
||||||
|
|
||||||
|
const coreMethodFields = ['reload', 'back', 'prefetch', 'beforePopState'];
|
||||||
|
|
||||||
|
const wrapRouter = (target: any): NextRouter => {
|
||||||
|
const router = {
|
||||||
|
push(url: URL, as?: URL, options?: never) {
|
||||||
|
return target.push(
|
||||||
|
addBasePath(url),
|
||||||
|
as ? addBasePath(as) : undefined,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
},
|
||||||
|
replace(url: URL, as?: URL, options?: never) {
|
||||||
|
return target.push(
|
||||||
|
addBasePath(url),
|
||||||
|
as ? addBasePath(as) : undefined,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
},
|
||||||
|
get basePath() {
|
||||||
|
return basePath;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
urlPropertyFields.forEach((field: string) => {
|
||||||
|
Object.defineProperty(router, field, {
|
||||||
|
get() {
|
||||||
|
return target[field] as string;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
coreMethodFields.forEach((field: string) => {
|
||||||
|
(router as any)[field] = (...args: any[]) => {
|
||||||
|
return target[field](...args);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return router as NextRouter;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useRouter = (): NextRouter => {
|
||||||
|
const router = useNextRouter();
|
||||||
|
return useMemo(() => wrapRouter(router), [router]);
|
||||||
|
};
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
import type { UrlObject } from 'url';
|
import type { UrlObject } from 'url';
|
||||||
import { useEffect, useState, Dispatch, SetStateAction } from 'react';
|
import { useEffect, useState, Dispatch, SetStateAction } from 'react';
|
||||||
import useDebouncedState from './useDebouncedState';
|
import useDebouncedState from './useDebouncedState';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import type { Nullable } from '../utils/typeHelpers';
|
import type { Nullable } from '../utils/typeHelpers';
|
||||||
|
import { useRouter } from './useRouter';
|
||||||
|
|
||||||
type Url = string | UrlObject;
|
type Url = string | UrlObject;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { NextRouter, useRouter } from 'next/router';
|
import { NextRouter } from 'next/router';
|
||||||
import { ParsedUrlQuery } from 'querystring';
|
import { ParsedUrlQuery } from 'querystring';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
import { useRouter } from './useRouter';
|
||||||
|
|
||||||
type UseQueryParamReturnedFunction = (
|
type UseQueryParamReturnedFunction = (
|
||||||
query: ParsedUrlQuery,
|
query: ParsedUrlQuery,
|
||||||
|
|||||||
@@ -680,13 +680,15 @@
|
|||||||
"components.Settings.apikey": "API Key",
|
"components.Settings.apikey": "API Key",
|
||||||
"components.Settings.applicationTitle": "Application Title",
|
"components.Settings.applicationTitle": "Application Title",
|
||||||
"components.Settings.applicationurl": "Application URL",
|
"components.Settings.applicationurl": "Application URL",
|
||||||
|
"components.Settings.basePath": "Base Path",
|
||||||
|
"components.Settings.basePathTip": "Configure Overseerr to run under a sub-path of a domain (Overseerr must be restarted for changes to take effect)",
|
||||||
"components.Settings.cacheImages": "Enable Image Caching",
|
"components.Settings.cacheImages": "Enable Image Caching",
|
||||||
"components.Settings.cacheImagesTip": "Optimize and store all images locally (consumes a significant amount of disk space)",
|
"components.Settings.cacheImagesTip": "Optimize and store all images locally (consumes a significant amount of disk space)",
|
||||||
"components.Settings.cancelscan": "Cancel Scan",
|
"components.Settings.cancelscan": "Cancel Scan",
|
||||||
"components.Settings.copied": "Copied API key to clipboard.",
|
"components.Settings.copied": "Copied API key to clipboard.",
|
||||||
"components.Settings.csrfProtection": "Enable CSRF Protection",
|
"components.Settings.csrfProtection": "Enable CSRF Protection",
|
||||||
"components.Settings.csrfProtectionHoverTip": "Do NOT enable this setting unless you understand what you are doing!",
|
"components.Settings.csrfProtectionHoverTip": "Do NOT enable this setting unless you understand what you are doing!",
|
||||||
"components.Settings.csrfProtectionTip": "Set external API access to read-only (requires HTTPS, and Overseerr must be reloaded for changes to take effect)",
|
"components.Settings.csrfProtectionTip": "Set external API access to read-only (requires HTTPS, and Overseerr must be restarted for changes to take effect)",
|
||||||
"components.Settings.currentlibrary": "Current Library: {name}",
|
"components.Settings.currentlibrary": "Current Library: {name}",
|
||||||
"components.Settings.default": "Default",
|
"components.Settings.default": "Default",
|
||||||
"components.Settings.default4k": "Default 4K",
|
"components.Settings.default4k": "Default 4K",
|
||||||
@@ -758,10 +760,12 @@
|
|||||||
"components.Settings.toastSettingsFailure": "Something went wrong while saving settings.",
|
"components.Settings.toastSettingsFailure": "Something went wrong while saving settings.",
|
||||||
"components.Settings.toastSettingsSuccess": "Settings saved successfully!",
|
"components.Settings.toastSettingsSuccess": "Settings saved successfully!",
|
||||||
"components.Settings.trustProxy": "Enable Proxy Support",
|
"components.Settings.trustProxy": "Enable Proxy Support",
|
||||||
"components.Settings.trustProxyTip": "Allow Overseerr to correctly register client IP addresses behind a proxy (Overseerr must be reloaded for changes to take effect)",
|
"components.Settings.trustProxyTip": "Allow Overseerr to correctly register client IP addresses behind a proxy (Overseerr must be restarted for changes to take effect)",
|
||||||
"components.Settings.validationApplicationTitle": "You must provide an application title",
|
"components.Settings.validationApplicationTitle": "You must provide an application title",
|
||||||
"components.Settings.validationApplicationUrl": "You must provide a valid URL",
|
"components.Settings.validationApplicationUrl": "You must provide a valid URL",
|
||||||
"components.Settings.validationApplicationUrlTrailingSlash": "URL must not end in a trailing slash",
|
"components.Settings.validationApplicationUrlTrailingSlash": "URL must not end in a trailing slash",
|
||||||
|
"components.Settings.validationBasePathLeadingSlash": "Path must begin with a leading slash",
|
||||||
|
"components.Settings.validationBasePathTrailingSlash": "Path must not end in a trailing slash",
|
||||||
"components.Settings.validationHostnameRequired": "You must provide a valid hostname or IP address",
|
"components.Settings.validationHostnameRequired": "You must provide a valid hostname or IP address",
|
||||||
"components.Settings.validationPortRequired": "You must provide a valid port number",
|
"components.Settings.validationPortRequired": "You must provide a valid port number",
|
||||||
"components.Settings.validationWebAppUrl": "You must provide a valid Plex Web App URL",
|
"components.Settings.validationWebAppUrl": "You must provide a valid Plex Web App URL",
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
||||||
import Link from 'next/link';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import PageTitle from '../components/Common/PageTitle';
|
import PageTitle from '../components/Common/PageTitle';
|
||||||
|
import Link from '../components/Link';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
errormessagewithcode: '{statusCode} - {error}',
|
errormessagewithcode: '{statusCode} - {error}',
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ import { SettingsProvider } from '../context/SettingsContext';
|
|||||||
import { UserContext } from '../context/UserContext';
|
import { UserContext } from '../context/UserContext';
|
||||||
import { User } from '../hooks/useUser';
|
import { User } from '../hooks/useUser';
|
||||||
import '../styles/globals.css';
|
import '../styles/globals.css';
|
||||||
|
import addBasePath from '../utils/addBasePath';
|
||||||
|
|
||||||
|
const basePath = addBasePath('');
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const loadLocaleData = (locale: AvailableLocale): Promise<any> => {
|
const loadLocaleData = (locale: AvailableLocale): Promise<any> => {
|
||||||
switch (locale) {
|
switch (locale) {
|
||||||
@@ -85,6 +87,10 @@ if (typeof window === 'undefined') {
|
|||||||
global.Intl = require('intl');
|
global.Intl = require('intl');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (basePath) {
|
||||||
|
axios.defaults.baseURL = basePath;
|
||||||
|
}
|
||||||
|
|
||||||
const CoreApp: Omit<NextAppComponentType, 'origGetInitialProps'> = ({
|
const CoreApp: Omit<NextAppComponentType, 'origGetInitialProps'> = ({
|
||||||
Component,
|
Component,
|
||||||
pageProps,
|
pageProps,
|
||||||
@@ -172,9 +178,10 @@ CoreApp.getInitialProps = async (initialProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (ctx.res) {
|
if (ctx.res) {
|
||||||
|
const serverUrl = `http://localhost:${process.env.PORT || 5055}${basePath}`;
|
||||||
// Check if app is initialized and redirect if necessary
|
// Check if app is initialized and redirect if necessary
|
||||||
const response = await axios.get<PublicSettingsResponse>(
|
const response = await axios.get<PublicSettingsResponse>(
|
||||||
`http://localhost:${process.env.PORT || 5055}/api/v1/settings/public`
|
`${serverUrl}/api/v1/settings/public`
|
||||||
);
|
);
|
||||||
|
|
||||||
currentSettings = response.data;
|
currentSettings = response.data;
|
||||||
@@ -184,22 +191,21 @@ CoreApp.getInitialProps = async (initialProps) => {
|
|||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
if (!router.pathname.match(/(setup|login\/plex)/)) {
|
if (!router.pathname.match(/(setup|login\/plex)/)) {
|
||||||
ctx.res.writeHead(307, {
|
ctx.res.writeHead(307, {
|
||||||
Location: '/setup',
|
Location: addBasePath('/setup'),
|
||||||
});
|
});
|
||||||
ctx.res.end();
|
ctx.res.end();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
// Attempt to get the user by running a request to the local api
|
// Attempt to get the user by running a request to the local api
|
||||||
const response = await axios.get<User>(
|
const response = await axios.get<User>(`${serverUrl}/api/v1/auth/me`, {
|
||||||
`http://localhost:${process.env.PORT || 5055}/api/v1/auth/me`,
|
headers: ctx.req ? { cookie: ctx.req.headers.cookie } : undefined,
|
||||||
{ headers: ctx.req ? { cookie: ctx.req.headers.cookie } : undefined }
|
});
|
||||||
);
|
|
||||||
user = response.data;
|
user = response.data;
|
||||||
|
|
||||||
if (router.pathname.match(/(setup|login)/)) {
|
if (router.pathname.match(/(setup|login)/)) {
|
||||||
ctx.res.writeHead(307, {
|
ctx.res.writeHead(307, {
|
||||||
Location: '/',
|
Location: addBasePath('/'),
|
||||||
});
|
});
|
||||||
ctx.res.end();
|
ctx.res.end();
|
||||||
}
|
}
|
||||||
@@ -209,7 +215,7 @@ CoreApp.getInitialProps = async (initialProps) => {
|
|||||||
// before anything actually renders
|
// before anything actually renders
|
||||||
if (!router.pathname.match(/(login|setup|resetpassword)/)) {
|
if (!router.pathname.match(/(login|setup|resetpassword)/)) {
|
||||||
ctx.res.writeHead(307, {
|
ctx.res.writeHead(307, {
|
||||||
Location: '/login',
|
Location: addBasePath('/login'),
|
||||||
});
|
});
|
||||||
ctx.res.end();
|
ctx.res.end();
|
||||||
}
|
}
|
||||||
@@ -223,7 +229,7 @@ CoreApp.getInitialProps = async (initialProps) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const locale = user?.settings?.locale
|
const locale = user?.settings?.locale
|
||||||
? user.settings.locale
|
? user?.settings.locale
|
||||||
: currentSettings.locale;
|
: currentSettings.locale;
|
||||||
|
|
||||||
const messages = await loadLocaleData(locale as AvailableLocale);
|
const messages = await loadLocaleData(locale as AvailableLocale);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
||||||
import type { NextPage } from 'next';
|
import type { NextPage } from 'next';
|
||||||
import Link from 'next/link';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import PageTitle from '../components/Common/PageTitle';
|
import PageTitle from '../components/Common/PageTitle';
|
||||||
|
import Link from '../components/Link';
|
||||||
import type { Undefinable } from '../utils/typeHelpers';
|
import type { Undefinable } from '../utils/typeHelpers';
|
||||||
|
|
||||||
interface ErrorProps {
|
interface ErrorProps {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { GetServerSideProps, NextPage } from 'next';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { Collection } from '../../../../server/models/Collection';
|
import type { Collection } from '../../../../server/models/Collection';
|
||||||
import CollectionDetails from '../../../components/CollectionDetails';
|
import CollectionDetails from '../../../components/CollectionDetails';
|
||||||
|
import addBasePath from '../../../utils/addBasePath';
|
||||||
|
|
||||||
interface CollectionPageProps {
|
interface CollectionPageProps {
|
||||||
collection?: Collection;
|
collection?: Collection;
|
||||||
@@ -12,12 +13,14 @@ const CollectionPage: NextPage<CollectionPageProps> = ({ collection }) => {
|
|||||||
return <CollectionDetails collection={collection} />;
|
return <CollectionDetails collection={collection} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const basePath = addBasePath('');
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps<CollectionPageProps> =
|
export const getServerSideProps: GetServerSideProps<CollectionPageProps> =
|
||||||
async (ctx) => {
|
async (ctx) => {
|
||||||
const response = await axios.get<Collection>(
|
const response = await axios.get<Collection>(
|
||||||
`http://localhost:${process.env.PORT || 5055}/api/v1/collection/${
|
`http://localhost:${
|
||||||
ctx.query.collectionId
|
process.env.PORT || 5055
|
||||||
}`,
|
}${basePath}/api/v1/collection/${ctx.query.collectionId}`,
|
||||||
{
|
{
|
||||||
headers: ctx.req?.headers?.cookie
|
headers: ctx.req?.headers?.cookie
|
||||||
? { cookie: ctx.req.headers.cookie }
|
? { cookie: ctx.req.headers.cookie }
|
||||||
|
|||||||
@@ -3,11 +3,14 @@ import { NextPage } from 'next';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { MovieDetails as MovieDetailsType } from '../../../../server/models/Movie';
|
import type { MovieDetails as MovieDetailsType } from '../../../../server/models/Movie';
|
||||||
import MovieDetails from '../../../components/MovieDetails';
|
import MovieDetails from '../../../components/MovieDetails';
|
||||||
|
import addBasePath from '../../../utils/addBasePath';
|
||||||
|
|
||||||
interface MoviePageProps {
|
interface MoviePageProps {
|
||||||
movie?: MovieDetailsType;
|
movie?: MovieDetailsType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const basePath = addBasePath('');
|
||||||
|
|
||||||
const MoviePage: NextPage<MoviePageProps> = ({ movie }) => {
|
const MoviePage: NextPage<MoviePageProps> = ({ movie }) => {
|
||||||
return <MovieDetails movie={movie} />;
|
return <MovieDetails movie={movie} />;
|
||||||
};
|
};
|
||||||
@@ -15,7 +18,7 @@ const MoviePage: NextPage<MoviePageProps> = ({ movie }) => {
|
|||||||
MoviePage.getInitialProps = async (ctx) => {
|
MoviePage.getInitialProps = async (ctx) => {
|
||||||
if (ctx.req) {
|
if (ctx.req) {
|
||||||
const response = await axios.get<MovieDetailsType>(
|
const response = await axios.get<MovieDetailsType>(
|
||||||
`http://localhost:${process.env.PORT || 5055}/api/v1/movie/${
|
`http://localhost:${process.env.PORT || 5055}${basePath}/api/v1/movie/${
|
||||||
ctx.query.movieId
|
ctx.query.movieId
|
||||||
}`,
|
}`,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,11 +3,14 @@ import { NextPage } from 'next';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { TvDetails as TvDetailsType } from '../../../../server/models/Tv';
|
import type { TvDetails as TvDetailsType } from '../../../../server/models/Tv';
|
||||||
import TvDetails from '../../../components/TvDetails';
|
import TvDetails from '../../../components/TvDetails';
|
||||||
|
import addBasePath from '../../../utils/addBasePath';
|
||||||
|
|
||||||
interface TvPageProps {
|
interface TvPageProps {
|
||||||
tv?: TvDetailsType;
|
tv?: TvDetailsType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const basePath = addBasePath('');
|
||||||
|
|
||||||
const TvPage: NextPage<TvPageProps> = ({ tv }) => {
|
const TvPage: NextPage<TvPageProps> = ({ tv }) => {
|
||||||
return <TvDetails tv={tv} />;
|
return <TvDetails tv={tv} />;
|
||||||
};
|
};
|
||||||
@@ -15,7 +18,7 @@ const TvPage: NextPage<TvPageProps> = ({ tv }) => {
|
|||||||
TvPage.getInitialProps = async (ctx) => {
|
TvPage.getInitialProps = async (ctx) => {
|
||||||
if (ctx.req) {
|
if (ctx.req) {
|
||||||
const response = await axios.get<TvDetailsType>(
|
const response = await axios.get<TvDetailsType>(
|
||||||
`http://localhost:${process.env.PORT || 5055}/api/v1/tv/${
|
`http://localhost:${process.env.PORT || 5055}${basePath}/api/v1/tv/${
|
||||||
ctx.query.tvId
|
ctx.query.tvId
|
||||||
}`,
|
}`,
|
||||||
{
|
{
|
||||||
|
|||||||
20
src/utils/addBasePath.ts
Normal file
20
src/utils/addBasePath.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import getConfig from 'next/config';
|
||||||
|
import { UrlObject } from 'url';
|
||||||
|
|
||||||
|
const {
|
||||||
|
publicRuntimeConfig: { basePath = '' },
|
||||||
|
} = getConfig();
|
||||||
|
|
||||||
|
declare type Url = string | UrlObject;
|
||||||
|
|
||||||
|
export default function addBasePath(url: UrlObject): UrlObject;
|
||||||
|
export default function addBasePath(url: string): string;
|
||||||
|
export default function addBasePath(url: Url): Url;
|
||||||
|
export default function addBasePath(url: Url): Url {
|
||||||
|
if (typeof url === 'string') {
|
||||||
|
return basePath + url;
|
||||||
|
}
|
||||||
|
|
||||||
|
url.pathname = basePath + url.pathname;
|
||||||
|
return url;
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import Bowser from 'bowser';
|
import Bowser from 'bowser';
|
||||||
|
import addBasePath from './addBasePath';
|
||||||
|
|
||||||
interface PlexHeaders {
|
interface PlexHeaders {
|
||||||
Accept: string;
|
Accept: string;
|
||||||
@@ -177,7 +178,7 @@ class PlexOAuth {
|
|||||||
|
|
||||||
//Set url to login/plex/loading so browser doesn't block popup
|
//Set url to login/plex/loading so browser doesn't block popup
|
||||||
const newWindow = window.open(
|
const newWindow = window.open(
|
||||||
'/login/plex/loading',
|
addBasePath('/login/plex/loading'),
|
||||||
title,
|
title,
|
||||||
'scrollbars=yes, width=' +
|
'scrollbars=yes, width=' +
|
||||||
w +
|
w +
|
||||||
|
|||||||
Reference in New Issue
Block a user