Skip to content
This repository was archived by the owner on Sep 1, 2021. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/components/blocks/image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import React from 'react';
import * as AssetUtils from 'utils/Asset';
import { BlockElement } from 'types/capi-thrift-models';

// ----- Setup ----- //

Expand All @@ -17,6 +18,9 @@ interface Image {

// ----- Functions ----- //

const isImage = (elem: BlockElement): boolean =>
elem.type === 'image';

const imageElement = (alt: string, assets: AssetUtils.Asset[], salt: string): React.ReactNode =>
h('img', {
sizes: '100%',
Expand All @@ -39,6 +43,7 @@ function imageBlock(image: Image, assets: AssetUtils.Asset[], salt: string): Rea
// ----- Exports ----- //

export {
isImage,
imageBlock,
imageElement
};
108 changes: 50 additions & 58 deletions src/components/liveblog/LiveblogArticle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import LiveblogKeyEvents from './LiveblogKeyEvents';
import LiveblogBody from './LiveblogBody';
import HeaderImage from '../shared/HeaderImage';
import Tags from '../shared/Tags';
import { Option } from 'types/Option';
import { PillarStyles, PillarId, wideColumnWidth, baseMultiply } from 'styles';
import { Series, Contributor } from '../../types/Capi';
import { Tag, Block, BlockElement } from 'types/capi-thrift-models';
import { PillarStyles, wideColumnWidth, baseMultiply, getPillarStyles } from 'styles';
import { Tag } from 'types/capi-thrift-models';
import { css, SerializedStyles } from '@emotion/core'
import { palette, wide } from '@guardian/src-foundations'
import { Env } from 'types/Env';
import { Reader } from 'types/Reader';
import { isImage } from 'components/blocks/image';
import { fromNullable } from 'types/Option';

const LiveblogArticleStyles: SerializedStyles = css`
background: ${palette.neutral[97]};
Expand Down Expand Up @@ -43,62 +45,52 @@ const HeaderImageStyles = (pillarStyles: PillarStyles): SerializedStyles => css`
`;

interface LiveblogArticleProps {
headline: string;
standfirst: string;
bylineHtml: string;
webPublicationDate: string;
body: string;
tags: Tag[];
pillarId: PillarId;
mainImage: Option<BlockElement>;
pillarStyles: PillarStyles;
contributors: Contributor[];
series: Series;
bodyElements: Block[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
capi: any;
isLive: boolean;
imageSalt: string;
}

const LiveblogArticle = ({
headline,
standfirst,
bylineHtml,
webPublicationDate,
pillarId,
tags,
mainImage,
pillarStyles,
contributors,
series,
bodyElements,
imageSalt
}: LiveblogArticleProps): JSX.Element =>
<main css={LiveblogArticleStyles}>
<div css={BorderStyles}>
<LiveblogSeries series={series} pillarStyles={pillarStyles}/>
<LiveblogHeadline headline={headline} pillarStyles={pillarStyles}/>
<LiveblogStandfirst standfirst={standfirst} pillarStyles={pillarStyles}/>
<LiveblogByline
byline={bylineHtml}
pillarStyles={pillarStyles}
pillarId={pillarId}
publicationDate={webPublicationDate}
contributors={contributors}
imageSalt={imageSalt}
/>
<HeaderImage
image={mainImage}
imageSalt={imageSalt}
className={HeaderImageStyles(pillarStyles)}
/>
<LiveblogKeyEvents bodyElements={bodyElements} pillarStyles={pillarStyles}/>
<LiveblogBody
bodyElements={bodyElements}
pillarStyles={pillarStyles}
imageSalt={imageSalt}
/>
<Tags tags={tags} background={palette.neutral[93]}/>
</div>
</main>
const LiveblogArticle = ({ capi }: LiveblogArticleProps): Reader<Env, JSX.Element | null> => Reader.do(function* () {

const { type, fields, tags, webPublicationDate, pillarId, blocks } = capi;
const [series] = tags.filter((tag: Tag) => tag.type === 'series');
const pillarStyles = getPillarStyles(pillarId);
const contributors = tags.filter((tag: Tag) => tag.type === 'contributor');
const bodyElements = type === 'liveblog' ? blocks.body : blocks.body[0].elements;
const image = fromNullable(blocks.main.elements.filter(isImage)[0]);

const headerImage = yield HeaderImage({ image: image, className: HeaderImageStyles(pillarStyles) });
const liveblogByline = yield LiveblogByline({
byline: fields.bylineHtml,
pillarId,
publicationDate: webPublicationDate,
contributors,
});
const liveblogBody = yield LiveblogBody({ bodyElements, pillarStyles });

return Reader.of<Env, JSX.Element | null>(
<main css={LiveblogArticleStyles}>
<div css={BorderStyles}>
<LiveblogSeries series={series} pillarStyles={pillarStyles}/>
<LiveblogHeadline
headline={fields.headline}
pillarStyles={pillarStyles}
/>
<LiveblogStandfirst
standfirst={fields.standfirst}
pillarStyles={pillarStyles}
/>
{ liveblogByline }
{ headerImage }
<LiveblogKeyEvents
bodyElements={bodyElements}
pillarStyles={pillarStyles}
/>
{ liveblogBody }
<Tags tags={tags} background={palette.neutral[93]}/>
</div>
</main>
);
}());

export default LiveblogArticle;
29 changes: 15 additions & 14 deletions src/components/liveblog/LiveblogBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { render } from 'renderBlocks';
import { Block } from 'types/capi-thrift-models';

import { css, SerializedStyles } from '@emotion/core'
import { Reader } from 'types/Reader';
import { Env } from 'types/Env';

const LiveBodyStyles = (pillarStyles: PillarStyles): SerializedStyles => css`
.rich-link,
Expand All @@ -25,33 +27,32 @@ const LiveBodyStyles = (pillarStyles: PillarStyles): SerializedStyles => css`
interface LiveblogBodyProps {
pillarStyles: PillarStyles;
bodyElements: Block[];
imageSalt: string;
}

const LiveblogBody= ({ pillarStyles, bodyElements, imageSalt }: LiveblogBodyProps): JSX.Element => {
function LiveblogBody({ pillarStyles, bodyElements }: LiveblogBodyProps): Reader<Env, JSX.Element> {
const initialBlocks = bodyElements.slice(0, 7);
const LoadMore = ({ total }: { total: number }): JSX.Element | null => total > 10
? <LiveblogLoadMore pillarStyles={pillarStyles}/>
: null;

return (
<article css={LiveBodyStyles(pillarStyles)}>
{
initialBlocks.map((block: Block) => {
return <LiveblogBlock
Reader.asks(env =>
<article css={LiveBodyStyles(pillarStyles)}>
{ initialBlocks.map((block: Block) =>
<LiveblogBlock
key={block.id}
pillarStyles={pillarStyles}
highlighted={!!block.attributes.keyEvent}
title={block.title}
firstPublishedDate={block.firstPublishedDate}
lastModifiedDate={block.lastModifiedDate}>
<>{render(block.elements, imageSalt).html}</>
</LiveblogBlock>
})
}
<LoadMore total={bodyElements.length}/>
</article>
)
lastModifiedDate={block.lastModifiedDate}
>
<>{render(block.elements).run(env).html}</>
</LiveblogBlock>
) }
<LoadMore total={bodyElements.length}/>
</article>
))
}

export default LiveblogBody;
27 changes: 12 additions & 15 deletions src/components/liveblog/LiveblogByline.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { textSans, PillarStyles, PillarId } from '../../styles';
import { textSans, PillarStyles, PillarId, getPillarStyles } from 'styles';

import { Keyline } from '../shared/Keyline';

Expand All @@ -9,6 +9,8 @@ import { Contributor } from '../../types/Capi';
import { formatDate } from 'utils/date';
import Avatar from 'components/shared/Avatar';
import LeftColumn from 'components/shared/LeftColumn';
import { Env } from 'types/Env';
import { Reader } from 'types/Reader';

const LiveblogBylineStyles = ({ liveblogBackground }: PillarStyles): SerializedStyles => css`
background: ${liveblogBackground};
Expand Down Expand Up @@ -46,39 +48,34 @@ const LiveblogBylineStyles = ({ liveblogBackground }: PillarStyles): SerializedS

interface LiveblogBylineProps {
byline: string;
pillarStyles: PillarStyles;
publicationDate: string;
contributors: Contributor[];
pillarId: PillarId;
imageSalt: string;
}

const LiveblogByline = ({
function LiveblogByline({
byline,
pillarStyles,
publicationDate,
contributors,
pillarId,
imageSalt
}: LiveblogBylineProps): JSX.Element => {

return (
}: LiveblogBylineProps): Reader<Env, JSX.Element> {
const pillarStyles = getPillarStyles(pillarId);

return Avatar({ contributors, bgColour: pillarStyles.featureHeadline }).map(avatar =>
// This is not an iterator, ESLint is confused
// eslint-disable-next-line react/jsx-key

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a valid warning but should we disable it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think React may throw a warning for this at runtime, so yeah maybe we don't need the lint rule. Also, I don't think it would matter on the server because isn't it a performance optimisation for re-renders?

<div css={[LiveblogBylineStyles(pillarStyles)]}>
<Keyline pillar={pillarId} type={'liveblog'}/>
<LeftColumn>
<Avatar
contributors={contributors}
bgColour={pillarStyles.featureHeadline}
imageSalt={imageSalt}
/>
{ avatar }
<div className="author">
<address dangerouslySetInnerHTML={{__html: byline}}></address>
<time className="date">{ formatDate(new Date(publicationDate)) }</time>
<div className="follow">Get alerts on this story</div>
</div>
</LeftColumn>
</div>
)
);
}

export default LiveblogByline;
Loading