From f157848009f41b07e9ca41205d800e473e29917f Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Fri, 8 Mar 2024 14:20:19 +0200 Subject: [PATCH] TW-1375: Fix some bugs of Hypelab ads (#1055) --- src/components/hypelab-promotion/index.tsx | 275 +++++++++--------- src/components/hypelab-promotion/styles.ts | 7 +- src/components/image-promotion-view/index.tsx | 2 +- 3 files changed, 145 insertions(+), 139 deletions(-) diff --git a/src/components/hypelab-promotion/index.tsx b/src/components/hypelab-promotion/index.tsx index 0b22d5981..2335c9a16 100644 --- a/src/components/hypelab-promotion/index.tsx +++ b/src/components/hypelab-promotion/index.tsx @@ -1,4 +1,4 @@ -import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; +import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { LayoutChangeEvent, LayoutRectangle, View } from 'react-native'; import { WebView, WebViewMessageEvent } from 'react-native-webview'; @@ -27,146 +27,149 @@ import { useHypelabPromotionStyles } from './styles'; const AD_CONTENT_RELATED_URL_SEARCH_PARAMS = ['campaign_slug', 'creative_set_slug', 'placement_slug']; -export const HypelabPromotion: FC = ({ - variant, - isVisible, - shouldShowCloseButton, - onClose, - onReady, - onError, - ...testIDProps -}) => { - const { testID, testIDProperties } = testIDProps; - const isImageAd = variant === PromotionVariantEnum.Image; - const colors = useColors(); - const styles = useHypelabPromotionStyles(); - const theme = useThemeSelector(); - const { trackEvent } = useAnalytics(); - const [adHref, setAdHref] = useState(); - - const [layoutRect, setLayoutRect] = useState(); - const initialSize = useMemo(() => { - if (isImageAd) { - return { w: 320, h: 50 }; - } - - return layoutRect ? { w: Math.round(layoutRect.width / layoutScale), h: 80 } : undefined; - }, [isImageAd, layoutRect]); - const [size, setSize] = useState(initialSize); - useEffect(() => void (initialSize && setSize(prevSize => prevSize ?? initialSize)), [initialSize]); +export const HypelabPromotion = memo( + ({ variant, isVisible, shouldShowCloseButton, onClose, onReady, onError, ...testIDProps }) => { + const { testID, testIDProperties } = testIDProps; + const isImageAd = variant === PromotionVariantEnum.Image; + const colors = useColors(); + const styles = useHypelabPromotionStyles(); + const theme = useThemeSelector(); + const { trackEvent } = useAnalytics(); + const [adHref, setAdHref] = useState(); + const adHrefRef = useRef(adHref); + + useEffect(() => void (adHrefRef.current = adHref), [adHref]); + + const [layoutRect, setLayoutRect] = useState(); + const initialSize = useMemo(() => { + if (isImageAd) { + return { w: 320, h: 50 }; + } - const adFrameSource = useMemo(() => { - const placementSlug = isImageAd ? HYPELAB_SMALL_PLACEMENT_SLUG : HYPELAB_NATIVE_PLACEMENT_SLUG; - const origin = theme === ThemesEnum.dark ? 'mobile-dark' : 'mobile-light'; + return layoutRect ? { w: Math.round(layoutRect.width / layoutScale), h: 80 } : undefined; + }, [isImageAd, layoutRect]); + const [size, setSize] = useState(initialSize); + useEffect(() => void (initialSize && setSize(prevSize => prevSize ?? initialSize)), [initialSize]); - if (!initialSize) { - return undefined; - } + const adFrameSource = useMemo(() => { + const placementSlug = isImageAd ? HYPELAB_SMALL_PLACEMENT_SLUG : HYPELAB_NATIVE_PLACEMENT_SLUG; + const origin = theme === ThemesEnum.dark ? 'mobile-dark' : 'mobile-light'; - const searchParams = new URLSearchParams({ - p: placementSlug, - o: origin, - vw: formatSize(Number(initialSize.w)).toString(), - w: Number(initialSize.w).toString(), - h: Number(initialSize.h).toString() - }); - - return { uri: `${HYPELAB_AD_FRAME_URL}/?${searchParams.toString()}` }; - }, [isImageAd, initialSize, theme]); - - const handleMainLayout = useCallback((e: LayoutChangeEvent) => { - e.persist(); - setLayoutRect(e.nativeEvent.layout); - }, []); - - useTimeout( - () => { - if (!isString(adHref)) { - onError(); + if (!initialSize) { + return undefined; } - }, - 30000, - [adHref, onError] - ); - - const handleAdFrameMessage = useCallback( - (e: WebViewMessageEvent) => { - try { - const message: AdFrameMessage = JSON.parse(e.nativeEvent.data); - - switch (message.type) { - case AdFrameMessageType.Resize: - setSize({ w: Math.round(message.width / layoutScale), h: Math.round(message.height / layoutScale) }); - break; - case AdFrameMessageType.Ready: - const prevAdHrefSearchParams = isDefined(adHref) ? new URL(adHref).searchParams : new URLSearchParams(); - const newAdHrefSearchParams = new URL(message.ad.cta_url).searchParams; - setAdHref(message.ad.cta_url); - if ( - AD_CONTENT_RELATED_URL_SEARCH_PARAMS.some( - paramName => prevAdHrefSearchParams.get(paramName) !== newAdHrefSearchParams.get(paramName) - ) - ) { - onReady(); - } - break; - case AdFrameMessageType.Error: - onError(); - break; - case AdFrameMessageType.Click: - if (isString(adHref)) { - trackEvent(testID, AnalyticsEventCategory.ButtonPress, testIDProperties); - openUrl(adHref); - } + + const searchParams = new URLSearchParams({ + p: placementSlug, + o: origin, + vw: formatSize(Number(initialSize.w)).toString(), + w: Number(initialSize.w).toString(), + h: Number(initialSize.h).toString() + }); + + return { uri: `${HYPELAB_AD_FRAME_URL}/?${searchParams.toString()}` }; + }, [isImageAd, initialSize, theme]); + + const handleMainLayout = useCallback((e: LayoutChangeEvent) => { + e.persist(); + setLayoutRect(e.nativeEvent.layout); + }, []); + + useTimeout( + () => { + if (!isString(adHrefRef.current)) { + onError(); } - } catch (err) { - console.error(err); - } - }, - [adHref, onError, onReady, testID, testIDProperties, trackEvent] - ); - - const webViewCommonProps = { - source: adFrameSource, - onError: onError, - onMessage: handleAdFrameMessage, - webviewDebuggingEnabled: __DEV__, - scrollEnabled: false, - scalesPageToFit: false, - textZoom: 100 - }; - - if (isImageAd) { - return ( - - - - - + }, + 30000, + [onError] ); - } - return ( - - {adFrameSource && size && ( - - )} - {shouldShowCloseButton && ( - { + try { + const message: AdFrameMessage = JSON.parse(e.nativeEvent.data); + + switch (message.type) { + case AdFrameMessageType.Resize: + if (message.height !== 0 && message.width !== 0) { + setSize({ w: Math.round(message.width / layoutScale), h: Math.round(message.height / layoutScale) }); + } + break; + case AdFrameMessageType.Ready: + const prevAdHrefSearchParams = isDefined(adHref) ? new URL(adHref).searchParams : new URLSearchParams(); + const newAdHrefSearchParams = new URL(message.ad.cta_url).searchParams; + setAdHref(message.ad.cta_url); + if ( + AD_CONTENT_RELATED_URL_SEARCH_PARAMS.some( + paramName => prevAdHrefSearchParams.get(paramName) !== newAdHrefSearchParams.get(paramName) + ) + ) { + onReady(); + } + break; + case AdFrameMessageType.Error: + onError(); + break; + case AdFrameMessageType.Click: + if (isString(adHref)) { + trackEvent(testID, AnalyticsEventCategory.ButtonPress, testIDProperties); + openUrl(adHref); + } + } + } catch (err) { + console.error(err); + } + }, + [adHref, onError, onReady, testID, testIDProperties, trackEvent] + ); + + const webViewCommonProps = { + source: adFrameSource, + onError: onError, + onMessage: handleAdFrameMessage, + webviewDebuggingEnabled: __DEV__, + scrollEnabled: false, + scalesPageToFit: false, + textZoom: 100 + }; + + if (isImageAd) { + return ( + - - - )} - - ); -}; + + + + + ); + } + + return ( + + {adFrameSource && size && ( + + )} + {shouldShowCloseButton && ( + + + + )} + + ); + } +); diff --git a/src/components/hypelab-promotion/styles.ts b/src/components/hypelab-promotion/styles.ts index f43c06be9..4478b6a32 100644 --- a/src/components/hypelab-promotion/styles.ts +++ b/src/components/hypelab-promotion/styles.ts @@ -5,8 +5,7 @@ export const useHypelabPromotionStyles = createUseStylesMemoized(() => ({ imageAdFrameWrapper: { width: formatSize(320), height: formatSize(50), - borderRadius: formatSize(8), - overflow: 'hidden' + borderRadius: formatSize(8) }, imageAdFrame: { width: '100%', @@ -28,5 +27,9 @@ export const useHypelabPromotionStyles = createUseStylesMemoized(() => ({ }, invisible: { opacity: 0 + }, + // https://stackoverflow.com/questions/54131875/screen-blinks-once-when-rendering-a-webview-on-android + webView: { + opacity: 0.99 } })); diff --git a/src/components/image-promotion-view/index.tsx b/src/components/image-promotion-view/index.tsx index 5311f9867..2062575cd 100644 --- a/src/components/image-promotion-view/index.tsx +++ b/src/components/image-promotion-view/index.tsx @@ -26,7 +26,7 @@ export const ImagePromotionView = memo openUrl(href), [href]); + const openLink = useCallback(() => href && openUrl(href), [href]); return (