Merge branch 'develop'

This commit is contained in:
sct
2020-12-08 08:16:24 +00:00
58 changed files with 1497 additions and 893 deletions

View File

@@ -1,8 +1,8 @@
module.exports = { module.exports = {
root: true, root: true,
parser: '@typescript-eslint/parser', // Specifies the ESLint parser parser: '@typescript-eslint/parser', // Specifies the ESLint parser
extends: [ extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin '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 '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. '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', '@typescript-eslint/explicit-function-return-type': 'off',
'prettier/prettier': ['error', { endOfLine: 'auto' }], 'prettier/prettier': ['error', { endOfLine: 'auto' }],
'formatjs/no-offset': 'error', 'formatjs/no-offset': 'error',
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': ['error'],
}, },
overrides: [ overrides: [
{ {
@@ -52,6 +54,5 @@ module.exports = {
jest: true, jest: true,
es6: true, es6: true,
}, },
reportUnusedDisableDirectives: true, reportUnusedDisableDirectives: true,
}; };

21
LICENSE Normal file
View File

@@ -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.

View File

@@ -2,8 +2,19 @@
<img src="https://i.imgur.com/TMoEG7g.png" alt="Overseerr"> <img src="https://i.imgur.com/TMoEG7g.png" alt="Overseerr">
</p> </p>
<p align="center"> <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"> <img src="https://github.com/sct/overseerr/workflows/Overseerr%20CI/badge.svg" alt="Overseerr CI">
</p> </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**! **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! - 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! - 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 - 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 - 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 - 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! - Mobile friendly design, for when you need to approve requests on the go!
## In Development ## 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 ## 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 ## 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. 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. 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 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!

View File

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

BIN
public/preview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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(); const settings = getSettings();
if ( if (

View File

@@ -7,10 +7,12 @@ import Email from 'email-templates';
import logger from '../../../logger'; import logger from '../../../logger';
import { getRepository } from 'typeorm'; import { getRepository } from 'typeorm';
import { User } from '../../../entity/User'; import { User } from '../../../entity/User';
import { hasPermission, Permission } from '../../permissions'; import { Permission } from '../../permissions';
class EmailAgent implements NotificationAgent { 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(); const settings = getSettings();
if (settings.notifications.agents.email.enabled) { if (settings.notifications.agents.email.enabled) {

View File

@@ -84,7 +84,7 @@ interface NotificationSettings {
} }
interface AllSettings { interface AllSettings {
clientId?: string; clientId: string;
main: MainSettings; main: MainSettings;
plex: PlexSettings; plex: PlexSettings;
radarr: RadarrSettings[]; radarr: RadarrSettings[];
@@ -100,8 +100,9 @@ class Settings {
constructor(initialSettings?: AllSettings) { constructor(initialSettings?: AllSettings) {
this.data = { this.data = {
clientId: '',
main: { main: {
apiKey: 'temp', apiKey: '',
applicationUrl: '', applicationUrl: '',
}, },
plex: { plex: {
@@ -143,6 +144,10 @@ class Settings {
} }
get main(): MainSettings { get main(): MainSettings {
if (!this.data.main.apiKey) {
this.data.main.apiKey = this.generateApiKey();
this.save();
}
return this.data.main; return this.data.main;
} }
@@ -199,6 +204,16 @@ class Settings {
return this.data.clientId; 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 * Settings Load
* *

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,10 +4,11 @@ import {
RadarrSettings, RadarrSettings,
SonarrSettings, SonarrSettings,
Library, Library,
MainSettings,
} from '../lib/settings'; } from '../lib/settings';
import { getRepository } from 'typeorm'; import { getRepository } from 'typeorm';
import { User } from '../entity/User'; import { User } from '../entity/User';
import PlexAPI, { PlexLibrary } from '../api/plexapi'; import PlexAPI from '../api/plexapi';
import { jobPlexFullSync } from '../job/plexsync'; import { jobPlexFullSync } from '../job/plexsync';
import SonarrAPI from '../api/sonarr'; import SonarrAPI from '../api/sonarr';
import RadarrAPI from '../api/radarr'; import RadarrAPI from '../api/radarr';
@@ -19,9 +20,15 @@ import { merge } from 'lodash';
const settingsRoutes = Router(); const settingsRoutes = Router();
settingsRoutes.get('/main', (_req, res) => { settingsRoutes.get('/main', (req, res) => {
const settings = getSettings(); 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); res.status(200).json(settings.main);
}); });
@@ -120,7 +127,7 @@ settingsRoutes.get('/plex/sync', (req, res) => {
return res.status(200).json(jobPlexFullSync.status()); return res.status(200).json(jobPlexFullSync.status());
}); });
settingsRoutes.get('/radarr', (req, res) => { settingsRoutes.get('/radarr', (_req, res) => {
const settings = getSettings(); const settings = getSettings();
res.status(200).json(settings.radarr); 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]); return res.status(200).json(removed[0]);
}); });
settingsRoutes.get('/sonarr', (req, res) => { settingsRoutes.get('/sonarr', (_req, res) => {
const settings = getSettings(); const settings = getSettings();
res.status(200).json(settings.sonarr); 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]); return res.status(200).json(removed[0]);
}); });
settingsRoutes.get('/jobs', (req, res) => { settingsRoutes.get('/jobs', (_req, res) => {
return res.status(200).json( return res.status(200).json(
scheduledJobs.map((job) => ({ scheduledJobs.map((job) => ({
name: job.name, name: job.name,
@@ -384,7 +391,7 @@ settingsRoutes.get('/jobs', (req, res) => {
settingsRoutes.get( settingsRoutes.get(
'/initialize', '/initialize',
isAuthenticated(Permission.ADMIN), isAuthenticated(Permission.ADMIN),
(req, res) => { (_req, res) => {
const settings = getSettings(); const settings = getSettings();
settings.public.initialized = true; 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(); const settings = getSettings();
res.status(200).json(settings.notifications.agents.discord); 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); res.status(200).json(settings.notifications.agents.discord);
}); });
settingsRoutes.get('/notifications/email', (req, res) => { settingsRoutes.get('/notifications/email', (_req, res) => {
const settings = getSettings(); const settings = getSettings();
res.status(200).json(settings.notifications.agents.email); res.status(200).json(settings.notifications.agents.email);

View File

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

View File

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

View File

@@ -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 });

View File

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

View File

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

View File

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

View File

@@ -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;

View File

@@ -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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,6 +7,7 @@ import { Form, Formik, Field } from 'formik';
import axios from 'axios'; import axios from 'axios';
import Button from '../Common/Button'; import Button from '../Common/Button';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import { useUser, Permission } from '../../hooks/useUser';
const messages = defineMessages({ const messages = defineMessages({
generalsettings: 'General Settings', generalsettings: 'General Settings',
@@ -19,6 +20,7 @@ const messages = defineMessages({
}); });
const SettingsMain: React.FC = () => { const SettingsMain: React.FC = () => {
const { hasPermission } = useUser();
const intl = useIntl(); const intl = useIntl();
const { data, error, revalidate } = useSWR<MainSettings>( const { data, error, revalidate } = useSWR<MainSettings>(
'/api/v1/settings/main' '/api/v1/settings/main'
@@ -41,7 +43,6 @@ const SettingsMain: React.FC = () => {
<div className="mt-6 sm:mt-5"> <div className="mt-6 sm:mt-5">
<Formik <Formik
initialValues={{ initialValues={{
apiKey: data?.apiKey,
applicationUrl: data?.applicationUrl, applicationUrl: data?.applicationUrl,
}} }}
onSubmit={async (values) => { onSubmit={async (values) => {
@@ -56,9 +57,10 @@ const SettingsMain: React.FC = () => {
} }
}} }}
> >
{({ errors, touched, isSubmitting }) => { {({ isSubmitting }) => {
return ( return (
<Form> <Form>
{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"> <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 <label
htmlFor="username" htmlFor="username"
@@ -70,7 +72,7 @@ const SettingsMain: React.FC = () => {
<div className="max-w-lg flex rounded-md shadow-sm"> <div className="max-w-lg flex rounded-md shadow-sm">
<input <input
type="text" type="text"
id="username" 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" 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} value={data?.apiKey}
readOnly readOnly
@@ -93,6 +95,7 @@ const SettingsMain: React.FC = () => {
</div> </div>
</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"> <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 <label
htmlFor="name" htmlFor="name"

View File

@@ -49,7 +49,7 @@ const SettingsNotifications: React.FC = ({ children }) => {
<Link href={route}> <Link href={route}>
<a <a
className={`whitespace-nowrap ml-8 first:ml-0 px-3 py-2 font-medium text-sm rounded-md ${ 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" aria-current="page"
> >

View File

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

View File

@@ -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;

View File

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

View File

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

View File

@@ -9,6 +9,7 @@ import { hasPermission } from '../../../server/lib/permissions';
import { Permission } from '../../hooks/useUser'; import { Permission } from '../../hooks/useUser';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import Header from '../Common/Header'; import Header from '../Common/Header';
import Table from '../Common/Table';
const messages = defineMessages({ const messages = defineMessages({
userlist: 'User List', userlist: 'User List',
@@ -37,38 +38,22 @@ const UserList: React.FC = () => {
return ( return (
<> <>
<Header extraMargin={4}>{intl.formatMessage(messages.userlist)}</Header> <Header extraMargin={4}>{intl.formatMessage(messages.userlist)}</Header>
<div className="flex flex-col"> <Table>
<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> <thead>
<tr> <tr>
<th className="px-6 py-3 bg-gray-500 text-left text-xs leading-4 font-medium text-gray-200 uppercase tracking-wider"> <Table.TH>{intl.formatMessage(messages.username)}</Table.TH>
{intl.formatMessage(messages.username)} <Table.TH>{intl.formatMessage(messages.totalrequests)}</Table.TH>
</th> <Table.TH>{intl.formatMessage(messages.usertype)}</Table.TH>
<th className="px-6 py-3 bg-gray-500 text-left text-xs leading-4 font-medium text-gray-200 uppercase tracking-wider"> <Table.TH>{intl.formatMessage(messages.role)}</Table.TH>
{intl.formatMessage(messages.totalrequests)} <Table.TH>{intl.formatMessage(messages.created)}</Table.TH>
</th> <Table.TH>{intl.formatMessage(messages.lastupdated)}</Table.TH>
<th className="px-6 py-3 bg-gray-500 text-left text-xs leading-4 font-medium text-gray-200 uppercase tracking-wider"> <Table.TH></Table.TH>
{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> </tr>
</thead> </thead>
<tbody className="bg-gray-600 divide-y divide-gray-700"> <Table.TBody>
{data?.map((user) => ( {data?.map((user) => (
<tr key={`user-list-${user.id}`}> <tr key={`user-list-${user.id}`}>
<td className="px-6 py-4 whitespace-nowrap"> <Table.TD>
<div className="flex items-center"> <div className="flex items-center">
<div className="flex-shrink-0 h-10 w-10"> <div className="flex-shrink-0 h-10 w-10">
<img <img
@@ -78,7 +63,7 @@ const UserList: React.FC = () => {
/> />
</div> </div>
<div className="ml-4"> <div className="ml-4">
<div className="text-sm leading-5 font-medium text-white"> <div className="text-sm leading-5 font-medium">
{user.username} {user.username}
</div> </div>
<div className="text-sm leading-5 text-gray-300"> <div className="text-sm leading-5 text-gray-300">
@@ -86,29 +71,27 @@ const UserList: React.FC = () => {
</div> </div>
</div> </div>
</div> </div>
</td> </Table.TD>
<td className="px-6 py-4 whitespace-nowrap"> <Table.TD>
<div className="text-sm leading-5 text-white"> <div className="text-sm leading-5">{user.requestCount}</div>
{user.requestCount} </Table.TD>
</div> <Table.TD>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<Badge badgeType="warning"> <Badge badgeType="warning">
{intl.formatMessage(messages.plexuser)} {intl.formatMessage(messages.plexuser)}
</Badge> </Badge>
</td> </Table.TD>
<td className="px-6 py-4 whitespace-nowrap text-sm leading-5 text-white"> <Table.TD>
{hasPermission(Permission.ADMIN, user.permissions) {hasPermission(Permission.ADMIN, user.permissions)
? intl.formatMessage(messages.admin) ? intl.formatMessage(messages.admin)
: intl.formatMessage(messages.user)} : intl.formatMessage(messages.user)}
</td> </Table.TD>
<td className="px-6 py-4 whitespace-nowrap text-sm leading-5 text-white"> <Table.TD>
<FormattedDate value={user.createdAt} /> <FormattedDate value={user.createdAt} />
</td> </Table.TD>
<td className="px-6 py-4 whitespace-nowrap text-sm leading-5 text-white"> <Table.TD>
<FormattedDate value={user.updatedAt} /> <FormattedDate value={user.updatedAt} />
</td> </Table.TD>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm leading-5 font-medium"> <Table.TD alignText="right">
<Button <Button
buttonType="warning" buttonType="warning"
className="mr-2" className="mr-2"
@@ -124,15 +107,11 @@ const UserList: React.FC = () => {
<Button buttonType="danger"> <Button buttonType="danger">
{intl.formatMessage(messages.delete)} {intl.formatMessage(messages.delete)}
</Button> </Button>
</td> </Table.TD>
</tr> </tr>
))} ))}
</tbody> </Table.TBody>
</table> </Table>
</div>
</div>
</div>
</div>
</> </>
); );
}; };

View File

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

View File

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

View File

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

View File

@@ -51,6 +51,17 @@
"components.RequestBlock.seasons": "Seasons", "components.RequestBlock.seasons": "Seasons",
"components.RequestCard.requestedby": "Requested by {username}", "components.RequestCard.requestedby": "Requested by {username}",
"components.RequestCard.seasons": "Seasons", "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.cancel": "Cancel Request",
"components.RequestModal.cancelling": "Cancelling...", "components.RequestModal.cancelling": "Cancelling...",
"components.RequestModal.cancelrequest": "This will remove your request. Are you sure you want to continue?", "components.RequestModal.cancelrequest": "This will remove your request. Are you sure you want to continue?",
@@ -275,6 +286,7 @@
"i18n.cancel": "Cancel", "i18n.cancel": "Cancel",
"i18n.decline": "Decline", "i18n.decline": "Decline",
"i18n.declined": "Declined", "i18n.declined": "Declined",
"i18n.delete": "Delete",
"i18n.movies": "Movies", "i18n.movies": "Movies",
"i18n.partiallyavailable": "Partially Available", "i18n.partiallyavailable": "Partially Available",
"i18n.pending": "Pending", "i18n.pending": "Pending",

View File

@@ -51,6 +51,17 @@
"components.RequestBlock.seasons": "Saisons", "components.RequestBlock.seasons": "Saisons",
"components.RequestCard.requestedby": "Demandé par {username}", "components.RequestCard.requestedby": "Demandé par {username}",
"components.RequestCard.seasons": "Saisons", "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.cancel": "Annuler la Demande",
"components.RequestModal.cancelling": "Annulation...", "components.RequestModal.cancelling": "Annulation...",
"components.RequestModal.cancelrequest": "Votre demande d'ajout va être annulée. Êtes-vous sûr de vouloir annuler?", "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.cancel": "Annuler",
"i18n.decline": "Refuser", "i18n.decline": "Refuser",
"i18n.declined": "Refusé", "i18n.declined": "Refusé",
"i18n.delete": "",
"i18n.movies": "Films", "i18n.movies": "Films",
"i18n.partiallyavailable": "Partiellement Disponible", "i18n.partiallyavailable": "Partiellement Disponible",
"i18n.pending": "En Attente", "i18n.pending": "En Attente",

View File

@@ -51,6 +51,17 @@
"components.RequestBlock.seasons": "", "components.RequestBlock.seasons": "",
"components.RequestCard.requestedby": "", "components.RequestCard.requestedby": "",
"components.RequestCard.seasons": "", "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.cancel": "チャンセルリクエスト",
"components.RequestModal.cancelling": "", "components.RequestModal.cancelling": "",
"components.RequestModal.cancelrequest": "このリクエストをキャンセルしてよろしいですか?", "components.RequestModal.cancelrequest": "このリクエストをキャンセルしてよろしいですか?",
@@ -275,6 +286,7 @@
"i18n.cancel": "", "i18n.cancel": "",
"i18n.decline": "", "i18n.decline": "",
"i18n.declined": "", "i18n.declined": "",
"i18n.delete": "",
"i18n.movies": "", "i18n.movies": "",
"i18n.partiallyavailable": "", "i18n.partiallyavailable": "",
"i18n.pending": "リクエスト中", "i18n.pending": "リクエスト中",

View File

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

View File

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

View File

@@ -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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -57,7 +57,6 @@ class PlexOAuth {
'You must initialize the plex headers clientside to login' 'You must initialize the plex headers clientside to login'
); );
} }
try {
const response = await axios.post( const response = await axios.post(
'https://plex.tv/api/v2/pins?strong=true', 'https://plex.tv/api/v2/pins?strong=true',
undefined, undefined,
@@ -67,9 +66,6 @@ class PlexOAuth {
this.pin = { id: response.data.id, code: response.data.code }; this.pin = { id: response.data.id, code: response.data.code };
return this.pin; return this.pin;
} catch (e) {
throw e;
}
} }
public preparePopup(): void { public preparePopup(): void {
@@ -77,7 +73,6 @@ class PlexOAuth {
} }
public async login(): Promise<string> { public async login(): Promise<string> {
try {
this.initializeHeaders(); this.initializeHeaders();
await this.getPin(); await this.getPin();
@@ -110,9 +105,6 @@ class PlexOAuth {
} }
return this.pinPoll(); return this.pinPoll();
} catch (e) {
throw e;
}
} }
private async pinPoll(): Promise<string> { private async pinPoll(): Promise<string> {
@@ -131,9 +123,9 @@ class PlexOAuth {
); );
if (response.data?.authToken) { if (response.data?.authToken) {
this.authToken = response.data.authToken; this.authToken = response.data.authToken as string;
this.closePopup(); this.closePopup();
resolve(response.data.authToken); resolve(this.authToken);
} else if (!response.data?.authToken && !this.popup?.closed) { } else if (!response.data?.authToken && !this.popup?.closed) {
setTimeout(executePoll, 1000, resolve, reject); setTimeout(executePoll, 1000, resolve, reject);
} else { } else {

View File

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

493
yarn.lock
View File

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