Skip to content

Commit 625dc74

Browse files
authored
Click to load: mobile block placeholder (#525)
* Add mobile blocked content placeholder to CTL * Add styles for YouTube Click to Load blocked placeholder * Fix media-queries for Firefox | Change DDGCtlPlaceholderBlocked to use arrow functions and keep reference on Firefox * Rollback translation changes * Fix text alignment * Update resizing of new templates | Fix issue with inline parent elements * - Add container to element inside shadow root to improve layouting. This container is set to copy the styles from the root element outside the shadow root. - Add Web Component lifecycle functions to update the styles from the Placeholder Blocked template when connected to the DOM and when the root elements gets its styles updated. - Use CSS classes to set the size and show/hide content instead of media queries. That because the size of the placeholder isn't related to the size of the screen. - Minor style adjustments. * Remove "inset" reset * Attend PR comments: - Add comment explaining new custom element - Rename DDGCtlPlaceholderBlocked -> DDGCtlPlaceholderBlockedElement - General fixes to CSS - reset styles from host element, clean up styles, etc * Style updates to attend Design Review - Introduce size-xs to show only the unblock button when there isn't enough height to show our placeholder copy -> pending review - Update CSS selectors to directly target size-md and size-lg when necessary. - Update the placeholder size after style updates from the parent element, because we change the root element height based on the parent size after injecting it on the page, and that might change our placeholder to a different size. * Fix issue with size var assignment * Define custom element params type | Move ternaries out of template | Introduce toggle size param and decouple it from platform logic * Create CSS variables for Design System colors | Use CSS vars and media query to automatically update values to dark theme * Move CSS vars to separate file | Move params definitions to top of constructor and split localized copy in different params * Add comment about using arrow functions on CTL class * Fix lint issues * Reduce min height to show only unblock button to 110px --------- Co-authored-by: francielefaccin <[email protected]>
1 parent 207dd9e commit 625dc74

File tree

4 files changed

+729
-28
lines changed

4 files changed

+729
-28
lines changed

src/features/click-to-load.js

Lines changed: 100 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createCustomEvent, sendMessage, originalWindowDispatchEvent } from '../
22
import { logoImg, loadingImages, closeIcon } from './click-to-load/ctl-assets.js'
33
import { getStyles, getConfig } from './click-to-load/ctl-config.js'
44
import ContentFeature from '../content-feature.js'
5+
import { DDGCtlPlaceholderBlockedElement } from './click-to-load/components/ctl-placeholder-blocked.js'
56

