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

📱 Improve mobile UI #4781

Merged
merged 46 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
91c83fb
Do not rely on `injectweb3-connect` for the "supportedWallets" list
thesan Jan 30, 2024
5e90b42
Remove Polkadot.js from the recommended wallets
thesan Jan 30, 2024
394a16a
Replace `Wallet.signer` by `Wallet.getSigner(address)`
thesan Feb 1, 2024
0cb8ab6
Expose `genesisHash` and `runtimeVersion` on the `ProxyApi`
thesan Feb 1, 2024
9277099
Install WalletConnect dependencies
thesan Feb 1, 2024
a31f2e8
Integrate WalletConnect
thesan Feb 1, 2024
9060a63
Fix types in stories and tests
thesan Feb 1, 2024
188fd43
Fix wallet connection
thesan Feb 2, 2024
8acf4c0
Fix transactions
thesan Feb 5, 2024
0c2a773
Fix with the proxy api
thesan Feb 5, 2024
2c7b298
Enable the onboarding flow on mobile
thesan Feb 7, 2024
12c8cbd
Fix the tests
thesan Feb 8, 2024
6b0ab95
Add the WalletConnect logo
thesan Feb 8, 2024
1b90830
Fix legacy tests
thesan Feb 9, 2024
5f5f6e7
Persist session
thesan Feb 16, 2024
c2d001f
Remove "getSigner" method
thesan Feb 19, 2024
bc6f34e
Implement "signRaw" with wallet connect
thesan Feb 19, 2024
41df27d
Handle disconnections
thesan Feb 19, 2024
40dff4a
Properly disconnect accounts
thesan Feb 20, 2024
8932e13
Fix wallet connection
thesan Feb 20, 2024
25d7975
Fix interaction tests
thesan Feb 20, 2024
dd7a6e6
Name the accounts
thesan Feb 20, 2024
c9b94bf
Fix the wallet reject app case
thesan Feb 20, 2024
045ed76
Load wallet connect in parallel to the RPC node
thesan Feb 20, 2024
e943e3d
Add some metadata
thesan Feb 20, 2024
0fb3e89
Revert "Enable the onboarding flow on mobile"
thesan Feb 21, 2024
953275c
Fix onBoarding logic
thesan Feb 21, 2024
8914ed0
Revert "Fix the tests"
thesan Feb 21, 2024
00c04b0
Fix wallet disconnection
thesan Feb 22, 2024
70fd672
Address change requests
thesan Feb 22, 2024
54514ad
Enable the onboarding flow on mobile
thesan Feb 7, 2024
e75de3f
Fix the tests
thesan Feb 8, 2024
a1d6249
Fix account selectors on mobile
thesan Feb 16, 2024
da11d9a
Fix some more mobile UI
thesan Feb 16, 2024
9c1bfd3
Hide most transaction buttons on mobile
thesan Feb 16, 2024
8a007ac
Fix confirm transaction modal on mobile
thesan Feb 16, 2024
9e2a028
Merge branch 'dev' into feature/mobile-ui-improvement
thesan Feb 22, 2024
36006bd
Fix modals footer on desktop
thesan Feb 22, 2024
bebf406
Fix Validator integration tests
thesan Feb 23, 2024
e09b432
Fix typo
thesan Feb 23, 2024
6e7c18c
Merge branch 'dev' of github.com:Joystream/pioneer into feature/mobil…
thesan Feb 26, 2024
7f6cbc8
Allow the editing and deleting posts on mobile
thesan Feb 27, 2024
fab33cc
Remove email subscription Lorem ipsum tooltip
thesan Feb 27, 2024
4884807
Fix modal footers again
thesan Feb 27, 2024
22b070b
Fix account select balance locks alignment
thesan Feb 27, 2024
dfb21f8
Fix unit tests
thesan Feb 27, 2024
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
40 changes: 26 additions & 14 deletions packages/ui/src/accounts/components/SelectAccount/OptionAccount.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import React from 'react'
import styled from 'styled-components'
import styled, { css } from 'styled-components'

