diff --git a/.github/workflows/e2e-tests.yaml b/.github/workflows/e2e-tests.yaml index e9a02c1b33..8aeec2fecf 100644 --- a/.github/workflows/e2e-tests.yaml +++ b/.github/workflows/e2e-tests.yaml @@ -90,6 +90,10 @@ jobs: with: cmd: install + - name: Install additional dependencies rollup-linux-x64-gnu + if: steps.check_file_changed.outputs.frontend_changed == 'True' + run : sudo yarn add @rollup/rollup-linux-x64-gnu + - name: Build frontend pkg if: steps.check_file_changed.outputs.frontend_changed == 'True' run: sudo yarn build:frontend diff --git a/idea/frontend/.eslintrc b/idea/frontend/.eslintrc index 77cf41759a..6f93750334 100644 --- a/idea/frontend/.eslintrc +++ b/idea/frontend/.eslintrc @@ -42,7 +42,13 @@ "rules": { "@typescript-eslint/no-unsafe-enum-comparison": "off", - "@typescript-eslint/no-floating-promises": "off" + "@typescript-eslint/no-floating-promises": "off", + "react/display-name": "off", + "import/no-named-as-default": "off", + + // airbnb cfg + "no-shadow": "error", + "no-shadow-restricted-names": "error" } } ] diff --git a/idea/frontend/package.json b/idea/frontend/package.json index fb7141cd63..64c701d38b 100644 --- a/idea/frontend/package.json +++ b/idea/frontend/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@gear-js/api": "0.34.0", - "@gear-js/react-hooks": "0.8.1", + "@gear-js/react-hooks": "workspace:^", "@gear-js/ui": "0.5.19", "@hcaptcha/react-hcaptcha": "1.8.1", "@mantine/form": "7.1.2", @@ -66,7 +66,7 @@ "sass": "1.69.1", "typescript": "5.2.2", "vite": "4.4.11", - "vite-plugin-eslint": "1.8.1", + "vite-plugin-checker": "0.6.2", "vite-plugin-node-polyfills": "0.15.0", "vite-plugin-svgr": "4.1.0" }, diff --git a/idea/frontend/src/app/providers/modal/consts.ts b/idea/frontend/src/app/providers/modal/consts.ts index 696dd50eb9..55c80b702a 100644 --- a/idea/frontend/src/app/providers/modal/consts.ts +++ b/idea/frontend/src/app/providers/modal/consts.ts @@ -1,5 +1,4 @@ import { NetworkModal } from '@/widgets/networkModal'; -import { AccountsModal } from '@/widgets/accountsModal'; import { TransactionModal } from '@/widgets/transactionModal'; import { UploadFileModal } from '@/widgets/uploadFileModal'; import { UploadMetadataModal } from '@/widgets/uploadMetadataModal'; @@ -8,7 +7,6 @@ import { TransferBalanceModal } from '@/widgets/transferBalanceModal'; const MODALS = { network: NetworkModal, - accounts: AccountsModal, metadata: UploadMetadataModal, uploadFile: UploadFileModal, transaction: TransactionModal, diff --git a/idea/frontend/src/features/voucher/ui/is-prepaid-checkbox/is-prepaid-checkbox.tsx b/idea/frontend/src/features/voucher/ui/is-prepaid-checkbox/is-prepaid-checkbox.tsx index 56f3c3d56f..7bc1ce4112 100644 --- a/idea/frontend/src/features/voucher/ui/is-prepaid-checkbox/is-prepaid-checkbox.tsx +++ b/idea/frontend/src/features/voucher/ui/is-prepaid-checkbox/is-prepaid-checkbox.tsx @@ -1,6 +1,6 @@ import { HexString } from '@gear-js/api'; import { InputWrapper, Checkbox } from '@gear-js/ui'; -import { useAccount, useBalanceFormat, useVoucher } from '@gear-js/react-hooks'; +import { useBalanceFormat, useVoucher } from '@gear-js/react-hooks'; import { FieldRenderProps, useField } from 'react-final-form'; import styles from './is-prepaid-checkbox.module.scss'; @@ -10,13 +10,14 @@ type Props = { }; const IsPrepaidCheckbox = ({ programId }: Props) => { - const { account } = useAccount(); const { isVoucherExists, voucherBalance } = useVoucher(programId); - const { getFormattedBalanceValue } = useBalanceFormat(); + const { getFormattedBalance } = useBalanceFormat(); const field = useField('isPrepaid', { type: 'checkbox' }); const input = field.input as Omit, 'type'>; // assert cuz Checkbox type is 'switch' | undefined + const formattedBalance = voucherBalance ? getFormattedBalance(voucherBalance) : undefined; + return isVoucherExists ? ( { - ( {voucherBalance && getFormattedBalanceValue(voucherBalance).toFixed()} {account?.balance.unit}) + ( {formattedBalance?.value} {formattedBalance?.unit}) diff --git a/idea/frontend/src/features/voucher/ui/voucher-table/voucher-table.tsx b/idea/frontend/src/features/voucher/ui/voucher-table/voucher-table.tsx index 93de67a6b7..74be582c06 100644 --- a/idea/frontend/src/features/voucher/ui/voucher-table/voucher-table.tsx +++ b/idea/frontend/src/features/voucher/ui/voucher-table/voucher-table.tsx @@ -1,5 +1,5 @@ import { HexString } from '@gear-js/api'; -import { useAccount, useBalanceFormat, useVoucher } from '@gear-js/react-hooks'; +import { useBalanceFormat, useVoucher } from '@gear-js/react-hooks'; import VoucherPlaceholderSVG from '@/features/voucher/assets/voucher-placeholder.svg?react'; import { ContentLoader } from '@/shared/ui/contentLoader'; @@ -14,12 +14,12 @@ type Props = { }; const VoucherTable = withAccount(({ programId }: Props) => { - const { account } = useAccount(); - const { getFormattedBalanceValue } = useBalanceFormat(); - const { isVoucherReady, isVoucherExists, voucherBalance } = useVoucher(programId); + const { getFormattedBalance } = useBalanceFormat(); + const status = isVoucherExists ? BulbStatus.Success : BulbStatus.Error; const text = isVoucherExists ? 'Available' : 'Not available'; + const balance = voucherBalance ? getFormattedBalance(voucherBalance) : undefined; return isVoucherReady ? ( @@ -28,8 +28,8 @@ const VoucherTable = withAccount(({ programId }: Props) => { - {voucherBalance && getFormattedBalanceValue(voucherBalance).toFixed()} - {account?.balance.unit} + {balance?.value} + {balance?.unit}
) : ( diff --git a/idea/frontend/src/shared/assets/images/wallets/enkrypt.svg b/idea/frontend/src/features/wallet/assets/enkrypt.svg similarity index 100% rename from idea/frontend/src/shared/assets/images/wallets/enkrypt.svg rename to idea/frontend/src/features/wallet/assets/enkrypt.svg diff --git a/idea/frontend/src/shared/assets/images/wallets/polkadot.svg b/idea/frontend/src/features/wallet/assets/polkadot.svg similarity index 100% rename from idea/frontend/src/shared/assets/images/wallets/polkadot.svg rename to idea/frontend/src/features/wallet/assets/polkadot.svg diff --git a/idea/frontend/src/shared/assets/images/wallets/subwallet.svg b/idea/frontend/src/features/wallet/assets/subwallet.svg similarity index 100% rename from idea/frontend/src/shared/assets/images/wallets/subwallet.svg rename to idea/frontend/src/features/wallet/assets/subwallet.svg diff --git a/idea/frontend/src/shared/assets/images/wallets/talisman.svg b/idea/frontend/src/features/wallet/assets/talisman.svg similarity index 100% rename from idea/frontend/src/shared/assets/images/wallets/talisman.svg rename to idea/frontend/src/features/wallet/assets/talisman.svg diff --git a/idea/frontend/src/features/wallet/consts.ts b/idea/frontend/src/features/wallet/consts.ts new file mode 100644 index 0000000000..ab279d78de --- /dev/null +++ b/idea/frontend/src/features/wallet/consts.ts @@ -0,0 +1,17 @@ +import { Entries } from '@/shared/types'; + +import PolkadotSVG from './assets/polkadot.svg?react'; +import SubwalletSVG from './assets/subwallet.svg?react'; +import TalismanSVG from './assets/talisman.svg?react'; +import EnkryptSVG from './assets/enkrypt.svg?react'; + +const WALLET = { + 'polkadot-js': { name: 'Polkadot JS', SVG: PolkadotSVG }, + 'subwallet-js': { name: 'SubWallet', SVG: SubwalletSVG }, + talisman: { name: 'Talisman', SVG: TalismanSVG }, + enkrypt: { name: 'Enkrypt', SVG: EnkryptSVG }, +}; + +const WALLETS = Object.entries(WALLET) as Entries; + +export { WALLET, WALLETS }; diff --git a/idea/frontend/src/features/wallet/hooks/index.ts b/idea/frontend/src/features/wallet/hooks/index.ts new file mode 100644 index 0000000000..586e954bce --- /dev/null +++ b/idea/frontend/src/features/wallet/hooks/index.ts @@ -0,0 +1,21 @@ +import { useAccount } from '@gear-js/react-hooks'; +import { useState } from 'react'; + +import { WALLET } from '../consts'; +import { WalletId } from '../types'; + +function useWallet() { + const { account, accounts } = useAccount(); + + const [walletId, setWalletId] = useState(account?.meta.source as WalletId | undefined); + const wallet = walletId ? WALLET[walletId] : undefined; + + const getWalletAccounts = (id: WalletId) => accounts?.filter(({ meta }) => meta.source === id); + const walletAccounts = walletId ? getWalletAccounts(walletId) : undefined; + + const resetWalletId = () => setWalletId(undefined); + + return { wallet, walletAccounts, setWalletId, resetWalletId, getWalletAccounts }; +} + +export { useWallet }; diff --git a/idea/frontend/src/features/wallet/index.ts b/idea/frontend/src/features/wallet/index.ts new file mode 100644 index 0000000000..e3bcb1cbd9 --- /dev/null +++ b/idea/frontend/src/features/wallet/index.ts @@ -0,0 +1,3 @@ +import { Wallet } from './ui'; + +export { Wallet }; diff --git a/idea/frontend/src/widgets/accountsModal/model/types.ts b/idea/frontend/src/features/wallet/types.ts similarity index 100% rename from idea/frontend/src/widgets/accountsModal/model/types.ts rename to idea/frontend/src/features/wallet/types.ts diff --git a/idea/frontend/src/shared/ui/accountButton/AccountButton.module.scss b/idea/frontend/src/features/wallet/ui/account-button/account-button.module.scss similarity index 100% rename from idea/frontend/src/shared/ui/accountButton/AccountButton.module.scss rename to idea/frontend/src/features/wallet/ui/account-button/account-button.module.scss diff --git a/idea/frontend/src/shared/ui/accountButton/AccountButton.tsx b/idea/frontend/src/features/wallet/ui/account-button/account-button.tsx similarity index 92% rename from idea/frontend/src/shared/ui/accountButton/AccountButton.tsx rename to idea/frontend/src/features/wallet/ui/account-button/account-button.tsx index d564d6dcdb..bf17b5d1f5 100644 --- a/idea/frontend/src/shared/ui/accountButton/AccountButton.tsx +++ b/idea/frontend/src/features/wallet/ui/account-button/account-button.tsx @@ -2,7 +2,7 @@ import Identicon from '@polkadot/react-identicon'; import clsx from 'clsx'; import { buttonStyles } from '@gear-js/ui'; -import styles from './AccountButton.module.scss'; +import styles from './account-button.module.scss'; type Props = { name?: string; diff --git a/idea/frontend/src/features/wallet/ui/account-button/index.ts b/idea/frontend/src/features/wallet/ui/account-button/index.ts new file mode 100644 index 0000000000..ae1232b095 --- /dev/null +++ b/idea/frontend/src/features/wallet/ui/account-button/index.ts @@ -0,0 +1,3 @@ +import { AccountButton } from './account-button'; + +export { AccountButton }; diff --git a/idea/frontend/src/features/wallet/ui/accounts-modal/accounts-modal.module.scss b/idea/frontend/src/features/wallet/ui/accounts-modal/accounts-modal.module.scss new file mode 100644 index 0000000000..7bfeb7f630 --- /dev/null +++ b/idea/frontend/src/features/wallet/ui/accounts-modal/accounts-modal.module.scss @@ -0,0 +1,129 @@ +@use '@gear-js/ui/breakpoints' as *; +@use '@gear-js/ui/variables' as ui; +@use '@gear-js/ui/headings' as *; +@use '@/shared/assets/styles/variables' as *; +@use '@/shared/assets/styles/mixins' as *; + +.simplebar { + @include customSimplebar; + max-height: 50vh; + margin-right: -16px; + + :global(.simplebar-content) { + margin-right: 16px; + } +} + +.modal { + position: relative; + + &:not(.empty) { + padding-bottom: 102px; + } +} + +.footer { + position: absolute; // workaround cuz footer has different bg color + bottom: 0; + left: 0; + width: 100%; + padding: 24px 32px; + border-radius: 0 0 $borderRadiusMedium $borderRadiusMedium; + display: flex; + background-color: $bgColor4; +} + +.logoutButton { + margin-left: auto; +} + +.list { + @include childrenMargin(16px); + + .accountButton { + width: 100%; + font-weight: 500; + text-transform: uppercase; + padding: 16px 40px; + + &.active { + background: $successColor; + } + } + + .accountName { + font-size: $fontSizeMedium; + padding: 0 5px; + } + + .accountIcon { + @include md { + display: none !important; + } + } +} + +.accountItem { + @include childrenMargin(10px, right); + + display: flex; + align-items: center; + + .accountButton { + flex: 1 1; + + &.active { + cursor: default; + background-color: $successColor; + + &:hover { + background-color: $successColor; + } + } + } +} + +.button { + display: flex; + justify-content: space-between; + background-color: ui.$buttonColorLight; + letter-spacing: 0.08em; + + span { + display: flex; + align-items: center; + } + + &:hover { + background-color: ui.$buttonColorLightHover; + } + + &:not(.enabled) { + pointer-events: none; + + .status { + opacity: 0.6; + } + } + + .status { + text-align: right; + + &Text { + font-size: 12px; + line-height: 14px; + } + + &Accounts { + font-size: 10px; + line-height: 10px; + color: $successColor; + } + } + + .connectIcon { + width: 12px; + height: 12px; + margin-left: 7px; + } +} diff --git a/idea/frontend/src/features/wallet/ui/accounts-modal/accounts-modal.tsx b/idea/frontend/src/features/wallet/ui/accounts-modal/accounts-modal.tsx new file mode 100644 index 0000000000..03edd6c634 --- /dev/null +++ b/idea/frontend/src/features/wallet/ui/accounts-modal/accounts-modal.tsx @@ -0,0 +1,151 @@ +import { decodeAddress } from '@gear-js/api'; +import { useAccount, useAlert } from '@gear-js/react-hooks'; +import { Button, Modal, buttonStyles } from '@gear-js/ui'; +import type { InjectedAccountWithMeta } from '@polkadot/extension-inject/types'; +import { isWeb3Injected } from '@polkadot/extension-dapp'; +import cx from 'clsx'; +import SimpleBar from 'simplebar-react'; + +import { copyToClipboard } from '@/shared/helpers'; +import LogoutSVG from '@/shared/assets/images/actions/logout.svg?react'; +import CopyKeySVG from '@/shared/assets/images/actions/copyKey.svg?react'; + +import { AccountButton } from '../account-button'; +import { useWallet } from '../../hooks'; +import { WALLETS } from '../../consts'; +import styles from './accounts-modal.module.scss'; + +type Props = { + close: () => void; +}; + +const AccountsModal = ({ close }: Props) => { + const { account, extensions, login, logout } = useAccount(); + + const alert = useAlert(); + + const { wallet, walletAccounts, setWalletId, resetWalletId, getWalletAccounts } = useWallet(); + + const handleLogoutClick = () => { + logout(); + close(); + }; + + const handleAccountClick = (newAccount: InjectedAccountWithMeta) => { + login(newAccount); + close(); + }; + + const modalClassName = cx(styles.modal, !isWeb3Injected && styles.empty); + const heading = wallet ? 'Connect account' : 'Choose Wallet'; + + const getWallets = () => + WALLETS.map(([id, { SVG, name }]) => { + const isEnabled = extensions?.some((extension) => extension.name === id); + + const accountsCount = getWalletAccounts(id)?.length; + const accountsStatus = `${accountsCount} ${accountsCount === 1 ? 'account' : 'accounts'}`; + + const buttonClassName = cx( + buttonStyles.button, + buttonStyles.large, + buttonStyles.block, + styles.button, + isEnabled && styles.enabled, + ); + + return ( +
  • + +
  • + ); + }); + + const getAccounts = () => + walletAccounts?.map((_account) => { + const { address, meta } = _account; + const isActive = address === account?.address; + + const handleClick = () => { + if (isActive) return; + handleAccountClick(_account); + }; + + const handleCopy = () => { + const decodedAddress = decodeAddress(address); + copyToClipboard(decodedAddress, alert); + }; + + const accountBtnClasses = cx( + buttonStyles.large, + styles.accountButton, + isActive ? styles.active : buttonStyles.light, + ); + + return ( +
  • + +
  • + ); + }); + + return ( + + {isWeb3Injected ? ( + <> + + {!wallet &&
      {getWallets()}
    } + + {!!wallet && + (walletAccounts?.length ? ( +
      {getAccounts()}
    + ) : ( +

    + No accounts found. Please open your Polkadot extension and create a new account or import existing. + Then reload this page. +

    + ))} +
    + +
    + {wallet &&
    + + ) : ( +

    + Wallet extension was not found or disconnected. Please check how to install a supported wallet and create an + account{' '} + + here + + . +

    + )} +
    + ); +}; + +export { AccountsModal }; diff --git a/idea/frontend/src/features/wallet/ui/accounts-modal/index.ts b/idea/frontend/src/features/wallet/ui/accounts-modal/index.ts new file mode 100644 index 0000000000..b514cf7463 --- /dev/null +++ b/idea/frontend/src/features/wallet/ui/accounts-modal/index.ts @@ -0,0 +1,3 @@ +import { AccountsModal } from './accounts-modal'; + +export { AccountsModal }; diff --git a/idea/frontend/src/widgets/header/ui/balanceInfo/BalanceInfo.module.scss b/idea/frontend/src/features/wallet/ui/balance/balance.module.scss similarity index 73% rename from idea/frontend/src/widgets/header/ui/balanceInfo/BalanceInfo.module.scss rename to idea/frontend/src/features/wallet/ui/balance/balance.module.scss index 1097ac3249..451eaa6dd3 100644 --- a/idea/frontend/src/widgets/header/ui/balanceInfo/BalanceInfo.module.scss +++ b/idea/frontend/src/features/wallet/ui/balance/balance.module.scss @@ -1,5 +1,5 @@ -@use '@/shared/assets/styles/_shared.scss' as *; -@use '@/shared/assets/styles/_variables.scss' as *; +@use '@/shared/assets/styles/shared' as *; +@use '@/shared/assets/styles/variables' as *; .balanceSection { .title { diff --git a/idea/frontend/src/features/wallet/ui/balance/balance.tsx b/idea/frontend/src/features/wallet/ui/balance/balance.tsx new file mode 100644 index 0000000000..153ba69749 --- /dev/null +++ b/idea/frontend/src/features/wallet/ui/balance/balance.tsx @@ -0,0 +1,27 @@ +import { useAccount, useBalance, useBalanceFormat } from '@gear-js/react-hooks'; +import cx from 'clsx'; + +import headerStyles from '@/widgets/header/ui/Header.module.scss'; + +import styles from './balance.module.scss'; + +const Balance = () => { + const { account } = useAccount(); + const { balance } = useBalance(account?.address); + const { getFormattedBalance } = useBalanceFormat(); + + const formattedBalance = balance ? getFormattedBalance(balance) : undefined; + + return formattedBalance ? ( +
    +

    Balance:

    + +

    + {formattedBalance.value} + {formattedBalance.unit} +

    +
    + ) : null; +}; + +export { Balance }; diff --git a/idea/frontend/src/features/wallet/ui/balance/index.ts b/idea/frontend/src/features/wallet/ui/balance/index.ts new file mode 100644 index 0000000000..d35c5d0857 --- /dev/null +++ b/idea/frontend/src/features/wallet/ui/balance/index.ts @@ -0,0 +1,3 @@ +import { Balance } from './balance'; + +export { Balance }; diff --git a/idea/frontend/src/features/wallet/ui/index.ts b/idea/frontend/src/features/wallet/ui/index.ts new file mode 100644 index 0000000000..c7f5f4752f --- /dev/null +++ b/idea/frontend/src/features/wallet/ui/index.ts @@ -0,0 +1,3 @@ +import { Wallet } from './wallet'; + +export { Wallet }; diff --git a/idea/frontend/src/features/wallet/ui/wallet/index.ts b/idea/frontend/src/features/wallet/ui/wallet/index.ts new file mode 100644 index 0000000000..c7f5f4752f --- /dev/null +++ b/idea/frontend/src/features/wallet/ui/wallet/index.ts @@ -0,0 +1,3 @@ +import { Wallet } from './wallet'; + +export { Wallet }; diff --git a/idea/frontend/src/features/wallet/ui/wallet/wallet.module.scss b/idea/frontend/src/features/wallet/ui/wallet/wallet.module.scss new file mode 100644 index 0000000000..59c4956111 --- /dev/null +++ b/idea/frontend/src/features/wallet/ui/wallet/wallet.module.scss @@ -0,0 +1,22 @@ +@use '@/shared/assets/styles/shared' as *; +@use '@/shared/assets/styles/mixins' as *; +@use '@/shared/assets/styles/variables' as *; + +.wallet { + @include childrenMargin(16px, right); + display: flex; + align-items: center; +} + +.accountBtn { + background-color: $bgColorSecondary; + + &:hover { + background-color: $bgColorTertiary; + } + + & > div { + // hacky, but need to override Identicon's 'cursor: copy' + cursor: inherit !important; + } +} diff --git a/idea/frontend/src/features/wallet/ui/wallet/wallet.tsx b/idea/frontend/src/features/wallet/ui/wallet/wallet.tsx new file mode 100644 index 0000000000..02f9eea833 --- /dev/null +++ b/idea/frontend/src/features/wallet/ui/wallet/wallet.tsx @@ -0,0 +1,47 @@ +import { useAccount } from '@gear-js/react-hooks'; +import { Button, buttonStyles } from '@gear-js/ui'; +import cx from 'clsx'; +import { useState } from 'react'; + +import { OnboardingTooltip } from '@/shared/ui/onboardingTooltip'; +import SubstrateSVG from '@/shared/assets/images/logos/substrate.svg?react'; + +import { AccountsModal } from '../accounts-modal'; +import { AccountButton } from '../account-button'; +import { Balance } from '../balance'; +import styles from './wallet.module.scss'; + +const Wallet = () => { + const { account, isAccountReady } = useAccount(); + + const [isModalOpen, setIsModalOpen] = useState(false); + + const openModal = () => setIsModalOpen(true); + const closeModal = () => setIsModalOpen(false); + + return ( + <> +
    + + + {isAccountReady && + (account ? ( + + ) : ( + +
    + + {isModalOpen && } + + ); +}; + +export { Wallet }; diff --git a/idea/frontend/src/shared/config/consts.ts b/idea/frontend/src/shared/config/consts.ts index 1ad98f48ee..82a08fb894 100644 --- a/idea/frontend/src/shared/config/consts.ts +++ b/idea/frontend/src/shared/config/consts.ts @@ -8,7 +8,7 @@ const NODE_ADRESS_URL_PARAM = 'node'; const EXAMPLES_HREF = 'https://www.gear-tech.io/developers'; const DEFAULT_LIMIT = 20; -const GEAR_BALANCE_TRANSFER_VALUE = process.env.VITE_DEFAULT_TRANSFER_BALANCE_VALUE as string; +const GEAR_BALANCE_TRANSFER_VALUE = import.meta.env.VITE_DEFAULT_TRANSFER_BALANCE_VALUE as string; const ACCOUNT_ERRORS = { WALLET_NOT_CONNECTED: 'Wallet not connected', @@ -32,7 +32,6 @@ enum LocalStorage { Account = 'account', Genesis = 'genesis', HideWelcomeBanner = 'hide_welcome_banner', - Wallet = 'wallet', IsNewUser = 'isNewUser', } diff --git a/idea/frontend/src/shared/types/index.ts b/idea/frontend/src/shared/types/index.ts index 92b38c3d8a..0887814713 100644 --- a/idea/frontend/src/shared/types/index.ts +++ b/idea/frontend/src/shared/types/index.ts @@ -1,4 +1,10 @@ import { PathParams } from './routes'; import { IBase } from './entities'; -export type { PathParams, IBase }; +// in case Object.entries return value is immutable +// ref: https://stackoverflow.com/a/60142095 +type Entries = { + [K in keyof T]: [K, T[K]]; +}[keyof T][]; + +export type { PathParams, IBase, Entries }; diff --git a/idea/frontend/src/shared/ui/accountButton/index.ts b/idea/frontend/src/shared/ui/accountButton/index.ts deleted file mode 100644 index e37e3e8978..0000000000 --- a/idea/frontend/src/shared/ui/accountButton/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { AccountButton } from './AccountButton'; - -export { AccountButton }; diff --git a/idea/frontend/src/widgets/accountsModal/hooks/index.ts b/idea/frontend/src/widgets/accountsModal/hooks/index.ts deleted file mode 100644 index 96381e52cc..0000000000 --- a/idea/frontend/src/widgets/accountsModal/hooks/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { useState } from 'react'; - -import { LocalStorage } from '@/shared/config'; - -import { WALLET, WalletId } from '../model'; - -function useWallet() { - const [walletId, setWalletId] = useState(localStorage[LocalStorage.Wallet]); - - const resetWallet = () => setWalletId(undefined); - - const wallet = walletId ? WALLET[walletId] : undefined; - - return { wallet, walletId, switchWallet: setWalletId, resetWallet }; -} - -export { useWallet }; diff --git a/idea/frontend/src/widgets/accountsModal/index.ts b/idea/frontend/src/widgets/accountsModal/index.ts deleted file mode 100644 index 57a728e34d..0000000000 --- a/idea/frontend/src/widgets/accountsModal/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { AccountsModal } from './ui/AccountsModal'; - -export { AccountsModal }; diff --git a/idea/frontend/src/widgets/accountsModal/model/consts.ts b/idea/frontend/src/widgets/accountsModal/model/consts.ts deleted file mode 100644 index 5816a1f08a..0000000000 --- a/idea/frontend/src/widgets/accountsModal/model/consts.ts +++ /dev/null @@ -1,15 +0,0 @@ -import PolkadotSVG from '@/shared/assets/images/wallets/polkadot.svg?react'; -import SubwalletSVG from '@/shared/assets/images/wallets/subwallet.svg?react'; -import TalismanSVG from '@/shared/assets/images/wallets/talisman.svg?react'; -import EnkryptSVG from '@/shared/assets/images/wallets/enkrypt.svg?react'; - -const WALLET = { - 'polkadot-js': { name: 'Polkadot JS', icon: PolkadotSVG }, - 'subwallet-js': { name: 'SubWallet', icon: SubwalletSVG }, - talisman: { name: 'Talisman', icon: TalismanSVG }, - enkrypt: { name: 'Enkrypt', icon: EnkryptSVG }, -}; - -const WALLETS = Object.keys(WALLET); - -export { WALLET, WALLETS }; diff --git a/idea/frontend/src/widgets/accountsModal/model/index.ts b/idea/frontend/src/widgets/accountsModal/model/index.ts deleted file mode 100644 index 9a4cd62a7a..0000000000 --- a/idea/frontend/src/widgets/accountsModal/model/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { WALLETS, WALLET } from './consts'; -import { WalletId } from './types'; - -export { WALLETS, WALLET }; -export type { WalletId }; diff --git a/idea/frontend/src/widgets/accountsModal/ui/AccountsModal.module.scss b/idea/frontend/src/widgets/accountsModal/ui/AccountsModal.module.scss deleted file mode 100644 index 9764de73d3..0000000000 --- a/idea/frontend/src/widgets/accountsModal/ui/AccountsModal.module.scss +++ /dev/null @@ -1,35 +0,0 @@ -@use '@/shared/assets/styles/variables' as *; -@use '@/shared/assets/styles/mixins' as *; - -.simplebar { - @include customSimplebar; - max-height: 50vh; - margin-right: -16px; - - :global(.simplebar-content) { - margin-right: 16px; - } -} - -.modal { - position: relative; - - &:not(.empty) { - padding-bottom: 102px; - } -} - -.footer { - position: absolute; // workaround cuz footer has different bg color - bottom: 0; - left: 0; - width: 100%; - padding: 24px 32px; - border-radius: 0 0 $borderRadiusMedium $borderRadiusMedium; - display: flex; - background-color: $bgColor4; -} - -.logoutButton { - margin-left: auto; -} diff --git a/idea/frontend/src/widgets/accountsModal/ui/AccountsModal.tsx b/idea/frontend/src/widgets/accountsModal/ui/AccountsModal.tsx deleted file mode 100644 index 50ecebab02..0000000000 --- a/idea/frontend/src/widgets/accountsModal/ui/AccountsModal.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import { useAccount } from '@gear-js/react-hooks'; -import type { InjectedAccountWithMeta } from '@polkadot/extension-inject/types'; -import { Button, Modal } from '@gear-js/ui'; -import { useEffect, useState } from 'react'; -import clsx from 'clsx'; -import SimpleBar from 'simplebar-react'; - -import { ModalProps } from '@/entities/modal'; -import { LocalStorage } from '@/shared/config'; -import logoutSVG from '@/shared/assets/images/actions/logout.svg?react'; -import arrowSVG from '@/shared/assets/images/actions/arrowLeft.svg?react'; - -import { isWeb3Injected } from '@polkadot/extension-dapp'; -import { useWallet } from '../hooks'; -import { AccountList } from './accountList'; -import { Wallets } from './wallets'; -import styles from './AccountsModal.module.scss'; - -type Props = ModalProps & { - accounts: InjectedAccountWithMeta[]; -}; - -const AccountsModal = ({ accounts, onClose }: Props) => { - const { account, extensions, login, logout } = useAccount(); - const { wallet, walletId, switchWallet, resetWallet } = useWallet(); - - const [isWalletSelection, setIsWalletSelection] = useState(!wallet); - - const toggleWalletSelection = () => setIsWalletSelection((prevValue) => !prevValue); - const enableWalletSelection = () => setIsWalletSelection(true); - const disableWalletSelection = () => setIsWalletSelection(false); - - const handleLogoutClick = () => { - logout(); - onClose(); - }; - - const handleAccountClick = (newAccount: InjectedAccountWithMeta) => { - if (walletId) { - login(newAccount); - localStorage.setItem(LocalStorage.Wallet, walletId); - onClose(); - } - }; - - useEffect(() => { - if (walletId) { - disableWalletSelection(); - } else { - enableWalletSelection(); - } - }, [walletId]); - - useEffect(() => { - const isChosenExtensionExists = extensions.some((ext) => ext.name === walletId); - - if (!isChosenExtensionExists) resetWallet(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [extensions]); - - const modalClassName = clsx(styles.modal, !isWeb3Injected && styles.empty); - const heading = isWalletSelection ? 'Choose Wallet' : 'Connect account'; - - return ( - - {isWeb3Injected ? ( - <> - - {isWalletSelection && ( - - )} - {!isWalletSelection && ( - meta.source === walletId)} - address={account?.address} - toggleAccount={handleAccountClick} - /> - )} - - -
    - {wallet && ( -
    - - ) : ( -

    - Wallet extension was not found or disconnected. Please check how to install a supported wallet and create an - account{' '} - - here - - . -

    - )} -
    - ); -}; - -export { AccountsModal }; diff --git a/idea/frontend/src/widgets/accountsModal/ui/accountItem/AccauntItem.tsx b/idea/frontend/src/widgets/accountsModal/ui/accountItem/AccauntItem.tsx deleted file mode 100644 index e64f43d9f9..0000000000 --- a/idea/frontend/src/widgets/accountsModal/ui/accountItem/AccauntItem.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { memo } from 'react'; -import type { InjectedAccountWithMeta } from '@polkadot/extension-inject/types'; -import clsx from 'clsx'; -import { decodeAddress } from '@gear-js/api'; -import { useAlert } from '@gear-js/react-hooks'; -import { Button, buttonStyles } from '@gear-js/ui'; - -import { copyToClipboard } from '@/shared/helpers'; -import { AccountButton } from '@/shared/ui/accountButton'; -import copyKeySVG from '@/shared/assets/images/actions/copyKey.svg?react'; - -import styles from './AccountItem.module.scss'; - -type Props = { - account: InjectedAccountWithMeta; - isActive: boolean; - onClick: (account: InjectedAccountWithMeta) => void; -}; - -const AccountItem = memo(({ account, isActive, onClick }: Props) => { - const alert = useAlert(); - - const handleClick = () => { - if (isActive) { - return; - } - - onClick(account); - }; - - const handleCopy = () => { - const decodedAddress = decodeAddress(account.address); - - copyToClipboard(decodedAddress, alert); - }; - - const accountBtnClasses = clsx( - buttonStyles.large, - styles.accountButton, - isActive ? styles.active : buttonStyles.light, - ); - - return ( -
  • - -
  • - ); -}); - -export { AccountItem }; diff --git a/idea/frontend/src/widgets/accountsModal/ui/accountItem/AccountItem.module.scss b/idea/frontend/src/widgets/accountsModal/ui/accountItem/AccountItem.module.scss deleted file mode 100644 index ef03f71b88..0000000000 --- a/idea/frontend/src/widgets/accountsModal/ui/accountItem/AccountItem.module.scss +++ /dev/null @@ -1,23 +0,0 @@ -@use '@/shared/assets/styles/_shared.scss' as *; -@use '@/shared/assets/styles/_mixins.scss' as *; -@use '@/shared/assets/styles/_variables.scss' as *; - -.accountItem { - @include childrenMargin(10px, right); - - display: flex; - align-items: center; - - .accountButton { - flex: 1 1; - - &.active { - cursor: default; - background-color: $successColor; - - &:hover { - background-color: $successColor; - } - } - } -} diff --git a/idea/frontend/src/widgets/accountsModal/ui/accountItem/index.ts b/idea/frontend/src/widgets/accountsModal/ui/accountItem/index.ts deleted file mode 100644 index c5fe25fbc3..0000000000 --- a/idea/frontend/src/widgets/accountsModal/ui/accountItem/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { AccountItem } from './AccauntItem'; - -export { AccountItem }; diff --git a/idea/frontend/src/widgets/accountsModal/ui/accountList/AccountList.module.scss b/idea/frontend/src/widgets/accountsModal/ui/accountList/AccountList.module.scss deleted file mode 100644 index 04fbd8514e..0000000000 --- a/idea/frontend/src/widgets/accountsModal/ui/accountList/AccountList.module.scss +++ /dev/null @@ -1,30 +0,0 @@ -@use '@gear-js/ui/breakpoints' as *; -@use '@/shared/assets/styles/_mixins.scss' as *; -@use '@/shared/assets/styles/_shared.scss' as *; -@use '@/shared/assets/styles/_variables.scss' as *; - -.accountList { - @include childrenMargin($margin); -} - -.accountButton { - width: 100%; - font-weight: 500; - text-transform: uppercase; - padding: 16px 40px; - - &.active { - background: $successColor; - } -} - -.accountName { - font-size: $fontSizeMedium; - padding: 0 5px; -} - -.accountIcon { - @include md { - display: none !important; - } -} diff --git a/idea/frontend/src/widgets/accountsModal/ui/accountList/AccountList.tsx b/idea/frontend/src/widgets/accountsModal/ui/accountList/AccountList.tsx deleted file mode 100644 index a69a042110..0000000000 --- a/idea/frontend/src/widgets/accountsModal/ui/accountList/AccountList.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import type { InjectedAccountWithMeta } from '@polkadot/extension-inject/types'; - -import styles from './AccountList.module.scss'; -import { AccountItem } from '../accountItem'; - -type Props = { - list: InjectedAccountWithMeta[]; - address?: string; - toggleAccount: (account: InjectedAccountWithMeta) => void; -}; - -const AccountList = ({ list, address, toggleAccount }: Props) => { - if (!list.length) { - return ( -

    - No accounts found. Please open your Polkadot extension and create a new account or import existing. Then reload - this page. -

    - ); - } - - return ( -
      - {list.map((account) => ( - - ))} -
    - ); -}; - -export { AccountList }; diff --git a/idea/frontend/src/widgets/accountsModal/ui/accountList/index.ts b/idea/frontend/src/widgets/accountsModal/ui/accountList/index.ts deleted file mode 100644 index 577ff7a510..0000000000 --- a/idea/frontend/src/widgets/accountsModal/ui/accountList/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { AccountList } from './AccountList'; diff --git a/idea/frontend/src/widgets/accountsModal/ui/wallet/Wallet.module.scss b/idea/frontend/src/widgets/accountsModal/ui/wallet/Wallet.module.scss deleted file mode 100644 index 33ee4befba..0000000000 --- a/idea/frontend/src/widgets/accountsModal/ui/wallet/Wallet.module.scss +++ /dev/null @@ -1,38 +0,0 @@ -@use '@gear-js/ui/variables' as *; -@use '@gear-js/ui/mixins' as *; - -.button { - display: flex; - justify-content: space-between; - background-color: $buttonColorLight; - letter-spacing: 0.08em; - - span { - display: flex; - align-items: center; - } - - &:hover { - background-color: $buttonColorLightHover; - } - - &.active { - background-color: $successColor; - } - - &.active, - &:not(.connected) { - pointer-events: none; - } -} - -.text { - align-items: center; - font-size: 12px; - font-weight: 400; -} - -.connectIcon { - @include square(12px); - margin-left: 7px; -} diff --git a/idea/frontend/src/widgets/accountsModal/ui/wallet/Wallet.tsx b/idea/frontend/src/widgets/accountsModal/ui/wallet/Wallet.tsx deleted file mode 100644 index 762bdd2cb0..0000000000 --- a/idea/frontend/src/widgets/accountsModal/ui/wallet/Wallet.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { buttonStyles } from '@gear-js/ui'; -import clsx from 'clsx'; -import { FunctionComponent, SVGProps } from 'react'; - -import ConnectSVG from '@/shared/assets/images/actions/plus.svg?react'; - -import styles from './Wallet.module.scss'; - -type Props = { - icon: FunctionComponent>; - name: string; - isConnected: boolean; - isActive: boolean; - onClick: () => void; -}; - -const Wallet = ({ icon: Icon, name, isConnected, isActive, onClick }: Props) => { - const buttonClassName = clsx( - buttonStyles.button, - buttonStyles.large, - buttonStyles.block, - styles.button, - isConnected && styles.connected, - isActive && styles.active, - ); - - return ( - - ); -}; - -export { Wallet }; diff --git a/idea/frontend/src/widgets/accountsModal/ui/wallet/index.ts b/idea/frontend/src/widgets/accountsModal/ui/wallet/index.ts deleted file mode 100644 index c9b6ae9bc9..0000000000 --- a/idea/frontend/src/widgets/accountsModal/ui/wallet/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Wallet } from './Wallet'; - -export { Wallet }; diff --git a/idea/frontend/src/widgets/accountsModal/ui/wallets/Wallets.module.scss b/idea/frontend/src/widgets/accountsModal/ui/wallets/Wallets.module.scss deleted file mode 100644 index 9c6fee2bc1..0000000000 --- a/idea/frontend/src/widgets/accountsModal/ui/wallets/Wallets.module.scss +++ /dev/null @@ -1,5 +0,0 @@ -@use '@/shared/assets/styles/mixins' as *; - -.wallets { - @include childrenMargin(16px); -} diff --git a/idea/frontend/src/widgets/accountsModal/ui/wallets/Wallets.tsx b/idea/frontend/src/widgets/accountsModal/ui/wallets/Wallets.tsx deleted file mode 100644 index 747dd57af6..0000000000 --- a/idea/frontend/src/widgets/accountsModal/ui/wallets/Wallets.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import type { InjectedExtension } from '@polkadot/extension-inject/types'; - -import { WALLETS, WALLET, WalletId } from '../../model'; -import { Wallet } from '../wallet'; -import styles from './Wallets.module.scss'; - -type Props = { - selectedWalletId: WalletId | undefined; - extensions: InjectedExtension[]; - onWalletClick: (id: WalletId) => void; -}; - -const Wallets = ({ selectedWalletId, extensions, onWalletClick }: Props) => { - const isWalletConnected = (id: string) => extensions.some(({ name }) => name === id); - - const getWallets = () => - WALLETS.map((id) => { - const walletId = id as WalletId; - const { name, icon } = WALLET[walletId]; - - return ( -
  • - onWalletClick(walletId)} - /> -
  • - ); - }); - - return
      {getWallets()}
    ; -}; - -export { Wallets }; diff --git a/idea/frontend/src/widgets/accountsModal/ui/wallets/index.ts b/idea/frontend/src/widgets/accountsModal/ui/wallets/index.ts deleted file mode 100644 index f22997d3bc..0000000000 --- a/idea/frontend/src/widgets/accountsModal/ui/wallets/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Wallets } from './Wallets'; - -export { Wallets }; diff --git a/idea/frontend/src/widgets/header/ui/balanceInfo/BalanceInfo.tsx b/idea/frontend/src/widgets/header/ui/balanceInfo/BalanceInfo.tsx deleted file mode 100644 index d2f0703ece..0000000000 --- a/idea/frontend/src/widgets/header/ui/balanceInfo/BalanceInfo.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import clsx from 'clsx'; -import { Account } from '@gear-js/react-hooks'; - -import styles from './BalanceInfo.module.scss'; -import headerStyles from '../Header.module.scss'; - -type Props = { - balance: Account['balance']; -}; - -const BalanceInfo = ({ balance }: Props) => { - const { unit = 'Unit', value } = balance; - - return ( -
    -

    Balance:

    -

    - {value} - {unit} -

    -
    - ); -}; - -export { BalanceInfo }; diff --git a/idea/frontend/src/widgets/header/ui/balanceInfo/index.ts b/idea/frontend/src/widgets/header/ui/balanceInfo/index.ts deleted file mode 100644 index 6527e36d8e..0000000000 --- a/idea/frontend/src/widgets/header/ui/balanceInfo/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { BalanceInfo } from './BalanceInfo'; - -export { BalanceInfo }; diff --git a/idea/frontend/src/widgets/header/ui/topSide/TopSide.module.scss b/idea/frontend/src/widgets/header/ui/topSide/TopSide.module.scss index 8c7a39385e..f3054a15b8 100644 --- a/idea/frontend/src/widgets/header/ui/topSide/TopSide.module.scss +++ b/idea/frontend/src/widgets/header/ui/topSide/TopSide.module.scss @@ -1,8 +1,8 @@ @use '@gear-js/ui/mixins' as *; -@use '@/shared/assets/styles/_shared.scss' as *; -@use '@/shared/assets/styles/_mixins.scss' as *; -@use '@/shared/assets/styles/_variables.scss' as *; -@use '@/shared/assets/styles/_animations.scss' as *; +@use '@/shared/assets/styles/shared' as *; +@use '@/shared/assets/styles/mixins' as *; +@use '@/shared/assets/styles/variables' as *; +@use '@/shared/assets/styles/animations' as *; .topSide { display: flex; @@ -19,6 +19,7 @@ } .rightSide { + @include childrenMargin(16px, right); display: flex; align-items: center; margin-left: auto; @@ -26,7 +27,6 @@ .privateContent { @include fadeAppear(); @include childrenMargin(16px, right); - display: flex; align-items: center; diff --git a/idea/frontend/src/widgets/header/ui/topSide/TopSide.tsx b/idea/frontend/src/widgets/header/ui/topSide/TopSide.tsx index 789853cbea..78b93a41bf 100644 --- a/idea/frontend/src/widgets/header/ui/topSide/TopSide.tsx +++ b/idea/frontend/src/widgets/header/ui/topSide/TopSide.tsx @@ -13,9 +13,8 @@ import { RecentBlocks } from '@/features/recentBlocks'; import { HCAPTCHA_SITE_KEY, AnimationTimeout, GEAR_BALANCE_TRANSFER_VALUE } from '@/shared/config'; import TestBalanceSVG from '@/shared/assets/images/actions/testBalance.svg?react'; import TransferBalanceSVG from '@/shared/assets/images/actions/transferBalance.svg?react'; +import { Wallet } from '@/features/wallet'; -import { Wallet } from '../wallet'; -import { BalanceInfo } from '../balanceInfo'; import { TotalIssuance } from '../totalIssuance'; import styles from './TopSide.module.scss'; @@ -23,7 +22,7 @@ const TopSide = () => { const alert = useAlert(); const { api, isApiReady } = useApi(); - const { account, isAccountReady } = useAccount(); + const { account } = useAccount(); const { isDevChain, isTestBalanceAvailable } = useChain(); const { showModal, closeModal } = useModal(); const { balanceMultiplier } = useBalanceMultiplier(); @@ -122,17 +121,11 @@ const TopSide = () => { - - )} - {isApiReady && isAccountReady && ( - - - - )} + div { - // hacky, but need to override Identicon's 'cursor: copy' - cursor: inherit !important; - } -} diff --git a/idea/frontend/src/widgets/header/ui/wallet/Wallet.tsx b/idea/frontend/src/widgets/header/ui/wallet/Wallet.tsx deleted file mode 100644 index 3de869cc61..0000000000 --- a/idea/frontend/src/widgets/header/ui/wallet/Wallet.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import clsx from 'clsx'; -import { Account, useAccount } from '@gear-js/react-hooks'; -import { Button, buttonStyles } from '@gear-js/ui'; - -import { useModal } from '@/hooks'; -import { AccountButton } from '@/shared/ui/accountButton'; -import substrateSVG from '@/shared/assets/images/logos/substrate.svg?react'; - -import { OnboardingTooltip } from '@/shared/ui/onboardingTooltip'; -import styles from './Wallet.module.scss'; - -type Props = { - account?: Account; -}; - -const Wallet = ({ account }: Props) => { - const { accounts } = useAccount(); - const { showModal } = useModal(); - - const openAccountsModal = () => showModal('accounts', { accounts }); - - const accountBtnClasses = clsx(buttonStyles.medium, styles.accountBtn); - - return ( - - {account ? ( - - ) : ( -