feat(MM-65625): enhance navigation overlay management (#9165)

* feat: enhance navigation overlay management

- Add overlay tracking to NavigationStore with exception handling
- Implement dismissAllOverlays with individual overlay dismissal
- Add overlay management methods (add, remove, filter exceptions)
- Preserve floating-banner-overlay when dismissing other overlays
- Add comprehensive tests for overlay stack management
- Update navigation command listeners for overlay events

This improves overlay management by tracking overlays in the store
and providing fine-grained control over which overlays to dismiss,
particularly preserving floating banners during navigation cleanup.

* make tests more meaningful in navigation

* ChatGPT review fix

* revert changes to dismissAllOverlays
* create a new function dismissAllOverlaysWithExceptions
* add new function removeAllOverlaysFromStack to clear the entire overlays

* added a mock to dismissAllOverlays

* update thread to call dismissAllOverlays with exceptions instead.

* remove unneeded removeAllOverlaysWithExceptions()

* silly ai

* use set instead of array.
This commit is contained in:
Rahim Rahman 2025-10-10 11:31:36 -06:00 committed by GitHub
parent f6e8ebc2e9
commit f1554f0341
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 527 additions and 22 deletions

View file

@ -13,7 +13,7 @@ import {getCurrentTeamId, getCurrentUserId, prepareCommonSystemValues, type Prep
import {addChannelToTeamHistory, addTeamToTeamHistory} from '@queries/servers/team';
import {getThreadById, prepareThreadsFromReceivedPosts, queryThreadsInTeam} from '@queries/servers/thread';
import {getCurrentUser} from '@queries/servers/user';
import {dismissAllModals, dismissAllModalsAndPopToRoot, dismissAllOverlays, goToScreen} from '@screens/navigation';
import {dismissAllModals, dismissAllModalsAndPopToRoot, dismissAllOverlaysWithExceptions, goToScreen} from '@screens/navigation';
import EphemeralStore from '@store/ephemeral_store';
import NavigationStore from '@store/navigation_store';
import {isTablet} from '@utils/helpers';
@ -95,7 +95,7 @@ export const switchToThread = async (serverUrl: string, rootId: string, isFromNo
if (isFromNotification) {
if (currentThreadId && currentThreadId === rootId && NavigationStore.getScreensInStack().includes(Screens.THREAD)) {
await dismissAllModals();
await dismissAllOverlays();
await dismissAllOverlaysWithExceptions();
return {};
}

View file

@ -6,11 +6,16 @@ import {Navigation} from 'react-native-navigation';
import {Events, Preferences, Screens} from '@constants';
import NavigationStore from '@store/navigation_store';
import {openAsBottomSheet} from './navigation';
jest.unmock('@screens/navigation');
import * as navigationModule from './navigation';
import {dismissAllOverlaysWithExceptions} from './navigation';
import type {FirstArgument} from '@typings/utils/utils';
import type {IntlShape} from 'react-intl';
const {registerNavigationListeners} = navigationModule;
function expectShowModalCalledWith(screen: string, title: string, props?: Record<string, unknown>) {
expect(Navigation.showModal).toHaveBeenCalledWith({
stack: {
@ -33,7 +38,7 @@ function expectShowModalOverCurrentContext(screen: string, props?: Record<string
expectShowModalCalledWith(screen, '', props);
}
function expectOpenAsBottomSheetCalledWith(props: FirstArgument<typeof openAsBottomSheet>, isTablet: boolean) {
function expectOpenAsBottomSheetCalledWith(props: FirstArgument<typeof navigationModule.openAsBottomSheet>, isTablet: boolean) {
if (isTablet) {
expectShowModalCalledWith(props.screen, props.title, {closeButtonId: props.closeButtonId, ...props.props});
} else {
@ -107,3 +112,373 @@ describe('openUserProfileModal', () => {
}, false);
});
});
describe('overlay command listeners', () => {
let mockCommandListener: jest.Mock;
beforeEach(() => {
jest.clearAllMocks();
NavigationStore.reset();
mockCommandListener = jest.fn();
const mockEvents = {
registerScreenPoppedListener: jest.fn(() => ({remove: jest.fn()})),
registerCommandListener: jest.fn((listener) => {
mockCommandListener = listener;
return {remove: jest.fn()};
}),
registerComponentWillAppearListener: jest.fn(() => ({remove: jest.fn()})),
};
(Navigation.events as jest.Mock).mockReturnValue(mockEvents);
registerNavigationListeners();
});
it('should call NavigationStore.addOverlayToStack when showOverlay command is received', () => {
const overlayId = 'test-overlay-id';
const params = {layout: {id: overlayId}};
expect(NavigationStore.getOverlaysInStack()).toEqual([]);
mockCommandListener('showOverlay', params);
expect(NavigationStore.getOverlaysInStack()).toEqual([overlayId]);
});
it('should call NavigationStore.removeOverlayFromStack when dismissOverlay command is received', () => {
const overlayId = 'test-overlay-id';
NavigationStore.addOverlayToStack(overlayId);
NavigationStore.addOverlayToStack('another-overlay');
expect(NavigationStore.getOverlaysInStack()).toEqual(['another-overlay', overlayId]);
mockCommandListener('dismissOverlay', {componentId: overlayId});
expect(NavigationStore.getOverlaysInStack()).toEqual(['another-overlay']);
});
it('should call NavigationStore.removeAllOverlaysFromStack when dismissAllOverlays command is received', () => {
NavigationStore.addOverlayToStack('overlay1');
NavigationStore.addOverlayToStack('floating-banner-overlay');
NavigationStore.addOverlayToStack('overlay2');
expect(NavigationStore.getOverlaysInStack()).toEqual(['overlay2', 'floating-banner-overlay', 'overlay1']);
mockCommandListener('dismissAllOverlays', {});
expect(NavigationStore.getOverlaysInStack()).toEqual([]);
});
it('should not affect NavigationStore when unrecognized commands are received', () => {
NavigationStore.addOverlayToStack('test-overlay');
const initialOverlays = NavigationStore.getOverlaysInStack();
mockCommandListener('someOtherCommand', {});
expect(NavigationStore.getOverlaysInStack()).toEqual(initialOverlays);
});
});
describe('showOverlay', () => {
beforeEach(() => {
jest.clearAllMocks();
NavigationStore.reset();
});
it('should call Navigation.showOverlay with correct parameters', () => {
const screenName = Screens.USER_PROFILE;
const passProps = {userId: 'user123'};
const options = {overlay: {interceptTouchOutside: true}};
const id = 'custom-overlay-id';
navigationModule.showOverlay(screenName, passProps, options, id);
expect(Navigation.showOverlay).toHaveBeenCalledWith({
component: {
id: 'custom-overlay-id',
name: screenName,
passProps,
options: expect.objectContaining({
layout: {
backgroundColor: 'transparent',
componentBackgroundColor: 'transparent',
},
overlay: {
interceptTouchOutside: true,
},
}),
},
});
});
it('should use default options when none provided', () => {
const screenName = Screens.USER_PROFILE;
navigationModule.showOverlay(screenName);
expect(Navigation.showOverlay).toHaveBeenCalledWith({
component: {
id: undefined,
name: screenName,
passProps: {},
options: {
layout: {
backgroundColor: 'transparent',
componentBackgroundColor: 'transparent',
},
overlay: {
interceptTouchOutside: false,
},
},
},
});
});
it('should merge custom options with default options correctly', () => {
const screenName = Screens.USER_PROFILE;
const customOptions = {
layout: {backgroundColor: 'red'},
overlay: {interceptTouchOutside: true},
customProperty: 'test',
};
navigationModule.showOverlay(screenName, {}, customOptions);
expect(Navigation.showOverlay).toHaveBeenCalledWith({
component: {
id: undefined,
name: screenName,
passProps: {},
options: expect.objectContaining({
layout: {
backgroundColor: 'red',
componentBackgroundColor: 'transparent',
},
overlay: {
interceptTouchOutside: true,
},
customProperty: 'test',
}),
},
});
});
});
describe('dismissOverlay', () => {
beforeEach(() => {
jest.clearAllMocks();
NavigationStore.reset();
});
it('should call Navigation.dismissOverlay with correct componentId', async () => {
const componentId = 'overlay-123';
await navigationModule.dismissOverlay(componentId);
expect(Navigation.dismissOverlay).toHaveBeenCalledWith(componentId);
});
it('should handle Navigation.dismissOverlay rejection gracefully', async () => {
const componentId = 'non-existent-overlay';
(Navigation.dismissOverlay as jest.Mock).mockRejectedValueOnce(new Error('Overlay not found'));
await expect(navigationModule.dismissOverlay(componentId)).resolves.not.toThrow();
expect(Navigation.dismissOverlay).toHaveBeenCalledWith(componentId);
});
});
describe('dismissAllOverlays', () => {
beforeEach(() => {
jest.clearAllMocks();
NavigationStore.reset();
});
it('should call Navigation.dismissAllOverlays', async () => {
NavigationStore.addOverlayToStack('overlay1');
NavigationStore.addOverlayToStack('floating-banner-overlay');
NavigationStore.addOverlayToStack('overlay2');
expect(NavigationStore.getOverlaysInStack()).toEqual(['overlay2', 'floating-banner-overlay', 'overlay1']);
await navigationModule.dismissAllOverlays();
expect(Navigation.dismissAllOverlays).toHaveBeenCalledTimes(1);
expect(Navigation.dismissOverlay).not.toHaveBeenCalled();
});
it('should handle empty overlay stack gracefully', async () => {
expect(NavigationStore.getOverlaysInStack()).toEqual([]);
await navigationModule.dismissAllOverlays();
expect(Navigation.dismissAllOverlays).toHaveBeenCalledTimes(1);
});
it('should handle only exception overlays in stack', async () => {
NavigationStore.addOverlayToStack('floating-banner-overlay');
expect(NavigationStore.getOverlaysInStack()).toEqual(['floating-banner-overlay']);
await navigationModule.dismissAllOverlays();
expect(Navigation.dismissAllOverlays).toHaveBeenCalledTimes(1);
});
it('should handle Navigation.dismissAllOverlays rejection gracefully', async () => {
(Navigation.dismissAllOverlays as jest.Mock).mockRejectedValueOnce(new Error('Dismiss failed'));
NavigationStore.addOverlayToStack('overlay1');
NavigationStore.addOverlayToStack('overlay2');
await expect(navigationModule.dismissAllOverlays()).resolves.not.toThrow();
expect(Navigation.dismissAllOverlays).toHaveBeenCalledTimes(1);
});
it('should call Navigation.dismissAllOverlays once regardless of overlay count', async () => {
NavigationStore.addOverlayToStack('overlay1');
NavigationStore.addOverlayToStack('overlay2');
NavigationStore.addOverlayToStack('overlay3');
await navigationModule.dismissAllOverlays();
expect(Navigation.dismissAllOverlays).toHaveBeenCalledTimes(1);
expect(Navigation.dismissOverlay).not.toHaveBeenCalled();
});
});
describe('dismissAllModalsAndPopToRoot', () => {
beforeEach(() => {
jest.clearAllMocks();
NavigationStore.reset();
});
it('should call dismissAllModals, dismissAllOverlaysWithExceptions, and popToRoot in sequence', async () => {
NavigationStore.addModalToStack('modal1');
NavigationStore.addScreenToStack('screen1');
await navigationModule.dismissAllModalsAndPopToRoot();
expect(Navigation.dismissModal).toHaveBeenCalledTimes(1);
expect(Navigation.dismissModal).toHaveBeenCalledWith('modal1', {animations: {dismissModal: {enabled: false}}});
expect(Navigation.popToRoot).toHaveBeenCalledTimes(1);
expect(Navigation.popToRoot).toHaveBeenCalledWith('screen1');
});
it('should dismiss non-exception overlays via dismissAllOverlaysWithExceptions', async () => {
NavigationStore.addOverlayToStack('overlay1');
NavigationStore.addOverlayToStack('floating-banner-overlay');
NavigationStore.addOverlayToStack('overlay2');
expect(NavigationStore.getOverlaysInStack()).toEqual(['overlay2', 'floating-banner-overlay', 'overlay1']);
await navigationModule.dismissAllModalsAndPopToRoot();
expect(Navigation.dismissOverlay).toHaveBeenCalledTimes(2);
expect(Navigation.dismissOverlay).toHaveBeenCalledWith('overlay2');
expect(Navigation.dismissOverlay).toHaveBeenCalledWith('overlay1');
expect(Navigation.dismissOverlay).not.toHaveBeenCalledWith('floating-banner-overlay');
});
it('should handle empty overlay stack gracefully', async () => {
expect(NavigationStore.getOverlaysInStack()).toEqual([]);
NavigationStore.addScreenToStack('screen1');
await navigationModule.dismissAllModalsAndPopToRoot();
expect(Navigation.dismissModal).not.toHaveBeenCalled();
expect(Navigation.dismissOverlay).not.toHaveBeenCalled();
expect(Navigation.popToRoot).toHaveBeenCalledTimes(1);
expect(Navigation.popToRoot).toHaveBeenCalledWith('screen1');
});
it('should continue with popToRoot even if overlay dismissal fails', async () => {
(Navigation.dismissOverlay as jest.Mock).mockRejectedValueOnce(new Error('Dismiss failed'));
NavigationStore.addOverlayToStack('overlay1');
NavigationStore.addScreenToStack('screen1');
await expect(navigationModule.dismissAllModalsAndPopToRoot()).resolves.not.toThrow();
expect(Navigation.dismissModal).not.toHaveBeenCalled();
expect(Navigation.dismissOverlay).toHaveBeenCalledWith('overlay1');
expect(Navigation.popToRoot).toHaveBeenCalledTimes(1);
expect(Navigation.popToRoot).toHaveBeenCalledWith('screen1');
});
});
describe('dismissAllOverlaysWithExceptions', () => {
beforeEach(() => {
jest.clearAllMocks();
NavigationStore.reset();
});
it('should dismiss non-exception overlays individually', async () => {
NavigationStore.addOverlayToStack('overlay1');
NavigationStore.addOverlayToStack('floating-banner-overlay');
NavigationStore.addOverlayToStack('overlay2');
expect(NavigationStore.getOverlaysInStack()).toEqual(['overlay2', 'floating-banner-overlay', 'overlay1']);
await dismissAllOverlaysWithExceptions();
expect(Navigation.dismissOverlay).toHaveBeenCalledTimes(2);
expect(Navigation.dismissOverlay).toHaveBeenCalledWith('overlay2');
expect(Navigation.dismissOverlay).toHaveBeenCalledWith('overlay1');
expect(Navigation.dismissOverlay).not.toHaveBeenCalledWith('floating-banner-overlay');
});
it('should handle empty overlay stack gracefully', async () => {
expect(NavigationStore.getOverlaysInStack()).toEqual([]);
await dismissAllOverlaysWithExceptions();
expect(Navigation.dismissOverlay).not.toHaveBeenCalled();
});
it('should handle only exception overlays in stack', async () => {
NavigationStore.addOverlayToStack('floating-banner-overlay');
expect(NavigationStore.getOverlaysInStack()).toEqual(['floating-banner-overlay']);
await dismissAllOverlaysWithExceptions();
expect(Navigation.dismissOverlay).not.toHaveBeenCalled();
});
it('should use Promise.all for concurrent dismissals', async () => {
const dismissalOrder: string[] = [];
(Navigation.dismissOverlay as jest.Mock).mockImplementation((overlayId: string) => {
dismissalOrder.push(overlayId);
return Promise.resolve();
});
NavigationStore.addOverlayToStack('overlay1');
NavigationStore.addOverlayToStack('overlay2');
NavigationStore.addOverlayToStack('overlay3');
await dismissAllOverlaysWithExceptions();
expect(Navigation.dismissOverlay).toHaveBeenCalledTimes(3);
expect(dismissalOrder).toContain('overlay1');
expect(dismissalOrder).toContain('overlay2');
expect(dismissalOrder).toContain('overlay3');
});
it('should continue dismissing even if one overlay dismissal fails', async () => {
(Navigation.dismissOverlay as jest.Mock).
mockImplementationOnce(() => Promise.reject(new Error('Dismiss failed'))).
mockImplementationOnce(() => Promise.resolve());
NavigationStore.addOverlayToStack('overlay1');
NavigationStore.addOverlayToStack('overlay2');
await expect(dismissAllOverlaysWithExceptions()).resolves.not.toThrow();
expect(Navigation.dismissOverlay).toHaveBeenCalledTimes(2);
expect(Navigation.dismissOverlay).toHaveBeenCalledWith('overlay2');
expect(Navigation.dismissOverlay).toHaveBeenCalledWith('overlay1');
});
});

View file

@ -76,6 +76,16 @@ function onCommandListener(name: string, params: any) {
case 'dismissModal':
NavigationStore.removeModalFromStack(params.componentId);
break;
case 'showOverlay':
NavigationStore.addOverlayToStack(params?.layout?.id);
break;
case 'dismissOverlay':
NavigationStore.removeOverlayFromStack(params?.componentId);
break;
case 'dismissAllOverlays': {
NavigationStore.removeAllOverlaysFromStack();
break;
}
}
if (NavigationStore.getVisibleScreen() === Screens.HOME) {
@ -553,7 +563,7 @@ export async function popToRoot() {
export async function dismissAllModalsAndPopToRoot() {
await dismissAllModals();
await dismissAllOverlays();
await dismissAllOverlaysWithExceptions();
await popToRoot();
}
@ -567,7 +577,7 @@ export async function dismissAllModalsAndPopToRoot() {
*/
export async function dismissAllModalsAndPopToScreen(screenId: AvailableScreens, title: string, passProps = {}, options = {}) {
await dismissAllModals();
await dismissAllOverlays();
await dismissAllOverlaysWithExceptions();
if (NavigationStore.getScreensInStack().includes(screenId)) {
let mergeOptions = options;
if (title) {
@ -791,6 +801,28 @@ export async function dismissOverlay(componentId: string) {
}
}
/**
* Instead of using native dismissAllOverlays, we're looping through the overlays
* and dismissing them individually. Native dismissAllOverlays is causing the app to
* dismiss 700+ overlays. Even though those overlays doesn't exist in the stack. Since we're
* tracking the overlays in the store, we can dismiss them individually.
* @returns
*/
export async function dismissAllOverlaysWithExceptions() {
try {
const overlaysToRemove = NavigationStore.getAllOverlaysOtherThanExceptions();
if (!overlaysToRemove.length) {
return;
}
await Promise.all(overlaysToRemove.map((overlayId) =>
Navigation.dismissOverlay(overlayId).catch(() => undefined),
));
} catch {
// do nothing
}
}
export async function dismissAllOverlays() {
try {
await Navigation.dismissAllOverlays();
@ -912,3 +944,4 @@ export async function openUserProfileModal(
Keyboard.dismiss();
openAsBottomSheet({screen, title, theme, closeButtonId, props: {...props}});
}

View file

@ -113,6 +113,77 @@ describe('NavigationStore', () => {
});
});
describe('overlay management', () => {
it('should add and remove overlays correctly', () => {
NavigationStore.addOverlayToStack('test-overlay');
NavigationStore.addOverlayToStack('another-overlay');
expect(NavigationStore.getOverlaysInStack()).toEqual(['another-overlay', 'test-overlay']);
});
it('should handle duplicate overlay additions', () => {
NavigationStore.addOverlayToStack('test-overlay');
NavigationStore.addOverlayToStack('test-overlay');
expect(NavigationStore.getOverlaysInStack()).toEqual(['test-overlay']);
});
it('should remove specific overlays from stack', () => {
NavigationStore.addOverlayToStack('overlay1');
NavigationStore.addOverlayToStack('overlay2');
NavigationStore.addOverlayToStack('overlay3');
NavigationStore.removeOverlayFromStack('overlay2');
expect(NavigationStore.getOverlaysInStack()).toEqual(['overlay3', 'overlay1']);
});
it('should return overlays other than exceptions', () => {
NavigationStore.addOverlayToStack('regular-overlay');
NavigationStore.addOverlayToStack('floating-banner-overlay');
NavigationStore.addOverlayToStack('another-overlay');
const overlaysToRemove = NavigationStore.getAllOverlaysOtherThanExceptions();
expect(overlaysToRemove).toEqual(['another-overlay', 'regular-overlay']);
expect(overlaysToRemove).not.toContain('floating-banner-overlay');
});
it('should return empty array when only exception overlays exist', () => {
NavigationStore.addOverlayToStack('floating-banner-overlay');
const overlaysToRemove = NavigationStore.getAllOverlaysOtherThanExceptions();
expect(overlaysToRemove).toEqual([]);
});
it('should return all overlays when no exceptions exist in stack', () => {
NavigationStore.addOverlayToStack('overlay1');
NavigationStore.addOverlayToStack('overlay2');
const overlaysToRemove = NavigationStore.getAllOverlaysOtherThanExceptions();
expect(overlaysToRemove).toEqual(['overlay2', 'overlay1']);
});
it('should handle empty overlay stack gracefully when getting overlays to remove', () => {
const overlaysToRemove = NavigationStore.getAllOverlaysOtherThanExceptions();
expect(overlaysToRemove).toEqual([]);
expect(NavigationStore.getOverlaysInStack()).toEqual([]);
});
it('should maintain overlay order when filtering', () => {
NavigationStore.addOverlayToStack('first-overlay');
NavigationStore.addOverlayToStack('floating-banner-overlay');
NavigationStore.addOverlayToStack('second-overlay');
NavigationStore.addOverlayToStack('third-overlay');
const overlaysToRemove = NavigationStore.getAllOverlaysOtherThanExceptions();
expect(overlaysToRemove).toEqual(['third-overlay', 'second-overlay', 'first-overlay']);
});
});
describe('screen loading and visibility', () => {
it('should handle waitUntilScreenHasLoaded', async () => {
const promise = NavigationStore.waitUntilScreenHasLoaded(Screens.ABOUT);

View file

@ -5,9 +5,12 @@ import {BehaviorSubject} from 'rxjs';
import type {AvailableScreens} from '@typings/screens/navigation';
const OVERLAY_EXCEPTIONS = new Set(['floating-banner-overlay']);
class NavigationStoreSingleton {
private screensInStack: AvailableScreens[] = [];
private modalsInStack: AvailableScreens[] = [];
private overlaysInStack: string[] = [];
private visibleTab = 'Home';
private tosOpen = false;
@ -20,6 +23,7 @@ class NavigationStoreSingleton {
reset = () => {
this.screensInStack = [];
this.modalsInStack = [];
this.overlaysInStack = [];
this.visibleTab = 'Home';
this.tosOpen = false;
this.subject.next(undefined);
@ -85,6 +89,28 @@ class NavigationStoreSingleton {
}
};
addOverlayToStack = (overlayId: string) => {
this.removeOverlayFromStack(overlayId);
this.overlaysInStack.unshift(overlayId);
};
removeOverlayFromStack = (overlayId: string) => {
const index = this.overlaysInStack.indexOf(overlayId);
if (index > -1) {
this.overlaysInStack.splice(index, 1);
}
};
getOverlaysInStack = () => this.overlaysInStack;
removeAllOverlaysFromStack = () => {
this.overlaysInStack = [];
};
getAllOverlaysOtherThanExceptions = () => {
return this.overlaysInStack.filter((overlayId) => !OVERLAY_EXCEPTIONS.has(overlayId));
};
setToSOpen = (open: boolean) => {
this.tosOpen = open;
};

View file

@ -1,8 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
/* eslint-disable react/no-multi-comp */
import {setGenerator} from '@nozbe/watermelondb/utils/common/randomId';
import * as ReactNative from 'react-native';
import mockSafeAreaContext from 'react-native-safe-area-context/jest/mock';
@ -342,22 +340,23 @@ jest.mock('react-native-navigation', () => {
RNN.Navigation.setLazyComponentRegistrator = jest.fn();
RNN.Navigation.setDefaultOptions = jest.fn();
RNN.Navigation.registerComponent = jest.fn();
const mockSubscription = {remove: jest.fn()};
const createMockEvents = () => ({
registerAppLaunchedListener: jest.fn(() => mockSubscription),
registerComponentListener: jest.fn(() => mockSubscription),
bindComponent: jest.fn(() => mockSubscription),
registerNavigationButtonPressedListener: jest.fn(() => mockSubscription),
registerScreenPoppedListener: jest.fn(() => mockSubscription),
registerCommandListener: jest.fn(() => mockSubscription),
registerComponentWillAppearListener: jest.fn(() => mockSubscription),
});
return {
...RNN,
Navigation: {
...RNN.Navigation,
events: () => ({
registerAppLaunchedListener: jest.fn(),
registerComponentListener: jest.fn(() => {
return {remove: jest.fn()};
}),
bindComponent: jest.fn(() => {
return {remove: jest.fn()};
}),
registerNavigationButtonPressedListener: jest.fn(() => {
return {remove: jest.fn()};
}),
}),
events: jest.fn(createMockEvents),
setRoot: jest.fn(),
pop: jest.fn(),
push: jest.fn(),
@ -367,7 +366,8 @@ jest.mock('react-native-navigation', () => {
popToRoot: jest.fn(),
mergeOptions: jest.fn(),
showOverlay: jest.fn(),
dismissOverlay: jest.fn(),
dismissOverlay: jest.fn(() => Promise.resolve()),
dismissAllOverlays: jest.fn(() => Promise.resolve()),
updateProps: jest.fn(),
},
};
@ -395,7 +395,7 @@ jest.mock('react-native-notifications', () => {
ios: {
getDeliveredNotifications: jest.fn().mockImplementation(() => Promise.resolve(deliveredNotifications)),
removeDeliveredNotifications: jest.fn((ids) => {
// eslint-disable-next-line
// @ts-ignore
deliveredNotifications = deliveredNotifications.filter((n) => !ids.includes(n.identifier));
}),