From 50cdbf4adf31fbb29c8915a547eb46e3ed3d248d Mon Sep 17 00:00:00 2001 From: Anders Nomerstad Date: Mon, 9 Sep 2024 16:57:33 +0200 Subject: [PATCH 1/2] Url'er til layer-spesifikke XP assets --- .../static/IllustrationStatic.tsx | 22 ++++++++++---- src/components/_common/image/XpImage.tsx | 4 +-- .../_common/parsed-html/ParsedHtml.tsx | 4 +-- .../_common/qbrick-video/QbrickVideo.tsx | 4 ++- .../parts/link-panel/LinkPanelPart.tsx | 9 ++---- src/utils/fetch/fetch-page-props.ts | 2 +- src/utils/languages.ts | 10 +++++++ src/utils/urls.ts | 30 ++++++++++++++++--- src/utils/usePublicUrl.ts | 28 +++++++++++++---- 9 files changed, 86 insertions(+), 27 deletions(-) diff --git a/src/components/_common/illustration/static/IllustrationStatic.tsx b/src/components/_common/illustration/static/IllustrationStatic.tsx index f405a034c..8e93f0505 100644 --- a/src/components/_common/illustration/static/IllustrationStatic.tsx +++ b/src/components/_common/illustration/static/IllustrationStatic.tsx @@ -6,6 +6,7 @@ import { classNames } from 'utils/classnames'; import { buildImageCacheUrl, NextImageProps } from 'components/_common/image/NextImage'; import { XpImage } from 'components/_common/image/XpImage'; import { useSWRImmutableOnScrollIntoView } from 'utils/fetch/useSWRImmutableOnScrollIntoView'; +import { Language } from 'translations'; import styleCommon from 'components/_common/illustration/Illustration.module.scss'; import styleStatic from './IllustrationStatic.module.scss'; @@ -16,6 +17,7 @@ type ValidIcon = DefinedIcon & Required>; type StaticIconProps = { icon: ValidIcon; isEditorView: boolean; + language: Language; className?: string; }; @@ -31,13 +33,13 @@ const fetchSvgData = (url: string) => .then((res) => (res.ok ? res.text() : null)) .catch((_) => null); -const SvgIcon = ({ icon, isEditorView, className }: StaticIconProps) => { +const SvgIcon = ({ icon, isEditorView, className, language }: StaticIconProps) => { const elementId = useId(); const { data: svgData } = useSWRImmutableOnScrollIntoView({ url: buildImageCacheUrl({ ...nextImageProps, - src: getMediaUrl(icon.mediaUrl, isEditorView), + src: getMediaUrl(icon.mediaUrl, isEditorView, language), isEditorView, }), fetchFunc: fetchSvgData, @@ -80,7 +82,7 @@ type Props = { }; export const IllustrationStatic = ({ illustration, className }: Props) => { - const { editorView } = usePageContentProps(); + const { editorView, language } = usePageContentProps(); if (!illustration) { return null; @@ -96,10 +98,20 @@ export const IllustrationStatic = ({ illustration, className }: Props) => { return ( {isValidIcon(icon1?.icon) && ( - + )} {isValidIcon(icon2?.icon) && ( - + )} ); diff --git a/src/components/_common/image/XpImage.tsx b/src/components/_common/image/XpImage.tsx index 550f51385..3a8126460 100644 --- a/src/components/_common/image/XpImage.tsx +++ b/src/components/_common/image/XpImage.tsx @@ -11,9 +11,9 @@ type Props = { NextImageProps; export const XpImage = ({ imageProps, alt, ...rest }: Props) => { - const { editorView } = usePageContentProps(); + const { editorView, language } = usePageContentProps(); - const imageUrl = getMediaUrl(imageProps.mediaUrl, !!editorView); + const imageUrl = getMediaUrl(imageProps.mediaUrl, !!editorView, language); if (!imageUrl) { return null; } diff --git a/src/components/_common/parsed-html/ParsedHtml.tsx b/src/components/_common/parsed-html/ParsedHtml.tsx index 5a7bf3a09..048821472 100644 --- a/src/components/_common/parsed-html/ParsedHtml.tsx +++ b/src/components/_common/parsed-html/ParsedHtml.tsx @@ -78,7 +78,7 @@ type Props = { }; export const ParsedHtml = ({ htmlProps }: Props) => { - const { editorView } = usePageContentProps(); + const { editorView, language } = usePageContentProps(); if (!htmlProps) { return null; @@ -123,7 +123,7 @@ export const ParsedHtml = ({ htmlProps }: Props) => { ); } diff --git a/src/components/_common/qbrick-video/QbrickVideo.tsx b/src/components/_common/qbrick-video/QbrickVideo.tsx index 745424d54..ad0680b36 100644 --- a/src/components/_common/qbrick-video/QbrickVideo.tsx +++ b/src/components/_common/qbrick-video/QbrickVideo.tsx @@ -36,7 +36,9 @@ export const QbrickVideo = (props: QbrickVideoProps) => { const durationAsString = getTimestampFromDuration(duration); - const imageUrl = poster?.startsWith('http') ? poster : getMediaUrl(poster, !!editorView); + const imageUrl = poster?.startsWith('http') + ? poster + : getMediaUrl(poster, !!editorView, contentLanguage); return (
diff --git a/src/components/parts/link-panel/LinkPanelPart.tsx b/src/components/parts/link-panel/LinkPanelPart.tsx index d71459e75..bf2359214 100644 --- a/src/components/parts/link-panel/LinkPanelPart.tsx +++ b/src/components/parts/link-panel/LinkPanelPart.tsx @@ -28,7 +28,7 @@ export type PartConfigLinkPanel = { } & LinkWithIngressMixin; export const LinkPanelPart = ({ config }: PartComponentProps) => { - const { editorView } = usePageContentProps(); + const { editorView, language } = usePageContentProps(); if (!config) { return ; @@ -40,7 +40,7 @@ export const LinkPanelPart = ({ config }: PartComponentProps const linkProps = getSelectableLinkProps(link); - const bgUrl = background?.mediaUrl && getMediaUrl(background.mediaUrl, isEditorView); + const bgUrl = background?.mediaUrl && getMediaUrl(background.mediaUrl, isEditorView, language); const selectedVariant = variant?._selected; const variantConfig = selectedVariant && variant[selectedVariant]; @@ -86,10 +86,7 @@ export const LinkPanelPart = ({ config }: PartComponentProps }), }} > - +
)} diff --git a/src/utils/fetch/fetch-page-props.ts b/src/utils/fetch/fetch-page-props.ts index d6640717a..02bc5d968 100644 --- a/src/utils/fetch/fetch-page-props.ts +++ b/src/utils/fetch/fetch-page-props.ts @@ -68,7 +68,7 @@ export const fetchPageProps = async ({ // Media content should redirect to the mediaUrl generated by XP (temporary redirect) if (isMediaContent(content)) { - const mediaUrl = getMediaUrl(content.mediaUrl, isDraft); + const mediaUrl = getMediaUrl(content.mediaUrl, isDraft, content.language); if (!mediaUrl) { return notFoundProps; } diff --git a/src/utils/languages.ts b/src/utils/languages.ts index 2a8678034..9edeae729 100644 --- a/src/utils/languages.ts +++ b/src/utils/languages.ts @@ -1,5 +1,6 @@ import { ContentProps } from 'types/content-props/_content-common'; import { LanguageProps } from 'types/language'; +import { Language } from 'translations'; export const getContentLanguages = (content: ContentProps): LanguageProps[] => content.languages || []; @@ -7,3 +8,12 @@ export const getContentLanguages = (content: ContentProps): LanguageProps[] => const norwegianLanguagesSet: ReadonlySet = new Set(['no', 'nn', 'nb']); export const isNorwegianLanguage = (language: string) => norwegianLanguagesSet.has(language); + +export const pageLanguageToLayerLanguage: { [key in Language]?: Language } = { + nn: 'nn', + se: 'se', + en: 'en', + ru: 'en', + uk: 'en', + pl: 'en', +} as const; diff --git a/src/utils/urls.ts b/src/utils/urls.ts index 6edddc9bb..2c1cae5f2 100644 --- a/src/utils/urls.ts +++ b/src/utils/urls.ts @@ -1,5 +1,7 @@ import { ContentProps } from 'types/content-props/_content-common'; import { logger } from 'srcCommon/logger'; +import { Language } from 'translations'; +import { pageLanguageToLayerLanguage } from './languages'; export const appOriginProd = 'https://www.nav.no'; export const xpContentPathPrefix = '/www.nav.no'; @@ -91,10 +93,22 @@ export const getInternalAbsoluteUrl = (url: string, isEditorView: boolean) => { }; // Media url must always be absolute, to prevent internal nextjs routing loopbacks on redirects -export function getMediaUrl(url: string, isEditorView: boolean): string; -export function getMediaUrl(url: string | undefined, isEditorView: boolean): string | undefined; -export function getMediaUrl(url: string | undefined, isEditorView: boolean) { - return url?.replace( +export function getMediaUrl(url: string, isEditorView: boolean, language?: Language): string; +export function getMediaUrl( + url: string | undefined, + isEditorView: boolean, + language?: Language +): string | undefined; +export function getMediaUrl( + url: string | undefined, + isEditorView: boolean, + language: Language = 'no' +) { + if (!url) { + return undefined; + } + + return transformToXpLayerUrl(url, language).replace( internalUrlPrefixPattern, isEditorView ? `${adminOrigin}${xpDraftPathPrefix}` : xpOrigin ); @@ -126,3 +140,11 @@ export const routerQueryToXpPathOrId = (routerQuery: string | string[]) => { return `${xpContentPathPrefix}${path}`; }; + +// Direct links to XP assets or services should point to the appropriate layer for the specified language +// The /_/ repo mappings are defined in the vhost config on the XP servers +export const transformToXpLayerUrl = (url: string, language: Language) => { + const layer = pageLanguageToLayerLanguage[language]; + + return layer ? url.replace('/_', `/_/${layer}`) : url; +}; diff --git a/src/utils/usePublicUrl.ts b/src/utils/usePublicUrl.ts index 8d7de1b64..cf3dffc56 100644 --- a/src/utils/usePublicUrl.ts +++ b/src/utils/usePublicUrl.ts @@ -1,21 +1,37 @@ import { usePageContentProps } from 'store/pageContext'; -import { getInternalRelativePath, isAppUrl, isInternalUrl, stripXpPathPrefix } from './urls'; +import { + getInternalRelativePath, + isAppUrl, + isXpUrl, + stripXpPathPrefix, + transformToXpLayerUrl, +} from './urls'; type ReturnValue = { url: string; - canRouteClientSide: boolean; // If this is true, navigation to the url can be done client-side with next router + canRouteClientSide: boolean; // If true, navigation to the url can be done client-side with next router }; export const usePublicUrl = (href: string): ReturnValue => { - const { editorView } = usePageContentProps(); + const { editorView, language } = usePageContentProps(); - if (isInternalUrl(href)) { + if (isXpUrl(href)) { + return { + url: transformToXpLayerUrl(href, language), + canRouteClientSide: false, + }; + } + + if (isAppUrl(href)) { const internalPath = getInternalRelativePath(href, !!editorView); return { url: internalPath, - canRouteClientSide: isAppUrl(internalPath), + canRouteClientSide: true, }; } - return { url: stripXpPathPrefix(href) || '/', canRouteClientSide: false }; + return { + url: stripXpPathPrefix(href) || '/', + canRouteClientSide: false, + }; }; From 05265c2a06a23e31c6f375d586058921ddbaade6 Mon Sep 17 00:00:00 2001 From: Anders Nomerstad Date: Tue, 10 Sep 2024 10:13:25 +0200 Subject: [PATCH 2/2] Fikser fil-lenker fra CS --- src/utils/urls.ts | 7 ++++--- src/utils/usePublicUrl.ts | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/utils/urls.ts b/src/utils/urls.ts index 2c1cae5f2..179f314c2 100644 --- a/src/utils/urls.ts +++ b/src/utils/urls.ts @@ -108,7 +108,7 @@ export function getMediaUrl( return undefined; } - return transformToXpLayerUrl(url, language).replace( + return transformToXpLayerUrl(url, isEditorView, language).replace( internalUrlPrefixPattern, isEditorView ? `${adminOrigin}${xpDraftPathPrefix}` : xpOrigin ); @@ -143,8 +143,9 @@ export const routerQueryToXpPathOrId = (routerQuery: string | string[]) => { // Direct links to XP assets or services should point to the appropriate layer for the specified language // The /_/ repo mappings are defined in the vhost config on the XP servers -export const transformToXpLayerUrl = (url: string, language: Language) => { +export const transformToXpLayerUrl = (url: string, isEditorView: boolean, language: Language) => { + const path = getInternalRelativePath(url, isEditorView); const layer = pageLanguageToLayerLanguage[language]; - return layer ? url.replace('/_', `/_/${layer}`) : url; + return layer ? path.replace(/^\/_/, `/_/${layer}`) : path; }; diff --git a/src/utils/usePublicUrl.ts b/src/utils/usePublicUrl.ts index cf3dffc56..8bcecb88e 100644 --- a/src/utils/usePublicUrl.ts +++ b/src/utils/usePublicUrl.ts @@ -17,7 +17,7 @@ export const usePublicUrl = (href: string): ReturnValue => { if (isXpUrl(href)) { return { - url: transformToXpLayerUrl(href, language), + url: transformToXpLayerUrl(href, !!editorView, language), canRouteClientSide: false, }; }