@prezly/theme-kit-nextjs 4.2.0-alpha.2 → 4.2.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/client.d.ts +7 -0
- package/build/cjs/client.js +24 -0
- package/build/cjs/client.js.map +1 -0
- package/build/cjs/components-nextjs/PageSeo/PageSeo.js +1 -0
- package/build/cjs/components-nextjs/PageSeo/PageSeo.js.map +1 -1
- package/build/cjs/components-nextjs/StorySeo/StorySeo.js +1 -0
- package/build/cjs/components-nextjs/StorySeo/StorySeo.js.map +1 -1
- package/build/cjs/components-nextjs/document.js +1 -0
- package/build/cjs/components-nextjs/document.js.map +1 -1
- package/build/cjs/newsroom-context/context.js +1 -0
- package/build/cjs/newsroom-context/context.js.map +1 -1
- package/build/cjs/server.d.ts +7 -0
- package/build/cjs/server.js +24 -0
- package/build/cjs/server.js.map +1 -0
- package/build/esm/client.js +8 -0
- package/build/esm/client.js.map +1 -0
- package/build/esm/components-nextjs/PageSeo/PageSeo.js +1 -0
- package/build/esm/components-nextjs/PageSeo/PageSeo.js.map +1 -1
- package/build/esm/components-nextjs/StorySeo/StorySeo.js +1 -0
- package/build/esm/components-nextjs/StorySeo/StorySeo.js.map +1 -1
- package/build/esm/components-nextjs/document.js +1 -0
- package/build/esm/components-nextjs/document.js.map +1 -1
- package/build/esm/newsroom-context/context.js +1 -0
- package/build/esm/newsroom-context/context.js.map +1 -1
- package/build/esm/server.js +8 -0
- package/build/esm/server.js.map +1 -0
- package/build/tsconfig.cjs.tsbuildinfo +1 -1
- package/build/tsconfig.esm.tsbuildinfo +1 -1
- package/build/types/client.d.ts +7 -0
- package/build/types/server.d.ts +7 -0
- package/package.json +18 -6
- package/src/__mocks__/languages.ts +0 -479
- package/src/adapter-nextjs/api-handlers/fetchGalleries.ts +0 -24
- package/src/adapter-nextjs/api-handlers/fetchStories.ts +0 -43
- package/src/adapter-nextjs/api-handlers/index.ts +0 -2
- package/src/adapter-nextjs/getNewsroomServerSideProps.ts +0 -29
- package/src/adapter-nextjs/getNewsroomStaticProps.ts +0 -29
- package/src/adapter-nextjs/index.ts +0 -6
- package/src/adapter-nextjs/page-props/category.ts +0 -156
- package/src/adapter-nextjs/page-props/gallery.ts +0 -114
- package/src/adapter-nextjs/page-props/galleryAlbum.ts +0 -89
- package/src/adapter-nextjs/page-props/home.ts +0 -131
- package/src/adapter-nextjs/page-props/index.ts +0 -26
- package/src/adapter-nextjs/page-props/lib/types.ts +0 -8
- package/src/adapter-nextjs/page-props/search.ts +0 -53
- package/src/adapter-nextjs/page-props/sitemap/SitemapBuilder.ts +0 -56
- package/src/adapter-nextjs/page-props/sitemap/createPaths.ts +0 -21
- package/src/adapter-nextjs/page-props/sitemap/getServerSideProps.ts +0 -45
- package/src/adapter-nextjs/page-props/sitemap/index.ts +0 -5
- package/src/adapter-nextjs/page-props/sitemap/types.ts +0 -5
- package/src/adapter-nextjs/page-props/story.ts +0 -96
- package/src/adapter-nextjs/page-props/storyPreview.ts +0 -33
- package/src/adapter-nextjs/processRequest.ts +0 -50
- package/src/adapter-nextjs/processStaticRequest.ts +0 -32
- package/src/components-nextjs/PageSeo/PageSeo.tsx +0 -132
- package/src/components-nextjs/PageSeo/index.ts +0 -1
- package/src/components-nextjs/PageSeo/lib/getAbsoluteUrl.ts +0 -7
- package/src/components-nextjs/PageSeo/lib/index.ts +0 -1
- package/src/components-nextjs/StorySeo/StorySeo.tsx +0 -87
- package/src/components-nextjs/StorySeo/index.ts +0 -1
- package/src/components-nextjs/document.tsx +0 -37
- package/src/components-nextjs/index.ts +0 -3
- package/src/data-fetching/NextPrezlyApi.ts +0 -57
- package/src/data-fetching/getNextPrezlyApi.ts +0 -23
- package/src/data-fetching/index.ts +0 -1
- package/src/index.ts +0 -8
- package/src/infinite-loading/index.ts +0 -4
- package/src/infinite-loading/types.ts +0 -9
- package/src/infinite-loading/useInfiniteGalleriesLoading.ts +0 -54
- package/src/infinite-loading/useInfiniteLoading.ts +0 -67
- package/src/infinite-loading/useInfiniteStoriesLoading.ts +0 -94
- package/src/intl/index.ts +0 -2
- package/src/intl/languages.test.ts +0 -37
- package/src/intl/languages.ts +0 -40
- package/src/intl/locale.test.ts +0 -42
- package/src/intl/locale.ts +0 -39
- package/src/newsroom-context/context.tsx +0 -114
- package/src/newsroom-context/hooks/index.ts +0 -11
- package/src/newsroom-context/hooks/useAlgoliaSettings.ts +0 -7
- package/src/newsroom-context/hooks/useCategories.ts +0 -7
- package/src/newsroom-context/hooks/useCompanyInformation.ts +0 -7
- package/src/newsroom-context/hooks/useCurrentCategory.ts +0 -7
- package/src/newsroom-context/hooks/useCurrentLocale.ts +0 -7
- package/src/newsroom-context/hooks/useCurrentStory.ts +0 -7
- package/src/newsroom-context/hooks/useGetLinkLocaleSlug.ts +0 -36
- package/src/newsroom-context/hooks/useGetTranslationUrl.ts +0 -99
- package/src/newsroom-context/hooks/useLanguages.ts +0 -7
- package/src/newsroom-context/hooks/useNewsroom.ts +0 -7
- package/src/newsroom-context/hooks/useNewsroomContext.ts +0 -15
- package/src/newsroom-context/index.ts +0 -2
- package/src/sentry/client.ts +0 -10
- package/src/sentry/common.ts +0 -82
- package/src/sentry/index.ts +0 -2
- package/src/sentry/server.ts +0 -10
- package/src/types.ts +0 -16
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { Category, Story } from '@prezly/sdk';
|
|
2
|
-
|
|
3
|
-
export function createPaths(stories: Story[], categories: Category[]) {
|
|
4
|
-
const storiesUrls = stories.map(({ slug }) => `/${slug}`);
|
|
5
|
-
const categoriesUrls = categories
|
|
6
|
-
.map((category) => {
|
|
7
|
-
const translations = Object.values(category.i18n);
|
|
8
|
-
const allSlugs = translations
|
|
9
|
-
.map(({ slug }) => slug || '')
|
|
10
|
-
.filter(Boolean)
|
|
11
|
-
.reduce<string[]>(
|
|
12
|
-
(slugs, slug) => (slugs.includes(slug) ? [...slugs] : [...slugs, slug]),
|
|
13
|
-
[],
|
|
14
|
-
);
|
|
15
|
-
|
|
16
|
-
return allSlugs.map((slug) => `/category/${slug}`);
|
|
17
|
-
})
|
|
18
|
-
.flat();
|
|
19
|
-
|
|
20
|
-
return [...storiesUrls, ...categoriesUrls];
|
|
21
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import type { NextPageContext } from 'next';
|
|
2
|
-
|
|
3
|
-
import { getNextPrezlyApi } from '../../../data-fetching';
|
|
4
|
-
|
|
5
|
-
import { createPaths } from './createPaths';
|
|
6
|
-
import { SitemapBuilder } from './SitemapBuilder';
|
|
7
|
-
|
|
8
|
-
export function getSitemapServerSideProps(
|
|
9
|
-
options: { additionalPaths?: string[]; pinning?: boolean } = {},
|
|
10
|
-
) {
|
|
11
|
-
return async function getServerSideProps(ctx: NextPageContext) {
|
|
12
|
-
const { res, req } = ctx;
|
|
13
|
-
|
|
14
|
-
if (!req || !res) {
|
|
15
|
-
// client side
|
|
16
|
-
return {
|
|
17
|
-
props: {},
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const baseUrl = req.headers.host || '/';
|
|
22
|
-
|
|
23
|
-
const api = getNextPrezlyApi(req);
|
|
24
|
-
const stories = await api.getAllStories({
|
|
25
|
-
pinning: options.pinning ?? false,
|
|
26
|
-
});
|
|
27
|
-
const categories = await api.getCategories();
|
|
28
|
-
|
|
29
|
-
const paths = createPaths(stories, categories);
|
|
30
|
-
const sitemapBuilder = new SitemapBuilder(baseUrl);
|
|
31
|
-
|
|
32
|
-
sitemapBuilder.addUrl('/');
|
|
33
|
-
paths.forEach((path) => sitemapBuilder.addUrl(path));
|
|
34
|
-
|
|
35
|
-
options.additionalPaths?.forEach((path) => sitemapBuilder.addUrl(path));
|
|
36
|
-
|
|
37
|
-
res.setHeader('Content-Type', 'text/xml');
|
|
38
|
-
res.write(sitemapBuilder.serialize());
|
|
39
|
-
res.end();
|
|
40
|
-
|
|
41
|
-
return {
|
|
42
|
-
props: {},
|
|
43
|
-
};
|
|
44
|
-
};
|
|
45
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
GetServerSidePropsContext,
|
|
3
|
-
GetServerSidePropsResult,
|
|
4
|
-
GetStaticPropsContext,
|
|
5
|
-
GetStaticPropsResult,
|
|
6
|
-
} from 'next';
|
|
7
|
-
|
|
8
|
-
import { getNextPrezlyApi } from '../../data-fetching';
|
|
9
|
-
import { DUMMY_DEFAULT_LOCALE } from '../../intl';
|
|
10
|
-
import { getNewsroomServerSideProps } from '../getNewsroomServerSideProps';
|
|
11
|
-
import { getNewsroomStaticProps } from '../getNewsroomStaticProps';
|
|
12
|
-
import { processRequest } from '../processRequest';
|
|
13
|
-
import { processStaticRequest } from '../processStaticRequest';
|
|
14
|
-
|
|
15
|
-
import type { PropsFunction } from './lib/types';
|
|
16
|
-
|
|
17
|
-
export function getStoryPageServerSideProps<CustomProps extends Record<string, any>>(
|
|
18
|
-
customProps: CustomProps | PropsFunction<CustomProps>,
|
|
19
|
-
) {
|
|
20
|
-
return async function getServerSideProps(
|
|
21
|
-
context: GetServerSidePropsContext,
|
|
22
|
-
): Promise<GetServerSidePropsResult<CustomProps>> {
|
|
23
|
-
const api = getNextPrezlyApi(context.req);
|
|
24
|
-
|
|
25
|
-
const { slug } = context.params as { slug?: string };
|
|
26
|
-
const story = slug ? await api.getStoryBySlug(slug) : null;
|
|
27
|
-
if (!story) {
|
|
28
|
-
return { notFound: true };
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const { serverSideProps } = await getNewsroomServerSideProps(context, { story });
|
|
32
|
-
|
|
33
|
-
const { locale } = context;
|
|
34
|
-
if (locale && locale !== DUMMY_DEFAULT_LOCALE) {
|
|
35
|
-
return {
|
|
36
|
-
redirect: {
|
|
37
|
-
destination: `/${slug}`,
|
|
38
|
-
permanent: true,
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
serverSideProps.newsroomContextProps.currentStory = story;
|
|
44
|
-
|
|
45
|
-
return processRequest(context, {
|
|
46
|
-
...serverSideProps,
|
|
47
|
-
...(typeof customProps === 'function'
|
|
48
|
-
? await (customProps as PropsFunction<CustomProps>)(context, serverSideProps)
|
|
49
|
-
: customProps),
|
|
50
|
-
});
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function getStoryPageStaticProps<CustomProps extends Record<string, any>>(
|
|
55
|
-
customProps: CustomProps | PropsFunction<CustomProps, GetStaticPropsContext>,
|
|
56
|
-
) {
|
|
57
|
-
return async function getStaticProps(
|
|
58
|
-
context: GetStaticPropsContext,
|
|
59
|
-
): Promise<GetStaticPropsResult<CustomProps>> {
|
|
60
|
-
const api = getNextPrezlyApi();
|
|
61
|
-
|
|
62
|
-
const { slug } = context.params as { slug?: string };
|
|
63
|
-
const story = slug ? await api.getStoryBySlug(slug) : null;
|
|
64
|
-
if (!story) {
|
|
65
|
-
return { notFound: true };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const { staticProps } = await getNewsroomStaticProps(context, { story });
|
|
69
|
-
|
|
70
|
-
staticProps.newsroomContextProps.currentStory = story;
|
|
71
|
-
|
|
72
|
-
return processStaticRequest(context, {
|
|
73
|
-
...staticProps,
|
|
74
|
-
...(typeof customProps === 'function'
|
|
75
|
-
? await (customProps as PropsFunction<CustomProps, GetStaticPropsContext>)(
|
|
76
|
-
context,
|
|
77
|
-
staticProps,
|
|
78
|
-
)
|
|
79
|
-
: customProps),
|
|
80
|
-
});
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export async function getStoryPageStaticPaths(options: { pinning?: boolean } = {}) {
|
|
85
|
-
const api = getNextPrezlyApi();
|
|
86
|
-
const stories = await api.getAllStories({
|
|
87
|
-
pinning: options.pinning ?? false,
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
const paths = stories.map(({ slug }) => ({ params: { slug } }));
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
paths,
|
|
94
|
-
fallback: 'blocking',
|
|
95
|
-
};
|
|
96
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import type { GetServerSidePropsContext, GetServerSidePropsResult } from 'next';
|
|
2
|
-
|
|
3
|
-
import { getNextPrezlyApi } from '../../data-fetching';
|
|
4
|
-
import { getNewsroomServerSideProps } from '../getNewsroomServerSideProps';
|
|
5
|
-
import { processRequest } from '../processRequest';
|
|
6
|
-
|
|
7
|
-
import type { PropsFunction } from './lib/types';
|
|
8
|
-
|
|
9
|
-
export function getStoryPreviewPageServerSideProps<CustomProps extends Record<string, any>>(
|
|
10
|
-
customProps: CustomProps | PropsFunction<CustomProps>,
|
|
11
|
-
) {
|
|
12
|
-
return async function getServerSideProps(
|
|
13
|
-
context: GetServerSidePropsContext,
|
|
14
|
-
): Promise<GetServerSidePropsResult<CustomProps>> {
|
|
15
|
-
const api = getNextPrezlyApi(context.req);
|
|
16
|
-
const { uuid } = context.params as { uuid: string };
|
|
17
|
-
const story = await api.getStory(uuid);
|
|
18
|
-
if (!story) {
|
|
19
|
-
return { notFound: true };
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const { serverSideProps } = await getNewsroomServerSideProps(context, { story });
|
|
23
|
-
|
|
24
|
-
serverSideProps.newsroomContextProps.currentStory = story;
|
|
25
|
-
|
|
26
|
-
return processRequest(context, {
|
|
27
|
-
...serverSideProps,
|
|
28
|
-
...(typeof customProps === 'function'
|
|
29
|
-
? await (customProps as PropsFunction<CustomProps>)(context, serverSideProps)
|
|
30
|
-
: customProps),
|
|
31
|
-
});
|
|
32
|
-
};
|
|
33
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { getShortestLocaleCode, LocaleObject } from '@prezly/theme-kit-core';
|
|
2
|
-
import type { GetServerSidePropsContext, GetServerSidePropsResult } from 'next';
|
|
3
|
-
|
|
4
|
-
import { getRedirectToCanonicalLocale } from '../intl';
|
|
5
|
-
import type { PageProps, ServerSidePageProps } from '../types';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* This function combines the props returned from `getNewsroomServerSideProps` and your optional custom props for the page.
|
|
9
|
-
* It also handles locale redirects, if `canonicalUrl` parameter is provided.
|
|
10
|
-
* See README for usage examples.
|
|
11
|
-
*
|
|
12
|
-
* @param context Next.js context passed to the `getServerSideProps` method of the page
|
|
13
|
-
* @param props Object containing the return of `getNewsroomServerSideProps` and your optional custom props.
|
|
14
|
-
* @param canonicalUrl Optional: a canonical URL for the current page without locale part. If provided, locale redirects will be handled by the function.
|
|
15
|
-
*/
|
|
16
|
-
export function processRequest<Props>(
|
|
17
|
-
context: GetServerSidePropsContext,
|
|
18
|
-
props: Props & PageProps & ServerSidePageProps,
|
|
19
|
-
canonicalUrl?: string,
|
|
20
|
-
): GetServerSidePropsResult<Props> {
|
|
21
|
-
const { locale: nextLocale, query } = context;
|
|
22
|
-
|
|
23
|
-
const { localeResolved, ...pageProps } = props;
|
|
24
|
-
|
|
25
|
-
// If no locale was provided by Next, it most likely means that the theme is not supporting multi-language.
|
|
26
|
-
// In that case we won't show 404 page.
|
|
27
|
-
if (!localeResolved && nextLocale) {
|
|
28
|
-
return { notFound: true };
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (canonicalUrl) {
|
|
32
|
-
const { languages, localeCode } = pageProps.newsroomContextProps;
|
|
33
|
-
const currentLocale = LocaleObject.fromAnyCode(localeCode);
|
|
34
|
-
const shortestLocaleCode = getShortestLocaleCode(languages, currentLocale);
|
|
35
|
-
|
|
36
|
-
const redirect = getRedirectToCanonicalLocale(
|
|
37
|
-
shortestLocaleCode,
|
|
38
|
-
nextLocale,
|
|
39
|
-
canonicalUrl,
|
|
40
|
-
query,
|
|
41
|
-
);
|
|
42
|
-
if (redirect) {
|
|
43
|
-
return { redirect };
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return {
|
|
48
|
-
props: pageProps as Props & PageProps,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { GetStaticPropsContext, GetStaticPropsResult } from 'next';
|
|
2
|
-
|
|
3
|
-
import type { PageProps, ServerSidePageProps } from '../types';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* This function combines the props returned from `getNewsroomStaticProps` and your optional custom props for the page.
|
|
7
|
-
* See README for usage examples.
|
|
8
|
-
*
|
|
9
|
-
* @param context Next.js context passed to the `getStaticProps` method of the page
|
|
10
|
-
* @param props Object containing the return of `getNewsroomServerSideProps` and your optional custom props.
|
|
11
|
-
* @param revalidateTimeout Optional: timeout in seconds to use with Next ISR.
|
|
12
|
-
*/
|
|
13
|
-
export function processStaticRequest<Props>(
|
|
14
|
-
context: GetStaticPropsContext,
|
|
15
|
-
props: Props & PageProps & ServerSidePageProps,
|
|
16
|
-
revalidateTimeout?: number,
|
|
17
|
-
): GetStaticPropsResult<Props> {
|
|
18
|
-
const { locale: nextLocale } = context;
|
|
19
|
-
|
|
20
|
-
const { localeResolved, ...pageProps } = props;
|
|
21
|
-
|
|
22
|
-
// If no locale was provided by Next, it most likely means that the theme is not supporting multi-language.
|
|
23
|
-
// In that case we won't show 404 page.
|
|
24
|
-
if (!localeResolved && nextLocale) {
|
|
25
|
-
return { notFound: true };
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
props: pageProps as Props & PageProps,
|
|
30
|
-
revalidate: revalidateTimeout,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getAlternateLanguageLinks,
|
|
3
|
-
getNewsroomLogoUrl,
|
|
4
|
-
getUsedLanguages,
|
|
5
|
-
} from '@prezly/theme-kit-core';
|
|
6
|
-
import type { AlternateLanguageLink } from '@prezly/theme-kit-core';
|
|
7
|
-
import { useRouter } from 'next/router';
|
|
8
|
-
import { NextSeo } from 'next-seo';
|
|
9
|
-
import type { NextSeoProps } from 'next-seo';
|
|
10
|
-
import { useMemo } from 'react';
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
useCompanyInformation,
|
|
14
|
-
useCurrentLocale,
|
|
15
|
-
useCurrentStory,
|
|
16
|
-
useGetLinkLocaleSlug,
|
|
17
|
-
useGetTranslationUrl,
|
|
18
|
-
useLanguages,
|
|
19
|
-
useNewsroom,
|
|
20
|
-
} from '../../newsroom-context';
|
|
21
|
-
|
|
22
|
-
import { getAbsoluteUrl } from './lib';
|
|
23
|
-
|
|
24
|
-
type Props = NextSeoProps & {
|
|
25
|
-
imageUrl?: string;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* This component provides base page attributes for every Newsroom page.
|
|
30
|
-
* It depends on information from NewsroomContext, so the component should be placed inside the `NewsroomContextProvider`.
|
|
31
|
-
* You can pass additional props expected by NextSeo component to override any info that is provided by the NewsroomContext.
|
|
32
|
-
*/
|
|
33
|
-
export function PageSeo({
|
|
34
|
-
title,
|
|
35
|
-
description,
|
|
36
|
-
imageUrl,
|
|
37
|
-
canonical,
|
|
38
|
-
openGraph,
|
|
39
|
-
twitter,
|
|
40
|
-
...nextSeoProps
|
|
41
|
-
}: Props) {
|
|
42
|
-
const companyInformation = useCompanyInformation();
|
|
43
|
-
const site = useNewsroom();
|
|
44
|
-
const currentLocale = useCurrentLocale();
|
|
45
|
-
const languages = useLanguages();
|
|
46
|
-
const getTranslationUrl = useGetTranslationUrl();
|
|
47
|
-
const getLinkLocaleSlug = useGetLinkLocaleSlug();
|
|
48
|
-
const currentStory = useCurrentStory();
|
|
49
|
-
const { asPath } = useRouter();
|
|
50
|
-
|
|
51
|
-
const pageTitle =
|
|
52
|
-
companyInformation.seo_settings.meta_title ||
|
|
53
|
-
companyInformation.seo_settings.default_meta_title ||
|
|
54
|
-
title ||
|
|
55
|
-
companyInformation.name;
|
|
56
|
-
const pageDescription =
|
|
57
|
-
companyInformation.seo_settings.meta_description ||
|
|
58
|
-
companyInformation.seo_settings.default_meta_description ||
|
|
59
|
-
description ||
|
|
60
|
-
companyInformation.about_plaintext;
|
|
61
|
-
const canonicalUrl =
|
|
62
|
-
canonical || getAbsoluteUrl(asPath, site.url, getLinkLocaleSlug(currentLocale));
|
|
63
|
-
const siteName = companyInformation.name;
|
|
64
|
-
const logoUrl = imageUrl || getNewsroomLogoUrl(site);
|
|
65
|
-
|
|
66
|
-
const alternateLanguageLinks: AlternateLanguageLink[] = useMemo(() => {
|
|
67
|
-
if (!languages.length) {
|
|
68
|
-
return [];
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const usedLanguages = getUsedLanguages(languages);
|
|
72
|
-
|
|
73
|
-
return getAlternateLanguageLinks(usedLanguages, (locale) => {
|
|
74
|
-
const translationUrl = getTranslationUrl(locale);
|
|
75
|
-
|
|
76
|
-
if (!translationUrl) {
|
|
77
|
-
return undefined;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return getAbsoluteUrl(
|
|
81
|
-
translationUrl,
|
|
82
|
-
site.url,
|
|
83
|
-
currentStory && translationUrl !== '/' ? false : getLinkLocaleSlug(locale),
|
|
84
|
-
);
|
|
85
|
-
});
|
|
86
|
-
}, [currentStory, getLinkLocaleSlug, getTranslationUrl, languages, site.url]);
|
|
87
|
-
|
|
88
|
-
return (
|
|
89
|
-
<NextSeo
|
|
90
|
-
title={pageTitle}
|
|
91
|
-
description={pageDescription}
|
|
92
|
-
canonical={canonicalUrl}
|
|
93
|
-
noindex={nextSeoProps.noindex ?? !site.is_indexable}
|
|
94
|
-
nofollow={nextSeoProps.nofollow ?? !site.is_indexable}
|
|
95
|
-
openGraph={{
|
|
96
|
-
url: canonicalUrl,
|
|
97
|
-
title: pageTitle,
|
|
98
|
-
description: pageDescription,
|
|
99
|
-
locale: currentLocale.toUnderscoreCode(),
|
|
100
|
-
images: [
|
|
101
|
-
{
|
|
102
|
-
url: logoUrl,
|
|
103
|
-
alt: pageTitle,
|
|
104
|
-
},
|
|
105
|
-
],
|
|
106
|
-
site_name: siteName,
|
|
107
|
-
...openGraph,
|
|
108
|
-
}}
|
|
109
|
-
twitter={{
|
|
110
|
-
site: siteName,
|
|
111
|
-
cardType: 'summary',
|
|
112
|
-
...twitter,
|
|
113
|
-
}}
|
|
114
|
-
additionalMetaTags={[
|
|
115
|
-
{ name: 'twitter:image', content: logoUrl },
|
|
116
|
-
site.google_search_console_key && {
|
|
117
|
-
name: 'google-site-verification',
|
|
118
|
-
content: site.google_search_console_key,
|
|
119
|
-
},
|
|
120
|
-
].filter((tag): tag is Exclude<typeof tag, string | null> => Boolean(tag))}
|
|
121
|
-
additionalLinkTags={[
|
|
122
|
-
{
|
|
123
|
-
rel: 'alternate',
|
|
124
|
-
type: 'application/rss+xml',
|
|
125
|
-
href: getAbsoluteUrl('/feed', site.url),
|
|
126
|
-
},
|
|
127
|
-
]}
|
|
128
|
-
languageAlternates={alternateLanguageLinks}
|
|
129
|
-
{...nextSeoProps}
|
|
130
|
-
/>
|
|
131
|
-
);
|
|
132
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { PageSeo } from './PageSeo';
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
// Returns absolute URL based on the path and without any query parameters
|
|
2
|
-
export function getAbsoluteUrl(path: string, origin: string, localeCode?: string | false): string {
|
|
3
|
-
const url = new URL(`${localeCode ? `/${localeCode}` : ''}${path || '/'}`, origin);
|
|
4
|
-
url.search = '';
|
|
5
|
-
|
|
6
|
-
return url.toString();
|
|
7
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { getAbsoluteUrl } from './getAbsoluteUrl';
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import type { ExtendedStory } from '@prezly/sdk';
|
|
2
|
-
import { Story } from '@prezly/sdk';
|
|
3
|
-
import { ArticleJsonLd, NextSeo } from 'next-seo';
|
|
4
|
-
|
|
5
|
-
type Props = {
|
|
6
|
-
story: ExtendedStory;
|
|
7
|
-
noindex?: boolean;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* This components provides additional page attributes for the Story page.
|
|
12
|
-
* It works in conjunction with `PageSeo` component, overidding some of its attributes.
|
|
13
|
-
*/
|
|
14
|
-
export function StorySeo({ story, noindex }: Props) {
|
|
15
|
-
const { title, subtitle, published_at, updated_at, author, oembed, newsroom, summary } = story;
|
|
16
|
-
|
|
17
|
-
const authorName = author?.display_name || author?.email || 'Unknown';
|
|
18
|
-
const seoTitle =
|
|
19
|
-
story.seo_settings.meta_title || story.seo_settings.default_meta_title || title;
|
|
20
|
-
const seoDescription =
|
|
21
|
-
story.seo_settings.meta_description ||
|
|
22
|
-
story.seo_settings.default_meta_description ||
|
|
23
|
-
subtitle ||
|
|
24
|
-
summary;
|
|
25
|
-
const canonical = story.seo_settings.canonical_url || oembed.url;
|
|
26
|
-
const indexable = newsroom.is_indexable && story.visibility === Story.Visibility.PUBLIC;
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<>
|
|
30
|
-
<NextSeo
|
|
31
|
-
title={seoTitle}
|
|
32
|
-
description={seoDescription}
|
|
33
|
-
canonical={canonical}
|
|
34
|
-
noindex={noindex ?? !indexable}
|
|
35
|
-
nofollow={noindex ?? !indexable}
|
|
36
|
-
openGraph={{
|
|
37
|
-
title,
|
|
38
|
-
description: seoDescription,
|
|
39
|
-
url: oembed.url,
|
|
40
|
-
site_name: newsroom.display_name,
|
|
41
|
-
type: 'article',
|
|
42
|
-
article: {
|
|
43
|
-
publishedTime: published_at || undefined,
|
|
44
|
-
modifiedTime: updated_at,
|
|
45
|
-
authors: [authorName],
|
|
46
|
-
},
|
|
47
|
-
...(oembed.thumbnail_url && {
|
|
48
|
-
images: [
|
|
49
|
-
{
|
|
50
|
-
url: oembed.thumbnail_url,
|
|
51
|
-
alt: oembed.title,
|
|
52
|
-
width: oembed.thumbnail_width,
|
|
53
|
-
height: oembed.thumbnail_height,
|
|
54
|
-
},
|
|
55
|
-
],
|
|
56
|
-
}),
|
|
57
|
-
}}
|
|
58
|
-
additionalMetaTags={
|
|
59
|
-
oembed.thumbnail_url
|
|
60
|
-
? [
|
|
61
|
-
{ name: 'twitter:card', content: 'summary_large_image' },
|
|
62
|
-
{ name: 'twitter:image', content: oembed.thumbnail_url },
|
|
63
|
-
]
|
|
64
|
-
: undefined
|
|
65
|
-
}
|
|
66
|
-
additionalLinkTags={[
|
|
67
|
-
{
|
|
68
|
-
rel: 'alternate',
|
|
69
|
-
type: 'application/json',
|
|
70
|
-
href: `${oembed.url}.json`,
|
|
71
|
-
},
|
|
72
|
-
]}
|
|
73
|
-
/>
|
|
74
|
-
<ArticleJsonLd
|
|
75
|
-
url={oembed.url}
|
|
76
|
-
title={title}
|
|
77
|
-
images={oembed.thumbnail_url ? [oembed.thumbnail_url] : []}
|
|
78
|
-
datePublished={published_at || ''}
|
|
79
|
-
dateModified={updated_at}
|
|
80
|
-
authorName={[authorName]}
|
|
81
|
-
publisherName={newsroom.display_name}
|
|
82
|
-
publisherLogo={newsroom.thumbnail_url}
|
|
83
|
-
description={seoDescription}
|
|
84
|
-
/>
|
|
85
|
-
</>
|
|
86
|
-
);
|
|
87
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { StorySeo } from './StorySeo';
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { DEFAULT_LOCALE, getLocaleDirection, LocaleObject } from '@prezly/theme-kit-core';
|
|
2
|
-
import Document, { Head, Html, Main, NextScript } from 'next/document';
|
|
3
|
-
|
|
4
|
-
import type { PageProps } from '../types';
|
|
5
|
-
|
|
6
|
-
export class PrezlyThemeDocument extends Document {
|
|
7
|
-
getLocaleCode() {
|
|
8
|
-
const {
|
|
9
|
-
newsroomContextProps,
|
|
10
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
11
|
-
} = this.props.__NEXT_DATA__.props.pageProps as PageProps;
|
|
12
|
-
|
|
13
|
-
// In some cases (like server errors) the newsroom context props will not be loaded.
|
|
14
|
-
// We need to fall back to default locale to keep the app working.
|
|
15
|
-
const { localeCode } = newsroomContextProps ?? { localeCode: DEFAULT_LOCALE };
|
|
16
|
-
|
|
17
|
-
return localeCode;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
render() {
|
|
21
|
-
const locale = LocaleObject.fromAnyCode(this.getLocaleCode());
|
|
22
|
-
// TODO: The direction can be pulled from the Language object
|
|
23
|
-
const direction = getLocaleDirection(locale);
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<Html lang={locale.toHyphenCode()} dir={direction}>
|
|
27
|
-
<Head>
|
|
28
|
-
<meta name="og:locale" content={locale.toHyphenCode()} />
|
|
29
|
-
</Head>
|
|
30
|
-
<body>
|
|
31
|
-
<Main />
|
|
32
|
-
<NextScript />
|
|
33
|
-
</body>
|
|
34
|
-
</Html>
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import type { Story } from '@prezly/sdk';
|
|
2
|
-
import {
|
|
3
|
-
getAlgoliaSettings,
|
|
4
|
-
getCompanyInformation,
|
|
5
|
-
getDefaultLanguage,
|
|
6
|
-
getLanguageFromStory,
|
|
7
|
-
getNotifications,
|
|
8
|
-
LocaleObject,
|
|
9
|
-
PrezlyApi,
|
|
10
|
-
} from '@prezly/theme-kit-core';
|
|
11
|
-
import type { IncomingMessage } from 'http';
|
|
12
|
-
|
|
13
|
-
import { getLanguageFromNextLocaleIsoCode } from '../intl';
|
|
14
|
-
import type { PageProps, ServerSidePageProps } from '../types';
|
|
15
|
-
|
|
16
|
-
export class NextPrezlyApi extends PrezlyApi {
|
|
17
|
-
async getNewsroomServerSideProps(
|
|
18
|
-
request?: IncomingMessage,
|
|
19
|
-
nextLocaleIsoCode?: string,
|
|
20
|
-
story?: Story,
|
|
21
|
-
): Promise<PageProps & ServerSidePageProps> {
|
|
22
|
-
const [newsroom, languages, categories, themePreset] = await Promise.all([
|
|
23
|
-
this.getNewsroom(),
|
|
24
|
-
this.getNewsroomLanguages(),
|
|
25
|
-
this.getCategories(),
|
|
26
|
-
this.getThemePreset(),
|
|
27
|
-
]);
|
|
28
|
-
|
|
29
|
-
const currentLanguage = story
|
|
30
|
-
? getLanguageFromStory(languages, story)
|
|
31
|
-
: getLanguageFromNextLocaleIsoCode(languages, nextLocaleIsoCode);
|
|
32
|
-
const defaultLanguage = getDefaultLanguage(languages);
|
|
33
|
-
|
|
34
|
-
const { code: localeCode } = currentLanguage || defaultLanguage;
|
|
35
|
-
const locale = LocaleObject.fromAnyCode(localeCode);
|
|
36
|
-
|
|
37
|
-
// TODO: if no information given for current language, show boilerplate from default language
|
|
38
|
-
const companyInformation = getCompanyInformation(languages, locale);
|
|
39
|
-
|
|
40
|
-
const notifications = getNotifications(languages, locale);
|
|
41
|
-
const algoliaSettings = getAlgoliaSettings(request);
|
|
42
|
-
|
|
43
|
-
return {
|
|
44
|
-
newsroomContextProps: {
|
|
45
|
-
newsroom,
|
|
46
|
-
companyInformation,
|
|
47
|
-
categories,
|
|
48
|
-
languages,
|
|
49
|
-
localeCode,
|
|
50
|
-
notifications,
|
|
51
|
-
themePreset,
|
|
52
|
-
algoliaSettings,
|
|
53
|
-
},
|
|
54
|
-
localeResolved: Boolean(currentLanguage),
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { getEnvVariables } from '@prezly/theme-kit-core';
|
|
2
|
-
import type { IncomingMessage } from 'http';
|
|
3
|
-
|
|
4
|
-
import { NextPrezlyApi } from './NextPrezlyApi';
|
|
5
|
-
|
|
6
|
-
export function getNextPrezlyApi(req?: IncomingMessage): NextPrezlyApi {
|
|
7
|
-
if (typeof window !== 'undefined') {
|
|
8
|
-
throw new Error('"getNextPrezlyApi" should only be used on back-end side.');
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// `getEnvVariables` handles both cases for envs parsing - .env and request headers
|
|
12
|
-
const { PREZLY_ACCESS_TOKEN, PREZLY_NEWSROOM_UUID, PREZLY_THEME_UUID } = getEnvVariables(req);
|
|
13
|
-
|
|
14
|
-
if (!PREZLY_ACCESS_TOKEN) {
|
|
15
|
-
throw new Error('"PREZLY_ACCESS_TOKEN" is not set in env variables.');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (!PREZLY_NEWSROOM_UUID) {
|
|
19
|
-
throw new Error('"PREZLY_NEWSROOM_UUID" is not set in env variables.');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return new NextPrezlyApi(PREZLY_ACCESS_TOKEN, PREZLY_NEWSROOM_UUID, PREZLY_THEME_UUID);
|
|
23
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { getNextPrezlyApi } from './getNextPrezlyApi';
|
package/src/index.ts
DELETED