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

feat(wallet-dashboard): add styles for Review & Send screen #3787

Merged
merged 77 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from 69 commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
54aa532
feat(wallet-dashboard): add styles for Review & Send screen
VmMad Oct 30, 2024
9ae17c0
Merge branch 'develop' into tooling-dashboard/style-send-screen
VmMad Oct 30, 2024
cc2a855
fix: move CoinIcon to core
VmMad Oct 30, 2024
7bd6cbf
feat(wallet-dashboard): style send entry screen WIP
cpl121 Oct 30, 2024
10c23f8
Merge branches 'tooling-dashboard/style-send-entry-screen' and 'devel…
cpl121 Oct 30, 2024
40d36db
Merge branches 'tooling-dashboard/style-send-entry-screen' and 'devel…
cpl121 Oct 31, 2024
79c283b
feat(wallet-dashboard): style send entry screen WIP
cpl121 Oct 31, 2024
993a16a
Merge branches 'tooling-dashboard/style-send-entry-screen' and 'devel…
cpl121 Nov 4, 2024
f820cf3
fix(wallet-dashboard): sort the dependencies
cpl121 Nov 4, 2024
cb75496
feat(wallet-dashboard): includes icon coin in coin selector
cpl121 Nov 4, 2024
9b56596
fix(wallet-dashboard): prettier
cpl121 Nov 4, 2024
758cba8
fix(wallet-dashboard): update schema validation and share gas budget …
cpl121 Nov 4, 2024
439f050
Merge branches 'tooling-dashboard/style-send-entry-screen' and 'devel…
cpl121 Nov 7, 2024
d011746
fix(wallet-dashboard): some fixes
cpl121 Nov 7, 2024
d20a617
fix(wallet-dashboard): some build errors
cpl121 Nov 7, 2024
f7ae2d7
fix(wallet-dashboard): fix change amount in send token input
cpl121 Nov 7, 2024
b23246f
fix(wallet-dashboard): linter
cpl121 Nov 7, 2024
193cf87
Merge branch 'develop' of github.com:iotaledger/iota into tooling-das…
cpl121 Nov 7, 2024
f4abb91
Merge branches 'tooling-dashboard/style-send-entry-screen' and 'devel…
cpl121 Nov 8, 2024
86bedbf
fix(wallet-dashboard): linter
cpl121 Nov 8, 2024
d0ca55d
fix(wallet-dashboard): core prettier
cpl121 Nov 8, 2024
657329a
Merge branch 'tooling-dashboard/style-send-entry-screen' into tooling…
VmMad Nov 8, 2024
eefbd09
feat: add review dialog as view
VmMad Nov 8, 2024
1de3906
Merge branches 'tooling-dashboard/style-send-entry-screen' and 'tooli…
cpl121 Nov 11, 2024
625aeef
Merge branch 'tooling-dashboard/style-send-entry-screen' into tooling…
VmMad Nov 11, 2024
be6d9f2
fix(wallet-dashboard): include interface with props and some fixes
cpl121 Nov 11, 2024
b3960fd
fix: update review comments
VmMad Nov 11, 2024
ea8646d
Merge branch 'tooling-dashboard/style-send-entry-screen' into tooling…
VmMad Nov 11, 2024
82909d8
fix(wallet-dashboard): fixes
cpl121 Nov 11, 2024
b9a659e
fix(wallet-dashboard): fixes
cpl121 Nov 12, 2024
58415c7
fix(wallet-dashboard): move FormInputs to a standalone component
cpl121 Nov 12, 2024
3b37425
fix(wallet-dashboard): improve AddressInputs props
cpl121 Nov 12, 2024
64d5693
fix(wallet-dashboard): linter
cpl121 Nov 12, 2024
df00b07
fix(wallet-dashboard): format core
cpl121 Nov 12, 2024
13aaf0e
fix(wallet-dashboard): clean debris
cpl121 Nov 12, 2024
288483f
fix: add ExplorerLink component and add missing dialog styles
VmMad Nov 13, 2024
088b300
Merge branch 'tooling-dashboard/style-send-entry-screen' into tooling…
VmMad Nov 13, 2024
f1699c0
fix: use correct values for keyvalue
VmMad Nov 13, 2024
28a9353
fix(wallet-dashboard): bring back the validation field
cpl121 Nov 13, 2024
72bfb38
fix(wallet-dashboard): bad merge removing duplicated image components
cpl121 Nov 13, 2024
86c27ae
fix(wallet-dashboard): remove unnecesary InputForm component
cpl121 Nov 13, 2024
7e6c12d
fix(wallet-dashboard): adjust to full height the dialog body
cpl121 Nov 13, 2024
af32d3a
fix(wallet-dashboard): prettier
cpl121 Nov 13, 2024
9b2c108
Merge branch 'tooling-dashboard/style-send-entry-screen' into tooling…
VmMad Nov 13, 2024
754d2db
fix: gas approximation
VmMad Nov 13, 2024
78e5527
fix(wallet-dashboard): max button disabled
cpl121 Nov 14, 2024
1fe1d05
feat(wallet-dashboard): improvements
cpl121 Nov 14, 2024
94bcca1
fix(wallet-dashboard): improve formik props
cpl121 Nov 14, 2024
17058ad
fix(wallet-dashboard): improvements
cpl121 Nov 14, 2024
ddca44c
refactor: Simplify SendTokenFormInput
marc2332 Nov 14, 2024
76d2e89
refactor: prettier:fix
marc2332 Nov 14, 2024
6b388b6
refactor: prettier:fix on apps/core
marc2332 Nov 14, 2024
78af8b4
refactor: Add missing license header to token.ts
marc2332 Nov 14, 2024
3e9fa71
fix: linter
cpl121 Nov 14, 2024
012b773
fix(wallet-dashboard): linter
cpl121 Nov 14, 2024
e97e879
fix(wallet-dashboard): linter
cpl121 Nov 14, 2024
19b96fc
Merge branch 'tooling-dashboard/style-send-entry-screen' into tooling…
VmMad Nov 15, 2024
1a2f009
fix: amount format
VmMad Nov 15, 2024
ed42962
feat: Improve validation flow of sent screen
marc2332 Nov 15, 2024
69dab33
fmt
marc2332 Nov 15, 2024
3ee028a
Merge branch 'tooling-dashboard/style-send-entry-screen' into tooling…
VmMad Nov 18, 2024
b394e26
fix: format gas outside of hook
VmMad Nov 18, 2024
bcf0e17
Merge branch 'tooling-epic/dashboard-styling' into tooling-dashboard/…
cpl121 Nov 19, 2024
7eb6be4
fix(wallet-dashboard): fixes
cpl121 Nov 19, 2024
4164afa
fix(wallet-dashboard): linter
cpl121 Nov 19, 2024
eece95d
Merge branch 'tooling-epic/dashboard-styling' into tooling-dashboard/…
brancoder Nov 19, 2024
b889904
fix(wallet-dashboard): error to click max button
cpl121 Nov 19, 2024
6eca201
fix(wallet-dashboard): add setFieldValue in useEffect
cpl121 Nov 20, 2024
bc4e0a8
Merge branch 'tooling-dashboard/style-send-entry-screen' into tooling…
brancoder Nov 20, 2024
f01d18d
Merge branch 'tooling-epic/dashboard-styling' into tooling-dashboard/…
VmMad Nov 20, 2024
1292ff6
fix: gas ticker
VmMad Nov 20, 2024
a5f5f58
fix: lint
VmMad Nov 20, 2024
3ce05be
fix: improve codebase as reviewed
VmMad Nov 20, 2024
4737361
fix: remove log and use isPayAllIota from form values
VmMad Nov 21, 2024
7fded74
Merge branch 'develop' into tooling-dashboard/style-send-screen
brancoder Nov 21, 2024
0a152bb
Merge branch 'develop' into tooling-dashboard/style-send-screen
brancoder Nov 21, 2024
28e1b27
use formatted amount
VmMad Nov 21, 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
4 changes: 4 additions & 0 deletions apps/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,16 @@
"@amplitude/analytics-types": "^0.20.0",
"@growthbook/growthbook-react": "^1.0.0",
"@hookform/resolvers": "^3.9.0",
"@iota/apps-ui-kit": "workspace:*",
"@iota/dapp-kit": "workspace:*",
"@iota/iota-sdk": "workspace:*",
"@iota/kiosk": "workspace:*",
"@iota/ui-icons": "workspace:*",
"@sentry/react": "^7.59.2",
"@tanstack/react-query": "^5.50.1",
"bignumber.js": "^9.1.1",
"clsx": "^2.1.1",
"formik": "^2.4.2",
"qrcode.react": "^4.0.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand Down
67 changes: 67 additions & 0 deletions apps/core/src/components/Inputs/AddressInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) Mysten Labs, Inc.
// Modifications Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { Input, InputType } from '@iota/apps-ui-kit';
import { Close } from '@iota/ui-icons';
import { useIotaAddressValidation } from '../../hooks';
import React, { useCallback } from 'react';
import { useField, useFormikContext } from 'formik';

