@applicaster/zapp-react-native-utils
Version:
Applicaster Zapp React Native utilities package
198 lines (169 loc) • 5.61 kB
text/typescript
/// <reference types="@types/react-native" />
import * as ReactNative from "react-native";
import { isTV, platformSelect } from "../reactUtils";
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
import {
QUICK_BRICK_EVENTS,
QuickBrickEvent,
sendQuickBrickEvent,
} from "@applicaster/zapp-react-native-bridge/QuickBrick";
import { utilsLogger } from "../logger";
const { DeviceEventEmitter, NativeModules, NativeEventEmitter } = ReactNative;
// simply evaluating new NativeEventEmitter(NativeModules.ReactNativeEventEmitter)
// will cause the native code to crash on tvOS, even if it's not the selected platform
const EventEmitter = platformSelect({
android: DeviceEventEmitter,
ios: isTV()
? DeviceEventEmitter
: new NativeEventEmitter(NativeModules.ReactNativeEventEmitter),
default: DeviceEventEmitter,
});
const logger = utilsLogger.addSubsystem("orientationHelper");
/**
* @enum {Orientations}
*/
export enum ORIENTATIONS {
unknown = 0,
portrait = 1,
landscapeRight = 2,
landscapeLeft = 4,
landscapeSensor = 6,
allButUpsideDown = 7,
portraitUpsideDown = 8,
all = 15,
}
export const ORIENTATIONS_BUILDER = {
toString: (orientation: ORIENTATIONS): string => {
return ORIENTATIONS[orientation];
},
fromString: (orientation: string): ORIENTATIONS => {
return (ORIENTATIONS as any)[orientation];
},
};
export function getOrientation(orientation: OrientationTypes): ORIENTATIONS {
return ORIENTATIONS[orientation];
}
type OrientationListener = (OrientationListenerArgs?) => void;
/**
* This events sets the allowed screen orientations for current screen
* example for portrait and both landscape: allowedOrientationsForScreen(ORIENTATIONS.allButUpsideDown)
* (use enum provided in this module)
* Example of forcing landscape right: allowedOrientationsForScreen(ORIENTATIONS.landscapeRight)
* (use enum provided in this module)
* @param {OrientationTypes} orientation - Allowed orientations
*/
export const allowedOrientationsForScreen = (
orientation: ORIENTATIONS
): void => {
logger.debug({
message: `allowedOrientationsForScreen: new orientation: ${ORIENTATIONS[orientation]}`,
});
sendQuickBrickEvent(
QUICK_BRICK_EVENTS.ALLOWED_ORIENTATIONS_FOR_SCREEN as QuickBrickEvent,
{
orientation,
}
);
};
/**
*
* @callback orientationChangeCallback
* @param {Object}: response
* @param {Orientations}: response.toOrientation - Current orientation
* @param {Orientations}: response.fromOrientation - Previous orientation
*/
/**
* Sets and return orientationChange listener
* @param {orientationChangeCallback} callback - called on orientation change with the new and old orientation
* @return {*} - orientationChange Listener instance
*/
export const addOrientationChangeListener = (
callback: OrientationListener
): any => {
try {
if (isTV()) () => {};
return EventEmitter.addListener("orientationChange", callback);
} catch (err) {
logger.error(err);
}
};
/**
* Removes listener by calling remove method on the provided orientationChange listener instance
* @param {*} listener - orientationChange listener instance
*/
export const removeOrientationChangeListener = (listener: any): void => {
try {
listener.remove();
} catch (err) {
logger.error(err);
}
};
/**
* Returns true if current screen aspect is compatible with required screen orientation.
* Uses provided device screen dimensions since RN hooks orientation state takes time to update.
* TODO: use `current` of orientationHandler.ts
*/
export const isOrientationCompatible = ({
orientation,
layoutData: { width, height },
}) => {
const isLandscape = width > height;
if (
[ORIENTATIONS.portrait, ORIENTATIONS.portraitUpsideDown].includes(
orientation
)
) {
return !isLandscape;
}
if (
[
ORIENTATIONS.landscapeLeft,
ORIENTATIONS.landscapeRight,
ORIENTATIONS.landscapeSensor,
].includes(orientation)
) {
return isLandscape;
}
return true;
};
export const useIsOrientationCompatible = (orientation) => {
const { height, width } = ReactNative.useWindowDimensions();
return isOrientationCompatible({
orientation,
layoutData: { width, height },
});
};
/**
* Determine final screen orientation based on device type,
* settings and orientation value provided by screen data settings
*/
export const getScreenOrientation = ({
screenData,
layoutData: { isTablet, isTabletPortrait },
}) => {
const isLandscapeTablet = isTablet && !isTabletPortrait;
if (isLandscapeTablet) {
return ORIENTATIONS.landscapeSensor;
}
/**
* HACK: Warning
* screenData?.targetScreen?.general?.screen_orientation for player plugins is hardcoded inside NavigationProvider's
* method called legacyScreenData. Check it for more details about the hack.
*/
const orientation =
screenData?.targetScreen?.general?.screen_orientation ||
screenData?.general?.screen_orientation;
const screenOrientation = getOrientation(orientation);
// TODO: Maybe return null if screen does not specify desired orientation (i.e. works on both)?
return screenOrientation || ORIENTATIONS.portrait;
};
export const useGetScreenOrientation = (screenData) => {
const isTablet = useIsTablet();
const { appData } = usePickFromState(["appData"]);
const isTabletPortrait = appData?.isTabletPortrait;
return getScreenOrientation({
screenData,
layoutData: { isTablet, isTabletPortrait },
});
};