All files / apps/host/src/components/RemoteSafeArea index.tsx

100% Statements 9/9
100% Branches 7/7
100% Functions 2/2
100% Lines 9/9

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56              7x   7x                                     7x 28x   28x 26x                 28x 2x             26x            
import React, { ReactNode, useMemo } from 'react';
import { type Edge, SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
import { StyleProp, View, ViewStyle } from 'react-native';
 
import { isIOS } from '@repo/utils/platform';
 
// iOS: bottom sheets inside the remote should sit flush with the screen edge.
const IOS_EDGES: Edge[] = ['top', 'right', 'left'];
// Remotes with an edge-to-edge header pad the top inset themselves.
const IOS_EDGES_WITHOUT_TOP: Edge[] = ['right', 'left'];
 
type RemoteSafeAreaProps = {
  style?: StyleProp<ViewStyle>;
  children: ReactNode;
  /** Set false when the remote draws its own header behind the status bar. */
  hasTopInset?: boolean;
};
 
/**
 * Safe-area container for host screens that embed a federated remote.
 *
 * On Android the native `SafeAreaView` applies its insets a frame after mount
 * under Fabric, so an already-cached remote renders unpadded for the first
 * frame and visibly jumps once the insets land. The inset values themselves
 * are available synchronously from the provider (mounted at app boot), so
 * Android applies them as plain padding instead — the first frame is final.
 * iOS applies SafeAreaView insets synchronously and keeps the original path.
 */
export const RemoteSafeArea = ({ style, children, hasTopInset = true }: RemoteSafeAreaProps) => {
  const insets = useSafeAreaInsets();
 
  const androidPadding = useMemo<ViewStyle>(
    () => ({
      paddingTop: hasTopInset ? insets.top : 0,
      paddingRight: insets.right,
      paddingBottom: insets.bottom,
      paddingLeft: insets.left,
    }),
    [hasTopInset, insets.top, insets.right, insets.bottom, insets.left],
  );
 
  if (isIOS()) {
    return (
      <SafeAreaView style={style} edges={hasTopInset ? IOS_EDGES : IOS_EDGES_WITHOUT_TOP}>
        {children}
      </SafeAreaView>
    );
  }
 
  return (
    <View testID="remote-safe-area" style={[style, androidPadding]}>
      {children}
    </View>
  );
};