1010
1111'use strict' ;
1212
13- const EventEmitter = require ( '../vendor/emitter/EventEmitter' ) ;
14- const Platform = require ( './Platform' ) ;
15- const RCTDeviceEventEmitter = require ( '../EventEmitter/RCTDeviceEventEmitter' ) ;
13+ import EventEmitter from '../vendor/emitter/EventEmitter' ;
14+ import RCTDeviceEventEmitter from '../EventEmitter/RCTDeviceEventEmitter' ;
15+ import NativeDeviceInfo , {
16+ type DisplayMetrics ,
17+ type DimensionsPayload ,
18+ } from './NativeDeviceInfo' ;
19+ import invariant from 'invariant' ;
1620
17- import NativeDeviceInfo from './NativeDeviceInfo' ;
18-
19- const invariant = require ( 'invariant' ) ;
21+ type DimensionsValue = { window ?: DisplayMetrics , screen ?: DisplayMetrics } ;
2022
2123const eventEmitter = new EventEmitter ( ) ;
2224let dimensionsInitialized = false ;
23- const dimensions = { } ;
25+ let dimensions : DimensionsValue ;
26+
2427class Dimensions {
28+ /**
29+ * NOTE: `useWindowDimensions` is the preffered API for React components.
30+ *
31+ * Initial dimensions are set before `runApplication` is called so they should
32+ * be available before any other require's are run, but may be updated later.
33+ *
34+ * Note: Although dimensions are available immediately, they may change (e.g
35+ * due to device rotation) so any rendering logic or styles that depend on
36+ * these constants should try to call this function on every render, rather
37+ * than caching the value (for example, using inline styles rather than
38+ * setting a value in a `StyleSheet`).
39+ *
40+ * Example: `const {height, width} = Dimensions.get('window');`
41+ *
42+ * @param {string } dim Name of dimension as defined when calling `set`.
43+ * @returns {Object? } Value for the dimension.
44+ */
45+ static get ( dim : string ) : Object {
46+ invariant ( dimensions [ dim ] , 'No dimension set for key ' + dim ) ;
47+ return dimensions [ dim ] ;
48+ }
49+
2550 /**
2651 * This should only be called from native code by sending the
2752 * didUpdateDimensions event.
2853 *
2954 * @param {object } dims Simple string-keyed object of dimensions to set
3055 */
31- static set ( dims : { [ key : string ] : any } ) : void {
56+ static set ( dims : $ReadOnly < { [ key : string ] : any } > ) : void {
3257 // We calculate the window dimensions in JS so that we don't encounter loss of
3358 // precision in transferring the dimensions (which could be non-integers) over
3459 // the bridge.
35- if ( dims && dims . windowPhysicalPixels ) {
36- // parse/stringify => Clone hack
37- dims = JSON . parse ( JSON . stringify ( dims ) ) ;
38-
39- const windowPhysicalPixels = dims . windowPhysicalPixels ;
40- dims . window = {
60+ let { screen, window} = dims ;
61+ const { windowPhysicalPixels} = dims ;
62+ if ( windowPhysicalPixels ) {
63+ window = {
4164 width : windowPhysicalPixels . width / windowPhysicalPixels . scale ,
4265 height : windowPhysicalPixels . height / windowPhysicalPixels . scale ,
4366 scale : windowPhysicalPixels . scale ,
4467 fontScale : windowPhysicalPixels . fontScale ,
4568 } ;
46- if ( Platform . OS === 'android' ) {
47- // Screen and window dimensions are different on android
48- const screenPhysicalPixels = dims . screenPhysicalPixels ;
49- dims . screen = {
50- width : screenPhysicalPixels . width / screenPhysicalPixels . scale ,
51- height : screenPhysicalPixels . height / screenPhysicalPixels . scale ,
52- scale : screenPhysicalPixels . scale ,
53- fontScale : screenPhysicalPixels . fontScale ,
54- } ;
55-
56- // delete so no callers rely on this existing
57- delete dims . screenPhysicalPixels ;
58- } else {
59- dims . screen = dims . window ;
60- }
61- // delete so no callers rely on this existing
62- delete dims . windowPhysicalPixels ;
69+ }
70+ const { screenPhysicalPixels} = dims ;
71+ if ( screenPhysicalPixels ) {
72+ screen = {
73+ width : screenPhysicalPixels . width / screenPhysicalPixels . scale ,
74+ height : screenPhysicalPixels . height / screenPhysicalPixels . scale ,
75+ scale : screenPhysicalPixels . scale ,
76+ fontScale : screenPhysicalPixels . fontScale ,
77+ } ;
78+ } else if ( screen == null ) {
79+ screen = window ;
6380 }
6481
65- Object . assign ( dimensions , dims ) ;
82+ dimensions = { window , screen } ;
6683 if ( dimensionsInitialized ) {
6784 // Don't fire 'change' the first time the dimensions are set.
68- eventEmitter . emit ( 'change' , {
69- window : dimensions . window ,
70- screen : dimensions . screen ,
71- } ) ;
85+ eventEmitter. emit ( 'change' , dimensions ) ;
7286 } else {
7387 dimensionsInitialized = true ;
7488 }
7589 }
7690
77- /**
78- * Initial dimensions are set before `runApplication` is called so they should
79- * be available before any other require's are run, but may be updated later.
80- *
81- * Note: Although dimensions are available immediately, they may change (e.g
82- * due to device rotation) so any rendering logic or styles that depend on
83- * these constants should try to call this function on every render, rather
84- * than caching the value (for example, using inline styles rather than
85- * setting a value in a `StyleSheet`).
86- *
87- * Example: `var {height, width} = Dimensions.get('window');`
88- *
89- * @param {string } dim Name of dimension as defined when calling `set`.
90- * @returns {Object? } Value for the dimension.
91- */
92- static get ( dim : string ) : Object {
93- invariant ( dimensions [ dim ] , 'No dimension set for key ' + dim ) ;
94- return dimensions [ dim ] ;
95- }
96-
9791 /**
9892 * Add an event handler. Supported events:
9993 *
@@ -102,7 +96,7 @@ class Dimensions {
10296 * are the same as the return values of `Dimensions.get('window')` and
10397 * `Dimensions.get('screen')`, respectively.
10498 */
105- static addEventListener ( type : string , handler : Function ) {
99+ static addEventListener ( type : 'change' , handler : Function ) {
106100 invariant (
107101 type === 'change' ,
108102 'Trying to subscribe to unknown event: "%s"' ,
@@ -114,7 +108,7 @@ class Dimensions {
114108 /**
115109 * Remove an event handler.
116110 */
117- static removeEventListener ( type : string , handler : Function ) {
111+ static removeEventListener ( type : 'change' , handler : Function ) {
118112 invariant (
119113 type === 'change' ,
120114 'Trying to remove listener for unknown event: "%s"' ,
@@ -124,25 +118,13 @@ class Dimensions {
124118 }
125119}
126120
127- let dims : ?{ [ key : string ] : any } =
128- global . nativeExtensions &&
129- global . nativeExtensions . DeviceInfo &&
130- global . nativeExtensions . DeviceInfo . Dimensions ;
131- let nativeExtensionsEnabled = true ;
132- if ( ! dims ) {
133- dims = NativeDeviceInfo . getConstants ( ) . Dimensions ;
134- nativeExtensionsEnabled = false ;
135- }
136-
137- invariant (
138- dims ,
139- 'Either DeviceInfo native extension or DeviceInfo Native Module must be registered' ,
140- ) ;
141- Dimensions . set ( dims ) ;
142- if ( ! nativeExtensionsEnabled ) {
143- RCTDeviceEventEmitter . addListener ( 'didUpdateDimensions' , function ( update ) {
121+ // Subscribe before calling getConstants to make sure we don't miss any updates in between.
122+ RCTDeviceEventEmitter . addListener (
123+ 'didUpdateDimensions' ,
124+ ( update : DimensionsPayload ) => {
144125 Dimensions . set ( update ) ;
145- } ) ;
146- }
126+ } ,
127+ ) ;
128+ Dimensions . set ( NativeDeviceInfo . getConstants ( ) . Dimensions ) ;
147129
148130module . exports = Dimensions ;
0 commit comments