import { AccountInfo } from '@/accounts/components/AccountInfo'
import { AccountLocks, AccountLocksWrapper } from '@/accounts/components/AccountLocks'
import { AccountLocks } from '@/accounts/components/AccountLocks'
import { useBalance } from '@/accounts/hooks/useBalance'
import { AccountOption } from '@/accounts/types'
import { BalanceInfoNarrow, InfoTitle, InfoValue } from '@/common/components/Modal'
import { TokenValue } from '@/common/components/typography'
import { Colors } from '@/common/constants'
import { BreakPoints, Colors } from '@/common/constants'

interface Props {
option: AccountOption
isForStaking?: boolean
variant?: 's' | 'm' | 'l'
isSelected?: boolean
}

export const OptionAccount = ({ option, isForStaking, variant }: Props) => {
export const OptionAccount = ({ option, isForStaking, variant, isSelected }: Props) => {
const balances = useBalance(option.address)
const balance = isForStaking ? balances?.total : balances?.transferable
const balanceType = isForStaking ? 'Total' : 'Transferable'
Expand All @@ -25,23 +26,34 @@ export const OptionAccount = ({ option, isForStaking, variant }: Props) => {
return (
<>
<AccountInfo account={option} locked={isLocked} variant={variant} />
<BalanceInfoNarrow>
<BalanceContainer isSelected={isSelected}>
<InfoTitle>{balanceType} balance</InfoTitle>
<InfoValueWithLocks>
<InfoValue>
<Value value={balance} locked={isLocked} />
<AccountLocks locks={balances?.locks} />
</InfoValueWithLocks>
</BalanceInfoNarrow>
</InfoValue>
</BalanceContainer>
</>
)
}

const Value = styled(TokenValue)<{ locked?: boolean }>`
color: ${({ locked }) => (locked ? Colors.Black[500] : 'default')};
`
const BalanceContainer = styled(BalanceInfoNarrow)<Pick<Props, 'isSelected'>>`
gap: 12px;

export const InfoValueWithLocks = styled(InfoValue)`
${AccountLocksWrapper} {
right: 0;
@media (max-width: ${BreakPoints.sm - 1}px) {
${({ isSelected }) =>
isSelected
? css`
display: none;
`
: css`
${InfoValue} {
display: contents;
}
`};
}
`

const Value = styled(TokenValue)<{ locked?: boolean }>`
color: ${({ locked }) => (locked ? Colors.Black[500] : 'default')};
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Meta, StoryFn } from '@storybook/react'
import React from 'react'
import styled from 'styled-components'

import { useMyAccounts } from '@/accounts/hooks/useMyAccounts'
import { member } from '@/mocks/data/members'
import { MocksParameters } from '@/mocks/providers'
import { AccountMock } from '@/mocks/providers/accounts'

import { OptionListAccount } from './OptionListAccount'

export const mockAccounts: AccountMock[] = [
{ member: member('alice'), balances: { transferable: 0.00001, locks: ['Apps Worker'] } },
{
member: member('bob'),
balances: { transferable: 101_300_098.123, locks: ['Councilor', 'Voting', 'Council Candidate'] },
},
{ member: member('charlie'), balances: 200 },
{ member: member('dave'), balances: { transferable: 0, locks: ['Bound Staking Account'] } },
{ member: member('eve'), balances: { transferable: 1000, locks: ['Staking'] } },
{ member: member('ferdie'), balances: 500 },
]

export default {
title: 'Accounts/SelectAccount/OptionListAccount',
component: OptionListAccount,

parameters: {
mocks: {
accounts: { list: mockAccounts },
} satisfies MocksParameters,
},

excludeStories: ['mockAccounts'],
} satisfies Meta

export const Default: StoryFn = () => {
const { allAccounts } = useMyAccounts()

const free = allAccounts.slice(0, 3)

const optionLocks = [['insufficientFunds', 'boundMembership'], ['rivalrousLock', 'recoverableLock'], ['optOutLock']]
const locked = allAccounts.slice(3).map((account, i) => ({ ...account, optionLocks: optionLocks[i] }))

return <AccountsOptions options={[...free, ...locked]} />
}

const AccountsOptions = styled(OptionListAccount)`
position: static;
transform: none;
max-height: none;
max-width: 856px;
`
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { OptionAccount } from './OptionAccount'

interface Props {
options: AccountOption[]
onChange: (option: AccountOption) => void
onChange?: (option: AccountOption) => void
className?: string
isForStaking?: boolean
variant?: 's' | 'm' | 'l'
Expand All @@ -21,13 +21,13 @@ export const OptionListAccount = React.memo(({ options, onChange, className, isF
return (
<OptionsListComponent className={className}>
{freeAccounts.map((option) => (
<Option key={option.address} onClick={() => onChange && onChange(option)} variant={variant}>
<Option key={option.address} onClick={() => onChange && onChange(option)}>
<OptionAccount option={option} isForStaking={isForStaking} variant={variant} />
</Option>
))}
{lockedAccounts.map((option) => (
<AccountLockTooltip boundaryClassName={className} key={option.address} locks={option.optionLocks}>
<Option key={option.address} onClick={() => onChange && onChange(option)} disabled variant={variant}>
<Option key={option.address} onClick={() => onChange && onChange(option)} disabled>
<OptionAccount option={option} isForStaking={isForStaking} />
</Option>
</AccountLockTooltip>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,60 +1,40 @@
import { Meta, Story } from '@storybook/react'
import React, { ReactNode, useState } from 'react'
import { Meta, StoryFn } from '@storybook/react'
import React, { useState } from 'react'
import styled from 'styled-components'

import { AccountsContextProvider } from '@/accounts/providers/accounts/provider'
import { Account } from '@/accounts/types'
import { InputComponent } from '@/common/components/forms'
import { Warning } from '@/common/components/Warning'
import { KeyringContextProvider } from '@/common/providers/keyring/provider'
import { MockApolloProvider } from '@/mocks/components/storybook/MockApolloProvider'
import { MocksParameters } from '@/mocks/providers'

import { SelectAccount } from '.'
import { mockAccounts } from './OptionListAccount.stories'

export default {
title: 'Accounts/SelectAccount',
component: SelectAccount,
} as Meta

const renderWarning = (accountSelect: ReactNode) => (
<>
<Warning
isClosable={false}
title="Warning: the Polkadot.js extension can't run in an iframe !"
content={
<>
To render this component properly:{' '}
<a href={window.location.href} target="_blank">
Open the canvas in a new tab
</a>
</>
}
/>
<br />
{accountSelect}
</>
)
parameters: {
mocks: {
accounts: { list: mockAccounts },
} satisfies MocksParameters,
},
} satisfies Meta

export const Default: Story = () => {
export const Default: StoryFn = () => {
const [selected, setSelected] = useState<Account>()

const accountSelect = (
<InputComponent
return (
<StyledInputComponent
label="Account select"
tooltipText="Lorem ipsum dolor sit amet consectetur, adipisicing elit."
required
inputSize="l"
>
<SelectAccount selected={selected} onChange={setSelected} />
</InputComponent>
)

return window.self === window.top ? (
<MockApolloProvider members>
<KeyringContextProvider>
<AccountsContextProvider>{accountSelect}</AccountsContextProvider>
</KeyringContextProvider>
</MockApolloProvider>
) : (
renderWarning(accountSelect)
</StyledInputComponent>
)
}

const StyledInputComponent = styled(InputComponent)`
max-width: 856px;
`
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ interface BaseSelectAccountProps extends SelectAccountProps {
isForStaking?: boolean
}

export const BaseSelectAccount = React.memo(
const BaseSelectAccount = React.memo(
({ id, onChange, accounts, filter, selected, disabled, onBlur, isForStaking, variant }: BaseSelectAccountProps) => {
const options = accounts.filter(filter || (() => true))

Expand Down Expand Up @@ -88,7 +88,7 @@ export const BaseSelectAccount = React.memo(
const renderSelected = (isForStaking?: boolean, variant?: 's' | 'm' | 'l') => (option: AccountOption) =>
(
<SelectedOption variant={variant}>
<OptionAccount option={option} isForStaking={isForStaking} />
<OptionAccount option={option} isForStaking={isForStaking} isSelected />
</SelectedOption>
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ import styled from 'styled-components'

import { AccountInfo } from '@/accounts/components/AccountInfo'
import { filterByText } from '@/accounts/components/SelectAccount/helpers'
import { InfoValueWithLocks } from '@/accounts/components/SelectAccount/OptionAccount'
import { useBalance } from '@/accounts/hooks/useBalance'
import { useMyAccounts } from '@/accounts/hooks/useMyAccounts'
import { accountOrNamed } from '@/accounts/model/accountOrNamed'
import { isValidAddress } from '@/accounts/model/isValidAddress'
import { Account, AccountOption } from '@/accounts/types'
import { VestingLockIcon } from '@/common/components/icons/locks/VestingLockIcon'
import { BalanceInfoInRow } from '@/common/components/Modal'
import { BalanceInfoInRow, InfoValue } from '@/common/components/Modal'
import { ColumnGapBlock } from '@/common/components/page/PageContent'
import { Option, OptionsListComponent, Select, SelectedOption } from '@/common/components/selects'
import { TextMedium, TokenValue } from '@/common/components/typography'
Expand Down Expand Up @@ -91,9 +90,9 @@ export const VestingListItem = ({ option, vestingClaimable }: { option: AccountO
<VestingLockIcon />
<TextMedium bold>Vesting</TextMedium>
</ColumnGapBlock>
<InfoValueWithLocks>
<InfoValue>
<TokenValue value={vestingClaimable} />
</InfoValueWithLocks>
</InfoValue>
</VestingInfoRow>
</>
)
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/accounts/model/walletConnect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export class WalletConnect extends BaseDotsamaWallet {
polkadot: {
methods: ['polkadot_signTransaction', 'polkadot_signMessage'],
chains: [chainCAIP],
events: ['chainChanged", "accountsChanged'],
events: ['chainChanged', 'accountsChanged'],
},
}

Expand Down
16 changes: 14 additions & 2 deletions packages/ui/src/app/App.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ export const NoAccount: Story = {
const modal = withinModal(canvasElement)
expectActiveStepToBe(modal, 'Connect account')
expect(modal.getByText('Connect account', { selector: '[class^=ModalBody] *' }))
expect(getButtonByText(modal, 'Return to wallet selection')).toBeEnabled()
expect(getButtonByText(modal, 'Change wallet')).toBeEnabled()
expect(getButtonByText(modal, 'Connect Account')).toBeDisabled()
expect(modal.queryByText('alice')).toBeNull()
},
Expand All @@ -376,7 +376,7 @@ export const FaucetMembership: Story = {
expectActiveStepToBe(modal, 'Connect account')
expect(modal.getByText('Connect account', { selector: '[class^=ModalBody] *' }))

expect(getButtonByText(modal, 'Return to wallet selection')).toBeEnabled()
expect(getButtonByText(modal, 'Change wallet')).toBeEnabled()

const connectAccountButton = getButtonByText(modal, 'Connect Account')
expect(connectAccountButton).toBeDisabled()
Expand Down Expand Up @@ -426,6 +426,7 @@ export const BuyMembershipHappy: Story = {
expect(screen.queryByText('Become a member')).toBeNull()

await userEvent.click(getButtonByText(screen, 'Join Now'))
await userEvent.click(await modal.findByText('New Member'))

await step('Form', async () => {
const createButton = getButtonByText(modal, 'Create a Membership')
Expand Down Expand Up @@ -539,6 +540,7 @@ export const BuyMembershipNotEnoughFund: Story = {
const modal = withinModal(canvasElement)

await userEvent.click(getButtonByText(screen, 'Join Now'))
await userEvent.click(await modal.findByText('New Member'))

await fillMembershipForm(modal)
const createButton = getButtonByText(modal, 'Create a Membership')
Expand All @@ -559,6 +561,7 @@ export const BuyMembershipTxFailure: Story = {
const modal = withinModal(canvasElement)

await userEvent.click(getButtonByText(screen, 'Join Now'))
await userEvent.click(await modal.findByText('New Member'))

await fillMembershipForm(modal)
const createButton = getButtonByText(modal, 'Create a Membership')
Expand Down Expand Up @@ -598,6 +601,7 @@ export const BuyMembershipHappyBindOneValidatorHappy: Story = {
expect(screen.queryByText('Become a member')).toBeNull()

await userEvent.click(getButtonByText(screen, 'Join Now'))
await userEvent.click(await modal.findByText('New Member'))

await step('Form', async () => {
const createButton = getButtonByText(modal, 'Create a Membership')
Expand Down Expand Up @@ -679,6 +683,7 @@ export const BuyMembershipHappyAddTwoValidatorHappy: Story = {
expect(screen.queryByText('Become a member')).toBeNull()

await userEvent.click(getButtonByText(screen, 'Join Now'))
await userEvent.click(await modal.findByText('New Member'))

await step('Form', async () => {
const createButton = getButtonByText(modal, 'Create a Membership')
Expand Down Expand Up @@ -773,6 +778,8 @@ export const InvalidValidatorAccountInput: Story = {
const modal = withinModal(canvasElement)

await userEvent.click(getButtonByText(screen, 'Join Now'))
await userEvent.click(await modal.findByText('New Member'))

await fillMembershipForm(modal)
const validatorCheckButton = modal.getAllByText('Yes')[1]
await userEvent.click(validatorCheckButton)
Expand All @@ -798,6 +805,7 @@ export const BuyMembershipWithValidatorAccountNotEnoughFunds: Story = {
const modal = withinModal(canvasElement)

await userEvent.click(getButtonByText(screen, 'Join Now'))
await userEvent.click(await modal.findByText('New Member'))

await fillMembershipFormValidatorAccounts(modal, ['charlie'])
const createButton = getButtonByText(modal, 'Create a Membership')
Expand All @@ -818,6 +826,7 @@ export const BuyMembershipWithValidatorAccountFailure: Story = {
const modal = withinModal(canvasElement)

await userEvent.click(getButtonByText(screen, 'Join Now'))
await userEvent.click(await modal.findByText('New Member'))

await fillMembershipFormValidatorAccounts(modal, ['charlie'])
const createButton = getButtonByText(modal, 'Create a Membership')
Expand All @@ -842,6 +851,7 @@ export const BuyMembershipHappyAddOneValidatorFailure: Story = {
expect(screen.queryByText('Become a member')).toBeNull()

await userEvent.click(getButtonByText(screen, 'Join Now'))
await userEvent.click(await modal.findByText('New Member'))

await step('Form', async () => {
const createButton = getButtonByText(modal, 'Create a Membership')
Expand Down Expand Up @@ -888,6 +898,7 @@ export const BuyMembershipAddValidatorAccHappyConfirmTxFailure: Story = {
expect(screen.queryByText('Become a member')).toBeNull()

await userEvent.click(getButtonByText(screen, 'Join Now'))
await userEvent.click(await modal.findByText('New Member'))

await step('Form', async () => {
const createButton = getButtonByText(modal, 'Create a Membership')
Expand Down Expand Up @@ -944,6 +955,7 @@ export const BuyMembershipAddTwoValidatorAccHappyConfirmTxFailure: Story = {
expect(screen.queryByText('Become a member')).toBeNull()

await userEvent.click(getButtonByText(screen, 'Join Now'))
await userEvent.click(await modal.findByText('New Member'))

await step('Form', async () => {
const createButton = getButtonByText(modal, 'Create a Membership')
Expand Down
Loading
Loading