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

feat(ramp): add deeplink handler #9153

Merged
merged 10 commits into from
Apr 23, 2024
34 changes: 34 additions & 0 deletions app/components/UI/Ramp/deeplink/handleRampUrl.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { NavigationProp, ParamListBase } from '@react-navigation/native';
import { RampType } from '../types';
import Routes from '../../../../constants/navigation/Routes';
import handleRampUrl from './handleRampUrl';

jest.mock('@react-navigation/native');

describe('handleRampUrl', () => {
let navigation: NavigationProp<ParamListBase>;

beforeEach(() => {
navigation = {
navigate: jest.fn(),
} as unknown as NavigationProp<ParamListBase>;
});

it('should navigate to BUY route when rampType is BUY', () => {
handleRampUrl({
rampPath: '/somePath?as=example',
rampType: RampType.BUY,
navigation,
});
expect(navigation.navigate).toHaveBeenCalledWith(Routes.RAMP.BUY);
});

it('should navigate to SELL route when rampType is SELL', () => {
handleRampUrl({
rampPath: '/somePath?as=example',
rampType: RampType.SELL,
navigation,
});
expect(navigation.navigate).toHaveBeenCalledWith(Routes.RAMP.SELL);
});
});
24 changes: 24 additions & 0 deletions app/components/UI/Ramp/deeplink/handleRampUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { NavigationProp, ParamListBase } from '@react-navigation/native';
import { RampType } from '../types';
import Routes from '../../../../constants/navigation/Routes';

interface RampUrlOptions {
rampPath: string;
rampType: RampType;
navigation: NavigationProp<ParamListBase>;
}

