Skip to content

Commit

Permalink
Feat: Wallet management icon (#3114)
Browse files Browse the repository at this point in the history
  • Loading branch information
tuul-wq authored Feb 6, 2025
1 parent 93a215f commit 220b8fb
Show file tree
Hide file tree
Showing 24 changed files with 311 additions and 180 deletions.
2 changes: 1 addition & 1 deletion docs/content/docs/onboarding/project-structure.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Domain's model **must**:
- Export single object without `Model` suffix (`accounts`, not `accountsModel`).
- Provide storage logic.
- Create integrations with data sources.
- Be isolated, no other models in dependecies.
- Be isolated, no other models in dependencies.
- Be testable.

Model file should export single object with same name as entity.
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/domains/network/accounts/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function isUniversalAccount(account: Pick<AnyAccount, 'type'>): account is Unive
}

function isAccountAvailableOnChain(account: Pick<AnyAccount, 'type' | 'cryptoType'>, chain: Chain) {
if (isCryptoMatch(account, chain) === false) {
if (!isCryptoMatch(account, chain)) {
return false;
}

Expand Down
2 changes: 1 addition & 1 deletion src/renderer/domains/network/accounts/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export interface UniversalAccount {

/**
* @summary
* Account related to specific chain. This is most common case and such accounts
* Account related to specific chain. This is the most common case and such accounts
* have "one to one" relations with other entities in the system.
*/
export interface ChainAccount {
Expand Down
6 changes: 2 additions & 4 deletions src/renderer/entities/wallet/ui/Cards/WalletCardLg.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
import { type PropsWithChildren, type ReactNode } from 'react';

import { type Wallet, WalletIconType } from '@/shared/core';
import { cnTw } from '@/shared/lib/utils';
import { BodyText, FootnoteText } from '@/shared/ui';
import { walletUtils } from '../../lib/wallet-utils';
import { WalletIcon } from '../WalletIcon/WalletIcon';

type Props = PropsWithChildren<{
className?: string;
wallet: Wallet;
description?: string | ReactNode;
additionalInfo?: ReactNode;
}>;

export const WalletCardLg = ({ wallet, description, additionalInfo, className, children }: Props) => {
export const WalletCardLg = ({ wallet, description, additionalInfo, children }: Props) => {
const type =
walletUtils.isFlexibleMultisig(wallet) && !wallet.activated
? WalletIconType.FLEXIBLE_MULTISIG_INACTIVE
: wallet.type;

return (
<div className={cnTw('flex h-8 w-full min-w-0 items-center gap-x-2', className)}>
<div className="flex h-8 w-full min-w-0 items-center gap-x-2">
<div className="relative">
<WalletIcon type={type} size={32} />
{additionalInfo}
Expand Down
20 changes: 4 additions & 16 deletions src/renderer/entities/wallet/ui/Cards/WalletCardMd.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,14 @@ import { cnTw, nonNullable, nullable } from '@/shared/lib/utils';
import { BodyText, FootnoteText } from '@/shared/ui';
import { WalletIcon } from '../WalletIcon/WalletIcon';

type Props = {
type Props = PropsWithChildren<{
wallet: Wallet;
description?: string | ReactNode;
meta?: ReactNode;
prefix?: ReactNode;
hideIcon?: boolean;
onClick?: () => void;
};
}>;

export const WalletCardMd = ({
wallet,
description,
meta,
prefix,
hideIcon,
children,
onClick,
}: PropsWithChildren<Props>) => {
export const WalletCardMd = ({ wallet, description, meta, children, onClick }: Props) => {
const handleClick = (fn?: () => void) => {
return (event: MouseEvent<HTMLButtonElement>) => {
if (!fn) return;
Expand All @@ -46,9 +36,7 @@ export const WalletCardMd = ({
})}
onClick={handleClick(onClick)}
>
{prefix}

{!hideIcon && <WalletIcon type={wallet.type} size={20} className="shrink-0" />}
<WalletIcon type={wallet.type} size={20} className="shrink-0" />
<div className="flex min-w-0 flex-col">
<div className="flex items-center gap-x-2">
<BodyText
Expand Down
27 changes: 15 additions & 12 deletions src/renderer/entities/wallet/ui/Cards/WalletCardSm.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
import { type MouseEvent } from 'react';
import { type MouseEvent, type PropsWithChildren } from 'react';

import { type Wallet } from '@/shared/core';
import { cnTw } from '@/shared/lib/utils';
import { FootnoteText, IconButton } from '@/shared/ui';
import { FootnoteText } from '@/shared/ui';
import { WalletIcon } from '../WalletIcon/WalletIcon';

type Props = {
type Props = PropsWithChildren<{
wallet: Wallet;
iconSize?: number;
className?: string;
onClick?: () => void;
onInfoClick?: () => void;
};
}>;

export const WalletCardSm = ({ wallet, className, iconSize = 16, onClick, onInfoClick }: Props) => {
export const WalletCardSm = ({ wallet, onClick, children }: Props) => {
const handleClick = (fn?: () => void) => {
return (event: MouseEvent<HTMLButtonElement>) => {
if (!fn) return;
Expand All @@ -28,11 +25,10 @@ export const WalletCardSm = ({ wallet, className, iconSize = 16, onClick, onInfo
className={cnTw(
'group relative flex w-full items-center rounded transition-colors',
'focus-within:bg-action-background-hover hover:bg-action-background-hover',
className,
)}
>
<button className="flex w-full items-center gap-x-2 rounded py-[3px] pl-2 pr-7" onClick={handleClick(onClick)}>
<WalletIcon className="shrink-0" type={wallet.type} size={iconSize} />
<WalletIcon className="shrink-0" type={wallet.type} size={16} />
<FootnoteText
className={cnTw(
'truncate text-text-secondary transition-colors',
Expand All @@ -42,8 +38,15 @@ export const WalletCardSm = ({ wallet, className, iconSize = 16, onClick, onInfo
{wallet.name}
</FootnoteText>
</button>
{/* TODO: do the same as in WalletCardMd */}
<IconButton className="absolute right-2" name="details" size={16} onClick={handleClick(onInfoClick)} />

<div
className={cnTw(
'absolute right-2 top-1/2 flex -translate-y-1/2 opacity-0 transition-opacity',
'focus:opacity-100 group-focus-within:opacity-100 group-hover:opacity-100',
)}
>
{children}
</div>
</div>
);
};
15 changes: 4 additions & 11 deletions src/renderer/features/extension-wallet/components/WalletGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { Slot, createSlot } from '@/shared/di';
import { useI18n } from '@/shared/i18n';
import { performSearch } from '@/shared/lib/utils';
import { Icon, type IconNames } from '@/shared/ui';
import { WalletManagement } from '@/shared/ui-entities';
import { Accordion, Box, Label } from '@/shared/ui-kit';
import { WalletCardMd } from '@/entities/wallet';
import { walletsFiatBalanceFeature } from '@/features/wallet-fiat-balance';

// TODO invert this dependency
Expand All @@ -26,6 +26,7 @@ type Props = {

export const WalletGroup = memo(({ wallets, icon, query, title, onSelect }: Props) => {
const { t } = useI18n();

const filteredWallets = performSearch({
query,
records: wallets,
Expand All @@ -48,24 +49,16 @@ export const WalletGroup = memo(({ wallets, icon, query, title, onSelect }: Prop
<Accordion.Content>
<Box gap={1} padding={[1, 0, 0]}>
{filteredWallets.map((wallet) => (
<WalletCardMd
<WalletManagement
key={wallet.id}
hideIcon
wallet={wallet}
description={
<WalletFiatBalance walletId={wallet.id} className="max-w-[215px] truncate text-help-text" />
}
prefix={
wallet.isActive ? (
<Icon name="checkmark" className="shrink-0 text-icon-accent" size={20} />
) : (
<div className="row-span-2 h-5 w-5 shrink-0" />
)
}
onClick={() => onSelect(wallet)}
>
<Slot id={walletActionsSlot} props={{ wallet }} />
</WalletCardMd>
</WalletManagement>
))}
</Box>
</Accordion.Content>
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/features/wallet-details/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import { walletActionsSlot as proxiedActionsSlot } from '@/features/wallet-proxi
import { walletActionsSlot as walletConnectActionsSlot } from '@/features/wallet-wallet-connect';
import { walletActionsSlot as watchOnlyActionsSlot } from '@/features/wallet-watch-only';

import { WalletDetails } from './ui/components/WalletDetails';
import { WalletDetails } from './ui/components';

export { WalletDetails };

/**
* The reason for the existence of this feature is WalletDetails component
* implementation. walletDetailsFeature should be absolete and details for each
* implementation. walletDetailsFeature should be obsolete and details for each
* type of wallet should be coupled with wallet implementation.
*/

Expand Down
31 changes: 4 additions & 27 deletions src/renderer/features/wallet-multisig/components/WalletGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
import { memo } from 'react';

import { type Wallet, WalletType } from '@/shared/core';
import { Slot, createSlot } from '@/shared/di';
import { useI18n } from '@/shared/i18n';
import { performSearch } from '@/shared/lib/utils';
import { Icon } from '@/shared/ui';
import { Accordion, Box, Tooltip } from '@/shared/ui-kit';
import { WalletCardMd, WalletIcon } from '@/entities/wallet';
import { walletsFiatBalanceFeature } from '@/features/wallet-fiat-balance';
import { WalletIcon } from '@/entities/wallet';

// TODO invert this dependency
const {
views: { WalletFiatBalance },
} = walletsFiatBalanceFeature;

export const walletActionsSlot = createSlot<{ wallet: Wallet }>();
import { WalletRow } from './WalletRow';

type Props = {
title: string;
Expand All @@ -26,6 +19,7 @@ type Props = {

export const WalletGroup = memo(({ wallets, walletType, query, title, onSelect }: Props) => {
const { t } = useI18n();

const filteredWallets = performSearch({
query,
records: wallets,
Expand Down Expand Up @@ -57,24 +51,7 @@ export const WalletGroup = memo(({ wallets, walletType, query, title, onSelect }
<Accordion.Content>
<Box gap={1} padding={[1, 0, 0]}>
{filteredWallets.map(wallet => (
<WalletCardMd
key={wallet.id}
hideIcon
wallet={wallet}
description={
<WalletFiatBalance walletId={wallet.id} className="max-w-[215px] truncate text-help-text" />
}
prefix={
wallet.isActive ? (
<Icon name="checkmark" className="shrink-0 text-icon-accent" size={20} />
) : (
<div className="row-span-2 h-5 w-5 shrink-0" />
)
}
onClick={() => onSelect(wallet)}
>
<Slot id={walletActionsSlot} props={{ wallet }} />
</WalletCardMd>
<WalletRow key={wallet.id} wallet={wallet} onSelect={onSelect} />
))}
</Box>
</Accordion.Content>
Expand Down
44 changes: 44 additions & 0 deletions src/renderer/features/wallet-multisig/components/WalletRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useStoreMap, useUnit } from 'effector-react';

import { type Wallet } from '@/shared/core';
import { Slot, createSlot } from '@/shared/di';
import { WalletManagement } from '@/shared/ui-entities';
import { accounts as accountsDomainModel, accountsService } from '@/domains/network';
import { ChainIcon } from '@/entities/chain';
import { networkModel } from '@/entities/network';
import { walletsFiatBalanceFeature } from '@/features/wallet-fiat-balance';

const {
views: { WalletFiatBalance },
} = walletsFiatBalanceFeature;

export const walletActionsSlot = createSlot<{ wallet: Wallet }>();

type Props = {
wallet: Wallet;
onSelect: (wallet: Wallet) => unknown;
};
export const WalletRow = ({ wallet, onSelect }: Props) => {
const chains = useUnit(networkModel.$chains);

const account = useStoreMap({
store: accountsDomainModel.$list,
keys: [wallet.id],
fn: (accounts, [walletId]) => {
const match = accountsService.filterAccountsByWallet(accounts, walletId).at(0);

return match && accountsService.isChainAccount(match) ? match : null;
},
});

return (
<WalletManagement
wallet={wallet}
meta={account ? <ChainIcon src={chains[account.chainId].icon} size={16} /> : null}
description={<WalletFiatBalance walletId={wallet.id} className="max-w-[215px] truncate text-help-text" />}
onClick={() => onSelect(wallet)}
>
<Slot id={walletActionsSlot} props={{ wallet }} />
</WalletManagement>
);
};
5 changes: 3 additions & 2 deletions src/renderer/features/wallet-multisig/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import { accountsService } from '@/domains/network';
import { WalletIcon, accountUtils, walletUtils } from '@/entities/wallet';
import { walletGroupSlot, walletIconSlot } from '@/features/wallet-select';

import { WalletGroup, walletActionsSlot } from './components/WalletGroup';
import { WalletGroup } from './components/WalletGroup';
import { walletActionsSlot } from './components/WalletRow';
import { walletsModel } from './model/wallets';

export { walletActionsSlot };
Expand Down Expand Up @@ -39,7 +40,7 @@ walletMultisigFeature.inject(walletGroupSlot, {
render({ query, onSelect }) {
const { t } = useI18n();
const regular = useUnit(walletsModel.$regularMultisig);
const flexible = useUnit(walletsModel.$flexibleMutisig);
const flexible = useUnit(walletsModel.$flexibleMultisig);

return (
<>
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/features/wallet-multisig/model/wallets.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { walletModel, walletUtils } from '@/entities/wallet';

export const $regularMultisig = walletModel.$wallets.map(list => list.filter(walletUtils.isRegularMultisig));
export const $flexibleMutisig = walletModel.$wallets.map(list => list.filter(walletUtils.isFlexibleMultisig));
export const $flexibleMultisig = walletModel.$wallets.map(list => list.filter(walletUtils.isFlexibleMultisig));

export const walletsModel = {
$regularMultisig,
$flexibleMutisig,
$flexibleMultisig,
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { memo } from 'react';
import { type Wallet, type WalletType } from '@/shared/core';
import { Slot, createSlot } from '@/shared/di';
import { performSearch } from '@/shared/lib/utils';
import { Icon } from '@/shared/ui';
import { WalletManagement } from '@/shared/ui-entities';
import { Accordion, Box } from '@/shared/ui-kit';
import { WalletCardMd, WalletIcon } from '@/entities/wallet';
import { WalletIcon } from '@/entities/wallet';
import { walletsFiatBalanceFeature } from '@/features/wallet-fiat-balance';

// TODO invert this dependency
Expand Down Expand Up @@ -45,24 +45,16 @@ export const WalletGroup = memo(({ wallets, walletType, query, title, onSelect }
<Accordion.Content>
<Box gap={1} padding={[1, 0, 0]}>
{filteredWallets.map(wallet => (
<WalletCardMd
<WalletManagement
key={wallet.id}
hideIcon
wallet={wallet}
description={
<WalletFiatBalance walletId={wallet.id} className="max-w-[215px] truncate text-help-text" />
}
prefix={
wallet.isActive ? (
<Icon name="checkmark" className="shrink-0 text-icon-accent" size={20} />
) : (
<div className="row-span-2 h-5 w-5 shrink-0" />
)
}
onClick={() => onSelect(wallet)}
>
<Slot id={walletActionsSlot} props={{ wallet }} />
</WalletCardMd>
</WalletManagement>
))}
</Box>
</Accordion.Content>
Expand Down
Loading

0 comments on commit 220b8fb

Please sign in to comment.