From f1c64be7ce709e917ebeb1cd06c7739842ce6cd5 Mon Sep 17 00:00:00 2001 From: Olga Bulat Date: Sat, 2 Dec 2023 13:54:21 +0300 Subject: [PATCH] Fix server rendering Signed-off-by: Olga Bulat --- frontend/src/components/VCollectionPage.vue | 35 ++++++- frontend/src/locales/scripts/en.json5 | 14 +++ frontend/src/middleware/collection.ts | 15 +++ frontend/src/pages/audio/source/_.vue | 30 +++--- frontend/src/pages/audio/tag/_tag.vue | 30 +++--- frontend/src/pages/image/source/_.vue | 29 +++--- frontend/src/pages/image/tag/_tag.vue | 34 +++---- frontend/src/utils/parse-collection-path.ts | 24 +++++ .../src/utils/validate-collection-params.ts | 99 ------------------- .../utils/validate-collection-params.spec.js | 81 +++++---------- 10 files changed, 158 insertions(+), 233 deletions(-) create mode 100644 frontend/src/middleware/collection.ts create mode 100644 frontend/src/utils/parse-collection-path.ts delete mode 100644 frontend/src/utils/validate-collection-params.ts diff --git a/frontend/src/components/VCollectionPage.vue b/frontend/src/components/VCollectionPage.vue index de73c406255..023454ba553 100644 --- a/frontend/src/components/VCollectionPage.vue +++ b/frontend/src/components/VCollectionPage.vue @@ -9,14 +9,14 @@ /> mediaStore.fetchState) @@ -64,11 +67,39 @@ export default defineComponent({ const searchStore = useSearchStore() const collectionParams = computed(() => searchStore.collectionParams) + const collectionLabel = computed(() => { + const collection = collectionParams.value?.collection + switch (collection) { + case "tag": + return i18n + .t(`collection.ariaLabel.tag.${props.mediaType}`, { + tag: collectionParams.value?.tag, + }) + .toString() + case "source": + return i18n + .t(`collection.ariaLabel.source.${props.mediaType}`, { + source: collectionParams.value?.source, + }) + .toString() + case "creator": + return i18n + .t(`collection.ariaLabel.creator.${props.mediaType}`, { + creator: collectionParams.value?.creator, + source: collectionParams.value?.source, + }) + .toString() + default: + return "" + } + }) + return { fetchState, results, creatorUrl, collectionParams, + collectionLabel, } }, }) diff --git a/frontend/src/locales/scripts/en.json5 b/frontend/src/locales/scripts/en.json5 index 494d33b9f9c..0fabb7a102f 100644 --- a/frontend/src/locales/scripts/en.json5 +++ b/frontend/src/locales/scripts/en.json5 @@ -774,6 +774,20 @@ source: "Open source site", creator: "Open creator page", }, + ariaLabel: { + creator: { + audio: "Audio files by {creator} in {source}", + image: "Images by {creator} in {source}", + }, + source: { + audio: "Audio files from {source}", + image: "Images from {source}", + }, + tag: { + audio: "Audio files with the tag {tag}", + image: "Images with the tag {tag}", + }, + }, resultCountLabel: { creator: { audio: { diff --git a/frontend/src/middleware/collection.ts b/frontend/src/middleware/collection.ts new file mode 100644 index 00000000000..8441199e59a --- /dev/null +++ b/frontend/src/middleware/collection.ts @@ -0,0 +1,15 @@ +import { useFeatureFlagStore } from "~/stores/feature-flag" + +import type { Middleware } from "@nuxt/types" + +export const collectionMiddleware: Middleware = async ({ + $pinia, + error: nuxtError, +}) => { + if (!useFeatureFlagStore($pinia).isOn("additional_search_views")) { + nuxtError({ + statusCode: 404, + message: "Additional search views are not enabled", + }) + } +} diff --git a/frontend/src/pages/audio/source/_.vue b/frontend/src/pages/audio/source/_.vue index b52b74d50a3..490680007cb 100644 --- a/frontend/src/pages/audio/source/_.vue +++ b/frontend/src/pages/audio/source/_.vue @@ -3,12 +3,13 @@ diff --git a/frontend/src/pages/audio/tag/_tag.vue b/frontend/src/pages/audio/tag/_tag.vue index 2270456780d..d2fde052828 100644 --- a/frontend/src/pages/audio/tag/_tag.vue +++ b/frontend/src/pages/audio/tag/_tag.vue @@ -3,12 +3,13 @@ diff --git a/frontend/src/pages/image/source/_.vue b/frontend/src/pages/image/source/_.vue index dfc5618fa96..e5799ea3ccc 100644 --- a/frontend/src/pages/image/source/_.vue +++ b/frontend/src/pages/image/source/_.vue @@ -3,12 +3,13 @@ diff --git a/frontend/src/pages/image/tag/_tag.vue b/frontend/src/pages/image/tag/_tag.vue index baf4ef73afb..b834814d8df 100644 --- a/frontend/src/pages/image/tag/_tag.vue +++ b/frontend/src/pages/image/tag/_tag.vue @@ -3,12 +3,13 @@ diff --git a/frontend/src/utils/parse-collection-path.ts b/frontend/src/utils/parse-collection-path.ts new file mode 100644 index 00000000000..0c3cd19e19e --- /dev/null +++ b/frontend/src/utils/parse-collection-path.ts @@ -0,0 +1,24 @@ +import { CreatorCollection, SourceCollection } from "~/types/search" + +export function parseCollectionPath( + pathMatch: string +): SourceCollection | CreatorCollection | null { + // Build collection params. + // pathMatch is the part of the path after the collection name: + //`/sourceName` or `/sourceName/creator/creatorName`. + const pathMatchParts = pathMatch + .split("/") + .map((part) => part.trim()) + .filter((part) => part !== "") + + if (pathMatchParts.length === 1) { + return { collection: "source", source: pathMatchParts[0] } + } else if (pathMatchParts.length === 3 && pathMatchParts[1] === "creator") { + return { + collection: "creator", + creator: pathMatchParts[2], + source: pathMatchParts[0], + } + } + return null +} diff --git a/frontend/src/utils/validate-collection-params.ts b/frontend/src/utils/validate-collection-params.ts deleted file mode 100644 index 41ea937c4b7..00000000000 --- a/frontend/src/utils/validate-collection-params.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { Context } from "@nuxt/types" -import { Pinia } from "pinia" - -import { SupportedMediaType } from "~/constants/media" -import { - CollectionParams, - CreatorCollection, - SourceCollection, -} from "~/types/search" -import { useProviderStore } from "~/stores/provider" -import { warn } from "~/utils/console" -import { useFeatureFlagStore } from "~/stores/feature-flag" - -/** - * Validate the params for a collection page. - * - * @param firstParam - The first param in the path, either "tag" or "source". - * If the first param is "source", the collection is either a source collection - * or a creator collection. - * @param mediaType - The media type of the collection. - * @param params - `params.pathMatch` is the part of the path after the collection name: - * `/sourceName` or `/sourceName/creator/creatorName`. - * @param $pinia - Pinia instance, necessary to check the feature flag, and validate the - * sources using providerStore. - */ -export function validateCollectionParams({ - firstParam, - mediaType, - params, - $pinia, -}: { - firstParam: "tag" | "source" - mediaType: SupportedMediaType - params: Context["params"] - $pinia: Pinia -}): CollectionParams | null { - // This page is shown only when the feature flag is `on`. - if (!useFeatureFlagStore($pinia).isOn("additional_search_views")) { - return null - } - return _validate({ firstParam, mediaType, params, $pinia }) -} - -function parseCollectionPath( - pathMatch: string -): SourceCollection | CreatorCollection | null { - // Build collection params. - // pathMatch is the part of the path after the collection name: - //`/sourceName` or `/sourceName/creator/creatorName`. - const pathMatchParts = pathMatch - .split("/") - .map((part) => part.trim()) - .filter((part) => part !== "") - - if (pathMatchParts.length === 1) { - return { collection: "source", source: pathMatchParts[0] } - } else if (pathMatchParts.length === 3 && pathMatchParts[1] === "creator") { - return { - collection: "creator", - creator: pathMatchParts[2], - source: pathMatchParts[0], - } - } - return null -} - -export function _validate({ - firstParam, - mediaType, - params, - $pinia, -}: { - firstParam: "tag" | "source" - mediaType: SupportedMediaType - params: Context["params"] - $pinia: Pinia -}): CollectionParams | null { - if (firstParam === "tag") { - return params.tag ? { collection: "tag", tag: params.tag } : null - } - - const collectionParams = parseCollectionPath(params.pathMatch) - if (!collectionParams) { - return null - } - // Validate source param - if ( - !useProviderStore($pinia).isSourceNameValid( - mediaType, - collectionParams.source - ) - ) { - warn( - `Invalid source name "${collectionParams.source}" for a ${collectionParams.collection} collection page.` - ) - return null - } - return collectionParams -} diff --git a/frontend/test/unit/specs/utils/validate-collection-params.spec.js b/frontend/test/unit/specs/utils/validate-collection-params.spec.js index e9a46e941cd..a3d39f7765b 100644 --- a/frontend/test/unit/specs/utils/validate-collection-params.spec.js +++ b/frontend/test/unit/specs/utils/validate-collection-params.spec.js @@ -1,14 +1,8 @@ import { createPinia, setActivePinia } from "~~/test/unit/test-utils/pinia" -import { validateCollectionParams } from "~/utils/validate-collection-params" +import { parseCollectionPath } from "~/utils/parse-collection-path" import { useProviderStore } from "~/stores/provider" import { useFeatureFlagStore } from "~/stores/feature-flag" -import { AUDIO, IMAGE } from "~/constants/media" -import { warn } from "~/utils/console" - -jest.mock("~/utils/console", () => ({ - warn: jest.fn(), -})) describe("validateCollectionParams", () => { /** @type { import("pinia").Pinia } **/ @@ -21,59 +15,34 @@ describe("validateCollectionParams", () => { useFeatureFlagStore().toggleFeature("additional_search_views", "on") }) - it("returns null if feature flag is off", () => { - useFeatureFlagStore().toggleFeature("additional_search_views", "off") - - const result = validateCollectionParams({ - firstParam: "tag", - mediaType: IMAGE, - params: { tag: "nature" }, - $pinia: pinia, - }) + it("returns source collection", () => { + const collection = parseCollectionPath("/flickr") - expect(result).toBeNull() + expect(collection).toEqual({ source: "flickr", collection: "source" }) }) - it.each` - firstParam | mediaType | params | expected - ${"tag"} | ${IMAGE} | ${{ tag: "nature" }} | ${{ collection: "tag", tag: "nature" }} - ${"source"} | ${IMAGE} | ${{ source: "flickr", pathMatch: "/flickr" }} | ${{ collection: "source", source: "flickr" }} - ${"source"} | ${AUDIO} | ${{ source: "freesound", pathMatch: "/freesound" }} | ${{ collection: "source", source: "freesound" }} - ${"source"} | ${IMAGE} | ${{ source: "flickr", pathMatch: "/flickr/creator/creatorName" }} | ${{ collection: "creator", source: "flickr", creator: "creatorName" }} - ${"source"} | ${IMAGE} | ${{ source: "flickr", pathMatch: "/flickr/creator/http%3A%2F%2FcreatorName.com%2Fme" }} | ${{ collection: "creator", source: "flickr", creator: "http%3A%2F%2FcreatorName.com%2Fme" }} - `( - "returns $expected for $firstParam and $mediaType", - ({ firstParam, mediaType, params, expected }) => { - const result = validateCollectionParams({ - firstParam, - mediaType, - params, - $pinia: pinia, - }) + it("returns null if `creator` parameter is blank", () => { + const collection = parseCollectionPath("/flickr/creator/") - expect(result).toEqual(expected) - } - ) - it.each` - firstParam | mediaType | params - ${"source"} | ${IMAGE} | ${{ source: "flickr", pathMatch: "/invalidSourceName/creator/creatorName" }} - ${"source"} | ${AUDIO} | ${{ source: "invalidSourceName", pathMatch: "/invalidSourceName" }} - `( - "returns `null` for invalid source name", - ({ firstParam, mediaType, params }) => { - useProviderStore().isSourceNameValid = jest.fn(() => false) + expect(collection).toBeNull() + }) + it("returns creator collection without trailing slash", () => { + const collection = parseCollectionPath("/flickr/creator/me") - const result = validateCollectionParams({ - firstParam, - mediaType, - params, - $pinia: pinia, - }) + expect(collection).toEqual({ + source: "flickr", + creator: "me", + collection: "creator", + }) + }) + + it("returns creator collection with trailing slash", () => { + const collection = parseCollectionPath("/flickr/creator/me/") - expect(result).toBeNull() - expect(warn).toHaveBeenCalledWith( - 'Invalid source name "invalidSourceName" for a creator collection page.' - ) - } - ) + expect(collection).toEqual({ + source: "flickr", + creator: "me", + collection: "creator", + }) + }) })