diff --git a/frontend/src/components/HomePage/PluginCard.tsx b/frontend/src/components/HomePage/PluginCard.tsx index 21e0bff46..add5be2b2 100644 --- a/frontend/src/components/HomePage/PluginCard.tsx +++ b/frontend/src/components/HomePage/PluginCard.tsx @@ -73,6 +73,10 @@ export function PluginCard({ const { t } = useTranslation(['pluginData']); const plausible = usePlausible(); + if (!plugin.name) { + return null; + } + return ( ([ 'writerSaveLayers', ]); -const logger = new Logger('MetadataListMetadataItem.ts'); - /** * Component for rendering a metadata value. */ @@ -112,27 +105,9 @@ export function MetadataListMetadataItem({ className, metadataKey, }: Props) { + const { licenses } = usePluginState(); const valueLabel = useMetadataValueLabel(metadataKey, value); - // Fetch SDPX license data to check if current license is OSI Approved. - const { data: licenses } = useQuery( - ['spdx'], - async () => { - const { data } = await spdxLicenseDataAPI.get(''); - return data.licenses; - }, - { - enabled: metadataKey === 'license', - onError(err) { - logger.error({ - message: - 'Error fetching spdx license data for MetadataListMetadataItem', - error: getErrorMessage(err), - }); - }, - }, - ); - const isOsiApproved = useMemo( () => metadataKey === 'license' && diff --git a/frontend/src/components/SearchPage/PluginSearchResult.tsx b/frontend/src/components/SearchPage/PluginSearchResult.tsx index e88ec8430..15c808dc0 100644 --- a/frontend/src/components/SearchPage/PluginSearchResult.tsx +++ b/frontend/src/components/SearchPage/PluginSearchResult.tsx @@ -344,7 +344,7 @@ export function PluginSearchResult({ ); // Convert to link when loading so that user can't click on result. - if (isLoading) { + if (isLoading || !plugin.name) { return (
{renderResult()} diff --git a/frontend/src/context/plugin.tsx b/frontend/src/context/plugin.tsx index 354d1a894..a7f86699a 100644 --- a/frontend/src/context/plugin.tsx +++ b/frontend/src/context/plugin.tsx @@ -5,6 +5,7 @@ import { createContext, ReactNode, useContext } from 'react'; import { DeepPartial } from 'utility-types'; import { SUPPORTED_PYTHON_VERSIONS } from '@/store/search/filter.store'; +import { SpdxLicenseData } from '@/store/search/types'; import { HubDimension, PluginData, @@ -20,6 +21,7 @@ import { formatDate } from '@/utils'; * Shared state for plugin data. */ interface PluginState { + licenses?: SpdxLicenseData[]; plugin?: DeepPartial; repo: PluginRepoData; repoFetchError?: PluginRepoFetchError; diff --git a/frontend/src/hooks/usePageTransitions.ts b/frontend/src/hooks/usePageTransitions.ts index 10d83646a..3e6112f22 100644 --- a/frontend/src/hooks/usePageTransitions.ts +++ b/frontend/src/hooks/usePageTransitions.ts @@ -22,6 +22,10 @@ interface RouteEvent { shallow: boolean; } +interface RouteError extends Error { + cancelled: boolean; +} + /** * Hook to manage page transitions effects and state. */ @@ -62,10 +66,13 @@ export function usePageTransitions() { pageTransitionsStore.loading = false; } - const onError = (error: Error, url: string, event: RouteEvent) => { - logger.error({ + const onError = (error: RouteError, url: string, event: RouteEvent) => { + const level = error.cancelled ? 'info' : 'error'; + + logger[level]({ message: 'Error loading route', error: getErrorMessage(error), + cancelled: error.cancelled, }); onFinishLoading(url, event); diff --git a/frontend/src/pages/plugins/[name].tsx b/frontend/src/pages/plugins/[name].tsx index 58994c392..8ca7872e4 100644 --- a/frontend/src/pages/plugins/[name].tsx +++ b/frontend/src/pages/plugins/[name].tsx @@ -8,10 +8,12 @@ import { PluginPage } from '@/components/PluginPage'; import { DEFAULT_REPO_DATA } from '@/constants/plugin'; import { useLoadingState } from '@/context/loading'; import { PluginStateProvider } from '@/context/plugin'; +import { SpdxLicenseData, SpdxLicenseResponse } from '@/store/search/types'; import { PluginData } from '@/types'; import { createUrl, fetchRepoData, FetchRepoDataResult, Logger } from '@/utils'; import { getErrorMessage } from '@/utils/error'; import { hubAPI } from '@/utils/HubAPIClient'; +import { spdxLicenseDataAPI } from '@/utils/spdx'; import { getServerSidePropsHandler } from '@/utils/ssr'; /** @@ -24,6 +26,7 @@ interface Params extends ParsedUrlQuery { interface BaseProps { error?: string; plugin?: PluginData; + licenses?: SpdxLicenseData[]; } type Props = FetchRepoDataResult & BaseProps; @@ -69,6 +72,20 @@ export const getServerSideProps = getServerSidePropsHandler({ }); } + try { + const { + data: { licenses }, + } = await spdxLicenseDataAPI.get(''); + props.licenses = licenses; + } catch (err) { + props.error = getErrorMessage(err); + + logger.error({ + message: 'Failed to fetch spdx license data', + error: props.error, + }); + } + return { props }; }, }); @@ -77,7 +94,13 @@ export const getServerSideProps = getServerSidePropsHandler({ * This page fetches plugin data from the hub API and renders it in the * PluginDetails component. */ -export default function Plugin({ error, plugin, repo, repoFetchError }: Props) { +export default function Plugin({ + error, + licenses, + plugin, + repo, + repoFetchError, +}: Props) { const isLoading = useLoadingState(); const [t] = useTranslation(['pageTitles', 'pluginPage']); @@ -127,6 +150,7 @@ export default function Plugin({ error, plugin, repo, repoFetchError }: Props) { <> {plugin ? ( { try { const data = await hubAPI.getPluginIndex(); - return data.map((plugin) => { - const url = `/plugins/${plugin.name}`; - const lastmod = new Date(plugin.release_date).toISOString(); - - return { - url, - lastmod, - name: plugin.display_name ?? plugin.name, - type: SitemapCategory.Plugin, - }; - }); + return data + .filter((plugin) => !!plugin.name) + .map((plugin) => { + const url = `/plugins/${plugin.name}`; + const lastmod = new Date(plugin.release_date).toISOString(); + + return { + url, + lastmod, + name: plugin.display_name ?? plugin.name, + type: SitemapCategory.Plugin, + }; + }); } catch (err) { logger.error({ message: 'Unable to fetch plugin list',