67
/**
78
* @typedef {'darkMode' | 'lightMode' | 'loginMode' | 'cancelMode'} displayMode
@@ -47,6 +48,10 @@ const readyToDisplayPlaceholders = new Promise(resolve => {
4748
let afterPageLoadResolver
4849
const afterPageLoad = new Promise(resolve => { afterPageLoadResolver = resolve })
4950

51+
// Used to choose between extension/desktop flow or mobile apps flow.
52+
// Updated on ClickToLoad.init()
53+
let isMobileApp
54+
5055
/*********************************************************
5156
* Widget Replacement logic
5257
*********************************************************/
@@ -544,17 +549,42 @@ function createPlaceholderElementAndReplace (widget, trackingElement) {
544549

545550
// Facebook
546551
if (widget.replaceSettings.type === 'dialog') {
547-
const icon = widget.replaceSettings.icon
548-
const button = makeButton(widget.replaceSettings.buttonText, widget.getMode())
549-
const textButton = makeTextButton(widget.replaceSettings.buttonText, widget.getMode())
550-
const { contentBlock, shadowRoot } = createContentBlock(
551-
widget, button, textButton, icon
552-
)
553-
button.addEventListener('click', widget.clickFunction(trackingElement, contentBlock))
554-
textButton.addEventListener('click', widget.clickFunction(trackingElement, contentBlock))
552+
if (isMobileApp) {
553+
/**
554+
* Creates a custom HTML element with the placeholder element for blocked
555+
* embedded content. The constructor gets a list of parameters with the
556+
* content and event handlers for this HTML element.
557+
*/
558+
const mobileBlockedPlaceholder = new DDGCtlPlaceholderBlockedElement({
559+
devMode,
560+
title: widget.replaceSettings.infoTitle, // Card title text
561+
body: widget.replaceSettings.infoText, // Card body text
562+
unblockBtnText: widget.replaceSettings.buttonText, // Unblock button text
563+
useSlimCard: false, // Flag for using less padding on card (ie YT CTL on mobile)
564+
originalElement: trackingElement, // The original element this placeholder is replacing.
565+
learnMore: { // Localized strings for "Learn More" link.
566+
readAbout: sharedStrings.readAbout,
567+
learnMore: sharedStrings.learnMore
568+
},
569+
onButtonClick: widget.clickFunction.bind(widget)
570+
})
571+
mobileBlockedPlaceholder.appendChild(makeFontFaceStyleElement())
572+
573+
replaceTrackingElement(widget, trackingElement, mobileBlockedPlaceholder)
574+
showExtraUnblockIfShortPlaceholder(null, mobileBlockedPlaceholder)
575+
} else {
576+
const icon = widget.replaceSettings.icon
577+
const button = makeButton(widget.replaceSettings.buttonText, widget.getMode())
578+
const textButton = makeTextButton(widget.replaceSettings.buttonText, widget.getMode())
579+
const { contentBlock, shadowRoot } = createContentBlock(
580+
widget, button, textButton, icon
581+
)
582+
button.addEventListener('click', widget.clickFunction(trackingElement, contentBlock))
583+
textButton.addEventListener('click', widget.clickFunction(trackingElement, contentBlock))
555584

556-
replaceTrackingElement(widget, trackingElement, contentBlock)
557-
showExtraUnblockIfShortPlaceholder(shadowRoot, contentBlock)
585+
replaceTrackingElement(widget, trackingElement, contentBlock)
586+
showExtraUnblockIfShortPlaceholder(shadowRoot, contentBlock)
587+
}
558588
}
559589

560590
// YouTube
@@ -597,11 +627,49 @@ function replaceYouTubeCTL (trackingElement, widget) {
597627
// Block YouTube embedded video and display blocking dialog
598628
widget.autoplay = false
599629
const oldPlaceholder = widget.placeholderElement
600-
const { blockingDialog, shadowRoot } = createYouTubeBlockingDialog(trackingElement, widget)
601-
resizeElementToMatch(oldPlaceholder || trackingElement, blockingDialog)
602-
replaceTrackingElement(widget, trackingElement, blockingDialog)
603-
showExtraUnblockIfShortPlaceholder(shadowRoot, blockingDialog)
604-
hideInfoTextIfNarrowPlaceholder(shadowRoot, blockingDialog, 460)
630+
631+
if (isMobileApp) {
632+
/**
633+
* Creates a custom HTML element with the placeholder element for blocked
634+
* embedded content. The constructor gets a list of parameters with the
635+
* content and event handlers for this HTML element.
636+
*/
637+
const mobileBlockedPlaceholderElement = new DDGCtlPlaceholderBlockedElement({
638+
devMode,
639+
title: widget.replaceSettings.infoTitle, // Card title text
640+
body: widget.replaceSettings.infoText, // Card body text
641+
unblockBtnText: widget.replaceSettings.buttonText, // Unblock button text
642+
useSlimCard: true, // Flag for using less padding on card (ie YT CTL on mobile)
643+
originalElement: trackingElement, // The original element this placeholder is replacing.
644+
learnMore: { // Localized strings for "Learn More" link.
645+
readAbout: sharedStrings.readAbout,
646+
learnMore: sharedStrings.learnMore
647+
},
648+
withToggle: { // Toggle config to be displayed in the bottom of the placeholder
649+
isActive: false, // Toggle state
650+
dataKey: 'yt-preview-toggle', // data-key attribute for button
651+
label: widget.replaceSettings.previewToggleText, // Text to be presented with toggle
652+
size: isMobileApp ? 'lg' : 'md',
653+
onClick: () => sendMessage('setYoutubePreviewsEnabled', true) // Toggle click callback
654+
},
655+
withFeedback: {
656+
label: sharedStrings.shareFeedback,
657+
onClick: () => openShareFeedbackPage()
658+
},
659+
onButtonClick: widget.clickFunction.bind(widget)
660+
})
661+
mobileBlockedPlaceholderElement.appendChild(makeFontFaceStyleElement())
662+
mobileBlockedPlaceholderElement.id = trackingElement.id
663+
resizeElementToMatch(oldPlaceholder || trackingElement, mobileBlockedPlaceholderElement)
664+
replaceTrackingElement(widget, trackingElement, mobileBlockedPlaceholderElement)
665+
showExtraUnblockIfShortPlaceholder(null, mobileBlockedPlaceholderElement)
666+
} else {
667+
const { blockingDialog, shadowRoot } = createYouTubeBlockingDialog(trackingElement, widget)
668+
resizeElementToMatch(oldPlaceholder || trackingElement, blockingDialog)
669+
replaceTrackingElement(widget, trackingElement, blockingDialog)
670+
showExtraUnblockIfShortPlaceholder(shadowRoot, blockingDialog)
671+
hideInfoTextIfNarrowPlaceholder(shadowRoot, blockingDialog, 460)
672+
}
605673
}
606674
}
607675

