98 lines
2.8 KiB
TypeScript
98 lines
2.8 KiB
TypeScript
import { useEffect, useState } from 'react'
|
|
import { StyleSheet, View } from 'react-native'
|
|
import {
|
|
BannerAd,
|
|
BannerAdSize,
|
|
} from 'react-native-google-mobile-ads'
|
|
import { useMobileAds } from '../lib/mobile-ads-context'
|
|
import { useMobilePreferences } from '../lib/preferences-context'
|
|
import { ThemeText } from '../theme/themed-components'
|
|
|
|
const INITIAL_RETRY_DELAY_MS = 30_000
|
|
const MAX_RETRY_DELAY_MS = 5 * 60_000
|
|
|
|
export default function FreeTierBanner(): React.ReactElement | null {
|
|
const ads = useMobileAds()
|
|
const { palette } = useMobilePreferences()
|
|
const [loaded, setLoaded] = useState(false)
|
|
const [failed, setFailed] = useState(false)
|
|
const [retryAttempt, setRetryAttempt] = useState(0)
|
|
const [retryKey, setRetryKey] = useState(0)
|
|
|
|
useEffect(() => {
|
|
setLoaded(false)
|
|
setFailed(false)
|
|
setRetryAttempt(0)
|
|
setRetryKey(0)
|
|
}, [ads.bannerUnitId])
|
|
|
|
useEffect(() => {
|
|
if (!failed || !ads.bannerUnitId) return undefined
|
|
const delay = Math.min(
|
|
INITIAL_RETRY_DELAY_MS * (2 ** retryAttempt),
|
|
MAX_RETRY_DELAY_MS,
|
|
)
|
|
const timer = setTimeout(() => {
|
|
setLoaded(false)
|
|
setFailed(false)
|
|
setRetryAttempt((current) => current + 1)
|
|
setRetryKey((current) => current + 1)
|
|
}, delay)
|
|
return () => clearTimeout(timer)
|
|
}, [ads.bannerUnitId, failed, retryAttempt])
|
|
|
|
if (!ads.eligible) return null
|
|
|
|
return (
|
|
<View
|
|
style={[
|
|
styles.slot,
|
|
{
|
|
backgroundColor: palette.bg.inset,
|
|
borderTopColor: palette.border.subtle,
|
|
},
|
|
]}
|
|
testID="free-tier-ad-slot"
|
|
accessibilityLabel="Sponsored"
|
|
>
|
|
{ads.bannerUnitId && !failed ? (
|
|
<BannerAd
|
|
key={`${ads.bannerUnitId}:${retryKey}`}
|
|
unitId={ads.bannerUnitId}
|
|
size={BannerAdSize.ANCHORED_ADAPTIVE_BANNER}
|
|
requestOptions={{ requestNonPersonalizedAdsOnly: true }}
|
|
onAdLoaded={() => {
|
|
setLoaded(true)
|
|
setFailed(false)
|
|
setRetryAttempt(0)
|
|
}}
|
|
onAdFailedToLoad={() => {
|
|
setLoaded(false)
|
|
setFailed(true)
|
|
}}
|
|
/>
|
|
) : (
|
|
<ThemeText variant="label" color="muted" style={styles.statusText}>
|
|
{failed ? 'AD UNAVAILABLE' : ads.status === 'ready' ? 'AD LOADING' : 'AD CONSENT'}
|
|
</ThemeText>
|
|
)}
|
|
{ads.bannerUnitId && !loaded && !failed && (
|
|
<ThemeText variant="label" color="muted" style={styles.loadingOverlay}>
|
|
AD LOADING
|
|
</ThemeText>
|
|
)}
|
|
</View>
|
|
)
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
slot: {
|
|
minHeight: 50,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
borderTopWidth: 1,
|
|
overflow: 'hidden',
|
|
},
|
|
statusText: { fontSize: 9, letterSpacing: 1.2 },
|
|
loadingOverlay: { position: 'absolute', fontSize: 9, letterSpacing: 1.2 },
|
|
})
|