Skip to content

Commit

Permalink
Add new layout for audio page (#12676)
Browse files Browse the repository at this point in the history
Add a new layout file for the audio articles (podcasts)
Moves Audio player up the grid



---------

Co-authored-by: Alex Sanders <alex@sndrs.dev>
  • Loading branch information
oliverabrahams and sndrs authored Oct 25, 2024
1 parent 69fc256 commit 58998f6
Show file tree
Hide file tree
Showing 10 changed files with 976 additions and 58 deletions.
34 changes: 14 additions & 20 deletions dotcom-rendering/src/components/ArticleMeta.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
type ArticleFormat,
ArticleSpecial,
} from '../lib/articleFormat';
import { getAudioData } from '../lib/audio-data';
import { getSoleContributor } from '../lib/byline';
import { palette as themePalette } from '../palette';
import type { Branding as BrandingType } from '../types/branding';
Expand Down Expand Up @@ -282,23 +283,11 @@ const metaNumbersExtrasLiveBlog = css`
}
`;

const getAudioDownloadUrl = (
mainMediaElements: FEElement[] | undefined,
): string => {
const audioBlockElement = mainMediaElements?.find(
(element) =>
element._type ===
'model.dotcomrendering.pageElements.AudioBlockElement',
);

return audioBlockElement?.assets[0]?.url ?? '';
};

const getSeriesTag = (tags: TagType[]): TagType | undefined => {
return tags.find((tag) => tag.type === 'Series' && tag.podcast);
};

