Skip to content

Commit

Permalink
Merge pull request #299 from TokelPlatform/feature/handling-deeplinks
Browse files Browse the repository at this point in the history
✨ Handling `tokel://` deeplinks
  • Loading branch information
lenilsonjr authored Aug 7, 2022
2 parents bedfdd9 + 51f8834 commit 8d0fe9b
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 14 deletions.
19 changes: 17 additions & 2 deletions src/components/Home/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React from 'react';
import React, { useEffect } from 'react';
import { useSelector } from 'react-redux';

import styled from '@emotion/styled';
import { ipcRenderer } from 'electron';

import { dispatch } from 'store/rematch';
import { selectModalName, selectView } from 'store/selectors';
import links from 'util/links';
import { TOPBAR_HEIGHT_PX, ViewType } from 'vars/defines';
import { DEEP_LINK_IPC_ID, TOPBAR_HEIGHT_PX, ViewType } from 'vars/defines';

import InfoNote from 'components/_General/InfoNote';
import CreateToken from 'components/CreateToken/CreateToken';
Expand Down Expand Up @@ -68,6 +70,19 @@ const Home = () => {
const currentView = useSelector(selectView);
const modalProps = modals[useSelector(selectModalName)];

useEffect(() => {
const listener = (_, { view, params }) => {
dispatch.environment.SET_VIEW(view || ViewType.DASHBOARD);
if (params) dispatch.environment.SET_DEEP_LINK_PARAMS(params);
};

ipcRenderer.on(DEEP_LINK_IPC_ID, listener);

return () => {
ipcRenderer.removeListener(DEEP_LINK_IPC_ID, listener);
};
}, []);

return (
<HomeRoot>
<SideMenu />
Expand Down
26 changes: 24 additions & 2 deletions src/components/Marketplace/Marketplace.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';

import { css } from '@emotion/react';
import styled from '@emotion/styled';
Expand All @@ -9,6 +10,8 @@ import fillOrderIcon from 'assets/fillOrder.svg';
import inboxIcon from 'assets/inbox.svg';
import listIcon from 'assets/list.svg';
import trendUpIcon from 'assets/trendUp.svg';
import { dispatch } from 'store/rematch';
import { selectDeepLinkParams } from 'store/selectors';
import { V } from 'util/theming';

import { Layout, SubTitle, Title } from 'components/_General/_UIElements/common';
Expand Down Expand Up @@ -68,9 +71,28 @@ const WelcomeMessageWrapper = styled.div`
`;

const Marketplace: React.FC = () => {
const deepLinkParams = useSelector(selectDeepLinkParams);
const [currentView, setCurrentView] = useState<MARKETPLACE_VIEWS | null>(null);
const [currentOrderId, setCurrentOrderId] = useState<string | undefined>();

const handleViewChange = (view: MARKETPLACE_VIEWS | null) => {
setCurrentView(view);
setCurrentOrderId(undefined);
};

useEffect(() => {
if (deepLinkParams?.length) {
const params = new URLSearchParams(deepLinkParams);
const action =
params.get('action') === 'bid' ? MARKETPLACE_VIEWS.BID : MARKETPLACE_VIEWS.FILL;

setCurrentView(action);
setCurrentOrderId(params.get('orderid') || params.get('tokenid'));

dispatch.environment.SET_DEEP_LINK_PARAMS('');
}
}, [deepLinkParams, currentView]);

const CurrentTab = () => {
switch (currentView) {
case MARKETPLACE_VIEWS.FILL:
Expand Down Expand Up @@ -123,7 +145,7 @@ const Marketplace: React.FC = () => {
{menuData.map(menuItem => (
<MenuItem
key={menuItem.name}
onClick={() => setCurrentView(menuItem.type)}
onClick={() => handleViewChange(menuItem.type)}
name={menuItem.name}
icon={menuItem.icon}
selected={menuItem.type === currentView}
Expand Down
10 changes: 3 additions & 7 deletions src/components/Marketplace/widgets/MarketOrder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ const MarketOrderWidget: React.FC<MarketOrderWidgetProps> = ({ type }) => {
const tokenDetails = useSelector(selectTokenDetails);
const myTokens = useMyTokens();
const fulfillOrderSchema = useFulfillOrderSchema(type);
const { currentOrderId: prefillOrderId, setCurrentOrderId: setPrefillOrderId } =
useContext(ViewContext);
const { currentOrderId: prefillOrderId } = useContext(ViewContext);

const handleMarketOrder = (values: MarketOrder, { setSubmitting }) => {
setSubmitting(false);
Expand Down Expand Up @@ -101,13 +100,11 @@ const MarketOrderWidget: React.FC<MarketOrderWidgetProps> = ({ type }) => {
: 'Review order';

useEffect(() => {
if (prefillOrderId) formikBag.setFieldValue('orderId', prefillOrderId);
if (prefillOrderId?.length)
formikBag.setFieldValue(type === 'fill' ? 'orderId' : 'assetId', prefillOrderId);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [prefillOrderId, formikBag.setFieldValue]);

// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => () => setPrefillOrderId(undefined), []);

useEffect(() => {
if (currentOrderDetails) {
const quantity = parseBigNumObject(currentOrderDetails.bnAmount).toNumber();
Expand Down Expand Up @@ -190,7 +187,6 @@ const MarketOrderWidget: React.FC<MarketOrderWidgetProps> = ({ type }) => {
formikBag.setFieldValue('order', {});
formikBag.setFieldValue('quantity', 0);
formikBag.setFieldValue('price', 0);
formikBag.setFieldValue('assetId', '');
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [formikBag.setFieldValue, formikBag.values.orderId]);
Expand Down
29 changes: 28 additions & 1 deletion src/electron/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@ import log from 'electron-log';
import { autoUpdater } from 'electron-updater';

import { BitgoAction, checkData } from '../util/bitgoHelper';
import { BITGO_IPC_ID, IPFS_IPC_ID, VERSIONS_MSG, WindowControl } from '../vars/defines';
import {
BITGO_IPC_ID,
DEEP_LINK_IPC_ID,
DEEP_LINK_PROTOCOL,
IPFS_IPC_ID,
VERSIONS_MSG,
WindowControl,
} from '../vars/defines';
import MenuBuilder from './menu';
import packagejson from './package.json';

Expand Down Expand Up @@ -104,6 +111,15 @@ ipcMain.on('window-controls', async (_, arg) => {
}
});

// handle deep links
if (process.defaultApp) {
if (process.argv.length >= 2) {
app.setAsDefaultProtocolClient('tokel', process.execPath, [path.resolve(process.argv[1])]);
}
} else {
app.setAsDefaultProtocolClient('tokel');
}

const createWindow = async () => {
if (isDev || process.env.DEBUG_PROD === 'true') {
await installExtensions();
Expand Down Expand Up @@ -217,6 +233,17 @@ autoUpdater.on('update-downloaded', data => {
mainWindow?.webContents.send('update-downloaded', data);
});

app.on('open-url', (_, url) => {
if (url.startsWith(DEEP_LINK_PROTOCOL)) {
console.log('RECEIVED DEEP LINK:', url);
const urlObj = new URL(url);
mainWindow.webContents.send(DEEP_LINK_IPC_ID, {
view: urlObj.hostname,
params: urlObj.search,
});
}
});

app.on('window-all-closed', () => {
// Respect the OSX convention of having the application in memory even
// after all windows have been closed
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/usePrevious.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useRef, useEffect } from 'react';
import { useEffect, useRef } from 'react';

const usePrevious = <T>(value: T): T | undefined => {
const ref = useRef<T>();

useEffect(() => {
ref.current = value;
});
}, [value]);

return ref.current;
};
Expand Down
3 changes: 3 additions & 0 deletions src/store/models/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { RootModel } from './models';
export type EnvironmentState = {
theme?: ThemeName;
view?: string;
deepLinkParams?: string;
modal: Modal;
tokenDetails: Record<string, TokenDetail>;
tokelPriceUSD?: number;
Expand Down Expand Up @@ -41,6 +42,7 @@ export default createModel<RootModel>()({
state: {
theme: themeNames[0],
view: ViewType.DASHBOARD,
deepLinkParams: '',
modal: DEFAULT_NULL_MODAL,
tokenDetails: {},
tokelPriceUSD: null,
Expand All @@ -56,6 +58,7 @@ export default createModel<RootModel>()({
reducers: {
SET_THEME: (state, theme: string) => ({ ...state, theme }),
SET_VIEW: (state, view: string) => ({ ...state, view }),
SET_DEEP_LINK_PARAMS: (state, deepLinkParams: string) => ({ ...state, deepLinkParams }),
SET_MODAL: (state, modal: Modal) => ({ ...state, modal }),
SET_MODAL_NAME: (state, modalName: ModalName) => dp.set(state, `modal.name`, modalName),
SET_TOKEN_DETAIL: (state, detail: TokenDetail) => {
Expand Down
1 change: 1 addition & 0 deletions src/store/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { RootState } from './rematch';

export const selectTheme = (state: RootState) => state.environment.theme;
export const selectView = (state: RootState) => state.environment.view;
export const selectDeepLinkParams = (state: RootState) => state.environment.deepLinkParams;
export const selectModal = (state: RootState) => state.environment.modal;
export const selectModalName = (state: RootState) => state.environment.modal.name;
export const selectModalOptions = (state: RootState) => state.environment.modal.options;
Expand Down
2 changes: 2 additions & 0 deletions src/vars/defines.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ export const TICKER = 'TKL';
export const RPC_PORT = '29405';
export const BITGO_IPC_ID = 'bitgo';
export const IPFS_IPC_ID = 'ipfs';
export const DEEP_LINK_IPC_ID = 'link';
export const VERSIONS_MSG = 'version';
export const DEEP_LINK_PROTOCOL = 'tokel://';
export enum IpfsAction {
GET = 'get',
}
Expand Down

0 comments on commit 8d0fe9b

Please sign in to comment.