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): style send entry screen #3807

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
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
1de3906
Merge branches 'tooling-dashboard/style-send-entry-screen' and 'tooli…
cpl121 Nov 11, 2024
be6d9f2
fix(wallet-dashboard): include interface with props and some fixes
cpl121 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
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
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
ed42962
feat: Improve validation flow of sent screen
marc2332 Nov 15, 2024
69dab33
fmt
marc2332 Nov 15, 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
0f1b54c
fix(wallet-dashboard): remove default exports
cpl121 Nov 20, 2024
ec1f344
fix(wallet-dashboard): lint
cpl121 Nov 20, 2024
2104062
fix(wallet-dashboard): build
cpl121 Nov 20, 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
}
/>
);
}
80 changes: 80 additions & 0 deletions apps/core/src/components/Inputs/SendTokenFormInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// 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 { useGasBudgetEstimation } from '../../hooks';
import { useEffect } from 'react';
import { useField, useFormikContext } from 'formik';
import { TokenForm } from '../../forms';

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 gasBudgetEstimation = useGasBudgetEstimation({
coinDecimals,
coins: coins ?? [],
activeAddress,
to: to,
amount: values.amount,
isPayAllIota: values.isPayAllIota,
});

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', gasBudgetEstimation, false);
}, [gasBudgetEstimation, setFieldValue, values.amount]);
cpl121 marked this conversation as resolved.
Show resolved Hide resolved

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 ? gasBudgetEstimation : '--') : undefined}
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,7 +29,6 @@ function NonIotaCoin({ coinType, size = ImageIconSize.Full, rounded }: NonIotaCo
</div>
);
}

export interface CoinIconProps {
coinType: string;
size?: ImageIconSize;
Expand All @@ -37,7 +37,7 @@ export interface CoinIconProps {

export function CoinIcon({ coinType, size = ImageIconSize.Full, rounded }: CoinIconProps) {
return coinType === IOTA_TYPE_ARG ? (
<div className={cx(size)}>
<div className={cx(size, 'text-neutral-10')}>
evavirseda marked this conversation as resolved.
Show resolved Hide resolved
<IotaLogoMark className="h-full w-full" />
</div>
) : (
Expand Down
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>
);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export { default as SendCoinPopup } from './SendCoinPopup';
export * from './CoinIcon';
export * from './CoinSelector';
evavirseda marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { useState } from 'react';
import Image from 'next/image';
import React, { useState } from 'react';
import cn from 'clsx';

export enum ImageIconSize {
Expand All @@ -12,67 +11,60 @@ export enum ImageIconSize {
Full = 'w-full h-full',
}

export interface ImageIconProps {
src: string | null | undefined;
label: string;
fallback: string;
alt?: string;
rounded?: boolean;
size?: ImageIconSize;
}

interface FallBackAvatarProps {
text: string;
str: string;
rounded?: boolean;
size?: ImageIconSize;
}
function FallBackAvatar({ text, rounded, size = ImageIconSize.Large }: FallBackAvatarProps) {
const textSize = (() => {

function FallBackAvatar({ str, rounded, size = ImageIconSize.Large }: FallBackAvatarProps) {
function generateTextSize(size: ImageIconSize) {
switch (size) {
case ImageIconSize.Small:
return 'text-label-sm';
case ImageIconSize.Medium:
return 'text-label-md';
case ImageIconSize.Large:
return 'text-title-md';
case ImageIconSize.Full:
return 'text-title-lg';
case ImageIconSize.Full:
return 'text-display-lg';
}
})();

}
return (
<div
className={cn(
'flex h-full w-full items-center justify-center bg-neutral-96 bg-gradient-to-r capitalize dark:bg-neutral-20',
{ 'rounded-full': rounded },
textSize,
'flex items-center justify-center bg-neutral-96 bg-gradient-to-r capitalize text-neutral-10 dark:bg-neutral-92 dark:text-primary-100',
{ 'rounded-full': rounded, 'rounded-lg': !rounded },
size,
generateTextSize(size),
)}
>
{text.slice(0, 2)}
{str?.slice(0, 2)}
</div>
);
}
export interface ImageIconProps {
src: string | null | undefined;
label: string;
fallbackText: string;
alt?: string;
rounded?: boolean;
size?: ImageIconSize;
}

export function ImageIcon({
src,
label,
alt = label,
fallbackText,
rounded,
size,
}: ImageIconProps) {
export function ImageIcon({ src, label, alt = label, fallback, rounded, size }: ImageIconProps) {
const [error, setError] = useState(false);
return (
<div role="img" aria-label={label} className={size}>
{error || !src ? (
<FallBackAvatar rounded={rounded} text={fallbackText} size={size} />
<FallBackAvatar rounded={rounded} str={fallback} size={size} />
) : (
<Image
<img
src={src}
alt={alt}
className="flex h-full w-full items-center justify-center rounded-full object-cover"
onError={() => setError(true)}
layout="fill"
objectFit="cover"
/>
)}
</div>
Expand Down
4 changes: 4 additions & 0 deletions apps/core/src/components/icon/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export * from './ImageIcon';
4 changes: 4 additions & 0 deletions apps/core/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@
// SPDX-License-Identifier: Apache-2.0

export * from './KioskClientProvider';

export * from './coin';
export * from './icon';
export * from './Inputs';
export * from './QR';
4 changes: 4 additions & 0 deletions apps/core/src/forms/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export * from './token';
Loading
Loading