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

Implement application bootstrap component #4815

Merged
merged 19 commits into from
Feb 20, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
e0b6708
feature: fimplemented bootstrap component
eniolam1000752 Feb 14, 2023
28ebb35
fix: resolved all client issues faced
eniolam1000752 Feb 16, 2023
2b4dd09
Merge branch 'feature/adapt-hw' into 4707-application-bootstrap
eniolam1000752 Feb 16, 2023
1d35985
fix: resolved deepscan issues
eniolam1000752 Feb 16, 2023
dd1c8f2
fix: merged from remote
eniolam1000752 Feb 16, 2023
a22ca88
fix: resolved failing test on useCurrentApplication
eniolam1000752 Feb 16, 2023
d2694f2
fix: resolved failing test on useApplicationManagement
eniolam1000752 Feb 16, 2023
b266fbd
fix: resolved blockchainApplication reducer.test.js
eniolam1000752 Feb 16, 2023
5c78a64
fix: resolved blockchainApplication action.test.js
eniolam1000752 Feb 16, 2023
85360ed
fix: resolved getNetwork.test.js test
eniolam1000752 Feb 16, 2023
133fb96
fix: added unit test to useGetDefaultApplication
eniolam1000752 Feb 16, 2023
b66e55f
fix: resolved coverage issue on useApplicationManagement.test.js
eniolam1000752 Feb 16, 2023
ffca466
fix: resolved coverage on useTransactions
eniolam1000752 Feb 16, 2023
7e6c2c3
fix: resolved pr issues rasied by masoud
eniolam1000752 Feb 17, 2023
49db6ec
Merge branch 'feature/adapt-hw' into 4707-application-bootstrap
eniolam1000752 Feb 20, 2023
bb8e94d
fix: resolved merge conflicts
eniolam1000752 Feb 20, 2023
e01ac85
fix: removed default_network from webpack env
eniolam1000752 Feb 20, 2023
f6faee1
Merge branch 'feature/adapt-hw' into 4707-application-bootstrap
eniolam1000752 Feb 20, 2023
8c3b308
resolved merge conflict
eniolam1000752 Feb 20, 2023
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
1 change: 1 addition & 0 deletions setup/config/webpack.config.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const config = {
new webpack.DefinePlugin({
Copy link
Contributor

@oskarleonard oskarleonard Feb 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment not related to this file

Question: This seems to be unrelated to hardware wallet? Shouldnt we merge this into development?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eniola its wrong env you can have .env file in your pc for test propose check this out
https://create-react-app.dev/docs/adding-custom-environment-variables/#what-other-env-files-can-be-used

PRODUCTION: JSON.stringify(false),
VERSION: JSON.stringify(version),
DEFAULT_NETWORK: 'devnet',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no iots wront its should be in .env
and need a prefix of REACT_APP like the REACT_APP_MSW you can find in config file
REACT_APP_ DEFAULT_NETWORK

}),
],
};
Expand Down
43 changes: 43 additions & 0 deletions setup/react/app/ApplicationBootstrap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React, { useEffect } from 'react';
import {
useGetDefaultApplication,
useApplicationManagement,
useCurrentApplication,
} from '@blockchainApplication/manage/hooks';
import { useTransactionUpdate } from '@transaction/hooks';
import { PrimaryButton } from 'src/theme/buttons';

const ApplicationBootstrap = ({ children }) => {
const {
applications: defaultApps = [],
isFetched,
error,
isLoading,
retry,
} = useGetDefaultApplication();
const { setApplications } = useApplicationManagement();
const [, setCurrentApplication] = useCurrentApplication();

useTransactionUpdate();

useEffect(() => {
if (defaultApps.length && isFetched) {
setCurrentApplication(defaultApps[0]);
setApplications(defaultApps);
}
}, [isFetched]);

if (error) {
// @TODO: this return should be replaced with an actual error message page
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

create a ticket if is not exist and add ticket number to todo

return (
<div>
error
<PrimaryButton onClick={retry}>Retry</PrimaryButton>
</div>
);
}

return !isLoading && isFetched ? children : null;
};

