Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose static methods to manipulate the StatusBar stack imperatively #21206

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 76 additions & 20 deletions Libraries/Components/StatusBar/StatusBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,34 @@ function createStackEntry(props: any): any {
*
* ### Imperative API
*
* For cases where using a component is not ideal, there is also an imperative
* API exposed as static functions on the component. It is however not recommended
* to use the static API and the component for the same prop because any value
* set by the static API will get overriden by the one set by the component in
* the next render.
* For cases where using a component is not ideal, there are static methods
* to manipulate the `StatusBar` display stack. These methods have the same
* behavior as mounting and unmounting a `StatusBar` component.
*
* For example, you can call `StatusBar.pushStackEntry` to update the status bar
* before launching a third-party native UI component, and then call
* `StatusBar.popStackEntry` when completed.
*
* ```
* const openThirdPartyBugReporter = async () => {
* // The bug reporter has a dark background, so we push a new status bar style.
* const stackEntry = StatusBar.pushStackEntry({barStyle: 'light-content'});
*
* // `open` returns a promise that resolves when the UI is dismissed.
* await BugReporter.open();
*
* // Don't forget to call `popStackEntry` when you're done.
* StatusBar.popStackEntry(stackEntry);
* };
* ```
*
* There is legacy imperative API that enables you to manually update the
* status bar styles. However, the legacy API does not update the internal
* `StatusBar` diaplay stack, which means that any changes will be overridden
* whenever a `StatusBar` component is mounted or unmounted.
*
* It is strongly advised that you use `pushStackEntry`, `popStackEntry`, or
* `replaceStackEntry` instead of the static methods beginning with `set`.
*
* ### Constants
*
Expand Down Expand Up @@ -261,6 +284,48 @@ class StatusBar extends React.Component<{
StatusBarManager.setTranslucent(translucent);
}

/**
* Push a StatusBar entry onto the stack.
* The return value should be passed to `popStackEntry` when complete.
*
* @param props Object containing the StatusBar props to use in the stack entry.
*/
jamesreggio marked this conversation as resolved.
Show resolved Hide resolved
static pushStackEntry(props) {
const entry = createStackEntry(props);
StatusBar._propsStack.push(entry);
StatusBar._updatePropsStack();
return entry;
}

/**
* Pop a StatusBar entry from the stack.
*
* @param entry Entry returned from `pushStackEntry`.
*/
jamesreggio marked this conversation as resolved.
Show resolved Hide resolved
static popStackEntry(entry) {
const index = StatusBar._propsStack.indexOf(entry);
if (index !== -1) {
StatusBar._propsStack.splice(index, 1);
}
StatusBar._updatePropsStack();
}

/**
* Replace an existing StatusBar stack entry with new props.
*
* @param entry Entry returned from `pushStackEntry` to replace.
* @param props Object containing the StatusBar props to use in the replacement stack entry.
*/
jamesreggio marked this conversation as resolved.
Show resolved Hide resolved
jamesreggio marked this conversation as resolved.
Show resolved Hide resolved
static replaceStackEntry(entry, props) {
const newEntry = createStackEntry(props);
const index = StatusBar._propsStack.indexOf(entry);
if (index !== -1) {
StatusBar._propsStack[index] = newEntry;
}
StatusBar._updatePropsStack();
return newEntry;
}

static propTypes = {
/**
* If the status bar is hidden.
Expand Down Expand Up @@ -314,33 +379,24 @@ class StatusBar extends React.Component<{
// Every time a StatusBar component is mounted, we push it's prop to a stack
// and always update the native status bar with the props from the top of then
// stack. This allows having multiple StatusBar components and the one that is
// added last or is deeper in the view hierarchy will have priority.
this._stackEntry = createStackEntry(this.props);
StatusBar._propsStack.push(this._stackEntry);
this._updatePropsStack();
// added last or is deeper in the view hierachy will have priority.
this._stackEntry = StatusBar.pushStackEntry(this.props);
}

componentWillUnmount() {
// When a StatusBar is unmounted, remove itself from the stack and update
// the native bar with the next props.
const index = StatusBar._propsStack.indexOf(this._stackEntry);
StatusBar._propsStack.splice(index, 1);

this._updatePropsStack();
StatusBar.popStackEntry(this._stackEntry);
}

componentDidUpdate() {
const index = StatusBar._propsStack.indexOf(this._stackEntry);
this._stackEntry = createStackEntry(this.props);
StatusBar._propsStack[index] = this._stackEntry;

jamesreggio marked this conversation as resolved.
Show resolved Hide resolved
this._updatePropsStack();
this._stackEntry = StatusBar.replaceStackEntry(this._stackEntry, this.props);
}

/**
* Updates the native status bar with the props from the stack.
*/
_updatePropsStack = () => {
static _updatePropsStack = () => {
// Send the update to the native module only once at the end of the frame.
clearImmediate(StatusBar._updateImmediate);
StatusBar._updateImmediate = setImmediate(() => {
Expand All @@ -358,7 +414,7 @@ class StatusBar extends React.Component<{
) {
StatusBarManager.setStyle(
mergedProps.barStyle.value,
mergedProps.barStyle.animated,
mergedProps.barStyle.animated || false,
);
}
if (!oldProps || oldProps.hidden.value !== mergedProps.hidden.value) {
Expand Down