Skip to content

Commit

Permalink
[DDW-716] Implement static screens for signing dApp interaction trans…
Browse files Browse the repository at this point in the history
…actions (#2626)

* [DDW-716] Initial file structure

* [DDW-716] Single wallet

* [DDW-716] Assets Transaction Confirmation basic component structure

* [DDW-716] AssetsTransactionConfirmation progress

* [DDW-716] DappTransactionRequest progress

* [DDW-716] Tokens list progress

* [DDW-716] Missing token - init

* [DDW-716] Missing token

* [DDW-716] Missing token - progress

* [DDW-716] Missing token - progress

* [DDW-716] Add Wallet option

* [DDW-716] Toggle button

* [DDW-716] Code content

* [DDW-716] Translation manager

* [DDW-716] Request Notification - init

* [DDW-716] General Notification Story

* [DDW-716] Notification with buttons - init

* [DDW-716] Notification with buttons - progress

* [DDW-716] Invert Buttons

* [DDW-716] Sorting themes object

* [DDW-716] Remove click to close when there are buttons

* [DDW-716] Color buttons

* [DDW-716] Add uniqueId to the wallet token

* [DDW-716] Add uniqueId to the wallet token

* [DDW-716] Assets amounts

* [DDW-716] Insuficient Balance

* [DDW-716] Validation and copy

* [DDW-716] Translation

* [DDW-716] Raw additional data and metadata

* [DDW-716] Fix Netifly

* [DDW-716] Translation and popover positioning

* [DDW-716] Not enough ada error

* [DDW-716] Disable button when no Ada funds

* [DDW-716] Not enough ada copy and translation

* [DDW-716] JP translation

* [DDW-716] Translation and adjustments

* [DDW-716] JP translation

* [DDW-716] Code improvements

* [DDW-716] Code improvements

* [DDW-716] Improve error states

* [DDW-716] Styling adjustments

* [DDW-716] New copy

* [DDW-716] Lint fix

* [DDW-716] Notification styling adjustment

* [DDW-716] Styling adjustments

* [DDW-716] Styling adjustments and Ada name

* [DDW-716] Adjustments and new copy

* [DDW-716] Adjustments

* [DDW-716] Styling adjustment

* [DDW-716] Styling adjustment

* [DDW-716] Styling adjustment

* [DDW-716] Adjustments

* [DDW-716] Scroll styles and correct ada capitalization

* [DDW-716] Small style adjust

* [DDW-716] Error message adjustment

* [DDW-716] Last adjustments

* [DDW-716] adaErrorPopOver placement

* Revert "[DDW-716] adaErrorPopOver placement"

This reverts commit b112193.

* [DDW-716] Code improvements

* [DDW-716] Fix lint issue

* [DDW-716] Test build error

* [DDW-716] Fixes CHANGELOG

* [DDW-716] Remove Debug files

* [DDW-716] Run linters

Co-authored-by: Nikola Glumac <niglumac@gmail.com>
  • Loading branch information
thedanheller and nikolaglumac committed Sep 13, 2021
1 parent 47ee582 commit f52ed6f
Show file tree
Hide file tree
Showing 64 changed files with 2,793 additions and 1,068 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -3,6 +3,10 @@ Changelog

## vNext

### Features

- Implemented static screens for signing dApp interaction transactions ([PR 2626](https://github.com/input-output-hk/daedalus/pull/2626))

### Fixes

- Fixed some Japanese translations for the external currencies ([PR 2667](https://github.com/input-output-hk/daedalus/pull/2667))
Expand Down
2 changes: 1 addition & 1 deletion netlify.toml
@@ -1,2 +1,2 @@
[build]
environment = { YARN_VERSION = "1.22.4", NODE_VERSION = "12.19.0" }
environment = { YARN_VERSION = "1.22.4", NODE_VERSION = "12.19.0" }
18 changes: 12 additions & 6 deletions source/renderer/app/api/api.js
Expand Up @@ -2733,17 +2733,23 @@ const _createWalletFromServerData = action(
// Mapping asset items from server data
const walletAssets = {
available: assets.available.map((item) => {
const { policy_id: policyId, asset_name: assetName, quantity } = item;
const uniqueId = `${policyId}${assetName}`;
return {
policyId: item.policy_id,
assetName: item.asset_name,
quantity: new BigNumber(item.quantity.toString()),
uniqueId,
policyId,
assetName,
quantity: new BigNumber(quantity.toString()),
};
}),
total: assets.total.map((item) => {
const { policy_id: policyId, asset_name: assetName, quantity } = item;
const uniqueId = `${policyId}${assetName}`;
return {
policyId: item.policy_id,
assetName: item.asset_name,
quantity: new BigNumber(item.quantity.toString()),
uniqueId,
policyId,
assetName,
quantity: new BigNumber(quantity.toString()),
};
}),
};
Expand Down
2 changes: 1 addition & 1 deletion source/renderer/app/api/assets/types.js
Expand Up @@ -53,7 +53,7 @@ export type Token = {
assetName: string,
quantity: BigNumber,
address?: ?string,
uniqueId?: string,
uniqueId: string,
};

export type Tokens = Array<Token>;
Expand Down
3 changes: 3 additions & 0 deletions source/renderer/app/components/assets/Asset.js
Expand Up @@ -84,6 +84,7 @@ type Props = {
// In case it's not possible to calculate the container width
// this props defines after how many characters the `metadata.name` text will cut off
metadataNameChars?: number,
hasError?: boolean,
};

type State = {
Expand Down Expand Up @@ -175,12 +176,14 @@ export default class Asset extends Component<Props, State> {
small,
fullFingerprint,
hasWarning,
hasError,
} = this.props;
const { fingerprint, metadata, decimals, recommendedDecimals } = asset;
const { name } = metadata || {};
const contentStyles = classnames([
styles.pill,
small ? styles.small : null,
hasError ? styles.error : null,
]);
let warningPopOverMessage;
if (hasWarning) {
Expand Down
9 changes: 9 additions & 0 deletions source/renderer/app/components/assets/Asset.scss
Expand Up @@ -42,6 +42,15 @@
width: 11px;
}
}
&.error {
.fingerprint {
background-color: var(--theme-background-color-error);
color: var(--theme-color-error);
}
.metadataName {
color: var(--theme-color-error);
}
}
}

.fingerprint {
Expand Down
4 changes: 3 additions & 1 deletion source/renderer/app/components/assets/AssetContent.js
Expand Up @@ -75,6 +75,7 @@ type Props = {
highlightFingerprint?: boolean,
className?: string,
intl: intlShape.isRequired,
hasError?: boolean,
};

type ItemCopied = ?string;
Expand Down Expand Up @@ -130,13 +131,14 @@ const AssetContent = observer((props: Props) => {
);
};

const { asset, highlightFingerprint, className, intl } = props;
const { asset, highlightFingerprint, className, intl, hasError } = props;
const { fingerprint, policyId, assetName, metadata } = asset;
const { name, ticker, description } = metadata || {};
const componentStyles = classnames([
styles.component,
className,
highlightFingerprint ? styles.highlightFingerprint : null,
hasError ? styles.error : null,
]);
return (
<div className={componentStyles}>
Expand Down
23 changes: 18 additions & 5 deletions source/renderer/app/components/assets/AssetContent.scss
Expand Up @@ -58,24 +58,37 @@
width: calc(100% - 87px);
}
}
&.error {
* {
color: var(--theme-color-error);
}
.fingerprint {
background-color: var(--theme-background-color-error);
}
.copyIcon {
g {
fill: var(--theme-color-error);
}
svg > path {
stroke: var(--theme-color-error);
}
}
}
}

.fingerprint {
background: var(--theme-widgets-asset-token-fingerprint-background-color);
border-radius: 3px;
color: var(--theme-widgets-asset-token-text-color);
cursor: pointer;
display: inline-flex;
font-family: var(--font-medium);
font-size: 11px;
font-weight: 500;
line-height: 1.27;
padding: 4px;
white-space: nowrap;
}

.fingerprint {
cursor: pointer;
position: relative;
white-space: nowrap;

.copyIcon {
margin-left: 9px;
Expand Down
185 changes: 185 additions & 0 deletions source/renderer/app/components/assets/AssetTransactionConfirmation.js
@@ -0,0 +1,185 @@
// @flow
import React from 'react';
import classnames from 'classnames';
import BigNumber from 'bignumber.js';
import {
defineMessages,
intlShape,
injectIntl,
FormattedHTMLMessage,
} from 'react-intl';
import { PopOver } from 'react-polymorph/lib/components/PopOver';
import { observer } from 'mobx-react';
import SVGInline from 'react-svg-inline';
import questionMarkIcon from '../../assets/images/question-mark.inline.svg';
import styles from './AssetTransactionConfirmation.scss';
import type { AssetToken } from '../../api/assets/types';
import Asset from './Asset';
import { formattedTokenWalletAmount } from '../../utils/formatters';

const messages = defineMessages({
assetLabel: {
id: 'asset.transactionConfirmation.assetLabel',
defaultMessage: '!!!Token #{assetNumber}',
description: '"assetLabel" item on AssetTransactionConfirmation.',
},
unformattedAmountLabel: {
id: 'asset.transactionConfirmation.unformattedAmountLabel',
defaultMessage: '!!!unformatted amount',
description:
'"unformattedAmountLabel" item on AssetTransactionConfirmation.',
},
unformattedAmountMessageForHardwareWallets: {
id:
'asset.transactionConfirmation.unformattedAmountMessageForHardwareWallets',
defaultMessage:
'!!!Native assets may specify a number of decimal places, as defined in the Cardano token registry. Daedalus uses this information to format the amount that is being sent in the transaction.<br /><br />The native token unformatted amount is the amount without these decimal places. Please ensure that you verify both amounts, as some wallet software may not yet use the Cardano token registry.',
description:
'"unformattedAmountMessageForHardwareWallets" item on AssetTransactionConfirmation.',
},
unformattedAmountMessageForSoftwareWallets: {
id:
'asset.transactionConfirmation.unformattedAmountMessageForSoftwareWallets',
defaultMessage:
'!!!Native assets may specify a number of decimal places, as defined in the Cardano token registry. Daedalus uses this information to format the amount that is being sent in the transaction.<br /><br />The native token unformatted amount is the amount without these decimal places. Please ensure that you verify both amounts, as some wallet software may not yet use the Cardano token registry.<br /><br />The native token unformatted amount will be displayed on the hardware wallet device during transaction confirmation.',
description:
'"unformattedAmountMessageForSoftwareWallets" item on AssetTransactionConfirmation.',
},
missingToken: {
id: 'asset.transactionConfirmation.missingToken',
defaultMessage: '!!!There is no such token in this wallet',
description: '"missingToken" item on AssetTransactionConfirmation.',
},
insufficientBalance: {
id: 'asset.transactionConfirmation.insufficientBalance',
defaultMessage:
'!!!Insufficient funds. The balance of the token in this wallet is {formattedBalance} (Unformatted: {unformattedBalance})',
description: '"insufficientBalance" item on AssetTransactionConfirmation.',
},
});

type Props = {
asset: AssetToken,
assetNumber: number,
intl: intlShape.isRequired,
isHardwareWallet: boolean,
tokenIsMissing?: boolean,
insufficientBalance?: boolean,
amount: BigNumber,
};

const onCopyAssetItem = () => {};

const AssetTransactionConfirmation = observer((props: Props) => {
const {
assetNumber,
asset,
intl,
isHardwareWallet,
tokenIsMissing,
insufficientBalance,
amount,
} = props;
const hasError = tokenIsMissing || insufficientBalance;
const { metadata, decimals } = asset;
const formattedAmount = formattedTokenWalletAmount(
amount,
metadata,
decimals
);
const unformattedAmount = formattedTokenWalletAmount(amount, null, 0);

const formattedBalance = formattedTokenWalletAmount(
asset.quantity,
metadata,
decimals
);
const unformattedBalance = formattedTokenWalletAmount(
asset.quantity,
null,
0
);

const componentStyles = classnames(styles.component, {
[styles.error]: hasError,
});

const content = (
<>
<div className={styles.assetsContainer}>
<h3>
<span className={styles.assetLabel}>
{intl.formatMessage(messages.assetLabel, { assetNumber })}{' '}
</span>
<Asset
asset={asset}
onCopyAssetItem={onCopyAssetItem}
hasError={hasError}
/>
</h3>
<div className={styles.amountFeesWrapper}>
<div className={styles.amount}>{formattedAmount}</div>
</div>
</div>
<div className={styles.assetsContainer}>
<div className={styles.unformattedAmountLine} />
<div className={styles.unformattedAmountLabel}>
{intl.formatMessage(messages.unformattedAmountLabel)}
<PopOver
content={
<FormattedHTMLMessage
{...messages[
isHardwareWallet
? 'unformattedAmountMessageForHardwareWallets'
: 'unformattedAmountMessageForSoftwareWallets'
]}
tagName="div"
/>
}
>
<div className={styles.questionMark}>
<SVGInline svg={questionMarkIcon} />
</div>
</PopOver>
{':'}
</div>
<div className={styles.unformattedAmount}>{unformattedAmount}</div>
</div>
</>
);

if (tokenIsMissing) {
return (
<div className={componentStyles}>
<PopOver
content={intl.formatMessage(messages.missingToken)}
appendTo="parent"
placement="bottom"
>
{content}
</PopOver>
</div>
);
}

if (insufficientBalance) {
return (
<div className={componentStyles}>
<PopOver
content={intl.formatMessage(messages.insufficientBalance, {
formattedBalance,
unformattedBalance,
})}
appendTo="parent"
placement="bottom"
>
{content}
</PopOver>
</div>
);
}

return <div className={componentStyles}>{content}</div>;
});

export default injectIntl(AssetTransactionConfirmation);

0 comments on commit f52ed6f

Please sign in to comment.