@prezly/theme-kit-nextjs 2.1.1 → 3.0.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prezly/theme-kit-nextjs",
3
- "version": "2.1.1",
3
+ "version": "3.0.0",
4
4
  "description": "Data layer and utility library for developing Prezly themes with NextJS",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -48,7 +48,7 @@
48
48
  "react-dom": "^17.x || ^18.x"
49
49
  },
50
50
  "dependencies": {
51
- "@prezly/sdk": "^11.9.0",
51
+ "@prezly/sdk": "^11.11.0",
52
52
  "@prezly/uploadcare": "^2.3.4",
53
53
  "@react-hookz/web": "^16.0.0",
54
54
  "next-seo": "^5.4.0",
@@ -56,7 +56,7 @@
56
56
  },
57
57
  "devDependencies": {
58
58
  "@prezly/eslint-config": "4.3.2",
59
- "@sentry/nextjs": "7.24.2",
59
+ "@sentry/nextjs": "7.26.0",
60
60
  "@types/jest": "29.2.4",
61
61
  "@types/node": "17.0.14",
62
62
  "@types/parse-data-url": "3.0.0",
@@ -46,6 +46,13 @@ export const LANGUAGES: Record<string, NewsroomLanguageSettings> = {
46
46
  '<p>We maken gebruik van cookies op onze website. Deze helpen ons om u iets beter te leren kennen en te zien hoe u onze website gebruikt. <br>Dit helpt ons een waardevollere en op maat gemaakte ervaring te bieden voor u en anderen.</p>',
47
47
  subscribe_disclaimer:
48
48
  '<p>Door uw abonnement te bevestigen, bevestigt u dat u begrijpt dat u zich registreert om materiaal te ontvangen en dat uw gegevens veilig worden verwerkt en opgeslagen.</p>',
49
+ seo_settings: {
50
+ canonical_url: null,
51
+ default_meta_title: null,
52
+ default_meta_description: null,
53
+ meta_title: null,
54
+ meta_description: null,
55
+ },
49
56
  },
50
57
  notifications: [],
51
58
  },
@@ -87,6 +94,13 @@ export const LANGUAGES: Record<string, NewsroomLanguageSettings> = {
87
94
  email_disclaimer: '',
88
95
  cookie_statement: '',
89
96
  subscribe_disclaimer: '',
97
+ seo_settings: {
98
+ canonical_url: null,
99
+ default_meta_title: null,
100
+ default_meta_description: null,
101
+ meta_title: null,
102
+ meta_description: null,
103
+ },
90
104
  },
91
105
  notifications: [],
92
106
  },
@@ -134,6 +148,13 @@ export const LANGUAGES: Record<string, NewsroomLanguageSettings> = {
134
148
  '<p>We use cookies on our website. They help us get to know you a little and how you use our website. This helps us provide a more valuable and tailored experience for you and others.</p>',
135
149
  subscribe_disclaimer:
136
150
  '<p>By confirming your subscription you are confirming you understand that you are registering to receive content and consent to your information being securely processed and stored.</p>',
151
+ seo_settings: {
152
+ canonical_url: null,
153
+ default_meta_title: null,
154
+ default_meta_description: null,
155
+ meta_title: null,
156
+ meta_description: null,
157
+ },
137
158
  },
138
159
  notifications: [],
139
160
  },
@@ -181,6 +202,13 @@ export const LANGUAGES: Record<string, NewsroomLanguageSettings> = {
181
202
  '<p>We use cookies on our website. They help us get to know you a little and how you use our website. This helps us provide a more valuable and tailored experience for you and others.</p>',
182
203
  subscribe_disclaimer:
183
204
  '<p>By confirming your subscription you are confirming you understand that you are registering to receive content and consent to your information being securely processed and stored.</p>',
205
+ seo_settings: {
206
+ canonical_url: null,
207
+ default_meta_title: null,
208
+ default_meta_description: null,
209
+ meta_title: null,
210
+ meta_description: null,
211
+ },
184
212
  },
