Skip to content
Merged
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ dependencies {
implementation platform('io.adapty:adapty-bom:3.15.2')
implementation 'io.adapty:android-sdk'
implementation 'io.adapty:android-ui'
implementation 'io.adapty.internal:crossplatform:3.15.3'
implementation 'io.adapty.internal:crossplatform:3.15.6'

// Compatible with older and newer RN
//noinspection GradleDynamicVersion
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package",
"name": "react-native-adapty",
"version": "3.15.1",
"version": "3.15.2",
"description": "Adapty React Native SDK",
"license": "MIT",
"author": "Adapty team <support@adapty.io>",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1670,14 +1670,14 @@ describe('ViewController - onPaywallClosed after user action close', () => {
expect(onCloseButtonPressHandler).toHaveBeenCalledTimes(1);
expect(dismissSpy).toHaveBeenCalledTimes(1);

// 2. Wait for dismiss to complete (including removeAllListeners)
await dismissSpy.mock.results[0]?.value;

// 3. Native code sends onPaywallClosed event after paywall actually closes
// 2. Native code sends onPaywallClosed before dismiss resolves
// (dismiss response is expected to be the last event)
emitPaywallViewDisappearedEvent(viewId, closedSample.view);

// 3. Wait for dismiss to complete (includes cleanup)
await dismissSpy.mock.results[0]?.value;

// VERIFICATION: onPaywallClosed handler should be called
// Cleanup happens via internal handler AFTER this client handler executes
expect(onPaywallClosedHandler).toHaveBeenCalledTimes(1);

dismissSpy.mockRestore();
Expand Down
15 changes: 3 additions & 12 deletions src/ui/view-controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ describe('ViewController', () => {
});

describe('dismiss', () => {
it('calls bridge and registers internal cleanup handler', async () => {
it('calls bridge and clears listeners after dismiss', async () => {
const { AdaptyPaywallCoder } = jest.requireMock(
'@/coders/adapty-paywall',
);
Expand All @@ -141,23 +141,16 @@ describe('ViewController', () => {
.mockResolvedValueOnce(undefined); // dismiss

const { ViewEmitter } = jest.requireMock('./view-emitter');
const addInternalListenerMock = jest.fn();
const removeAllListenersMock = jest.fn();
(ViewEmitter as unknown as jest.Mock).mockImplementation(() => ({
addListener: jest.fn(),
addInternalListener: addInternalListenerMock,
addInternalListener: jest.fn(),
removeAllListeners: removeAllListenersMock,
}));

const view = await ViewController.create(paywall, {} as any);
view.setEventHandlers({ onCloseButtonPress: () => true });

// Verify internal handler was registered for cleanup
expect(addInternalListenerMock).toHaveBeenCalledWith(
'onPaywallClosed',
expect.any(Function),
);

await view.dismiss();

expect($bridge.request).toHaveBeenLastCalledWith(
Expand All @@ -167,9 +160,7 @@ describe('ViewController', () => {
expect.any(Object),
);

// Cleanup now happens via internal handler, not directly in dismiss()
// So removeAllListeners should NOT be called immediately after dismiss
expect(removeAllListenersMock).not.toHaveBeenCalled();
expect(removeAllListenersMock).toHaveBeenCalledTimes(1);
});

it('throws if id is null', async () => {
Expand Down
12 changes: 4 additions & 8 deletions src/ui/view-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,6 @@ export class ViewController {

view.setEventHandlers(DEFAULT_EVENT_HANDLERS);

// Register internal handler for cleanup
view.viewEmitter.addInternalListener('onPaywallClosed', () => {
// Called AFTER client's onPaywallClosed handler
if (view.viewEmitter) {
view.viewEmitter.removeAllListeners();
}
});

return view;
}

Expand Down Expand Up @@ -193,6 +185,10 @@ export class ViewController {
} satisfies Req['AdaptyUIDismissPaywallView.Request']);

await this.handle<void>(methodKey, body, 'Void', ctx, log);

if (this.viewEmitter) {
this.viewEmitter.removeAllListeners();
}
}

/**
Expand Down
Loading