diff --git a/web/pages/api/update-algolia-indexes.js b/web/pages/api/update-algolia-indexes.js index 9ae93ff8e..59cfa60ab 100644 --- a/web/pages/api/update-algolia-indexes.js +++ b/web/pages/api/update-algolia-indexes.js @@ -14,15 +14,49 @@ export const config = { }, } +const updateAlgoliaIndex = async (body) => { + const headersList = { + Accept: '*/*', + } + const response = await fetch(ALGOLIA_FUNCTION_URL, { + method: 'POST', + headers: headersList, + body, + }) + return response +} + export default async function handler(req, res) { const signature = req.headers[SIGNATURE_HEADER_NAME] const body = (await getRawBody(req)).toString() if (!isValidSignature(body, signature, SANITY_API_TOKEN)) { - console.log(req, 'Unauthorized request: Revalidate Endpoint') + console.log(req, 'Unauthorized request: Update Algolia indexes Endpoint') return res.status(401).json({ success: false, msg: 'Unauthorized!' }) } const data = JSON.parse(body) + + const revalidateNewsroomPages = async () => { + console.log(new Date(), 'Revalidating: /news') + res.revalidate(`/news`) + console.log(new Date(), 'Revalidating: /no/nyheter') + res.revalidate('/no/nyheter') + } + + if (data._type === 'news') { + // wait for news index and revalidate... + const response = await updateAlgoliaIndex(body) + if (response.status == 204) { + console.log('Algolia Indexing Success , Revalidating newsroom') + // wait for a second revalidate newsroom pages + const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay)) + await sleep(1000) // wait for a second to let algolia index temporary fix as we dont know the status yet + await revalidateNewsroomPages() + return res.status(200).json('Index updated and newsroom revalidated') + } else { + return res.status(400).json(response.body) + } + } const result = await sanityClient.fetch( groq`*[_type match "route_*" && content._ref == $id && excludeFromSearch != true][0]{"slug": slug.current}`, { @@ -32,14 +66,7 @@ export default async function handler(req, res) { try { if (result?.slug) { // slug exists for the topic or event - const headersList = { - Accept: '*/*', - } - const response = await fetch(ALGOLIA_FUNCTION_URL, { - method: 'POST', - headers: headersList, - body, - }) + const response = updateAlgoliaIndex(body) if (response.status == 204) { return res.status(200).json('Index updated') } else { diff --git a/web/pages/news/index.global.tsx b/web/pages/news/index.global.tsx index 49949f9fd..75d19f7ed 100644 --- a/web/pages/news/index.global.tsx +++ b/web/pages/news/index.global.tsx @@ -1,29 +1,41 @@ -import { GetServerSideProps } from 'next' +import { GetStaticProps } from 'next' import type { AppProps } from 'next/app' import { IntlProvider } from 'react-intl' import Footer from '../../pageComponents/shared/Footer' import Header from '../../pageComponents/shared/Header' -import { allNewsDocuments, newsroomQuery } from '../../lib/queries/newsroom' +import { renderToString } from 'react-dom/server' +import { newsroomQuery } from '../../lib/queries/newsroom' import getIntl from '../../common/helpers/getIntl' import { getNameFromLocale, getIsoFromLocale } from '../../lib/localization' import { defaultLanguage } from '../../languages' import { AlgoliaIndexPageType, NewsRoomPageType } from '../../types' -import { getComponentsData, getData } from '../../lib/fetchData' -import NewsRoomTemplateSanity from '@templates/newsroom/sanity/NewsroomSanity' +import { getComponentsData } from '../../lib/fetchData' +import NewsRoomTemplate from '@templates/newsroom/Newsroom' +import { getServerState, InstantSearchSSRProvider } from 'react-instantsearch' +import { Flags } from '../../common/helpers/datasetHelpers' +import algoliasearch from 'algoliasearch/lite' +import { algolia } from '../../lib/config' -export default function NewsRoom({ data }: AlgoliaIndexPageType) { +export default function NewsRoom({ data, serverState }: AlgoliaIndexPageType) { const defaultLocale = defaultLanguage.locale const { pageData, slug, intl } = data const locale = data?.intl?.locale || defaultLocale return ( - - - + + + + + ) } @@ -56,7 +68,7 @@ NewsRoom.getLayout = (page: AppProps) => { ) } -export const getServerSideProps: GetServerSideProps = async ({ req, preview = false, locale = 'en' }) => { +export const getStaticProps: GetStaticProps = async ({ preview = false, locale = 'en' }) => { // For the time being, let's just give 404 for satellites // We will also return 404 if the locale is not English. // This is a hack and and we should improve this at some point @@ -71,12 +83,22 @@ export const getServerSideProps: GetServerSideProps = async ({ req, preview = fa const lang = getNameFromLocale(locale) const intl = await getIntl(locale, false) + const envPrefix = Flags.IS_GLOBAL_PROD ? 'prod' : 'dev' + const indexName = `${envPrefix}_NEWS_en-GB` + + const searchClient = algoliasearch(algolia.applicationId, algolia.searchApiKey) + const index = searchClient.initIndex(indexName) + const response = await index.search('', { + hitsPerPage: 50, + facetFilters: ['type:news', 'topicTags:-Crude Oil Assays'], + facetingAfterDistinct: true, + facets: ['countryTags', 'topicTags', 'year'], + }) + const queryParams = { lang, } - const slug = req.url - const { menuData, pageData, footerData } = await getComponentsData( { query: newsroomQuery, @@ -85,26 +107,19 @@ export const getServerSideProps: GetServerSideProps = async ({ req, preview = fa preview, ) - console.log(JSON.stringify(req.headers)) - const url = new URL(req.headers.referer || `https://${req.headers.host}${req.url}`).toString() - const { data } = await getData({ - query: allNewsDocuments, - queryParams, + const serverState = await getServerState(, { + renderToString, }) - return { props: { - url, data: { menuData, footerData, intl, - pageData: { - ...pageData, - newsArticles: data, - }, - slug, + pageData, + response, }, + serverState, }, } } diff --git a/web/pages/nyheter/index.global.tsx b/web/pages/nyheter/index.global.tsx index 257504cd9..1573d34ba 100644 --- a/web/pages/nyheter/index.global.tsx +++ b/web/pages/nyheter/index.global.tsx @@ -1,29 +1,41 @@ -import { GetServerSideProps } from 'next' +import { GetStaticProps } from 'next' import type { AppProps } from 'next/app' import { IntlProvider } from 'react-intl' import Footer from '../../pageComponents/shared/Footer' import Header from '../../pageComponents/shared/Header' -import { allNewsDocuments, newsroomQuery } from '../../lib/queries/newsroom' +import { renderToString } from 'react-dom/server' +import { newsroomQuery } from '../../lib/queries/newsroom' import getIntl from '../../common/helpers/getIntl' import { getNameFromLocale, getIsoFromLocale } from '../../lib/localization' import { defaultLanguage } from '../../languages' import { AlgoliaIndexPageType, NewsRoomPageType } from '../../types' -import { getComponentsData, getData } from '../../lib/fetchData' -import NewsRoomTemplateSanity from '@templates/newsroom/sanity/NewsroomSanity' +import { getComponentsData } from '../../lib/fetchData' +import NewsRoomTemplate from '@templates/newsroom/Newsroom' +import { getServerState, InstantSearchSSRProvider } from 'react-instantsearch' +import algoliasearch from 'algoliasearch' +import { algolia } from '../../lib/config' +import { Flags } from '../../common/helpers/datasetHelpers' -export default function NorwegianNewsRoom({ data, url }: AlgoliaIndexPageType) { +export default function NorwegianNewsRoom({ data, serverState }: AlgoliaIndexPageType) { const defaultLocale = defaultLanguage.locale const { pageData, slug, intl } = data const locale = intl?.locale || defaultLocale return ( - - - + + + + + ) } @@ -32,6 +44,8 @@ NorwegianNewsRoom.getLayout = (page: AppProps) => { // @ts-ignore const { props } = page const { data } = props + + // Too hardcoded? const slugs = [ { slug: '/news', lang: 'en_GB' }, { slug: '/nyheter', lang: 'nb_NO' }, @@ -54,10 +68,10 @@ NorwegianNewsRoom.getLayout = (page: AppProps) => { ) } -export const getServerSideProps: GetServerSideProps = async ({ req, preview = false, locale = 'no' }) => { +export const getStaticProps: GetStaticProps = async ({ preview = false, locale = 'en' }) => { // For the time being, let's just give 404 for satellites - // We will also return 404 if the locale is not Norwegian. - // This is a hack, and we should improve this at some point + // We will also return 404 if the locale is not English. + // This is a hack and and we should improve this at some point // See https://github.com/vercel/next.js/discussions/18485 if (locale !== 'no') { @@ -69,13 +83,22 @@ export const getServerSideProps: GetServerSideProps = async ({ req, preview = fa const lang = getNameFromLocale(locale) const intl = await getIntl(locale, false) - const url = new URL(req.headers.referer || `https://${req.headers.host}${req.url}`).toString() + const envPrefix = Flags.IS_GLOBAL_PROD ? 'prod' : 'dev' + const indexName = `${envPrefix}_NEWS_nb-NO` + + const searchClient = algoliasearch(algolia.applicationId, algolia.searchApiKey) + const index = searchClient.initIndex(indexName) + const response = await index.search('', { + hitsPerPage: 50, + facetFilters: ['type:news', 'topicTags:-Crude Oil Assays'], + facetingAfterDistinct: true, + facets: ['countryTags', 'topicTags', 'year'], + }) + const queryParams = { lang, } - const slug = req.url - const { menuData, pageData, footerData } = await getComponentsData( { query: newsroomQuery, @@ -83,25 +106,23 @@ export const getServerSideProps: GetServerSideProps = async ({ req, preview = fa }, preview, ) - const { data } = await getData({ - query: allNewsDocuments, - queryParams, - }) + const serverState = await getServerState( + , + { + renderToString, + }, + ) return { props: { - locale, - url, data: { menuData, footerData, intl, - pageData: { - ...pageData, - newsArticles: data, - }, - slug, + pageData, + response, }, + serverState, }, } } diff --git a/web/templates/newsroom/Newsroom.tsx b/web/templates/newsroom/Newsroom.tsx index 54a4f9dd8..416a3a901 100644 --- a/web/templates/newsroom/Newsroom.tsx +++ b/web/templates/newsroom/Newsroom.tsx @@ -1,4 +1,4 @@ -import { forwardRef, useMemo, useRef } from 'react' +import { forwardRef, useRef } from 'react' import singletonRouter from 'next/router' import Blocks from '../../pageComponents/shared/portableText/Blocks' import type { NewsRoomPageType } from '../../types' @@ -8,7 +8,7 @@ import { ResourceLink } from '@core/Link' import { getIsoFromLocale } from '../../lib/localization' import { Flags } from '../../common/helpers/datasetHelpers' import { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs' -import { UiState } from 'instantsearch.js' +import { SearchClient, SearchResponse, UiState } from 'instantsearch.js' import Seo from '../../pageComponents/shared/Seo' import { Configure, InstantSearch } from 'react-instantsearch' import { FormattedMessage, useIntl } from 'react-intl' @@ -20,15 +20,14 @@ import { List } from '@core/List' import { PaginationContextProvider } from '../../common/contexts/PaginationContext' type NewsRoomTemplateProps = { - isServerRendered?: boolean locale?: string pageData?: NewsRoomPageType | undefined slug?: string - url?: string + initialSearchResponse: SearchResponse } const NewsRoomTemplate = forwardRef(function NewsRoomTemplate( - { isServerRendered, locale, pageData, slug, url }, + { locale, pageData, slug, initialSearchResponse }, ref, ) { const { ingress, title, seoAndSome, subscriptionLink, subscriptionLinkTitle, localNewsPages, fallbackImages } = @@ -89,7 +88,7 @@ const NewsRoomTemplate = forwardRef(function const routing = { router: createInstantSearchRouterNext({ singletonRouter, - serverUrl: url, + serverUrl: `https://www.equinor.com${isoCode === 'nb-NO' ? '/no/nyheter' : '/news'}`, // temporary fix for url to be available during build time routerOptions: { createURL: createURL, parseURL: parseURL, @@ -130,23 +129,31 @@ const NewsRoomTemplate = forwardRef(function }, } - const searchClient = useMemo(() => { - return isServerRendered - ? client({ headers: { - //@ts-ignore: TODO - Referer: url } }) - : client(undefined); - }, [isServerRendered, url]); - + const searchClient = client() + + const queriedSearchClient: SearchClient = { + ...searchClient, + search(requests: any) { + if (requests.every(({ params }: any) => !params.query)) { + return Promise.resolve({ + results: requests.map(() => initialSearchResponse), + }) + } + + return searchClient.search(requests) + }, + } + return ( -
- +
+ (function > {subscriptionLink?.slug && ( - - {subscriptionLinkTitle} - + {subscriptionLinkTitle} )} {localNewsPages && @@ -182,7 +187,7 @@ const NewsRoomTemplate = forwardRef(function localNewsPages?.map((localNewsPage) => { return localNewsPage?.link?.slug ? ( - + {localNewsPage?.label} diff --git a/web/templates/newsroom/sanity/NewsHeadlinerSanity.tsx b/web/templates/newsroom/sanity/NewsHeadlinerSanity.tsx deleted file mode 100644 index 862e3337c..000000000 --- a/web/templates/newsroom/sanity/NewsHeadlinerSanity.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { FormattedDate } from '@components/FormattedDateTime' -import { forwardRef, HTMLAttributes } from 'react' -import { BaseLink } from '@core/Link' -import { Typography } from '@core/Typography' -import Image, { Ratios } from '../../../pageComponents/shared/SanityImage' -import envisTwMerge from '../../../twMerge' -import { NewsRoomNewsItem } from '../../../types/algoliaIndexPage' -import { SanityImageObject } from '@sanity/image-url/lib/types/types' -import Blocks from '../../../pageComponents/shared/portableText/Blocks' - -export type NewsHeadlinerProps = { - data: NewsRoomNewsItem - fallbackImage?: SanityImageObject -} & HTMLAttributes - -const NewsHeadlinerSanity = forwardRef(function NewsHeadlinerSanity( - { data, fallbackImage, className = '', ...rest }, - ref, -) { - const { slug, title, ingress, publishDateTime, heroImage, tags, countryTags } = data - - return ( -
- - {(heroImage?.image?.asset || fallbackImage) && ( -
- -
- )} - {publishDateTime && ( - - )} - {title && ( - - {title} - - )} -
- {tags?.map((tag: any, i: number) => { - return ( - - {tag.label} - {i < tags.length - 1 && ,} - - ) - })} - {countryTags?.length > 0 && ,} - {countryTags?.map((country: any, i: number) => { - return ( - - {country.label} - {i < countryTags.length - 1 && ,} - - ) - })} -
- {Array.isArray(ingress) ? ( - - ) : ( - - {ingress} - - )} -
-
- ) -}) -export default NewsHeadlinerSanity diff --git a/web/templates/newsroom/sanity/NewsItemSanity.tsx b/web/templates/newsroom/sanity/NewsItemSanity.tsx deleted file mode 100644 index 7eefc48dc..000000000 --- a/web/templates/newsroom/sanity/NewsItemSanity.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { FormattedDate } from '@components/FormattedDateTime' -import { forwardRef, HTMLAttributes } from 'react' -import { BaseLink } from '@core/Link' -import { Typography } from '@core/Typography' -import Image, { Ratios } from '../../../pageComponents/shared/SanityImage' -import envisTwMerge from '../../../twMerge' -import { NewsRoomNewsItem } from '../../../types/algoliaIndexPage' -import { SanityImageObject } from '@sanity/image-url/lib/types/types' - -export type NewsListItemProps = { - data: NewsRoomNewsItem - fallbackImage?: SanityImageObject -} & HTMLAttributes - -/* Not a semantic list even tho name implies it, used as other news pages with sections */ -const NewsItemSanity = forwardRef(function NewsItemSanity( - { data, fallbackImage, className = '', ...rest }, - ref, -) { - const { slug, title, publishDateTime, heroImage, thumbnailUrl, tags, countryTags } = data || {} - - return ( -
- -
- {publishDateTime && ( - - )} - {title && ( - - {title} - - )} -
- {tags?.map((tag: any, i: number) => { - return ( - - {tag?.label} - {i < tags.length - 1 && ,} - - ) - })} - {countryTags?.length > 0 && ,} - {countryTags?.map((country: any, i: number) => { - return ( - - {country?.label} - {i < countryTags.length - 1 && ,} - - ) - })} -
-
-
- {(heroImage?.image?.asset || fallbackImage || thumbnailUrl) && ( - <> - {thumbnailUrl ? ( - - ) : ( - (heroImage?.image?.asset || fallbackImage) && ( - - ) - )} - - )} -
-
-
- ) -}) -export default NewsItemSanity diff --git a/web/templates/newsroom/sanity/NewsSectionsSanity.tsx b/web/templates/newsroom/sanity/NewsSectionsSanity.tsx deleted file mode 100644 index c5dacd811..000000000 --- a/web/templates/newsroom/sanity/NewsSectionsSanity.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { forwardRef } from 'react' -import { FormattedMessage } from 'react-intl' -import envisTwMerge from '../../../twMerge' -import { SanityImageObject } from '@sanity/image-url/lib/types/types' -import { NewsRoomNewsItem } from '../../../types/algoliaIndexPage' -import NewsHeadlinerSanity from './NewsHeadlinerSanity' -import NewsItemSanity from './NewsItemSanity' - -type NewsSectionsProps = { - newslist: NewsRoomNewsItem[] | undefined - fallbackImages?: SanityImageObject[] -} & React.ComponentProps<'div'> - -const NewsSectionsSanity = forwardRef(function NewsSectionsSanity( - { newslist, fallbackImages, className = '' }, - ref, -) { - if (!newslist || newslist?.length === 0) { - return - } - - return ( -
- -
- {newslist.map((item: NewsRoomNewsItem, index: number) => { - return index !== 0 ? ( - - ) : null - })} -
-
- ) -}) - -export default NewsSectionsSanity diff --git a/web/templates/newsroom/sanity/NewsroomSanity.tsx b/web/templates/newsroom/sanity/NewsroomSanity.tsx deleted file mode 100644 index 4f1aa0a29..000000000 --- a/web/templates/newsroom/sanity/NewsroomSanity.tsx +++ /dev/null @@ -1,186 +0,0 @@ -import { forwardRef, useRef, useState } from 'react' -import Blocks from '../../../pageComponents/shared/portableText/Blocks' -import type { NewsRoomNewsItem, NewsRoomPageType } from '../../../types' -import { Heading, Typography } from '@core/Typography' -import { ResourceLink } from '@core/Link' -import Seo from '../../../pageComponents/shared/Seo' -import { FormattedMessage, useIntl } from 'react-intl' -import { List } from '@core/List' -import { PaginationContextProvider } from '../../../common/contexts/PaginationContext' -import NewsSectionsSanity from './NewsSectionsSanity' -import { stringify } from 'querystring' -import { useRouter } from 'next/router' -import { SimplePagination } from '@core/SimplePagination/SimplePagination' -import NewsSectionsSkeleton from '../NewsSections/NewsSectionsSkeleton' -import { getNameFromLocale } from '../../../lib/localization' - -type NewsRoomTemplateProps = { - isServerRendered?: boolean - locale?: string - pageData?: NewsRoomPageType | undefined - slug?: string - url?: string -} - -const NewsRoomTemplateSanity = forwardRef(function NewsRoomTemplateSanity( - { pageData, slug }, - ref, -) { - const { - newsArticles = [], - ingress, - title, - seoAndSome, - subscriptionLink, - subscriptionLinkTitle, - localNewsPages, - fallbackImages, - } = pageData || {} - - const intl = useIntl() - const router = useRouter() - const { locale } = router - const resultsRef = useRef(null) - const [isLoading, setIsLoading] = useState(false) - const [lastId, setLastId] = useState(newsArticles?.length > 0 ? newsArticles[newsArticles?.length - 1]?.id : null) - const [firstId, setFirstId] = useState(newsArticles?.length > 0 ? newsArticles[0]?.id : null) - const [firstPublished, setFirstPublished] = useState( - newsArticles?.[0]?.firstPublishedAt ?? newsArticles?.[0]?.publishDateTime ?? null, - ) - const [lastPublished, setLastPublished] = useState( - newsArticles?.[newsArticles?.length - 1]?.firstPublishedAt ?? - newsArticles?.[newsArticles?.length - 1]?.publishDateTime ?? - null, - ) - const filterCrudeAssays = (list: NewsRoomNewsItem[]) => { - return list?.filter((item: NewsRoomNewsItem) => item?.tags?.some((tag: any) => tag.key !== 'crude-oil-assays')) - } - const [newsList, setNewsList] = useState(filterCrudeAssays(newsArticles) ?? []) - - const setSearchStates = (filteredNews: any) => { - setNewsList(filterCrudeAssays(filteredNews)) - setFirstId(filteredNews?.length > 0 ? filteredNews[0].id : null) - setLastId(filteredNews?.length > 0 ? filteredNews[filteredNews.length - 1].id : null) - setFirstPublished( - filteredNews?.length > 0 ? filteredNews[0]?.firstPublishedAt ?? filteredNews[0]?.publishDateTime : null, - ) - setLastPublished( - filteredNews?.length > 0 - ? filteredNews[filteredNews?.length - 1]?.firstPublishedAt ?? - filteredNews[filteredNews?.length - 1]?.publishDateTime - : null, - ) - } - - const getNextNews = async () => { - setIsLoading(true) - const query = { - lang: getNameFromLocale(locale), - lastId: lastId, - lastPublishedAt: lastPublished, - } - const urlParams = stringify(query) - const res = await fetch(`/api/news/next?${urlParams}`) - let filteredNews = [] - try { - const response = await res.json() - filteredNews = response.news - } catch (e) { - console.log('Error', e) - } - setSearchStates(filteredNews) - setIsLoading(false) - } - const getPreviousNews = async () => { - setIsLoading(true) - const query = { - lang: getNameFromLocale(locale), - lastId: firstId, - lastPublishedAt: firstPublished, - } - const urlParams = stringify(query) - const res = await fetch(`/api/news/prev?${urlParams}`) - let filteredNews = [] - try { - const response = await res.json() - filteredNews = response.news - } catch (e) { - console.log('Error', e) - } - setSearchStates(filteredNews) - setIsLoading(false) - } - - return ( - - -
-
-
-
- {title && } - {ingress && } -
- - - {subscriptionLink?.slug && ( - - {subscriptionLinkTitle} - - )} - - {localNewsPages && - localNewsPages?.length > 0 && - localNewsPages?.map((localNewsPage) => { - return localNewsPage?.link?.slug || localNewsPage?.href ? ( - - - {localNewsPage?.label} - - - ) : null - })} - -
-
-
-
-
- - - - {isLoading ? ( - - ) : ( - <> - - - - )} -
-
-
-
-
- ) -}) - -export default NewsRoomTemplateSanity diff --git a/web/types/algoliaIndexPage.ts b/web/types/algoliaIndexPage.ts index 50c4e84ff..6915f4723 100644 --- a/web/types/algoliaIndexPage.ts +++ b/web/types/algoliaIndexPage.ts @@ -13,17 +13,17 @@ import type { } from './index' import { PortableTextBlock } from '@portabletext/types' import { SanityImageObject } from '@sanity/image-url/lib/types/types' +import { SearchResponse } from 'instantsearch.js' export type AlgoliaIndexPageType = { - isServerRendered?: boolean serverState?: InstantSearchServerState - url: string data: { menuData?: MenuData footerData?: { footerColumns: FooterColumns[] } intl: IntlData pageData: MagazineIndexPageType | NewsRoomPageType slug?: string + response: SearchResponse } } @@ -36,8 +36,6 @@ export type NewsRoomNewsItem = { heroImage: ImageWithCaptionData thumbnailUrl?: string ingress?: string - tags?: any - countryTags?: any } export type NewsRoomPageType = { @@ -46,7 +44,6 @@ export type NewsRoomPageType = { ingress?: PortableTextBlock[] subscriptionLink?: { slug: string; type: string; lang: string } subscriptionLinkTitle?: string - newsArticles: NewsRoomNewsItem[] localNewsPages?: LinkData[] fallbackImages?: SanityImageObject[] }