mirror of
https://github.com/kremalicious/blog.git
synced 2024-12-22 09:13:35 +01:00
Web3 cleanup (#678)
* rainbowkit setup and web3 cleanup * finalize setup * make transaction work * switch to @kremalicious/react-feather * update tests * remove @loadable/component * update test
This commit is contained in:
parent
c2429ac05a
commit
fd30a31c4b
@ -1,3 +1,4 @@
|
||||
GATSBY_GITHUB_TOKEN=your_token
|
||||
GATSBY_MAPBOX_ACCESS_TOKEN=
|
||||
GATSBY_TYPEKIT_ID=xxx
|
||||
INFURA_ID=xxx
|
@ -12,7 +12,6 @@ module.exports = {
|
||||
uri: 'https://matthiaskretschmann.com',
|
||||
twitter: 'https://twitter.com/kremalicious',
|
||||
github: 'https://github.com/kremalicious',
|
||||
facebook: 'https://facebook.com/matthiaskretschmann',
|
||||
bitcoin: '171qDmKEXm9YBgBLXyGjjPvopP5o9htQ1V',
|
||||
ether: '0xf50F267b5689b005FE107cfdb34619f24c014457'
|
||||
},
|
||||
|
@ -146,3 +146,13 @@ exports.onPostBuild = async ({ graphql }) => {
|
||||
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
exports.onCreateWebpackConfig = ({ actions }) => {
|
||||
actions.setWebpackConfig({
|
||||
resolve: {
|
||||
fallback: {
|
||||
util: false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
13
jest/__mocks__/@rainbow-me/rainbowkit.js
Normal file
13
jest/__mocks__/@rainbow-me/rainbowkit.js
Normal file
@ -0,0 +1,13 @@
|
||||
export function configureChains() {
|
||||
return { chains: [{}], provider: {} }
|
||||
}
|
||||
|
||||
export const apiProvider = {
|
||||
infura: jest.fn(),
|
||||
alchemy: jest.fn(),
|
||||
fallback: jest.fn()
|
||||
}
|
||||
|
||||
export function getDefaultWallets() {
|
||||
return { connectors: [{}] }
|
||||
}
|
72
jest/__mocks__/wagmi.js
Normal file
72
jest/__mocks__/wagmi.js
Normal file
@ -0,0 +1,72 @@
|
||||
import { chain as chainOrig } from 'wagmi'
|
||||
|
||||
export function useNetwork() {
|
||||
return {
|
||||
activeChain: {
|
||||
nativeCurrency: {
|
||||
symbol: 'ETH'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function useAccount() {
|
||||
return {
|
||||
data: {
|
||||
address: '0x0000000000000000000000000000000000000000'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function useSendTransaction() {
|
||||
return {
|
||||
data: {
|
||||
address: '0x0000000000000000000000000000000000000000'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function useEnsAvatar() {
|
||||
return {
|
||||
data: 'xxx.jpg'
|
||||
}
|
||||
}
|
||||
|
||||
export function useEnsName() {
|
||||
return {
|
||||
data: 'fguhifgvewtyifgwyufew.eth'
|
||||
}
|
||||
}
|
||||
|
||||
export function useBalance() {
|
||||
return {
|
||||
data: { formatted: '0.22', symbol: 'ETH' }
|
||||
}
|
||||
}
|
||||
|
||||
export function useConnect() {
|
||||
return {
|
||||
connect: jest.fn()
|
||||
}
|
||||
}
|
||||
|
||||
export function useDisconnect() {
|
||||
return {
|
||||
disconnect: jest.fn()
|
||||
}
|
||||
}
|
||||
|
||||
export function useProvider() {
|
||||
return {}
|
||||
}
|
||||
|
||||
export const chain = chainOrig
|
||||
|
||||
export function createClient() {
|
||||
return {
|
||||
queryClient: {
|
||||
mount: jest.fn(),
|
||||
unmount: jest.fn()
|
||||
}
|
||||
}
|
||||
}
|
@ -21,7 +21,8 @@ const esModules = [
|
||||
'ccount',
|
||||
'escape-string-regexp',
|
||||
'markdown-table',
|
||||
'web-namespaces'
|
||||
'web-namespaces',
|
||||
'@rainbow-me/rainbowkit'
|
||||
].join('|')
|
||||
|
||||
module.exports = {
|
||||
@ -34,8 +35,7 @@ module.exports = {
|
||||
'.+\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
|
||||
'<rootDir>/jest/__mocks__/file-mock.js',
|
||||
'\\.svg': '<rootDir>/jest/__mocks__/svgr-mock.js',
|
||||
'^@reach/router(.*)': '<rootDir>/node_modules/@gatsbyjs/reach-router$1',
|
||||
'^gatsby-page-utils/(.*)$': `gatsby-page-utils/dist/$1` // Workaround for https://github.com/facebook/jest/issues/9771
|
||||
'^@reach/router(.*)': '<rootDir>/node_modules/@gatsbyjs/reach-router$1'
|
||||
},
|
||||
testPathIgnorePatterns: ['node_modules', '.cache', 'public', 'coverage'],
|
||||
transformIgnorePatterns: [`node_modules/(?!(gatsby|${esModules})/)`],
|
||||
|
21717
package-lock.json
generated
21717
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
@ -28,16 +28,13 @@
|
||||
"not op_mini all"
|
||||
],
|
||||
"dependencies": {
|
||||
"@ethersproject/providers": "^5.6.5",
|
||||
"@ethersproject/units": "^5.6.0",
|
||||
"@loadable/component": "^5.15.2",
|
||||
"@web3-react/core": "^6.1.9",
|
||||
"@web3-react/injected-connector": "^6.0.7",
|
||||
"@kremalicious/react-feather": "^2.1.0",
|
||||
"@rainbow-me/rainbowkit": "^0.1.0",
|
||||
"axios": "^0.27.2",
|
||||
"classnames": "^2.3.1",
|
||||
"date-fns": "^2.28.0",
|
||||
"dms2dec": "^1.1.0",
|
||||
"ethereum-blockies": "github:MyEtherWallet/blockies",
|
||||
"ethers": "^5.6.5",
|
||||
"fast-exif": "^1.0.1",
|
||||
"feather-icons": "^4.29.0",
|
||||
"fraction.js": "^4.2.0",
|
||||
@ -71,15 +68,14 @@
|
||||
"react": "^18.1.0",
|
||||
"react-clipboard.js": "^2.0.16",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-feather": "^2.0.9",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-qr-svg": "^2.4.0",
|
||||
"react-transition-group": "^4.4.2",
|
||||
"rehype-react": "^7.1.1",
|
||||
"remark-parse": "^10.0.1",
|
||||
"remark-rehype": "^10.1.0",
|
||||
"slugify": "^1.6.5",
|
||||
"unified": "^10.1.2"
|
||||
"unified": "^10.1.2",
|
||||
"wagmi": "^0.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
@ -108,7 +104,8 @@
|
||||
"eslint-plugin-testing-library": "^5.4.0",
|
||||
"fs-extra": "^10.1.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^27.5.1",
|
||||
"jest": "^28.1.0",
|
||||
"jest-environment-jsdom": "^28.1.0",
|
||||
"markdownlint-cli": "^0.31.1",
|
||||
"node-iptc": "^1.0.5",
|
||||
"npm-run-all": "^4.1.5",
|
||||
|
1
src/@types/Site.d.ts
vendored
1
src/@types/Site.d.ts
vendored
@ -9,7 +9,6 @@ export interface Author {
|
||||
uri: string
|
||||
twitter: string
|
||||
github: string
|
||||
facebook: string
|
||||
bitcoin: string
|
||||
ether: string
|
||||
}
|
||||
|
6
src/@types/node_modules.d.ts
vendored
6
src/@types/node_modules.d.ts
vendored
@ -1,11 +1,5 @@
|
||||
declare module 'pigeon-maps'
|
||||
declare module 'pigeon-marker'
|
||||
declare module 'react-blockies'
|
||||
declare module 'remark-react'
|
||||
declare module 'unified'
|
||||
declare module 'fast-exif'
|
||||
declare module 'node-iptc'
|
||||
|
||||
declare module 'ethereum-blockies' {
|
||||
export function toDataUrl(address: string): string
|
||||
}
|
||||
|
11
src/components/atoms/Copy.test.tsx
Normal file
11
src/components/atoms/Copy.test.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
import { render, screen, fireEvent } from '@testing-library/react'
|
||||
|
||||
import Copy from './Copy'
|
||||
|
||||
describe('Copy', () => {
|
||||
it('renders without crashing', () => {
|
||||
render(<Copy text="hello" />)
|
||||
fireEvent.click(screen.getByTitle('Copy to clipboard'))
|
||||
})
|
||||
})
|
@ -1,9 +1,7 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import loadable from '@loadable/component'
|
||||
import { copied, button } from './Copy.module.css'
|
||||
import Icon from './Icon'
|
||||
|
||||
const Clipboard = loadable(() => import('react-clipboard.js'))
|
||||
import Clipboard from 'react-clipboard.js'
|
||||
|
||||
const onCopySuccess = (e: any) => {
|
||||
e.trigger.classList.add(copied)
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { FunctionComponent, ReactElement } from 'react'
|
||||
|
||||
// https://featherstyles.com
|
||||
// import * as Feather from 'react-feather'
|
||||
// import * as Feather from '@kremalicious/react-feather'
|
||||
import {
|
||||
ArrowDownCircle,
|
||||
Edit,
|
||||
@ -22,7 +22,7 @@ import {
|
||||
Aperture,
|
||||
Maximize,
|
||||
Crosshair
|
||||
} from 'react-feather'
|
||||
} from '@kremalicious/react-feather'
|
||||
// custom icons
|
||||
import { ReactComponent as Jsonfeed } from '../../images/jsonfeed.svg'
|
||||
import { ReactComponent as Bitcoin } from '../../images/bitcoin.svg'
|
||||
|
@ -1,11 +1,9 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import React, { ReactElement, InputHTMLAttributes } from 'react'
|
||||
import { input } from './Input.module.css'
|
||||
|
||||
export default function Input({
|
||||
className,
|
||||
...props
|
||||
}: {
|
||||
className: string
|
||||
}): ReactElement {
|
||||
}: InputHTMLAttributes<HTMLInputElement>): ReactElement {
|
||||
return <input className={`${input} ${className || ''}`} {...props} />
|
||||
}
|
||||
|
@ -1,21 +0,0 @@
|
||||
.qr {
|
||||
margin-bottom: calc(var(--spacer) / 2);
|
||||
}
|
||||
|
||||
.code {
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
padding-right: 2rem;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.code code {
|
||||
padding: calc(var(--spacer) / 2);
|
||||
font-size: 0.65rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: calc(var(--spacer) / 4);
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
import React, { Suspense } from 'react'
|
||||
import { render, waitFor } from '@testing-library/react'
|
||||
|
||||
import Qr from './Qr'
|
||||
|
||||
describe('Qr', () => {
|
||||
test('renders lazy', async () => {
|
||||
const { container } = render(
|
||||
<Suspense fallback="test loading">
|
||||
<Qr address="xxx" />
|
||||
</Suspense>
|
||||
)
|
||||
expect(container.firstChild).toBeInTheDocument()
|
||||
await waitFor(() => container.querySelector('button'))
|
||||
})
|
||||
})
|
@ -1,31 +0,0 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
// import { QRCode } from 'react-qr-svg'
|
||||
import { title as styleTitle, code } from './Qr.module.css'
|
||||
import Copy from './Copy'
|
||||
|
||||
export default function Qr({
|
||||
address,
|
||||
title
|
||||
}: {
|
||||
address: string
|
||||
title?: string
|
||||
}): ReactElement {
|
||||
return (
|
||||
<>
|
||||
{title && <h4 className={styleTitle}>{title}</h4>}
|
||||
{/* <QRCode
|
||||
bgColor="transparent"
|
||||
fgColor="#6b7f88"
|
||||
level="Q"
|
||||
style={{ width: 120 }}
|
||||
value={address}
|
||||
className={qr}
|
||||
/> */}
|
||||
|
||||
<pre className={code}>
|
||||
<code>{address}</code>
|
||||
<Copy text={address} />
|
||||
</pre>
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,41 +1,13 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import loadable from '@loadable/component'
|
||||
|
||||
const LazyDate = loadable.lib(() => import('date-fns'))
|
||||
|
||||
function TimeMarkup({
|
||||
date,
|
||||
format,
|
||||
formatDistance
|
||||
}: {
|
||||
date: Date
|
||||
format?: any
|
||||
formatDistance?: any
|
||||
}) {
|
||||
const dateIso = date.toISOString()
|
||||
|
||||
return (
|
||||
<time
|
||||
title={format ? format(date, 'yyyy/MM/dd HH:mm') : dateIso}
|
||||
dateTime={dateIso}
|
||||
>
|
||||
{format ? formatDistance(date, Date.now(), { addSuffix: true }) : dateIso}
|
||||
</time>
|
||||
)
|
||||
}
|
||||
import { format, formatDistance } from 'date-fns'
|
||||
|
||||
export default function Time({ date }: { date: string }): ReactElement {
|
||||
const dateNew = new Date(date)
|
||||
const dateIso = dateNew.toISOString()
|
||||
|
||||
return (
|
||||
<LazyDate fallback={<TimeMarkup date={dateNew} />}>
|
||||
{({ format, formatDistance }) => (
|
||||
<TimeMarkup
|
||||
date={dateNew}
|
||||
format={format}
|
||||
formatDistance={formatDistance}
|
||||
/>
|
||||
)}
|
||||
</LazyDate>
|
||||
<time title={format(dateNew, 'yyyy/MM/dd HH:mm')} dateTime={dateIso}>
|
||||
{formatDistance(dateNew, Date.now(), { addSuffix: true })}
|
||||
</time>
|
||||
)
|
||||
}
|
||||
|
@ -1,26 +0,0 @@
|
||||
.link {
|
||||
margin-bottom: calc(var(--spacer) / 2);
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.accountWrap {
|
||||
font-size: var(--font-size-small);
|
||||
color: var(--text-color-light);
|
||||
margin-bottom: calc(var(--spacer) / 2);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.blockies {
|
||||
width: 1.2rem;
|
||||
height: 1.2rem;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: calc(var(--spacer) / 4);
|
||||
}
|
||||
|
||||
.balance {
|
||||
margin-left: var(--spacer);
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import React from 'react'
|
||||
import { render } from '@testing-library/react'
|
||||
|
||||
import Account from './Account'
|
||||
|
||||
describe('Account', () => {
|
||||
it('renders without crashing', () => {
|
||||
const { container } = render(<Account />)
|
||||
expect(container.firstChild).toBeInTheDocument()
|
||||
})
|
||||
})
|
@ -1,49 +0,0 @@
|
||||
import React, { ReactElement, useState, useEffect } from 'react'
|
||||
import { toDataUrl } from 'ethereum-blockies'
|
||||
import { formatEther } from '@ethersproject/units'
|
||||
import useWeb3, { connectors } from '../../../hooks/useWeb3'
|
||||
import {
|
||||
accountWrap,
|
||||
blockies as styleBlockies,
|
||||
balance,
|
||||
link
|
||||
} from './Account.module.css'
|
||||
|
||||
export default function Account(): ReactElement {
|
||||
const { library, account, activate } = useWeb3()
|
||||
const [ethBalance, setEthBalance] = useState('0')
|
||||
const blockies = account && toDataUrl(account)
|
||||
|
||||
useEffect(() => {
|
||||
if (!library || !account) return
|
||||
|
||||
async function init() {
|
||||
const balance = await library.getBalance(account)
|
||||
setEthBalance(balance.toString())
|
||||
}
|
||||
init()
|
||||
}, [library, account])
|
||||
|
||||
const accountDisplay =
|
||||
account &&
|
||||
`${account.substring(0, 8)}...${account.substring(account.length - 4)}`
|
||||
const balanceDisplay =
|
||||
ethBalance && `Ξ${parseFloat(formatEther(ethBalance)).toPrecision(4)}`
|
||||
|
||||
return account ? (
|
||||
<div className={accountWrap} title={account}>
|
||||
<span>
|
||||
<img className={styleBlockies} src={blockies} alt="Blockies" />
|
||||
{accountDisplay}
|
||||
</span>
|
||||
<span className={balance}>{balanceDisplay}</span>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
className={`link ${link}`}
|
||||
onClick={() => activate(connectors.MetaMask)}
|
||||
>
|
||||
Connect MetaMask
|
||||
</button>
|
||||
)
|
||||
}
|
15
src/components/molecules/Web3Donation/Alert.test.tsx
Normal file
15
src/components/molecules/Web3Donation/Alert.test.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import React from 'react'
|
||||
import { render } from '@testing-library/react'
|
||||
|
||||
import Alert from './Alert'
|
||||
|
||||
describe('Alert', () => {
|
||||
it('renders without crashing', async () => {
|
||||
render(
|
||||
<Alert
|
||||
message={{ status: 'loading', text: 'Loading' }}
|
||||
transactionHash="0xxxx"
|
||||
/>
|
||||
)
|
||||
})
|
||||
})
|
@ -6,7 +6,5 @@ import Conversion from './Conversion'
|
||||
describe('Conversion', () => {
|
||||
it('renders without crashing', async () => {
|
||||
render(<Conversion amount="1" />)
|
||||
// const lazyElement = await findByText(/= €/)
|
||||
// expect(lazyElement).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
@ -1,16 +1,17 @@
|
||||
import React, { useState, useEffect, ReactElement } from 'react'
|
||||
import axios from 'axios'
|
||||
import { conversion as styleConversion } from './Conversion.module.css'
|
||||
import { useNetwork } from 'wagmi'
|
||||
|
||||
export async function getFiat(
|
||||
amount: number
|
||||
amount: number,
|
||||
tokenId = 'ethereum'
|
||||
): Promise<{ [key: string]: string }> {
|
||||
const url =
|
||||
'https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=eur%2Cusd'
|
||||
const url = `https://api.coingecko.com/api/v3/simple/price?ids=${tokenId}&vs_currencies=eur%2Cusd`
|
||||
const response = await axios(url)
|
||||
|
||||
if (!response) console.error(response.statusText)
|
||||
const { usd, eur } = response.data.ethereum
|
||||
const { usd, eur } = response.data[tokenId]
|
||||
const dollar = (amount * usd).toFixed(2)
|
||||
const euro = (amount * eur).toFixed(2)
|
||||
|
||||
@ -22,24 +23,32 @@ export default function Conversion({
|
||||
}: {
|
||||
amount: string
|
||||
}): ReactElement {
|
||||
const { activeChain } = useNetwork()
|
||||
|
||||
const [conversion, setConversion] = useState({
|
||||
euro: '0.00',
|
||||
dollar: '0.00'
|
||||
})
|
||||
const { dollar, euro } = conversion
|
||||
|
||||
async function getFiatResponse() {
|
||||
try {
|
||||
const { dollar, euro } = await getFiat(Number(amount))
|
||||
setConversion({ euro, dollar })
|
||||
} catch (error) {
|
||||
console.error(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeChain?.nativeCurrency?.symbol) return
|
||||
|
||||
async function getFiatResponse() {
|
||||
try {
|
||||
const tokenId =
|
||||
activeChain?.nativeCurrency?.symbol === 'MATIC'
|
||||
? 'matic-network'
|
||||
: 'ethereum'
|
||||
const { dollar, euro } = await getFiat(Number(amount), tokenId)
|
||||
setConversion({ euro, dollar })
|
||||
} catch (error) {
|
||||
console.error(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
getFiatResponse()
|
||||
}, [amount])
|
||||
}, [amount, activeChain?.nativeCurrency?.symbol])
|
||||
|
||||
return (
|
||||
<div className={styleConversion}>
|
||||
|
@ -1,8 +1,8 @@
|
||||
.inputGroup {
|
||||
max-width: 20rem;
|
||||
margin: auto;
|
||||
position: relative;
|
||||
animation: fadeIn 0.8s ease-out backwards;
|
||||
margin-top: var(--spacer);
|
||||
}
|
||||
|
||||
@media (min-width: 40rem) {
|
||||
@ -16,7 +16,7 @@
|
||||
width: 100%;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
border-color: var(--text-color-light);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
@media (min-width: 40rem) {
|
||||
@ -29,10 +29,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
:global(.dark) .inputGroup button {
|
||||
border-color: #000;
|
||||
}
|
||||
|
||||
.input {
|
||||
position: relative;
|
||||
}
|
||||
@ -43,9 +39,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
.input input {
|
||||
.inputInput {
|
||||
text-align: center;
|
||||
border: 1px solid var(--text-color-light);
|
||||
border: 1px solid var(--border-color);
|
||||
font-size: var(--font-size-large);
|
||||
padding: calc(var(--spacer) / 3) calc(var(--spacer) / 3)
|
||||
calc(var(--spacer) / 3) calc(var(--spacer) * 1.7);
|
||||
@ -55,21 +51,21 @@
|
||||
}
|
||||
|
||||
@media (min-width: 40rem) {
|
||||
.input input {
|
||||
.inputInput {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: var(--border-radius);
|
||||
border-bottom: 1px solid var(--text-color-light);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
border-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.input input::-webkit-inner-spin-button {
|
||||
.inputInput::-webkit-inner-spin-button {
|
||||
margin-left: -1rem;
|
||||
}
|
||||
|
||||
:global(.dark) .input input {
|
||||
border-color: #000;
|
||||
:global(.dark) .inputInput {
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
.currency {
|
||||
@ -80,7 +76,7 @@
|
||||
font-size: var(--font-size-small);
|
||||
padding: calc(var(--spacer) / 3);
|
||||
background: var(--box-background-color);
|
||||
border-right: 1px solid var(--text-color-light);
|
||||
border-right: 1px solid var(--text-color-dimmed);
|
||||
border-top-left-radius: var(--border-radius);
|
||||
border-bottom-left-radius: var(--border-radius);
|
||||
display: flex;
|
||||
|
@ -13,9 +13,9 @@ describe('InputGroup', () => {
|
||||
expect(container.firstChild).toBeInTheDocument()
|
||||
|
||||
const input = container.querySelector('input')
|
||||
// const button = container.querySelector('button')
|
||||
const button = container.querySelector('button')
|
||||
fireEvent.change(input, { target: { value: '3' } })
|
||||
// fireEvent.click(button)
|
||||
// expect(sendTransaction).toHaveBeenCalled()
|
||||
fireEvent.click(button)
|
||||
expect(sendTransaction).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
@ -1,15 +1,21 @@
|
||||
import React, { ReactElement, useState } from 'react'
|
||||
import useWeb3 from '../../../hooks/useWeb3'
|
||||
import { useAccount, useNetwork } from 'wagmi'
|
||||
import Input from '../../atoms/Input'
|
||||
import Conversion from './Conversion'
|
||||
import { inputGroup, input, currency } from './InputGroup.module.css'
|
||||
import {
|
||||
inputGroup,
|
||||
input,
|
||||
inputInput,
|
||||
currency
|
||||
} from './InputGroup.module.css'
|
||||
|
||||
export default function InputGroup({
|
||||
sendTransaction
|
||||
}: {
|
||||
sendTransaction(amount: string): void
|
||||
}): ReactElement {
|
||||
const { account } = useWeb3()
|
||||
const { data: account } = useAccount()
|
||||
const { activeChain } = useNetwork()
|
||||
const [amount, setAmount] = useState('0.01')
|
||||
|
||||
const onAmountChange = ({ target }: { target: any }) => {
|
||||
@ -21,14 +27,16 @@ export default function InputGroup({
|
||||
<div className={inputGroup}>
|
||||
<div className={input}>
|
||||
<Input
|
||||
type="number"
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
pattern="[0-9.]*"
|
||||
value={amount}
|
||||
onChange={onAmountChange}
|
||||
min="0"
|
||||
step="0.01"
|
||||
className={inputInput}
|
||||
disabled={!account}
|
||||
/>
|
||||
<div className={currency}>
|
||||
<span>ETH</span>
|
||||
<span>{activeChain?.nativeCurrency?.symbol || 'ETH'}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
|
@ -1,10 +1,16 @@
|
||||
.web3 {
|
||||
composes: container from '../../Layout.module.css';
|
||||
max-width: 20rem;
|
||||
margin-bottom: calc(var(--spacer) * 2);
|
||||
text-align: center;
|
||||
min-height: 165px;
|
||||
}
|
||||
|
||||
.web3:empty {
|
||||
display: none;
|
||||
.web3 > div:first-child {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: var(--font-size-small);
|
||||
margin-bottom: var(--spacer);
|
||||
}
|
||||
|
||||
.message {
|
||||
|
@ -1,24 +0,0 @@
|
||||
import React from 'react'
|
||||
import { render, waitFor, fireEvent, screen } from '@testing-library/react'
|
||||
import { Web3ReactProvider } from '@web3-react/core'
|
||||
import { getLibrary } from '../../../hooks/useWeb3'
|
||||
|
||||
import Web3Donation from '.'
|
||||
|
||||
describe('Web3Donation', () => {
|
||||
it('renders without crashing', async () => {
|
||||
const { container } = render(
|
||||
<Web3ReactProvider getLibrary={getLibrary}>
|
||||
<Web3Donation address="xxx" />
|
||||
</Web3ReactProvider>
|
||||
)
|
||||
const lazyElement = await waitFor(() => container.querySelector('button'))
|
||||
expect(lazyElement).toBeInTheDocument()
|
||||
|
||||
fireEvent.click(lazyElement)
|
||||
const message = await screen.findByText(
|
||||
/No Ethereum browser extension detected/
|
||||
)
|
||||
expect(message).toBeInTheDocument()
|
||||
})
|
||||
})
|
@ -1,42 +1,33 @@
|
||||
import React, { ReactElement, useState, useEffect } from 'react'
|
||||
import React, { ReactElement, useState } from 'react'
|
||||
import { parseEther } from '@ethersproject/units'
|
||||
import useWeb3, { getErrorMessage } from '../../../hooks/useWeb3'
|
||||
import InputGroup from './InputGroup'
|
||||
import Alert, { getTransactionMessage } from './Alert'
|
||||
import { web3 as styleWeb3 } from './index.module.css'
|
||||
import Account from './Account'
|
||||
import { useSendTransaction } from 'wagmi'
|
||||
import { ConnectButton } from '@rainbow-me/rainbowkit'
|
||||
|
||||
export default function Web3Donation({
|
||||
address
|
||||
}: {
|
||||
address: string
|
||||
}): ReactElement {
|
||||
const { connector, library, chainId, account, active, error } = useWeb3()
|
||||
const { sendTransactionAsync } = useSendTransaction()
|
||||
|
||||
const [message, setMessage] = useState<{ status: string; text: string }>()
|
||||
const [transactionHash, setTransactionHash] = useState<string>()
|
||||
|
||||
useEffect(() => {
|
||||
setMessage(undefined)
|
||||
|
||||
error &&
|
||||
setMessage({
|
||||
status: 'error',
|
||||
text: getErrorMessage(error, chainId)
|
||||
})
|
||||
}, [connector, account, library, chainId, active, error])
|
||||
|
||||
async function sendTransaction(amount: string) {
|
||||
const signer = library.getSigner()
|
||||
|
||||
async function handleSendTransaction(amount: string) {
|
||||
setMessage({
|
||||
status: 'loading',
|
||||
text: getTransactionMessage().waitingForUser
|
||||
})
|
||||
|
||||
try {
|
||||
const tx = await signer.sendTransaction({
|
||||
to: address,
|
||||
value: parseEther(amount) // ETH -> Wei
|
||||
const tx = await sendTransactionAsync({
|
||||
request: {
|
||||
to: address,
|
||||
value: parseEther(amount) // ETH -> Wei
|
||||
}
|
||||
})
|
||||
setTransactionHash(tx.hash)
|
||||
setMessage({
|
||||
@ -57,11 +48,12 @@ export default function Web3Donation({
|
||||
|
||||
return (
|
||||
<div className={styleWeb3}>
|
||||
<Account />
|
||||
<ConnectButton chainStatus="icon" showBalance={false} />
|
||||
|
||||
{message ? (
|
||||
<Alert message={message} transactionHash={transactionHash} />
|
||||
) : (
|
||||
<InputGroup sendTransaction={sendTransaction} />
|
||||
<InputGroup sendTransaction={handleSendTransaction} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
@ -124,13 +124,13 @@
|
||||
--body-background-color: #161a1b;
|
||||
--box-background-color: rgba(255 255 255 / 3%);
|
||||
|
||||
--box-shadow: 0 1.3px 5.4px rgba(0 7 8 / 60%),
|
||||
0 4.5px 18.1px rgba(0 7 8 / 40%), 0 20px 81px rgba(0 7 8 / 10%);
|
||||
--box-shadow: 0 1.3px 5.4px rgba(0 7 8 / 50%),
|
||||
0 4.5px 18.1px rgba(0 7 8 / 30%), 0 20px 81px rgba(0 7 8 / 10%);
|
||||
|
||||
--text-color: var(--brand-grey-light);
|
||||
--text-color-light: var(--brand-grey);
|
||||
--text-color-dimmed: var(--brand-grey-dark);
|
||||
--border-color: var(--brand-grey-dark);
|
||||
--border-color: #111;
|
||||
--color-headings: var(--brand-main-light);
|
||||
|
||||
--input-bg: var(--body-background-color);
|
||||
|
@ -2,3 +2,4 @@
|
||||
@import '_code.css';
|
||||
@import '_toast.css';
|
||||
@import '_alerts.css';
|
||||
@import '@rainbow-me/rainbowkit/styles.css';
|
||||
|
74
src/helpers/rainbowkit.ts
Normal file
74
src/helpers/rainbowkit.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import {
|
||||
configureChains,
|
||||
apiProvider,
|
||||
getDefaultWallets,
|
||||
Theme
|
||||
} from '@rainbow-me/rainbowkit'
|
||||
import { chain, createClient } from 'wagmi'
|
||||
|
||||
export const { chains, provider } = configureChains(
|
||||
[chain.mainnet, chain.polygon, chain.optimism, chain.arbitrum, chain.rinkeby],
|
||||
[apiProvider.infura(process.env.INFURA_ID), apiProvider.fallback()]
|
||||
)
|
||||
|
||||
export const { connectors } = getDefaultWallets({
|
||||
appName: 'kremalicious.com',
|
||||
chains
|
||||
})
|
||||
|
||||
export const wagmiClient = createClient({
|
||||
autoConnect: true,
|
||||
connectors,
|
||||
provider
|
||||
})
|
||||
|
||||
export const theme: Theme = {
|
||||
colors: {
|
||||
accentColor: 'var(--brand-cyan)',
|
||||
accentColorForeground: '#161a1b',
|
||||
actionButtonBorder: 'var(--body-background-color)',
|
||||
actionButtonBorderMobile: 'var(--body-background-color)',
|
||||
actionButtonSecondaryBackground: 'var(--box-background-color)',
|
||||
closeButton: 'var(--text-color)',
|
||||
closeButtonBackground: 'var(--box-background-color)',
|
||||
connectButtonBackground: 'var(--body-background-color)',
|
||||
connectButtonBackgroundError: 'var(--alert-error)',
|
||||
connectButtonInnerBackground: 'var(--box-background-color)',
|
||||
connectButtonText: 'var(--text-color)',
|
||||
connectButtonTextError: '#161a1b',
|
||||
connectionIndicator: 'var(--alert-success)',
|
||||
error: 'var(--alert-error)',
|
||||
generalBorder: 'var(--border-color)',
|
||||
generalBorderDim: 'var(--border-color)',
|
||||
menuItemBackground: 'var(--link-color)',
|
||||
modalBackdrop: 'rgba(0, 0, 0, 0.5)',
|
||||
modalBackground: 'var(--body-background-color)',
|
||||
modalBorder: 'var(--body-background-color)',
|
||||
modalText: 'var(--text-color)',
|
||||
modalTextDim: 'var(--text-color-dimmed)',
|
||||
modalTextSecondary: 'var(--text-color-light)',
|
||||
profileAction: 'var(--body-background-color)',
|
||||
profileActionHover: 'var(--box-background-color)',
|
||||
profileForeground: 'var(--body-background-color)',
|
||||
selectedOptionBorder: 'var(--boder-color)',
|
||||
standby: 'var(--text-color-dimmed)'
|
||||
},
|
||||
fonts: {
|
||||
body: 'var(--font-family-headings)'
|
||||
},
|
||||
radii: {
|
||||
actionButton: 'var(--border-radius)',
|
||||
connectButton: 'var(--border-radius)',
|
||||
menuButton: 'var(--border-radius)',
|
||||
modal: 'var(--border-radius)',
|
||||
modalMobile: 'var(--border-radius)'
|
||||
},
|
||||
shadows: {
|
||||
connectButton: 'var(--box-shadow)',
|
||||
dialog: 'var(--box-shadow)',
|
||||
profileDetailsAction: 'none',
|
||||
selectedOption: 'var(--box-shadow)',
|
||||
selectedWallet: 'var(--box-shadow)',
|
||||
walletLogo: 'var(--box-shadow)'
|
||||
}
|
||||
}
|
@ -15,7 +15,6 @@ const query = graphql`
|
||||
uri
|
||||
twitter
|
||||
github
|
||||
facebook
|
||||
bitcoin
|
||||
ether
|
||||
}
|
||||
|
@ -1,75 +0,0 @@
|
||||
import { InjectedConnector } from '@web3-react/injected-connector'
|
||||
// import { NetworkConnector } from '@web3-react/network-connector'
|
||||
// import { WalletConnectConnector } from '@web3-react/walletconnect-connector'
|
||||
// import { WalletLinkConnector } from '@web3-react/walletlink-connector'
|
||||
// import { LedgerConnector } from '@web3-react/ledger-connector'
|
||||
// import { TrezorConnector } from '@web3-react/trezor-connector'
|
||||
// import { FrameConnector } from '@web3-react/frame-connector'
|
||||
// import { AuthereumConnector } from '@web3-react/authereum-connector'
|
||||
// import { FortmaticConnector } from '@web3-react/fortmatic-connector'
|
||||
// import { PortisConnector } from '@web3-react/portis-connector'
|
||||
// import { SquarelinkConnector } from '@web3-react/squarelink-connector'
|
||||
// import { TorusConnector } from '@web3-react/torus-connector'
|
||||
|
||||
// const POLLING_INTERVAL = 8000
|
||||
// const RPC_URLS: { [chainId: number]: string } = {
|
||||
// 1: process.env.RPC_URL_1 as string,
|
||||
// 4: process.env.RPC_URL_4 as string
|
||||
// }
|
||||
|
||||
export const MetaMask = new InjectedConnector({
|
||||
supportedChainIds: [1]
|
||||
})
|
||||
|
||||
// export const network = new NetworkConnector({
|
||||
// urls: { 1: RPC_URLS[1], 4: RPC_URLS[4] },
|
||||
// defaultChainId: 1,
|
||||
// pollingInterval: POLLING_INTERVAL
|
||||
// })
|
||||
|
||||
// export const walletconnect = new WalletConnectConnector({
|
||||
// rpc: { 1: RPC_URLS[1] },
|
||||
// bridge: 'https://bridge.walletconnect.org',
|
||||
// qrcode: true,
|
||||
// pollingInterval: POLLING_INTERVAL
|
||||
// })
|
||||
|
||||
// export const walletlink = new WalletLinkConnector({
|
||||
// url: RPC_URLS[1],
|
||||
// appName: 'web3-react example'
|
||||
// })
|
||||
|
||||
// export const ledger = new LedgerConnector({
|
||||
// chainId: 1,
|
||||
// url: RPC_URLS[1],
|
||||
// pollingInterval: POLLING_INTERVAL
|
||||
// })
|
||||
|
||||
// export const trezor = new TrezorConnector({
|
||||
// chainId: 1,
|
||||
// url: RPC_URLS[1],
|
||||
// pollingInterval: POLLING_INTERVAL,
|
||||
// manifestEmail: 'dummy@abc.xyz',
|
||||
// manifestAppUrl: 'http://localhost:1234'
|
||||
// })
|
||||
|
||||
// export const frame = new FrameConnector({ supportedChainIds: [1] })
|
||||
|
||||
// export const authereum = new AuthereumConnector({ chainId: 42 })
|
||||
|
||||
// export const fortmatic = new FortmaticConnector({
|
||||
// apiKey: process.env.FORTMATIC_API_KEY as string,
|
||||
// chainId: 4
|
||||
// })
|
||||
|
||||
// export const portis = new PortisConnector({
|
||||
// dAppId: process.env.PORTIS_DAPP_ID as string,
|
||||
// networks: [1, 100]
|
||||
// })
|
||||
|
||||
// export const squarelink = new SquarelinkConnector({
|
||||
// clientId: process.env.SQUARELINK_CLIENT_ID as string,
|
||||
// networks: [1, 100]
|
||||
// })
|
||||
|
||||
// export const torus = new TorusConnector({ chainId: 1 })
|
@ -1,44 +0,0 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { Web3ReactContextInterface } from '@web3-react/core/dist/types'
|
||||
import { Web3Provider } from '@ethersproject/providers'
|
||||
import * as connectors from './connectors'
|
||||
import { getLibrary, getNetworkName, getErrorMessage } from './utils'
|
||||
|
||||
function useEagerConnect(): boolean {
|
||||
const { MetaMask } = connectors
|
||||
const { activate, active } = useWeb3React<Web3Provider>()
|
||||
const [tried, setTried] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
MetaMask.isAuthorized().then((isAuthorized) => {
|
||||
if (isAuthorized) {
|
||||
activate(MetaMask, undefined, true).catch(() => {
|
||||
setTried(true)
|
||||
})
|
||||
} else {
|
||||
setTried(true)
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
// if the connection worked, wait until we get confirmation of that to flip the flag
|
||||
useEffect(() => {
|
||||
if (!tried && active) {
|
||||
setTried(true)
|
||||
}
|
||||
}, [tried, active])
|
||||
|
||||
return tried
|
||||
}
|
||||
|
||||
// Helper hook around useWeb3React to push typings, and connect by default
|
||||
export default function useWeb3(): Web3ReactContextInterface<Web3Provider> {
|
||||
const context = useWeb3React<Web3Provider>()
|
||||
|
||||
useEagerConnect()
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
export { connectors, getLibrary, getNetworkName, getErrorMessage }
|
@ -1,52 +0,0 @@
|
||||
import { UnsupportedChainIdError } from '@web3-react/core'
|
||||
import {
|
||||
NoEthereumProviderError,
|
||||
UserRejectedRequestError
|
||||
} from '@web3-react/injected-connector'
|
||||
import { Web3Provider, ExternalProvider } from '@ethersproject/providers'
|
||||
|
||||
export function getLibrary(provider: ExternalProvider): Web3Provider {
|
||||
const library = new Web3Provider(provider)
|
||||
library.pollingInterval = 10000
|
||||
return library
|
||||
}
|
||||
|
||||
export function getNetworkName(netId: number): string {
|
||||
let networkName: string
|
||||
|
||||
switch (netId) {
|
||||
case 1:
|
||||
networkName = 'Main'
|
||||
break
|
||||
case 2:
|
||||
networkName = 'Morden'
|
||||
break
|
||||
case 3:
|
||||
networkName = 'Ropsten'
|
||||
break
|
||||
case 4:
|
||||
networkName = 'Rinkeby'
|
||||
break
|
||||
case 42:
|
||||
networkName = 'Kovan'
|
||||
break
|
||||
default:
|
||||
networkName = 'Private'
|
||||
}
|
||||
|
||||
return networkName
|
||||
}
|
||||
|
||||
export function getErrorMessage(error: Error, chainId: number): string {
|
||||
if (error instanceof NoEthereumProviderError) {
|
||||
return 'No Ethereum browser extension detected, install <a href="https://metamask.io">MetaMask</a> or <a href="https://brave.com">Brave</a>.'
|
||||
} else if (error instanceof UnsupportedChainIdError) {
|
||||
const networkName = getNetworkName(chainId)
|
||||
return `Please connect to <strong>Main</strong> network. You are on <strong>${networkName}</strong> right now.`
|
||||
} else if (error instanceof UserRejectedRequestError) {
|
||||
return 'Please authorize this website to access your Ethereum account.'
|
||||
} else {
|
||||
console.error(error)
|
||||
return 'An unknown error occurred. Check the console for more details.'
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import React from 'react'
|
||||
import { render, waitFor } from '@testing-library/react'
|
||||
import Thanks from '../thanks'
|
||||
|
||||
describe('/thanks', () => {
|
||||
it('renders without crashing', async () => {
|
||||
const { container } = render(<Thanks />)
|
||||
const lazyElement = await waitFor(() => container.querySelector('button'))
|
||||
expect(lazyElement).toBeInTheDocument()
|
||||
})
|
||||
})
|
@ -19,80 +19,49 @@
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.thanks h2 {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
margin-bottom: calc(var(--spacer) / 8);
|
||||
color: var(--text-color);
|
||||
font-size: var(--font-size-h4);
|
||||
}
|
||||
|
||||
.thanks h4 {
|
||||
color: var(--text-color);
|
||||
text-align: center;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.thanks header {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-bottom: var(--spacer);
|
||||
}
|
||||
|
||||
.thanks header h4 {
|
||||
font-size: var(--font-size-large);
|
||||
margin-top: 0;
|
||||
margin-bottom: calc(var(--spacer) / 6);
|
||||
}
|
||||
|
||||
.thanks header p {
|
||||
color: var(--text-color-light);
|
||||
}
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
margin-top: 0;
|
||||
margin-bottom: calc(var(--spacer) * 2);
|
||||
font-size: var(--font-size-h2);
|
||||
}
|
||||
|
||||
.web3 {
|
||||
width: 100%;
|
||||
.subTitle {
|
||||
text-align: center;
|
||||
margin-top: calc(var(--spacer) / 2);
|
||||
margin-bottom: calc(var(--spacer) * 2);
|
||||
padding-bottom: var(--spacer);
|
||||
}
|
||||
|
||||
.web3 small {
|
||||
/* color: darken($alert-info, 60%); */
|
||||
margin-top: -1rem;
|
||||
display: block;
|
||||
font-size: var(--font-size-base);
|
||||
color: var(--text-color-light);
|
||||
}
|
||||
|
||||
.coins {
|
||||
composes: container from '../components/Layout.module.css';
|
||||
max-width: 20rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 40rem) {
|
||||
.coins {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.coin {
|
||||
margin-top: var(--spacer);
|
||||
}
|
||||
|
||||
@media (min-width: 40rem) {
|
||||
.coin {
|
||||
width: 48%;
|
||||
margin-top: 0;
|
||||
}
|
||||
.titleCoin {
|
||||
margin-bottom: 0;
|
||||
font-size: var(--font-size-base);
|
||||
}
|
||||
|
||||
.loading {
|
||||
.code {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
padding-right: 2rem;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.code code {
|
||||
padding: calc(var(--spacer) / 2);
|
||||
font-size: 0.65rem;
|
||||
}
|
||||
|
@ -1,31 +1,34 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import loadable from '@loadable/component'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { Web3ReactProvider } from '@web3-react/core'
|
||||
import { Author } from '../@types/Site'
|
||||
import { useSiteMetadata } from '../hooks/use-site-metadata'
|
||||
import { getLibrary } from '../hooks/useWeb3'
|
||||
import Qr from '../components/atoms/Qr'
|
||||
import Icon from '../components/atoms/Icon'
|
||||
import {
|
||||
thanks,
|
||||
title,
|
||||
web3,
|
||||
loading,
|
||||
coins as styleCoins,
|
||||
coin,
|
||||
buttonBack
|
||||
code,
|
||||
buttonBack,
|
||||
titleCoin,
|
||||
subTitle
|
||||
} from './thanks.module.css'
|
||||
import Web3Donation from '../components/molecules/Web3Donation'
|
||||
import Copy from '../components/atoms/Copy'
|
||||
import { RainbowKitProvider } from '@rainbow-me/rainbowkit'
|
||||
import { WagmiProvider } from 'wagmi'
|
||||
import { chains, theme, wagmiClient } from '../helpers/rainbowkit'
|
||||
|
||||
const LazyWeb3Donation = loadable(
|
||||
() => import('../components/molecules/Web3Donation')
|
||||
)
|
||||
|
||||
const Coin = ({ address, author }: { address: string; author: Author }) => (
|
||||
<div className={coin}>
|
||||
<Qr title={address} address={(author as any)[address]} />
|
||||
</div>
|
||||
)
|
||||
function Coin({ address, title }: { address: string; title: string }) {
|
||||
return (
|
||||
<div className={coin}>
|
||||
<h4 className={titleCoin}>{title}</h4>
|
||||
<pre className={code}>
|
||||
<code>{address}</code>
|
||||
<Copy text={address} />
|
||||
</pre>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const BackButton = () => (
|
||||
<button
|
||||
@ -38,8 +41,8 @@ const BackButton = () => (
|
||||
|
||||
export default function Thanks(): ReactElement {
|
||||
const { author } = useSiteMetadata()
|
||||
const coins = Object.keys(author).filter(
|
||||
(key) => key === 'bitcoin' || key === 'ether'
|
||||
const coins = Object.entries(author).filter(
|
||||
([key]) => key === 'bitcoin' || key === 'ether'
|
||||
)
|
||||
|
||||
return (
|
||||
@ -55,28 +58,19 @@ export default function Thanks(): ReactElement {
|
||||
<h1 className={title}>Say Thanks</h1>
|
||||
</header>
|
||||
|
||||
<div className={web3}>
|
||||
<header>
|
||||
<h2>With Web3 Wallet</h2>
|
||||
<p>Send Ether with MetaMask or Brave.</p>
|
||||
</header>
|
||||
|
||||
<Web3ReactProvider getLibrary={getLibrary}>
|
||||
<LazyWeb3Donation
|
||||
fallback={<div className={loading}>Loading...</div>}
|
||||
address={author.ether}
|
||||
/>
|
||||
</Web3ReactProvider>
|
||||
</div>
|
||||
<WagmiProvider client={wagmiClient}>
|
||||
<RainbowKitProvider chains={chains} theme={theme}>
|
||||
<Web3Donation address={author.ether} />
|
||||
</RainbowKitProvider>
|
||||
</WagmiProvider>
|
||||
|
||||
<div className={styleCoins}>
|
||||
<header>
|
||||
<h2>With Any Other Wallet</h2>
|
||||
<p>Send Bitcoin or Ether from any wallet.</p>
|
||||
</header>
|
||||
<h3 className={subTitle}>
|
||||
Send Bitcoin or ERC-20 tokens from any wallet.
|
||||
</h3>
|
||||
|
||||
{coins.map((address: string) => (
|
||||
<Coin key={address} address={address} author={author} />
|
||||
{coins.map(([key, value]) => (
|
||||
<Coin key={key} title={key} address={value} />
|
||||
))}
|
||||
</div>
|
||||
</article>
|
||||
|
Loading…
Reference in New Issue
Block a user