import React, { createContext, useContext, useRef, type ReactNode, type RefObject, } from 'react'; import { BlurTargetView, BlurView } from 'expo-blur'; import { Platform, StyleSheet, View, type StyleProp, type ViewProps, type ViewStyle, } from 'react-native'; import { colors } from '../theme/proulr-theme'; import { CHROME_GLASS_ANDROID_BLUR_METHOD, CHROME_GLASS_BLUR_INTENSITY, CHROME_GLASS_BLUR_TINT, } from '../theme/chrome-glass'; import { chromePillStyles } from '../theme/chrome-pill-styles'; const absoluteFill: ViewStyle = { position: 'absolute', top: 0, right: 0, bottom: 0, left: 0, }; /** * Android: `BlurView` só faz blur real apontando para um `BlurTargetView` * (ref via `blurTarget`) — iOS/web usam o backdrop nativo e ignoram o target. */ const ChromeGlassTargetContext = createContext | null>(null); /** Provider do alvo de blur — envolve chrome + conteúdo (uma vez por shell). */ export function ChromeGlassTargetProvider({ children }: { children: ReactNode }) { const targetRef = useRef(null); return ( {children} ); } type ChromeGlassBlurTargetProps = { children: ReactNode; style?: StyleProp; }; /** * Envolve o conteúdo que fica atrás do vidro. No Android vira `BlurTargetView`; * nas demais plataformas (ou sem provider) é um `View` comum. * * Descendentes NÃO recebem o target: um BlurView aninhado dentro do próprio * alvo cria ciclo de render nodes no hwui (recursão infinita em * computeTransformImpl → SIGSEGV na RenderThread). Glass dentro do conteúdo * (ex.: compose pill do chat) cai no fallback translúcido no Android. */ export function ChromeGlassBlurTarget({ children, style }: ChromeGlassBlurTargetProps) { const targetRef = useContext(ChromeGlassTargetContext); if (Platform.OS !== 'android' || !targetRef) { return {children}; } return ( {children} ); } function useAndroidBlurProps(): { blurTarget: RefObject; blurMethod: typeof CHROME_GLASS_ANDROID_BLUR_METHOD; } | null { const targetRef = useContext(ChromeGlassTargetContext); if (Platform.OS !== 'android' || !targetRef) return null; return { blurTarget: targetRef, blurMethod: CHROME_GLASS_ANDROID_BLUR_METHOD }; } type ChromeGlassLayerProps = { style?: StyleProp; pointerEvents?: ViewProps['pointerEvents']; }; /** * Camada de frost única do sistema (igual AppSideDrawer topo/base). * BlurView + `colors.chromeGlassOverlay` — sem cast cinza. * * Web: BlurView vira `backdrop-filter` em camada fixa sobre conteúdo que rola — * repaint contínuo no scroll (custo direto de INP). Fallback: pílula OLED opaca * (`colors.chromePillBackground`), mesmo visual do chrome sem glass. */ export function ChromeGlassLayer({ style, pointerEvents = 'none', }: ChromeGlassLayerProps) { const androidBlurProps = useAndroidBlurProps(); if (Platform.OS === 'web') { return ( ); } return ( ); } type ChromeGlassShellProps = { children: ReactNode; style?: StyleProp; contentStyle?: StyleProp; }; /** * Pílula chrome com o mesmo glass do drawer (shell + blur + overlay). */ export function ChromeGlassShell({ children, style, contentStyle }: ChromeGlassShellProps) { return ( {children} ); } const styles = StyleSheet.create({ tint: { backgroundColor: colors.chromeGlassOverlay, }, webGlassFallback: { backgroundColor: colors.chromePillBackground, }, shellTransparent: { backgroundColor: 'transparent', }, content: { zIndex: 1, }, });