export default function handleRampUrl({
rampPath: _rampPath,
wachunei marked this conversation as resolved.
Show resolved Hide resolved
rampType,
navigation,
}: RampUrlOptions) {
switch (rampType) {
case RampType.BUY:
navigation.navigate(Routes.RAMP.BUY);
break;
case RampType.SELL:
navigation.navigate(Routes.RAMP.SELL);
break;
}
}
4 changes: 4 additions & 0 deletions app/constants/deeplinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export enum ACTIONS {
CONNECT = 'connect',
MMSDK = 'mmsdk',
ANDROID_SDK = 'bind',
BUY = 'buy',
BUY_CRYPTO = 'buy-crypto',
SELL = 'sell',
SELL_CRYPTO = 'sell-crypto',
EMPTY = '',
}
Expand All @@ -37,6 +39,8 @@ export const PREFIXES = {
[ACTIONS.WC]: '',
[ACTIONS.CONNECT]: '',
[ACTIONS.ANDROID_SDK]: '',
[ACTIONS.BUY]: '',
[ACTIONS.SELL]: '',
[ACTIONS.BUY_CRYPTO]: '',
[ACTIONS.SELL_CRYPTO]: '',
METAMASK: 'metamask://',
Expand Down
25 changes: 21 additions & 4 deletions app/core/DeeplinkManager/DeeplinkManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ import { NavigationProp, ParamListBase } from '@react-navigation/native';
import DeeplinkManager from './DeeplinkManager';
import handleBrowserUrl from './Handlers/handleBrowserUrl';
import handleEthereumUrl from './Handlers/handleEthereumUrl';
import handleRampUrl from './Handlers/handleRampUrl';
import switchNetwork from './Handlers/switchNetwork';
import parseDeeplink from './ParseManager/parseDeeplink';
import approveTransaction from './TransactionManager/approveTransaction';
import { RampType } from '../../reducers/fiatOrders/types';

jest.mock('./TransactionManager/approveTransaction');
jest.mock('./Handlers/handleEthereumUrl');
jest.mock('./Handlers/handleBrowserUrl');
jest.mock('./Handlers/handleRampUrl');
jest.mock('./ParseManager/parseDeeplink');
jest.mock('./Handlers/switchNetwork');

Expand Down Expand Up @@ -96,13 +99,27 @@ describe('DeeplinkManager', () => {
});

it('should handle buy crypto action correctly', () => {
deeplinkManager._handleBuyCrypto();
expect(mockNavigation.navigate).toHaveBeenCalledWith('RampBuy');
const rampPath = '/example/path?and=params';
deeplinkManager._handleBuyCrypto(rampPath);
expect(handleRampUrl).toHaveBeenCalledWith(
expect.objectContaining({
rampPath,
navigation: mockNavigation,
rampType: RampType.BUY,
}),
);
});

it('should handle sell crypto action correctly', () => {
deeplinkManager._handleSellCrypto();
expect(mockNavigation.navigate).toHaveBeenCalledWith('RampSell');
const rampPath = '/example/path?and=params';
deeplinkManager._handleSellCrypto(rampPath);
expect(handleRampUrl).toHaveBeenCalledWith(
expect.objectContaining({
rampPath,
navigation: mockNavigation,
rampType: RampType.SELL,
}),
);
});

it('should parse deeplinks correctly', () => {
Expand Down
19 changes: 14 additions & 5 deletions app/core/DeeplinkManager/DeeplinkManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
import { NavigationProp, ParamListBase } from '@react-navigation/native';
import { ParseOutput } from 'eth-url-parser';
import { AnyAction, Dispatch, Store } from 'redux';
import Routes from '../../constants/navigation/Routes';
import handleBrowserUrl from './Handlers/handleBrowserUrl';
import handleEthereumUrl from './Handlers/handleEthereumUrl';
import handleRampUrl from './Handlers/handleRampUrl';
import switchNetwork from './Handlers/switchNetwork';
import parseDeeplink from './ParseManager/parseDeeplink';
import approveTransaction from './TransactionManager/approveTransaction';
import { RampType } from '../../reducers/fiatOrders/types';

class DeeplinkManager {
public navigation: NavigationProp<ParamListBase>;
Expand Down Expand Up @@ -67,12 +68,20 @@ class DeeplinkManager {
});
}

_handleBuyCrypto() {
this.navigation.navigate(Routes.RAMP.BUY);
_handleBuyCrypto(rampPath: string) {
handleRampUrl({
rampPath,
navigation: this.navigation,
rampType: RampType.BUY,
});
}

_handleSellCrypto() {
this.navigation.navigate(Routes.RAMP.SELL);
_handleSellCrypto(rampPath: string) {
handleRampUrl({
rampPath,
navigation: this.navigation,
rampType: RampType.SELL,
});
}

parse(
Expand Down
1 change: 1 addition & 0 deletions app/core/DeeplinkManager/Handlers/handleRampUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '../../../components/UI/Ramp/deeplink/handleRampUrl';
20 changes: 16 additions & 4 deletions app/core/DeeplinkManager/ParseManager/handleMetaMaskDeeplink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,22 @@ export function handleMetaMaskDeeplink({
.catch((err) => {
console.warn(`DeepLinkManager failed to connect`, err);
});
} else if (url.startsWith(`${PREFIXES.METAMASK}${ACTIONS.BUY_CRYPTO}`)) {
instance._handleBuyCrypto();
} else if (url.startsWith(`${PREFIXES.METAMASK}${ACTIONS.SELL_CRYPTO}`)) {
instance._handleSellCrypto();
} else if (
url.startsWith(`${PREFIXES.METAMASK}${ACTIONS.BUY_CRYPTO}`) ||
url.startsWith(`${PREFIXES.METAMASK}${ACTIONS.BUY}`)
) {
const rampPath = url
.replace(`${PREFIXES.METAMASK}${ACTIONS.BUY_CRYPTO}`, '')
.replace(`${PREFIXES.METAMASK}${ACTIONS.BUY}`, '');
instance._handleBuyCrypto(rampPath);
} else if (
url.startsWith(`${PREFIXES.METAMASK}${ACTIONS.SELL_CRYPTO}`) ||
url.startsWith(`${PREFIXES.METAMASK}${ACTIONS.SELL}`)
) {
const rampPath = url
.replace(`${PREFIXES.METAMASK}${ACTIONS.SELL_CRYPTO}`, '')
.replace(`${PREFIXES.METAMASK}${ACTIONS.SELL}`, '');
instance._handleSellCrypto(rampPath);
}
}

Expand Down
14 changes: 10 additions & 4 deletions app/core/DeeplinkManager/ParseManager/handleUniversalLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,16 @@ function handleUniversalLink({
);
// loops back to open the link with the right protocol
instance.parse(deeplinkUrl, { browserCallBack, origin });
} else if (action === ACTIONS.BUY_CRYPTO) {
instance._handleBuyCrypto();
} else if (action === ACTIONS.SELL_CRYPTO) {
instance._handleSellCrypto();
} else if (action === ACTIONS.BUY_CRYPTO || action === ACTIONS.BUY) {
const rampPath = urlObj.href
.replace(`${DEEP_LINK_BASE}/${ACTIONS.BUY_CRYPTO}`, '')
.replace(`${DEEP_LINK_BASE}/${ACTIONS.BUY}`, '');
instance._handleBuyCrypto(rampPath);
} else if (action === ACTIONS.SELL_CRYPTO || action === ACTIONS.SELL) {
const rampPath = urlObj.href
.replace(`${DEEP_LINK_BASE}/${ACTIONS.SELL_CRYPTO}`, '')
.replace(`${DEEP_LINK_BASE}/${ACTIONS.SELL}`, '');
instance._handleSellCrypto(rampPath);
} else {
// If it's our universal link or Apple store deep link don't open it in the browser
if (
Expand Down
Loading