export interface AddressInputProps {
name: string;
disabled?: boolean;
placeholder?: string;
label?: string;
}

export function AddressInput({
name,
disabled,
placeholder = '0x...',
label = 'Enter Recipient Address',
}: AddressInputProps) {
const { validateField } = useFormikContext();
const [field, meta, helpers] = useField<string>(name);
const iotaAddressValidation = useIotaAddressValidation();

const formattedValue = iotaAddressValidation.cast(field.value);

const handleOnChange = useCallback(
async (e: React.ChangeEvent<HTMLInputElement>) => {
const address = e.currentTarget.value;
await helpers.setValue(iotaAddressValidation.cast(address));
validateField(name);
},
[name, iotaAddressValidation],
);

const clearAddress = () => {
helpers.setValue('');
};

return (
<Input
type={InputType.Text}
disabled={disabled}
placeholder={placeholder}
value={formattedValue}
name={field.name}
onBlur={field.onBlur}
label={label}
onChange={handleOnChange}
errorMessage={meta.error}
trailingElement={
formattedValue ? (
<button
onClick={clearAddress}
type="button"
className="flex items-center justify-center"
>
<Close />
</button>
) : undefined
}
/>
);
}
91 changes: 91 additions & 0 deletions apps/core/src/components/Inputs/SendTokenFormInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { ButtonPill, Input, InputType } from '@iota/apps-ui-kit';
import { CoinStruct } from '@iota/iota-sdk/client';
import { useFormatCoin, useGasBudgetEstimation } from '../../hooks';
import React, { useEffect } from 'react';
import { GAS_SYMBOL } from '../../constants';
import { useField, useFormikContext } from 'formik';
import { TokenForm } from '../../forms';
import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils';

