Skip to content

Commit 22ff26d

Browse files
authored
Merge pull request #14396 from guardian/dina/ads-in-galleries-dcar
Add ads in galleries on both web and app
2 parents 155e5bd + 7c018c7 commit 22ff26d

File tree

4 files changed

+138
-67
lines changed

4 files changed

+138
-67
lines changed

dotcom-rendering/src/layouts/GalleryLayout.tsx

Lines changed: 51 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
} from '@guardian/source/foundations';
88
import { Hide } from '@guardian/source/react-components';
99
import { Fragment } from 'react';
10+
import { AdPlaceholder } from '../components/AdPlaceholder.apps';
11+
import { AdPortals } from '../components/AdPortals.importable';
1012
import { AdSlot } from '../components/AdSlot.web';
1113
import { AppsFooter } from '../components/AppsFooter.importable';
1214
import { ArticleHeadline } from '../components/ArticleHeadline';
@@ -31,7 +33,6 @@ import { type ArticleFormat, ArticleSpecial } from '../lib/articleFormat';
3133
import { canRenderAds } from '../lib/canRenderAds';
3234
import { getContributionsServiceUrl } from '../lib/contributions';
3335
import { decideMainMediaCaption } from '../lib/decide-caption';
34-
import { getAdPositions } from '../lib/getGalleryAdPositions';
3536
import type { NavType } from '../model/extract-nav';
3637
import { palette as themePalette } from '../palette';
3738
import type { Gallery } from '../types/article';
@@ -171,10 +172,6 @@ export const GalleryLayout = (props: WebProps | AppProps) => {
171172

172173
const contributionsServiceUrl = getContributionsServiceUrl(frontendData);
173174

174-
const adPositions: number[] = renderAds
175-
? getAdPositions(gallery.images)
176-
: [];
177-
178175
return (
179176
<>
180177
{isWeb && (
@@ -231,6 +228,11 @@ export const GalleryLayout = (props: WebProps | AppProps) => {
231228
backgroundColor: themePalette('--article-background'),
232229
}}
233230
>
231+
{isApps && renderAds && (
232+
<Island priority="critical">
233+
<AdPortals />
234+
</Island>
235+
)}
234236
<header css={headerStyles}>
235237
<MainMediaGallery
236238
mainMedia={gallery.mainMedia}
@@ -310,41 +312,51 @@ export const GalleryLayout = (props: WebProps | AppProps) => {
310312
) : null}
311313
</div>
312314
</header>
313-
{gallery.images.map((element, idx) => {
314-
const index = idx + 1;
315-
const shouldShowAds = adPositions.includes(index);
316-
315+
{gallery.bodyElements.map((element, index) => {
316+
const isImage =
317+
element._type ===
318+
'model.dotcomrendering.pageElements.ImageBlockElement';
319+
const shouldShowAds =
320+
element._type ===
321+
'model.dotcomrendering.pageElements.AdPlaceholderBlockElement';
317322
return (
318-
<Fragment key={element.elementId}>
319-
<GalleryImage
320-
image={element}
321-
format={format}
322-
pageId={frontendData.pageId}
323-
webTitle={frontendData.webTitle}
324-
renderingTarget={props.renderingTarget}
325-
/>
326-
{isWeb && shouldShowAds && (
327-
<div css={galleryItemAdvertStyles}>
328-
<div css={galleryInlineAdContainerStyles}>
329-
<Hide until="tablet">
330-
<DesktopAdSlot
331-
renderAds={renderAds}
332-
adSlotIndex={adPositions.indexOf(
333-
index,
334-
)}
335-
/>
336-
</Hide>
337-
<Hide from="tablet">
338-
<MobileAdSlot
339-
renderAds={renderAds}
340-
adSlotIndex={adPositions.indexOf(
341-
index,
342-
)}
343-
/>
344-
</Hide>
345-
</div>
346-
<div css={galleryBorder}></div>
347-
</div>
323+
<Fragment key={isImage ? element.elementId : index}>
324+
{isImage && (
325+
<GalleryImage
326+
image={element}
327+
format={format}
328+
pageId={frontendData.pageId}
329+
webTitle={frontendData.webTitle}
330+
renderingTarget={props.renderingTarget}
331+
/>
332+
)}
333+
{shouldShowAds && renderAds && (
334+
<>
335+
{isWeb && (
336+
<div css={galleryItemAdvertStyles}>
337+
<div
338+
css={
339+
galleryInlineAdContainerStyles
340+
}
341+
>
342+
<Hide until="tablet">
343+
<DesktopAdSlot
344+
renderAds={renderAds}
345+
adSlotIndex={index}
346+
/>
347+
</Hide>
348+
<Hide from="tablet">
349+
<MobileAdSlot
350+
renderAds={renderAds}
351+
adSlotIndex={index}
352+
/>
353+
</Hide>
354+
</div>
355+
<div css={galleryBorder}></div>
356+
</div>
357+
)}
358+
{isApps && <AdPlaceholder />}
359+
</>
348360
)}
349361
</Fragment>
350362
);

dotcom-rendering/src/lib/getGalleryAdPositions.ts

Lines changed: 0 additions & 17 deletions
This file was deleted.

dotcom-rendering/src/model/enhance-ad-placeholders.ts

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,63 @@ const insertPlaceholder = (
6969
return [...prevElements, placeholder, currentElement];
7070
};
7171

72+
const insertPlaceholderAfterCurrentElement = (
73+
prevElements: FEElement[],
74+
currentElement: FEElement,
75+
): FEElement[] => {
76+
const placeholder: AdPlaceholderBlockElement = {
77+
_type: 'model.dotcomrendering.pageElements.AdPlaceholderBlockElement',
78+
};
79+
return [...prevElements, currentElement, placeholder];
80+
};
81+
82+
type ReducerAccumulatorGallery = {
83+
elements: FEElement[];
84+
imageBlockElementCounter: number;
85+
};
86+
87+
/**
88+
* Insert ad placeholders for gallery articles.
89+
* Gallery-specific rules:
90+
* - Place ads after every 4th image
91+
* - Start placing ads after the 4th image
92+
* @param elements - The array of elements to enhance
93+
* @returns The enhanced array of elements with ad placeholders inserted
94+
*/
95+
const insertAdPlaceholdersForGallery = (elements: FEElement[]): FEElement[] => {
96+
const elementsWithReducerContext = elements.reduce(
97+
(
98+
prev: ReducerAccumulatorGallery,
99+
currentElement: FEElement,
100+
): ReducerAccumulatorGallery => {
101+
const imageBlockElementCounter =
102+
currentElement._type ===
103+
'model.dotcomrendering.pageElements.ImageBlockElement'
104+
? prev.imageBlockElementCounter + 1
105+
: prev.imageBlockElementCounter;
106+
107+
const shouldInsertAd = imageBlockElementCounter % 4 === 0;
108+
109+
return {
110+
elements: shouldInsertAd
111+
? insertPlaceholderAfterCurrentElement(
112+
prev.elements,
113+
currentElement,
114+
)
115+
: [...prev.elements, currentElement],
116+
imageBlockElementCounter,
117+
};
118+
},
119+
// Initial value for reducer function
120+
{
121+
elements: [],
122+
imageBlockElementCounter: 0,
123+
},
124+
);
125+
126+
return elementsWithReducerContext.elements;
127+
};
128+
72129
/**
73130
* - elementCounter: the number of paragraphs and images that we've counted when considering ad insertion
74131
* - lastAdIndex: the index of the most recently inserted ad, used to calculate the elements between subsequent ads
@@ -140,10 +197,22 @@ export const enhanceAdPlaceholders =
140197
renderingTarget: RenderingTarget,
141198
shouldHideAds: boolean,
142199
) =>
143-
(elements: FEElement[]): FEElement[] =>
144-
renderingTarget === 'Apps' &&
145-
!shouldHideAds &&
146-
format.design !== ArticleDesign.LiveBlog &&
147-
format.design !== ArticleDesign.DeadBlog
148-
? insertAdPlaceholders(elements)
149-
: elements;
200+
(elements: FEElement[]): FEElement[] => {
201+
if (shouldHideAds) return elements;
202+
203+
// In galleries the AdPlaceholders are inserted in both
204+
// Web & App because the same logic is used for both
205+
if (format.design === ArticleDesign.Gallery) {
206+
return insertAdPlaceholdersForGallery(elements);
207+
}
208+
209+
if (
210+
renderingTarget === 'Apps' &&
211+
format.design !== ArticleDesign.LiveBlog &&
212+
format.design !== ArticleDesign.DeadBlog
213+
) {
214+
return insertAdPlaceholders(elements);
215+
}
216+
217+
return elements;
218+
};

dotcom-rendering/src/types/article.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@ import {
1818
type TableOfContentsItem,
1919
} from '../model/enhanceTableOfContents';
2020
import { enhancePinnedPost } from '../model/pinnedPost';
21-
import type { FEElement, ImageBlockElement, ImageForLightbox } from './content';
21+
import type {
22+
AdPlaceholderBlockElement,
23+
FEElement,
24+
ImageBlockElement,
25+
ImageForLightbox,
26+
} from './content';
2227
import { type RenderingTarget } from './renderingTarget';
2328

2429
/**
@@ -40,7 +45,7 @@ export type ArticleFields = {
4045

4146
export type Gallery = ArticleFields & {
4247
design: ArticleDesign.Gallery;
43-
images: ImageBlockElement[];
48+
bodyElements: (ImageBlockElement | AdPlaceholderBlockElement)[];
4449
mainMedia: ImageBlockElement;
4550
};
4651

@@ -130,11 +135,13 @@ export const enhanceArticleType = (
130135
design,
131136
display: format.display,
132137
theme: format.theme,
133-
images: blocks.flatMap((block) =>
138+
bodyElements: blocks.flatMap((block) =>
134139
block.elements.filter(
135140
(element) =>
136141
element._type ===
137-
'model.dotcomrendering.pageElements.ImageBlockElement',
142+
'model.dotcomrendering.pageElements.ImageBlockElement' ||
143+
element._type ===
144+
'model.dotcomrendering.pageElements.AdPlaceholderBlockElement',
138145
),
139146
),
140147
mainMedia: getGalleryMainMedia(

0 commit comments

Comments
 (0)