Compare commits
2 Commits
build/use-
...
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.
|
||||
// This variable is intentionally declared and unused.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const OFFLINE_VERSION = 3;
|
||||
const OFFLINE_VERSION = 4;
|
||||
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.
|
||||
const OFFLINE_URL = "/offline.html";
|
||||
const OFFLINE_URL = BASE_PATH + "/offline.html";
|
||||
|
||||
self.addEventListener("install", (event) => {
|
||||
event.waitUntil(
|
||||
@@ -120,16 +123,16 @@ self.addEventListener('notificationclick', (event) => {
|
||||
event.notification.close();
|
||||
|
||||
if (event.action === 'approve') {
|
||||
fetch(`/api/v1/request/${notificationData.requestId}/approve`, {
|
||||
fetch(`${BASE_PATH}/api/v1/request/${notificationData.requestId}/approve`, {
|
||||
method: 'POST',
|
||||
});
|
||||
} else if (event.action === 'decline') {
|
||||
fetch(`/api/v1/request/${notificationData.requestId}/decline`, {
|
||||
fetch(`${BASE_PATH}/api/v1/request/${notificationData.requestId}/decline`, {
|
||||
method: 'POST',
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (notificationData.actionUrl) {
|
||||
clients.openWindow(notificationData.actionUrl);
|
||||
clients.openWindow(BASE_PATH + notificationData.actionUrl);
|
||||
}
|
||||
}, false);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { getClientIp } from '@supercharge/request-ip';
|
||||
import { TypeormStore } from 'connect-typeorm/out';
|
||||
import cookieParser from 'cookie-parser';
|
||||
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 session, { Store } from 'express-session';
|
||||
import next from 'next';
|
||||
@@ -30,17 +30,13 @@ import routes from './routes';
|
||||
import { getAppVersion } from './utils/appVersion';
|
||||
|
||||
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()}`);
|
||||
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
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
await dbConnection.query('PRAGMA foreign_keys=OFF');
|
||||
@@ -51,6 +47,34 @@ app
|
||||
// Load Settings
|
||||
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
|
||||
if (
|
||||
settings.plex.libraries.length > 1 &&
|
||||
@@ -131,7 +155,9 @@ app
|
||||
|
||||
// Set up sessions
|
||||
const sessionRespository = getRepository(Session);
|
||||
server.use(
|
||||
const apiRoutes = Router();
|
||||
server.use(basePath || '/', apiRoutes);
|
||||
apiRoutes.use(
|
||||
'/api',
|
||||
session({
|
||||
secret: settings.clientId,
|
||||
@@ -150,8 +176,8 @@ app
|
||||
})
|
||||
);
|
||||
const apiDocs = YAML.load(API_SPEC_PATH);
|
||||
server.use('/api-docs', swaggerUi.serve, swaggerUi.setup(apiDocs));
|
||||
server.use(
|
||||
apiRoutes.use('/api-docs', swaggerUi.serve, swaggerUi.setup(apiDocs));
|
||||
apiRoutes.use(
|
||||
OpenApiValidator.middleware({
|
||||
apiSpec: API_SPEC_PATH,
|
||||
validateRequests: true,
|
||||
@@ -162,15 +188,15 @@ app
|
||||
* OpenAPI validator. Otherwise, they are treated as objects instead of strings
|
||||
* and response validation will fail
|
||||
*/
|
||||
server.use((_req, res, next) => {
|
||||
apiRoutes.use((_req, res, next) => {
|
||||
const original = res.json;
|
||||
res.json = function jsonp(json) {
|
||||
return original.call(this, JSON.parse(JSON.stringify(json)));
|
||||
};
|
||||
next();
|
||||
});
|
||||
server.use('/api/v1', routes);
|
||||
server.get('*', (req, res) => handle(req, res));
|
||||
apiRoutes.use('/api/v1', routes);
|
||||
apiRoutes.get('*', (req, res) => handle(req, res));
|
||||
server.use(
|
||||
(
|
||||
err: { status: number; message: string; errors: string[] },
|
||||
|
||||
@@ -92,6 +92,7 @@ export interface MainSettings {
|
||||
trustProxy: boolean;
|
||||
partialRequestsEnabled: boolean;
|
||||
locale: string;
|
||||
basePath: string;
|
||||
}
|
||||
|
||||
interface PublicSettings {
|
||||
@@ -271,6 +272,7 @@ class Settings {
|
||||
trustProxy: false,
|
||||
partialRequestsEnabled: true,
|
||||
locale: 'en',
|
||||
basePath: '',
|
||||
},
|
||||
plex: {
|
||||
name: '',
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { DownloadIcon } from '@heroicons/react/outline';
|
||||
import { uniq } from 'lodash';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
@@ -15,6 +14,7 @@ import ButtonWithDropdown from '../Common/ButtonWithDropdown';
|
||||
import CachedImage from '../Common/CachedImage';
|
||||
import LoadingSpinner from '../Common/LoadingSpinner';
|
||||
import PageTitle from '../Common/PageTitle';
|
||||
import Link from '../Link';
|
||||
import RequestModal from '../RequestModal';
|
||||
import Slider from '../Slider';
|
||||
import StatusBadge from '../StatusBadge';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Image, { ImageProps } from 'next/image';
|
||||
import React from 'react';
|
||||
import useSettings from '../../../hooks/useSettings';
|
||||
import addBasePath from '../../../utils/addBasePath';
|
||||
|
||||
/**
|
||||
* The CachedImage component should be used wherever
|
||||
@@ -12,6 +13,10 @@ import useSettings from '../../../hooks/useSettings';
|
||||
const CachedImage: React.FC<ImageProps> = (props) => {
|
||||
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} />;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { hasPermission, Permission } from '../../../../server/lib/permissions';
|
||||
import { useRouter } from '../../../hooks/useRouter';
|
||||
import { useUser } from '../../../hooks/useUser';
|
||||
import Link from '../../Link';
|
||||
|
||||
export interface SettingsRoute {
|
||||
text: string;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import type { MovieResult } from '../../../../server/models/Search';
|
||||
import { useRouter } from '../../../hooks/useRouter';
|
||||
import ListView from '../../Common/ListView';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import Header from '../../Common/Header';
|
||||
import PageTitle from '../../Common/PageTitle';
|
||||
import { useRouter } from 'next/router';
|
||||
import globalMessages from '../../../i18n/globalMessages';
|
||||
import useDiscover from '../../../hooks/useDiscover';
|
||||
import Error from '../../../pages/_error';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import type { TvResult } from '../../../../server/models/Search';
|
||||
import ListView from '../../Common/ListView';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import Header from '../../Common/Header';
|
||||
import PageTitle from '../../Common/PageTitle';
|
||||
import { useRouter } from 'next/router';
|
||||
import globalMessages from '../../../i18n/globalMessages';
|
||||
import useDiscover from '../../../hooks/useDiscover';
|
||||
import Error from '../../../pages/_error';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import type { MovieResult } from '../../../../server/models/Search';
|
||||
import { useRouter } from '../../../hooks/useRouter';
|
||||
import ListView from '../../Common/ListView';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import Header from '../../Common/Header';
|
||||
import PageTitle from '../../Common/PageTitle';
|
||||
import { useRouter } from 'next/router';
|
||||
import globalMessages from '../../../i18n/globalMessages';
|
||||
import useDiscover from '../../../hooks/useDiscover';
|
||||
import Error from '../../../pages/_error';
|
||||
|
||||
@@ -4,10 +4,10 @@ import ListView from '../../Common/ListView';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import Header from '../../Common/Header';
|
||||
import PageTitle from '../../Common/PageTitle';
|
||||
import { useRouter } from 'next/router';
|
||||
import globalMessages from '../../../i18n/globalMessages';
|
||||
import useDiscover from '../../../hooks/useDiscover';
|
||||
import Error from '../../../pages/_error';
|
||||
import { useRouter } from '../../../hooks/useRouter';
|
||||
|
||||
const messages = defineMessages({
|
||||
genreSeries: '{genre} Series',
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import type { TvResult } from '../../../../server/models/Search';
|
||||
import { useRouter } from '../../../hooks/useRouter';
|
||||
import ListView from '../../Common/ListView';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import Header from '../../Common/Header';
|
||||
import PageTitle from '../../Common/PageTitle';
|
||||
import { useRouter } from 'next/router';
|
||||
import globalMessages from '../../../i18n/globalMessages';
|
||||
import useDiscover from '../../../hooks/useDiscover';
|
||||
import Error from '../../../pages/_error';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
||||
import Link from 'next/link';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
import { GenreSliderItem } from '../../../../server/interfaces/api/discoverInterfaces';
|
||||
import GenreCard from '../../GenreCard';
|
||||
import Link from '../../Link';
|
||||
import Slider from '../../Slider';
|
||||
import { genreColorMap } from '../constants';
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
||||
import Link from 'next/link';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
import { GenreSliderItem } from '../../../../server/interfaces/api/discoverInterfaces';
|
||||
import GenreCard from '../../GenreCard';
|
||||
import Link from '../../Link';
|
||||
import Slider from '../../Slider';
|
||||
import { genreColorMap } from '../constants';
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
||||
import Link from 'next/link';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
import type { MediaResultsResponse } from '../../../server/interfaces/api/mediaInterfaces';
|
||||
import type { RequestResultsResponse } from '../../../server/interfaces/api/requestInterfaces';
|
||||
import PageTitle from '../Common/PageTitle';
|
||||
import Link from '../Link';
|
||||
import MediaSlider from '../MediaSlider';
|
||||
import RequestCard from '../RequestCard';
|
||||
import Slider from '../Slider';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Link from 'next/link';
|
||||
import React, { useState } from 'react';
|
||||
import { withProperties } from '../../utils/typeHelpers';
|
||||
import CachedImage from '../Common/CachedImage';
|
||||
import Link from '../Link';
|
||||
|
||||
interface GenreCardProps {
|
||||
name: string;
|
||||
|
||||
@@ -4,13 +4,13 @@ import {
|
||||
EyeIcon,
|
||||
UserIcon,
|
||||
} from '@heroicons/react/solid';
|
||||
import Link from 'next/link';
|
||||
import React from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import type Issue from '../../../server/entity/Issue';
|
||||
import globalMessages from '../../i18n/globalMessages';
|
||||
import Button from '../Common/Button';
|
||||
import { issueOptions } from '../IssueModal/constants';
|
||||
import Link from '../Link';
|
||||
|
||||
interface IssueBlockProps {
|
||||
issue: Issue;
|
||||
|
||||
@@ -3,7 +3,6 @@ import { ExclamationIcon } from '@heroicons/react/outline';
|
||||
import { DotsVerticalIcon } from '@heroicons/react/solid';
|
||||
import axios from 'axios';
|
||||
import { Field, Form, Formik } from 'formik';
|
||||
import Link from 'next/link';
|
||||
import React, { useState } from 'react';
|
||||
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
|
||||
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 Button from '../../Common/Button';
|
||||
import Modal from '../../Common/Modal';
|
||||
import Link from '../../Link';
|
||||
import Transition from '../../Transition';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
||||
@@ -8,8 +8,6 @@ import {
|
||||
import { RefreshIcon } from '@heroicons/react/solid';
|
||||
import axios from 'axios';
|
||||
import { Field, Form, Formik } from 'formik';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useState } from 'react';
|
||||
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
|
||||
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 { MovieDetails } from '../../../server/models/Movie';
|
||||
import type { TvDetails } from '../../../server/models/Tv';
|
||||
import { useRouter } from '../../hooks/useRouter';
|
||||
import { Permission, useUser } from '../../hooks/useUser';
|
||||
import globalMessages from '../../i18n/globalMessages';
|
||||
import Error from '../../pages/_error';
|
||||
@@ -30,6 +29,7 @@ import LoadingSpinner from '../Common/LoadingSpinner';
|
||||
import Modal from '../Common/Modal';
|
||||
import PageTitle from '../Common/PageTitle';
|
||||
import { issueOptions } from '../IssueModal/constants';
|
||||
import Link from '../Link';
|
||||
import Transition from '../Transition';
|
||||
import IssueComment from './IssueComment';
|
||||
import IssueDescription from './IssueDescription';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { EyeIcon } from '@heroicons/react/solid';
|
||||
import Link from 'next/link';
|
||||
import React from 'react';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
|
||||
@@ -15,6 +14,7 @@ import Badge from '../../Common/Badge';
|
||||
import Button from '../../Common/Button';
|
||||
import CachedImage from '../../Common/CachedImage';
|
||||
import { issueOptions } from '../../IssueModal/constants';
|
||||
import Link from '../../Link';
|
||||
|
||||
const messages = defineMessages({
|
||||
openeduserdate: '{date} by {user}',
|
||||
|
||||
@@ -4,12 +4,12 @@ import {
|
||||
FilterIcon,
|
||||
SortDescendingIcon,
|
||||
} from '@heroicons/react/solid';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
import { IssueResultsResponse } from '../../../server/interfaces/api/issueInterfaces';
|
||||
import Button from '../../components/Common/Button';
|
||||
import { useRouter } from '../../hooks/useRouter';
|
||||
import { useUpdateQueryParams } from '../../hooks/useUpdateQueryParams';
|
||||
import globalMessages from '../../i18n/globalMessages';
|
||||
import Header from '../Common/Header';
|
||||
|
||||
@@ -3,7 +3,6 @@ import { ExclamationIcon } from '@heroicons/react/outline';
|
||||
import { ArrowCircleRightIcon } from '@heroicons/react/solid';
|
||||
import axios from 'axios';
|
||||
import { Field, Formik } from 'formik';
|
||||
import Link from 'next/link';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useToasts } from 'react-toast-notifications';
|
||||
@@ -18,6 +17,7 @@ import { Permission, useUser } from '../../../hooks/useUser';
|
||||
import globalMessages from '../../../i18n/globalMessages';
|
||||
import Button from '../../Common/Button';
|
||||
import Modal from '../../Common/Modal';
|
||||
import Link from '../../Link';
|
||||
import { issueOptions } from '../constants';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
||||
@@ -6,12 +6,13 @@ import {
|
||||
UsersIcon,
|
||||
XIcon,
|
||||
} from '@heroicons/react/outline';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { ReactNode, useRef } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useClickOutside from '../../../hooks/useClickOutside';
|
||||
import { useRouter } from '../../../hooks/useRouter';
|
||||
import { Permission, useUser } from '../../../hooks/useUser';
|
||||
import addBasePath from '../../../utils/addBasePath';
|
||||
import Link from '../../Link';
|
||||
import Transition from '../../Transition';
|
||||
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">
|
||||
<span className="px-4 text-xl text-gray-50">
|
||||
<a href="/">
|
||||
<img src="/logo_full.svg" alt="Logo" />
|
||||
<a href={addBasePath('/')}>
|
||||
<img src={addBasePath('/logo_full.svg')} alt="Logo" />
|
||||
</a>
|
||||
</span>
|
||||
</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 items-center flex-shrink-0">
|
||||
<span className="px-4 text-2xl text-gray-50">
|
||||
<a href="/">
|
||||
<img src="/logo_full.svg" alt="Logo" />
|
||||
<a href={addBasePath('/')}>
|
||||
<img src={addBasePath('/logo_full.svg')} alt="Logo" />
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { LogoutIcon } from '@heroicons/react/outline';
|
||||
import { CogIcon, UserIcon } from '@heroicons/react/solid';
|
||||
import axios from 'axios';
|
||||
import Link from 'next/link';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useClickOutside from '../../../hooks/useClickOutside';
|
||||
import { useUser } from '../../../hooks/useUser';
|
||||
import Link from '../../Link';
|
||||
import Transition from '../../Transition';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
||||
@@ -4,11 +4,11 @@ import {
|
||||
CodeIcon,
|
||||
ServerIcon,
|
||||
} from '@heroicons/react/outline';
|
||||
import Link from 'next/link';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
import { StatusResponse } from '../../../../server/interfaces/api/settingsInterfaces';
|
||||
import Link from '../../Link';
|
||||
|
||||
const messages = defineMessages({
|
||||
streamdevelop: 'Overseerr Develop',
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { MenuAlt2Icon } from '@heroicons/react/outline';
|
||||
import { ArrowLeftIcon } from '@heroicons/react/solid';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { AvailableLocale } from '../../context/LanguageContext';
|
||||
import useLocale from '../../hooks/useLocale';
|
||||
import { useRouter } from '../../hooks/useRouter';
|
||||
import useSettings from '../../hooks/useSettings';
|
||||
import { useUser } from '../../hooks/useUser';
|
||||
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 React, { useEffect, useState } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useRouter } from '../../hooks/useRouter';
|
||||
|
||||
interface BarProps {
|
||||
progress: number;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { LoginIcon, SupportIcon } from '@heroicons/react/outline';
|
||||
import axios from 'axios';
|
||||
import { Field, Form, Formik } from 'formik';
|
||||
import Link from 'next/link';
|
||||
import React, { useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import * as Yup from 'yup';
|
||||
import useSettings from '../../hooks/useSettings';
|
||||
import Button from '../Common/Button';
|
||||
import SensitiveInput from '../Common/SensitiveInput';
|
||||
import Link from '../Link';
|
||||
|
||||
const messages = defineMessages({
|
||||
email: 'Email Address',
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { XCircleIcon } from '@heroicons/react/solid';
|
||||
import axios from 'axios';
|
||||
import { useRouter } from 'next/dist/client/router';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
import { useRouter } from '../../hooks/useRouter';
|
||||
import useSettings from '../../hooks/useSettings';
|
||||
import { useUser } from '../../hooks/useUser';
|
||||
import addBasePath from '../../utils/addBasePath';
|
||||
import Accordion from '../Common/Accordion';
|
||||
import ImageFader from '../Common/ImageFader';
|
||||
import PageTitle from '../Common/PageTitle';
|
||||
@@ -81,7 +82,11 @@ const Login: React.FC = () => {
|
||||
<LanguagePicker />
|
||||
</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">
|
||||
<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">
|
||||
{intl.formatMessage(messages.signinheader)}
|
||||
</h2>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ArrowCircleRightIcon } from '@heroicons/react/solid';
|
||||
import Link from 'next/link';
|
||||
import React, { useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import Link from '../../Link';
|
||||
|
||||
const messages = defineMessages({
|
||||
seemore: 'See More',
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
||||
import Link from 'next/link';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useSWRInfinite } from 'swr';
|
||||
import { MediaStatus } from '../../../server/constants/media';
|
||||
@@ -9,6 +8,7 @@ import type {
|
||||
TvResult,
|
||||
} from '../../../server/models/Search';
|
||||
import useSettings from '../../hooks/useSettings';
|
||||
import Link from '../Link';
|
||||
import PersonCard from '../PersonCard';
|
||||
import Slider from '../Slider';
|
||||
import TitleCard from '../TitleCard';
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
import { MovieDetails } from '../../../../server/models/Movie';
|
||||
import { useRouter } from '../../../hooks/useRouter';
|
||||
import Error from '../../../pages/_error';
|
||||
import Header from '../../Common/Header';
|
||||
import LoadingSpinner from '../../Common/LoadingSpinner';
|
||||
import PageTitle from '../../Common/PageTitle';
|
||||
import Link from '../../Link';
|
||||
import PersonCard from '../../PersonCard';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
import { MovieDetails } from '../../../../server/models/Movie';
|
||||
import { useRouter } from '../../../hooks/useRouter';
|
||||
import Error from '../../../pages/_error';
|
||||
import Header from '../../Common/Header';
|
||||
import LoadingSpinner from '../../Common/LoadingSpinner';
|
||||
import PageTitle from '../../Common/PageTitle';
|
||||
import Link from '../../Link';
|
||||
import PersonCard from '../../PersonCard';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
@@ -10,6 +9,7 @@ import Error from '../../pages/_error';
|
||||
import Header from '../Common/Header';
|
||||
import ListView from '../Common/ListView';
|
||||
import PageTitle from '../Common/PageTitle';
|
||||
import Link from '../Link';
|
||||
|
||||
const messages = defineMessages({
|
||||
recommendations: 'Recommendations',
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
import type { MovieDetails } from '../../../server/models/Movie';
|
||||
import type { MovieResult } from '../../../server/models/Search';
|
||||
import useDiscover from '../../hooks/useDiscover';
|
||||
import { useRouter } from '../../hooks/useRouter';
|
||||
import Error from '../../pages/_error';
|
||||
import Header from '../Common/Header';
|
||||
import ListView from '../Common/ListView';
|
||||
import PageTitle from '../Common/PageTitle';
|
||||
import Link from '../Link';
|
||||
|
||||
const messages = defineMessages({
|
||||
similar: 'Similar Titles',
|
||||
|
||||
@@ -14,8 +14,6 @@ import {
|
||||
import { hasFlag } from 'country-flag-icons';
|
||||
import 'country-flag-icons/3x2/flags.css';
|
||||
import { uniqBy } from 'lodash';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
@@ -29,6 +27,7 @@ import RTFresh from '../../assets/rt_fresh.svg';
|
||||
import RTRotten from '../../assets/rt_rotten.svg';
|
||||
import TmdbLogo from '../../assets/tmdb_logo.svg';
|
||||
import useLocale from '../../hooks/useLocale';
|
||||
import { useRouter } from '../../hooks/useRouter';
|
||||
import useSettings from '../../hooks/useSettings';
|
||||
import { Permission, useUser } from '../../hooks/useUser';
|
||||
import globalMessages from '../../i18n/globalMessages';
|
||||
@@ -41,6 +40,7 @@ import PageTitle from '../Common/PageTitle';
|
||||
import PlayButton, { PlayButtonLink } from '../Common/PlayButton';
|
||||
import ExternalLinkBlock from '../ExternalLinkBlock';
|
||||
import IssueModal from '../IssueModal';
|
||||
import Link from '../Link';
|
||||
import ManageSlideOver from '../ManageSlideOver';
|
||||
import MediaSlider from '../MediaSlider';
|
||||
import PersonCard from '../PersonCard';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import addBasePath from '../../utils/addBasePath';
|
||||
|
||||
interface PWAHeaderProps {
|
||||
applicationTitle?: string;
|
||||
@@ -10,148 +11,148 @@ const PWAHeader: React.FC<PWAHeaderProps> = ({ applicationTitle }) => {
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="/apple-touch-icon.png"
|
||||
href={addBasePath('/apple-touch-icon.png')}
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="/favicon-32x32.png"
|
||||
href={addBasePath('/favicon-32x32.png')}
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="/favicon-16x16.png"
|
||||
href={addBasePath('/favicon-16x16.png')}
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<link
|
||||
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)"
|
||||
/>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
@@ -161,7 +162,7 @@ const PWAHeader: React.FC<PWAHeaderProps> = ({ applicationTitle }) => {
|
||||
/>
|
||||
<link
|
||||
rel="manifest"
|
||||
href="/site.webmanifest"
|
||||
href={addBasePath('/site.webmanifest')}
|
||||
crossOrigin="use-credentials"
|
||||
/>
|
||||
<meta name="application-name" content={applicationTitle ?? 'Overseerr'} />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { UserCircleIcon } from '@heroicons/react/solid';
|
||||
import Link from 'next/link';
|
||||
import React, { useState } from 'react';
|
||||
import CachedImage from '../Common/CachedImage';
|
||||
import Link from '../Link';
|
||||
|
||||
interface PersonCardProps {
|
||||
personId: number;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { groupBy } from 'lodash';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import TruncateMarkup from 'react-truncate-markup';
|
||||
@@ -7,6 +6,7 @@ import useSWR from 'swr';
|
||||
import type { PersonCombinedCreditsResponse } from '../../../server/interfaces/api/personInterfaces';
|
||||
import type { PersonDetail } from '../../../server/models/Person';
|
||||
import Ellipsis from '../../assets/ellipsis.svg';
|
||||
import { useRouter } from '../../hooks/useRouter';
|
||||
import globalMessages from '../../i18n/globalMessages';
|
||||
import Error from '../../pages/_error';
|
||||
import CachedImage from '../Common/CachedImage';
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
XIcon,
|
||||
} from '@heroicons/react/solid';
|
||||
import axios from 'axios';
|
||||
import Link from 'next/link';
|
||||
import React, { useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { MediaRequestStatus } from '../../../server/constants/media';
|
||||
@@ -17,6 +16,7 @@ import useRequestOverride from '../../hooks/useRequestOverride';
|
||||
import globalMessages from '../../i18n/globalMessages';
|
||||
import Badge from '../Common/Badge';
|
||||
import Button from '../Common/Button';
|
||||
import Link from '../Link';
|
||||
import RequestModal from '../RequestModal';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
XIcon,
|
||||
} from '@heroicons/react/solid';
|
||||
import axios from 'axios';
|
||||
import Link from 'next/link';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
@@ -25,6 +24,7 @@ import { withProperties } from '../../utils/typeHelpers';
|
||||
import Badge from '../Common/Badge';
|
||||
import Button from '../Common/Button';
|
||||
import CachedImage from '../Common/CachedImage';
|
||||
import Link from '../Link';
|
||||
import RequestModal from '../RequestModal';
|
||||
import StatusBadge from '../StatusBadge';
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
XIcon,
|
||||
} from '@heroicons/react/solid';
|
||||
import axios from 'axios';
|
||||
import Link from 'next/link';
|
||||
import React, { useState } from 'react';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
|
||||
@@ -25,6 +24,7 @@ import Badge from '../../Common/Badge';
|
||||
import Button from '../../Common/Button';
|
||||
import CachedImage from '../../Common/CachedImage';
|
||||
import ConfirmButton from '../../Common/ConfirmButton';
|
||||
import Link from '../../Link';
|
||||
import RequestModal from '../../RequestModal';
|
||||
import StatusBadge from '../../StatusBadge';
|
||||
|
||||
|
||||
@@ -4,12 +4,11 @@ import {
|
||||
FilterIcon,
|
||||
SortDescendingIcon,
|
||||
} from '@heroicons/react/solid';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
import type { RequestResultsResponse } from '../../../server/interfaces/api/requestInterfaces';
|
||||
import { useRouter } from '../../hooks/useRouter';
|
||||
import { useUpdateQueryParams } from '../../hooks/useUpdateQueryParams';
|
||||
import { useUser } from '../../hooks/useUser';
|
||||
import globalMessages from '../../i18n/globalMessages';
|
||||
@@ -17,6 +16,7 @@ import Button from '../Common/Button';
|
||||
import Header from '../Common/Header';
|
||||
import LoadingSpinner from '../Common/LoadingSpinner';
|
||||
import PageTitle from '../Common/PageTitle';
|
||||
import Link from '../Link';
|
||||
import RequestItem from './RequestItem';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/solid';
|
||||
import Link from 'next/link';
|
||||
import React, { useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { QuotaStatus } from '../../../../server/interfaces/api/userInterfaces';
|
||||
import ProgressCircle from '../../Common/ProgressCircle';
|
||||
import Link from '../../Link';
|
||||
|
||||
const messages = defineMessages({
|
||||
requestsremaining:
|
||||
|
||||
@@ -5,6 +5,7 @@ import useSWR from 'swr';
|
||||
import { SonarrSeries } from '../../../../server/api/servarr/sonarr';
|
||||
import globalMessages from '../../../i18n/globalMessages';
|
||||
import Alert from '../../Common/Alert';
|
||||
import CachedImage from '../../Common/CachedImage';
|
||||
import { SmallLoadingSpinner } from '../../Common/LoadingSpinner';
|
||||
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">
|
||||
<img
|
||||
<CachedImage
|
||||
src={
|
||||
item.remotePoster ??
|
||||
'/images/overseerr_poster_not_found.png'
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { ArrowLeftIcon, MailIcon } from '@heroicons/react/solid';
|
||||
import axios from 'axios';
|
||||
import { Field, Form, Formik } from 'formik';
|
||||
import Link from 'next/link';
|
||||
import React, { useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import * as Yup from 'yup';
|
||||
import addBasePath from '../../utils/addBasePath';
|
||||
import Button from '../Common/Button';
|
||||
import ImageFader from '../Common/ImageFader';
|
||||
import PageTitle from '../Common/PageTitle';
|
||||
import LanguagePicker from '../Layout/LanguagePicker';
|
||||
import Link from '../Link';
|
||||
|
||||
const messages = defineMessages({
|
||||
passwordreset: 'Password Reset',
|
||||
@@ -49,7 +50,11 @@ const ResetPassword: React.FC = () => {
|
||||
<LanguagePicker />
|
||||
</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">
|
||||
<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">
|
||||
{intl.formatMessage(messages.resetpassword)}
|
||||
</h2>
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { SupportIcon } from '@heroicons/react/outline';
|
||||
import axios from 'axios';
|
||||
import { Form, Formik } from 'formik';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import * as Yup from 'yup';
|
||||
import { useRouter } from '../../hooks/useRouter';
|
||||
import globalMessages from '../../i18n/globalMessages';
|
||||
import addBasePath from '../../utils/addBasePath';
|
||||
import Button from '../Common/Button';
|
||||
import ImageFader from '../Common/ImageFader';
|
||||
import SensitiveInput from '../Common/SensitiveInput';
|
||||
import LanguagePicker from '../Layout/LanguagePicker';
|
||||
import Link from '../Link';
|
||||
|
||||
const messages = defineMessages({
|
||||
passwordreset: 'Password Reset',
|
||||
@@ -64,7 +65,11 @@ const ResetPassword: React.FC = () => {
|
||||
<LanguagePicker />
|
||||
</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">
|
||||
<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">
|
||||
{intl.formatMessage(messages.resetpassword)}
|
||||
</h2>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import {
|
||||
TvResult,
|
||||
MovieResult,
|
||||
PersonResult,
|
||||
} from '../../../server/models/Search';
|
||||
import { useRouter } from '../../hooks/useRouter';
|
||||
import ListView from '../Common/ListView';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import Header from '../Common/Header';
|
||||
|
||||
@@ -3,6 +3,7 @@ import axios from 'axios';
|
||||
import React, { useEffect } from 'react';
|
||||
import useSettings from '../../hooks/useSettings';
|
||||
import { useUser } from '../../hooks/useUser';
|
||||
import addBasePath from '../../utils/addBasePath';
|
||||
|
||||
const ServiceWorkerSetup: React.FC = () => {
|
||||
const { currentSettings } = useSettings();
|
||||
@@ -10,7 +11,7 @@ const ServiceWorkerSetup: React.FC = () => {
|
||||
useEffect(() => {
|
||||
if ('serviceWorker' in navigator && user?.id) {
|
||||
navigator.serviceWorker
|
||||
.register('/sw.js')
|
||||
.register(addBasePath('/sw.js'))
|
||||
.then(async (registration) => {
|
||||
console.log(
|
||||
'[SW] Registration successful, scope is:',
|
||||
|
||||
@@ -3,7 +3,6 @@ import { QuestionMarkCircleIcon, RefreshIcon } from '@heroicons/react/solid';
|
||||
import axios from 'axios';
|
||||
import { Field, Form, Formik } from 'formik';
|
||||
import dynamic from 'next/dynamic';
|
||||
import Link from 'next/link';
|
||||
import React, { useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useToasts } from 'react-toast-notifications';
|
||||
@@ -12,6 +11,7 @@ import * as Yup from 'yup';
|
||||
import globalMessages from '../../../../i18n/globalMessages';
|
||||
import Button from '../../../Common/Button';
|
||||
import LoadingSpinner from '../../../Common/LoadingSpinner';
|
||||
import Link from '../../../Link';
|
||||
import NotificationTypeSelector from '../../../NotificationTypeSelector';
|
||||
|
||||
const JSONEditor = dynamic(() => import('../../../JSONEditor'), { ssr: false });
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
PlayIcon,
|
||||
} from '@heroicons/react/solid';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useToasts } from 'react-toast-notifications';
|
||||
@@ -17,6 +16,7 @@ import {
|
||||
LogMessage,
|
||||
LogsResultsResponse,
|
||||
} from '../../../../server/interfaces/api/settingsInterfaces';
|
||||
import { useRouter } from '../../../hooks/useRouter';
|
||||
import { useUpdateQueryParams } from '../../../hooks/useUpdateQueryParams';
|
||||
import globalMessages from '../../../i18n/globalMessages';
|
||||
import Error from '../../../pages/_error';
|
||||
|
||||
@@ -33,6 +33,9 @@ const messages = defineMessages({
|
||||
apikey: 'API Key',
|
||||
applicationTitle: 'Application Title',
|
||||
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',
|
||||
regionTip: 'Filter content by regional availability',
|
||||
originallanguage: 'Discover Language',
|
||||
@@ -44,7 +47,7 @@ const messages = defineMessages({
|
||||
hideAvailable: 'Hide Available Media',
|
||||
csrfProtection: 'Enable CSRF Protection',
|
||||
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:
|
||||
'Do NOT enable this setting unless you understand what you are doing!',
|
||||
cacheImages: 'Enable Image Caching',
|
||||
@@ -52,10 +55,12 @@ const messages = defineMessages({
|
||||
'Optimize and store all images locally (consumes a significant amount of disk space)',
|
||||
trustProxy: 'Enable Proxy Support',
|
||||
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',
|
||||
validationApplicationUrl: 'You must provide a valid URL',
|
||||
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',
|
||||
locale: 'Display Language',
|
||||
});
|
||||
@@ -81,12 +86,18 @@ const SettingsMain: React.FC = () => {
|
||||
.test(
|
||||
'no-trailing-slash',
|
||||
intl.formatMessage(messages.validationApplicationUrlTrailingSlash),
|
||||
(value) => {
|
||||
if (value?.substr(value.length - 1) === '/') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
(value) => !value || !value.endsWith('/')
|
||||
),
|
||||
basePath: Yup.string()
|
||||
.test(
|
||||
'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={{
|
||||
applicationTitle: data?.applicationTitle,
|
||||
applicationUrl: data?.applicationUrl,
|
||||
basePath: data?.basePath,
|
||||
csrfProtection: data?.csrfProtection,
|
||||
hideAvailable: data?.hideAvailable,
|
||||
locale: data?.locale ?? 'en',
|
||||
@@ -147,6 +159,7 @@ const SettingsMain: React.FC = () => {
|
||||
await axios.post('/api/v1/settings/main', {
|
||||
applicationTitle: values.applicationTitle,
|
||||
applicationUrl: values.applicationUrl,
|
||||
basePath: values.basePath,
|
||||
csrfProtection: values.csrfProtection,
|
||||
hideAvailable: values.hideAvailable,
|
||||
locale: values.locale,
|
||||
@@ -255,6 +268,27 @@ const SettingsMain: React.FC = () => {
|
||||
)}
|
||||
</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">
|
||||
<label htmlFor="trustProxy" className="checkbox-label">
|
||||
<span>{intl.formatMessage(messages.trustProxy)}</span>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import axios from 'axios';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR, { mutate } from 'swr';
|
||||
import useLocale from '../../hooks/useLocale';
|
||||
import { useRouter } from '../../hooks/useRouter';
|
||||
import addBasePath from '../../utils/addBasePath';
|
||||
import AppDataWarning from '../AppDataWarning';
|
||||
import Badge from '../Common/Badge';
|
||||
import Button from '../Common/Button';
|
||||
@@ -72,7 +73,7 @@ const Setup: React.FC = () => {
|
||||
</div>
|
||||
<div className="relative z-40 px-4 sm:mx-auto sm:w-full sm:max-w-4xl">
|
||||
<img
|
||||
src="/logo_stacked.svg"
|
||||
src={addBasePath('/logo_stacked.svg')}
|
||||
className="max-w-full mb-10 sm:max-w-md sm:mx-auto"
|
||||
alt="Logo"
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { DownloadIcon } from '@heroicons/react/outline';
|
||||
import { BellIcon, CheckIcon, ClockIcon } from '@heroicons/react/solid';
|
||||
import Link from 'next/link';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { MediaStatus } from '../../../server/constants/media';
|
||||
@@ -12,6 +11,7 @@ import globalMessages from '../../i18n/globalMessages';
|
||||
import { withProperties } from '../../utils/typeHelpers';
|
||||
import Button from '../Common/Button';
|
||||
import CachedImage from '../Common/CachedImage';
|
||||
import Link from '../Link';
|
||||
import RequestModal from '../RequestModal';
|
||||
import Transition from '../Transition';
|
||||
import Placeholder from './Placeholder';
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
import type { TvDetails } from '../../../../server/models/Tv';
|
||||
import { useRouter } from '../../../hooks/useRouter';
|
||||
import Error from '../../../pages/_error';
|
||||
import Header from '../../Common/Header';
|
||||
import LoadingSpinner from '../../Common/LoadingSpinner';
|
||||
import PageTitle from '../../Common/PageTitle';
|
||||
import Link from '../../Link';
|
||||
import PersonCard from '../../PersonCard';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
import type { TvDetails } from '../../../../server/models/Tv';
|
||||
import { useRouter } from '../../../hooks/useRouter';
|
||||
import Error from '../../../pages/_error';
|
||||
import Header from '../../Common/Header';
|
||||
import LoadingSpinner from '../../Common/LoadingSpinner';
|
||||
import PageTitle from '../../Common/PageTitle';
|
||||
import Link from '../../Link';
|
||||
import PersonCard from '../../PersonCard';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
import type { TvResult } from '../../../server/models/Search';
|
||||
import { TvDetails } from '../../../server/models/Tv';
|
||||
import useDiscover from '../../hooks/useDiscover';
|
||||
import { useRouter } from '../../hooks/useRouter';
|
||||
import Error from '../../pages/_error';
|
||||
import Header from '../Common/Header';
|
||||
import ListView from '../Common/ListView';
|
||||
import PageTitle from '../Common/PageTitle';
|
||||
import Link from '../Link';
|
||||
|
||||
const messages = defineMessages({
|
||||
recommendations: 'Recommendations',
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
import type { TvResult } from '../../../server/models/Search';
|
||||
import type { TvDetails } from '../../../server/models/Tv';
|
||||
import useDiscover from '../../hooks/useDiscover';
|
||||
import { useRouter } from '../../hooks/useRouter';
|
||||
import Error from '../../pages/_error';
|
||||
import Header from '../Common/Header';
|
||||
import ListView from '../Common/ListView';
|
||||
import PageTitle from '../Common/PageTitle';
|
||||
import Link from '../Link';
|
||||
|
||||
const messages = defineMessages({
|
||||
similar: 'Similar Series',
|
||||
|
||||
@@ -7,8 +7,6 @@ import {
|
||||
} from '@heroicons/react/outline';
|
||||
import { hasFlag } from 'country-flag-icons';
|
||||
import 'country-flag-icons/3x2/flags.css';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
@@ -24,6 +22,7 @@ import RTFresh from '../../assets/rt_fresh.svg';
|
||||
import RTRotten from '../../assets/rt_rotten.svg';
|
||||
import TmdbLogo from '../../assets/tmdb_logo.svg';
|
||||
import useLocale from '../../hooks/useLocale';
|
||||
import { useRouter } from '../../hooks/useRouter';
|
||||
import useSettings from '../../hooks/useSettings';
|
||||
import { Permission, useUser } from '../../hooks/useUser';
|
||||
import globalMessages from '../../i18n/globalMessages';
|
||||
@@ -36,6 +35,7 @@ import PageTitle from '../Common/PageTitle';
|
||||
import PlayButton, { PlayButtonLink } from '../Common/PlayButton';
|
||||
import ExternalLinkBlock from '../ExternalLinkBlock';
|
||||
import IssueModal from '../IssueModal';
|
||||
import Link from '../Link';
|
||||
import ManageSlideOver from '../ManageSlideOver';
|
||||
import MediaSlider from '../MediaSlider';
|
||||
import PersonCard from '../PersonCard';
|
||||
|
||||
@@ -9,8 +9,6 @@ import {
|
||||
} from '@heroicons/react/solid';
|
||||
import axios from 'axios';
|
||||
import { Field, Form, Formik } from 'formik';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useToasts } from 'react-toast-notifications';
|
||||
@@ -18,6 +16,7 @@ import useSWR from 'swr';
|
||||
import * as Yup from 'yup';
|
||||
import type { UserResultsResponse } from '../../../server/interfaces/api/userInterfaces';
|
||||
import { hasPermission } from '../../../server/lib/permissions';
|
||||
import { useRouter } from '../../hooks/useRouter';
|
||||
import useSettings from '../../hooks/useSettings';
|
||||
import { useUpdateQueryParams } from '../../hooks/useUpdateQueryParams';
|
||||
import { Permission, User, UserType, useUser } from '../../hooks/useUser';
|
||||
@@ -31,6 +30,7 @@ import Modal from '../Common/Modal';
|
||||
import PageTitle from '../Common/PageTitle';
|
||||
import SensitiveInput from '../Common/SensitiveInput';
|
||||
import Table from '../Common/Table';
|
||||
import Link from '../Link';
|
||||
import Transition from '../Transition';
|
||||
import BulkEditModal from './BulkEditModal';
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { CogIcon, UserIcon } from '@heroicons/react/solid';
|
||||
import Link from 'next/link';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { Permission, User, useUser } from '../../../hooks/useUser';
|
||||
import Button from '../../Common/Button';
|
||||
import Link from '../../Link';
|
||||
|
||||
const messages = defineMessages({
|
||||
settings: 'Edit Settings',
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { SaveIcon } from '@heroicons/react/outline';
|
||||
import axios from 'axios';
|
||||
import { Field, Form, Formik } from 'formik';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useToasts } from 'react-toast-notifications';
|
||||
@@ -12,6 +11,7 @@ import {
|
||||
AvailableLocale,
|
||||
} from '../../../../context/LanguageContext';
|
||||
import useLocale from '../../../../hooks/useLocale';
|
||||
import { useRouter } from '../../../../hooks/useRouter';
|
||||
import useSettings from '../../../../hooks/useSettings';
|
||||
import { Permission, UserType, useUser } from '../../../../hooks/useUser';
|
||||
import globalMessages from '../../../../i18n/globalMessages';
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { SaveIcon } from '@heroicons/react/outline';
|
||||
import axios from 'axios';
|
||||
import { Field, Form, Formik } from 'formik';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useToasts } from 'react-toast-notifications';
|
||||
import useSWR from 'swr';
|
||||
import * as Yup from 'yup';
|
||||
import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
|
||||
import { useRouter } from '../../../../hooks/useRouter';
|
||||
import { useUser } from '../../../../hooks/useUser';
|
||||
import globalMessages from '../../../../i18n/globalMessages';
|
||||
import Button from '../../../Common/Button';
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { SaveIcon } from '@heroicons/react/outline';
|
||||
import axios from 'axios';
|
||||
import { Form, Formik } from 'formik';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useToasts } from 'react-toast-notifications';
|
||||
import useSWR from 'swr';
|
||||
import * as Yup from 'yup';
|
||||
import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
|
||||
import { useRouter } from '../../../../hooks/useRouter';
|
||||
import { useUser } from '../../../../hooks/useUser';
|
||||
import globalMessages from '../../../../i18n/globalMessages';
|
||||
import Badge from '../../../Common/Badge';
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import axios from 'axios';
|
||||
import { Form, Formik } from 'formik';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useToasts } from 'react-toast-notifications';
|
||||
import useSWR from 'swr';
|
||||
import * as Yup from 'yup';
|
||||
import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
|
||||
import { useRouter } from '../../../../hooks/useRouter';
|
||||
import { useUser } from '../../../../hooks/useUser';
|
||||
import globalMessages from '../../../../i18n/globalMessages';
|
||||
import Button from '../../../Common/Button';
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import axios from 'axios';
|
||||
import { Field, Form, Formik } from 'formik';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useToasts } from 'react-toast-notifications';
|
||||
import useSWR from 'swr';
|
||||
import * as Yup from 'yup';
|
||||
import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
|
||||
import { useRouter } from '../../../../hooks/useRouter';
|
||||
import useSettings from '../../../../hooks/useSettings';
|
||||
import { useUser } from '../../../../hooks/useUser';
|
||||
import globalMessages from '../../../../i18n/globalMessages';
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { SaveIcon } from '@heroicons/react/outline';
|
||||
import axios from 'axios';
|
||||
import { Field, Form, Formik } from 'formik';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useToasts } from 'react-toast-notifications';
|
||||
import useSWR from 'swr';
|
||||
import * as Yup from 'yup';
|
||||
import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
|
||||
import { useRouter } from '../../../../hooks/useRouter';
|
||||
import { useUser } from '../../../../hooks/useUser';
|
||||
import globalMessages from '../../../../i18n/globalMessages';
|
||||
import Button from '../../../Common/Button';
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { SaveIcon } from '@heroicons/react/outline';
|
||||
import axios from 'axios';
|
||||
import { Form, Formik } from 'formik';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useToasts } from 'react-toast-notifications';
|
||||
import useSWR, { mutate } from 'swr';
|
||||
import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces';
|
||||
import { useRouter } from '../../../../hooks/useRouter';
|
||||
import { useUser } from '../../../../hooks/useUser';
|
||||
import globalMessages from '../../../../i18n/globalMessages';
|
||||
import Button from '../../../Common/Button';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { CloudIcon, MailIcon } from '@heroicons/react/solid';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
@@ -8,6 +7,7 @@ import DiscordLogo from '../../../../assets/extlogos/discord.svg';
|
||||
import PushbulletLogo from '../../../../assets/extlogos/pushbullet.svg';
|
||||
import PushoverLogo from '../../../../assets/extlogos/pushover.svg';
|
||||
import TelegramLogo from '../../../../assets/extlogos/telegram.svg';
|
||||
import { useRouter } from '../../../../hooks/useRouter';
|
||||
import { useUser } from '../../../../hooks/useUser';
|
||||
import globalMessages from '../../../../i18n/globalMessages';
|
||||
import Error from '../../../../pages/_error';
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { SaveIcon } from '@heroicons/react/outline';
|
||||
import axios from 'axios';
|
||||
import { Form, Formik } from 'formik';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useToasts } from 'react-toast-notifications';
|
||||
import useSWR from 'swr';
|
||||
import * as Yup from 'yup';
|
||||
import { useRouter } from '../../../../hooks/useRouter';
|
||||
import { Permission, useUser } from '../../../../hooks/useUser';
|
||||
import globalMessages from '../../../../i18n/globalMessages';
|
||||
import Error from '../../../../pages/_error';
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { SaveIcon } from '@heroicons/react/outline';
|
||||
import axios from 'axios';
|
||||
import { Form, Formik } from 'formik';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useToasts } from 'react-toast-notifications';
|
||||
import useSWR from 'swr';
|
||||
import { useRouter } from '../../../../hooks/useRouter';
|
||||
import { useUser } from '../../../../hooks/useUser';
|
||||
import globalMessages from '../../../../i18n/globalMessages';
|
||||
import Error from '../../../../pages/_error';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
import { UserSettingsNotificationsResponse } from '../../../../server/interfaces/api/userSettingsInterfaces';
|
||||
import { hasPermission, Permission } from '../../../../server/lib/permissions';
|
||||
import { useRouter } from '../../../hooks/useRouter';
|
||||
import useSettings from '../../../hooks/useSettings';
|
||||
import { useUser } from '../../../hooks/useUser';
|
||||
import globalMessages from '../../../i18n/globalMessages';
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import useSWR from 'swr';
|
||||
@@ -10,12 +8,14 @@ import {
|
||||
} from '../../../server/interfaces/api/userInterfaces';
|
||||
import { MovieDetails } from '../../../server/models/Movie';
|
||||
import { TvDetails } from '../../../server/models/Tv';
|
||||
import { useRouter } from '../../hooks/useRouter';
|
||||
import { Permission, useUser } from '../../hooks/useUser';
|
||||
import Error from '../../pages/_error';
|
||||
import ImageFader from '../Common/ImageFader';
|
||||
import LoadingSpinner from '../Common/LoadingSpinner';
|
||||
import PageTitle from '../Common/PageTitle';
|
||||
import ProgressCircle from '../Common/ProgressCircle';
|
||||
import Link from '../Link';
|
||||
import RequestCard from '../RequestCard';
|
||||
import Slider from '../Slider';
|
||||
import ProfileHeader from './ProfileHeader';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { useUser, User } from '../hooks/useUser';
|
||||
import { useRouter } from 'next/dist/client/router';
|
||||
import addBasePath from '../utils/addBasePath';
|
||||
|
||||
interface UserContextProps {
|
||||
initialUser: User;
|
||||
@@ -30,7 +31,7 @@ export const UserContext: React.FC<UserContextProps> = ({
|
||||
!routing.current
|
||||
) {
|
||||
routing.current = true;
|
||||
location.href = '/login';
|
||||
location.href = addBasePath('/login');
|
||||
}
|
||||
}, [router, user, error]);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect } from 'react';
|
||||
import { useRouter } from './useRouter';
|
||||
import { Permission, PermissionCheckOptions, useUser } from './useUser';
|
||||
|
||||
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 { useEffect, useState, Dispatch, SetStateAction } from 'react';
|
||||
import useDebouncedState from './useDebouncedState';
|
||||
import { useRouter } from 'next/router';
|
||||
import type { Nullable } from '../utils/typeHelpers';
|
||||
import { useRouter } from './useRouter';
|
||||
|
||||
type Url = string | UrlObject;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { NextRouter, useRouter } from 'next/router';
|
||||
import { NextRouter } from 'next/router';
|
||||
import { ParsedUrlQuery } from 'querystring';
|
||||
import { useCallback } from 'react';
|
||||
import { useRouter } from './useRouter';
|
||||
|
||||
type UseQueryParamReturnedFunction = (
|
||||
query: ParsedUrlQuery,
|
||||
|
||||
@@ -680,13 +680,15 @@
|
||||
"components.Settings.apikey": "API Key",
|
||||
"components.Settings.applicationTitle": "Application Title",
|
||||
"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.cacheImagesTip": "Optimize and store all images locally (consumes a significant amount of disk space)",
|
||||
"components.Settings.cancelscan": "Cancel Scan",
|
||||
"components.Settings.copied": "Copied API key to clipboard.",
|
||||
"components.Settings.csrfProtection": "Enable CSRF Protection",
|
||||
"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.default": "Default",
|
||||
"components.Settings.default4k": "Default 4K",
|
||||
@@ -758,10 +760,12 @@
|
||||
"components.Settings.toastSettingsFailure": "Something went wrong while saving settings.",
|
||||
"components.Settings.toastSettingsSuccess": "Settings saved successfully!",
|
||||
"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.validationApplicationUrl": "You must provide a valid URL",
|
||||
"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.validationPortRequired": "You must provide a valid port number",
|
||||
"components.Settings.validationWebAppUrl": "You must provide a valid Plex Web App URL",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
||||
import Link from 'next/link';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import PageTitle from '../components/Common/PageTitle';
|
||||
import Link from '../components/Link';
|
||||
|
||||
const messages = defineMessages({
|
||||
errormessagewithcode: '{statusCode} - {error}',
|
||||
|
||||
@@ -19,7 +19,9 @@ import { SettingsProvider } from '../context/SettingsContext';
|
||||
import { UserContext } from '../context/UserContext';
|
||||
import { User } from '../hooks/useUser';
|
||||
import '../styles/globals.css';
|
||||
import addBasePath from '../utils/addBasePath';
|
||||
|
||||
const basePath = addBasePath('');
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const loadLocaleData = (locale: AvailableLocale): Promise<any> => {
|
||||
switch (locale) {
|
||||
@@ -85,6 +87,10 @@ if (typeof window === 'undefined') {
|
||||
global.Intl = require('intl');
|
||||
}
|
||||
|
||||
if (basePath) {
|
||||
axios.defaults.baseURL = basePath;
|
||||
}
|
||||
|
||||
const CoreApp: Omit<NextAppComponentType, 'origGetInitialProps'> = ({
|
||||
Component,
|
||||
pageProps,
|
||||
@@ -172,9 +178,10 @@ CoreApp.getInitialProps = async (initialProps) => {
|
||||
};
|
||||
|
||||
if (ctx.res) {
|
||||
const serverUrl = `http://localhost:${process.env.PORT || 5055}${basePath}`;
|
||||
// Check if app is initialized and redirect if necessary
|
||||
const response = await axios.get<PublicSettingsResponse>(
|
||||
`http://localhost:${process.env.PORT || 5055}/api/v1/settings/public`
|
||||
`${serverUrl}/api/v1/settings/public`
|
||||
);
|
||||
|
||||
currentSettings = response.data;
|
||||
@@ -184,22 +191,21 @@ CoreApp.getInitialProps = async (initialProps) => {
|
||||
if (!initialized) {
|
||||
if (!router.pathname.match(/(setup|login\/plex)/)) {
|
||||
ctx.res.writeHead(307, {
|
||||
Location: '/setup',
|
||||
Location: addBasePath('/setup'),
|
||||
});
|
||||
ctx.res.end();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
// Attempt to get the user by running a request to the local api
|
||||
const response = await axios.get<User>(
|
||||
`http://localhost:${process.env.PORT || 5055}/api/v1/auth/me`,
|
||||
{ headers: ctx.req ? { cookie: ctx.req.headers.cookie } : undefined }
|
||||
);
|
||||
const response = await axios.get<User>(`${serverUrl}/api/v1/auth/me`, {
|
||||
headers: ctx.req ? { cookie: ctx.req.headers.cookie } : undefined,
|
||||
});
|
||||
user = response.data;
|
||||
|
||||
if (router.pathname.match(/(setup|login)/)) {
|
||||
ctx.res.writeHead(307, {
|
||||
Location: '/',
|
||||
Location: addBasePath('/'),
|
||||
});
|
||||
ctx.res.end();
|
||||
}
|
||||
@@ -209,7 +215,7 @@ CoreApp.getInitialProps = async (initialProps) => {
|
||||
// before anything actually renders
|
||||
if (!router.pathname.match(/(login|setup|resetpassword)/)) {
|
||||
ctx.res.writeHead(307, {
|
||||
Location: '/login',
|
||||
Location: addBasePath('/login'),
|
||||
});
|
||||
ctx.res.end();
|
||||
}
|
||||
@@ -223,7 +229,7 @@ CoreApp.getInitialProps = async (initialProps) => {
|
||||
);
|
||||
|
||||
const locale = user?.settings?.locale
|
||||
? user.settings.locale
|
||||
? user?.settings.locale
|
||||
: currentSettings.locale;
|
||||
|
||||
const messages = await loadLocaleData(locale as AvailableLocale);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ArrowCircleRightIcon } from '@heroicons/react/outline';
|
||||
import type { NextPage } from 'next';
|
||||
import Link from 'next/link';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import PageTitle from '../components/Common/PageTitle';
|
||||
import Link from '../components/Link';
|
||||
import type { Undefinable } from '../utils/typeHelpers';
|
||||
|
||||
interface ErrorProps {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { GetServerSideProps, NextPage } from 'next';
|
||||
import React from 'react';
|
||||
import type { Collection } from '../../../../server/models/Collection';
|
||||
import CollectionDetails from '../../../components/CollectionDetails';
|
||||
import addBasePath from '../../../utils/addBasePath';
|
||||
|
||||
interface CollectionPageProps {
|
||||
collection?: Collection;
|
||||
@@ -12,12 +13,14 @@ const CollectionPage: NextPage<CollectionPageProps> = ({ collection }) => {
|
||||
return <CollectionDetails collection={collection} />;
|
||||
};
|
||||
|
||||
const basePath = addBasePath('');
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<CollectionPageProps> =
|
||||
async (ctx) => {
|
||||
const response = await axios.get<Collection>(
|
||||
`http://localhost:${process.env.PORT || 5055}/api/v1/collection/${
|
||||
ctx.query.collectionId
|
||||
}`,
|
||||
`http://localhost:${
|
||||
process.env.PORT || 5055
|
||||
}${basePath}/api/v1/collection/${ctx.query.collectionId}`,
|
||||
{
|
||||
headers: ctx.req?.headers?.cookie
|
||||
? { cookie: ctx.req.headers.cookie }
|
||||
|
||||
@@ -3,11 +3,14 @@ import { NextPage } from 'next';
|
||||
import React from 'react';
|
||||
import type { MovieDetails as MovieDetailsType } from '../../../../server/models/Movie';
|
||||
import MovieDetails from '../../../components/MovieDetails';
|
||||
import addBasePath from '../../../utils/addBasePath';
|
||||
|
||||
interface MoviePageProps {
|
||||
movie?: MovieDetailsType;
|
||||
}
|
||||
|
||||
const basePath = addBasePath('');
|
||||
|
||||
const MoviePage: NextPage<MoviePageProps> = ({ movie }) => {
|
||||
return <MovieDetails movie={movie} />;
|
||||
};
|
||||
@@ -15,7 +18,7 @@ const MoviePage: NextPage<MoviePageProps> = ({ movie }) => {
|
||||
MoviePage.getInitialProps = async (ctx) => {
|
||||
if (ctx.req) {
|
||||
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
|
||||
}`,
|
||||
{
|
||||
|
||||
@@ -3,11 +3,14 @@ import { NextPage } from 'next';
|
||||
import React from 'react';
|
||||
import type { TvDetails as TvDetailsType } from '../../../../server/models/Tv';
|
||||
import TvDetails from '../../../components/TvDetails';
|
||||
import addBasePath from '../../../utils/addBasePath';
|
||||
|
||||
interface TvPageProps {
|
||||
tv?: TvDetailsType;
|
||||
}
|
||||
|
||||
const basePath = addBasePath('');
|
||||
|
||||
const TvPage: NextPage<TvPageProps> = ({ tv }) => {
|
||||
return <TvDetails tv={tv} />;
|
||||
};
|
||||
@@ -15,7 +18,7 @@ const TvPage: NextPage<TvPageProps> = ({ tv }) => {
|
||||
TvPage.getInitialProps = async (ctx) => {
|
||||
if (ctx.req) {
|
||||
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
|
||||
}`,
|
||||
{
|
||||
|
||||
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 Bowser from 'bowser';
|
||||
import addBasePath from './addBasePath';
|
||||
|
||||
interface PlexHeaders {
|
||||
Accept: string;
|
||||
@@ -177,7 +178,7 @@ class PlexOAuth {
|
||||
|
||||
//Set url to login/plex/loading so browser doesn't block popup
|
||||
const newWindow = window.open(
|
||||
'/login/plex/loading',
|
||||
addBasePath('/login/plex/loading'),
|
||||
title,
|
||||
'scrollbars=yes, width=' +
|
||||
w +
|
||||
|
||||
Reference in New Issue
Block a user