export interface SendTokenInputProps {
coins: CoinStruct[];
symbol: string;
coinDecimals: number;
activeAddress: string;
to: string;
onActionClick: () => Promise<void>;
isMaxActionDisabled?: boolean;
name: string;
}

export function SendTokenFormInput({
coins,
to,
symbol,
coinDecimals,
activeAddress,
onActionClick,
isMaxActionDisabled,
name,
}: SendTokenInputProps) {
const { values, setFieldValue, isSubmitting, validateField } = useFormikContext<TokenForm>();

const { data: gasBudgetEstimation } = useGasBudgetEstimation({
coinDecimals,
coins: coins ?? [],
activeAddress,
to: to,
amount: values.amount,
isPayAllIota: values.isPayAllIota,
showGasSymbol: false,
});
evavirseda marked this conversation as resolved.
Show resolved Hide resolved
const [formattedGasBudgetEstimation] = useFormatCoin(gasBudgetEstimation, IOTA_TYPE_ARG);

const [field, meta, helpers] = useField<string>(name);
const errorMessage = meta.error;
const isActionButtonDisabled = isSubmitting || isMaxActionDisabled;

const renderAction = () => (
<ButtonPill disabled={isActionButtonDisabled} onClick={onActionClick}>
Max
</ButtonPill>
);

// gasBudgetEstimation should change when the amount above changes
useEffect(() => {
setFieldValue('gasBudgetEst', formattedGasBudgetEstimation, false);
}, [formattedGasBudgetEstimation, setFieldValue, values.amount]);

return (
<Input
type={InputType.NumericFormat}
name={field.name}
onBlur={field.onBlur}
value={field.value}
caption="Est. Gas Fees:"
placeholder="0.00"
label="Send Amount"
suffix={` ${symbol}`}
prefix={values.isPayAllIota ? '~ ' : undefined}
allowNegative={false}
errorMessage={errorMessage}
amountCounter={
!errorMessage
? coins && formattedGasBudgetEstimation !== '--'
? `${formattedGasBudgetEstimation} ${GAS_SYMBOL}`
: '--'
: undefined
}
Copy link
Contributor

Choose a reason for hiding this comment

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

with this change the GAS SYMBOL is always show even when there is no estimation:
before:
image

after:
image

trailingElement={renderAction()}
decimalScale={coinDecimals ? undefined : 0}
thousandSeparator
onValueChange={async (values) => {
await helpers.setValue(values.value);
validateField(name);
}}
/>
);
}
5 changes: 5 additions & 0 deletions apps/core/src/components/Inputs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export * from './AddressInput';
export * from './SendTokenFormInput';
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
// Modifications Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { ImageIcon, ImageIconSize } from '_app/shared/image-icon';
import { useCoinMetadata } from '@iota/core';
import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils';
import React from 'react';
import { useCoinMetadata } from '../../hooks';
import { IotaLogoMark } from '@iota/ui-icons';
import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils';
import { ImageIcon, ImageIconSize } from '../icon';
import cx from 'clsx';

interface NonIotaCoinProps {
Expand All @@ -28,19 +29,45 @@ function NonIotaCoin({ coinType, size = ImageIconSize.Full, rounded }: NonIotaCo
</div>
);
}

export interface CoinIconProps {
coinType: string;
size?: ImageIconSize;
rounded?: boolean;
hasCoinWrapper?: boolean;
cpl121 marked this conversation as resolved.
Show resolved Hide resolved
}