const getPodcastTag = (tags: TagType[]): Podcast | undefined => {
const getPodcast = (tags: TagType[]): Podcast | undefined => {
const seriesTag = getSeriesTag(tags);

return seriesTag?.podcast;
Expand Down Expand Up @@ -339,8 +328,9 @@ export const ArticleMeta = ({

const { renderingTarget } = useConfig();

const audioDownloadUrl = getAudioDownloadUrl(mainMediaElements);
const podcastTag = getPodcastTag(tags);
const seriesTag = getSeriesTag(tags);
const audioData = getAudioData(mainMediaElements);
const podcast = getPodcast(tags);
const rssFeedUrl = getRssFeedUrl(tags);

return (
Expand Down Expand Up @@ -380,12 +370,16 @@ export const ArticleMeta = ({
</MetaAvatarContainer>
)}
<div>
{isAudio && podcastTag && (
{isAudio && podcast && seriesTag && (
<PodcastMeta
image={podcastTag.image}
spotifyUrl={podcastTag.spotifyUrl}
subscriptionUrl={podcastTag.subscriptionUrl}
audioDownloadUrl={audioDownloadUrl}
series={seriesTag}
format={format}
image={podcast.image}
spotifyUrl={podcast.spotifyUrl}
subscriptionUrl={podcast.subscriptionUrl}
audioDownloadUrl={
audioData?.audioDownloadUrl
}
rssFeedUrl={rssFeedUrl}
/>
)}
Expand Down
53 changes: 53 additions & 0 deletions dotcom-rendering/src/components/AudioPlayerWrapper.importable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { getConsentFor, onConsentChange } from '@guardian/libs';
import { useEffect, useState } from 'react';
import { AudioPlayer } from './AudioPlayer/AudioPlayer';

type Props = {
mediaId: string;
duration?: number;
src: string;
contentIsNotSensitive: boolean;
isAcastEnabled: boolean;
};

/**
* ## Why does this need to be an Island?
*
* The audio player is interactive.
* Requires consent to use audio ads.
*
* ---
*
* (No visual story exists)
*/
export const AudioPlayerWrapper = ({
duration,
src,
mediaId,
contentIsNotSensitive,
isAcastEnabled,
}: Props) => {
const [finalSrc, setFinalSrc] = useState<string>(src);

useEffect(() => {
// this is how frontend checks for whether to show ads or not,
// and it's on the window in DCR but it's not clear how...
// so this just carries over the existing logic
// https://github.com/guardian/frontend/blob/ba57677baaa06f37235e8d7a983cb383d0f5c989/static/src/javascripts/projects/common/modules/audio/index.js#L25-L44
const isPodcast = window.guardian.config.page.isPodcast;

if (contentIsNotSensitive && isAcastEnabled && isPodcast) {
onConsentChange((consentState) => {
const consentForAcast = getConsentFor('acast', consentState);

if (consentForAcast) {
setFinalSrc(
src.replace('https://', 'https://flex.acast.com/'),
);
}
});
}
}, [src, contentIsNotSensitive, isAcastEnabled]);

return <AudioPlayer src={finalSrc} mediaId={mediaId} duration={duration} />;
};
41 changes: 41 additions & 0 deletions dotcom-rendering/src/components/PodcastCoverImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { css } from '@emotion/react';
import { from } from '@guardian/source/foundations';
import type { ArticleFormat } from '../lib/articleFormat';
import type { TagType } from '../types/tag';
import { Picture } from './Picture';

const podcastResponsiveCoverImage = css`
img {
width: 140px;
height: 140px;
}
margin-bottom: 0.375rem;
${from.wide} {
img {
width: 219px;
height: 219px;
}
}
`;

export const PodcastCoverImage = ({
format,
series,
}: {
format: ArticleFormat;
series: TagType;
}) => {
return (
<div css={podcastResponsiveCoverImage}>
<Picture
role={'podcastCover'}
format={format}
master={series.podcast?.image ?? ''}
alt={series.title}
height={1}
width={1}
loading="lazy"
/>
</div>
);
};
67 changes: 56 additions & 11 deletions dotcom-rendering/src/components/PodcastMeta.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,76 @@
import { css } from '@emotion/react';
import { palette } from '@guardian/source/foundations';
import { from, palette } from '@guardian/source/foundations';
import type { ArticleFormat } from '../lib/articleFormat';
import { ArticleDesign, ArticleDisplay, Pillar } from '../lib/articleFormat';
import type { TagType } from '../types/tag';
import { PodcastMeta } from './PodcastMeta';

const format: ArticleFormat = {
design: ArticleDesign.Audio,
display: ArticleDisplay.Standard,
theme: Pillar.News,
};

const podcastSeries: TagType = {
id: 'lifeandstyle/series/comforteatingwithgracedent',
type: 'Series',
title: 'Comfort Eating with Grace Dent',
podcast: {
subscriptionUrl:
'https://podcasts.apple.com/gb/podcast/comfort-eating-with-grace-dent/id1571446706',
spotifyUrl:
'https://open.spotify.com/show/5fMtMMKSlUoDuxhd3a3IS0?si=3B38GpeFThy4YtxSyNxApA&dl_branch=1',
image: 'https://uploads.guim.co.uk/2023/09/18/GD_ComfortEating_3000x3000.jpg',
},
};

const Wrapper = ({ children }: { children: React.ReactNode }) => (
<div
css={css`
width: 620px;
padding: 20px;
background-color: ${palette.neutral[7]};
margin: 20px;
${from.leftCol} {
width: 140px;
}
${from.wide} {
width: 219px;
}
`}
>
{children}
</div>
);

export const Default = () => (
<Wrapper>
<PodcastMeta
spotifyUrl="#"
subscriptionUrl="#"
rssFeedUrl="#"
audioDownloadUrl="#"
/>
</Wrapper>
<div
css={css`
width: 100%;
height: 100%;
background-color: ${palette.neutral[7]};
`}
>
<Wrapper>
<PodcastMeta
series={podcastSeries}
format={format}
spotifyUrl="#"
subscriptionUrl="#"
rssFeedUrl="#"
audioDownloadUrl="#"
/>
</Wrapper>
</div>
);

export default {
component: PodcastMeta,
title: 'Components/PodcastMeta',
parameters: {
formats: [
{
design: ArticleDesign.Audio,
display: ArticleDisplay.Standard,
},
],
},
};
65 changes: 56 additions & 9 deletions dotcom-rendering/src/components/PodcastMeta.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { css } from '@emotion/react';
import {
articleBold17,
from,
headlineBold17,
space,
textSans14,
until,
} from '@guardian/source/foundations';
import { LinkButton, SvgDownload } from '@guardian/source/react-components';
import { StraightLines } from '@guardian/source-development-kitchen/react-components';
import type { ReactElement } from 'react';
import type { ArticleFormat } from '../lib/articleFormat';
import { palette as themePalette } from '../palette';
import type { Podcast } from '../types/tag';
import type { Podcast, TagType } from '../types/tag';
import { DecideLines } from './DecideLines';
import { GridItem } from './GridItem';
import { PodcastCoverImage } from './PodcastCoverImage';

type PodcastButtonProps = {
label: string;
Expand Down Expand Up @@ -39,8 +45,9 @@ const ApplePodcastsSvg = () => (

const podcastTitleStyles = css`
color: ${themePalette('--podcast-meta-title')};
${articleBold17};
${headlineBold17};
padding: ${space[2]}px 0;
grid-area: meta-title;
`;

const podcastButtonStyles = css`
Expand All @@ -60,16 +67,20 @@ const podcastButtonStyles = css`
`;

const podcastButtonListStyles = css`
display: inline-flex;
flex-wrap: wrap;
grid-area: meta-links;
${from.leftCol} {
display: block;
}
`;

const PodcastButton = ({ label, url, icon }: PodcastButtonProps) => (
<li>
<li
css={css`
${until.leftCol} {
display: inline-block;
}
`}
>
<LinkButton
size="small"
href={url}
Expand All @@ -84,16 +95,43 @@ const PodcastButton = ({ label, url, icon }: PodcastButtonProps) => (
interface PodcastMetaProps extends Podcast {
rssFeedUrl?: string;
audioDownloadUrl?: string;
format: ArticleFormat;
series: TagType;
}

export const PodcastMeta = ({
format,
series,
subscriptionUrl,
spotifyUrl,
rssFeedUrl,
audioDownloadUrl,
}: PodcastMetaProps) => {
return (
<>
<div
css={css`
margin-top: -2px;
${until.leftCol} {
display: grid;
grid-template-columns: 140px 1fr;
grid-column-gap: ${space[3]}px;
grid-template-areas:
'meta-three meta-three'
'meta-title meta-title'
'meta-image meta-links'
'meta-lines meta-lines';
}
`}
>
<GridItem area="meta-image">
<PodcastCoverImage format={format} series={series} />
</GridItem>
<GridItem area="meta-three">
<DecideLines
format={format}
color={themePalette('--article-border')}
/>
</GridItem>
<div css={podcastTitleStyles}>More ways to listen</div>
<ul css={podcastButtonListStyles}>
{!!subscriptionUrl && (
Expand Down Expand Up @@ -121,6 +159,15 @@ export const PodcastMeta = ({
/>
)}
</ul>
</>

<StraightLines
cssOverrides={css`
grid-area: meta-lines;
margin: ${space[3]}px 0 ${space[2]}px;
`}
count={1}
color={themePalette('--article-border')}
/>
</div>
);
};
Loading

0 comments on commit 58998f6

Please sign in to comment.