export default ApplicationBootstrap;
25 changes: 2 additions & 23 deletions setup/react/app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,15 @@ import NotFound from 'src/modules/common/components/NotFound';
import useIpc from '@update/hooks/useIpc';
import ConnectionProvider from '@libs/wcm/context/connectionProvider';
import FlashMessageHolder from 'src/theme/flashMessage/holder';
import client from 'src/utils/api/client';
import DialogHolder from 'src/theme/dialog/holder';
import OfflineWrapper from 'src/modules/common/components/offlineWrapper';
import CustomRoute from 'src/modules/common/components/customRoute';
import NavigationBars from 'src/modules/common/components/bars';
import ThemeContext from 'src/theme/themeProvider';
import routesMap from 'src/routes/routesMap';
import { useTransactionUpdate } from '@transaction/hooks';
import routes from 'src/routes/routes';
import { MOCK_SERVICE_WORKER } from 'src/const/config';
import { useBlockchainApplicationMeta } from 'src/modules/blockchainApplication/manage/hooks/queries/useBlockchainApplicationMeta';
import {
useApplicationManagement,
useCurrentApplication,
} from 'src/modules/blockchainApplication/manage/hooks';

import './variables.css';
import styles from './app.css';

Expand All @@ -42,32 +36,17 @@ const App = ({ history }) => {
const dispatch = useDispatch();
const [loaded, setLoaded] = useState(false);
const theme = useSelector((state) => (state.settings.darkMode ? 'dark' : 'light'));
const { data: chainMetaData, isLoading } = useBlockchainApplicationMeta();
const { setApplication } = useApplicationManagement();
const [, setCurrentApplication] = useCurrentApplication();

useIpc(history);

useEffect(() => {
setLoaded(true);
// Initialize client on first render to get default application
client.create({
http: 'http://165.227.246.146:9901',
ws: 'ws://165.227.246.146:9901',
});

dispatch(bookmarksRetrieved());
dispatch(settingsRetrieved());
dispatch(watchListRetrieved());
}, []);

useEffect(() => {
if (!isLoading && chainMetaData) {
chainMetaData.data.map((data) => setApplication(data));
setCurrentApplication(chainMetaData.data[0]);
}
}, [isLoading, chainMetaData]);
useTransactionUpdate(loaded);

const routesList = Object.keys(routes);
const routeObj = Object.values(routes).find((r) => r.path === history.location.pathname) || {};
return (
Expand Down
23 changes: 15 additions & 8 deletions setup/react/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ import ipcLocale from 'src/utils/ipcLocale';
import updateApp from '@update/utils/updateApp';
import i18n from 'src/utils/i18n/i18n';
import App from './app';
import ApplicationBootstrap from './app/ApplicationBootstrap';

// eslint-disable-next-line no-extend-native
BigInt.prototype.toJSON = function () { return `${this.toString()}n`; };
BigInt.prototype.toJSON = function () {
return `${this.toString()}n`;
};

ipcLocale.init(i18n);
updateApp.init();
Expand All @@ -39,16 +42,20 @@ const queryClient = new QueryClient({
});
const rootElement = document.getElementById('app');

const renderWithRouter = Component => (
const renderWithRouter = (Component) => (
<QueryClientProvider client={queryClient}>
<Provider store={store}>
<PersistGate loading={null} persistor={persistedStore}>
<Router>
<I18nextProvider i18n={i18n}>
<Component />
</I18nextProvider>
</Router>
<ReactQueryDevtools />
<ApplicationBootstrap>
<>
<Router>
<I18nextProvider i18n={i18n}>
<Component />
</I18nextProvider>
</Router>
<ReactQueryDevtools />
</>
</ApplicationBootstrap>
</PersistGate>
</Provider>
</QueryClientProvider>
Expand Down
2 changes: 2 additions & 0 deletions src/const/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ export const METHOD = 'rest';
export const LIMIT = 20;
export const API_VERSION = 'v3';
export const MOCK_SERVICE_WORKER = process.env.REACT_APP_MSW;

export const DEFAULT_NETWORK = process.env.DEFAULT_NETWORK || 'devnet';
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { BLOCKCHAIN_APPS } from 'src/const/queries';
import {
LIMIT as limit,
API_VERSION,
} from 'src/const/config';
import { LIMIT as limit, API_VERSION } from 'src/const/config';
import { useCustomInfiniteQuery } from 'src/modules/common/hooks';

/**
Expand All @@ -23,7 +20,11 @@ import { useCustomInfiniteQuery } from 'src/modules/common/hooks';
* @returns the query object
*/

export const useBlockchainApplicationExplore = ({ config: customConfig = {}, options } = {}) => {
export const useBlockchainApplicationExplore = ({
config: customConfig = {},
options,
client,
} = {}) => {
const config = {
url: `/api/${API_VERSION}/blockchain/apps`,
method: 'get',
Expand All @@ -35,6 +36,7 @@ export const useBlockchainApplicationExplore = ({ config: customConfig = {}, opt
return useCustomInfiniteQuery({
config,
options,
client,
keys: [BLOCKCHAIN_APPS],
});
};
1 change: 1 addition & 0 deletions src/modules/blockchainApplication/manage/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export * from './useCurrentApplication';
export * from './useApplicationManagement';
export * from './usePinBlockchainApplication';
export * from './useCurrentNode';
export * from './useGetDefaultApplication';
export * from './useApplicationExploreAndMetaData'
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ export const useBlockchainApplicationMeta = ({
method: 'get',
...customConfig,
event: 'get.blockchain.apps.meta',
params: { limit, ...(customConfig?.params || {}), network },
params: {
limit,
...(customConfig?.params || {}),
network,
},
};

return useCustomInfiniteQuery({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,54 +1,58 @@
import { useCallback, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { addApplication, deleteApplication } from '../store/action';
import { addApplication, deleteApplication, setApplications as setApps } from '../store/action';
import { selectApplications } from '../store/selectors';
import { useCurrentApplication } from './useCurrentApplication';
import { usePinBlockchainApplication } from './usePinBlockchainApplication';
import { useBlockchainApplicationMeta } from './queries/useBlockchainApplicationMeta';
import { useApplicationExploreAndMetaData } from './useApplicationExploreAndMetaData';

// eslint-disable-next-line max-statements
export function useApplicationManagement() {
const dispatch = useDispatch();
const [currentApplication, setCurrentApplication] = useCurrentApplication();
const {data: defaultApplications} = useBlockchainApplicationMeta()
const { applications: defaultApplications } = useApplicationExploreAndMetaData();

const { checkPinByChainId, pins } = usePinBlockchainApplication();
const applicationsObject = useSelector(selectApplications);
const applications = useMemo(
() => {
const appsList = Object.values(applicationsObject);
return appsList.map((app) => ({
const applications = useMemo(() => {
const appsList = Object.values(applicationsObject);
return appsList
.map((app) => ({
...app,
isPinned: checkPinByChainId(app.chainID),
})).sort((a) => (a.isPinned ? -1 : 1));
},
[applicationsObject, pins],
);
}))
.sort((a) => (a.isPinned ? -1 : 1));
}, [applicationsObject, pins]);

const setApplication = useCallback(
(application) => {
if (application.isDefault) return;
dispatch(addApplication(application));
},
[],
);
const setApplication = useCallback((application) => {
if (application.isDefault) return;
dispatch(addApplication(application));
}, []);

const setApplications = (apps) => {
dispatch(setApps(apps));
};

const getApplicationByChainId = useCallback(
(chainId) => applications.find((app) => app.chainID === chainId),
[applications],
);

const deleteApplicationByChainId = useCallback(
(chainId) => {
dispatch(deleteApplication(chainId));
if (currentApplication.chainID === chainId) {
// Set Lisk as default if application in use is being deleted
setCurrentApplication(defaultApplications.data[0]);
}
},
[],
[applications]
);

const deleteApplicationByChainId = useCallback((chainId) => {
if (currentApplication.isDefault) return;

dispatch(deleteApplication(chainId));
if (currentApplication.chainID === chainId) {
// Set Lisk as default if application in use is being deleted
setCurrentApplication(defaultApplications[0]);
}
}, []);

return {
applications, setApplication, getApplicationByChainId, deleteApplicationByChainId,
applications,
setApplication,
setApplications,
getApplicationByChainId,
deleteApplicationByChainId,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import flushPromises from '@tests/unit-test-utils/flushPromises';
import { queryWrapper as wrapper } from 'src/utils/test/queryWrapper';
import actionTypes from '../store/actionTypes';
import { useApplicationManagement } from './useApplicationManagement';
import { useCurrentApplication } from './useCurrentApplication';

jest.mock('./queries/useBlockchainApplicationMeta', () => ({
useBlockchainApplicationMeta: jest.fn(() => ({
Expand All @@ -25,24 +26,22 @@ jest.mock('react-redux', () => ({
useSelector: jest.fn().mockImplementation((fn) => fn(mockState)),
useDispatch: () => mockDispatch,
}));
jest.mock('./useCurrentApplication', () => ({
useCurrentApplication: jest.fn(() => (
[mockCurrentApplication, mockSetApplication]
)),
}));
jest.mock('./useCurrentApplication');

describe('useApplicationManagement hook', () => {
beforeEach(() => {
mockDispatch.mockClear();
});

useCurrentApplication.mockImplementation(() => [mockCurrentApplication, mockSetApplication]);

const { result } = renderHook(() => useApplicationManagement(), { wrapper });

it('setApplication should dispatch an action', () => {
const { setApplication } = result.current;
const expectedAction = {
type: actionTypes.addApplicationByChainId,
application: mockApplications[3],
app: mockApplications[3],
};
act(() => {
setApplication(mockApplications[3]);
Expand All @@ -51,6 +50,19 @@ describe('useApplicationManagement hook', () => {
expect(mockDispatch).toHaveBeenCalledWith(expectedAction);
});

it('setApplications should dispatch an action', () => {
const { setApplications } = result.current;
const expectedAction = {
type: actionTypes.setApplications,
apps: mockApplications,
};
act(() => {
setApplications(mockApplications);
});
expect(mockDispatch).toHaveBeenCalledTimes(1);
expect(mockDispatch).toHaveBeenCalledWith(expectedAction);
});

it('setApplication should not dispatch an action while adding default application', () => {
const { setApplication } = result.current;
act(() => {
Expand Down Expand Up @@ -98,4 +110,21 @@ describe('useApplicationManagement hook', () => {
expect(mockSetApplication).toHaveBeenCalledTimes(1);
expect(mockSetApplication).toHaveBeenCalledWith(mockApplications[0]);
});

it('deleteApplicationByChainId should not dispatch an action if application is default', async () => {
jest.clearAllMocks();
useCurrentApplication.mockImplementation(() => [mockApplications[1], mockSetApplication]);

const {
result: { current },
} = renderHook(() => useApplicationManagement(), { wrapper });

const { deleteApplicationByChainId } = current;
act(() => {
deleteApplicationByChainId(mockCurrentApplication.chainID);
});
await flushPromises();
expect(mockDispatch).not.toHaveBeenCalledTimes(1);
expect(mockSetApplication).not.toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,13 @@ export function useCurrentApplication() {

const { setCurrentNode } = useCurrentNode();

const setApplication = useCallback(
(application) => {
dispatch(setCurrentApplication(application));
/* istanbul ignore next */
client.create(application.serviceURLs[0]);
setCurrentNode(application.serviceURLs[0]);
},
[],
);
const setApplication = useCallback((application) => {
dispatch(setCurrentApplication(application));
// @TODO: probe to verify if serviceURL is reachable
/* istanbul ignore next */
client.create(application.serviceURLs[0]);
setCurrentNode(application.serviceURLs[0]);
}, []);

return [currentApplication ?? {}, setApplication];
}
Loading