diff --git a/web/pages/news/index.global.tsx b/web/pages/news/index.global.tsx index 012e15af1..49949f9fd 100644 --- a/web/pages/news/index.global.tsx +++ b/web/pages/news/index.global.tsx @@ -1,33 +1,29 @@ -import { GetStaticProps } from 'next' +import { GetServerSideProps } 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 { renderToString } from 'react-dom/server' -import { newsroomQuery } from '../../lib/queries/newsroom' +import { allNewsDocuments, 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 } from '../../lib/fetchData' -import NewsRoomTemplate from '@templates/newsroom/Newsroom' -import { getServerState, InstantSearchSSRProvider } from 'react-instantsearch' +import { getComponentsData, getData } from '../../lib/fetchData' +import NewsRoomTemplateSanity from '@templates/newsroom/sanity/NewsroomSanity' -export default function NewsRoom({ data, serverState }: AlgoliaIndexPageType) { +export default function NewsRoom({ data }: AlgoliaIndexPageType) { const defaultLocale = defaultLanguage.locale const { pageData, slug, intl } = data const locale = data?.intl?.locale || defaultLocale return ( - - - - - + + + ) } @@ -60,7 +56,7 @@ NewsRoom.getLayout = (page: AppProps) => { ) } -export const getStaticProps: GetStaticProps = async ({ preview = false, locale = 'en' }) => { +export const getServerSideProps: GetServerSideProps = async ({ req, 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 @@ -79,6 +75,8 @@ export const getStaticProps: GetStaticProps = async ({ preview = false, locale = lang, } + const slug = req.url + const { menuData, pageData, footerData } = await getComponentsData( { query: newsroomQuery, @@ -87,18 +85,26 @@ export const getStaticProps: GetStaticProps = async ({ preview = false, locale = preview, ) - const serverState = await getServerState(, { - renderToString, + 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, }) + return { props: { + url, data: { menuData, footerData, intl, - pageData, + pageData: { + ...pageData, + newsArticles: data, + }, + slug, }, - serverState, }, } } diff --git a/web/pages/nyheter/index.global.tsx b/web/pages/nyheter/index.global.tsx index dec408332..257504cd9 100644 --- a/web/pages/nyheter/index.global.tsx +++ b/web/pages/nyheter/index.global.tsx @@ -1,33 +1,29 @@ -import { GetStaticProps } from 'next' +import { GetServerSideProps } 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 { renderToString } from 'react-dom/server' -import { newsroomQuery } from '../../lib/queries/newsroom' +import { allNewsDocuments, 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 } from '../../lib/fetchData' -import NewsRoomTemplate from '@templates/newsroom/Newsroom' -import { getServerState, InstantSearchSSRProvider } from 'react-instantsearch' +import { getComponentsData, getData } from '../../lib/fetchData' +import NewsRoomTemplateSanity from '@templates/newsroom/sanity/NewsroomSanity' -export default function NorwegianNewsRoom({ data, serverState }: AlgoliaIndexPageType) { +export default function NorwegianNewsRoom({ data, url }: AlgoliaIndexPageType) { const defaultLocale = defaultLanguage.locale const { pageData, slug, intl } = data const locale = intl?.locale || defaultLocale return ( - - - - - + + + ) } @@ -36,8 +32,6 @@ 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' }, @@ -60,10 +54,10 @@ NorwegianNewsRoom.getLayout = (page: AppProps) => { ) } -export const getStaticProps: GetStaticProps = async ({ preview = false, locale = 'en' }) => { +export const getServerSideProps: GetServerSideProps = async ({ req, preview = false, locale = 'no' }) => { // 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 + // We will also return 404 if the locale is not Norwegian. + // This is a hack, and we should improve this at some point // See https://github.com/vercel/next.js/discussions/18485 if (locale !== 'no') { @@ -75,10 +69,13 @@ export const getStaticProps: GetStaticProps = async ({ preview = false, locale = 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 queryParams = { lang, } + const slug = req.url + const { menuData, pageData, footerData } = await getComponentsData( { query: newsroomQuery, @@ -86,19 +83,25 @@ export const getStaticProps: GetStaticProps = async ({ preview = false, locale = }, preview, ) - - const serverState = await getServerState(, { - renderToString, + const { data } = await getData({ + query: allNewsDocuments, + queryParams, }) + return { props: { + locale, + url, data: { menuData, footerData, intl, - pageData, + pageData: { + ...pageData, + newsArticles: data, + }, + slug, }, - serverState, }, } } diff --git a/web/templates/newsroom/Newsroom.tsx b/web/templates/newsroom/Newsroom.tsx index 76366b15f..54a4f9dd8 100644 --- a/web/templates/newsroom/Newsroom.tsx +++ b/web/templates/newsroom/Newsroom.tsx @@ -1,4 +1,4 @@ -import { forwardRef, useRef } from 'react' +import { forwardRef, useMemo, useRef } from 'react' import singletonRouter from 'next/router' import Blocks from '../../pageComponents/shared/portableText/Blocks' import type { NewsRoomPageType } from '../../types' @@ -20,13 +20,15 @@ import { List } from '@core/List' import { PaginationContextProvider } from '../../common/contexts/PaginationContext' type NewsRoomTemplateProps = { + isServerRendered?: boolean locale?: string pageData?: NewsRoomPageType | undefined slug?: string + url?: string } const NewsRoomTemplate = forwardRef(function NewsRoomTemplate( - { locale, pageData, slug }, + { isServerRendered, locale, pageData, slug, url }, ref, ) { const { ingress, title, seoAndSome, subscriptionLink, subscriptionLinkTitle, localNewsPages, fallbackImages } = @@ -87,7 +89,7 @@ const NewsRoomTemplate = forwardRef(function const routing = { router: createInstantSearchRouterNext({ singletonRouter, - // serverUrl: `http://localhost:3000/${isoCode === 'nb-NO' ? 'no/nyheter' : 'news'}`, // uncomment this line for local development + serverUrl: url, routerOptions: { createURL: createURL, parseURL: parseURL, @@ -128,17 +130,23 @@ const NewsRoomTemplate = forwardRef(function }, } - const searchClient = client() + const searchClient = useMemo(() => { + return isServerRendered + ? client({ headers: { + //@ts-ignore: TODO + Referer: url } }) + : client(undefined); + }, [isServerRendered, url]); + return (
- + + +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 new file mode 100644 index 000000000..7eefc48dc --- /dev/null +++ b/web/templates/newsroom/sanity/NewsItemSanity.tsx @@ -0,0 +1,89 @@ +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 new file mode 100644 index 000000000..c5dacd811 --- /dev/null +++ b/web/templates/newsroom/sanity/NewsSectionsSanity.tsx @@ -0,0 +1,52 @@ +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 new file mode 100644 index 000000000..4f1aa0a29 --- /dev/null +++ b/web/templates/newsroom/sanity/NewsroomSanity.tsx @@ -0,0 +1,186 @@ +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 1b1a24ea8..50c4e84ff 100644 --- a/web/types/algoliaIndexPage.ts +++ b/web/types/algoliaIndexPage.ts @@ -15,7 +15,9 @@ import { PortableTextBlock } from '@portabletext/types' import { SanityImageObject } from '@sanity/image-url/lib/types/types' export type AlgoliaIndexPageType = { + isServerRendered?: boolean serverState?: InstantSearchServerState + url: string data: { menuData?: MenuData footerData?: { footerColumns: FooterColumns[] } @@ -34,6 +36,8 @@ export type NewsRoomNewsItem = { heroImage: ImageWithCaptionData thumbnailUrl?: string ingress?: string + tags?: any + countryTags?: any } export type NewsRoomPageType = { @@ -42,6 +46,7 @@ export type NewsRoomPageType = { ingress?: PortableTextBlock[] subscriptionLink?: { slug: string; type: string; lang: string } subscriptionLinkTitle?: string + newsArticles: NewsRoomNewsItem[] localNewsPages?: LinkData[] fallbackImages?: SanityImageObject[] }