Skip to content

Commit

Permalink
Release 1.24 patch (#1056)
Browse files Browse the repository at this point in the history
Merge pull request #1056 from madfish-solutions/development
  • Loading branch information
alex-tsx authored Mar 8, 2024
2 parents 4f5a444 + f157848 commit e905b0e
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 139 deletions.
275 changes: 139 additions & 136 deletions src/components/hypelab-promotion/index.tsx
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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<SingleProviderPromotionProps> = ({
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<string>();

const [layoutRect, setLayoutRect] = useState<LayoutRectangle | undefined>();
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<SingleProviderPromotionProps>(
({ 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<string>();
const adHrefRef = useRef(adHref);

useEffect(() => void (adHrefRef.current = adHref), [adHref]);

const [layoutRect, setLayoutRect] = useState<LayoutRectangle | undefined>();
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 (
<ImagePromotionView
onClose={onClose}
shouldShowCloseButton={shouldShowCloseButton}
href={adHref ?? ''}
isVisible={isVisible}
shouldShowAdBage
{...testIDProps}
>
<View style={styles.imageAdFrameWrapper}>
<WebView {...webViewCommonProps} containerStyle={styles.imageAdFrame} />
</View>
</ImagePromotionView>
},
30000,
[onError]
);
}

return (
<View style={[styles.textAdFrameContainer, !isVisible && styles.invisible]} onLayout={handleMainLayout}>
{adFrameSource && size && (
<WebView {...webViewCommonProps} containerStyle={[styles.textAdFrame, { aspectRatio: size.w / size.h }]} />
)}
{shouldShowCloseButton && (
<TouchableWithAnalytics
style={styles.closeButton}
onPress={onClose}
testID={TextPromotionItemSelectors.closeButton}
const handleAdFrameMessage = useCallback(
(e: WebViewMessageEvent) => {
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 (
<ImagePromotionView
onClose={onClose}
shouldShowCloseButton={shouldShowCloseButton}
href={adHref ?? ''}
isVisible={isVisible}
shouldShowAdBage
{...testIDProps}
>
<Icon name={IconNameEnum.X} size={formatSize(16)} color={colors.peach} />
</TouchableWithAnalytics>
)}
</View>
);
};
<View style={styles.imageAdFrameWrapper}>
<WebView {...webViewCommonProps} containerStyle={styles.imageAdFrame} style={styles.webView} />
</View>
</ImagePromotionView>
);
}

return (
<View style={[styles.textAdFrameContainer, !isVisible && styles.invisible]} onLayout={handleMainLayout}>
{adFrameSource && size && (
<WebView
{...webViewCommonProps}
containerStyle={[styles.textAdFrame, { aspectRatio: size.w / size.h }]}
style={styles.webView}
/>
)}
{shouldShowCloseButton && (
<TouchableWithAnalytics
style={styles.closeButton}
onPress={onClose}
testID={TextPromotionItemSelectors.closeButton}
>
<Icon name={IconNameEnum.X} size={formatSize(16)} color={colors.peach} />
</TouchableWithAnalytics>
)}
</View>
);
}
);
7 changes: 5 additions & 2 deletions src/components/hypelab-promotion/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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%',
Expand All @@ -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
}
}));
2 changes: 1 addition & 1 deletion src/components/image-promotion-view/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const ImagePromotionView = memo<PropsWithChildren<ImagePromotionViewProps
const colors = useColors();
const styles = useImagePromotionViewStyles();

const openLink = useCallback(() => openUrl(href), [href]);
const openLink = useCallback(() => href && openUrl(href), [href]);

return (
<TouchableWithAnalytics
Expand Down

0 comments on commit e905b0e

Please sign in to comment.