From eb5248d8d14f07d9efca72236b7f582e8f4f9573 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 28 May 2022 19:13:54 +0900 Subject: [PATCH 01/81] docs: add sambartik as a contributor for code (#2783) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ README.md | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index a230a4685..da00cf331 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -665,6 +665,15 @@ "contributions": [ "translation" ] + }, + { + "login": "sambartik", + "name": "Samuel Bartík", + "avatar_url": "https://avatars.githubusercontent.com/u/63553146?v=4", + "profile": "https://github.com/sambartik", + "contributions": [ + "code" + ] } ], "badgeTemplate": "\"All-orange.svg\"/>", diff --git a/README.md b/README.md index 6bca69eb0..7e9c7cee7 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Language grade: JavaScript GitHub -All Contributors +All Contributors

@@ -167,6 +167,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Andersborrits

🌍
Maxent

🌍 +
Samuel Bartík

💻 From a6c1f3f7ce498e32817cc8c74d439e8d99d6fbf4 Mon Sep 17 00:00:00 2001 From: Danshil Kokil Mungur Date: Sat, 28 May 2022 16:48:23 +0400 Subject: [PATCH 02/81] fix(api): ignore filter if unset in media route (#2647) Co-authored-by: Ryan Cohen --- server/routes/media.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/routes/media.ts b/server/routes/media.ts index 429b2010f..0c6e784df 100644 --- a/server/routes/media.ts +++ b/server/routes/media.ts @@ -66,7 +66,7 @@ mediaRoutes.get('/', async (req, res, next) => { try { const [media, mediaCount] = await mediaRepository.findAndCount({ order: sortFilter, - where: { + where: statusFilter && { status: statusFilter, }, take: pageSize, From 6cd0c9b2c81db2728ce09a9bc273a9aee366acbc Mon Sep 17 00:00:00 2001 From: Danshil Kokil Mungur Date: Fri, 22 Jul 2022 23:00:59 +0400 Subject: [PATCH 03/81] fix(api): use correct path param type in openapi spec (#2834) --- overseerr-api.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/overseerr-api.yml b/overseerr-api.yml index be9ee0c02..a00ada890 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -3170,8 +3170,8 @@ paths: name: guid required: true schema: - type: number - example: 1 + type: string + example: '9afef5a7-ec89-4d5f-9397-261e96970b50' responses: '200': description: OK From 575da306b03eea3561de8d7dbe1b4b69674c7b2b Mon Sep 17 00:00:00 2001 From: Brandon Cohen Date: Tue, 2 Aug 2022 02:41:09 -0400 Subject: [PATCH 04/81] feat: plex deep links for iOS devices (#2680) --- server/entity/Media.ts | 7 +++++ src/components/MovieDetails/index.tsx | 33 ++++++++++++++++++++---- src/components/TvDetails/index.tsx | 37 ++++++++++++++++++++------- 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/server/entity/Media.ts b/server/entity/Media.ts index 9d106d4f5..4de7698b5 100644 --- a/server/entity/Media.ts +++ b/server/entity/Media.ts @@ -145,6 +145,9 @@ class Media { public plexUrl?: string; public plexUrl4k?: string; + public iOSPlexUrl?: string; + public iOSPlexUrl4k?: string; + public tautulliUrl?: string; public tautulliUrl4k?: string; @@ -164,6 +167,8 @@ class Media { this.ratingKey }`; + this.iOSPlexUrl = `plex://preplay/?metadataKey=%2Flibrary%2Fmetadata%2F${this.ratingKey}&server=${machineId}`; + if (tautulliUrl) { this.tautulliUrl = `${tautulliUrl}/info?rating_key=${this.ratingKey}`; } @@ -176,6 +181,8 @@ class Media { this.ratingKey4k }`; + this.iOSPlexUrl4k = `plex://preplay/?metadataKey=%2Flibrary%2Fmetadata%2F${this.ratingKey4k}&server=${machineId}`; + if (tautulliUrl) { this.tautulliUrl4k = `${tautulliUrl}/info?rating_key=${this.ratingKey4k}`; } diff --git a/src/components/MovieDetails/index.tsx b/src/components/MovieDetails/index.tsx index 8dd00159c..fb749b882 100644 --- a/src/components/MovieDetails/index.tsx +++ b/src/components/MovieDetails/index.tsx @@ -113,6 +113,30 @@ const MovieDetails: React.FC = ({ movie }) => { setShowManager(router.query.manage == '1' ? true : false); }, [router.query.manage]); + const [plexUrl, setPlexUrl] = useState(data?.mediaInfo?.plexUrl); + const [plexUrl4k, setPlexUrl4k] = useState(data?.mediaInfo?.plexUrl4k); + + useEffect(() => { + if (data) { + if ( + /iPad|iPhone|iPod/.test(navigator.userAgent) || + (navigator.userAgent === 'MacIntel' && navigator.maxTouchPoints > 1) + ) { + setPlexUrl(data.mediaInfo?.iOSPlexUrl); + setPlexUrl4k(data.mediaInfo?.iOSPlexUrl4k); + } else { + setPlexUrl(data.mediaInfo?.plexUrl); + setPlexUrl4k(data.mediaInfo?.plexUrl4k); + } + } + }, [ + data, + data?.mediaInfo?.iOSPlexUrl, + data?.mediaInfo?.iOSPlexUrl4k, + data?.mediaInfo?.plexUrl, + data?.mediaInfo?.plexUrl4k, + ]); + if (!data && !error) { return ; } @@ -125,32 +149,31 @@ const MovieDetails: React.FC = ({ movie }) => { const mediaLinks: PlayButtonLink[] = []; if ( - data.mediaInfo?.plexUrl && + plexUrl && hasPermission([Permission.REQUEST, Permission.REQUEST_MOVIE], { type: 'or', }) ) { mediaLinks.push({ text: intl.formatMessage(messages.playonplex), - url: data.mediaInfo?.plexUrl, + url: plexUrl, svg: , }); } if ( settings.currentSettings.movie4kEnabled && - data.mediaInfo?.plexUrl4k && + plexUrl4k && hasPermission([Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE], { type: 'or', }) ) { mediaLinks.push({ text: intl.formatMessage(messages.play4konplex), - url: data.mediaInfo?.plexUrl4k, + url: plexUrl4k, svg: , }); } - const trailerUrl = data.relatedVideos ?.filter((r) => r.type === 'Trailer') .sort((a, b) => a.size - b.size) diff --git a/src/components/TvDetails/index.tsx b/src/components/TvDetails/index.tsx index 64327648b..ce86c47f1 100644 --- a/src/components/TvDetails/index.tsx +++ b/src/components/TvDetails/index.tsx @@ -106,6 +106,30 @@ const TvDetails: React.FC = ({ tv }) => { setShowManager(router.query.manage == '1' ? true : false); }, [router.query.manage]); + const [plexUrl, setPlexUrl] = useState(data?.mediaInfo?.plexUrl); + const [plexUrl4k, setPlexUrl4k] = useState(data?.mediaInfo?.plexUrl4k); + + useEffect(() => { + if (data) { + if ( + /iPad|iPhone|iPod/.test(navigator.userAgent) || + (navigator.userAgent === 'MacIntel' && navigator.maxTouchPoints > 1) + ) { + setPlexUrl(data.mediaInfo?.iOSPlexUrl); + setPlexUrl4k(data.mediaInfo?.iOSPlexUrl4k); + } else { + setPlexUrl(data.mediaInfo?.plexUrl); + setPlexUrl4k(data.mediaInfo?.plexUrl4k); + } + } + }, [ + data, + data?.mediaInfo?.iOSPlexUrl, + data?.mediaInfo?.iOSPlexUrl4k, + data?.mediaInfo?.plexUrl, + data?.mediaInfo?.plexUrl4k, + ]); + if (!data && !error) { return ; } @@ -116,29 +140,24 @@ const TvDetails: React.FC = ({ tv }) => { const mediaLinks: PlayButtonLink[] = []; - if ( - data.mediaInfo?.plexUrl && - hasPermission([Permission.REQUEST, Permission.REQUEST_TV], { - type: 'or', - }) - ) { + if (plexUrl) { mediaLinks.push({ text: intl.formatMessage(messages.playonplex), - url: data.mediaInfo?.plexUrl, + url: plexUrl, svg: , }); } if ( settings.currentSettings.series4kEnabled && - data.mediaInfo?.plexUrl4k && + plexUrl4k && hasPermission([Permission.REQUEST_4K, Permission.REQUEST_4K_TV], { type: 'or', }) ) { mediaLinks.push({ text: intl.formatMessage(messages.play4konplex), - url: data.mediaInfo?.plexUrl4k, + url: plexUrl4k, svg: , }); } From aed1409f29d5b6360e87381d78dfeb4cc86d6fc6 Mon Sep 17 00:00:00 2001 From: Ryan Cohen Date: Tue, 2 Aug 2022 16:00:34 +0900 Subject: [PATCH 05/81] fix(api): add rate limiter to TMDb requests to hopefully deal with 429s (#2881) fixes #2853 --- package.json | 1 + server/api/externalapi.ts | 13 +++++++++++++ server/api/themoviedb/index.ts | 4 ++++ yarn.lock | 5 +++++ 4 files changed, 23 insertions(+) diff --git a/package.json b/package.json index 5a2d43fe8..85213ed4d 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@tanem/react-nprogress": "^4.0.10", "ace-builds": "^1.4.14", "axios": "^0.26.1", + "axios-rate-limit": "^1.3.0", "bcrypt": "^5.0.1", "bowser": "^2.11.0", "connect-typeorm": "^1.1.4", diff --git a/server/api/externalapi.ts b/server/api/externalapi.ts index 2a1d94950..d39701008 100644 --- a/server/api/externalapi.ts +++ b/server/api/externalapi.ts @@ -1,4 +1,5 @@ import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'; +import rateLimit from 'axios-rate-limit'; import NodeCache from 'node-cache'; // 5 minute default TTL (in seconds) @@ -10,6 +11,10 @@ const DEFAULT_ROLLING_BUFFER = 10000; interface ExternalAPIOptions { nodeCache?: NodeCache; headers?: Record; + rateLimit?: { + maxRPS: number; + maxRequests: number; + }; } class ExternalAPI { @@ -31,6 +36,14 @@ class ExternalAPI { ...options.headers, }, }); + + if (options.rateLimit) { + this.axios = rateLimit(this.axios, { + maxRequests: options.rateLimit.maxRequests, + maxRPS: options.rateLimit.maxRPS, + }); + } + this.baseUrl = baseUrl; this.cache = options.nodeCache; } diff --git a/server/api/themoviedb/index.ts b/server/api/themoviedb/index.ts index b5060c030..69d26d5f9 100644 --- a/server/api/themoviedb/index.ts +++ b/server/api/themoviedb/index.ts @@ -92,6 +92,10 @@ class TheMovieDb extends ExternalAPI { }, { nodeCache: cacheManager.getCache('tmdb').data, + rateLimit: { + maxRequests: 20, + maxRPS: 1, + }, } ); this.region = region; diff --git a/yarn.lock b/yarn.lock index b32edc909..a66ef1dfa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3256,6 +3256,11 @@ axe-core@^4.3.5: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.1.tgz#7dbdc25989298f9ad006645cd396782443757413" integrity sha512-gd1kmb21kwNuWr6BQz8fv6GNECPBnUasepcoLbekws23NVBLODdsClRZ+bQ8+9Uomf3Sm3+Vwn0oYG9NvwnJCw== +axios-rate-limit@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/axios-rate-limit/-/axios-rate-limit-1.3.0.tgz#03241d24c231c47432dab6e8234cfde819253c2e" + integrity sha512-cKR5wTbU/CeeyF1xVl5hl6FlYsmzDVqxlN4rGtfO5x7J83UxKDckudsW0yW21/ZJRcO0Qrfm3fUFbhEbWTLayw== + axios@^0.26.1: version "0.26.1" resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" From 9da84612254fe0c36b38bc49184cc1fc52ff6212 Mon Sep 17 00:00:00 2001 From: Chun Yeung Wong <90653148+frank-cywong@users.noreply.github.com> Date: Tue, 2 Aug 2022 21:24:57 -0400 Subject: [PATCH 06/81] fix: update Discord ID regex to include 19 digit IDs (#2860) --- .../UserProfile/UserSettings/UserGeneralSettings/index.tsx | 2 +- .../UserNotificationSettings/UserNotificationsDiscord.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx index 5f563ee95..34a3f6170 100644 --- a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx +++ b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx @@ -80,7 +80,7 @@ const UserGeneralSettings: React.FC = () => { const UserGeneralSettingsSchema = Yup.object().shape({ discordId: Yup.string() .nullable() - .matches(/^\d{17,18}$/, intl.formatMessage(messages.validationDiscordId)), + .matches(/^\d{17,19}$/, intl.formatMessage(messages.validationDiscordId)), }); useEffect(() => { diff --git a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsDiscord.tsx b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsDiscord.tsx index b88f69e62..95802857f 100644 --- a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsDiscord.tsx +++ b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsDiscord.tsx @@ -46,7 +46,7 @@ const UserNotificationsDiscord: React.FC = () => { .required(intl.formatMessage(messages.validationDiscordId)), otherwise: Yup.string().nullable(), }) - .matches(/^\d{17,18}$/, intl.formatMessage(messages.validationDiscordId)), + .matches(/^\d{17,19}$/, intl.formatMessage(messages.validationDiscordId)), }); if (!data && !error) { From 25eb765f9b2cfb201672e2f690ddcd4758c872ef Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 3 Aug 2022 10:26:02 +0900 Subject: [PATCH 07/81] docs: add frank-cywong as a contributor for code (#2884) [skip ci] * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ README.md | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index da00cf331..fe52e371e 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -674,6 +674,15 @@ "contributions": [ "code" ] + }, + { + "login": "frank-cywong", + "name": "Chun Yeung Wong", + "avatar_url": "https://avatars.githubusercontent.com/u/90653148?v=4", + "profile": "https://github.com/frank-cywong", + "contributions": [ + "code" + ] } ], "badgeTemplate": "\"All-orange.svg\"/>", diff --git a/README.md b/README.md index 7e9c7cee7..c99323ba8 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Language grade: JavaScript GitHub -All Contributors +All Contributors

@@ -168,6 +168,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Andersborrits

🌍
Maxent

🌍
Samuel Bartík

💻 +
Chun Yeung Wong

💻 From f5864b49de65ad1bff1e8c757a13aac80f15b6a8 Mon Sep 17 00:00:00 2001 From: Ryan Cohen Date: Wed, 3 Aug 2022 12:57:51 +0900 Subject: [PATCH 08/81] refactor: update a few dev deps and convert to using type imports where possible (#2886) * build: bump deps and add some new eslint rules * refactor: run eslint --fix on code to convert to type imports where possible --- .eslintrc.js | 10 +- .github/workflows/ci.yml | 2 + .vscode/settings.json | 3 - package.json | 28 +- server/api/externalapi.ts | 5 +- server/api/plexapi.ts | 4 +- server/api/plextv.ts | 5 +- server/api/servarr/base.ts | 5 +- server/api/tautulli.ts | 7 +- server/api/themoviedb/index.ts | 2 +- server/entity/Issue.ts | 3 +- server/entity/Media.ts | 3 +- server/entity/MediaRequest.ts | 9 +- server/entity/Session.ts | 2 +- server/entity/User.ts | 9 +- server/entity/UserSettings.ts | 2 +- server/index.ts | 6 +- server/interfaces/api/issueInterfaces.ts | 4 +- server/interfaces/api/mediaInterfaces.ts | 4 +- server/interfaces/api/personInterfaces.ts | 2 +- server/interfaces/api/plexInterfaces.ts | 2 +- server/interfaces/api/serviceInterfaces.ts | 4 +- server/interfaces/api/userInterfaces.ts | 6 +- .../interfaces/api/userSettingsInterfaces.ts | 2 +- server/job/schedule.ts | 3 +- server/lib/email/index.ts | 3 +- server/lib/email/openpgpEncrypt.ts | 3 +- server/lib/notifications/agents/agent.ts | 12 +- server/lib/notifications/agents/discord.ts | 10 +- server/lib/notifications/agents/email.ts | 12 +- server/lib/notifications/agents/gotify.ts | 6 +- server/lib/notifications/agents/lunasea.ts | 6 +- server/lib/notifications/agents/pushbullet.ts | 10 +- server/lib/notifications/agents/pushover.ts | 10 +- server/lib/notifications/agents/slack.ts | 6 +- server/lib/notifications/agents/telegram.ts | 10 +- server/lib/notifications/agents/webhook.ts | 6 +- server/lib/notifications/agents/webpush.ts | 10 +- server/lib/notifications/index.ts | 2 +- server/lib/scanners/plex/index.ts | 11 +- server/lib/scanners/radarr/index.ts | 9 +- server/lib/scanners/sonarr/index.ts | 11 +- server/lib/search.ts | 2 +- server/middleware/auth.ts | 2 +- .../1603944374840-InitialMigration.ts | 2 +- .../migration/1605085519544-SeasonStatus.ts | 2 +- .../1606730060700-CascadeMigration.ts | 2 +- .../1607928251245-DropImdbIdConstraint.ts | 3 +- ...8217312474-AddUserRequestDeleteCascades.ts | 2 +- .../1608477467935-AddLastSeasonChangeMedia.ts | 2 +- ...477467936-ForceDropImdbUniqueConstraint.ts | 2 +- ...9236552057-RemoveTmdbIdUniqueConstraint.ts | 2 +- server/migration/1610070934506-LocalUsers.ts | 2 +- .../1610370640747-Add4kStatusFields.ts | 2 +- ...1610522845513-AddMediaAddedFieldToMedia.ts | 2 +- .../1611508672722-AddDisplayNameToUser.ts | 2 +- ...757511674-SonarrRadarrSyncServiceFields.ts | 2 +- .../1611801511397-AddRatingKeysToMedia.ts | 2 +- ...78137-AddResetPasswordGuidAndExpiryDate.ts | 2 +- .../1612571545781-AddLanguageProfileId.ts | 2 +- .../1613615266968-CreateUserSettings.ts | 2 +- ...1613955393450-UpdateUserSettingsRegions.ts | 2 +- ...95680-AddTelegramSettingsToUserSettings.ts | 2 +- .../1615333940450-AddPGPToUserSettings.ts | 2 +- .../1616576677254-AddUserQuotaFields.ts | 2 +- ...624225464-CreateTagsFieldonMediaRequest.ts | 2 +- ...-AddUserSettingsNotificationAgentsField.ts | 2 +- ...18912653565-CreateUserPushSubscriptions.ts | 2 +- .../1619239659754-AddUserSettingsLocale.ts | 2 +- ...817343-AddUserSettingsNotificationTypes.ts | 2 +- server/migration/1634904083966-AddIssues.ts | 2 +- ...63457-AddPushbulletPushoverUserSettings.ts | 2 +- server/models/Collection.ts | 5 +- server/models/Movie.ts | 10 +- server/models/Person.ts | 2 +- server/models/Search.ts | 2 +- server/models/Tv.ts | 12 +- server/models/common.ts | 2 +- server/routes/discover.ts | 4 +- server/routes/index.ts | 7 +- server/routes/issue.ts | 2 +- server/routes/media.ts | 5 +- server/routes/request.ts | 2 +- server/routes/search.ts | 2 +- server/routes/service.ts | 2 +- server/routes/settings/index.ts | 10 +- server/routes/settings/notifications.ts | 4 +- server/routes/settings/radarr.ts | 3 +- server/routes/settings/sonarr.ts | 3 +- server/routes/user/index.ts | 2 +- server/routes/user/usersettings.ts | 2 +- server/subscriber/IssueCommentSubscriber.ts | 8 +- server/subscriber/IssueSubscriber.ts | 4 +- server/subscriber/MediaSubscriber.ts | 9 +- src/components/Common/Button/index.tsx | 3 +- .../Common/ButtonWithDropdown/index.tsx | 5 +- src/components/Common/CachedImage/index.tsx | 3 +- src/components/Common/ImageFader/index.tsx | 8 +- src/components/Common/ListView/index.tsx | 2 +- src/components/Common/Modal/index.tsx | 6 +- src/components/Common/PlayButton/index.tsx | 3 +- src/components/Common/SettingsTabs/index.tsx | 3 +- src/components/Common/Table/index.tsx | 3 +- .../Discover/DiscoverNetwork/index.tsx | 2 +- .../Discover/DiscoverStudio/index.tsx | 2 +- .../Discover/MovieGenreList/index.tsx | 2 +- .../Discover/MovieGenreSlider/index.tsx | 2 +- src/components/Discover/TvGenreList/index.tsx | 2 +- .../Discover/TvGenreSlider/index.tsx | 2 +- src/components/DownloadBlock/index.tsx | 2 +- src/components/IssueList/IssueItem/index.tsx | 6 +- src/components/IssueList/index.tsx | 2 +- .../IssueModal/CreateIssueModal/index.tsx | 4 +- src/components/IssueModal/constants.ts | 3 +- src/components/JSONEditor/index.tsx | 3 +- src/components/LanguageSelector/index.tsx | 5 +- .../Layout/LanguagePicker/index.tsx | 6 +- src/components/Layout/Sidebar/index.tsx | 3 +- src/components/Layout/VersionStatus/index.tsx | 2 +- src/components/Layout/index.tsx | 2 +- src/components/ManageSlideOver/index.tsx | 6 +- .../MovieDetails/MovieCast/index.tsx | 2 +- .../MovieDetails/MovieCrew/index.tsx | 2 +- src/components/MovieDetails/index.tsx | 3 +- .../NotificationType/index.tsx | 3 +- .../NotificationTypeSelector/index.tsx | 3 +- src/components/PermissionEdit/index.tsx | 6 +- src/components/PermissionOption/index.tsx | 3 +- src/components/RequestButton/index.tsx | 4 +- .../RequestModal/AdvancedRequester/index.tsx | 3 +- .../RequestModal/CollectionRequestModal.tsx | 9 +- .../RequestModal/MovieRequestModal.tsx | 9 +- .../RequestModal/QuotaDisplay/index.tsx | 2 +- .../RequestModal/SearchByNameModal/index.tsx | 2 +- .../RequestModal/TvRequestModal.tsx | 11 +- src/components/RequestModal/index.tsx | 2 +- src/components/Search/index.tsx | 2 +- src/components/ServiceWorkerSetup/index.tsx | 3 +- .../Settings/SettingsAbout/index.tsx | 2 +- .../Settings/SettingsJobsCache/index.tsx | 12 +- src/components/Settings/SettingsLayout.tsx | 3 +- .../Settings/SettingsLogs/index.tsx | 2 +- src/components/Settings/SettingsMain.tsx | 8 +- .../Settings/SettingsNotifications.tsx | 3 +- src/components/Settings/SonarrModal/index.tsx | 3 +- src/components/Slider/index.tsx | 9 +- src/components/StatusChacker/index.tsx | 2 +- src/components/ToastContainer/index.tsx | 2 +- .../TvDetails/TvRecommendations.tsx | 2 +- src/components/TvDetails/index.tsx | 7 +- src/components/UserList/BulkEditModal.tsx | 3 +- src/components/UserList/index.tsx | 3 +- .../UserProfile/ProfileHeader/index.tsx | 3 +- .../UserGeneralSettings/index.tsx | 8 +- .../UserNotificationsDiscord.tsx | 2 +- .../UserNotificationsEmail.tsx | 2 +- .../UserNotificationsPushbullet.tsx | 2 +- .../UserNotificationsPushover.tsx | 2 +- .../UserNotificationsTelegram.tsx | 2 +- .../UserNotificationsWebPush.tsx | 2 +- .../UserNotificationSettings/index.tsx | 5 +- .../UserProfile/UserSettings/index.tsx | 5 +- src/components/UserProfile/index.tsx | 6 +- src/context/LanguageContext.tsx | 3 +- src/context/SettingsContext.tsx | 2 +- src/context/UserContext.tsx | 3 +- src/hooks/useDebouncedState.ts | 3 +- src/hooks/useLocale.ts | 6 +- src/hooks/useRequestOverride.ts | 4 +- src/hooks/useRouteGuard.ts | 3 +- src/hooks/useSearchInput.ts | 3 +- src/hooks/useSettings.ts | 6 +- src/hooks/useUpdateQueryParams.ts | 5 +- src/hooks/useUser.ts | 11 +- src/hooks/useVerticalScroll.ts | 3 +- src/pages/_app.tsx | 10 +- src/pages/_document.tsx | 10 +- src/pages/collection/[collectionId]/index.tsx | 2 +- .../discover/movies/genre/[genreId]/index.tsx | 2 +- src/pages/discover/movies/genres.tsx | 2 +- src/pages/discover/movies/index.tsx | 2 +- .../movies/language/[language]/index.tsx | 2 +- .../movies/studio/[studioId]/index.tsx | 2 +- src/pages/discover/movies/upcoming.tsx | 2 +- .../discover/tv/genre/[genreId]/index.tsx | 2 +- src/pages/discover/tv/genres.tsx | 2 +- src/pages/discover/tv/index.tsx | 2 +- .../discover/tv/language/[language]/index.tsx | 2 +- .../discover/tv/network/[networkId]/index.tsx | 2 +- src/pages/discover/tv/upcoming.tsx | 2 +- src/pages/issues/[issueId]/index.tsx | 2 +- src/pages/issues/index.tsx | 2 +- src/pages/movie/[movieId]/cast.tsx | 2 +- src/pages/movie/[movieId]/crew.tsx | 2 +- src/pages/movie/[movieId]/index.tsx | 2 +- src/pages/movie/[movieId]/recommendations.tsx | 2 +- src/pages/movie/[movieId]/similar.tsx | 2 +- src/pages/person/[personId]/index.tsx | 2 +- src/pages/profile/index.tsx | 2 +- src/pages/profile/settings/index.tsx | 2 +- src/pages/profile/settings/main.tsx | 2 +- .../settings/notifications/discord.tsx | 2 +- .../profile/settings/notifications/email.tsx | 2 +- .../settings/notifications/pushbullet.tsx | 2 +- .../settings/notifications/pushover.tsx | 2 +- .../settings/notifications/telegram.tsx | 2 +- .../settings/notifications/webpush.tsx | 2 +- src/pages/profile/settings/password.tsx | 2 +- src/pages/profile/settings/permissions.tsx | 2 +- src/pages/settings/about.tsx | 2 +- src/pages/settings/index.tsx | 2 +- src/pages/settings/logs.tsx | 2 +- src/pages/settings/main.tsx | 2 +- src/pages/settings/notifications/discord.tsx | 2 +- src/pages/settings/notifications/email.tsx | 2 +- src/pages/settings/notifications/gotify.tsx | 2 +- src/pages/settings/notifications/lunasea.tsx | 2 +- .../settings/notifications/pushbullet.tsx | 2 +- src/pages/settings/notifications/pushover.tsx | 2 +- src/pages/settings/notifications/slack.tsx | 2 +- src/pages/settings/notifications/telegram.tsx | 2 +- src/pages/settings/notifications/webhook.tsx | 2 +- src/pages/settings/notifications/webpush.tsx | 2 +- src/pages/settings/users.tsx | 2 +- src/pages/setup.tsx | 2 +- src/pages/tv/[tvId]/cast.tsx | 2 +- src/pages/tv/[tvId]/crew.tsx | 2 +- src/pages/tv/[tvId]/index.tsx | 2 +- src/pages/tv/[tvId]/recommendations.tsx | 2 +- src/pages/tv/[tvId]/similar.tsx | 2 +- src/pages/users/[userId]/index.tsx | 2 +- src/pages/users/[userId]/requests.tsx | 2 +- src/pages/users/[userId]/settings/index.tsx | 2 +- src/pages/users/[userId]/settings/main.tsx | 2 +- .../settings/notifications/discord.tsx | 2 +- .../[userId]/settings/notifications/email.tsx | 2 +- .../settings/notifications/pushbullet.tsx | 2 +- .../settings/notifications/pushover.tsx | 2 +- .../settings/notifications/telegram.tsx | 2 +- .../settings/notifications/webpush.tsx | 2 +- .../users/[userId]/settings/password.tsx | 2 +- .../users/[userId]/settings/permissions.tsx | 2 +- src/types/react-intl-auto.d.ts | 2 +- src/utils/creditHelpers.ts | 2 +- yarn.lock | 755 +++++++++++++----- 245 files changed, 1034 insertions(+), 620 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index b1c6f4b9f..fd45e0aa9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -26,11 +26,17 @@ module.exports = { 'react-hooks/rules-of-hooks': 'error', 'react-hooks/exhaustive-deps': 'warn', '@typescript-eslint/explicit-function-return-type': 'off', - 'prettier/prettier': ['error', { endOfLine: 'auto' }], 'formatjs/no-offset': 'error', 'no-unused-vars': 'off', '@typescript-eslint/no-unused-vars': ['error'], + '@typescript-eslint/array-type': ['error', { default: 'array' }], 'jsx-a11y/no-onchange': 'off', + '@typescript-eslint/consistent-type-imports': [ + 'error', + { + prefer: 'type-imports', + }, + ], }, overrides: [ { @@ -40,7 +46,7 @@ module.exports = { }, }, ], - plugins: ['jsx-a11y', 'prettier', 'react-hooks', 'formatjs'], + plugins: ['jsx-a11y', 'react-hooks', 'formatjs'], settings: { react: { pragma: 'React', diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5aa1572dd..b8163b5bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,8 @@ jobs: run: yarn - name: Lint run: yarn lint + - name: Formatting + run: yarn format:check - name: Build run: yarn build diff --git a/.vscode/settings.json b/.vscode/settings.json index 26aca34b8..b8cd61954 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,8 +15,5 @@ "database": "./config/db/db.sqlite3" } ], - "editor.codeActionsOnSave": { - "source.organizeImports": true - }, "editor.formatOnSave": true } diff --git a/package.json b/package.json index 85213ed4d..adbc0399c 100644 --- a/package.json +++ b/package.json @@ -7,13 +7,14 @@ "build:server": "tsc --project server/tsconfig.json && copyfiles -u 2 server/templates/**/*.{html,pug} dist/templates", "build:next": "next build", "build": "yarn build:next && yarn build:server", - "lint": "eslint \"./server/**/*.{ts,tsx}\" \"./src/**/*.{ts,tsx}\"", + "lint": "eslint \"./server/**/*.{ts,tsx}\" \"./src/**/*.{ts,tsx}\" --cache", "start": "NODE_ENV=production node dist/index.js", "i18n:extract": "extract-messages -l=en -o src/i18n/locale -d en --flat true --overwriteDefault true \"./src/**/!(*.test).{ts,tsx}\"", "migration:generate": "ts-node --project server/tsconfig.json ./node_modules/typeorm/cli.js migration:generate", "migration:create": "ts-node --project server/tsconfig.json ./node_modules/typeorm/cli.js migration:create", "migration:run": "ts-node --project server/tsconfig.json ./node_modules/typeorm/cli.js migration:run", - "format": "prettier --write .", + "format": "prettier --loglevel warn --write --cache .", + "format:check": "prettier --check --cache .", "prepare": "husky install" }, "repository": { @@ -121,26 +122,27 @@ "commitizen": "^4.2.4", "copyfiles": "^2.4.1", "cz-conventional-changelog": "^3.3.0", - "eslint": "^8.11.0", - "eslint-config-next": "^12.1.0", + "eslint": "^8.21.0", + "eslint-config-next": "^12.2.3", "eslint-config-prettier": "^8.5.0", - "eslint-plugin-formatjs": "^3.0.0", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-prettier": "^4.0.0", - "eslint-plugin-react": "^7.29.3", - "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-formatjs": "^4.0.2", + "eslint-plugin-jsx-a11y": "^6.6.1", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-react": "^7.30.1", + "eslint-plugin-react-hooks": "^4.6.0", "extract-react-intl-messages": "^4.1.1", "husky": "^7.0.4", "lint-staged": "^12.3.5", "nodemon": "^2.0.15", "postcss": "^8.4.8", - "prettier": "^2.5.1", - "prettier-plugin-tailwindcss": "^0.1.8", + "prettier": "^2.7.1", + "prettier-plugin-organize-imports": "^3.0.1", + "prettier-plugin-tailwindcss": "^0.1.13", "semantic-release": "^19.0.2", "semantic-release-docker-buildx": "^1.0.1", - "tailwindcss": "^3.0.23", + "tailwindcss": "^3.1.7", "ts-node": "^10.7.0", - "typescript": "^4.6.2" + "typescript": "^4.7.4" }, "resolutions": { "sqlite3/node-gyp": "^8.4.1" diff --git a/server/api/externalapi.ts b/server/api/externalapi.ts index d39701008..cc1e429ff 100644 --- a/server/api/externalapi.ts +++ b/server/api/externalapi.ts @@ -1,6 +1,7 @@ -import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'; +import type { AxiosInstance, AxiosRequestConfig } from 'axios'; +import axios from 'axios'; import rateLimit from 'axios-rate-limit'; -import NodeCache from 'node-cache'; +import type NodeCache from 'node-cache'; // 5 minute default TTL (in seconds) const DEFAULT_TTL = 300; diff --git a/server/api/plexapi.ts b/server/api/plexapi.ts index 73278387a..0fbfa8091 100644 --- a/server/api/plexapi.ts +++ b/server/api/plexapi.ts @@ -1,5 +1,6 @@ import NodePlexAPI from 'plex-api'; -import { getSettings, Library, PlexSettings } from '../lib/settings'; +import type { Library, PlexSettings } from '../lib/settings'; +import { getSettings } from '../lib/settings'; import logger from '../logger'; export interface PlexLibraryItem { @@ -130,7 +131,6 @@ class PlexAPI { }); } - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types public async getStatus() { return await this.plexClient.query('/'); } diff --git a/server/api/plextv.ts b/server/api/plextv.ts index 1733a85a6..a90957551 100644 --- a/server/api/plextv.ts +++ b/server/api/plextv.ts @@ -1,6 +1,7 @@ -import axios, { AxiosInstance } from 'axios'; +import type { AxiosInstance } from 'axios'; +import axios from 'axios'; import xml2js from 'xml2js'; -import { PlexDevice } from '../interfaces/api/plexInterfaces'; +import type { PlexDevice } from '../interfaces/api/plexInterfaces'; import { getSettings } from '../lib/settings'; import logger from '../logger'; diff --git a/server/api/servarr/base.ts b/server/api/servarr/base.ts index 9e4559339..9c8baeb5a 100644 --- a/server/api/servarr/base.ts +++ b/server/api/servarr/base.ts @@ -1,5 +1,6 @@ -import cacheManager, { AvailableCacheIds } from '../../lib/cache'; -import { DVRSettings } from '../../lib/settings'; +import type { AvailableCacheIds } from '../../lib/cache'; +import cacheManager from '../../lib/cache'; +import type { DVRSettings } from '../../lib/settings'; import ExternalAPI from '../externalapi'; export interface SystemStatus { diff --git a/server/api/tautulli.ts b/server/api/tautulli.ts index bb7f37235..deac60c95 100644 --- a/server/api/tautulli.ts +++ b/server/api/tautulli.ts @@ -1,7 +1,8 @@ -import axios, { AxiosInstance } from 'axios'; +import type { AxiosInstance } from 'axios'; +import axios from 'axios'; import { uniqWith } from 'lodash'; -import { User } from '../entity/User'; -import { TautulliSettings } from '../lib/settings'; +import type { User } from '../entity/User'; +import type { TautulliSettings } from '../lib/settings'; import logger from '../logger'; export interface TautulliHistoryRecord { diff --git a/server/api/themoviedb/index.ts b/server/api/themoviedb/index.ts index 69d26d5f9..7be7cd96f 100644 --- a/server/api/themoviedb/index.ts +++ b/server/api/themoviedb/index.ts @@ -1,7 +1,7 @@ import { sortBy } from 'lodash'; import cacheManager from '../../lib/cache'; import ExternalAPI from '../externalapi'; -import { +import type { TmdbCollection, TmdbExternalIdResponse, TmdbGenre, diff --git a/server/entity/Issue.ts b/server/entity/Issue.ts index d8e05c565..4d168135e 100644 --- a/server/entity/Issue.ts +++ b/server/entity/Issue.ts @@ -7,7 +7,8 @@ import { PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; -import { IssueStatus, IssueType } from '../constants/issue'; +import type { IssueType } from '../constants/issue'; +import { IssueStatus } from '../constants/issue'; import IssueComment from './IssueComment'; import Media from './Media'; import { User } from './User'; diff --git a/server/entity/Media.ts b/server/entity/Media.ts index 4de7698b5..166fb51fa 100644 --- a/server/entity/Media.ts +++ b/server/entity/Media.ts @@ -13,7 +13,8 @@ import { import RadarrAPI from '../api/servarr/radarr'; import SonarrAPI from '../api/servarr/sonarr'; import { MediaStatus, MediaType } from '../constants/media'; -import downloadTracker, { DownloadingItem } from '../lib/downloadtracker'; +import type { DownloadingItem } from '../lib/downloadtracker'; +import downloadTracker from '../lib/downloadtracker'; import { getSettings } from '../lib/settings'; import logger from '../logger'; import Issue from './Issue'; diff --git a/server/entity/MediaRequest.ts b/server/entity/MediaRequest.ts index f7f821156..eac3eae01 100644 --- a/server/entity/MediaRequest.ts +++ b/server/entity/MediaRequest.ts @@ -13,11 +13,10 @@ import { RelationCount, UpdateDateColumn, } from 'typeorm'; -import RadarrAPI, { RadarrMovieOptions } from '../api/servarr/radarr'; -import SonarrAPI, { - AddSeriesOptions, - SonarrSeries, -} from '../api/servarr/sonarr'; +import type { RadarrMovieOptions } from '../api/servarr/radarr'; +import RadarrAPI from '../api/servarr/radarr'; +import type { AddSeriesOptions, SonarrSeries } from '../api/servarr/sonarr'; +import SonarrAPI from '../api/servarr/sonarr'; import TheMovieDb from '../api/themoviedb'; import { ANIME_KEYWORD_ID } from '../api/themoviedb/constants'; import { MediaRequestStatus, MediaStatus, MediaType } from '../constants/media'; diff --git a/server/entity/Session.ts b/server/entity/Session.ts index e7462c195..a0b10db0f 100644 --- a/server/entity/Session.ts +++ b/server/entity/Session.ts @@ -1,4 +1,4 @@ -import { ISession } from 'connect-typeorm'; +import type { ISession } from 'connect-typeorm'; import { Index, Column, PrimaryColumn, Entity } from 'typeorm'; @Entity() diff --git a/server/entity/User.ts b/server/entity/User.ts index d54e31ae5..7276810a2 100644 --- a/server/entity/User.ts +++ b/server/entity/User.ts @@ -18,13 +18,10 @@ import { } from 'typeorm'; import { MediaRequestStatus, MediaType } from '../constants/media'; import { UserType } from '../constants/user'; -import { QuotaResponse } from '../interfaces/api/userInterfaces'; +import type { QuotaResponse } from '../interfaces/api/userInterfaces'; import PreparedEmail from '../lib/email'; -import { - hasPermission, - Permission, - PermissionCheckOptions, -} from '../lib/permissions'; +import type { PermissionCheckOptions } from '../lib/permissions'; +import { hasPermission, Permission } from '../lib/permissions'; import { getSettings } from '../lib/settings'; import logger from '../logger'; import Issue from './Issue'; diff --git a/server/entity/UserSettings.ts b/server/entity/UserSettings.ts index 08397b12f..fb738c59c 100644 --- a/server/entity/UserSettings.ts +++ b/server/entity/UserSettings.ts @@ -5,7 +5,7 @@ import { OneToOne, PrimaryGeneratedColumn, } from 'typeorm'; -import { NotificationAgentTypes } from '../interfaces/api/userSettingsInterfaces'; +import type { NotificationAgentTypes } from '../interfaces/api/userSettingsInterfaces'; import { hasNotificationType, Notification } from '../lib/notifications'; import { NotificationAgentKey } from '../lib/settings'; import { User } from './User'; diff --git a/server/index.ts b/server/index.ts index c80530120..fb3cb0b10 100644 --- a/server/index.ts +++ b/server/index.ts @@ -2,9 +2,11 @@ import { getClientIp } from '@supercharge/request-ip'; import { TypeormStore } from 'connect-typeorm/out'; import cookieParser from 'cookie-parser'; import csurf from 'csurf'; -import express, { NextFunction, Request, Response } from 'express'; +import type { NextFunction, Request, Response } from 'express'; +import express from 'express'; import * as OpenApiValidator from 'express-openapi-validator'; -import session, { Store } from 'express-session'; +import type { Store } from 'express-session'; +import session from 'express-session'; import next from 'next'; import path from 'path'; import swaggerUi from 'swagger-ui-express'; diff --git a/server/interfaces/api/issueInterfaces.ts b/server/interfaces/api/issueInterfaces.ts index bd17f1958..d86d9a4db 100644 --- a/server/interfaces/api/issueInterfaces.ts +++ b/server/interfaces/api/issueInterfaces.ts @@ -1,5 +1,5 @@ -import Issue from '../../entity/Issue'; -import { PaginatedResponse } from './common'; +import type Issue from '../../entity/Issue'; +import type { PaginatedResponse } from './common'; export interface IssueResultsResponse extends PaginatedResponse { results: Issue[]; diff --git a/server/interfaces/api/mediaInterfaces.ts b/server/interfaces/api/mediaInterfaces.ts index d17716d20..b322fd710 100644 --- a/server/interfaces/api/mediaInterfaces.ts +++ b/server/interfaces/api/mediaInterfaces.ts @@ -1,6 +1,6 @@ import type Media from '../../entity/Media'; -import { User } from '../../entity/User'; -import { PaginatedResponse } from './common'; +import type { User } from '../../entity/User'; +import type { PaginatedResponse } from './common'; export interface MediaResultsResponse extends PaginatedResponse { results: Media[]; diff --git a/server/interfaces/api/personInterfaces.ts b/server/interfaces/api/personInterfaces.ts index 19d3468ce..09f86ae25 100644 --- a/server/interfaces/api/personInterfaces.ts +++ b/server/interfaces/api/personInterfaces.ts @@ -1,4 +1,4 @@ -import { PersonCreditCast, PersonCreditCrew } from '../../models/Person'; +import type { PersonCreditCast, PersonCreditCrew } from '../../models/Person'; export interface PersonCombinedCreditsResponse { id: number; diff --git a/server/interfaces/api/plexInterfaces.ts b/server/interfaces/api/plexInterfaces.ts index 5373cb58a..0418ad4bf 100644 --- a/server/interfaces/api/plexInterfaces.ts +++ b/server/interfaces/api/plexInterfaces.ts @@ -1,4 +1,4 @@ -import { PlexSettings } from '../../lib/settings'; +import type { PlexSettings } from '../../lib/settings'; export interface PlexStatus { settings: PlexSettings; diff --git a/server/interfaces/api/serviceInterfaces.ts b/server/interfaces/api/serviceInterfaces.ts index 1188f24c0..8ea52de98 100644 --- a/server/interfaces/api/serviceInterfaces.ts +++ b/server/interfaces/api/serviceInterfaces.ts @@ -1,5 +1,5 @@ -import { QualityProfile, RootFolder, Tag } from '../../api/servarr/base'; -import { LanguageProfile } from '../../api/servarr/sonarr'; +import type { QualityProfile, RootFolder, Tag } from '../../api/servarr/base'; +import type { LanguageProfile } from '../../api/servarr/sonarr'; export interface ServiceCommonServer { id: number; diff --git a/server/interfaces/api/userInterfaces.ts b/server/interfaces/api/userInterfaces.ts index e5f564826..9ab7d4148 100644 --- a/server/interfaces/api/userInterfaces.ts +++ b/server/interfaces/api/userInterfaces.ts @@ -1,7 +1,7 @@ -import Media from '../../entity/Media'; -import { MediaRequest } from '../../entity/MediaRequest'; +import type Media from '../../entity/Media'; +import type { MediaRequest } from '../../entity/MediaRequest'; import type { User } from '../../entity/User'; -import { PaginatedResponse } from './common'; +import type { PaginatedResponse } from './common'; export interface UserResultsResponse extends PaginatedResponse { results: User[]; diff --git a/server/interfaces/api/userSettingsInterfaces.ts b/server/interfaces/api/userSettingsInterfaces.ts index a3e132d65..de7888b24 100644 --- a/server/interfaces/api/userSettingsInterfaces.ts +++ b/server/interfaces/api/userSettingsInterfaces.ts @@ -1,4 +1,4 @@ -import { NotificationAgentKey } from '../../lib/settings'; +import type { NotificationAgentKey } from '../../lib/settings'; export interface UserSettingsGeneralResponse { username?: string; diff --git a/server/job/schedule.ts b/server/job/schedule.ts index 568b28c97..9697a00cc 100644 --- a/server/job/schedule.ts +++ b/server/job/schedule.ts @@ -3,7 +3,8 @@ import downloadTracker from '../lib/downloadtracker'; import { plexFullScanner, plexRecentScanner } from '../lib/scanners/plex'; import { radarrScanner } from '../lib/scanners/radarr'; import { sonarrScanner } from '../lib/scanners/sonarr'; -import { getSettings, JobId } from '../lib/settings'; +import type { JobId } from '../lib/settings'; +import { getSettings } from '../lib/settings'; import logger from '../logger'; interface ScheduledJob { diff --git a/server/lib/email/index.ts b/server/lib/email/index.ts index 1274d6a8b..0ed74b9c3 100644 --- a/server/lib/email/index.ts +++ b/server/lib/email/index.ts @@ -1,7 +1,8 @@ import Email from 'email-templates'; import nodemailer from 'nodemailer'; import { URL } from 'url'; -import { getSettings, NotificationAgentEmail } from '../settings'; +import type { NotificationAgentEmail } from '../settings'; +import { getSettings } from '../settings'; import { openpgpEncrypt } from './openpgpEncrypt'; class PreparedEmail extends Email { diff --git a/server/lib/email/openpgpEncrypt.ts b/server/lib/email/openpgpEncrypt.ts index c067a7d58..585f870bf 100644 --- a/server/lib/email/openpgpEncrypt.ts +++ b/server/lib/email/openpgpEncrypt.ts @@ -1,6 +1,7 @@ import { randomBytes } from 'crypto'; import * as openpgp from 'openpgp'; -import { Transform, TransformCallback } from 'stream'; +import type { TransformCallback } from 'stream'; +import { Transform } from 'stream'; import logger from '../../logger'; interface EncryptorOptions { diff --git a/server/lib/notifications/agents/agent.ts b/server/lib/notifications/agents/agent.ts index edfa1262d..364dce86c 100644 --- a/server/lib/notifications/agents/agent.ts +++ b/server/lib/notifications/agents/agent.ts @@ -1,10 +1,10 @@ -import { Notification } from '..'; +import type { Notification } from '..'; import type Issue from '../../../entity/Issue'; -import IssueComment from '../../../entity/IssueComment'; -import Media from '../../../entity/Media'; -import { MediaRequest } from '../../../entity/MediaRequest'; -import { User } from '../../../entity/User'; -import { NotificationAgentConfig } from '../../settings'; +import type IssueComment from '../../../entity/IssueComment'; +import type Media from '../../../entity/Media'; +import type { MediaRequest } from '../../../entity/MediaRequest'; +import type { User } from '../../../entity/User'; +import type { NotificationAgentConfig } from '../../settings'; export interface NotificationPayload { event?: string; diff --git a/server/lib/notifications/agents/discord.ts b/server/lib/notifications/agents/discord.ts index 321200350..b71bc425d 100644 --- a/server/lib/notifications/agents/discord.ts +++ b/server/lib/notifications/agents/discord.ts @@ -8,12 +8,10 @@ import { import { IssueStatus, IssueTypeName } from '../../../constants/issue'; import { User } from '../../../entity/User'; import logger from '../../../logger'; -import { - getSettings, - NotificationAgentDiscord, - NotificationAgentKey, -} from '../../settings'; -import { BaseAgent, NotificationAgent, NotificationPayload } from './agent'; +import type { NotificationAgentDiscord } from '../../settings'; +import { getSettings, NotificationAgentKey } from '../../settings'; +import type { NotificationAgent, NotificationPayload } from './agent'; +import { BaseAgent } from './agent'; enum EmbedColors { DEFAULT = 0, diff --git a/server/lib/notifications/agents/email.ts b/server/lib/notifications/agents/email.ts index a1dd7e4e4..ebc88772e 100644 --- a/server/lib/notifications/agents/email.ts +++ b/server/lib/notifications/agents/email.ts @@ -1,4 +1,4 @@ -import { EmailOptions } from 'email-templates'; +import type { EmailOptions } from 'email-templates'; import path from 'path'; import { getRepository } from 'typeorm'; import { Notification, shouldSendAdminNotification } from '..'; @@ -7,12 +7,10 @@ import { MediaType } from '../../../constants/media'; import { User } from '../../../entity/User'; import logger from '../../../logger'; import PreparedEmail from '../../email'; -import { - getSettings, - NotificationAgentEmail, - NotificationAgentKey, -} from '../../settings'; -import { BaseAgent, NotificationAgent, NotificationPayload } from './agent'; +import type { NotificationAgentEmail } from '../../settings'; +import { getSettings, NotificationAgentKey } from '../../settings'; +import type { NotificationAgent, NotificationPayload } from './agent'; +import { BaseAgent } from './agent'; class EmailAgent extends BaseAgent diff --git a/server/lib/notifications/agents/gotify.ts b/server/lib/notifications/agents/gotify.ts index ecd54ce75..c0e4024eb 100644 --- a/server/lib/notifications/agents/gotify.ts +++ b/server/lib/notifications/agents/gotify.ts @@ -2,8 +2,10 @@ import axios from 'axios'; import { hasNotificationType, Notification } from '..'; import { IssueStatus, IssueTypeName } from '../../../constants/issue'; import logger from '../../../logger'; -import { getSettings, NotificationAgentGotify } from '../../settings'; -import { BaseAgent, NotificationAgent, NotificationPayload } from './agent'; +import type { NotificationAgentGotify } from '../../settings'; +import { getSettings } from '../../settings'; +import type { NotificationAgent, NotificationPayload } from './agent'; +import { BaseAgent } from './agent'; interface GotifyPayload { title: string; diff --git a/server/lib/notifications/agents/lunasea.ts b/server/lib/notifications/agents/lunasea.ts index 0269e2600..efa89f064 100644 --- a/server/lib/notifications/agents/lunasea.ts +++ b/server/lib/notifications/agents/lunasea.ts @@ -3,8 +3,10 @@ import { hasNotificationType, Notification } from '..'; import { IssueStatus, IssueType } from '../../../constants/issue'; import { MediaStatus } from '../../../constants/media'; import logger from '../../../logger'; -import { getSettings, NotificationAgentLunaSea } from '../../settings'; -import { BaseAgent, NotificationAgent, NotificationPayload } from './agent'; +import type { NotificationAgentLunaSea } from '../../settings'; +import { getSettings } from '../../settings'; +import type { NotificationAgent, NotificationPayload } from './agent'; +import { BaseAgent } from './agent'; class LunaSeaAgent extends BaseAgent diff --git a/server/lib/notifications/agents/pushbullet.ts b/server/lib/notifications/agents/pushbullet.ts index b7bc1919f..7474620bc 100644 --- a/server/lib/notifications/agents/pushbullet.ts +++ b/server/lib/notifications/agents/pushbullet.ts @@ -8,12 +8,10 @@ import { import { IssueStatus, IssueTypeName } from '../../../constants/issue'; import { User } from '../../../entity/User'; import logger from '../../../logger'; -import { - getSettings, - NotificationAgentKey, - NotificationAgentPushbullet, -} from '../../settings'; -import { BaseAgent, NotificationAgent, NotificationPayload } from './agent'; +import type { NotificationAgentPushbullet } from '../../settings'; +import { getSettings, NotificationAgentKey } from '../../settings'; +import type { NotificationAgent, NotificationPayload } from './agent'; +import { BaseAgent } from './agent'; interface PushbulletPayload { type: string; diff --git a/server/lib/notifications/agents/pushover.ts b/server/lib/notifications/agents/pushover.ts index f8364c3f2..2a002432e 100644 --- a/server/lib/notifications/agents/pushover.ts +++ b/server/lib/notifications/agents/pushover.ts @@ -8,12 +8,10 @@ import { import { IssueStatus, IssueTypeName } from '../../../constants/issue'; import { User } from '../../../entity/User'; import logger from '../../../logger'; -import { - getSettings, - NotificationAgentKey, - NotificationAgentPushover, -} from '../../settings'; -import { BaseAgent, NotificationAgent, NotificationPayload } from './agent'; +import type { NotificationAgentPushover } from '../../settings'; +import { getSettings, NotificationAgentKey } from '../../settings'; +import type { NotificationAgent, NotificationPayload } from './agent'; +import { BaseAgent } from './agent'; interface PushoverPayload { token: string; diff --git a/server/lib/notifications/agents/slack.ts b/server/lib/notifications/agents/slack.ts index ca10c269c..9caac7253 100644 --- a/server/lib/notifications/agents/slack.ts +++ b/server/lib/notifications/agents/slack.ts @@ -2,8 +2,10 @@ import axios from 'axios'; import { hasNotificationType, Notification } from '..'; import { IssueStatus, IssueTypeName } from '../../../constants/issue'; import logger from '../../../logger'; -import { getSettings, NotificationAgentSlack } from '../../settings'; -import { BaseAgent, NotificationAgent, NotificationPayload } from './agent'; +import type { NotificationAgentSlack } from '../../settings'; +import { getSettings } from '../../settings'; +import type { NotificationAgent, NotificationPayload } from './agent'; +import { BaseAgent } from './agent'; interface EmbedField { type: 'plain_text' | 'mrkdwn'; diff --git a/server/lib/notifications/agents/telegram.ts b/server/lib/notifications/agents/telegram.ts index 3450a3c2a..6ea2ee72f 100644 --- a/server/lib/notifications/agents/telegram.ts +++ b/server/lib/notifications/agents/telegram.ts @@ -8,12 +8,10 @@ import { import { IssueStatus, IssueTypeName } from '../../../constants/issue'; import { User } from '../../../entity/User'; import logger from '../../../logger'; -import { - getSettings, - NotificationAgentKey, - NotificationAgentTelegram, -} from '../../settings'; -import { BaseAgent, NotificationAgent, NotificationPayload } from './agent'; +import type { NotificationAgentTelegram } from '../../settings'; +import { getSettings, NotificationAgentKey } from '../../settings'; +import type { NotificationAgent, NotificationPayload } from './agent'; +import { BaseAgent } from './agent'; interface TelegramMessagePayload { text: string; diff --git a/server/lib/notifications/agents/webhook.ts b/server/lib/notifications/agents/webhook.ts index ba2bf5e59..b5673f81c 100644 --- a/server/lib/notifications/agents/webhook.ts +++ b/server/lib/notifications/agents/webhook.ts @@ -4,8 +4,10 @@ import { hasNotificationType, Notification } from '..'; import { IssueStatus, IssueType } from '../../../constants/issue'; import { MediaStatus } from '../../../constants/media'; import logger from '../../../logger'; -import { getSettings, NotificationAgentWebhook } from '../../settings'; -import { BaseAgent, NotificationAgent, NotificationPayload } from './agent'; +import type { NotificationAgentWebhook } from '../../settings'; +import { getSettings } from '../../settings'; +import type { NotificationAgent, NotificationPayload } from './agent'; +import { BaseAgent } from './agent'; type KeyMapFunction = ( payload: NotificationPayload, diff --git a/server/lib/notifications/agents/webpush.ts b/server/lib/notifications/agents/webpush.ts index c87d9496c..64090d415 100644 --- a/server/lib/notifications/agents/webpush.ts +++ b/server/lib/notifications/agents/webpush.ts @@ -6,12 +6,10 @@ import { MediaType } from '../../../constants/media'; import { User } from '../../../entity/User'; import { UserPushSubscription } from '../../../entity/UserPushSubscription'; import logger from '../../../logger'; -import { - getSettings, - NotificationAgentConfig, - NotificationAgentKey, -} from '../../settings'; -import { BaseAgent, NotificationAgent, NotificationPayload } from './agent'; +import type { NotificationAgentConfig } from '../../settings'; +import { getSettings, NotificationAgentKey } from '../../settings'; +import type { NotificationAgent, NotificationPayload } from './agent'; +import { BaseAgent } from './agent'; interface PushNotificationPayload { notificationType: string; diff --git a/server/lib/notifications/index.ts b/server/lib/notifications/index.ts index b8111d02f..fe87fcc8d 100644 --- a/server/lib/notifications/index.ts +++ b/server/lib/notifications/index.ts @@ -1,4 +1,4 @@ -import { User } from '../../entity/User'; +import type { User } from '../../entity/User'; import logger from '../../logger'; import { Permission } from '../permissions'; import type { NotificationAgent, NotificationPayload } from './agents/agent'; diff --git a/server/lib/scanners/plex/index.ts b/server/lib/scanners/plex/index.ts index cd8dbd76a..463588f46 100644 --- a/server/lib/scanners/plex/index.ts +++ b/server/lib/scanners/plex/index.ts @@ -1,17 +1,20 @@ import { uniqWith } from 'lodash'; import { getRepository } from 'typeorm'; import animeList from '../../../api/animelist'; -import PlexAPI, { PlexLibraryItem, PlexMetadata } from '../../../api/plexapi'; -import { TmdbTvDetails } from '../../../api/themoviedb/interfaces'; +import type { PlexLibraryItem, PlexMetadata } from '../../../api/plexapi'; +import PlexAPI from '../../../api/plexapi'; +import type { TmdbTvDetails } from '../../../api/themoviedb/interfaces'; import { User } from '../../../entity/User'; import cacheManager from '../../cache'; -import { getSettings, Library } from '../../settings'; -import BaseScanner, { +import type { Library } from '../../settings'; +import { getSettings } from '../../settings'; +import type { MediaIds, ProcessableSeason, RunnableScanner, StatusBase, } from '../baseScanner'; +import BaseScanner from '../baseScanner'; const imdbRegex = new RegExp(/imdb:\/\/(tt[0-9]+)/); const tmdbRegex = new RegExp(/tmdb:\/\/([0-9]+)/); diff --git a/server/lib/scanners/radarr/index.ts b/server/lib/scanners/radarr/index.ts index 5f47b9d97..89f937e56 100644 --- a/server/lib/scanners/radarr/index.ts +++ b/server/lib/scanners/radarr/index.ts @@ -1,7 +1,10 @@ import { uniqWith } from 'lodash'; -import RadarrAPI, { RadarrMovie } from '../../../api/servarr/radarr'; -import { getSettings, RadarrSettings } from '../../settings'; -import BaseScanner, { RunnableScanner, StatusBase } from '../baseScanner'; +import type { RadarrMovie } from '../../../api/servarr/radarr'; +import RadarrAPI from '../../../api/servarr/radarr'; +import type { RadarrSettings } from '../../settings'; +import { getSettings } from '../../settings'; +import type { RunnableScanner, StatusBase } from '../baseScanner'; +import BaseScanner from '../baseScanner'; type SyncStatus = StatusBase & { currentServer: RadarrSettings; diff --git a/server/lib/scanners/sonarr/index.ts b/server/lib/scanners/sonarr/index.ts index 044f74ec7..851196546 100644 --- a/server/lib/scanners/sonarr/index.ts +++ b/server/lib/scanners/sonarr/index.ts @@ -1,14 +1,17 @@ import { uniqWith } from 'lodash'; import { getRepository } from 'typeorm'; -import SonarrAPI, { SonarrSeries } from '../../../api/servarr/sonarr'; -import { TmdbTvDetails } from '../../../api/themoviedb/interfaces'; +import type { SonarrSeries } from '../../../api/servarr/sonarr'; +import SonarrAPI from '../../../api/servarr/sonarr'; +import type { TmdbTvDetails } from '../../../api/themoviedb/interfaces'; import Media from '../../../entity/Media'; -import { getSettings, SonarrSettings } from '../../settings'; -import BaseScanner, { +import type { SonarrSettings } from '../../settings'; +import { getSettings } from '../../settings'; +import type { ProcessableSeason, RunnableScanner, StatusBase, } from '../baseScanner'; +import BaseScanner from '../baseScanner'; type SyncStatus = StatusBase & { currentServer: SonarrSettings; diff --git a/server/lib/search.ts b/server/lib/search.ts index c625f512d..7cd914aca 100644 --- a/server/lib/search.ts +++ b/server/lib/search.ts @@ -1,5 +1,5 @@ import TheMovieDb from '../api/themoviedb'; -import { +import type { TmdbMovieDetails, TmdbMovieResult, TmdbPersonDetails, diff --git a/server/middleware/auth.ts b/server/middleware/auth.ts index 68869222f..a8ff6c550 100644 --- a/server/middleware/auth.ts +++ b/server/middleware/auth.ts @@ -1,6 +1,6 @@ import { getRepository } from 'typeorm'; import { User } from '../entity/User'; -import { Permission, PermissionCheckOptions } from '../lib/permissions'; +import type { Permission, PermissionCheckOptions } from '../lib/permissions'; import { getSettings } from '../lib/settings'; export const checkUser: Middleware = async (req, _res, next) => { diff --git a/server/migration/1603944374840-InitialMigration.ts b/server/migration/1603944374840-InitialMigration.ts index 73640565c..db71471ae 100644 --- a/server/migration/1603944374840-InitialMigration.ts +++ b/server/migration/1603944374840-InitialMigration.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class InitialMigration1603944374840 implements MigrationInterface { name = 'InitialMigration1603944374840'; diff --git a/server/migration/1605085519544-SeasonStatus.ts b/server/migration/1605085519544-SeasonStatus.ts index bcff6f609..059c6bf51 100644 --- a/server/migration/1605085519544-SeasonStatus.ts +++ b/server/migration/1605085519544-SeasonStatus.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class SeasonStatus1605085519544 implements MigrationInterface { name = 'SeasonStatus1605085519544'; diff --git a/server/migration/1606730060700-CascadeMigration.ts b/server/migration/1606730060700-CascadeMigration.ts index 341bc00b3..3b1ae0702 100644 --- a/server/migration/1606730060700-CascadeMigration.ts +++ b/server/migration/1606730060700-CascadeMigration.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class CascadeMigration1606730060700 implements MigrationInterface { name = 'CascadeMigration1606730060700'; diff --git a/server/migration/1607928251245-DropImdbIdConstraint.ts b/server/migration/1607928251245-DropImdbIdConstraint.ts index 97baa861a..f602ea7fa 100644 --- a/server/migration/1607928251245-DropImdbIdConstraint.ts +++ b/server/migration/1607928251245-DropImdbIdConstraint.ts @@ -1,4 +1,5 @@ -import { MigrationInterface, QueryRunner, TableUnique } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; +import { TableUnique } from 'typeorm'; export class DropImdbIdConstraint1607928251245 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { diff --git a/server/migration/1608217312474-AddUserRequestDeleteCascades.ts b/server/migration/1608217312474-AddUserRequestDeleteCascades.ts index e2aa88653..622a2c90e 100644 --- a/server/migration/1608217312474-AddUserRequestDeleteCascades.ts +++ b/server/migration/1608217312474-AddUserRequestDeleteCascades.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class AddUserRequestDeleteCascades1608219049304 implements MigrationInterface diff --git a/server/migration/1608477467935-AddLastSeasonChangeMedia.ts b/server/migration/1608477467935-AddLastSeasonChangeMedia.ts index fba7af7f3..e5ab02506 100644 --- a/server/migration/1608477467935-AddLastSeasonChangeMedia.ts +++ b/server/migration/1608477467935-AddLastSeasonChangeMedia.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class AddLastSeasonChangeMedia1608477467935 implements MigrationInterface diff --git a/server/migration/1608477467936-ForceDropImdbUniqueConstraint.ts b/server/migration/1608477467936-ForceDropImdbUniqueConstraint.ts index 6a109e4d1..d54c450e4 100644 --- a/server/migration/1608477467936-ForceDropImdbUniqueConstraint.ts +++ b/server/migration/1608477467936-ForceDropImdbUniqueConstraint.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class ForceDropImdbUniqueConstraint1608477467935 implements MigrationInterface diff --git a/server/migration/1609236552057-RemoveTmdbIdUniqueConstraint.ts b/server/migration/1609236552057-RemoveTmdbIdUniqueConstraint.ts index 2cd5415e7..500568927 100644 --- a/server/migration/1609236552057-RemoveTmdbIdUniqueConstraint.ts +++ b/server/migration/1609236552057-RemoveTmdbIdUniqueConstraint.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class RemoveTmdbIdUniqueConstraint1609236552057 implements MigrationInterface diff --git a/server/migration/1610070934506-LocalUsers.ts b/server/migration/1610070934506-LocalUsers.ts index 0ece00f4d..88b0ae607 100644 --- a/server/migration/1610070934506-LocalUsers.ts +++ b/server/migration/1610070934506-LocalUsers.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class LocalUsers1610070934506 implements MigrationInterface { name = 'LocalUsers1610070934506'; diff --git a/server/migration/1610370640747-Add4kStatusFields.ts b/server/migration/1610370640747-Add4kStatusFields.ts index a313bf135..5502b9c0f 100644 --- a/server/migration/1610370640747-Add4kStatusFields.ts +++ b/server/migration/1610370640747-Add4kStatusFields.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class Add4kStatusFields1610370640747 implements MigrationInterface { name = 'Add4kStatusFields1610370640747'; diff --git a/server/migration/1610522845513-AddMediaAddedFieldToMedia.ts b/server/migration/1610522845513-AddMediaAddedFieldToMedia.ts index 25e42a74e..d6574d396 100644 --- a/server/migration/1610522845513-AddMediaAddedFieldToMedia.ts +++ b/server/migration/1610522845513-AddMediaAddedFieldToMedia.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class AddMediaAddedFieldToMedia1610522845513 implements MigrationInterface diff --git a/server/migration/1611508672722-AddDisplayNameToUser.ts b/server/migration/1611508672722-AddDisplayNameToUser.ts index cacea0597..6a36f29a9 100644 --- a/server/migration/1611508672722-AddDisplayNameToUser.ts +++ b/server/migration/1611508672722-AddDisplayNameToUser.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class AddDisplayNameToUser1611508672722 implements MigrationInterface { name = 'AddDisplayNameToUser1611508672722'; diff --git a/server/migration/1611757511674-SonarrRadarrSyncServiceFields.ts b/server/migration/1611757511674-SonarrRadarrSyncServiceFields.ts index 355384a05..5a5b65533 100644 --- a/server/migration/1611757511674-SonarrRadarrSyncServiceFields.ts +++ b/server/migration/1611757511674-SonarrRadarrSyncServiceFields.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class SonarrRadarrSyncServiceFields1611757511674 implements MigrationInterface diff --git a/server/migration/1611801511397-AddRatingKeysToMedia.ts b/server/migration/1611801511397-AddRatingKeysToMedia.ts index f9865c8f5..92ab4d4b4 100644 --- a/server/migration/1611801511397-AddRatingKeysToMedia.ts +++ b/server/migration/1611801511397-AddRatingKeysToMedia.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class AddRatingKeysToMedia1611801511397 implements MigrationInterface { name = 'AddRatingKeysToMedia1611801511397'; diff --git a/server/migration/1612482778137-AddResetPasswordGuidAndExpiryDate.ts b/server/migration/1612482778137-AddResetPasswordGuidAndExpiryDate.ts index 7d191d106..55a20a390 100644 --- a/server/migration/1612482778137-AddResetPasswordGuidAndExpiryDate.ts +++ b/server/migration/1612482778137-AddResetPasswordGuidAndExpiryDate.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class AddResetPasswordGuidAndExpiryDate1612482778137 implements MigrationInterface diff --git a/server/migration/1612571545781-AddLanguageProfileId.ts b/server/migration/1612571545781-AddLanguageProfileId.ts index fa89d81b7..7694f4e4f 100644 --- a/server/migration/1612571545781-AddLanguageProfileId.ts +++ b/server/migration/1612571545781-AddLanguageProfileId.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class AddLanguageProfileId1612571545781 implements MigrationInterface { name = 'AddLanguageProfileId1612571545781'; diff --git a/server/migration/1613615266968-CreateUserSettings.ts b/server/migration/1613615266968-CreateUserSettings.ts index 4d4a973e9..fbe85339c 100644 --- a/server/migration/1613615266968-CreateUserSettings.ts +++ b/server/migration/1613615266968-CreateUserSettings.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class CreateUserSettings1613615266968 implements MigrationInterface { name = 'CreateUserSettings1613615266968'; diff --git a/server/migration/1613955393450-UpdateUserSettingsRegions.ts b/server/migration/1613955393450-UpdateUserSettingsRegions.ts index d33df4eef..69060a0cb 100644 --- a/server/migration/1613955393450-UpdateUserSettingsRegions.ts +++ b/server/migration/1613955393450-UpdateUserSettingsRegions.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class UpdateUserSettingsRegions1613955393450 implements MigrationInterface diff --git a/server/migration/1614334195680-AddTelegramSettingsToUserSettings.ts b/server/migration/1614334195680-AddTelegramSettingsToUserSettings.ts index 5e480d481..6e2598ab4 100644 --- a/server/migration/1614334195680-AddTelegramSettingsToUserSettings.ts +++ b/server/migration/1614334195680-AddTelegramSettingsToUserSettings.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class AddTelegramSettingsToUserSettings1614334195680 implements MigrationInterface diff --git a/server/migration/1615333940450-AddPGPToUserSettings.ts b/server/migration/1615333940450-AddPGPToUserSettings.ts index b88e0dcaa..6940d4adc 100644 --- a/server/migration/1615333940450-AddPGPToUserSettings.ts +++ b/server/migration/1615333940450-AddPGPToUserSettings.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class AddPGPToUserSettings1615333940450 implements MigrationInterface { name = 'AddPGPToUserSettings1615333940450'; diff --git a/server/migration/1616576677254-AddUserQuotaFields.ts b/server/migration/1616576677254-AddUserQuotaFields.ts index 44947baba..e20c3d72d 100644 --- a/server/migration/1616576677254-AddUserQuotaFields.ts +++ b/server/migration/1616576677254-AddUserQuotaFields.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class AddUserQuotaFields1616576677254 implements MigrationInterface { name = 'AddUserQuotaFields1616576677254'; diff --git a/server/migration/1617624225464-CreateTagsFieldonMediaRequest.ts b/server/migration/1617624225464-CreateTagsFieldonMediaRequest.ts index d498a8b17..9e6761825 100644 --- a/server/migration/1617624225464-CreateTagsFieldonMediaRequest.ts +++ b/server/migration/1617624225464-CreateTagsFieldonMediaRequest.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class CreateTagsFieldonMediaRequest1617624225464 implements MigrationInterface diff --git a/server/migration/1617730837489-AddUserSettingsNotificationAgentsField.ts b/server/migration/1617730837489-AddUserSettingsNotificationAgentsField.ts index 79cd061b8..9dd9288e6 100644 --- a/server/migration/1617730837489-AddUserSettingsNotificationAgentsField.ts +++ b/server/migration/1617730837489-AddUserSettingsNotificationAgentsField.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class AddUserSettingsNotificationAgentsField1617730837489 implements MigrationInterface diff --git a/server/migration/1618912653565-CreateUserPushSubscriptions.ts b/server/migration/1618912653565-CreateUserPushSubscriptions.ts index 539221d17..970705990 100644 --- a/server/migration/1618912653565-CreateUserPushSubscriptions.ts +++ b/server/migration/1618912653565-CreateUserPushSubscriptions.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class CreateUserPushSubscriptions1618912653565 implements MigrationInterface diff --git a/server/migration/1619239659754-AddUserSettingsLocale.ts b/server/migration/1619239659754-AddUserSettingsLocale.ts index 9842bca71..ba182b03a 100644 --- a/server/migration/1619239659754-AddUserSettingsLocale.ts +++ b/server/migration/1619239659754-AddUserSettingsLocale.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class AddUserSettingsLocale1619239659754 implements MigrationInterface { name = 'AddUserSettingsLocale1619239659754'; diff --git a/server/migration/1619339817343-AddUserSettingsNotificationTypes.ts b/server/migration/1619339817343-AddUserSettingsNotificationTypes.ts index cccdae2fa..50de959b2 100644 --- a/server/migration/1619339817343-AddUserSettingsNotificationTypes.ts +++ b/server/migration/1619339817343-AddUserSettingsNotificationTypes.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class AddUserSettingsNotificationTypes1619339817343 implements MigrationInterface diff --git a/server/migration/1634904083966-AddIssues.ts b/server/migration/1634904083966-AddIssues.ts index 0c6116f9d..ebcf8d89d 100644 --- a/server/migration/1634904083966-AddIssues.ts +++ b/server/migration/1634904083966-AddIssues.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class AddIssues1634904083966 implements MigrationInterface { name = 'AddIssues1634904083966'; diff --git a/server/migration/1635079863457-AddPushbulletPushoverUserSettings.ts b/server/migration/1635079863457-AddPushbulletPushoverUserSettings.ts index 8934866fa..c29cef6d0 100644 --- a/server/migration/1635079863457-AddPushbulletPushoverUserSettings.ts +++ b/server/migration/1635079863457-AddPushbulletPushoverUserSettings.ts @@ -1,4 +1,4 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import type { MigrationInterface, QueryRunner } from 'typeorm'; export class AddPushbulletPushoverUserSettings1635079863457 implements MigrationInterface diff --git a/server/models/Collection.ts b/server/models/Collection.ts index 9cc4f3788..efaf449e3 100644 --- a/server/models/Collection.ts +++ b/server/models/Collection.ts @@ -1,8 +1,9 @@ import { sortBy } from 'lodash'; import type { TmdbCollection } from '../api/themoviedb/interfaces'; import { MediaType } from '../constants/media'; -import Media from '../entity/Media'; -import { mapMovieResult, MovieResult } from './Search'; +import type Media from '../entity/Media'; +import type { MovieResult } from './Search'; +import { mapMovieResult } from './Search'; export interface Collection { id: number; diff --git a/server/models/Movie.ts b/server/models/Movie.ts index ff8a96a47..9ae1761fd 100644 --- a/server/models/Movie.ts +++ b/server/models/Movie.ts @@ -3,19 +3,21 @@ import type { TmdbMovieReleaseResult, TmdbProductionCompany, } from '../api/themoviedb/interfaces'; -import Media from '../entity/Media'; -import { +import type Media from '../entity/Media'; +import type { Cast, Crew, ExternalIds, Genre, + ProductionCompany, + WatchProviders, +} from './common'; +import { mapCast, mapCrew, mapExternalIds, mapVideos, mapWatchProviders, - ProductionCompany, - WatchProviders, } from './common'; export interface Video { diff --git a/server/models/Person.ts b/server/models/Person.ts index 087ab1c7b..9137b266c 100644 --- a/server/models/Person.ts +++ b/server/models/Person.ts @@ -3,7 +3,7 @@ import type { TmdbPersonCreditCrew, TmdbPersonDetails, } from '../api/themoviedb/interfaces'; -import Media from '../entity/Media'; +import type Media from '../entity/Media'; export interface PersonDetails { id: number; diff --git a/server/models/Search.ts b/server/models/Search.ts index 73427a378..caeb003c3 100644 --- a/server/models/Search.ts +++ b/server/models/Search.ts @@ -7,7 +7,7 @@ import type { TmdbTvResult, } from '../api/themoviedb/interfaces'; import { MediaType as MainMediaType } from '../constants/media'; -import Media from '../entity/Media'; +import type Media from '../entity/Media'; export type MediaType = 'tv' | 'movie' | 'person'; diff --git a/server/models/Tv.ts b/server/models/Tv.ts index b596b1d2b..1159fc0b9 100644 --- a/server/models/Tv.ts +++ b/server/models/Tv.ts @@ -7,22 +7,24 @@ import type { TmdbTvSeasonResult, } from '../api/themoviedb/interfaces'; import type Media from '../entity/Media'; -import { +import type { Cast, Crew, ExternalIds, Genre, Keyword, + ProductionCompany, + TvNetwork, + WatchProviders, +} from './common'; +import { mapAggregateCast, mapCrew, mapExternalIds, mapVideos, mapWatchProviders, - ProductionCompany, - TvNetwork, - WatchProviders, } from './common'; -import { Video } from './Movie'; +import type { Video } from './Movie'; interface Episode { id: number; diff --git a/server/models/common.ts b/server/models/common.ts index 49e2305cb..9a7656fae 100644 --- a/server/models/common.ts +++ b/server/models/common.ts @@ -8,7 +8,7 @@ import type { TmdbWatchProviderDetails, TmdbWatchProviders, } from '../api/themoviedb/interfaces'; -import { Video } from '../models/Movie'; +import type { Video } from '../models/Movie'; export interface ProductionCompany { id: number; diff --git a/server/routes/discover.ts b/server/routes/discover.ts index ea78bf03d..e0b8f78ed 100644 --- a/server/routes/discover.ts +++ b/server/routes/discover.ts @@ -3,8 +3,8 @@ import { sortBy } from 'lodash'; import TheMovieDb from '../api/themoviedb'; import { MediaType } from '../constants/media'; import Media from '../entity/Media'; -import { User } from '../entity/User'; -import { GenreSliderItem } from '../interfaces/api/discoverInterfaces'; +import type { User } from '../entity/User'; +import type { GenreSliderItem } from '../interfaces/api/discoverInterfaces'; import { getSettings } from '../lib/settings'; import logger from '../logger'; import { mapProductionCompany } from '../models/Movie'; diff --git a/server/routes/index.ts b/server/routes/index.ts index e28666385..2be9533bb 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -1,8 +1,11 @@ import { Router } from 'express'; import GithubAPI from '../api/github'; import TheMovieDb from '../api/themoviedb'; -import { TmdbMovieResult, TmdbTvResult } from '../api/themoviedb/interfaces'; -import { StatusResponse } from '../interfaces/api/settingsInterfaces'; +import type { + TmdbMovieResult, + TmdbTvResult, +} from '../api/themoviedb/interfaces'; +import type { StatusResponse } from '../interfaces/api/settingsInterfaces'; import { Permission } from '../lib/permissions'; import { getSettings } from '../lib/settings'; import logger from '../logger'; diff --git a/server/routes/issue.ts b/server/routes/issue.ts index 07cf3277d..9058f574e 100644 --- a/server/routes/issue.ts +++ b/server/routes/issue.ts @@ -4,7 +4,7 @@ import { IssueStatus, IssueType } from '../constants/issue'; import Issue from '../entity/Issue'; import IssueComment from '../entity/IssueComment'; import Media from '../entity/Media'; -import { IssueResultsResponse } from '../interfaces/api/issueInterfaces'; +import type { IssueResultsResponse } from '../interfaces/api/issueInterfaces'; import { Permission } from '../lib/permissions'; import logger from '../logger'; import { isAuthenticated } from '../middleware/auth'; diff --git a/server/routes/media.ts b/server/routes/media.ts index 0c6e784df..becd384e2 100644 --- a/server/routes/media.ts +++ b/server/routes/media.ts @@ -1,10 +1,11 @@ import { Router } from 'express'; -import { FindOneOptions, FindOperator, getRepository, In } from 'typeorm'; +import type { FindOneOptions, FindOperator } from 'typeorm'; +import { getRepository, In } from 'typeorm'; import TautulliAPI from '../api/tautulli'; import { MediaStatus, MediaType } from '../constants/media'; import Media from '../entity/Media'; import { User } from '../entity/User'; -import { +import type { MediaResultsResponse, MediaWatchDataResponse, } from '../interfaces/api/mediaInterfaces'; diff --git a/server/routes/request.ts b/server/routes/request.ts index cd269f4ef..48a18c599 100644 --- a/server/routes/request.ts +++ b/server/routes/request.ts @@ -6,7 +6,7 @@ import Media from '../entity/Media'; import { MediaRequest } from '../entity/MediaRequest'; import SeasonRequest from '../entity/SeasonRequest'; import { User } from '../entity/User'; -import { RequestResultsResponse } from '../interfaces/api/requestInterfaces'; +import type { RequestResultsResponse } from '../interfaces/api/requestInterfaces'; import { Permission } from '../lib/permissions'; import logger from '../logger'; import { isAuthenticated } from '../middleware/auth'; diff --git a/server/routes/search.ts b/server/routes/search.ts index 3f26a3939..2e997432b 100644 --- a/server/routes/search.ts +++ b/server/routes/search.ts @@ -1,6 +1,6 @@ import { Router } from 'express'; import TheMovieDb from '../api/themoviedb'; -import { TmdbSearchMultiResponse } from '../api/themoviedb/interfaces'; +import type { TmdbSearchMultiResponse } from '../api/themoviedb/interfaces'; import Media from '../entity/Media'; import { findSearchProvider } from '../lib/search'; import logger from '../logger'; diff --git a/server/routes/service.ts b/server/routes/service.ts index 862ab3748..5c2fe969d 100644 --- a/server/routes/service.ts +++ b/server/routes/service.ts @@ -2,7 +2,7 @@ import { Router } from 'express'; import RadarrAPI from '../api/servarr/radarr'; import SonarrAPI from '../api/servarr/sonarr'; import TheMovieDb from '../api/themoviedb'; -import { +import type { ServiceCommonServer, ServiceCommonServerWithDetails, } from '../interfaces/api/serviceInterfaces'; diff --git a/server/routes/settings/index.ts b/server/routes/settings/index.ts index bd9c1164f..60267aaaf 100644 --- a/server/routes/settings/index.ts +++ b/server/routes/settings/index.ts @@ -13,17 +13,19 @@ import TautulliAPI from '../../api/tautulli'; import Media from '../../entity/Media'; import { MediaRequest } from '../../entity/MediaRequest'; import { User } from '../../entity/User'; -import { PlexConnection } from '../../interfaces/api/plexInterfaces'; -import { +import type { PlexConnection } from '../../interfaces/api/plexInterfaces'; +import type { LogMessage, LogsResultsResponse, SettingsAboutResponse, } from '../../interfaces/api/settingsInterfaces'; import { scheduledJobs } from '../../job/schedule'; -import cacheManager, { AvailableCacheIds } from '../../lib/cache'; +import type { AvailableCacheIds } from '../../lib/cache'; +import cacheManager from '../../lib/cache'; import { Permission } from '../../lib/permissions'; import { plexFullScanner } from '../../lib/scanners/plex'; -import { getSettings, MainSettings } from '../../lib/settings'; +import type { MainSettings } from '../../lib/settings'; +import { getSettings } from '../../lib/settings'; import logger from '../../logger'; import { isAuthenticated } from '../../middleware/auth'; import { appDataPath } from '../../utils/appDataVolume'; diff --git a/server/routes/settings/notifications.ts b/server/routes/settings/notifications.ts index 5a337237d..61c1a403d 100644 --- a/server/routes/settings/notifications.ts +++ b/server/routes/settings/notifications.ts @@ -1,7 +1,7 @@ import { Router } from 'express'; -import { User } from '../../entity/User'; +import type { User } from '../../entity/User'; import { Notification } from '../../lib/notifications'; -import { NotificationAgent } from '../../lib/notifications/agents/agent'; +import type { NotificationAgent } from '../../lib/notifications/agents/agent'; import DiscordAgent from '../../lib/notifications/agents/discord'; import EmailAgent from '../../lib/notifications/agents/email'; import GotifyAgent from '../../lib/notifications/agents/gotify'; diff --git a/server/routes/settings/radarr.ts b/server/routes/settings/radarr.ts index a33bfcdba..12e5d3484 100644 --- a/server/routes/settings/radarr.ts +++ b/server/routes/settings/radarr.ts @@ -1,6 +1,7 @@ import { Router } from 'express'; import RadarrAPI from '../../api/servarr/radarr'; -import { getSettings, RadarrSettings } from '../../lib/settings'; +import type { RadarrSettings } from '../../lib/settings'; +import { getSettings } from '../../lib/settings'; import logger from '../../logger'; const radarrRoutes = Router(); diff --git a/server/routes/settings/sonarr.ts b/server/routes/settings/sonarr.ts index da5a5bb3f..06656614f 100644 --- a/server/routes/settings/sonarr.ts +++ b/server/routes/settings/sonarr.ts @@ -1,6 +1,7 @@ import { Router } from 'express'; import SonarrAPI from '../../api/servarr/sonarr'; -import { getSettings, SonarrSettings } from '../../lib/settings'; +import type { SonarrSettings } from '../../lib/settings'; +import { getSettings } from '../../lib/settings'; import logger from '../../logger'; const sonarrRoutes = Router(); diff --git a/server/routes/user/index.ts b/server/routes/user/index.ts index a4e8861e5..b18932507 100644 --- a/server/routes/user/index.ts +++ b/server/routes/user/index.ts @@ -10,7 +10,7 @@ import Media from '../../entity/Media'; import { MediaRequest } from '../../entity/MediaRequest'; import { User } from '../../entity/User'; import { UserPushSubscription } from '../../entity/UserPushSubscription'; -import { +import type { QuotaResponse, UserRequestsResponse, UserResultsResponse, diff --git a/server/routes/user/usersettings.ts b/server/routes/user/usersettings.ts index 0c53c94a0..617092cd8 100644 --- a/server/routes/user/usersettings.ts +++ b/server/routes/user/usersettings.ts @@ -3,7 +3,7 @@ import { getRepository } from 'typeorm'; import { canMakePermissionsChange } from '.'; import { User } from '../../entity/User'; import { UserSettings } from '../../entity/UserSettings'; -import { +import type { UserSettingsGeneralResponse, UserSettingsNotificationsResponse, } from '../../interfaces/api/userSettingsInterfaces'; diff --git a/server/subscriber/IssueCommentSubscriber.ts b/server/subscriber/IssueCommentSubscriber.ts index 1b1b7b55c..62aea04ee 100644 --- a/server/subscriber/IssueCommentSubscriber.ts +++ b/server/subscriber/IssueCommentSubscriber.ts @@ -1,10 +1,6 @@ import { sortBy } from 'lodash'; -import { - EntitySubscriberInterface, - EventSubscriber, - getRepository, - InsertEvent, -} from 'typeorm'; +import type { EntitySubscriberInterface, InsertEvent } from 'typeorm'; +import { EventSubscriber, getRepository } from 'typeorm'; import TheMovieDb from '../api/themoviedb'; import { IssueType, IssueTypeName } from '../constants/issue'; import { MediaType } from '../constants/media'; diff --git a/server/subscriber/IssueSubscriber.ts b/server/subscriber/IssueSubscriber.ts index b593095cd..ab4347244 100644 --- a/server/subscriber/IssueSubscriber.ts +++ b/server/subscriber/IssueSubscriber.ts @@ -1,10 +1,10 @@ import { sortBy } from 'lodash'; -import { +import type { EntitySubscriberInterface, - EventSubscriber, InsertEvent, UpdateEvent, } from 'typeorm'; +import { EventSubscriber } from 'typeorm'; import TheMovieDb from '../api/themoviedb'; import { IssueStatus, IssueType, IssueTypeName } from '../constants/issue'; import { MediaType } from '../constants/media'; diff --git a/server/subscriber/MediaSubscriber.ts b/server/subscriber/MediaSubscriber.ts index 01752b0d1..dbfd76da3 100644 --- a/server/subscriber/MediaSubscriber.ts +++ b/server/subscriber/MediaSubscriber.ts @@ -1,11 +1,6 @@ import { truncate } from 'lodash'; -import { - EntitySubscriberInterface, - EventSubscriber, - getRepository, - Not, - UpdateEvent, -} from 'typeorm'; +import type { EntitySubscriberInterface, UpdateEvent } from 'typeorm'; +import { EventSubscriber, getRepository, Not } from 'typeorm'; import TheMovieDb from '../api/themoviedb'; import { MediaRequestStatus, MediaStatus, MediaType } from '../constants/media'; import Media from '../entity/Media'; diff --git a/src/components/Common/Button/index.tsx b/src/components/Common/Button/index.tsx index f1083e5b2..59196d16d 100644 --- a/src/components/Common/Button/index.tsx +++ b/src/components/Common/Button/index.tsx @@ -1,4 +1,5 @@ -import React, { ForwardedRef } from 'react'; +import type { ForwardedRef } from 'react'; +import React from 'react'; export type ButtonType = | 'default' diff --git a/src/components/Common/ButtonWithDropdown/index.tsx b/src/components/Common/ButtonWithDropdown/index.tsx index 6edb4a11f..d4825fb14 100644 --- a/src/components/Common/ButtonWithDropdown/index.tsx +++ b/src/components/Common/ButtonWithDropdown/index.tsx @@ -1,11 +1,10 @@ import { ChevronDownIcon } from '@heroicons/react/solid'; -import React, { +import type { AnchorHTMLAttributes, ButtonHTMLAttributes, ReactNode, - useRef, - useState, } from 'react'; +import React, { useRef, useState } from 'react'; import useClickOutside from '../../../hooks/useClickOutside'; import { withProperties } from '../../../utils/typeHelpers'; import Transition from '../../Transition'; diff --git a/src/components/Common/CachedImage/index.tsx b/src/components/Common/CachedImage/index.tsx index c91f09465..b52b6301a 100644 --- a/src/components/Common/CachedImage/index.tsx +++ b/src/components/Common/CachedImage/index.tsx @@ -1,4 +1,5 @@ -import Image, { ImageProps } from 'next/image'; +import type { ImageProps } from 'next/image'; +import Image from 'next/image'; import React from 'react'; import useSettings from '../../../hooks/useSettings'; diff --git a/src/components/Common/ImageFader/index.tsx b/src/components/Common/ImageFader/index.tsx index 5f68376c0..706f4f43e 100644 --- a/src/components/Common/ImageFader/index.tsx +++ b/src/components/Common/ImageFader/index.tsx @@ -1,9 +1,5 @@ -import React, { - ForwardRefRenderFunction, - HTMLAttributes, - useEffect, - useState, -} from 'react'; +import type { ForwardRefRenderFunction, HTMLAttributes } from 'react'; +import React, { useEffect, useState } from 'react'; import CachedImage from '../CachedImage'; interface ImageFaderProps extends HTMLAttributes { diff --git a/src/components/Common/ListView/index.tsx b/src/components/Common/ListView/index.tsx index 0c2a0e4ed..d373a4026 100644 --- a/src/components/Common/ListView/index.tsx +++ b/src/components/Common/ListView/index.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useIntl } from 'react-intl'; -import { +import type { MovieResult, PersonResult, TvResult, diff --git a/src/components/Common/Modal/index.tsx b/src/components/Common/Modal/index.tsx index ac8d9fea9..083a5fd7c 100644 --- a/src/components/Common/Modal/index.tsx +++ b/src/components/Common/Modal/index.tsx @@ -1,11 +1,13 @@ -import React, { MouseEvent, ReactNode, useRef } from 'react'; +import type { MouseEvent, ReactNode } from 'react'; +import React, { useRef } from 'react'; import ReactDOM from 'react-dom'; import { useIntl } from 'react-intl'; import useClickOutside from '../../../hooks/useClickOutside'; import { useLockBodyScroll } from '../../../hooks/useLockBodyScroll'; import globalMessages from '../../../i18n/globalMessages'; import Transition from '../../Transition'; -import Button, { ButtonType } from '../Button'; +import type { ButtonType } from '../Button'; +import Button from '../Button'; import CachedImage from '../CachedImage'; import LoadingSpinner from '../LoadingSpinner'; diff --git a/src/components/Common/PlayButton/index.tsx b/src/components/Common/PlayButton/index.tsx index c41935aee..eb87e6c07 100644 --- a/src/components/Common/PlayButton/index.tsx +++ b/src/components/Common/PlayButton/index.tsx @@ -1,4 +1,5 @@ -import React, { ReactNode } from 'react'; +import type { ReactNode } from 'react'; +import React from 'react'; import ButtonWithDropdown from '../ButtonWithDropdown'; interface PlayButtonProps { diff --git a/src/components/Common/SettingsTabs/index.tsx b/src/components/Common/SettingsTabs/index.tsx index 751587050..4d3522d0c 100644 --- a/src/components/Common/SettingsTabs/index.tsx +++ b/src/components/Common/SettingsTabs/index.tsx @@ -1,7 +1,8 @@ import Link from 'next/link'; import { useRouter } from 'next/router'; import React from 'react'; -import { hasPermission, Permission } from '../../../../server/lib/permissions'; +import type { Permission } from '../../../../server/lib/permissions'; +import { hasPermission } from '../../../../server/lib/permissions'; import { useUser } from '../../../hooks/useUser'; export interface SettingsRoute { diff --git a/src/components/Common/Table/index.tsx b/src/components/Common/Table/index.tsx index 9e0cb0ca5..6ef0ef790 100644 --- a/src/components/Common/Table/index.tsx +++ b/src/components/Common/Table/index.tsx @@ -1,4 +1,5 @@ -import React, { AllHTMLAttributes } from 'react'; +import type { AllHTMLAttributes } from 'react'; +import React from 'react'; import { withProperties } from '../../../utils/typeHelpers'; const TBody: React.FC = ({ children }) => { diff --git a/src/components/Discover/DiscoverNetwork/index.tsx b/src/components/Discover/DiscoverNetwork/index.tsx index 247c5ece7..9c14f18a8 100644 --- a/src/components/Discover/DiscoverNetwork/index.tsx +++ b/src/components/Discover/DiscoverNetwork/index.tsx @@ -8,7 +8,7 @@ import { useRouter } from 'next/router'; import globalMessages from '../../../i18n/globalMessages'; import useDiscover from '../../../hooks/useDiscover'; import Error from '../../../pages/_error'; -import { TvNetwork } from '../../../../server/models/common'; +import type { TvNetwork } from '../../../../server/models/common'; const messages = defineMessages({ networkSeries: '{network} Series', diff --git a/src/components/Discover/DiscoverStudio/index.tsx b/src/components/Discover/DiscoverStudio/index.tsx index b1f3b0662..f6cbe9b6e 100644 --- a/src/components/Discover/DiscoverStudio/index.tsx +++ b/src/components/Discover/DiscoverStudio/index.tsx @@ -8,7 +8,7 @@ import { useRouter } from 'next/router'; import globalMessages from '../../../i18n/globalMessages'; import useDiscover from '../../../hooks/useDiscover'; import Error from '../../../pages/_error'; -import { ProductionCompany } from '../../../../server/models/common'; +import type { ProductionCompany } from '../../../../server/models/common'; const messages = defineMessages({ studioMovies: '{studio} Movies', diff --git a/src/components/Discover/MovieGenreList/index.tsx b/src/components/Discover/MovieGenreList/index.tsx index bc85adad4..cc9e4c496 100644 --- a/src/components/Discover/MovieGenreList/index.tsx +++ b/src/components/Discover/MovieGenreList/index.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import useSWR from 'swr'; -import { GenreSliderItem } from '../../../../server/interfaces/api/discoverInterfaces'; +import type { GenreSliderItem } from '../../../../server/interfaces/api/discoverInterfaces'; import Error from '../../../pages/_error'; import Header from '../../Common/Header'; import LoadingSpinner from '../../Common/LoadingSpinner'; diff --git a/src/components/Discover/MovieGenreSlider/index.tsx b/src/components/Discover/MovieGenreSlider/index.tsx index cf1b8ce1f..1b1bcbae4 100644 --- a/src/components/Discover/MovieGenreSlider/index.tsx +++ b/src/components/Discover/MovieGenreSlider/index.tsx @@ -3,7 +3,7 @@ import Link from 'next/link'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import useSWR from 'swr'; -import { GenreSliderItem } from '../../../../server/interfaces/api/discoverInterfaces'; +import type { GenreSliderItem } from '../../../../server/interfaces/api/discoverInterfaces'; import GenreCard from '../../GenreCard'; import Slider from '../../Slider'; import { genreColorMap } from '../constants'; diff --git a/src/components/Discover/TvGenreList/index.tsx b/src/components/Discover/TvGenreList/index.tsx index 15fe9a017..e08910798 100644 --- a/src/components/Discover/TvGenreList/index.tsx +++ b/src/components/Discover/TvGenreList/index.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import useSWR from 'swr'; -import { GenreSliderItem } from '../../../../server/interfaces/api/discoverInterfaces'; +import type { GenreSliderItem } from '../../../../server/interfaces/api/discoverInterfaces'; import Error from '../../../pages/_error'; import Header from '../../Common/Header'; import LoadingSpinner from '../../Common/LoadingSpinner'; diff --git a/src/components/Discover/TvGenreSlider/index.tsx b/src/components/Discover/TvGenreSlider/index.tsx index 54f8daa34..acff82995 100644 --- a/src/components/Discover/TvGenreSlider/index.tsx +++ b/src/components/Discover/TvGenreSlider/index.tsx @@ -3,7 +3,7 @@ import Link from 'next/link'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import useSWR from 'swr'; -import { GenreSliderItem } from '../../../../server/interfaces/api/discoverInterfaces'; +import type { GenreSliderItem } from '../../../../server/interfaces/api/discoverInterfaces'; import GenreCard from '../../GenreCard'; import Slider from '../../Slider'; import { genreColorMap } from '../constants'; diff --git a/src/components/DownloadBlock/index.tsx b/src/components/DownloadBlock/index.tsx index d7c3922c9..c1d5713fb 100644 --- a/src/components/DownloadBlock/index.tsx +++ b/src/components/DownloadBlock/index.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl'; -import { DownloadingItem } from '../../../server/lib/downloadtracker'; +import type { DownloadingItem } from '../../../server/lib/downloadtracker'; import Badge from '../Common/Badge'; const messages = defineMessages({ diff --git a/src/components/IssueList/IssueItem/index.tsx b/src/components/IssueList/IssueItem/index.tsx index e5cd058a3..0361f5b8f 100644 --- a/src/components/IssueList/IssueItem/index.tsx +++ b/src/components/IssueList/IssueItem/index.tsx @@ -6,9 +6,9 @@ import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl'; import useSWR from 'swr'; import { IssueStatus } from '../../../../server/constants/issue'; import { MediaType } from '../../../../server/constants/media'; -import Issue from '../../../../server/entity/Issue'; -import { MovieDetails } from '../../../../server/models/Movie'; -import { TvDetails } from '../../../../server/models/Tv'; +import type Issue from '../../../../server/entity/Issue'; +import type { MovieDetails } from '../../../../server/models/Movie'; +import type { TvDetails } from '../../../../server/models/Tv'; import { Permission, useUser } from '../../../hooks/useUser'; import globalMessages from '../../../i18n/globalMessages'; import Badge from '../../Common/Badge'; diff --git a/src/components/IssueList/index.tsx b/src/components/IssueList/index.tsx index 91e9a8eb4..b123a4e80 100644 --- a/src/components/IssueList/index.tsx +++ b/src/components/IssueList/index.tsx @@ -8,7 +8,7 @@ import { useRouter } from 'next/router'; import React, { useEffect, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import useSWR from 'swr'; -import { IssueResultsResponse } from '../../../server/interfaces/api/issueInterfaces'; +import type { IssueResultsResponse } from '../../../server/interfaces/api/issueInterfaces'; import Button from '../../components/Common/Button'; import { useUpdateQueryParams } from '../../hooks/useUpdateQueryParams'; import globalMessages from '../../i18n/globalMessages'; diff --git a/src/components/IssueModal/CreateIssueModal/index.tsx b/src/components/IssueModal/CreateIssueModal/index.tsx index 5dbc41802..9ca3d7970 100644 --- a/src/components/IssueModal/CreateIssueModal/index.tsx +++ b/src/components/IssueModal/CreateIssueModal/index.tsx @@ -11,8 +11,8 @@ import useSWR from 'swr'; import * as Yup from 'yup'; import { MediaStatus } from '../../../../server/constants/media'; import type Issue from '../../../../server/entity/Issue'; -import { MovieDetails } from '../../../../server/models/Movie'; -import { TvDetails } from '../../../../server/models/Tv'; +import type { MovieDetails } from '../../../../server/models/Movie'; +import type { TvDetails } from '../../../../server/models/Tv'; import useSettings from '../../../hooks/useSettings'; import { Permission, useUser } from '../../../hooks/useUser'; import globalMessages from '../../../i18n/globalMessages'; diff --git a/src/components/IssueModal/constants.ts b/src/components/IssueModal/constants.ts index 92cf6bc77..f658e2238 100644 --- a/src/components/IssueModal/constants.ts +++ b/src/components/IssueModal/constants.ts @@ -1,4 +1,5 @@ -import { defineMessages, MessageDescriptor } from 'react-intl'; +import type { MessageDescriptor } from 'react-intl'; +import { defineMessages } from 'react-intl'; import { IssueType } from '../../../server/constants/issue'; const messages = defineMessages({ diff --git a/src/components/JSONEditor/index.tsx b/src/components/JSONEditor/index.tsx index b1de78a75..6a509e52f 100644 --- a/src/components/JSONEditor/index.tsx +++ b/src/components/JSONEditor/index.tsx @@ -1,4 +1,5 @@ -import React, { HTMLAttributes } from 'react'; +import type { HTMLAttributes } from 'react'; +import React from 'react'; import AceEditor from 'react-ace'; import 'ace-builds/src-noconflict/mode-json'; import 'ace-builds/src-noconflict/theme-dracula'; diff --git a/src/components/LanguageSelector/index.tsx b/src/components/LanguageSelector/index.tsx index 74c84b152..ad4411b52 100644 --- a/src/components/LanguageSelector/index.tsx +++ b/src/components/LanguageSelector/index.tsx @@ -1,9 +1,10 @@ import { sortBy } from 'lodash'; import React, { useMemo } from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import Select, { CSSObjectWithLabel } from 'react-select'; +import type { CSSObjectWithLabel } from 'react-select'; +import Select from 'react-select'; import useSWR from 'swr'; -import { Language } from '../../../server/lib/settings'; +import type { Language } from '../../../server/lib/settings'; import globalMessages from '../../i18n/globalMessages'; const messages = defineMessages({ diff --git a/src/components/Layout/LanguagePicker/index.tsx b/src/components/Layout/LanguagePicker/index.tsx index 1d610604f..857b7df5b 100644 --- a/src/components/Layout/LanguagePicker/index.tsx +++ b/src/components/Layout/LanguagePicker/index.tsx @@ -1,10 +1,8 @@ import { TranslateIcon } from '@heroicons/react/solid'; import React, { useRef, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import { - availableLanguages, - AvailableLocale, -} from '../../../context/LanguageContext'; +import type { AvailableLocale } from '../../../context/LanguageContext'; +import { availableLanguages } from '../../../context/LanguageContext'; import useClickOutside from '../../../hooks/useClickOutside'; import useLocale from '../../../hooks/useLocale'; import Transition from '../../Transition'; diff --git a/src/components/Layout/Sidebar/index.tsx b/src/components/Layout/Sidebar/index.tsx index 45716eeb5..5530d954f 100644 --- a/src/components/Layout/Sidebar/index.tsx +++ b/src/components/Layout/Sidebar/index.tsx @@ -8,7 +8,8 @@ import { } from '@heroicons/react/outline'; import Link from 'next/link'; import { useRouter } from 'next/router'; -import React, { ReactNode, useRef } from 'react'; +import type { ReactNode } from 'react'; +import React, { useRef } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import useClickOutside from '../../../hooks/useClickOutside'; import { Permission, useUser } from '../../../hooks/useUser'; diff --git a/src/components/Layout/VersionStatus/index.tsx b/src/components/Layout/VersionStatus/index.tsx index d682df4ca..efce74580 100644 --- a/src/components/Layout/VersionStatus/index.tsx +++ b/src/components/Layout/VersionStatus/index.tsx @@ -8,7 +8,7 @@ import Link from 'next/link'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import useSWR from 'swr'; -import { StatusResponse } from '../../../../server/interfaces/api/settingsInterfaces'; +import type { StatusResponse } from '../../../../server/interfaces/api/settingsInterfaces'; const messages = defineMessages({ streamdevelop: 'Overseerr Develop', diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx index bde592778..5b57df971 100644 --- a/src/components/Layout/index.tsx +++ b/src/components/Layout/index.tsx @@ -2,7 +2,7 @@ import { MenuAlt2Icon } from '@heroicons/react/outline'; import { ArrowLeftIcon } from '@heroicons/react/solid'; import { useRouter } from 'next/router'; import React, { useEffect, useState } from 'react'; -import { AvailableLocale } from '../../context/LanguageContext'; +import type { AvailableLocale } from '../../context/LanguageContext'; import useLocale from '../../hooks/useLocale'; import useSettings from '../../hooks/useSettings'; import { useUser } from '../../hooks/useUser'; diff --git a/src/components/ManageSlideOver/index.tsx b/src/components/ManageSlideOver/index.tsx index a1e9bab17..6a6e2af28 100644 --- a/src/components/ManageSlideOver/index.tsx +++ b/src/components/ManageSlideOver/index.tsx @@ -10,9 +10,9 @@ import { MediaRequestStatus, MediaStatus, } from '../../../server/constants/media'; -import { MediaWatchDataResponse } from '../../../server/interfaces/api/mediaInterfaces'; -import { MovieDetails } from '../../../server/models/Movie'; -import { TvDetails } from '../../../server/models/Tv'; +import type { MediaWatchDataResponse } from '../../../server/interfaces/api/mediaInterfaces'; +import type { MovieDetails } from '../../../server/models/Movie'; +import type { TvDetails } from '../../../server/models/Tv'; import useSettings from '../../hooks/useSettings'; import { Permission, useUser } from '../../hooks/useUser'; import globalMessages from '../../i18n/globalMessages'; diff --git a/src/components/MovieDetails/MovieCast/index.tsx b/src/components/MovieDetails/MovieCast/index.tsx index 0cc9c2e03..53f3cf968 100644 --- a/src/components/MovieDetails/MovieCast/index.tsx +++ b/src/components/MovieDetails/MovieCast/index.tsx @@ -3,7 +3,7 @@ import { useRouter } from 'next/router'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import useSWR from 'swr'; -import { MovieDetails } from '../../../../server/models/Movie'; +import type { MovieDetails } from '../../../../server/models/Movie'; import Error from '../../../pages/_error'; import Header from '../../Common/Header'; import LoadingSpinner from '../../Common/LoadingSpinner'; diff --git a/src/components/MovieDetails/MovieCrew/index.tsx b/src/components/MovieDetails/MovieCrew/index.tsx index 14268e425..98ab0a2ae 100644 --- a/src/components/MovieDetails/MovieCrew/index.tsx +++ b/src/components/MovieDetails/MovieCrew/index.tsx @@ -3,7 +3,7 @@ import { useRouter } from 'next/router'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import useSWR from 'swr'; -import { MovieDetails } from '../../../../server/models/Movie'; +import type { MovieDetails } from '../../../../server/models/Movie'; import Error from '../../../pages/_error'; import Header from '../../Common/Header'; import LoadingSpinner from '../../Common/LoadingSpinner'; diff --git a/src/components/MovieDetails/index.tsx b/src/components/MovieDetails/index.tsx index fb749b882..edb4d8bd2 100644 --- a/src/components/MovieDetails/index.tsx +++ b/src/components/MovieDetails/index.tsx @@ -38,7 +38,8 @@ import Button from '../Common/Button'; import CachedImage from '../Common/CachedImage'; import LoadingSpinner from '../Common/LoadingSpinner'; import PageTitle from '../Common/PageTitle'; -import PlayButton, { PlayButtonLink } from '../Common/PlayButton'; +import type { PlayButtonLink } from '../Common/PlayButton'; +import PlayButton from '../Common/PlayButton'; import ExternalLinkBlock from '../ExternalLinkBlock'; import IssueModal from '../IssueModal'; import ManageSlideOver from '../ManageSlideOver'; diff --git a/src/components/NotificationTypeSelector/NotificationType/index.tsx b/src/components/NotificationTypeSelector/NotificationType/index.tsx index 9662ebd36..57a41e0fd 100644 --- a/src/components/NotificationTypeSelector/NotificationType/index.tsx +++ b/src/components/NotificationTypeSelector/NotificationType/index.tsx @@ -1,5 +1,6 @@ import React from 'react'; -import { hasNotificationType, NotificationItem } from '..'; +import type { NotificationItem } from '..'; +import { hasNotificationType } from '..'; interface NotificationTypeProps { option: NotificationItem; diff --git a/src/components/NotificationTypeSelector/index.tsx b/src/components/NotificationTypeSelector/index.tsx index 70b003aee..934ffcc95 100644 --- a/src/components/NotificationTypeSelector/index.tsx +++ b/src/components/NotificationTypeSelector/index.tsx @@ -2,7 +2,8 @@ import { sortBy } from 'lodash'; import React, { useMemo, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import useSettings from '../../hooks/useSettings'; -import { Permission, User, useUser } from '../../hooks/useUser'; +import type { User } from '../../hooks/useUser'; +import { Permission, useUser } from '../../hooks/useUser'; import NotificationType from './NotificationType'; const messages = defineMessages({ diff --git a/src/components/PermissionEdit/index.tsx b/src/components/PermissionEdit/index.tsx index 504e615db..c6315b8a5 100644 --- a/src/components/PermissionEdit/index.tsx +++ b/src/components/PermissionEdit/index.tsx @@ -1,7 +1,9 @@ import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import { Permission, User } from '../../hooks/useUser'; -import PermissionOption, { PermissionItem } from '../PermissionOption'; +import type { User } from '../../hooks/useUser'; +import { Permission } from '../../hooks/useUser'; +import type { PermissionItem } from '../PermissionOption'; +import PermissionOption from '../PermissionOption'; export const messages = defineMessages({ admin: 'Admin', diff --git a/src/components/PermissionOption/index.tsx b/src/components/PermissionOption/index.tsx index 739234759..2638419d5 100644 --- a/src/components/PermissionOption/index.tsx +++ b/src/components/PermissionOption/index.tsx @@ -1,7 +1,8 @@ import React from 'react'; import { hasPermission } from '../../../server/lib/permissions'; import useSettings from '../../hooks/useSettings'; -import { Permission, User } from '../../hooks/useUser'; +import type { User } from '../../hooks/useUser'; +import { Permission } from '../../hooks/useUser'; export interface PermissionItem { id: string; diff --git a/src/components/RequestButton/index.tsx b/src/components/RequestButton/index.tsx index 5ba5bf5d5..6852d174d 100644 --- a/src/components/RequestButton/index.tsx +++ b/src/components/RequestButton/index.tsx @@ -11,8 +11,8 @@ import { MediaRequestStatus, MediaStatus, } from '../../../server/constants/media'; -import Media from '../../../server/entity/Media'; -import { MediaRequest } from '../../../server/entity/MediaRequest'; +import type Media from '../../../server/entity/Media'; +import type { MediaRequest } from '../../../server/entity/MediaRequest'; import useSettings from '../../hooks/useSettings'; import { Permission, useUser } from '../../hooks/useUser'; import globalMessages from '../../i18n/globalMessages'; diff --git a/src/components/RequestModal/AdvancedRequester/index.tsx b/src/components/RequestModal/AdvancedRequester/index.tsx index 58bbe777a..2130d4dbe 100644 --- a/src/components/RequestModal/AdvancedRequester/index.tsx +++ b/src/components/RequestModal/AdvancedRequester/index.tsx @@ -12,7 +12,8 @@ import type { ServiceCommonServerWithDetails, } from '../../../../server/interfaces/api/serviceInterfaces'; import type { UserResultsResponse } from '../../../../server/interfaces/api/userInterfaces'; -import { Permission, User, useUser } from '../../../hooks/useUser'; +import type { User } from '../../../hooks/useUser'; +import { Permission, useUser } from '../../../hooks/useUser'; import globalMessages from '../../../i18n/globalMessages'; import { formatBytes } from '../../../utils/numberHelpers'; import { SmallLoadingSpinner } from '../../Common/LoadingSpinner'; diff --git a/src/components/RequestModal/CollectionRequestModal.tsx b/src/components/RequestModal/CollectionRequestModal.tsx index 5695760f3..5f5e88067 100644 --- a/src/components/RequestModal/CollectionRequestModal.tsx +++ b/src/components/RequestModal/CollectionRequestModal.tsx @@ -8,17 +8,18 @@ import { MediaRequestStatus, MediaStatus, } from '../../../server/constants/media'; -import { MediaRequest } from '../../../server/entity/MediaRequest'; -import { QuotaResponse } from '../../../server/interfaces/api/userInterfaces'; +import type { MediaRequest } from '../../../server/entity/MediaRequest'; +import type { QuotaResponse } from '../../../server/interfaces/api/userInterfaces'; import { Permission } from '../../../server/lib/permissions'; -import { Collection } from '../../../server/models/Collection'; +import type { Collection } from '../../../server/models/Collection'; import { useUser } from '../../hooks/useUser'; import globalMessages from '../../i18n/globalMessages'; import Alert from '../Common/Alert'; import Badge from '../Common/Badge'; import CachedImage from '../Common/CachedImage'; import Modal from '../Common/Modal'; -import AdvancedRequester, { RequestOverrides } from './AdvancedRequester'; +import type { RequestOverrides } from './AdvancedRequester'; +import AdvancedRequester from './AdvancedRequester'; import QuotaDisplay from './QuotaDisplay'; const messages = defineMessages({ diff --git a/src/components/RequestModal/MovieRequestModal.tsx b/src/components/RequestModal/MovieRequestModal.tsx index 648f9aff0..73f6b7336 100644 --- a/src/components/RequestModal/MovieRequestModal.tsx +++ b/src/components/RequestModal/MovieRequestModal.tsx @@ -5,15 +5,16 @@ import { defineMessages, useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import useSWR from 'swr'; import { MediaStatus } from '../../../server/constants/media'; -import { MediaRequest } from '../../../server/entity/MediaRequest'; -import { QuotaResponse } from '../../../server/interfaces/api/userInterfaces'; +import type { MediaRequest } from '../../../server/entity/MediaRequest'; +import type { QuotaResponse } from '../../../server/interfaces/api/userInterfaces'; import { Permission } from '../../../server/lib/permissions'; -import { MovieDetails } from '../../../server/models/Movie'; +import type { MovieDetails } from '../../../server/models/Movie'; import { useUser } from '../../hooks/useUser'; import globalMessages from '../../i18n/globalMessages'; import Alert from '../Common/Alert'; import Modal from '../Common/Modal'; -import AdvancedRequester, { RequestOverrides } from './AdvancedRequester'; +import type { RequestOverrides } from './AdvancedRequester'; +import AdvancedRequester from './AdvancedRequester'; import QuotaDisplay from './QuotaDisplay'; const messages = defineMessages({ diff --git a/src/components/RequestModal/QuotaDisplay/index.tsx b/src/components/RequestModal/QuotaDisplay/index.tsx index d5a268ba5..6438b6ed9 100644 --- a/src/components/RequestModal/QuotaDisplay/index.tsx +++ b/src/components/RequestModal/QuotaDisplay/index.tsx @@ -2,7 +2,7 @@ import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/solid'; import Link from 'next/link'; import React, { useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import { QuotaStatus } from '../../../../server/interfaces/api/userInterfaces'; +import type { QuotaStatus } from '../../../../server/interfaces/api/userInterfaces'; import ProgressCircle from '../../Common/ProgressCircle'; const messages = defineMessages({ diff --git a/src/components/RequestModal/SearchByNameModal/index.tsx b/src/components/RequestModal/SearchByNameModal/index.tsx index e2dc3c457..44a60aab2 100644 --- a/src/components/RequestModal/SearchByNameModal/index.tsx +++ b/src/components/RequestModal/SearchByNameModal/index.tsx @@ -2,7 +2,7 @@ import { DownloadIcon } from '@heroicons/react/outline'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import useSWR from 'swr'; -import { SonarrSeries } from '../../../../server/api/servarr/sonarr'; +import type { SonarrSeries } from '../../../../server/api/servarr/sonarr'; import globalMessages from '../../../i18n/globalMessages'; import Alert from '../../Common/Alert'; import { SmallLoadingSpinner } from '../../Common/LoadingSpinner'; diff --git a/src/components/RequestModal/TvRequestModal.tsx b/src/components/RequestModal/TvRequestModal.tsx index 0a6da80f0..fb915e636 100644 --- a/src/components/RequestModal/TvRequestModal.tsx +++ b/src/components/RequestModal/TvRequestModal.tsx @@ -9,18 +9,19 @@ import { MediaRequestStatus, MediaStatus, } from '../../../server/constants/media'; -import { MediaRequest } from '../../../server/entity/MediaRequest'; -import SeasonRequest from '../../../server/entity/SeasonRequest'; -import { QuotaResponse } from '../../../server/interfaces/api/userInterfaces'; +import type { MediaRequest } from '../../../server/entity/MediaRequest'; +import type SeasonRequest from '../../../server/entity/SeasonRequest'; +import type { QuotaResponse } from '../../../server/interfaces/api/userInterfaces'; import { Permission } from '../../../server/lib/permissions'; -import { TvDetails } from '../../../server/models/Tv'; +import type { TvDetails } from '../../../server/models/Tv'; import useSettings from '../../hooks/useSettings'; import { useUser } from '../../hooks/useUser'; import globalMessages from '../../i18n/globalMessages'; import Alert from '../Common/Alert'; import Badge from '../Common/Badge'; import Modal from '../Common/Modal'; -import AdvancedRequester, { RequestOverrides } from './AdvancedRequester'; +import type { RequestOverrides } from './AdvancedRequester'; +import AdvancedRequester from './AdvancedRequester'; import QuotaDisplay from './QuotaDisplay'; import SearchByNameModal from './SearchByNameModal'; diff --git a/src/components/RequestModal/index.tsx b/src/components/RequestModal/index.tsx index dfbb715e6..e6dbdb8d9 100644 --- a/src/components/RequestModal/index.tsx +++ b/src/components/RequestModal/index.tsx @@ -1,6 +1,6 @@ import React from 'react'; import type { MediaStatus } from '../../../server/constants/media'; -import { MediaRequest } from '../../../server/entity/MediaRequest'; +import type { MediaRequest } from '../../../server/entity/MediaRequest'; import Transition from '../Transition'; import CollectionRequestModal from './CollectionRequestModal'; import MovieRequestModal from './MovieRequestModal'; diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index e36442cf2..80effb0d0 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useRouter } from 'next/router'; -import { +import type { TvResult, MovieResult, PersonResult, diff --git a/src/components/ServiceWorkerSetup/index.tsx b/src/components/ServiceWorkerSetup/index.tsx index 56a558a3d..d3cd7ac5b 100644 --- a/src/components/ServiceWorkerSetup/index.tsx +++ b/src/components/ServiceWorkerSetup/index.tsx @@ -1,6 +1,7 @@ /* eslint-disable no-console */ import axios from 'axios'; -import React, { useEffect } from 'react'; +import type React from 'react'; +import { useEffect } from 'react'; import useSettings from '../../hooks/useSettings'; import { useUser } from '../../hooks/useUser'; diff --git a/src/components/Settings/SettingsAbout/index.tsx b/src/components/Settings/SettingsAbout/index.tsx index 2b638108f..7dfa75d04 100644 --- a/src/components/Settings/SettingsAbout/index.tsx +++ b/src/components/Settings/SettingsAbout/index.tsx @@ -2,7 +2,7 @@ import { InformationCircleIcon } from '@heroicons/react/solid'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import useSWR from 'swr'; -import { +import type { SettingsAboutResponse, StatusResponse, } from '../../../../server/interfaces/api/settingsInterfaces'; diff --git a/src/components/Settings/SettingsJobsCache/index.tsx b/src/components/Settings/SettingsJobsCache/index.tsx index 015747aff..38403a16b 100644 --- a/src/components/Settings/SettingsJobsCache/index.tsx +++ b/src/components/Settings/SettingsJobsCache/index.tsx @@ -2,16 +2,12 @@ import { PlayIcon, StopIcon, TrashIcon } from '@heroicons/react/outline'; import { PencilIcon } from '@heroicons/react/solid'; import axios from 'axios'; import React, { useState } from 'react'; -import { - defineMessages, - FormattedRelativeTime, - MessageDescriptor, - useIntl, -} from 'react-intl'; +import type { MessageDescriptor } from 'react-intl'; +import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import useSWR from 'swr'; -import { CacheItem } from '../../../../server/interfaces/api/settingsInterfaces'; -import { JobId } from '../../../../server/lib/settings'; +import type { CacheItem } from '../../../../server/interfaces/api/settingsInterfaces'; +import type { JobId } from '../../../../server/lib/settings'; import Spinner from '../../../assets/spinner.svg'; import globalMessages from '../../../i18n/globalMessages'; import { formatBytes } from '../../../utils/numberHelpers'; diff --git a/src/components/Settings/SettingsLayout.tsx b/src/components/Settings/SettingsLayout.tsx index 65f4d5485..4a366cbc5 100644 --- a/src/components/Settings/SettingsLayout.tsx +++ b/src/components/Settings/SettingsLayout.tsx @@ -2,7 +2,8 @@ import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import globalMessages from '../../i18n/globalMessages'; import PageTitle from '../Common/PageTitle'; -import SettingsTabs, { SettingsRoute } from '../Common/SettingsTabs'; +import type { SettingsRoute } from '../Common/SettingsTabs'; +import SettingsTabs from '../Common/SettingsTabs'; const messages = defineMessages({ menuGeneralSettings: 'General', diff --git a/src/components/Settings/SettingsLogs/index.tsx b/src/components/Settings/SettingsLogs/index.tsx index 0a607e0a7..a42bb820b 100644 --- a/src/components/Settings/SettingsLogs/index.tsx +++ b/src/components/Settings/SettingsLogs/index.tsx @@ -13,7 +13,7 @@ import React, { useEffect, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import useSWR from 'swr'; -import { +import type { LogMessage, LogsResultsResponse, } from '../../../../server/interfaces/api/settingsInterfaces'; diff --git a/src/components/Settings/SettingsMain.tsx b/src/components/Settings/SettingsMain.tsx index 8c9d06f66..11e88e7f2 100644 --- a/src/components/Settings/SettingsMain.tsx +++ b/src/components/Settings/SettingsMain.tsx @@ -7,12 +7,10 @@ import { defineMessages, useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import useSWR, { mutate } from 'swr'; import * as Yup from 'yup'; -import { UserSettingsGeneralResponse } from '../../../server/interfaces/api/userSettingsInterfaces'; +import type { UserSettingsGeneralResponse } from '../../../server/interfaces/api/userSettingsInterfaces'; import type { MainSettings } from '../../../server/lib/settings'; -import { - availableLanguages, - AvailableLocale, -} from '../../context/LanguageContext'; +import type { AvailableLocale } from '../../context/LanguageContext'; +import { availableLanguages } from '../../context/LanguageContext'; import useLocale from '../../hooks/useLocale'; import { Permission, useUser } from '../../hooks/useUser'; import globalMessages from '../../i18n/globalMessages'; diff --git a/src/components/Settings/SettingsNotifications.tsx b/src/components/Settings/SettingsNotifications.tsx index c086512ef..e275e0a43 100644 --- a/src/components/Settings/SettingsNotifications.tsx +++ b/src/components/Settings/SettingsNotifications.tsx @@ -10,7 +10,8 @@ import SlackLogo from '../../assets/extlogos/slack.svg'; import TelegramLogo from '../../assets/extlogos/telegram.svg'; import globalMessages from '../../i18n/globalMessages'; import PageTitle from '../Common/PageTitle'; -import SettingsTabs, { SettingsRoute } from '../Common/SettingsTabs'; +import type { SettingsRoute } from '../Common/SettingsTabs'; +import SettingsTabs from '../Common/SettingsTabs'; const messages = defineMessages({ notifications: 'Notifications', diff --git a/src/components/Settings/SonarrModal/index.tsx b/src/components/Settings/SonarrModal/index.tsx index 391db2863..3e9e239f9 100644 --- a/src/components/Settings/SonarrModal/index.tsx +++ b/src/components/Settings/SonarrModal/index.tsx @@ -3,7 +3,8 @@ import axios from 'axios'; import { Field, Formik } from 'formik'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import Select, { OnChangeValue } from 'react-select'; +import type { OnChangeValue } from 'react-select'; +import Select from 'react-select'; import { useToasts } from 'react-toast-notifications'; import * as Yup from 'yup'; import type { SonarrSettings } from '../../../../server/lib/settings'; diff --git a/src/components/Slider/index.tsx b/src/components/Slider/index.tsx index 89b3173d6..a24983237 100644 --- a/src/components/Slider/index.tsx +++ b/src/components/Slider/index.tsx @@ -1,12 +1,7 @@ import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/outline'; import { debounce } from 'lodash'; -import React, { - ReactNode, - useCallback, - useEffect, - useRef, - useState, -} from 'react'; +import type { ReactNode } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useIntl } from 'react-intl'; import { useSpring } from 'react-spring'; import globalMessages from '../../i18n/globalMessages'; diff --git a/src/components/StatusChacker/index.tsx b/src/components/StatusChacker/index.tsx index 82e449db6..ba4cd5e58 100644 --- a/src/components/StatusChacker/index.tsx +++ b/src/components/StatusChacker/index.tsx @@ -2,7 +2,7 @@ import { SparklesIcon } from '@heroicons/react/outline'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import useSWR from 'swr'; -import { StatusResponse } from '../../../server/interfaces/api/settingsInterfaces'; +import type { StatusResponse } from '../../../server/interfaces/api/settingsInterfaces'; import Modal from '../Common/Modal'; import Transition from '../Transition'; diff --git a/src/components/ToastContainer/index.tsx b/src/components/ToastContainer/index.tsx index e9e048c41..9b5863a10 100644 --- a/src/components/ToastContainer/index.tsx +++ b/src/components/ToastContainer/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { ToastContainerProps } from 'react-toast-notifications'; +import type { ToastContainerProps } from 'react-toast-notifications'; const ToastContainer: React.FC = ({ hasToasts, diff --git a/src/components/TvDetails/TvRecommendations.tsx b/src/components/TvDetails/TvRecommendations.tsx index 94e6f761b..5e473e400 100644 --- a/src/components/TvDetails/TvRecommendations.tsx +++ b/src/components/TvDetails/TvRecommendations.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import useSWR from 'swr'; import type { TvResult } from '../../../server/models/Search'; -import { TvDetails } from '../../../server/models/Tv'; +import type { TvDetails } from '../../../server/models/Tv'; import useDiscover from '../../hooks/useDiscover'; import Error from '../../pages/_error'; import Header from '../Common/Header'; diff --git a/src/components/TvDetails/index.tsx b/src/components/TvDetails/index.tsx index ce86c47f1..50558892a 100644 --- a/src/components/TvDetails/index.tsx +++ b/src/components/TvDetails/index.tsx @@ -16,8 +16,8 @@ import type { RTRating } from '../../../server/api/rottentomatoes'; import { ANIME_KEYWORD_ID } from '../../../server/api/themoviedb/constants'; import { IssueStatus } from '../../../server/constants/issue'; import { MediaStatus } from '../../../server/constants/media'; -import { Crew } from '../../../server/models/common'; -import { TvDetails as TvDetailsType } from '../../../server/models/Tv'; +import type { Crew } from '../../../server/models/common'; +import type { TvDetails as TvDetailsType } from '../../../server/models/Tv'; import RTAudFresh from '../../assets/rt_aud_fresh.svg'; import RTAudRotten from '../../assets/rt_aud_rotten.svg'; import RTFresh from '../../assets/rt_fresh.svg'; @@ -33,7 +33,8 @@ import Button from '../Common/Button'; import CachedImage from '../Common/CachedImage'; import LoadingSpinner from '../Common/LoadingSpinner'; import PageTitle from '../Common/PageTitle'; -import PlayButton, { PlayButtonLink } from '../Common/PlayButton'; +import type { PlayButtonLink } from '../Common/PlayButton'; +import PlayButton from '../Common/PlayButton'; import ExternalLinkBlock from '../ExternalLinkBlock'; import IssueModal from '../IssueModal'; import ManageSlideOver from '../ManageSlideOver'; diff --git a/src/components/UserList/BulkEditModal.tsx b/src/components/UserList/BulkEditModal.tsx index 1397afa0a..8a789e0b3 100644 --- a/src/components/UserList/BulkEditModal.tsx +++ b/src/components/UserList/BulkEditModal.tsx @@ -3,7 +3,8 @@ import axios from 'axios'; import React, { useEffect, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; -import { User, useUser } from '../../hooks/useUser'; +import type { User } from '../../hooks/useUser'; +import { useUser } from '../../hooks/useUser'; import globalMessages from '../../i18n/globalMessages'; import Modal from '../Common/Modal'; import PermissionEdit from '../PermissionEdit'; diff --git a/src/components/UserList/index.tsx b/src/components/UserList/index.tsx index 58c51d663..a0330c0cb 100644 --- a/src/components/UserList/index.tsx +++ b/src/components/UserList/index.tsx @@ -20,7 +20,8 @@ import type { UserResultsResponse } from '../../../server/interfaces/api/userInt import { hasPermission } from '../../../server/lib/permissions'; import useSettings from '../../hooks/useSettings'; import { useUpdateQueryParams } from '../../hooks/useUpdateQueryParams'; -import { Permission, User, UserType, useUser } from '../../hooks/useUser'; +import type { User } from '../../hooks/useUser'; +import { Permission, UserType, useUser } from '../../hooks/useUser'; import globalMessages from '../../i18n/globalMessages'; import Alert from '../Common/Alert'; import Badge from '../Common/Badge'; diff --git a/src/components/UserProfile/ProfileHeader/index.tsx b/src/components/UserProfile/ProfileHeader/index.tsx index 3485c7976..2470a4f3d 100644 --- a/src/components/UserProfile/ProfileHeader/index.tsx +++ b/src/components/UserProfile/ProfileHeader/index.tsx @@ -2,7 +2,8 @@ import { CogIcon, UserIcon } from '@heroicons/react/solid'; import Link from 'next/link'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import { Permission, User, useUser } from '../../../hooks/useUser'; +import type { User } from '../../../hooks/useUser'; +import { Permission, useUser } from '../../../hooks/useUser'; import Button from '../../Common/Button'; const messages = defineMessages({ diff --git a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx index 34a3f6170..a6793cb5a 100644 --- a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx +++ b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx @@ -7,11 +7,9 @@ import { defineMessages, useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import useSWR from 'swr'; import * as Yup from 'yup'; -import { UserSettingsGeneralResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces'; -import { - availableLanguages, - AvailableLocale, -} from '../../../../context/LanguageContext'; +import type { UserSettingsGeneralResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces'; +import type { AvailableLocale } from '../../../../context/LanguageContext'; +import { availableLanguages } from '../../../../context/LanguageContext'; import useLocale from '../../../../hooks/useLocale'; import useSettings from '../../../../hooks/useSettings'; import { Permission, UserType, useUser } from '../../../../hooks/useUser'; diff --git a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsDiscord.tsx b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsDiscord.tsx index 95802857f..ca5c9b56b 100644 --- a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsDiscord.tsx +++ b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsDiscord.tsx @@ -7,7 +7,7 @@ import { defineMessages, useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import useSWR from 'swr'; import * as Yup from 'yup'; -import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces'; +import type { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces'; import { useUser } from '../../../../hooks/useUser'; import globalMessages from '../../../../i18n/globalMessages'; import Button from '../../../Common/Button'; diff --git a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsEmail.tsx b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsEmail.tsx index b61507b35..252b3a954 100644 --- a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsEmail.tsx +++ b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsEmail.tsx @@ -7,7 +7,7 @@ import { defineMessages, useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import useSWR from 'swr'; import * as Yup from 'yup'; -import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces'; +import type { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces'; import { useUser } from '../../../../hooks/useUser'; import globalMessages from '../../../../i18n/globalMessages'; import Badge from '../../../Common/Badge'; diff --git a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsPushbullet.tsx b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsPushbullet.tsx index 0cefb0155..cb6ed4489 100644 --- a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsPushbullet.tsx +++ b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsPushbullet.tsx @@ -6,7 +6,7 @@ import { defineMessages, useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import useSWR from 'swr'; import * as Yup from 'yup'; -import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces'; +import type { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces'; import { useUser } from '../../../../hooks/useUser'; import globalMessages from '../../../../i18n/globalMessages'; import Button from '../../../Common/Button'; diff --git a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsPushover.tsx b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsPushover.tsx index ecdc96e78..c0fe7cac1 100644 --- a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsPushover.tsx +++ b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsPushover.tsx @@ -6,7 +6,7 @@ import { defineMessages, useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import useSWR from 'swr'; import * as Yup from 'yup'; -import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces'; +import type { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces'; import useSettings from '../../../../hooks/useSettings'; import { useUser } from '../../../../hooks/useUser'; import globalMessages from '../../../../i18n/globalMessages'; diff --git a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsTelegram.tsx b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsTelegram.tsx index a5303b0e3..c0fcf72ca 100644 --- a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsTelegram.tsx +++ b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsTelegram.tsx @@ -7,7 +7,7 @@ import { defineMessages, useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import useSWR from 'swr'; import * as Yup from 'yup'; -import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces'; +import type { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces'; import { useUser } from '../../../../hooks/useUser'; import globalMessages from '../../../../i18n/globalMessages'; import Button from '../../../Common/Button'; diff --git a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsWebPush.tsx b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsWebPush.tsx index 4a932cb99..9445fde55 100644 --- a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsWebPush.tsx +++ b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsWebPush.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import useSWR, { mutate } from 'swr'; -import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces'; +import type { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces'; import { useUser } from '../../../../hooks/useUser'; import globalMessages from '../../../../i18n/globalMessages'; import Button from '../../../Common/Button'; diff --git a/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx b/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx index ff6c149cb..450e79515 100644 --- a/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx +++ b/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx @@ -3,7 +3,7 @@ import { useRouter } from 'next/router'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import useSWR from 'swr'; -import { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces'; +import type { UserSettingsNotificationsResponse } from '../../../../../server/interfaces/api/userSettingsInterfaces'; import DiscordLogo from '../../../../assets/extlogos/discord.svg'; import PushbulletLogo from '../../../../assets/extlogos/pushbullet.svg'; import PushoverLogo from '../../../../assets/extlogos/pushover.svg'; @@ -13,7 +13,8 @@ import globalMessages from '../../../../i18n/globalMessages'; import Error from '../../../../pages/_error'; import LoadingSpinner from '../../../Common/LoadingSpinner'; import PageTitle from '../../../Common/PageTitle'; -import SettingsTabs, { SettingsRoute } from '../../../Common/SettingsTabs'; +import type { SettingsRoute } from '../../../Common/SettingsTabs'; +import SettingsTabs from '../../../Common/SettingsTabs'; const messages = defineMessages({ notifications: 'Notifications', diff --git a/src/components/UserProfile/UserSettings/index.tsx b/src/components/UserProfile/UserSettings/index.tsx index 7e2e43219..348c68940 100644 --- a/src/components/UserProfile/UserSettings/index.tsx +++ b/src/components/UserProfile/UserSettings/index.tsx @@ -2,7 +2,7 @@ import { useRouter } from 'next/router'; import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; import useSWR from 'swr'; -import { UserSettingsNotificationsResponse } from '../../../../server/interfaces/api/userSettingsInterfaces'; +import type { UserSettingsNotificationsResponse } from '../../../../server/interfaces/api/userSettingsInterfaces'; import { hasPermission, Permission } from '../../../../server/lib/permissions'; import useSettings from '../../../hooks/useSettings'; import { useUser } from '../../../hooks/useUser'; @@ -11,7 +11,8 @@ import Error from '../../../pages/_error'; import Alert from '../../Common/Alert'; import LoadingSpinner from '../../Common/LoadingSpinner'; import PageTitle from '../../Common/PageTitle'; -import SettingsTabs, { SettingsRoute } from '../../Common/SettingsTabs'; +import type { SettingsRoute } from '../../Common/SettingsTabs'; +import SettingsTabs from '../../Common/SettingsTabs'; import ProfileHeader from '../ProfileHeader'; const messages = defineMessages({ diff --git a/src/components/UserProfile/index.tsx b/src/components/UserProfile/index.tsx index c6c016db4..565240fb3 100644 --- a/src/components/UserProfile/index.tsx +++ b/src/components/UserProfile/index.tsx @@ -4,13 +4,13 @@ import { useRouter } from 'next/router'; import React, { useCallback, useEffect, useState } from 'react'; import { defineMessages, FormattedNumber, useIntl } from 'react-intl'; import useSWR from 'swr'; -import { +import type { QuotaResponse, UserRequestsResponse, UserWatchDataResponse, } from '../../../server/interfaces/api/userInterfaces'; -import { MovieDetails } from '../../../server/models/Movie'; -import { TvDetails } from '../../../server/models/Tv'; +import type { MovieDetails } from '../../../server/models/Movie'; +import type { TvDetails } from '../../../server/models/Tv'; import { Permission, UserType, useUser } from '../../hooks/useUser'; import Error from '../../pages/_error'; import ImageFader from '../Common/ImageFader'; diff --git a/src/context/LanguageContext.tsx b/src/context/LanguageContext.tsx index 44d3baaae..9cabd5cfd 100644 --- a/src/context/LanguageContext.tsx +++ b/src/context/LanguageContext.tsx @@ -1,4 +1,5 @@ -import React, { ReactNode } from 'react'; +import type { ReactNode } from 'react'; +import React from 'react'; export type AvailableLocale = | 'ca' diff --git a/src/context/SettingsContext.tsx b/src/context/SettingsContext.tsx index 211d40600..e0c36e1fa 100644 --- a/src/context/SettingsContext.tsx +++ b/src/context/SettingsContext.tsx @@ -1,6 +1,6 @@ import React from 'react'; import useSWR from 'swr'; -import { PublicSettingsResponse } from '../../server/interfaces/api/settingsInterfaces'; +import type { PublicSettingsResponse } from '../../server/interfaces/api/settingsInterfaces'; export interface SettingsContextProps { currentSettings: PublicSettingsResponse; diff --git a/src/context/UserContext.tsx b/src/context/UserContext.tsx index 09440d237..4f88e8837 100644 --- a/src/context/UserContext.tsx +++ b/src/context/UserContext.tsx @@ -1,6 +1,7 @@ import { useRouter } from 'next/dist/client/router'; import React, { useEffect, useRef } from 'react'; -import { User, useUser } from '../hooks/useUser'; +import type { User } from '../hooks/useUser'; +import { useUser } from '../hooks/useUser'; interface UserContextProps { initialUser: User; diff --git a/src/hooks/useDebouncedState.ts b/src/hooks/useDebouncedState.ts index a73e3a06a..b4ab47c5e 100644 --- a/src/hooks/useDebouncedState.ts +++ b/src/hooks/useDebouncedState.ts @@ -1,4 +1,5 @@ -import { useState, useEffect, Dispatch, SetStateAction } from 'react'; +import type { Dispatch, SetStateAction } from 'react'; +import { useState, useEffect } from 'react'; /** * A hook to help with debouncing state diff --git a/src/hooks/useLocale.ts b/src/hooks/useLocale.ts index a0281e7ed..0647e1a09 100644 --- a/src/hooks/useLocale.ts +++ b/src/hooks/useLocale.ts @@ -1,8 +1,6 @@ import { useContext } from 'react'; -import { - LanguageContext, - LanguageContextProps, -} from '../context/LanguageContext'; +import type { LanguageContextProps } from '../context/LanguageContext'; +import { LanguageContext } from '../context/LanguageContext'; const useLocale = (): Omit => { const languageContext = useContext(LanguageContext); diff --git a/src/hooks/useRequestOverride.ts b/src/hooks/useRequestOverride.ts index be992e8b2..e3b49d987 100644 --- a/src/hooks/useRequestOverride.ts +++ b/src/hooks/useRequestOverride.ts @@ -1,6 +1,6 @@ import useSWR from 'swr'; -import { MediaRequest } from '../../server/entity/MediaRequest'; -import { +import type { MediaRequest } from '../../server/entity/MediaRequest'; +import type { ServiceCommonServer, ServiceCommonServerWithDetails, } from '../../server/interfaces/api/serviceInterfaces'; diff --git a/src/hooks/useRouteGuard.ts b/src/hooks/useRouteGuard.ts index fb6e0209a..dbf68490a 100644 --- a/src/hooks/useRouteGuard.ts +++ b/src/hooks/useRouteGuard.ts @@ -1,6 +1,7 @@ import { useRouter } from 'next/router'; import { useEffect } from 'react'; -import { Permission, PermissionCheckOptions, useUser } from './useUser'; +import type { Permission, PermissionCheckOptions } from './useUser'; +import { useUser } from './useUser'; const useRouteGuard = ( permission: Permission | Permission[], diff --git a/src/hooks/useSearchInput.ts b/src/hooks/useSearchInput.ts index fd4d20885..7b7f1af6b 100644 --- a/src/hooks/useSearchInput.ts +++ b/src/hooks/useSearchInput.ts @@ -1,6 +1,7 @@ /* eslint-disable react-hooks/exhaustive-deps */ import { useRouter } from 'next/router'; -import { Dispatch, SetStateAction, useEffect, useState } from 'react'; +import type { Dispatch, SetStateAction } from 'react'; +import { useEffect, useState } from 'react'; import type { UrlObject } from 'url'; import type { Nullable } from '../utils/typeHelpers'; import useDebouncedState from './useDebouncedState'; diff --git a/src/hooks/useSettings.ts b/src/hooks/useSettings.ts index 0fb7d7e9e..a436afa0f 100644 --- a/src/hooks/useSettings.ts +++ b/src/hooks/useSettings.ts @@ -1,8 +1,6 @@ import { useContext } from 'react'; -import { - SettingsContext, - SettingsContextProps, -} from '../context/SettingsContext'; +import type { SettingsContextProps } from '../context/SettingsContext'; +import { SettingsContext } from '../context/SettingsContext'; const useSettings = (): SettingsContextProps => { const settings = useContext(SettingsContext); diff --git a/src/hooks/useUpdateQueryParams.ts b/src/hooks/useUpdateQueryParams.ts index 645d1d9d0..e2ef801de 100644 --- a/src/hooks/useUpdateQueryParams.ts +++ b/src/hooks/useUpdateQueryParams.ts @@ -1,5 +1,6 @@ -import { NextRouter, useRouter } from 'next/router'; -import { ParsedUrlQuery } from 'querystring'; +import type { NextRouter } from 'next/router'; +import { useRouter } from 'next/router'; +import type { ParsedUrlQuery } from 'querystring'; import { useCallback } from 'react'; type UseQueryParamReturnedFunction = ( diff --git a/src/hooks/useUser.ts b/src/hooks/useUser.ts index 223cea326..2fb539ef6 100644 --- a/src/hooks/useUser.ts +++ b/src/hooks/useUser.ts @@ -1,12 +1,9 @@ import useSWR from 'swr'; -import { MutatorCallback } from 'swr/dist/types'; +import type { MutatorCallback } from 'swr/dist/types'; import { UserType } from '../../server/constants/user'; -import { - hasPermission, - Permission, - PermissionCheckOptions, -} from '../../server/lib/permissions'; -import { NotificationAgentKey } from '../../server/lib/settings'; +import type { PermissionCheckOptions } from '../../server/lib/permissions'; +import { hasPermission, Permission } from '../../server/lib/permissions'; +import type { NotificationAgentKey } from '../../server/lib/settings'; export { Permission, UserType }; export type { PermissionCheckOptions }; diff --git a/src/hooks/useVerticalScroll.ts b/src/hooks/useVerticalScroll.ts index cb80fe5b4..55417621f 100644 --- a/src/hooks/useVerticalScroll.ts +++ b/src/hooks/useVerticalScroll.ts @@ -1,4 +1,5 @@ -import { useState, useEffect, useRef, MutableRefObject } from 'react'; +import type { MutableRefObject } from 'react'; +import { useState, useEffect, useRef } from 'react'; import { debounce } from 'lodash'; const IS_SCROLLING_CHECK_THROTTLE = 200; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index dd6c3e975..3094de960 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,11 +1,12 @@ import axios from 'axios'; -import App, { AppInitialProps, AppProps } from 'next/app'; +import type { AppInitialProps, AppProps } from 'next/app'; +import App from 'next/app'; import Head from 'next/head'; import React, { useEffect, useState } from 'react'; import { IntlProvider } from 'react-intl'; import { ToastProvider } from 'react-toast-notifications'; import { SWRConfig } from 'swr'; -import { PublicSettingsResponse } from '../../server/interfaces/api/settingsInterfaces'; +import type { PublicSettingsResponse } from '../../server/interfaces/api/settingsInterfaces'; import Layout from '../components/Layout'; import LoadingBar from '../components/LoadingBar'; import PWAHeader from '../components/PWAHeader'; @@ -14,10 +15,11 @@ import StatusChecker from '../components/StatusChacker'; import Toast from '../components/Toast'; import ToastContainer from '../components/ToastContainer'; import { InteractionProvider } from '../context/InteractionContext'; -import { AvailableLocale, LanguageContext } from '../context/LanguageContext'; +import type { AvailableLocale } from '../context/LanguageContext'; +import { LanguageContext } from '../context/LanguageContext'; import { SettingsProvider } from '../context/SettingsContext'; import { UserContext } from '../context/UserContext'; -import { User } from '../hooks/useUser'; +import type { User } from '../hooks/useUser'; import '../styles/globals.css'; // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx index 9cd04471b..4027ec489 100644 --- a/src/pages/_document.tsx +++ b/src/pages/_document.tsx @@ -1,11 +1,5 @@ -import Document, { - DocumentContext, - DocumentInitialProps, - Head, - Html, - Main, - NextScript, -} from 'next/document'; +import type { DocumentContext, DocumentInitialProps } from 'next/document'; +import Document, { Head, Html, Main, NextScript } from 'next/document'; import React from 'react'; class MyDocument extends Document { diff --git a/src/pages/collection/[collectionId]/index.tsx b/src/pages/collection/[collectionId]/index.tsx index 82214aa80..4e67116a7 100644 --- a/src/pages/collection/[collectionId]/index.tsx +++ b/src/pages/collection/[collectionId]/index.tsx @@ -1,5 +1,5 @@ import axios from 'axios'; -import { GetServerSideProps, NextPage } from 'next'; +import type { GetServerSideProps, NextPage } from 'next'; import React from 'react'; import type { Collection } from '../../../../server/models/Collection'; import CollectionDetails from '../../../components/CollectionDetails'; diff --git a/src/pages/discover/movies/genre/[genreId]/index.tsx b/src/pages/discover/movies/genre/[genreId]/index.tsx index 71fd2b01d..f9a819ec2 100644 --- a/src/pages/discover/movies/genre/[genreId]/index.tsx +++ b/src/pages/discover/movies/genre/[genreId]/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import DiscoverMovieGenre from '../../../../../components/Discover/DiscoverMovieGenre'; const DiscoverMoviesGenrePage: NextPage = () => { diff --git a/src/pages/discover/movies/genres.tsx b/src/pages/discover/movies/genres.tsx index 13a4cfe80..212275676 100644 --- a/src/pages/discover/movies/genres.tsx +++ b/src/pages/discover/movies/genres.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import MovieGenreList from '../../../components/Discover/MovieGenreList'; const MovieGenresPage: NextPage = () => { diff --git a/src/pages/discover/movies/index.tsx b/src/pages/discover/movies/index.tsx index 823898e8e..e7bc69dad 100644 --- a/src/pages/discover/movies/index.tsx +++ b/src/pages/discover/movies/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import DiscoverMovies from '../../../components/Discover/DiscoverMovies'; const DiscoverMoviesPage: NextPage = () => { diff --git a/src/pages/discover/movies/language/[language]/index.tsx b/src/pages/discover/movies/language/[language]/index.tsx index a1fba4fed..ad2cbd9d4 100644 --- a/src/pages/discover/movies/language/[language]/index.tsx +++ b/src/pages/discover/movies/language/[language]/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import DiscoverMovieLanguage from '../../../../../components/Discover/DiscoverMovieLanguage'; const DiscoverMovieLanguagePage: NextPage = () => { diff --git a/src/pages/discover/movies/studio/[studioId]/index.tsx b/src/pages/discover/movies/studio/[studioId]/index.tsx index 4756ffbfe..5c70fd3cd 100644 --- a/src/pages/discover/movies/studio/[studioId]/index.tsx +++ b/src/pages/discover/movies/studio/[studioId]/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import DiscoverMovieStudio from '../../../../../components/Discover/DiscoverStudio'; const DiscoverMoviesStudioPage: NextPage = () => { diff --git a/src/pages/discover/movies/upcoming.tsx b/src/pages/discover/movies/upcoming.tsx index 13c62cc0b..23fc0b005 100644 --- a/src/pages/discover/movies/upcoming.tsx +++ b/src/pages/discover/movies/upcoming.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import UpcomingMovies from '../../../components/Discover/Upcoming'; const UpcomingMoviesPage: NextPage = () => { diff --git a/src/pages/discover/tv/genre/[genreId]/index.tsx b/src/pages/discover/tv/genre/[genreId]/index.tsx index bb5cbd0e8..e9e844b13 100644 --- a/src/pages/discover/tv/genre/[genreId]/index.tsx +++ b/src/pages/discover/tv/genre/[genreId]/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import DiscoverTvGenre from '../../../../../components/Discover/DiscoverTvGenre'; const DiscoverTvGenrePage: NextPage = () => { diff --git a/src/pages/discover/tv/genres.tsx b/src/pages/discover/tv/genres.tsx index 36475e917..76f0cb7d2 100644 --- a/src/pages/discover/tv/genres.tsx +++ b/src/pages/discover/tv/genres.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import TvGenreList from '../../../components/Discover/TvGenreList'; const TvGenresPage: NextPage = () => { diff --git a/src/pages/discover/tv/index.tsx b/src/pages/discover/tv/index.tsx index 193694402..b0b3d9bca 100644 --- a/src/pages/discover/tv/index.tsx +++ b/src/pages/discover/tv/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import DiscoverTv from '../../../components/Discover/DiscoverTv'; const DiscoverTvPage: NextPage = () => { diff --git a/src/pages/discover/tv/language/[language]/index.tsx b/src/pages/discover/tv/language/[language]/index.tsx index 7d81b6c41..5fc6be784 100644 --- a/src/pages/discover/tv/language/[language]/index.tsx +++ b/src/pages/discover/tv/language/[language]/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import DiscoverTvLanguage from '../../../../../components/Discover/DiscoverTvLanguage'; const DiscoverTvLanguagePage: NextPage = () => { diff --git a/src/pages/discover/tv/network/[networkId]/index.tsx b/src/pages/discover/tv/network/[networkId]/index.tsx index d864a4f92..cde53231b 100644 --- a/src/pages/discover/tv/network/[networkId]/index.tsx +++ b/src/pages/discover/tv/network/[networkId]/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import DiscoverNetwork from '../../../../../components/Discover/DiscoverNetwork'; const DiscoverTvNetworkPage: NextPage = () => { diff --git a/src/pages/discover/tv/upcoming.tsx b/src/pages/discover/tv/upcoming.tsx index 62af7996b..f1b2a858d 100644 --- a/src/pages/discover/tv/upcoming.tsx +++ b/src/pages/discover/tv/upcoming.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import DiscoverTvUpcoming from '../../../components/Discover/DiscoverTvUpcoming'; const DiscoverTvPage: NextPage = () => { diff --git a/src/pages/issues/[issueId]/index.tsx b/src/pages/issues/[issueId]/index.tsx index 48aadebf7..499de059f 100644 --- a/src/pages/issues/[issueId]/index.tsx +++ b/src/pages/issues/[issueId]/index.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import IssueDetails from '../../../components/IssueDetails'; import useRouteGuard from '../../../hooks/useRouteGuard'; diff --git a/src/pages/issues/index.tsx b/src/pages/issues/index.tsx index f352b89b0..deda3a317 100644 --- a/src/pages/issues/index.tsx +++ b/src/pages/issues/index.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import IssueList from '../../components/IssueList'; import useRouteGuard from '../../hooks/useRouteGuard'; diff --git a/src/pages/movie/[movieId]/cast.tsx b/src/pages/movie/[movieId]/cast.tsx index ea23d0914..e0c5fbf3f 100644 --- a/src/pages/movie/[movieId]/cast.tsx +++ b/src/pages/movie/[movieId]/cast.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import MovieCast from '../../../components/MovieDetails/MovieCast'; diff --git a/src/pages/movie/[movieId]/crew.tsx b/src/pages/movie/[movieId]/crew.tsx index 6ba590539..c69eb85b2 100644 --- a/src/pages/movie/[movieId]/crew.tsx +++ b/src/pages/movie/[movieId]/crew.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import MovieCrew from '../../../components/MovieDetails/MovieCrew'; diff --git a/src/pages/movie/[movieId]/index.tsx b/src/pages/movie/[movieId]/index.tsx index c69fbfefd..736bbc872 100644 --- a/src/pages/movie/[movieId]/index.tsx +++ b/src/pages/movie/[movieId]/index.tsx @@ -1,5 +1,5 @@ import axios from 'axios'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import type { MovieDetails as MovieDetailsType } from '../../../../server/models/Movie'; import MovieDetails from '../../../components/MovieDetails'; diff --git a/src/pages/movie/[movieId]/recommendations.tsx b/src/pages/movie/[movieId]/recommendations.tsx index 3fd1d6226..f1c3b81cf 100644 --- a/src/pages/movie/[movieId]/recommendations.tsx +++ b/src/pages/movie/[movieId]/recommendations.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import MovieRecommendations from '../../../components/MovieDetails/MovieRecommendations'; const MovieRecommendationsPage: NextPage = () => { diff --git a/src/pages/movie/[movieId]/similar.tsx b/src/pages/movie/[movieId]/similar.tsx index 90ee37d98..76a227b53 100644 --- a/src/pages/movie/[movieId]/similar.tsx +++ b/src/pages/movie/[movieId]/similar.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import MovieSimilar from '../../../components/MovieDetails/MovieSimilar'; const MovieSimilarPage: NextPage = () => { diff --git a/src/pages/person/[personId]/index.tsx b/src/pages/person/[personId]/index.tsx index d905ef542..b08e43d76 100644 --- a/src/pages/person/[personId]/index.tsx +++ b/src/pages/person/[personId]/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import PersonDetails from '../../../components/PersonDetails'; const MoviePage: NextPage = () => { diff --git a/src/pages/profile/index.tsx b/src/pages/profile/index.tsx index 22fef09e5..89a137c31 100644 --- a/src/pages/profile/index.tsx +++ b/src/pages/profile/index.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserProfile from '../../components/UserProfile'; diff --git a/src/pages/profile/settings/index.tsx b/src/pages/profile/settings/index.tsx index 40fb7d2db..79b03c385 100644 --- a/src/pages/profile/settings/index.tsx +++ b/src/pages/profile/settings/index.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../components/UserProfile/UserSettings'; import UserGeneralSettings from '../../../components/UserProfile/UserSettings/UserGeneralSettings'; diff --git a/src/pages/profile/settings/main.tsx b/src/pages/profile/settings/main.tsx index 083358e07..212a105d6 100644 --- a/src/pages/profile/settings/main.tsx +++ b/src/pages/profile/settings/main.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../components/UserProfile/UserSettings'; import UserGeneralSettings from '../../../components/UserProfile/UserSettings/UserGeneralSettings'; diff --git a/src/pages/profile/settings/notifications/discord.tsx b/src/pages/profile/settings/notifications/discord.tsx index 06e580ffd..30442dc54 100644 --- a/src/pages/profile/settings/notifications/discord.tsx +++ b/src/pages/profile/settings/notifications/discord.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../../components/UserProfile/UserSettings'; import UserNotificationSettings from '../../../../components/UserProfile/UserSettings/UserNotificationSettings'; diff --git a/src/pages/profile/settings/notifications/email.tsx b/src/pages/profile/settings/notifications/email.tsx index 370258cac..b101717eb 100644 --- a/src/pages/profile/settings/notifications/email.tsx +++ b/src/pages/profile/settings/notifications/email.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../../components/UserProfile/UserSettings'; import UserNotificationSettings from '../../../../components/UserProfile/UserSettings/UserNotificationSettings'; diff --git a/src/pages/profile/settings/notifications/pushbullet.tsx b/src/pages/profile/settings/notifications/pushbullet.tsx index 12afd9413..10cf6a765 100644 --- a/src/pages/profile/settings/notifications/pushbullet.tsx +++ b/src/pages/profile/settings/notifications/pushbullet.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../../components/UserProfile/UserSettings'; import UserNotificationSettings from '../../../../components/UserProfile/UserSettings/UserNotificationSettings'; diff --git a/src/pages/profile/settings/notifications/pushover.tsx b/src/pages/profile/settings/notifications/pushover.tsx index 83b8d71db..4eba87ddc 100644 --- a/src/pages/profile/settings/notifications/pushover.tsx +++ b/src/pages/profile/settings/notifications/pushover.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../../components/UserProfile/UserSettings'; import UserNotificationSettings from '../../../../components/UserProfile/UserSettings/UserNotificationSettings'; diff --git a/src/pages/profile/settings/notifications/telegram.tsx b/src/pages/profile/settings/notifications/telegram.tsx index 3a641aab3..e584be9f7 100644 --- a/src/pages/profile/settings/notifications/telegram.tsx +++ b/src/pages/profile/settings/notifications/telegram.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../../components/UserProfile/UserSettings'; import UserNotificationSettings from '../../../../components/UserProfile/UserSettings/UserNotificationSettings'; diff --git a/src/pages/profile/settings/notifications/webpush.tsx b/src/pages/profile/settings/notifications/webpush.tsx index a44f254c0..65696fa28 100644 --- a/src/pages/profile/settings/notifications/webpush.tsx +++ b/src/pages/profile/settings/notifications/webpush.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../../components/UserProfile/UserSettings'; import UserNotificationSettings from '../../../../components/UserProfile/UserSettings/UserNotificationSettings'; diff --git a/src/pages/profile/settings/password.tsx b/src/pages/profile/settings/password.tsx index 304e29aa3..7e0a1ec3c 100644 --- a/src/pages/profile/settings/password.tsx +++ b/src/pages/profile/settings/password.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../components/UserProfile/UserSettings'; import UserPasswordChange from '../../../components/UserProfile/UserSettings/UserPasswordChange'; diff --git a/src/pages/profile/settings/permissions.tsx b/src/pages/profile/settings/permissions.tsx index 7926a2ebe..9572c5c93 100644 --- a/src/pages/profile/settings/permissions.tsx +++ b/src/pages/profile/settings/permissions.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../components/UserProfile/UserSettings'; import UserPermissions from '../../../components/UserProfile/UserSettings/UserPermissions'; diff --git a/src/pages/settings/about.tsx b/src/pages/settings/about.tsx index 442669d9c..e10fe7b77 100644 --- a/src/pages/settings/about.tsx +++ b/src/pages/settings/about.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import SettingsAbout from '../../components/Settings/SettingsAbout'; import SettingsLayout from '../../components/Settings/SettingsLayout'; diff --git a/src/pages/settings/index.tsx b/src/pages/settings/index.tsx index e67d53b40..7c603a850 100644 --- a/src/pages/settings/index.tsx +++ b/src/pages/settings/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import SettingsLayout from '../../components/Settings/SettingsLayout'; import SettingsMain from '../../components/Settings/SettingsMain'; import useRouteGuard from '../../hooks/useRouteGuard'; diff --git a/src/pages/settings/logs.tsx b/src/pages/settings/logs.tsx index 09c28fe05..00f40b2f9 100644 --- a/src/pages/settings/logs.tsx +++ b/src/pages/settings/logs.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import SettingsLayout from '../../components/Settings/SettingsLayout'; import SettingsLogs from '../../components/Settings/SettingsLogs'; diff --git a/src/pages/settings/main.tsx b/src/pages/settings/main.tsx index 7c450a37e..06a432843 100644 --- a/src/pages/settings/main.tsx +++ b/src/pages/settings/main.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import SettingsLayout from '../../components/Settings/SettingsLayout'; import SettingsMain from '../../components/Settings/SettingsMain'; import { Permission } from '../../hooks/useUser'; diff --git a/src/pages/settings/notifications/discord.tsx b/src/pages/settings/notifications/discord.tsx index 6345b6252..329b297ea 100644 --- a/src/pages/settings/notifications/discord.tsx +++ b/src/pages/settings/notifications/discord.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import NotificationsDiscord from '../../../components/Settings/Notifications/NotificationsDiscord'; import SettingsLayout from '../../../components/Settings/SettingsLayout'; diff --git a/src/pages/settings/notifications/email.tsx b/src/pages/settings/notifications/email.tsx index c751f4308..06464496a 100644 --- a/src/pages/settings/notifications/email.tsx +++ b/src/pages/settings/notifications/email.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import NotificationsEmail from '../../../components/Settings/Notifications/NotificationsEmail'; import SettingsLayout from '../../../components/Settings/SettingsLayout'; diff --git a/src/pages/settings/notifications/gotify.tsx b/src/pages/settings/notifications/gotify.tsx index a47c9c288..35fbee5bf 100644 --- a/src/pages/settings/notifications/gotify.tsx +++ b/src/pages/settings/notifications/gotify.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import NotificationsGotify from '../../../components/Settings/Notifications/NotificationsGotify'; import SettingsLayout from '../../../components/Settings/SettingsLayout'; diff --git a/src/pages/settings/notifications/lunasea.tsx b/src/pages/settings/notifications/lunasea.tsx index 070e6e3cf..92b269570 100644 --- a/src/pages/settings/notifications/lunasea.tsx +++ b/src/pages/settings/notifications/lunasea.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import NotificationsLunaSea from '../../../components/Settings/Notifications/NotificationsLunaSea'; import SettingsLayout from '../../../components/Settings/SettingsLayout'; diff --git a/src/pages/settings/notifications/pushbullet.tsx b/src/pages/settings/notifications/pushbullet.tsx index cdfa2d05d..af345e57f 100644 --- a/src/pages/settings/notifications/pushbullet.tsx +++ b/src/pages/settings/notifications/pushbullet.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import NotificationsPushbullet from '../../../components/Settings/Notifications/NotificationsPushbullet'; import SettingsLayout from '../../../components/Settings/SettingsLayout'; diff --git a/src/pages/settings/notifications/pushover.tsx b/src/pages/settings/notifications/pushover.tsx index 23df0939b..1eb4ffdd6 100644 --- a/src/pages/settings/notifications/pushover.tsx +++ b/src/pages/settings/notifications/pushover.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import NotificationsPushover from '../../../components/Settings/Notifications/NotificationsPushover'; import SettingsLayout from '../../../components/Settings/SettingsLayout'; diff --git a/src/pages/settings/notifications/slack.tsx b/src/pages/settings/notifications/slack.tsx index 768799b75..35e22fd7f 100644 --- a/src/pages/settings/notifications/slack.tsx +++ b/src/pages/settings/notifications/slack.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import NotificationsSlack from '../../../components/Settings/Notifications/NotificationsSlack'; import SettingsLayout from '../../../components/Settings/SettingsLayout'; diff --git a/src/pages/settings/notifications/telegram.tsx b/src/pages/settings/notifications/telegram.tsx index a651ff69c..fb3bfc69e 100644 --- a/src/pages/settings/notifications/telegram.tsx +++ b/src/pages/settings/notifications/telegram.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import NotificationsTelegram from '../../../components/Settings/Notifications/NotificationsTelegram'; import SettingsLayout from '../../../components/Settings/SettingsLayout'; diff --git a/src/pages/settings/notifications/webhook.tsx b/src/pages/settings/notifications/webhook.tsx index 4be0d30c0..9c06434a9 100644 --- a/src/pages/settings/notifications/webhook.tsx +++ b/src/pages/settings/notifications/webhook.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import NotificationsWebhook from '../../../components/Settings/Notifications/NotificationsWebhook'; import SettingsLayout from '../../../components/Settings/SettingsLayout'; diff --git a/src/pages/settings/notifications/webpush.tsx b/src/pages/settings/notifications/webpush.tsx index e91fe7376..6c888976d 100644 --- a/src/pages/settings/notifications/webpush.tsx +++ b/src/pages/settings/notifications/webpush.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import NotificationsWebPush from '../../../components/Settings/Notifications/NotificationsWebPush'; import SettingsLayout from '../../../components/Settings/SettingsLayout'; diff --git a/src/pages/settings/users.tsx b/src/pages/settings/users.tsx index 1461d7245..b0adc7bc0 100644 --- a/src/pages/settings/users.tsx +++ b/src/pages/settings/users.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import SettingsLayout from '../../components/Settings/SettingsLayout'; import SettingsUsers from '../../components/Settings/SettingsUsers'; import { Permission } from '../../hooks/useUser'; diff --git a/src/pages/setup.tsx b/src/pages/setup.tsx index 82a9c4597..a32fdb208 100644 --- a/src/pages/setup.tsx +++ b/src/pages/setup.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import Setup from '../components/Setup'; const SetupPage: NextPage = () => { diff --git a/src/pages/tv/[tvId]/cast.tsx b/src/pages/tv/[tvId]/cast.tsx index b7cc28b21..7c0fe83d0 100644 --- a/src/pages/tv/[tvId]/cast.tsx +++ b/src/pages/tv/[tvId]/cast.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import TvCast from '../../../components/TvDetails/TvCast'; diff --git a/src/pages/tv/[tvId]/crew.tsx b/src/pages/tv/[tvId]/crew.tsx index aec04080d..fcf3fca9a 100644 --- a/src/pages/tv/[tvId]/crew.tsx +++ b/src/pages/tv/[tvId]/crew.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import TvCrew from '../../../components/TvDetails/TvCrew'; diff --git a/src/pages/tv/[tvId]/index.tsx b/src/pages/tv/[tvId]/index.tsx index 43e817096..032c87310 100644 --- a/src/pages/tv/[tvId]/index.tsx +++ b/src/pages/tv/[tvId]/index.tsx @@ -1,5 +1,5 @@ import axios from 'axios'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import type { TvDetails as TvDetailsType } from '../../../../server/models/Tv'; import TvDetails from '../../../components/TvDetails'; diff --git a/src/pages/tv/[tvId]/recommendations.tsx b/src/pages/tv/[tvId]/recommendations.tsx index 37269e2d5..02028892d 100644 --- a/src/pages/tv/[tvId]/recommendations.tsx +++ b/src/pages/tv/[tvId]/recommendations.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import TvRecommendations from '../../../components/TvDetails/TvRecommendations'; const TvRecommendationsPage: NextPage = () => { diff --git a/src/pages/tv/[tvId]/similar.tsx b/src/pages/tv/[tvId]/similar.tsx index cca4500a7..107edc524 100644 --- a/src/pages/tv/[tvId]/similar.tsx +++ b/src/pages/tv/[tvId]/similar.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import TvSimilar from '../../../components/TvDetails/TvSimilar'; const TvSimilarPage: NextPage = () => { diff --git a/src/pages/users/[userId]/index.tsx b/src/pages/users/[userId]/index.tsx index 4385981cc..ebc39c218 100644 --- a/src/pages/users/[userId]/index.tsx +++ b/src/pages/users/[userId]/index.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserProfile from '../../../components/UserProfile'; diff --git a/src/pages/users/[userId]/requests.tsx b/src/pages/users/[userId]/requests.tsx index 105b56b42..f1877f860 100644 --- a/src/pages/users/[userId]/requests.tsx +++ b/src/pages/users/[userId]/requests.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import RequestList from '../../../components/RequestList'; import useRouteGuard from '../../../hooks/useRouteGuard'; diff --git a/src/pages/users/[userId]/settings/index.tsx b/src/pages/users/[userId]/settings/index.tsx index d6708b9ff..011616bca 100644 --- a/src/pages/users/[userId]/settings/index.tsx +++ b/src/pages/users/[userId]/settings/index.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../../components/UserProfile/UserSettings'; import UserGeneralSettings from '../../../../components/UserProfile/UserSettings/UserGeneralSettings'; diff --git a/src/pages/users/[userId]/settings/main.tsx b/src/pages/users/[userId]/settings/main.tsx index a2d5e7fe3..5db134ace 100644 --- a/src/pages/users/[userId]/settings/main.tsx +++ b/src/pages/users/[userId]/settings/main.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../../components/UserProfile/UserSettings'; import UserGeneralSettings from '../../../../components/UserProfile/UserSettings/UserGeneralSettings'; diff --git a/src/pages/users/[userId]/settings/notifications/discord.tsx b/src/pages/users/[userId]/settings/notifications/discord.tsx index f24b0810d..8a7e9d39b 100644 --- a/src/pages/users/[userId]/settings/notifications/discord.tsx +++ b/src/pages/users/[userId]/settings/notifications/discord.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../../../components/UserProfile/UserSettings'; import UserNotificationSettings from '../../../../../components/UserProfile/UserSettings/UserNotificationSettings'; diff --git a/src/pages/users/[userId]/settings/notifications/email.tsx b/src/pages/users/[userId]/settings/notifications/email.tsx index 7e62b1273..e81bd50ce 100644 --- a/src/pages/users/[userId]/settings/notifications/email.tsx +++ b/src/pages/users/[userId]/settings/notifications/email.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../../../components/UserProfile/UserSettings'; import UserNotificationSettings from '../../../../../components/UserProfile/UserSettings/UserNotificationSettings'; diff --git a/src/pages/users/[userId]/settings/notifications/pushbullet.tsx b/src/pages/users/[userId]/settings/notifications/pushbullet.tsx index cd7ca10c8..3e7bf8a7a 100644 --- a/src/pages/users/[userId]/settings/notifications/pushbullet.tsx +++ b/src/pages/users/[userId]/settings/notifications/pushbullet.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../../../components/UserProfile/UserSettings'; import UserNotificationSettings from '../../../../../components/UserProfile/UserSettings/UserNotificationSettings'; diff --git a/src/pages/users/[userId]/settings/notifications/pushover.tsx b/src/pages/users/[userId]/settings/notifications/pushover.tsx index b37a866f7..a3f57150c 100644 --- a/src/pages/users/[userId]/settings/notifications/pushover.tsx +++ b/src/pages/users/[userId]/settings/notifications/pushover.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../../../components/UserProfile/UserSettings'; import UserNotificationSettings from '../../../../../components/UserProfile/UserSettings/UserNotificationSettings'; diff --git a/src/pages/users/[userId]/settings/notifications/telegram.tsx b/src/pages/users/[userId]/settings/notifications/telegram.tsx index d26ad8b4b..d7815fdf4 100644 --- a/src/pages/users/[userId]/settings/notifications/telegram.tsx +++ b/src/pages/users/[userId]/settings/notifications/telegram.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../../../components/UserProfile/UserSettings'; import UserNotificationSettings from '../../../../../components/UserProfile/UserSettings/UserNotificationSettings'; diff --git a/src/pages/users/[userId]/settings/notifications/webpush.tsx b/src/pages/users/[userId]/settings/notifications/webpush.tsx index ddba1e3f5..2080dc84d 100644 --- a/src/pages/users/[userId]/settings/notifications/webpush.tsx +++ b/src/pages/users/[userId]/settings/notifications/webpush.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../../../components/UserProfile/UserSettings'; import UserNotificationSettings from '../../../../../components/UserProfile/UserSettings/UserNotificationSettings'; diff --git a/src/pages/users/[userId]/settings/password.tsx b/src/pages/users/[userId]/settings/password.tsx index ee1da6b9c..239f07a25 100644 --- a/src/pages/users/[userId]/settings/password.tsx +++ b/src/pages/users/[userId]/settings/password.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../../components/UserProfile/UserSettings'; import UserPasswordChange from '../../../../components/UserProfile/UserSettings/UserPasswordChange'; diff --git a/src/pages/users/[userId]/settings/permissions.tsx b/src/pages/users/[userId]/settings/permissions.tsx index 826689368..06fcf4d13 100644 --- a/src/pages/users/[userId]/settings/permissions.tsx +++ b/src/pages/users/[userId]/settings/permissions.tsx @@ -1,4 +1,4 @@ -import { NextPage } from 'next'; +import type { NextPage } from 'next'; import React from 'react'; import UserSettings from '../../../../components/UserProfile/UserSettings'; import UserPermissions from '../../../../components/UserProfile/UserSettings/UserPermissions'; diff --git a/src/types/react-intl-auto.d.ts b/src/types/react-intl-auto.d.ts index 232168602..7fa708a79 100644 --- a/src/types/react-intl-auto.d.ts +++ b/src/types/react-intl-auto.d.ts @@ -1,4 +1,4 @@ -import { MessageDescriptor } from 'react-intl'; +import type { MessageDescriptor } from 'react-intl'; declare module 'react-intl' { interface ExtractableMessage { diff --git a/src/utils/creditHelpers.ts b/src/utils/creditHelpers.ts index 929f529d0..a077a07e2 100644 --- a/src/utils/creditHelpers.ts +++ b/src/utils/creditHelpers.ts @@ -1,4 +1,4 @@ -import { Crew } from '../../server/models/common'; +import type { Crew } from '../../server/models/common'; const priorityJobs = [ 'Director', 'Creator', diff --git a/yarn.lock b/yarn.lock index a66ef1dfa..ec42882a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -981,6 +981,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" + integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" @@ -1337,19 +1344,19 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== -"@eslint/eslintrc@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.1.tgz#8b5e1c49f4077235516bc9ec7d41378c0f69b8c6" - integrity sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ== +"@eslint/eslintrc@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f" + integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.3.1" - globals "^13.9.0" + espree "^9.3.2" + globals "^13.15.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" - minimatch "^3.0.4" + minimatch "^3.1.2" strip-json-comments "^3.1.1" "@formatjs/ecma402-abstract@1.11.3": @@ -1360,6 +1367,14 @@ "@formatjs/intl-localematcher" "0.2.24" tslib "^2.1.0" +"@formatjs/ecma402-abstract@1.11.8": + version "1.11.8" + resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.8.tgz#f4015dfb6a837369d94c6ba82455c609e45bce20" + integrity sha512-fgLqyWlwmTEuqV/TSLEL/t9JOmHNLFvCdgzXB0jc2w+WOItPCOJ1T0eyN6fQBQKRPfSqqNlu+kWj7ijcOVTVVQ== + dependencies: + "@formatjs/intl-localematcher" "0.2.28" + tslib "2.4.0" + "@formatjs/ecma402-abstract@1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.4.0.tgz#ac6c17a8fffac43c6d68c849a7b732626d32654c" @@ -1390,6 +1405,23 @@ "@formatjs/icu-skeleton-parser" "1.3.5" tslib "^2.1.0" +"@formatjs/icu-messageformat-parser@2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.4.tgz#f1e32b9937f151c1dd5c30536ce3e920b7f23813" + integrity sha512-3PqMvKWV1oyok0BuiXUAHIaotdhdTJw6OICqCZbfUgKT+ZRwRWO4IlCgvXJeCITaKS5p+PY0XXKjf/vUyIpWjQ== + dependencies: + "@formatjs/ecma402-abstract" "1.11.8" + "@formatjs/icu-skeleton-parser" "1.3.10" + tslib "2.4.0" + +"@formatjs/icu-skeleton-parser@1.3.10": + version "1.3.10" + resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.10.tgz#2f504e56ac80137ee2baad55c7fa0b5dc7f4e4df" + integrity sha512-kXJmtLDqFF5aLTf8IxdJXnhrIX1Qb4Qp3a9jqRecGDYfzOa9hMhi9U0nKyhrJJ4cXxBzptcgb+LWkyeHL6nlBQ== + dependencies: + "@formatjs/ecma402-abstract" "1.11.8" + tslib "2.4.0" + "@formatjs/icu-skeleton-parser@1.3.5": version "1.3.5" resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.5.tgz#babc93a1c36383cf87cbb3d2f2145d26c2f7cb40" @@ -1423,6 +1455,13 @@ dependencies: tslib "^2.1.0" +"@formatjs/intl-localematcher@0.2.28": + version "0.2.28" + resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.2.28.tgz#412ea7fefbfc7ed33cd6b43aa304fc14d816e564" + integrity sha512-FLsc6Gifs1np/8HnCn/7Q+lHMmenrD5fuDhRT82yj0gi9O19kfaFwjQUw1gZsyILuRyT93GuzdifHj7TKRhBcw== + dependencies: + tslib "2.4.0" + "@formatjs/intl-numberformat@^5.5.2": version "5.7.6" resolved "https://registry.yarnpkg.com/@formatjs/intl-numberformat/-/intl-numberformat-5.7.6.tgz#630206bb0acefd2d508ccf4f82367c6875cad611" @@ -1453,15 +1492,17 @@ tslib "^2.0.1" typescript "^4.0" -"@formatjs/ts-transformer@3.9.2": - version "3.9.2" - resolved "https://registry.yarnpkg.com/@formatjs/ts-transformer/-/ts-transformer-3.9.2.tgz#958582b16ed9125fd904c051a9dda1ecd2474a5a" - integrity sha512-Iff+ca1ue3IOb/PDNANR6++EArwlyMpW+t6AL4MG5sordpgflsIh8BMz6nGs+/tUOjP0xioNAu/acYiQ+rW5Bw== +"@formatjs/ts-transformer@3.9.9": + version "3.9.9" + resolved "https://registry.yarnpkg.com/@formatjs/ts-transformer/-/ts-transformer-3.9.9.tgz#4804e0560e1944407e9dc5f5ae568a329d5668e4" + integrity sha512-V5BDpn5XW1wWBkpDn5SFHRH4Ndf3oyjxmuqWMxe2EwOKkV4XJvzZI73k3p/Hut3Xg55AxBQQmkFK9hyeBJPyIg== dependencies: - "@formatjs/icu-messageformat-parser" "2.0.18" + "@formatjs/icu-messageformat-parser" "2.1.4" + "@types/json-stable-stringify" "^1.0.32" "@types/node" "14 || 16 || 17" chalk "^4.0.0" - tslib "^2.1.0" + json-stable-stringify "^1.0.1" + tslib "2.4.0" typescript "^4.5" "@gar/promisify@^1.0.1": @@ -1491,15 +1532,20 @@ resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-1.0.6.tgz#35dd26987228b39ef2316db3b1245c42eb19e324" integrity sha512-JJCXydOFWMDpCP4q13iEplA503MQO3xLoZiKum+955ZCtHINWnx26CUxVxxFQu/uLb4LW3ge15ZpzIkXKkJ8oQ== -"@humanwhocodes/config-array@^0.9.2": - version "0.9.5" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7" - integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw== +"@humanwhocodes/config-array@^0.10.4": + version "0.10.4" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.10.4.tgz#01e7366e57d2ad104feea63e72248f22015c520c" + integrity sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw== dependencies: "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" minimatch "^3.0.4" +"@humanwhocodes/gitignore-to-minimatch@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz#316b0a63b91c10e53f242efb4ace5c3b34e8728d" + integrity sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA== + "@humanwhocodes/object-schema@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" @@ -1615,10 +1661,10 @@ resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.0.tgz#73713399399b34aa5a01771fb73272b55b22c314" integrity sha512-nrIgY6t17FQ9xxwH3jj0a6EOiQ/WDHUos35Hghtr+SWN/ntHIQ7UpuvSi0vaLzZVHQWaDupKI+liO5vANcDeTQ== -"@next/eslint-plugin-next@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.0.tgz#32586a11378b3ffa5a93ac40a3c44ad99d70e95a" - integrity sha512-WFiyvSM2G5cQmh32t/SiQuJ+I2O+FHVlK/RFw5b1565O2kEM/36EXncjt88Pa+X5oSc+1SS+tWxowWJd1lqI+g== +"@next/eslint-plugin-next@12.2.3": + version "12.2.3" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-12.2.3.tgz#63726691aac6a7f01b64190a0323d590a0e8154d" + integrity sha512-B2e8Yg1MpuLsGxhCx4rU8/Tcnr5wFmCx1O2eyLXBPnaCcsFXfGCo067ujagtDLtWASL3GNgzg78U1SB0dbc38A== dependencies: glob "7.1.7" @@ -2049,10 +2095,10 @@ "@react-spring/shared" "~9.4.4" "@react-spring/types" "~9.4.4" -"@rushstack/eslint-patch@^1.0.8": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz#7f698254aadf921e48dda8c0a6b304026b8a9323" - integrity sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A== +"@rushstack/eslint-patch@^1.1.3": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.1.4.tgz#0c8b74c50f29ee44f423f7416829c0bf8bb5eb27" + integrity sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA== "@selderee/plugin-htmlparser2@^0.6.0": version "0.6.0" @@ -2464,10 +2510,10 @@ "@types/nodemailer" "*" juice "^7.0.0" -"@types/eslint@8": - version "8.4.1" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.1.tgz#c48251553e8759db9e656de3efc846954ac32304" - integrity sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA== +"@types/eslint@7 || 8": + version "8.4.5" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.5.tgz#acdfb7dd36b91cc5d812d7c093811a8f3d9b31e4" + integrity sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ== dependencies: "@types/estree" "*" "@types/json-schema" "*" @@ -2535,6 +2581,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== +"@types/json-stable-stringify@^1.0.32": + version "1.0.34" + resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.0.34.tgz#c0fb25e4d957e0ee2e497c1f553d7f8bb668fd75" + integrity sha512-s2cfwagOQAS8o06TcwKfr9Wx11dNGbH2E9vJz1cqV+a/LOyhWNLUNd6JSRYNzvB4d29UuJX2M0Dj9vE1T8fRXw== + "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" @@ -2613,6 +2664,11 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/picomatch@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@types/picomatch/-/picomatch-2.3.0.tgz#75db5e75a713c5a83d5b76780c3da84a82806003" + integrity sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g== + "@types/prop-types@*": version "15.7.4" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" @@ -2743,7 +2799,7 @@ semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/parser@^5.0.0", "@typescript-eslint/parser@^5.14.0": +"@typescript-eslint/parser@^5.14.0": version "5.14.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.14.0.tgz#7c79f898aa3cff0ceee6f1d34eeed0f034fb9ef3" integrity sha512-aHJN8/FuIy1Zvqk4U/gcO/fxeMKyoSv/rS46UXMXOJKVsLQ+iYPuXNbpbH7cBLcpSbmyyFbwrniLx5+kutu1pw== @@ -2753,6 +2809,16 @@ "@typescript-eslint/typescript-estree" "5.14.0" debug "^4.3.2" +"@typescript-eslint/parser@^5.21.0": + version "5.32.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.32.0.tgz#1de243443bc6186fb153b9e395b842e46877ca5d" + integrity sha512-IxRtsehdGV9GFQ35IGm5oKKR2OGcazUoiNBxhRV160iF9FoyuXxjY+rIqs1gfnd+4eL98OjeGnMpE7RF/NBb3A== + dependencies: + "@typescript-eslint/scope-manager" "5.32.0" + "@typescript-eslint/types" "5.32.0" + "@typescript-eslint/typescript-estree" "5.32.0" + debug "^4.3.4" + "@typescript-eslint/scope-manager@5.14.0": version "5.14.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.14.0.tgz#ea518962b42db8ed0a55152ea959c218cb53ca7b" @@ -2761,6 +2827,14 @@ "@typescript-eslint/types" "5.14.0" "@typescript-eslint/visitor-keys" "5.14.0" +"@typescript-eslint/scope-manager@5.32.0": + version "5.32.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.32.0.tgz#763386e963a8def470580cc36cf9228864190b95" + integrity sha512-KyAE+tUON0D7tNz92p1uetRqVJiiAkeluvwvZOqBmW9z2XApmk5WSMV9FrzOroAcVxJZB3GfUwVKr98Dr/OjOg== + dependencies: + "@typescript-eslint/types" "5.32.0" + "@typescript-eslint/visitor-keys" "5.32.0" + "@typescript-eslint/type-utils@5.14.0": version "5.14.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.14.0.tgz#711f08105860b12988454e91df433567205a8f0b" @@ -2775,6 +2849,11 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.14.0.tgz#96317cf116cea4befabc0defef371a1013f8ab11" integrity sha512-BR6Y9eE9360LNnW3eEUqAg6HxS9Q35kSIs4rp4vNHRdfg0s+/PgHgskvu5DFTM7G5VKAVjuyaN476LCPrdA7Mw== +"@typescript-eslint/types@5.32.0": + version "5.32.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.32.0.tgz#484273021eeeae87ddb288f39586ef5efeb6dcd8" + integrity sha512-EBUKs68DOcT/EjGfzywp+f8wG9Zw6gj6BjWu7KV/IYllqKJFPlZlLSYw/PTvVyiRw50t6wVbgv4p9uE2h6sZrQ== + "@typescript-eslint/typescript-estree@5.14.0", "@typescript-eslint/typescript-estree@^5.9.1": version "5.14.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.14.0.tgz#78b7f7385d5b6f2748aacea5c9b7f6ae62058314" @@ -2788,6 +2867,19 @@ semver "^7.3.5" tsutils "^3.21.0" +"@typescript-eslint/typescript-estree@5.32.0": + version "5.32.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.32.0.tgz#282943f34babf07a4afa7b0ff347a8e7b6030d12" + integrity sha512-ZVAUkvPk3ITGtCLU5J4atCw9RTxK+SRc6hXqLtllC2sGSeMFWN+YwbiJR9CFrSFJ3w4SJfcWtDwNb/DmUIHdhg== + dependencies: + "@typescript-eslint/types" "5.32.0" + "@typescript-eslint/visitor-keys" "5.32.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + "@typescript-eslint/utils@5.14.0": version "5.14.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.14.0.tgz#6c8bc4f384298cbbb32b3629ba7415f9f80dc8c4" @@ -2808,6 +2900,14 @@ "@typescript-eslint/types" "5.14.0" eslint-visitor-keys "^3.0.0" +"@typescript-eslint/visitor-keys@5.32.0": + version "5.32.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.32.0.tgz#b9715d0b11fdb5dd10fd0c42ff13987470525394" + integrity sha512-S54xOHZgfThiZ38/ZGTgB2rqx51CMJ5MCfVT2IplK4Q7hgzGfe0nLzLCcenDnc/cSjP568hdeKfeDcBgqNHD/g== + dependencies: + "@typescript-eslint/types" "5.32.0" + eslint-visitor-keys "^3.3.0" + JSONStream@^1.0.4: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -2834,12 +2934,12 @@ ace-builds@^1.4.13, ace-builds@^1.4.14: resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.4.14.tgz#2c41ccbccdd09e665d3489f161a20baeb3a3c852" integrity sha512-NBOQlm9+7RBqRqZwimpgquaLeTJFayqb9UEPtTkpC3TkkwDnlsT/TwsCC0svjt9kEZ6G9mH5AEOHSz6Q/HrzQQ== -acorn-jsx@^5.3.1: +acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-node@^1.6.1: +acorn-node@^1.8.2: version "1.8.2" resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== @@ -2863,11 +2963,16 @@ acorn@^7.0.0, acorn@^7.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.4.1, acorn@^8.7.0: +acorn@^8.4.1: version "8.7.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== +acorn@^8.8.0: + version "8.8.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" + integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== + agent-base@6, agent-base@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -3083,10 +3188,10 @@ arg@^4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== -arg@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.1.tgz#eb0c9a8f77786cad2af8ff2b862899842d7b6adb" - integrity sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA== +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== argparse@^1.0.7: version "1.0.10" @@ -3139,6 +3244,17 @@ array-includes@^3.1.3, array-includes@^3.1.4: get-intrinsic "^1.1.1" is-string "^1.0.7" +array-includes@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb" + integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + get-intrinsic "^1.1.1" + is-string "^1.0.7" + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -3153,14 +3269,15 @@ array.prototype.flat@^1.2.5: define-properties "^1.1.3" es-abstract "^1.19.0" -array.prototype.flatmap@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz#908dc82d8a406930fdf38598d51e7411d18d4446" - integrity sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA== +array.prototype.flatmap@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz#a7e8ed4225f4788a70cd910abcf0791e76a5534f" + integrity sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg== dependencies: - call-bind "^1.0.0" + call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.19.0" + es-abstract "^1.19.2" + es-shim-unscopables "^1.0.0" arrify@^1.0.1: version "1.0.1" @@ -3256,6 +3373,11 @@ axe-core@^4.3.5: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.1.tgz#7dbdc25989298f9ad006645cd396782443757413" integrity sha512-gd1kmb21kwNuWr6BQz8fv6GNECPBnUasepcoLbekws23NVBLODdsClRZ+bQ8+9Uomf3Sm3+Vwn0oYG9NvwnJCw== +axe-core@^4.4.3: + version "4.4.3" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.3.tgz#11c74d23d5013c0fa5d183796729bc3482bd2f6f" + integrity sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w== + axios-rate-limit@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/axios-rate-limit/-/axios-rate-limit-1.3.0.tgz#03241d24c231c47432dab6e8234cfde819253c2e" @@ -4392,7 +4514,7 @@ cz-conventional-changelog@^3.3.0: optionalDependencies: "@commitlint/load" ">6.1.1" -damerau-levenshtein@^1.0.7: +damerau-levenshtein@^1.0.7, damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== @@ -4440,6 +4562,13 @@ debug@^3.2.6, debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -4516,6 +4645,14 @@ define-properties@^1.1.3: dependencies: object-keys "^1.0.12" +define-properties@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" + integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + defined@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" @@ -4590,14 +4727,14 @@ detect-libc@^1.0.2, detect-libc@^1.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= -detective@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.0.tgz#feb2a77e85b904ecdea459ad897cc90a99bd2a7b" - integrity sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg== +detective@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.1.tgz#6af01eeda11015acb0e73f933242b70f24f91034" + integrity sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw== dependencies: - acorn-node "^1.6.1" + acorn-node "^1.8.2" defined "^1.0.0" - minimist "^1.1.1" + minimist "^1.2.6" dezalgo@^1.0.0: version "1.0.3" @@ -4891,6 +5028,42 @@ es-abstract@^1.19.0, es-abstract@^1.19.1: string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.1" +es-abstract@^1.19.2, es-abstract@^1.19.5: + version "1.20.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" + integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + is-callable "^1.2.4" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-weakref "^1.0.2" + object-inspect "^1.12.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + regexp.prototype.flags "^1.4.3" + string.prototype.trimend "^1.0.5" + string.prototype.trimstart "^1.0.5" + unbox-primitive "^1.0.2" + +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -4930,27 +5103,27 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -eslint-config-next@^12.1.0: - version "12.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-12.1.0.tgz#8ace680dc5207e6ab6c915f3989adec122f582e7" - integrity sha512-tBhuUgoDITcdcM7xFvensi9I5WTI4dnvH4ETGRg1U8ZKpXrZsWQFdOKIDzR3RLP5HR3xXrLviaMM4c3zVoE/pA== +eslint-config-next@^12.2.3: + version "12.2.3" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-12.2.3.tgz#468fe9756ccbf7e4452139062db5b4e6557dc885" + integrity sha512-xAQqAqwa2bu9ZMRypz58ym4tNCo22Wc6LuoLpbpf3yW5c4ZkVib9934AgGDDvh2zKrP56Z6X0Pp6gNnuuZzcRw== dependencies: - "@next/eslint-plugin-next" "12.1.0" - "@rushstack/eslint-patch" "^1.0.8" - "@typescript-eslint/parser" "^5.0.0" - eslint-import-resolver-node "^0.3.4" - eslint-import-resolver-typescript "^2.4.0" - eslint-plugin-import "^2.25.2" + "@next/eslint-plugin-next" "12.2.3" + "@rushstack/eslint-patch" "^1.1.3" + "@typescript-eslint/parser" "^5.21.0" + eslint-import-resolver-node "^0.3.6" + eslint-import-resolver-typescript "^2.7.1" + eslint-plugin-import "^2.26.0" eslint-plugin-jsx-a11y "^6.5.1" - eslint-plugin-react "^7.27.0" - eslint-plugin-react-hooks "^4.3.0" + eslint-plugin-react "^7.29.4" + eslint-plugin-react-hooks "^4.5.0" eslint-config-prettier@^8.5.0: version "8.5.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== -eslint-import-resolver-node@^0.3.4, eslint-import-resolver-node@^0.3.6: +eslint-import-resolver-node@^0.3.6: version "0.3.6" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== @@ -4958,18 +5131,18 @@ eslint-import-resolver-node@^0.3.4, eslint-import-resolver-node@^0.3.6: debug "^3.2.7" resolve "^1.20.0" -eslint-import-resolver-typescript@^2.4.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz#07661966b272d14ba97f597b51e1a588f9722f0a" - integrity sha512-qZ6e5CFr+I7K4VVhQu3M/9xGv9/YmwsEXrsm3nimw8vWaVHRDrQRp26BgCypTxBp3vUp4o5aVEJRiy0F2DFddQ== +eslint-import-resolver-typescript@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz#a90a4a1c80da8d632df25994c4c5fdcdd02b8751" + integrity sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ== dependencies: - debug "^4.3.1" - glob "^7.1.7" - is-glob "^4.0.1" - resolve "^1.20.0" - tsconfig-paths "^3.9.0" + debug "^4.3.4" + glob "^7.2.0" + is-glob "^4.0.3" + resolve "^1.22.0" + tsconfig-paths "^3.14.1" -eslint-module-utils@^2.7.2: +eslint-module-utils@^2.7.3: version "2.7.3" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== @@ -4977,37 +5150,39 @@ eslint-module-utils@^2.7.2: debug "^3.2.7" find-up "^2.1.0" -eslint-plugin-formatjs@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-formatjs/-/eslint-plugin-formatjs-3.0.0.tgz#1864f82a90fa17b4c0e4f901f56bd81e35bdfa78" - integrity sha512-htZef5UkVG1I4MkleWXGYsQRrUo+2+iruxPRlqEMN4skrDNOZmp78/KPcaFGhQ1oaaI673y89gEgDZdq+chHsQ== +eslint-plugin-formatjs@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-formatjs/-/eslint-plugin-formatjs-4.0.2.tgz#63e92f634bc2c824ee423ad30f29685c895ff555" + integrity sha512-6tEtLtH4N39pnYp5MjelxHDdohU6yMp5fmXqQq4Uu8MAbkAWAiufNKXfFdLSjuPa0lY5x/ExXKJHr1NJsZtzVA== dependencies: - "@formatjs/icu-messageformat-parser" "2.0.18" - "@formatjs/ts-transformer" "3.9.2" - "@types/eslint" "8" + "@formatjs/icu-messageformat-parser" "2.1.4" + "@formatjs/ts-transformer" "3.9.9" + "@types/eslint" "7 || 8" + "@types/picomatch" "^2.3.0" "@typescript-eslint/typescript-estree" "^5.9.1" emoji-regex "^10.0.0" - tslib "^2.1.0" + picomatch "^2.3.1" + tslib "2.4.0" typescript "^4.5" -eslint-plugin-import@^2.25.2: - version "2.25.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz#322f3f916a4e9e991ac7af32032c25ce313209f1" - integrity sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA== +eslint-plugin-import@^2.26.0: + version "2.26.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" + integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== dependencies: array-includes "^3.1.4" array.prototype.flat "^1.2.5" debug "^2.6.9" doctrine "^2.1.0" eslint-import-resolver-node "^0.3.6" - eslint-module-utils "^2.7.2" + eslint-module-utils "^2.7.3" has "^1.0.3" - is-core-module "^2.8.0" + is-core-module "^2.8.1" is-glob "^4.0.3" - minimatch "^3.0.4" + minimatch "^3.1.2" object.values "^1.1.5" - resolve "^1.20.0" - tsconfig-paths "^3.12.0" + resolve "^1.22.0" + tsconfig-paths "^3.14.1" eslint-plugin-jsx-a11y@^6.5.1: version "6.5.1" @@ -5027,37 +5202,56 @@ eslint-plugin-jsx-a11y@^6.5.1: language-tags "^1.0.5" minimatch "^3.0.4" -eslint-plugin-prettier@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz#8b99d1e4b8b24a762472b4567992023619cb98e0" - integrity sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ== +eslint-plugin-jsx-a11y@^6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz#93736fc91b83fdc38cc8d115deedfc3091aef1ff" + integrity sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q== + dependencies: + "@babel/runtime" "^7.18.9" + aria-query "^4.2.2" + array-includes "^3.1.5" + ast-types-flow "^0.0.7" + axe-core "^4.4.3" + axobject-query "^2.2.0" + damerau-levenshtein "^1.0.8" + emoji-regex "^9.2.2" + has "^1.0.3" + jsx-ast-utils "^3.3.2" + language-tags "^1.0.5" + minimatch "^3.1.2" + semver "^6.3.0" + +eslint-plugin-prettier@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" + integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== dependencies: prettier-linter-helpers "^1.0.0" -eslint-plugin-react-hooks@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz#318dbf312e06fab1c835a4abef00121751ac1172" - integrity sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA== +eslint-plugin-react-hooks@^4.5.0, eslint-plugin-react-hooks@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" + integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== -eslint-plugin-react@^7.27.0, eslint-plugin-react@^7.29.3: - version "7.29.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.29.3.tgz#f4eab757f2756d25d6d4c2a58a9e20b004791f05" - integrity sha512-MzW6TuCnDOcta67CkpDyRfRsEVx9FNMDV8wZsDqe1luHPdGTrQIUaUXD27Ja3gHsdOIs/cXzNchWGlqm+qRVRg== +eslint-plugin-react@^7.29.4, eslint-plugin-react@^7.30.1: + version "7.30.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.30.1.tgz#2be4ab23ce09b5949c6631413ba64b2810fd3e22" + integrity sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg== dependencies: - array-includes "^3.1.4" - array.prototype.flatmap "^1.2.5" + array-includes "^3.1.5" + array.prototype.flatmap "^1.3.0" doctrine "^2.1.0" estraverse "^5.3.0" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.1.2" object.entries "^1.1.5" object.fromentries "^2.0.5" - object.hasown "^1.1.0" + object.hasown "^1.1.1" object.values "^1.1.5" prop-types "^15.8.1" resolve "^2.0.0-next.3" semver "^6.3.0" - string.prototype.matchall "^4.0.6" + string.prototype.matchall "^4.0.7" eslint-scope@^5.1.1: version "5.1.1" @@ -5092,13 +5286,14 @@ eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== -eslint@^8.11.0: - version "8.11.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.11.0.tgz#88b91cfba1356fc10bb9eb592958457dfe09fb37" - integrity sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA== +eslint@^8.21.0: + version "8.21.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.21.0.tgz#1940a68d7e0573cef6f50037addee295ff9be9ef" + integrity sha512-/XJ1+Qurf1T9G2M5IHrsjp+xrGT73RZf23xA1z5wB1ZzzEAWSZKvRwhWxTFp1rvkvCfwcvAUNAP31bhKTTGfDA== dependencies: - "@eslint/eslintrc" "^1.2.1" - "@humanwhocodes/config-array" "^0.9.2" + "@eslint/eslintrc" "^1.3.0" + "@humanwhocodes/config-array" "^0.10.4" + "@humanwhocodes/gitignore-to-minimatch" "^1.0.2" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -5108,14 +5303,17 @@ eslint@^8.11.0: eslint-scope "^7.1.1" eslint-utils "^3.0.0" eslint-visitor-keys "^3.3.0" - espree "^9.3.1" + espree "^9.3.3" esquery "^1.4.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" + find-up "^5.0.0" functional-red-black-tree "^1.0.1" glob-parent "^6.0.1" - globals "^13.6.0" + globals "^13.15.0" + globby "^11.1.0" + grapheme-splitter "^1.0.4" ignore "^5.2.0" import-fresh "^3.0.0" imurmurhash "^0.1.4" @@ -5124,7 +5322,7 @@ eslint@^8.11.0: json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" lodash.merge "^4.6.2" - minimatch "^3.0.4" + minimatch "^3.1.2" natural-compare "^1.4.0" optionator "^0.9.1" regexpp "^3.2.0" @@ -5133,13 +5331,13 @@ eslint@^8.11.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^9.3.1: - version "9.3.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd" - integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ== +espree@^9.3.2, espree@^9.3.3: + version "9.3.3" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.3.tgz#2dd37c4162bb05f433ad3c1a52ddf8a49dc08e9d" + integrity sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng== dependencies: - acorn "^8.7.0" - acorn-jsx "^5.3.1" + acorn "^8.8.0" + acorn-jsx "^5.3.2" eslint-visitor-keys "^3.3.0" esprima@^4.0.0, esprima@~4.0.0: @@ -5622,11 +5820,26 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +functions-have-names@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + gauge@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" @@ -5791,7 +6004,7 @@ glob@7.1.7: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.5, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7, glob@^7.2.0: +glob@^7.0.0, glob@^7.0.5, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== @@ -5842,14 +6055,14 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.6.0, globals@^13.9.0: - version "13.12.1" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.12.1.tgz#ec206be932e6c77236677127577aa8e50bf1c5cb" - integrity sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw== +globals@^13.15.0: + version "13.17.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" + integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== dependencies: type-fest "^0.20.2" -globby@^11.0.0, globby@^11.0.1, globby@^11.0.4: +globby@^11.0.0, globby@^11.0.1, globby@^11.0.4, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -5883,6 +6096,11 @@ graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== + gravatar-url@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/gravatar-url/-/gravatar-url-3.1.0.tgz#0cbeedab7c00a7bc9b627b3716e331359efcc999" @@ -5926,6 +6144,11 @@ has-bigints@^1.0.1: resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== +has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -5936,7 +6159,14 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbols@^1.0.1, has-symbols@^1.0.2: +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== @@ -6420,13 +6650,20 @@ is-cidr@^4.0.2: dependencies: cidr-regex "^3.1.1" -is-core-module@^2.2.0, is-core-module@^2.5.0, is-core-module@^2.8.0, is-core-module@^2.8.1: +is-core-module@^2.2.0, is-core-module@^2.5.0, is-core-module@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== dependencies: has "^1.0.3" +is-core-module@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" + integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== + dependencies: + has "^1.0.3" + is-date-object@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" @@ -6502,7 +6739,7 @@ is-nan@^1.3.2: call-bind "^1.0.0" define-properties "^1.1.3" -is-negative-zero@^2.0.1: +is-negative-zero@^2.0.1, is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== @@ -6577,6 +6814,13 @@ is-shared-array-buffer@^1.0.1: resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" @@ -6613,7 +6857,7 @@ is-utf8@^0.2.1: resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= -is-weakref@^1.0.1: +is-weakref@^1.0.1, is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== @@ -6755,6 +6999,13 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + integrity sha512-i/J297TW6xyj7sDFa7AmBPkQvLIxWr2kKPWI26tXydnZrzVAocNqn5DMNT1Mzk0vit1V5UkRM7C1KdVNp7Lmcg== + dependencies: + jsonify "~0.0.0" + json-stringify-nice@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz#2c937962b80181d3f317dd39aa323e14f5a60a67" @@ -6795,6 +7046,11 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha512-trvBk1ki43VZptdBI5rIlG4YOzyeH/WefQt5rj1grasPn4iiZWKet8nkgc4GlsAylaztn0qZfUYOiTsASJFdNA== + jsonparse@^1.2.0, jsonparse@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" @@ -6826,6 +7082,14 @@ jstransformer@1.0.0: array-includes "^3.1.3" object.assign "^4.1.2" +jsx-ast-utils@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz#afe5efe4332cd3515c065072bd4d6b0aa22152bd" + integrity sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q== + dependencies: + array-includes "^3.1.5" + object.assign "^4.1.2" + juice@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/juice/-/juice-7.0.0.tgz#509bed6adbb6e4bbaa7fbfadac4e2e83e8c89ba3" @@ -7045,11 +7309,16 @@ libqp@1.1.0: resolved "https://registry.yarnpkg.com/libqp/-/libqp-1.1.0.tgz#f5e6e06ad74b794fb5b5b66988bf728ef1dedbe8" integrity sha1-9ebgatdLeU+1tbZpiL9yjvHe2+g= -lilconfig@2.0.4, lilconfig@^2.0.4: +lilconfig@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082" integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA== +lilconfig@^2.0.5, lilconfig@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4" + integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg== + line-height@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/line-height/-/line-height-0.3.1.tgz#4b1205edde182872a5efa3c8f620b3187a9c54c9" @@ -7856,11 +8125,16 @@ minimist-options@4.1.0, minimist-options@^4.0.2: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@1.2.5, minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: +minimist@1.2.5, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minimist@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + minipass-collect@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" @@ -8069,6 +8343,11 @@ nanoid@^3.1.30, nanoid@^3.3.1: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== +nanoid@^3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" + integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -8541,11 +8820,16 @@ object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= -object-hash@^2.0.1, object-hash@^2.2.0: +object-hash@^2.0.1: version "2.2.0" resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + object-inspect@^1.11.0, object-inspect@^1.12.0, object-inspect@^1.9.0: version "1.12.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" @@ -8584,13 +8868,13 @@ object.fromentries@^2.0.5: define-properties "^1.1.3" es-abstract "^1.19.1" -object.hasown@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.0.tgz#7232ed266f34d197d15cac5880232f7a4790afe5" - integrity sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg== +object.hasown@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.1.tgz#ad1eecc60d03f49460600430d97f23882cf592a3" + integrity sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A== dependencies: - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.19.5" object.values@^1.1.5: version "1.1.5" @@ -8960,11 +9244,16 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" @@ -9014,6 +9303,15 @@ plex-api@^5.3.2: uuid "^3.0.0" xml2js "0.4.16" +postcss-import@^14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.1.0.tgz#a7333ffe32f0b8795303ee9e40215dac922781f0" + integrity sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw== + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + postcss-js@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.0.tgz#31db79889531b80dc7bc9b0ad283e418dce0ac00" @@ -9021,12 +9319,12 @@ postcss-js@^4.0.0: dependencies: camelcase-css "^2.0.1" -postcss-load-config@^3.1.0: - version "3.1.3" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.3.tgz#21935b2c43b9a86e6581a576ca7ee1bde2bd1d23" - integrity sha512-5EYgaM9auHGtO//ljHH+v/aC/TQ5LHXtL7bQajNAUBKUVKiYE8rYpFms7+V26D9FncaGe2zwCoPQsFKb5zF/Hw== +postcss-load-config@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855" + integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg== dependencies: - lilconfig "^2.0.4" + lilconfig "^2.0.5" yaml "^1.10.2" postcss-nested@5.0.6: @@ -9036,7 +9334,15 @@ postcss-nested@5.0.6: dependencies: postcss-selector-parser "^6.0.6" -postcss-selector-parser@^6.0.6, postcss-selector-parser@^6.0.9: +postcss-selector-parser@^6.0.10: + version "6.0.10" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" + integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-selector-parser@^6.0.6: version "6.0.9" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz#ee71c3b9ff63d9cd130838876c13a2ec1a992b2f" integrity sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ== @@ -9044,7 +9350,7 @@ postcss-selector-parser@^6.0.6, postcss-selector-parser@^6.0.9: cssesc "^3.0.0" util-deprecate "^1.0.2" -postcss-value-parser@^4.2.0: +postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== @@ -9058,7 +9364,16 @@ postcss@8.4.5: picocolors "^1.0.0" source-map-js "^1.0.1" -postcss@^8.4.6, postcss@^8.4.8: +postcss@^8.4.14: + version "8.4.14" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" + integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== + dependencies: + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +postcss@^8.4.8: version "8.4.8" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.8.tgz#dad963a76e82c081a0657d3a2f3602ce10c2e032" integrity sha512-2tXEqGxrjvAO6U+CJzDL2Fk2kPHTv1jQsYkSoMeOis2SsYaXRO2COxTdQp99cYvif9JTXaAk9lYGc3VhJt7JPQ== @@ -9084,15 +9399,20 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier-plugin-tailwindcss@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.1.8.tgz#ba0f606ed91959ede670303d905b99106e9e6293" - integrity sha512-hwarSBCswAXa+kqYtaAkFr3Vop9o04WOyZs0qo3NyvW8L7f1rif61wRyq0+ArmVThOuRBcJF5hjGXYk86cwemg== +prettier-plugin-organize-imports@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.0.1.tgz#52e1faa57742ad36925b40ddcd7c044cb527af22" + integrity sha512-pdMWKtoHRQ+9y1WB3mA1wzMkjzJzB4ycfOPlfZpmzq+YFZQIpkszJJQQHQ8EYVyULlLohlOnAxSmXfaKWpRAAw== -prettier@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" - integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== +prettier-plugin-tailwindcss@^0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.1.13.tgz#ca1071361dc7e2ed5d95a2ee36825ce45f814942" + integrity sha512-/EKQURUrxLu66CMUg4+1LwGdxnz8of7IDvrSLqEtDqhLH61SAlNNUSr90UTvZaemujgl3OH/VHg+fyGltrNixw== + +prettier@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" + integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== preview-email@^3.0.5: version "3.0.5" @@ -9559,6 +9879,13 @@ read-babelrc-up@^1.1.0: find-up "^4.1.0" json5 "^2.1.2" +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== + dependencies: + pify "^2.3.0" + read-cmd-shim@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz#4a50a71d6f0965364938e9038476f7eede3928d9" @@ -9711,13 +10038,14 @@ regenerator-transform@^0.14.2: dependencies: "@babel/runtime" "^7.8.4" -regexp.prototype.flags@^1.3.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307" - integrity sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ== +regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" + integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" + functions-have-names "^1.2.2" regexpp@^3.2.0: version "3.2.0" @@ -9864,6 +10192,15 @@ resolve-global@1.0.0, resolve-global@^1.0.0: dependencies: global-dirs "^0.1.1" +resolve@^1.1.7, resolve@^1.22.1: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^1.10.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.15.1, resolve@^1.20.0, resolve@^1.22.0: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" @@ -10141,6 +10478,13 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.3.7: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + dependencies: + lru-cache "^6.0.0" + send@0.17.2: version "0.17.2" resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" @@ -10492,18 +10836,18 @@ string-width@^5.0.0: emoji-regex "^9.2.2" strip-ansi "^7.0.1" -string.prototype.matchall@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz#5abb5dabc94c7b0ea2380f65ba610b3a544b15fa" - integrity sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg== +string.prototype.matchall@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" + integrity sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" es-abstract "^1.19.1" get-intrinsic "^1.1.1" - has-symbols "^1.0.2" + has-symbols "^1.0.3" internal-slot "^1.0.3" - regexp.prototype.flags "^1.3.1" + regexp.prototype.flags "^1.4.1" side-channel "^1.0.4" string.prototype.trimend@^1.0.4: @@ -10514,6 +10858,15 @@ string.prototype.trimend@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" +string.prototype.trimend@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" + integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + string.prototype.trimstart@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" @@ -10522,6 +10875,15 @@ string.prototype.trimstart@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" +string.prototype.trimstart@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" + integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -10702,32 +11064,33 @@ swr@^1.2.2: resolved "https://registry.yarnpkg.com/swr/-/swr-1.2.2.tgz#6cae09928d30593a7980d80f85823e57468fac5d" integrity sha512-ky0BskS/V47GpW8d6RU7CPsr6J8cr7mQD6+do5eky3bM0IyJaoi3vO8UhvrzJaObuTlGhPl2szodeB2dUd76Xw== -tailwindcss@^3.0.23: - version "3.0.23" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.0.23.tgz#c620521d53a289650872a66adfcb4129d2200d10" - integrity sha512-+OZOV9ubyQ6oI2BXEhzw4HrqvgcARY38xv3zKcjnWtMIZstEsXdI9xftd1iB7+RbOnj2HOEzkA0OyB5BaSxPQA== +tailwindcss@^3.1.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.1.7.tgz#ce99425f30a74e01457a2e6a724463b0df3159ac" + integrity sha512-r7mgumZ3k0InfVPpGWcX8X/Ut4xBfv+1O/+C73ar/m01LxGVzWvPxF/w6xIUPEztrCoz7axfx0SMdh8FH8ZvRQ== dependencies: - arg "^5.0.1" - chalk "^4.1.2" + arg "^5.0.2" chokidar "^3.5.3" color-name "^1.1.4" - cosmiconfig "^7.0.1" - detective "^5.2.0" + detective "^5.2.1" didyoumean "^1.2.2" dlv "^1.1.3" fast-glob "^3.2.11" glob-parent "^6.0.2" is-glob "^4.0.3" + lilconfig "^2.0.6" normalize-path "^3.0.0" - object-hash "^2.2.0" - postcss "^8.4.6" + object-hash "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.14" + postcss-import "^14.1.0" postcss-js "^4.0.0" - postcss-load-config "^3.1.0" + postcss-load-config "^3.1.4" postcss-nested "5.0.6" - postcss-selector-parser "^6.0.9" + postcss-selector-parser "^6.0.10" postcss-value-parser "^4.2.0" quick-lru "^5.1.1" - resolve "^1.22.0" + resolve "^1.22.1" tar@^4: version "4.4.19" @@ -10957,16 +11320,21 @@ ts-node@^10.6.0, ts-node@^10.7.0: v8-compile-cache-lib "^3.0.0" yn "3.1.1" -tsconfig-paths@^3.12.0, tsconfig-paths@^3.9.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.13.0.tgz#f3e9b8f6876698581d94470c03c95b3a48c0e3d7" - integrity sha512-nWuffZppoaYK0vQ1SQmkSsQzJoHA4s6uzdb2waRpD806x9yfq153AdVsWz4je2qZcW+pENrMQXbGQ3sMCkXuhw== +tsconfig-paths@^3.14.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" + integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.1" - minimist "^1.2.0" + minimist "^1.2.6" strip-bom "^3.0.0" +tslib@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -11091,11 +11459,16 @@ typeorm@0.2.45: yargs "^17.0.1" zen-observable-ts "^1.0.0" -typescript@^4.0, typescript@^4.4.3, typescript@^4.5, typescript@^4.6.2: +typescript@^4.0, typescript@^4.4.3, typescript@^4.5: version "4.6.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4" integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== +typescript@^4.7.4: + version "4.7.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" + integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== + uc.micro@^1.0.1: version "1.0.6" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" @@ -11123,6 +11496,16 @@ unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + undefsafe@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" From b33956e6b85b2c2fc12e23951c06f36e009fb627 Mon Sep 17 00:00:00 2001 From: TheMeanCanEHdian Date: Tue, 2 Aug 2022 23:13:21 -0500 Subject: [PATCH 09/81] feat: add 20th Century Studios to Studio Slider (#2288) * Add 20th Century Studios to Studio Sliders * Remove 20th Century Fox from Studio Sliders Co-authored-by: TheCatLady <52870424+TheCatLady@users.noreply.github.com> Co-authored-by: Ryan Cohen --- src/components/Discover/StudioSlider/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Discover/StudioSlider/index.tsx b/src/components/Discover/StudioSlider/index.tsx index 59f0e8c07..3e040643b 100644 --- a/src/components/Discover/StudioSlider/index.tsx +++ b/src/components/Discover/StudioSlider/index.tsx @@ -21,10 +21,10 @@ const studios: Studio[] = [ url: '/discover/movies/studio/2', }, { - name: '20th Century Fox', + name: '20th Century Studios', image: - 'https://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/qZCc1lty5FzX30aOCVRBLzaVmcp.png', - url: '/discover/movies/studio/25', + 'https://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)/h0rjX5vjW5r8yEnUBStFarjcLT4.png', + url: '/discover/movies/studio/127928', }, { name: 'Sony Pictures', From db898db9f2b7da9b3434b4f4198803d4939cb0e1 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 4 Aug 2022 16:05:30 +0900 Subject: [PATCH 10/81] docs: add TheMeanCanEHdian as a contributor for code (#2889) [skip ci] * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ README.md | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index fe52e371e..fabe31882 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -683,6 +683,15 @@ "contributions": [ "code" ] + }, + { + "login": "TheMeanCanEHdian", + "name": "TheMeanCanEHdian", + "avatar_url": "https://avatars.githubusercontent.com/u/16025103?v=4", + "profile": "https://github.com/TheMeanCanEHdian", + "contributions": [ + "code" + ] } ], "badgeTemplate": "\"All-orange.svg\"/>", diff --git a/README.md b/README.md index c99323ba8..9a8c43fe9 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Language grade: JavaScript GitHub -All Contributors +All Contributors

@@ -169,6 +169,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Maxent

🌍
Samuel Bartík

💻
Chun Yeung Wong

💻 +
TheMeanCanEHdian

💻 From 22ec058431e07de625e0687e65e801aa81f68c6d Mon Sep 17 00:00:00 2001 From: Ryan Cohen Date: Mon, 15 Aug 2022 08:34:38 +0900 Subject: [PATCH 11/81] test: add cypress foundation (#2903) [skip ci] --- .dockerignore | 1 + .github/workflows/cypress.yml | 30 ++ .gitignore | 5 + cypress.config.ts | 15 + cypress/config/settings.cypress.json | 149 ++++++++++ cypress/e2e/discover.cy.ts | 44 +++ cypress/e2e/login.cy.ts | 13 + cypress/e2e/movie-details.cy.ts | 12 + cypress/e2e/tv-details.cy.ts | 12 + cypress/e2e/user/user-list.cy.ts | 67 +++++ cypress/support/commands.ts | 26 ++ cypress/support/e2e.ts | 7 + cypress/support/index.ts | 12 + cypress/tsconfig.json | 10 + package.json | 6 +- server/scripts/prepareTestDb.ts | 53 ++++ src/components/Common/Header/index.tsx | 5 +- src/components/Common/Modal/index.tsx | 4 + src/components/Layout/Sidebar/index.tsx | 4 + src/components/Layout/index.tsx | 1 + src/components/Login/LocalLogin.tsx | 3 + src/components/MovieDetails/index.tsx | 2 +- src/components/Slider/index.tsx | 2 +- src/components/TitleCard/index.tsx | 6 +- src/components/TvDetails/index.tsx | 2 +- src/components/UserList/index.tsx | 2 +- yarn.lock | 372 +++++++++++++++++++++++- 27 files changed, 848 insertions(+), 17 deletions(-) create mode 100644 .github/workflows/cypress.yml create mode 100644 cypress.config.ts create mode 100644 cypress/config/settings.cypress.json create mode 100644 cypress/e2e/discover.cy.ts create mode 100644 cypress/e2e/login.cy.ts create mode 100644 cypress/e2e/movie-details.cy.ts create mode 100644 cypress/e2e/tv-details.cy.ts create mode 100644 cypress/e2e/user/user-list.cy.ts create mode 100644 cypress/support/commands.ts create mode 100644 cypress/support/e2e.ts create mode 100644 cypress/support/index.ts create mode 100644 cypress/tsconfig.json create mode 100644 server/scripts/prepareTestDb.ts diff --git a/.dockerignore b/.dockerignore index 7d669c86d..21a5da869 100644 --- a/.dockerignore +++ b/.dockerignore @@ -26,3 +26,4 @@ public/os_logo_filled.png public/preview.jpg snap stylelint.config.js +cypress diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml new file mode 100644 index 000000000..8a0ac34ff --- /dev/null +++ b/.github/workflows/cypress.yml @@ -0,0 +1,30 @@ +name: Cypress Tests + +on: + pull_request: + branches: + - '*' + push: + branches: + - develop + +jobs: + cypress-run: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Cypress run + uses: cypress-io/github-action@v4 + with: + build: yarn cypress:build + start: yarn start + wait-on: 'http://localhost:5055' + record: true + env: + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + WITH_MIGRATIONS: true + # Fix test titles in cypress dashboard + COMMIT_INFO_MESSAGE: ${{github.event.pull_request.title}} + COMMIT_INFO_SHA: ${{github.event.pull_request.head.sha}} diff --git a/.gitignore b/.gitignore index 7d606105b..1509c597a 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,8 @@ config/db/db.sqlite3-journal # VS Code .vscode/launch.json + +# Cypress +cypress.env.json +cypress/videos +cypress/screenshots diff --git a/cypress.config.ts b/cypress.config.ts new file mode 100644 index 000000000..1c454730b --- /dev/null +++ b/cypress.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'cypress'; + +export default defineConfig({ + projectId: 'onnqy3', + e2e: { + baseUrl: 'http://localhost:5055', + experimentalSessionAndOrigin: true, + }, + env: { + ADMIN_EMAIL: 'admin@seerr.dev', + ADMIN_PASSWORD: 'test1234', + USER_EMAIL: 'friend@seerr.dev', + USER_PASSWORD: 'test1234', + }, +}); diff --git a/cypress/config/settings.cypress.json b/cypress/config/settings.cypress.json new file mode 100644 index 000000000..bb7b661b0 --- /dev/null +++ b/cypress/config/settings.cypress.json @@ -0,0 +1,149 @@ +{ + "clientId": "6919275e-142a-48d8-be6b-93594cbd4626", + "vapidPrivate": "tmnslaO8ZWN6bNbSEv_rolPeBTlNxOwCCAHrM9oZz3M", + "vapidPublic": "BK_EpP8NDm9waor2zn6_S28o3ZYv4kCkJOfYpO3pt3W6jnPmxrgTLANUBNbbyaNatPnSQ12De9CeqSYQrqWzHTs", + "main": { + "apiKey": "testkey", + "applicationTitle": "Overseerr", + "applicationUrl": "", + "csrfProtection": false, + "cacheImages": false, + "defaultPermissions": 32, + "defaultQuotas": { + "movie": {}, + "tv": {} + }, + "hideAvailable": false, + "localLogin": true, + "newPlexLogin": true, + "region": "", + "originalLanguage": "", + "trustProxy": false, + "partialRequestsEnabled": true, + "locale": "en" + }, + "plex": { + "name": "Seerr", + "ip": "192.168.1.1", + "port": 32400, + "useSsl": false, + "libraries": [ + { + "id": "1", + "name": "Movies", + "enabled": true, + "type": "movie" + } + ], + "machineId": "test" + }, + "tautulli": {}, + "radarr": [], + "sonarr": [], + "public": { + "initialized": true + }, + "notifications": { + "agents": { + "email": { + "enabled": false, + "options": { + "emailFrom": "", + "smtpHost": "", + "smtpPort": 587, + "secure": false, + "ignoreTls": false, + "requireTls": false, + "allowSelfSigned": false, + "senderName": "Overseerr" + } + }, + "discord": { + "enabled": false, + "types": 0, + "options": { + "webhookUrl": "", + "enableMentions": true + } + }, + "lunasea": { + "enabled": false, + "types": 0, + "options": { + "webhookUrl": "" + } + }, + "slack": { + "enabled": false, + "types": 0, + "options": { + "webhookUrl": "" + } + }, + "telegram": { + "enabled": false, + "types": 0, + "options": { + "botAPI": "", + "chatId": "", + "sendSilently": false + } + }, + "pushbullet": { + "enabled": false, + "types": 0, + "options": { + "accessToken": "" + } + }, + "pushover": { + "enabled": false, + "types": 0, + "options": { + "accessToken": "", + "userToken": "" + } + }, + "webhook": { + "enabled": false, + "types": 0, + "options": { + "webhookUrl": "", + "jsonPayload": "IntcbiAgICBcIm5vdGlmaWNhdGlvbl90eXBlXCI6IFwie3tub3RpZmljYXRpb25fdHlwZX19XCIsXG4gICAgXCJldmVudFwiOiBcInt7ZXZlbnR9fVwiLFxuICAgIFwic3ViamVjdFwiOiBcInt7c3ViamVjdH19XCIsXG4gICAgXCJtZXNzYWdlXCI6IFwie3ttZXNzYWdlfX1cIixcbiAgICBcImltYWdlXCI6IFwie3tpbWFnZX19XCIsXG4gICAgXCJ7e21lZGlhfX1cIjoge1xuICAgICAgICBcIm1lZGlhX3R5cGVcIjogXCJ7e21lZGlhX3R5cGV9fVwiLFxuICAgICAgICBcInRtZGJJZFwiOiBcInt7bWVkaWFfdG1kYmlkfX1cIixcbiAgICAgICAgXCJ0dmRiSWRcIjogXCJ7e21lZGlhX3R2ZGJpZH19XCIsXG4gICAgICAgIFwic3RhdHVzXCI6IFwie3ttZWRpYV9zdGF0dXN9fVwiLFxuICAgICAgICBcInN0YXR1czRrXCI6IFwie3ttZWRpYV9zdGF0dXM0a319XCJcbiAgICB9LFxuICAgIFwie3tyZXF1ZXN0fX1cIjoge1xuICAgICAgICBcInJlcXVlc3RfaWRcIjogXCJ7e3JlcXVlc3RfaWR9fVwiLFxuICAgICAgICBcInJlcXVlc3RlZEJ5X2VtYWlsXCI6IFwie3tyZXF1ZXN0ZWRCeV9lbWFpbH19XCIsXG4gICAgICAgIFwicmVxdWVzdGVkQnlfdXNlcm5hbWVcIjogXCJ7e3JlcXVlc3RlZEJ5X3VzZXJuYW1lfX1cIixcbiAgICAgICAgXCJyZXF1ZXN0ZWRCeV9hdmF0YXJcIjogXCJ7e3JlcXVlc3RlZEJ5X2F2YXRhcn19XCJcbiAgICB9LFxuICAgIFwie3tpc3N1ZX19XCI6IHtcbiAgICAgICAgXCJpc3N1ZV9pZFwiOiBcInt7aXNzdWVfaWR9fVwiLFxuICAgICAgICBcImlzc3VlX3R5cGVcIjogXCJ7e2lzc3VlX3R5cGV9fVwiLFxuICAgICAgICBcImlzc3VlX3N0YXR1c1wiOiBcInt7aXNzdWVfc3RhdHVzfX1cIixcbiAgICAgICAgXCJyZXBvcnRlZEJ5X2VtYWlsXCI6IFwie3tyZXBvcnRlZEJ5X2VtYWlsfX1cIixcbiAgICAgICAgXCJyZXBvcnRlZEJ5X3VzZXJuYW1lXCI6IFwie3tyZXBvcnRlZEJ5X3VzZXJuYW1lfX1cIixcbiAgICAgICAgXCJyZXBvcnRlZEJ5X2F2YXRhclwiOiBcInt7cmVwb3J0ZWRCeV9hdmF0YXJ9fVwiXG4gICAgfSxcbiAgICBcInt7Y29tbWVudH19XCI6IHtcbiAgICAgICAgXCJjb21tZW50X21lc3NhZ2VcIjogXCJ7e2NvbW1lbnRfbWVzc2FnZX19XCIsXG4gICAgICAgIFwiY29tbWVudGVkQnlfZW1haWxcIjogXCJ7e2NvbW1lbnRlZEJ5X2VtYWlsfX1cIixcbiAgICAgICAgXCJjb21tZW50ZWRCeV91c2VybmFtZVwiOiBcInt7Y29tbWVudGVkQnlfdXNlcm5hbWV9fVwiLFxuICAgICAgICBcImNvbW1lbnRlZEJ5X2F2YXRhclwiOiBcInt7Y29tbWVudGVkQnlfYXZhdGFyfX1cIlxuICAgIH0sXG4gICAgXCJ7e2V4dHJhfX1cIjogW11cbn0i" + } + }, + "webpush": { + "enabled": false, + "options": {} + }, + "gotify": { + "enabled": false, + "types": 0, + "options": { + "url": "", + "token": "" + } + } + } + }, + "jobs": { + "plex-recently-added-scan": { + "schedule": "0 */5 * * * *" + }, + "plex-full-scan": { + "schedule": "0 0 3 * * *" + }, + "radarr-scan": { + "schedule": "0 0 4 * * *" + }, + "sonarr-scan": { + "schedule": "0 30 4 * * *" + }, + "download-sync": { + "schedule": "0 * * * * *" + }, + "download-sync-reset": { + "schedule": "0 0 1 * * *" + } + } + } diff --git a/cypress/e2e/discover.cy.ts b/cypress/e2e/discover.cy.ts new file mode 100644 index 000000000..c08615f5d --- /dev/null +++ b/cypress/e2e/discover.cy.ts @@ -0,0 +1,44 @@ +const clickFirstTitleCardInSlider = (sliderTitle: string): void => { + cy.contains('.slider-header', sliderTitle) + .next('[data-testid=media-slider]') + .find('[data-testid=title-card]') + .first() + .trigger('mouseover') + .find('[data-testid=title-card-title]') + .invoke('text') + .then((text) => { + cy.contains('.slider-header', sliderTitle) + .next('[data-testid=media-slider]') + .find('[data-testid=title-card]') + .first() + .click(); + cy.get('[data-testid=media-title]').should('contain', text); + }); +}; + +describe('Discover', () => { + beforeEach(() => { + cy.login(Cypress.env('ADMIN_EMAIL'), Cypress.env('ADMIN_PASSWORD')); + cy.visit('/'); + }); + + it('loads a trending item', () => { + clickFirstTitleCardInSlider('Trending'); + }); + + it('loads popular movies', () => { + clickFirstTitleCardInSlider('Popular Movies'); + }); + + it('loads upcoming movies', () => { + clickFirstTitleCardInSlider('Upcoming Movies'); + }); + + it('loads popular series', () => { + clickFirstTitleCardInSlider('Popular Series'); + }); + + it('loads upcoming series', () => { + clickFirstTitleCardInSlider('Upcoming Series'); + }); +}); diff --git a/cypress/e2e/login.cy.ts b/cypress/e2e/login.cy.ts new file mode 100644 index 000000000..7aba583b9 --- /dev/null +++ b/cypress/e2e/login.cy.ts @@ -0,0 +1,13 @@ +describe('Login Page', () => { + it('succesfully logs in as an admin', () => { + cy.login(Cypress.env('ADMIN_EMAIL'), Cypress.env('ADMIN_PASSWORD')); + cy.visit('/'); + cy.contains('Recently Added'); + }); + + it('succesfully logs in as a local user', () => { + cy.login(Cypress.env('USER_EMAIL'), Cypress.env('USER_PASSWORD')); + cy.visit('/'); + cy.contains('Recently Added'); + }); +}); diff --git a/cypress/e2e/movie-details.cy.ts b/cypress/e2e/movie-details.cy.ts new file mode 100644 index 000000000..c68e052fe --- /dev/null +++ b/cypress/e2e/movie-details.cy.ts @@ -0,0 +1,12 @@ +describe('Movie Details', () => { + it('loads a movie page', () => { + cy.login(Cypress.env('ADMIN_EMAIL'), Cypress.env('ADMIN_PASSWORD')); + // Try to load minions: rise of gru + cy.visit('/movie/438148'); + + cy.get('[data-testid=media-title]').should( + 'contain', + 'Minions: The Rise of Gru (2022)' + ); + }); +}); diff --git a/cypress/e2e/tv-details.cy.ts b/cypress/e2e/tv-details.cy.ts new file mode 100644 index 000000000..3cb2f8cf6 --- /dev/null +++ b/cypress/e2e/tv-details.cy.ts @@ -0,0 +1,12 @@ +describe('TV Details', () => { + it('loads a movie page', () => { + cy.login(Cypress.env('ADMIN_EMAIL'), Cypress.env('ADMIN_PASSWORD')); + // Try to load stranger things + cy.visit('/tv/66732'); + + cy.get('[data-testid=media-title]').should( + 'contain', + 'Stranger Things (2016)' + ); + }); +}); diff --git a/cypress/e2e/user/user-list.cy.ts b/cypress/e2e/user/user-list.cy.ts new file mode 100644 index 000000000..4e8cf4bd5 --- /dev/null +++ b/cypress/e2e/user/user-list.cy.ts @@ -0,0 +1,67 @@ +const testUser = { + displayName: 'Test User', + emailAddress: 'test@seeerr.dev', + password: 'test1234', +}; + +describe('User List', () => { + beforeEach(() => { + cy.login(Cypress.env('ADMIN_EMAIL'), Cypress.env('ADMIN_PASSWORD')); + }); + + it('opens the user list from the home page', () => { + cy.visit('/'); + + cy.get('[data-testid=sidebar-toggle]').click(); + cy.get('[data-testid=sidebar-menu-users-mobile]').click(); + + cy.get('[data-testid=page-header').should('contain', 'User List'); + }); + + it('can find the admin user and friend user in the user list', () => { + cy.visit('/users'); + + cy.get('[data-testid=user-list-row]').contains(Cypress.env('ADMIN_EMAIL')); + cy.get('[data-testid=user-list-row]').contains(Cypress.env('USER_EMAIL')); + }); + + it('can create a local user', () => { + cy.visit('/users'); + + cy.contains('Create Local User').click(); + + cy.get('[data-testid=modal-title').should('contain', 'Create Local User'); + + cy.get('#displayName').type(testUser.displayName); + cy.get('#email').type(testUser.emailAddress); + cy.get('#password').type(testUser.password); + + cy.intercept('/api/v1/user?take=10&skip=0&sort=displayname').as('user'); + + cy.get('[data-testid=modal-ok-button').click(); + + cy.wait('@user'); + + cy.get('[data-testid=user-list-row]').contains(testUser.emailAddress); + }); + + it('can delete the created local test user', () => { + cy.visit('/users'); + + cy.contains('[data-testid=user-list-row]', testUser.emailAddress) + .contains('Delete') + .click(); + + cy.get('[data-testid=modal-title]').should('contain', 'Delete User'); + + cy.intercept('/api/v1/user?take=10&skip=0&sort=displayname').as('user'); + + cy.get('[data-testid=modal-ok-button').should('contain', 'Delete').click(); + + cy.wait('@user'); + + cy.get('[data-testid=user-list-row]') + .contains(testUser.emailAddress) + .should('not.exist'); + }); +}); diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts new file mode 100644 index 000000000..a4c3cfcd7 --- /dev/null +++ b/cypress/support/commands.ts @@ -0,0 +1,26 @@ +/// + +Cypress.Commands.add('login', (email, password) => { + cy.session( + [email, password], + () => { + cy.visit('/login'); + cy.contains('Use your Overseerr account').click(); + + cy.get('[data-testid=email]').type(email); + cy.get('[data-testid=password]').type(password); + + cy.intercept('/api/v1/auth/local').as('localLogin'); + cy.get('[data-testid=local-signin-button]').click(); + + cy.wait('@localLogin'); + + cy.url().should('contain', '/'); + }, + { + validate() { + cy.request('/api/v1/auth/me').its('status').should('eq', 200); + }, + } + ); +}); diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts new file mode 100644 index 000000000..7a7697cab --- /dev/null +++ b/cypress/support/e2e.ts @@ -0,0 +1,7 @@ +import './commands'; + +before(() => { + if (Cypress.env('SEED_DATABASE')) { + cy.exec('yarn cypress:prepare'); + } +}); diff --git a/cypress/support/index.ts b/cypress/support/index.ts new file mode 100644 index 000000000..72eb11bd9 --- /dev/null +++ b/cypress/support/index.ts @@ -0,0 +1,12 @@ +/* eslint-disable @typescript-eslint/no-namespace */ +/// + +declare global { + namespace Cypress { + interface Chainable { + login(email?: string, password?: string): Chainable; + } + } +} + +export {}; diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json new file mode 100644 index 000000000..1b6425b80 --- /dev/null +++ b/cypress/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["es5", "dom"], + "types": ["cypress", "node"], + "resolveJsonModule": true, + "esModuleInterop": true + }, + "include": ["**/*.ts"] +} diff --git a/package.json b/package.json index adbc0399c..1f3b226f9 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,10 @@ "migration:run": "ts-node --project server/tsconfig.json ./node_modules/typeorm/cli.js migration:run", "format": "prettier --loglevel warn --write --cache .", "format:check": "prettier --check --cache .", - "prepare": "husky install" + "prepare": "husky install", + "cypress:open": "cypress open", + "cypress:prepare": "ts-node --files server/scripts/prepareTestDb.ts", + "cypress:build": "yarn build && yarn cypress:prepare" }, "repository": { "type": "git", @@ -121,6 +124,7 @@ "babel-plugin-react-intl-auto": "^3.3.0", "commitizen": "^4.2.4", "copyfiles": "^2.4.1", + "cypress": "^10.4.0", "cz-conventional-changelog": "^3.3.0", "eslint": "^8.21.0", "eslint-config-next": "^12.2.3", diff --git a/server/scripts/prepareTestDb.ts b/server/scripts/prepareTestDb.ts new file mode 100644 index 000000000..d38e37d7c --- /dev/null +++ b/server/scripts/prepareTestDb.ts @@ -0,0 +1,53 @@ +import { createConnection, getRepository } from 'typeorm'; +import { copyFileSync } from 'fs'; +import { UserType } from '../constants/user'; +import { User } from '../entity/User'; +import path from 'path'; + +const prepareDb = async () => { + // Copy over test settings.json + copyFileSync( + path.join(__dirname, '../../cypress/config/settings.cypress.json'), + path.join(__dirname, '../../config/settings.json') + ); + + // Connect to DB and seed test data + const dbConnection = await createConnection(); + + await dbConnection.dropDatabase(); + + // Run migrations in production + if (process.env.WITH_MIGRATIONS === 'true') { + await dbConnection.runMigrations(); + } else { + await dbConnection.synchronize(); + } + + const userRepository = getRepository(User); + + // Create the admin user + const user = new User(); + user.plexId = 1; + user.plexToken = '1234'; + user.plexUsername = 'admin'; + user.username = 'admin'; + user.email = 'admin@seerr.dev'; + user.userType = UserType.PLEX; + await user.setPassword('test1234'); + user.permissions = 2; + user.avatar = 'https://plex.tv/assets/images/avatar/default.png'; + await userRepository.save(user); + + // Create the other user + const otherUser = new User(); + otherUser.plexId = 1; + otherUser.username = 'friend'; + otherUser.email = 'friend@seerr.dev'; + otherUser.userType = UserType.LOCAL; + await otherUser.setPassword('test1234'); + otherUser.permissions = 32; + otherUser.avatar = 'https://plex.tv/assets/images/avatar/default.png'; + await userRepository.save(otherUser); +}; + +prepareDb(); diff --git a/src/components/Common/Header/index.tsx b/src/components/Common/Header/index.tsx index b7c88ddd9..aa81e45eb 100644 --- a/src/components/Common/Header/index.tsx +++ b/src/components/Common/Header/index.tsx @@ -13,7 +13,10 @@ const Header: React.FC = ({ return (
-

+

{children} diff --git a/src/components/Common/Modal/index.tsx b/src/components/Common/Modal/index.tsx index 083a5fd7c..777128c04 100644 --- a/src/components/Common/Modal/index.tsx +++ b/src/components/Common/Modal/index.tsx @@ -141,6 +141,7 @@ const Modal: React.FC = ({ {title} @@ -160,6 +161,7 @@ const Modal: React.FC = ({ onClick={onOk} className="ml-3" disabled={okDisabled} + data-testid="modal-ok-button" > {okText ? okText : 'Ok'} @@ -170,6 +172,7 @@ const Modal: React.FC = ({ onClick={onSecondary} className="ml-3" disabled={secondaryDisabled} + data-testid="modal-secondary-button" > {secondaryText} @@ -189,6 +192,7 @@ const Modal: React.FC = ({ buttonType={cancelButtonType} onClick={onCancel} className="ml-3 sm:ml-0" + data-testid="modal-cancel-button" > {cancelText ? cancelText diff --git a/src/components/Layout/Sidebar/index.tsx b/src/components/Layout/Sidebar/index.tsx index 5530d954f..6c5daae2b 100644 --- a/src/components/Layout/Sidebar/index.tsx +++ b/src/components/Layout/Sidebar/index.tsx @@ -37,6 +37,7 @@ interface SidebarLinkProps { as?: string; requiredPermission?: Permission | Permission[]; permissionType?: 'and' | 'or'; + dataTestId?: string; } const SidebarLinks: SidebarLinkProps[] = [ @@ -72,6 +73,7 @@ const SidebarLinks: SidebarLinkProps[] = [ svgIcon: , activeRegExp: /^\/users/, requiredPermission: Permission.MANAGE_USERS, + dataTestId: 'sidebar-menu-users', }, { href: '/settings', @@ -168,6 +170,7 @@ const Sidebar: React.FC = ({ open, setClosed }) => { : 'hover:bg-gray-700 focus:bg-gray-700' } `} + data-testid={`${sidebarLink.dataTestId}-mobile`} > {sidebarLink.svgIcon} {intl.formatMessage( @@ -229,6 +232,7 @@ const Sidebar: React.FC = ({ open, setClosed }) => { : 'hover:bg-gray-700 focus:bg-gray-700' } `} + data-testid={sidebarLink.dataTestId} > {sidebarLink.svgIcon} {intl.formatMessage(messages[sidebarLink.messagesKey])} diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx index 5b57df971..41ba1553d 100644 --- a/src/components/Layout/index.tsx +++ b/src/components/Layout/index.tsx @@ -68,6 +68,7 @@ const Layout: React.FC = ({ children }) => { } transition duration-300 focus:outline-none lg:hidden`} aria-label="Open sidebar" onClick={() => setSidebarOpen(true)} + data-testid="sidebar-toggle" > diff --git a/src/components/Login/LocalLogin.tsx b/src/components/Login/LocalLogin.tsx index 2480a8d5c..8ab772dbb 100644 --- a/src/components/Login/LocalLogin.tsx +++ b/src/components/Login/LocalLogin.tsx @@ -77,6 +77,7 @@ const LocalLogin: React.FC = ({ revalidate }) => { name="email" type="text" inputMode="email" + data-testid="email" />

{errors.email && touched.email && ( @@ -94,6 +95,7 @@ const LocalLogin: React.FC = ({ revalidate }) => { name="password" type="password" autoComplete="current-password" + data-testid="password" />
{errors.password && touched.password && ( @@ -113,6 +115,7 @@ const LocalLogin: React.FC = ({ revalidate }) => { buttonType="primary" type="submit" disabled={isSubmitting || !isValid} + data-testid="local-signin-button" > diff --git a/src/components/MovieDetails/index.tsx b/src/components/MovieDetails/index.tsx index edb4d8bd2..fc211807c 100644 --- a/src/components/MovieDetails/index.tsx +++ b/src/components/MovieDetails/index.tsx @@ -340,7 +340,7 @@ const MovieDetails: React.FC = ({ movie }) => { /> )} -

+

{data.title}{' '} {data.releaseDate && ( diff --git a/src/components/Slider/index.tsx b/src/components/Slider/index.tsx index a24983237..45d43f7a9 100644 --- a/src/components/Slider/index.tsx +++ b/src/components/Slider/index.tsx @@ -149,7 +149,7 @@ const Slider: React.FC = ({ }; return ( -
+

diff --git a/src/components/TvDetails/index.tsx b/src/components/TvDetails/index.tsx index 50558892a..f94c183b3 100644 --- a/src/components/TvDetails/index.tsx +++ b/src/components/TvDetails/index.tsx @@ -343,7 +343,7 @@ const TvDetails: React.FC = ({ tv }) => { /> )} -

+

{data.name}{' '} {data.firstAirDate && ( diff --git a/src/components/UserList/index.tsx b/src/components/UserList/index.tsx index a0330c0cb..1a3f78940 100644 --- a/src/components/UserList/index.tsx +++ b/src/components/UserList/index.tsx @@ -557,7 +557,7 @@ const UserList: React.FC = () => { {data?.results.map((user) => ( - + {isUserPermsEditable(user.id) && ( = 2.1.2 < 3.0.0" -ieee754@^1.2.1: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -6643,6 +6880,13 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" +is-ci@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" + integrity sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ== + dependencies: + ci-info "^3.2.0" + is-cidr@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/is-cidr/-/is-cidr-4.0.2.tgz#94c7585e4c6c77ceabf920f8cde51b8c0fda8814" @@ -6718,7 +6962,7 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-installed-globally@^0.4.0: +is-installed-globally@^0.4.0, is-installed-globally@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== @@ -6852,6 +7096,11 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + is-utf8@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" @@ -7066,6 +7315,16 @@ jsprim@^1.2.2: json-schema "0.4.0" verror "1.10.0" +jsprim@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" + integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ== + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" + verror "1.10.0" + jstransformer@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/jstransformer/-/jstransformer-1.0.0.tgz#ed8bf0921e2f3f1ed4d5c1a44f68709ed24722c3" @@ -7169,6 +7428,11 @@ latest-version@^5.1.0: dependencies: package-json "^6.3.0" +lazy-ass@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw== + levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -7357,6 +7621,20 @@ lint-staged@^12.3.5: supports-color "^9.2.1" yaml "^1.10.2" +listr2@^3.8.3: + version "3.14.0" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.14.0.tgz#23101cc62e1375fd5836b248276d1d2b51fdbe9e" + integrity sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g== + dependencies: + cli-truncate "^2.1.0" + colorette "^2.0.16" + log-update "^4.0.0" + p-map "^4.0.0" + rfdc "^1.3.0" + rxjs "^7.5.1" + through "^2.3.8" + wrap-ansi "^7.0.0" + listr2@^4.0.1: version "4.0.5" resolved "https://registry.yarnpkg.com/listr2/-/listr2-4.0.5.tgz#9dcc50221583e8b4c71c43f9c7dfd0ef546b75d5" @@ -7483,6 +7761,11 @@ lodash.mergewith@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== +lodash.once@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + lodash.pick@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" @@ -7508,6 +7791,14 @@ lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.15, lodash@^4.17.1 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +log-symbols@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + log-update@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" @@ -8679,7 +8970,7 @@ npm-registry-fetch@^13.0.0, npm-registry-fetch@^13.0.1: npm-package-arg "^9.0.0" proc-log "^2.0.0" -npm-run-path@^4.0.1: +npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -8989,6 +9280,11 @@ osenv@^0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +ospath@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" + integrity sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA== + p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -9234,6 +9530,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -9249,7 +9550,7 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pify@^2.3.0: +pify@^2.2.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== @@ -9414,6 +9715,11 @@ prettier@^2.7.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== +pretty-bytes@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" + integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== + preview-email@^3.0.5: version "3.0.5" resolved "https://registry.yarnpkg.com/preview-email/-/preview-email-3.0.5.tgz#09c32ba43c450ead16b309d9e5cb10f90ff45a95" @@ -9501,6 +9807,11 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" +proxy-from-env@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" + integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A== + psl@^1.1.28: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" @@ -10109,6 +10420,13 @@ remark-rehype@^10.0.0: mdast-util-to-hast "^12.1.0" unified "^10.0.0" +request-progress@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" + integrity sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg== + dependencies: + throttleit "^1.0.0" + request-promise-core@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.2.tgz#339f6aababcafdb31c799ff158700336301d3346" @@ -10304,6 +10622,13 @@ rxjs@^6.4.0: dependencies: tslib "^1.9.0" +rxjs@^7.5.1: + version "7.5.6" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.6.tgz#0446577557862afd6903517ce7cae79ecb9662bc" + integrity sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw== + dependencies: + tslib "^2.1.0" + rxjs@^7.5.5: version "7.5.5" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f" @@ -10741,7 +11066,7 @@ sqlite3@^5.0.2: optionalDependencies: node-gyp "3.x" -sshpk@^1.7.0: +sshpk@^1.14.1, sshpk@^1.7.0: version "1.17.0" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== @@ -11011,6 +11336,13 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-color@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-color@^9.2.1: version "9.2.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.2.1.tgz#599dc9d45acf74c6176e0d880bab1d7d718fe891" @@ -11162,6 +11494,11 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" +throttleit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" + integrity sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g== + through2@^2.0.1, through2@~2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -11214,6 +11551,13 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" +tmp@~0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -12133,6 +12477,14 @@ yargs@^17.0.0, yargs@^17.0.1: y18n "^5.0.5" yargs-parser "^21.0.0" +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" From ebd22ffceadf191df6499efb939091ba0e5c85b1 Mon Sep 17 00:00:00 2001 From: Ryan Cohen Date: Mon, 15 Aug 2022 19:50:42 +0900 Subject: [PATCH 12/81] test: add waits in user test (#2907) [skip ci] --- cypress.config.ts | 4 ++++ cypress/e2e/user/user-list.cy.ts | 3 +++ 2 files changed, 7 insertions(+) diff --git a/cypress.config.ts b/cypress.config.ts index 1c454730b..07b0c8b1d 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -12,4 +12,8 @@ export default defineConfig({ USER_EMAIL: 'friend@seerr.dev', USER_PASSWORD: 'test1234', }, + retries: { + runMode: 2, + openMode: 0, + }, }); diff --git a/cypress/e2e/user/user-list.cy.ts b/cypress/e2e/user/user-list.cy.ts index 4e8cf4bd5..ccd1000d4 100644 --- a/cypress/e2e/user/user-list.cy.ts +++ b/cypress/e2e/user/user-list.cy.ts @@ -41,6 +41,8 @@ describe('User List', () => { cy.get('[data-testid=modal-ok-button').click(); cy.wait('@user'); + // Wait a little longer for the user list to fully re-render + cy.wait(1000); cy.get('[data-testid=user-list-row]').contains(testUser.emailAddress); }); @@ -59,6 +61,7 @@ describe('User List', () => { cy.get('[data-testid=modal-ok-button').should('contain', 'Delete').click(); cy.wait('@user'); + cy.wait(1000); cy.get('[data-testid=user-list-row]') .contains(testUser.emailAddress) From 004e1bb17e14bef1697b6b993f6ff92b77cdaeb4 Mon Sep 17 00:00:00 2001 From: Gylesie <86306812+Gylesie@users.noreply.github.com> Date: Tue, 16 Aug 2022 17:56:46 +0200 Subject: [PATCH 13/81] fix(api): lookup shows using english title only (#2911) Fixes most of the time irrelevant lookup list when using localized TMDB metadata. #2801 --- server/routes/service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/routes/service.ts b/server/routes/service.ts index 5c2fe969d..a0c375f00 100644 --- a/server/routes/service.ts +++ b/server/routes/service.ts @@ -191,7 +191,7 @@ serviceRoutes.get<{ tmdbId: string }>( try { const tv = await tmdb.getTvShow({ tvId: Number(req.params.tmdbId), - language: req.locale ?? (req.query.language as string), + language: 'en', }); const response = await sonarr.getSeriesByTitle(tv.name); From 6428b8d4195c736a384441a761a746a35a3ad009 Mon Sep 17 00:00:00 2001 From: TheCatLady <52870424+TheCatLady@users.noreply.github.com> Date: Tue, 16 Aug 2022 09:33:13 -0700 Subject: [PATCH 14/81] fix: log level value should not be case sensitive (#2913) --- server/logger.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/logger.ts b/server/logger.ts index 4f736e4ab..d5809a0ed 100644 --- a/server/logger.ts +++ b/server/logger.ts @@ -26,7 +26,7 @@ const hformat = winston.format.printf( ); const logger = winston.createLogger({ - level: process.env.LOG_LEVEL || 'debug', + level: process.env.LOG_LEVEL?.toLowerCase() || 'debug', format: winston.format.combine( winston.format.splat(), winston.format.timestamp(), From 70dc4c4b3b7fbdcb7076ffde1ab5c7e8bdbe8f88 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 16:39:35 +0000 Subject: [PATCH 15/81] docs: add Gylesie as a contributor for code (#2912) [skip ci] * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ README.md | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index fabe31882..b0482d5b8 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -692,6 +692,15 @@ "contributions": [ "code" ] + }, + { + "login": "Gylesie", + "name": "Gylesie", + "avatar_url": "https://avatars.githubusercontent.com/u/86306812?v=4", + "profile": "https://github.com/Gylesie", + "contributions": [ + "code" + ] } ], "badgeTemplate": "\"All-orange.svg\"/>", diff --git a/README.md b/README.md index 9a8c43fe9..d0b02d5dc 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Language grade: JavaScript GitHub -All Contributors +All Contributors

@@ -170,6 +170,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Samuel Bartík

💻
Chun Yeung Wong

💻
TheMeanCanEHdian

💻 +
Gylesie

💻 From f3e56da3b719285095a59d5a0e822087e095b709 Mon Sep 17 00:00:00 2001 From: TheCatLady <52870424+TheCatLady@users.noreply.github.com> Date: Tue, 16 Aug 2022 09:58:11 -0700 Subject: [PATCH 16/81] feat: show alert/prompt when settings changes require restart (#2401) * fix: correct 'StatusChecker' typo * feat: add restart required check to StatusChecker * fix(perms): remove MANAGE_SETTINGS permission * fix: allow alert to be dismissed * fix(lang): add missing string in SettingsServices * fix(frontend): fix modal icon border * fix(frontend): un-dismiss alert if setting reverted not require server restart * fix(backend): restart flag only needs to track main settings * fix: rebase issue * refactor: appease Prettier * refactor: swap settings badge order * fix: type import for MainSettings * test: add cypress test for restart prompt --- cypress/e2e/settings/general-settings.cy.ts | 32 +++++++ cypress/e2e/user/user-list.cy.ts | 8 +- overseerr-api.yml | 12 ++- server/index.ts | 2 + server/interfaces/api/settingsInterfaces.ts | 1 + server/lib/permissions.ts | 1 - server/routes/index.ts | 8 +- server/routes/user/index.ts | 7 +- server/utils/restartFlag.ts | 23 +++++ src/components/Common/Modal/index.tsx | 8 +- src/components/Layout/Sidebar/index.tsx | 3 +- src/components/PermissionEdit/index.tsx | 9 -- src/components/PermissionOption/index.tsx | 11 +-- src/components/Settings/SettingsMain.tsx | 18 +++- src/components/Settings/SettingsServices.tsx | 8 +- src/components/StatusChacker/index.tsx | 54 ----------- src/components/StatusChecker/index.tsx | 94 +++++++++++++++++++ src/components/UserList/index.tsx | 2 +- .../UserProfile/UserSettings/index.tsx | 5 +- src/i18n/globalMessages.ts | 1 + src/i18n/locale/en.json | 16 ++-- src/pages/_app.tsx | 2 +- src/pages/settings/about.tsx | 2 +- src/pages/settings/index.tsx | 2 +- src/pages/settings/jobs.tsx | 8 +- src/pages/settings/logs.tsx | 2 +- src/pages/settings/main.tsx | 4 +- src/pages/settings/notifications/discord.tsx | 2 +- src/pages/settings/notifications/email.tsx | 2 +- src/pages/settings/notifications/gotify.tsx | 2 +- src/pages/settings/notifications/lunasea.tsx | 2 +- .../settings/notifications/pushbullet.tsx | 2 +- src/pages/settings/notifications/pushover.tsx | 2 +- src/pages/settings/notifications/slack.tsx | 2 +- src/pages/settings/notifications/telegram.tsx | 2 +- src/pages/settings/notifications/webhook.tsx | 2 +- src/pages/settings/notifications/webpush.tsx | 2 +- src/pages/settings/plex.tsx | 6 +- src/pages/settings/services.tsx | 6 +- src/pages/settings/users.tsx | 4 +- 40 files changed, 239 insertions(+), 140 deletions(-) create mode 100644 cypress/e2e/settings/general-settings.cy.ts create mode 100644 server/utils/restartFlag.ts delete mode 100644 src/components/StatusChacker/index.tsx create mode 100644 src/components/StatusChecker/index.tsx diff --git a/cypress/e2e/settings/general-settings.cy.ts b/cypress/e2e/settings/general-settings.cy.ts new file mode 100644 index 000000000..9fb9b82fb --- /dev/null +++ b/cypress/e2e/settings/general-settings.cy.ts @@ -0,0 +1,32 @@ +describe('General Settings', () => { + beforeEach(() => { + cy.login(Cypress.env('ADMIN_EMAIL'), Cypress.env('ADMIN_PASSWORD')); + }); + + it('opens the settings page from the home page', () => { + cy.visit('/'); + + cy.get('[data-testid=sidebar-toggle]').click(); + cy.get('[data-testid=sidebar-menu-settings-mobile]').click(); + + cy.get('.heading').should('contain', 'General Settings'); + }); + + it('modifies setting that requires restart', () => { + cy.visit('/settings'); + + cy.get('#trustProxy').click(); + cy.get('form').submit(); + cy.get('[data-testid=modal-title]').should( + 'contain', + 'Server Restart Required' + ); + + cy.get('[data-testid=modal-ok-button]').click(); + cy.get('[data-testid=modal-title]').should('not.exist'); + + cy.get('[type=checkbox]#trustProxy').click(); + cy.get('form').submit(); + cy.get('[data-testid=modal-title]').should('not.exist'); + }); +}); diff --git a/cypress/e2e/user/user-list.cy.ts b/cypress/e2e/user/user-list.cy.ts index ccd1000d4..d2593d512 100644 --- a/cypress/e2e/user/user-list.cy.ts +++ b/cypress/e2e/user/user-list.cy.ts @@ -15,7 +15,7 @@ describe('User List', () => { cy.get('[data-testid=sidebar-toggle]').click(); cy.get('[data-testid=sidebar-menu-users-mobile]').click(); - cy.get('[data-testid=page-header').should('contain', 'User List'); + cy.get('[data-testid=page-header]').should('contain', 'User List'); }); it('can find the admin user and friend user in the user list', () => { @@ -30,7 +30,7 @@ describe('User List', () => { cy.contains('Create Local User').click(); - cy.get('[data-testid=modal-title').should('contain', 'Create Local User'); + cy.get('[data-testid=modal-title]').should('contain', 'Create Local User'); cy.get('#displayName').type(testUser.displayName); cy.get('#email').type(testUser.emailAddress); @@ -38,7 +38,7 @@ describe('User List', () => { cy.intercept('/api/v1/user?take=10&skip=0&sort=displayname').as('user'); - cy.get('[data-testid=modal-ok-button').click(); + cy.get('[data-testid=modal-ok-button]').click(); cy.wait('@user'); // Wait a little longer for the user list to fully re-render @@ -58,7 +58,7 @@ describe('User List', () => { cy.intercept('/api/v1/user?take=10&skip=0&sort=displayname').as('user'); - cy.get('[data-testid=modal-ok-button').should('contain', 'Delete').click(); + cy.get('[data-testid=modal-ok-button]').should('contain', 'Delete').click(); cy.wait('@user'); cy.wait(1000); diff --git a/overseerr-api.yml b/overseerr-api.yml index a00ada890..6bf7f69b7 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -1793,14 +1793,14 @@ components: paths: /status: get: - summary: Get Overseerr version - description: Returns the current Overseerr version in a JSON object. + summary: Get Overseerr status + description: Returns the current Overseerr status in a JSON object. security: [] tags: - public responses: '200': - description: Returned version + description: Returned status content: application/json: schema: @@ -1811,6 +1811,12 @@ paths: example: 1.0.0 commitTag: type: string + updateAvailable: + type: boolean + commitsBehind: + type: number + restartRequired: + type: boolean /status/appdata: get: summary: Get application data volume status diff --git a/server/index.ts b/server/index.ts index fb3cb0b10..ba955ac99 100644 --- a/server/index.ts +++ b/server/index.ts @@ -31,6 +31,7 @@ import { getSettings } from './lib/settings'; import logger from './logger'; import routes from './routes'; import { getAppVersion } from './utils/appVersion'; +import restartFlag from './utils/restartFlag'; const API_SPEC_PATH = path.join(__dirname, '../overseerr-api.yml'); @@ -53,6 +54,7 @@ app // Load Settings const settings = getSettings().load(); + restartFlag.initializeSettings(settings.main); // Migrate library types if ( diff --git a/server/interfaces/api/settingsInterfaces.ts b/server/interfaces/api/settingsInterfaces.ts index 8e4f66c4c..0e5ab45a2 100644 --- a/server/interfaces/api/settingsInterfaces.ts +++ b/server/interfaces/api/settingsInterfaces.ts @@ -56,4 +56,5 @@ export interface StatusResponse { commitTag: string; updateAvailable: boolean; commitsBehind: number; + restartRequired: boolean; } diff --git a/server/lib/permissions.ts b/server/lib/permissions.ts index 95160d380..3ccc87388 100644 --- a/server/lib/permissions.ts +++ b/server/lib/permissions.ts @@ -1,7 +1,6 @@ export enum Permission { NONE = 0, ADMIN = 2, - MANAGE_SETTINGS = 4, MANAGE_USERS = 8, MANAGE_REQUESTS = 16, REQUEST = 32, diff --git a/server/routes/index.ts b/server/routes/index.ts index 2be9533bb..9c0c2f498 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -14,6 +14,7 @@ import { mapProductionCompany } from '../models/Movie'; import { mapNetwork } from '../models/Tv'; import { appDataPath, appDataStatus } from '../utils/appDataVolume'; import { getAppVersion, getCommitTag } from '../utils/appVersion'; +import restartFlag from '../utils/restartFlag'; import { isPerson } from '../utils/typeHelpers'; import authRoutes from './auth'; import collectionRoutes from './collection'; @@ -78,6 +79,7 @@ router.get('/status', async (req, res) => { commitTag: getCommitTag(), updateAvailable, commitsBehind, + restartRequired: restartFlag.isSet(), }); }); @@ -100,11 +102,7 @@ router.get('/settings/public', async (req, res) => { return res.status(200).json(settings.fullPublicSettings); } }); -router.use( - '/settings', - isAuthenticated(Permission.MANAGE_SETTINGS), - settingsRoutes -); +router.use('/settings', isAuthenticated(Permission.ADMIN), settingsRoutes); router.use('/search', isAuthenticated(), searchRoutes); router.use('/discover', isAuthenticated(), discoverRoutes); router.use('/request', isAuthenticated(), requestRoutes); diff --git a/server/routes/user/index.ts b/server/routes/user/index.ts index b18932507..1d06e0fa6 100644 --- a/server/routes/user/index.ts +++ b/server/routes/user/index.ts @@ -258,12 +258,7 @@ export const canMakePermissionsChange = ( user?: User ): boolean => // Only let the owner grant admin privileges - !(hasPermission(Permission.ADMIN, permissions) && user?.id !== 1) || - // Only let users with the manage settings permission, grant the same permission - !( - hasPermission(Permission.MANAGE_SETTINGS, permissions) && - !hasPermission(Permission.MANAGE_SETTINGS, user?.permissions ?? 0) - ); + !(hasPermission(Permission.ADMIN, permissions) && user?.id !== 1); router.put< Record, diff --git a/server/utils/restartFlag.ts b/server/utils/restartFlag.ts new file mode 100644 index 000000000..8590137a5 --- /dev/null +++ b/server/utils/restartFlag.ts @@ -0,0 +1,23 @@ +import type { MainSettings } from '../lib/settings'; +import { getSettings } from '../lib/settings'; + +class RestartFlag { + private settings: MainSettings; + + public initializeSettings(settings: MainSettings): void { + this.settings = { ...settings }; + } + + public isSet(): boolean { + const settings = getSettings().main; + + return ( + this.settings.csrfProtection !== settings.csrfProtection || + this.settings.trustProxy !== settings.trustProxy + ); + } +} + +const restartFlag = new RestartFlag(); + +export default restartFlag; diff --git a/src/components/Common/Modal/index.tsx b/src/components/Common/Modal/index.tsx index 777128c04..671a4ff82 100644 --- a/src/components/Common/Modal/index.tsx +++ b/src/components/Common/Modal/index.tsx @@ -130,7 +130,7 @@ const Modal: React.FC = ({ /> )} -
+
{iconSvg &&
{iconSvg}
}
= ({
{children && ( -
+
{children}
)} diff --git a/src/components/Layout/Sidebar/index.tsx b/src/components/Layout/Sidebar/index.tsx index 6c5daae2b..26bdb78bf 100644 --- a/src/components/Layout/Sidebar/index.tsx +++ b/src/components/Layout/Sidebar/index.tsx @@ -80,7 +80,8 @@ const SidebarLinks: SidebarLinkProps[] = [ messagesKey: 'settings', svgIcon: , activeRegExp: /^\/settings/, - requiredPermission: Permission.MANAGE_SETTINGS, + requiredPermission: Permission.ADMIN, + dataTestId: 'sidebar-menu-settings', }, ]; diff --git a/src/components/PermissionEdit/index.tsx b/src/components/PermissionEdit/index.tsx index c6315b8a5..b85c81bcc 100644 --- a/src/components/PermissionEdit/index.tsx +++ b/src/components/PermissionEdit/index.tsx @@ -12,9 +12,6 @@ export const messages = defineMessages({ users: 'Manage Users', usersDescription: 'Grant permission to manage users. Users with this permission cannot modify users with or grant the Admin privilege.', - settings: 'Manage Settings', - settingsDescription: - 'Grant permission to modify global settings. A user must have this permission to grant it to others.', managerequests: 'Manage Requests', managerequestsDescription: 'Grant permission to manage media requests. All requests made by a user with this permission will be automatically approved.', @@ -88,12 +85,6 @@ export const PermissionEdit: React.FC = ({ description: intl.formatMessage(messages.adminDescription), permission: Permission.ADMIN, }, - { - id: 'settings', - name: intl.formatMessage(messages.settings), - description: intl.formatMessage(messages.settingsDescription), - permission: Permission.MANAGE_SETTINGS, - }, { id: 'users', name: intl.formatMessage(messages.users), diff --git a/src/components/PermissionOption/index.tsx b/src/components/PermissionOption/index.tsx index 2638419d5..8aebc9490 100644 --- a/src/components/PermissionOption/index.tsx +++ b/src/components/PermissionOption/index.tsx @@ -67,14 +67,9 @@ const PermissionOption: React.FC = ({ } if ( - // Non-Admin users cannot modify the Admin permission - (actingUser && - !hasPermission(Permission.ADMIN, actingUser.permissions) && - option.permission === Permission.ADMIN) || - // Users without the Manage Settings permission cannot modify/grant that permission - (actingUser && - !hasPermission(Permission.MANAGE_SETTINGS, actingUser.permissions) && - option.permission === Permission.MANAGE_SETTINGS) + // Only the owner can modify the Admin permission + actingUser?.id !== 1 && + option.permission === Permission.ADMIN ) { disabled = true; } diff --git a/src/components/Settings/SettingsMain.tsx b/src/components/Settings/SettingsMain.tsx index 11e88e7f2..bc2397b7f 100644 --- a/src/components/Settings/SettingsMain.tsx +++ b/src/components/Settings/SettingsMain.tsx @@ -41,8 +41,7 @@ const messages = defineMessages({ toastSettingsFailure: 'Something went wrong while saving settings.', hideAvailable: 'Hide Available Media', csrfProtection: 'Enable CSRF Protection', - csrfProtectionTip: - 'Set external API access to read-only (requires HTTPS, and Overseerr must be reloaded for changes to take effect)', + csrfProtectionTip: 'Set external API access to read-only (requires HTTPS)', csrfProtectionHoverTip: 'Do NOT enable this setting unless you understand what you are doing!', cacheImages: 'Enable Image Caching', @@ -50,7 +49,7 @@ const messages = defineMessages({ 'Optimize and store all images locally (consumes a significant amount of disk space)', trustProxy: 'Enable Proxy Support', trustProxyTip: - 'Allow Overseerr to correctly register client IP addresses behind a proxy (Overseerr must be reloaded for changes to take effect)', + 'Allow Overseerr to correctly register client IP addresses behind a proxy', validationApplicationTitle: 'You must provide an application title', validationApplicationUrl: 'You must provide a valid URL', validationApplicationUrlTrailingSlash: 'URL must not end in a trailing slash', @@ -151,6 +150,7 @@ const SettingsMain: React.FC = () => { trustProxy: values.trustProxy, }); mutate('/api/v1/settings/public'); + mutate('/api/v1/status'); if (setLocale) { setLocale( @@ -252,7 +252,12 @@ const SettingsMain: React.FC = () => {
@@ -210,9 +210,11 @@ const NotificationsDiscord: React.FC = () => { placeholder={settings.currentSettings.applicationTitle} />
- {errors.botUsername && touched.botUsername && ( -
{errors.botUsername}
- )} + {errors.botUsername && + touched.botUsername && + typeof errors.botUsername === 'string' && ( +
{errors.botUsername}
+ )}
@@ -228,9 +230,11 @@ const NotificationsDiscord: React.FC = () => { inputMode="url" />
- {errors.botAvatarUrl && touched.botAvatarUrl && ( -
{errors.botAvatarUrl}
- )} + {errors.botAvatarUrl && + touched.botAvatarUrl && + typeof errors.botAvatarUrl === 'string' && ( +
{errors.botAvatarUrl}
+ )}
diff --git a/src/components/Settings/Notifications/NotificationsEmail.tsx b/src/components/Settings/Notifications/NotificationsEmail.tsx index 403083903..4076acc1f 100644 --- a/src/components/Settings/Notifications/NotificationsEmail.tsx +++ b/src/components/Settings/Notifications/NotificationsEmail.tsx @@ -46,7 +46,7 @@ const messages = defineMessages({ validationPgpPassword: 'You must provide a PGP password', }); -export function OpenPgpLink(msg: string): JSX.Element { +export function OpenPgpLink(msg: React.ReactNode) { return ( {msg} @@ -54,7 +54,7 @@ export function OpenPgpLink(msg: string): JSX.Element { ); } -const NotificationsEmail: React.FC = () => { +const NotificationsEmail = () => { const intl = useIntl(); const { addToast, removeToast } = useToasts(); const [isTesting, setIsTesting] = useState(false); @@ -265,9 +265,11 @@ const NotificationsEmail: React.FC = () => { inputMode="email" />
- {errors.emailFrom && touched.emailFrom && ( -
{errors.emailFrom}
- )} + {errors.emailFrom && + touched.emailFrom && + typeof errors.emailFrom === 'string' && ( +
{errors.emailFrom}
+ )}
@@ -284,9 +286,11 @@ const NotificationsEmail: React.FC = () => { inputMode="url" />
- {errors.smtpHost && touched.smtpHost && ( -
{errors.smtpHost}
- )} + {errors.smtpHost && + touched.smtpHost && + typeof errors.smtpHost === 'string' && ( +
{errors.smtpHost}
+ )}
@@ -302,9 +306,11 @@ const NotificationsEmail: React.FC = () => { inputMode="numeric" className="short" /> - {errors.smtpPort && touched.smtpPort && ( -
{errors.smtpPort}
- )} + {errors.smtpPort && + touched.smtpPort && + typeof errors.smtpPort === 'string' && ( +
{errors.smtpPort}
+ )}
@@ -396,9 +402,11 @@ const NotificationsEmail: React.FC = () => { className="font-mono text-xs" />
- {errors.pgpPrivateKey && touched.pgpPrivateKey && ( -
{errors.pgpPrivateKey}
- )} + {errors.pgpPrivateKey && + touched.pgpPrivateKey && + typeof errors.pgpPrivateKey === 'string' && ( +
{errors.pgpPrivateKey}
+ )}
@@ -424,9 +432,11 @@ const NotificationsEmail: React.FC = () => { autoComplete="one-time-code" />
- {errors.pgpPassword && touched.pgpPassword && ( -
{errors.pgpPassword}
- )} + {errors.pgpPassword && + touched.pgpPassword && + typeof errors.pgpPassword === 'string' && ( +
{errors.pgpPassword}
+ )}
diff --git a/src/components/Settings/Notifications/NotificationsGotify/index.tsx b/src/components/Settings/Notifications/NotificationsGotify/index.tsx index dd03210cf..e7573bb00 100644 --- a/src/components/Settings/Notifications/NotificationsGotify/index.tsx +++ b/src/components/Settings/Notifications/NotificationsGotify/index.tsx @@ -26,7 +26,7 @@ const messages = defineMessages({ validationTypes: 'You must select at least one notification type', }); -const NotificationsGotify: React.FC = () => { +const NotificationsGotify = () => { const intl = useIntl(); const { addToast, removeToast } = useToasts(); const [isTesting, setIsTesting] = useState(false); @@ -173,9 +173,11 @@ const NotificationsGotify: React.FC = () => {
- {errors.url && touched.url && ( -
{errors.url}
- )} + {errors.url && + touched.url && + typeof errors.url === 'string' && ( +
{errors.url}
+ )}
@@ -187,9 +189,11 @@ const NotificationsGotify: React.FC = () => {
- {errors.token && touched.token && ( -
{errors.token}
- )} + {errors.token && + touched.token && + typeof errors.token === 'string' && ( +
{errors.token}
+ )}
{ +const NotificationsLunaSea = () => { const intl = useIntl(); const { addToast, removeToast } = useToasts(); const [isTesting, setIsTesting] = useState(false); @@ -155,18 +155,16 @@ const NotificationsLunaSea: React.FC = () => { * {intl.formatMessage(messages.webhookUrlTip, { - LunaSeaLink: function LunaSeaLink(msg) { - return ( -
- {msg} - - ); - }, + LunaSeaLink: (msg: React.ReactNode) => ( + + {msg} + + ), })}
@@ -179,9 +177,11 @@ const NotificationsLunaSea: React.FC = () => { inputMode="url" /> - {errors.webhookUrl && touched.webhookUrl && ( -
{errors.webhookUrl}
- )} + {errors.webhookUrl && + touched.webhookUrl && + typeof errors.webhookUrl === 'string' && ( +
{errors.webhookUrl}
+ )}
@@ -189,9 +189,9 @@ const NotificationsLunaSea: React.FC = () => { {intl.formatMessage(messages.profileName)} {intl.formatMessage(messages.profileNameTip, { - code: function code(msg) { - return {msg}; - }, + code: (msg: React.ReactNode) => ( + {msg} + ), })} diff --git a/src/components/Settings/Notifications/NotificationsPushbullet/index.tsx b/src/components/Settings/Notifications/NotificationsPushbullet/index.tsx index 1aa631c97..c6ec8432e 100644 --- a/src/components/Settings/Notifications/NotificationsPushbullet/index.tsx +++ b/src/components/Settings/Notifications/NotificationsPushbullet/index.tsx @@ -28,7 +28,7 @@ const messages = defineMessages({ validationTypes: 'You must select at least one notification type', }); -const NotificationsPushbullet: React.FC = () => { +const NotificationsPushbullet = () => { const intl = useIntl(); const { addToast, removeToast } = useToasts(); const [isTesting, setIsTesting] = useState(false); @@ -154,20 +154,16 @@ const NotificationsPushbullet: React.FC = () => { * {intl.formatMessage(messages.accessTokenTip, { - PushbulletSettingsLink: function PushbulletSettingsLink( - msg - ) { - return ( - - {msg} - - ); - }, + PushbulletSettingsLink: (msg: React.ReactNode) => ( + + {msg} + + ), })} @@ -180,9 +176,11 @@ const NotificationsPushbullet: React.FC = () => { autoComplete="one-time-code" />
- {errors.accessToken && touched.accessToken && ( -
{errors.accessToken}
- )} + {errors.accessToken && + touched.accessToken && + typeof errors.accessToken === 'string' && ( +
{errors.accessToken}
+ )}
diff --git a/src/components/Settings/Notifications/NotificationsPushover/index.tsx b/src/components/Settings/Notifications/NotificationsPushover/index.tsx index 44229f57e..9f78a6784 100644 --- a/src/components/Settings/Notifications/NotificationsPushover/index.tsx +++ b/src/components/Settings/Notifications/NotificationsPushover/index.tsx @@ -29,7 +29,7 @@ const messages = defineMessages({ validationTypes: 'You must select at least one notification type', }); -const NotificationsPushover: React.FC = () => { +const NotificationsPushover = () => { const intl = useIntl(); const { addToast, removeToast } = useToasts(); const [isTesting, setIsTesting] = useState(false); @@ -172,19 +172,16 @@ const NotificationsPushover: React.FC = () => { * {intl.formatMessage(messages.accessTokenTip, { - ApplicationRegistrationLink: - function ApplicationRegistrationLink(msg) { - return ( - - {msg} - - ); - }, + ApplicationRegistrationLink: (msg: React.ReactNode) => ( + + {msg} + + ), })} @@ -192,9 +189,11 @@ const NotificationsPushover: React.FC = () => {
- {errors.accessToken && touched.accessToken && ( -
{errors.accessToken}
- )} + {errors.accessToken && + touched.accessToken && + typeof errors.accessToken === 'string' && ( +
{errors.accessToken}
+ )}
@@ -203,18 +202,16 @@ const NotificationsPushover: React.FC = () => { * {intl.formatMessage(messages.userTokenTip, { - UsersGroupsLink: function UsersGroupsLink(msg) { - return ( - - {msg} - - ); - }, + UsersGroupsLink: (msg: React.ReactNode) => ( + + {msg} + + ), })} @@ -222,9 +219,11 @@ const NotificationsPushover: React.FC = () => {
- {errors.userToken && touched.userToken && ( -
{errors.userToken}
- )} + {errors.userToken && + touched.userToken && + typeof errors.userToken === 'string' && ( +
{errors.userToken}
+ )}
{ +const NotificationsSlack = () => { const intl = useIntl(); const { addToast, removeToast } = useToasts(); const [isTesting, setIsTesting] = useState(false); @@ -150,18 +150,16 @@ const NotificationsSlack: React.FC = () => { * {intl.formatMessage(messages.webhookUrlTip, { - WebhookLink: function WebhookLink(msg) { - return ( - - {msg} - - ); - }, + WebhookLink: (msg: React.ReactNode) => ( + + {msg} + + ), })} @@ -174,9 +172,11 @@ const NotificationsSlack: React.FC = () => { inputMode="url" /> - {errors.webhookUrl && touched.webhookUrl && ( -
{errors.webhookUrl}
- )} + {errors.webhookUrl && + touched.webhookUrl && + typeof errors.webhookUrl === 'string' && ( +
{errors.webhookUrl}
+ )} { +const NotificationsTelegram = () => { const intl = useIntl(); const { addToast, removeToast } = useToasts(); const [isTesting, setIsTesting] = useState(false); @@ -179,33 +179,29 @@ const NotificationsTelegram: React.FC = () => { * {intl.formatMessage(messages.botApiTip, { - CreateBotLink: function CreateBotLink(msg) { - return ( - - {msg} - - ); - }, - GetIdBotLink: function GetIdBotLink(msg) { - return ( - - {msg} - - ); - }, - code: function code(msg) { - return {msg}; - }, + CreateBotLink: (msg: React.ReactNode) => ( + + {msg} + + ), + GetIdBotLink: (msg: React.ReactNode) => ( + + {msg} + + ), + code: (msg: React.ReactNode) => ( + {msg} + ), })} @@ -218,9 +214,11 @@ const NotificationsTelegram: React.FC = () => { autoComplete="one-time-code" /> - {errors.botAPI && touched.botAPI && ( -
{errors.botAPI}
- )} + {errors.botAPI && + touched.botAPI && + typeof errors.botAPI === 'string' && ( +
{errors.botAPI}
+ )}
@@ -234,9 +232,11 @@ const NotificationsTelegram: React.FC = () => {
- {errors.botUsername && touched.botUsername && ( -
{errors.botUsername}
- )} + {errors.botUsername && + touched.botUsername && + typeof errors.botUsername === 'string' && ( +
{errors.botUsername}
+ )}
@@ -245,20 +245,16 @@ const NotificationsTelegram: React.FC = () => { * {intl.formatMessage(messages.chatIdTip, { - GetIdBotLink: function GetIdBotLink(msg) { - return ( - - {msg} - - ); - }, - code: function code(msg) { - return {msg}; - }, + GetIdBotLink: (msg: React.ReactNode) => ( + + {msg} + + ), + code: (msg: React.ReactNode) => {msg}, })} @@ -266,9 +262,11 @@ const NotificationsTelegram: React.FC = () => {
- {errors.chatId && touched.chatId && ( -
{errors.chatId}
- )} + {errors.chatId && + touched.chatId && + typeof errors.chatId === 'string' && ( +
{errors.chatId}
+ )}
diff --git a/src/components/Settings/Notifications/NotificationsWebPush/index.tsx b/src/components/Settings/Notifications/NotificationsWebPush/index.tsx index 8d4a6c0b0..55bfb9490 100644 --- a/src/components/Settings/Notifications/NotificationsWebPush/index.tsx +++ b/src/components/Settings/Notifications/NotificationsWebPush/index.tsx @@ -21,7 +21,7 @@ const messages = defineMessages({ 'In order to receive web push notifications, Overseerr must be served over HTTPS.', }); -const NotificationsWebPush: React.FC = () => { +const NotificationsWebPush = () => { const intl = useIntl(); const { addToast, removeToast } = useToasts(); const [isTesting, setIsTesting] = useState(false); diff --git a/src/components/Settings/Notifications/NotificationsWebhook/index.tsx b/src/components/Settings/Notifications/NotificationsWebhook/index.tsx index d47bd1b25..8c0ef1dc1 100644 --- a/src/components/Settings/Notifications/NotificationsWebhook/index.tsx +++ b/src/components/Settings/Notifications/NotificationsWebhook/index.tsx @@ -70,7 +70,7 @@ const messages = defineMessages({ validationTypes: 'You must select at least one notification type', }); -const NotificationsWebhook: React.FC = () => { +const NotificationsWebhook = () => { const intl = useIntl(); const { addToast, removeToast } = useToasts(); const [isTesting, setIsTesting] = useState(false); @@ -244,9 +244,11 @@ const NotificationsWebhook: React.FC = () => { inputMode="url" />
- {errors.webhookUrl && touched.webhookUrl && ( -
{errors.webhookUrl}
- )} + {errors.webhookUrl && + touched.webhookUrl && + typeof errors.webhookUrl === 'string' && ( +
{errors.webhookUrl}
+ )}
@@ -273,9 +275,11 @@ const NotificationsWebhook: React.FC = () => { onBlur={() => setFieldTouched('jsonPayload')} />
- {errors.jsonPayload && touched.jsonPayload && ( -
{errors.jsonPayload}
- )} + {errors.jsonPayload && + touched.jsonPayload && + typeof errors.jsonPayload === 'string' && ( +
{errors.jsonPayload}
+ )}
- {errors.name && touched.name && ( -
{errors.name}
- )} + {errors.name && + touched.name && + typeof errors.name === 'string' && ( +
{errors.name}
+ )}
@@ -410,9 +408,11 @@ const RadarrModal: React.FC = ({ className="rounded-r-only" />
- {errors.hostname && touched.hostname && ( -
{errors.hostname}
- )} + {errors.hostname && + touched.hostname && + typeof errors.hostname === 'string' && ( +
{errors.hostname}
+ )}
@@ -432,9 +432,11 @@ const RadarrModal: React.FC = ({ setFieldValue('port', e.target.value); }} /> - {errors.port && touched.port && ( -
{errors.port}
- )} + {errors.port && + touched.port && + typeof errors.port === 'string' && ( +
{errors.port}
+ )}
@@ -471,9 +473,11 @@ const RadarrModal: React.FC = ({ }} />
- {errors.apiKey && touched.apiKey && ( -
{errors.apiKey}
- )} + {errors.apiKey && + touched.apiKey && + typeof errors.apiKey === 'string' && ( +
{errors.apiKey}
+ )}
@@ -493,9 +497,11 @@ const RadarrModal: React.FC = ({ }} />
- {errors.baseUrl && touched.baseUrl && ( -
{errors.baseUrl}
- )} + {errors.baseUrl && + touched.baseUrl && + typeof errors.baseUrl === 'string' && ( +
{errors.baseUrl}
+ )}
@@ -531,9 +537,11 @@ const RadarrModal: React.FC = ({ ))}
- {errors.activeProfileId && touched.activeProfileId && ( -
{errors.activeProfileId}
- )} + {errors.activeProfileId && + touched.activeProfileId && + typeof errors.activeProfileId === 'string' && ( +
{errors.activeProfileId}
+ )}
@@ -567,9 +575,11 @@ const RadarrModal: React.FC = ({ ))}
- {errors.rootFolder && touched.rootFolder && ( -
{errors.rootFolder}
- )} + {errors.rootFolder && + touched.rootFolder && + typeof errors.rootFolder === 'string' && ( +
{errors.rootFolder}
+ )}
@@ -673,9 +683,11 @@ const RadarrModal: React.FC = ({ inputMode="url" />
- {errors.externalUrl && touched.externalUrl && ( -
{errors.externalUrl}
- )} + {errors.externalUrl && + touched.externalUrl && + typeof errors.externalUrl === 'string' && ( +
{errors.externalUrl}
+ )}
diff --git a/src/components/Settings/SettingsAbout/Releases/index.tsx b/src/components/Settings/SettingsAbout/Releases/index.tsx index 81db42e54..cfe92882a 100644 --- a/src/components/Settings/SettingsAbout/Releases/index.tsx +++ b/src/components/Settings/SettingsAbout/Releases/index.tsx @@ -48,11 +48,7 @@ interface ReleaseProps { currentVersion: string; } -const Release: React.FC = ({ - currentVersion, - release, - isLatest, -}) => { +const Release = ({ currentVersion, release, isLatest }: ReleaseProps) => { const intl = useIntl(); const [isModalOpen, setModalOpen] = useState(false); @@ -120,7 +116,7 @@ interface ReleasesProps { currentVersion: string; } -const Releases: React.FC = ({ currentVersion }) => { +const Releases = ({ currentVersion }: ReleasesProps) => { const intl = useIntl(); const { data, error } = useSWR(REPO_RELEASE_API); diff --git a/src/components/Settings/SettingsAbout/index.tsx b/src/components/Settings/SettingsAbout/index.tsx index 7dfa75d04..c484a5f0e 100644 --- a/src/components/Settings/SettingsAbout/index.tsx +++ b/src/components/Settings/SettingsAbout/index.tsx @@ -37,7 +37,7 @@ const messages = defineMessages({ 'You are running the develop branch of Overseerr, which is only recommended for those contributing to development or assisting with bleeding-edge testing.', }); -const SettingsAbout: React.FC = () => { +const SettingsAbout = () => { const intl = useIntl(); const { data, error } = useSWR( '/api/v1/settings/about' @@ -88,9 +88,9 @@ const SettingsAbout: React.FC = () => { {data.version.startsWith('develop-') && ( {msg}; - }, + code: (msg: React.ReactNode) => ( + {msg} + ), })} /> )} diff --git a/src/components/Settings/SettingsJobsCache/index.tsx b/src/components/Settings/SettingsJobsCache/index.tsx index 38403a16b..eaaf57934 100644 --- a/src/components/Settings/SettingsJobsCache/index.tsx +++ b/src/components/Settings/SettingsJobsCache/index.tsx @@ -70,7 +70,7 @@ interface Job { running: boolean; } -const SettingsJobs: React.FC = () => { +const SettingsJobs = () => { const intl = useIntl(); const { addToast } = useToasts(); const { diff --git a/src/components/Settings/SettingsLayout.tsx b/src/components/Settings/SettingsLayout.tsx index 4a366cbc5..f5bdb6f3e 100644 --- a/src/components/Settings/SettingsLayout.tsx +++ b/src/components/Settings/SettingsLayout.tsx @@ -16,7 +16,11 @@ const messages = defineMessages({ menuAbout: 'About', }); -const SettingsLayout: React.FC = ({ children }) => { +type SettingsLayoutProps = { + children: React.ReactNode; +}; + +const SettingsLayout = ({ children }: SettingsLayoutProps) => { const intl = useIntl(); const settingsRoutes: SettingsRoute[] = [ diff --git a/src/components/Settings/SettingsLogs/index.tsx b/src/components/Settings/SettingsLogs/index.tsx index a42bb820b..412941771 100644 --- a/src/components/Settings/SettingsLogs/index.tsx +++ b/src/components/Settings/SettingsLogs/index.tsx @@ -51,7 +51,7 @@ const messages = defineMessages({ type Filter = 'debug' | 'info' | 'warn' | 'error'; -const SettingsLogs: React.FC = () => { +const SettingsLogs = () => { const router = useRouter(); const intl = useIntl(); const { addToast } = useToasts(); @@ -232,9 +232,9 @@ const SettingsLogs: React.FC = () => {

{intl.formatMessage(messages.logs)}

{intl.formatMessage(messages.logsDescription, { - code: function code(msg) { - return {msg}; - }, + code: (msg: React.ReactNode) => ( + {msg} + ), appDataPath: appData ? appData.appDataPath : '/app/config', })}

@@ -388,9 +388,9 @@ const SettingsLogs: React.FC = () => { data.results.length : (pageIndex + 1) * currentPageSize, total: data.pageInfo.results, - strong: function strong(msg) { - return {msg}; - }, + strong: (msg: React.ReactNode) => ( + {msg} + ), })}

diff --git a/src/components/Settings/SettingsMain.tsx b/src/components/Settings/SettingsMain.tsx index bc2397b7f..c4dd34b43 100644 --- a/src/components/Settings/SettingsMain.tsx +++ b/src/components/Settings/SettingsMain.tsx @@ -57,7 +57,7 @@ const messages = defineMessages({ locale: 'Display Language', }); -const SettingsMain: React.FC = () => { +const SettingsMain = () => { const { addToast } = useToasts(); const { user: currentUser, hasPermission: userHasPermission } = useUser(); const intl = useIntl(); @@ -227,9 +227,11 @@ const SettingsMain: React.FC = () => { type="text" /> - {errors.applicationTitle && touched.applicationTitle && ( -
{errors.applicationTitle}
- )} + {errors.applicationTitle && + touched.applicationTitle && + typeof errors.applicationTitle === 'string' && ( +
{errors.applicationTitle}
+ )}
@@ -245,9 +247,11 @@ const SettingsMain: React.FC = () => { inputMode="url" />
- {errors.applicationUrl && touched.applicationUrl && ( -
{errors.applicationUrl}
- )} + {errors.applicationUrl && + touched.applicationUrl && + typeof errors.applicationUrl === 'string' && ( +
{errors.applicationUrl}
+ )}
diff --git a/src/components/Settings/SettingsNotifications.tsx b/src/components/Settings/SettingsNotifications.tsx index e275e0a43..c8a7a5223 100644 --- a/src/components/Settings/SettingsNotifications.tsx +++ b/src/components/Settings/SettingsNotifications.tsx @@ -23,7 +23,11 @@ const messages = defineMessages({ webpush: 'Web Push', }); -const SettingsNotifications: React.FC = ({ children }) => { +type SettingsNotificationsProps = { + children: React.ReactNode; +}; + +const SettingsNotifications = ({ children }: SettingsNotificationsProps) => { const intl = useIntl(); const settingsRoutes: SettingsRoute[] = [ diff --git a/src/components/Settings/SettingsPlex.tsx b/src/components/Settings/SettingsPlex.tsx index d5aa77492..4a57aac99 100644 --- a/src/components/Settings/SettingsPlex.tsx +++ b/src/components/Settings/SettingsPlex.tsx @@ -107,7 +107,7 @@ interface SettingsPlexProps { onComplete?: () => void; } -const SettingsPlex: React.FC = ({ onComplete }) => { +const SettingsPlex = ({ onComplete }: SettingsPlexProps) => { const [isSyncing, setIsSyncing] = useState(false); const [isRefreshingPresets, setIsRefreshingPresets] = useState(false); const [availableServers, setAvailableServers] = useState( @@ -344,18 +344,16 @@ const SettingsPlex: React.FC = ({ onComplete }) => {
- {msg} - - ); - }, + RegisterPlexTVLink: (msg: React.ReactNode) => ( + + {msg} + + ), })} type="info" /> @@ -517,9 +515,11 @@ const SettingsPlex: React.FC = ({ onComplete }) => { className="rounded-r-only" />
- {errors.hostname && touched.hostname && ( -
{errors.hostname}
- )} + {errors.hostname && + touched.hostname && + typeof errors.hostname === 'string' && ( +
{errors.hostname}
+ )}
@@ -535,9 +535,11 @@ const SettingsPlex: React.FC = ({ onComplete }) => { name="port" className="short" /> - {errors.port && touched.port && ( -
{errors.port}
- )} + {errors.port && + touched.port && + typeof errors.port === 'string' && ( +
{errors.port}
+ )}
@@ -558,17 +560,15 @@ const SettingsPlex: React.FC = ({ onComplete }) => {
- {errors.webAppUrl && touched.webAppUrl && ( -
{errors.webAppUrl}
- )} + {errors.webAppUrl && + touched.webAppUrl && + typeof errors.webAppUrl === 'string' && ( +
{errors.webAppUrl}
+ )}
@@ -803,9 +805,11 @@ const SettingsPlex: React.FC = ({ onComplete }) => { className="rounded-r-only" />
- {errors.tautulliHostname && touched.tautulliHostname && ( -
{errors.tautulliHostname}
- )} + {errors.tautulliHostname && + touched.tautulliHostname && + typeof errors.tautulliHostname === 'string' && ( +
{errors.tautulliHostname}
+ )}
@@ -821,9 +825,11 @@ const SettingsPlex: React.FC = ({ onComplete }) => { name="tautulliPort" className="short" /> - {errors.tautulliPort && touched.tautulliPort && ( -
{errors.tautulliPort}
- )} + {errors.tautulliPort && + touched.tautulliPort && + typeof errors.tautulliPort === 'string' && ( +
{errors.tautulliPort}
+ )}
@@ -857,9 +863,11 @@ const SettingsPlex: React.FC = ({ onComplete }) => { name="tautulliUrlBase" />
- {errors.tautulliUrlBase && touched.tautulliUrlBase && ( -
{errors.tautulliUrlBase}
- )} + {errors.tautulliUrlBase && + touched.tautulliUrlBase && + typeof errors.tautulliUrlBase === 'string' && ( +
{errors.tautulliUrlBase}
+ )}
@@ -876,9 +884,11 @@ const SettingsPlex: React.FC = ({ onComplete }) => { autoComplete="one-time-code" />
- {errors.tautulliApiKey && touched.tautulliApiKey && ( -
{errors.tautulliApiKey}
- )} + {errors.tautulliApiKey && + touched.tautulliApiKey && + typeof errors.tautulliApiKey === 'string' && ( +
{errors.tautulliApiKey}
+ )}
diff --git a/src/components/Settings/SettingsServices.tsx b/src/components/Settings/SettingsServices.tsx index 36272f1b3..8d6398dbc 100644 --- a/src/components/Settings/SettingsServices.tsx +++ b/src/components/Settings/SettingsServices.tsx @@ -60,7 +60,7 @@ interface ServerInstanceProps { onDelete: () => void; } -const ServerInstance: React.FC = ({ +const ServerInstance = ({ name, hostname, port, @@ -72,7 +72,7 @@ const ServerInstance: React.FC = ({ externalUrl, onEdit, onDelete, -}) => { +}: ServerInstanceProps) => { const intl = useIntl(); const internalUrl = @@ -161,7 +161,7 @@ const ServerInstance: React.FC = ({ ); }; -const SettingsServices: React.FC = () => { +const SettingsServices = () => { const intl = useIntl(); const { data: radarrData, @@ -294,13 +294,11 @@ const SettingsServices: React.FC = () => { - {msg} - - ); - }, + strong: (msg: React.ReactNode) => ( + + {msg} + + ), })} /> ) : ( @@ -384,13 +382,11 @@ const SettingsServices: React.FC = () => { - {msg} - - ); - }, + strong: (msg: React.ReactNode) => ( + + {msg} + + ), })} /> ) : ( diff --git a/src/components/Settings/SettingsUsers/index.tsx b/src/components/Settings/SettingsUsers/index.tsx index 8e1d9350f..f6afd4cb7 100644 --- a/src/components/Settings/SettingsUsers/index.tsx +++ b/src/components/Settings/SettingsUsers/index.tsx @@ -30,7 +30,7 @@ const messages = defineMessages({ defaultPermissionsTip: 'Initial permissions assigned to new users', }); -const SettingsUsers: React.FC = () => { +const SettingsUsers = () => { const { addToast } = useToasts(); const intl = useIntl(); const { diff --git a/src/components/Settings/SonarrModal/index.tsx b/src/components/Settings/SonarrModal/index.tsx index 3e9e239f9..d4237801b 100644 --- a/src/components/Settings/SonarrModal/index.tsx +++ b/src/components/Settings/SonarrModal/index.tsx @@ -99,11 +99,7 @@ interface SonarrModalProps { onSave: () => void; } -const SonarrModal: React.FC = ({ - onClose, - sonarr, - onSave, -}) => { +const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => { const intl = useIntl(); const initialLoad = useRef(false); const { addToast } = useToasts(); @@ -412,9 +408,11 @@ const SonarrModal: React.FC = ({ }} />
- {errors.name && touched.name && ( -
{errors.name}
- )} + {errors.name && + touched.name && + typeof errors.name === 'string' && ( +
{errors.name}
+ )}
@@ -439,9 +437,11 @@ const SonarrModal: React.FC = ({ className="rounded-r-only" />
- {errors.hostname && touched.hostname && ( -
{errors.hostname}
- )} + {errors.hostname && + touched.hostname && + typeof errors.hostname === 'string' && ( +
{errors.hostname}
+ )}
@@ -461,9 +461,11 @@ const SonarrModal: React.FC = ({ setFieldValue('port', e.target.value); }} /> - {errors.port && touched.port && ( -
{errors.port}
- )} + {errors.port && + touched.port && + typeof errors.port === 'string' && ( +
{errors.port}
+ )}
@@ -500,9 +502,11 @@ const SonarrModal: React.FC = ({ }} />
- {errors.apiKey && touched.apiKey && ( -
{errors.apiKey}
- )} + {errors.apiKey && + touched.apiKey && + typeof errors.apiKey === 'string' && ( +
{errors.apiKey}
+ )}
@@ -522,9 +526,11 @@ const SonarrModal: React.FC = ({ }} />
- {errors.baseUrl && touched.baseUrl && ( -
{errors.baseUrl}
- )} + {errors.baseUrl && + touched.baseUrl && + typeof errors.baseUrl === 'string' && ( +
{errors.baseUrl}
+ )}
@@ -560,9 +566,11 @@ const SonarrModal: React.FC = ({ ))}
- {errors.activeProfileId && touched.activeProfileId && ( -
{errors.activeProfileId}
- )} + {errors.activeProfileId && + touched.activeProfileId && + typeof errors.activeProfileId === 'string' && ( +
{errors.activeProfileId}
+ )}
@@ -596,9 +604,11 @@ const SonarrModal: React.FC = ({ ))}
- {errors.rootFolder && touched.rootFolder && ( -
{errors.rootFolder}
- )} + {errors.rootFolder && + touched.rootFolder && + typeof errors.rootFolder === 'string' && ( +
{errors.rootFolder}
+ )}
@@ -920,9 +930,11 @@ const SonarrModal: React.FC = ({ inputMode="url" />
- {errors.externalUrl && touched.externalUrl && ( -
{errors.externalUrl}
- )} + {errors.externalUrl && + touched.externalUrl && + typeof errors.externalUrl === 'string' && ( +
{errors.externalUrl}
+ )}
diff --git a/src/components/Setup/LoginWithPlex.tsx b/src/components/Setup/LoginWithPlex.tsx index 90d4425b4..b5341ad47 100644 --- a/src/components/Setup/LoginWithPlex.tsx +++ b/src/components/Setup/LoginWithPlex.tsx @@ -13,7 +13,7 @@ interface LoginWithPlexProps { onComplete: () => void; } -const LoginWithPlex: React.FC = ({ onComplete }) => { +const LoginWithPlex = ({ onComplete }: LoginWithPlexProps) => { const intl = useIntl(); const [authToken, setAuthToken] = useState(undefined); const { user, revalidate } = useUser(); diff --git a/src/components/Setup/SetupSteps.tsx b/src/components/Setup/SetupSteps.tsx index cee5263df..0fa87c928 100644 --- a/src/components/Setup/SetupSteps.tsx +++ b/src/components/Setup/SetupSteps.tsx @@ -9,13 +9,13 @@ interface CurrentStep { isLastStep?: boolean; } -const SetupSteps: React.FC = ({ +const SetupSteps = ({ stepNumber, description, active = false, completed = false, isLastStep = false, -}) => { +}: CurrentStep) => { return (
  • diff --git a/src/components/Setup/index.tsx b/src/components/Setup/index.tsx index a1916290a..88e4bb3c2 100644 --- a/src/components/Setup/index.tsx +++ b/src/components/Setup/index.tsx @@ -28,7 +28,7 @@ const messages = defineMessages({ 'Scanning will run in the background. You can continue the setup process in the meantime.', }); -const Setup: React.FC = () => { +const Setup = () => { const intl = useIntl(); const [isUpdating, setIsUpdating] = useState(false); const [currentStep, setCurrentStep] = useState(1); diff --git a/src/components/Slider/index.tsx b/src/components/Slider/index.tsx index 45d43f7a9..b958567ff 100644 --- a/src/components/Slider/index.tsx +++ b/src/components/Slider/index.tsx @@ -21,14 +21,14 @@ enum Direction { LEFT, } -const Slider: React.FC = ({ +const Slider = ({ sliderKey, items, isLoading, isEmpty, emptyMessage, placeholder = , -}) => { +}: SliderProps) => { const intl = useIntl(); const containerRef = useRef(null); const [scrollPos, setScrollPos] = useState({ isStart: true, isEnd: false }); diff --git a/src/components/StatusBadge/index.tsx b/src/components/StatusBadge/index.tsx index 196b3a5de..e891328bb 100644 --- a/src/components/StatusBadge/index.tsx +++ b/src/components/StatusBadge/index.tsx @@ -22,7 +22,7 @@ interface StatusBadgeProps { mediaType?: 'movie' | 'tv'; } -const StatusBadge: React.FC = ({ +const StatusBadge = ({ status, is4k = false, inProgress = false, @@ -30,7 +30,7 @@ const StatusBadge: React.FC = ({ serviceUrl, tmdbId, mediaType, -}) => { +}: StatusBadgeProps) => { const intl = useIntl(); const { hasPermission } = useUser(); const settings = useSettings(); diff --git a/src/components/StatusChecker/index.tsx b/src/components/StatusChecker/index.tsx index dd1fef3d5..0204b8ea9 100644 --- a/src/components/StatusChecker/index.tsx +++ b/src/components/StatusChecker/index.tsx @@ -19,7 +19,7 @@ const messages = defineMessages({ 'Please restart the server to apply the updated settings.', }); -const StatusChecker: React.FC = () => { +const StatusChecker = () => { const intl = useIntl(); const settings = useSettings(); const { hasPermission } = useUser(); diff --git a/src/components/TitleCard/Placeholder.tsx b/src/components/TitleCard/Placeholder.tsx index 14148ddf3..268dd8e62 100644 --- a/src/components/TitleCard/Placeholder.tsx +++ b/src/components/TitleCard/Placeholder.tsx @@ -4,7 +4,7 @@ interface PlaceholderProps { canExpand?: boolean; } -const Placeholder: React.FC = ({ canExpand = false }) => { +const Placeholder = ({ canExpand = false }: PlaceholderProps) => { return (
    { return (movie as MovieDetails).title !== undefined; }; -const TmdbTitleCard: React.FC = ({ tmdbId, type }) => { +const TmdbTitleCard = ({ tmdbId, type }: TmdbTitleCardProps) => { const { ref, inView } = useInView({ triggerOnce: true, }); diff --git a/src/components/TitleCard/index.tsx b/src/components/TitleCard/index.tsx index 433745712..816a183e2 100644 --- a/src/components/TitleCard/index.tsx +++ b/src/components/TitleCard/index.tsx @@ -29,7 +29,7 @@ interface TitleCardProps { inProgress?: boolean; } -const TitleCard: React.FC = ({ +const TitleCard = ({ id, image, summary, @@ -39,7 +39,7 @@ const TitleCard: React.FC = ({ mediaType, inProgress = false, canExpand = false, -}) => { +}: TitleCardProps) => { const isTouch = useIsTouch(); const intl = useIntl(); const { hasPermission } = useUser(); diff --git a/src/components/Toast/index.tsx b/src/components/Toast/index.tsx index 39d8e68b7..0d35aec0e 100644 --- a/src/components/Toast/index.tsx +++ b/src/components/Toast/index.tsx @@ -9,12 +9,12 @@ import React from 'react'; import type { ToastProps } from 'react-toast-notifications'; import Transition from '../Transition'; -const Toast: React.FC = ({ +const Toast = ({ appearance, children, onDismiss, transitionState, -}) => { +}: ToastProps) => { return (
    = ({ - hasToasts, - ...props -}) => { +const ToastContainer = ({ hasToasts, ...props }: ToastContainerProps) => { return (
    = ({ +const CSSTransition = ({ show, enter = '', enterFrom = '', @@ -37,7 +38,7 @@ const CSSTransition: React.FC = ({ leaveTo = '', appear, children, -}) => { +}: CSSTransitionProps) => { const enterClasses = enter.split(' ').filter((s) => s.length); const enterFromClasses = enterFrom.split(' ').filter((s) => s.length); const enterToClasses = enterTo.split(' ').filter((s) => s.length); @@ -87,11 +88,7 @@ const CSSTransition: React.FC = ({ ); }; -const Transition: React.FC = ({ - show, - appear, - ...rest -}) => { +const Transition = ({ show, appear, ...rest }: CSSTransitionProps) => { const { parent } = useContext(TransitionContext); const isInitialRender = useIsInitialRender(); const isChild = show === undefined; diff --git a/src/components/TvDetails/TvCast/index.tsx b/src/components/TvDetails/TvCast/index.tsx index 9631ad491..cf7d60c42 100644 --- a/src/components/TvDetails/TvCast/index.tsx +++ b/src/components/TvDetails/TvCast/index.tsx @@ -14,7 +14,7 @@ const messages = defineMessages({ fullseriescast: 'Full Series Cast', }); -const TvCast: React.FC = () => { +const TvCast = () => { const router = useRouter(); const intl = useIntl(); const { data, error } = useSWR(`/api/v1/tv/${router.query.tvId}`); diff --git a/src/components/TvDetails/TvCrew/index.tsx b/src/components/TvDetails/TvCrew/index.tsx index 5ed0297d2..621479e3c 100644 --- a/src/components/TvDetails/TvCrew/index.tsx +++ b/src/components/TvDetails/TvCrew/index.tsx @@ -14,7 +14,7 @@ const messages = defineMessages({ fullseriescrew: 'Full Series Crew', }); -const TvCrew: React.FC = () => { +const TvCrew = () => { const router = useRouter(); const intl = useIntl(); const { data, error } = useSWR(`/api/v1/tv/${router.query.tvId}`); diff --git a/src/components/TvDetails/TvRecommendations.tsx b/src/components/TvDetails/TvRecommendations.tsx index 5e473e400..4f5832d50 100644 --- a/src/components/TvDetails/TvRecommendations.tsx +++ b/src/components/TvDetails/TvRecommendations.tsx @@ -15,7 +15,7 @@ const messages = defineMessages({ recommendations: 'Recommendations', }); -const TvRecommendations: React.FC = () => { +const TvRecommendations = () => { const router = useRouter(); const intl = useIntl(); const { data: tvData } = useSWR(`/api/v1/tv/${router.query.tvId}`); diff --git a/src/components/TvDetails/TvSimilar.tsx b/src/components/TvDetails/TvSimilar.tsx index a82147470..fb2615736 100644 --- a/src/components/TvDetails/TvSimilar.tsx +++ b/src/components/TvDetails/TvSimilar.tsx @@ -15,7 +15,7 @@ const messages = defineMessages({ similar: 'Similar Series', }); -const TvSimilar: React.FC = () => { +const TvSimilar = () => { const router = useRouter(); const intl = useIntl(); const { data: tvData } = useSWR(`/api/v1/tv/${router.query.tvId}`); diff --git a/src/components/TvDetails/index.tsx b/src/components/TvDetails/index.tsx index f94c183b3..2c768e31b 100644 --- a/src/components/TvDetails/index.tsx +++ b/src/components/TvDetails/index.tsx @@ -74,7 +74,7 @@ interface TvDetailsProps { tv?: TvDetailsType; } -const TvDetails: React.FC = ({ tv }) => { +const TvDetails = ({ tv }: TvDetailsProps) => { const settings = useSettings(); const { user, hasPermission } = useUser(); const router = useRouter(); diff --git a/src/components/UserList/BulkEditModal.tsx b/src/components/UserList/BulkEditModal.tsx index 8a789e0b3..c002a206a 100644 --- a/src/components/UserList/BulkEditModal.tsx +++ b/src/components/UserList/BulkEditModal.tsx @@ -23,13 +23,13 @@ const messages = defineMessages({ edituser: 'Edit User Permissions', }); -const BulkEditModal: React.FC = ({ +const BulkEditModal = ({ selectedUserIds, users, onCancel, onComplete, onSaving, -}) => { +}: BulkEditProps) => { const { user: currentUser } = useUser(); const intl = useIntl(); const { addToast } = useToasts(); diff --git a/src/components/UserList/PlexImportModal.tsx b/src/components/UserList/PlexImportModal.tsx index 1afb87736..93f355ecb 100644 --- a/src/components/UserList/PlexImportModal.tsx +++ b/src/components/UserList/PlexImportModal.tsx @@ -25,10 +25,7 @@ const messages = defineMessages({ 'The Enable New Plex Sign-In setting is currently enabled. Plex users with library access do not need to be imported in order to sign in.', }); -const PlexImportModal: React.FC = ({ - onCancel, - onComplete, -}) => { +const PlexImportModal = ({ onCancel, onComplete }: PlexImportProps) => { const intl = useIntl(); const settings = useSettings(); const { addToast } = useToasts(); @@ -62,9 +59,7 @@ const PlexImportModal: React.FC = ({ addToast( intl.formatMessage(messages.importedfromplex, { userCount: createdUsers.length, - strong: function strong(msg) { - return {msg}; - }, + strong: (msg: React.ReactNode) => {msg}, }), { autoDismiss: true, @@ -125,11 +120,9 @@ const PlexImportModal: React.FC = ({ {settings.currentSettings.newPlexLogin && ( {msg} - ); - }, + strong: (msg: React.ReactNode) => ( + {msg} + ), })} type="info" /> diff --git a/src/components/UserList/index.tsx b/src/components/UserList/index.tsx index db2d310f9..46d3ed1dd 100644 --- a/src/components/UserList/index.tsx +++ b/src/components/UserList/index.tsx @@ -81,7 +81,7 @@ const messages = defineMessages({ type Sort = 'created' | 'updated' | 'requests' | 'displayname'; -const UserList: React.FC = () => { +const UserList = () => { const intl = useIntl(); const router = useRouter(); const settings = useSettings(); @@ -325,13 +325,11 @@ const UserList: React.FC = () => { {!settings.currentSettings.localLogin && ( - {msg} - - ); - }, + strong: (msg: React.ReactNode) => ( + + {msg} + + ), })} type="warning" /> @@ -374,9 +372,11 @@ const UserList: React.FC = () => { inputMode="email" />
    - {errors.email && touched.email && ( -
    {errors.email}
    - )} + {errors.email && + touched.email && + typeof errors.email === 'string' && ( +
    {errors.email}
    + )}
    { disabled={values.genpassword} />
    - {errors.password && touched.password && ( -
    {errors.password}
    - )} + {errors.password && + touched.password && + typeof errors.password === 'string' && ( +
    {errors.password}
    + )}
  • @@ -680,9 +682,9 @@ const UserList: React.FC = () => { ? pageIndex * currentPageSize + data.results.length : (pageIndex + 1) * currentPageSize, total: data.pageInfo.results, - strong: function strong(msg) { - return {msg}; - }, + strong: (msg: React.ReactNode) => ( + {msg} + ), })}

    diff --git a/src/components/UserProfile/ProfileHeader/index.tsx b/src/components/UserProfile/ProfileHeader/index.tsx index 2470a4f3d..fd8428083 100644 --- a/src/components/UserProfile/ProfileHeader/index.tsx +++ b/src/components/UserProfile/ProfileHeader/index.tsx @@ -18,10 +18,7 @@ interface ProfileHeaderProps { isSettingsPage?: boolean; } -const ProfileHeader: React.FC = ({ - user, - isSettingsPage, -}) => { +const ProfileHeader = ({ user, isSettingsPage }: ProfileHeaderProps) => { const intl = useIntl(); const { user: loggedInUser, hasPermission } = useUser(); diff --git a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx index a6793cb5a..27714407e 100644 --- a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx +++ b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx @@ -51,7 +51,7 @@ const messages = defineMessages({ validationDiscordId: 'You must provide a valid Discord user ID', }); -const UserGeneralSettings: React.FC = () => { +const UserGeneralSettings = () => { const intl = useIntl(); const { addToast } = useToasts(); const { locale, setLocale } = useLocale(); @@ -221,9 +221,11 @@ const UserGeneralSettings: React.FC = () => { } /> - {errors.displayName && touched.displayName && ( -
    {errors.displayName}
    - )} + {errors.displayName && + touched.displayName && + typeof errors.displayName === 'string' && ( +
    {errors.displayName}
    + )}
    @@ -232,17 +234,15 @@ const UserGeneralSettings: React.FC = () => { {currentUser?.id === user?.id && ( {intl.formatMessage(messages.discordIdTip, { - FindDiscordIdLink: function FindDiscordIdLink(msg) { - return ( - - {msg} - - ); - }, + FindDiscordIdLink: (msg: React.ReactNode) => ( + + {msg} + + ), })} )} @@ -251,9 +251,11 @@ const UserGeneralSettings: React.FC = () => {
    - {errors.discordId && touched.discordId && ( -
    {errors.discordId}
    - )} + {errors.discordId && + touched.discordId && + typeof errors.discordId === 'string' && ( +
    {errors.discordId}
    + )}
    diff --git a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsDiscord.tsx b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsDiscord.tsx index ca5c9b56b..0d03506c8 100644 --- a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsDiscord.tsx +++ b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsDiscord.tsx @@ -23,7 +23,7 @@ const messages = defineMessages({ validationDiscordId: 'You must provide a valid user ID', }); -const UserNotificationsDiscord: React.FC = () => { +const UserNotificationsDiscord = () => { const intl = useIntl(); const { addToast } = useToasts(); const router = useRouter(); @@ -111,17 +111,15 @@ const UserNotificationsDiscord: React.FC = () => { {currentUser?.id === user?.id && ( {intl.formatMessage(messages.discordIdTip, { - FindDiscordIdLink: function FindDiscordIdLink(msg) { - return ( - - {msg} - - ); - }, + FindDiscordIdLink: (msg: React.ReactNode) => ( + + {msg} + + ), })} )} @@ -130,9 +128,11 @@ const UserNotificationsDiscord: React.FC = () => {
    - {errors.discordId && touched.discordId && ( -
    {errors.discordId}
    - )} + {errors.discordId && + touched.discordId && + typeof errors.discordId === 'string' && ( +
    {errors.discordId}
    + )}
    { +const UserEmailSettings = () => { const intl = useIntl(); const { addToast } = useToasts(); const router = useRouter(); @@ -126,9 +126,11 @@ const UserEmailSettings: React.FC = () => { className="font-mono text-xs" /> - {errors.pgpKey && touched.pgpKey && ( -
    {errors.pgpKey}
    - )} + {errors.pgpKey && + touched.pgpKey && + typeof errors.pgpKey === 'string' && ( +
    {errors.pgpKey}
    + )} { +const UserPushbulletSettings = () => { const intl = useIntl(); const { addToast } = useToasts(); const router = useRouter(); @@ -105,20 +105,16 @@ const UserPushbulletSettings: React.FC = () => { {data?.pushbulletAccessToken && ( {intl.formatMessage(messages.pushbulletAccessTokenTip, { - PushbulletSettingsLink: function PushbulletSettingsLink( - msg - ) { - return ( - - {msg} - - ); - }, + PushbulletSettingsLink: (msg: React.ReactNode) => ( + + {msg} + + ), })} )} diff --git a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsPushover.tsx b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsPushover.tsx index c0fe7cac1..4a1cf0bf2 100644 --- a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsPushover.tsx +++ b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsPushover.tsx @@ -28,7 +28,7 @@ const messages = defineMessages({ validationPushoverUserKey: 'You must provide a valid user or group key', }); -const UserPushoverSettings: React.FC = () => { +const UserPushoverSettings = () => { const intl = useIntl(); const settings = useSettings(); const { addToast } = useToasts(); @@ -129,19 +129,16 @@ const UserPushoverSettings: React.FC = () => { * {intl.formatMessage(messages.pushoverApplicationTokenTip, { - ApplicationRegistrationLink: - function ApplicationRegistrationLink(msg) { - return ( - - {msg} - - ); - }, + ApplicationRegistrationLink: (msg: React.ReactNode) => ( + + {msg} + + ), applicationTitle: settings.currentSettings.applicationTitle, })} @@ -167,18 +164,16 @@ const UserPushoverSettings: React.FC = () => { {intl.formatMessage(messages.pushoverUserKey)} {intl.formatMessage(messages.pushoverUserKeyTip, { - UsersGroupsLink: function UsersGroupsLink(msg) { - return ( - - {msg} - - ); - }, + UsersGroupsLink: (msg: React.ReactNode) => ( + + {msg} + + ), })} @@ -190,9 +185,11 @@ const UserPushoverSettings: React.FC = () => { type="text" /> - {errors.pushoverUserKey && touched.pushoverUserKey && ( -
    {errors.pushoverUserKey}
    - )} + {errors.pushoverUserKey && + touched.pushoverUserKey && + typeof errors.pushoverUserKey === 'string' && ( +
    {errors.pushoverUserKey}
    + )} { +const UserTelegramSettings = () => { const intl = useIntl(); const { addToast } = useToasts(); const router = useRouter(); @@ -112,31 +112,25 @@ const UserTelegramSettings: React.FC = () => { {data?.telegramBotUsername && ( {intl.formatMessage(messages.telegramChatIdTipLong, { - TelegramBotLink: function TelegramBotLink(msg) { - return ( - - {msg} - - ); - }, - GetIdBotLink: function GetIdBotLink(msg) { - return ( - - {msg} - - ); - }, - code: function code(msg) { - return {msg}; - }, + TelegramBotLink: (msg: React.ReactNode) => ( + + {msg} + + ), + GetIdBotLink: (msg: React.ReactNode) => ( + + {msg} + + ), + code: (msg: React.ReactNode) => {msg}, })} )} @@ -149,9 +143,11 @@ const UserTelegramSettings: React.FC = () => { type="text" /> - {errors.telegramChatId && touched.telegramChatId && ( -
    {errors.telegramChatId}
    - )} + {errors.telegramChatId && + touched.telegramChatId && + typeof errors.telegramChatId === 'string' && ( +
    {errors.telegramChatId}
    + )}
    diff --git a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsWebPush.tsx b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsWebPush.tsx index 9445fde55..1438a0505 100644 --- a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsWebPush.tsx +++ b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsWebPush.tsx @@ -20,7 +20,7 @@ const messages = defineMessages({ webpushsettingsfailed: 'Web push notification settings failed to save.', }); -const UserWebPushSettings: React.FC = () => { +const UserWebPushSettings = () => { const intl = useIntl(); const { addToast } = useToasts(); const router = useRouter(); diff --git a/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx b/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx index 450e79515..ff2194429 100644 --- a/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx +++ b/src/components/UserProfile/UserSettings/UserNotificationSettings/index.tsx @@ -23,7 +23,13 @@ const messages = defineMessages({ webpush: 'Web Push', }); -const UserNotificationSettings: React.FC = ({ children }) => { +type UserNotificationSettingsProps = { + children: React.ReactNode; +}; + +const UserNotificationSettings = ({ + children, +}: UserNotificationSettingsProps) => { const intl = useIntl(); const router = useRouter(); const { user } = useUser({ id: Number(router.query.userId) }); diff --git a/src/components/UserProfile/UserSettings/UserPasswordChange/index.tsx b/src/components/UserProfile/UserSettings/UserPasswordChange/index.tsx index 6288f0645..bf2756752 100644 --- a/src/components/UserProfile/UserSettings/UserPasswordChange/index.tsx +++ b/src/components/UserProfile/UserSettings/UserPasswordChange/index.tsx @@ -39,7 +39,7 @@ const messages = defineMessages({ "You do not have permission to modify this user's password.", }); -const UserPasswordChange: React.FC = () => { +const UserPasswordChange = () => { const intl = useIntl(); const { addToast } = useToasts(); const router = useRouter(); @@ -176,9 +176,11 @@ const UserPasswordChange: React.FC = () => { autoComplete="current-password" />
    - {errors.currentPassword && touched.currentPassword && ( -
    {errors.currentPassword}
    - )} + {errors.currentPassword && + touched.currentPassword && + typeof errors.currentPassword === 'string' && ( +
    {errors.currentPassword}
    + )} )} @@ -196,9 +198,11 @@ const UserPasswordChange: React.FC = () => { autoComplete="new-password" /> - {errors.newPassword && touched.newPassword && ( -
    {errors.newPassword}
    - )} + {errors.newPassword && + touched.newPassword && + typeof errors.newPassword === 'string' && ( +
    {errors.newPassword}
    + )}
    @@ -215,9 +219,11 @@ const UserPasswordChange: React.FC = () => { autoComplete="new-password" />
    - {errors.confirmPassword && touched.confirmPassword && ( -
    {errors.confirmPassword}
    - )} + {errors.confirmPassword && + touched.confirmPassword && + typeof errors.confirmPassword === 'string' && ( +
    {errors.confirmPassword}
    + )}
    diff --git a/src/components/UserProfile/UserSettings/UserPermissions/index.tsx b/src/components/UserProfile/UserSettings/UserPermissions/index.tsx index 1d9fe22d5..b77d4bcc8 100644 --- a/src/components/UserProfile/UserSettings/UserPermissions/index.tsx +++ b/src/components/UserProfile/UserSettings/UserPermissions/index.tsx @@ -22,7 +22,7 @@ const messages = defineMessages({ unauthorizedDescription: 'You cannot modify your own permissions.', }); -const UserPermissions: React.FC = () => { +const UserPermissions = () => { const intl = useIntl(); const { addToast } = useToasts(); const router = useRouter(); diff --git a/src/components/UserProfile/UserSettings/index.tsx b/src/components/UserProfile/UserSettings/index.tsx index 8e03709d7..fbed2ba2c 100644 --- a/src/components/UserProfile/UserSettings/index.tsx +++ b/src/components/UserProfile/UserSettings/index.tsx @@ -24,7 +24,11 @@ const messages = defineMessages({ "You do not have permission to modify this user's settings.", }); -const UserSettings: React.FC = ({ children }) => { +type UserSettingsProps = { + children: React.ReactNode; +}; + +const UserSettings = ({ children }: UserSettingsProps) => { const router = useRouter(); const settings = useSettings(); const { user: currentUser } = useUser(); diff --git a/src/components/UserProfile/index.tsx b/src/components/UserProfile/index.tsx index 565240fb3..26813a987 100644 --- a/src/components/UserProfile/index.tsx +++ b/src/components/UserProfile/index.tsx @@ -37,7 +37,7 @@ const messages = defineMessages({ type MediaTitle = MovieDetails | TvDetails; -const UserProfile: React.FC = () => { +const UserProfile = () => { const intl = useIntl(); const router = useRouter(); const { user, error } = useUser({ diff --git a/src/context/InteractionContext.tsx b/src/context/InteractionContext.tsx index 0a78cdac1..9622acbf3 100644 --- a/src/context/InteractionContext.tsx +++ b/src/context/InteractionContext.tsx @@ -2,14 +2,15 @@ import React from 'react'; import useInteraction from '../hooks/useInteraction'; interface InteractionContextProps { - isTouch: boolean; + isTouch?: boolean; + children?: React.ReactNode; } export const InteractionContext = React.createContext({ isTouch: false, }); -export const InteractionProvider: React.FC = ({ children }) => { +export const InteractionProvider = ({ children }: InteractionContextProps) => { const isTouch = useInteraction(); return ( diff --git a/src/context/SettingsContext.tsx b/src/context/SettingsContext.tsx index e0c36e1fa..45d6f5749 100644 --- a/src/context/SettingsContext.tsx +++ b/src/context/SettingsContext.tsx @@ -4,6 +4,7 @@ import type { PublicSettingsResponse } from '../../server/interfaces/api/setting export interface SettingsContextProps { currentSettings: PublicSettingsResponse; + children?: React.ReactNode; } const defaultSettings = { @@ -29,10 +30,10 @@ export const SettingsContext = React.createContext({ currentSettings: defaultSettings, }); -export const SettingsProvider: React.FC = ({ +export const SettingsProvider = ({ children, currentSettings, -}) => { +}: SettingsContextProps) => { const { data, error } = useSWR( '/api/v1/settings/public', { fallbackData: currentSettings } diff --git a/src/context/UserContext.tsx b/src/context/UserContext.tsx index 4f88e8837..e81cda130 100644 --- a/src/context/UserContext.tsx +++ b/src/context/UserContext.tsx @@ -5,6 +5,7 @@ import { useUser } from '../hooks/useUser'; interface UserContextProps { initialUser: User; + children?: React.ReactNode; } /** @@ -12,10 +13,7 @@ interface UserContextProps { * cache on server side render. It also will handle redirecting the user to * the login page if their session ever becomes invalid. */ -export const UserContext: React.FC = ({ - initialUser, - children, -}) => { +export const UserContext = ({ initialUser, children }: UserContextProps) => { const { user, error, revalidate } = useUser({ initialData: initialUser }); const router = useRouter(); const routing = useRef(false); diff --git a/src/hooks/useIsTouch.ts b/src/hooks/useIsTouch.ts index 2f5ac9831..6342b0ef9 100644 --- a/src/hooks/useIsTouch.ts +++ b/src/hooks/useIsTouch.ts @@ -3,5 +3,5 @@ import { InteractionContext } from '../context/InteractionContext'; export const useIsTouch = (): boolean => { const { isTouch } = useContext(InteractionContext); - return isTouch; + return isTouch ?? false; }; diff --git a/src/pages/404.tsx b/src/pages/404.tsx index f4589467b..eff80720d 100644 --- a/src/pages/404.tsx +++ b/src/pages/404.tsx @@ -10,7 +10,7 @@ const messages = defineMessages({ returnHome: 'Return Home', }); -const Custom404: React.FC = () => { +const Custom404 = () => { const intl = useIntl(); return ( diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 3ff2e9ec4..0abbd55f0 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -21,6 +21,7 @@ import { SettingsProvider } from '../context/SettingsContext'; import { UserContext } from '../context/UserContext'; import type { User } from '../hooks/useUser'; import '../styles/globals.css'; +import { polyfillIntl } from '../utils/polyfillIntl'; // eslint-disable-next-line @typescript-eslint/no-explicit-any const loadLocaleData = (locale: AvailableLocale): Promise => { @@ -241,6 +242,7 @@ CoreApp.getInitialProps = async (initialProps) => { : currentSettings.locale; const messages = await loadLocaleData(locale as AvailableLocale); + await polyfillIntl(locale); return { ...appInitialProps, user, messages, locale, currentSettings }; }; diff --git a/src/pages/login/plex/loading.tsx b/src/pages/login/plex/loading.tsx index c8ba268a6..487463381 100644 --- a/src/pages/login/plex/loading.tsx +++ b/src/pages/login/plex/loading.tsx @@ -1,7 +1,7 @@ import React from 'react'; import LoadingSpinner from '../../../components/Common/LoadingSpinner'; -const PlexLoading: React.FC = () => { +const PlexLoading = () => { return (
    diff --git a/src/pages/search.tsx b/src/pages/search.tsx index 05ed18a80..55f95f136 100644 --- a/src/pages/search.tsx +++ b/src/pages/search.tsx @@ -1,7 +1,7 @@ import React from 'react'; import Search from '../components/Search'; -const SearchPage: React.FC = () => { +const SearchPage = () => { return ; }; diff --git a/src/utils/polyfillIntl.ts b/src/utils/polyfillIntl.ts new file mode 100644 index 000000000..0a9b79e32 --- /dev/null +++ b/src/utils/polyfillIntl.ts @@ -0,0 +1,37 @@ +import { shouldPolyfill as shouldPolyfillPluralrules } from '@formatjs/intl-pluralrules/should-polyfill'; +import { shouldPolyfill as shouldPolyfillLocale } from '@formatjs/intl-locale/should-polyfill'; +import { shouldPolyfill as shouldPolyfillDisplayNames } from '@formatjs/intl-displaynames/should-polyfill'; + +const polyfillLocale = async () => { + if (shouldPolyfillLocale()) { + await import('@formatjs/intl-locale/polyfill'); + } +}; + +const polyfillPluralRules = async (locale: string) => { + const unsupportedLocale = shouldPolyfillPluralrules(locale); + // This locale is supported + if (!unsupportedLocale) { + return; + } + // Load the polyfill 1st BEFORE loading data + await import('@formatjs/intl-pluralrules/polyfill-force'); + await import(`@formatjs/intl-pluralrules/locale-data/${unsupportedLocale}`); +}; + +const polyfillDisplayNames = async (locale: string) => { + const unsupportedLocale = shouldPolyfillDisplayNames(locale); + // This locale is supported + if (!unsupportedLocale) { + return; + } + // Load the polyfill 1st BEFORE loading data + await import('@formatjs/intl-displaynames/polyfill-force'); + await import(`@formatjs/intl-displaynames/locale-data/${unsupportedLocale}`); +}; + +export const polyfillIntl = async (locale: string) => { + await polyfillLocale(); + await polyfillPluralRules(locale); + await polyfillDisplayNames(locale); +}; diff --git a/yarn.lock b/yarn.lock index d81226633..d8f15b20a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1646,14 +1646,6 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@formatjs/ecma402-abstract@1.11.4": - version "1.11.4" - resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz#b962dfc4ae84361f9f08fbce411b4e4340930eda" - integrity sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw== - dependencies: - "@formatjs/intl-localematcher" "0.2.25" - tslib "^2.1.0" - "@formatjs/ecma402-abstract@1.11.8": version "1.11.8" resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.8.tgz#f4015dfb6a837369d94c6ba82455c609e45bce20" @@ -1676,21 +1668,12 @@ dependencies: tslib "^2.0.1" -"@formatjs/fast-memoize@1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz#e6f5aee2e4fd0ca5edba6eba7668e2d855e0fc21" - integrity sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg== +"@formatjs/fast-memoize@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-1.2.4.tgz#4b5ddce9eb7803ff0bd4052387672151a8b7f8a0" + integrity sha512-9ARYoLR8AEzXvj2nYrOVHY/h1dDMDWGTnKDLXSISF1uoPakSmfcZuSqjiqZX2wRkEUimPxdwTu/agyozBtZRHA== dependencies: - tslib "^2.1.0" - -"@formatjs/icu-messageformat-parser@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz#a54293dd7f098d6a6f6a084ab08b6d54a3e8c12d" - integrity sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw== - dependencies: - "@formatjs/ecma402-abstract" "1.11.4" - "@formatjs/icu-skeleton-parser" "1.3.6" - tslib "^2.1.0" + tslib "2.4.0" "@formatjs/icu-messageformat-parser@2.1.4": version "2.1.4" @@ -1709,38 +1692,39 @@ "@formatjs/ecma402-abstract" "1.11.8" tslib "2.4.0" -"@formatjs/icu-skeleton-parser@1.3.6": - version "1.3.6" - resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz#4ce8c0737d6f07b735288177049e97acbf2e8964" - integrity sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg== +"@formatjs/intl-displaynames@6.0.3", "@formatjs/intl-displaynames@^6.0.3": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@formatjs/intl-displaynames/-/intl-displaynames-6.0.3.tgz#e648a91bccd9fb21519090eaafece3be9d15f480" + integrity sha512-Mxh6W1VOlmiEvO/QPBrBQHlXrIn5VxjJWyyEI0V7ZHNGl0ee8AjSlq7vIJG8GodRJqGUuutF6N3OB/6qFv0YWg== dependencies: - "@formatjs/ecma402-abstract" "1.11.4" - tslib "^2.1.0" + "@formatjs/ecma402-abstract" "1.11.8" + "@formatjs/intl-localematcher" "0.2.28" + tslib "2.4.0" -"@formatjs/intl-displaynames@5.4.3": - version "5.4.3" - resolved "https://registry.yarnpkg.com/@formatjs/intl-displaynames/-/intl-displaynames-5.4.3.tgz#e468586694350c722c7efab1a31fcde68aeaed8b" - integrity sha512-4r12A3mS5dp5hnSaQCWBuBNfi9Amgx2dzhU4lTFfhSxgb5DOAiAbMpg6+7gpWZgl4ahsj3l2r/iHIjdmdXOE2Q== +"@formatjs/intl-getcanonicallocales@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@formatjs/intl-getcanonicallocales/-/intl-getcanonicallocales-2.0.2.tgz#e9fd5d2215ce0dba4d8611b37ac87c5fae60a1a7" + integrity sha512-nMkPblAjgK49ORueVhyuKJDXOCq8at2h9Xf0lqabBZylaX3xLEeuAW2eMXuwJ/ascYYQnwuxeukd/qlzDkuJzQ== dependencies: - "@formatjs/ecma402-abstract" "1.11.4" - "@formatjs/intl-localematcher" "0.2.25" - tslib "^2.1.0" + tslib "2.4.0" -"@formatjs/intl-listformat@6.5.3": - version "6.5.3" - resolved "https://registry.yarnpkg.com/@formatjs/intl-listformat/-/intl-listformat-6.5.3.tgz#f29da613a8062dc3e4e3d847ba890c3ea745f051" - integrity sha512-ozpz515F/+3CU+HnLi5DYPsLa6JoCfBggBSSg/8nOB5LYSFW9+ZgNQJxJ8tdhKYeODT+4qVHX27EeJLoxLGLNg== +"@formatjs/intl-listformat@7.0.3": + version "7.0.3" + resolved "https://registry.yarnpkg.com/@formatjs/intl-listformat/-/intl-listformat-7.0.3.tgz#8627969b77849559d148bc9536d0884c2271e6de" + integrity sha512-ampNLRGZl/08epHa3i5sRmcHGLneC6JrknexbbgnexYFNSmJ6AbL/dCzgrQzw2Efl+5AZK7UbNFxcDYY3RePvw== dependencies: - "@formatjs/ecma402-abstract" "1.11.4" - "@formatjs/intl-localematcher" "0.2.25" - tslib "^2.1.0" + "@formatjs/ecma402-abstract" "1.11.8" + "@formatjs/intl-localematcher" "0.2.28" + tslib "2.4.0" -"@formatjs/intl-localematcher@0.2.25": - version "0.2.25" - resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz#60892fe1b271ec35ba07a2eb018a2dd7bca6ea3a" - integrity sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA== +"@formatjs/intl-locale@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@formatjs/intl-locale/-/intl-locale-3.0.3.tgz#ae112831c06209d1d7286b36e7b824a5e84ddd63" + integrity sha512-AZrh8z/GaR+4ogy9LR1lKouXwXWKRK4TDtwFv6SIFIJgF9SQ6l4gdJ1/k13susw6NMAQiKdtG9IdhEtKXsp+6w== dependencies: - tslib "^2.1.0" + "@formatjs/ecma402-abstract" "1.11.8" + "@formatjs/intl-getcanonicallocales" "2.0.2" + tslib "2.4.0" "@formatjs/intl-localematcher@0.2.28": version "0.2.28" @@ -1757,18 +1741,27 @@ "@formatjs/ecma402-abstract" "1.4.0" tslib "^2.0.1" -"@formatjs/intl@2.2.1": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@formatjs/intl/-/intl-2.2.1.tgz#6daf4dabed055b17f467f0aa1bc073a626bc9189" - integrity sha512-vgvyUOOrzqVaOFYzTf2d3+ToSkH2JpR7x/4U1RyoHQLmvEaTQvXJ7A2qm1Iy3brGNXC/+/7bUlc3lpH+h/LOJA== +"@formatjs/intl-pluralrules@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@formatjs/intl-pluralrules/-/intl-pluralrules-5.0.3.tgz#78d76da83470495e3e4d82df8cd6250578c1685c" + integrity sha512-av3ks022vDE1dbCUAqwYc8bnGRcAPKK6C7ZRINNjjQn43ZQVX1AXc7PY2RqO/GXzkixsSWfHFZjSgzzbhbd21A== dependencies: - "@formatjs/ecma402-abstract" "1.11.4" - "@formatjs/fast-memoize" "1.2.1" - "@formatjs/icu-messageformat-parser" "2.1.0" - "@formatjs/intl-displaynames" "5.4.3" - "@formatjs/intl-listformat" "6.5.3" - intl-messageformat "9.13.0" - tslib "^2.1.0" + "@formatjs/ecma402-abstract" "1.11.8" + "@formatjs/intl-localematcher" "0.2.28" + tslib "2.4.0" + +"@formatjs/intl@2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@formatjs/intl/-/intl-2.3.1.tgz#eccd6d03e4db18c256181f235b1d0a7f7aaebf5a" + integrity sha512-f06qZ/ukpeN24gc01qFjh3P+r3FU/ikY4yG+fDJu6dPNvpUQzDy98lYogA1dr6ig2UtrnoEk3xncyFPL1e9cZw== + dependencies: + "@formatjs/ecma402-abstract" "1.11.8" + "@formatjs/fast-memoize" "1.2.4" + "@formatjs/icu-messageformat-parser" "2.1.4" + "@formatjs/intl-displaynames" "6.0.3" + "@formatjs/intl-listformat" "7.0.3" + intl-messageformat "10.1.1" + tslib "2.4.0" "@formatjs/ts-transformer@2.13.0", "@formatjs/ts-transformer@^2.6.0": version "2.13.0" @@ -3052,12 +3045,12 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== -"@types/react-dom@17.0.17": - version "17.0.17" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.17.tgz#2e3743277a793a96a99f1bf87614598289da68a1" - integrity sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg== +"@types/react-dom@18.0.6": + version "18.0.6" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.6.tgz#36652900024842b74607a17786b6662dd1e103a1" + integrity sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA== dependencies: - "@types/react" "^17" + "@types/react" "*" "@types/react-transition-group@4.4.5": version "4.4.5" @@ -3073,10 +3066,10 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@16 || 17 || 18", "@types/react@17.0.45", "@types/react@^17": - version "17.0.45" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.45.tgz#9b3d5b661fd26365fefef0e766a1c6c30ccf7b3f" - integrity sha512-YfhQ22Lah2e3CHPsb93tRwIGNiSwkuz1/blk4e6QrWS0jQzCSNbGLtOEYhPg02W0yGTTmpajp7dCTbBAMN3qsg== +"@types/react@*", "@types/react@16 || 17 || 18", "@types/react@18.0.17": + version "18.0.17" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.17.tgz#4583d9c322d67efe4b39a935d223edcc7050ccf4" + integrity sha512-38ETy4tL+rn4uQQi7mB81G7V1g0u2ryquNmsVIOKUAEIDK+3CUjZ6rSRpdvS99dNBnkLFL83qfmtLacGOTIhwQ== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -7106,15 +7099,15 @@ intl-messageformat-parser@^5.3.7: dependencies: "@formatjs/intl-numberformat" "^5.5.2" -intl-messageformat@9.13.0: - version "9.13.0" - resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-9.13.0.tgz#97360b73bd82212e4f6005c712a4a16053165468" - integrity sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw== +intl-messageformat@10.1.1: + version "10.1.1" + resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.1.1.tgz#226767e7921fa86cef2cbe4a13911050716720bc" + integrity sha512-FeJne2oooYW6shLPbrqyjRX6hTELVrQ90Dn88z7NomLk/xZBCLxLPAkgaYaTQJBRBV78nZ933d8APHHkTQrD9Q== dependencies: - "@formatjs/ecma402-abstract" "1.11.4" - "@formatjs/fast-memoize" "1.2.1" - "@formatjs/icu-messageformat-parser" "2.1.0" - tslib "^2.1.0" + "@formatjs/ecma402-abstract" "1.11.8" + "@formatjs/fast-memoize" "1.2.4" + "@formatjs/icu-messageformat-parser" "2.1.4" + tslib "2.4.0" intl@1.2.5: version "1.2.5" @@ -10232,14 +10225,13 @@ react-animate-height@2.1.2: classnames "^2.2.5" prop-types "^15.6.1" -react-dom@17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" - integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== +react-dom@18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler "^0.20.2" + scheduler "^0.23.0" react-fast-compare@^2.0.1: version "2.0.4" @@ -10251,21 +10243,21 @@ react-intersection-observer@9.4.0: resolved "https://registry.yarnpkg.com/react-intersection-observer/-/react-intersection-observer-9.4.0.tgz#f6b6e616e625f9bf255857c5cba9dbf7b1825ec7" integrity sha512-v0403CmomOVlzhqFXlzOxg0ziLcVq8mfbP0AwAcEQWgZmR2OulOT79Ikznw4UlB3N+jlUYqLMe4SDHUOyp0t2A== -react-intl@5.25.1: - version "5.25.1" - resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-5.25.1.tgz#68a73aefc485c9bf70062381ae7f6f4791680879" - integrity sha512-pkjdQDvpJROoXLMltkP/5mZb0/XqrqLoPGKUCfbdkP8m6U9xbK40K51Wu+a4aQqTEvEK5lHBk0fWzUV72SJ3Hg== +react-intl@6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-6.0.5.tgz#8adcb9108682c82e625a8ce7826283afd3537f62" + integrity sha512-nDZ3BosuE8WdovcGxsrjj1aIgJZklSL5aORs5oah+5tLQTzUdOEstzJEYQPM+sxl1dkDOu7RCuw0z9oI9ENf9g== dependencies: - "@formatjs/ecma402-abstract" "1.11.4" - "@formatjs/icu-messageformat-parser" "2.1.0" - "@formatjs/intl" "2.2.1" - "@formatjs/intl-displaynames" "5.4.3" - "@formatjs/intl-listformat" "6.5.3" + "@formatjs/ecma402-abstract" "1.11.8" + "@formatjs/icu-messageformat-parser" "2.1.4" + "@formatjs/intl" "2.3.1" + "@formatjs/intl-displaynames" "6.0.3" + "@formatjs/intl-listformat" "7.0.3" "@types/hoist-non-react-statics" "^3.3.1" "@types/react" "16 || 17 || 18" hoist-non-react-statics "^3.3.2" - intl-messageformat "9.13.0" - tslib "^2.1.0" + intl-messageformat "10.1.1" + tslib "2.4.0" react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" @@ -10368,13 +10360,12 @@ react-use-clipboard@1.0.8: dependencies: copy-to-clipboard "^3.3.1" -react@17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" - integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== +react@18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" read-babelrc-up@^1.1.0: version "1.1.0" @@ -10848,13 +10839,12 @@ sax@>=0.6.0: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -scheduler@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" - integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" schema-utils@*: version "4.0.0" From c143c0b8d285896a7ad5f42f2a7dc1e38b55cc3a Mon Sep 17 00:00:00 2001 From: TheCatLady <52870424+TheCatLady@users.noreply.github.com> Date: Thu, 18 Aug 2022 04:30:40 -0400 Subject: [PATCH 32/81] fix: better ordering of RequestButton options & properly handle failed requests (#2944) * fix: better ordering of RequestButton options & properly handle failed requests * fix: appease prettier --- overseerr-api.yml | 11 +- server/constants/media.ts | 1 + server/entity/MediaRequest.ts | 18 +- server/routes/request.ts | 4 + src/components/RequestBlock/index.tsx | 5 + src/components/RequestButton/index.tsx | 338 +++++++++--------- src/components/RequestCard/index.tsx | 12 +- .../RequestList/RequestItem/index.tsx | 13 +- src/components/RequestList/index.tsx | 4 + 9 files changed, 210 insertions(+), 196 deletions(-) diff --git a/overseerr-api.yml b/overseerr-api.yml index 6bf7f69b7..6eaef6ba0 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -4430,7 +4430,16 @@ paths: schema: type: string nullable: true - enum: [all, approved, available, pending, processing, unavailable] + enum: + [ + all, + approved, + available, + pending, + processing, + unavailable, + failed, + ] - in: query name: sort schema: diff --git a/server/constants/media.ts b/server/constants/media.ts index d9ef9e022..de2bf834d 100644 --- a/server/constants/media.ts +++ b/server/constants/media.ts @@ -2,6 +2,7 @@ export enum MediaRequestStatus { PENDING = 1, APPROVED, DECLINED, + FAILED, } export enum MediaType { diff --git a/server/entity/MediaRequest.ts b/server/entity/MediaRequest.ts index 3420c46d3..da19d0d4d 100644 --- a/server/entity/MediaRequest.ts +++ b/server/entity/MediaRequest.ts @@ -451,10 +451,13 @@ export class MediaRequest { await mediaRepository.save(media); }) .catch(async () => { - media[this.is4k ? 'status4k' : 'status'] = MediaStatus.UNKNOWN; - await mediaRepository.save(media); + const requestRepository = getRepository(MediaRequest); + + this.status = MediaRequestStatus.FAILED; + requestRepository.save(this); + logger.warn( - 'Something went wrong sending movie request to Radarr, marking status as UNKNOWN', + 'Something went wrong sending movie request to Radarr, marking status as FAILED', { label: 'Media Request', requestId: this.id, @@ -684,10 +687,13 @@ export class MediaRequest { await mediaRepository.save(media); }) .catch(async () => { - media[this.is4k ? 'status4k' : 'status'] = MediaStatus.UNKNOWN; - await mediaRepository.save(media); + const requestRepository = getRepository(MediaRequest); + + this.status = MediaRequestStatus.FAILED; + requestRepository.save(this); + logger.warn( - 'Something went wrong sending series request to Sonarr, marking status as UNKNOWN', + 'Something went wrong sending series request to Sonarr, marking status as FAILED', { label: 'Media Request', requestId: this.id, diff --git a/server/routes/request.ts b/server/routes/request.ts index a8ad189d5..68d252547 100644 --- a/server/routes/request.ts +++ b/server/routes/request.ts @@ -40,11 +40,15 @@ requestRoutes.get, RequestResultsResponse>( MediaRequestStatus.APPROVED, ]; break; + case 'failed': + statusFilter = [MediaRequestStatus.FAILED]; + break; default: statusFilter = [ MediaRequestStatus.PENDING, MediaRequestStatus.APPROVED, MediaRequestStatus.DECLINED, + MediaRequestStatus.FAILED, ]; } diff --git a/src/components/RequestBlock/index.tsx b/src/components/RequestBlock/index.tsx index c20b8f25c..7521a5f1c 100644 --- a/src/components/RequestBlock/index.tsx +++ b/src/components/RequestBlock/index.tsx @@ -179,6 +179,11 @@ const RequestBlock = ({ request, onUpdate }: RequestBlockProps) => { {intl.formatMessage(globalMessages.pending)} )} + {request.status === MediaRequestStatus.FAILED && ( + + {intl.formatMessage(globalMessages.failed)} + + )}
    diff --git a/src/components/RequestButton/index.tsx b/src/components/RequestButton/index.tsx index 8117d7bee..417acd846 100644 --- a/src/components/RequestButton/index.tsx +++ b/src/components/RequestButton/index.tsx @@ -77,13 +77,13 @@ const RequestButton = ({ (request) => request.status === MediaRequestStatus.PENDING && request.is4k ); + // Current user's pending request, or the first pending request const activeRequest = useMemo(() => { return activeRequests && activeRequests.length > 0 ? activeRequests.find((request) => request.requestedBy.id === user?.id) ?? activeRequests[0] : undefined; }, [activeRequests, user]); - const active4kRequest = useMemo(() => { return active4kRequests && active4kRequests.length > 0 ? active4kRequests.find( @@ -121,6 +121,151 @@ const RequestButton = ({ }; const buttons: ButtonOption[] = []; + + // If there are pending requests, show request management options first + if (activeRequest || active4kRequest) { + if ( + activeRequest && + (activeRequest.requestedBy.id === user?.id || + (activeRequests?.length === 1 && + hasPermission(Permission.MANAGE_REQUESTS))) + ) { + buttons.push({ + id: 'active-request', + text: intl.formatMessage(messages.viewrequest), + action: () => { + setEditRequest(true); + setShowRequestModal(true); + }, + svg: , + }); + } + + if ( + activeRequest && + hasPermission(Permission.MANAGE_REQUESTS) && + mediaType === 'movie' + ) { + buttons.push( + { + id: 'approve-request', + text: intl.formatMessage(messages.approverequest), + action: () => { + modifyRequest(activeRequest, 'approve'); + }, + svg: , + }, + { + id: 'decline-request', + text: intl.formatMessage(messages.declinerequest), + action: () => { + modifyRequest(activeRequest, 'decline'); + }, + svg: , + } + ); + } else if ( + activeRequests && + activeRequests.length > 0 && + hasPermission(Permission.MANAGE_REQUESTS) && + mediaType === 'tv' + ) { + buttons.push( + { + id: 'approve-request-batch', + text: intl.formatMessage(messages.approverequests, { + requestCount: activeRequests.length, + }), + action: () => { + modifyRequests(activeRequests, 'approve'); + }, + svg: , + }, + { + id: 'decline-request-batch', + text: intl.formatMessage(messages.declinerequests, { + requestCount: activeRequests.length, + }), + action: () => { + modifyRequests(activeRequests, 'decline'); + }, + svg: , + } + ); + } + + if ( + active4kRequest && + (active4kRequest.requestedBy.id === user?.id || + (active4kRequests?.length === 1 && + hasPermission(Permission.MANAGE_REQUESTS))) + ) { + buttons.push({ + id: 'active-4k-request', + text: intl.formatMessage(messages.viewrequest4k), + action: () => { + setEditRequest(true); + setShowRequest4kModal(true); + }, + svg: , + }); + } + + if ( + active4kRequest && + hasPermission(Permission.MANAGE_REQUESTS) && + mediaType === 'movie' + ) { + buttons.push( + { + id: 'approve-4k-request', + text: intl.formatMessage(messages.approverequest4k), + action: () => { + modifyRequest(active4kRequest, 'approve'); + }, + svg: , + }, + { + id: 'decline-4k-request', + text: intl.formatMessage(messages.declinerequest4k), + action: () => { + modifyRequest(active4kRequest, 'decline'); + }, + svg: , + } + ); + } else if ( + active4kRequests && + active4kRequests.length > 0 && + hasPermission(Permission.MANAGE_REQUESTS) && + mediaType === 'tv' + ) { + buttons.push( + { + id: 'approve-4k-request-batch', + text: intl.formatMessage(messages.approve4krequests, { + requestCount: active4kRequests.length, + }), + action: () => { + modifyRequests(active4kRequests, 'approve'); + }, + svg: , + }, + { + id: 'decline-4k-request-batch', + text: intl.formatMessage(messages.decline4krequests, { + requestCount: active4kRequests.length, + }), + action: () => { + modifyRequests(active4kRequests, 'decline'); + }, + svg: , + } + ); + } + } + + // Standard request button if ( (!media || media.status === MediaStatus.UNKNOWN) && hasPermission( @@ -142,8 +287,29 @@ const RequestButton = ({ }, svg: , }); + } else if ( + mediaType === 'tv' && + (!activeRequest || activeRequest.requestedBy.id !== user?.id) && + hasPermission([Permission.REQUEST, Permission.REQUEST_TV], { + type: 'or', + }) && + media && + media.status !== MediaStatus.AVAILABLE && + media.status !== MediaStatus.UNKNOWN && + !isShowComplete + ) { + buttons.push({ + id: 'request-more', + text: intl.formatMessage(messages.requestmore), + action: () => { + setEditRequest(false); + setShowRequestModal(true); + }, + svg: , + }); } + // 4K request button if ( (!media || media.status4k === MediaStatus.UNKNOWN) && hasPermission( @@ -167,175 +333,7 @@ const RequestButton = ({ }, svg: , }); - } - - if ( - activeRequest && - (activeRequest.requestedBy.id === user?.id || - (activeRequests?.length === 1 && - hasPermission(Permission.MANAGE_REQUESTS))) - ) { - buttons.push({ - id: 'active-request', - text: intl.formatMessage(messages.viewrequest), - action: () => { - setEditRequest(true); - setShowRequestModal(true); - }, - svg: , - }); - } - - if ( - active4kRequest && - (active4kRequest.requestedBy.id === user?.id || - (active4kRequests?.length === 1 && - hasPermission(Permission.MANAGE_REQUESTS))) - ) { - buttons.push({ - id: 'active-4k-request', - text: intl.formatMessage(messages.viewrequest4k), - action: () => { - setEditRequest(true); - setShowRequest4kModal(true); - }, - svg: , - }); - } - - if ( - activeRequest && - hasPermission(Permission.MANAGE_REQUESTS) && - mediaType === 'movie' - ) { - buttons.push( - { - id: 'approve-request', - text: intl.formatMessage(messages.approverequest), - action: () => { - modifyRequest(activeRequest, 'approve'); - }, - svg: , - }, - { - id: 'decline-request', - text: intl.formatMessage(messages.declinerequest), - action: () => { - modifyRequest(activeRequest, 'decline'); - }, - svg: , - } - ); - } - - if ( - activeRequests && - activeRequests.length > 0 && - hasPermission(Permission.MANAGE_REQUESTS) && - mediaType === 'tv' - ) { - buttons.push( - { - id: 'approve-request-batch', - text: intl.formatMessage(messages.approverequests, { - requestCount: activeRequests.length, - }), - action: () => { - modifyRequests(activeRequests, 'approve'); - }, - svg: , - }, - { - id: 'decline-request-batch', - text: intl.formatMessage(messages.declinerequests, { - requestCount: activeRequests.length, - }), - action: () => { - modifyRequests(activeRequests, 'decline'); - }, - svg: , - } - ); - } - - if ( - active4kRequest && - hasPermission(Permission.MANAGE_REQUESTS) && - mediaType === 'movie' - ) { - buttons.push( - { - id: 'approve-4k-request', - text: intl.formatMessage(messages.approverequest4k), - action: () => { - modifyRequest(active4kRequest, 'approve'); - }, - svg: , - }, - { - id: 'decline-4k-request', - text: intl.formatMessage(messages.declinerequest4k), - action: () => { - modifyRequest(active4kRequest, 'decline'); - }, - svg: , - } - ); - } - - if ( - active4kRequests && - active4kRequests.length > 0 && - hasPermission(Permission.MANAGE_REQUESTS) && - mediaType === 'tv' - ) { - buttons.push( - { - id: 'approve-4k-request-batch', - text: intl.formatMessage(messages.approve4krequests, { - requestCount: active4kRequests.length, - }), - action: () => { - modifyRequests(active4kRequests, 'approve'); - }, - svg: , - }, - { - id: 'decline-4k-request-batch', - text: intl.formatMessage(messages.decline4krequests, { - requestCount: active4kRequests.length, - }), - action: () => { - modifyRequests(active4kRequests, 'decline'); - }, - svg: , - } - ); - } - - if ( - mediaType === 'tv' && - (!activeRequest || activeRequest.requestedBy.id !== user?.id) && - hasPermission([Permission.REQUEST, Permission.REQUEST_TV], { - type: 'or', - }) && - media && - media.status !== MediaStatus.AVAILABLE && - media.status !== MediaStatus.UNKNOWN && - !isShowComplete - ) { - buttons.push({ - id: 'request-more', - text: intl.formatMessage(messages.requestmore), - action: () => { - setEditRequest(false); - setShowRequestModal(true); - }, - svg: , - }); - } - - if ( + } else if ( mediaType === 'tv' && (!active4kRequest || active4kRequest.requestedBy.id !== user?.id) && hasPermission([Permission.REQUEST_4K, Permission.REQUEST_4K_TV], { diff --git a/src/components/RequestCard/index.tsx b/src/components/RequestCard/index.tsx index 53f7b6841..bbeaa8bd5 100644 --- a/src/components/RequestCard/index.tsx +++ b/src/components/RequestCard/index.tsx @@ -12,10 +12,7 @@ import { useInView } from 'react-intersection-observer'; import { defineMessages, useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import useSWR, { mutate } from 'swr'; -import { - MediaRequestStatus, - MediaStatus, -} from '../../../server/constants/media'; +import { MediaRequestStatus } from '../../../server/constants/media'; import type { MediaRequest } from '../../../server/entity/MediaRequest'; import type { MovieDetails } from '../../../server/models/Movie'; import type { TvDetails } from '../../../server/models/Tv'; @@ -275,8 +272,7 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => { {intl.formatMessage(globalMessages.declined)} - ) : requestData.media[requestData.is4k ? 'status4k' : 'status'] === - MediaStatus.UNKNOWN ? ( + ) : requestData.status === MediaRequestStatus.FAILED ? ( { )}
    - {requestData.media[requestData.is4k ? 'status4k' : 'status'] === - MediaStatus.UNKNOWN && - requestData.status !== MediaRequestStatus.DECLINED && + {requestData.status === MediaRequestStatus.FAILED && hasPermission(Permission.MANAGE_REQUESTS) && (
    - {requestData.media[requestData.is4k ? 'status4k' : 'status'] === - MediaStatus.UNKNOWN && - requestData.status !== MediaRequestStatus.DECLINED && + {requestData.status === MediaRequestStatus.FAILED && hasPermission(Permission.MANAGE_REQUESTS) && ( + {requestData && ( + <> + {hasPermission( + [Permission.MANAGE_REQUESTS, Permission.REQUEST_VIEW], + { type: 'or' } + ) && ( + + )} +
    + + {intl.formatMessage(globalMessages.status)} + + {requestData.status === MediaRequestStatus.DECLINED || + requestData.status === MediaRequestStatus.FAILED ? ( + + {requestData.status === MediaRequestStatus.DECLINED + ? intl.formatMessage(globalMessages.declined) + : intl.formatMessage(globalMessages.failed)} + + ) : ( + 0 + } + is4k={requestData.is4k} + plexUrl={ + requestData.is4k + ? requestData.media.plexUrl4k + : requestData.media.plexUrl + } + serviceUrl={ + hasPermission(Permission.ADMIN) + ? requestData.is4k + ? requestData.media.serviceUrl4k + : requestData.media.serviceUrl + : undefined + } + /> + )} +
    + )} +
    + {hasPermission(Permission.MANAGE_REQUESTS) && + requestData?.media.id && ( + + )} +
    @@ -165,7 +253,7 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => { } if (!title || !requestData) { - return ; + return ; } return ( @@ -182,7 +270,10 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => { setShowEditModal(false); }} /> -
    +
    {title.backdropPath && (
    { />
    )} -
    +
    {(isMovie(title) ? title.releaseDate : title.firstAirDate)?.slice( 0, diff --git a/src/components/RequestList/RequestItem/index.tsx b/src/components/RequestList/RequestItem/index.tsx index 4a5e396ae..aef965293 100644 --- a/src/components/RequestList/RequestItem/index.tsx +++ b/src/components/RequestList/RequestItem/index.tsx @@ -32,50 +32,229 @@ const messages = defineMessages({ requesteddate: 'Requested', modified: 'Modified', modifieduserdate: '{date} by {user}', - mediaerror: 'The associated title for this request is no longer available.', + mediaerror: '{mediaType} Not Found', editrequest: 'Edit Request', deleterequest: 'Delete Request', cancelRequest: 'Cancel Request', + tmdbid: 'TMDb ID', + tvdbid: 'TVDB ID', }); const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => { return (movie as MovieDetails).title !== undefined; }; -interface RequestItemErroProps { - mediaId?: number; +interface RequestItemErrorProps { + requestData?: MediaRequest; revalidateList: () => void; } const RequestItemError = ({ - mediaId, + requestData, revalidateList, -}: RequestItemErroProps) => { +}: RequestItemErrorProps) => { const intl = useIntl(); const { hasPermission } = useUser(); const deleteRequest = async () => { - await axios.delete(`/api/v1/media/${mediaId}`); + await axios.delete(`/api/v1/media/${requestData?.media.id}`); revalidateList(); }; return ( -
    - - {intl.formatMessage(messages.mediaerror)} - - {hasPermission(Permission.MANAGE_REQUESTS) && mediaId && ( -
    +
    +
    +
    +
    + {intl.formatMessage(messages.mediaerror, { + mediaType: intl.formatMessage( + requestData?.type + ? requestData?.type === 'movie' + ? globalMessages.movie + : globalMessages.tvshow + : globalMessages.request + ), + })} +
    + {requestData && hasPermission(Permission.MANAGE_REQUESTS) && ( + <> +
    + + {intl.formatMessage(messages.tmdbid)} + + + {requestData.media.tmdbId} + +
    + {requestData.media.tvdbId && ( +
    + + {intl.formatMessage(messages.tvdbid)} + + + {requestData?.media.tvdbId} + +
    + )} + + )} +
    +
    + {requestData && ( + <> +
    + + {intl.formatMessage(globalMessages.status)} + + {requestData.status === MediaRequestStatus.DECLINED || + requestData.status === MediaRequestStatus.FAILED ? ( + + {requestData.status === MediaRequestStatus.DECLINED + ? intl.formatMessage(globalMessages.declined) + : intl.formatMessage(globalMessages.failed)} + + ) : ( + 0 + } + is4k={requestData.is4k} + plexUrl={ + requestData.is4k + ? requestData.media.plexUrl4k + : requestData.media.plexUrl + } + serviceUrl={ + hasPermission(Permission.ADMIN) + ? requestData.is4k + ? requestData.media.serviceUrl4k + : requestData.media.serviceUrl + : undefined + } + /> + )} +
    +
    + {hasPermission( + [Permission.MANAGE_REQUESTS, Permission.REQUEST_VIEW], + { type: 'or' } + ) ? ( + <> + + {intl.formatMessage(messages.requested)} + + + {intl.formatMessage(messages.modifieduserdate, { + date: ( + + ), + user: ( + + + + + {requestData.requestedBy.displayName} + + + + ), + })} + + + ) : ( + <> + + {intl.formatMessage(messages.requesteddate)} + + + + + + )} +
    + {requestData.modifiedBy && ( +
    + + {intl.formatMessage(messages.modified)} + + + {intl.formatMessage(messages.modifieduserdate, { + date: ( + + ), + user: ( + + + + + {requestData.modifiedBy.displayName} + + + + ), + })} + +
    + )} + + )} +
    +
    +
    + {hasPermission(Permission.MANAGE_REQUESTS) && requestData?.media.id && ( -
    - )} + )} +
    ); }; @@ -151,7 +330,7 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => { if (!title || !requestData) { return ( ); diff --git a/src/components/TitleCard/ErrorCard.tsx b/src/components/TitleCard/ErrorCard.tsx new file mode 100644 index 000000000..df619289b --- /dev/null +++ b/src/components/TitleCard/ErrorCard.tsx @@ -0,0 +1,131 @@ +import { defineMessages, useIntl } from 'react-intl'; +import globalMessages from '../../i18n/globalMessages'; +import Button from '../Common/Button'; +import { CheckIcon, TrashIcon } from '@heroicons/react/solid'; +import axios from 'axios'; +import { mutate } from 'swr'; + +interface ErrorCardProps { + id: number; + tmdbId: number; + tvdbId?: number; + type: 'movie' | 'tv'; + canExpand?: boolean; +} + +const messages = defineMessages({ + mediaerror: '{mediaType} Not Found', + tmdbid: 'TMDb ID', + tvdbid: 'TVDB ID', + cleardata: 'Clear Data', +}); + +const Error = ({ id, tmdbId, tvdbId, type, canExpand }: ErrorCardProps) => { + const intl = useIntl(); + + const deleteMedia = async () => { + await axios.delete(`/api/v1/media/${id}`); + mutate('/api/v1/media?filter=allavailable&take=20&sort=mediaAdded'); + mutate('/api/v1/request?filter=all&take=10&sort=modified&skip=0'); + }; + + return ( +
    +
    +
    +
    +
    +
    + {type === 'movie' + ? intl.formatMessage(globalMessages.movie) + : intl.formatMessage(globalMessages.tvshow)} +
    +
    +
    +
    + +
    +
    +
    + +
    +
    +

    + {intl.formatMessage(messages.mediaerror, { + mediaType: intl.formatMessage( + type === 'movie' + ? globalMessages.movie + : globalMessages.tvshow + ), + })} +

    +
    +
    + + {intl.formatMessage(messages.tmdbid)} + + {tmdbId} +
    + {!!tvdbId && ( +
    + + {intl.formatMessage(messages.tvdbid)} + + {tvdbId} +
    + )} +
    +
    +
    + +
    + +
    +
    +
    +
    + ); +}; +export default Error; diff --git a/src/components/TitleCard/TmdbTitleCard.tsx b/src/components/TitleCard/TmdbTitleCard.tsx index 827db1bf1..16cb699f8 100644 --- a/src/components/TitleCard/TmdbTitleCard.tsx +++ b/src/components/TitleCard/TmdbTitleCard.tsx @@ -3,9 +3,12 @@ import useSWR from 'swr'; import TitleCard from '.'; import type { MovieDetails } from '../../../server/models/Movie'; import type { TvDetails } from '../../../server/models/Tv'; +import { Permission, useUser } from '../../hooks/useUser'; -interface TmdbTitleCardProps { +export interface TmdbTitleCardProps { + id: number; tmdbId: number; + tvdbId?: number; type: 'movie' | 'tv'; } @@ -13,7 +16,9 @@ const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => { return (movie as MovieDetails).title !== undefined; }; -const TmdbTitleCard = ({ tmdbId, type }: TmdbTitleCardProps) => { +const TmdbTitleCard = ({ id, tmdbId, tvdbId, type }: TmdbTitleCardProps) => { + const { hasPermission } = useUser(); + const { ref, inView } = useInView({ triggerOnce: true, }); @@ -32,7 +37,14 @@ const TmdbTitleCard = ({ tmdbId, type }: TmdbTitleCardProps) => { } if (!title) { - return ; + return hasPermission(Permission.ADMIN) ? ( + + ) : null; } return isMovie(title) ? ( diff --git a/src/components/TitleCard/index.tsx b/src/components/TitleCard/index.tsx index 455c75c49..8437707ad 100644 --- a/src/components/TitleCard/index.tsx +++ b/src/components/TitleCard/index.tsx @@ -15,6 +15,7 @@ import CachedImage from '../Common/CachedImage'; import RequestModal from '../RequestModal'; import Transition from '../Transition'; import Placeholder from './Placeholder'; +import ErrorCard from './ErrorCard'; interface TitleCardProps { id: number; @@ -266,4 +267,4 @@ const TitleCard = ({ ); }; -export default withProperties(TitleCard, { Placeholder }); +export default withProperties(TitleCard, { Placeholder, ErrorCard }); diff --git a/src/components/UserProfile/index.tsx b/src/components/UserProfile/index.tsx index a6b4e1e94..3fef28fc7 100644 --- a/src/components/UserProfile/index.tsx +++ b/src/components/UserProfile/index.tsx @@ -293,7 +293,9 @@ const UserProfile = () => { items={watchData.recentlyWatched.map((item) => ( ))} diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index 54043de42..ca405970c 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -292,18 +292,22 @@ "components.RequestButton.viewrequest4k": "View 4K Request", "components.RequestCard.deleterequest": "Delete Request", "components.RequestCard.failedretry": "Something went wrong while retrying the request.", - "components.RequestCard.mediaerror": "The associated title for this request is no longer available.", + "components.RequestCard.mediaerror": "{mediaType} Not Found", "components.RequestCard.seasons": "{seasonCount, plural, one {Season} other {Seasons}}", + "components.RequestCard.tmdbid": "TMDb ID", + "components.RequestCard.tvdbid": "TVDB ID", "components.RequestList.RequestItem.cancelRequest": "Cancel Request", "components.RequestList.RequestItem.deleterequest": "Delete Request", "components.RequestList.RequestItem.editrequest": "Edit Request", "components.RequestList.RequestItem.failedretry": "Something went wrong while retrying the request.", - "components.RequestList.RequestItem.mediaerror": "The associated title for this request is no longer available.", + "components.RequestList.RequestItem.mediaerror": "{mediaType} Not Found", "components.RequestList.RequestItem.modified": "Modified", "components.RequestList.RequestItem.modifieduserdate": "{date} by {user}", "components.RequestList.RequestItem.requested": "Requested", "components.RequestList.RequestItem.requesteddate": "Requested", "components.RequestList.RequestItem.seasons": "{seasonCount, plural, one {Season} other {Seasons}}", + "components.RequestList.RequestItem.tmdbid": "TMDb ID", + "components.RequestList.RequestItem.tvdbid": "TVDB ID", "components.RequestList.requests": "Requests", "components.RequestList.showallrequests": "Show All Requests", "components.RequestList.sortAdded": "Most Recent", @@ -827,6 +831,10 @@ "components.StatusChecker.reloadApp": "Reload {applicationTitle}", "components.StatusChecker.restartRequired": "Server Restart Required", "components.StatusChecker.restartRequiredDescription": "Please restart the server to apply the updated settings.", + "components.TitleCard.cleardata": "Clear Data", + "components.TitleCard.mediaerror": "{mediaType} Not Found", + "components.TitleCard.tmdbid": "TMDb ID", + "components.TitleCard.tvdbid": "TVDB ID", "components.TvDetails.TvCast.fullseriescast": "Full Series Cast", "components.TvDetails.TvCrew.fullseriescrew": "Full Series Crew", "components.TvDetails.anime": "Anime", From 9021696cf085bd6387701811e64466e14893d1db Mon Sep 17 00:00:00 2001 From: TheCatLady <52870424+TheCatLady@users.noreply.github.com> Date: Fri, 19 Aug 2022 23:23:16 -0400 Subject: [PATCH 44/81] fix(lang): correct capitalization of 'TMDB' (#2953) --- CONTRIBUTING.md | 2 +- cypress/e2e/discover.cy.ts | 4 +- docs/extending-overseerr/third-party.md | 2 +- docs/support/faq.md | 4 +- .../using-overseerr/notifications/webhooks.md | 2 +- overseerr-api.yml | 8 +-- server/api/animelist.ts | 2 +- server/api/servarr/radarr.ts | 2 +- server/api/themoviedb/index.ts | 52 +++++++++---------- server/lib/scanners/baseScanner.ts | 2 +- server/lib/scanners/plex/index.ts | 10 ++-- src/components/RequestCard/index.tsx | 4 +- .../RequestList/RequestItem/index.tsx | 4 +- src/components/TitleCard/ErrorCard.tsx | 4 +- src/i18n/locale/en.json | 12 ++--- 15 files changed, 57 insertions(+), 57 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e54e45d67..51df9e141 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -86,7 +86,7 @@ When adding new UI text, please try to adhere to the following guidelines: 1. Be concise and clear, and use as few words as possible to make your point. 2. Use the Oxford comma where appropriate. 3. Use the appropriate Unicode characters for ellipses, arrows, and other special characters/symbols. -4. Capitalize proper nouns, such as Plex, Radarr, Sonarr, Telegram, Slack, Pushover, etc. Be sure to also use the official capitalization for any abbreviations; e.g., TMDb and IMDb have a lowercase 'b', whereas TheTVDB has a capital 'B'. +4. Capitalize proper nouns, such as Plex, Radarr, Sonarr, Telegram, Slack, Pushover, etc. Be sure to also use the official capitalization for any abbreviations; e.g., IMDb has a lowercase 'b', whereas TMDB and TheTVDB have a capital 'B'. 5. Title case headings, button text, and form labels. Note that verbs such as "is" should be capitalized, whereas prepositions like "from" should be lowercase (unless as the first or last word of the string, in which case they are also capitalized). 6. Capitalize the first word in validation error messages, dropdowns, and form "tips." These strings should not end in punctuation. 7. Ensure that toast notification strings are complete sentences ending in punctuation. diff --git a/cypress/e2e/discover.cy.ts b/cypress/e2e/discover.cy.ts index b2dea0c6a..ec4c4afea 100644 --- a/cypress/e2e/discover.cy.ts +++ b/cypress/e2e/discover.cy.ts @@ -56,7 +56,7 @@ describe('Discover', () => { clickFirstTitleCardInSlider('Upcoming Series'); }); - it('displays error for media with invalid TMDb ID', () => { + it('displays error for media with invalid TMDB ID', () => { cy.intercept('GET', '/api/v1/media?*', { pageInfo: { pages: 1, pageSize: 20, results: 1, page: 1 }, results: [ @@ -97,7 +97,7 @@ describe('Discover', () => { .contains('Movie Not Found'); }); - it('displays error for request with invalid TMDb ID', () => { + it('displays error for request with invalid TMDB ID', () => { cy.intercept('GET', '/api/v1/request?*', { pageInfo: { pages: 1, pageSize: 10, results: 1, page: 1 }, results: [ diff --git a/docs/extending-overseerr/third-party.md b/docs/extending-overseerr/third-party.md index 7ff2bcabf..e1bacfc67 100644 --- a/docs/extending-overseerr/third-party.md +++ b/docs/extending-overseerr/third-party.md @@ -9,7 +9,7 @@ - [LunaSea](https://docs.lunasea.app/modules/overseerr), a self-hosted controller for mobile and macOS - [Requestrr](https://github.com/darkalfx/requestrr/wiki/Configuring-Overseerr), a Discord chatbot - [Doplarr](https://github.com/kiranshila/Doplarr), a Discord request bot -- [Overseerr Assistant](https://github.com/RemiRigal/Overseerr-Assistant), a browser extension for requesting directly from TMDb and IMDb +- [Overseerr Assistant](https://github.com/RemiRigal/Overseerr-Assistant), a browser extension for requesting directly from TMDB and IMDb - [ha-overseerr](https://github.com/vaparr/ha-overseerr), a custom Home Assistant component - [OverCLIrr](https://github.com/WillFantom/OverCLIrr), a command-line tool - [Overseerr Exporter](https://github.com/WillFantom/overseerr-exporter), a Prometheus exporter diff --git a/docs/support/faq.md b/docs/support/faq.md index d3ea3cdce..f487d18fe 100644 --- a/docs/support/faq.md +++ b/docs/support/faq.md @@ -45,7 +45,7 @@ Overseerr currently supports the following agents: - New Plex TV - Legacy Plex TV - TheTVDB -- TMDb +- TMDB - [HAMA](https://github.com/ZeroQI/Hama.bundle) Please verify that your library is using one of the agents previously listed. @@ -67,7 +67,7 @@ You can also perform the following to verify the media item has a GUID Overseerr 1. Go to the media item in Plex and **"Get info"** and click on **"View XML"**. 2. Verify that the media item's GUID follows one of the below formats: - 1. TMDb agent `guid="com.plexapp.agents.themoviedb://1705"` + 1. TMDB agent `guid="com.plexapp.agents.themoviedb://1705"` 2. New Plex Movie agent `` 3. TheTVDB agent `guid="com.plexapp.agents.thetvdb://78874/1/1"` 4. Legacy Plex Movie agent `guid="com.plexapp.agents.imdb://tt0765446"` diff --git a/docs/using-overseerr/notifications/webhooks.md b/docs/using-overseerr/notifications/webhooks.md index 37a5c0486..cd2ada314 100644 --- a/docs/using-overseerr/notifications/webhooks.md +++ b/docs/using-overseerr/notifications/webhooks.md @@ -81,7 +81,7 @@ These following special variables are only included in media-related notificatio | Variable | Value | | -------------------- | -------------------------------------------------------------------------------------------------------------- | | `{{media_type}}` | The media type (`movie` or `tv`) | -| `{{media_tmdbid}}` | The media's TMDb ID | +| `{{media_tmdbid}}` | The media's TMDB ID | | `{{media_tvdbid}}` | The media's TheTVDB ID | | `{{media_status}}` | The media's availability status (`UNKNOWN`, `PENDING`, `PROCESSING`, `PARTIALLY_AVAILABLE`, or `AVAILABLE`) | | `{{media_status4k}}` | The media's 4K availability status (`UNKNOWN`, `PENDING`, `PROCESSING`, `PARTIALLY_AVAILABLE`, or `AVAILABLE`) | diff --git a/overseerr-api.yml b/overseerr-api.yml index 6eaef6ba0..b6fb50fbb 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -5342,7 +5342,7 @@ paths: $ref: '#/components/schemas/SonarrSeries' /regions: get: - summary: Regions supported by TMDb + summary: Regions supported by TMDB description: Returns a list of regions in a JSON object. tags: - tmdb @@ -5364,7 +5364,7 @@ paths: example: United States of America /languages: get: - summary: Languages supported by TMDb + summary: Languages supported by TMDB description: Returns a list of languages in a JSON object. tags: - tmdb @@ -5429,7 +5429,7 @@ paths: $ref: '#/components/schemas/ProductionCompany' /genres/movie: get: - summary: Get list of official TMDb movie genres + summary: Get list of official TMDB movie genres description: Returns a list of genres in a JSON array. tags: - tmdb @@ -5457,7 +5457,7 @@ paths: example: Family /genres/tv: get: - summary: Get list of official TMDb movie genres + summary: Get list of official TMDB movie genres description: Returns a list of genres in a JSON array. tags: - tmdb diff --git a/server/api/animelist.ts b/server/api/animelist.ts index 20eb2f60e..064d3c279 100644 --- a/server/api/animelist.ts +++ b/server/api/animelist.ts @@ -14,7 +14,7 @@ const LOCAL_PATH = process.env.CONFIG_DIRECTORY const mappingRegexp = new RegExp(/;[0-9]+-([0-9]+)/g); -// Anime-List xml files are community maintained mappings that Hama agent uses to map AniDB IDs to TVDB/TMDb IDs +// Anime-List xml files are community maintained mappings that Hama agent uses to map AniDB IDs to TVDB/TMDB IDs // https://github.com/Anime-Lists/anime-lists/ interface AnimeMapping { diff --git a/server/api/servarr/radarr.ts b/server/api/servarr/radarr.ts index 7305baf09..cd2e61b61 100644 --- a/server/api/servarr/radarr.ts +++ b/server/api/servarr/radarr.ts @@ -69,7 +69,7 @@ class RadarrAPI extends ServarrBase<{ movieId: number }> { return response.data[0]; } catch (e) { - logger.error('Error retrieving movie by TMDb ID', { + logger.error('Error retrieving movie by TMDB ID', { label: 'Radarr API', errorMessage: e.message, tmdbId: id, diff --git a/server/api/themoviedb/index.ts b/server/api/themoviedb/index.ts index 7be7cd96f..f150e76c0 100644 --- a/server/api/themoviedb/index.ts +++ b/server/api/themoviedb/index.ts @@ -196,7 +196,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { - throw new Error(`[TMDb] Failed to fetch person details: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch person details: ${e.message}`); } }; @@ -218,7 +218,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { throw new Error( - `[TMDb] Failed to fetch person combined credits: ${e.message}` + `[TMDB] Failed to fetch person combined credits: ${e.message}` ); } }; @@ -245,7 +245,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { - throw new Error(`[TMDb] Failed to fetch movie details: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch movie details: ${e.message}`); } }; @@ -271,7 +271,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { - throw new Error(`[TMDb] Failed to fetch TV show details: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch TV show details: ${e.message}`); } }; @@ -297,7 +297,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { - throw new Error(`[TMDb] Failed to fetch TV show details: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch TV show details: ${e.message}`); } }; @@ -323,7 +323,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { - throw new Error(`[TMDb] Failed to fetch discover movies: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch discover movies: ${e.message}`); } } @@ -349,7 +349,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { - throw new Error(`[TMDb] Failed to fetch discover movies: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch discover movies: ${e.message}`); } } @@ -375,7 +375,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { - throw new Error(`[TMDb] Failed to fetch movies by keyword: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch movies by keyword: ${e.message}`); } } @@ -402,7 +402,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { throw new Error( - `[TMDb] Failed to fetch TV recommendations: ${e.message}` + `[TMDB] Failed to fetch TV recommendations: ${e.message}` ); } } @@ -426,7 +426,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { - throw new Error(`[TMDb] Failed to fetch TV similar: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch TV similar: ${e.message}`); } } @@ -459,7 +459,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { - throw new Error(`[TMDb] Failed to fetch discover movies: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch discover movies: ${e.message}`); } }; @@ -492,7 +492,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { - throw new Error(`[TMDb] Failed to fetch discover TV: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch discover TV: ${e.message}`); } }; @@ -518,7 +518,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { - throw new Error(`[TMDb] Failed to fetch upcoming movies: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch upcoming movies: ${e.message}`); } }; @@ -545,7 +545,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { - throw new Error(`[TMDb] Failed to fetch all trending: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch all trending: ${e.message}`); } }; @@ -568,7 +568,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { - throw new Error(`[TMDb] Failed to fetch all trending: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch all trending: ${e.message}`); } }; @@ -591,7 +591,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { - throw new Error(`[TMDb] Failed to fetch all trending: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch all trending: ${e.message}`); } }; @@ -623,7 +623,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { - throw new Error(`[TMDb] Failed to find by external ID: ${e.message}`); + throw new Error(`[TMDB] Failed to find by external ID: ${e.message}`); } } @@ -661,7 +661,7 @@ class TheMovieDb extends ExternalAPI { throw new Error(`No movie or show returned from API for ID ${imdbId}`); } catch (e) { throw new Error( - `[TMDb] Failed to find media using external IMDb ID: ${e.message}` + `[TMDB] Failed to find media using external IMDb ID: ${e.message}` ); } } @@ -691,7 +691,7 @@ class TheMovieDb extends ExternalAPI { throw new Error(`No show returned from API for ID ${tvdbId}`); } catch (e) { throw new Error( - `[TMDb] Failed to get TV show using the external TVDB ID: ${e.message}` + `[TMDB] Failed to get TV show using the external TVDB ID: ${e.message}` ); } } @@ -715,7 +715,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { - throw new Error(`[TMDb] Failed to fetch collection: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch collection: ${e.message}`); } } @@ -731,7 +731,7 @@ class TheMovieDb extends ExternalAPI { return regions; } catch (e) { - throw new Error(`[TMDb] Failed to fetch countries: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch countries: ${e.message}`); } } @@ -747,7 +747,7 @@ class TheMovieDb extends ExternalAPI { return languages; } catch (e) { - throw new Error(`[TMDb] Failed to fetch langauges: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch langauges: ${e.message}`); } } @@ -759,7 +759,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { - throw new Error(`[TMDb] Failed to fetch movie studio: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch movie studio: ${e.message}`); } } @@ -769,7 +769,7 @@ class TheMovieDb extends ExternalAPI { return data; } catch (e) { - throw new Error(`[TMDb] Failed to fetch TV network: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch TV network: ${e.message}`); } } @@ -820,7 +820,7 @@ class TheMovieDb extends ExternalAPI { return movieGenres; } catch (e) { - throw new Error(`[TMDb] Failed to fetch movie genres: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch movie genres: ${e.message}`); } } @@ -871,7 +871,7 @@ class TheMovieDb extends ExternalAPI { return tvGenres; } catch (e) { - throw new Error(`[TMDb] Failed to fetch TV genres: ${e.message}`); + throw new Error(`[TMDB] Failed to fetch TV genres: ${e.message}`); } } } diff --git a/server/lib/scanners/baseScanner.ts b/server/lib/scanners/baseScanner.ts index 69ae32483..e9ce9e04b 100644 --- a/server/lib/scanners/baseScanner.ts +++ b/server/lib/scanners/baseScanner.ts @@ -210,7 +210,7 @@ class BaseScanner { } /** - * processShow takes a TMDb ID and an array of ProcessableSeasons, which + * processShow takes a TMDB ID and an array of ProcessableSeasons, which * should include the total episodes a sesaon has + the total available * episodes that each season currently has. Unlike processMovie, this method * does not take an `is4k` option. We handle both the 4k _and_ non 4k status diff --git a/server/lib/scanners/plex/index.ts b/server/lib/scanners/plex/index.ts index 2932b8c71..c79b5b27c 100644 --- a/server/lib/scanners/plex/index.ts +++ b/server/lib/scanners/plex/index.ts @@ -374,7 +374,7 @@ class PlexScanner } }); - // If we got an IMDb ID, but no TMDb ID, lookup the TMDb ID with the IMDb ID + // If we got an IMDb ID, but no TMDB ID, lookup the TMDB ID with the IMDb ID if (mediaIds.imdbId && !mediaIds.tmdbId) { const tmdbMedia = await this.tmdb.getMediaByImdbId({ imdbId: mediaIds.imdbId, @@ -395,7 +395,7 @@ class PlexScanner }); mediaIds.tmdbId = tmdbMedia.id; } - // Check if the agent is TMDb + // Check if the agent is TMDB } else if (plexitem.guid.match(tmdbRegex)) { const tmdbMatch = plexitem.guid.match(tmdbRegex); if (tmdbMatch) { @@ -414,7 +414,7 @@ class PlexScanner mediaIds.tvdbId = Number(matchedtvdb[1]); mediaIds.tmdbId = show.id; } - // Check if the agent (for shows) is TMDb + // Check if the agent (for shows) is TMDB } else if (plexitem.guid.match(tmdbShowRegex)) { const matchedtmdb = plexitem.guid.match(tmdbShowRegex); if (matchedtmdb) { @@ -489,10 +489,10 @@ class PlexScanner } if (!mediaIds.tmdbId) { - throw new Error('Unable to find TMDb ID'); + throw new Error('Unable to find TMDB ID'); } - // We check above if we have the TMDb ID, so we can safely assert the type below + // We check above if we have the TMDB ID, so we can safely assert the type below return mediaIds as MediaIds; } diff --git a/src/components/RequestCard/index.tsx b/src/components/RequestCard/index.tsx index 4bbdb1087..b9916636c 100644 --- a/src/components/RequestCard/index.tsx +++ b/src/components/RequestCard/index.tsx @@ -29,8 +29,8 @@ const messages = defineMessages({ seasons: '{seasonCount, plural, one {Season} other {Seasons}}', failedretry: 'Something went wrong while retrying the request.', mediaerror: '{mediaType} Not Found', - tmdbid: 'TMDb ID', - tvdbid: 'TVDB ID', + tmdbid: 'TMDB ID', + tvdbid: 'TheTVDB ID', deleterequest: 'Delete Request', }); diff --git a/src/components/RequestList/RequestItem/index.tsx b/src/components/RequestList/RequestItem/index.tsx index aef965293..5056314cd 100644 --- a/src/components/RequestList/RequestItem/index.tsx +++ b/src/components/RequestList/RequestItem/index.tsx @@ -36,8 +36,8 @@ const messages = defineMessages({ editrequest: 'Edit Request', deleterequest: 'Delete Request', cancelRequest: 'Cancel Request', - tmdbid: 'TMDb ID', - tvdbid: 'TVDB ID', + tmdbid: 'TMDB ID', + tvdbid: 'TheTVDB ID', }); const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => { diff --git a/src/components/TitleCard/ErrorCard.tsx b/src/components/TitleCard/ErrorCard.tsx index df619289b..aa5f37f20 100644 --- a/src/components/TitleCard/ErrorCard.tsx +++ b/src/components/TitleCard/ErrorCard.tsx @@ -15,8 +15,8 @@ interface ErrorCardProps { const messages = defineMessages({ mediaerror: '{mediaType} Not Found', - tmdbid: 'TMDb ID', - tvdbid: 'TVDB ID', + tmdbid: 'TMDB ID', + tvdbid: 'TheTVDB ID', cleardata: 'Clear Data', }); diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index ca405970c..f98abb46f 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -294,8 +294,8 @@ "components.RequestCard.failedretry": "Something went wrong while retrying the request.", "components.RequestCard.mediaerror": "{mediaType} Not Found", "components.RequestCard.seasons": "{seasonCount, plural, one {Season} other {Seasons}}", - "components.RequestCard.tmdbid": "TMDb ID", - "components.RequestCard.tvdbid": "TVDB ID", + "components.RequestCard.tmdbid": "TMDB ID", + "components.RequestCard.tvdbid": "TheTVDB ID", "components.RequestList.RequestItem.cancelRequest": "Cancel Request", "components.RequestList.RequestItem.deleterequest": "Delete Request", "components.RequestList.RequestItem.editrequest": "Edit Request", @@ -306,8 +306,8 @@ "components.RequestList.RequestItem.requested": "Requested", "components.RequestList.RequestItem.requesteddate": "Requested", "components.RequestList.RequestItem.seasons": "{seasonCount, plural, one {Season} other {Seasons}}", - "components.RequestList.RequestItem.tmdbid": "TMDb ID", - "components.RequestList.RequestItem.tvdbid": "TVDB ID", + "components.RequestList.RequestItem.tmdbid": "TMDB ID", + "components.RequestList.RequestItem.tvdbid": "TheTVDB ID", "components.RequestList.requests": "Requests", "components.RequestList.showallrequests": "Show All Requests", "components.RequestList.sortAdded": "Most Recent", @@ -833,8 +833,8 @@ "components.StatusChecker.restartRequiredDescription": "Please restart the server to apply the updated settings.", "components.TitleCard.cleardata": "Clear Data", "components.TitleCard.mediaerror": "{mediaType} Not Found", - "components.TitleCard.tmdbid": "TMDb ID", - "components.TitleCard.tvdbid": "TVDB ID", + "components.TitleCard.tmdbid": "TMDB ID", + "components.TitleCard.tvdbid": "TheTVDB ID", "components.TvDetails.TvCast.fullseriescast": "Full Series Cast", "components.TvDetails.TvCrew.fullseriescrew": "Full Series Crew", "components.TvDetails.anime": "Anime", From a0301e2d83b2fca07f69a2c274d4745edef6d0cd Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Sat, 20 Aug 2022 22:47:15 +0200 Subject: [PATCH 45/81] feat(lang): translations update from Hosted Weblate (#2915) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(lang): translated using Weblate (Greek) Currently translated at 74.1% (775 of 1045 strings) feat(lang): translated using Weblate (Greek) Currently translated at 74.0% (774 of 1045 strings) Update translation files Updated by "Cleanup translation files" hook in Weblate. Co-authored-by: Hosted Weblate Co-authored-by: TheCatLady Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/el/ Translation: Overseerr/Overseerr Frontend * feat(lang): translated using Weblate (Japanese) Currently translated at 62.9% (658 of 1045 strings) feat(lang): translated using Weblate (Japanese) Currently translated at 62.9% (658 of 1045 strings) Update translation files Updated by "Cleanup translation files" hook in Weblate. Co-authored-by: Hosted Weblate Co-authored-by: TheCatLady Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ja/ Translation: Overseerr/Overseerr Frontend * feat(lang): translated using Weblate (Albanian) Currently translated at 99.2% (1037 of 1045 strings) feat(lang): translated using Weblate (Albanian) Currently translated at 99.1% (1036 of 1045 strings) Update translation files Updated by "Cleanup translation files" hook in Weblate. Co-authored-by: Hosted Weblate Co-authored-by: TheCatLady Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/sq/ Translation: Overseerr/Overseerr Frontend * feat(lang): translated using Weblate (French) Currently translated at 99.2% (1037 of 1045 strings) feat(lang): translated using Weblate (French) Currently translated at 99.1% (1036 of 1045 strings) Update translation files Updated by "Cleanup translation files" hook in Weblate. Co-authored-by: Hosted Weblate Co-authored-by: TheCatLady Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/fr/ Translation: Overseerr/Overseerr Frontend * feat(lang): translated using Weblate (Spanish) Currently translated at 92.4% (966 of 1045 strings) feat(lang): translated using Weblate (Spanish) Currently translated at 92.3% (965 of 1045 strings) Update translation files Updated by "Cleanup translation files" hook in Weblate. Co-authored-by: Hosted Weblate Co-authored-by: TheCatLady Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/es/ Translation: Overseerr/Overseerr Frontend * feat(lang): translated using Weblate (Catalan) Currently translated at 99.3% (1038 of 1045 strings) feat(lang): translated using Weblate (Catalan) Currently translated at 99.2% (1037 of 1045 strings) Update translation files Updated by "Cleanup translation files" hook in Weblate. Co-authored-by: Hosted Weblate Co-authored-by: TheCatLady Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ca/ Translation: Overseerr/Overseerr Frontend * feat(lang): translated using Weblate (Czech) Currently translated at 94.6% (989 of 1045 strings) feat(lang): translated using Weblate (Czech) Currently translated at 94.5% (988 of 1045 strings) Co-authored-by: Hosted Weblate Co-authored-by: TheCatLady Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/cs/ Translation: Overseerr/Overseerr Frontend * feat(lang): translated using Weblate (Polish) Currently translated at 100.0% (1048 of 1048 strings) feat(lang): translated using Weblate (Polish) Currently translated at 100.0% (1045 of 1045 strings) feat(lang): translated using Weblate (Polish) Currently translated at 99.2% (1037 of 1045 strings) feat(lang): translated using Weblate (Polish) Currently translated at 99.1% (1036 of 1045 strings) Update translation files Updated by "Cleanup translation files" hook in Weblate. Co-authored-by: Hosted Weblate Co-authored-by: Patryk Co-authored-by: TheCatLady Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/pl/ Translation: Overseerr/Overseerr Frontend * feat(lang): translated using Weblate (Swedish) Currently translated at 100.0% (1045 of 1045 strings) feat(lang): translated using Weblate (Swedish) Currently translated at 99.9% (1044 of 1045 strings) feat(lang): translated using Weblate (Swedish) Currently translated at 99.2% (1037 of 1045 strings) Update translation files Updated by "Cleanup translation files" hook in Weblate. Co-authored-by: Hosted Weblate Co-authored-by: Mikael Nilsson Co-authored-by: TheCatLady Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/sv/ Translation: Overseerr/Overseerr Frontend * feat(lang): translated using Weblate (Russian) Currently translated at 93.7% (980 of 1045 strings) feat(lang): translated using Weblate (Russian) Currently translated at 93.6% (979 of 1045 strings) Co-authored-by: Hosted Weblate Co-authored-by: TheCatLady Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ru/ Translation: Overseerr/Overseerr Frontend * Update translation files Updated by "Cleanup translation files" hook in Weblate. Co-authored-by: Hosted Weblate Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ Translation: Overseerr/Overseerr Frontend * feat(lang): translated using Weblate (Portuguese (Portugal)) Currently translated at 77.7% (813 of 1045 strings) feat(lang): translated using Weblate (Portuguese (Portugal)) Currently translated at 77.7% (812 of 1045 strings) Update translation files Updated by "Cleanup translation files" hook in Weblate. Co-authored-by: Hosted Weblate Co-authored-by: TheCatLady Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/pt_PT/ Translation: Overseerr/Overseerr Frontend * feat(lang): translated using Weblate (Danish) Currently translated at 98.7% (1032 of 1045 strings) feat(lang): translated using Weblate (Danish) Currently translated at 98.6% (1031 of 1045 strings) Co-authored-by: Hosted Weblate Co-authored-by: TheCatLady Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/da/ Translation: Overseerr/Overseerr Frontend * feat(lang): translated using Weblate (Chinese (Simplified)) Currently translated at 98.2% (1039 of 1058 strings) feat(lang): translated using Weblate (Chinese (Simplified)) Currently translated at 98.1% (1038 of 1058 strings) feat(lang): translated using Weblate (Chinese (Simplified)) Currently translated at 99.3% (1038 of 1045 strings) feat(lang): translated using Weblate (Chinese (Simplified)) Currently translated at 99.3% (1038 of 1045 strings) Update translation files Updated by "Cleanup translation files" hook in Weblate. Co-authored-by: Eric Co-authored-by: Hosted Weblate Co-authored-by: TheCatLady Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/zh_Hans/ Translation: Overseerr/Overseerr Frontend * feat(lang): translated using Weblate (Hungarian) Currently translated at 94.8% (991 of 1045 strings) feat(lang): translated using Weblate (Hungarian) Currently translated at 94.7% (990 of 1045 strings) Update translation files Updated by "Cleanup translation files" hook in Weblate. Co-authored-by: Hosted Weblate Co-authored-by: TheCatLady Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/hu/ Translation: Overseerr/Overseerr Frontend * feat(lang): translated using Weblate (Dutch) Currently translated at 99.3% (1038 of 1045 strings) feat(lang): translated using Weblate (Dutch) Currently translated at 99.2% (1037 of 1045 strings) Update translation files Updated by "Cleanup translation files" hook in Weblate. Co-authored-by: Hosted Weblate Co-authored-by: TheCatLady Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/nl/ Translation: Overseerr/Overseerr Frontend * feat(lang): translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (1058 of 1058 strings) feat(lang): translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (1058 of 1058 strings) feat(lang): translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (1050 of 1050 strings) feat(lang): translated using Weblate (Portuguese (Brazil)) Currently translated at 99.9% (1047 of 1048 strings) feat(lang): translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (1045 of 1045 strings) Co-authored-by: Hosted Weblate Co-authored-by: TheCatLady Co-authored-by: Tijuco Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/pt_BR/ Translation: Overseerr/Overseerr Frontend * feat(lang): translated using Weblate (Italian) Currently translated at 99.3% (1038 of 1045 strings) feat(lang): translated using Weblate (Italian) Currently translated at 99.2% (1037 of 1045 strings) Update translation files Updated by "Cleanup translation files" hook in Weblate. Co-authored-by: Hosted Weblate Co-authored-by: TheCatLady Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/it/ Translation: Overseerr/Overseerr Frontend * feat(lang): translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (1058 of 1058 strings) feat(lang): translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (1058 of 1058 strings) feat(lang): translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (1050 of 1050 strings) feat(lang): translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (1048 of 1048 strings) feat(lang): translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (1045 of 1045 strings) Co-authored-by: Hosted Weblate Co-authored-by: TheCatLady Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/zh_Hant/ Translation: Overseerr/Overseerr Frontend * feat(lang): translated using Weblate (German) Currently translated at 99.1% (1036 of 1045 strings) feat(lang): translated using Weblate (German) Currently translated at 99.0% (1035 of 1045 strings) Update translation files Updated by "Cleanup translation files" hook in Weblate. Co-authored-by: Hosted Weblate Co-authored-by: TheCatLady Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/de/ Translation: Overseerr/Overseerr Frontend * feat(lang): translated using Weblate (Norwegian Bokmål) Currently translated at 98.9% (1047 of 1058 strings) feat(lang): translated using Weblate (Norwegian Bokmål) Currently translated at 97.9% (1036 of 1058 strings) feat(lang): translated using Weblate (Norwegian Bokmål) Currently translated at 99.3% (1038 of 1045 strings) feat(lang): translated using Weblate (Norwegian Bokmål) Currently translated at 99.2% (1037 of 1045 strings) Update translation files Updated by "Cleanup translation files" hook in Weblate. Co-authored-by: Hosted Weblate Co-authored-by: TheCatLady Co-authored-by: exentler Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/ Translate-URL: https://hosted.weblate.org/projects/overseerr/overseerr-frontend/nb_NO/ Translation: Overseerr/Overseerr Frontend Co-authored-by: TheCatLady Co-authored-by: Patryk Co-authored-by: Mikael Nilsson Co-authored-by: Eric Co-authored-by: Tijuco Co-authored-by: exentler --- src/i18n/locale/ar.json | 2 -- src/i18n/locale/ca.json | 6 ++---- src/i18n/locale/cs.json | 4 ++-- src/i18n/locale/da.json | 4 ++-- src/i18n/locale/de.json | 6 ++---- src/i18n/locale/el.json | 6 ++---- src/i18n/locale/es.json | 6 ++---- src/i18n/locale/fr.json | 6 ++---- src/i18n/locale/hu.json | 6 ++---- src/i18n/locale/it.json | 5 ++--- src/i18n/locale/ja.json | 11 +++++------ src/i18n/locale/nb_NO.json | 21 +++++++++++++++------ src/i18n/locale/nl.json | 6 ++---- src/i18n/locale/pl.json | 20 +++++++++++++++----- src/i18n/locale/pt_BR.json | 29 ++++++++++++++++++++++------- src/i18n/locale/pt_PT.json | 6 ++---- src/i18n/locale/ru.json | 4 ++-- src/i18n/locale/sq.json | 6 ++---- src/i18n/locale/sv.json | 15 ++++++++++----- src/i18n/locale/zh_Hans.json | 15 +++++++-------- src/i18n/locale/zh_Hant.json | 25 +++++++++++++++++++------ 21 files changed, 119 insertions(+), 90 deletions(-) diff --git a/src/i18n/locale/ar.json b/src/i18n/locale/ar.json index 460eb6f5f..0c145714c 100644 --- a/src/i18n/locale/ar.json +++ b/src/i18n/locale/ar.json @@ -250,7 +250,6 @@ "components.PermissionEdit.requestMoviesDescription": "إعطاء صلاحية بطلب أفلام ليست بجودة ٤ك.", "components.PermissionEdit.requestTv": "طلب مسلسلات", "components.PermissionEdit.requestTvDescription": "إعطاء إذن بطلب مسلسلات ليست بجودة ٤ك.", - "components.PermissionEdit.settingsDescription": "إعطاء الإذن بإدارة جميع الإعدادات العامة. المستخدم يجب أن يملك هذه الصلاحية ليتمكن من إعطاءها للأخرين.", "components.PermissionEdit.users": "إدارة المستخدمين", "components.PermissionEdit.usersDescription": "إعطاء الإذن بإدارة المستخدمين. الذين يملكون هذه الصلاحية لا يستطيعون تعديل أي مستخدم مسؤول أو إعطاء صلاحية مسوؤل.", "components.PermissionEdit.viewissues": "الإطلاع على المشاكل", @@ -854,7 +853,6 @@ "components.Setup.welcome": "مرحبا بك في أوفرسيرر", "components.StatusBadge.status": "{status}", "components.StatusBadge.status4k": "4K {status}", - "components.StatusChacker.newversionavailable": "تحديث التطبيق", "components.TvDetails.TvCast.fullseriescast": "طاقم عمل المسلسل", "components.TvDetails.TvCrew.fullseriescrew": "طاقم فريق المسلسل", "components.TvDetails.anime": "إنمي", diff --git a/src/i18n/locale/ca.json b/src/i18n/locale/ca.json index 98917852f..8a1793f48 100644 --- a/src/i18n/locale/ca.json +++ b/src/i18n/locale/ca.json @@ -89,7 +89,6 @@ "components.PermissionEdit.viewrequestsDescription": "Concedeix permís per veure les sol·licituds de continguts d'altres usuaris.", "components.PermissionEdit.viewrequests": "Veure sol·licituds", "components.PermissionEdit.users": "Gestiona els usuaris", - "components.PermissionEdit.settings": "Gestiona la configuració", "components.PermissionEdit.requestDescription": "Concedeix permís per sol·licitar contingut no 4K.", "components.PermissionEdit.request4kTvDescription": "Concedeix permís per sol·licitar sèrie en 4K.", "components.PermissionEdit.request4kTv": "Sol·licita Sèrie en 4K", @@ -412,7 +411,6 @@ "components.TvDetails.anime": "Anime", "components.TvDetails.TvCrew.fullseriescrew": "Equip complet de la sèrie", "components.TvDetails.TvCast.fullseriescast": "Repartiment complet de la sèrie", - "components.StatusChacker.newversionDescription": "Overseerr s'ha actualitzat! Feu clic al botó següent per tornar a carregar la pàgina.", "components.StatusBadge.status4k": "4K {status}", "components.Setup.welcome": "Benvingut a Overseerr", "components.Setup.tip": "Consell", @@ -433,7 +431,7 @@ "components.Settings.validationApplicationUrlTrailingSlash": "L'URL no pot acabar amb una barra inclinada final", "components.Settings.validationApplicationUrl": "Heu de proporcionar un URL vàlid", "components.Settings.validationApplicationTitle": "Heu de proporcionar un títol d'aplicació", - "components.Settings.trustProxyTip": "Permet a Overseerr registrar correctament les adreces IP del client darrere d’un servidor intermediari (s’ha de tornar a carregar Overseerr perquè els canvis tinguin efecte)", + "components.Settings.trustProxyTip": "Permet a Overseerr registrar correctament les adreces IP del client darrere d’un servidor intermediari", "components.Settings.toastSettingsSuccess": "La configuració s'ha desat correctament!", "components.Settings.toastSettingsFailure": "S'ha produït un error en desar la configuració.", "components.Settings.toastPlexRefreshSuccess": "La llista de servidors Plex s'ha recuperat correctament!", @@ -500,7 +498,7 @@ "components.Settings.email": "Adreça electrònica", "components.Settings.default4k": "4K predeterminat", "components.Settings.default": "Predeterminat", - "components.Settings.csrfProtectionTip": "Estableix l'accés a l'API externa de només lectura (requereix HTTPS i s'ha de tornar a carregar Overseerr perquè els canvis tinguin efecte)", + "components.Settings.csrfProtectionTip": "Estableix l'accés a l'API externa de només lectura (requereix HTTPS)", "components.Settings.csrfProtectionHoverTip": "NO activeu aquesta configuració tret que entengueu el que esteu fent!", "components.Settings.csrfProtection": "Activeu la protecció CSRF", "components.Settings.copied": "S'ha copiat la clau API al porta-retalls.", diff --git a/src/i18n/locale/cs.json b/src/i18n/locale/cs.json index eb27e9ef4..a32ce6c39 100644 --- a/src/i18n/locale/cs.json +++ b/src/i18n/locale/cs.json @@ -633,7 +633,7 @@ "components.Settings.SonarrModal.testFirstRootFolders": "Test připojení pro načtení kořenových složek", "components.Settings.SonarrModal.validationApiKeyRequired": "Musíte zadat klíč API", "components.Settings.SonarrModal.validationApplicationUrl": "Musíte zadat platnou adresu URL", - "components.Settings.csrfProtectionTip": "Nastavení externího přístupu k rozhraní API pouze pro čtení (vyžaduje protokol HTTPS a aby se změny projevily, musí být znovu načtena aplikace Overseerr)", + "components.Settings.csrfProtectionTip": "Nastavení externího přístupu k rozhraní API pouze pro čtení (vyžaduje protokol HTTPS)", "components.Settings.deleteserverconfirm": "Opravdu chcete tento server odstranit?", "components.Settings.menuJobs": "Práce a mezipaměť", "components.Settings.toastApiKeyFailure": "Při generování nového klíče API se něco pokazilo.", @@ -894,7 +894,7 @@ "components.RequestCard.failedretry": "Při opakovaném pokusu o zadání požadavku se něco pokazilo.", "components.Settings.Notifications.NotificationsLunaSea.profileNameTip": "Vyžaduje se pouze v případě, že nepoužíváte profil default", "components.RequestCard.mediaerror": "Související titul pro tuto žádost již není k dispozici.", - "components.Settings.trustProxyTip": "Povolit Overseerr správně registrovat IP adresy klientů za proxy serverem (aby se změny projevily, musí být Overseerr znovu načten)", + "components.Settings.trustProxyTip": "Povolit Overseerr správně registrovat IP adresy klientů za proxy serverem", "components.RequestList.RequestItem.mediaerror": "Související titul pro tuto žádost již není k dispozici.", "components.RequestModal.QuotaDisplay.allowedRequests": "Můžete požádat o {limit} {type} každé {days} dny.", "components.RequestModal.SearchByNameModal.notvdbiddescription": "Váš požadavek jsme nemohli automaticky porovnat. Vyberte prosím správnou shodu ze seznamu níže.", diff --git a/src/i18n/locale/da.json b/src/i18n/locale/da.json index 2f84f90da..d498738b6 100644 --- a/src/i18n/locale/da.json +++ b/src/i18n/locale/da.json @@ -636,7 +636,7 @@ "components.Settings.partialRequestsEnabled": "Tillad Delvise Serieforespørgsler", "components.Settings.plex": "Plex", "components.Settings.SettingsJobsCache.jobScheduleEditSaved": "Jobbet er blevet redigeret!", - "components.Settings.csrfProtectionTip": "Sæt ekstern API-adgang til skrivebeskyttet (kræver HTTPS samt at Overseerr genstartes for at ændringerne vil træde i kraft)", + "components.Settings.csrfProtectionTip": "Sæt ekstern API-adgang til skrivebeskyttet (kræver HTTPS)", "components.Settings.generalsettingsDescription": "Konfigurér global- og standardindstillinger for Overseerr.", "components.Settings.general": "Generelt", "components.Settings.hideAvailable": "Skjul Tilgængelige Medier", @@ -758,7 +758,7 @@ "components.Settings.radarrsettings": "Radarr-indstillinger", "components.Settings.region": "Udforsk Region", "components.Settings.trustProxy": "Aktivér Proxy-understøttelse", - "components.Settings.trustProxyTip": "Tillad Overseerr at registrere klienters IP addresser korrekt bag en proxy (Overseerr skal genstartes for at ændringerne træder i kraft)", + "components.Settings.trustProxyTip": "Tillad Overseerr at registrere klienters IP addresser korrekt bag en proxy", "components.TvDetails.nextAirDate": "Næste Udsendelsesdato", "components.TvDetails.playonplex": "Afspil i Plex", "components.TvDetails.recommendations": "Anbefalinger", diff --git a/src/i18n/locale/de.json b/src/i18n/locale/de.json index fd41481b4..2366a2cc0 100644 --- a/src/i18n/locale/de.json +++ b/src/i18n/locale/de.json @@ -252,7 +252,6 @@ "components.Settings.Notifications.senderName": "Absendername", "components.Settings.Notifications.chatId": "Chat-ID", "components.Settings.Notifications.botAPI": "Bot-Autorisierungstoken", - "components.StatusChacker.newversionavailable": "Anwendungsaktualisierung", "components.Settings.SettingsAbout.documentation": "Dokumentation", "components.NotificationTypeSelector.mediarequestedDescription": "Sende Benachrichtigungen, wenn neue Medien angefordert wurden und auf Genehmigung warten.", "components.NotificationTypeSelector.mediarequested": "Anfrage in Bearbeitung", @@ -368,13 +367,12 @@ "components.Settings.serverpreset": "Server", "components.Settings.serverRemote": "entfernt", "components.Settings.serverLocal": "lokal", - "components.Settings.csrfProtectionTip": "Macht den externen API Zugang schreibgeschützt (setzt HTTPS voraus und Overseerr muss neu gestartet werden, damit die Änderungen wirksam werden)", + "components.Settings.csrfProtectionTip": "Macht den externen API Zugang schreibgeschützt (setzt HTTPS voraus)", "components.Settings.csrfProtection": "Aktiviere CSRF Schutz", "components.Settings.SonarrModal.toastSonarrTestSuccess": "Sonarr-Verbindung erfolgreich hergestellt!", "components.Settings.SonarrModal.toastSonarrTestFailure": "Verbindung zu Sonarr fehlgeschlagen.", "components.PermissionEdit.usersDescription": "Gewähre Berechtigung zum Verwalten von Benutzern. Benutzer mit dieser Berechtigung können Benutzer mit Adminrechten nicht bearbeiten oder Adminrechte erteilen.", "components.PermissionEdit.users": "Benutzer verwalten", - "components.PermissionEdit.settings": "Einstellungen verwalten", "components.PermissionEdit.requestDescription": "Berechtigt, nicht-4K Inhalte anzufragen.", "components.PermissionEdit.request4kTvDescription": "Berechtigt, Serien in 4K anzufragen.", "components.PermissionEdit.request4kTv": "4K Serien anfragen", @@ -395,7 +393,7 @@ "components.MovieDetails.playonplex": "Auf Plex abspielen", "components.TvDetails.play4konplex": "In 4K auf Plex abspielen", "components.MovieDetails.play4konplex": "In 4K auf Plex abspielen", - "components.Settings.trustProxyTip": "Erlaubt es Overseerr Client IP Adressen hinter einem Proxy korrekt zu registrieren (Overseerr muss neu gestartet werden, damit die Änderungen wirksam werden)", + "components.Settings.trustProxyTip": "Erlaubt es Overseerr Client IP Adressen hinter einem Proxy korrekt zu registrieren", "components.Settings.trustProxy": "Proxy-Unterstützung aktivieren", "components.Settings.SettingsJobsCache.runnow": "Jetzt ausführen", "components.Settings.SettingsJobsCache.nextexecution": "Nächste Ausführung", diff --git a/src/i18n/locale/el.json b/src/i18n/locale/el.json index 4e29570f4..ad7be636c 100644 --- a/src/i18n/locale/el.json +++ b/src/i18n/locale/el.json @@ -1,6 +1,5 @@ { "components.PermissionEdit.users": "Διαχείριση Χρηστών", - "components.PermissionEdit.settings": "Διαχείριση Ρυθμίσεων", "components.PermissionEdit.requestTvDescription": "Εκχώρηση άδειας για αιτήματα που δεν είναι 4K σειρές.", "components.PermissionEdit.requestTv": "Αιτήματα για Σειρές", "components.PermissionEdit.requestMoviesDescription": "Εκχώρηση άδειας για αιτήματα που δεν είναι 4K ταινίες.", @@ -635,7 +634,6 @@ "components.TvDetails.anime": "Anime", "components.TvDetails.TvCrew.fullseriescrew": "Όλο το Πλήρωμα της Σειράς", "components.TvDetails.TvCast.fullseriescast": "Όλοι οι Ηθοποιοί της Σειράς", - "components.StatusChacker.newversionavailable": "Ενημέρωση εφαρμογής", "components.StatusBadge.status4k": "4K {status}", "components.Setup.welcome": "Καλώς ήρθες στο Overseerr", "components.Setup.tip": "Συμβουλή", @@ -656,7 +654,7 @@ "components.Settings.validationApplicationUrlTrailingSlash": "Η διεύθυνση URL δεν πρέπει να τελειώνει με κάθετο", "components.Settings.validationApplicationUrl": "Πρέπει να βάλεις μια έγκυρη διεύθυνση URL", "components.Settings.validationApplicationTitle": "Πρέπει να δώσεις έναν τίτλο εφαρμογής", - "components.Settings.trustProxyTip": "Επίτρεψε στο Overseerr να καταχωρίζει σωστά τις διευθύνσεις IP του πελάτη πίσω από έναν διακομιστή μεσολάβησης (το Overseerr πρέπει να φορτωθεί ξανά για να εφαρμοστούν οι αλλαγές)", + "components.Settings.trustProxyTip": "Επίτρεψε στο Overseerr να καταχωρίζει σωστά τις διευθύνσεις IP του πελάτη πίσω από έναν διακομιστή μεσολάβησης", "components.Settings.trustProxy": "Ενεργοποίηση υποστήριξης διαμεσολαβητή", "components.Settings.toastSettingsSuccess": "Οι ρυθμίσεις αποθηκεύτηκαν με επιτυχία!", "components.Settings.toastSettingsFailure": "Κάτι πήγε στραβά κατά την αποθήκευση των ρυθμίσεων.", @@ -727,7 +725,7 @@ "components.Settings.default4k": "Προεπιλεγμένο 4K", "components.Settings.default": "Προκαθορισμένο", "components.Settings.currentlibrary": "Τρέχουσα βιβλιοθήκη: {name}", - "components.Settings.csrfProtectionTip": "Ορισμός εξωτερικής πρόσβασης API σε μόνο για ανάγνωση (απαιτείται HTTPS και το Overseerr πρέπει να φορτωθεί ξανά για να εφαρμοστούν οι αλλαγές)", + "components.Settings.csrfProtectionTip": "Ορισμός εξωτερικής πρόσβασης API σε μόνο για ανάγνωση (απαιτείται HTTPS)", "components.Settings.csrfProtectionHoverTip": "ΜΗΝ ενεργοποιήσεις αυτή τη ρύθμιση αν δεν καταλαβαίνεις τι κάνεις!", "components.Settings.csrfProtection": "Ενεργοποίηση της προστασίας CSRF", "components.Settings.copied": "Αντιγράφηκε το κλειδί API στο πρόχειρο.", diff --git a/src/i18n/locale/es.json b/src/i18n/locale/es.json index d0b3bead3..2196a6e48 100644 --- a/src/i18n/locale/es.json +++ b/src/i18n/locale/es.json @@ -246,7 +246,6 @@ "components.RequestList.RequestItem.failedretry": "Algo salió mal al reintentar la solicitud.", "components.MovieDetails.watchtrailer": "Ver Trailer", "components.NotificationTypeSelector.mediarequestedDescription": "Envía una notificación cuando se solicita nuevo contenido que requiere ser aprobado.", - "components.StatusChacker.newversionavailable": "Actualización de Aplicación", "components.Settings.SettingsAbout.documentation": "Documentación", "components.Settings.Notifications.validationChatIdRequired": "Debes proporcionar un ID de chat válido", "components.Settings.Notifications.validationBotAPIRequired": "Debes proporcionar un token de autorización del bot", @@ -372,7 +371,6 @@ "components.PermissionEdit.viewrequests": "Ver Solicitudes", "components.PermissionEdit.usersDescription": "Concede permisos para gestionar usuarios. Los usuarios con este permiso no pueden modificar usuarios o conceder privilegios de Administrador.", "components.PermissionEdit.users": "Gestión de Usuarios", - "components.PermissionEdit.settings": "Gestionar Ajustes", "components.PermissionEdit.requestDescription": "Concede permisos para solicitar contenidos que no sean 4K.", "components.PermissionEdit.request4kTvDescription": "Concede permisos para solicitar series en 4K.", "components.PermissionEdit.request4kTv": "Pedir Series 4K", @@ -513,7 +511,7 @@ "components.Settings.validationApplicationUrlTrailingSlash": "La URL no puede acabar con una barra", "components.Settings.validationApplicationUrl": "Debes indicar una URL válida", "components.Settings.validationApplicationTitle": "Debes indicar un título de aplicación", - "components.Settings.trustProxyTip": "Permite a Overserr registrar correctamente la IP del cliente detrás de un Proxy (Overseer debe recargarse para que los cambios surtan efecto)", + "components.Settings.trustProxyTip": "Permite a Overserr registrar correctamente la IP del cliente detrás de un proxy", "components.Settings.trustProxy": "Habilitar soporte Proxy", "components.Settings.toastPlexRefreshSuccess": "¡Recibida la lista de servidores de Plex con éxito!", "components.Settings.toastPlexRefreshFailure": "Fallo al obtener la lista de servidores de Plex.", @@ -538,7 +536,7 @@ "components.Settings.notificationAgentSettingsDescription": "Configura y habilita los agentes de notificaciones.", "components.Settings.menuUsers": "Usuarios", "components.Settings.email": "Email", - "components.Settings.csrfProtectionTip": "Asigna acceso a una API externa en modo solo lectura (requiere HTTPS y debe recargarse Overseer para poder aplicar los cambios)", + "components.Settings.csrfProtectionTip": "Asigna acceso a una API externa en modo solo lectura (requiere HTTPS)", "components.Settings.csrfProtectionHoverTip": "¡NO habilitar esta opción a menos que seas consciente de lo que estás haciendo!", "components.Settings.csrfProtection": "Habilitar Protección CSRF", "components.Settings.applicationTitle": "Título de la Aplicación", diff --git a/src/i18n/locale/fr.json b/src/i18n/locale/fr.json index 95dff6a59..2cde1c694 100644 --- a/src/i18n/locale/fr.json +++ b/src/i18n/locale/fr.json @@ -252,7 +252,6 @@ "components.Settings.Notifications.telegramsettingsfailed": "Les paramètres de notification Telegram n'ont pas pu être enregistrés.", "components.Settings.Notifications.senderName": "Nom de l'expéditeur", "components.Settings.Notifications.chatId": "ID discussion", - "components.StatusChacker.newversionavailable": "Mise à jour de l'application", "components.Settings.SettingsAbout.documentation": "Documentation", "components.NotificationTypeSelector.mediarequestedDescription": "Envoyer des notifications lorsque des utilisateurs soumettent une demande de média qui nécessite une validation.", "components.NotificationTypeSelector.mediarequested": "Demande en attente de validation", @@ -347,11 +346,10 @@ "components.PlexLoginButton.signingin": "Connexion en cours…", "components.UserList.userssaved": "Les permissions d'utilisateur ont été enregistrées avec succès !", "components.UserList.bulkedit": "Modification en masse", - "components.Settings.csrfProtectionTip": "Définir l'accès à l'API externe en lecture seule (nécessite HTTPS et Overseerr doit être rechargé pour que les modifications prennent effet)", + "components.Settings.csrfProtectionTip": "Définir l'accès à l'API externe en lecture seule (nécessite HTTPS)", "components.Settings.csrfProtection": "Activer la protection CSRF", "components.PermissionEdit.usersDescription": "Autorise à gérer les utilisateurs. Les utilisateurs avec cette autorisation ne peuvent pas modifier les utilisateurs dotés de privilèges d'administrateur ni les accorder.", "components.PermissionEdit.users": "Gérer les utilisateurs", - "components.PermissionEdit.settings": "Gérer les paramètres", "components.PermissionEdit.requestDescription": "Autorise à demander des médias non-4K.", "components.PermissionEdit.request4kTvDescription": "Autorise à demander des séries en 4K.", "components.PermissionEdit.request4kTv": "Demander des séries en 4K", @@ -397,7 +395,7 @@ "components.MovieDetails.mark4kavailable": "Marquer comme disponible en 4K", "components.MovieDetails.playonplex": "Lire sur Plex", "components.MovieDetails.play4konplex": "Lire en 4K sur Plex", - "components.Settings.trustProxyTip": "Permettre Overseerr à enregistrer correctement les adresses IP des clients derrière un proxy (Overseerr doit être rechargé pour que les modifications prennent effet)", + "components.Settings.trustProxyTip": "Permettre Overseerr à enregistrer correctement les adresses IP des clients derrière un proxy", "components.Settings.trustProxy": "Activer la prise en charge proxy", "components.Settings.SettingsJobsCache.jobsDescription": "Overseerr effectue certaines tâches de maintenance comme des tâches planifiées régulièrement, mais elles peuvent également être déclenchées manuellement ci-dessous. L'exécution manuelle d'une tâche ne modifiera pas sa planification.", "components.Settings.SettingsJobsCache.cachemisses": "Manqués", diff --git a/src/i18n/locale/hu.json b/src/i18n/locale/hu.json index 6ba9f7bda..bc53c2888 100644 --- a/src/i18n/locale/hu.json +++ b/src/i18n/locale/hu.json @@ -28,7 +28,6 @@ "components.PersonDetails.appearsin": "Szerepel a következőkben", "components.PermissionEdit.usersDescription": "Engedélyt ad az Overseerr-nek felhasználók kezelésére. Az ezzel az engedéllyel rendelkező felhasználók nem módosíthatják a rendszergazdai jogosultsággal rendelkező felhasználókat, és nem adhatják meg a jogosultságot más felhasználónak.", "components.PermissionEdit.users": "Felhasználók kezelése", - "components.PermissionEdit.settings": "Beállítások kezelése", "components.PermissionEdit.requestDescription": "Engedélyt ad filmek és sorozatok kérésére.", "components.PermissionEdit.request4kTvDescription": "Engedélyt ad 4K-s sorozatok kérésére.", "components.PermissionEdit.request4kTv": "Kérés - 4K sorozatok", @@ -192,7 +191,6 @@ "components.TvDetails.anime": "Anime", "components.TvDetails.TvCrew.fullseriescrew": "Sorozat teljes stábja", "components.TvDetails.TvCast.fullseriescast": "Sorozat teljes szereposztása", - "components.StatusChacker.newversionavailable": "Alkalmazásfrissítés", "components.StatusBadge.status4k": "4K {status}", "components.Setup.welcome": "Üdv az Overseerr-ben", "components.Setup.signinMessage": "Kezdésnek lépj be a Plex fiókoddal", @@ -310,7 +308,7 @@ "components.Settings.validationApplicationUrlTrailingSlash": "Az URL-nek nem lehet vége perjellel", "components.Settings.validationApplicationUrl": "Érvényes URL-t kell megadnia", "components.Settings.validationApplicationTitle": "Meg kell adnia egy alkalmazás címet", - "components.Settings.trustProxyTip": "Lehetővé teszi az Overseerr számára, hogy helyesen regisztrálja az ügyfelek IP-címeit proxy mögött (az Overseerr-t újra kell tölteni a változások érvénybe lépéséhez)", + "components.Settings.trustProxyTip": "Lehetővé teszi az Overseerr számára, hogy helyesen regisztrálja az ügyfelek IP-címeit proxy mögött", "components.Settings.trustProxy": "Proxy-támogatás engedélyezése", "components.Settings.toastSettingsSuccess": "A beállítások sikeresen mentve!", "components.Settings.toastSettingsFailure": "Valami elromlott a beállítások mentése közben.", @@ -381,7 +379,7 @@ "components.Settings.default4k": "Alapértelmezett 4K", "components.Settings.default": "Alapértelmezett", "components.Settings.currentlibrary": "Jelenlegi könyvtár: {name}", - "components.Settings.csrfProtectionTip": "Külső API-hozzáférés beállítása csak olvashatóra (HTTPS szükséges, és az Overseerr-t újra kell tölteni a módosítások érvénybe lépéséhez)", + "components.Settings.csrfProtectionTip": "Külső API-hozzáférés beállítása csak olvashatóra (HTTPS szükséges)", "components.Settings.csrfProtectionHoverTip": "NE engedélyezze ezt a beállítást, csak ha megérti, mit csinál!", "components.Settings.csrfProtection": "Engedélyezze a CSRF-védelmet", "components.Settings.copied": "API-kulcs másolva a vágólapra.", diff --git a/src/i18n/locale/it.json b/src/i18n/locale/it.json index 87884c5d8..bfe43344e 100644 --- a/src/i18n/locale/it.json +++ b/src/i18n/locale/it.json @@ -252,7 +252,6 @@ "components.Settings.Notifications.senderName": "Nome del mittente", "components.Settings.Notifications.chatId": "ID chat", "components.Settings.Notifications.botAPI": "Token di autorizzazione bot", - "components.StatusChacker.newversionavailable": "Aggiornamento Applicazione", "components.Settings.SettingsAbout.documentation": "Documentazione", "components.NotificationTypeSelector.mediarequestedDescription": "Invia notifiche quando gli utenti presentano nuove richieste di media che richiedono approvazione.", "components.NotificationTypeSelector.mediarequested": "Richiesta in attesa di approvazione", @@ -363,7 +362,7 @@ "components.Settings.serverRemote": "remoto", "components.Settings.serverLocal": "locale", "components.Settings.notificationAgentSettingsDescription": "Configura e abilita gli agenti di notifica.", - "components.Settings.csrfProtectionTip": "Imposta l'accesso alle API esterne in sola lettura (richiede HTTPS e Overseerr deve essere ricaricato affinché le modifiche abbiano effetto)", + "components.Settings.csrfProtectionTip": "Imposta l'accesso alle API esterne in sola lettura (richiede HTTPS)", "components.Settings.csrfProtectionHoverTip": "NON abilitate questa opzione se non sapete cosa state facendo!", "components.Settings.csrfProtection": "Abilita protezione CSRF", "components.Settings.applicationTitle": "Titolo dell'applicazione", @@ -465,7 +464,7 @@ "components.Settings.serverpresetRefreshing": "Recupero di server…", "components.Settings.serverpresetManualMessage": "Configurazione manuale", "components.TvDetails.nextAirDate": "Prossima data di messa in onda", - "components.Settings.trustProxyTip": "Permette a Overseerr di registrare correttamente gli indirizzi IP dei client dietro un proxy (Overseerr deve essere ricaricato perché le modifiche abbiano effetto)", + "components.Settings.trustProxyTip": "Permette a Overseerr di registrare correttamente gli indirizzi IP dei client dietro un proxy", "components.Settings.settingUpPlexDescription": "Per impostare Plex, potete inserire i dati manualmente o selezionare un server recuperato da plex.tv. Premi il pulsante a destra del menu a tendina per recuperare la lista di server disponibili.", "components.Settings.Notifications.sendSilentlyTip": "Invia notifiche senza suono", "components.Settings.Notifications.sendSilently": "Invia silenziosamente", diff --git a/src/i18n/locale/ja.json b/src/i18n/locale/ja.json index 35f9b7c12..611732ebb 100644 --- a/src/i18n/locale/ja.json +++ b/src/i18n/locale/ja.json @@ -230,8 +230,8 @@ "i18n.requested": "リクエスト済み", "components.TvDetails.watchtrailer": "予告編を見る", "components.MovieDetails.watchtrailer": "予告編を見る", - "components.UserList.importfromplexerror": "Plexからユーザーをインポート中に問題が発生しました。", - "components.UserList.importfromplex": "Plexからユーザーをインポート", + "components.UserList.importfromplexerror": "Plex からユーザーをインポート中に問題が発生しました。", + "components.UserList.importfromplex": "Plex からユーザーをインポート", "components.UserList.importedfromplex": "Plex から新ユーザー {userCount} 名をインポートしました。", "components.TvDetails.viewfullcrew": "フルクルーを表示", "components.TvDetails.firstAirDate": "初放送日", @@ -430,7 +430,6 @@ "components.PermissionEdit.usersDescription": "ユーザーを管理する権限を付与する。この権限を持つユーザーは、Admin 権限を持つユーザーの変更や、Admin 権限を付与することはできません。", "components.NotificationTypeSelector.mediarequestedDescription": "ユーザーが承認を必要とする新メディアリクエストをすると通知する。", "components.PermissionEdit.users": "ユーザー管理", - "components.PermissionEdit.settings": "設定の管理", "components.PermissionEdit.requestTvDescription": "4K 以外のシリーズをリクエストする権限を与える。", "components.PermissionEdit.requestTv": "シリーズをリクエスト", "components.PermissionEdit.requestMoviesDescription": "4K 以外の映画をリクエストする権限を与える。", @@ -617,8 +616,8 @@ "components.RequestModal.approve": "リクエストを承認", "i18n.open": "未解決", "components.RequestBlock.languageprofile": "言語プロフィール", - "components.RequestModal.requestApproved": "{title}のリクエストは承認されました!", - "components.RequestModal.requestcancelled": "{title}のリクエストはキャンセルされました。", + "components.RequestModal.requestApproved": "{title} のリクエストは承認されました!", + "components.RequestModal.requestcancelled": "{title} のリクエストはキャンセルされました。", "components.RequestModal.requestmovies": "{count}{count, plural, one {映画} other {映画}}をリクエスト", "components.RequestModal.requestmovies4k": "{count}{count, plural, one {映画} other {映画}}を4Kでリクエスト", "components.ResetPassword.emailresetlink": "パスワードリセットリンクを送信", @@ -655,7 +654,7 @@ "components.NotificationTypeSelector.adminissueresolvedDescription": "他ユーザーがチケットを解決した際に通知を受ける", "components.NotificationTypeSelector.issuereopenedDescription": "チケットが再度開く際に通知を受ける", "components.ManageSlideOver.markavailable": "視聴可能にする", - "components.RequestModal.request4ktitle": "4Kで{title}をリクエストする", + "components.RequestModal.request4ktitle": "4Kで {title} をリクエストする", "components.RequestModal.requestedited": "{title}のリクエストは編集に成功しました!", "components.RequestModal.requesterror": "リクエストの送信中に問題が発生しました。", "components.Settings.Notifications.NotificationsGotify.validationTokenRequired": "アプリケーショントークンを入力してください", diff --git a/src/i18n/locale/nb_NO.json b/src/i18n/locale/nb_NO.json index 90ae5bcec..134edc454 100644 --- a/src/i18n/locale/nb_NO.json +++ b/src/i18n/locale/nb_NO.json @@ -300,7 +300,6 @@ "components.PersonDetails.birthdate": "Født {birthdate}", "components.PersonDetails.alsoknownas": "Også kjent som: {names}", "components.PermissionEdit.viewrequests": "Vis Forespørsler", - "components.PermissionEdit.settings": "Administrer Innstillinger", "components.PermissionEdit.requestDescription": "Gi tilgang til å forespørre medie som ikke er i 4K.", "components.PermissionEdit.request4kTvDescription": "Gi tilgang til å forespørre serier i 4K.", "components.PermissionEdit.request4kTv": "Forespør Serier i 4K", @@ -459,7 +458,7 @@ "components.RequestModal.QuotaDisplay.quotaLinkUser": "Du kan se en oppsummering av denne brukerens forespørselbegrensninger på profilsiden deres.", "components.RequestModal.QuotaDisplay.quotaLink": "Du kan se en oppsummering av dine forespørselbegrensninger på profilsiden.", "components.RequestModal.QuotaDisplay.notenoughseasonrequests": "Ikke nok gjenværende sesongforespørsler", - "components.Settings.csrfProtectionTip": "Sett ekstern API-tilgang til skrivebeskyttet modus (krever HTTPS og en omstart av Overseer for at endringen skal tre i kraft)", + "components.Settings.csrfProtectionTip": "Sett ekstern API-tilgang til skrivebeskyttet modus (krever HTTPS)", "components.Settings.csrfProtectionHoverTip": "Ikke aktiver dette med mindre du vet hva du gjør!", "components.Settings.csrfProtection": "Aktiver CSRF-beskyttelse", "components.Settings.cacheImages": "Aktiver mellomlagring av bilder", @@ -578,11 +577,11 @@ "components.UserProfile.norequests": "Ingen forespørsler.", "components.Settings.noDefaultServer": "Minst én {serverType} server må være definert som standard for at {mediaType}-forespørsler skal kunne bli håndtert.", "components.RequestModal.pendingapproval": "Forespørselen din avventer godkjenning.", - "components.RequestList.RequestItem.mediaerror": "TIttelen som var knyttet til denne forespørselen er ikke lenger tilgjengelig.", + "components.RequestList.RequestItem.mediaerror": "{mediaType} Finnes ikke", "components.RequestCard.deleterequest": "Slett forespørsel", "components.RequestList.RequestItem.deleterequest": "Slett forespørsel", "components.RequestList.RequestItem.cancelRequest": "Avbryt forespørsel", - "components.RequestCard.mediaerror": "Tittelen som var knyttet til denne forespørselen er ikke lenger tilgjengelig.", + "components.RequestCard.mediaerror": "{mediaType} Finnes ikke", "components.Discover.noRequests": "Ingen forespørsler.", "components.Settings.SettingsAbout.betawarning": "Dette er beta-programvare. Funksjoner kan slutte og fungerer og/eller være ustabile. Reporter gjerne problemer på GitHub!", "components.Settings.noDefaultNon4kServer": "Dersom du har én {serverType} server for både ikke-4K og 4K-media (eller hvis du kun laster ned 4K-innhold), skal {serverType} server IKKE være huket av som en 4K server.", @@ -991,7 +990,7 @@ "components.Settings.Notifications.NotificationsWebhook.resetPayloadSuccess": "JSON payload tilbakestilt!", "components.Settings.Notifications.NotificationsPushover.userTokenTip": "Din 30-tegns bruker- eller gruppe-nøkkel", "components.Settings.SettingsJobsCache.cachevsize": "Verdistørrelse", - "components.Settings.trustProxyTip": "Tillatt Overseerr å registrere klienters IP addresser korrekt bak en proxy (Overseerr må startes på nytt før endringene trer i kraft)", + "components.Settings.trustProxyTip": "Tillatt Overseerr å registrere klienters IP addresser korrekt bak en proxy", "components.Settings.serviceSettingsDescription": "Konfigurer dine {serverType}tjener(e) nedenfor. Du kan koble til flere forskellige {serverType}tjenere men kun to av dem kan markeres som standard (en som ikke er 4K og en 4K). Administratorer kan endre hvilken tjener som brukes før godkjennelse av nye forespørsler.", "components.ManageSlideOver.manageModalClearMediaWarning": "* Dette vil slette all data for denne tittelen uten mulighet for å bli gjennopprettet, det inkluderer alle forespørsler, avvik osv. Hvis denne tittelen finnes i ditt Plex bibliotek vil medieinformasjon bli opprettet på nytt under neste skanning.", "components.Settings.Notifications.NotificationsWebhook.authheader": "Autorisasjonshode", @@ -1037,5 +1036,15 @@ "components.UserProfile.UserSettings.UserGeneralSettings.validationDiscordId": "Du må oppgi en gyldig Bruker ID for Discord", "components.UserProfile.UserSettings.UserGeneralSettings.discordIdTip": "ID-nummeret til din brukerkonto", "components.Settings.SettingsAbout.appDataPath": "Datakatalog", - "components.RequestBlock.languageprofile": "Språkprofil" + "components.RequestBlock.languageprofile": "Språkprofil", + "components.MovieDetails.digitalrelease": "Digital utgivelse", + "components.MovieDetails.physicalrelease": "Fysisk Utgivelse", + "components.MovieDetails.theatricalrelease": "Kinopremiere", + "components.StatusChecker.appUpdated": "{applicationTitle} Oppdatert", + "components.PermissionEdit.viewrecent": "Vis nylig lag til", + "components.PermissionEdit.viewrecentDescription": "Gi tillatelse til å vise nylig lagt til.", + "components.Settings.deleteServer": "Slett {serverType} tjener", + "components.RequestCard.tmdbid": "TMDB ID", + "components.RequestCard.tvdbid": "TheTVDB ID", + "components.StatusChecker.appUpdatedDescription": "Vennligst klikk her for å laste applikasjonen på nytt." } diff --git a/src/i18n/locale/nl.json b/src/i18n/locale/nl.json index 77b4a49ca..4d36a3230 100644 --- a/src/i18n/locale/nl.json +++ b/src/i18n/locale/nl.json @@ -223,7 +223,6 @@ "components.TvDetails.network": "{networkCount, plural, one {Netwerk} other {Netwerken}}", "components.TvDetails.firstAirDate": "Datum eerste uitzending", "components.TvDetails.anime": "Anime", - "components.StatusChacker.newversionavailable": "Toepassingsupdate", "components.Settings.toastSettingsSuccess": "Instellingen succesvol opgeslagen!", "components.Settings.toastSettingsFailure": "Er ging iets mis bij het opslaan van de instellingen.", "components.Settings.toastApiKeySuccess": "Nieuwe API-sleutel succesvol gegenereerd!", @@ -362,11 +361,10 @@ "components.Settings.serverpreset": "Server", "components.Settings.serverRemote": "extern", "components.Settings.serverLocal": "lokaal", - "components.Settings.csrfProtectionTip": "Externe API-toegang instellen op alleen-lezen (vereist HTTPS en Overseerr moet opnieuw worden geladen om wijzigingen door te voeren)", + "components.Settings.csrfProtectionTip": "Externe API-toegang instellen op alleen-lezen (vereist HTTPS)", "components.Settings.csrfProtection": "CSRF-bescherming inschakelen", "components.PermissionEdit.usersDescription": "Toestemming geven om gebruikers te beheren. Gebruikers met deze toestemming kunnen gebruikers met beheerdersrechten niet wijzigen of die rechten verlenen.", "components.PermissionEdit.users": "Gebruikers beheren", - "components.PermissionEdit.settings": "Instellingen beheren", "components.PermissionEdit.requestDescription": "Toestemming geven om niet-4K-media aan te vragen.", "components.PermissionEdit.request4kTvDescription": "Toestemming geven om series in 4K aan te vragen.", "components.PermissionEdit.request4kTv": "4K-series aanvragen", @@ -397,7 +395,7 @@ "components.MovieDetails.play4konplex": "Afspelen in 4K op Plex", "components.MovieDetails.mark4kavailable": "Als beschikbaar in 4K markeren", "components.MovieDetails.markavailable": "Als beschikbaar markeren", - "components.Settings.trustProxyTip": "Overseerr toestaan om IP-adressen van clients correct te registreren achter een proxy (Overseerr moet opnieuw worden geladen om de wijzigingen door te voeren)", + "components.Settings.trustProxyTip": "Overseerr toestaan om IP-adressen van clients correct te registreren achter een proxy", "components.Settings.trustProxy": "Proxy-ondersteuning inschakelen", "components.Settings.SettingsJobsCache.cacheflushed": "{cachename} cache leeggemaakt.", "components.Settings.SettingsJobsCache.cache": "Cache", diff --git a/src/i18n/locale/pl.json b/src/i18n/locale/pl.json index d4dd888cc..919577cf4 100644 --- a/src/i18n/locale/pl.json +++ b/src/i18n/locale/pl.json @@ -764,7 +764,6 @@ "components.Setup.configureplex": "Skonfiguruj Plex", "components.Setup.configureservices": "Skonfiguruj usługi", "components.Setup.continue": "Kontynuuj", - "components.StatusChacker.reloadOverseerr": "Odśwież", "components.TvDetails.TvCast.fullseriescast": "Pełna obsada serialu", "components.TvDetails.TvCrew.fullseriescrew": "Pełna ekipa serialu", "components.TvDetails.episodeRuntime": "Czas trwania odcinka", @@ -782,7 +781,7 @@ "components.UserList.autogeneratepasswordTip": "Wyślij do użytkownika wiadomość e-mail z hasłem wygenerowanym przez serwer", "components.UserList.createlocaluser": "Utwórz użytkownika lokalnego", "components.UserList.creating": "Tworzenie…", - "components.UserList.deleteuser": "Usuń użytkownika", + "components.UserList.deleteuser": "Usuń {username}", "components.UserList.displayName": "Wyświetlana nazwa", "components.UserList.importedfromplex": "{userCount} {userCount, plural, one {użytkownik Plex został zaimportowany} other {użytkowników Plex zostało zaimportowanych}} pomyślnie!", "components.UserList.localLoginDisabled": "Ustawienie Włącz Lokalne Logowanie jest obecnie wyłączone.", @@ -909,7 +908,7 @@ "components.Settings.SonarrModal.animerootfolder": "Folder główny anime", "components.Settings.SonarrModal.selectQualityProfile": "Wybierz profil jakości", "components.Settings.SonarrModal.selecttags": "Wybierz tagi", - "components.Settings.csrfProtectionTip": "Ustaw zewnętrzny dostęp api na tylko do odczytu (wymaga HTTPS, a Overseerr musi zostać ponownie załadowany, aby zmiany zostały wprowadzone)", + "components.Settings.csrfProtectionTip": "Ustaw zewnętrzny dostęp api na tylko do odczytu (wymaga HTTPS)", "components.Settings.toastPlexRefreshFailure": "Nie udało się pobrać listy serwerów Plex.", "components.Settings.SonarrModal.apiKey": "Klucz API", "components.Settings.cancelscan": "Anuluj skanowanie", @@ -930,7 +929,7 @@ "components.Settings.partialRequestsEnabled": "Zezwalaj na prośby o część serialu", "components.Settings.sonarrsettings": "Ustawienia Sonarr", "components.Settings.ssl": "Protokół SSL", - "components.Settings.trustProxyTip": "Pozwól Overseerr poprawnie rejestrować adresy IP klientów za serwerem proxy (Overseerr musi zostać ponownie załadowany, aby zmiany zostały wprowadzone)", + "components.Settings.trustProxyTip": "Pozwól Overseerr poprawnie rejestrować adresy IP klientów za serwerem proxy", "components.Settings.toastPlexConnectingSuccess": "Połączenie Plex nawiązane pomyślnie!", "components.Settings.toastSettingsSuccess": "Ustawienia zostały zapisane pomyślnie!", "components.Settings.serviceSettingsDescription": "Skonfiguruj poniżej swój serwer(y) {serverType}. Możesz podłączyć wiele serwerów {serverType}, ale tylko dwa z nich mogą być oznaczone jako domyślne (jeden nie-4K i jeden 4K). Administratorzy mogą zmienić serwer używany do przetwarzania nowych żądań przed zatwierdzeniem.", @@ -1036,5 +1035,16 @@ "components.UserProfile.UserSettings.UserGeneralSettings.discordId": "ID użytkownika Discorda", "components.UserProfile.UserSettings.UserGeneralSettings.discordIdTip": "Wielocyfrowy numer ID powiązany z Twoim kontem użytkownika Discord", "components.UserProfile.UserSettings.UserGeneralSettings.validationDiscordId": "Musisz podać poprawne ID użytkownika Discord", - "components.Settings.SettingsAbout.appDataPath": "Katalog danych" + "components.Settings.SettingsAbout.appDataPath": "Katalog danych", + "components.StatusChecker.restartRequiredDescription": "Uruchom ponownie serwer, aby zastosować zaktualizowane ustawienia.", + "components.StatusChecker.appUpdated": "Zaktualizowano {applicationTitle}", + "components.Settings.deleteServer": "Usuń serwer {serverType}", + "components.StatusChecker.appUpdatedDescription": "Kliknij przycisk poniżej, aby ponownie załadować aplikację.", + "components.StatusChecker.reloadApp": "Odśwież {applicationTitle}", + "i18n.restartRequired": "Wymagane jest ponowne uruchomienie", + "components.RequestBlock.languageprofile": "Profil językowy", + "components.StatusChecker.restartRequired": "Wymagane ponowne uruchomienie serwera", + "components.MovieDetails.digitalrelease": "Wydanie cyfrowe", + "components.MovieDetails.physicalrelease": "Wydanie fizyczne", + "components.MovieDetails.theatricalrelease": "Wydanie kinowe" } diff --git a/src/i18n/locale/pt_BR.json b/src/i18n/locale/pt_BR.json index fe9c32b50..462fd5859 100644 --- a/src/i18n/locale/pt_BR.json +++ b/src/i18n/locale/pt_BR.json @@ -215,8 +215,8 @@ "components.UserList.totalrequests": "Solicitações", "components.UserList.role": "Privilégio", "components.UserList.plexuser": "Usuário Plex", - "components.UserList.deleteuser": "Deletar Usuário", - "components.UserList.deleteconfirm": "Tem certeza que deseja deletar esse usuário? Todas informações de solicitações feitas por esse usuário serão permanentemente removidas.", + "components.UserList.deleteuser": "Remover {username}", + "components.UserList.deleteconfirm": "Tem certeza que deseja apagar esse usuário? Todas informações de solicitações feitas por esse usuário serão permanentemente removidas.", "components.UserList.created": "Criado", "components.UserList.admin": "Administrador", "components.TvDetails.similar": "Séries Semelhantes", @@ -354,7 +354,7 @@ "components.Settings.serverRemote": "remoto", "components.Settings.serverLocal": "local", "components.Settings.notificationAgentSettingsDescription": "Configure e habilite agentes de notificação.", - "components.Settings.csrfProtectionTip": "Define acesso externo à API como apenas leitura (Requer HTTPS e é necessário reiniciar Overseerr para mudança ter efeito)", + "components.Settings.csrfProtectionTip": "Define acesso externo à API como apenas leitura (Requer HTTPS)", "components.Settings.csrfProtection": "Habilitar Proteção Contra CSRF", "components.PlexLoginButton.signinwithplex": "Entrar", "components.Login.signingin": "Autenticando…", @@ -396,7 +396,7 @@ "components.MovieDetails.markavailable": "Marcar como Disponível", "components.MovieDetails.mark4kavailable": "Marcar como Disponível em 4K", "components.Settings.trustProxy": "Habilitar Suporte a Proxy", - "components.Settings.trustProxyTip": "Permite que Overseerr exiba o IP correto do cliente atrás de um proxy (Overseerr precisa ser reiniciado para que as mudanças tenham efeito)", + "components.Settings.trustProxyTip": "Permite que Overseerr exiba o IP correto do cliente atrás de um proxy", "components.Settings.SettingsJobsCache.cacheflushed": "Cache {cachename} limpo.", "components.Settings.SettingsJobsCache.cache": "Cache", "components.Settings.SettingsJobsCache.cachevsize": "Tamanho do Valor", @@ -732,10 +732,10 @@ "components.Settings.Notifications.validationPgpPassword": "Você deve prover uma senha PGP", "components.Settings.Notifications.botUsernameTip": "Permitir que usuários iniciem uma conversa com o seu bot e configure suas próprias notificações", "components.RequestModal.pendingapproval": "Sua solicitação está aguardando aprovação.", - "components.RequestList.RequestItem.mediaerror": "O título associado à essa solicitação não está mais disponível.", + "components.RequestList.RequestItem.mediaerror": "{mediaType} Não Encontrado", "components.RequestList.RequestItem.deleterequest": "Apagar Solicitação", "components.RequestList.RequestItem.cancelRequest": "Cancelar Solicitação", - "components.RequestCard.mediaerror": "O título associado à essa solicitação não está mais disponível.", + "components.RequestCard.mediaerror": "{mediaType} Não Encontrado", "components.RequestCard.deleterequest": "Apagar Solicitação", "components.NotificationTypeSelector.notificationTypes": "Tipos de Notificação", "components.Layout.VersionStatus.streamstable": "Overseerr Estável", @@ -1041,5 +1041,20 @@ "components.StatusChecker.restartRequiredDescription": "Por favor, reinicie o servidor para aplicar as novas configurações.", "i18n.restartRequired": "Reinicialização Necessária", "components.StatusChecker.reloadApp": "Recarregar {applicationTitle}", - "components.StatusChecker.restartRequired": "Reinicialização do Servidor Necessária" + "components.StatusChecker.restartRequired": "Reinicialização do Servidor Necessária", + "components.StatusChecker.appUpdatedDescription": "Por favor, clique no botão abaixo para recarregar a aplicação.", + "components.Settings.deleteServer": "Remover Servidor {serverType}", + "components.MovieDetails.digitalrelease": "Lançamento Digital", + "components.MovieDetails.physicalrelease": "Lançamento em Disco", + "components.MovieDetails.theatricalrelease": "Lançamento no Cinema", + "components.PermissionEdit.viewrecent": "Ver Recentemente Adicionados", + "components.PermissionEdit.viewrecentDescription": "Concede permissão para ver lista de mídias adicionadas recentemente.", + "components.RequestCard.tmdbid": "ID do TMDB", + "components.RequestCard.tvdbid": "ID do TheTVDB", + "components.RequestList.RequestItem.tmdbid": "ID do TMDB", + "components.RequestList.RequestItem.tvdbid": "ID do TheTVDB", + "components.TitleCard.mediaerror": "{mediaType} Não Encontrado", + "components.TitleCard.tmdbid": "ID do TMDB", + "components.TitleCard.tvdbid": "ID do TheTVDB", + "components.TitleCard.cleardata": "Limpar Dados" } diff --git a/src/i18n/locale/pt_PT.json b/src/i18n/locale/pt_PT.json index 8930e0e56..194893199 100644 --- a/src/i18n/locale/pt_PT.json +++ b/src/i18n/locale/pt_PT.json @@ -333,7 +333,6 @@ "components.TvDetails.anime": "Anime", "components.TvDetails.TvCrew.fullseriescrew": "Equipa Técnica Completa da Série", "components.TvDetails.TvCast.fullseriescast": "Elenco Completo da Série", - "components.StatusChacker.newversionavailable": "Atualização de Aplicação", "components.StatusBadge.status4k": "4K {status}", "components.Login.signinwithplex": "Iniciar sessão com a sua conta Plex", "components.RequestModal.requesterror": "Ocorreu um erro ao submeter o pedido.", @@ -360,11 +359,10 @@ "components.Settings.serverpreset": "Servidor", "components.Settings.serverRemote": "remoto", "components.Settings.serverLocal": "local", - "components.Settings.csrfProtectionTip": "Definir o acesso externo API para somente leitura (requer HTTPS, e Overseerr deve ser recarregado para que as alterações tenham efeito)", + "components.Settings.csrfProtectionTip": "Definir o acesso externo API para somente leitura (requer HTTPS)", "components.Settings.csrfProtection": "Ativar Proteção CSRF", "components.PermissionEdit.usersDescription": "Conceder permissão para gerir utilizadores Overseerr. Os utilizadores com essa permissão não podem modificar os utilizadores ou conceder o privilégio de administrador.", "components.PermissionEdit.users": "Gerir Utilizadores", - "components.PermissionEdit.settings": "Gerir Definições", "components.PermissionEdit.requestDescription": "Conceder permissão para pedir multimédia não 4K.", "components.PermissionEdit.request4kTvDescription": "Conceder permissão para pedir séries em 4K.", "components.PermissionEdit.request4kTv": "Pedir Séries 4K", @@ -395,7 +393,7 @@ "components.TvDetails.play4konplex": "Ver em 4K no Plex", "components.MovieDetails.play4konplex": "Ver em 4K no Plex", "components.MovieDetails.playonplex": "Ver no Plex", - "components.Settings.trustProxyTip": "Permitir que o Overseerr registe corretamente os endereços IP do cliente por trás de um proxy (o Overseerr deve ser recarregado para que as alterações tenham efeito)", + "components.Settings.trustProxyTip": "Permitir que o Overseerr registe corretamente os endereços IP do cliente por trás de um proxy", "components.Settings.trustProxy": "Ativar Suporte de Proxy", "components.MovieDetails.markavailable": "Marcar como Disponível", "components.MovieDetails.mark4kavailable": "Marcar como Disponível em 4K", diff --git a/src/i18n/locale/ru.json b/src/i18n/locale/ru.json index 9fbc7c7a2..fb1418489 100644 --- a/src/i18n/locale/ru.json +++ b/src/i18n/locale/ru.json @@ -601,7 +601,7 @@ "components.TvDetails.showtype": "Тип сериала", "components.TvDetails.TvCrew.fullseriescrew": "Полная съёмочная группа сериала", "components.TvDetails.TvCast.fullseriescast": "Полный актёрский состав сериала", - "components.Settings.trustProxyTip": "Позволяет Overseerr корректно регистрировать IP-адреса клиентов за прокси-сервером (Overseerr необходимо перезагрузить, чтобы изменения вступили в силу)", + "components.Settings.trustProxyTip": "Позволяет Overseerr корректно регистрировать IP-адреса клиентов за прокси-сервером", "components.Settings.originallanguageTip": "Контент фильтруется по языку оригинала", "components.Settings.noDefaultNon4kServer": "Если вы используете один сервер {serverType} для контента, в том числе и для 4К, или если вы загружаете только контент 4K, ваш сервер {serverType} НЕ должен быть помечен как 4К сервер.", "components.UserList.localLoginDisabled": "Параметр Включить локальный вход в настоящее время отключен.", @@ -621,7 +621,7 @@ "components.Settings.SonarrModal.enableSearch": "Включить автоматический поиск", "components.Settings.SonarrModal.edit4ksonarr": "Редактировать 4К сервер Sonarr", "components.Settings.toastApiKeyFailure": "Что-то пошло не так при создании нового ключа API.", - "components.Settings.csrfProtectionTip": "Устанавливает доступ к API извне только для чтения (требуется HTTPS, для вступления изменений в силу необходимо перезагрузить Overseerr)", + "components.Settings.csrfProtectionTip": "Устанавливает доступ к API извне только для чтения (требуется HTTPS)", "components.Settings.SonarrModal.animequalityprofile": "Профиль качества для аниме", "components.Settings.SonarrModal.animelanguageprofile": "Языковой профиль для аниме", "components.Settings.SonarrModal.animeTags": "Теги для аниме", diff --git a/src/i18n/locale/sq.json b/src/i18n/locale/sq.json index fea1b4bb6..c15b5a64b 100644 --- a/src/i18n/locale/sq.json +++ b/src/i18n/locale/sq.json @@ -248,7 +248,6 @@ "components.PermissionEdit.requestDescription": "Jep leje për të paraqitur kërkesat për media jo-4K.", "components.PermissionEdit.requestTv": "Kërko Serialin", "components.PermissionEdit.requestTvDescription": "Jep leje për të paraqitur kërkesa për seri jo-4K.", - "components.PermissionEdit.settingsDescription": "Jep leje për të modifikuar cilësimet globale. Një përdorues duhet ta ketë këtë leje për t'ua dhënë të tjerëve.", "components.PermissionEdit.users": "Menaxho Përdoruesit", "components.PermissionEdit.usersDescription": "Jep leje për të menaxhuar përdoruesit. Përdoruesit me këtë leje nuk mund të modifikojnë ose të japin privilegjin e administratorit.", "components.PermissionEdit.viewissues": "Shiko Problemet", @@ -661,7 +660,7 @@ "components.Settings.toastTautulliSettingsFailure": "Diçka shkoi keq duke ruajtur cilësimet e Tautullit.", "components.Settings.toastTautulliSettingsSuccess": "Cilësimet e Tautulli u ruajtën me sukses!", "components.Settings.trustProxy": "Aktivo suportin Proxy", - "components.Settings.trustProxyTip": "Lejo Overseerr të regjistrojë në mënyrë korrekte adresat IP të klientit prapa një proxy (Overseerr duhet të ristartohet që ndryshimet të hyjnë në fuqi)", + "components.Settings.trustProxyTip": "Lejo Overseerr të regjistrojë në mënyrë korrekte adresat IP të klientit prapa një proxy", "components.Settings.urlBase": "Baza URL", "components.Settings.validationApiKey": "Duhet të japësh një çelës API", "components.Settings.validationApplicationTitle": "Duhet të japësh një titull aplikacioni", @@ -688,7 +687,6 @@ "components.Setup.welcome": "Mirë se vini në Overseerr", "components.StatusBadge.status": "{status}", "components.StatusBadge.status4k": "{status} 4K", - "components.StatusChacker.newversionavailable": "Përditësimi i aplikacionit", "components.TvDetails.TvCast.fullseriescast": "Kast i plotë i serisë", "components.TvDetails.TvCrew.fullseriescrew": "Ekuipazhi i plotë i serisë", "components.TvDetails.anime": "Anime", @@ -965,7 +963,7 @@ "components.Settings.copied": "Çelësi API u kopjua.", "components.Settings.csrfProtection": "Aktivo mbrojtjen e CSRF", "components.Settings.csrfProtectionHoverTip": "MOS e aktivizoni këtë cilësim nëse nuk e kuptoni se çfarë po bëni!", - "components.Settings.csrfProtectionTip": "Cakto aksesin e API-së së jashtme vetëm për lexim (kërkon HTTPS dhe Overseerr duhet të ringarkohet që ndryshimet të hyjnë në fuqi)", + "components.Settings.csrfProtectionTip": "Cakto aksesin e API-së së jashtme vetëm për lexim (kërkon HTTPS)", "components.Settings.currentlibrary": "Libraria aktuale: {name}", "components.Settings.default": "E paracaktuar", "components.Settings.default4k": "E Paracaktuar 4K", diff --git a/src/i18n/locale/sv.json b/src/i18n/locale/sv.json index 5bb5927d9..1014a0fe4 100644 --- a/src/i18n/locale/sv.json +++ b/src/i18n/locale/sv.json @@ -242,7 +242,6 @@ "components.TvDetails.viewfullcrew": "Visa hela rollistan", "components.TvDetails.firstAirDate": "Första sändningsdatum", "components.TvDetails.TvCrew.fullseriescrew": "Hela rollistan", - "components.StatusChacker.newversionavailable": "Applikationsuppdatering", "components.StatusBadge.status4k": "4K {status}", "components.Settings.SettingsAbout.documentation": "Dokumentation", "components.Settings.Notifications.validationChatIdRequired": "Du måste ange ett giltigt chatt-ID", @@ -302,7 +301,6 @@ "components.UserList.bulkedit": "Mass-redigering", "components.PermissionEdit.usersDescription": "Bevilja behörighet att hantera användare. Användare med denna behörighet kan inte ändra användare med eller bevilja administratörsbehörighet.", "components.PermissionEdit.users": "Hantera Användare", - "components.PermissionEdit.settings": "Hantera Inställningar", "components.PermissionEdit.requestDescription": "Ge tillstånd att skicka förfrågningar för icke-4K-media.", "components.PermissionEdit.request4kTvDescription": "Bevilja tillstånd att skicka förfrågningar för 4K-serien.", "components.PermissionEdit.request4kTv": "Begära 4K Serier", @@ -434,7 +432,7 @@ "components.Settings.SettingsJobsCache.cacheflushed": "{cachename} cache rensad.", "components.Settings.SettingsJobsCache.cacheDescription": "Overseerr cachear förfrågningar till externa API-endpoints för att optimera prestanda och att undvika onödiga api-kall.", "components.Settings.SettingsJobsCache.cache": "Cache", - "components.Settings.trustProxyTip": "Tillåt Overseerr att korrekt registrera klienters IP-adresser bakom en proxy (Overseerr måste laddas om för att ändringarna skall gå i kraft)", + "components.Settings.trustProxyTip": "Tillåt Overseerr att korrekt registrera klienters IP-adresser bakom en proxy", "components.Settings.trustProxy": "Aktivera Proxy-stöd", "components.TvDetails.playonplex": "Spela upp på Plex", "components.TvDetails.play4konplex": "Spela upp i 4K på Plex", @@ -457,7 +455,7 @@ "components.Settings.serverpreset": "Server", "components.Settings.serverRemote": "fjärr", "components.Settings.serverLocal": "lokal", - "components.Settings.csrfProtectionTip": "Ställ in extern API-åtkomst till skrivskyddad (kräver HTTPS och Overseerr måste laddas om för att ändringar ska träda i kraft)", + "components.Settings.csrfProtectionTip": "Ställ in extern API-åtkomst till skrivskyddad (kräver HTTPS)", "i18n.loading": "Laddar…", "components.UserProfile.recentrequests": "Senaste förfrågningar", "components.UserProfile.UserSettings.menuPermissions": "Behörigheter", @@ -1038,5 +1036,12 @@ "components.UserProfile.UserSettings.UserGeneralSettings.discordId": "Discord användar-ID", "components.UserProfile.UserSettings.UserGeneralSettings.discordIdTip": "Det flersiffriga ID-numret som är kopplat till ditt Discord-användarkonto", "components.Settings.SettingsAbout.appDataPath": "Datakatalog", - "components.RequestBlock.languageprofile": "Språkprofil" + "components.RequestBlock.languageprofile": "Språkprofil", + "components.StatusChecker.reloadApp": "Ladda om {applicationTitle}", + "i18n.restartRequired": "Omstart krävs", + "components.Settings.deleteServer": "Radera {serverType} Server", + "components.StatusChecker.appUpdated": "{applicationTitle} Uppdaterad", + "components.StatusChecker.appUpdatedDescription": "Klicka på knappen under för att ladda om programmet.", + "components.StatusChecker.restartRequired": "Servern behöver startas om", + "components.StatusChecker.restartRequiredDescription": "Starta om servern för att verkställa uppdaterade inställningar." } diff --git a/src/i18n/locale/zh_Hans.json b/src/i18n/locale/zh_Hans.json index 3c4520851..64af7b517 100644 --- a/src/i18n/locale/zh_Hans.json +++ b/src/i18n/locale/zh_Hans.json @@ -29,7 +29,7 @@ "components.UserList.email": "电子邮件地址", "components.UserList.edituser": "编辑用户权限", "components.UserList.displayName": "显示名称", - "components.UserList.deleteuser": "刪除用户", + "components.UserList.deleteuser": "刪除 {username}", "components.UserList.deleteconfirm": "确定要刪除这个用户吗?此用户的所有储存资料将被清除。", "components.UserList.creating": "创建中…", "components.UserList.createlocaluser": "建立本地用户", @@ -61,7 +61,6 @@ "components.TvDetails.anime": "动漫", "components.TvDetails.TvCrew.fullseriescrew": "制作群", "components.TvDetails.TvCast.fullseriescast": "演员阵容", - "components.StatusChacker.newversionavailable": "软件更新", "components.StatusBadge.status4k": "4K 版{status}", "components.Setup.welcome": "欢迎來到 Overseerr", "components.Setup.tip": "提示", @@ -83,7 +82,7 @@ "components.Settings.validationApplicationUrlTrailingSlash": "必须刪除結尾斜線", "components.Settings.validationApplicationUrl": "请输入有效的网址", "components.Settings.validationApplicationTitle": "请输入应用程序名", - "components.Settings.trustProxyTip": "使用代理服务器时,允许 Overseerr 探明客户端 IP 地址(Overseerr 必须重新启动)", + "components.Settings.trustProxyTip": "允许 Overseerr 使用代理正确注册客户端 IP 地址", "components.Settings.trustProxy": "启用代理服务器所需功能", "components.Settings.toastSettingsSuccess": "设置保存成功!", "components.Settings.toastSettingsFailure": "保存设置中出了点问题。", @@ -155,7 +154,7 @@ "components.Settings.default4k": "设置 4K 为默认分辨率", "components.Settings.default": "默认", "components.Settings.currentlibrary": "当前媒体库: {name}", - "components.Settings.csrfProtectionTip": "设置外部访问权限为只讀(Overseerr 必须重新启动)", + "components.Settings.csrfProtectionTip": "设置外部访问权限为只读(需要 HTTPS)", "components.Settings.csrfProtectionHoverTip": "除非你了解此功能,请勿启用它!", "components.Settings.csrfProtection": "防止跨站请求伪造(CSRF)攻击", "components.Settings.copied": "应用程序密钥已复制到剪贴板。", @@ -756,13 +755,13 @@ "components.RequestList.RequestItem.requested": "请求者", "components.RequestList.RequestItem.modifieduserdate": "{user}({date})", "components.RequestList.RequestItem.modified": "最后修改者", - "components.RequestList.RequestItem.mediaerror": "找不到此请求的媒体项目。", + "components.RequestList.RequestItem.mediaerror": "未找到{mediaType}", "components.RequestList.RequestItem.failedretry": "重试提交请求中出了点问题。", "components.RequestList.RequestItem.editrequest": "编辑请求", "components.RequestList.RequestItem.deleterequest": "刪除请求", "components.RequestList.RequestItem.cancelRequest": "取消请求", "components.RequestCard.seasons": "季数", - "components.RequestCard.mediaerror": "找不到此请求的媒体项目。", + "components.RequestCard.mediaerror": "未找到{mediaType}", "components.RequestCard.failedretry": "重试提交请求中出了点问题。", "components.RequestCard.deleterequest": "刪除请求", "components.RequestButton.viewrequest4k": "查看 4K 请求", @@ -801,7 +800,6 @@ "components.PermissionEdit.viewrequests": "查看请求", "components.PermissionEdit.usersDescription": "授予管理用户的权限。 拥有此权限的用户无法修改具有管理员权限的用户或授予管理员权限。", "components.PermissionEdit.users": "用户管理", - "components.PermissionEdit.settings": "设置管理", "components.PermissionEdit.requestTvDescription": "授予提交非 4K 电视剧请求的权限。", "components.PermissionEdit.requestTv": "提交电视节目请求", "components.PermissionEdit.requestMoviesDescription": "授予提交非 4K 电影请求的权限。", @@ -1038,5 +1036,6 @@ "components.UserProfile.UserSettings.UserNotificationSettings.validationPushoverUserKey": "请输入有效的用户或群组令牌", "i18n.import": "导入", "i18n.importing": "导入中…", - "components.RequestBlock.languageprofile": "语言配置文件" + "components.RequestBlock.languageprofile": "语言配置文件", + "components.TitleCard.mediaerror": "未找到{mediaType}" } diff --git a/src/i18n/locale/zh_Hant.json b/src/i18n/locale/zh_Hant.json index 6ab7d33c5..ae245233d 100644 --- a/src/i18n/locale/zh_Hant.json +++ b/src/i18n/locale/zh_Hant.json @@ -153,7 +153,7 @@ "components.UserList.totalrequests": "請求數", "components.UserList.plexuser": "Plex 使用者", "components.UserList.email": "電子郵件地址", - "components.UserList.deleteuser": "刪除使用者", + "components.UserList.deleteuser": "刪除 {username}", "components.UserList.role": "角色", "components.UserList.password": "密碼", "i18n.movies": "電影", @@ -508,7 +508,7 @@ "components.RequestModal.SearchByNameModal.notvdbiddescription": "無法自動配對您的請求。請從以下列表中選擇正確的媒體項。", "components.CollectionDetails.requestcollection4k": "提出 4K 系列請求", "components.Settings.trustProxyTip": "使用代理伺服器時,允許 Overseerr 註冊客戶端的正確 IP 位址", - "components.Settings.csrfProtectionTip": "設定外部訪問權限為只讀", + "components.Settings.csrfProtectionTip": "設定外部訪問權限為只讀(需要 HTTPS)", "components.ResetPassword.requestresetlinksuccessmessage": "通過電子郵件發送了密碼重設鏈接。", "components.ResetPassword.resetpasswordsuccessmessage": "密碼重設成功!", "components.RegionSelector.regionDefault": "所有地區", @@ -668,7 +668,7 @@ "i18n.status": "狀態", "i18n.showingresults": "{from}{to} 列(共 {total} 列)", "i18n.saving": "儲存中…", - "i18n.save": "儲存", + "i18n.save": "儲存變更", "i18n.resultsperpage": "每頁顯示 {pageSize} 列", "i18n.requesting": "提出請求中…", "i18n.request4k": "提出 4K 請求", @@ -709,8 +709,8 @@ "components.Settings.RadarrModal.tags": "標籤", "components.RequestModal.AdvancedRequester.tags": "標籤", "components.Settings.RadarrModal.loadingTags": "載入中…", - "components.RequestList.RequestItem.mediaerror": "找不到此請求的媒體項目。", - "components.RequestCard.mediaerror": "找不到此請求的媒體項目。", + "components.RequestList.RequestItem.mediaerror": "找不到{mediaType}", + "components.RequestCard.mediaerror": "找不到{mediaType}", "components.RequestList.RequestItem.deleterequest": "刪除請求", "components.RequestCard.deleterequest": "刪除請求", "components.Settings.Notifications.botUsernameTip": "允許使用者也把機器人加到自己的聊天室以及設定自己的通知", @@ -1043,5 +1043,18 @@ "i18n.restartRequired": "必須重新啟動伺服器", "components.Settings.deleteServer": "刪除 {serverType} 伺服器", "components.StatusChecker.appUpdatedDescription": "請點擊以下的按鈕刷新頁面。", - "components.StatusChecker.restartRequiredDescription": "請重新啟動伺服器以應用更新的設定。" + "components.StatusChecker.restartRequiredDescription": "請重新啟動伺服器以應用更新的設定。", + "components.MovieDetails.physicalrelease": "實體光碟", + "components.MovieDetails.theatricalrelease": "影院", + "components.MovieDetails.digitalrelease": "數字版本", + "components.PermissionEdit.viewrecent": "查看最近新增", + "components.PermissionEdit.viewrecentDescription": "授予查看最近新增的媒體的權限。", + "components.RequestList.RequestItem.tmdbid": "TMDB ID", + "components.TitleCard.mediaerror": "找不到{mediaType}", + "components.TitleCard.tvdbid": "TheTVDB ID", + "components.RequestCard.tvdbid": "TheTVDB ID", + "components.TitleCard.cleardata": "清除儲存資料", + "components.TitleCard.tmdbid": "TMDB ID", + "components.RequestCard.tmdbid": "TMDB ID", + "components.RequestList.RequestItem.tvdbid": "TheTVDB ID" } From 6ce0aa5b1042cc6af8e6a5d9f585115d643b9eaf Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 21 Aug 2022 01:14:50 +0000 Subject: [PATCH 46/81] docs: add byakurau as a contributor for translation (#2954) [skip ci] * docs: update README.md * docs: update .all-contributorsrc Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ README.md | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index d748344dd..748f6b488 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -719,6 +719,15 @@ "contributions": [ "translation" ] + }, + { + "login": "byakurau", + "name": "byakurau", + "avatar_url": "https://avatars.githubusercontent.com/u/1811683?v=4", + "profile": "https://github.com/byakurau", + "contributions": [ + "translation" + ] } ], "badgeTemplate": "\"All-orange.svg\"/>", diff --git a/README.md b/README.md index fc63af8da..f3a2dccd0 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Language grade: JavaScript GitHub -All Contributors +All Contributors

    @@ -175,6 +175,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
    PovilasID

    🌍 +
    byakurau

    🌍 From 7943e0c339070cf430b76ba57fd4aee82aa6053b Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 21 Aug 2022 01:25:12 +0000 Subject: [PATCH 47/81] docs: add miknii as a contributor for translation (#2955) [skip ci] * docs: update README.md * docs: update .all-contributorsrc Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ README.md | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 748f6b488..3cf5e765c 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -728,6 +728,15 @@ "contributions": [ "translation" ] + }, + { + "login": "miknii", + "name": "miknii", + "avatar_url": "https://avatars.githubusercontent.com/u/109232569?v=4", + "profile": "https://github.com/miknii", + "contributions": [ + "translation" + ] } ], "badgeTemplate": "\"All-orange.svg\"/>", diff --git a/README.md b/README.md index f3a2dccd0..05de02e23 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Language grade: JavaScript GitHub -All Contributors +All Contributors

    @@ -176,6 +176,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
    PovilasID

    🌍
    byakurau

    🌍 +
    miknii

    🌍 From 301f2bf7ab0c5e7c5aef9d78a58d6449df0f55b8 Mon Sep 17 00:00:00 2001 From: Ryan Cohen Date: Sun, 21 Aug 2022 16:33:49 +0900 Subject: [PATCH 48/81] feat: plex watchlist sync integration (#2885) --- cypress/e2e/discover.cy.ts | 37 ++ cypress/e2e/user/auto-request-settings.cy.ts | 74 ++++ cypress/fixtures/watchlist.json | 25 ++ overseerr-api.yml | 40 +++ server/api/plextv.ts | 135 ++++++- server/api/themoviedb/index.ts | 2 +- server/entity/MediaRequest.ts | 334 ++++++++++++++++++ server/entity/UserSettings.ts | 6 + server/interfaces/api/discoverInterfaces.ts | 7 + server/interfaces/api/requestInterfaces.ts | 15 + .../interfaces/api/userSettingsInterfaces.ts | 2 + server/job/schedule.ts | 15 + server/lib/cache.ts | 7 +- server/lib/permissions.ts | 3 + server/lib/settings.ts | 4 + server/lib/watchlistsync.ts | 165 +++++++++ ...60632269368-AddWatchlistSyncUserSetting.ts | 33 ++ ...373-AddMediaRequestIsAutoRequestedField.ts | 33 ++ server/routes/discover.ts | 55 ++- server/routes/request.ts | 322 ++--------------- server/routes/user/usersettings.ts | 8 + server/scripts/prepareTestDb.ts | 8 +- src/components/Common/ListView/index.tsx | 20 +- src/components/Common/SettingsTabs/index.tsx | 2 +- .../Discover/DiscoverWatchlist/index.tsx | 51 +++ src/components/Discover/index.tsx | 39 +- src/components/ManageSlideOver/index.tsx | 10 +- src/components/PermissionEdit/index.tsx | 46 +++ .../Settings/SettingsJobsCache/index.tsx | 1 + src/components/Slider/index.tsx | 4 +- src/components/TitleCard/TmdbTitleCard.tsx | 13 +- src/components/UserList/index.tsx | 5 +- .../UserGeneralSettings/index.tsx | 103 ++++++ src/i18n/locale/en.json | 13 + src/pages/discover/watchlist.tsx | 8 + 35 files changed, 1325 insertions(+), 320 deletions(-) create mode 100644 cypress/e2e/user/auto-request-settings.cy.ts create mode 100644 cypress/fixtures/watchlist.json create mode 100644 server/lib/watchlistsync.ts create mode 100644 server/migration/1660632269368-AddWatchlistSyncUserSetting.ts create mode 100644 server/migration/1660714479373-AddMediaRequestIsAutoRequestedField.ts create mode 100644 src/components/Discover/DiscoverWatchlist/index.tsx create mode 100644 src/pages/discover/watchlist.tsx diff --git a/cypress/e2e/discover.cy.ts b/cypress/e2e/discover.cy.ts index ec4c4afea..cf4f1d6b4 100644 --- a/cypress/e2e/discover.cy.ts +++ b/cypress/e2e/discover.cy.ts @@ -171,4 +171,41 @@ describe('Discover', () => { .find('[data-testid=request-card-title]') .contains('Movie Not Found'); }); + + it('loads plex watchlist', () => { + cy.intercept('/api/v1/discover/watchlist', { fixture: 'watchlist' }).as( + 'getWatchlist' + ); + // Wait for one of the watchlist movies to resolve + cy.intercept('/api/v1/movie/361743').as('getTmdbMovie'); + + cy.visit('/'); + + cy.wait('@getWatchlist'); + + const sliderHeader = cy.contains('.slider-header', 'Plex Watchlist'); + + sliderHeader.scrollIntoView(); + + cy.wait('@getTmdbMovie'); + // Wait a little longer to make sure the movie component reloaded + cy.wait(500); + + sliderHeader + .next('[data-testid=media-slider]') + .find('[data-testid=title-card]') + .first() + .trigger('mouseover') + .find('[data-testid=title-card-title]') + .invoke('text') + .then((text) => { + cy.contains('.slider-header', 'Plex Watchlist') + .next('[data-testid=media-slider]') + .find('[data-testid=title-card]') + .first() + .click() + .click(); + cy.get('[data-testid=media-title]').should('contain', text); + }); + }); }); diff --git a/cypress/e2e/user/auto-request-settings.cy.ts b/cypress/e2e/user/auto-request-settings.cy.ts new file mode 100644 index 000000000..dfc7c672e --- /dev/null +++ b/cypress/e2e/user/auto-request-settings.cy.ts @@ -0,0 +1,74 @@ +const visitUserEditPage = (email: string): void => { + cy.visit('/users'); + + cy.contains('[data-testid=user-list-row]', email).contains('Edit').click(); +}; + +describe('Auto Request Settings', () => { + beforeEach(() => { + cy.login(Cypress.env('ADMIN_EMAIL'), Cypress.env('ADMIN_PASSWORD')); + }); + + it('should not see watchlist sync settings on an account without permissions', () => { + visitUserEditPage(Cypress.env('USER_EMAIL')); + + cy.contains('Auto-Request Movies').should('not.exist'); + cy.contains('Auto-Request Series').should('not.exist'); + }); + + it('should see watchlist sync settings on an admin account', () => { + visitUserEditPage(Cypress.env('ADMIN_EMAIL')); + + cy.contains('Auto-Request Movies').should('exist'); + cy.contains('Auto-Request Series').should('exist'); + }); + + it('should see auto-request settings after being given permission', () => { + visitUserEditPage(Cypress.env('USER_EMAIL')); + + cy.get('[data-testid=settings-nav-desktop').contains('Permissions').click(); + + cy.get('#autorequest').should('not.be.checked').click(); + + cy.intercept('/api/v1/user/*/settings/permissions').as('userPermissions'); + + cy.contains('Save Changes').click(); + + cy.wait('@userPermissions'); + + cy.reload(); + + cy.get('#autorequest').should('be.checked'); + cy.get('#autorequestmovies').should('be.checked'); + cy.get('#autorequesttv').should('be.checked'); + + cy.get('[data-testid=settings-nav-desktop').contains('General').click(); + + cy.contains('Auto-Request Movies').should('exist'); + cy.contains('Auto-Request Series').should('exist'); + + cy.get('#watchlistSyncMovies').should('not.be.checked').click(); + cy.get('#watchlistSyncTv').should('not.be.checked').click(); + + cy.intercept('/api/v1/user/*/settings/main').as('userMain'); + + cy.contains('Save Changes').click(); + + cy.wait('@userMain'); + + cy.reload(); + + cy.get('#watchlistSyncMovies').should('be.checked').click(); + cy.get('#watchlistSyncTv').should('be.checked').click(); + + cy.contains('Save Changes').click(); + + cy.wait('@userMain'); + + cy.get('[data-testid=settings-nav-desktop').contains('Permissions').click(); + + cy.get('#autorequest').should('be.checked').click(); + + cy.contains('Save Changes').click(); + }); +}); diff --git a/cypress/fixtures/watchlist.json b/cypress/fixtures/watchlist.json new file mode 100644 index 000000000..0f80b27b4 --- /dev/null +++ b/cypress/fixtures/watchlist.json @@ -0,0 +1,25 @@ +{ + "page": 1, + "totalPages": 1, + "totalResults": 20, + "results": [ + { + "ratingKey": "5d776be17a53e9001e732ab9", + "title": "Top Gun: Maverick", + "mediaType": "movie", + "tmdbId": 361743 + }, + { + "ratingKey": "5e16338fbc1372003ea68ab3", + "title": "Nope", + "mediaType": "movie", + "tmdbId": 762504 + }, + { + "ratingKey": "5f409b8452f200004161e126", + "title": "Hocus Pocus 2", + "mediaType": "movie", + "tmdbId": 642885 + } + ] +} diff --git a/overseerr-api.yml b/overseerr-api.yml index b6fb50fbb..fccc26bc0 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -4403,6 +4403,46 @@ paths: name: type: string example: Genre Name + /discover/watchlist: + get: + summary: Get the Plex watchlist. + tags: + - search + parameters: + - in: query + name: page + schema: + type: number + example: 1 + default: 1 + responses: + '200': + description: Watchlist data returned + content: + application/json: + schema: + type: object + properties: + page: + type: number + totalPages: + type: number + totalResults: + type: number + results: + type: array + items: + type: object + properties: + tmdbId: + type: number + example: 1 + ratingKey: + type: string + type: + type: string + title: + type: string /request: get: summary: Get all requests diff --git a/server/api/plextv.ts b/server/api/plextv.ts index a90957551..acebcf5d8 100644 --- a/server/api/plextv.ts +++ b/server/api/plextv.ts @@ -1,9 +1,9 @@ -import type { AxiosInstance } from 'axios'; -import axios from 'axios'; import xml2js from 'xml2js'; import type { PlexDevice } from '../interfaces/api/plexInterfaces'; +import cacheManager from '../lib/cache'; import { getSettings } from '../lib/settings'; import logger from '../logger'; +import ExternalAPI from './externalapi'; interface PlexAccountResponse { user: PlexUser; @@ -112,20 +112,54 @@ interface UsersResponse { }; } -class PlexTvAPI { +interface WatchlistResponse { + MediaContainer: { + totalSize: number; + Metadata?: { + ratingKey: string; + }[]; + }; +} + +interface MetadataResponse { + MediaContainer: { + Metadata: { + ratingKey: string; + type: 'movie' | 'show'; + title: string; + Guid: { + id: `imdb://tt${number}` | `tmdb://${number}` | `tvdb://${number}`; + }[]; + }[]; + }; +} + +export interface PlexWatchlistItem { + ratingKey: string; + tmdbId: number; + tvdbId?: number; + type: 'movie' | 'show'; + title: string; +} + +class PlexTvAPI extends ExternalAPI { private authToken: string; - private axios: AxiosInstance; constructor(authToken: string) { + super( + 'https://plex.tv', + {}, + { + headers: { + 'X-Plex-Token': authToken, + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + nodeCache: cacheManager.getCache('plextv').data, + } + ); + this.authToken = authToken; - this.axios = axios.create({ - baseURL: 'https://plex.tv', - headers: { - 'X-Plex-Token': this.authToken, - 'Content-Type': 'application/json', - Accept: 'application/json', - }, - }); } public async getDevices(): Promise { @@ -253,6 +287,83 @@ class PlexTvAPI { )) as UsersResponse; return parsedXml; } + + public async getWatchlist({ + offset = 0, + size = 20, + }: { offset?: number; size?: number } = {}): Promise<{ + offset: number; + size: number; + totalSize: number; + items: PlexWatchlistItem[]; + }> { + try { + const response = await this.axios.get( + '/library/sections/watchlist/all', + { + params: { + 'X-Plex-Container-Start': offset, + 'X-Plex-Container-Size': size, + }, + baseURL: 'https://metadata.provider.plex.tv', + } + ); + + const watchlistDetails = await Promise.all( + (response.data.MediaContainer.Metadata ?? []).map( + async (watchlistItem) => { + const detailedResponse = await this.getRolling( + `/library/metadata/${watchlistItem.ratingKey}`, + { + baseURL: 'https://metadata.provider.plex.tv', + } + ); + + const metadata = detailedResponse.MediaContainer.Metadata[0]; + + const tmdbString = metadata.Guid.find((guid) => + guid.id.startsWith('tmdb') + ); + const tvdbString = metadata.Guid.find((guid) => + guid.id.startsWith('tvdb') + ); + + return { + ratingKey: metadata.ratingKey, + // This should always be set? But I guess it also cannot be? + // We will filter out the 0's afterwards + tmdbId: tmdbString ? Number(tmdbString.id.split('//')[1]) : 0, + tvdbId: tvdbString + ? Number(tvdbString.id.split('//')[1]) + : undefined, + title: metadata.title, + type: metadata.type, + }; + } + ) + ); + + const filteredList = watchlistDetails.filter((detail) => detail.tmdbId); + + return { + offset, + size, + totalSize: response.data.MediaContainer.totalSize, + items: filteredList, + }; + } catch (e) { + logger.error('Failed to retrieve watchlist items', { + label: 'Plex.TV Metadata API', + errorMessage: e.message, + }); + return { + offset, + size, + totalSize: 0, + items: [], + }; + } + } } export default PlexTvAPI; diff --git a/server/api/themoviedb/index.ts b/server/api/themoviedb/index.ts index f150e76c0..d409bb035 100644 --- a/server/api/themoviedb/index.ts +++ b/server/api/themoviedb/index.ts @@ -94,7 +94,7 @@ class TheMovieDb extends ExternalAPI { nodeCache: cacheManager.getCache('tmdb').data, rateLimit: { maxRequests: 20, - maxRPS: 1, + maxRPS: 50, }, } ); diff --git a/server/entity/MediaRequest.ts b/server/entity/MediaRequest.ts index da19d0d4d..0cef4b1b6 100644 --- a/server/entity/MediaRequest.ts +++ b/server/entity/MediaRequest.ts @@ -20,15 +20,346 @@ import TheMovieDb from '../api/themoviedb'; import { ANIME_KEYWORD_ID } from '../api/themoviedb/constants'; import { MediaRequestStatus, MediaStatus, MediaType } from '../constants/media'; import { getRepository } from '../datasource'; +import type { MediaRequestBody } from '../interfaces/api/requestInterfaces'; import notificationManager, { Notification } from '../lib/notifications'; +import { Permission } from '../lib/permissions'; import { getSettings } from '../lib/settings'; import logger from '../logger'; import Media from './Media'; import SeasonRequest from './SeasonRequest'; import { User } from './User'; +export class RequestPermissionError extends Error {} +export class QuotaRestrictedError extends Error {} +export class DuplicateMediaRequestError extends Error {} +export class NoSeasonsAvailableError extends Error {} + +type MediaRequestOptions = { + isAutoRequest?: boolean; +}; + @Entity() export class MediaRequest { + public static async request( + requestBody: MediaRequestBody, + user: User, + options: MediaRequestOptions = {} + ): Promise { + const tmdb = new TheMovieDb(); + const mediaRepository = getRepository(Media); + const requestRepository = getRepository(MediaRequest); + const userRepository = getRepository(User); + + let requestUser = user; + + if ( + requestBody.userId && + !requestUser.hasPermission([ + Permission.MANAGE_USERS, + Permission.MANAGE_REQUESTS, + ]) + ) { + throw new RequestPermissionError( + 'You do not have permission to modify the request user.' + ); + } else if (requestBody.userId) { + requestUser = await userRepository.findOneOrFail({ + where: { id: requestBody.userId }, + }); + } + + if (!requestUser) { + throw new Error('User missing from request context.'); + } + + if ( + requestBody.mediaType === MediaType.MOVIE && + !requestUser.hasPermission( + requestBody.is4k + ? [Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE] + : [Permission.REQUEST, Permission.REQUEST_MOVIE], + { + type: 'or', + } + ) + ) { + throw new RequestPermissionError( + `You do not have permission to make ${ + requestBody.is4k ? '4K ' : '' + }movie requests.` + ); + } else if ( + requestBody.mediaType === MediaType.TV && + !requestUser.hasPermission( + requestBody.is4k + ? [Permission.REQUEST_4K, Permission.REQUEST_4K_TV] + : [Permission.REQUEST, Permission.REQUEST_TV], + { + type: 'or', + } + ) + ) { + throw new RequestPermissionError( + `You do not have permission to make ${ + requestBody.is4k ? '4K ' : '' + }series requests.` + ); + } + + const quotas = await requestUser.getQuota(); + + if (requestBody.mediaType === MediaType.MOVIE && quotas.movie.restricted) { + throw new QuotaRestrictedError('Movie Quota exceeded.'); + } else if (requestBody.mediaType === MediaType.TV && quotas.tv.restricted) { + throw new QuotaRestrictedError('Series Quota exceeded.'); + } + + const tmdbMedia = + requestBody.mediaType === MediaType.MOVIE + ? await tmdb.getMovie({ movieId: requestBody.mediaId }) + : await tmdb.getTvShow({ tvId: requestBody.mediaId }); + + let media = await mediaRepository.findOne({ + where: { + tmdbId: requestBody.mediaId, + mediaType: requestBody.mediaType, + }, + relations: ['requests'], + }); + + if (!media) { + media = new Media({ + tmdbId: tmdbMedia.id, + tvdbId: requestBody.tvdbId ?? tmdbMedia.external_ids.tvdb_id, + status: !requestBody.is4k ? MediaStatus.PENDING : MediaStatus.UNKNOWN, + status4k: requestBody.is4k ? MediaStatus.PENDING : MediaStatus.UNKNOWN, + mediaType: requestBody.mediaType, + }); + } else { + if (media.status === MediaStatus.UNKNOWN && !requestBody.is4k) { + media.status = MediaStatus.PENDING; + } + + if (media.status4k === MediaStatus.UNKNOWN && requestBody.is4k) { + media.status4k = MediaStatus.PENDING; + } + } + + const existing = await requestRepository + .createQueryBuilder('request') + .leftJoin('request.media', 'media') + .leftJoinAndSelect('request.requestedBy', 'user') + .where('request.is4k = :is4k', { is4k: requestBody.is4k }) + .andWhere('media.tmdbId = :tmdbId', { tmdbId: tmdbMedia.id }) + .andWhere('media.mediaType = :mediaType', { + mediaType: requestBody.mediaType, + }) + .getMany(); + + if (existing && existing.length > 0) { + // If there is an existing movie request that isn't declined, don't allow a new one. + if ( + requestBody.mediaType === MediaType.MOVIE && + existing[0].status !== MediaRequestStatus.DECLINED + ) { + logger.warn('Duplicate request for media blocked', { + tmdbId: tmdbMedia.id, + mediaType: requestBody.mediaType, + is4k: requestBody.is4k, + label: 'Media Request', + }); + + throw new DuplicateMediaRequestError( + 'Request for this media already exists.' + ); + } + + // If an existing auto-request for this media exists from the same user, + // don't allow a new one. + if ( + existing.find( + (r) => r.requestedBy.id === requestUser.id && r.isAutoRequest + ) + ) { + throw new DuplicateMediaRequestError( + 'Auto-request for this media and user already exists.' + ); + } + } + + if (requestBody.mediaType === MediaType.MOVIE) { + await mediaRepository.save(media); + + const request = new MediaRequest({ + type: MediaType.MOVIE, + media, + requestedBy: requestUser, + // If the user is an admin or has the "auto approve" permission, automatically approve the request + status: user.hasPermission( + [ + requestBody.is4k + ? Permission.AUTO_APPROVE_4K + : Permission.AUTO_APPROVE, + requestBody.is4k + ? Permission.AUTO_APPROVE_4K_MOVIE + : Permission.AUTO_APPROVE_MOVIE, + Permission.MANAGE_REQUESTS, + ], + { type: 'or' } + ) + ? MediaRequestStatus.APPROVED + : MediaRequestStatus.PENDING, + modifiedBy: user.hasPermission( + [ + requestBody.is4k + ? Permission.AUTO_APPROVE_4K + : Permission.AUTO_APPROVE, + requestBody.is4k + ? Permission.AUTO_APPROVE_4K_MOVIE + : Permission.AUTO_APPROVE_MOVIE, + Permission.MANAGE_REQUESTS, + ], + { type: 'or' } + ) + ? user + : undefined, + is4k: requestBody.is4k, + serverId: requestBody.serverId, + profileId: requestBody.profileId, + rootFolder: requestBody.rootFolder, + tags: requestBody.tags, + isAutoRequest: options.isAutoRequest ?? false, + }); + + await requestRepository.save(request); + return request; + } else { + const tmdbMediaShow = tmdbMedia as Awaited< + ReturnType + >; + const requestedSeasons = + requestBody.seasons === 'all' + ? tmdbMediaShow.seasons + .map((season) => season.season_number) + .filter((sn) => sn > 0) + : (requestBody.seasons as number[]); + let existingSeasons: number[] = []; + + // We need to check existing requests on this title to make sure we don't double up on seasons that were + // already requested. In the case they were, we just throw out any duplicates but still approve the request. + // (Unless there are no seasons, in which case we abort) + if (media.requests) { + existingSeasons = media.requests + .filter( + (request) => + request.is4k === requestBody.is4k && + request.status !== MediaRequestStatus.DECLINED + ) + .reduce((seasons, request) => { + const combinedSeasons = request.seasons.map( + (season) => season.seasonNumber + ); + + return [...seasons, ...combinedSeasons]; + }, [] as number[]); + } + + // We should also check seasons that are available/partially available but don't have existing requests + if (media.seasons) { + existingSeasons = [ + ...existingSeasons, + ...media.seasons + .filter( + (season) => + season[requestBody.is4k ? 'status4k' : 'status'] !== + MediaStatus.UNKNOWN + ) + .map((season) => season.seasonNumber), + ]; + } + + const finalSeasons = requestedSeasons.filter( + (rs) => !existingSeasons.includes(rs) + ); + + if (finalSeasons.length === 0) { + throw new NoSeasonsAvailableError('No seasons available to request'); + } else if ( + quotas.tv.limit && + finalSeasons.length > (quotas.tv.remaining ?? 0) + ) { + throw new QuotaRestrictedError('Series Quota exceeded.'); + } + + await mediaRepository.save(media); + + const request = new MediaRequest({ + type: MediaType.TV, + media, + requestedBy: requestUser, + // If the user is an admin or has the "auto approve" permission, automatically approve the request + status: user.hasPermission( + [ + requestBody.is4k + ? Permission.AUTO_APPROVE_4K + : Permission.AUTO_APPROVE, + requestBody.is4k + ? Permission.AUTO_APPROVE_4K_TV + : Permission.AUTO_APPROVE_TV, + Permission.MANAGE_REQUESTS, + ], + { type: 'or' } + ) + ? MediaRequestStatus.APPROVED + : MediaRequestStatus.PENDING, + modifiedBy: user.hasPermission( + [ + requestBody.is4k + ? Permission.AUTO_APPROVE_4K + : Permission.AUTO_APPROVE, + requestBody.is4k + ? Permission.AUTO_APPROVE_4K_TV + : Permission.AUTO_APPROVE_TV, + Permission.MANAGE_REQUESTS, + ], + { type: 'or' } + ) + ? user + : undefined, + is4k: requestBody.is4k, + serverId: requestBody.serverId, + profileId: requestBody.profileId, + rootFolder: requestBody.rootFolder, + languageProfileId: requestBody.languageProfileId, + tags: requestBody.tags, + seasons: finalSeasons.map( + (sn) => + new SeasonRequest({ + seasonNumber: sn, + status: user.hasPermission( + [ + requestBody.is4k + ? Permission.AUTO_APPROVE_4K + : Permission.AUTO_APPROVE, + requestBody.is4k + ? Permission.AUTO_APPROVE_4K_TV + : Permission.AUTO_APPROVE_TV, + Permission.MANAGE_REQUESTS, + ], + { type: 'or' } + ) + ? MediaRequestStatus.APPROVED + : MediaRequestStatus.PENDING, + }) + ), + isAutoRequest: options.isAutoRequest ?? false, + }); + + await requestRepository.save(request); + return request; + } + } + @PrimaryGeneratedColumn() public id: number; @@ -119,6 +450,9 @@ export class MediaRequest { }) public tags?: number[]; + @Column({ default: false }) + public isAutoRequest: boolean; + constructor(init?: Partial) { Object.assign(this, init); } diff --git a/server/entity/UserSettings.ts b/server/entity/UserSettings.ts index fb738c59c..6def14f41 100644 --- a/server/entity/UserSettings.ts +++ b/server/entity/UserSettings.ts @@ -57,6 +57,12 @@ export class UserSettings { @Column({ nullable: true }) public telegramSendSilently?: boolean; + @Column({ nullable: true }) + public watchlistSyncMovies?: boolean; + + @Column({ nullable: true }) + public watchlistSyncTv?: boolean; + @Column({ type: 'text', nullable: true, diff --git a/server/interfaces/api/discoverInterfaces.ts b/server/interfaces/api/discoverInterfaces.ts index db90e55d2..20bdc494a 100644 --- a/server/interfaces/api/discoverInterfaces.ts +++ b/server/interfaces/api/discoverInterfaces.ts @@ -3,3 +3,10 @@ export interface GenreSliderItem { name: string; backdrops: string[]; } + +export interface WatchlistItem { + ratingKey: string; + tmdbId: number; + mediaType: 'movie' | 'tv'; + title: string; +} diff --git a/server/interfaces/api/requestInterfaces.ts b/server/interfaces/api/requestInterfaces.ts index ca39515bd..f4b0ab8c2 100644 --- a/server/interfaces/api/requestInterfaces.ts +++ b/server/interfaces/api/requestInterfaces.ts @@ -1,6 +1,21 @@ import type { PaginatedResponse } from './common'; import type { MediaRequest } from '../../entity/MediaRequest'; +import type { MediaType } from '../../constants/media'; export interface RequestResultsResponse extends PaginatedResponse { results: MediaRequest[]; } + +export type MediaRequestBody = { + mediaType: MediaType; + mediaId: number; + tvdbId?: number; + seasons?: number[] | 'all'; + is4k?: boolean; + serverId?: number; + profileId?: number; + rootFolder?: string; + languageProfileId?: number; + userId?: number; + tags?: number[]; +}; diff --git a/server/interfaces/api/userSettingsInterfaces.ts b/server/interfaces/api/userSettingsInterfaces.ts index de7888b24..eb7fe0f99 100644 --- a/server/interfaces/api/userSettingsInterfaces.ts +++ b/server/interfaces/api/userSettingsInterfaces.ts @@ -14,6 +14,8 @@ export interface UserSettingsGeneralResponse { globalMovieQuotaLimit?: number; globalTvQuotaLimit?: number; globalTvQuotaDays?: number; + watchlistSyncMovies?: boolean; + watchlistSyncTv?: boolean; } export type NotificationAgentTypes = Record; diff --git a/server/job/schedule.ts b/server/job/schedule.ts index 9697a00cc..949913c08 100644 --- a/server/job/schedule.ts +++ b/server/job/schedule.ts @@ -5,6 +5,7 @@ import { radarrScanner } from '../lib/scanners/radarr'; import { sonarrScanner } from '../lib/scanners/sonarr'; import type { JobId } from '../lib/settings'; import { getSettings } from '../lib/settings'; +import watchlistSync from '../lib/watchlistsync'; import logger from '../logger'; interface ScheduledJob { @@ -54,6 +55,20 @@ export const startJobs = (): void => { cancelFn: () => plexFullScanner.cancel(), }); + // Run watchlist sync every 5 minutes + scheduledJobs.push({ + id: 'plex-watchlist-sync', + name: 'Plex Watchlist Sync', + type: 'process', + interval: 'long', + job: schedule.scheduleJob(jobs['plex-watchlist-sync'].schedule, () => { + logger.info('Starting scheduled job: Plex Watchlist Sync', { + label: 'Jobs', + }); + watchlistSync.syncWatchlist(); + }), + }); + // Run full radarr scan every 24 hours scheduledJobs.push({ id: 'radarr-scan', diff --git a/server/lib/cache.ts b/server/lib/cache.ts index 7782a05a8..e81466629 100644 --- a/server/lib/cache.ts +++ b/server/lib/cache.ts @@ -6,7 +6,8 @@ export type AvailableCacheIds = | 'sonarr' | 'rt' | 'github' - | 'plexguid'; + | 'plexguid' + | 'plextv'; const DEFAULT_TTL = 300; const DEFAULT_CHECK_PERIOD = 120; @@ -58,6 +59,10 @@ class CacheManager { stdTtl: 86400 * 7, // 1 week cache checkPeriod: 60 * 30, }), + plextv: new Cache('plextv', 'Plex TV', { + stdTtl: 86400 * 7, // 1 week cache + checkPeriod: 60, + }), }; public getCache(id: AvailableCacheIds): Cache { diff --git a/server/lib/permissions.ts b/server/lib/permissions.ts index ce14f7a1a..5c85c6c91 100644 --- a/server/lib/permissions.ts +++ b/server/lib/permissions.ts @@ -21,6 +21,9 @@ export enum Permission { MANAGE_ISSUES = 1048576, VIEW_ISSUES = 2097152, CREATE_ISSUES = 4194304, + AUTO_REQUEST = 8388608, + AUTO_REQUEST_MOVIE = 16777216, + AUTO_REQUEST_TV = 33554432, RECENT_VIEW = 67108864, } diff --git a/server/lib/settings.ts b/server/lib/settings.ts index 7a4f5f93d..5a2d2b8aa 100644 --- a/server/lib/settings.ts +++ b/server/lib/settings.ts @@ -243,6 +243,7 @@ interface JobSettings { export type JobId = | 'plex-recently-added-scan' | 'plex-full-scan' + | 'plex-watchlist-sync' | 'radarr-scan' | 'sonarr-scan' | 'download-sync' @@ -398,6 +399,9 @@ class Settings { 'plex-full-scan': { schedule: '0 0 3 * * *', }, + 'plex-watchlist-sync': { + schedule: '0 */10 * * * *', + }, 'radarr-scan': { schedule: '0 0 4 * * *', }, diff --git a/server/lib/watchlistsync.ts b/server/lib/watchlistsync.ts new file mode 100644 index 000000000..2a4d611dd --- /dev/null +++ b/server/lib/watchlistsync.ts @@ -0,0 +1,165 @@ +import { Not } from 'typeorm'; +import PlexTvAPI from '../api/plextv'; +import { User } from '../entity/User'; +import Media from '../entity/Media'; +import logger from '../logger'; +import { MediaType } from '../constants/media'; +import { MediaStatus } from '../constants/media'; +import { + DuplicateMediaRequestError, + MediaRequest, + NoSeasonsAvailableError, + QuotaRestrictedError, + RequestPermissionError, +} from '../entity/MediaRequest'; +import { Permission } from './permissions'; +import { getRepository } from '../datasource'; + +class WatchlistSync { + public async syncWatchlist() { + const userRepository = getRepository(User); + + // Get users who actually have plex tokens + const users = await userRepository.find({ + select: { id: true, plexToken: true, permissions: true }, + where: { + plexToken: Not(''), + }, + }); + + for (const user of users) { + await this.syncUserWatchlist(user); + } + } + + private async syncUserWatchlist(user: User) { + if (!user.plexToken) { + logger.warn('Skipping user watchlist sync for user without plex token', { + label: 'Plex Watchlist Sync', + userId: user.id, + }); + return; + } + + if ( + !user.hasPermission( + [ + Permission.AUTO_REQUEST, + Permission.AUTO_REQUEST_MOVIE, + Permission.AUTO_APPROVE_TV, + ], + { type: 'or' } + ) + ) { + return; + } + + if ( + !user.settings?.watchlistSyncMovies && + !user.settings?.watchlistSyncTv + ) { + // Skip sync if user settings have it disabled + return; + } + + const plexTvApi = new PlexTvAPI(user.plexToken); + + const response = await plexTvApi.getWatchlist({ size: 200 }); + + const mediaItems = await Media.getRelatedMedia( + response.items.map((i) => i.tmdbId) + ); + + const unavailableItems = response.items.filter( + // If we can find watchlist items in our database that are also available, we should exclude them + (i) => + !mediaItems.find( + (m) => + m.tmdbId === i.tmdbId && + ((m.status !== MediaStatus.UNKNOWN && m.mediaType === 'movie') || + (m.mediaType === 'tv' && m.status === MediaStatus.AVAILABLE)) + ) + ); + + await Promise.all( + unavailableItems.map(async (mediaItem) => { + try { + logger.info("Creating media request from user's Plex Watchlist", { + label: 'Watchlist Sync', + userId: user.id, + mediaTitle: mediaItem.title, + }); + + if (mediaItem.type === 'show' && !mediaItem.tvdbId) { + throw new Error('Missing TVDB ID from Plex Metadata'); + } + + // Check if they have auto-request permissons and watchlist sync + // enabled for the media type + if ( + ((!user.hasPermission( + [Permission.AUTO_REQUEST, Permission.AUTO_REQUEST_MOVIE], + { type: 'or' } + ) || + !user.settings?.watchlistSyncMovies) && + mediaItem.type === 'movie') || + ((!user.hasPermission( + [Permission.AUTO_REQUEST, Permission.AUTO_REQUEST_TV], + { type: 'or' } + ) || + !user.settings?.watchlistSyncTv) && + mediaItem.type === 'show') + ) { + return; + } + + await MediaRequest.request( + { + mediaId: mediaItem.tmdbId, + mediaType: + mediaItem.type === 'show' ? MediaType.TV : MediaType.MOVIE, + seasons: mediaItem.type === 'show' ? 'all' : undefined, + tvdbId: mediaItem.tvdbId, + is4k: false, + }, + user, + { isAutoRequest: true } + ); + } catch (e) { + if (!(e instanceof Error)) { + return; + } + + switch (e.constructor) { + // During watchlist sync, these errors aren't necessarily + // a problem with Overseerr. Since we are auto syncing these constantly, it's + // possible they are unexpectedly at their quota limit, for example. So we'll + // instead log these as debug messages. + case RequestPermissionError: + case DuplicateMediaRequestError: + case QuotaRestrictedError: + case NoSeasonsAvailableError: + logger.debug('Failed to create media request from watchlist', { + label: 'Watchlist Sync', + userId: user.id, + mediaTitle: mediaItem.title, + errorMessage: e.message, + }); + break; + default: + logger.error('Failed to create media request from watchlist', { + label: 'Watchlist Sync', + userId: user.id, + mediaTitle: mediaItem.title, + errorMessage: e.message, + }); + } + } + }) + ); + } +} + +const watchlistSync = new WatchlistSync(); + +export default watchlistSync; diff --git a/server/migration/1660632269368-AddWatchlistSyncUserSetting.ts b/server/migration/1660632269368-AddWatchlistSyncUserSetting.ts new file mode 100644 index 000000000..c0d0e947f --- /dev/null +++ b/server/migration/1660632269368-AddWatchlistSyncUserSetting.ts @@ -0,0 +1,33 @@ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddWatchlistSyncUserSetting1660632269368 + implements MigrationInterface +{ + name = 'AddWatchlistSyncUserSetting1660632269368'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "temporary_user_settings" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "notificationTypes" text, "discordId" varchar, "userId" integer, "region" varchar, "originalLanguage" varchar, "telegramChatId" varchar, "telegramSendSilently" boolean, "pgpKey" varchar, "locale" varchar NOT NULL DEFAULT (''), "pushbulletAccessToken" varchar, "pushoverApplicationToken" varchar, "pushoverUserKey" varchar, "watchlistSyncMovies" boolean, "watchlistSyncTv" boolean, CONSTRAINT "UQ_986a2b6d3c05eb4091bb8066f78" UNIQUE ("userId"), CONSTRAINT "FK_986a2b6d3c05eb4091bb8066f78" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "temporary_user_settings"("id", "notificationTypes", "discordId", "userId", "region", "originalLanguage", "telegramChatId", "telegramSendSilently", "pgpKey", "locale", "pushbulletAccessToken", "pushoverApplicationToken", "pushoverUserKey") SELECT "id", "notificationTypes", "discordId", "userId", "region", "originalLanguage", "telegramChatId", "telegramSendSilently", "pgpKey", "locale", "pushbulletAccessToken", "pushoverApplicationToken", "pushoverUserKey" FROM "user_settings"` + ); + await queryRunner.query(`DROP TABLE "user_settings"`); + await queryRunner.query( + `ALTER TABLE "temporary_user_settings" RENAME TO "user_settings"` + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "user_settings" RENAME TO "temporary_user_settings"` + ); + await queryRunner.query( + `CREATE TABLE "user_settings" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "notificationTypes" text, "discordId" varchar, "userId" integer, "region" varchar, "originalLanguage" varchar, "telegramChatId" varchar, "telegramSendSilently" boolean, "pgpKey" varchar, "locale" varchar NOT NULL DEFAULT (''), "pushbulletAccessToken" varchar, "pushoverApplicationToken" varchar, "pushoverUserKey" varchar, CONSTRAINT "UQ_986a2b6d3c05eb4091bb8066f78" UNIQUE ("userId"), CONSTRAINT "FK_986a2b6d3c05eb4091bb8066f78" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "user_settings"("id", "notificationTypes", "discordId", "userId", "region", "originalLanguage", "telegramChatId", "telegramSendSilently", "pgpKey", "locale", "pushbulletAccessToken", "pushoverApplicationToken", "pushoverUserKey") SELECT "id", "notificationTypes", "discordId", "userId", "region", "originalLanguage", "telegramChatId", "telegramSendSilently", "pgpKey", "locale", "pushbulletAccessToken", "pushoverApplicationToken", "pushoverUserKey" FROM "temporary_user_settings"` + ); + await queryRunner.query(`DROP TABLE "temporary_user_settings"`); + } +} diff --git a/server/migration/1660714479373-AddMediaRequestIsAutoRequestedField.ts b/server/migration/1660714479373-AddMediaRequestIsAutoRequestedField.ts new file mode 100644 index 000000000..8580bb4ed --- /dev/null +++ b/server/migration/1660714479373-AddMediaRequestIsAutoRequestedField.ts @@ -0,0 +1,33 @@ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddMediaRequestIsAutoRequestedField1660714479373 + implements MigrationInterface +{ + name = 'AddMediaRequestIsAutoRequestedField1660714479373'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "temporary_media_request" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "status" integer NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "type" varchar NOT NULL, "mediaId" integer, "requestedById" integer, "modifiedById" integer, "is4k" boolean NOT NULL DEFAULT (0), "serverId" integer, "profileId" integer, "rootFolder" varchar, "languageProfileId" integer, "tags" text, "isAutoRequest" boolean NOT NULL DEFAULT (0), CONSTRAINT "FK_a1aa713f41c99e9d10c48da75a0" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_6997bee94720f1ecb7f31137095" FOREIGN KEY ("requestedById") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_f4fc4efa14c3ba2b29c4525fa15" FOREIGN KEY ("modifiedById") REFERENCES "user" ("id") ON DELETE SET NULL ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "temporary_media_request"("id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById", "is4k", "serverId", "profileId", "rootFolder", "languageProfileId", "tags") SELECT "id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById", "is4k", "serverId", "profileId", "rootFolder", "languageProfileId", "tags" FROM "media_request"` + ); + await queryRunner.query(`DROP TABLE "media_request"`); + await queryRunner.query( + `ALTER TABLE "temporary_media_request" RENAME TO "media_request"` + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "media_request" RENAME TO "temporary_media_request"` + ); + await queryRunner.query( + `CREATE TABLE "media_request" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "status" integer NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "type" varchar NOT NULL, "mediaId" integer, "requestedById" integer, "modifiedById" integer, "is4k" boolean NOT NULL DEFAULT (0), "serverId" integer, "profileId" integer, "rootFolder" varchar, "languageProfileId" integer, "tags" text, CONSTRAINT "FK_a1aa713f41c99e9d10c48da75a0" FOREIGN KEY ("mediaId") REFERENCES "media" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_6997bee94720f1ecb7f31137095" FOREIGN KEY ("requestedById") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_f4fc4efa14c3ba2b29c4525fa15" FOREIGN KEY ("modifiedById") REFERENCES "user" ("id") ON DELETE SET NULL ON UPDATE NO ACTION)` + ); + await queryRunner.query( + `INSERT INTO "media_request"("id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById", "is4k", "serverId", "profileId", "rootFolder", "languageProfileId", "tags") SELECT "id", "status", "createdAt", "updatedAt", "type", "mediaId", "requestedById", "modifiedById", "is4k", "serverId", "profileId", "rootFolder", "languageProfileId", "tags" FROM "temporary_media_request"` + ); + await queryRunner.query(`DROP TABLE "temporary_media_request"`); + } +} diff --git a/server/routes/discover.ts b/server/routes/discover.ts index e0b8f78ed..d12a9e971 100644 --- a/server/routes/discover.ts +++ b/server/routes/discover.ts @@ -1,10 +1,15 @@ import { Router } from 'express'; import { sortBy } from 'lodash'; +import PlexTvAPI from '../api/plextv'; import TheMovieDb from '../api/themoviedb'; import { MediaType } from '../constants/media'; +import { getRepository } from '../datasource'; import Media from '../entity/Media'; -import type { User } from '../entity/User'; -import type { GenreSliderItem } from '../interfaces/api/discoverInterfaces'; +import { User } from '../entity/User'; +import type { + GenreSliderItem, + WatchlistItem, +} from '../interfaces/api/discoverInterfaces'; import { getSettings } from '../lib/settings'; import logger from '../logger'; import { mapProductionCompany } from '../models/Movie'; @@ -704,4 +709,50 @@ discoverRoutes.get<{ language: string }, GenreSliderItem[]>( } ); +discoverRoutes.get< + { page?: number }, + { + page: number; + totalPages: number; + totalResults: number; + results: WatchlistItem[]; + } +>('/watchlist', async (req, res) => { + const userRepository = getRepository(User); + const itemsPerPage = 20; + const page = req.params.page ?? 1; + const offset = (page - 1) * itemsPerPage; + + const activeUser = await userRepository.findOne({ + where: { id: req.user?.id }, + select: ['id', 'plexToken'], + }); + + if (!activeUser?.plexToken) { + // We will just return an empty array if the user has no plex token + return res.json({ + page: 1, + totalPages: 1, + totalResults: 0, + results: [], + }); + } + + const plexTV = new PlexTvAPI(activeUser?.plexToken); + + const watchlist = await plexTV.getWatchlist({ offset }); + + return res.json({ + page, + totalPages: Math.ceil(watchlist.size / itemsPerPage), + totalResults: watchlist.size, + results: watchlist.items.map((item) => ({ + ratingKey: item.ratingKey, + title: item.title, + mediaType: item.type === 'show' ? 'tv' : 'movie', + tmdbId: item.tmdbId, + })), + }); +}); + export default discoverRoutes; diff --git a/server/routes/request.ts b/server/routes/request.ts index 68d252547..71657cdb8 100644 --- a/server/routes/request.ts +++ b/server/routes/request.ts @@ -1,12 +1,20 @@ import { Router } from 'express'; -import TheMovieDb from '../api/themoviedb'; import { MediaRequestStatus, MediaStatus, MediaType } from '../constants/media'; import { getRepository } from '../datasource'; import Media from '../entity/Media'; -import { MediaRequest } from '../entity/MediaRequest'; +import { + DuplicateMediaRequestError, + MediaRequest, + NoSeasonsAvailableError, + QuotaRestrictedError, + RequestPermissionError, +} from '../entity/MediaRequest'; import SeasonRequest from '../entity/SeasonRequest'; import { User } from '../entity/User'; -import type { RequestResultsResponse } from '../interfaces/api/requestInterfaces'; +import type { + MediaRequestBody, + RequestResultsResponse, +} from '../interfaces/api/requestInterfaces'; import { Permission } from '../lib/permissions'; import logger from '../logger'; import { isAuthenticated } from '../middleware/auth'; @@ -146,302 +154,38 @@ requestRoutes.get, RequestResultsResponse>( } ); -requestRoutes.post('/', async (req, res, next) => { - const tmdb = new TheMovieDb(); - const mediaRepository = getRepository(Media); - const requestRepository = getRepository(MediaRequest); - const userRepository = getRepository(User); - - try { - let requestUser = req.user; - - if ( - req.body.userId && - !req.user?.hasPermission([ - Permission.MANAGE_USERS, - Permission.MANAGE_REQUESTS, - ]) - ) { - return next({ - status: 403, - message: 'You do not have permission to modify the request user.', - }); - } else if (req.body.userId) { - requestUser = await userRepository.findOneOrFail({ - where: { id: req.body.userId }, - }); - } - - if (!requestUser) { - return next({ - status: 500, - message: 'User missing from request context.', - }); - } - - if ( - req.body.mediaType === MediaType.MOVIE && - !req.user?.hasPermission( - req.body.is4k - ? [Permission.REQUEST_4K, Permission.REQUEST_4K_MOVIE] - : [Permission.REQUEST, Permission.REQUEST_MOVIE], - { - type: 'or', - } - ) - ) { - return next({ - status: 403, - message: `You do not have permission to make ${ - req.body.is4k ? '4K ' : '' - }movie requests.`, - }); - } else if ( - req.body.mediaType === MediaType.TV && - !req.user?.hasPermission( - req.body.is4k - ? [Permission.REQUEST_4K, Permission.REQUEST_4K_TV] - : [Permission.REQUEST, Permission.REQUEST_TV], - { - type: 'or', - } - ) - ) { - return next({ - status: 403, - message: `You do not have permission to make ${ - req.body.is4k ? '4K ' : '' - }series requests.`, - }); - } - - const quotas = await requestUser.getQuota(); - - if (req.body.mediaType === MediaType.MOVIE && quotas.movie.restricted) { - return next({ - status: 403, - message: 'Movie Quota Exceeded', - }); - } else if (req.body.mediaType === MediaType.TV && quotas.tv.restricted) { - return next({ - status: 403, - message: 'Series Quota Exceeded', - }); - } - - const tmdbMedia = - req.body.mediaType === MediaType.MOVIE - ? await tmdb.getMovie({ movieId: req.body.mediaId }) - : await tmdb.getTvShow({ tvId: req.body.mediaId }); - - let media = await mediaRepository.findOne({ - where: { tmdbId: req.body.mediaId, mediaType: req.body.mediaType }, - relations: { requests: true }, - }); - - if (!media) { - media = new Media({ - tmdbId: tmdbMedia.id, - tvdbId: req.body.tvdbId ?? tmdbMedia.external_ids.tvdb_id, - status: !req.body.is4k ? MediaStatus.PENDING : MediaStatus.UNKNOWN, - status4k: req.body.is4k ? MediaStatus.PENDING : MediaStatus.UNKNOWN, - mediaType: req.body.mediaType, - }); - } else { - if (media.status === MediaStatus.UNKNOWN && !req.body.is4k) { - media.status = MediaStatus.PENDING; - } - - if (media.status4k === MediaStatus.UNKNOWN && req.body.is4k) { - media.status4k = MediaStatus.PENDING; - } - } - - if (req.body.mediaType === MediaType.MOVIE) { - const existing = await requestRepository - .createQueryBuilder('request') - .leftJoin('request.media', 'media') - .where('request.is4k = :is4k', { is4k: req.body.is4k }) - .andWhere('media.tmdbId = :tmdbId', { tmdbId: tmdbMedia.id }) - .andWhere('media.mediaType = :mediaType', { - mediaType: MediaType.MOVIE, - }) - .andWhere('request.status != :requestStatus', { - requestStatus: MediaRequestStatus.DECLINED, - }) - .getOne(); - - if (existing) { - logger.warn('Duplicate request for media blocked', { - tmdbId: tmdbMedia.id, - mediaType: req.body.mediaType, - is4k: req.body.is4k, - label: 'Media Request', - }); +requestRoutes.post( + '/', + async (req, res, next) => { + try { + if (!req.user) { return next({ - status: 409, - message: 'Request for this media already exists.', + status: 401, + message: 'You must be logged in to request media.', }); } + const request = await MediaRequest.request(req.body, req.user); - await mediaRepository.save(media); - - const request = new MediaRequest({ - type: MediaType.MOVIE, - media, - requestedBy: requestUser, - // If the user is an admin or has the "auto approve" permission, automatically approve the request - status: req.user?.hasPermission( - [ - req.body.is4k - ? Permission.AUTO_APPROVE_4K - : Permission.AUTO_APPROVE, - req.body.is4k - ? Permission.AUTO_APPROVE_4K_MOVIE - : Permission.AUTO_APPROVE_MOVIE, - Permission.MANAGE_REQUESTS, - ], - { type: 'or' } - ) - ? MediaRequestStatus.APPROVED - : MediaRequestStatus.PENDING, - modifiedBy: req.user?.hasPermission( - [ - req.body.is4k - ? Permission.AUTO_APPROVE_4K - : Permission.AUTO_APPROVE, - req.body.is4k - ? Permission.AUTO_APPROVE_4K_MOVIE - : Permission.AUTO_APPROVE_MOVIE, - Permission.MANAGE_REQUESTS, - ], - { type: 'or' } - ) - ? req.user - : undefined, - is4k: req.body.is4k, - serverId: req.body.serverId, - profileId: req.body.profileId, - rootFolder: req.body.rootFolder, - tags: req.body.tags, - }); - - await requestRepository.save(request); return res.status(201).json(request); - } else if (req.body.mediaType === MediaType.TV) { - const requestedSeasons = req.body.seasons as number[]; - let existingSeasons: number[] = []; - - // We need to check existing requests on this title to make sure we don't double up on seasons that were - // already requested. In the case they were, we just throw out any duplicates but still approve the request. - // (Unless there are no seasons, in which case we abort) - if (media.requests) { - existingSeasons = media.requests - .filter( - (request) => - request.is4k === req.body.is4k && - request.status !== MediaRequestStatus.DECLINED - ) - .reduce((seasons, request) => { - const combinedSeasons = request.seasons.map( - (season) => season.seasonNumber - ); - - return [...seasons, ...combinedSeasons]; - }, [] as number[]); + } catch (error) { + if (!(error instanceof Error)) { + return; } - const finalSeasons = requestedSeasons.filter( - (rs) => !existingSeasons.includes(rs) - ); - - if (finalSeasons.length === 0) { - return next({ - status: 202, - message: 'No seasons available to request', - }); - } else if ( - quotas.tv.limit && - finalSeasons.length > (quotas.tv.remaining ?? 0) - ) { - return next({ - status: 403, - message: 'Series Quota Exceeded', - }); + switch (error.constructor) { + case RequestPermissionError: + case QuotaRestrictedError: + return next({ status: 403, message: error.message }); + case DuplicateMediaRequestError: + return next({ status: 409, message: error.message }); + case NoSeasonsAvailableError: + return next({ status: 202, message: error.message }); + default: + return next({ status: 500, message: error.message }); } - - await mediaRepository.save(media); - - const request = new MediaRequest({ - type: MediaType.TV, - media, - requestedBy: requestUser, - // If the user is an admin or has the "auto approve" permission, automatically approve the request - status: req.user?.hasPermission( - [ - req.body.is4k - ? Permission.AUTO_APPROVE_4K - : Permission.AUTO_APPROVE, - req.body.is4k - ? Permission.AUTO_APPROVE_4K_TV - : Permission.AUTO_APPROVE_TV, - Permission.MANAGE_REQUESTS, - ], - { type: 'or' } - ) - ? MediaRequestStatus.APPROVED - : MediaRequestStatus.PENDING, - modifiedBy: req.user?.hasPermission( - [ - req.body.is4k - ? Permission.AUTO_APPROVE_4K - : Permission.AUTO_APPROVE, - req.body.is4k - ? Permission.AUTO_APPROVE_4K_TV - : Permission.AUTO_APPROVE_TV, - Permission.MANAGE_REQUESTS, - ], - { type: 'or' } - ) - ? req.user - : undefined, - is4k: req.body.is4k, - serverId: req.body.serverId, - profileId: req.body.profileId, - rootFolder: req.body.rootFolder, - languageProfileId: req.body.languageProfileId, - tags: req.body.tags, - seasons: finalSeasons.map( - (sn) => - new SeasonRequest({ - seasonNumber: sn, - status: req.user?.hasPermission( - [ - req.body.is4k - ? Permission.AUTO_APPROVE_4K - : Permission.AUTO_APPROVE, - req.body.is4k - ? Permission.AUTO_APPROVE_4K_TV - : Permission.AUTO_APPROVE_TV, - Permission.MANAGE_REQUESTS, - ], - { type: 'or' } - ) - ? MediaRequestStatus.APPROVED - : MediaRequestStatus.PENDING, - }) - ), - }); - - await requestRepository.save(request); - return res.status(201).json(request); } - - next({ status: 500, message: 'Invalid media type' }); - } catch (e) { - next({ status: 500, message: e.message }); } -}); +); requestRoutes.get('/count', async (_req, res, next) => { const requestRepository = getRepository(MediaRequest); diff --git a/server/routes/user/usersettings.ts b/server/routes/user/usersettings.ts index 84d95b399..8b80175d1 100644 --- a/server/routes/user/usersettings.ts +++ b/server/routes/user/usersettings.ts @@ -63,6 +63,8 @@ userSettingsRoutes.get<{ id: string }, UserSettingsGeneralResponse>( globalMovieQuotaLimit: defaultQuotas.movie.quotaLimit, globalTvQuotaDays: defaultQuotas.tv.quotaDays, globalTvQuotaLimit: defaultQuotas.tv.quotaLimit, + watchlistSyncMovies: user.settings?.watchlistSyncMovies, + watchlistSyncTv: user.settings?.watchlistSyncTv, }); } catch (e) { next({ status: 500, message: e.message }); @@ -114,12 +116,16 @@ userSettingsRoutes.post< locale: req.body.locale, region: req.body.region, originalLanguage: req.body.originalLanguage, + watchlistSyncMovies: req.body.watchlistSyncMovies, + watchlistSyncTv: req.body.watchlistSyncTv, }); } else { user.settings.discordId = req.body.discordId; user.settings.locale = req.body.locale; user.settings.region = req.body.region; user.settings.originalLanguage = req.body.originalLanguage; + user.settings.watchlistSyncMovies = req.body.watchlistSyncMovies; + user.settings.watchlistSyncTv = req.body.watchlistSyncTv; } await userRepository.save(user); @@ -130,6 +136,8 @@ userSettingsRoutes.post< locale: user.settings.locale, region: user.settings.region, originalLanguage: user.settings.originalLanguage, + watchlistSyncMovies: user.settings.watchlistSyncMovies, + watchlistSyncTv: user.settings.watchlistSyncTv, }); } catch (e) { next({ status: 500, message: e.message }); diff --git a/server/scripts/prepareTestDb.ts b/server/scripts/prepareTestDb.ts index 371805130..41cfc46ad 100644 --- a/server/scripts/prepareTestDb.ts +++ b/server/scripts/prepareTestDb.ts @@ -14,7 +14,9 @@ const prepareDb = async () => { // Connect to DB and seed test data const dbConnection = await dataSource.initialize(); - await dbConnection.dropDatabase(); + if (process.env.PRESERVE_DB !== 'true') { + await dbConnection.dropDatabase(); + } // Run migrations in production if (process.env.WITH_MIGRATIONS === 'true') { @@ -41,9 +43,11 @@ const prepareDb = async () => { // Create the other user const otherUser = new User(); otherUser.plexId = 1; + otherUser.plexToken = '1234'; + otherUser.plexUsername = 'friend'; otherUser.username = 'friend'; otherUser.email = 'friend@seerr.dev'; - otherUser.userType = UserType.LOCAL; + otherUser.userType = UserType.PLEX; await otherUser.setPassword('test1234'); otherUser.permissions = 32; otherUser.avatar = 'https://plex.tv/assets/images/avatar/default.png'; diff --git a/src/components/Common/ListView/index.tsx b/src/components/Common/ListView/index.tsx index ff9013bb8..a99b29392 100644 --- a/src/components/Common/ListView/index.tsx +++ b/src/components/Common/ListView/index.tsx @@ -1,4 +1,5 @@ import { useIntl } from 'react-intl'; +import type { WatchlistItem } from '../../../../server/interfaces/api/discoverInterfaces'; import type { MovieResult, PersonResult, @@ -8,14 +9,16 @@ import useVerticalScroll from '../../../hooks/useVerticalScroll'; import globalMessages from '../../../i18n/globalMessages'; import PersonCard from '../../PersonCard'; import TitleCard from '../../TitleCard'; +import TmdbTitleCard from '../../TitleCard/TmdbTitleCard'; -interface ListViewProps { +type ListViewProps = { items?: (TvResult | MovieResult | PersonResult)[]; + plexItems?: WatchlistItem[]; isEmpty?: boolean; isLoading?: boolean; isReachingEnd?: boolean; onScrollBottom: () => void; -} +}; const ListView = ({ items, @@ -23,6 +26,7 @@ const ListView = ({ isLoading, onScrollBottom, isReachingEnd, + plexItems, }: ListViewProps) => { const intl = useIntl(); useVerticalScroll(onScrollBottom, !isLoading && !isEmpty && !isReachingEnd); @@ -34,6 +38,18 @@ const ListView = ({
    )}
      + {plexItems?.map((title, index) => { + return ( +
    • + +
    • + ); + })} {items?.map((title, index) => { let titleCard: React.ReactNode; diff --git a/src/components/Common/SettingsTabs/index.tsx b/src/components/Common/SettingsTabs/index.tsx index 060aef772..045416fcb 100644 --- a/src/components/Common/SettingsTabs/index.tsx +++ b/src/components/Common/SettingsTabs/index.tsx @@ -143,7 +143,7 @@ const SettingsTabs = ({
    ) : (
    -
    +
    + +
    + { + setFieldValue('cacheImages', !values.cacheImages); + }} + /> +
    +
    diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index 1cc403ceb..dc8b588c3 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -1,4 +1,6 @@ { + "components.AirDateBadge.airedrelative": "Aired {relativeTime}", + "components.AirDateBadge.airsrelative": "Airs {relativeTime}", "components.AppDataWarning.dockerVolumeMissingDescription": "The {appDataPath} volume mount was not configured properly. All data will be cleared when the container is stopped or restarted.", "components.CollectionDetails.numberofmovies": "{count} Movies", "components.CollectionDetails.overview": "Overview", @@ -858,10 +860,12 @@ "components.TitleCard.mediaerror": "{mediaType} Not Found", "components.TitleCard.tmdbid": "TMDB ID", "components.TitleCard.tvdbid": "TheTVDB ID", + "components.TvDetails.Season.somethingwentwrong": "Something went wrong while retrieving season data.", "components.TvDetails.TvCast.fullseriescast": "Full Series Cast", "components.TvDetails.TvCrew.fullseriescrew": "Full Series Crew", "components.TvDetails.anime": "Anime", "components.TvDetails.cast": "Cast", + "components.TvDetails.episodeCount": "{episodeCount, plural, one {# Episode} other {# Episodes}}", "components.TvDetails.episodeRuntime": "Episode Runtime", "components.TvDetails.episodeRuntimeMinutes": "{runtime} minutes", "components.TvDetails.firstAirDate": "First Air Date", @@ -877,9 +881,12 @@ "components.TvDetails.productioncountries": "Production {countryCount, plural, one {Country} other {Countries}}", "components.TvDetails.recommendations": "Recommendations", "components.TvDetails.reportissue": "Report an Issue", + "components.TvDetails.seasonnumber": "Season {seasonNumber}", "components.TvDetails.seasons": "{seasonCount, plural, one {# Season} other {# Seasons}}", + "components.TvDetails.seasonstitle": "Seasons", "components.TvDetails.showtype": "Series Type", "components.TvDetails.similar": "Similar Series", + "components.TvDetails.status4k": "4K {status}", "components.TvDetails.streamingproviders": "Currently Streaming On", "components.TvDetails.viewfullcrew": "View Full Crew", "components.TvDetails.watchtrailer": "Watch Trailer", diff --git a/yarn.lock b/yarn.lock index 03f3d4af4..d59f4cb5e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1750,6 +1750,13 @@ "@formatjs/intl-localematcher" "0.2.28" tslib "2.4.0" +"@formatjs/intl-utils@^3.8.4": + version "3.8.4" + resolved "https://registry.yarnpkg.com/@formatjs/intl-utils/-/intl-utils-3.8.4.tgz#291baac91001db428fc3275c515a3e40fbe95945" + integrity sha512-j5C6NyfKevIxsfLK8KwO1C0vvP7k1+h4A9cFpc+cr6mEwCc1sPkr17dzh0Ke6k9U5pQccAQoXdcNBl3IYa4+ZQ== + dependencies: + emojis-list "^3.0.0" + "@formatjs/intl@2.3.1": version "2.3.1" resolved "https://registry.yarnpkg.com/@formatjs/intl/-/intl-2.3.1.tgz#eccd6d03e4db18c256181f235b1d0a7f7aaebf5a" @@ -5435,6 +5442,11 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + enabled@2.0.x: version "2.0.0" resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" From 815d709bcfa4cca1528cc697defe9cd773ea0089 Mon Sep 17 00:00:00 2001 From: TheCatLady <52870424+TheCatLady@users.noreply.github.com> Date: Tue, 23 Aug 2022 21:59:26 -0700 Subject: [PATCH 63/81] feat(frontend): a few more tooltips (#2972) * feat(frontend): a few more tooltips * feat: add tooltips to status badges --- src/components/Common/Badge/index.tsx | 30 ++- src/components/ExternalLinkBlock/index.tsx | 2 +- src/components/MovieDetails/index.tsx | 64 ++++-- src/components/RequestBlock/index.tsx | 88 +++++--- src/components/RequestCard/index.tsx | 189 ++++++++++++------ .../RequestList/RequestItem/index.tsx | 19 +- src/components/Settings/SettingsMain.tsx | 2 +- src/components/StatusBadge/index.tsx | 114 +++++++---- src/components/TvDetails/index.tsx | 70 +++++-- src/i18n/locale/en.json | 22 +- src/styles/globals.css | 4 +- 11 files changed, 410 insertions(+), 194 deletions(-) diff --git a/src/components/Common/Badge/index.tsx b/src/components/Common/Badge/index.tsx index 5619eaefe..1a2a7a055 100644 --- a/src/components/Common/Badge/index.tsx +++ b/src/components/Common/Badge/index.tsx @@ -1,4 +1,5 @@ import Link from 'next/link'; +import React from 'react'; interface BadgeProps { badgeType?: @@ -14,12 +15,10 @@ interface BadgeProps { children: React.ReactNode; } -const Badge = ({ - badgeType = 'default', - className, - href, - children, -}: BadgeProps) => { +const Badge = ( + { badgeType = 'default', className, href, children }: BadgeProps, + ref?: React.Ref +) => { const badgeStyle = [ 'px-2 inline-flex text-xs leading-5 font-semibold rounded-full whitespace-nowrap', ]; @@ -79,6 +78,7 @@ const Badge = ({ target="_blank" rel="noopener noreferrer" className={badgeStyle.join(' ')} + ref={ref as React.Ref} > {children} @@ -86,12 +86,24 @@ const Badge = ({ } else if (href) { return ( - {children} + } + > + {children} + ); } else { - return {children}; + return ( + } + > + {children} + + ); } }; -export default Badge; +export default React.forwardRef(Badge) as typeof Badge; diff --git a/src/components/ExternalLinkBlock/index.tsx b/src/components/ExternalLinkBlock/index.tsx index fc7518f69..c4386c38b 100644 --- a/src/components/ExternalLinkBlock/index.tsx +++ b/src/components/ExternalLinkBlock/index.tsx @@ -70,7 +70,7 @@ const ExternalLinkBlock = ({ )} {rtUrl && ( { tmdbId={data.mediaInfo?.tmdbId} mediaType="movie" plexUrl={data.mediaInfo?.plexUrl} + serviceUrl={data.mediaInfo?.serviceUrl} /> {settings.currentSettings.movie4kEnabled && hasPermission( @@ -343,6 +347,7 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => { tmdbId={data.mediaInfo?.tmdbId} mediaType="movie" plexUrl={data.mediaInfo?.plexUrl4k} + serviceUrl={data.mediaInfo?.serviceUrl4k} /> )}
    @@ -499,36 +504,55 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => { (ratingData?.audienceRating && !!ratingData?.audienceScore)) && ( )} diff --git a/src/components/RequestBlock/index.tsx b/src/components/RequestBlock/index.tsx index 97889a08f..b32c32129 100644 --- a/src/components/RequestBlock/index.tsx +++ b/src/components/RequestBlock/index.tsx @@ -1,5 +1,6 @@ import Badge from '@app/components/Common/Badge'; import Button from '@app/components/Common/Button'; +import Tooltip from '@app/components/Common/Tooltip'; import RequestModal from '@app/components/RequestModal'; import useRequestOverride from '@app/hooks/useRequestOverride'; import { useUser } from '@app/hooks/useUser'; @@ -27,6 +28,13 @@ const messages = defineMessages({ profilechanged: 'Quality Profile', rootfolder: 'Root Folder', languageprofile: 'Language Profile', + requestdate: 'Request Date', + requestedby: 'Requested By', + lastmodifiedby: 'Last Modified By', + approve: 'Approve Request', + decline: 'Decline Request', + edit: 'Edit Request', + delete: 'Delete Request', }); interface RequestBlockProps { @@ -83,7 +91,9 @@ const RequestBlock = ({ request, onUpdate }: RequestBlockProps) => {
    - + + + {
    {request.modifiedBy && (
    - + + + {
    {request.status === MediaRequestStatus.PENDING && ( <> - - - + + + + + + + + + )} {request.status !== MediaRequestStatus.PENDING && ( - + + + )}
    @@ -187,7 +207,9 @@ const RequestBlock = ({ request, onUpdate }: RequestBlockProps) => {
    - + + + {intl.formatDate(request.createdAt, { year: 'numeric', diff --git a/src/components/RequestCard/index.tsx b/src/components/RequestCard/index.tsx index 952f79ba3..18b81ac4d 100644 --- a/src/components/RequestCard/index.tsx +++ b/src/components/RequestCard/index.tsx @@ -1,6 +1,7 @@ import Badge from '@app/components/Common/Badge'; import Button from '@app/components/Common/Button'; import CachedImage from '@app/components/Common/CachedImage'; +import Tooltip from '@app/components/Common/Tooltip'; import RequestModal from '@app/components/RequestModal'; import StatusBadge from '@app/components/StatusBadge'; import { Permission, useUser } from '@app/hooks/useUser'; @@ -31,6 +32,10 @@ const messages = defineMessages({ mediaerror: '{mediaType} Not Found', tmdbid: 'TMDB ID', tvdbid: 'TheTVDB ID', + approverequest: 'Approve Request', + declinerequest: 'Decline Request', + editrequest: 'Edit Request', + cancelrequest: 'Cancel Request', deleterequest: 'Delete Request', }); @@ -139,11 +144,9 @@ const RequestCardError = ({ requestData }: RequestCardErrorProps) => { : requestData.media.plexUrl } serviceUrl={ - hasPermission(Permission.ADMIN) - ? requestData.is4k - ? requestData.media.serviceUrl4k - : requestData.media.serviceUrl - : undefined + requestData.is4k + ? requestData.media.serviceUrl4k + : requestData.media.serviceUrl } /> )} @@ -153,17 +156,29 @@ const RequestCardError = ({ requestData }: RequestCardErrorProps) => {
    {hasPermission(Permission.MANAGE_REQUESTS) && requestData?.media.id && ( - + <> + + + + + )}
    @@ -389,7 +404,14 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => { tmdbId={requestData.media.tmdbId} mediaType={requestData.type} plexUrl={ - requestData.media[requestData.is4k ? 'plexUrl4k' : 'plexUrl'] + requestData.is4k + ? requestData.media.plexUrl4k + : requestData.media.plexUrl + } + serviceUrl={ + requestData.is4k + ? requestData.media.serviceUrl4k + : requestData.media.serviceUrl } /> )} @@ -415,26 +437,52 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => { {requestData.status === MediaRequestStatus.PENDING && hasPermission(Permission.MANAGE_REQUESTS) && ( <> - - +
    + + + + +
    +
    + + + + +
    )} {requestData.status === MediaRequestStatus.PENDING && @@ -442,33 +490,54 @@ const RequestCard = ({ request, onTitleData }: RequestCardProps) => { requestData.requestedBy.id === user?.id && (requestData.type === 'tv' || hasPermission(Permission.REQUEST_ADVANCED)) && ( - +
    + {!hasPermission(Permission.MANAGE_REQUESTS) && ( + + )} + + + +
    )} {requestData.status === MediaRequestStatus.PENDING && !hasPermission(Permission.MANAGE_REQUESTS) && requestData.requestedBy.id === user?.id && ( - +
    + + + + +
    )}
    diff --git a/src/components/RequestList/RequestItem/index.tsx b/src/components/RequestList/RequestItem/index.tsx index 095d1aba6..7949a263c 100644 --- a/src/components/RequestList/RequestItem/index.tsx +++ b/src/components/RequestList/RequestItem/index.tsx @@ -136,11 +136,9 @@ const RequestItemError = ({ : requestData.media.plexUrl } serviceUrl={ - hasPermission(Permission.ADMIN) - ? requestData.is4k - ? requestData.media.serviceUrl4k - : requestData.media.serviceUrl - : undefined + requestData.is4k + ? requestData.media.serviceUrl4k + : requestData.media.serviceUrl } /> )} @@ -472,9 +470,14 @@ const RequestItem = ({ request, revalidateList }: RequestItemProps) => { tmdbId={requestData.media.tmdbId} mediaType={requestData.type} plexUrl={ - requestData.media[ - requestData.is4k ? 'plexUrl4k' : 'plexUrl' - ] + requestData.is4k + ? requestData.media.plexUrl4k + : requestData.media.plexUrl + } + serviceUrl={ + requestData.is4k + ? requestData.media.serviceUrl4k + : requestData.media.serviceUrl } /> )} diff --git a/src/components/Settings/SettingsMain.tsx b/src/components/Settings/SettingsMain.tsx index e8a2063b3..3ac7b09b5 100644 --- a/src/components/Settings/SettingsMain.tsx +++ b/src/components/Settings/SettingsMain.tsx @@ -46,7 +46,7 @@ const messages = defineMessages({ 'Do NOT enable this setting unless you understand what you are doing!', cacheImages: 'Enable Image Caching', cacheImagesTip: - 'Optimize and store all images locally (consumes a significant amount of disk space)', + 'Cache and serve optimized images (requires a significant amount of disk space)', trustProxy: 'Enable Proxy Support', trustProxyTip: 'Allow Overseerr to correctly register client IP addresses behind a proxy', diff --git a/src/components/StatusBadge/index.tsx b/src/components/StatusBadge/index.tsx index 5929263f1..add2bbb88 100644 --- a/src/components/StatusBadge/index.tsx +++ b/src/components/StatusBadge/index.tsx @@ -1,5 +1,6 @@ import Spinner from '@app/assets/spinner.svg'; import Badge from '@app/components/Common/Badge'; +import Tooltip from '@app/components/Common/Tooltip'; import useSettings from '@app/hooks/useSettings'; import { Permission, useUser } from '@app/hooks/useUser'; import globalMessages from '@app/i18n/globalMessages'; @@ -9,6 +10,9 @@ import { defineMessages, useIntl } from 'react-intl'; const messages = defineMessages({ status: '{status}', status4k: '4K {status}', + playonplex: 'Play on Plex', + openinarr: 'Open in {arr}', + managemedia: 'Manage {mediaType}', }); interface StatusBadgeProps { @@ -35,6 +39,7 @@ const StatusBadge = ({ const settings = useSettings(); let mediaLink: string | undefined; + let mediaLinkDescription: string | undefined; if ( mediaType && @@ -63,63 +68,94 @@ const StatusBadge = ({ : settings.currentSettings.series4kEnabled)) ) { mediaLink = plexUrl; + mediaLinkDescription = intl.formatMessage(messages.playonplex); } else if (hasPermission(Permission.MANAGE_REQUESTS)) { - mediaLink = - mediaType && tmdbId ? `/${mediaType}/${tmdbId}?manage=1` : serviceUrl; + if (mediaType && tmdbId) { + mediaLink = `/${mediaType}/${tmdbId}?manage=1`; + mediaLinkDescription = intl.formatMessage(messages.managemedia, { + mediaType: intl.formatMessage( + mediaType === 'movie' ? globalMessages.movie : globalMessages.tvshow + ), + }); + } else if (hasPermission(Permission.ADMIN)) { + mediaLink = serviceUrl; + mediaLinkDescription = intl.formatMessage(messages.openinarr, { + arr: mediaType === 'movie' ? 'Radarr' : 'Sonarr', + }); + } } switch (status) { case MediaStatus.AVAILABLE: return ( - -
    - - {intl.formatMessage(is4k ? messages.status4k : messages.status, { - status: intl.formatMessage(globalMessages.available), - })} - - {inProgress && } -
    -
    + + +
    + + {intl.formatMessage( + is4k ? messages.status4k : messages.status, + { + status: intl.formatMessage(globalMessages.available), + } + )} + + {inProgress && } +
    +
    +
    ); case MediaStatus.PARTIALLY_AVAILABLE: return ( - -
    - - {intl.formatMessage(is4k ? messages.status4k : messages.status, { - status: intl.formatMessage(globalMessages.partiallyavailable), - })} - - {inProgress && } -
    -
    + + +
    + + {intl.formatMessage( + is4k ? messages.status4k : messages.status, + { + status: intl.formatMessage( + globalMessages.partiallyavailable + ), + } + )} + + {inProgress && } +
    +
    +
    ); case MediaStatus.PROCESSING: return ( - -
    - - {intl.formatMessage(is4k ? messages.status4k : messages.status, { - status: inProgress - ? intl.formatMessage(globalMessages.processing) - : intl.formatMessage(globalMessages.requested), - })} - - {inProgress && } -
    -
    + + +
    + + {intl.formatMessage( + is4k ? messages.status4k : messages.status, + { + status: inProgress + ? intl.formatMessage(globalMessages.processing) + : intl.formatMessage(globalMessages.requested), + } + )} + + {inProgress && } +
    +
    +
    ); case MediaStatus.PENDING: return ( - - {intl.formatMessage(is4k ? messages.status4k : messages.status, { - status: intl.formatMessage(globalMessages.pending), - })} - + + + {intl.formatMessage(is4k ? messages.status4k : messages.status, { + status: intl.formatMessage(globalMessages.pending), + })} + + ); default: diff --git a/src/components/TvDetails/index.tsx b/src/components/TvDetails/index.tsx index 35b995d8a..0d16442c8 100644 --- a/src/components/TvDetails/index.tsx +++ b/src/components/TvDetails/index.tsx @@ -79,6 +79,9 @@ const messages = defineMessages({ episodeCount: '{episodeCount, plural, one {# Episode} other {# Episodes}}', seasonnumber: 'Season {seasonNumber}', status4k: '4K {status}', + rtcriticsscore: 'Rotten Tomatoes Tomatometer', + rtaudiencescore: 'Rotten Tomatoes Audience Score', + tmdbuserscore: 'TMDB User Score', }); interface TvDetailsProps { @@ -330,6 +333,7 @@ const TvDetails = ({ tv }: TvDetailsProps) => { tmdbId={data.mediaInfo?.tmdbId} mediaType="tv" plexUrl={data.mediaInfo?.plexUrl} + serviceUrl={data.mediaInfo?.serviceUrl} /> {settings.currentSettings.series4kEnabled && hasPermission( @@ -351,6 +355,7 @@ const TvDetails = ({ tv }: TvDetailsProps) => { tmdbId={data.mediaInfo?.tmdbId} mediaType="tv" plexUrl={data.mediaInfo?.plexUrl4k} + serviceUrl={data.mediaInfo?.serviceUrl4k} /> )}
    @@ -660,30 +665,55 @@ const TvDetails = ({ tv }: TvDetailsProps) => { (ratingData?.audienceRating && !!ratingData?.audienceScore)) && (
    {ratingData?.criticsRating && !!ratingData?.criticsScore && ( - - {ratingData.criticsRating === 'Rotten' ? ( - - ) : ( - - )} - {ratingData.criticsScore}% - + + + {ratingData.criticsRating === 'Rotten' ? ( + + ) : ( + + )} + {ratingData.criticsScore}% + + )} {ratingData?.audienceRating && !!ratingData?.audienceScore && ( - - {ratingData.audienceRating === 'Spilled' ? ( - - ) : ( - - )} - {ratingData.audienceScore}% - + + + {ratingData.audienceRating === 'Spilled' ? ( + + ) : ( + + )} + {ratingData.audienceScore}% + + )} {!!data.voteCount && ( - - - {data.voteAverage}/10 - + + + + {Math.round(data.voteAverage * 10)}% + + )}
    )} diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index dc8b588c3..6c65446f5 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -181,6 +181,8 @@ "components.MovieDetails.releasedate": "{releaseCount, plural, one {Release Date} other {Release Dates}}", "components.MovieDetails.reportissue": "Report an Issue", "components.MovieDetails.revenue": "Revenue", + "components.MovieDetails.rtaudiencescore": "Rotten Tomatoes Audience Score", + "components.MovieDetails.rtcriticsscore": "Rotten Tomatoes Tomatometer", "components.MovieDetails.runtime": "{minutes} minutes", "components.MovieDetails.showless": "Show Less", "components.MovieDetails.showmore": "Show More", @@ -188,6 +190,7 @@ "components.MovieDetails.streamingproviders": "Currently Streaming On", "components.MovieDetails.studio": "{studioCount, plural, one {Studio} other {Studios}}", "components.MovieDetails.theatricalrelease": "Theatrical Release", + "components.MovieDetails.tmdbuserscore": "TMDB User Score", "components.MovieDetails.viewfullcrew": "View Full Crew", "components.MovieDetails.watchtrailer": "Watch Trailer", "components.NotificationTypeSelector.adminissuecommentDescription": "Get notified when other users comment on issues.", @@ -292,8 +295,15 @@ "components.QuotaSelector.unlimited": "Unlimited", "components.RegionSelector.regionDefault": "All Regions", "components.RegionSelector.regionServerDefault": "Default ({region})", + "components.RequestBlock.approve": "Approve Request", + "components.RequestBlock.decline": "Decline Request", + "components.RequestBlock.delete": "Delete Request", + "components.RequestBlock.edit": "Edit Request", "components.RequestBlock.languageprofile": "Language Profile", + "components.RequestBlock.lastmodifiedby": "Last Modified By", "components.RequestBlock.profilechanged": "Quality Profile", + "components.RequestBlock.requestdate": "Request Date", + "components.RequestBlock.requestedby": "Requested By", "components.RequestBlock.requestoverrides": "Request Overrides", "components.RequestBlock.rootfolder": "Root Folder", "components.RequestBlock.seasons": "{seasonCount, plural, one {Season} other {Seasons}}", @@ -310,7 +320,11 @@ "components.RequestButton.requestmore4k": "Request More in 4K", "components.RequestButton.viewrequest": "View Request", "components.RequestButton.viewrequest4k": "View 4K Request", + "components.RequestCard.approverequest": "Approve Request", + "components.RequestCard.cancelrequest": "Cancel Request", + "components.RequestCard.declinerequest": "Decline Request", "components.RequestCard.deleterequest": "Delete Request", + "components.RequestCard.editrequest": "Edit Request", "components.RequestCard.failedretry": "Something went wrong while retrying the request.", "components.RequestCard.mediaerror": "{mediaType} Not Found", "components.RequestCard.seasons": "{seasonCount, plural, one {Season} other {Seasons}}", @@ -736,7 +750,7 @@ "components.Settings.applicationTitle": "Application Title", "components.Settings.applicationurl": "Application URL", "components.Settings.cacheImages": "Enable Image Caching", - "components.Settings.cacheImagesTip": "Optimize and store all images locally (consumes a significant amount of disk space)", + "components.Settings.cacheImagesTip": "Cache and serve optimized images (requires a significant amount of disk space)", "components.Settings.cancelscan": "Cancel Scan", "components.Settings.copied": "Copied API key to clipboard.", "components.Settings.csrfProtection": "Enable CSRF Protection", @@ -849,6 +863,9 @@ "components.Setup.signinMessage": "Get started by signing in with your Plex account", "components.Setup.tip": "Tip", "components.Setup.welcome": "Welcome to Overseerr", + "components.StatusBadge.managemedia": "Manage {mediaType}", + "components.StatusBadge.openinarr": "Open in {arr}", + "components.StatusBadge.playonplex": "Play on Plex", "components.StatusBadge.status": "{status}", "components.StatusBadge.status4k": "4K {status}", "components.StatusChecker.appUpdated": "{applicationTitle} Updated", @@ -881,6 +898,8 @@ "components.TvDetails.productioncountries": "Production {countryCount, plural, one {Country} other {Countries}}", "components.TvDetails.recommendations": "Recommendations", "components.TvDetails.reportissue": "Report an Issue", + "components.TvDetails.rtaudiencescore": "Rotten Tomatoes Audience Score", + "components.TvDetails.rtcriticsscore": "Rotten Tomatoes Tomatometer", "components.TvDetails.seasonnumber": "Season {seasonNumber}", "components.TvDetails.seasons": "{seasonCount, plural, one {# Season} other {# Seasons}}", "components.TvDetails.seasonstitle": "Seasons", @@ -888,6 +907,7 @@ "components.TvDetails.similar": "Similar Series", "components.TvDetails.status4k": "4K {status}", "components.TvDetails.streamingproviders": "Currently Streaming On", + "components.TvDetails.tmdbuserscore": "TMDB User Score", "components.TvDetails.viewfullcrew": "View Full Crew", "components.TvDetails.watchtrailer": "Watch Trailer", "components.UserList.accounttype": "Type", diff --git a/src/styles/globals.css b/src/styles/globals.css index 203b6a1cd..b3e2543ee 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -184,11 +184,11 @@ } .media-ratings { - @apply flex items-center justify-center border-b border-gray-700 px-4 py-2 font-medium last:border-b-0; + @apply flex items-center justify-center space-x-5 border-b border-gray-700 px-4 py-2 font-medium last:border-b-0; } .media-rating { - @apply mr-4 flex items-center last:mr-0; + @apply flex items-center space-x-1; } .error-message { From 22360f3b87055baab2046df7bbd038fe0d3f10e3 Mon Sep 17 00:00:00 2001 From: Ryan Cohen Date: Wed, 24 Aug 2022 15:00:04 +0900 Subject: [PATCH 64/81] refactor: slideover redesign (#2973) --- src/components/Common/SlideOver/index.tsx | 26 ++++++++++----------- src/components/ManageSlideOver/index.tsx | 28 +++++++++++------------ 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/components/Common/SlideOver/index.tsx b/src/components/Common/SlideOver/index.tsx index 895745a95..079bd7a8d 100644 --- a/src/components/Common/SlideOver/index.tsx +++ b/src/components/Common/SlideOver/index.tsx @@ -1,8 +1,8 @@ /* eslint-disable jsx-a11y/click-events-have-key-events */ -import Transition from '@app/components/Transition'; import { useLockBodyScroll } from '@app/hooks/useLockBodyScroll'; +import { Transition } from '@headlessui/react'; import { XIcon } from '@heroicons/react/outline'; -import { useEffect, useRef, useState } from 'react'; +import { Fragment, useEffect, useRef, useState } from 'react'; import ReactDOM from 'react-dom'; interface SlideOverProps { @@ -34,6 +34,7 @@ const SlideOver = ({ return ReactDOM.createPortal(
    -
    - + {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
    e.stopPropagation()} > -
    -
    +
    +
    -

    +

    {title}

    {subText && (
    -

    - {subText} -

    +

    {subText}

    )}
    @@ -100,7 +98,7 @@ const SlideOver = ({
    -
    +
    diff --git a/src/components/ManageSlideOver/index.tsx b/src/components/ManageSlideOver/index.tsx index a2df65b05..87b96e3c5 100644 --- a/src/components/ManageSlideOver/index.tsx +++ b/src/components/ManageSlideOver/index.tsx @@ -137,11 +137,11 @@ const ManageSlideOver = ({
    {((data?.mediaInfo?.downloadStatus ?? []).length > 0 || (data?.mediaInfo?.downloadStatus4k ?? []).length > 0) && ( -
    + <>

    {intl.formatMessage(messages.downloadstatus)}

    -
    +
      {data.mediaInfo?.downloadStatus?.map((status, index) => (
    -
    + )} {hasPermission([Permission.MANAGE_ISSUES, Permission.VIEW_ISSUES], { type: 'or', @@ -171,7 +171,7 @@ const ManageSlideOver = ({

    {intl.formatMessage(messages.manageModalIssues)}

    -
    +
    - +
    )} {hasPermission([Permission.MANAGE_ISSUES, Permission.VIEW_ISSUES], { type: 'or', }) && openIssues.length > 0 && ( - <> +

    {intl.formatMessage(messages.manageModalIssues)}

    @@ -183,10 +183,10 @@ const ManageSlideOver = ({ ))}
    - +
    )} {requests.length > 0 && ( - <> +

    {intl.formatMessage(messages.manageModalRequests)}

    @@ -205,13 +205,13 @@ const ManageSlideOver = ({ ))}
    - +
    )} {hasPermission(Permission.ADMIN) && (data.mediaInfo?.serviceUrl || data.mediaInfo?.tautulliUrl || watchData?.data) && ( - <> +

    {intl.formatMessage(messages.manageModalMedia)}

    @@ -324,7 +324,7 @@ const ManageSlideOver = ({ )}
    - + )} {hasPermission(Permission.ADMIN) && (data.mediaInfo?.serviceUrl4k || diff --git a/src/components/TvDetails/index.tsx b/src/components/TvDetails/index.tsx index 0d16442c8..5b8de4a61 100644 --- a/src/components/TvDetails/index.tsx +++ b/src/components/TvDetails/index.tsx @@ -536,7 +536,7 @@ const TvDetails = ({ tv }: TvDetailsProps) => { {({ open }) => ( <> { {appDataPath} volume mount was not configured properly. All data will be cleared when the container is stopped or restarted.", "components.CollectionDetails.numberofmovies": "{count} Movies", "components.CollectionDetails.overview": "Overview", From b925857dfa27483558eab24bc5f20f654e294a08 Mon Sep 17 00:00:00 2001 From: Brandon Cohen Date: Wed, 24 Aug 2022 21:45:05 -0400 Subject: [PATCH 72/81] fix: username will not show undefined on cancel or delete (#2982) --- src/components/UserList/index.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/UserList/index.tsx b/src/components/UserList/index.tsx index 66580b005..078fbd134 100644 --- a/src/components/UserList/index.tsx +++ b/src/components/UserList/index.tsx @@ -183,7 +183,7 @@ const UserList = () => { autoDismiss: true, appearance: 'success', }); - setDeleteModal({ isOpen: false }); + setDeleteModal({ isOpen: false, user: deleteModal.user }); } catch (e) { addToast(intl.formatMessage(messages.userdeleteerror), { autoDismiss: true, @@ -246,9 +246,11 @@ const UserList = () => { } okDisabled={isDeleting} okButtonType="danger" - onCancel={() => setDeleteModal({ isOpen: false })} + onCancel={() => + setDeleteModal({ isOpen: false, user: deleteModal.user }) + } title={intl.formatMessage(messages.deleteuser, { - username: `${deleteModal.user?.username}`, + username: `${deleteModal.user?.displayName}`, })} iconSvg={} > From 1a0053221b58d198bdcca0249fcd76226122c6a2 Mon Sep 17 00:00:00 2001 From: Ryan Cohen Date: Thu, 25 Aug 2022 13:28:44 +0900 Subject: [PATCH 73/81] fix: clicking outside modal closes modal again (#2984) --- src/components/Common/Modal/index.tsx | 150 ++++++++++-------- .../RequestModal/TvRequestModal.tsx | 4 +- 2 files changed, 82 insertions(+), 72 deletions(-) diff --git a/src/components/Common/Modal/index.tsx b/src/components/Common/Modal/index.tsx index a6c2aac58..06b4ba5fe 100644 --- a/src/components/Common/Modal/index.tsx +++ b/src/components/Common/Modal/index.tsx @@ -7,7 +7,7 @@ import { useLockBodyScroll } from '@app/hooks/useLockBodyScroll'; import globalMessages from '@app/i18n/globalMessages'; import { Transition } from '@headlessui/react'; import type { MouseEvent } from 'react'; -import { Fragment, useRef } from 'react'; +import React, { Fragment, useRef } from 'react'; import ReactDOM from 'react-dom'; import { useIntl } from 'react-intl'; @@ -36,81 +36,89 @@ interface ModalProps { children?: React.ReactNode; } -const Modal = ({ - title, - onCancel, - onOk, - cancelText, - okText, - okDisabled = false, - cancelButtonType = 'default', - okButtonType = 'primary', - children, - disableScrollLock, - backgroundClickable = true, - iconSvg, - secondaryButtonType = 'default', - secondaryDisabled = false, - onSecondary, - secondaryText, - tertiaryButtonType = 'default', - tertiaryDisabled = false, - tertiaryText, - onTertiary, - backdrop, -}: ModalProps) => { - const intl = useIntl(); - const modalRef = useRef(null); - useClickOutside(modalRef, () => { - typeof onCancel === 'function' && backgroundClickable - ? onCancel() - : undefined; - }); - useLockBodyScroll(true, disableScrollLock); +const Modal = React.forwardRef( + ( + { + title, + onCancel, + onOk, + cancelText, + okText, + okDisabled = false, + cancelButtonType = 'default', + okButtonType = 'primary', + children, + disableScrollLock, + backgroundClickable = true, + iconSvg, + secondaryButtonType = 'default', + secondaryDisabled = false, + onSecondary, + secondaryText, + tertiaryButtonType = 'default', + tertiaryDisabled = false, + tertiaryText, + loading = false, + onTertiary, + backdrop, + }, + parentRef + ) => { + const intl = useIntl(); + const modalRef = useRef(null); + useClickOutside(modalRef, () => { + typeof onCancel === 'function' && backgroundClickable + ? onCancel() + : undefined; + }); + useLockBodyScroll(true, disableScrollLock); - return ReactDOM.createPortal( - // eslint-disable-next-line jsx-a11y/no-static-element-interactions -
    { - if (e.key === 'Escape') { - typeof onCancel === 'function' && backgroundClickable - ? onCancel() - : undefined; - } - }} - > + return ReactDOM.createPortal( -
    - -
    -
    - -
    +
    + +
    + + {backdrop && (
    @@ -205,11 +213,13 @@ const Modal = ({ )}
    )} -
    -
    -
    , - document.body - ); -}; +
    + , + document.body + ); + } +); + +Modal.displayName = 'Modal'; export default Modal; diff --git a/src/components/RequestModal/TvRequestModal.tsx b/src/components/RequestModal/TvRequestModal.tsx index 59e65dfc2..6de4253b7 100644 --- a/src/components/RequestModal/TvRequestModal.tsx +++ b/src/components/RequestModal/TvRequestModal.tsx @@ -359,12 +359,12 @@ const TvRequestModal = ({ const isOwner = editRequest && editRequest.requestedBy.id === user?.id; - return !data?.externalIds.tvdbId && searchModal.show ? ( + return data && !data.externalIds.tvdbId && searchModal.show ? ( setSearchModal({ show: false })} - loading={!data && !error} + loading={!error} onCancel={onCancel} modalTitle={intl.formatMessage( is4k ? messages.request4ktitle : messages.requesttitle, From 4d563208709fe581ef6bf475e969197d586d907b Mon Sep 17 00:00:00 2001 From: Brandon Cohen Date: Fri, 26 Aug 2022 08:38:13 -0400 Subject: [PATCH 74/81] fix: settings log modal when closing (#2985) --- .../Settings/SettingsLogs/index.tsx | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/components/Settings/SettingsLogs/index.tsx b/src/components/Settings/SettingsLogs/index.tsx index ad068ceea..39aa05d80 100644 --- a/src/components/Settings/SettingsLogs/index.tsx +++ b/src/components/Settings/SettingsLogs/index.tsx @@ -60,7 +60,10 @@ const SettingsLogs = () => { const [currentFilter, setCurrentFilter] = useState('debug'); const [currentPageSize, setCurrentPageSize] = useState(25); const [refreshInterval, setRefreshInterval] = useState(5000); - const [activeLog, setActiveLog] = useState(null); + const [activeLog, setActiveLog] = useState<{ + isOpen: boolean; + log?: LogMessage; + }>({ isOpen: false }); const page = router.query.page ? Number(router.query.page) : 1; const pageIndex = page - 1; @@ -143,14 +146,16 @@ const SettingsLogs = () => { leaveFrom="opacity-100" leaveTo="opacity-0" appear - show={!!activeLog} + show={activeLog.isOpen} > } - onCancel={() => setActiveLog(null)} + onCancel={() => setActiveLog({ log: activeLog.log, isOpen: false })} cancelText={intl.formatMessage(globalMessages.close)} - onOk={() => (activeLog ? copyLogString(activeLog) : undefined)} + onOk={() => + activeLog.log ? copyLogString(activeLog.log) : undefined + } okText={intl.formatMessage(messages.copyToClipboard)} okButtonType="primary" > @@ -162,7 +167,7 @@ const SettingsLogs = () => {
    - {intl.formatDate(activeLog.timestamp, { + {intl.formatDate(activeLog.log?.timestamp, { year: 'numeric', month: 'short', day: '2-digit', @@ -181,16 +186,16 @@ const SettingsLogs = () => {
    - {activeLog.level.toUpperCase()} + {activeLog.log?.level.toUpperCase()}
    @@ -201,7 +206,7 @@ const SettingsLogs = () => {
    - {activeLog.label} + {activeLog.log?.label}
    @@ -211,18 +216,18 @@ const SettingsLogs = () => {
    - {activeLog.message} + {activeLog.log?.message}
    - {activeLog.data && ( + {activeLog.log?.data && (
    {intl.formatMessage(messages.extraData)}
    - {JSON.stringify(activeLog.data, null, ' ')} + {JSON.stringify(activeLog.log?.data, null, ' ')}
    @@ -336,7 +341,9 @@ const SettingsLogs = () => {

    {subtext &&
    {subtext}
    } diff --git a/src/components/Common/Modal/index.tsx b/src/components/Common/Modal/index.tsx index 06b4ba5fe..0d7eff19e 100644 --- a/src/components/Common/Modal/index.tsx +++ b/src/components/Common/Modal/index.tsx @@ -13,6 +13,7 @@ import { useIntl } from 'react-intl'; interface ModalProps { title?: string; + subTitle?: string; onCancel?: (e?: MouseEvent) => void; onOk?: (e?: MouseEvent) => void; onSecondary?: (e?: MouseEvent) => void; @@ -30,7 +31,6 @@ interface ModalProps { tertiaryButtonType?: ButtonType; disableScrollLock?: boolean; backgroundClickable?: boolean; - iconSvg?: React.ReactNode; loading?: boolean; backdrop?: string; children?: React.ReactNode; @@ -40,6 +40,7 @@ const Modal = React.forwardRef( ( { title, + subTitle, onCancel, onOk, cancelText, @@ -50,7 +51,6 @@ const Modal = React.forwardRef( children, disableScrollLock, backgroundClickable = true, - iconSvg, secondaryButtonType = 'default', secondaryDisabled = false, onSecondary, @@ -67,9 +67,9 @@ const Modal = React.forwardRef( const intl = useIntl(); const modalRef = useRef(null); useClickOutside(modalRef, () => { - typeof onCancel === 'function' && backgroundClickable - ? onCancel() - : undefined; + if (onCancel && backgroundClickable) { + onCancel(); + } }); useLockBodyScroll(true, disableScrollLock); @@ -102,7 +102,7 @@ const Modal = React.forwardRef( ( className="absolute inset-0" style={{ backgroundImage: - 'linear-gradient(180deg, rgba(55, 65, 81, 0.85) 0%, rgba(55, 65, 81, 1) 100%)', + 'linear-gradient(180deg, rgba(31, 41, 55, 0.75) 0%, rgba(31, 41, 55, 1) 100%)', }} /> )} -
    - {iconSvg &&
    {iconSvg}
    } +
    - {title && ( - - {title} - + {(title || subTitle) && ( +
    + {title && ( + + {title} + + )} + {subTitle && ( + + {subTitle} + + )} +
    )}
    diff --git a/src/components/Common/SlideOver/index.tsx b/src/components/Common/SlideOver/index.tsx index f69d3e519..e7ccf7063 100644 --- a/src/components/Common/SlideOver/index.tsx +++ b/src/components/Common/SlideOver/index.tsx @@ -74,7 +74,7 @@ const SlideOver = ({
    -

    +

    {title}

    @@ -89,7 +89,9 @@ const SlideOver = ({
    {subText && (
    -

    {subText}

    +

    + {subText} +

    )}
    diff --git a/src/components/IssueDetails/IssueComment/index.tsx b/src/components/IssueDetails/IssueComment/index.tsx index fa7777469..b941c9f31 100644 --- a/src/components/IssueDetails/IssueComment/index.tsx +++ b/src/components/IssueDetails/IssueComment/index.tsx @@ -2,7 +2,6 @@ import Button from '@app/components/Common/Button'; import Modal from '@app/components/Common/Modal'; import { Permission, useUser } from '@app/hooks/useUser'; import { Menu, Transition } from '@headlessui/react'; -import { ExclamationIcon } from '@heroicons/react/outline'; import { DotsVerticalIcon } from '@heroicons/react/solid'; import type { default as IssueCommentType } from '@server/entity/IssueComment'; import axios from 'axios'; @@ -65,7 +64,7 @@ const IssueComment = ({ } mt-4 space-x-4`} > deleteComment()} okText={intl.formatMessage(messages.delete)} okButtonType="danger" - iconSvg={} > {intl.formatMessage(messages.areyousuredelete)} diff --git a/src/components/IssueDetails/index.tsx b/src/components/IssueDetails/index.tsx index 378a6a70f..7868b626c 100644 --- a/src/components/IssueDetails/index.tsx +++ b/src/components/IssueDetails/index.tsx @@ -14,7 +14,6 @@ import { Transition } from '@headlessui/react'; import { ChatIcon, CheckCircleIcon, - ExclamationIcon, PlayIcon, ServerIcon, } from '@heroicons/react/outline'; @@ -189,7 +188,6 @@ const IssueDetails = () => { onOk={() => deleteIssue()} okText={intl.formatMessage(messages.deleteissue)} okButtonType="danger" - iconSvg={} > {intl.formatMessage(messages.deleteissueconfirm)} diff --git a/src/components/IssueModal/CreateIssueModal/index.tsx b/src/components/IssueModal/CreateIssueModal/index.tsx index 93510988f..edbdd0831 100644 --- a/src/components/IssueModal/CreateIssueModal/index.tsx +++ b/src/components/IssueModal/CreateIssueModal/index.tsx @@ -5,7 +5,6 @@ import useSettings from '@app/hooks/useSettings'; import { Permission, useUser } from '@app/hooks/useUser'; import globalMessages from '@app/i18n/globalMessages'; import { RadioGroup } from '@headlessui/react'; -import { ExclamationIcon } from '@heroicons/react/outline'; import { ArrowCircleRightIcon } from '@heroicons/react/solid'; import { MediaStatus } from '@server/constants/media'; import type Issue from '@server/entity/Issue'; @@ -150,23 +149,14 @@ const CreateIssueModal = ({ } title={intl.formatMessage(messages.reportissue)} + subTitle={data && isMovie(data) ? data?.title : data?.name} cancelText={intl.formatMessage(globalMessages.close)} onOk={() => handleSubmit()} okText={intl.formatMessage(messages.submitissue)} loading={!data && !error} backdrop={`https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/${data?.backdropPath}`} > - {data && ( -
    - - {intl.formatMessage(messages.issomethingwrong, { - title: isMovie(data) ? data.title : data.name, - })} - -
    - )} {mediaType === 'tv' && data && !isMovie(data) && ( <>
    @@ -264,7 +254,7 @@ const CreateIssueModal = ({ ? 'rounded-bl-md rounded-br-md' : '', checked - ? 'z-10 border-indigo-500 bg-indigo-600' + ? 'z-10 border border-indigo-500 bg-indigo-400 bg-opacity-20' : 'border-gray-500', 'relative flex cursor-pointer border p-4 focus:outline-none' ) @@ -275,7 +265,7 @@ const CreateIssueModal = ({ { name: string; value: string; diff --git a/src/components/MovieDetails/index.tsx b/src/components/MovieDetails/index.tsx index 39b36e766..88cffb210 100644 --- a/src/components/MovieDetails/index.tsx +++ b/src/components/MovieDetails/index.tsx @@ -408,7 +408,7 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => { {hasPermission(Permission.MANAGE_REQUESTS) && data.mediaInfo && (