@applicaster/zapp-react-native-utils
Version:
Applicaster Zapp React Native utilities package
116 lines (94 loc) • 3.71 kB
text/typescript
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;
};