@applicaster/zapp-react-native-ui-components
Version:
Applicaster Zapp React Native ui components for the Quick Brick App
124 lines (101 loc) • 3.82 kB
text/typescript
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
import {
useGetScreenOrientation,
isOrientationCompatible,
} from "@applicaster/zapp-react-native-utils/appUtils/orientationHelper";
import {
useCurrentScreenData,
useRoute,
useIsTablet,
useIsScreenActive,
} from "@applicaster/zapp-react-native-utils/reactHooks";
import { useMemo, useEffect, useState } from "react";
import { useSafeAreaFrame } from "react-native-safe-area-context";
type MemoizedSafeAreaFrameWithActiveStateOptions = {
updateForInactiveScreens?: boolean;
isActive: boolean;
};
/**
* Base hook that wraps useSafeAreaFrame with memoization and inactive screen filtering.
* Requires isActive to be passed explicitly - use useMemoizedSafeAreaFrame for automatic detection.
*
* @param options.updateForInactiveScreens - If false, frame won't update when screen is inactive (default: true)
* @param options.isActive - Whether the screen is currently active
* @returns The memoized safe area frame { x, y, width, height }
*/
export const useMemoizedSafeAreaFrameWithActiveState = (
options: MemoizedSafeAreaFrameWithActiveStateOptions
) => {
const { updateForInactiveScreens = true, isActive } = options;
const frame = useSafeAreaFrame();
const [memoFrame, setMemoFrame] = useState(frame);
useEffect(() => {
const shouldUpdate = isActive || updateForInactiveScreens;
const dimensionsChanged =
frame.width !== memoFrame.width || frame.height !== memoFrame.height;
if (shouldUpdate && dimensionsChanged) {
setMemoFrame(frame);
}
}, [
frame.width,
frame.height,
isActive,
updateForInactiveScreens,
frame,
memoFrame.width,
memoFrame.height,
]);
return memoFrame;
};
type MemoizedSafeAreaFrameOptions = {
updateForInactiveScreens?: boolean;
};
/**
* Hook that wraps useSafeAreaFrame with memoization and inactive screen filtering.
* Uses useIsScreenActive() internally to determine active state - use useMemoizedSafeAreaFrameWithActiveState
* if you need to pass isActive explicitly.
*
* @param options.updateForInactiveScreens - If false, frame won't update when screen is inactive (default: true)
* @returns The memoized safe area frame { x, y, width, height }
*/
export const useMemoizedSafeAreaFrame = (
options: MemoizedSafeAreaFrameOptions = {}
) => {
const { updateForInactiveScreens = true } = options;
const isActive = useIsScreenActive();
return useMemoizedSafeAreaFrameWithActiveState({
updateForInactiveScreens,
isActive,
});
};
export const useWaitForValidOrientation = () => {
// Use memoized safe area frame to synchronize with Scene's dimension source
// This prevents race conditions where the orientation check passes before
// Scene's memoFrame has updated to the new dimensions
const { width: screenWidth, height } = useMemoizedSafeAreaFrame({
updateForInactiveScreens: false,
});
const currentScreenData = useCurrentScreenData();
const { screenData } = useRoute();
const [readyState, setReadyState] = useState(false);
const isTablet = useIsTablet();
const { appData } = usePickFromState(["appData"]);
const isTabletPortrait = appData?.isTabletPortrait;
const layoutData = useMemo(
() => ({ isTablet, isTabletPortrait, width: screenWidth, height }),
[isTablet, isTabletPortrait, screenWidth, height]
);
const targetScreenData =
currentScreenData || (screenData as any)?.targetScreen || screenData;
const orientation = useGetScreenOrientation(targetScreenData);
const isReadyForDisplay = isOrientationCompatible({
orientation,
layoutData,
});
useEffect(() => {
if (isReadyForDisplay && !readyState) {
setReadyState(true);
}
}, [readyState, orientation, layoutData]);
return readyState;
};