diff --git a/packages/yoroi-extension/app/Routes.js b/packages/yoroi-extension/app/Routes.js index bc4672a430..533f7c3dda 100644 --- a/packages/yoroi-extension/app/Routes.js +++ b/packages/yoroi-extension/app/Routes.js @@ -227,7 +227,6 @@ export const Routes = ( ) )} /> - ( @@ -237,7 +236,6 @@ export const Routes = ( ) )} /> - } /> + } + /> @@ -352,6 +355,11 @@ const WalletsSubpages = (stores, actions) => ( path={ROUTES.WALLETS.CATALYST_VOTING} component={(props) => } /> + } + /> ); diff --git a/packages/yoroi-extension/app/api/localStorage/index.js b/packages/yoroi-extension/app/api/localStorage/index.js index 61a38ad860..d4ba8a788f 100644 --- a/packages/yoroi-extension/app/api/localStorage/index.js +++ b/packages/yoroi-extension/app/api/localStorage/index.js @@ -37,6 +37,7 @@ const storageKeys = { CATALYST_ROUND_INFO: networkForLocalStorage + '-CATALYST_ROUND_INFO', // ========== CONNECTOR ========== // ERGO_CONNECTOR_WHITELIST: 'connector_whitelist', + SELECTED_WALLET: 'SELECTED_WALLET', }; export type SetCustomUserThemeRequest = {| @@ -118,6 +119,19 @@ export default class LocalStorageApi { unsetUserTheme: void => Promise = () => removeLocalItem(storageKeys.THEME); + // ========== Select Wallet ========== // + + getSelectedWalletId: void => number | null = () => { + const id = localStorage.getItem(storageKeys.SELECTED_WALLET); + if (!id) return null + if (isNaN(Number(id))) throw new Error(`Invalid wallet Id: ${id}`); + return Number(id) + } + + setSelectedWalletId: number => void = (id) => { + localStorage.setItem(storageKeys.SELECTED_WALLET, id.toString()) + } + // ========== Custom User Theme ========== // getCustomUserTheme: void => Promise = () => getLocalItem(storageKeys.CUSTOM_THEME); diff --git a/packages/yoroi-extension/app/components/common/TextField.js b/packages/yoroi-extension/app/components/common/TextField.js index 7900d6a2b4..2456380262 100644 --- a/packages/yoroi-extension/app/components/common/TextField.js +++ b/packages/yoroi-extension/app/components/common/TextField.js @@ -76,6 +76,7 @@ function TextField({ theme.name === 'classic' ? { shrink: true, ...InputLabelProps } : { ...InputLabelProps } } InputProps={{ + disableUnderline: revamp, ...(theme.name === 'classic' ? { notched: false } : {}), endAdornment: type === 'password' ? ( @@ -112,7 +113,6 @@ function TextField({ {Boolean(error) === true ? : done === true ? : null} ), - disableUnderline: revamp, placeholder: placeholder != null ? placeholder : '', }} {...props} diff --git a/packages/yoroi-extension/app/components/settings/categories/general-setting/ThemeSettingsBlock.js b/packages/yoroi-extension/app/components/settings/categories/general-setting/ThemeSettingsBlock.js index a731289b31..f34bd334cf 100644 --- a/packages/yoroi-extension/app/components/settings/categories/general-setting/ThemeSettingsBlock.js +++ b/packages/yoroi-extension/app/components/settings/categories/general-setting/ThemeSettingsBlock.js @@ -73,9 +73,8 @@ const messages = defineMessages({ type Props = {| +currentTheme: Theme, - +selectTheme: ({| theme: string |}) => PossiblyAsync, + +onSubmit: (theme: string) => PossiblyAsync, +onExternalLinkClick: MouseEvent => void, - +switchToFirstWallet: void => void, |}; const NEW_THEME = THEMES.YOROI_REVAMP @@ -89,7 +88,7 @@ export default class ThemeSettingsBlock extends Component { render(): Node { const { currentTheme, - selectTheme, + onSubmit, onExternalLinkClick, } = this.props; const { intl } = this.context; @@ -121,7 +120,7 @@ export default class ThemeSettingsBlock extends Component { value={currentTheme === NEW_THEME ? NEW_THEME : OLD_THEME} onChange={(e) => { const theme = e.target.value === NEW_THEME ? NEW_THEME : THEMES.YOROI_MODERN - selectTheme({ theme }) + onSubmit(theme) }} sx={{ display: 'flex', @@ -177,7 +176,7 @@ export default class ThemeSettingsBlock extends Component { row value={currentTheme} onChange={(e) => { - selectTheme({ theme: e.target.value }) + onSubmit(e.target.value) }} > diff --git a/packages/yoroi-extension/app/components/wallet/transactions/TransactionRevamp.js b/packages/yoroi-extension/app/components/wallet/transactions/TransactionRevamp.js index 0cc8fcd40e..a7fefda7a9 100644 --- a/packages/yoroi-extension/app/components/wallet/transactions/TransactionRevamp.js +++ b/packages/yoroi-extension/app/components/wallet/transactions/TransactionRevamp.js @@ -471,7 +471,7 @@ export default class TransactionRevamp extends Component { {this.generateAddressButton(request.address.address)} - + {renderAmount(request.address.value.getDefaultEntry())} {request.address.value.nonDefaultEntries().map(entry => ( diff --git a/packages/yoroi-extension/app/containers/settings/categories/GeneralSettingsPage.js b/packages/yoroi-extension/app/containers/settings/categories/GeneralSettingsPage.js index 266bdf8b7c..53bdd3c9d0 100644 --- a/packages/yoroi-extension/app/containers/settings/categories/GeneralSettingsPage.js +++ b/packages/yoroi-extension/app/containers/settings/categories/GeneralSettingsPage.js @@ -12,6 +12,7 @@ import AboutYoroiSettingsBlock from '../../../components/settings/categories/gen import UnitOfAccountSettings from '../../../components/settings/categories/general-setting/UnitOfAccountSettings'; import LocalizableError from '../../../i18n/LocalizableError'; import type { LanguageType } from '../../../i18n/translations'; +import { THEMES } from '../../../styles/utils'; import type { Theme } from '../../../styles/utils'; import { PublicDeriver } from '../../../api/ada/lib/storage/models/PublicDeriver'; import { ReactComponent as AdaCurrency } from '../../../assets/images/currencies/ADA.inline.svg'; @@ -64,22 +65,6 @@ export default class GeneralSettingsPage extends Component) => void = publicDeriver => { - this.generated.actions.router.goToRoute.trigger({ - route: this.generated.stores.app.currentRoute, - publicDeriver, - }); - }; - - handleSwitchToFirstWallet: void => void = () => { - const selectedWallet = this.generated.stores.wallets.selected; - if (selectedWallet == null) { - const wallets = this.generated.stores.wallets.publicDerivers; - const firstWallet = wallets[0]; - this.switchWallet(firstWallet); - } - }; - onSelectUnitOfAccount: string => Promise = async (value) => { const unitOfAccount = (value === 'ADA') ? unitOfAccountDisabledValue @@ -143,8 +128,21 @@ export default class GeneralSettingsPage extends Component { + if (theme === THEMES.YOROI_REVAMP) { + const { wallets } = this.generated.stores; + const publicDeriver = wallets.selected; + const publicDerivers = wallets.publicDerivers; + + if (publicDeriver == null && publicDerivers.length !== 0) { + const lastSelectedWallet = wallets.getLastSelectedWallet(); + this.generated.actions.wallets.setActiveWallet.trigger({ + wallet: lastSelectedWallet ?? publicDerivers[0], + }); + } + } + this.generated.actions.profile.updateTheme.trigger({ theme }) + }} onExternalLinkClick={handleExternalLinkClick} /> void, |}, |}, + wallets: {| + setActiveWallet: {| + trigger: (params: {| + wallet: PublicDeriver<>, + |}) => void, + |}, + |}, |}, stores: {| app: {| currentRoute: string |}, @@ -206,6 +211,7 @@ export default class GeneralSettingsPage extends Component, publicDerivers: Array>, + getLastSelectedWallet: void => ?PublicDeriver<>, |}, coinPriceStore: {| getCurrentPrice: ( @@ -248,6 +254,7 @@ export default class GeneralSettingsPage extends Component { render(): Node { const sidebarContainer = ; - - const menu = ( - ({ - className: category.className, - label: this.context.intl.formatMessage(category.label), - route: category.route, - }))} - onItemClick={route => this.generated.actions.router.goToRoute.trigger({ route })} - isActiveItem={route => this.generated.stores.app.currentRoute.startsWith(route)} - /> - ); - const navbarClassic = ( + const navbar = ( { } /> ); - - const navbarRevamp = ( - - } - menu={menu} - /> - ); - - const navbar = this.props.renderLayoutComponent({ - CLASSIC: navbarClassic, - REVAMP: navbarRevamp, - }); - - return ( + const content = this.getContent(); + const transferClassic = ( } navbar={navbar} sidebar={sidebarContainer} showInContainer > - {this.getContent()} + {content} ); + + return this.props.renderLayoutComponent({ + CLASSIC: transferClassic, + REVAMP: content, + }) } getContent: void => Node = () => { diff --git a/packages/yoroi-extension/app/containers/wallet/MyWalletsPage.js b/packages/yoroi-extension/app/containers/wallet/MyWalletsPage.js index cab2a26524..69f0e2b27d 100644 --- a/packages/yoroi-extension/app/containers/wallet/MyWalletsPage.js +++ b/packages/yoroi-extension/app/containers/wallet/MyWalletsPage.js @@ -42,16 +42,16 @@ import { genLookupOrFail, getTokenName } from '../../stores/stateless/tokenHelpe import { getReceiveAddress } from '../../stores/stateless/addressStores'; import BuySellDialog from '../../components/buySell/BuySellDialog'; import type { WalletInfo } from '../../components/buySell/BuySellDialog'; -import { addressToDisplayString } from '../../api/ada/lib/storage/bridge/utils' -import { networks } from '../../api/ada/lib/storage/database/prepackaged/networks' -import NavBarRevamp from '../../components/topbar/NavBarRevamp' -import { withLayout } from '../../styles/context/layout' -import type { LayoutComponentMap } from '../../styles/context/layout' -import { Box } from '@mui/system' +import { addressToDisplayString } from '../../api/ada/lib/storage/bridge/utils'; +import { networks } from '../../api/ada/lib/storage/database/prepackaged/networks'; +import NavBarRevamp from '../../components/topbar/NavBarRevamp'; +import { withLayout } from '../../styles/context/layout'; +import type { LayoutComponentMap } from '../../styles/context/layout'; +import { Box } from '@mui/system'; export type GeneratedData = typeof MyWalletsPage.prototype.generated; -type Props = InjectedOrGenerated +type Props = InjectedOrGenerated; type InjectedProps = {| +renderLayoutComponent: LayoutComponentMap => Node |}; type AllProps = {| ...Props, ...InjectedProps |}; @@ -76,7 +76,14 @@ class MyWalletsPage extends Component { } componentDidMount () { - this.generated.actions.wallets.unselectWallet.trigger(); + const isRevamp = this.generated.stores.profile.isRevampTheme; + if (isRevamp) { + this.generated.actions.router.goToRoute.trigger({ + route: ROUTES.WALLETS.ROOT, + }); + } else { + this.generated.actions.wallets.unselectWallet.trigger(); + } } handleWalletNavItemClick: PublicDeriver<> => void = ( @@ -105,13 +112,10 @@ class MyWalletsPage extends Component { const { uiDialogs } = stores; const sidebarContainer = - const wallets = this.generated.stores.wallets.publicDerivers; - const navbarTitle = ( ); - const navbarElementClassic = ( { |}, wallets: {| unselectWallet: {| trigger: (params: void) => void |}, - setActiveWallet: {| trigger: (params: {| wallet: PublicDeriver<> |}) => void |}, + setActiveWallet: {| trigger: (params: {| + wallet: PublicDeriver<>, + |}) => void |}, |}, |}, stores: {| - profile: {| shouldHideBalance: boolean |}, + profile: {| + shouldHideBalance: boolean, + isRevampTheme: boolean, + |}, uiDialogs: {| isOpen: any => boolean |}, @@ -462,6 +471,7 @@ class MyWalletsPage extends Component { stores: { profile: { shouldHideBalance: stores.profile.shouldHideBalance, + isRevampTheme: stores.profile.isRevampTheme, }, uiDialogs: { isOpen: stores.uiDialogs.isOpen, diff --git a/packages/yoroi-extension/app/containers/wallet/MyWalletsPage.stories.js b/packages/yoroi-extension/app/containers/wallet/MyWalletsPage.stories.js index be829b9c0e..f1536829ca 100644 --- a/packages/yoroi-extension/app/containers/wallet/MyWalletsPage.stories.js +++ b/packages/yoroi-extension/app/containers/wallet/MyWalletsPage.stories.js @@ -98,6 +98,7 @@ const genBaseProps: {| stores: { profile: { shouldHideBalance: false, + isRevampTheme: false, }, wallets: { publicDerivers: request.publicDerivers, diff --git a/packages/yoroi-extension/app/containers/wallet/NFTsWrapper.js b/packages/yoroi-extension/app/containers/wallet/NFTsWrapper.js index 6fd96c29f1..abe8f46c6a 100644 --- a/packages/yoroi-extension/app/containers/wallet/NFTsWrapper.js +++ b/packages/yoroi-extension/app/containers/wallet/NFTsWrapper.js @@ -79,7 +79,6 @@ export default class NFTsWrapper extends Component { BannerContainerProps: InjectedOrGenerated, SidebarContainerProps: InjectedOrGenerated, NavBarContainerRevampProps: InjectedOrGenerated, - stores: {| router: {| location: any |}, tokenInfoStore: {| diff --git a/packages/yoroi-extension/app/containers/wallet/Wallet.js b/packages/yoroi-extension/app/containers/wallet/Wallet.js index df28adf8b8..bf4797a04f 100644 --- a/packages/yoroi-extension/app/containers/wallet/Wallet.js +++ b/packages/yoroi-extension/app/containers/wallet/Wallet.js @@ -30,8 +30,6 @@ import NavBarTitle from '../../components/topbar/NavBarTitle'; import SubMenu from '../../components/topbar/SubMenu'; import type { GeneratedData as NavBarContainerRevampData } from '../NavBarContainerRevamp'; import WalletSyncingOverlay from '../../components/wallet/syncingOverlay/WalletSyncingOverlay'; -import { THEMES } from '../../styles/utils'; -import type { Theme } from '../../styles/utils'; export type GeneratedData = typeof Wallet.prototype.generated; @@ -56,6 +54,18 @@ class Wallet extends Component { }; componentDidMount() { + const { wallets } = this.generated.stores; + const publicDeriver = wallets.selected; + const publicDerivers = wallets.publicDerivers; + const isRevamp = this.generated.stores.profile.isRevampTheme; + + if (publicDeriver == null && isRevamp && publicDerivers.length !== 0) { + const lastSelectedWallet = wallets.getLastSelectedWallet(); + this.generated.actions.wallets.setActiveWallet.trigger({ + wallet: lastSelectedWallet ?? publicDerivers[0], + }); + } + // reroute to the default path for the wallet const newRoute = this.checkRoute(); if (newRoute != null) { @@ -66,16 +76,13 @@ class Wallet extends Component { } checkRoute(): void | string { - let categories; - if (this.generated.stores.profile.currentTheme === THEMES.YOROI_REVAMP) { - categories = allCategories.filter(c => c.route !== ROUTES.WALLETS.DELEGATION_DASHBOARD); - } else { - categories = allCategories; - } + const isRevamp = this.generated.stores.profile.isRevampTheme; + const categories = isRevamp ? allSubcategoriesRevamp : allCategories; + // void -> this route is fine for this wallet type // string -> what you should be redirected to const publicDeriver = this.generated.stores.wallets.selected; - if (publicDeriver == null) throw new Error(`${nameof(Wallet)} no public deriver`); + if (publicDeriver == null) return; const spendableBalance = this.generated.stores.transactions.getBalanceRequest.result; const walletHasAssets = !!(spendableBalance?.nonDefaultEntries().length); @@ -96,7 +103,6 @@ class Wallet extends Component { } return firstValidCategory.route; } - return undefined; } navigateToMyWallets: string => void = destination => { @@ -110,7 +116,7 @@ class Wallet extends Component { if (this.generated.stores.wallets.firstSync === publicDeriver.getPublicDeriverId()) { return ( this.navigateToMyWallets(ROUTES.MY_WALLETS)} /> ) @@ -255,6 +261,13 @@ class Wallet extends Component { |}) => void, |}, |}, + wallets: {| + setActiveWallet: {| + trigger: (params: {| + wallet: PublicDeriver<>, + |}) => void, + |}, + |}, |}, stores: {| app: {| currentRoute: string |}, @@ -263,7 +276,9 @@ class Wallet extends Component { |}, wallets: {| selected: null | PublicDeriver<>, + publicDerivers: Array>, firstSync: ?number, + getLastSelectedWallet: void => ?PublicDeriver<>, |}, router: {| location: any |}, transactions: {| @@ -272,7 +287,8 @@ class Wallet extends Component { |}, |}, profile: {| - currentTheme: Theme, + isRevampTheme: boolean, + isClassicTheme: boolean, |}, |} |} { @@ -291,7 +307,9 @@ class Wallet extends Component { }, wallets: { selected: stores.wallets.selected, - firstSync: stores.wallets.firstSync + publicDerivers: stores.wallets.publicDerivers, + firstSync: stores.wallets.firstSync, + getLastSelectedWallet: stores.wallets.getLastSelectedWallet, }, walletSettings: { getWalletWarnings: settingStore.getWalletWarnings, @@ -312,7 +330,8 @@ class Wallet extends Component { })(), }, profile: { - currentTheme: stores.profile.currentTheme, + isRevampTheme: stores.profile.isRevampTheme, + isClassicTheme: stores.profile.isClassicTheme, } }, actions: { @@ -320,6 +339,9 @@ class Wallet extends Component { goToRoute: { trigger: actions.router.goToRoute.trigger }, redirect: { trigger: actions.router.redirect.trigger }, }, + wallets: { + setActiveWallet: { trigger: actions.wallets.setActiveWallet.trigger }, + } }, SidebarContainerProps: ({ actions, stores }: InjectedOrGenerated), NavBarContainerProps: ({ actions, stores }: InjectedOrGenerated), diff --git a/packages/yoroi-extension/app/containers/wallet/Wallet.mock.js b/packages/yoroi-extension/app/containers/wallet/Wallet.mock.js index ec2485738e..7bda93871b 100644 --- a/packages/yoroi-extension/app/containers/wallet/Wallet.mock.js +++ b/packages/yoroi-extension/app/containers/wallet/Wallet.mock.js @@ -13,7 +13,6 @@ import WalletStore from '../../stores/toplevel/WalletStore'; import type { GeneratedData } from './Wallet'; import { mockFromDefaults, getDefaultEntryTokenInfo, } from '../../stores/toplevel/TokenInfoStore'; import { defaultAssets, } from '../../api/ada/lib/storage/database/prepackaged/networks'; -import { THEMES } from '../../styles/utils'; export const mockWalletProps: { selected: null | PublicDeriver<>, @@ -37,11 +36,14 @@ export const mockWalletProps: { currentRoute: request.location, }, profile: { - currentTheme: THEMES.YOROI_CLASSIC, + isRevampTheme: false, + isClassicTheme: false, }, wallets: { selected: request.selected, + publicDerivers: request.selected ? [request.selected] : [], firstSync: null, + getLastSelectedWallet: () => request.selected, }, transactions: { getBalanceRequest: { @@ -62,6 +64,9 @@ export const mockWalletProps: { goToRoute: { trigger: action('goToRoute') }, redirect: { trigger: action('redirect') }, }, + wallets: { + setActiveWallet: { trigger: action('setActiveWallet') } + } }, SidebarContainerProps: { generated: { diff --git a/packages/yoroi-extension/app/containers/wallet/voting/VotingPage.js b/packages/yoroi-extension/app/containers/wallet/voting/VotingPage.js index 845ff26264..db98eb5096 100644 --- a/packages/yoroi-extension/app/containers/wallet/voting/VotingPage.js +++ b/packages/yoroi-extension/app/containers/wallet/voting/VotingPage.js @@ -1,5 +1,5 @@ // @flow -import type { Node } from 'react'; +import type { Node, ComponentType } from 'react'; import { Component } from 'react'; import { observer } from 'mobx-react'; import { computed } from 'mobx'; @@ -29,10 +29,24 @@ import { isTrezorTWallet, } from '../../../api/ada/lib/storage/models/ConceptualWallet/index'; import type { CatalystRoundInfoResponse } from '../../../api/ada/lib/state-fetch/types' +import TopBarLayout from '../../../components/layout/TopBarLayout'; +import BannerContainer from '../../banners/BannerContainer'; +import SidebarContainer from '../../SidebarContainer'; +import NavBarContainerRevamp from '../../NavBarContainerRevamp'; +import NavBarTitle from '../../../components/topbar/NavBarTitle'; +import globalMessages from '../../../i18n/global-messages'; +import { withLayout } from '../../../styles/context/layout'; +import type { LayoutComponentMap } from '../../../styles/context/layout' +import type { GeneratedData as SidebarContainerData } from '../../SidebarContainer' +import type { GeneratedData as BannerContainerData } from '../../banners/BannerContainer' +import type { GeneratedData as NavBarContainerRevampData } from '../../NavBarContainerRevamp'; export type GeneratedData = typeof VotingPage.prototype.generated; -type Props = {| - ...InjectedOrGenerated, +type Props = InjectedOrGenerated +type InjectedProps = {| +renderLayoutComponent: LayoutComponentMap => Node |}; +type AllProps = {| + ...Props, + ...InjectedProps, |}; const messages: * = defineMessages({ @@ -75,7 +89,7 @@ const messages: * = defineMessages({ }); @observer -export default class VotingPage extends Component { +class VotingPage extends Component { static contextTypes: {| intl: $npm$ReactIntl$IntlFormat |} = { intl: intlShape.isRequired }; onClose: void => void = () => { @@ -117,6 +131,32 @@ export default class VotingPage extends Component { } render(): Node { + const { intl } = this.context; + const pageContent = this.getPageContent(); + const revampLayout = ( + } + sidebar={} + navbar={ + } + /> + } + showInContainer + showAsCard + > + {pageContent} + + ); + + return this.props.renderLayoutComponent({ + CLASSIC: pageContent, + REVAMP: revampLayout, + }); + } + + getPageContent(): Node { const { intl } = this.context const { uiDialogs, @@ -292,7 +332,6 @@ export default class VotingPage extends Component { const round = catalystRoundInfo?.currentFund?.id || catalystRoundInfo?.nextFund?.id || 5 const fundName = catalystRoundInfo?.currentFund?.name || round.toString(); return ( - //
{activeDialog} { @computed get generated(): {| VotingRegistrationDialogProps: InjectedOrGenerated, + BannerContainerProps: InjectedOrGenerated, + NavBarContainerRevampProps: InjectedOrGenerated, + SidebarContainerProps: InjectedOrGenerated, actions: {| dialogs: {| closeActiveDialog: {| @@ -406,6 +448,15 @@ export default class VotingPage extends Component { actions, stores, }: InjectedOrGenerated), + SidebarContainerProps: ({ actions, stores }: InjectedOrGenerated), + NavBarContainerRevampProps: ({ + actions, + stores, + }: InjectedOrGenerated), + BannerContainerProps: ({ actions, stores }: InjectedOrGenerated), }); - } -} + }; +}; + + +export default (withLayout(VotingPage): ComponentType); \ No newline at end of file diff --git a/packages/yoroi-extension/app/containers/wallet/voting/VotingPage.stories.js b/packages/yoroi-extension/app/containers/wallet/voting/VotingPage.stories.js index fa5a1333d7..a313ef6bf1 100644 --- a/packages/yoroi-extension/app/containers/wallet/voting/VotingPage.stories.js +++ b/packages/yoroi-extension/app/containers/wallet/voting/VotingPage.stories.js @@ -32,6 +32,7 @@ import { MultiToken } from '../../../api/common/lib/MultiToken'; import BigNumber from 'bignumber.js'; import type { ShelleyCip1852CacheValue } from '../../../../stories/helpers/cardano/ShelleyCip1852Mocks'; import { CATALYST_MIN_AMOUNT } from '../../../config/numbersConfig'; +import { ServerStatusErrors } from '../../../types/serverStatusErrorType'; export default { title: `${__filename.split('.')[0]}`, @@ -50,7 +51,7 @@ const defaultProps: ({| VotingRegistrationDialogProps?: *, balance: ?MultiToken, hasAnyPending: boolean, -|}) => * = request => ({ +|}) => any = request => ({ balance: request.balance, hasAnyPending: request.hasAnyPending, stores: { @@ -86,6 +87,55 @@ const defaultProps: ({| }, }, VotingRegistrationDialogProps: request.VotingRegistrationDialogProps || (null: any), + SidebarContainerProps: { + generated: { + stores: { + wallets: { + hasAnyWallets: true, + selected: null, + }, + app: { currentRoute: ROUTES.MY_WALLETS }, + profile: { + isSidebarExpanded: false, + }, + }, + actions: { + profile: { + toggleSidebar: { trigger: async (req) => action('toggleSidebar')(req) }, + }, + router: { + goToRoute: { trigger: action('goToRoute') }, + }, + }, + }, + }, + BannerContainerProps: { + generated: { + stores: { + serverConnectionStore: { + checkAdaServerStatus: select( + 'checkAdaServerStatus', + ServerStatusErrors, + ServerStatusErrors.Healthy, + ), + serverTime: undefined, + }, + tokenInfoStore: { + tokenInfo: mockFromDefaults(defaultAssets), + }, + wallets: { + selected: null, + }, + }, + actions: Object.freeze({}), + }, + }, + NavBarContainerRevampProps: { + generated: { + stores: {}, + actions: {}, + } + } }); const genVotingRegistrationDialogProps: ({| @@ -421,7 +471,7 @@ export const Register = (): Node => { }) } }) - } + }, }))} /> ) diff --git a/packages/yoroi-extension/app/routes-config.js b/packages/yoroi-extension/app/routes-config.js index 25f4694b49..56dce7a6b8 100644 --- a/packages/yoroi-extension/app/routes-config.js +++ b/packages/yoroi-extension/app/routes-config.js @@ -91,5 +91,12 @@ export const ROUTES = { YOROI_PALETTE: '/experimental/yoroi-palette', YOROI_COMPONENTS: '/experimental/components', THEMES: '/experimental/themes' + }, + // Revamp specific routes: + REVAMP: { + // `transfer` the `wallet` + TRANSFER: '/wallets/transfer', + // `voting` is part of the sidebar + CATALYST_VOTING: '/voting', } }; diff --git a/packages/yoroi-extension/app/stores/base/BaseProfileStore.js b/packages/yoroi-extension/app/stores/base/BaseProfileStore.js index 9eab767a54..731bf5f0c7 100644 --- a/packages/yoroi-extension/app/stores/base/BaseProfileStore.js +++ b/packages/yoroi-extension/app/stores/base/BaseProfileStore.js @@ -276,6 +276,10 @@ export default class BaseProfileStore return THEMES.YOROI_MODERN; } + @computed get isRevampTheme(): boolean { + return this.currentTheme === THEMES.YOROI_REVAMP + } + @computed get isModernTheme(): boolean { return this.currentTheme === THEMES.YOROI_MODERN; } diff --git a/packages/yoroi-extension/app/stores/stateless/sidebarCategories.js b/packages/yoroi-extension/app/stores/stateless/sidebarCategories.js index e4052fd464..7b2f86d929 100644 --- a/packages/yoroi-extension/app/stores/stateless/sidebarCategories.js +++ b/packages/yoroi-extension/app/stores/stateless/sidebarCategories.js @@ -50,6 +50,7 @@ export const MY_WALLETS: SidebarCategory = registerCategory({ request.selected == null && matchRoute(ROUTES.WALLETS.ADD, request.currentRoute) === false, }); + export const WALLETS_ROOT: SidebarCategory = registerCategory({ className: 'wallets', route: ROUTES.WALLETS.ROOT, @@ -109,27 +110,32 @@ export const NOTICE_BOARD: SidebarCategory = registerCategory({ isVisible: _request => !environment.isProduction(), }); +type isVisibleFunc = ({| + hasAnyWallets: boolean, + selected: null | PublicDeriver<>, + currentRoute: string, + |}) => boolean; + export type SidebarCategoryRevamp = {| +className: string, +route: string, +icon: string, +label?: MessageDescriptor, - +isVisible: ({| - hasAnyWallets: boolean, - selected: null | PublicDeriver<>, - currentRoute: string, - |}) => boolean, + +isVisible: isVisibleFunc, |}; // TODO: Fix routes and isVisible prop export const allCategoriesRevamp: Array = [ - { - className: 'wallets', - route: ROUTES.MY_WALLETS, - icon: walletIcon, - label: globalMessages.walletLabel, - isVisible: _request => true, - }, + // Open `/wallets` only if the user is on any other page other than `/wallets/add` + makeWalletCategory( + ROUTES.WALLETS.ROOT, + ({ currentRoute }) => currentRoute !== ROUTES.WALLETS.ADD + ), + // Open `/wallets/transactions` if the user is on the `/wallet/add` + makeWalletCategory( + ROUTES.WALLETS.TRANSACTIONS, + ({ currentRoute }) => currentRoute === ROUTES.WALLETS.ADD + ), { className: 'staking', route: ROUTES.STAKING, @@ -155,7 +161,7 @@ export const allCategoriesRevamp: Array = [ }, { className: 'voting', - route: ROUTES.WALLETS.CATALYST_VOTING, + route: ROUTES.REVAMP.CATALYST_VOTING, icon: votingIcon, label: globalMessages.sidebarVoting, // $FlowFixMe[prop-missing] @@ -197,3 +203,13 @@ export const allCategoriesRevamp: Array = [ // isVisible: _request => true, // }, ]; + +function makeWalletCategory(route: string, isVisible: isVisibleFunc): SidebarCategoryRevamp { + return { + className: 'wallets', + route, + icon: walletIcon, + label: globalMessages.walletLabel, + isVisible, + } +}; \ No newline at end of file diff --git a/packages/yoroi-extension/app/stores/stateless/topbarCategories.js b/packages/yoroi-extension/app/stores/stateless/topbarCategories.js index 7ac94c6da0..766086492a 100644 --- a/packages/yoroi-extension/app/stores/stateless/topbarCategories.js +++ b/packages/yoroi-extension/app/stores/stateless/topbarCategories.js @@ -165,7 +165,7 @@ export const allSubcategoriesRevamp: Array = [ }, { className: 'claimTransfer', - route: ROUTES.TRANSFER.ROOT, + route: ROUTES.REVAMP.TRANSFER, label: messages.claimTransfer, isVisible: _request => true, } diff --git a/packages/yoroi-extension/app/stores/toplevel/TokenInfoStore.js b/packages/yoroi-extension/app/stores/toplevel/TokenInfoStore.js index 77da714681..1da6bf12be 100644 --- a/packages/yoroi-extension/app/stores/toplevel/TokenInfoStore.js +++ b/packages/yoroi-extension/app/stores/toplevel/TokenInfoStore.js @@ -71,7 +71,7 @@ export default class TokenInfoStore< setup(): void { super.setup(); this.tokenInfo = new Map(); - // the Ergo connector doesn't have this action + // the connector doesn't have this action if (this.actions.wallets?.setActiveWallet) { this.actions.wallets.setActiveWallet.listen( ({ wallet }) => { this.fetchMissingTokenInfoForWallet(wallet) } diff --git a/packages/yoroi-extension/app/stores/toplevel/WalletStore.js b/packages/yoroi-extension/app/stores/toplevel/WalletStore.js index d2e40609b6..8a0677eb27 100644 --- a/packages/yoroi-extension/app/stores/toplevel/WalletStore.js +++ b/packages/yoroi-extension/app/stores/toplevel/WalletStore.js @@ -95,9 +95,9 @@ export default class WalletStore extends Store { @observable firstSync: ?number; @observable publicDerivers: Array>; - @observable selected: null | PublicDeriver<>; @observable getInitialWallets: Request = new Request(getWallets); + @observable createWalletRequest: Request> = new Request< DeferredCall >(async create => { @@ -157,6 +157,7 @@ export default class WalletStore extends Store { 'visibilitychange', debounce((_e) => this._pollRefresh(), this.ON_VISIBLE_DEBOUNCE_WAIT) ); + } @action @@ -374,13 +375,20 @@ export default class WalletStore extends Store { // =================== ACTIVE WALLET ==================== // - @action _setActiveWallet: ({| wallet: PublicDeriver<> |}) => void = ({ wallet }) => { - this.actions.profile.setSelectedNetwork.trigger(wallet.getParent().getNetworkInfo()); - - this.selected = wallet; - // do not await on purpose since the UI will handle adding loaders while refresh is happening - this.refreshWalletFromRemote(wallet); - }; + @action _setActiveWallet: ({| wallet: PublicDeriver<> |}) => void = + ({ wallet }) => { + this.actions.profile.setSelectedNetwork.trigger(wallet.getParent().getNetworkInfo()); + this.selected = wallet; + // Cache select wallet + this.api.localStorage.setSelectedWalletId(wallet.getPublicDeriverId()) + // do not await on purpose since the UI will handle adding loaders while refresh is happening + this.refreshWalletFromRemote(wallet); + }; + + getLastSelectedWallet: void => ?PublicDeriver<> = () => { + const walletId = this.api.localStorage.getSelectedWalletId(); + return this.publicDerivers.find(pd => pd.getPublicDeriverId() === walletId); + } @action _unsetActiveWallet: void => void = () => { this.actions.profile.setSelectedNetwork.trigger(undefined); diff --git a/packages/yoroi-extension/package-lock.json b/packages/yoroi-extension/package-lock.json index b197117120..c87061355f 100644 --- a/packages/yoroi-extension/package-lock.json +++ b/packages/yoroi-extension/package-lock.json @@ -8599,7 +8599,7 @@ "any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", "dev": true }, "anymatch": { @@ -9761,7 +9761,7 @@ "becke-ch--regex--s0-0-v1--base--pl--lib": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/becke-ch--regex--s0-0-v1--base--pl--lib/-/becke-ch--regex--s0-0-v1--base--pl--lib-1.4.0.tgz", - "integrity": "sha512-FnWonOyaw7Vivg5nIkrUll9HSS5TjFbyuURAiDssuL6VxrBe3ERzudRxOcWRhZYlP89UArMDikz7SapRPQpmZQ==", + "integrity": "sha1-Qpzuu/pffpNueNc/vcfacWKyDiA=", "dev": true }, "before-after-hook": { @@ -12348,25 +12348,25 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, "indent-string": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", "dev": true }, "semver": { @@ -12378,7 +12378,7 @@ "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { "shebang-regex": "^1.0.0" @@ -12387,7 +12387,7 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, "string-width": { @@ -12403,7 +12403,7 @@ "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { "ansi-regex": "^3.0.0" @@ -12498,7 +12498,7 @@ "cucumber-tag-expressions": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/cucumber-tag-expressions/-/cucumber-tag-expressions-1.1.1.tgz", - "integrity": "sha512-V9jv81sR/HaJ87FoidrvHkviXId7KmBcUi7aQPfi+W3nRO30N6GqH6lcp8K+nyiT1DgemRJBPDDeBMS93xJqMQ==", + "integrity": "sha1-f1x7cACbwrZmWRv+ZIVFeL7e6Fo=", "dev": true }, "currently-unhandled": { @@ -13565,7 +13565,7 @@ "es6-iterator": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", "dev": true, "requires": { "d": "1", @@ -14796,7 +14796,7 @@ "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", "dev": true, "requires": { "escape-string-regexp": "^1.0.5" @@ -14805,7 +14805,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true } } @@ -16051,7 +16051,7 @@ "gherkin": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/gherkin/-/gherkin-5.1.0.tgz", - "integrity": "sha512-axTCsxH0m0cixijLvo7s9591h5pMb8ifQxFDun5FnfFhVsUhxgdnH0H7TSK7q8I4ASUU18DJ/tmlnMegMuLUUQ==", + "integrity": "sha1-aEu7A63STq9731RPWAM+so+zxtU=", "dev": true }, "glob": { @@ -16764,7 +16764,7 @@ "httpolyglot": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/httpolyglot/-/httpolyglot-0.1.2.tgz", - "integrity": "sha512-ouHI1AaQMLgn4L224527S5+vq6hgvqPteurVfbm7ChViM3He2Wa8KP1Ny7pTYd7QKnDSPKcN8JYfC8r/lmsE3A==", + "integrity": "sha1-5NNH/omEpi9GfUBg31J/GFH2mXs=", "dev": true }, "https-browserify": { @@ -17256,7 +17256,7 @@ "is-generator": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-generator/-/is-generator-1.0.3.tgz", - "integrity": "sha512-G56jBpbJeg7ds83HW1LuShNs8J73Fv3CPz/bmROHOHlnKkN8sWb9ujiagjmxxMUywftgq48HlBZELKKqFLk0oA==", + "integrity": "sha1-wUwhBX7TbjKNuANHlmxpP4hjifM=", "dev": true }, "is-generator-fn": { @@ -19707,7 +19707,7 @@ "knuth-shuffle-seeded": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/knuth-shuffle-seeded/-/knuth-shuffle-seeded-1.0.6.tgz", - "integrity": "sha512-9pFH0SplrfyKyojCLxZfMcvkhf5hH0d+UwR9nTVJ/DDQJGuzcXjTwB7TP7sDfehSudlGGaOLblmEWqv04ERVWg==", + "integrity": "sha1-AfG2VzOqdUDuCNiwF0Fk0iCB5OE=", "dev": true, "requires": { "seed-random": "~2.2.0" @@ -19955,7 +19955,7 @@ "lodash.omit": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", - "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==", + "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=", "dev": true }, "lodash.set": { @@ -22182,7 +22182,7 @@ "pad-right": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/pad-right/-/pad-right-0.2.2.tgz", - "integrity": "sha512-4cy8M95ioIGolCoMmm2cMntGR1lPLEbOMzOKu8bzjuJP6JpzEMQcDHmh7hHLYGgob+nKe1YHFMaG4V59HQa89g==", + "integrity": "sha1-b7ySQEXSRPKiokRQMGDTv8YAl3Q=", "dev": true, "requires": { "repeat-string": "^1.5.2" @@ -25133,7 +25133,7 @@ "seed-random": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz", - "integrity": "sha512-34EQV6AAHQGhoc0tn/96a9Fsi6v2xdqe/dMUwljGRaFOzR3EgRmECvD0O8vi8X+/uQ50LGHfkNu/Eue5TPKZkQ==", + "integrity": "sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ=", "dev": true }, "selenium-webdriver": { @@ -26741,7 +26741,7 @@ "thenify-all": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", "dev": true, "requires": { "thenify": ">= 3.1.0 < 4" @@ -26858,7 +26858,7 @@ "title-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz", - "integrity": "sha512-EkJoZ2O3zdCz3zJsYCsxyq2OC5hrxR9mfdd5I+w8h/tmFfeOxJ+vvkxsKxdmN0WtS9zLdHEgfgVOiMVgv+Po4Q==", + "integrity": "sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o=", "dev": true, "requires": { "no-case": "^2.2.0", @@ -26868,7 +26868,7 @@ "lower-case": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", "dev": true }, "no-case": { @@ -27626,7 +27626,7 @@ "upper-case": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", "dev": true }, "uri-js": { @@ -27761,7 +27761,7 @@ "util-arity": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/util-arity/-/util-arity-1.1.0.tgz", - "integrity": "sha512-kkyIsXKwemfSy8ZEoaIz06ApApnWsk5hQO0vLjZS6UkBiGiW++Jsyb8vSBoc0WKlffGoGs5yYy/j5pp8zckrFA==", + "integrity": "sha1-WdAa8f2z/t4KxOYysKtfbOl8kzA=", "dev": true }, "util-deprecate": {