Skip to content

Commit

Permalink
Merge pull request #687 from magiclabs/rominhalltari-sc-91530-investi…
Browse files Browse the repository at this point in the history
…gate-and-provide-solution-for-issue

Fix multiple network setups for React Native SDKs
  • Loading branch information
romin-halltari authored Dec 15, 2023
2 parents f415ba6 + ad624ad commit 9a60ebe
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 64 deletions.
10 changes: 8 additions & 2 deletions packages/@magic-sdk/provider/src/core/view-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ async function persistMagicEventRefreshToken(event: MagicMessageEvent) {

export abstract class ViewController {
public checkIsReadyForRequest: Promise<void>;
public isReadyForRequest: boolean;
protected readonly messageHandlers = new Set<(event: MagicMessageEvent) => any>();
protected isConnectedToInternet = true;

Expand All @@ -132,6 +133,7 @@ export abstract class ViewController {
protected readonly networkHash: string,
) {
this.checkIsReadyForRequest = this.waitForReady();
this.isReadyForRequest = false;
this.listen();
}

Expand Down Expand Up @@ -167,7 +169,9 @@ export abstract class ViewController {
reject(error);
}

await this.checkIsReadyForRequest;
if (!this.isReadyForRequest) {
await this.waitForReady();
}

const batchData: JsonRpcResponse[] = [];
const batchIds = Array.isArray(payload) ? payload.map((p) => p.id) : [];
Expand Down Expand Up @@ -236,8 +240,10 @@ export abstract class ViewController {

private waitForReady() {
return new Promise<void>((resolve) => {
this.on(MagicIncomingWindowMessage.MAGIC_OVERLAY_READY, () => {
const unsubscribe = this.on(MagicIncomingWindowMessage.MAGIC_OVERLAY_READY, () => {
this.isReadyForRequest = true;
resolve();
unsubscribe();
});
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import browserEnv from '@ikscodes/browser-env';
import { MagicIncomingWindowMessage, MagicOutgoingWindowMessage, JsonRpcRequestPayload } from '@magic-sdk/types';
import { createViewController } from '../../../factories';
import { createViewController, TestViewController } from '../../../factories';
import { JsonRpcResponse } from '../../../../src/core/json-rpc';
import * as storage from '../../../../src/util/storage';
import * as webCryptoUtils from '../../../../src/util/web-crypto';
Expand Down Expand Up @@ -73,6 +73,8 @@ const FAKE_RT = 'will freshen';
const FAKE_INJECTED_JWT = 'fake injected jwt';
let FAKE_STORE: any = {};

let viewController: TestViewController;

beforeEach(() => {
jest.restoreAllMocks();
createJwtStub = jest.spyOn(webCryptoUtils, 'createJwt');
Expand All @@ -86,6 +88,8 @@ beforeEach(() => {
FAKE_STORE[key] = value;
});
SDKEnvironment.platform = 'web';
viewController = createViewController('asdf');
viewController.isReadyForRequest = true;
});

afterEach(() => {
Expand All @@ -94,7 +98,6 @@ afterEach(() => {

test('Sends payload; recieves MAGIC_HANDLE_REQUEST event; resolves response', async () => {
createJwtStub.mockImplementationOnce(() => Promise.resolve(FAKE_JWT_TOKEN));
const viewController = createViewController('asdf');
const { handlerSpy, onSpy } = stubViewController(viewController, [
[MagicIncomingWindowMessage.MAGIC_HANDLE_RESPONSE, responseEvent()],
]);
Expand All @@ -109,7 +112,6 @@ test('Sends payload; recieves MAGIC_HANDLE_REQUEST event; resolves response', as

test('Sends payload with jwt when web crypto is supported', async () => {
createJwtStub.mockImplementationOnce(() => Promise.resolve(FAKE_JWT_TOKEN));
const viewController = createViewController('asdf');
const { handlerSpy, onSpy, postSpy } = stubViewController(viewController, [
[MagicIncomingWindowMessage.MAGIC_HANDLE_RESPONSE, responseEvent()],
]);
Expand All @@ -129,7 +131,6 @@ test('Sends payload with deviceShare when it is saved', async () => {
getDecryptedDeviceShareStub.mockImplementationOnce(() => Promise.resolve(FAKE_DEVICE_SHARE));
const eventWithDeviceShare = { data: { ...responseEvent().data, deviceShare: FAKE_DEVICE_SHARE } };

const viewController = createViewController('asdf');
const { postSpy } = stubViewController(viewController, [
[MagicIncomingWindowMessage.MAGIC_HANDLE_RESPONSE, eventWithDeviceShare],
]);
Expand All @@ -154,7 +155,6 @@ test('device share should be cleared if replied user denied account access.', as
},
};

const viewController = createViewController('asdf');
const { postSpy } = stubViewController(viewController, [
[MagicIncomingWindowMessage.MAGIC_HANDLE_RESPONSE, eventWithError],
]);
Expand All @@ -169,7 +169,6 @@ test('Sends payload with rt and jwt when rt is saved', async () => {
createJwtStub.mockImplementationOnce(() => Promise.resolve(FAKE_JWT_TOKEN));
FAKE_STORE.rt = FAKE_RT;

const viewController = createViewController('asdf');
const { postSpy } = stubViewController(viewController, [
[MagicIncomingWindowMessage.MAGIC_HANDLE_RESPONSE, responseEvent()],
]);
Expand All @@ -185,7 +184,6 @@ test('Sends payload with rt and an injected jwt when both rt and jwt are saved',
FAKE_STORE.rt = FAKE_RT;
FAKE_STORE.jwt = FAKE_INJECTED_JWT;

const viewController = createViewController('asdf');
const { postSpy } = stubViewController(viewController, [
[MagicIncomingWindowMessage.MAGIC_HANDLE_RESPONSE, responseEvent()],
]);
Expand All @@ -200,7 +198,6 @@ test('Sends payload without rt if no jwt can be made', async () => {
createJwtStub.mockImplementation(() => Promise.resolve(undefined));
FAKE_STORE.rt = FAKE_RT;

const viewController = createViewController('asdf');
const { postSpy } = stubViewController(viewController, [
[MagicIncomingWindowMessage.MAGIC_HANDLE_RESPONSE, responseEvent()],
]);
Expand All @@ -215,7 +212,6 @@ test('Sends payload when web crypto jwt fails', async () => {
createJwtStub.mockRejectedValueOnce('danger');
FAKE_STORE.rt = FAKE_RT;

const viewController = createViewController('asdf');
const { handlerSpy, onSpy, postSpy } = stubViewController(viewController, [
[MagicIncomingWindowMessage.MAGIC_HANDLE_RESPONSE, responseEvent()],
]);
Expand All @@ -234,7 +230,6 @@ test('Sends payload when web crypto jwt fails', async () => {

test('Sends payload and stores rt if response event contains rt', async () => {
const eventWithRt = { data: { ...responseEvent().data, rt: FAKE_RT } };
const viewController = createViewController('asdf');
const { handlerSpy, onSpy } = stubViewController(viewController, [
[MagicIncomingWindowMessage.MAGIC_HANDLE_RESPONSE, eventWithRt],
]);
Expand All @@ -251,7 +246,6 @@ test('Sends payload and stores rt if response event contains rt', async () => {

test('throws MODAL_NOT_READY error when not connected to the internet', async () => {
const eventWithRt = { data: { ...responseEvent().data } };
const viewController = createViewController('asdf');
const { handlerSpy, onSpy } = stubViewController(viewController, [
[MagicIncomingWindowMessage.MAGIC_HANDLE_RESPONSE, eventWithRt],
]);
Expand All @@ -271,7 +265,6 @@ test('throws MODAL_NOT_READY error when not connected to the internet', async ()
test('does not call web crypto api if platform is not web', async () => {
SDKEnvironment.platform = 'react-native';
const eventWithRt = { data: { ...responseEvent().data } };
const viewController = createViewController('asdf');
const { handlerSpy, onSpy } = stubViewController(viewController, [
[MagicIncomingWindowMessage.MAGIC_HANDLE_RESPONSE, eventWithRt],
]);
Expand All @@ -286,7 +279,6 @@ test('does not call web crypto api if platform is not web', async () => {
});

test('Sends payload recieves MAGIC_HANDLE_REQUEST event; skips payloads with non-matching ID; resolves response', async () => {
const viewController = createViewController('asdf');
const { handlerSpy, onSpy } = stubViewController(viewController, [
[MagicIncomingWindowMessage.MAGIC_HANDLE_RESPONSE, responseEvent({ id: 1234 })], // Should be skipped
[MagicIncomingWindowMessage.MAGIC_HANDLE_RESPONSE, responseEvent()],
Expand All @@ -301,7 +293,6 @@ test('Sends payload recieves MAGIC_HANDLE_REQUEST event; skips payloads with non
});

test('Sends payload and standardizes malformed response', async () => {
const viewController = createViewController('asdf');
const payload = requestPayload();

stubViewController(viewController, [
Expand All @@ -318,7 +309,6 @@ test('Sends a batch payload and resolves with multiple responses', async () => {
const response2 = responseEvent({ result: 'two', id: 2 });
const response3 = responseEvent({ result: 'three', id: 3 });

const viewController = createViewController('asdf');
const { handlerSpy, onSpy } = stubViewController(viewController, [
[MagicIncomingWindowMessage.MAGIC_HANDLE_RESPONSE, response1],
[MagicIncomingWindowMessage.MAGIC_HANDLE_RESPONSE, response2],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ export class ReactNativeWebViewController extends ViewController {
this.isConnectedToInternet = isConnected;
}, [isConnected]);

useEffect(() => {
return () => {
this.isReadyForRequest = false;
};
}, []);

/**
* Saves a reference to the underlying `<WebView>` node so we can interact
* with incoming messages.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ export class ReactNativeWebViewController extends ViewController {
this.isConnectedToInternet = isConnected;
}, [isConnected]);

useEffect(() => {
return () => {
this.isReadyForRequest = false;
};
}, []);

/**
* Saves a reference to the underlying `<WebView>` node so we can interact
* with incoming messages.
Expand Down
Loading

0 comments on commit 9a60ebe

Please sign in to comment.