diff --git a/e2e/Overlay.test.js b/e2e/Overlay.test.js index a2788e7e9fc..571972d864f 100644 --- a/e2e/Overlay.test.js +++ b/e2e/Overlay.test.js @@ -55,3 +55,24 @@ describe('Overlay', () => { await expect(elementById(TestIDs.TOP_BAR_ELEMENT)).toBeVisible(); }); }); + +describe('Overlay Dismiss all', () => { + beforeEach(async () => { + await device.launchApp({ newInstance: true }); + await elementById(TestIDs.NAVIGATION_TAB).tap(); + await elementById(TestIDs.OVERLAY_BTN).tap(); + }); + + it('dismissAllOverlays should dismiss all opened overlays', async() => { + await elementById(TestIDs.SHOW_FULLSCREEN_OVERLAY_BTN).tap(); + await elementById(TestIDs.SHOW_OVERLAY_BTN).tap(); + await elementById(TestIDs.DISMISS_ALL_OVERLAYS_BUTTON).tap(); + await expect(elementById(TestIDs.OVERLAY_DISMISSED_COUNT)).toHaveText('2'); + }); + + it('dismissAllOverlays should be able to dismiss only one overlay', async() => { + await elementById(TestIDs.SHOW_OVERLAY_BTN).tap(); + await elementById(TestIDs.DISMISS_ALL_OVERLAYS_BUTTON).tap(); + await expect(elementById(TestIDs.OVERLAY_DISMISSED_COUNT)).toHaveText('1'); + }) +}) diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java b/lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java index 3bfbc0b4083..2d8f1ba523e 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java @@ -184,6 +184,11 @@ public void dismissOverlay(String commandId, String componentId, Promise promise handle(() -> navigator().dismissOverlay(componentId, new NativeCommandListener("dismissOverlay", commandId, promise, eventEmitter, now))); } + @ReactMethod + public void dismissAllOverlays(String commandId, Promise promise) { + handle(() -> navigator().dismissAllOverlays(new NativeCommandListener("dismissAllOverlays", commandId, promise, eventEmitter, now))); + } + private Navigator navigator() { return activity().getNavigator(); } diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/Navigator.java b/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/Navigator.java index 5d5e1c076f8..dbbc98dfb50 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/Navigator.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/Navigator.java @@ -210,6 +210,10 @@ public void dismissOverlay(final String componentId, CommandListener listener) { overlayManager.dismiss(overlaysLayout, componentId, listener); } + public void dismissAllOverlays(CommandListener listener) { + overlayManager.dismissAll(overlaysLayout, listener); + } + @Nullable @Override public ViewController findController(String id) { diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/overlay/OverlayManager.java b/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/overlay/OverlayManager.java index 6309841ebd7..f2578d18c23 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/overlay/OverlayManager.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/overlay/OverlayManager.java @@ -35,6 +35,11 @@ public void dismiss(ViewGroup overlaysContainer, String componentId, CommandList } } + public void dismissAll(ViewGroup overlaysContainer, CommandListener listener) { + destroy(overlaysContainer); + listener.onSuccess(""); + } + public void destroy(ViewGroup overlaysContainer) { forEach(overlayRegistry.values(), overlay -> destroyOverlay(overlaysContainer, overlay)); } diff --git a/lib/ios/RNNBridgeModule.m b/lib/ios/RNNBridgeModule.m index 51bd90e3ee9..d7b37488055 100644 --- a/lib/ios/RNNBridgeModule.m +++ b/lib/ios/RNNBridgeModule.m @@ -218,6 +218,16 @@ - (instancetype)initWithCommandsHandler:(RNNCommandsHandler *)commandsHandler { }); } +RCT_EXPORT_METHOD(dismissAllOverlays + : (NSString *)commandId componentId + : (RCTPromiseResolveBlock)resolve rejecter + : (RCTPromiseRejectBlock)reject) { + RCTExecuteOnMainQueue(^{ + [self->_commandsHandler dismissAllOverlays:commandId]; + resolve(nil); + }); +} + RCT_EXPORT_METHOD(getLaunchArgs : (NSString *)commandId : (RCTPromiseResolveBlock)resolve rejecter diff --git a/lib/ios/RNNCommandsHandler.h b/lib/ios/RNNCommandsHandler.h index 77f22570eea..04842f6483c 100644 --- a/lib/ios/RNNCommandsHandler.h +++ b/lib/ios/RNNCommandsHandler.h @@ -82,4 +82,6 @@ completion:(RNNTransitionCompletionBlock)completion rejection:(RNNTransitionRejectionBlock)reject; +- (void)dismissAllOverlays:(NSString *)commandId; + @end diff --git a/lib/ios/RNNCommandsHandler.m b/lib/ios/RNNCommandsHandler.m index 7f75c080d92..14c4031b23a 100644 --- a/lib/ios/RNNCommandsHandler.m +++ b/lib/ios/RNNCommandsHandler.m @@ -22,6 +22,7 @@ static NSString *const dismissAllModals = @"dismissAllModals"; static NSString *const showOverlay = @"showOverlay"; static NSString *const dismissOverlay = @"dismissOverlay"; +static NSString *const dismissAllOverlays = @"dismissAllOverlays"; static NSString *const mergeOptions = @"mergeOptions"; static NSString *const setDefaultOptions = @"setDefaultOptions"; @@ -467,6 +468,14 @@ - (void)dismissOverlay:(NSString *)componentId } } +- (void)dismissAllOverlays:(NSString *)commandId { + [self assertReady]; + RNNAssertMainQueue(); + + [_overlayManager dismissAllOverlays]; + [self->_eventEmitter sendOnNavigationCommandCompletion:dismissAllOverlays commandId:commandId]; +} + #pragma mark - private - (void)assertReady { diff --git a/lib/src/Navigation.ts b/lib/src/Navigation.ts index 47cd17baaa6..58520f47577 100644 --- a/lib/src/Navigation.ts +++ b/lib/src/Navigation.ts @@ -261,6 +261,13 @@ export class NavigationRoot { return this.commands.dismissOverlay(componentId); } + /** + * dismiss all overlays + */ + public dismissAllOverlays(): Promise { + return this.commands.dismissAllOverlays(); + } + /** * Resolves arguments passed on launch */ diff --git a/lib/src/adapters/NativeCommandsSender.ts b/lib/src/adapters/NativeCommandsSender.ts index 5974b84d4bf..fc7e4b18d57 100644 --- a/lib/src/adapters/NativeCommandsSender.ts +++ b/lib/src/adapters/NativeCommandsSender.ts @@ -14,6 +14,7 @@ interface NativeCommandsModule { dismissAllModals(commandId: string, options?: object): Promise; showOverlay(commandId: string, layout: object): Promise; dismissOverlay(commandId: string, componentId: string): Promise; + dismissAllOverlays(commandId: string): Promise; getLaunchArgs(commandId: string): Promise; } @@ -75,6 +76,10 @@ export class NativeCommandsSender { return this.nativeCommandsModule.dismissOverlay(commandId, componentId); } + dismissAllOverlays(commandId: string) { + return this.nativeCommandsModule.dismissAllOverlays(commandId); + } + getLaunchArgs(commandId: string) { return this.nativeCommandsModule.getLaunchArgs(commandId); } diff --git a/lib/src/commands/Commands.test.ts b/lib/src/commands/Commands.test.ts index c11fd80dd37..ed5eea6a421 100644 --- a/lib/src/commands/Commands.test.ts +++ b/lib/src/commands/Commands.test.ts @@ -517,6 +517,7 @@ describe('Commands', () => { setStackRoot: ['id', [{}]], showOverlay: [{}], dismissOverlay: ['id'], + dismissAllOverlays: [{}], getLaunchArgs: ['id'], }; const paramsForMethodName: Record = { @@ -541,6 +542,7 @@ describe('Commands', () => { }, showOverlay: { commandId: 'showOverlay+UNIQUE_ID', layout: null }, dismissOverlay: { commandId: 'dismissOverlay+UNIQUE_ID', componentId: 'id' }, + dismissAllOverlays: { commandId: 'dismissAllOverlays+UNIQUE_ID' }, getLaunchArgs: { commandId: 'getLaunchArgs+UNIQUE_ID' }, }; forEach(getAllMethodsOfUut(), (m) => { diff --git a/lib/src/commands/Commands.ts b/lib/src/commands/Commands.ts index 13430b4e962..219b3245064 100644 --- a/lib/src/commands/Commands.ts +++ b/lib/src/commands/Commands.ts @@ -185,6 +185,13 @@ export class Commands { return result; } + public dismissAllOverlays() { + const commandId = this.uniqueIdProvider.generate(CommandName.DismissAllOverlays); + const result = this.nativeCommandsSender.dismissAllOverlays(commandId); + this.commandsObserver.notify(CommandName.DismissAllOverlays, { commandId }); + return result; + } + public getLaunchArgs() { const commandId = this.uniqueIdProvider.generate(CommandName.GetLaunchArgs); const result = this.nativeCommandsSender.getLaunchArgs(commandId); diff --git a/lib/src/interfaces/CommandName.ts b/lib/src/interfaces/CommandName.ts index 5d9069c1bad..0e8aa1bad21 100644 --- a/lib/src/interfaces/CommandName.ts +++ b/lib/src/interfaces/CommandName.ts @@ -13,5 +13,6 @@ export enum CommandName { SetStackRoot = 'setStackRoot', ShowOverlay = 'showOverlay', DismissOverlay = 'dismissOverlay', + DismissAllOverlays = 'dismissAllOverlays', GetLaunchArgs = 'getLaunchArgs', } diff --git a/playground/src/screens/OverlayAlert.tsx b/playground/src/screens/OverlayAlert.tsx index f8dca04b04d..d5586529361 100644 --- a/playground/src/screens/OverlayAlert.tsx +++ b/playground/src/screens/OverlayAlert.tsx @@ -5,9 +5,28 @@ import { component } from '../commons/Layouts'; import Screens from './Screens'; import testIDs from '../testIDs'; -const { OVERLAY_ALERT_HEADER, DISMISS_BTN, SET_INTERCEPT_TOUCH } = testIDs; +const { + OVERLAY_ALERT_HEADER, + DISMISS_BTN, + SET_INTERCEPT_TOUCH, + DISMISS_ALL_OVERLAYS_BUTTON, +} = testIDs; -export default class OverlayAlert extends React.PureComponent { +interface Props extends NavigationComponentProps { + incrementDismissedOverlays: any; +} + +export default class OverlayAlert extends React.PureComponent { + constructor(props: Props) { + super(props); + Navigation.events().registerCommandCompletedListener((event) => { + if (event.commandName === 'dismissAllOverlays') { + if (this.props.incrementDismissedOverlays) { + this.props.incrementDismissedOverlays(); + } + } + }); + } render() { return ( @@ -15,6 +34,11 @@ export default class OverlayAlert extends React.PureComponent