export function CoinIcon({ coinType, size = ImageIconSize.Full, rounded }: CoinIconProps) {
export function CoinIcon({
coinType,
size = ImageIconSize.Full,
rounded,
hasCoinWrapper,
}: CoinIconProps) {
const Component = hasCoinWrapper ? CoinIconWrapper : React.Fragment;
const coinWrapperProps = hasCoinWrapper ? { hasBorder: true, size: ImageIconSize.Large } : {};

return coinType === IOTA_TYPE_ARG ? (
<div className={cx(size)}>
<IotaLogoMark className="h-full w-full" />
</div>
<Component {...coinWrapperProps}>
<div className={cx(size, 'text-neutral-10')}>
<IotaLogoMark className="h-full w-full" />
</div>
</Component>
) : (
<NonIotaCoin rounded={rounded} size={size} coinType={coinType} />
);
}
type CoinIconWrapperProps = React.PropsWithChildren<Pick<CoinIconProps, 'size'>> & {
hasBorder?: boolean;
};
export function CoinIconWrapper({ children, size, hasBorder }: CoinIconWrapperProps) {
return (
<div
className={cx(
size,
hasBorder && 'border border-shader-neutral-light-8',
'flex items-center justify-center rounded-full bg-neutral-100',
)}
>
{children}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import React from 'react';
import {
Card,
CardAction,
Expand All @@ -10,11 +11,10 @@ import {
CardType,
ImageType,
} from '@iota/apps-ui-kit';
import { useFormatCoin } from '@iota/core';
import { CoinIcon, ImageIconSize } from '../';
import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils';
import { type ReactNode } from 'react';
import { ImageIconSize } from '../ImageIcon';
import { CoinIcon } from './CoinIcon';
import { useFormatCoin } from '../../hooks';

interface CoinItemProps {
coinType: string;
Expand All @@ -25,7 +25,7 @@ interface CoinItemProps {
usd?: number;
}

function CoinItem({
export function CoinItem({
coinType,
balance,
onClick,
Expand All @@ -39,8 +39,13 @@ function CoinItem({
return (
<Card type={CardType.Default} onClick={onClick}>
<CardImage type={ImageType.BgTransparent}>
<div className="flex h-10 w-10 items-center justify-center rounded-full ">
<CoinIcon coinType={coinType} rounded size={ImageIconSize.Small} />
<div className="flex h-10 w-10 items-center justify-center rounded-full">
<CoinIcon
coinType={coinType}
rounded
size={ImageIconSize.Small}
hasCoinWrapper
/>
</div>
</CardImage>
<CardBody
Expand All @@ -57,5 +62,3 @@ function CoinItem({
</Card>
);
}

export default CoinItem;
61 changes: 61 additions & 0 deletions apps/core/src/components/coin/CoinSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils';
import { Select, SelectOption } from '@iota/apps-ui-kit';
import { CoinBalance } from '@iota/iota-sdk/client';
import { useFormatCoin } from '../../hooks';
import { CoinIcon } from './CoinIcon';
import { ImageIconSize } from '../icon';

interface CoinSelectorProps {
activeCoinType: string;
coins: CoinBalance[];
onClick: (coinType: string) => void;
}

export function CoinSelector({
activeCoinType = IOTA_TYPE_ARG,
coins,
onClick,
}: CoinSelectorProps) {
const activeCoin = coins?.find(({ coinType }) => coinType === activeCoinType) ?? coins?.[0];
const initialValue = activeCoin?.coinType;
const coinsOptions: SelectOption[] =
coins?.map((coin) => ({
id: coin.coinType,
renderLabel: () => <CoinSelectOption coin={coin} />,
})) || [];

return (
<Select
label="Select Coins"
value={initialValue}
options={coinsOptions}
onValueChange={(coinType) => {
onClick(coinType);
}}
/>
);
}

function CoinSelectOption({ coin: { coinType, totalBalance } }: { coin: CoinBalance }) {
const [formatted, symbol, { data: coinMeta }] = useFormatCoin(totalBalance, coinType);
const isIota = coinType === IOTA_TYPE_ARG;

return (
<div className="flex w-full flex-row items-center justify-between">
<div className="flex flex-row items-center gap-x-md">
<div className="flex h-6 w-6 items-center justify-center">
<CoinIcon size={ImageIconSize.Small} coinType={coinType} rounded />
</div>
<span className="text-body-lg text-neutral-10">
{isIota ? (coinMeta?.name || '').toUpperCase() : coinMeta?.name || symbol}
</span>
</div>
<span className="text-label-lg text-neutral-60">
{formatted} {symbol}
</span>
</div>
);
}
6 changes: 6 additions & 0 deletions apps/core/src/components/coin/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export * from './CoinIcon';
export * from './CoinSelector';
export * from './CoinItem';
Loading
Loading