diff --git a/src/apps/dashboard/features/plugins/api/usePluginDetails.ts b/src/apps/dashboard/features/plugins/api/usePluginDetails.ts index b2e7a75cc8..ba1cd8b679 100644 --- a/src/apps/dashboard/features/plugins/api/usePluginDetails.ts +++ b/src/apps/dashboard/features/plugins/api/usePluginDetails.ts @@ -3,12 +3,14 @@ import { useMemo } from 'react'; import { useApi } from 'hooks/useApi'; -import { useConfigurationPages } from './useConfigurationPages'; -import { usePlugins } from './usePlugins'; +import { PluginCategory } from '../constants/pluginCategory'; import type { PluginDetails } from '../types/PluginDetails'; + import { findBestConfigurationPage } from './configurationPage'; import { findBestPluginInfo } from './pluginInfo'; +import { useConfigurationPages } from './useConfigurationPages'; import { usePackages } from './usePackages'; +import { usePlugins } from './usePlugins'; export const usePluginDetails = () => { const { api } = useApi(); @@ -64,9 +66,25 @@ export const usePluginDetails = () => { imageUrl = api?.getUri(`/Plugins/${pluginInfo.Id}/${pluginInfo.Version}/Image`); } + let category = packageInfo?.category; + if (!packageInfo) { + switch (id) { + case 'a629c0dafac54c7e931a7174223f14c8': // AudioDB + case '8c95c4d2e50c4fb0a4f36c06ff0f9a1a': // MusicBrainz + category = PluginCategory.Music; + break; + case 'a628c0dafac54c7e9d1a7134223f14c8': // OMDb + case 'b8715ed16c4745289ad3f72deb539cd4': // TMDb + category = PluginCategory.MoviesAndShows; + break; + case '872a78491171458da6fb3de3d442ad30': // Studio Images + category = PluginCategory.General; + } + } + return { canUninstall: !!pluginInfo?.CanUninstall, - category: packageInfo?.category, + category, description: pluginInfo?.Description || packageInfo?.description || packageInfo?.overview, id, imageUrl: imageUrl || packageInfo?.imageUrl || undefined, diff --git a/src/apps/dashboard/features/plugins/constants/categoryLabels.ts b/src/apps/dashboard/features/plugins/constants/categoryLabels.ts index 79867ff1ab..1e301174a1 100644 --- a/src/apps/dashboard/features/plugins/constants/categoryLabels.ts +++ b/src/apps/dashboard/features/plugins/constants/categoryLabels.ts @@ -1,15 +1,14 @@ +import { PluginCategory } from './pluginCategory'; + /** A mapping of category names used by the plugin repository to translation keys. */ -export const CATEGORY_LABELS: Record = { - Administration: 'HeaderAdmin', - General: 'General', - Anime: 'Anime', - // Authentication: 'LabelAuthProvider', // Legacy - Books: 'Books', - // Channel: 'Channels', // Unused? - LiveTV: 'LiveTV', - // Metadata: 'LabelMetadata', // Legacy - MoviesAndShows: 'MoviesAndShows', - Music: 'TabMusic', - Subtitles: 'Subtitles', - Other: 'Other' +export const CATEGORY_LABELS: Record = { + [PluginCategory.Administration]: 'HeaderAdmin', + [PluginCategory.General]: 'General', + [PluginCategory.Anime]: 'Anime', + [PluginCategory.Books]: 'Books', + [PluginCategory.LiveTV]: 'LiveTV', + [PluginCategory.MoviesAndShows]: 'MoviesAndShows', + [PluginCategory.Music]: 'TabMusic', + [PluginCategory.Subtitles]: 'Subtitles', + [PluginCategory.Other]: 'Other' }; diff --git a/src/apps/dashboard/features/plugins/constants/pluginCategory.ts b/src/apps/dashboard/features/plugins/constants/pluginCategory.ts new file mode 100644 index 0000000000..9da85de96c --- /dev/null +++ b/src/apps/dashboard/features/plugins/constants/pluginCategory.ts @@ -0,0 +1,12 @@ +/** Supported plugin category values. */ +export enum PluginCategory { + Administration = 'Administration', + General = 'General', + Anime = 'Anime', + Books = 'Books', + LiveTV = 'LiveTV', + MoviesAndShows = 'MoviesAndShows', + Music = 'Music', + Subtitles = 'Subtitles', + Other = 'Other' +} diff --git a/src/apps/dashboard/routes/plugins/catalog.tsx b/src/apps/dashboard/routes/plugins/catalog.tsx index e550963387..cd98de6721 100644 --- a/src/apps/dashboard/routes/plugins/catalog.tsx +++ b/src/apps/dashboard/routes/plugins/catalog.tsx @@ -1,20 +1,22 @@ +import Settings from '@mui/icons-material/Settings'; +import Box from '@mui/material/Box'; +import Grid from '@mui/material/Grid2'; +import IconButton from '@mui/material/IconButton'; +import Stack from '@mui/material/Stack'; +import TextField from '@mui/material/TextField'; +import Typography from '@mui/material/Typography'; import React, { useCallback, useMemo, useState } from 'react'; +import { Link } from 'react-router-dom'; + +import { usePackages } from 'apps/dashboard/features/plugins/api/usePackages'; +import PackageCard from 'apps/dashboard/features/plugins/components/PackageCard'; +import { PluginCategory } from 'apps/dashboard/features/plugins/constants/pluginCategory'; +import { CATEGORY_LABELS } from 'apps/dashboard/features/plugins/constants/categoryLabels'; +import getPackageCategories from 'apps/dashboard/features/plugins/utils/getPackageCategories'; +import getPackagesByCategory from 'apps/dashboard/features/plugins/utils/getPackagesByCategory'; +import Loading from 'components/loading/LoadingComponent'; import Page from 'components/Page'; import globalize from 'lib/globalize'; -import Box from '@mui/material/Box'; -import Typography from '@mui/material/Typography'; -import { usePackages } from 'apps/dashboard/features/plugins/api/usePackages'; -import Loading from 'components/loading/LoadingComponent'; -import getPackageCategories from 'apps/dashboard/features/plugins/utils/getPackageCategories'; -import Stack from '@mui/material/Stack'; -import getPackagesByCategory from 'apps/dashboard/features/plugins/utils/getPackagesByCategory'; -import PackageCard from 'apps/dashboard/features/plugins/components/PackageCard'; -import Grid from '@mui/material/Grid2'; -import TextField from '@mui/material/TextField'; -import IconButton from '@mui/material/IconButton'; -import Settings from '@mui/icons-material/Settings'; -import { Link } from 'react-router-dom'; -import { CATEGORY_LABELS } from 'apps/dashboard/features/plugins/constants/categoryLabels'; export const Component = () => { const { data: packages, isPending: isPackagesPending } = usePackages(); @@ -31,7 +33,7 @@ export const Component = () => { }, []); const getCategoryLabel = (category: string) => { - const categoryKey = category.replace(/\s/g, ''); + const categoryKey = category.replace(/\s/g, '') as PluginCategory; if (CATEGORY_LABELS[categoryKey]) { return globalize.translate(CATEGORY_LABELS[categoryKey]); diff --git a/src/apps/dashboard/routes/plugins/index.tsx b/src/apps/dashboard/routes/plugins/index.tsx index 2341e31ac5..e85ad9e851 100644 --- a/src/apps/dashboard/routes/plugins/index.tsx +++ b/src/apps/dashboard/routes/plugins/index.tsx @@ -1,34 +1,42 @@ +import Settings from '@mui/icons-material/Settings'; import Alert from '@mui/material/Alert'; import Box from '@mui/material/Box'; +import Chip from '@mui/material/Chip'; +import Divider from '@mui/material/Divider'; import Grid from '@mui/material/Grid2'; +import IconButton from '@mui/material/IconButton/IconButton'; import Stack from '@mui/material/Stack'; import Typography from '@mui/material/Typography'; import React, { useCallback, useMemo, useState } from 'react'; +import { Link } from 'react-router-dom'; +import SearchInput from 'apps/dashboard/components/SearchInput'; +import { usePluginDetails } from 'apps/dashboard/features/plugins/api/usePluginDetails'; import PluginCard from 'apps/dashboard/features/plugins/components/PluginCard'; +import { PluginCategory } from 'apps/dashboard/features/plugins/constants/pluginCategory'; +import { CATEGORY_LABELS } from 'apps/dashboard/features/plugins/constants/categoryLabels'; import Loading from 'components/loading/LoadingComponent'; import Page from 'components/Page'; import globalize from 'lib/globalize'; -import { usePluginDetails } from 'apps/dashboard/features/plugins/api/usePluginDetails'; -import { Link } from 'react-router-dom'; -import IconButton from '@mui/material/IconButton/IconButton'; -import Settings from '@mui/icons-material/Settings'; -import Chip from '@mui/material/Chip'; -import Divider from '@mui/material/Divider'; -import { CATEGORY_LABELS } from 'apps/dashboard/features/plugins/constants/categoryLabels'; -import SearchInput from 'apps/dashboard/components/SearchInput'; +/** + * The list of primary/main categories. + * Any category not in this list will be added to the "other" category. + */ const MAIN_CATEGORIES = [ - 'administration', - 'general', - 'anime', - 'books', - 'livetv', - 'moviesandshows', - 'music', - 'subtitles' + PluginCategory.Administration.toLowerCase(), + PluginCategory.General.toLowerCase(), + PluginCategory.Anime.toLowerCase(), + PluginCategory.Books.toLowerCase(), + PluginCategory.LiveTV.toLowerCase(), + PluginCategory.MoviesAndShows.toLowerCase(), + PluginCategory.Music.toLowerCase(), + PluginCategory.Subtitles.toLowerCase() ]; +/** The installed meta category. */ +const INSTALLED_CATEGORY = 'installed'; + export const Component = () => { const { data: pluginDetails, @@ -47,18 +55,19 @@ export const Component = () => { let filtered = pluginDetails; if (category) { - if (category === 'installed') { + if (category === INSTALLED_CATEGORY) { + // Installed plugins will have a status filtered = filtered.filter(p => p.status); - } else if (category === 'other') { + } else if (category === PluginCategory.Other.toLowerCase()) { filtered = filtered.filter(p => ( - p.category && !MAIN_CATEGORIES.includes(p.category.toLocaleLowerCase()) + p.category && !MAIN_CATEGORIES.includes(p.category.toLowerCase()) )); } else { - filtered = filtered.filter(p => p.category?.toLocaleLowerCase() === category); + filtered = filtered.filter(p => p.category?.toLowerCase() === category); } } return filtered - .filter(i => i.name?.toLocaleLowerCase().includes(searchQuery.toLocaleLowerCase())); + .filter(i => i.name?.toLowerCase().includes(searchQuery.toLowerCase())); } else { return []; } @@ -147,21 +156,21 @@ export const Component = () => { /> setCategory('installed')} + onClick={() => setCategory(INSTALLED_CATEGORY)} label={globalize.translate('LabelInstalled')} /> - {Object.keys(CATEGORY_LABELS).map(c => ( + {Object.values(PluginCategory).map(c => ( setCategory(c.toLocaleLowerCase())} - label={globalize.translate(CATEGORY_LABELS[c])} + onClick={() => setCategory(c.toLowerCase())} + label={globalize.translate(CATEGORY_LABELS[c as PluginCategory])} /> ))}