Merge branch 'develop'

pull/570/head
sct 4 years ago
commit 16902f85fb

@ -1,8 +1,8 @@
module.exports = {
root: true,
parser: '@typescript-eslint/parser', // Specifies the ESLint parser
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin
'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
@ -30,6 +30,8 @@ module.exports = {
'@typescript-eslint/explicit-function-return-type': 'off',
'prettier/prettier': ['error', { endOfLine: 'auto' }],
'formatjs/no-offset': 'error',
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': ['error'],
},
overrides: [
{
@ -52,6 +54,5 @@ module.exports = {
jest: true,
es6: true,
},
reportUnusedDisableDirectives: true,
};

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 sct
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -2,8 +2,19 @@
<img src="https://i.imgur.com/TMoEG7g.png" alt="Overseerr">
</p>
<p align="center">
<img src="https://github.com/sct/overseerr/workflows/Overseerr%20Release/badge.svg?branch=master" alt="Overseerr Release" />
<img src="https://github.com/sct/overseerr/workflows/Overseerr%20CI/badge.svg" alt="Overseerr CI">
</p>
<p align="center">
<a href="https://discord.gg/ySfaEUcQ">
<img src="https://img.shields.io/discord/783137440809746482" alt="Discord">
</a>
<img src="https://img.shields.io/docker/pulls/sctx/overseerr" alt="Docker pulls">
<a href="https://hosted.weblate.org/engage/overseerr/">
<img src="https://hosted.weblate.org/widgets/overseerr/-/overseerr-frontend/svg-badge.svg" alt="Translation status" />
</a>
<a href="https://lgtm.com/projects/g/sct/overseerr/context:javascript"><img alt="Language grade: JavaScript" src="https://img.shields.io/lgtm/grade/javascript/g/sct/overseerr.svg?logo=lgtm&logoWidth=18"/></a>
</p>
**Overseerr** is a tool for managing requests for your media library. It integrates with existing services such as **Sonarr** and **Radarr**!
@ -11,25 +22,77 @@
- Full Plex integration. Login and manage user access with Plex!
- Integrates easily with your existing services. Currently Overseerr supports Sonarr and Radarr. More in the future!
- Syncs to your Plex library to know what titles you already have
- Complex request system that allows users to request individual seasons or movies in a friendly, easy to use UI
- Incredibly simple request management UI. Don't dig through the app to simply approve recent requests
- Syncs to your Plex library to know what titles you already have.
- Complex request system that allows users to request individual seasons or movies in a friendly, easy to use UI.
- Incredibly simple request management UI. Don't dig through the app to simply approve recent requests.
- Granular permissions system
- Mobile friendly design, for when you need to approve requests on the go!
## In Development
- A lot!
- User profiles
- User settings page to give users the ability to modify their Overseerr experience to their liking
- Version update notifications in-app
## Planned Features
- More notification types (Slack/Telegram/etc)
- Issues system. This will allow users to report issues with content on your media server.
- Local user system (for those who do not use Plex)
- Compatiblity APIs to work with existing tools in your system
## Running Overseerr
Currently, the only distribution of Overseerr is through Docker images. If you have Docker, you can run Overseerr with the following command:
```
docker run -d \
-e LOG_LEVEL=info \
-e TZ=Asia/Tokyo \
-p 5055:3000 \
-v /path/to/appdata/config:/config \
--restart unless-stopped \
sctx/overseer
```
After running Overseerr for the first time, visit the web UI at http://[address]:5055 and complete the setup steps to configure Overseerr.
⚠️ Overseerr is currently under very heavy, rapid development and things are likely to break often. We need all the help we can get to find bugs and get them fixed to hit a more stable release. If you would like to help test the bleeding edge, please use the image **sctx/overseerr:develop** instead! ⚠️
## Preview
<img src="https://i.imgur.com/Mjbyruv.png">
## Support
- You can reach us for support on [Discord](https://discord.gg/ySfaEUcQ).
- Bugs can be opened with an issue on [Github](https://github.com/sct/overseerr/issues).
## API Documentation
- Coming soon
Full API documentation will soon be published automatically and available outside of running the app. But currently, you can access the api docs by running Overseerr locally and visiting http://localhost:3000/api-docs
## Contribution
Anyone is welcome to contribute to Docker and pull requests are greatly appreciated! Contributors will be recognized in the future on this very README.
### Developing Overseerr
You can develop Overseer entirely in docker. Make sure you have [Docker Desktop](https://www.docker.com/products/docker-desktop) installed before continuing.
1. Make sure you have [Docker Desktop](https://www.docker.com/products/docker-desktop) installed.
2. Run `docker-compose up -d` to start the server
2. Run `docker-compose up -d` to start the server.
3. Access the container at http://localhost:3000
That's it!
If Docker isn't your jam, you can always run Overseer with the following yarn commands:
```
yarn
yarn dev
```
You will need NodeJS installed. Once it's built and running, access it locally at http://localhost:3000 just like Docker.
### Translation
We use [Weblate](https://hosted.weblate.org/engage/overseerr/) for our translations so please feel free to contribute to localizing Overseerr!

@ -14,6 +14,7 @@
"migration:run": "ts-node --project server/tsconfig.json ./node_modules/.bin/typeorm migration:run",
"format": "prettier --write ."
},
"license": "MIT",
"dependencies": {
"@svgr/webpack": "^5.4.0",
"axios": "^0.20.0",
@ -36,6 +37,7 @@
"pug": "^3.0.0",
"react": "16.13.1",
"react-dom": "16.13.1",
"react-intersection-observer": "^8.31.0",
"react-intl": "^5.8.5",
"react-spring": "^8.0.27",
"react-toast-notifications": "^2.4.0",
@ -46,14 +48,14 @@
"swagger-ui-express": "^4.1.4",
"swr": "^0.3.5",
"typeorm": "^0.2.26",
"uuid": "^8.3.0",
"uuid": "^8.3.1",
"winston": "^3.3.3",
"xml2js": "^0.4.23",
"yamljs": "^0.3.0",
"yup": "^0.29.3"
},
"devDependencies": {
"@babel/cli": "^7.11.6",
"@babel/cli": "^7.12.8",
"@commitlint/cli": "^11.0.0",
"@commitlint/config-conventional": "^11.0.0",
"@semantic-release/changelog": "^5.0.1",
@ -68,38 +70,38 @@
"@types/email-templates": "^7.1.0",
"@types/express": "^4.17.8",
"@types/express-session": "^1.17.0",
"@types/lodash": "^4.14.161",
"@types/node": "^14.10.0",
"@types/lodash": "^4.14.165",
"@types/node": "^14.14.10",
"@types/node-schedule": "^1.3.1",
"@types/nodemailer": "^6.4.0",
"@types/react": "^16.9.49",
"@types/react-dom": "^16.9.8",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@types/react-toast-notifications": "^2.4.0",
"@types/react-transition-group": "^4.4.0",
"@types/swagger-ui-express": "^4.1.2",
"@types/uuid": "^8.3.0",
"@types/xml2js": "^0.4.5",
"@types/xml2js": "^0.4.7",
"@types/yamljs": "^0.2.31",
"@types/yup": "^0.29.9",
"@typescript-eslint/eslint-plugin": "^4.0.0",
"@typescript-eslint/parser": "^3.10.1",
"@types/yup": "^0.29.10",
"@typescript-eslint/eslint-plugin": "^4.9.1",
"@typescript-eslint/parser": "^4.9.1",
"autoprefixer": "^9",
"babel-plugin-react-intl": "^8.2.2",
"babel-plugin-react-intl-auto": "^3.3.0",
"commitizen": "^4.2.1",
"copyfiles": "^2.4.0",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^7.10.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-formatjs": "^2.7.10",
"eslint-plugin-jsx-a11y": "^6.3.1",
"eslint-plugin-prettier": "^3.1.4",
"eslint-plugin-react": "^7.20.6",
"eslint-plugin-react-hooks": "^4.1.2",
"eslint": "^7.15.0",
"eslint-config-prettier": "^7.0.0",
"eslint-plugin-formatjs": "^2.9.10",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-prettier": "^3.2.0",
"eslint-plugin-react": "^7.21.5",
"eslint-plugin-react-hooks": "^4.2.0",
"extract-react-intl-messages": "^4.1.1",
"husky": "^4.3.0",
"lint-staged": "^10.4.0",
"nodemon": "^2.0.4",
"husky": "^4.3.5",
"lint-staged": "^10.5.3",
"nodemon": "^2.0.6",
"postcss": "^7",
"postcss-preset-env": "^6.7.0",
"prettier": "^2.1.2",

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

@ -679,9 +679,10 @@ class TheMovieDb {
public getMovieTrending = async ({
page = 1,
timeWindow = 'day',
}: { page?: number; timeWindow?: 'day' | 'week' } = {}): Promise<
TmdbSearchMovieResponse
> => {
}: {
page?: number;
timeWindow?: 'day' | 'week';
} = {}): Promise<TmdbSearchMovieResponse> => {
try {
const response = await this.axios.get<TmdbSearchMovieResponse>(
`/trending/movie/${timeWindow}`,
@ -701,9 +702,10 @@ class TheMovieDb {
public getTvTrending = async ({
page = 1,
timeWindow = 'day',
}: { page?: number; timeWindow?: 'day' | 'week' } = {}): Promise<
TmdbSearchTvResponse
> => {
}: {
page?: number;
timeWindow?: 'day' | 'week';
} = {}): Promise<TmdbSearchTvResponse> => {
try {
const response = await this.axios.get<TmdbSearchTvResponse>(
`/trending/tv/${timeWindow}`,

@ -100,7 +100,7 @@ class Media {
}
@AfterUpdate()
private async notifyAvailable() {
private async _notifyAvailable() {
if (this.status === MediaStatus.AVAILABLE) {
if (this.mediaType === MediaType.MOVIE) {
const requestRepository = getRepository(MediaRequest);

@ -62,7 +62,7 @@ export class MediaRequest {
}
@AfterInsert()
private async notifyNewRequest() {
private async _notifyNewRequest() {
if (this.status === MediaRequestStatus.PENDING) {
const mediaRepository = getRepository(Media);
const media = await mediaRepository.findOne({
@ -110,7 +110,7 @@ export class MediaRequest {
* auto approved content
*/
@AfterUpdate()
private async notifyApproved() {
private async _notifyApproved() {
if (this.status === MediaRequestStatus.APPROVED) {
const mediaRepository = getRepository(Media);
const media = await mediaRepository.findOne({
@ -151,7 +151,7 @@ export class MediaRequest {
@AfterUpdate()
@AfterInsert()
private async updateParentStatus() {
private async _updateParentStatus() {
const mediaRepository = getRepository(Media);
const media = await mediaRepository.findOne({
where: { id: this.media.id },
@ -206,7 +206,7 @@ export class MediaRequest {
}
@AfterRemove()
private async handleRemoveParentUpdate() {
private async _handleRemoveParentUpdate() {
const mediaRepository = getRepository(Media);
const fullMedia = await mediaRepository.findOneOrFail({
where: { id: this.media.id },
@ -219,7 +219,7 @@ export class MediaRequest {
@AfterUpdate()
@AfterInsert()
private async sendToRadarr() {
private async _sendToRadarr() {
if (
this.status === MediaRequestStatus.APPROVED &&
this.type === MediaType.MOVIE
@ -267,7 +267,7 @@ export class MediaRequest {
@AfterUpdate()
@AfterInsert()
private async sendToSonarr() {
private async _sendToSonarr() {
if (
this.status === MediaRequestStatus.APPROVED &&
this.type === MediaType.TV

@ -8,7 +8,6 @@ import {
AfterInsert,
AfterUpdate,
getRepository,
RelationId,
} from 'typeorm';
import { MediaStatus } from '../constants/media';
import Media from './Media';
@ -42,7 +41,7 @@ class Season {
@AfterInsert()
@AfterUpdate()
private async sendSeasonAvailableNotification() {
private async _sendSeasonAvailableNotification() {
if (this.status === MediaStatus.AVAILABLE) {
try {
const lazyMedia = await this.media;

@ -5,7 +5,7 @@ import { createConnection, getRepository } from 'typeorm';
import routes from './routes';
import bodyParser from 'body-parser';
import cookieParser from 'cookie-parser';
import session from 'express-session';
import session, { Store } from 'express-session';
import { TypeormStore } from 'connect-typeorm/out';
import YAML from 'yamljs';
import swaggerUi from 'swagger-ui-express';
@ -29,7 +29,7 @@ app
.then(async () => {
await createConnection();
// Load Settings
getSettings().load();
const settings = getSettings().load();
// Register Notification Agents
notificationManager.registerAgents([new DiscordAgent(), new EmailAgent()]);
@ -47,7 +47,7 @@ app
server.use(
'/api',
session({
secret: 'verysecret',
secret: settings.clientId,
resave: false,
saveUninitialized: false,
cookie: {
@ -56,7 +56,7 @@ app
store: new TypeormStore({
cleanupLimit: 2,
ttl: 1000 * 60 * 60 * 24 * 30,
}).connect(sessionRespository),
}).connect(sessionRespository) as Store,
})
);
const apiDocs = YAML.load(API_SPEC_PATH);
@ -71,7 +71,7 @@ app
* OpenAPI validator. Otherwise, they are treated as objects instead of strings
* and response validation will fail
*/
server.use((req, res, next) => {
server.use((_req, res, next) => {
const original = res.json;
res.json = function jsonp(json) {
return original.call(this, JSON.parse(JSON.stringify(json)));
@ -83,8 +83,10 @@ app
server.use(
(
err: { status: number; message: string; errors: string[] },
req: Request,
_req: Request,
res: Response,
// We must provide a next function for the function signature here even though its not used
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_next: NextFunction
) => {
// format error
@ -96,10 +98,7 @@ app
);
const port = Number(process.env.PORT) || 3000;
server.listen(port, (err) => {
if (err) {
throw err;
}
server.listen(port, () => {
logger.info(`Server ready on port ${port}`, {
label: 'SERVER',
});

@ -127,7 +127,9 @@ class DiscordAgent implements NotificationAgent {
};
}
public shouldSend(type: Notification): boolean {
// TODO: Add checking for type here once we add notification type filters for agents
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public shouldSend(_type: Notification): boolean {
const settings = getSettings();
if (

@ -7,10 +7,12 @@ import Email from 'email-templates';
import logger from '../../../logger';
import { getRepository } from 'typeorm';
import { User } from '../../../entity/User';
import { hasPermission, Permission } from '../../permissions';
import { Permission } from '../../permissions';
class EmailAgent implements NotificationAgent {
public shouldSend(type: Notification): boolean {
// TODO: Add checking for type here once we add notification type filters for agents
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public shouldSend(_type: Notification): boolean {
const settings = getSettings();
if (settings.notifications.agents.email.enabled) {

@ -84,7 +84,7 @@ interface NotificationSettings {
}
interface AllSettings {
clientId?: string;
clientId: string;
main: MainSettings;
plex: PlexSettings;
radarr: RadarrSettings[];
@ -100,8 +100,9 @@ class Settings {
constructor(initialSettings?: AllSettings) {
this.data = {
clientId: '',
main: {
apiKey: 'temp',
apiKey: '',
applicationUrl: '',
},
plex: {
@ -143,6 +144,10 @@ class Settings {
}
get main(): MainSettings {
if (!this.data.main.apiKey) {
this.data.main.apiKey = this.generateApiKey();
this.save();
}
return this.data.main;
}
@ -199,6 +204,16 @@ class Settings {
return this.data.clientId;
}
public regenerateApiKey(): MainSettings {
this.main.apiKey = this.generateApiKey();
this.save();
return this.main;
}
private generateApiKey(): string {
return Buffer.from(`${Date.now()}${this.clientId}`).toString('base64');
}
/**
* Settings Load
*

@ -1,5 +1,4 @@
import { TmdbMovieDetails } from '../api/themoviedb';
import { MediaRequest } from '../entity/MediaRequest';
import {
ProductionCompany,
Genre,

@ -3,7 +3,6 @@ import type {
TmdbPersonResult,
TmdbTvResult,
} from '../api/themoviedb';
import type { MediaRequest } from '../entity/MediaRequest';
import Media from '../entity/Media';
export type MediaType = 'tv' | 'movie' | 'person';

@ -1,9 +1,5 @@
import { Router } from 'express';
import TheMovieDb, {
TmdbMovieResult,
TmdbTvResult,
TmdbPersonResult,
} from '../api/themoviedb';
import TheMovieDb from '../api/themoviedb';
import { mapMovieResult, mapTvResult, mapPersonResult } from '../models/Search';
import Media from '../entity/Media';
import { isMovie, isPerson } from '../utils/typeHelpers';

@ -1,7 +1,6 @@
import { Router } from 'express';
import TheMovieDb from '../api/themoviedb';
import { mapMovieDetails } from '../models/Movie';
import { MediaRequest } from '../entity/MediaRequest';
import { mapMovieResult } from '../models/Search';
import Media from '../entity/Media';
import RottenTomatoes from '../api/rottentomatoes';

@ -1,5 +1,4 @@
import { Router } from 'express';
import next from 'next';
import TheMovieDb from '../api/themoviedb';
import logger from '../logger';
import {

@ -4,10 +4,11 @@ import {
RadarrSettings,
SonarrSettings,
Library,
MainSettings,
} from '../lib/settings';
import { getRepository } from 'typeorm';
import { User } from '../entity/User';
import PlexAPI, { PlexLibrary } from '../api/plexapi';
import PlexAPI from '../api/plexapi';
import { jobPlexFullSync } from '../job/plexsync';
import SonarrAPI from '../api/sonarr';
import RadarrAPI from '../api/radarr';
@ -19,9 +20,15 @@ import { merge } from 'lodash';
const settingsRoutes = Router();
settingsRoutes.get('/main', (_req, res) => {
settingsRoutes.get('/main', (req, res) => {
const settings = getSettings();
if (!req.user?.hasPermission(Permission.ADMIN)) {
return res.status(200).json({
applicationUrl: settings.main.applicationUrl,
} as Partial<MainSettings>);
}
res.status(200).json(settings.main);
});
@ -120,7 +127,7 @@ settingsRoutes.get('/plex/sync', (req, res) => {
return res.status(200).json(jobPlexFullSync.status());
});
settingsRoutes.get('/radarr', (req, res) => {
settingsRoutes.get('/radarr', (_req, res) => {
const settings = getSettings();
res.status(200).json(settings.radarr);
@ -261,7 +268,7 @@ settingsRoutes.delete<{ id: string }>('/radarr/:id', (req, res) => {
return res.status(200).json(removed[0]);
});
settingsRoutes.get('/sonarr', (req, res) => {
settingsRoutes.get('/sonarr', (_req, res) => {
const settings = getSettings();
res.status(200).json(settings.sonarr);
@ -372,7 +379,7 @@ settingsRoutes.delete<{ id: string }>('/sonarr/:id', (req, res) => {
return res.status(200).json(removed[0]);
});
settingsRoutes.get('/jobs', (req, res) => {
settingsRoutes.get('/jobs', (_req, res) => {
return res.status(200).json(
scheduledJobs.map((job) => ({
name: job.name,
@ -384,7 +391,7 @@ settingsRoutes.get('/jobs', (req, res) => {
settingsRoutes.get(
'/initialize',
isAuthenticated(Permission.ADMIN),
(req, res) => {
(_req, res) => {
const settings = getSettings();
settings.public.initialized = true;
@ -394,7 +401,7 @@ settingsRoutes.get(
}
);
settingsRoutes.get('/notifications/discord', (req, res) => {
settingsRoutes.get('/notifications/discord', (_req, res) => {
const settings = getSettings();
res.status(200).json(settings.notifications.agents.discord);
@ -409,7 +416,7 @@ settingsRoutes.post('/notifications/discord', (req, res) => {
res.status(200).json(settings.notifications.agents.discord);
});
settingsRoutes.get('/notifications/email', (req, res) => {
settingsRoutes.get('/notifications/email', (_req, res) => {
const settings = getSettings();
res.status(200).json(settings.notifications.agents.email);

@ -1,6 +1,5 @@
import { Router } from 'express';
import TheMovieDb from '../api/themoviedb';
import { MediaRequest } from '../entity/MediaRequest';
import { mapTvDetails, mapSeasonWithEpisodes } from '../models/Tv';
import { mapTvResult } from '../models/Search';
import Media from '../entity/Media';

@ -5,7 +5,7 @@ import { hasPermission, Permission } from '../lib/permissions';
const router = Router();
router.get('/', async (req, res) => {
router.get('/', async (_req, res) => {
const userRepository = getRepository(User);
const users = await userRepository.find();

@ -0,0 +1,85 @@
import React, { AllHTMLAttributes } from 'react';
import { withProperties } from '../../../utils/typeHelpers';
const TBody: React.FC = ({ children }) => {
return (
<tbody className="bg-gray-600 divide-y divide-gray-700">{children}</tbody>
);
};
const TH: React.FC<AllHTMLAttributes<HTMLTableHeaderCellElement>> = ({
children,
className,
...props
}) => {
const style = [
'px-6 py-3 bg-gray-500 text-left text-xs leading-4 font-medium text-gray-200 uppercase tracking-wider',
];
if (className) {
style.push(className);
}
return (
<th className={style.join(' ')} {...props}>
{children}
</th>
);
};
interface TDProps extends AllHTMLAttributes<HTMLTableCellElement> {
alignText?: 'left' | 'center' | 'right';
noPadding?: boolean;
}
const TD: React.FC<TDProps> = ({
children,
alignText = 'left',
noPadding,
className,
...props
}) => {
const style = ['whitespace-nowrap text-sm leading-5 text-white'];
switch (alignText) {
case 'left':
style.push('text-left');
break;
case 'center':
style.push('text-center');
break;
case 'right':
style.push('text-right');
break;
}
if (!noPadding) {
style.push('px-6 py-4');
}
if (className) {
style.push(className);
}
return (
<td className={style.join(' ')} {...props}>
{children}
</td>
);
};
const Table: React.FC = ({ children }) => {
return (
<div className="flex flex-col">
<div className="my-2 overflow-x-auto -mx-6 sm:-mx-6 md:mx-4 lg:mx-4">
<div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
<div className="shadow overflow-hidden sm:rounded-lg">
<table className="min-w-full">{children}</table>
</div>
</div>
</div>
</div>
);
};
export default withProperties(Table, { TH, TBody, TD });

@ -7,13 +7,11 @@ import type {
} from '../../../server/models/Search';
import TitleCard from '../TitleCard';
import PersonCard from '../PersonCard';
import { MediaRequest } from '../../../server/entity/MediaRequest';
import TmdbTitleCard from '../TitleCard/TmdbTitleCard';
import Slider from '../Slider';
import Link from 'next/link';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { LanguageContext } from '../../context/LanguageContext';
import type Media from '../../../server/entity/Media';
import type { MediaResultsResponse } from '../../../server/interfaces/api/mediaInterfaces';
import type { RequestResultsResponse } from '../../../server/interfaces/api/requestInterfaces';
import RequestCard from '../RequestCard';

@ -522,7 +522,7 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
</div>
<Slider
sliderKey="cast"
isLoading={!data && !error}
isLoading={false}
isEmpty={false}
items={data?.credits.cast.slice(0, 20).map((person) => (
<PersonCard

@ -1,13 +1,11 @@
import React, { useContext, useState } from 'react';
import React, { useContext } from 'react';
import { useInView } from 'react-intersection-observer';
import type { MediaRequest } from '../../../server/entity/MediaRequest';
import type { TvDetails } from '../../../server/models/Tv';
import type { MovieDetails } from '../../../server/models/Movie';
import useSWR from 'swr';
import { LanguageContext } from '../../context/LanguageContext';
import {
MediaStatus,
MediaRequestStatus,
} from '../../../server/constants/media';
import { MediaRequestStatus } from '../../../server/constants/media';
import Badge from '../Common/Badge';
import { useUser, Permission } from '../../hooks/useUser';
import axios from 'axios';
@ -16,6 +14,7 @@ import { withProperties } from '../../utils/typeHelpers';
import Link from 'next/link';
import { defineMessages, useIntl } from 'react-intl';
import globalMessages from '../../i18n/globalMessages';
import StatusBadge from '../StatusBadge';
const messages = defineMessages({
requestedby: 'Requested by {username}',
@ -41,6 +40,9 @@ interface RequestCardProps {
}
const RequestCard: React.FC<RequestCardProps> = ({ request }) => {
const { ref, inView } = useInView({
triggerOnce: true,
});
const intl = useIntl();
const { hasPermission } = useUser();
const { locale } = useContext(LanguageContext);
@ -49,7 +51,7 @@ const RequestCard: React.FC<RequestCardProps> = ({ request }) => {
? `/api/v1/movie/${request.media.tmdbId}`
: `/api/v1/tv/${request.media.tmdbId}`;
const { data: title, error } = useSWR<MovieDetails | TvDetails>(
`${url}?language=${locale}`
inView ? `${url}?language=${locale}` : null
);
const { data: requestData, error: requestError, revalidate } = useSWR<
MediaRequest
@ -66,7 +68,11 @@ const RequestCard: React.FC<RequestCardProps> = ({ request }) => {
};
if (!title && !error) {
return <RequestCardPlaceholder />;
return (
<div ref={ref}>
<RequestCardPlaceholder />
</div>
);
}
if (!requestData && !requestError) {
@ -102,28 +108,11 @@ const RequestCard: React.FC<RequestCardProps> = ({ request }) => {
username: requestData.requestedBy.username,
})}
</div>
<div className="mt-1 sm:mt-2">
{requestData.media.status === MediaStatus.AVAILABLE && (
<Badge badgeType="success">
{intl.formatMessage(globalMessages.available)}
</Badge>
)}
{requestData.media.status === MediaStatus.PARTIALLY_AVAILABLE && (
<Badge badgeType="success">
{intl.formatMessage(globalMessages.partiallyavailable)}
</Badge>
)}
{requestData.media.status === MediaStatus.PROCESSING && (
<Badge badgeType="danger">
{intl.formatMessage(globalMessages.unavailable)}
</Badge>
)}
{requestData.media.status === MediaStatus.PENDING && (
<Badge badgeType="warning">
{intl.formatMessage(globalMessages.pending)}
</Badge>
)}
</div>
{requestData.media.status && (
<div className="mt-1 sm:mt-2">
<StatusBadge status={requestData.media.status} />
</div>
)}
{request.seasons.length > 0 && (
<div className="hidden mt-2 text-sm sm:flex items-center">
<span className="mr-2">{intl.formatMessage(messages.seasons)}</span>

@ -0,0 +1,250 @@
import React, { useContext } from 'react';
import { useInView } from 'react-intersection-observer';
import type { MediaRequest } from '../../../../server/entity/MediaRequest';
import {
useIntl,
FormattedDate,
FormattedRelativeTime,
defineMessages,
} from 'react-intl';
import { useUser, Permission } from '../../../hooks/useUser';
import { LanguageContext } from '../../../context/LanguageContext';
import type { MovieDetails } from '../../../../server/models/Movie';
import type { TvDetails } from '../../../../server/models/Tv';
import useSWR from 'swr';
import Badge from '../../Common/Badge';
import StatusBadge from '../../StatusBadge';
import Table from '../../Common/Table';
import { MediaRequestStatus } from '../../../../server/constants/media';
import Button from '../../Common/Button';
import axios from 'axios';
import globalMessages from '../../../i18n/globalMessages';
import Link from 'next/link';
const messages = defineMessages({
requestedby: 'Requested by {username}',
seasons: 'Seasons',
notavailable: 'N/A',
});
const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => {
return (movie as MovieDetails).title !== undefined;
};
interface RequestItemProps {
request: MediaRequest;
onDelete: () => void;
}
const RequestItem: React.FC<RequestItemProps> = ({ request, onDelete }) => {
const { ref, inView } = useInView({
triggerOnce: true,
});
const intl = useIntl();
const { hasPermission } = useUser();
const { locale } = useContext(LanguageContext);
const url =
request.type === 'movie'
? `/api/v1/movie/${request.media.tmdbId}`
: `/api/v1/tv/${request.media.tmdbId}`;
const { data: title, error } = useSWR<MovieDetails | TvDetails>(
inView ? `${url}?language=${locale}` : null
);
const { data: requestData, revalidate } = useSWR<MediaRequest>(
`/api/v1/request/${request.id}`,
{
initialData: request,
}
);
const modifyRequest = async (type: 'approve' | 'decline') => {
const response = await axios.get(`/api/v1/request/${request.id}/${type}`);
if (response) {
revalidate();
}
};
const deleteRequest = async () => {
await axios.delete(`/api/v1/request/${request.id}`);
onDelete();
};
if (!title && !error) {
return (
<tr className="w-full bg-gray-800 animate-pulse h-24" ref={ref}>
<td colSpan={6}></td>
</tr>
);
}
if (!title || !requestData) {
return (
<tr className="w-full bg-gray-800 animate-pulse h-24">
<td colSpan={6}></td>
</tr>
);
}
return (
<tr className="w-full bg-gray-800 h-24 p-2 relative text-white">
<Table.TD
noPadding
className="w-20 px-4 relative hidden sm:table-cell align-middle"
>
<Link
href={
request.type === 'movie'
? `/movie/${request.media.tmdbId}`
: `/tv/${request.media.tmdbId}`
}
>
<a>
<img
src={`//image.tmdb.org/t/p/w600_and_h900_bestv2${title.posterPath}`}
alt=""
className="rounded-md shadow-sm cursor-pointer transition transform-gpu duration-300 scale-100 hover:scale-105 hover:shadow-md"
/>
</a>
</Link>
</Table.TD>
<Table.TD>
<Link
href={
requestData.type === 'movie'
? `/movie/${requestData.media.tmdbId}`
: `/tv/${requestData.media.tmdbId}`
}
>
<a className="text-white text-xl mr-2 hover:underline">
{isMovie(title) ? title.title : title.name}
</a>
</Link>
<div className="text-sm">
{intl.formatMessage(messages.requestedby, {
username: requestData.requestedBy.username,
})}
</div>
{requestData.seasons.length > 0 && (
<div className="hidden mt-2 text-sm sm:flex items-center">
<span className="mr-2">{intl.formatMessage(messages.seasons)}</span>
{requestData.seasons.map((season) => (
<span key={`season-${season.id}`} className="mr-2">
<Badge>{season.seasonNumber}</Badge>
</span>
))}
</div>
)}
</Table.TD>
<Table.TD>
<StatusBadge status={requestData.media.status} />
</Table.TD>
<Table.TD>
<div className="flex flex-col">
<span className="text-sm text-gray-300">
<FormattedDate value={requestData.createdAt} />
</span>
</div>
</Table.TD>
<Table.TD>
<div className="flex flex-col">
{requestData.modifiedBy ? (
<span className="text-sm text-gray-300">
{requestData.modifiedBy.username} (
<FormattedRelativeTime
value={Math.floor(
(new Date(requestData.updatedAt).getTime() - Date.now()) /
1000
)}
updateIntervalInSeconds={1}
/>
)
</span>
) : (
<span className="text-sm text-gray-300">N/A</span>
)}
</div>
</Table.TD>
<Table.TD alignText="right">
{requestData.status !== MediaRequestStatus.PENDING &&
hasPermission(Permission.MANAGE_REQUESTS) && (
<Button
buttonType="danger"
buttonSize="sm"
onClick={() => deleteRequest()}
>
<svg
className="w-4 h-4 mr-0 sm:mr-1"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
clipRule="evenodd"
/>
</svg>
<span className="hidden sm:block">
{intl.formatMessage(globalMessages.delete)}
</span>
</Button>
)}
{requestData.status === MediaRequestStatus.PENDING &&
hasPermission(Permission.MANAGE_REQUESTS) && (
<>
<span className="mr-2">
<Button
buttonType="success"
buttonSize="sm"
onClick={() => modifyRequest('approve')}
>
<svg
className="w-4 h-4 mr-0 sm:mr-1"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clipRule="evenodd"
/>
</svg>
<span className="hidden sm:block">
{intl.formatMessage(globalMessages.approve)}
</span>
</Button>
</span>
<span>
<Button
buttonType="danger"
buttonSize="sm"
onClick={() => modifyRequest('decline')}
>
<svg
className="w-4 h-4 mr-0 sm:mr-1"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clipRule="evenodd"
/>
</svg>
<span className="hidden sm:block">
{intl.formatMessage(globalMessages.decline)}
</span>
</Button>
</span>
</>
)}
</Table.TD>
</tr>
);
};
export default RequestItem;

@ -0,0 +1,109 @@
import React, { useState } from 'react';
import useSWR from 'swr';
import type { RequestResultsResponse } from '../../../server/interfaces/api/requestInterfaces';
import LoadingSpinner from '../Common/LoadingSpinner';
import RequestItem from './RequestItem';
import Header from '../Common/Header';
import Table from '../Common/Table';
import Button from '../Common/Button';
import { defineMessages, useIntl } from 'react-intl';
const messages = defineMessages({
requests: 'Requests',
mediaInfo: 'Media Info',
status: 'Status',
requestedAt: 'Requested At',
modifiedBy: 'Last Modified By',
showingresults:
'Showing <strong>{from}</strong> to <strong>{to}</strong> of <strong>{total}</strong> results',
next: 'Next',
previous: 'Previous',
});
const RequestList: React.FC = () => {
const intl = useIntl();
const [pageIndex, setPageIndex] = useState(0);
const { data, error, revalidate } = useSWR<RequestResultsResponse>(
`/api/v1/request?take=10&skip=${pageIndex * 10}`
);
if (!data && !error) {
return <LoadingSpinner />;
}
if (!data) {
return <LoadingSpinner />;
}
const hasNextPage = data.pageInfo.pages > pageIndex + 1;
const hasPrevPage = pageIndex > 0;
return (
<>
<Header>{intl.formatMessage(messages.requests)}</Header>
<Table>
<thead>
<Table.TH className="hidden sm:table-cell"></Table.TH>
<Table.TH>{intl.formatMessage(messages.mediaInfo)}</Table.TH>
<Table.TH>{intl.formatMessage(messages.status)}</Table.TH>
<Table.TH>{intl.formatMessage(messages.requestedAt)}</Table.TH>
<Table.TH>{intl.formatMessage(messages.modifiedBy)}</Table.TH>
<Table.TH></Table.TH>
</thead>
<Table.TBody>
{data.results.map((request) => {
return (
<RequestItem
request={request}
key={`request-list-${request.id}`}
onDelete={() => revalidate()}
/>
);
})}
<tr>
<Table.TD colSpan={6} noPadding>
<nav
className="bg-gray-700 px-4 py-3 flex items-center justify-between text-white sm:px-6"
aria-label="Pagination"
>
<div className="hidden sm:block">
<p className="text-sm">
{intl.formatMessage(messages.showingresults, {
from: pageIndex * 10,
to:
data.results.length < 10
? pageIndex * 10 + data.results.length
: pageIndex + 1 * 10,
total: data.pageInfo.results,
strong: function strong(msg) {
return <span className="font-medium">{msg}</span>;
},
})}
</p>
</div>
<div className="flex-1 flex justify-start sm:justify-end">
<span className="mr-2">
<Button
disabled={!hasPrevPage}
onClick={() => setPageIndex((current) => current - 1)}
>
{intl.formatMessage(messages.previous)}
</Button>
</span>
<Button
disabled={!hasNextPage}
onClick={() => setPageIndex((current) => current + 1)}
>
{intl.formatMessage(messages.next)}
</Button>
</div>
</nav>
</Table.TD>
</tr>
</Table.TBody>
</Table>
</>
);
};
export default RequestList;

@ -11,10 +11,10 @@ import {
MediaStatus,
MediaRequestStatus,
} from '../../../server/constants/media';
import { TvDetails, SeasonWithEpisodes } from '../../../server/models/Tv';
import type SeasonRequest from '../../../server/entity/SeasonRequest';
import { TvDetails } from '../../../server/models/Tv';
import Badge from '../Common/Badge';
import globalMessages from '../../i18n/globalMessages';
import SeasonRequest from '../../../server/entity/SeasonRequest';
const messages = defineMessages({
requestadmin: 'Your request will be immediately approved.',

@ -1,10 +1,8 @@
import React from 'react';
import useSWR from 'swr';
import MovieRequestModal from './MovieRequestModal';
import type { MediaRequest } from '../../../server/entity/MediaRequest';
import type { MediaStatus } from '../../../server/constants/media';
import TvRequestModal from './TvRequestModal';
import { useTransition, animated } from 'react-spring';
import { useTransition } from 'react-spring';
interface RequestModalProps {
show: boolean;
@ -21,7 +19,6 @@ const RequestModal: React.FC<RequestModalProps> = ({
show,
tmdbId,
onComplete,
onError,
onUpdating,
onCancel,
}) => {

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React from 'react';
import { Field, Form, Formik } from 'formik';
import useSWR from 'swr';
import LoadingSpinner from '../../Common/LoadingSpinner';

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React from 'react';
import { Field, Form, Formik } from 'formik';
import useSWR from 'swr';
import LoadingSpinner from '../../Common/LoadingSpinner';

@ -226,7 +226,7 @@ const RadarrModal: React.FC<RadarrModalProps> = ({
okText={
isSubmitting
? intl.formatMessage(messages.saving)
: !!radarr
: radarr
? intl.formatMessage(messages.save)
: intl.formatMessage(messages.add)
}

@ -3,6 +3,7 @@ import useSWR from 'swr';
import LoadingSpinner from '../Common/LoadingSpinner';
import { FormattedRelativeTime, defineMessages, useIntl } from 'react-intl';
import Button from '../Common/Button';
import Table from '../Common/Table';
const messages = defineMessages({
jobname: 'Job Name',
@ -21,55 +22,38 @@ const SettingsJobs: React.FC = () => {
}
return (
<div className="flex flex-col">
<div className="my-2 overflow-x-auto -mx-6 sm:-mx-6 md:mx-4 lg:mx-4">
<div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
<div className="shadow overflow-hidden sm:rounded-lg">
<table className="min-w-full">
<thead>
<tr>
<th className="px-6 py-3 bg-gray-500 text-left text-xs leading-4 font-medium text-gray-200 uppercase tracking-wider">
{intl.formatMessage(messages.jobname)}
</th>
<th className="px-6 py-3 bg-gray-500 text-left text-xs leading-4 font-medium text-gray-200 uppercase tracking-wider">
{intl.formatMessage(messages.nextexecution)}
</th>
<th className="px-6 py-3 bg-gray-500"></th>
</tr>
</thead>
<tbody className="bg-gray-600 divide-y divide-gray-700">
{data?.map((job, index) => (
<tr key={`job-list-${index}`}>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm leading-5 text-white">
{job.name}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm leading-5 text-white">
<FormattedRelativeTime
value={Math.floor(
(new Date(job.nextExecutionTime).getTime() -
Date.now()) /
1000
)}
updateIntervalInSeconds={1}
/>
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm leading-5 font-medium">
<Button buttonType="primary">
{intl.formatMessage(messages.runnow)}
</Button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
<Table>
<thead>
<Table.TH>{intl.formatMessage(messages.jobname)}</Table.TH>
<Table.TH>{intl.formatMessage(messages.nextexecution)}</Table.TH>
<Table.TH></Table.TH>
</thead>
<Table.TBody>
{data?.map((job, index) => (
<tr key={`job-list-${index}`}>
<Table.TD>
<div className="text-sm leading-5 text-white">{job.name}</div>
</Table.TD>
<Table.TD>
<div className="text-sm leading-5 text-white">
<FormattedRelativeTime
value={Math.floor(
(new Date(job.nextExecutionTime).getTime() - Date.now()) /
1000
)}
updateIntervalInSeconds={1}
/>
</div>
</Table.TD>
<Table.TD alignText="right">
<Button buttonType="primary">
{intl.formatMessage(messages.runnow)}
</Button>
</Table.TD>
</tr>
))}
</Table.TBody>
</Table>
);
};

@ -80,7 +80,7 @@ const SettingsLayout: React.FC = ({ children }) => {
<Link href={route}>
<a
className={`whitespace-nowrap ml-8 first:ml-0 py-4 px-1 border-b-2 border-transparent font-medium text-sm leading-5 ${
!!router.pathname.match(regex) ? activeLinkColor : inactiveLinkColor
router.pathname.match(regex) ? activeLinkColor : inactiveLinkColor
}`}
aria-current="page"
>

@ -7,6 +7,7 @@ import { Form, Formik, Field } from 'formik';
import axios from 'axios';
import Button from '../Common/Button';
import { defineMessages, useIntl } from 'react-intl';
import { useUser, Permission } from '../../hooks/useUser';
const messages = defineMessages({
generalsettings: 'General Settings',
@ -19,6 +20,7 @@ const messages = defineMessages({
});
const SettingsMain: React.FC = () => {
const { hasPermission } = useUser();
const intl = useIntl();
const { data, error, revalidate } = useSWR<MainSettings>(
'/api/v1/settings/main'
@ -41,7 +43,6 @@ const SettingsMain: React.FC = () => {
<div className="mt-6 sm:mt-5">
<Formik
initialValues={{
apiKey: data?.apiKey,
applicationUrl: data?.applicationUrl,
}}
onSubmit={async (values) => {
@ -56,43 +57,45 @@ const SettingsMain: React.FC = () => {
}
}}
>
{({ errors, touched, isSubmitting }) => {
{({ isSubmitting }) => {
return (
<Form>
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800 sm:pt-5">
<label
htmlFor="username"
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2"
>
{intl.formatMessage(messages.apikey)}
</label>
<div className="mt-1 sm:mt-0 sm:col-span-2">
<div className="max-w-lg flex rounded-md shadow-sm">
<input
type="text"
id="username"
className="flex-1 form-input block w-full min-w-0 rounded-none rounded-l-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-gray-700 border border-gray-500"
value={data?.apiKey}
readOnly
/>
<CopyButton textToCopy={data?.apiKey ?? ''} />
<button className="-ml-px relative inline-flex items-center px-4 py-2 border border-gray-500 text-sm leading-5 font-medium rounded-r-md text-white bg-indigo-500 hover:bg-indigo-400 focus:outline-none focus:ring-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z"
clipRule="evenodd"
/>
</svg>
</button>
{hasPermission(Permission.ADMIN) && (
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800 sm:pt-5">
<label
htmlFor="username"
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2"
>
{intl.formatMessage(messages.apikey)}
</label>
<div className="mt-1 sm:mt-0 sm:col-span-2">
<div className="max-w-lg flex rounded-md shadow-sm">
<input
type="text"
id="apiKey"
className="flex-1 form-input block w-full min-w-0 rounded-none rounded-l-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-gray-700 border border-gray-500"
value={data?.apiKey}
readOnly
/>
<CopyButton textToCopy={data?.apiKey ?? ''} />
<button className="-ml-px relative inline-flex items-center px-4 py-2 border border-gray-500 text-sm leading-5 font-medium rounded-r-md text-white bg-indigo-500 hover:bg-indigo-400 focus:outline-none focus:ring-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z"
clipRule="evenodd"
/>
</svg>
</button>
</div>
</div>
</div>
</div>
)}
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800 sm:pt-5">
<label
htmlFor="name"

@ -49,7 +49,7 @@ const SettingsNotifications: React.FC = ({ children }) => {
<Link href={route}>
<a
className={`whitespace-nowrap ml-8 first:ml-0 px-3 py-2 font-medium text-sm rounded-md ${
!!router.pathname.match(regex) ? activeLinkColor : inactiveLinkColor
router.pathname.match(regex) ? activeLinkColor : inactiveLinkColor
}`}
aria-current="page"
>

@ -229,7 +229,7 @@ const SonarrModal: React.FC<SonarrModalProps> = ({
okText={
isSubmitting
? intl.formatMessage(messages.saving)
: !!sonarr
: sonarr
? intl.formatMessage(messages.save)
: intl.formatMessage(messages.add)
}

@ -0,0 +1,40 @@
import React from 'react';
import { MediaStatus } from '../../../server/constants/media';
import Badge from '../Common/Badge';
import { useIntl } from 'react-intl';
import globalMessages from '../../i18n/globalMessages';
interface StatusBadgeProps {
status: MediaStatus;
}
const StatusBadge: React.FC<StatusBadgeProps> = ({ status }) => {
const intl = useIntl();
return (
<>
{status === MediaStatus.AVAILABLE && (
<Badge badgeType="success">
{intl.formatMessage(globalMessages.available)}
</Badge>
)}
{status === MediaStatus.PARTIALLY_AVAILABLE && (
<Badge badgeType="success">
{intl.formatMessage(globalMessages.partiallyavailable)}
</Badge>
)}
{status === MediaStatus.PROCESSING && (
<Badge badgeType="danger">
{intl.formatMessage(globalMessages.unavailable)}
</Badge>
)}
{status === MediaStatus.PENDING && (
<Badge badgeType="warning">
{intl.formatMessage(globalMessages.pending)}
</Badge>
)}
</>
);
};
export default StatusBadge;

@ -1,4 +1,5 @@
import React, { useContext } from 'react';
import { useInView } from 'react-intersection-observer';
import useSWR from 'swr';
import type { MovieDetails } from '../../../server/models/Movie';
import type { TvDetails } from '../../../server/models/Tv';
@ -15,15 +16,22 @@ const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => {
};
const TmdbTitleCard: React.FC<TmdbTitleCardProps> = ({ tmdbId, type }) => {
const { ref, inView } = useInView({
triggerOnce: true,
});
const { locale } = useContext(LanguageContext);
const url =
type === 'movie' ? `/api/v1/movie/${tmdbId}` : `/api/v1/tv/${tmdbId}`;
const { data: title, error } = useSWR<MovieDetails | TvDetails>(
`${url}?language=${locale}`
inView ? `${url}?language=${locale}` : null
);
if (!title && !error) {
return <TitleCard.Placeholder />;
return (
<div ref={ref}>
<TitleCard.Placeholder />
</div>
);
}
if (!title) {

@ -156,12 +156,12 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
/>
<SlideOver
show={showManager}
title="Manage Series"
title={intl.formatMessage(messages.manageModalTitle)}
onClose={() => setShowManager(false)}
subText={data.name}
>
<h3 className="text-xl mb-2">
{intl.formatMessage(messages.manageModalTitle)}
{intl.formatMessage(messages.manageModalRequests)}
</h3>
<div className="bg-gray-600 shadow overflow-hidden rounded-md">
<ul>
@ -473,7 +473,7 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
</div>
<Slider
sliderKey="cast"
isLoading={!data && !error}
isLoading={false}
isEmpty={false}
items={data?.credits.cast.slice(0, 20).map((person) => (
<PersonCard

@ -9,6 +9,7 @@ import { hasPermission } from '../../../server/lib/permissions';
import { Permission } from '../../hooks/useUser';
import { useRouter } from 'next/router';
import Header from '../Common/Header';
import Table from '../Common/Table';
const messages = defineMessages({
userlist: 'User List',
@ -37,102 +38,80 @@ const UserList: React.FC = () => {
return (
<>
<Header extraMargin={4}>{intl.formatMessage(messages.userlist)}</Header>
<div className="flex flex-col">
<div className="my-2 overflow-x-auto -mx-6 sm:-mx-6 md:mx-4 lg:mx-4">
<div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
<div className="shadow overflow-hidden sm:rounded-lg">
<table className="min-w-full">
<thead>
<tr>
<th className="px-6 py-3 bg-gray-500 text-left text-xs leading-4 font-medium text-gray-200 uppercase tracking-wider">
{intl.formatMessage(messages.username)}
</th>
<th className="px-6 py-3 bg-gray-500 text-left text-xs leading-4 font-medium text-gray-200 uppercase tracking-wider">
{intl.formatMessage(messages.totalrequests)}
</th>
<th className="px-6 py-3 bg-gray-500 text-left text-xs leading-4 font-medium text-gray-200 uppercase tracking-wider">
{intl.formatMessage(messages.usertype)}
</th>
<th className="px-6 py-3 bg-gray-500 text-left text-xs leading-4 font-medium text-gray-200 uppercase tracking-wider">
{intl.formatMessage(messages.role)}
</th>
<th className="px-6 py-3 bg-gray-500 text-left text-xs leading-4 font-medium text-gray-200 uppercase tracking-wider">
{intl.formatMessage(messages.created)}
</th>
<th className="px-6 py-3 bg-gray-500 text-left text-xs leading-4 font-medium text-gray-200 uppercase tracking-wider">
{intl.formatMessage(messages.lastupdated)}
</th>
<th className="px-6 py-3 bg-gray-500"></th>
</tr>
</thead>
<tbody className="bg-gray-600 divide-y divide-gray-700">
{data?.map((user) => (
<tr key={`user-list-${user.id}`}>
<td className="px-6 py-4 whitespace-nowrap">
<div className="flex items-center">
<div className="flex-shrink-0 h-10 w-10">
<img
className="h-10 w-10 rounded-full"
src={user.avatar}
alt=""
/>
</div>
<div className="ml-4">
<div className="text-sm leading-5 font-medium text-white">
{user.username}
</div>
<div className="text-sm leading-5 text-gray-300">
{user.email}
</div>
</div>
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm leading-5 text-white">
{user.requestCount}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<Badge badgeType="warning">
{intl.formatMessage(messages.plexuser)}
</Badge>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm leading-5 text-white">
{hasPermission(Permission.ADMIN, user.permissions)
? intl.formatMessage(messages.admin)
: intl.formatMessage(messages.user)}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm leading-5 text-white">
<FormattedDate value={user.createdAt} />
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm leading-5 text-white">
<FormattedDate value={user.updatedAt} />
</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm leading-5 font-medium">
<Button
buttonType="warning"
className="mr-2"
onClick={() =>
router.push(
'/users/[userId]/edit',
`/users/${user.id}/edit`
)
}
>
{intl.formatMessage(messages.edit)}
</Button>
<Button buttonType="danger">
{intl.formatMessage(messages.delete)}
</Button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
<Table>
<thead>
<tr>
<Table.TH>{intl.formatMessage(messages.username)}</Table.TH>
<Table.TH>{intl.formatMessage(messages.totalrequests)}</Table.TH>
<Table.TH>{intl.formatMessage(messages.usertype)}</Table.TH>
<Table.TH>{intl.formatMessage(messages.role)}</Table.TH>
<Table.TH>{intl.formatMessage(messages.created)}</Table.TH>
<Table.TH>{intl.formatMessage(messages.lastupdated)}</Table.TH>
<Table.TH></Table.TH>
</tr>
</thead>
<Table.TBody>
{data?.map((user) => (
<tr key={`user-list-${user.id}`}>
<Table.TD>
<div className="flex items-center">
<div className="flex-shrink-0 h-10 w-10">
<img
className="h-10 w-10 rounded-full"
src={user.avatar}
alt=""
/>
</div>
<div className="ml-4">
<div className="text-sm leading-5 font-medium">
{user.username}
</div>
<div className="text-sm leading-5 text-gray-300">
{user.email}
</div>
</div>
</div>
</Table.TD>
<Table.TD>
<div className="text-sm leading-5">{user.requestCount}</div>
</Table.TD>
<Table.TD>
<Badge badgeType="warning">
{intl.formatMessage(messages.plexuser)}
</Badge>
</Table.TD>
<Table.TD>
{hasPermission(Permission.ADMIN, user.permissions)
? intl.formatMessage(messages.admin)
: intl.formatMessage(messages.user)}
</Table.TD>
<Table.TD>
<FormattedDate value={user.createdAt} />
</Table.TD>
<Table.TD>
<FormattedDate value={user.updatedAt} />
</Table.TD>
<Table.TD alignText="right">
<Button
buttonType="warning"
className="mr-2"
onClick={() =>
router.push(
'/users/[userId]/edit',
`/users/${user.id}/edit`
)
}
>
{intl.formatMessage(messages.edit)}
</Button>
<Button buttonType="danger">
{intl.formatMessage(messages.delete)}
</Button>
</Table.TD>
</tr>
))}
</Table.TBody>
</Table>
</>
);
};

@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, useRef } from 'react';
import { User, useUser } from '../hooks/useUser';
import { useRouter } from 'next/dist/client/router';
@ -17,20 +17,19 @@ export const UserContext: React.FC<UserContextProps> = ({
}) => {
const { user, error, revalidate } = useUser({ initialData: initialUser });
const router = useRouter();
const routing = useRef(false);
useEffect(() => {
revalidate();
}, [router.pathname, revalidate]);
useEffect(() => {
let routing = false;
if (
!router.pathname.match(/(setup|login)/) &&
(!user || error) &&
!routing
!routing.current
) {
routing = true;
routing.current = true;
location.href = '/login';
}
}, [router, user, error]);

@ -1,5 +1,4 @@
import useSwr from 'swr';
import { useRef } from 'react';
import { hasPermission, Permission } from '../../server/lib/permissions';
export interface User {

@ -13,6 +13,7 @@ const globalMessages = defineMessages({
cancel: 'Cancel',
approve: 'Approve',
decline: 'Decline',
delete: 'Delete',
});
export default globalMessages;

@ -51,6 +51,17 @@
"components.RequestBlock.seasons": "Seasons",
"components.RequestCard.requestedby": "Requested by {username}",
"components.RequestCard.seasons": "Seasons",
"components.RequestList.RequestItem.notavailable": "N/A",
"components.RequestList.RequestItem.requestedby": "Requested by {username}",
"components.RequestList.RequestItem.seasons": "Seasons",
"components.RequestList.mediaInfo": "Media Info",
"components.RequestList.modifiedBy": "Last Modified By",
"components.RequestList.next": "Next",
"components.RequestList.previous": "Previous",
"components.RequestList.requestedAt": "Requested At",
"components.RequestList.requests": "Requests",
"components.RequestList.showingresults": "Showing <strong>{from}</strong> to <strong>{to}</strong> of <strong>{total}</strong> results",
"components.RequestList.status": "Status",
"components.RequestModal.cancel": "Cancel Request",
"components.RequestModal.cancelling": "Cancelling...",
"components.RequestModal.cancelrequest": "This will remove your request. Are you sure you want to continue?",
@ -275,6 +286,7 @@
"i18n.cancel": "Cancel",
"i18n.decline": "Decline",
"i18n.declined": "Declined",
"i18n.delete": "Delete",
"i18n.movies": "Movies",
"i18n.partiallyavailable": "Partially Available",
"i18n.pending": "Pending",

@ -51,6 +51,17 @@
"components.RequestBlock.seasons": "Saisons",
"components.RequestCard.requestedby": "Demandé par {username}",
"components.RequestCard.seasons": "Saisons",
"components.RequestList.RequestItem.notavailable": "",
"components.RequestList.RequestItem.requestedby": "",
"components.RequestList.RequestItem.seasons": "",
"components.RequestList.mediaInfo": "",
"components.RequestList.modifiedBy": "",
"components.RequestList.next": "",
"components.RequestList.previous": "",
"components.RequestList.requestedAt": "",
"components.RequestList.requests": "",
"components.RequestList.showingresults": "",
"components.RequestList.status": "",
"components.RequestModal.cancel": "Annuler la Demande",
"components.RequestModal.cancelling": "Annulation...",
"components.RequestModal.cancelrequest": "Votre demande d'ajout va être annulée. Êtes-vous sûr de vouloir annuler?",
@ -275,6 +286,7 @@
"i18n.cancel": "Annuler",
"i18n.decline": "Refuser",
"i18n.declined": "Refusé",
"i18n.delete": "",
"i18n.movies": "Films",
"i18n.partiallyavailable": "Partiellement Disponible",
"i18n.pending": "En Attente",

@ -1,290 +1,302 @@
{
"components.Discover.discovermovies": "人気の映画",
"components.Discover.discovertv": "人気のテレビ番組",
"components.Discover.nopending": "",
"components.Discover.popularmovies": "人気の映画",
"components.Discover.populartv": "人気のテレビ番組",
"components.Discover.recentlyAdded": "",
"components.Discover.recentrequests": "最近のリクエスト",
"components.Discover.trending": "",
"components.Discover.upcoming": "",
"components.Discover.upcomingmovies": "",
"components.Layout.LanguagePicker.changelanguage": "言語",
"components.Layout.SearchInput.searchPlaceholder": "作品名で検索",
"components.Layout.Sidebar.dashboard": "ホーム",
"components.Layout.Sidebar.requests": "リクエスト",
"components.Layout.Sidebar.settings": "設定",
"components.Layout.Sidebar.users": "",
"components.Layout.UserDropdown.signout": "",
"components.Layout.alphawarning": "",
"components.Login.signinplex": "",
"components.MovieDetails.approve": "",
"components.MovieDetails.available": "",
"components.MovieDetails.budget": "興行収入",
"components.MovieDetails.cancelrequest": "チャンセルリクエスト",
"components.MovieDetails.cast": "キャスト",
"components.MovieDetails.decline": "",
"components.MovieDetails.manageModalClearMedia": "",
"components.MovieDetails.manageModalClearMediaWarning": "",
"components.MovieDetails.manageModalNoRequests": "",
"components.MovieDetails.manageModalRequests": "リクエスト",
"components.MovieDetails.manageModalTitle": "",
"components.MovieDetails.originallanguage": "言語",
"components.MovieDetails.overview": "ストーリー",
"components.MovieDetails.overviewunavailable": "",
"components.MovieDetails.pending": "リクエスト中",
"components.MovieDetails.recommendations": "オススメの作品",
"components.MovieDetails.recommendationssubtext": "",
"components.MovieDetails.releasedate": "公開日",
"components.MovieDetails.request": "リクエストする",
"components.MovieDetails.revenue": "製作費",
"components.MovieDetails.runtime": "{minutes}分",
"components.MovieDetails.similar": "関連作品",
"components.MovieDetails.similarsubtext": "",
"components.MovieDetails.status": "状態",
"components.MovieDetails.unavailable": "",
"components.MovieDetails.userrating": "ユーザー評価",
"components.MovieDetails.viewrequest": "",
"components.PlexLoginButton.loading": "",
"components.PlexLoginButton.loggingin": "",
"components.PlexLoginButton.loginwithplex": "",
"components.RequestBlock.seasons": "",
"components.RequestCard.requestedby": "",
"components.RequestCard.seasons": "",
"components.RequestModal.cancel": "チャンセルリクエスト",
"components.RequestModal.cancelling": "",
"components.RequestModal.cancelrequest": "このリクエストをキャンセルしてよろしいですか?",
"components.RequestModal.close": "",
"components.RequestModal.extras": "",
"components.RequestModal.notrequested": "",
"components.RequestModal.numberofepisodes": "",
"components.RequestModal.pendingrequest": "",
"components.RequestModal.request": "リクエストする",
"components.RequestModal.requestCancel": "",
"components.RequestModal.requestSuccess": "",
"components.RequestModal.requestadmin": "このリクエストが今すぐ承認致します。よろしいですか?",
"components.RequestModal.requestfrom": "",
"components.RequestModal.requesting": "",
"components.RequestModal.requestseasons": "",
"components.RequestModal.requesttitle": "",
"components.RequestModal.season": "",
"components.RequestModal.seasonnumber": "",
"components.RequestModal.selectseason": "",
"components.RequestModal.status": "状態",
"components.Search.searchresults": "",
"components.Settings.Notifications.agentenabled": "",
"components.Settings.Notifications.authPass": "",
"components.Settings.Notifications.authUser": "",
"components.Settings.Notifications.emailsender": "",
"components.Settings.Notifications.enableSsl": "",
"components.Settings.Notifications.save": "",
"components.Settings.Notifications.saving": "",
"components.Settings.Notifications.smtpHost": "",
"components.Settings.Notifications.smtpPort": "",
"components.Settings.Notifications.validationFromRequired": "",
"components.Settings.Notifications.validationSmtpHostRequired": "",
"components.Settings.Notifications.validationSmtpPortRequired": "",
"components.Settings.Notifications.validationWebhookUrlRequired": "",
"components.Settings.Notifications.webhookUrl": "",
"components.Settings.Notifications.webhookUrlPlaceholder": "",
"components.Settings.RadarrModal.add": "",
"components.Settings.RadarrModal.apiKey": "",
"components.Settings.RadarrModal.apiKeyPlaceholder": "",
"components.Settings.RadarrModal.baseUrl": "",
"components.Settings.RadarrModal.baseUrlPlaceholder": "",
"components.Settings.RadarrModal.createradarr": "",
"components.Settings.RadarrModal.defaultserver": "",
"components.Settings.RadarrModal.editradarr": "",
"components.Settings.RadarrModal.hostname": "",
"components.Settings.RadarrModal.minimumAvailability": "",
"components.Settings.RadarrModal.port": "",
"components.Settings.RadarrModal.qualityprofile": "",
"components.Settings.RadarrModal.rootfolder": "",
"components.Settings.RadarrModal.save": "",
"components.Settings.RadarrModal.saving": "",
"components.Settings.RadarrModal.selectMinimumAvailability": "",
"components.Settings.RadarrModal.selectQualityProfile": "",
"components.Settings.RadarrModal.selectRootFolder": "",
"components.Settings.RadarrModal.server4k": "",
"components.Settings.RadarrModal.servername": "",
"components.Settings.RadarrModal.servernamePlaceholder": "",
"components.Settings.RadarrModal.ssl": "",
"components.Settings.RadarrModal.test": "",
"components.Settings.RadarrModal.testing": "",
"components.Settings.RadarrModal.toastRadarrTestFailure": "",
"components.Settings.RadarrModal.toastRadarrTestSuccess": "",
"components.Settings.RadarrModal.validationApiKeyRequired": "",
"components.Settings.RadarrModal.validationHostnameRequired": "",
"components.Settings.RadarrModal.validationPortRequired": "",
"components.Settings.RadarrModal.validationProfileRequired": "",
"components.Settings.RadarrModal.validationRootFolderRequired": "",
"components.Settings.SonarrModal.add": "",
"components.Settings.SonarrModal.apiKey": "",
"components.Settings.SonarrModal.apiKeyPlaceholder": "",
"components.Settings.SonarrModal.baseUrl": "",
"components.Settings.SonarrModal.baseUrlPlaceholder": "",
"components.Settings.SonarrModal.createsonarr": "",
"components.Settings.SonarrModal.defaultserver": "",
"components.Settings.SonarrModal.editsonarr": "",
"components.Settings.SonarrModal.hostname": "",
"components.Settings.SonarrModal.port": "",
"components.Settings.SonarrModal.qualityprofile": "",
"components.Settings.SonarrModal.rootfolder": "",
"components.Settings.SonarrModal.save": "",
"components.Settings.SonarrModal.saving": "",
"components.Settings.SonarrModal.seasonfolders": "",
"components.Settings.SonarrModal.selectQualityProfile": "",
"components.Settings.SonarrModal.selectRootFolder": "",
"components.Settings.SonarrModal.server4k": "",
"components.Settings.SonarrModal.servername": "",
"components.Settings.SonarrModal.servernamePlaceholder": "",
"components.Settings.SonarrModal.ssl": "",
"components.Settings.SonarrModal.test": "",
"components.Settings.SonarrModal.testing": "",
"components.Settings.SonarrModal.toastRadarrTestFailure": "",
"components.Settings.SonarrModal.toastRadarrTestSuccess": "",
"components.Settings.SonarrModal.validationApiKeyRequired": "",
"components.Settings.SonarrModal.validationHostnameRequired": "",
"components.Settings.SonarrModal.validationPortRequired": "",
"components.Settings.SonarrModal.validationProfileRequired": "",
"components.Settings.SonarrModal.validationRootFolderRequired": "",
"components.Settings.activeProfile": "",
"components.Settings.addradarr": "",
"components.Settings.address": "",
"components.Settings.addsonarr": "",
"components.Settings.apikey": "",
"components.Settings.applicationurl": "",
"components.Settings.cancelscan": "",
"components.Settings.copied": "",
"components.Settings.currentlibrary": "",
"components.Settings.default": "",
"components.Settings.default4k": "",
"components.Settings.delete": "",
"components.Settings.deleteserverconfirm": "",
"components.Settings.edit": "",
"components.Settings.generalsettings": "",
"components.Settings.generalsettingsDescription": "",
"components.Settings.hostname": "",
"components.Settings.jobname": "",
"components.Settings.librariesRemaining": "",
"components.Settings.manualscan": "",
"components.Settings.manualscanDescription": "",
"components.Settings.menuAbout": "",
"components.Settings.menuGeneralSettings": "",
"components.Settings.menuJobs": "",
"components.Settings.menuLogs": "",
"components.Settings.menuNotifications": "",
"components.Settings.menuPlexSettings": "",
"components.Settings.menuServices": "",
"components.Settings.nextexecution": "",
"components.Settings.notificationsettings": "",
"components.Settings.notificationsettingsDescription": "",
"components.Settings.notrunning": "",
"components.Settings.plexlibraries": "",
"components.Settings.plexlibrariesDescription": "",
"components.Settings.plexsettings": "",
"components.Settings.plexsettingsDescription": "",
"components.Settings.port": "",
"components.Settings.radarrSettingsDescription": "",
"components.Settings.radarrsettings": "",
"components.Settings.runnow": "",
"components.Settings.save": "",
"components.Settings.saving": "",
"components.Settings.servername": "",
"components.Settings.servernamePlaceholder": "",
"components.Settings.sonarrSettingsDescription": "",
"components.Settings.sonarrsettings": "",
"components.Settings.ssl": "",
"components.Settings.startscan": "",
"components.Settings.sync": "",
"components.Settings.syncing": "",
"components.Setup.configureplex": "",
"components.Setup.configureservices": "",
"components.Setup.continue": "",
"components.Setup.finish": "",
"components.Setup.finishing": "",
"components.Setup.loginwithplex": "",
"components.Setup.signinMessage": "",
"components.Setup.welcome": "",
"components.Slider.noresults": "",
"components.TitleCard.movie": "",
"components.TitleCard.tvshow": "",
"components.TvDetails.approve": "",
"components.TvDetails.approverequests": "",
"components.TvDetails.available": "",
"components.TvDetails.cancelrequest": "チャンセルリクエスト",
"components.TvDetails.cast": "キャスト",
"components.TvDetails.decline": "",
"components.TvDetails.declinerequests": "",
"components.TvDetails.manageModalClearMedia": "",
"components.TvDetails.manageModalClearMediaWarning": "",
"components.TvDetails.manageModalNoRequests": "",
"components.TvDetails.manageModalRequests": "リクエスト",
"components.TvDetails.manageModalTitle": "",
"components.TvDetails.originallanguage": "言語",
"components.TvDetails.overview": "ストーリー",
"components.TvDetails.overviewunavailable": "",
"components.TvDetails.pending": "リクエスト中",
"components.TvDetails.recommendations": "オススメの作品",
"components.TvDetails.recommendationssubtext": "",
"components.TvDetails.request": "リクエストする",
"components.TvDetails.requestmore": "",
"components.TvDetails.similar": "",
"components.TvDetails.similarsubtext": "",
"components.TvDetails.status": "状態",
"components.TvDetails.unavailable": "",
"components.TvDetails.userrating": "ユーザー評価",
"components.UserEdit.admin": "",
"components.UserEdit.adminDescription": "",
"components.UserEdit.autoapprove": "",
"components.UserEdit.autoapproveDescription": "",
"components.UserEdit.avatar": "",
"components.UserEdit.edituser": "",
"components.UserEdit.email": "",
"components.UserEdit.managerequests": "",
"components.UserEdit.managerequestsDescription": "",
"components.UserEdit.permissions": "",
"components.UserEdit.request": "リクエストする",
"components.UserEdit.requestDescription": "",
"components.UserEdit.save": "",
"components.UserEdit.saving": "",
"components.UserEdit.settings": "",
"components.UserEdit.settingsDescription": "",
"components.UserEdit.userfail": "",
"components.UserEdit.username": "",
"components.UserEdit.users": "",
"components.UserEdit.usersDescription": "",
"components.UserEdit.usersaved": "",
"components.UserEdit.vote": "",
"components.UserEdit.voteDescription": "",
"components.UserList.admin": "",
"components.UserList.created": "",
"components.UserList.delete": "",
"components.UserList.edit": "",
"components.UserList.lastupdated": "",
"components.UserList.plexuser": "",
"components.UserList.role": "",
"components.UserList.totalrequests": "",
"components.UserList.user": "",
"components.UserList.userlist": "",
"components.UserList.username": "",
"components.UserList.usertype": "",
"i18n.approve": "",
"i18n.approved": "",
"i18n.available": "",
"i18n.cancel": "",
"i18n.decline": "",
"i18n.declined": "",
"i18n.movies": "",
"i18n.partiallyavailable": "",
"i18n.pending": "リクエスト中",
"i18n.processing": "",
"i18n.tvshows": "",
"i18n.unavailable": "",
"pages.internalServerError": "",
"pages.oops": "ああ",
"pages.pageNotFound": "",
"pages.returnHome": "ホームへ戻る",
"pages.serviceUnavailable": "",
"pages.somethingWentWrong": ""
}
"components.Discover.discovermovies": "人気の映画",
"components.Discover.discovertv": "人気のテレビ番組",
"components.Discover.nopending": "",
"components.Discover.popularmovies": "人気の映画",
"components.Discover.populartv": "人気のテレビ番組",
"components.Discover.recentlyAdded": "",
"components.Discover.recentrequests": "最近のリクエスト",
"components.Discover.trending": "",
"components.Discover.upcoming": "",
"components.Discover.upcomingmovies": "",
"components.Layout.LanguagePicker.changelanguage": "言語",
"components.Layout.SearchInput.searchPlaceholder": "作品名で検索",
"components.Layout.Sidebar.dashboard": "ホーム",
"components.Layout.Sidebar.requests": "リクエスト",
"components.Layout.Sidebar.settings": "設定",
"components.Layout.Sidebar.users": "",
"components.Layout.UserDropdown.signout": "",
"components.Layout.alphawarning": "",
"components.Login.signinplex": "",
"components.MovieDetails.approve": "",
"components.MovieDetails.available": "",
"components.MovieDetails.budget": "興行収入",
"components.MovieDetails.cancelrequest": "チャンセルリクエスト",
"components.MovieDetails.cast": "キャスト",
"components.MovieDetails.decline": "",
"components.MovieDetails.manageModalClearMedia": "",
"components.MovieDetails.manageModalClearMediaWarning": "",
"components.MovieDetails.manageModalNoRequests": "",
"components.MovieDetails.manageModalRequests": "リクエスト",
"components.MovieDetails.manageModalTitle": "",
"components.MovieDetails.originallanguage": "言語",
"components.MovieDetails.overview": "ストーリー",
"components.MovieDetails.overviewunavailable": "",
"components.MovieDetails.pending": "リクエスト中",
"components.MovieDetails.recommendations": "オススメの作品",
"components.MovieDetails.recommendationssubtext": "",
"components.MovieDetails.releasedate": "公開日",
"components.MovieDetails.request": "リクエストする",
"components.MovieDetails.revenue": "製作費",
"components.MovieDetails.runtime": "{minutes}分",
"components.MovieDetails.similar": "関連作品",
"components.MovieDetails.similarsubtext": "",
"components.MovieDetails.status": "状態",
"components.MovieDetails.unavailable": "",
"components.MovieDetails.userrating": "ユーザー評価",
"components.MovieDetails.viewrequest": "",
"components.PlexLoginButton.loading": "",
"components.PlexLoginButton.loggingin": "",
"components.PlexLoginButton.loginwithplex": "",
"components.RequestBlock.seasons": "",
"components.RequestCard.requestedby": "",
"components.RequestCard.seasons": "",
"components.RequestList.RequestItem.notavailable": "",
"components.RequestList.RequestItem.requestedby": "",
"components.RequestList.RequestItem.seasons": "",
"components.RequestList.mediaInfo": "",
"components.RequestList.modifiedBy": "",
"components.RequestList.next": "",
"components.RequestList.previous": "",
"components.RequestList.requestedAt": "",
"components.RequestList.requests": "",
"components.RequestList.showingresults": "",
"components.RequestList.status": "",
"components.RequestModal.cancel": "チャンセルリクエスト",
"components.RequestModal.cancelling": "",
"components.RequestModal.cancelrequest": "このリクエストをキャンセルしてよろしいですか?",
"components.RequestModal.close": "",
"components.RequestModal.extras": "",
"components.RequestModal.notrequested": "",
"components.RequestModal.numberofepisodes": "",
"components.RequestModal.pendingrequest": "",
"components.RequestModal.request": "リクエストする",
"components.RequestModal.requestCancel": "",
"components.RequestModal.requestSuccess": "",
"components.RequestModal.requestadmin": "このリクエストが今すぐ承認致します。よろしいですか?",
"components.RequestModal.requestfrom": "",
"components.RequestModal.requesting": "",
"components.RequestModal.requestseasons": "",
"components.RequestModal.requesttitle": "",
"components.RequestModal.season": "",
"components.RequestModal.seasonnumber": "",
"components.RequestModal.selectseason": "",
"components.RequestModal.status": "状態",
"components.Search.searchresults": "",
"components.Settings.Notifications.agentenabled": "",
"components.Settings.Notifications.authPass": "",
"components.Settings.Notifications.authUser": "",
"components.Settings.Notifications.emailsender": "",
"components.Settings.Notifications.enableSsl": "",
"components.Settings.Notifications.save": "",
"components.Settings.Notifications.saving": "",
"components.Settings.Notifications.smtpHost": "",
"components.Settings.Notifications.smtpPort": "",
"components.Settings.Notifications.validationFromRequired": "",
"components.Settings.Notifications.validationSmtpHostRequired": "",
"components.Settings.Notifications.validationSmtpPortRequired": "",
"components.Settings.Notifications.validationWebhookUrlRequired": "",
"components.Settings.Notifications.webhookUrl": "",
"components.Settings.Notifications.webhookUrlPlaceholder": "",
"components.Settings.RadarrModal.add": "",
"components.Settings.RadarrModal.apiKey": "",
"components.Settings.RadarrModal.apiKeyPlaceholder": "",
"components.Settings.RadarrModal.baseUrl": "",
"components.Settings.RadarrModal.baseUrlPlaceholder": "",
"components.Settings.RadarrModal.createradarr": "",
"components.Settings.RadarrModal.defaultserver": "",
"components.Settings.RadarrModal.editradarr": "",
"components.Settings.RadarrModal.hostname": "",
"components.Settings.RadarrModal.minimumAvailability": "",
"components.Settings.RadarrModal.port": "",
"components.Settings.RadarrModal.qualityprofile": "",
"components.Settings.RadarrModal.rootfolder": "",
"components.Settings.RadarrModal.save": "",
"components.Settings.RadarrModal.saving": "",
"components.Settings.RadarrModal.selectMinimumAvailability": "",
"components.Settings.RadarrModal.selectQualityProfile": "",
"components.Settings.RadarrModal.selectRootFolder": "",
"components.Settings.RadarrModal.server4k": "",
"components.Settings.RadarrModal.servername": "",
"components.Settings.RadarrModal.servernamePlaceholder": "",
"components.Settings.RadarrModal.ssl": "",
"components.Settings.RadarrModal.test": "",
"components.Settings.RadarrModal.testing": "",
"components.Settings.RadarrModal.toastRadarrTestFailure": "",
"components.Settings.RadarrModal.toastRadarrTestSuccess": "",
"components.Settings.RadarrModal.validationApiKeyRequired": "",
"components.Settings.RadarrModal.validationHostnameRequired": "",
"components.Settings.RadarrModal.validationPortRequired": "",
"components.Settings.RadarrModal.validationProfileRequired": "",
"components.Settings.RadarrModal.validationRootFolderRequired": "",
"components.Settings.SonarrModal.add": "",
"components.Settings.SonarrModal.apiKey": "",
"components.Settings.SonarrModal.apiKeyPlaceholder": "",
"components.Settings.SonarrModal.baseUrl": "",
"components.Settings.SonarrModal.baseUrlPlaceholder": "",
"components.Settings.SonarrModal.createsonarr": "",
"components.Settings.SonarrModal.defaultserver": "",
"components.Settings.SonarrModal.editsonarr": "",
"components.Settings.SonarrModal.hostname": "",
"components.Settings.SonarrModal.port": "",
"components.Settings.SonarrModal.qualityprofile": "",
"components.Settings.SonarrModal.rootfolder": "",
"components.Settings.SonarrModal.save": "",
"components.Settings.SonarrModal.saving": "",
"components.Settings.SonarrModal.seasonfolders": "",
"components.Settings.SonarrModal.selectQualityProfile": "",
"components.Settings.SonarrModal.selectRootFolder": "",
"components.Settings.SonarrModal.server4k": "",
"components.Settings.SonarrModal.servername": "",
"components.Settings.SonarrModal.servernamePlaceholder": "",
"components.Settings.SonarrModal.ssl": "",
"components.Settings.SonarrModal.test": "",
"components.Settings.SonarrModal.testing": "",
"components.Settings.SonarrModal.toastRadarrTestFailure": "",
"components.Settings.SonarrModal.toastRadarrTestSuccess": "",
"components.Settings.SonarrModal.validationApiKeyRequired": "",
"components.Settings.SonarrModal.validationHostnameRequired": "",
"components.Settings.SonarrModal.validationPortRequired": "",
"components.Settings.SonarrModal.validationProfileRequired": "",
"components.Settings.SonarrModal.validationRootFolderRequired": "",
"components.Settings.activeProfile": "",
"components.Settings.addradarr": "",
"components.Settings.address": "",
"components.Settings.addsonarr": "",
"components.Settings.apikey": "",
"components.Settings.applicationurl": "",
"components.Settings.cancelscan": "",
"components.Settings.copied": "",
"components.Settings.currentlibrary": "",
"components.Settings.default": "",
"components.Settings.default4k": "",
"components.Settings.delete": "",
"components.Settings.deleteserverconfirm": "",
"components.Settings.edit": "",
"components.Settings.generalsettings": "",
"components.Settings.generalsettingsDescription": "",
"components.Settings.hostname": "",
"components.Settings.jobname": "",
"components.Settings.librariesRemaining": "",
"components.Settings.manualscan": "",
"components.Settings.manualscanDescription": "",
"components.Settings.menuAbout": "",
"components.Settings.menuGeneralSettings": "",
"components.Settings.menuJobs": "",
"components.Settings.menuLogs": "",
"components.Settings.menuNotifications": "",
"components.Settings.menuPlexSettings": "",
"components.Settings.menuServices": "",
"components.Settings.nextexecution": "",
"components.Settings.notificationsettings": "",
"components.Settings.notificationsettingsDescription": "",
"components.Settings.notrunning": "",
"components.Settings.plexlibraries": "",
"components.Settings.plexlibrariesDescription": "",
"components.Settings.plexsettings": "",
"components.Settings.plexsettingsDescription": "",
"components.Settings.port": "",
"components.Settings.radarrSettingsDescription": "",
"components.Settings.radarrsettings": "",
"components.Settings.runnow": "",
"components.Settings.save": "",
"components.Settings.saving": "",
"components.Settings.servername": "",
"components.Settings.servernamePlaceholder": "",
"components.Settings.sonarrSettingsDescription": "",
"components.Settings.sonarrsettings": "",
"components.Settings.ssl": "",
"components.Settings.startscan": "",
"components.Settings.sync": "",
"components.Settings.syncing": "",
"components.Setup.configureplex": "",
"components.Setup.configureservices": "",
"components.Setup.continue": "",
"components.Setup.finish": "",
"components.Setup.finishing": "",
"components.Setup.loginwithplex": "",
"components.Setup.signinMessage": "",
"components.Setup.welcome": "",
"components.Slider.noresults": "",
"components.TitleCard.movie": "",
"components.TitleCard.tvshow": "",
"components.TvDetails.approve": "",
"components.TvDetails.approverequests": "",
"components.TvDetails.available": "",
"components.TvDetails.cancelrequest": "チャンセルリクエスト",
"components.TvDetails.cast": "キャスト",
"components.TvDetails.decline": "",
"components.TvDetails.declinerequests": "",
"components.TvDetails.manageModalClearMedia": "",
"components.TvDetails.manageModalClearMediaWarning": "",
"components.TvDetails.manageModalNoRequests": "",
"components.TvDetails.manageModalRequests": "リクエスト",
"components.TvDetails.manageModalTitle": "",
"components.TvDetails.originallanguage": "言語",
"components.TvDetails.overview": "ストーリー",
"components.TvDetails.overviewunavailable": "",
"components.TvDetails.pending": "リクエスト中",
"components.TvDetails.recommendations": "オススメの作品",
"components.TvDetails.recommendationssubtext": "",
"components.TvDetails.request": "リクエストする",
"components.TvDetails.requestmore": "",
"components.TvDetails.similar": "",
"components.TvDetails.similarsubtext": "",
"components.TvDetails.status": "状態",
"components.TvDetails.unavailable": "",
"components.TvDetails.userrating": "ユーザー評価",
"components.UserEdit.admin": "",
"components.UserEdit.adminDescription": "",
"components.UserEdit.autoapprove": "",
"components.UserEdit.autoapproveDescription": "",
"components.UserEdit.avatar": "",
"components.UserEdit.edituser": "",
"components.UserEdit.email": "",
"components.UserEdit.managerequests": "",
"components.UserEdit.managerequestsDescription": "",
"components.UserEdit.permissions": "",
"components.UserEdit.request": "リクエストする",
"components.UserEdit.requestDescription": "",
"components.UserEdit.save": "",
"components.UserEdit.saving": "",
"components.UserEdit.settings": "",
"components.UserEdit.settingsDescription": "",
"components.UserEdit.userfail": "",
"components.UserEdit.username": "",
"components.UserEdit.users": "",
"components.UserEdit.usersDescription": "",
"components.UserEdit.usersaved": "",
"components.UserEdit.vote": "",
"components.UserEdit.voteDescription": "",
"components.UserList.admin": "",
"components.UserList.created": "",
"components.UserList.delete": "",
"components.UserList.edit": "",
"components.UserList.lastupdated": "",
"components.UserList.plexuser": "",
"components.UserList.role": "",
"components.UserList.totalrequests": "",
"components.UserList.user": "",
"components.UserList.userlist": "",
"components.UserList.username": "",
"components.UserList.usertype": "",
"i18n.approve": "",
"i18n.approved": "",
"i18n.available": "",
"i18n.cancel": "",
"i18n.decline": "",
"i18n.declined": "",
"i18n.delete": "",
"i18n.movies": "",
"i18n.partiallyavailable": "",
"i18n.pending": "リクエスト中",
"i18n.processing": "",
"i18n.tvshows": "",
"i18n.unavailable": "",
"pages.internalServerError": "",
"pages.oops": "ああ",
"pages.pageNotFound": "",
"pages.returnHome": "ホームへ戻る",
"pages.serviceUnavailable": "",
"pages.somethingWentWrong": ""
}

@ -100,13 +100,11 @@ CoreApp.getInitialProps = async (initialProps) => {
if (ctx.res) {
// Check if app is initialized and redirect if necessary
let initialized = true;
const response = await axios.get<{ initialized: boolean }>(
`http://localhost:${process.env.PORT || 3000}/api/v1/settings/public`
);
initialized = response.data.initialized;
const initialized = response.data.initialized;
if (!initialized) {
if (!router.pathname.match(/(setup|login\/plex)/)) {
@ -145,7 +143,7 @@ CoreApp.getInitialProps = async (initialProps) => {
const cookies = parseCookies(ctx);
if (!!cookies.locale) {
if (cookies.locale) {
locale = cookies.locale;
}
}

@ -65,7 +65,7 @@ Error.getInitialProps = async ({ res, err }): Promise<ErrorProps> => {
// Apologies for how gross ternary is but this is just temporary. Honestly,
// blame the nextjs docs
let statusCode: Undefinable<number>;
if (!!res) {
if (res) {
statusCode = res.statusCode;
} else {
statusCode = err ? err.statusCode : undefined;

@ -0,0 +1,9 @@
import React from 'react';
import type { NextPage } from 'next';
import RequestList from '../../components/RequestList';
const RequestsPage: NextPage = () => {
return <RequestList />;
};
export default RequestsPage;

@ -6,7 +6,7 @@ import useRouteGuard from '../../hooks/useRouteGuard';
import { Permission } from '../../hooks/useUser';
const SettingsPage: NextPage = () => {
useRouteGuard(Permission.MANAGE_USERS);
useRouteGuard(Permission.MANAGE_SETTINGS);
return (
<SettingsLayout>
<SettingsMain />

@ -6,7 +6,7 @@ import { Permission } from '../../hooks/useUser';
import useRouteGuard from '../../hooks/useRouteGuard';
const SettingsMainPage: NextPage = () => {
useRouteGuard(Permission.MANAGE_USERS);
useRouteGuard(Permission.MANAGE_SETTINGS);
return (
<SettingsLayout>
<SettingsJobs />

@ -6,7 +6,7 @@ import { Permission } from '../../hooks/useUser';
import useRouteGuard from '../../hooks/useRouteGuard';
const SettingsMainPage: NextPage = () => {
useRouteGuard(Permission.MANAGE_USERS);
useRouteGuard(Permission.MANAGE_SETTINGS);
return (
<SettingsLayout>
<SettingsMain />

@ -6,7 +6,7 @@ import { Permission } from '../../hooks/useUser';
import useRouteGuard from '../../hooks/useRouteGuard';
const PlexSettingsPage: NextPage = () => {
useRouteGuard(Permission.MANAGE_USERS);
useRouteGuard(Permission.MANAGE_SETTINGS);
return (
<SettingsLayout>
<SettingsPlex />

@ -6,7 +6,7 @@ import { Permission } from '../../hooks/useUser';
import useRouteGuard from '../../hooks/useRouteGuard';
const ServicesSettingsPage: NextPage = () => {
useRouteGuard(Permission.MANAGE_USERS);
useRouteGuard(Permission.MANAGE_SETTINGS);
return (
<SettingsLayout>
<SettingsServices />

@ -57,19 +57,15 @@ class PlexOAuth {
'You must initialize the plex headers clientside to login'
);
}
try {
const response = await axios.post(
'https://plex.tv/api/v2/pins?strong=true',
undefined,
{ headers: this.plexHeaders }
);
const response = await axios.post(
'https://plex.tv/api/v2/pins?strong=true',
undefined,
{ headers: this.plexHeaders }
);
this.pin = { id: response.data.id, code: response.data.code };
this.pin = { id: response.data.id, code: response.data.code };
return this.pin;
} catch (e) {
throw e;
}
return this.pin;
}
public preparePopup(): void {
@ -77,42 +73,38 @@ class PlexOAuth {
}
public async login(): Promise<string> {
try {
this.initializeHeaders();
await this.getPin();
this.initializeHeaders();
await this.getPin();
if (!this.plexHeaders || !this.pin) {
throw new Error('Unable to call login if class is not initialized.');
}
if (!this.plexHeaders || !this.pin) {
throw new Error('Unable to call login if class is not initialized.');
}
const params = {
clientID: this.plexHeaders['X-Plex-Client-Identifier'],
'context[device][product]': this.plexHeaders['X-Plex-Product'],
'context[device][version]': this.plexHeaders['X-Plex-Version'],
'context[device][platform]': this.plexHeaders['X-Plex-Platform'],
'context[device][platformVersion]': this.plexHeaders[
'X-Plex-Platform-Version'
],
'context[device][device]': this.plexHeaders['X-Plex-Device'],
'context[device][deviceName]': this.plexHeaders['X-Plex-Device-Name'],
'context[device][model]': this.plexHeaders['X-Plex-Model'],
'context[device][screenResolution]': this.plexHeaders[
'X-Plex-Device-Screen-Resolution'
],
'context[device][layout]': 'desktop',
code: this.pin.code,
};
if (this.popup) {
this.popup.location.href = `https://app.plex.tv/auth/#!?${this.encodeData(
params
)}`;
}
const params = {
clientID: this.plexHeaders['X-Plex-Client-Identifier'],
'context[device][product]': this.plexHeaders['X-Plex-Product'],
'context[device][version]': this.plexHeaders['X-Plex-Version'],
'context[device][platform]': this.plexHeaders['X-Plex-Platform'],
'context[device][platformVersion]': this.plexHeaders[
'X-Plex-Platform-Version'
],
'context[device][device]': this.plexHeaders['X-Plex-Device'],
'context[device][deviceName]': this.plexHeaders['X-Plex-Device-Name'],
'context[device][model]': this.plexHeaders['X-Plex-Model'],
'context[device][screenResolution]': this.plexHeaders[
'X-Plex-Device-Screen-Resolution'
],
'context[device][layout]': 'desktop',
code: this.pin.code,
};
return this.pinPoll();
} catch (e) {
throw e;
if (this.popup) {
this.popup.location.href = `https://app.plex.tv/auth/#!?${this.encodeData(
params
)}`;
}
return this.pinPoll();
}
private async pinPoll(): Promise<string> {
@ -131,9 +123,9 @@ class PlexOAuth {
);
if (response.data?.authToken) {
this.authToken = response.data.authToken;
this.authToken = response.data.authToken as string;
this.closePopup();
resolve(response.data.authToken);
resolve(this.authToken);
} else if (!response.data?.authToken && !this.popup?.closed) {
setTimeout(executePoll, 1000, resolve, reject);
} else {

@ -1,4 +1,5 @@
module.exports = {
ignoreFiles: ['**/*.js'],
rules: {
'at-rule-no-unknown': [
true,

@ -62,10 +62,10 @@
call-me-maybe "^1.0.1"
js-yaml "^3.13.1"
"@babel/cli@^7.11.6":
version "7.11.6"
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.11.6.tgz#1fcbe61c2a6900c3539c06ee58901141f3558482"
integrity sha512-+w7BZCvkewSmaRM6H4L2QM3RL90teqEIHDIFXAmrW33+0jhlymnDAEdqVeCZATvxhQuio1ifoGVlJJbIiH9Ffg==
"@babel/cli@^7.12.8":
version "7.12.8"
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.12.8.tgz#3b24ed2fd5da353ee6f19e8935ff8c93b5fe8430"
integrity sha512-/6nQj11oaGhLmZiuRUfxsujiPDc9BBReemiXgIbxc+M5W+MIiFKYwvNDJvBfnGKNsJTKbUfEheKc9cwoPHAVQA==
dependencies:
commander "^4.0.1"
convert-source-map "^1.1.0"
@ -76,7 +76,8 @@
slash "^2.0.0"
source-map "^0.5.0"
optionalDependencies:
chokidar "^2.1.8"
"@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents"
chokidar "^3.4.0"
"@babel/code-frame@7.10.4", "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4":
version "7.10.4"
@ -1310,10 +1311,10 @@
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
"@eslint/eslintrc@^0.1.3":
version "0.1.3"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.1.3.tgz#7d1a2b2358552cc04834c0979bd4275362e37085"
integrity sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==
"@eslint/eslintrc@^0.2.2":
version "0.2.2"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.2.2.tgz#d01fc791e2fc33e88a29d6f3dc7e93d0cd784b76"
integrity sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==
dependencies:
ajv "^6.12.4"
debug "^4.1.1"
@ -1326,6 +1327,13 @@
minimatch "^3.0.4"
strip-json-comments "^3.1.1"
"@formatjs/ecma402-abstract@1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.5.0.tgz#759c8f11ff45e96f8fb58741e7fbdb41096d5ddd"
integrity sha512-wXv36yo+mfWllweN0Fq7sUs7PUiNopn7I0JpLTe3hGu6ZMR4CV7LqK1llhB18pndwpKoafQKb1et2DCJAOW20Q==
dependencies:
tslib "^2.0.1"
"@formatjs/ecma402-abstract@^1.2.1", "@formatjs/ecma402-abstract@^1.2.4":
version "1.2.4"
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.2.4.tgz#0f11e0309bc885d53ddc823e36d04d520fda7674"
@ -1378,6 +1386,15 @@
intl-messageformat-parser "^6.0.9"
tslib "^2.0.1"
"@formatjs/ts-transformer@2.12.10":
version "2.12.10"
resolved "https://registry.yarnpkg.com/@formatjs/ts-transformer/-/ts-transformer-2.12.10.tgz#4f8758ea89e2536239b573da98f99454a4952ebf"
integrity sha512-H8mtPQcyXxLo3GJGkNVj3ZlmebeqxQfVTIvGsdpE1oXKZ/SxKqvC7ZeHlbZUyXUEiRwdJ4Hfsgw1QzsmTJnicw==
dependencies:
intl-messageformat-parser "6.0.18"
tslib "^2.0.1"
typescript "^4.0"
"@formatjs/ts-transformer@^2.10.0", "@formatjs/ts-transformer@^2.6.0":
version "2.10.0"
resolved "https://registry.yarnpkg.com/@formatjs/ts-transformer/-/ts-transformer-2.10.0.tgz#06f292b6cbcea661e2cecf7b8945ac59f21b7c93"
@ -1477,6 +1494,23 @@
resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-10.0.3.tgz#276bec60eae18768f96baf8a52f668f657f50ab4"
integrity sha512-XtzzPX2R4+MIyu1waEQUo2tiNwWVEkmObA6pboRCDTPOs4Ri8ckaIE08lN5A5opyF6GVN+IEq/J8KQrgsePsZQ==
"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents":
version "2.1.8-no-fsevents"
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.tgz#da7c3996b8e6e19ebd14d82eaced2313e7769f9b"
integrity sha512-+nb9vWloHNNMFHjGofEam3wopE3m1yuambrrd/fnPc+lFOMB9ROTqQlche9ByFWNkdNqfSgR/kkQtQ8DzEWt2w==
dependencies:
anymatch "^2.0.0"
async-each "^1.0.1"
braces "^2.3.2"
glob-parent "^3.1.0"
inherits "^2.0.3"
is-binary-path "^1.0.0"
is-glob "^4.0.0"
normalize-path "^3.0.0"
path-is-absolute "^1.0.0"
readdirp "^2.2.1"
upath "^1.1.1"
"@nodelib/fs.scandir@2.1.3":
version "2.1.3"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b"
@ -1927,11 +1961,6 @@
resolved "https://registry.yarnpkg.com/@types/emoji-regex/-/emoji-regex-8.0.0.tgz#df215c9ff818e071087fb8e7e6e74c4cb42a1303"
integrity sha512-iacbaYN9IWWrGWTwlYLVOeUtN/e4cjN9Uh6v7Yo1Qa/vJzeSQeh10L/erBBSl53BTmbnQ07vsWp8mmNHGI4WbQ==
"@types/eslint-visitor-keys@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==
"@types/eslint@^7.2.0":
version "7.2.2"
resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.2.tgz#c88426b896efeb0b2732a92431ce8aa7ec0dee61"
@ -2002,10 +2031,10 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd"
integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==
"@types/lodash@^4.14.161":
version "4.14.161"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.161.tgz#a21ca0777dabc6e4f44f3d07f37b765f54188b18"
integrity sha512-EP6O3Jkr7bXvZZSZYlsgt5DIjiGr0dXP1/jVEwVLTFgg0d+3lWVQkRavYVQszV7dYUwvg0B8R0MBDpcmXg7XIA==
"@types/lodash@^4.14.165":
version "4.14.165"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.165.tgz#74d55d947452e2de0742bad65270433b63a8c30f"
integrity sha512-tjSSOTHhI5mCHTy/OOXYIhi2Wt1qcbHmuXD1Ha7q70CgI/I71afO4XtLb/cVexki1oVYchpul/TOuu3Arcdxrg==
"@types/mime@*":
version "2.0.3"
@ -2029,12 +2058,12 @@
dependencies:
"@types/node" "*"
"@types/node@*", "@types/node@^14.10.0":
"@types/node@*":
version "14.10.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.10.0.tgz#15815dff82c8dc30827f6b1286f865902945095a"
integrity sha512-SOIyrdADB4cq6eY1F+9iU48iIomFAPltu11LCvA9PKcyEwHadjCFzNVPotAR+oEJA0bCP4Xvvgy+vwu1ZjVh8g==
"@types/node@>= 8":
"@types/node@>= 8", "@types/node@^14.14.10":
version "14.14.10"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.10.tgz#5958a82e41863cfc71f2307b3748e3491ba03785"
integrity sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ==
@ -2076,10 +2105,10 @@
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c"
integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==
"@types/react-dom@^16.9.8":
version "16.9.8"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.8.tgz#fe4c1e11dfc67155733dfa6aa65108b4971cb423"
integrity sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA==
"@types/react-dom@^17.0.0":
version "17.0.0"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.0.tgz#b3b691eb956c4b3401777ee67b900cb28415d95a"
integrity sha512-lUqY7OlkF/RbNtD5nIq7ot8NquXrdFrjSOR6+w9a9RFQevGi1oZO1dcJbXMeONAPKtZ2UrZOEJ5UOCVsxbLk/g==
dependencies:
"@types/react" "*"
@ -2097,7 +2126,7 @@
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@^16.9.49":
"@types/react@*":
version "16.9.49"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.49.tgz#09db021cf8089aba0cdb12a49f8021a69cce4872"
integrity sha512-DtLFjSj0OYAdVLBbyjhuV9CdGVHCkHn2R+xr3XkBvK2rS1Y1tkc14XSGjYgm5Fjjr90AxH9tiSzc1pCFMGO06g==
@ -2105,6 +2134,14 @@
"@types/prop-types" "*"
csstype "^3.0.2"
"@types/react@^17.0.0":
version "17.0.0"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.0.tgz#5af3eb7fad2807092f0046a1302b7823e27919b8"
integrity sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw==
dependencies:
"@types/prop-types" "*"
csstype "^3.0.2"
"@types/retry@^0.12.0":
version "0.12.0"
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
@ -2138,10 +2175,10 @@
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f"
integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==
"@types/xml2js@^0.4.5":
version "0.4.5"
resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.4.5.tgz#d21759b056f282d9c7066f15bbf5c19b908f22fa"
integrity sha512-yohU3zMn0fkhlape1nxXG2bLEGZRc1FeqF80RoHaYXJN7uibaauXfhzhOJr1Xh36sn+/tx21QAOf07b/xYVk1w==
"@types/xml2js@^0.4.7":
version "0.4.7"
resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.4.7.tgz#cd5b6c67bbec741ac625718a76e6cb99bc34365e"
integrity sha512-f5VOKSMEE0O+/L54FHwA/a7vcx9mHeSDM71844yHCOhh8Cin2xQa0UFw0b7Vc5hoZ3Ih6ZHaDobjfLih4tWPNw==
dependencies:
"@types/node" "*"
@ -2150,99 +2187,87 @@
resolved "https://registry.yarnpkg.com/@types/yamljs/-/yamljs-0.2.31.tgz#b1a620b115c96db7b3bfdf0cf54aee0c57139245"
integrity sha512-QcJ5ZczaXAqbVD3o8mw/mEBhRvO5UAdTtbvgwL/OgoWubvNBh6/MxLBAigtcgIFaq3shon9m3POIxQaLQt4fxQ==
"@types/yup@^0.29.9":
version "0.29.9"
resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.29.9.tgz#e2015187ae5739fd3b791b3b7ab9094f2aa5a474"
integrity sha512-ZtjjlrHuHTYctHDz3c8XgInjj0v+Hahe32N/4cDa2banibf9w6aAgxwx0jZtBjKKzmGIU4NXhofEsBW1BbqrNg==
"@types/yup@^0.29.10":
version "0.29.10"
resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.29.10.tgz#1bfa4c4a47a6f57fcc8510948757b9e47c0d6ca3"
integrity sha512-kRKRZaWkxxnOK7H5C4oWqhCw9ID1QF3cBZ2oAPoXYsjIncwgpDGigWtXGjZ91t+hsc3cvPdBci9YoJo1A96CYg==
"@typescript-eslint/eslint-plugin@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.0.0.tgz#99349a501447fed91de18346705c0c65cf603bee"
integrity sha512-5e6q1TR7gS2P+8W2xndCu7gBh3BzmYEo70OyIdsmCmknHha/yNbz2vdevl+tP1uoaMOcrzg4gyrAijuV3DDBHA==
"@typescript-eslint/eslint-plugin@^4.9.1":
version "4.9.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.9.1.tgz#66758cbe129b965fe9c63b04b405d0cf5280868b"
integrity sha512-QRLDSvIPeI1pz5tVuurD+cStNR4sle4avtHhxA+2uyixWGFjKzJ+EaFVRW6dA/jOgjV5DTAjOxboQkRDE8cRlQ==
dependencies:
"@typescript-eslint/experimental-utils" "4.0.0"
"@typescript-eslint/scope-manager" "4.0.0"
"@typescript-eslint/experimental-utils" "4.9.1"
"@typescript-eslint/scope-manager" "4.9.1"
debug "^4.1.1"
functional-red-black-tree "^1.0.1"
regexpp "^3.0.0"
semver "^7.3.2"
tsutils "^3.17.1"
"@typescript-eslint/experimental-utils@3.10.1":
version "3.10.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz#e179ffc81a80ebcae2ea04e0332f8b251345a686"
integrity sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==
"@typescript-eslint/experimental-utils@4.9.1":
version "4.9.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.9.1.tgz#86633e8395191d65786a808dc3df030a55267ae2"
integrity sha512-c3k/xJqk0exLFs+cWSJxIjqLYwdHCuLWhnpnikmPQD2+NGAx9KjLYlBDcSI81EArh9FDYSL6dslAUSwILeWOxg==
dependencies:
"@types/json-schema" "^7.0.3"
"@typescript-eslint/types" "3.10.1"
"@typescript-eslint/typescript-estree" "3.10.1"
"@typescript-eslint/scope-manager" "4.9.1"
"@typescript-eslint/types" "4.9.1"
"@typescript-eslint/typescript-estree" "4.9.1"
eslint-scope "^5.0.0"
eslint-utils "^2.0.0"
"@typescript-eslint/experimental-utils@4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.0.0.tgz#fbec21a3b5ab59127edb6ce2e139ed378cc50eb5"
integrity sha512-hbX6zR+a/vcpFVNJYN/Nbd7gmaMosDTxHEKcvmhWeWcq/0UDifrqmCfkkodbAKL46Fn4ekSBMTyq2zlNDzcQxw==
"@typescript-eslint/parser@^4.9.1":
version "4.9.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.9.1.tgz#2d74c4db5dd5117379a9659081a4d1ec02629055"
integrity sha512-Gv2VpqiomvQ2v4UL+dXlQcZ8zCX4eTkoIW+1aGVWT6yTO+6jbxsw7yQl2z2pPl/4B9qa5JXeIbhJpONKjXIy3g==
dependencies:
"@types/json-schema" "^7.0.3"
"@typescript-eslint/scope-manager" "4.0.0"
"@typescript-eslint/types" "4.0.0"
"@typescript-eslint/typescript-estree" "4.0.0"
eslint-scope "^5.0.0"
eslint-utils "^2.0.0"
"@typescript-eslint/scope-manager" "4.9.1"
"@typescript-eslint/types" "4.9.1"
"@typescript-eslint/typescript-estree" "4.9.1"
debug "^4.1.1"
"@typescript-eslint/parser@^3.10.1":
version "3.10.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.10.1.tgz#1883858e83e8b442627e1ac6f408925211155467"
integrity sha512-Ug1RcWcrJP02hmtaXVS3axPPTTPnZjupqhgj+NnZ6BCkwSImWk/283347+x9wN+lqOdK9Eo3vsyiyDHgsmiEJw==
"@typescript-eslint/scope-manager@4.9.1":
version "4.9.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.9.1.tgz#cc2fde310b3f3deafe8436a924e784eaab265103"
integrity sha512-sa4L9yUfD/1sg9Kl8OxPxvpUcqxKXRjBeZxBuZSSV1v13hjfEJkn84n0An2hN8oLQ1PmEl2uA6FkI07idXeFgQ==
dependencies:
"@types/eslint-visitor-keys" "^1.0.0"
"@typescript-eslint/experimental-utils" "3.10.1"
"@typescript-eslint/types" "3.10.1"
"@typescript-eslint/typescript-estree" "3.10.1"
eslint-visitor-keys "^1.1.0"
"@typescript-eslint/scope-manager@4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.0.0.tgz#8c9e3b3b8cdf5a1fbe671d9fad73ff67bc027ea8"
integrity sha512-9gcWUPoWo7gk/+ZQPg7L1ySRmR5HLIy3Vu6/LfhQbuzIkGm6v2CGIjpVRISoDLFRovNRDImd4aP/sa8O4yIEBg==
dependencies:
"@typescript-eslint/types" "4.0.0"
"@typescript-eslint/visitor-keys" "4.0.0"
"@typescript-eslint/types" "4.9.1"
"@typescript-eslint/visitor-keys" "4.9.1"
"@typescript-eslint/types@3.10.1":
version "3.10.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.10.1.tgz#1d7463fa7c32d8a23ab508a803ca2fe26e758727"
integrity sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==
"@typescript-eslint/types@4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.0.0.tgz#ec1f9fc06b8558a1d5afa6e337182d08beece7f5"
integrity sha512-bK+c2VLzznX2fUWLK6pFDv3cXGTp7nHIuBMq1B9klA+QCsqLHOOqe5TQReAQDl7DN2RfH+neweo0oC5hYlG7Rg==
"@typescript-eslint/types@4.9.1":
version "4.9.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.9.1.tgz#a1a7dd80e4e5ac2c593bc458d75dd1edaf77faa2"
integrity sha512-fjkT+tXR13ks6Le7JiEdagnwEFc49IkOyys7ueWQ4O8k4quKPwPJudrwlVOJCUQhXo45PrfIvIarcrEjFTNwUA==
"@typescript-eslint/typescript-estree@3.10.1", "@typescript-eslint/typescript-estree@^3.6.0":
version "3.10.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz#fd0061cc38add4fad45136d654408569f365b853"
integrity sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w==
"@typescript-eslint/typescript-estree@4.9.1":
version "4.9.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.9.1.tgz#6e5b86ff5a5f66809e1f347469fadeec69ac50bf"
integrity sha512-bzP8vqwX6Vgmvs81bPtCkLtM/Skh36NE6unu6tsDeU/ZFoYthlTXbBmpIrvosgiDKlWTfb2ZpPELHH89aQjeQw==
dependencies:
"@typescript-eslint/types" "3.10.1"
"@typescript-eslint/visitor-keys" "3.10.1"
"@typescript-eslint/types" "4.9.1"
"@typescript-eslint/visitor-keys" "4.9.1"
debug "^4.1.1"
glob "^7.1.6"
globby "^11.0.1"
is-glob "^4.0.1"
lodash "^4.17.15"
semver "^7.3.2"
tsutils "^3.17.1"
"@typescript-eslint/typescript-estree@4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.0.0.tgz#2244c63de2f2190bc5718eb0fb3fd2c437d42097"
integrity sha512-ewFMPi2pMLDNIXGMPdf8r7El2oPSZw9PEYB0j+WcpKd7AX2ARmajGa7RUHTukllWX2bj4vWX6JLE1Oih2BMokA==
"@typescript-eslint/typescript-estree@^3.6.0":
version "3.10.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz#fd0061cc38add4fad45136d654408569f365b853"
integrity sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w==
dependencies:
"@typescript-eslint/types" "4.0.0"
"@typescript-eslint/visitor-keys" "4.0.0"
"@typescript-eslint/types" "3.10.1"
"@typescript-eslint/visitor-keys" "3.10.1"
debug "^4.1.1"
globby "^11.0.1"
glob "^7.1.6"
is-glob "^4.0.1"
lodash "^4.17.15"
semver "^7.3.2"
@ -2255,12 +2280,12 @@
dependencies:
eslint-visitor-keys "^1.1.0"
"@typescript-eslint/visitor-keys@4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.0.0.tgz#e2bbb69d98076d6a3f06abcb2048225a74362c33"
integrity sha512-sTouJbv6rjVJeTE4lpSBVYXq/u5K3gbB6LKt7ccFEZPTZB/VeQ0ssUz9q5Hx++sCqBbdF8PzrrgvEnicXAR6NQ==
"@typescript-eslint/visitor-keys@4.9.1":
version "4.9.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.9.1.tgz#d76374a58c4ead9e92b454d186fea63487b25ae1"
integrity sha512-9gspzc6UqLQHd7lXQS7oWs+hrYggspv/rk6zzEMhCbYwPE/sF7oxo7GAjkS35Tdlt7wguIG+ViWCPtVZHz/ybQ==
dependencies:
"@typescript-eslint/types" "4.0.0"
"@typescript-eslint/types" "4.9.1"
eslint-visitor-keys "^2.0.0"
"@webassemblyjs/ast@1.9.0":
@ -2451,6 +2476,11 @@ acorn-jsx@^5.2.0:
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe"
integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==
acorn-jsx@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b"
integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==
acorn-node@^1.6.1:
version "1.8.2"
resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8"
@ -2913,10 +2943,10 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428"
integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==
axe-core@^3.5.4:
version "3.5.5"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.5.5.tgz#84315073b53fa3c0c51676c588d59da09a192227"
integrity sha512-5P0QZ6J5xGikH780pghEdbEKijCTrruK9KxtPZCFWUpef0f6GipO+xEZ5GKCb020mmqgbiNO6TcA55CriL784Q==
axe-core@^4.0.2:
version "4.1.1"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.1.1.tgz#70a7855888e287f7add66002211a423937063eaf"
integrity sha512-5Kgy8Cz6LPC9DJcNb3yjAXTu3XihQgEdnIg50c//zOC/MyLP0Clg+Y8Sh9ZjjnvBrDZU4DgXS9C3T9r4/scGZQ==
axios@^0.20.0:
version "0.20.0"
@ -2925,7 +2955,7 @@ axios@^0.20.0:
dependencies:
follow-redirects "^1.10.0"
axobject-query@^2.1.2:
axobject-query@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==
@ -3582,7 +3612,7 @@ cheerio@^1.0.0-rc.3:
lodash "^4.15.0"
parse5 "^3.0.1"
chokidar@3.4.3:
chokidar@3.4.3, chokidar@^3.4.0:
version "3.4.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b"
integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==
@ -3956,6 +3986,11 @@ commander@^6.0.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-6.1.0.tgz#f8d722b78103141006b66f4c7ba1e97315ba75bc"
integrity sha512-wl7PNrYWd2y5mp1OK/LhTlv8Ff4kQJQRXXAvF+uU/TPNiVJUxZLRYGj/B0y/lPGAVcSbJqH2Za/cvHmrPMC8mA==
commander@^6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.0.tgz#b990bfb8ac030aedc6d11bc04d1488ffef56db75"
integrity sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==
commitizen@^4.0.3, commitizen@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/commitizen/-/commitizen-4.2.1.tgz#3b098b16c6b1a37f0d129018dff6751b20cd3103"
@ -5391,70 +5426,69 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
eslint-config-prettier@^6.11.0:
version "6.11.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz#f6d2238c1290d01c859a8b5c1f7d352a0b0da8b1"
integrity sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA==
dependencies:
get-stdin "^6.0.0"
eslint-config-prettier@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-7.0.0.tgz#c1ae4106f74e6c0357f44adb076771d032ac0e97"
integrity sha512-8Y8lGLVPPZdaNA7JXqnvETVC7IiVRgAP6afQu9gOQRn90YY3otMNh+x7Vr2vMePQntF+5erdSUBqSzCmU/AxaQ==
eslint-plugin-formatjs@^2.7.10:
version "2.7.10"
resolved "https://registry.yarnpkg.com/eslint-plugin-formatjs/-/eslint-plugin-formatjs-2.7.10.tgz#436bfe8283d5108c3e93617c8bea929cbbc8e35b"
integrity sha512-rqhw+AgicCWDD38jluqwLKqAEuVEBI3/XcUu/AWoWrhsyg692KmBdNl0hiKaN9bv+U837q0PYXcdcFwv5TuBeQ==
eslint-plugin-formatjs@^2.9.10:
version "2.9.10"
resolved "https://registry.yarnpkg.com/eslint-plugin-formatjs/-/eslint-plugin-formatjs-2.9.10.tgz#dc5b80792e4166f3b2c4ca927ca47a70c89f27d2"
integrity sha512-MFkJ6ZBs70Zdyeq2JdYn950jSgSROL4x9eWlxU/AzhNvDIiHiU0oXahx02X7wdAl1vzjCC7Ro4VWiGGecQ5cpA==
dependencies:
"@formatjs/ts-transformer" "^2.10.0"
"@formatjs/ts-transformer" "2.12.10"
"@types/emoji-regex" "^8.0.0"
"@types/eslint" "^7.2.0"
"@types/estree" "^0.0.45"
"@typescript-eslint/typescript-estree" "^3.6.0"
emoji-regex "^9.0.0"
intl-messageformat-parser "^6.0.6"
intl-messageformat-parser "6.0.18"
tslib "^2.0.1"
eslint-plugin-jsx-a11y@^6.3.1:
version "6.3.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.3.1.tgz#99ef7e97f567cc6a5b8dd5ab95a94a67058a2660"
integrity sha512-i1S+P+c3HOlBJzMFORRbC58tHa65Kbo8b52/TwCwSKLohwvpfT5rm2GjGWzOHTEuq4xxf2aRlHHTtmExDQOP+g==
eslint-plugin-jsx-a11y@^6.4.1:
version "6.4.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.4.1.tgz#a2d84caa49756942f42f1ffab9002436391718fd"
integrity sha512-0rGPJBbwHoGNPU73/QCLP/vveMlM1b1Z9PponxO87jfr6tuH5ligXbDT6nHSSzBC8ovX2Z+BQu7Bk5D/Xgq9zg==
dependencies:
"@babel/runtime" "^7.10.2"
"@babel/runtime" "^7.11.2"
aria-query "^4.2.2"
array-includes "^3.1.1"
ast-types-flow "^0.0.7"
axe-core "^3.5.4"
axobject-query "^2.1.2"
axe-core "^4.0.2"
axobject-query "^2.2.0"
damerau-levenshtein "^1.0.6"
emoji-regex "^9.0.0"
has "^1.0.3"
jsx-ast-utils "^2.4.1"
jsx-ast-utils "^3.1.0"
language-tags "^1.0.5"
eslint-plugin-prettier@^3.1.4:
version "3.1.4"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz#168ab43154e2ea57db992a2cd097c828171f75c2"
integrity sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg==
eslint-plugin-prettier@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.2.0.tgz#af391b2226fa0e15c96f36c733f6e9035dbd952c"
integrity sha512-kOUSJnFjAUFKwVxuzy6sA5yyMx6+o9ino4gCdShzBNx4eyFRudWRYKCFolKjoM40PEiuU6Cn7wBLfq3WsGg7qg==
dependencies:
prettier-linter-helpers "^1.0.0"
eslint-plugin-react-hooks@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.1.2.tgz#2eb53731d11c95826ef7a7272303eabb5c9a271e"
integrity sha512-ykUeqkGyUGgwTtk78C0o8UG2fzwmgJ0qxBGPp2WqRKsTwcLuVf01kTDRAtOsd4u6whX2XOC8749n2vPydP82fg==
eslint-plugin-react-hooks@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz#8c229c268d468956334c943bb45fc860280f5556"
integrity sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ==
eslint-plugin-react@^7.20.6:
version "7.20.6"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.20.6.tgz#4d7845311a93c463493ccfa0a19c9c5d0fd69f60"
integrity sha512-kidMTE5HAEBSLu23CUDvj8dc3LdBU0ri1scwHBZjI41oDv4tjsWZKU7MQccFzH1QYPYhsnTF2ovh7JlcIcmxgg==
eslint-plugin-react@^7.21.5:
version "7.21.5"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.21.5.tgz#50b21a412b9574bfe05b21db176e8b7b3b15bff3"
integrity sha512-8MaEggC2et0wSF6bUeywF7qQ46ER81irOdWS4QWxnnlAEsnzeBevk1sWh7fhpCghPpXb+8Ks7hvaft6L/xsR6g==
dependencies:
array-includes "^3.1.1"
array.prototype.flatmap "^1.2.3"
doctrine "^2.1.0"
has "^1.0.3"
jsx-ast-utils "^2.4.1"
jsx-ast-utils "^2.4.1 || ^3.0.0"
object.entries "^1.1.2"
object.fromentries "^2.0.2"
object.values "^1.1.1"
prop-types "^15.7.2"
resolve "^1.17.0"
resolve "^1.18.1"
string.prototype.matchall "^4.0.2"
eslint-scope@^4.0.3:
@ -5490,13 +5524,13 @@ eslint-visitor-keys@^2.0.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8"
integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
eslint@^7.10.0:
version "7.10.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.10.0.tgz#494edb3e4750fb791133ca379e786a8f648c72b9"
integrity sha512-BDVffmqWl7JJXqCjAK6lWtcQThZB/aP1HXSH1JKwGwv0LQEdvpR7qzNrUT487RM39B5goWuboFad5ovMBmD8yA==
eslint@^7.15.0:
version "7.15.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.15.0.tgz#eb155fb8ed0865fcf5d903f76be2e5b6cd7e0bc7"
integrity sha512-Vr64xFDT8w30wFll643e7cGrIkPEU50yIiI36OdSIDoSGguIeaLzBo0vpGvzo9RECUqq7htURfwEtKqwytkqzA==
dependencies:
"@babel/code-frame" "^7.0.0"
"@eslint/eslintrc" "^0.1.3"
"@eslint/eslintrc" "^0.2.2"
ajv "^6.10.0"
chalk "^4.0.0"
cross-spawn "^7.0.2"
@ -5505,11 +5539,11 @@ eslint@^7.10.0:
enquirer "^2.3.5"
eslint-scope "^5.1.1"
eslint-utils "^2.1.0"
eslint-visitor-keys "^1.3.0"
espree "^7.3.0"
eslint-visitor-keys "^2.0.0"
espree "^7.3.1"
esquery "^1.2.0"
esutils "^2.0.2"
file-entry-cache "^5.0.1"
file-entry-cache "^6.0.0"
functional-red-black-tree "^1.0.1"
glob-parent "^5.0.0"
globals "^12.1.0"
@ -5542,6 +5576,15 @@ espree@^7.3.0:
acorn-jsx "^5.2.0"
eslint-visitor-keys "^1.3.0"
espree@^7.3.1:
version "7.3.1"
resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6"
integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==
dependencies:
acorn "^7.4.0"
acorn-jsx "^5.3.1"
eslint-visitor-keys "^1.3.0"
esprima@^4.0.0, esprima@~4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
@ -5632,7 +5675,7 @@ execa@^0.7.0:
signal-exit "^3.0.0"
strip-eof "^1.0.0"
execa@^4.0.0:
execa@^4.0.0, execa@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a"
integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==
@ -5647,21 +5690,6 @@ execa@^4.0.0:
signal-exit "^3.0.2"
strip-final-newline "^2.0.0"
execa@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.3.tgz#0a34dabbad6d66100bd6f2c576c8669403f317f2"
integrity sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==
dependencies:
cross-spawn "^7.0.0"
get-stream "^5.0.0"
human-signals "^1.1.1"
is-stream "^2.0.0"
merge-stream "^2.0.0"
npm-run-path "^4.0.0"
onetime "^5.1.0"
signal-exit "^3.0.2"
strip-final-newline "^2.0.0"
expand-brackets@^2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
@ -5915,12 +5943,12 @@ figures@^3.0.0, figures@^3.2.0:
dependencies:
escape-string-regexp "^1.0.5"
file-entry-cache@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c"
integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==
file-entry-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.0.tgz#7921a89c391c6d93efec2169ac6bf300c527ea0a"
integrity sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA==
dependencies:
flat-cache "^2.0.1"
flat-cache "^3.0.4"
file-uri-to-path@1.0.0:
version "1.0.0"
@ -6040,24 +6068,23 @@ findup-sync@^3.0.0:
micromatch "^3.0.4"
resolve-dir "^1.0.1"
flat-cache@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0"
integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==
flat-cache@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==
dependencies:
flatted "^2.0.0"
rimraf "2.6.3"
write "1.0.3"
flatted "^3.1.0"
rimraf "^3.0.2"
flat@^5.0.0:
version "5.0.2"
resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
flatted@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"
integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==
flatted@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.0.tgz#a5d06b4a8b01e3a63771daa5cb7a1903e2e57067"
integrity sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==
flatten@^1.0.2:
version "1.0.3"
@ -6335,11 +6362,6 @@ get-stdin@8.0.0:
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53"
integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==
get-stdin@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==
get-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
@ -6846,10 +6868,10 @@ humanize-ms@^1.2.1:
dependencies:
ms "^2.0.0"
husky@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/husky/-/husky-4.3.0.tgz#0b2ec1d66424e9219d359e26a51c58ec5278f0de"
integrity sha512-tTMeLCLqSBqnflBZnlVDhpaIMucSGaYyX6855jM4AguGeWCeSzNdb1mfyWduTZ3pe3SJVvVWGL0jO1iKZVPfTA==
husky@^4.3.5:
version "4.3.5"
resolved "https://registry.yarnpkg.com/husky/-/husky-4.3.5.tgz#ab8d2a0eb6b62fef2853ee3d442c927d89290902"
integrity sha512-E5S/1HMoDDaqsH8kDF5zeKEQbYqe3wL9zJDyqyYqc8I4vHBtAoxkDBGXox0lZ9RI+k5GyB728vZdmnM4bYap+g==
dependencies:
chalk "^4.0.0"
ci-info "^2.0.0"
@ -7061,6 +7083,14 @@ internal-slot@^1.0.2:
has "^1.0.3"
side-channel "^1.0.2"
intl-messageformat-parser@6.0.18:
version "6.0.18"
resolved "https://registry.yarnpkg.com/intl-messageformat-parser/-/intl-messageformat-parser-6.0.18.tgz#bf2855b82b0749e1f34e452f0a15d08d3277c8c7"
integrity sha512-vLjACEunfi5uSUCWFLOR4PXQ9DGLpED3tM7o9zsYsOvjl0VIheoxyG0WZXnsnhn+S+Zu158M6CkuHXeNZfKRRg==
dependencies:
"@formatjs/ecma402-abstract" "1.5.0"
tslib "^2.0.1"
intl-messageformat-parser@^5.3.7:
version "5.5.1"
resolved "https://registry.yarnpkg.com/intl-messageformat-parser/-/intl-messageformat-parser-5.5.1.tgz#f09a692755813e6220081e3374df3fb1698bd0c6"
@ -7675,13 +7705,13 @@ jstransformer@1.0.0:
is-promise "^2.0.0"
promise "^7.0.1"
jsx-ast-utils@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz#1114a4c1209481db06c690c2b4f488cc665f657e"
integrity sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w==
"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.1.0.tgz#642f1d7b88aa6d7eb9d8f2210e166478444fa891"
integrity sha512-d4/UOjg+mxAWxCiF0c5UTSwyqbchkbqCvK87aBovhnh8GtysTjWmgC63tY0cJx/HzGgm9qnA147jVBdpOiQ2RA==
dependencies:
array-includes "^3.1.1"
object.assign "^4.1.0"
object.assign "^4.1.1"
juice@^7.0.0:
version "7.0.0"
@ -7994,20 +8024,20 @@ linkify-it@3.0.2:
dependencies:
uc.micro "^1.0.1"
lint-staged@^10.4.0:
version "10.4.0"
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.4.0.tgz#d18628f737328e0bbbf87d183f4020930e9a984e"
integrity sha512-uaiX4U5yERUSiIEQc329vhCTDDwUcSvKdRLsNomkYLRzijk3v8V9GWm2Nz0RMVB87VcuzLvtgy6OsjoH++QHIg==
lint-staged@^10.5.3:
version "10.5.3"
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.5.3.tgz#c682838b3eadd4c864d1022da05daa0912fb1da5"
integrity sha512-TanwFfuqUBLufxCc3RUtFEkFraSPNR3WzWcGF39R3f2J7S9+iF9W0KTVLfSy09lYGmZS5NDCxjNvhGMSJyFCWg==
dependencies:
chalk "^4.1.0"
cli-truncate "^2.1.0"
commander "^6.0.0"
commander "^6.2.0"
cosmiconfig "^7.0.0"
debug "^4.1.1"
debug "^4.2.0"
dedent "^0.7.0"
enquirer "^2.3.6"
execa "^4.0.3"
listr2 "^2.6.0"
execa "^4.1.0"
listr2 "^3.2.2"
log-symbols "^4.0.0"
micromatch "^4.0.2"
normalize-path "^3.0.0"
@ -8015,10 +8045,10 @@ lint-staged@^10.4.0:
string-argv "0.3.1"
stringify-object "^3.3.0"
listr2@^2.6.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/listr2/-/listr2-2.6.2.tgz#4912eb01e1e2dd72ec37f3895a56bf2622d6f36a"
integrity sha512-6x6pKEMs8DSIpA/tixiYY2m/GcbgMplMVmhQAaLFxEtNSKLeWTGjtmU57xvv6QCm2XcqzyNXL/cTSVf4IChCRA==
listr2@^3.2.2:
version "3.2.3"
resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.2.3.tgz#ef9e0d790862f038dde8a9837be552b1adfd1c07"
integrity sha512-vUb80S2dSUi8YxXahO8/I/s29GqnOL8ozgHVLjfWQXa03BNEeS1TpBLjh2ruaqq5ufx46BRGvfymdBSuoXET5w==
dependencies:
chalk "^4.1.0"
cli-truncate "^2.1.0"
@ -8026,7 +8056,7 @@ listr2@^2.6.0:
indent-string "^4.0.0"
log-update "^4.0.0"
p-map "^4.0.0"
rxjs "^6.6.2"
rxjs "^6.6.3"
through "^2.3.8"
load-json-file@^2.0.0:
@ -9221,10 +9251,10 @@ nodemailer@^6.4.16:
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.4.16.tgz#5cb6391b1d79ab7eff32d6f9f48366b5a7117293"
integrity sha512-68K0LgZ6hmZ7PVmwL78gzNdjpj5viqBdFqKrTtr9bZbJYj6BRj5W6WGkxXrEnUl3Co3CBXi3CZBUlpV/foGnOQ==
nodemon@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.4.tgz#55b09319eb488d6394aa9818148c0c2d1c04c416"
integrity sha512-Ltced+hIfTmaS28Zjv1BM552oQ3dbwPqI4+zI0SLgq+wpJhSyqgYude/aZa/3i31VCQWMfXJVxvu86abcam3uQ==
nodemon@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.6.tgz#1abe1937b463aaf62f0d52e2b7eaadf28cc2240d"
integrity sha512-4I3YDSKXg6ltYpcnZeHompqac4E6JeAMpGm8tJnB9Y3T0ehasLa4139dJOcCrB93HHrUMsCrKtoAlXTqT5n4AQ==
dependencies:
chokidar "^3.2.2"
debug "^3.2.6"
@ -9234,8 +9264,8 @@ nodemon@^2.0.4:
semver "^5.7.1"
supports-color "^5.5.0"
touch "^3.1.0"
undefsafe "^2.0.2"
update-notifier "^4.0.0"
undefsafe "^2.0.3"
update-notifier "^4.1.0"
noms@0.0.0:
version "0.0.0"
@ -11160,6 +11190,11 @@ react-fast-compare@^2.0.1:
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
react-intersection-observer@^8.31.0:
version "8.31.0"
resolved "https://registry.yarnpkg.com/react-intersection-observer/-/react-intersection-observer-8.31.0.tgz#0ed21aaf93c4c0475b22b0ccaba6169076d01605"
integrity sha512-XraIC/tkrD9JtrmVA7ypEN1QIpKc52mXBH1u/bz/aicRLo8QQEJQAMUTb8mz4B6dqpPwyzgjrr7Ljv/2ACDtqw==
react-intl@^5.8.5:
version "5.8.5"
resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-5.8.5.tgz#bc5dfab259049830621e129b8bffb1ac33ef4124"
@ -11653,7 +11688,7 @@ resolve@^1.10.0, resolve@^1.12.0, resolve@^1.17.0, resolve@^1.3.2:
dependencies:
path-parse "^1.0.6"
resolve@^1.15.1, resolve@^1.19.0:
resolve@^1.15.1, resolve@^1.18.1, resolve@^1.19.0:
version "1.19.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c"
integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==
@ -11724,13 +11759,6 @@ rimraf@2, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.
dependencies:
glob "^7.1.3"
rimraf@2.6.3:
version "2.6.3"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
dependencies:
glob "^7.1.3"
rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
@ -11763,13 +11791,20 @@ run-queue@^1.0.0, run-queue@^1.0.3:
dependencies:
aproba "^1.1.1"
rxjs@^6.4.0, rxjs@^6.6.2:
rxjs@^6.4.0:
version "6.6.2"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2"
integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==
dependencies:
tslib "^1.9.0"
rxjs@^6.6.3:
version "6.6.3"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552"
integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==
dependencies:
tslib "^1.9.0"
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
@ -13361,7 +13396,7 @@ umask@^1.1.0, umask@~1.1.0:
resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d"
integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0=
undefsafe@^2.0.2:
undefsafe@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.3.tgz#6b166e7094ad46313b2202da7ecc2cd7cc6e7aae"
integrity sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==
@ -13508,10 +13543,10 @@ update-notifier@^2.2.0, update-notifier@^2.3.0, update-notifier@^2.5.0:
semver-diff "^2.0.0"
xdg-basedir "^3.0.0"
update-notifier@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.0.tgz#4866b98c3bc5b5473c020b1250583628f9a328f3"
integrity sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew==
update-notifier@^4.1.0:
version "4.1.3"
resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.3.tgz#be86ee13e8ce48fb50043ff72057b5bd598e1ea3"
integrity sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==
dependencies:
boxen "^4.2.0"
chalk "^3.0.0"
@ -13634,11 +13669,6 @@ uuid@^3.3.2, uuid@^3.3.3:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
uuid@^8.3.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea"
integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==
uuid@^8.3.1:
version "8.3.1"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31"
@ -13946,13 +13976,6 @@ write-json-file@^4.3.0:
sort-keys "^4.0.0"
write-file-atomic "^3.0.0"
write@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3"
integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==
dependencies:
mkdirp "^0.5.1"
xdg-basedir@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"

Loading…
Cancel
Save