UNPKG

@applicaster/zapp-react-native-utils

Version:

Applicaster Zapp React Native utilities package

116 lines (94 loc) 3.71 kB
import { useLayoutEffect, useMemo, useRef, useState } from "react"; import * as R from "ramda"; import { Dimensions, Platform, StatusBar } from "react-native"; import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks"; import { isTV } from "../../../reactUtils"; import { Options, UseDimensions } from "../types"; import { getDeviceInfo } from "../getDeviceInfo"; import { getInitialDimensions } from "./helpers"; import { useIsScreenActive } from "../../navigation"; function compensateForScaleIfNeeded(context) { return function () { return isTV() && Platform.OS !== "ios" && context === "screen"; }; } const applyScaleToDimensions = R.unless(R.propEq("scale", 1), (dimensions) => ({ width: dimensions.width * dimensions.scale, height: dimensions.height * dimensions.scale, scale: 1, })); const statusBarHeight = StatusBar?.currentHeight; /** * Returns React-native Dimensions object and updates it on any dimension change * @param {('screen'|'window')} [context=window] - Dimensions context passed to Dimensions.get method * @param {boolean} [fullDimensions=false] - Should return full versions of Dimensions objet * @returns {RNDimensions} - Returns Dimensions object * */ export const useDimensions: UseDimensions = ( context = "window", fullDimensions = { fullDimensions: false, updateForInactiveScreens: true } ) => { const isActive = useIsScreenActive(); const { appData } = usePickFromState(["appData"]); if (typeof fullDimensions === "boolean") { // eslint-disable-next-line no-console console.warn( "Deprecation Warning\nSecond argument is now the options object. {fullDimensions: boolean, ...}" ); } const options: Options = typeof fullDimensions === "boolean" ? { fullDimensions } : { ...fullDimensions }; const [dimensions, setDimensions] = useState(getInitialDimensions(context)); const deviceInfo = useRef(getDeviceInfo(dimensions, appData)); const applyOptions = R.compose( R.when(() => options.deviceInfo, R.assoc("deviceInfo", deviceInfo.current)), R.when( () => options.fullDimensions, (dims) => { const { scale, fontScale } = Dimensions.get(context); return { ...dims, scale, fontScale }; } ), R.when(() => options.rounded, R.map(Math.ceil)), R.when( () => options.excludeStatusBar, R.over(R.lensProp("height"), R.subtract(R.__, statusBarHeight)) ), R.when(compensateForScaleIfNeeded(context), applyScaleToDimensions) ); useLayoutEffect(() => { const handler = (dims) => { const newDimensions = dims?.[context]; if (newDimensions.width === newDimensions.height) { /** * Ignoring case occurred on iOS when Dimensions.get sometimes returning * wrong `window` sizes * when switch background -> foreground state. * ( TODO fix when 1:1 aspect ratio will be used ) */ // eslint-disable-next-line no-console console.log("change: wrong values - width equal to height, ignoring"); } else if (isActive || options.updateForInactiveScreens) { deviceInfo.current = getDeviceInfo(newDimensions, appData); setDimensions(newDimensions); } }; const activeSubscription = Dimensions.addEventListener("change", handler); handler({ [context]: Dimensions.get(context) }); return () => { activeSubscription.remove(); }; }, [isActive]); const memoizedDimensions = useMemo(() => { return { ...applyOptions(dimensions), statusBarHeight }; }, [ dimensions.width, dimensions.height, dimensions.fontScale, dimensions.scale, ]); return memoizedDimensions; };