@@ -610,7 +678,7 @@ function replaceYouTubeCTL (trackingElement, widget) {
610678
* its parent is too short for the normal unblock button to be visible.
611679
* Note: This does not take into account the placeholder's vertical
612680
* position in the parent element.
613-
* @param {ShadowRoot} shadowRoot
681+
* @param {ShadowRoot?} shadowRoot
614682
* @param {HTMLElement} placeholder Placeholder for tracking element
615683
*/
616684
function showExtraUnblockIfShortPlaceholder (shadowRoot, placeholder) {
@@ -628,22 +696,25 @@ function showExtraUnblockIfShortPlaceholder (shadowRoot, placeholder) {
628696
const { height: placeholderHeight } = placeholder.getBoundingClientRect()
629697
const { height: parentHeight } = placeholder.parentElement.getBoundingClientRect()
630698

631-
if ((placeholderHeight > 0 && placeholderHeight <= 200) ||
632-
(parentHeight > 0 && parentHeight <= 230)) {
633-
/** @type {HTMLElement?} */
634-
const titleRowTextButton = shadowRoot.querySelector(`#${titleID + 'TextButton'}`)
635-
if (titleRowTextButton) {
636-
titleRowTextButton.style.display = 'block'
699+
if (
700+
(placeholderHeight > 0 && placeholderHeight <= 200) ||
701+
(parentHeight > 0 && parentHeight <= 230)
702+
) {
703+
if (shadowRoot) {
704+
/** @type {HTMLElement?} */
705+
const titleRowTextButton = shadowRoot.querySelector(`#${titleID + 'TextButton'}`)
706+
if (titleRowTextButton) {
707+
titleRowTextButton.style.display = 'block'
708+
}
637709
}
638-
639710
// Avoid the placeholder being taller than the containing element
640711
// and overflowing.
641712
/** @type {HTMLElement?} */
642-
const innerDiv = shadowRoot.querySelector('.DuckDuckGoSocialContainer')
643-
if (innerDiv) {
644-
innerDiv.style.minHeight = 'initial'
645-
innerDiv.style.maxHeight = parentHeight + 'px'
646-
innerDiv.style.overflow = 'hidden'
713+
const blockedDiv = shadowRoot?.querySelector('.DuckDuckGoSocialContainer') || placeholder
714+
if (blockedDiv) {
715+
blockedDiv.style.minHeight = 'initial'
716+
blockedDiv.style.maxHeight = parentHeight + 'px'
717+
blockedDiv.style.overflow = 'hidden'
647718
}
648719
}
649720
}
@@ -1652,6 +1723,7 @@ export default class ClickToLoad extends ContentFeature {
16521723
sharedStrings = localizedConfig.sharedStrings
16531724
// update styles if asset config was sent
16541725
styles = getStyles(this.assetConfig)
1726+
isMobileApp = this.platform.name === 'ios' || this.platform.name === 'android'
16551727

16561728
for (const entity of Object.keys(config)) {
16571729
// Strip config entities that are first-party, or aren't enabled in the

0 commit comments

Comments
 (0)