14.05.2026 rip
This commit is contained in:
@@ -2,6 +2,7 @@ import { BrowserRouter, Route, Routes } from 'react-router-dom'
|
|||||||
import { HomePage } from '@pages/home'
|
import { HomePage } from '@pages/home'
|
||||||
import { WalletPage } from '@pages/wallet'
|
import { WalletPage } from '@pages/wallet'
|
||||||
import { SwapPage } from '@pages/swap'
|
import { SwapPage } from '@pages/swap'
|
||||||
|
import { BridgePage } from '@pages/bridge'
|
||||||
import { ProfilePage } from '@pages/profile'
|
import { ProfilePage } from '@pages/profile'
|
||||||
import { LoginPage } from '@pages/login'
|
import { LoginPage } from '@pages/login'
|
||||||
import { RegisterPage } from '@pages/register'
|
import { RegisterPage } from '@pages/register'
|
||||||
@@ -30,6 +31,7 @@ export function RouterProvider() {
|
|||||||
<Route element={<ProtectedRoute />}>
|
<Route element={<ProtectedRoute />}>
|
||||||
<Route path={ROUTES.WALLET} element={<WalletPage />} />
|
<Route path={ROUTES.WALLET} element={<WalletPage />} />
|
||||||
<Route path={ROUTES.SWAP} element={<SwapPage />} />
|
<Route path={ROUTES.SWAP} element={<SwapPage />} />
|
||||||
|
<Route path={ROUTES.BRIDGE} element={<BridgePage />} />
|
||||||
<Route path={ROUTES.PROFILE} element={<ProfilePage />} />
|
<Route path={ROUTES.PROFILE} element={<ProfilePage />} />
|
||||||
<Route path={ROUTES.SEED_PHRASE} element={<SeedPhrasePage />} />
|
<Route path={ROUTES.SEED_PHRASE} element={<SeedPhrasePage />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|||||||
1
src/pages/bridge/index.ts
Normal file
1
src/pages/bridge/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { BridgePage } from './ui/BridgePage'
|
||||||
52
src/pages/bridge/ui/BridgePage.module.css
Normal file
52
src/pages/bridge/ui/BridgePage.module.css
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
.page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
|
background: var(--bg-deep);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 24px 28px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
padding: 10px 24px;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
background: linear-gradient(135deg, var(--grad-edge), var(--grad-center));
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.inactive {
|
||||||
|
background: rgba(255, 255, 255, 0.06);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.inactive:hover {
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 32px 20px 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 650px) {
|
||||||
|
.main {
|
||||||
|
padding: 32px 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/pages/bridge/ui/BridgePage.tsx
Normal file
37
src/pages/bridge/ui/BridgePage.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
import { Footer } from '@widgets/footer'
|
||||||
|
import { BridgeForm } from '@widgets/bridge-form'
|
||||||
|
import { WalletHeader } from '@widgets/wallet-header'
|
||||||
|
import { ROUTES } from '@shared/config/routes'
|
||||||
|
import styles from './BridgePage.module.css'
|
||||||
|
|
||||||
|
export function BridgePage() {
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.page}>
|
||||||
|
<WalletHeader />
|
||||||
|
|
||||||
|
<div className={styles.tabs}>
|
||||||
|
<button
|
||||||
|
className={`${styles.tab} ${styles.inactive}`}
|
||||||
|
onClick={() => navigate(ROUTES.SWAP)}
|
||||||
|
>
|
||||||
|
СВОП
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={`${styles.tab} ${styles.active}`}
|
||||||
|
onClick={() => navigate(ROUTES.BRIDGE)}
|
||||||
|
>
|
||||||
|
БРИДЖ
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<main className={styles.main}>
|
||||||
|
<BridgeForm />
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
import { useState } from 'react'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { Footer } from '@widgets/footer'
|
import { Footer } from '@widgets/footer'
|
||||||
import { SwapForm } from '@widgets/swap-form'
|
import { SwapForm } from '@widgets/swap-form'
|
||||||
import { WalletHeader } from '@widgets/wallet-header'
|
import { WalletHeader } from '@widgets/wallet-header'
|
||||||
|
import { ROUTES } from '@shared/config/routes'
|
||||||
import styles from './SwapPage.module.css'
|
import styles from './SwapPage.module.css'
|
||||||
|
|
||||||
type Tab = 'swap' | 'bridge'
|
|
||||||
|
|
||||||
export function SwapPage() {
|
export function SwapPage() {
|
||||||
const [tab, setTab] = useState<Tab>('swap')
|
const navigate = useNavigate()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.page}>
|
<div className={styles.page}>
|
||||||
@@ -15,14 +14,14 @@ export function SwapPage() {
|
|||||||
|
|
||||||
<div className={styles.tabs}>
|
<div className={styles.tabs}>
|
||||||
<button
|
<button
|
||||||
className={`${styles.tab} ${tab === 'swap' ? styles.active : styles.inactive}`}
|
className={`${styles.tab} ${styles.active}`}
|
||||||
onClick={() => setTab('swap')}
|
onClick={() => navigate(ROUTES.SWAP)}
|
||||||
>
|
>
|
||||||
СВОП
|
СВОП
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={`${styles.tab} ${tab === 'bridge' ? styles.active : styles.inactive}`}
|
className={`${styles.tab} ${styles.inactive}`}
|
||||||
onClick={() => setTab('bridge')}
|
onClick={() => navigate(ROUTES.BRIDGE)}
|
||||||
>
|
>
|
||||||
БРИДЖ
|
БРИДЖ
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ export const ROUTES = {
|
|||||||
HOME: '/',
|
HOME: '/',
|
||||||
WALLET: '/wallet',
|
WALLET: '/wallet',
|
||||||
SWAP: "/swap",
|
SWAP: "/swap",
|
||||||
|
BRIDGE: "/bridge",
|
||||||
LOGIN: '/login',
|
LOGIN: '/login',
|
||||||
REGISTER: '/register',
|
REGISTER: '/register',
|
||||||
PROFILE: '/profile',
|
PROFILE: '/profile',
|
||||||
|
|||||||
1
src/widgets/bridge-form/index.ts
Normal file
1
src/widgets/bridge-form/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { BridgeForm } from './ui/BridgeForm'
|
||||||
7
src/widgets/bridge-form/ui/BridgeForm.module.css
Normal file
7
src/widgets/bridge-form/ui/BridgeForm.module.css
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 520px;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
177
src/widgets/bridge-form/ui/BridgeForm.tsx
Normal file
177
src/widgets/bridge-form/ui/BridgeForm.tsx
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import { PrimaryButton } from '@shared/ui'
|
||||||
|
import {
|
||||||
|
useWalletBalance, useWalletAddresses, useTokensList,
|
||||||
|
useRelayQuote, useExecuteRelaySwap, useSignSwap,
|
||||||
|
useTrxSwapQuote, useFetchTrxQuote, useExecuteTrxSwap,
|
||||||
|
type Chain, type RelaySwapResponse, type TrxSwapQuoteData,
|
||||||
|
} from '@features/wallet'
|
||||||
|
import { useDebounce } from '@shared/lib/hooks/useDebounce'
|
||||||
|
import { TOKENS_LIST, buildTokensFromBalance, useSwapForm } from '../../swap-form/model/useSwapForm'
|
||||||
|
import { SwapCard } from '../../swap-form/ui/SwapCard'
|
||||||
|
import { SwapDirectionButton } from '../../swap-form/ui/SwapDirectionButton'
|
||||||
|
import { SwapInfoPanel } from '../../swap-form/ui/SwapInfoPanel'
|
||||||
|
import { SwapConfirmModal } from '../../swap-form/ui/SwapConfirmModal'
|
||||||
|
import { TrxConfirmModal } from '../../swap-form/ui/TrxConfirmModal'
|
||||||
|
import { NetworkSelect } from './NetworkSelect'
|
||||||
|
import styles from './BridgeForm.module.css'
|
||||||
|
|
||||||
|
const CHAIN_ID: Record<string, number> = { ETH: 1, BSC: 56, SOL: 792703809 }
|
||||||
|
const NATIVE_ADDR: Record<string, string> = {
|
||||||
|
SOL: '11111111111111111111111111111111',
|
||||||
|
DEFAULT: '0x0000000000000000000000000000000000000000',
|
||||||
|
}
|
||||||
|
function nativeAddr(chain: string) {
|
||||||
|
return NATIVE_ADDR[chain] ?? NATIVE_ADDR.DEFAULT
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BridgeForm() {
|
||||||
|
const {
|
||||||
|
fromAmount, fromUsd,
|
||||||
|
fromToken, toToken,
|
||||||
|
setFromAmount, setPercent, swapTokens,
|
||||||
|
setFromToken, setToToken,
|
||||||
|
} = useSwapForm()
|
||||||
|
|
||||||
|
const [fromNetwork, setFromNetwork] = useState('ETH')
|
||||||
|
const [toNetwork, setToNetwork] = useState('BSC')
|
||||||
|
const [modalData, setModalData] = useState<RelaySwapResponse | null>(null)
|
||||||
|
const [trxModalQuote, setTrxModalQuote] = useState<TrxSwapQuoteData | null>(null)
|
||||||
|
|
||||||
|
const isTrxNetwork = fromNetwork === 'TRX'
|
||||||
|
|
||||||
|
const { data: walletData } = useWalletBalance(fromNetwork as Chain)
|
||||||
|
const tokenOptions = walletData ? buildTokensFromBalance(walletData) : TOKENS_LIST
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (tokenOptions.length === 0) return
|
||||||
|
setFromToken(t => tokenOptions.find(o => o.symbol === t.symbol) ?? tokenOptions[0])
|
||||||
|
setToToken(t => tokenOptions.find(o => o.symbol === t.symbol) ?? (tokenOptions[1] ?? tokenOptions[0]))
|
||||||
|
}, [walletData, fromNetwork])
|
||||||
|
|
||||||
|
const debouncedAmount = useDebounce(fromAmount, 500)
|
||||||
|
const { data: addresses } = useWalletAddresses()
|
||||||
|
const { data: tokensList } = useTokensList()
|
||||||
|
|
||||||
|
const parsedAmount = parseFloat(debouncedAmount)
|
||||||
|
|
||||||
|
const chainId = CHAIN_ID[fromNetwork]
|
||||||
|
const walletAddress = addresses?.find(a => a.chain === fromNetwork)?.address
|
||||||
|
const fromContract = tokensList?.find(t => t.chain === fromNetwork && t.symbol === fromToken.symbol)?.contract ?? nativeAddr(fromNetwork)
|
||||||
|
const toContract = tokensList?.find(t => t.chain === fromNetwork && t.symbol === toToken.symbol)?.contract ?? nativeAddr(fromNetwork)
|
||||||
|
|
||||||
|
const quotePayload = !isTrxNetwork && chainId && walletAddress && parsedAmount > 0
|
||||||
|
? {
|
||||||
|
user: walletAddress,
|
||||||
|
recipient: walletAddress,
|
||||||
|
originChainId: chainId,
|
||||||
|
destinationChainId: chainId,
|
||||||
|
originCurrency: fromContract,
|
||||||
|
destinationCurrency: toContract,
|
||||||
|
amount: Math.round(parsedAmount * Math.pow(10, fromToken.decimals)).toString(),
|
||||||
|
tradeType: 'EXACT_INPUT' as const,
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
|
||||||
|
const { data: quoteData } = useRelayQuote(quotePayload)
|
||||||
|
const { mutate: executeSwap, isPending: isSwapping } = useExecuteRelaySwap()
|
||||||
|
const { mutate: signSwap } = useSignSwap()
|
||||||
|
|
||||||
|
const trxQuotePayload = isTrxNetwork && parsedAmount > 0
|
||||||
|
? { from: fromToken.symbol, to: toToken.symbol, amountHuman: debouncedAmount }
|
||||||
|
: null
|
||||||
|
|
||||||
|
const { data: trxQuoteData } = useTrxSwapQuote(trxQuotePayload)
|
||||||
|
const { mutate: fetchTrxQuote, isPending: isFetchingTrxQuote } = useFetchTrxQuote()
|
||||||
|
const { mutate: executeTrxSwap } = useExecuteTrxSwap()
|
||||||
|
|
||||||
|
const displayToAmount = isTrxNetwork
|
||||||
|
? (trxQuoteData?.expectedOutFormatted ?? '0')
|
||||||
|
: (quoteData?.details.currencyOut.amountFormatted ?? '0')
|
||||||
|
|
||||||
|
const displayToUsd = isTrxNetwork
|
||||||
|
? undefined
|
||||||
|
: quoteData?.details.currencyOut.amountUsd
|
||||||
|
|
||||||
|
const gasFee = isTrxNetwork
|
||||||
|
? trxQuoteData?.fees.network.amountUsd?.toString()
|
||||||
|
: quoteData?.fees.gas.amountUsd
|
||||||
|
|
||||||
|
const isButtonDisabled = isTrxNetwork
|
||||||
|
? parsedAmount <= 0 || isFetchingTrxQuote
|
||||||
|
: !quotePayload || isSwapping
|
||||||
|
|
||||||
|
function handleSwap() {
|
||||||
|
if (isTrxNetwork) {
|
||||||
|
if (!trxQuotePayload) return
|
||||||
|
fetchTrxQuote(trxQuotePayload, {
|
||||||
|
onSuccess: (quote) => setTrxModalQuote(quote),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if (!quotePayload) return
|
||||||
|
executeSwap(quotePayload, {
|
||||||
|
onSuccess: (data) => setModalData(data),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.form}>
|
||||||
|
<NetworkSelect label="ИЗ" value={fromNetwork} onChange={setFromNetwork} />
|
||||||
|
<SwapCard
|
||||||
|
mode="from"
|
||||||
|
token={fromToken}
|
||||||
|
tokenOptions={tokenOptions}
|
||||||
|
amount={fromAmount}
|
||||||
|
usd={fromUsd}
|
||||||
|
onAmountChange={setFromAmount}
|
||||||
|
onSetPercent={setPercent}
|
||||||
|
onTokenChange={setFromToken}
|
||||||
|
hideNetworkSelect
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SwapDirectionButton onClick={swapTokens} />
|
||||||
|
|
||||||
|
<NetworkSelect label="В" value={toNetwork} onChange={setToNetwork} />
|
||||||
|
<SwapCard
|
||||||
|
mode="to"
|
||||||
|
token={toToken}
|
||||||
|
tokenOptions={tokenOptions}
|
||||||
|
amount={displayToAmount}
|
||||||
|
usd={displayToUsd}
|
||||||
|
onTokenChange={setToToken}
|
||||||
|
hideNetworkSelect
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SwapInfoPanel gasFee={gasFee} />
|
||||||
|
|
||||||
|
<PrimaryButton onClick={handleSwap} disabled={isButtonDisabled} />
|
||||||
|
|
||||||
|
{modalData && (
|
||||||
|
<SwapConfirmModal
|
||||||
|
data={modalData}
|
||||||
|
onClose={() => setModalData(null)}
|
||||||
|
onConfirm={() => {
|
||||||
|
const txData = modalData.steps[0]?.items[0]?.data
|
||||||
|
if (txData) signSwap({ chain: fromNetwork as Chain, txData })
|
||||||
|
setModalData(null)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{trxModalQuote && (
|
||||||
|
<TrxConfirmModal
|
||||||
|
quote={trxModalQuote}
|
||||||
|
fromSymbol={fromToken.symbol}
|
||||||
|
toSymbol={toToken.symbol}
|
||||||
|
amountHuman={fromAmount}
|
||||||
|
onClose={() => setTrxModalQuote(null)}
|
||||||
|
onConfirm={() => {
|
||||||
|
executeTrxSwap(trxModalQuote.quoteId)
|
||||||
|
setTrxModalQuote(null)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
42
src/widgets/bridge-form/ui/NetworkSelect.module.css
Normal file
42
src/widgets/bridge-form/ui/NetworkSelect.module.css
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
.wrap {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 0 4px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1.2px;
|
||||||
|
font-weight: 700;
|
||||||
|
min-width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select {
|
||||||
|
appearance: none;
|
||||||
|
background: rgba(255, 255, 255, 0.07);
|
||||||
|
border: 1px solid var(--glass-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 5px 28px 5px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23888' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 10px center;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--grad-center);
|
||||||
|
}
|
||||||
|
|
||||||
|
.select option {
|
||||||
|
background: #1a1a2e;
|
||||||
|
}
|
||||||
26
src/widgets/bridge-form/ui/NetworkSelect.tsx
Normal file
26
src/widgets/bridge-form/ui/NetworkSelect.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import styles from './NetworkSelect.module.css'
|
||||||
|
|
||||||
|
const NETWORKS = ['ETH', 'BSC', 'TRX', 'SOL']
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
label: string
|
||||||
|
value: string
|
||||||
|
onChange: (v: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NetworkSelect({ label, value, onChange }: Props) {
|
||||||
|
return (
|
||||||
|
<div className={styles.wrap}>
|
||||||
|
<span className={styles.label}>{label}</span>
|
||||||
|
<select
|
||||||
|
className={styles.select}
|
||||||
|
value={value}
|
||||||
|
onChange={e => onChange(e.target.value)}
|
||||||
|
>
|
||||||
|
{NETWORKS.map(n => (
|
||||||
|
<option key={n} value={n}>{n}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ interface Props {
|
|||||||
onSetPercent?: (p: number) => void
|
onSetPercent?: (p: number) => void
|
||||||
selectedNetwork?: string
|
selectedNetwork?: string
|
||||||
onNetworkChange?: (network: string) => void
|
onNetworkChange?: (network: string) => void
|
||||||
|
hideNetworkSelect?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const NETWORKS = ['ETH', 'BSC', 'TRX', 'SOL']
|
const NETWORKS = ['ETH', 'BSC', 'TRX', 'SOL']
|
||||||
@@ -21,7 +22,7 @@ const PERCENTS = [25, 50, 100]
|
|||||||
export function SwapCard({
|
export function SwapCard({
|
||||||
mode, token, tokenOptions, amount, usd,
|
mode, token, tokenOptions, amount, usd,
|
||||||
onTokenChange, onAmountChange, onSetPercent,
|
onTokenChange, onAmountChange, onSetPercent,
|
||||||
selectedNetwork, onNetworkChange,
|
selectedNetwork, onNetworkChange, hideNetworkSelect,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const [intPart, decPart] = amount.split('.')
|
const [intPart, decPart] = amount.split('.')
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ export function SwapCard({
|
|||||||
<div className={styles.card}>
|
<div className={styles.card}>
|
||||||
<div className={styles.top}>
|
<div className={styles.top}>
|
||||||
<div className={styles.label}>
|
<div className={styles.label}>
|
||||||
{mode === 'from' && (
|
{mode === 'from' && !hideNetworkSelect && (
|
||||||
<div className={styles.networkPills}>
|
<div className={styles.networkPills}>
|
||||||
{NETWORKS.map(n => (
|
{NETWORKS.map(n => (
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { useEffect, useRef, useState } from 'react'
|
|||||||
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { logout } from '@features/auth/api/registrationApi'
|
import { logout } from '@features/auth/api/registrationApi'
|
||||||
import { AUTH_QUERY_KEY } from '@features/auth'
|
import { AUTH_QUERY_KEY, useMe } from '@features/auth'
|
||||||
import { tokenStore } from '@shared/api/tokenStore'
|
import { tokenStore } from '@shared/api/tokenStore'
|
||||||
import { Notification } from '@shared/ui'
|
import { Notification } from '@shared/ui'
|
||||||
|
|
||||||
@@ -22,6 +22,8 @@ export function WalletHeader() {
|
|||||||
const ref = useRef<HTMLDivElement>(null)
|
const ref = useRef<HTMLDivElement>(null)
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
|
const { data: me } = useMe()
|
||||||
|
const fullName = me ? `${me.first_name} ${me.middle_name}`.trim() : ''
|
||||||
|
|
||||||
const { mutate: logoutMutate } = useMutation({
|
const { mutate: logoutMutate } = useMutation({
|
||||||
mutationFn: logout,
|
mutationFn: logout,
|
||||||
@@ -68,7 +70,7 @@ export function WalletHeader() {
|
|||||||
<div className={styles.accountWrapper} ref={ref}>
|
<div className={styles.accountWrapper} ref={ref}>
|
||||||
<button className={styles.account} onClick={() => setOpen(v => !v)}>
|
<button className={styles.account} onClick={() => setOpen(v => !v)}>
|
||||||
<div className={styles.avatar} />
|
<div className={styles.avatar} />
|
||||||
<span>Test account</span>
|
<span>{fullName || 'Test account'}</span>
|
||||||
</button>
|
</button>
|
||||||
{open && (
|
{open && (
|
||||||
<div className={styles.dropdown}>
|
<div className={styles.dropdown}>
|
||||||
|
|||||||
Reference in New Issue
Block a user