185
213
  notifications: [],
186
214
  },
@@ -228,6 +256,13 @@ export const LANGUAGES: Record<string, NewsroomLanguageSettings> = {
228
256
  '<p>We use cookies on our website. They help us get to know you a little and how you use our website. This helps us provide a more valuable and tailored experience for you and others.</p>',
229
257
  subscribe_disclaimer:
230
258
  '<p>By confirming your subscription you are confirming you understand that you are registering to receive content and consent to your information being securely processed and stored.</p>',
259
+ seo_settings: {
260
+ canonical_url: null,
261
+ default_meta_title: null,
262
+ default_meta_description: null,
263
+ meta_title: null,
264
+ meta_description: null,
265
+ },
231
266
  },
232
267
  notifications: [],
233
268
  },
@@ -275,6 +310,13 @@ export const LANGUAGES: Record<string, NewsroomLanguageSettings> = {
275
310
  "<p>Nous utilisons des cookies sur notre site internet. Ils nous aident à en savoir un tout petit peu plus sur vous et sur votre façon d'utiliser notre site. Ainsi, il nous est possible de vous proposer, à vous comme à d'autres, une expérience plus riche et personnalisée.</p>",
276
311
  subscribe_disclaimer:
277
312
  '<p>En confirmant votre inscription, vous confirmez que vous comprenez que vous vous inscrivez pour recevoir du contenu, et consentez à ce que vos données personnelles soient traitées et stockées en toute sécurité.</p>',
313
+ seo_settings: {
314
+ canonical_url: null,
315
+ default_meta_title: null,
316
+ default_meta_description: null,
317
+ meta_title: null,
318
+ meta_description: null,
319
+ },
278
320
  },
279
321
  notifications: [],
280
322
  },
@@ -322,6 +364,13 @@ export const LANGUAGES: Record<string, NewsroomLanguageSettings> = {
322
364
  "<p>Nous utilisons des cookies sur notre site internet. Ils nous aident à en savoir un tout petit peu plus sur vous et sur votre façon d'utiliser notre site. Ainsi, il nous est possible de vous proposer, à vous comme à d'autres, une expérience plus riche et personnalisée.</p>",
323
365
  subscribe_disclaimer:
324
366
  '<p>En confirmant votre inscription, vous confirmez que vous comprenez que vous vous inscrivez pour recevoir du contenu, et consentez à ce que vos données personnelles soient traitées et stockées en toute sécurité.</p>',
367
+ seo_settings: {
368
+ canonical_url: null,
369
+ default_meta_title: null,
370
+ default_meta_description: null,
371
+ meta_title: null,
372
+ meta_description: null,
373
+ },
325
374
  },
326
375
  notifications: [],
327
376
  },
@@ -366,6 +415,13 @@ export const LANGUAGES: Record<string, NewsroomLanguageSettings> = {
366
415
  '<p>Utilizamos cookies en nuestro sitio web. Nos ayudan a conocerle un poco y saber cómo usa nuestro sitio web, lo que nos sirve para proporcionarle a usted y a los demás una experiencia más valiosa y personalizada.</p>',
367
416
  subscribe_disclaimer:
368
417
  '<p>Al confirmar su suscripción, confirma que entiende que se está registrando para recibir contenido y accede a que su información se procese y guarde de manera segura.</p>',
418
+ seo_settings: {
419
+ canonical_url: null,
420
+ default_meta_title: null,
421
+ default_meta_description: null,
422
+ meta_title: null,
423
+ meta_description: null,
424
+ },
369
425
  },
370
426
  notifications: [],
371
427
  },
