123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- /**
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- * @format
- * @flow
- */
- 'use strict';
- const EventEmitter = require('../vendor/emitter/EventEmitter');
- const NativeEventEmitter = require('../EventEmitter/NativeEventEmitter');
- const invariant = require('invariant');
- const logError = require('../Utilities/logError');
- import NativeAppState from './NativeAppState';
- /**
- * `AppState` can tell you if the app is in the foreground or background,
- * and notify you when the state changes.
- *
- * See https://reactnative.dev/docs/appstate.html
- */
- class AppState extends NativeEventEmitter {
- _eventHandlers: Object;
- _supportedEvents = ['change', 'memoryWarning', 'blur', 'focus'];
- currentState: ?string;
- isAvailable: boolean;
- constructor() {
- super(NativeAppState);
- this.isAvailable = true;
- this._eventHandlers = this._supportedEvents.reduce((handlers, key) => {
- handlers[key] = new Map();
- return handlers;
- }, {});
- this.currentState = NativeAppState.getConstants().initialAppState;
- let eventUpdated = false;
- // TODO: this is a terrible solution - in order to ensure `currentState`
- // prop is up to date, we have to register an observer that updates it
- // whenever the state changes, even if nobody cares. We should just
- // deprecate the `currentState` property and get rid of this.
- this.addListener('appStateDidChange', appStateData => {
- eventUpdated = true;
- this.currentState = appStateData.app_state;
- });
- // TODO: see above - this request just populates the value of `currentState`
- // when the module is first initialized. Would be better to get rid of the
- // prop and expose `getCurrentAppState` method directly.
- NativeAppState.getCurrentAppState(appStateData => {
- // It's possible that the state will have changed here & listeners need to be notified
- if (!eventUpdated && this.currentState !== appStateData.app_state) {
- this.currentState = appStateData.app_state;
- this.emit('appStateDidChange', appStateData);
- }
- }, logError);
- }
- // TODO: now that AppState is a subclass of NativeEventEmitter, we could
- // deprecate `addEventListener` and `removeEventListener` and just use
- // addListener` and `listener.remove()` directly. That will be a breaking
- // change though, as both the method and event names are different
- // (addListener events are currently required to be globally unique).
- /**
- * Add a handler to AppState changes by listening to the `change` event type
- * and providing the handler.
- *
- * See https://reactnative.dev/docs/appstate.html#addeventlistener
- */
- addEventListener(type: string, handler: Function) {
- invariant(
- this._supportedEvents.indexOf(type) !== -1,
- 'Trying to subscribe to unknown event: "%s"',
- type,
- );
- switch (type) {
- case 'change': {
- this._eventHandlers[type].set(
- handler,
- this.addListener('appStateDidChange', appStateData => {
- handler(appStateData.app_state);
- }),
- );
- break;
- }
- case 'memoryWarning': {
- this._eventHandlers[type].set(
- handler,
- this.addListener('memoryWarning', handler),
- );
- break;
- }
- case 'blur':
- case 'focus': {
- this._eventHandlers[type].set(
- handler,
- this.addListener('appStateFocusChange', hasFocus => {
- if (type === 'blur' && !hasFocus) {
- handler();
- }
- if (type === 'focus' && hasFocus) {
- handler();
- }
- }),
- );
- }
- }
- }
- /**
- * Remove a handler by passing the `change` event type and the handler.
- *
- * See https://reactnative.dev/docs/appstate.html#removeeventlistener
- */
- removeEventListener(type: string, handler: Function) {
- invariant(
- this._supportedEvents.indexOf(type) !== -1,
- 'Trying to remove listener for unknown event: "%s"',
- type,
- );
- if (!this._eventHandlers[type].has(handler)) {
- return;
- }
- this._eventHandlers[type].get(handler).remove();
- this._eventHandlers[type].delete(handler);
- }
- }
- function throwMissingNativeModule() {
- invariant(
- false,
- 'Cannot use AppState module when native RCTAppState is not included in the build.\n' +
- 'Either include it, or check AppState.isAvailable before calling any methods.',
- );
- }
- class MissingNativeAppStateShim extends EventEmitter {
- // AppState
- isAvailable: boolean = false;
- currentState: ?string = null;
- addEventListener(type: string, handler: Function) {
- throwMissingNativeModule();
- }
- removeEventListener(type: string, handler: Function) {
- throwMissingNativeModule();
- }
- // EventEmitter
- addListener() {
- throwMissingNativeModule();
- }
- removeAllListeners() {
- throwMissingNativeModule();
- }
- removeSubscription() {
- throwMissingNativeModule();
- }
- }
- // This module depends on the native `RCTAppState` module. If you don't include it,
- // `AppState.isAvailable` will return `false`, and any method calls will throw.
- // We reassign the class variable to keep the autodoc generator happy.
- const AppStateInstance: AppState | MissingNativeAppStateShim = NativeAppState
- ? new AppState()
- : new MissingNativeAppStateShim();
- module.exports = AppStateInstance;
|