From 5e75d92dbbce346760742ab2bb55b89dd1a2ddac Mon Sep 17 00:00:00 2001 From: jinhojang6 Date: Tue, 22 Oct 2024 01:13:17 +0900 Subject: [PATCH] feat: implement wallet connect dropdown --- .../Dashboard/OperatorPanel/OperatorPanel.tsx | 115 +++++----- src/components/Header/Header/Header.tsx | 108 ++------- .../WalletConnect/WalletConnect.tsx | 215 ++++++++++++++++++ src/components/WalletConnect/index.ts | 1 + 4 files changed, 286 insertions(+), 153 deletions(-) create mode 100644 src/components/WalletConnect/WalletConnect.tsx create mode 100644 src/components/WalletConnect/index.ts diff --git a/src/components/Dashboard/OperatorPanel/OperatorPanel.tsx b/src/components/Dashboard/OperatorPanel/OperatorPanel.tsx index 6515f0a5d4..700f385cad 100644 --- a/src/components/Dashboard/OperatorPanel/OperatorPanel.tsx +++ b/src/components/Dashboard/OperatorPanel/OperatorPanel.tsx @@ -1,4 +1,3 @@ -import { Collapse } from '@/components/common/Collapse' import { breakpoints } from '@/configs/ui.configs' import { truncateString } from '@/utils/general.utils' import styled from '@emotion/styled' @@ -9,33 +8,33 @@ import useGetPillars from '../../../../apis/general/useGetPillars' interface OperatorPanelProps {} -const TEMP_BADGES = [ - { - id: 1, - image: '/dashboard/badges/1-year-streak.svg', - }, - { - id: 2, - image: '/dashboard/badges/10-day-streak.svg', - }, - { - id: 3, - image: '/dashboard/badges/100-days-streak.svg', - }, - { - id: 4, - image: '/dashboard/badges/badege-placeholder.svg', - }, -] +// const TEMP_BADGES = [ +// { +// id: 1, +// image: '/dashboard/badges/1-year-streak.svg', +// }, +// { +// id: 2, +// image: '/dashboard/badges/10-day-streak.svg', +// }, +// { +// id: 3, +// image: '/dashboard/badges/100-days-streak.svg', +// }, +// { +// id: 4, +// image: '/dashboard/badges/badege-placeholder.svg', +// }, +// ] const OperatorPanel: React.FC = () => { const { data: currentBlock } = useGetCurrentBTCBlock() const { data: epochs } = useGetEpochs() const { data: pillars } = useGetPillars() - console.log('currentBlock', currentBlock) - console.log('epochs', epochs) - console.log('pillars', pillars) + // console.log('currentBlock', currentBlock) + // console.log('epochs', epochs) + // console.log('pillars', pillars) return ( @@ -79,7 +78,7 @@ const OperatorPanel: React.FC = () => { - + {/* @@ -95,12 +94,12 @@ const OperatorPanel: React.FC = () => { ))} - + */} - + /> */} ) } @@ -159,11 +158,11 @@ const InfoRow = styled.div` background-color: var(--grey-900); ` -const ProfileInfo = styled.div` - display: flex; - flex-direction: column; - gap: 2px; -` +// const ProfileInfo = styled.div` +// display: flex; +// flex-direction: column; +// gap: 2px; +// ` const CallSignContainer = styled.div` margin-top: 24px; @@ -187,38 +186,38 @@ const Value = styled.span` } ` -const BadgesSection = styled.div` - display: flex; - flex-direction: column; - background-color: var(--grey-900); - padding: 16px 8px; -` +// const BadgesSection = styled.div` +// display: flex; +// flex-direction: column; +// background-color: var(--grey-900); +// padding: 16px 8px; +// ` -const BadgeTitle = styled.div` - display: flex; - align-items: center; - margin-bottom: 16px; - width: 100%; -` +// const BadgeTitle = styled.div` +// display: flex; +// align-items: center; +// margin-bottom: 16px; +// width: 100%; +// ` -const BadgeIcon = styled.img` - width: 14px; - height: 14px; +// const BadgeIcon = styled.img` +// width: 14px; +// height: 14px; - margin-left: auto; -` +// margin-left: auto; +// ` -const BadgeList = styled.div` - display: flex; - flex-wrap: wrap; - gap: 16px; -` +// const BadgeList = styled.div` +// display: flex; +// flex-wrap: wrap; +// gap: 16px; +// ` -const Badge = styled.img` - width: 52px; - height: 52px; - object-fit: contain; -` +// const Badge = styled.img` +// width: 52px; +// height: 52px; +// object-fit: contain; +// ` const ActionButton = styled.button` display: flex; diff --git a/src/components/Header/Header/Header.tsx b/src/components/Header/Header/Header.tsx index 183a3eaa53..87b24ab4af 100644 --- a/src/components/Header/Header/Header.tsx +++ b/src/components/Header/Header/Header.tsx @@ -1,11 +1,9 @@ import HamburguerMenu from '@/components/HamburgerMenu/HamburgerMenu' +import { WalletConnect } from '@/components/WalletConnect' import { breakpoints } from '@/configs/ui.configs' -import { truncateString } from '@/utils/general.utils' import styled from '@emotion/styled' import Link from 'next/link' -import React, { useState } from 'react' -import { api } from '../../../../common/api' -import { WALLET_SIGN_MESSAGE_REQUEST } from '../../../../constants/wallet' +import React from 'react' import { Navbar } from '../Navbar' declare global { @@ -18,42 +16,6 @@ declare global { interface NavbarProps {} const Header: React.FC = () => { - const [walletAddress, setWalletAddress] = useState(null) - - const connectWallet = async () => { - try { - if (walletAddress) { - setWalletAddress(null) - alert('Wallet disconnected.') - } else { - if (window.okxwallet) { - // DOCS: https://www.okx.com/web3/build/docs/sdks/chains/bitcoin/provider#connect - const result = await window.okxwallet.bitcoin.connect() - const address = result.address - - setWalletAddress(address) - - // Docs: https://www.okx.com/web3/build/docs/sdks/chains/bitcoin/provider#signmessage - const signature = await window.okxwallet.bitcoin.signMessage( - WALLET_SIGN_MESSAGE_REQUEST, - 'bip322-simple', - ) - - const response = await api.post('/token/pair', { - address, - signature, - }) - - console.log('Token pair response:', response) - } else { - alert('No Bitcoin wallet found. Please install OKX Wallet.') - } - } - } catch (error) { - console.error('Failed to connect or disconnect wallet:', error) - } - } - return ( @@ -72,7 +34,7 @@ const Header: React.FC = () => { Gitbook - = () => { Join our Discord - + */} + - - - {walletAddress ? truncateString(walletAddress) : 'Connect'} - - {/* */} - - {/* - 4,278 - - */} ) @@ -129,30 +82,15 @@ const UserActions = styled.div` } ` -const Button = styled.button` - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 12px; - height: 28px; - font-weight: 400; - font-size: 12px; - line-height: 16px; - border: 1px solid rgb(var(--lsd-border-primary)); - background: transparent; - color: rgb(var(--lsd-text-primary)); - cursor: pointer; -` +// const SocialButton = styled(Button)` +// width: 142px; +// box-sizing: border-box; +// gap: 12px; -const SocialButton = styled(Button)` - width: 142px; - box-sizing: border-box; - gap: 12px; - - span { - white-space: nowrap; - } -` +// span { +// white-space: nowrap; +// } +// ` const DesktopNavbar = styled.div` position: absolute; @@ -164,26 +102,6 @@ const DesktopNavbar = styled.div` } ` -const WalletButton = styled(Button)` - width: fit-content; -` - -const WalletAddress = styled.span` - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -` - -// const PointsButton = styled(Button)` -// width: 83px; -// ` - -// const PointsValue = styled.span`` - -const Icon = styled.img` - padding: 0; -` - const GitbookButton = styled.button` display: flex; padding: 6px 12px; diff --git a/src/components/WalletConnect/WalletConnect.tsx b/src/components/WalletConnect/WalletConnect.tsx new file mode 100644 index 0000000000..4ecca479d3 --- /dev/null +++ b/src/components/WalletConnect/WalletConnect.tsx @@ -0,0 +1,215 @@ +import { truncateString } from '@/utils/general.utils' +import styled from '@emotion/styled' +import React, { useEffect, useRef, useState } from 'react' +import { api } from '../../../common/api' +import { WALLET_SIGN_MESSAGE_REQUEST } from '../../../constants/wallet' + +const options = [ + { label: 'Multiplass', value: 'multiplass' }, + { label: 'Unisat', value: 'unisat' }, + { label: 'Magic Eden', value: 'magic-eden' }, + { label: 'Phantom', value: 'phantom' }, + { label: 'OKX', value: 'okx' }, +] + +const Dropdown: React.FC = () => { + const [isExpanded, setIsExpanded] = useState(false) + const [walletAddress, setWalletAddress] = useState(null) + + const connectWallet = async (wallet: string) => { + try { + if (walletAddress) { + setWalletAddress(null) + alert('Wallet disconnected.') + } else { + if (wallet === 'okx' && window.okxwallet) { + // Docs: https://www.okx.com/web3/build/docs/sdks/chains/bitcoin/provider#connect + const result = await window.okxwallet.bitcoin.connect() + const address = result.address + + setWalletAddress(address) + + // Docs: https://www.okx.com/web3/build/docs/sdks/chains/bitcoin/provider#signmessage + const signature = await window.okxwallet.bitcoin.signMessage( + WALLET_SIGN_MESSAGE_REQUEST, + 'bip322-simple', + ) + + const response = await api.post('/token/pair', { + address, + signature, + }) + + console.log('Token pair response:', response) + } else { + alert('Only OKX wallet is supported for now.') + } + } + } catch (error) { + console.error('Failed to connect or disconnect wallet:', error) + } + } + + const dropdownRef = useRef(null) + + const toggleDropdown = () => { + setIsExpanded(!isExpanded) + } + + const handleClickOutside = (event: MouseEvent) => { + if ( + dropdownRef.current && + !dropdownRef.current.contains(event.target as Node) + ) { + setIsExpanded(false) + } + } + + useEffect(() => { + if (isExpanded) { + document.addEventListener('mousedown', handleClickOutside) + } else { + document.removeEventListener('mousedown', handleClickOutside) + } + return () => { + document.removeEventListener('mousedown', handleClickOutside) + } + }, [isExpanded]) + + return ( + + +
+ + + {walletAddress ? truncateString(walletAddress) : 'Connect Wallet'} + + + + {isExpanded && ( + + + {options.map((option, index) => ( + connectWallet(option.value)} + key={'wallet-' + index} + > + {option.label} + + ))} + + + )} +
+ {walletAddress && ( + + 4,278 XP + + )} +
+
+ ) +} + +const DropdownContainer = styled.div` + position: relative; + display: inline-block; + + @media (max-width: 768px) { + width: 100%; + } +` + +const DropdownHeader = styled.div<{ isExpanded: boolean }>` + display: flex; + justify-content: space-between; + align-items: center; + position: relative; + + font-size: 14px; + line-height: 20px; +` + +const ScrollDiv = styled.div` + box-sizing: border-box; +` + +const WalletName = styled.div` + font-size: 12px; + line-height: 16px; + border-bottom: 1px solid rgb(var(--lsd-border-primary)); + display: flex; + padding: 6px 10px 6px 12px; + align-items: center; + align-self: stretch; + cursor: pointer; + + &:last-of-type { + border-bottom: none; + } +` + +const WalletButton = styled.button` + width: fit-content; + padding: 0 10px; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 12px; + height: 28px; + font-size: 12px; + line-height: 16px; + border: 1px solid rgb(var(--lsd-border-primary)); + background: transparent; + color: rgb(var(--lsd-text-primary)); + cursor: pointer; +` + +const WalletAddress = styled.span` + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + min-width: 90px; +` + +const Icon = styled.img` + padding: 0; + margin-left: 10px; +` + +const DropdownContent = styled.div` + position: absolute; + top: 28px; + left: 0; + width: 100%; + background-color: black; + border: 1px solid white; + border-top: none; + z-index: 10; + + box-sizing: border-box; +` + +const PointsButton = styled.button` + display: flex; + align-items: center; + justify-content: center; + width: fit-content; + position: absolute; + background-color: transparent; + cursor: pointer; + height: 28px; + border: 1px solid rgb(var(--lsd-border-primary)); + padding: 8px 12px; + top: 36px; + right: 0; +` + +const PointsValue = styled.span` + font-size: 12px; + line-height: 16px; + white-space: nowrap; + color: white; +` + +export default Dropdown diff --git a/src/components/WalletConnect/index.ts b/src/components/WalletConnect/index.ts new file mode 100644 index 0000000000..2174fc5794 --- /dev/null +++ b/src/components/WalletConnect/index.ts @@ -0,0 +1 @@ +export { default as WalletConnect } from './WalletConnect'