@@ -410,6 +466,13 @@ export const LANGUAGES: Record<string, NewsroomLanguageSettings> = {
410
466
  '<p>Utilizamos cookies en nuestro sitio web. Nos ayudan a conocerle un poco y saber cómo usa nuestro sitio web, lo que nos sirve para proporcionarle a usted y a los demás una experiencia más valiosa y personalizada.</p>',
411
467
  subscribe_disclaimer:
412
468
  '<p>Al confirmar su suscripción, confirma que entiende que se está registrando para recibir contenido y accede a que su información se procese y guarde de manera segura.</p>',
469
+ seo_settings: {
470
+ canonical_url: null,
471
+ default_meta_title: null,
472
+ default_meta_description: null,
473
+ meta_title: null,
474
+ meta_description: null,
475
+ },
413
476
  },
414
477
  notifications: [],
415
478
  },
@@ -23,13 +23,14 @@ export interface HomePageProps<StoryType extends Story = Story> {
23
23
  interface Options {
24
24
  extraStoryFields?: (keyof Story.ExtraFields)[];
25
25
  pageSize?: number;
26
+ pinning?: boolean;
26
27
  }
27
28
 
28
29
  export function getHomepageServerSideProps<
29
30
  CustomProps extends Record<string, any>,
30
31
  StoryType extends Story = Story,
31
32
  >(customProps: CustomProps | PropsFunction<CustomProps>, options?: Options) {
32
- const { pageSize = DEFAULT_PAGE_SIZE, extraStoryFields } = options || {};
33
+ const { pageSize = DEFAULT_PAGE_SIZE, extraStoryFields, pinning = false } = options || {};
33
34
 
34
35
  return async function getServerSideProps(
35
36
  context: GetServerSidePropsContext,
@@ -46,6 +47,7 @@ export function getHomepageServerSideProps<
46
47
  const storiesPaginated = await api.getStories({
47
48
  page,
48
49
  pageSize,
50
+ pinning,
49
51
  include: extraStoryFields,
50
52
  localeCode,
51
53
  });
@@ -75,7 +77,7 @@ export function getHomepageStaticProps<
75
77
  CustomProps extends Record<string, any>,
76
78
  StoryType extends Story = Story,
77
79
  >(customProps: CustomProps | PropsFunction<CustomProps, GetStaticPropsContext>, options?: Options) {
78
- const { pageSize = DEFAULT_PAGE_SIZE, extraStoryFields } = options || {};
80
+ const { pageSize = DEFAULT_PAGE_SIZE, extraStoryFields, pinning = false } = options || {};
79
81
 
80
82
  return async function getStaticProps(
81
83
  context: GetStaticPropsContext,
@@ -88,6 +90,7 @@ export function getHomepageStaticProps<
88
90
 
89
91
  const storiesPaginated = await api.getStories({
90
92
  pageSize,
93
+ pinning,
91
94
  include: extraStoryFields,
92
95
  localeCode,
93
96
  });
@@ -5,7 +5,9 @@ import { getPrezlyApi } from '../../../data-fetching';
5
5
  import { createPaths } from './createPaths';
6
6
  import { SitemapBuilder } from './SitemapBuilder';
7
7
 
8
- export function getSitemapServerSideProps(additionalPaths?: string[]) {
8
+ export function getSitemapServerSideProps(
9
+ options: { additionalPaths?: string[]; pinning?: boolean } = {},
10
+ ) {
9
11
  return async function getServerSideProps(ctx: NextPageContext) {
10
12
  const { res, req } = ctx;
11
13
 
@@ -19,7 +21,9 @@ export function getSitemapServerSideProps(additionalPaths?: string[]) {
19
21
  const baseUrl = req.headers.host || '/';
20
22
 
21
23
  const api = getPrezlyApi(req);
22
- const stories = await api.getAllStories();
24
+ const stories = await api.getAllStories({
25
+ pinning: options.pinning ?? false,
26
+ });
23
27
  const categories = await api.getCategories();
24
28
 
25
29
  const paths = createPaths(stories, categories);
@@ -28,9 +32,7 @@ export function getSitemapServerSideProps(additionalPaths?: string[]) {
28
32
  sitemapBuilder.addUrl('/');
29
33
  paths.forEach((path) => sitemapBuilder.addUrl(path));
30
34
 
31
- if (additionalPaths) {
32
- additionalPaths.forEach((path) => sitemapBuilder.addUrl(path));
33
- }
35
+ options.additionalPaths?.forEach((path) => sitemapBuilder.addUrl(path));
34
36
 
35
37
  res.setHeader('Content-Type', 'text/xml');
36
38
  res.write(sitemapBuilder.serialize());
@@ -81,9 +81,11 @@ export function getStoryPageStaticProps<CustomProps extends Record<string, any>>
81
81
  };
82
82
  }
83
83
 
84
- export async function getStoryPageStaticPaths() {
84
+ export async function getStoryPageStaticPaths(options: { pinning?: boolean } = {}) {
85
85
  const api = getPrezlyApi();
86
- const stories = await api.getAllStories();
86
+ const stories = await api.getAllStories({
87
+ pinning: options.pinning ?? false,
88
+ });
87
89
 
88
90
  const paths = stories.map(({ slug }) => ({ params: { slug } }));
89
91
 
@@ -1,4 +1,4 @@
1
- import { createPrezlyClient } from '@prezly/sdk';
1
+ import { createPrezlyClient, SortOrder } from '@prezly/sdk';
2
2
  import type {
3
3
  Category,
4
4
  Newsroom,
@@ -22,26 +22,25 @@ import { DEFAULT_PAGE_SIZE } from '../../utils';
22
22
  import { getAlgoliaSettings, isSdkError, isUuid } from '../lib';
23
23
 
24
24
  import {
25
+ getChronologicalSortOrder,
25
26
  getContactsQuery,
26
27
  getGalleriesQuery,
27
28
  getSlugQuery,
28
- getSortByPublishedDate,
29
29
  getStoriesQuery,
30
30
  } from './queries';
31
31
 
32
32
  const CATEGORIES_SORT_ORDER = '+order';
33
- const DEFAULT_SORT_ORDER: SortOrder = 'desc';
33
+ const DEFAULT_SORT_ORDER = SortOrder.Direction.DESC;
34
34
 
35
35
  const ERROR_CODE_NOT_FOUND = 404;
36
36
  const ERROR_CODE_FORBIDDEN = 403;
37
37
  const ERROR_CODE_GONE = 410;
38
38
 
39
- type SortOrder = 'desc' | 'asc';
40
-
41
39
  interface GetStoriesOptions {
42
40
  page?: number;
43
41
  pageSize?: number;
44
- order?: SortOrder;
42
+ order?: `${SortOrder.Direction}`;
43
+ pinning?: boolean;
45
44
  include?: (keyof Story.ExtraFields)[];
46
45
  localeCode?: string;
47
46
  }
@@ -108,10 +107,10 @@ export class PrezlyApi {
108
107
  /**
109
108
  * Note: this method returns ALL stories from the newsroom. It's intended to be used for sitemaps and not to display actual content.
110
109
  */
111
- async getAllStories(order: SortOrder = DEFAULT_SORT_ORDER) {
112
- const sortOrder = getSortByPublishedDate(order);
110
+ async getAllStories({ order = DEFAULT_SORT_ORDER, pinning = false }: GetStoriesOptions = {}) {
111
+ const sortOrder = getChronologicalSortOrder(order, pinning);
113
112
  const newsroom = await this.getNewsroom();
114
- const jsonQuery = JSON.stringify(getStoriesQuery(newsroom.uuid));
113
+ const query = JSON.stringify(getStoriesQuery(newsroom.uuid));
115
114
  const maxStories = newsroom.stories_number;
116
115
  const chunkSize = 200;
117
116
 
@@ -120,7 +119,7 @@ export class PrezlyApi {
120
119
  this.searchStories({
121
120
  limit: chunkSize,
122
121
  sortOrder,
123
- jsonQuery,
122
+ query,
124
123
  offset: pageIndex * chunkSize,
125
124
  }),
126
125
  );
@@ -136,17 +135,18 @@ export class PrezlyApi {
136
135
  page = undefined,
137
136
  pageSize = DEFAULT_PAGE_SIZE,
138
137
  order = DEFAULT_SORT_ORDER,
138
+ pinning = false,
139
139
  include,
140
140
  localeCode,
141
141
  }: GetStoriesOptions = {}) {
142
- const sortOrder = getSortByPublishedDate(order);
143
- const jsonQuery = JSON.stringify(getStoriesQuery(this.newsroomUuid, undefined, localeCode));
142
+ const sortOrder = getChronologicalSortOrder(order, pinning);
143
+ const query = JSON.stringify(getStoriesQuery(this.newsroomUuid, undefined, localeCode));
144
144
 
145
145
  const { stories, pagination } = await this.searchStories({
146
146
  limit: pageSize,
147
147
  offset: typeof page === 'undefined' ? undefined : (page - 1) * pageSize,
148
148
  sortOrder,
149
- jsonQuery,
149
+ query,
150
150
  include,
151
151
  });
152
152
 
@@ -163,18 +163,16 @@ export class PrezlyApi {
163
163
  order = DEFAULT_SORT_ORDER,
164
164
  include,
165
165
  localeCode,
166
- }: GetStoriesOptions = {},
166
+ }: Omit<GetStoriesOptions, 'pinning'> = {},
167
167
  ) {
168
- const sortOrder = getSortByPublishedDate(order);
169
- const jsonQuery = JSON.stringify(
170
- getStoriesQuery(this.newsroomUuid, category.id, localeCode),
171
- );
168
+ const sortOrder = getChronologicalSortOrder(order);
169
+ const query = JSON.stringify(getStoriesQuery(this.newsroomUuid, category.id, localeCode));
172
170
 
173
171
  const { stories, pagination } = await this.searchStories({
174
172
  limit: pageSize,
175
173
  offset: typeof page === 'undefined' ? undefined : (page - 1) * pageSize,
176
174
  sortOrder,
177
- jsonQuery,
175
+ query,
178
176
  include,
179
177
  });
180
178
 
@@ -184,10 +182,10 @@ export class PrezlyApi {
184
182
  }
185
183
 
186
184
  async getStoryBySlug(slug: string) {
187
- const jsonQuery = JSON.stringify(getSlugQuery(this.newsroomUuid, slug));
185
+ const query = JSON.stringify(getSlugQuery(this.newsroomUuid, slug));
188
186
  const { stories } = await this.searchStories({
189
187
  limit: 1,
190
- jsonQuery,
188
+ query,
191
189
  });
192
190
 
193
191
  if (stories[0]) {
@@ -1,3 +1,5 @@
1
+ import { SortOrder } from '@prezly/sdk';
2
+
1
3
  const publishedAndAccessible = [
2
4
  { lifecycle_status: { $in: ['published', 'embargo'] } },
3
5
  { visibility: { $in: ['public', 'private', 'embargo'] } },
@@ -34,8 +36,14 @@ export function getStoriesQuery(newsroomUuid: string, categoryId?: number, local
34
36
  return query;
35
37
  }
36
38
 
37
- export function getSortByPublishedDate(order: 'asc' | 'desc') {
38
- return order === 'desc' ? '-published_at' : 'published_at';
39
+ export function getChronologicalSortOrder(direction: `${SortOrder.Direction}`, pinning = false) {
40
+ const pinnedFirst = SortOrder.desc('is_pinned');
41
+ const chronological =
42
+ direction === SortOrder.Direction.ASC
43
+ ? SortOrder.asc('published_at')
44
+ : SortOrder.desc('published_at');
45
+
46
+ return pinning ? SortOrder.combine(pinnedFirst, chronological) : chronological;
39
47
  }
40
48
 
41
49
  export function getGalleriesQuery() {