diff --git a/suite-common/icons/generateIconFont.ts b/suite-common/icons/generateIconFont.ts index 92331abac8c..3d109e628a9 100644 --- a/suite-common/icons/generateIconFont.ts +++ b/suite-common/icons/generateIconFont.ts @@ -18,6 +18,7 @@ const usedIcons = [ 'arrowLineUpRight', 'arrowRight', 'arrowsCounterClockwise', + 'arrowsLeftRight', 'arrowSquareOut', 'arrowUp', 'arrowUpRight', diff --git a/suite-common/icons/iconFontsMobile/TrezorSuiteIcons.json b/suite-common/icons/iconFontsMobile/TrezorSuiteIcons.json index aa54fd7a014..5bc84d79bd8 100644 --- a/suite-common/icons/iconFontsMobile/TrezorSuiteIcons.json +++ b/suite-common/icons/iconFontsMobile/TrezorSuiteIcons.json @@ -89,14 +89,15 @@ "bugBeetle": 61784, "bookmarkSimple": 61785, "backspace": 61786, - "arrowsCounterClockwise": 61787, - "arrowUpRight": 61788, - "arrowUp": 61789, - "arrowURightDown": 61790, - "arrowSquareOut": 61791, - "arrowRight": 61792, - "arrowLineUpRight": 61793, - "arrowLineUp": 61794, - "arrowLineDown": 61795, - "arrowDown": 61796 + "arrowsLeftRight": 61787, + "arrowsCounterClockwise": 61788, + "arrowUpRight": 61789, + "arrowUp": 61790, + "arrowURightDown": 61791, + "arrowSquareOut": 61792, + "arrowRight": 61793, + "arrowLineUpRight": 61794, + "arrowLineUp": 61795, + "arrowLineDown": 61796, + "arrowDown": 61797 } diff --git a/suite-common/icons/iconFontsMobile/TrezorSuiteIcons.ttf b/suite-common/icons/iconFontsMobile/TrezorSuiteIcons.ttf index 0077f90e0d2..8002f2c04e4 100644 Binary files a/suite-common/icons/iconFontsMobile/TrezorSuiteIcons.ttf and b/suite-common/icons/iconFontsMobile/TrezorSuiteIcons.ttf differ diff --git a/suite-native/app/package.json b/suite-native/app/package.json index 1d07e01300c..878a8ace124 100644 --- a/suite-native/app/package.json +++ b/suite-native/app/package.json @@ -69,6 +69,7 @@ "@suite-native/module-send": "workspace:*", "@suite-native/module-settings": "workspace:*", "@suite-native/module-staking-management": "workspace:*", + "@suite-native/module-trading": "workspace:*", "@suite-native/navigation": "workspace:*", "@suite-native/notifications": "workspace:*", "@suite-native/receive": "workspace:*", diff --git a/suite-native/app/src/navigation/AppTabNavigator.tsx b/suite-native/app/src/navigation/AppTabNavigator.tsx index 1bd9e2f9c60..6de1e06761b 100644 --- a/suite-native/app/src/navigation/AppTabNavigator.tsx +++ b/suite-native/app/src/navigation/AppTabNavigator.tsx @@ -1,3 +1,5 @@ +import { useSelector } from 'react-redux'; + import { BottomTabBarProps, createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { HomeStackNavigator } from '@suite-native/module-home'; @@ -5,6 +7,8 @@ import { AccountsStackNavigator } from '@suite-native/module-accounts-management import { SettingsScreen } from '@suite-native/module-settings'; import { AppTabsParamList, AppTabsRoutes, TabBar } from '@suite-native/navigation'; import { useHandleDeviceRequestsPassphrase } from '@suite-native/device-authorization'; +import { createSelectIsFeatureFlagEnabled, FeatureFlag } from '@suite-native/feature-flags'; +import { TradingStackNavigator } from '@suite-native/module-trading'; import { rootTabsOptions } from './routes'; @@ -13,6 +17,10 @@ const Tab = createBottomTabNavigator(); export const AppTabNavigator = () => { useHandleDeviceRequestsPassphrase(); + const isTradingEnabled = useSelector( + createSelectIsFeatureFlagEnabled(FeatureFlag.IsTradingEnabled), + ); + return ( { > + {isTradingEnabled && ( + + )} ); diff --git a/suite-native/app/src/navigation/__tests__/AppTabNavigator.comp.test.tsx b/suite-native/app/src/navigation/__tests__/AppTabNavigator.comp.test.tsx index 8823937a633..10174cd308e 100644 --- a/suite-native/app/src/navigation/__tests__/AppTabNavigator.comp.test.tsx +++ b/suite-native/app/src/navigation/__tests__/AppTabNavigator.comp.test.tsx @@ -1,14 +1,46 @@ -import { renderWithStore, waitFor } from '@suite-native/test-utils'; +import { renderWithStore, waitFor, PreloadedState, fireEvent } from '@suite-native/test-utils'; +import { FeatureFlag, featureFlagsInitialState } from '@suite-native/feature-flags'; import { AppTabNavigator } from '../AppTabNavigator'; describe('AppTabNavigator', () => { + const renderTabs = async (preloadedState?: PreloadedState) => { + const result = renderWithStore(, { preloadedState }); + await waitFor(() => expect(result.getByText('Home')).toBeDefined()); + + return result; + }; + it('should render 3 buttons', async () => { - const { getByText } = renderWithStore(); - await waitFor(() => expect(getByText('Home')).toBeDefined()); + const { getByText } = await renderTabs(); expect(getByText('Home')).toBeDefined(); expect(getByText('My assets')).toBeDefined(); expect(getByText('Settings')).toBeDefined(); }); + + it('should not render Trade tab when IsTradingEnabled is false', async () => { + const { queryByText } = await renderTabs({ + featureFlags: { + ...featureFlagsInitialState, + [FeatureFlag.IsTradingEnabled]: false, + }, + }); + + expect(queryByText('Trade')).toBe(null); + }); + + it('should render Trade tab when IsTradingEnabled is true', async () => { + const { getByText } = await renderTabs({ + featureFlags: { + ...featureFlagsInitialState, + [FeatureFlag.IsTradingEnabled]: true, + }, + }); + + const tradeTab = getByText('Trade'); + fireEvent.press(tradeTab); + + expect(getByText('Trading placeholder')).toBeDefined(); + }); }); diff --git a/suite-native/app/src/navigation/routes.ts b/suite-native/app/src/navigation/routes.ts index 81cc1deb0e8..727ceebbbe2 100644 --- a/suite-native/app/src/navigation/routes.ts +++ b/suite-native/app/src/navigation/routes.ts @@ -19,6 +19,13 @@ const accountsStack = enhanceTabOption({ }, }); +const tradeStack = enhanceTabOption({ + routeName: AppTabsRoutes.TradeStack, + iconName: 'arrowsLeftRight', + focusedIconName: 'arrowsLeftRight', + label: 'Trade', +}); + const settings = enhanceTabOption({ routeName: AppTabsRoutes.Settings, iconName: 'gear', @@ -29,5 +36,6 @@ const settings = enhanceTabOption({ export const rootTabsOptions = { ...homeStack, ...accountsStack, + ...tradeStack, ...settings, }; diff --git a/suite-native/app/tsconfig.json b/suite-native/app/tsconfig.json index 6f57b258499..1012708196f 100644 --- a/suite-native/app/tsconfig.json +++ b/suite-native/app/tsconfig.json @@ -60,6 +60,7 @@ { "path": "../module-staking-management" }, + { "path": "../module-trading" }, { "path": "../navigation" }, { "path": "../notifications" }, { "path": "../receive" }, diff --git a/suite-native/module-trading/package.json b/suite-native/module-trading/package.json new file mode 100644 index 00000000000..25182f5e1e3 --- /dev/null +++ b/suite-native/module-trading/package.json @@ -0,0 +1,21 @@ +{ + "name": "@suite-native/module-trading", + "version": "1.0.0", + "private": true, + "license": "See LICENSE.md in repo root", + "sideEffects": false, + "main": "src/index", + "scripts": { + "depcheck": "yarn g:depcheck", + "type-check": "yarn g:tsc --build", + "test:unit": "yarn g:jest -c ../../jest.config.native.js" + }, + "dependencies": { + "@react-navigation/native-stack": "6.11.0", + "@reduxjs/toolkit": "1.9.5", + "@suite-native/navigation": "workspace:*", + "@suite-native/test-utils": "workspace:*", + "react": "18.2.0", + "react-native": "0.76.1" + } +} diff --git a/suite-native/module-trading/redux.d.ts b/suite-native/module-trading/redux.d.ts new file mode 100644 index 00000000000..df9a0c3f969 --- /dev/null +++ b/suite-native/module-trading/redux.d.ts @@ -0,0 +1,7 @@ +import { AsyncThunkAction } from '@reduxjs/toolkit'; + +declare module 'redux' { + export interface Dispatch { + >(thunk: TThunk): ReturnType; + } +} diff --git a/suite-native/module-trading/src/index.ts b/suite-native/module-trading/src/index.ts new file mode 100644 index 00000000000..4000905f8f1 --- /dev/null +++ b/suite-native/module-trading/src/index.ts @@ -0,0 +1 @@ +export * from './navigation/TradingStackNavigator'; diff --git a/suite-native/module-trading/src/navigation/TradingStackNavigator.tsx b/suite-native/module-trading/src/navigation/TradingStackNavigator.tsx new file mode 100644 index 00000000000..0d7be891a0f --- /dev/null +++ b/suite-native/module-trading/src/navigation/TradingStackNavigator.tsx @@ -0,0 +1,24 @@ +import { createNativeStackNavigator } from '@react-navigation/native-stack'; + +import { + stackNavigationOptionsConfig, + TradingStackParamList, + TradingStackRoutes, +} from '@suite-native/navigation'; + +import { TradingScreen } from '../screens/TradingScreen'; + +const TradingStack = createNativeStackNavigator(); + +export const TradingStackNavigator = () => ( + + + +); diff --git a/suite-native/module-trading/src/navigation/__tests__/TradingStackNavigator.comp.test.tsx b/suite-native/module-trading/src/navigation/__tests__/TradingStackNavigator.comp.test.tsx new file mode 100644 index 00000000000..7f300814a22 --- /dev/null +++ b/suite-native/module-trading/src/navigation/__tests__/TradingStackNavigator.comp.test.tsx @@ -0,0 +1,10 @@ +import { renderWithStore, waitFor } from '@suite-native/test-utils'; + +import { TradingStackNavigator } from '../TradingStackNavigator'; + +describe('TradingStackNavigator', () => { + it('should render', async () => { + const { getByText } = renderWithStore(); + await waitFor(() => expect(getByText('Trading placeholder')).toBeDefined()); + }); +}); diff --git a/suite-native/module-trading/src/screens/TradingScreen.tsx b/suite-native/module-trading/src/screens/TradingScreen.tsx new file mode 100644 index 00000000000..7c6c8996fec --- /dev/null +++ b/suite-native/module-trading/src/screens/TradingScreen.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +import { Screen } from '@suite-native/navigation'; +import { Card, Text } from '@suite-native/atoms'; +import { DeviceManagerScreenHeader } from '@suite-native/device-manager'; + +export const TradingScreen = () => ( + }> + + Trading placeholder + + +); diff --git a/suite-native/module-trading/tsconfig.json b/suite-native/module-trading/tsconfig.json new file mode 100644 index 00000000000..ca53899b029 --- /dev/null +++ b/suite-native/module-trading/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { "outDir": "libDev" }, + "references": [ + { "path": "../navigation" }, + { "path": "../test-utils" } + ], + "include": [".", "**/*.json"] +} diff --git a/suite-native/navigation/src/navigators.ts b/suite-native/navigation/src/navigators.ts index 5689ce9bd15..d0330c26832 100644 --- a/suite-native/navigation/src/navigators.ts +++ b/suite-native/navigation/src/navigators.ts @@ -28,6 +28,7 @@ import { RootStackRoutes, SendStackRoutes, SettingsStackRoutes, + TradingStackRoutes, } from './routes'; import { NavigateParameters } from './types'; @@ -102,6 +103,7 @@ export type SendStackParamList = { export type AppTabsParamList = { [AppTabsRoutes.HomeStack]: NavigatorScreenParams; [AppTabsRoutes.AccountsStack]: NavigatorScreenParams; + [AppTabsRoutes.TradeStack]: NavigatorScreenParams; [AppTabsRoutes.Settings]: undefined; }; @@ -222,3 +224,7 @@ export type RootStackParamList = { }; [RootStackRoutes.SettingsScreenStack]: NavigatorScreenParams; }; + +export type TradingStackParamList = { + [TradingStackRoutes.Trading]: undefined; +}; diff --git a/suite-native/navigation/src/routes.ts b/suite-native/navigation/src/routes.ts index b720e01d290..4fef1ea46d4 100644 --- a/suite-native/navigation/src/routes.ts +++ b/suite-native/navigation/src/routes.ts @@ -21,6 +21,7 @@ export enum RootStackRoutes { export enum AppTabsRoutes { HomeStack = 'HomeStack', AccountsStack = 'AccountsStack', + TradeStack = 'TradeStack', Settings = 'Settings', } @@ -124,3 +125,7 @@ export enum SettingsStackRoutes { SettingsFAQ = 'SettingsFAQ', SettingsCoinEnabling = 'SettingsCoinEnabling', } + +export enum TradingStackRoutes { + Trading = 'Trading', +} diff --git a/suite-native/state/src/index.ts b/suite-native/state/src/index.ts index 5b121d4634d..65d3a26c9bd 100644 --- a/suite-native/state/src/index.ts +++ b/suite-native/state/src/index.ts @@ -1,2 +1,3 @@ export * from './StoreProvider'; export * from './appSlice'; +export * from './store'; diff --git a/yarn.lock b/yarn.lock index fce04a7a7a7..e9b2ffaf490 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9996,6 +9996,7 @@ __metadata: "@suite-native/module-send": "workspace:*" "@suite-native/module-settings": "workspace:*" "@suite-native/module-staking-management": "workspace:*" + "@suite-native/module-trading": "workspace:*" "@suite-native/navigation": "workspace:*" "@suite-native/notifications": "workspace:*" "@suite-native/receive": "workspace:*" @@ -10950,17 +10951,16 @@ __metadata: languageName: unknown linkType: soft -"@suite-native/module-trading@workspace:suite-native/module-trading": +"@suite-native/module-trading@workspace:*, @suite-native/module-trading@workspace:suite-native/module-trading": version: 0.0.0-use.local resolution: "@suite-native/module-trading@workspace:suite-native/module-trading" dependencies: "@react-navigation/native-stack": "npm:6.11.0" "@reduxjs/toolkit": "npm:1.9.5" "@suite-native/navigation": "workspace:*" - "@trezor/styles": "workspace:*" + "@suite-native/test-utils": "workspace:*" react: "npm:18.2.0" react-native: "npm:0.76.1" - react-native-reanimated: "npm:^3.16.7" languageName: unknown linkType: soft