UNPKG

expo-screen-orientation

Version:

Expo universal module for managing device's screen orientation

201 lines (180 loc) 7.23 kB
import { NativeModule, Platform, registerWebModule } from 'expo-modules-core'; import { getOrientationLockAsync, getOrientationAsync } from './ScreenOrientation'; import type { ExpoOrientationEvents } from './ScreenOrientation.types'; import { Orientation, OrientationLock, WebOrientationLock, WebOrientation, } from './ScreenOrientation.types'; const OrientationLockAPIToWeb: { [lock: string]: WebOrientationLock; } = { [OrientationLock.DEFAULT]: WebOrientationLock.NATURAL, [OrientationLock.ALL]: WebOrientationLock.ANY, [OrientationLock.PORTRAIT]: WebOrientationLock.PORTRAIT, [OrientationLock.PORTRAIT_UP]: WebOrientationLock.PORTRAIT_PRIMARY, [OrientationLock.PORTRAIT_DOWN]: WebOrientationLock.PORTRAIT_SECONDARY, [OrientationLock.LANDSCAPE]: WebOrientationLock.LANDSCAPE, [OrientationLock.LANDSCAPE_LEFT]: WebOrientationLock.LANDSCAPE_PRIMARY, [OrientationLock.LANDSCAPE_RIGHT]: WebOrientationLock.LANDSCAPE_SECONDARY, }; const OrientationWebToAPI: { [orientationWeb: string]: Orientation; } = { [WebOrientation.PORTRAIT_PRIMARY]: Orientation.PORTRAIT_UP, [WebOrientation.PORTRAIT_SECONDARY]: Orientation.PORTRAIT_DOWN, [WebOrientation.LANDSCAPE_PRIMARY]: Orientation.LANDSCAPE_LEFT, [WebOrientation.LANDSCAPE_SECONDARY]: Orientation.LANDSCAPE_RIGHT, }; declare const window: Window; const screen: Screen = Platform.canUseViewport ? window.screen : ({} as Screen); function _convertToLegacyOrientationLock(orientationLock: WebOrientationLock) { switch (orientationLock) { case WebOrientationLock.UNKNOWN: throw new Error( `expo-screen-orientation: WebOrientationLock.UNKNOWN is not a valid lock to be converted.` ); case WebOrientationLock.ANY: return ['portrait', 'landscape']; case WebOrientationLock.NATURAL: return 'default'; default: return orientationLock; } } declare global { interface Screen { msOrientation?: Screen['orientation']['type']; mozOrientation?: Screen['orientation']; mozUnlockOrientation?(): boolean | undefined; msUnlockOrientation?(): boolean | undefined; unlockOrientation?(): boolean | undefined; } } async function _lockAsync(webOrientationLock: WebOrientationLock): Promise<void> { if (webOrientationLock === WebOrientationLock.UNKNOWN) { throw new Error( `expo-screen-orientation: WebOrientationLock.UNKNOWN is not a valid lock that can be applied to the device.` ); } // Handle modern lock screen web API // See: https://developer.mozilla.org/en-US/docs/Web/API/ScreenOrientation/lock if ( screen.orientation && 'lock' in screen.orientation && typeof screen.orientation.lock === 'function' ) { await screen.orientation.lock(webOrientationLock); return; } // See: https://developer.mozilla.org/en-US/docs/Web/API/Screen/lockOrientation const _legacyLockUniversal: | undefined | ((orientation: ReturnType<typeof _convertToLegacyOrientationLock>) => boolean) = // @ts-expect-error - These legacy APIs are removed from the types screen.lockOrientation || screen.mozLockOrientation || screen.msLockOrientation; // Fallback to outdated legacy web API // See: https://developer.mozilla.org/en-US/docs/Web/API/Screen/lockOrientation if (typeof _legacyLockUniversal === 'function') { const legacyLock = _convertToLegacyOrientationLock(webOrientationLock); const isSuccess = _legacyLockUniversal.call(screen, legacyLock); if (!isSuccess) { throw new Error( `Applying orientation lock: ${JSON.stringify(webOrientationLock)} to device was denied` ); } return; } throw new Error( `expo-screen-orientation: The browser doesn't support locking screen orientation.` ); } let _lastWebOrientationLock: WebOrientationLock = WebOrientationLock.UNKNOWN; class ExpoScreenOrientation extends NativeModule<ExpoOrientationEvents> { orientation: ScreenOrientation | null = Platform.canUseViewport ? screen.orientation || (screen as any).msOrientation || null : null; emitOrientationEvent = async () => { const [orientationLock, orientation] = await Promise.all([ getOrientationLockAsync(), getOrientationAsync(), ]); this.emit('expoDidUpdateDimensions', { orientationLock, orientationInfo: { orientation }, }); }; startObserving() { if (Platform.canUseEventListeners) { if (this.orientation?.addEventListener) { this.orientation.addEventListener('change', this.emitOrientationEvent); } else { window.addEventListener('orientationchange', this.emitOrientationEvent); } } } stopObserving(): void { if (Platform.canUseEventListeners) { if (this.orientation?.removeEventListener) { this.orientation.removeEventListener('change', this.emitOrientationEvent); } else { window.removeEventListener('orientationchange', this.emitOrientationEvent); } } } async supportsOrientationLockAsync(orientationLock: OrientationLock): Promise<boolean> { return orientationLock in OrientationLockAPIToWeb; } async getPlatformOrientationLockAsync(): Promise<WebOrientationLock> { return _lastWebOrientationLock; } async getOrientationAsync(): Promise<Orientation> { const webOrientation = screen['msOrientation'] || (screen.orientation || screen['mozOrientation'] || {}).type; const orientation = OrientationWebToAPI[webOrientation]; if (!orientation) { return Orientation.UNKNOWN; } return orientation; } async lockAsync(orientationLock: OrientationLock): Promise<void> { const webOrientationLock = OrientationLockAPIToWeb[orientationLock]; if (!webOrientationLock) { throw new TypeError(`Invalid Orientation Lock: ${orientationLock}`); } await _lockAsync(webOrientationLock); } async lockPlatformAsync(webOrientationLock: WebOrientationLock): Promise<void> { await _lockAsync(webOrientationLock); _lastWebOrientationLock = webOrientationLock; } async unlockAsync(): Promise<void> { // Handle modern lock screen web API // See: https://developer.mozilla.org/en-US/docs/Web/API/ScreenOrientation/unlock if ( screen.orientation && 'unlock' in screen.orientation && typeof screen.orientation.unlock === 'function' ) { screen.orientation.unlock(); return; } // See: https://developer.mozilla.org/en-US/docs/Web/API/Screen/unlockOrientation const _legacyUnlockUniversal: undefined | (() => boolean | undefined) = screen.unlockOrientation || screen.mozUnlockOrientation || screen.msUnlockOrientation; // Fallback to outdated legacy web API // See: https://developer.mozilla.org/en-US/docs/Web/API/Screen/unlockOrientation if (typeof _legacyUnlockUniversal === 'function') { const isSuccess = _legacyUnlockUniversal.call(screen); if (!isSuccess) { throw new Error(`Unlocking screen orientation on device was denied`); } return; } throw new Error( `expo-screen-orientation: The browser doesn't support unlocking screen orientation.` ); } } export default registerWebModule(ExpoScreenOrientation, 'ExpoScreenOrientation');