diff --git a/craco.config.cjs b/craco.config.cjs deleted file mode 100644 index d9919abb..00000000 --- a/craco.config.cjs +++ /dev/null @@ -1,62 +0,0 @@ -const webpack = require('webpack'); - -module.exports = { - webpack: { - configure: (config) => { - if(config.resolve.fallback === undefined) { - config.resolve.fallback = {}; - } - config.resolve.fallback.buffer = require.resolve('buffer'); - config.resolve.fallback.process = require.resolve('process/browser'); - config.resolve.fallback.stream = require.resolve('stream-browserify'); - config.resolve.fallback.util = require.resolve('text-encoding'); - config.resolve.fallback.fs = false; - config.resolve.fallback.crypto = require.resolve('crypto-browserify'); - config.resolve.fallback.http = require.resolve('stream-http'); - config.resolve.fallback.https = require.resolve('https-browserify'); - config.resolve.fallback.assert = require.resolve('assert/'); - config.resolve.fallback.url = require.resolve('url/'); - config.resolve.fallback.os = require.resolve('os-browserify/browser'); - - config.plugins.push( - new webpack.ProvidePlugin({ - process: 'process/browser.js', - Buffer: ['buffer', 'Buffer'] - }), - ); - - const fileLoaderRule = getFileLoaderRule(config.module.rules); - if(!fileLoaderRule) { - throw new Error("File loader not found"); - } - fileLoaderRule.exclude.push(/\.cjs$/); - - return config; - } - }, - jest: { - configure: (config) => { - config.transformIgnorePatterns = [ - "/node_modules/(?!(@polkadot|@babel|react-calendar|react-date-picker|pagedjs|@creativecommons|@logion)/).*" - ]; - - delete config.transform['^.+\\.(js|jsx|mjs|cjs|ts|tsx)$']; - config.transform["\\.[jt]sx?$"] = "babel-jest"; - - return config; - } - } -}; - -function getFileLoaderRule(rules) { - for(const rule of rules) { - if("oneOf" in rule) { - const found = getFileLoaderRule(rule.oneOf); - if(found) { - return found; - } - } else if(rule.test === undefined && rule.type === 'asset/resource') { - return rule; - } - } -} diff --git a/package.json b/package.json index e74a98ee..a6b85380 100644 --- a/package.json +++ b/package.json @@ -212,7 +212,8 @@ "snapshotFormat": { "escapeString": true, "printBasicPrototype": true - } + }, + "workerIdleMemoryLimit": "512MB" }, "babel": { "presets": [ diff --git a/src/common/AssetNameCell.tsx b/src/common/AssetNameCell.tsx new file mode 100644 index 00000000..7c327c8d --- /dev/null +++ b/src/common/AssetNameCell.tsx @@ -0,0 +1,15 @@ +import { CoinBalance } from "@logion/node-api"; +import CoinName from "src/components/coin/CoinName"; + +export interface Props { + balance: CoinBalance; +} + +export default function AssetNameCell(props: Props) { + + return ( +
+ ({ props.balance.available.prefix.symbol }{ props.balance.coin.symbol }) +
+ ); +} diff --git a/src/common/BalanceDetails.css b/src/common/BalanceDetails.css index c6743e94..2b7a03f9 100644 --- a/src/common/BalanceDetails.css +++ b/src/common/BalanceDetails.css @@ -13,9 +13,3 @@ display: inline-block; width: 130px; } - -.BalanceDetails.arc { - width: 300px; - padding-top: 40px; - padding-bottom: 30px; -} diff --git a/src/common/BalanceDetails.tsx b/src/common/BalanceDetails.tsx index 7fe4dd1e..f155c939 100644 --- a/src/common/BalanceDetails.tsx +++ b/src/common/BalanceDetails.tsx @@ -4,12 +4,11 @@ import "./BalanceDetails.css"; export interface Props { balance: CoinBalance; - type: 'arc' | 'linear'; } export default function BalanceDetails(props: Props) { return ( -
+
Available:
diff --git a/src/common/Gauge.css b/src/common/Gauge.css index afe6afc5..a8ac0de5 100644 --- a/src/common/Gauge.css +++ b/src/common/Gauge.css @@ -2,21 +2,6 @@ position: relative; } -.Gauge.arc .reading { - display: flex; - align-items: baseline; - justify-content: center; - background-image: url("../img/gauge.svg"); - background-size: contain; - background-position: center 100%; - background-repeat: no-repeat; - height: 240px; -} - -.Gauge.arc.light .reading { - background-image: url("../img/gauge-light.svg"); -} - .Gauge .reading .value-unit { display: flex; align-items: baseline; @@ -47,36 +32,20 @@ align-items: baseline; } -.Gauge.linear:before { - content: " "; - display: block; - background-image: url("../img/linear-gauge.svg"); - background-size: contain; - background-position: center 100%; - background-repeat: no-repeat; - width: 600px; - height: 15px; - margin-right: 20px; -} - -.Gauge.linear.light:before { - background-image: url("../img/linear-gauge-light.svg"); -} - -.Gauge.linear .reading { +.Gauge .reading { margin-right: 40px; } -.Gauge.linear .reading .value .integer-part { +.Gauge .reading .value .integer-part { line-height: 1; } -.Gauge.linear .reading .value-unit { +.Gauge .reading .value-unit { padding-top: 0; } @media (max-width: 1385px) { - .Gauge.linear:before { + .Gauge:before { width: 200px; } .Gauge .reading .value .integer-part { diff --git a/src/common/Gauge.test.tsx b/src/common/Gauge.test.tsx index 459a9e30..57440630 100644 --- a/src/common/Gauge.test.tsx +++ b/src/common/Gauge.test.tsx @@ -2,21 +2,15 @@ import { shallowRender } from '../tests'; import Gauge from './Gauge'; -test("renders arc", () => { - testGauge('arc'); -}); - -function testGauge(type: 'arc' | 'linear') { - const result = shallowRender(); - expect(result).toMatchSnapshot(); -} +describe("Gauge", () => { -test("renders linear", () => { - testGauge('linear'); + it("renders", () => { + const result = shallowRender(); + expect(result).toMatchSnapshot(); + }); }); diff --git a/src/common/Gauge.tsx b/src/common/Gauge.tsx index bbee4d1c..5de15d13 100644 --- a/src/common/Gauge.tsx +++ b/src/common/Gauge.tsx @@ -6,7 +6,6 @@ export interface Props { readingDecimalPart?: string, unit: string, level: number, - type: 'arc' | 'linear', } const RED: string = '#ea1f46'; @@ -26,7 +25,7 @@ export default function Gauge(props: Props) { } return ( -
+
{ const result = shallowRender( { } as BalanceState); const result = shallowRender( { } as BalanceState); const result = shallowRender( navigate(props.backPath) } > @@ -75,6 +74,14 @@ function Content(props: ContentProps) { const { coinId } = useParams<"coinId">(); const { width } = useResponsiveContext(); + const addressTitle = useMemo(() => { + if(props.type === "Wallet") { + return "Your address"; + } else { + return "Your vault address"; + } + }, [ props.type ]); + if (balances === null || transactions === null) { return ; } @@ -138,94 +145,78 @@ function Content(props: ContentProps) { <> - } + - No transaction yet } + + + +

+ { props.address } + +

+
+ + { + props.type === "Vault" && + + } + text={

+ You can use this Vault public address to transfer assets directly to your Vault.
+ Once transferred, your assets will be immediately protected by a Legal Officer + signature-based transfer protocol. +

} + /> + + } + + + + + +
+ + - { type === "Wallet" && - - - } - > - - - - - } { type === "Vault" && - + - - - - Current balance - - } - className="gauge-container" - > - - - } + + + +
No transaction yet } + /> + + + ); } -function TransactionsFrameTitle(props: { coin: Coin, vaultOutButton: boolean }) { - return ( - - - - Transaction history: ({ props.coin.symbol }) - - - { props.vaultOutButton && - - - - } - - ); -} - -function BalanceFrameTitle(props: {coin: Coin}) { - return ( - - Current { props.coin.symbol } balance - - ); -} - function FromToHeader() { return ( diff --git a/src/common/Wallet.css b/src/common/Wallet.css deleted file mode 100644 index 6e0be018..00000000 --- a/src/common/Wallet.css +++ /dev/null @@ -1,34 +0,0 @@ -.Wallet .asset-name-cell { - padding-top: 3px; -} - -.Wallet .asset-name-cell .name { - margin-left: 10px; -} - -.Wallet .not-available { - display: flex; - align-items: center; - justify-content: center; - color: #e6007a; - border: 2px solid #e6007a; - border-radius: 6px; - padding: 9px 20px; -} - -.Wallet .gauge-title { - display: flex; - justify-content: center; - margin-bottom: 40px; - align-items: center; -} - -.Wallet .gauge-title img { - margin-right: 10px; -} - -@media (max-width: 1599px) { - .Wallet .gauge-container { - margin-top: 30px; - } -} diff --git a/src/common/Wallet.test.tsx b/src/common/Wallet.test.tsx deleted file mode 100644 index b9c775e4..00000000 --- a/src/common/Wallet.test.tsx +++ /dev/null @@ -1,41 +0,0 @@ -jest.mock("./CommonContext"); - -import { shallowRender } from '../tests'; - -import Wallet, { Content } from './Wallet'; -import { DEFAULT_COIN_BALANCE, DEFAULT_TRANSACTION } from './TestData'; - -test('renders content', () => { - const result = shallowRender( coinId } - settingsPath='' - balances={ [] } - transactions={ [] } - address="" - />); - expect(result).toMatchSnapshot(); -}); - -test('renders loader with no data', () => { - const result = shallowRender( coinId } - settingsPath='' - balances={ null } - transactions={ null } - type="Wallet" - address="" - />); - expect(result).toMatchSnapshot(); -}); - -test('renders with all data', () => { - const result = shallowRender( coinId } - settingsPath='' - balances={ [ DEFAULT_COIN_BALANCE ] } - transactions={ [ DEFAULT_TRANSACTION ] } - type="Wallet" - address="" - />); - expect(result).toMatchSnapshot(); -}); diff --git a/src/common/Wallet.tsx b/src/common/Wallet.tsx deleted file mode 100644 index 129b38dc..00000000 --- a/src/common/Wallet.tsx +++ /dev/null @@ -1,191 +0,0 @@ -import Row from 'react-bootstrap/Row'; -import Col from 'react-bootstrap/Col'; -import { useNavigate } from 'react-router-dom'; -import { CoinBalance, Lgnt } from '@logion/node-api'; - -import { useCommonContext } from './CommonContext'; - -import { FullWidthPane } from './Dashboard'; -import Frame from './Frame'; -import Table, { Cell, DateCell, EmptyTableMessage, ActionCell } from './Table'; -import Button from './Button'; -import WalletGauge from './WalletGauge'; -import Loader from './Loader'; - -import './Wallet.css'; -import NetworkWarning from './NetworkWarning'; -import { useResponsiveContext } from './Responsive'; -import { buildTransactionType } from "./TransactionType"; -import { Transaction } from '@logion/client/dist/TransactionClient.js'; -import AmountCell from './AmountCell'; -import CoinIcon from 'src/components/coin/CoinIcon'; -import CoinName from 'src/components/coin/CoinName'; - -export type WalletType = "Wallet" | "Vault" - -export interface Props { - transactionsPath: (coinId: string) => string, - settingsPath: string, - balances: CoinBalance[] | null, - transactions: Transaction[] | null, - address: string, - vaultAddress?: string -} - -export default function Wallet(props: Props) { - const { colorTheme } = useCommonContext(); - - return ( - - - - ); -} - -export function Content(props: Props & { type: WalletType }) { - const { nodesDown } = useCommonContext(); - const { balances, transactions, vaultAddress } = props; - const navigate = useNavigate(); - const { width } = useResponsiveContext(); - - if(balances === null || balances.length === 0 || transactions === null) { - return ; - } - - const latestTransaction = transactions[0]; - - return ( - <> - { - nodesDown.length > 0 && - - - - - - } - -
- -
, - width: "70px", - }, - { - header: "Asset name", - render: balance => , - width: width({ - onSmallScreen: "180px", - otherwise: "200px" - }), - align: 'left', - }, - { - header: "Balance", - render: balance => , - width: width({ - onSmallScreen: "120px", - otherwise: "140px" - }), - align: 'right', - }, - { - header: "Last transaction date", - render: balance => - }, - { - header: "Last transaction type", - render: balance => - }, - { - header: "Last transaction amount", - render: balance => , - align: 'right', - }, - { - header: "", - render: balance => balance.coin.id !== 'dot' ? : , - width: width({ - onSmallScreen: "184px", - otherwise: "201px" - }), - } - ]} - data={ balances } - renderEmpty={ () => You have no asset yet } - /> - - - { props.type === "Wallet" && -
- - - Current { Lgnt.CODE } balance -
- } - className="gauge-container" - > - - - - } - - - ); -} - -interface AssetNameCellProps { - balance: CoinBalance, -} - -export function AssetNameCell(props: AssetNameCellProps) { - - return ( -
- ({ props.balance.available.prefix.symbol }{ props.balance.coin.symbol }) -
- ); -} - -function NotAvailable() { - - return ( -
- Not available (yet) -
- ); -} - -function transactionAmount(transaction: Transaction | undefined): Lgnt | null { - if (transaction === undefined) { - return null; - } else if(transaction.transferDirection === 'Received') { - return Lgnt.fromCanonical(BigInt(transaction.transferValue)); - } else if(transaction.transferDirection === 'Sent') { - return Lgnt.fromCanonical(BigInt(transaction.transferValue)).negate(); - } else { - return Lgnt.fromCanonical(BigInt(transaction.total)); - } -} diff --git a/src/common/WalletGauge.css b/src/common/WalletGauge.css index 9f599ef5..a899a05d 100644 --- a/src/common/WalletGauge.css +++ b/src/common/WalletGauge.css @@ -3,23 +3,18 @@ justify-content: center; } -.WalletGauge.arc .actions { - margin-bottom: 30px; - margin-left: auto; - margin-right: auto; -} - .WalletGauge .actions button:nth-child(n+2) { margin-left: 25px; } -.WalletGauge.linear { +.WalletGauge { display: flex; align-items: flex-end; justify-content: space-between; } -.WalletGauge.linear .actions { +.WalletGauge .actions { margin-bottom: 10px; flex-shrink: 0; + height: 36px; } diff --git a/src/common/WalletGauge.test.tsx b/src/common/WalletGauge.test.tsx index 1cfc976f..918d06b1 100644 --- a/src/common/WalletGauge.test.tsx +++ b/src/common/WalletGauge.test.tsx @@ -5,25 +5,22 @@ import { CoinBalance, Queries, Numbers } from '@logion/node-api'; import WalletGauge from './WalletGauge'; -test("renders arc", () => { - testWalletGauge('arc'); -}); - -function testWalletGauge(type: 'arc' | 'linear') { - const balance: CoinBalance = { - coin: Queries.getCoin('lgnt'), - available: new Numbers.PrefixedNumber("20.00", Numbers.MILLI), - reserved: new Numbers.PrefixedNumber("0", Numbers.NONE), - total: new Numbers.PrefixedNumber("20.00", Numbers.MILLI), - level: 0.5, - }; - const result = shallowRender(); - expect(result).toMatchSnapshot(); -} +describe("WalletGauge", () => { -test("renders linear", () => { - testWalletGauge('linear'); + it("renders", () => { + const balance: CoinBalance = { + coin: Queries.getCoin('lgnt'), + available: new Numbers.PrefixedNumber("20.00", Numbers.MILLI), + reserved: new Numbers.PrefixedNumber("0", Numbers.NONE), + total: new Numbers.PrefixedNumber("20.00", Numbers.MILLI), + level: 0.5, + }; + const result = shallowRender(); + expect(result).toMatchSnapshot(); + }); }); diff --git a/src/common/WalletGauge.tsx b/src/common/WalletGauge.tsx index 02e84a9a..81c2563b 100644 --- a/src/common/WalletGauge.tsx +++ b/src/common/WalletGauge.tsx @@ -16,12 +16,14 @@ import Alert from './Alert'; import './WalletGauge.css'; import BalanceDetails from './BalanceDetails'; import ExtrinsicSubmissionStateView from 'src/ExtrinsicSubmissionStateView'; +import VaultOutRequest from 'src/vault/VaultOutRequest'; export interface Props { balance: CoinBalance, - type: 'arc' | 'linear', vaultAddress?: string, - sendButton?: boolean + sendButton: boolean, + sendToVault: boolean, + withdrawFromVault: boolean, } interface TransferDialogState { @@ -88,20 +90,20 @@ export default function WalletGauge(props: Props) { } return ( -
+
- { (sendButton === undefined || sendButton) && + { (sendButton || props.sendToVault || props.withdrawFromVault) &&
+ { + sendButton && - { vaultAddress !== undefined && + } + { + props.sendToVault && vaultAddress !== undefined && } + { + props.withdrawFromVault && + + }
} -
-
-
-
- 99 -
-
- . - 00 -
-
-
- mLGNT -
-
-
-
-`; - -exports[`renders linear 1`] = ` -
- - -`; - -exports[`renders loader with no data 1`] = ``; - -exports[`renders with all data 1`] = ` - - -
- -
- - -
- - - - Current - LGNT - balance - -
- } - > - - - - - -`; diff --git a/src/common/__snapshots__/WalletGauge.test.tsx.snap b/src/common/__snapshots__/WalletGauge.test.tsx.snap index d8b486ae..d3d7551b 100644 --- a/src/common/__snapshots__/WalletGauge.test.tsx.snap +++ b/src/common/__snapshots__/WalletGauge.test.tsx.snap @@ -1,14 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`renders arc 1`] = ` +exports[`WalletGauge renders 1`] = `
-
- -
- -

- -

- - - } - id="destination" - label="Destination" - /> - - - - - LGNT - - - mLGNT - - - µLGNT - - - nLGNT - - - pLGNT - - - fLGNT - - - aLGNT - - - - Please enter a valid amount. - - - } - id="amount" - label="Amount" - noFeedback={true} - /> - - -
-
-`; - -exports[`renders linear 1`] = ` -
- -
-

Your logion Vault public address:

- - { vaultState.vaultAddress } - - - - - } - text={

- You can use this Vault public address to transfer assets directly to your Vault.
- Once transferred, your assets will be immediately protected by a Legal Officer - signature-based transfer protocol. -

} - /> - - - - - ) -} diff --git a/src/wallet-user/trust-protection/VaultRecoveryProcessTab.tsx b/src/wallet-user/trust-protection/VaultRecoveryProcessTab.tsx index bc2c3268..0adad799 100644 --- a/src/wallet-user/trust-protection/VaultRecoveryProcessTab.tsx +++ b/src/wallet-user/trust-protection/VaultRecoveryProcessTab.tsx @@ -7,7 +7,6 @@ import { CallCallback, useLogionChain } from "../../logion-chain"; import { useCommonContext } from "../../common/CommonContext"; import Button from "../../common/Button"; -import { AssetNameCell } from "../../common/Wallet"; import Dialog from "../../common/Dialog"; import Icon from "../../common/Icon"; import Table, { Cell, EmptyTableMessage } from "../../common/Table"; @@ -24,6 +23,7 @@ import "./VaultRecoveryProcessTab.css" import CoinIcon from "src/components/coin/CoinIcon"; import ExtrinsicSubmissionStateView from "src/ExtrinsicSubmissionStateView"; import AmountCell from "src/common/AmountCell"; +import AssetNameCell from "src/common/AssetNameCell"; interface FormValues { legalOfficer: string | null; diff --git a/src/wallet-user/trust-protection/WalletRecoveryProcessTab.tsx b/src/wallet-user/trust-protection/WalletRecoveryProcessTab.tsx index c9b43136..111f45de 100644 --- a/src/wallet-user/trust-protection/WalletRecoveryProcessTab.tsx +++ b/src/wallet-user/trust-protection/WalletRecoveryProcessTab.tsx @@ -5,7 +5,6 @@ import { BalanceState } from "@logion/client"; import Table, { EmptyTableMessage } from "../../common/Table"; import Icon from "../../common/Icon"; -import { AssetNameCell } from "../../common/Wallet"; import Button from "../../common/Button"; import Dialog from "../../common/Dialog"; import Alert from "../../common/Alert"; @@ -17,6 +16,7 @@ import CoinIcon from "src/components/coin/CoinIcon"; import ExtrinsicSubmissionStateView from "src/ExtrinsicSubmissionStateView"; import AmountCell from "src/common/AmountCell"; import { ExpectNewTransactionStatus, useCommonContext } from "src/common/CommonContext"; +import AssetNameCell from "src/common/AssetNameCell"; interface Props { vaultFirst: boolean