diff --git a/packages/ui/cypress/fixtures/landingData.ts b/packages/ui/cypress/fixtures/landingData.ts index 85fac439..44387239 100644 --- a/packages/ui/cypress/fixtures/landingData.ts +++ b/packages/ui/cypress/fixtures/landingData.ts @@ -2,3 +2,5 @@ export const baseUrl = 'http://localhost:3333' export const networkParams = 'network=rococo' export const landingPageUrl = `${baseUrl}?${networkParams}` export const settingsPageUrl = `${baseUrl}/settings?${networkParams}` +const WATCH_ACCOUNT_ANCHOR = '#watched-accounts' +export const settingsPageWatchAccountUrl = `${settingsPageUrl}${WATCH_ACCOUNT_ANCHOR}` diff --git a/packages/ui/cypress/tests/watched-acccounts/watched-accounts.cy.ts b/packages/ui/cypress/tests/watched-acccounts/watched-accounts.cy.ts index 2150d7b7..2ea7593e 100644 --- a/packages/ui/cypress/tests/watched-acccounts/watched-accounts.cy.ts +++ b/packages/ui/cypress/tests/watched-acccounts/watched-accounts.cy.ts @@ -1,5 +1,5 @@ import { addresses } from '../../fixtures/accounts' -import { landingPageUrl, settingsPageUrl } from '../../fixtures/landingData' +import { landingPageUrl, settingsPageWatchAccountUrl } from '../../fixtures/landingData' import { landingPage } from '../../support/page-objects/landingPage' import { settingsPage } from '../../support/page-objects/settingsPage' @@ -28,7 +28,7 @@ describe('Watched Accounts', () => { it('can remove an account from the watch list', () => { // add an account first - cy.visit(settingsPageUrl) + cy.visit(settingsPageWatchAccountUrl) addWatchAccount(addresses.Alice, 'Alice') // now remove it settingsPage.accountContainer().within(() => { @@ -41,7 +41,7 @@ describe('Watched Accounts', () => { it('can see error when attemping to add same address more than once', () => { // add an account first - cy.visit(settingsPageUrl) + cy.visit(settingsPageWatchAccountUrl) addWatchAccount(addresses.Alice, 'Alice') settingsPage.accountContainer().should('have.length', 1) // attempt to add the same account again @@ -52,7 +52,7 @@ describe('Watched Accounts', () => { }) it('can see error when attempting to add an invalid address', () => { - cy.visit(settingsPageUrl) + cy.visit(settingsPageWatchAccountUrl) addWatchAccount('123') settingsPage.errorLabel().should('be.visible').should('have.text', 'Invalid address') settingsPage.accountContainer().should('have.length', 0) diff --git a/packages/ui/src/components/WalletConnect/WalletConnectActiveSessions.tsx b/packages/ui/src/components/WalletConnect/WalletConnectActiveSessions.tsx index 81180689..eadb3b03 100644 --- a/packages/ui/src/components/WalletConnect/WalletConnectActiveSessions.tsx +++ b/packages/ui/src/components/WalletConnect/WalletConnectActiveSessions.tsx @@ -32,48 +32,46 @@ export const WalletConnectActiveSessions = () => { return ( - <> - Active sessions: - {activeSessions.map((session) => { - const { name, url } = session.peer.metadata - const expiryDate = new Date(session.expiry * 1000) - - const content = ( - - - - Namespace: {session.requiredNamespaces.polkadot.chains?.join(', ')} - Methods: {session.requiredNamespaces.polkadot.methods?.join(', ')} - Expiring: {expiryDate.toDateString()} - - - - onDeleteSession(session.topic)} - disabled={isLoading} - > - {isLoading ? : 'Delete session'} - - - - ) - - return ( - - {name} - {url} - - } - content={content} - /> - ) - })} - > + Active sessions: + {activeSessions.map((session) => { + const { name, url } = session.peer.metadata + const expiryDate = new Date(session.expiry * 1000) + + const content = ( + + + + Namespace: {session.requiredNamespaces.polkadot.chains?.join(', ')} + Methods: {session.requiredNamespaces.polkadot.methods?.join(', ')} + Expiring: {expiryDate.toDateString()} + + + + onDeleteSession(session.topic)} + disabled={isLoading} + > + {isLoading ? : 'Delete session'} + + + + ) + + return ( + + {name} + {url} + + } + content={content} + /> + ) + })} ) } diff --git a/packages/ui/src/components/WalletConnect/WalletConnectSession.tsx b/packages/ui/src/components/WalletConnect/WalletConnectSession.tsx index f368b7b3..21389d9b 100644 --- a/packages/ui/src/components/WalletConnect/WalletConnectSession.tsx +++ b/packages/ui/src/components/WalletConnect/WalletConnectSession.tsx @@ -1,6 +1,6 @@ import { Alert, CircularProgress, Grid, styled } from '@mui/material' -import { Button, TextFieldStyled } from '../library' -import { useCallback, useMemo, useState } from 'react' +import { Button, InputField } from '../library' +import { useCallback, useMemo, useState, ChangeEvent } from 'react' import { useWalletConnect } from '../../contexts/WalletConnectContext' import { useMultiProxy } from '../../contexts/MultiProxyContext' @@ -25,7 +25,7 @@ export const WalletConnectSession = () => { } }, [pair, uri]) - const onUriChange = useCallback((event: React.ChangeEvent) => { + const onUriChange = useCallback((event: ChangeEvent) => { setUri(event.target.value.trim()) }, []) @@ -36,9 +36,6 @@ export const WalletConnectSession = () => { > {!canUseWalletConnect && ( @@ -52,24 +49,28 @@ export const WalletConnectSession = () => { )} - - - {loading ? : 'Connect Dapp'} - + + + {loading ? : 'Connect Dapp'} + + ) @@ -81,5 +82,5 @@ const AlertStyled = styled(Alert)` ` const ButtonStyled = styled(Button)` - margin-left: 1rem; + margin-top: 0.5rem; ` diff --git a/packages/ui/src/components/WatchedAccounts.tsx b/packages/ui/src/components/WatchedAccounts.tsx deleted file mode 100644 index b8e078d2..00000000 --- a/packages/ui/src/components/WatchedAccounts.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { styled } from '@mui/material/styles' -import { Box, Grid, IconButton, Paper } from '@mui/material' -import { useWatchedAddresses } from '../contexts/WatchedAddressesContext' -import AccountDisplay from './AccountDisplay' -import { HiOutlineTrash } from 'react-icons/hi2' -import AccountSelection from './select/AccountSelection' - -interface Props { - className?: string -} - -const WatchedAccounts = ({ className }: Props) => { - const { watchedAddresses, removeWatchedAccount, addWatchedAccount } = useWatchedAddresses() - - return ( - - {watchedAddresses.length > 0 && ( - - - {watchedAddresses.map((address) => { - const removeItem = () => removeWatchedAccount(address) - return ( - - - - - - - ) - })} - - - )} - - Watch new account... - - - - - - ) -} - -const PaperStyled = styled(Paper)` - padding: 1rem; - max-height: 13.5rem; - overflow: auto; - - .selectedSignatory { - margin-bottom: 1rem; - display: flex; - - &:last-child { - margin-bottom: 0; - } - - .deleteButton { - margin-left: 1rem; - height: 2.5rem; - align-self: center; - } - } -` -const TitleStyled = styled(Box)` - margin-bottom: 0.5rem; -` - -const AccountSelectionWrapperStyled = styled(Box)` - display: flex; - margin-bottom: 2rem; - - .accountDropdown { - flex: 1; - } -` - -export default WatchedAccounts diff --git a/packages/ui/src/components/library/Button.tsx b/packages/ui/src/components/library/Button.tsx index ba32c411..b78afc16 100644 --- a/packages/ui/src/components/library/Button.tsx +++ b/packages/ui/src/components/library/Button.tsx @@ -7,7 +7,7 @@ interface ButtonProps extends React.ButtonHTMLAttributes { } export const Button = styled('button')` - display: inline-flex; + display: inline-block; padding: 0.5rem 1rem; font-size: 1rem; height: 100%; @@ -21,6 +21,7 @@ export const Button = styled('button')` box-shadow: ${({ theme }) => theme.custom.boxShadow}; transition: background 0.2s ease-in-out; white-space: nowrap; + text-align: center; &:disabled { cursor: not-allowed; diff --git a/packages/ui/src/components/select/AccountSelection.tsx b/packages/ui/src/components/select/AccountSelection.tsx index e49db7ba..2022d025 100644 --- a/packages/ui/src/components/select/AccountSelection.tsx +++ b/packages/ui/src/components/select/AccountSelection.tsx @@ -18,6 +18,8 @@ interface Props { addAccount?: (address: string) => void value?: string label?: string + actionButtonLabel?: string + actionButtonVariant?: 'primary' | 'secondary' currentSelection?: string[] withName?: boolean withAddButton?: boolean @@ -31,6 +33,8 @@ const AccountSelection = ({ nameDisabled = false, value, label = 'Address', + actionButtonLabel = 'Add', + actionButtonVariant = 'secondary', currentSelection = [], withName = false, withAddButton = false, @@ -179,11 +183,11 @@ const AccountSelection = ({ {withAddButton && ( - Add + {actionButtonLabel} )} @@ -203,6 +207,7 @@ const BoxStyled = styled(Box)` const ButtonStyled = styled(Button)` margin-left: 1rem; align-self: end; + text-align: center; ` export default styled(AccountSelection)` diff --git a/packages/ui/src/contexts/WalletConnectContext.tsx b/packages/ui/src/contexts/WalletConnectContext.tsx index 52b40eff..c3c17d39 100644 --- a/packages/ui/src/contexts/WalletConnectContext.tsx +++ b/packages/ui/src/contexts/WalletConnectContext.tsx @@ -13,7 +13,7 @@ export interface IWalletConnectContext { web3wallet: IWeb3Wallet | undefined core: ICore pair: (params: { uri: string }) => Promise - refresh: () => void + refresh: () => Promise } const WalletConnectContext = createContext(undefined) @@ -58,7 +58,7 @@ const WalletConnectContextProvider = ({ children }: WalletConnectContextProps) = ) const refresh = useCallback(() => { - createWeb3Wallet() + return createWeb3Wallet() }, [createWeb3Wallet]) useEffect(() => { diff --git a/packages/ui/src/logos/walletConnectSVG.svg b/packages/ui/src/logos/walletConnectSVG.svg new file mode 100644 index 00000000..7baac649 --- /dev/null +++ b/packages/ui/src/logos/walletConnectSVG.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/packages/ui/src/pages/Home/Home.tsx b/packages/ui/src/pages/Home/Home.tsx index c7dc07bf..c2b5ae83 100644 --- a/packages/ui/src/pages/Home/Home.tsx +++ b/packages/ui/src/pages/Home/Home.tsx @@ -17,6 +17,7 @@ import MultisigView from './MultisigView' import TransactionList from '../../components/Transactions/TransactionList' import { ConnectOrWatch } from '../../components/ConnectOrWatch' import { HiOutlineArrowTopRightOnSquare as LaunchIcon } from 'react-icons/hi2' +import { WATCH_ACCOUNT_ANCHOR } from '../Settings/Settings' // import CurrentReferendumBanner from '../../components/CurrentReferendumBanner' interface HomeProps { @@ -63,7 +64,7 @@ const Home = ({ className }: HomeProps) => { Connect Wallet or navigate('/settings')} + onClick={() => navigate(`/settings${WATCH_ACCOUNT_ANCHOR}`)} data-cy="button-watch-address" > Watch an address diff --git a/packages/ui/src/pages/Settings.tsx b/packages/ui/src/pages/Settings.tsx deleted file mode 100644 index a0c3f86e..00000000 --- a/packages/ui/src/pages/Settings.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { Box } from '@mui/material' -import WatchedAccounts from '../components/WatchedAccounts' -import { WalletConnectSession } from '../components/WalletConnect/WalletConnectSession' -import styled from '@emotion/styled' -import { WalletConnectActiveSessions } from '../components/WalletConnect/WalletConnectActiveSessions' - -interface Props { - className?: string -} - -const Settings = ({ className }: Props) => { - return ( - - Watched Accounts - - WalletConnect - - - - ) -} - -const BoxStyled = styled(Box)` - margin-bottom: 2rem; -` - -export default Settings diff --git a/packages/ui/src/pages/Settings/Settings.tsx b/packages/ui/src/pages/Settings/Settings.tsx new file mode 100644 index 00000000..8e3330d8 --- /dev/null +++ b/packages/ui/src/pages/Settings/Settings.tsx @@ -0,0 +1,125 @@ +import { Accordion, AccordionDetails, AccordionSummary } from '@mui/material' +import { css, styled } from '@mui/material/styles' +import WatchedAccounts from './WatchedAccounts' +import { WalletConnectSession } from '../../components/WalletConnect/WalletConnectSession' +import { WalletConnectActiveSessions } from '../../components/WalletConnect/WalletConnectActiveSessions' +import { HiOutlineChevronDown as ExpandMoreIcon, HiOutlineEye } from 'react-icons/hi2' +import { theme } from '../../styles/theme' +import { useCallback, useEffect, useState } from 'react' +import { useLocation } from 'react-router-dom' +import { ReactComponent as WalletConnectSVG } from '../../logos/walletConnectSVG.svg' + +const ACCORDION_WATCHED_ACCOUNTS = 'panel-watched-accounts' +const ACCORDION_WALLET_CONNECT = 'panel-wallet-connect' +export const WATCH_ACCOUNT_ANCHOR = '#watched-accounts' + +type AccordionNames = typeof ACCORDION_WATCHED_ACCOUNTS | typeof ACCORDION_WALLET_CONNECT + +const Settings = () => { + const { hash } = useLocation() + const [expanded, setExpanded] = useState(undefined) + + const onToggle = useCallback((panel: AccordionNames, forceOpen = false) => { + setExpanded((prev) => { + return prev === panel && !forceOpen ? undefined : panel + }) + }, []) + + useEffect(() => { + if (hash === WATCH_ACCOUNT_ANCHOR) { + onToggle(ACCORDION_WATCHED_ACCOUNTS, true) + } + }, [hash, onToggle]) + + return ( + <> + Settings + onToggle(ACCORDION_WATCHED_ACCOUNTS)} + > + }> + + Watched accounts + + + + + + onToggle(ACCORDION_WALLET_CONNECT)} + > + }> + + WalletConnect + + + + + + + > + ) +} + +const SettingsHeaderStyled = styled('h1')` + color: ${({ theme }) => theme.custom.gray[900]}; + font-weight: 500; + font-size: 1.5rem; + margin: 1.5rem 0 2rem 0; +` + +const AccordionStyled = styled(Accordion)` + max-width: 42.5625rem; + box-shadow: none; + border-bottom: 1px solid ${({ theme }) => theme.custom.neutral[200]}; + + &.Mui-expanded { + margin: 0; + background: ${({ theme }) => theme.custom.neutral[50]}; + + .MuiAccordionSummary-root { + margin: 0; + min-height: auto; + } + } +` + +const AccordionSummaryStyled = styled(AccordionSummary)` + padding: 1.5rem 1rem; + + .MuiAccordionSummary-content { + margin: 0; + display: flex; + align-items: center; + } + + .MuiAccordionSummary-content.Mui-expanded { + margin-top: 0; + } +` + +const commonCssImgs = css` + display: flex; + align-items: center; + justify-content: center; + width: 2rem; + height: 1.9375rem; + padding: 0.35rem; + border-radius: 0.25rem; + margin: 0 0.75rem 0 0; + background: ${theme.custom.gray[400]}; +` + +const WalletConnectSVGStyled = styled(WalletConnectSVG)(commonCssImgs) + +const HiOutlineEyeStyled = styled(HiOutlineEye)(commonCssImgs) + +const SummaryLabelStyled = styled('div')` + color: ${({ theme }) => theme.custom.gray[900]}; + font-size: 1.25rem; + font-weight: 500; +` + +export default Settings diff --git a/packages/ui/src/pages/Settings/WatchedAccounts.tsx b/packages/ui/src/pages/Settings/WatchedAccounts.tsx new file mode 100644 index 00000000..da5a6fd3 --- /dev/null +++ b/packages/ui/src/pages/Settings/WatchedAccounts.tsx @@ -0,0 +1,130 @@ +import { styled } from '@mui/material/styles' +import { Box, Grid, IconButton, Paper } from '@mui/material' +import { useWatchedAddresses } from '../../contexts/WatchedAddressesContext' +import AccountDisplay from '../../components/AccountDisplay' +import { HiOutlineXMark } from 'react-icons/hi2' +import AccountSelection from '../../components/select/AccountSelection' +import { useMemo } from 'react' + +const WatchedAccounts = () => { + const { watchedAddresses, removeWatchedAccount, addWatchedAccount } = useWatchedAddresses() + const hasWatchedAddresses = useMemo(() => watchedAddresses.length > 0, [watchedAddresses]) + + return ( + <> + {hasWatchedAddresses && ( + Currently watched accounts: + )} + + {hasWatchedAddresses && ( + + + {watchedAddresses.map((address) => { + return ( + + + removeWatchedAccount(address)} + data-cy="button-delete-watched-account" + > + + + + ) + })} + + + )} + + + + + + + > + ) +} + +const WatchAccountsHeaderStyled = styled('h3')` + color: ${({ theme }) => theme.custom.gray[800]}; + font-size: 1rem; + font-weight: 400; + margin: 0 0 0.5rem 0; +` + +const PaperStyled = styled(Paper)` + border-radius: ${({ theme }) => theme.custom.borderRadius}; + border: 1px solid ${({ theme }) => theme.custom.text.borderColor}; + box-shadow: none; + + & > :last-of-type { + border-bottom: none; + } +` + +const IconButtonDeleteStyled = styled(IconButton)` + margin-left: 1rem; + height: 2.5rem; + align-self: center; +` + +const AccountDisplayStyled = styled(AccountDisplay)` + flex: 1; +` + +const AccountItemWrapperStyled = styled(Box)` + display: flex; + align-items: center; + padding: 0.75rem 1rem; + border-bottom: 1px solid ${({ theme }) => theme.custom.text.borderColor}; +` + +const AccountSelectionWrapperStyled = styled(Box)` + display: flex; + margin-bottom: 2rem; + + .MuiAutocomplete-root { + margin-right: 0 !important; + } + + .accountDropdown { + display: flex; + flex-direction: column; + + & > * { + width: 100%; + } + + & > :last-child { + margin-top: 0.5rem; + } + } +` + +export default WatchedAccounts diff --git a/packages/ui/src/pages/index.ts b/packages/ui/src/pages/index.ts index c5adfd0b..9b932095 100644 --- a/packages/ui/src/pages/index.ts +++ b/packages/ui/src/pages/index.ts @@ -2,4 +2,4 @@ export { default as Creation } from './Creation' export { default as Home } from './Home/Home' export { default as About } from './About' export { default as Overview } from './Overview' -export { default as Settings } from './Settings' +export { default as Settings } from './Settings/Settings' diff --git a/packages/ui/src/styles/theme.ts b/packages/ui/src/styles/theme.ts index 27388565..73ab8b79 100644 --- a/packages/ui/src/styles/theme.ts +++ b/packages/ui/src/styles/theme.ts @@ -35,6 +35,10 @@ declare module '@mui/material/styles' { 800: string 900: string } + neutral: { + 50: string + 200: string + } button: { primaryDisabledColor: string primaryDisabledBackground: string @@ -79,6 +83,10 @@ declare module '@mui/material/styles' { 800: string 900: string } + neutral: { + 50: string + 200: string + } button: { primaryDisabledColor: string primaryDisabledBackground: string @@ -140,6 +148,10 @@ export const theme = createTheme({ 800: '#485568', 900: '#020617' }, + neutral: { + 50: '#FAFAFA', + 200: '#e5e5e5' + }, button: { primaryDisabledColor: '#A8B3DC', primaryDisabledBackground: '#E3E9FF',