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_GITHUB_TOKEN=your_token
|
||||||
GATSBY_MAPBOX_ACCESS_TOKEN=
|
GATSBY_MAPBOX_ACCESS_TOKEN=
|
||||||
GATSBY_TYPEKIT_ID=xxx
|
GATSBY_TYPEKIT_ID=xxx
|
||||||
|
INFURA_ID=xxx
|
@ -12,7 +12,6 @@ module.exports = {
|
|||||||
uri: 'https://matthiaskretschmann.com',
|
uri: 'https://matthiaskretschmann.com',
|
||||||
twitter: 'https://twitter.com/kremalicious',
|
twitter: 'https://twitter.com/kremalicious',
|
||||||
github: 'https://github.com/kremalicious',
|
github: 'https://github.com/kremalicious',
|
||||||
facebook: 'https://facebook.com/matthiaskretschmann',
|
|
||||||
bitcoin: '171qDmKEXm9YBgBLXyGjjPvopP5o9htQ1V',
|
bitcoin: '171qDmKEXm9YBgBLXyGjjPvopP5o9htQ1V',
|
||||||
ether: '0xf50F267b5689b005FE107cfdb34619f24c014457'
|
ether: '0xf50F267b5689b005FE107cfdb34619f24c014457'
|
||||||
},
|
},
|
||||||
|
@ -146,3 +146,13 @@ exports.onPostBuild = async ({ graphql }) => {
|
|||||||
|
|
||||||
return Promise.resolve()
|
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',
|
'ccount',
|
||||||
'escape-string-regexp',
|
'escape-string-regexp',
|
||||||
'markdown-table',
|
'markdown-table',
|
||||||
'web-namespaces'
|
'web-namespaces',
|
||||||
|
'@rainbow-me/rainbowkit'
|
||||||
].join('|')
|
].join('|')
|
||||||
|
|
||||||
module.exports = {
|
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)$':
|
'.+\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
|
||||||
'<rootDir>/jest/__mocks__/file-mock.js',
|
'<rootDir>/jest/__mocks__/file-mock.js',
|
||||||
'\\.svg': '<rootDir>/jest/__mocks__/svgr-mock.js',
|
'\\.svg': '<rootDir>/jest/__mocks__/svgr-mock.js',
|
||||||
'^@reach/router(.*)': '<rootDir>/node_modules/@gatsbyjs/reach-router$1',
|
'^@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
|
|
||||||
},
|
},
|
||||||
testPathIgnorePatterns: ['node_modules', '.cache', 'public', 'coverage'],
|
testPathIgnorePatterns: ['node_modules', '.cache', 'public', 'coverage'],
|
||||||
transformIgnorePatterns: [`node_modules/(?!(gatsby|${esModules})/)`],
|
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"
|
"not op_mini all"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ethersproject/providers": "^5.6.5",
|
"@kremalicious/react-feather": "^2.1.0",
|
||||||
"@ethersproject/units": "^5.6.0",
|
"@rainbow-me/rainbowkit": "^0.1.0",
|
||||||
"@loadable/component": "^5.15.2",
|
|
||||||
"@web3-react/core": "^6.1.9",
|
|
||||||
"@web3-react/injected-connector": "^6.0.7",
|
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"date-fns": "^2.28.0",
|
"date-fns": "^2.28.0",
|
||||||
"dms2dec": "^1.1.0",
|
"dms2dec": "^1.1.0",
|
||||||
"ethereum-blockies": "github:MyEtherWallet/blockies",
|
"ethers": "^5.6.5",
|
||||||
"fast-exif": "^1.0.1",
|
"fast-exif": "^1.0.1",
|
||||||
"feather-icons": "^4.29.0",
|
"feather-icons": "^4.29.0",
|
||||||
"fraction.js": "^4.2.0",
|
"fraction.js": "^4.2.0",
|
||||||
@ -71,15 +68,14 @@
|
|||||||
"react": "^18.1.0",
|
"react": "^18.1.0",
|
||||||
"react-clipboard.js": "^2.0.16",
|
"react-clipboard.js": "^2.0.16",
|
||||||
"react-dom": "^18.1.0",
|
"react-dom": "^18.1.0",
|
||||||
"react-feather": "^2.0.9",
|
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-qr-svg": "^2.4.0",
|
|
||||||
"react-transition-group": "^4.4.2",
|
"react-transition-group": "^4.4.2",
|
||||||
"rehype-react": "^7.1.1",
|
"rehype-react": "^7.1.1",
|
||||||
"remark-parse": "^10.0.1",
|
"remark-parse": "^10.0.1",
|
||||||
"remark-rehype": "^10.1.0",
|
"remark-rehype": "^10.1.0",
|
||||||
"slugify": "^1.6.5",
|
"slugify": "^1.6.5",
|
||||||
"unified": "^10.1.2"
|
"unified": "^10.1.2",
|
||||||
|
"wagmi": "^0.3.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@svgr/webpack": "^5.5.0",
|
"@svgr/webpack": "^5.5.0",
|
||||||
@ -108,7 +104,8 @@
|
|||||||
"eslint-plugin-testing-library": "^5.4.0",
|
"eslint-plugin-testing-library": "^5.4.0",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
"identity-obj-proxy": "^3.0.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",
|
"markdownlint-cli": "^0.31.1",
|
||||||
"node-iptc": "^1.0.5",
|
"node-iptc": "^1.0.5",
|
||||||
"npm-run-all": "^4.1.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
|
uri: string
|
||||||
twitter: string
|
twitter: string
|
||||||
github: string
|
github: string
|
||||||
facebook: string
|
|
||||||
bitcoin: string
|
bitcoin: string
|
||||||
ether: 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-maps'
|
||||||
declare module 'pigeon-marker'
|
declare module 'pigeon-marker'
|
||||||
declare module 'react-blockies'
|
|
||||||
declare module 'remark-react'
|
|
||||||
declare module 'unified'
|
declare module 'unified'
|
||||||
declare module 'fast-exif'
|
declare module 'fast-exif'
|
||||||
declare module 'node-iptc'
|
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 React, { ReactElement } from 'react'
|
||||||
import loadable from '@loadable/component'
|
|
||||||
import { copied, button } from './Copy.module.css'
|
import { copied, button } from './Copy.module.css'
|
||||||
import Icon from './Icon'
|
import Icon from './Icon'
|
||||||
|
import Clipboard from 'react-clipboard.js'
|
||||||
const Clipboard = loadable(() => import('react-clipboard.js'))
|
|
||||||
|
|
||||||
const onCopySuccess = (e: any) => {
|
const onCopySuccess = (e: any) => {
|
||||||
e.trigger.classList.add(copied)
|
e.trigger.classList.add(copied)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { FunctionComponent, ReactElement } from 'react'
|
import React, { FunctionComponent, ReactElement } from 'react'
|
||||||
|
|
||||||
// https://featherstyles.com
|
// https://featherstyles.com
|
||||||
// import * as Feather from 'react-feather'
|
// import * as Feather from '@kremalicious/react-feather'
|
||||||
import {
|
import {
|
||||||
ArrowDownCircle,
|
ArrowDownCircle,
|
||||||
Edit,
|
Edit,
|
||||||
@ -22,7 +22,7 @@ import {
|
|||||||
Aperture,
|
Aperture,
|
||||||
Maximize,
|
Maximize,
|
||||||
Crosshair
|
Crosshair
|
||||||
} from 'react-feather'
|
} from '@kremalicious/react-feather'
|
||||||
// custom icons
|
// custom icons
|
||||||
import { ReactComponent as Jsonfeed } from '../../images/jsonfeed.svg'
|
import { ReactComponent as Jsonfeed } from '../../images/jsonfeed.svg'
|
||||||
import { ReactComponent as Bitcoin } from '../../images/bitcoin.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'
|
import { input } from './Input.module.css'
|
||||||
|
|
||||||
export default function Input({
|
export default function Input({
|
||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: InputHTMLAttributes<HTMLInputElement>): ReactElement {
|
||||||
className: string
|
|
||||||
}): ReactElement {
|
|
||||||
return <input className={`${input} ${className || ''}`} {...props} />
|
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 React, { ReactElement } from 'react'
|
||||||
import loadable from '@loadable/component'
|
import { format, formatDistance } from 'date-fns'
|
||||||
|
|
||||||
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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Time({ date }: { date: string }): ReactElement {
|
export default function Time({ date }: { date: string }): ReactElement {
|
||||||
const dateNew = new Date(date)
|
const dateNew = new Date(date)
|
||||||
|
const dateIso = dateNew.toISOString()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LazyDate fallback={<TimeMarkup date={dateNew} />}>
|
<time title={format(dateNew, 'yyyy/MM/dd HH:mm')} dateTime={dateIso}>
|
||||||
{({ format, formatDistance }) => (
|
{formatDistance(dateNew, Date.now(), { addSuffix: true })}
|
||||||
<TimeMarkup
|
</time>
|
||||||
date={dateNew}
|
|
||||||
format={format}
|
|
||||||
formatDistance={formatDistance}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</LazyDate>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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', () => {
|
describe('Conversion', () => {
|
||||||
it('renders without crashing', async () => {
|
it('renders without crashing', async () => {
|
||||||
render(<Conversion amount="1" />)
|
render(<Conversion amount="1" />)
|
||||||
// const lazyElement = await findByText(/= €/)
|
|
||||||
// expect(lazyElement).toBeInTheDocument()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import React, { useState, useEffect, ReactElement } from 'react'
|
import React, { useState, useEffect, ReactElement } from 'react'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { conversion as styleConversion } from './Conversion.module.css'
|
import { conversion as styleConversion } from './Conversion.module.css'
|
||||||
|
import { useNetwork } from 'wagmi'
|
||||||
|
|
||||||
export async function getFiat(
|
export async function getFiat(
|
||||||
amount: number
|
amount: number,
|
||||||
|
tokenId = 'ethereum'
|
||||||
): Promise<{ [key: string]: string }> {
|
): Promise<{ [key: string]: string }> {
|
||||||
const url =
|
const url = `https://api.coingecko.com/api/v3/simple/price?ids=${tokenId}&vs_currencies=eur%2Cusd`
|
||||||
'https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=eur%2Cusd'
|
|
||||||
const response = await axios(url)
|
const response = await axios(url)
|
||||||
|
|
||||||
if (!response) console.error(response.statusText)
|
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 dollar = (amount * usd).toFixed(2)
|
||||||
const euro = (amount * eur).toFixed(2)
|
const euro = (amount * eur).toFixed(2)
|
||||||
|
|
||||||
@ -22,24 +23,32 @@ export default function Conversion({
|
|||||||
}: {
|
}: {
|
||||||
amount: string
|
amount: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
|
const { activeChain } = useNetwork()
|
||||||
|
|
||||||
const [conversion, setConversion] = useState({
|
const [conversion, setConversion] = useState({
|
||||||
euro: '0.00',
|
euro: '0.00',
|
||||||
dollar: '0.00'
|
dollar: '0.00'
|
||||||
})
|
})
|
||||||
const { dollar, euro } = conversion
|
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(() => {
|
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()
|
getFiatResponse()
|
||||||
}, [amount])
|
}, [amount, activeChain?.nativeCurrency?.symbol])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styleConversion}>
|
<div className={styleConversion}>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
.inputGroup {
|
.inputGroup {
|
||||||
max-width: 20rem;
|
|
||||||
margin: auto;
|
margin: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
animation: fadeIn 0.8s ease-out backwards;
|
animation: fadeIn 0.8s ease-out backwards;
|
||||||
|
margin-top: var(--spacer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 40rem) {
|
@media (min-width: 40rem) {
|
||||||
@ -16,7 +16,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
border-color: var(--text-color-light);
|
border-color: var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 40rem) {
|
@media (min-width: 40rem) {
|
||||||
@ -29,10 +29,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.dark) .inputGroup button {
|
|
||||||
border-color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
@ -43,9 +39,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.input input {
|
.inputInput {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border: 1px solid var(--text-color-light);
|
border: 1px solid var(--border-color);
|
||||||
font-size: var(--font-size-large);
|
font-size: var(--font-size-large);
|
||||||
padding: calc(var(--spacer) / 3) calc(var(--spacer) / 3)
|
padding: calc(var(--spacer) / 3) calc(var(--spacer) / 3)
|
||||||
calc(var(--spacer) / 3) calc(var(--spacer) * 1.7);
|
calc(var(--spacer) / 3) calc(var(--spacer) * 1.7);
|
||||||
@ -55,21 +51,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 40rem) {
|
@media (min-width: 40rem) {
|
||||||
.input input {
|
.inputInput {
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
border-bottom-right-radius: 0;
|
border-bottom-right-radius: 0;
|
||||||
border-bottom-left-radius: var(--border-radius);
|
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;
|
border-right: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.input input::-webkit-inner-spin-button {
|
.inputInput::-webkit-inner-spin-button {
|
||||||
margin-left: -1rem;
|
margin-left: -1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.dark) .input input {
|
:global(.dark) .inputInput {
|
||||||
border-color: #000;
|
border-color: var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.currency {
|
.currency {
|
||||||
@ -80,7 +76,7 @@
|
|||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
padding: calc(var(--spacer) / 3);
|
padding: calc(var(--spacer) / 3);
|
||||||
background: var(--box-background-color);
|
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-top-left-radius: var(--border-radius);
|
||||||
border-bottom-left-radius: var(--border-radius);
|
border-bottom-left-radius: var(--border-radius);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -13,9 +13,9 @@ describe('InputGroup', () => {
|
|||||||
expect(container.firstChild).toBeInTheDocument()
|
expect(container.firstChild).toBeInTheDocument()
|
||||||
|
|
||||||
const input = container.querySelector('input')
|
const input = container.querySelector('input')
|
||||||
// const button = container.querySelector('button')
|
const button = container.querySelector('button')
|
||||||
fireEvent.change(input, { target: { value: '3' } })
|
fireEvent.change(input, { target: { value: '3' } })
|
||||||
// fireEvent.click(button)
|
fireEvent.click(button)
|
||||||
// expect(sendTransaction).toHaveBeenCalled()
|
expect(sendTransaction).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,15 +1,21 @@
|
|||||||
import React, { ReactElement, useState } from 'react'
|
import React, { ReactElement, useState } from 'react'
|
||||||
import useWeb3 from '../../../hooks/useWeb3'
|
import { useAccount, useNetwork } from 'wagmi'
|
||||||
import Input from '../../atoms/Input'
|
import Input from '../../atoms/Input'
|
||||||
import Conversion from './Conversion'
|
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({
|
export default function InputGroup({
|
||||||
sendTransaction
|
sendTransaction
|
||||||
}: {
|
}: {
|
||||||
sendTransaction(amount: string): void
|
sendTransaction(amount: string): void
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { account } = useWeb3()
|
const { data: account } = useAccount()
|
||||||
|
const { activeChain } = useNetwork()
|
||||||
const [amount, setAmount] = useState('0.01')
|
const [amount, setAmount] = useState('0.01')
|
||||||
|
|
||||||
const onAmountChange = ({ target }: { target: any }) => {
|
const onAmountChange = ({ target }: { target: any }) => {
|
||||||
@ -21,14 +27,16 @@ export default function InputGroup({
|
|||||||
<div className={inputGroup}>
|
<div className={inputGroup}>
|
||||||
<div className={input}>
|
<div className={input}>
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="text"
|
||||||
|
inputMode="decimal"
|
||||||
|
pattern="[0-9.]*"
|
||||||
value={amount}
|
value={amount}
|
||||||
onChange={onAmountChange}
|
onChange={onAmountChange}
|
||||||
min="0"
|
className={inputInput}
|
||||||
step="0.01"
|
disabled={!account}
|
||||||
/>
|
/>
|
||||||
<div className={currency}>
|
<div className={currency}>
|
||||||
<span>ETH</span>
|
<span>{activeChain?.nativeCurrency?.symbol || 'ETH'}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
.web3 {
|
.web3 {
|
||||||
composes: container from '../../Layout.module.css';
|
composes: container from '../../Layout.module.css';
|
||||||
max-width: 20rem;
|
max-width: 20rem;
|
||||||
|
margin-bottom: calc(var(--spacer) * 2);
|
||||||
|
text-align: center;
|
||||||
|
min-height: 165px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.web3:empty {
|
.web3 > div:first-child {
|
||||||
display: none;
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
margin-bottom: var(--spacer);
|
||||||
}
|
}
|
||||||
|
|
||||||
.message {
|
.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 { parseEther } from '@ethersproject/units'
|
||||||
import useWeb3, { getErrorMessage } from '../../../hooks/useWeb3'
|
|
||||||
import InputGroup from './InputGroup'
|
import InputGroup from './InputGroup'
|
||||||
import Alert, { getTransactionMessage } from './Alert'
|
import Alert, { getTransactionMessage } from './Alert'
|
||||||
import { web3 as styleWeb3 } from './index.module.css'
|
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({
|
export default function Web3Donation({
|
||||||
address
|
address
|
||||||
}: {
|
}: {
|
||||||
address: string
|
address: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { connector, library, chainId, account, active, error } = useWeb3()
|
const { sendTransactionAsync } = useSendTransaction()
|
||||||
|
|
||||||
const [message, setMessage] = useState<{ status: string; text: string }>()
|
const [message, setMessage] = useState<{ status: string; text: string }>()
|
||||||
const [transactionHash, setTransactionHash] = useState<string>()
|
const [transactionHash, setTransactionHash] = useState<string>()
|
||||||
|
|
||||||
useEffect(() => {
|
async function handleSendTransaction(amount: string) {
|
||||||
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()
|
|
||||||
|
|
||||||
setMessage({
|
setMessage({
|
||||||
status: 'loading',
|
status: 'loading',
|
||||||
text: getTransactionMessage().waitingForUser
|
text: getTransactionMessage().waitingForUser
|
||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const tx = await signer.sendTransaction({
|
const tx = await sendTransactionAsync({
|
||||||
to: address,
|
request: {
|
||||||
value: parseEther(amount) // ETH -> Wei
|
to: address,
|
||||||
|
value: parseEther(amount) // ETH -> Wei
|
||||||
|
}
|
||||||
})
|
})
|
||||||
setTransactionHash(tx.hash)
|
setTransactionHash(tx.hash)
|
||||||
setMessage({
|
setMessage({
|
||||||
@ -57,11 +48,12 @@ export default function Web3Donation({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styleWeb3}>
|
<div className={styleWeb3}>
|
||||||
<Account />
|
<ConnectButton chainStatus="icon" showBalance={false} />
|
||||||
|
|
||||||
{message ? (
|
{message ? (
|
||||||
<Alert message={message} transactionHash={transactionHash} />
|
<Alert message={message} transactionHash={transactionHash} />
|
||||||
) : (
|
) : (
|
||||||
<InputGroup sendTransaction={sendTransaction} />
|
<InputGroup sendTransaction={handleSendTransaction} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -124,13 +124,13 @@
|
|||||||
--body-background-color: #161a1b;
|
--body-background-color: #161a1b;
|
||||||
--box-background-color: rgba(255 255 255 / 3%);
|
--box-background-color: rgba(255 255 255 / 3%);
|
||||||
|
|
||||||
--box-shadow: 0 1.3px 5.4px rgba(0 7 8 / 60%),
|
--box-shadow: 0 1.3px 5.4px rgba(0 7 8 / 50%),
|
||||||
0 4.5px 18.1px rgba(0 7 8 / 40%), 0 20px 81px rgba(0 7 8 / 10%);
|
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: var(--brand-grey-light);
|
||||||
--text-color-light: var(--brand-grey);
|
--text-color-light: var(--brand-grey);
|
||||||
--text-color-dimmed: var(--brand-grey-dark);
|
--text-color-dimmed: var(--brand-grey-dark);
|
||||||
--border-color: var(--brand-grey-dark);
|
--border-color: #111;
|
||||||
--color-headings: var(--brand-main-light);
|
--color-headings: var(--brand-main-light);
|
||||||
|
|
||||||
--input-bg: var(--body-background-color);
|
--input-bg: var(--body-background-color);
|
||||||
|
@ -2,3 +2,4 @@
|
|||||||
@import '_code.css';
|
@import '_code.css';
|
||||||
@import '_toast.css';
|
@import '_toast.css';
|
||||||
@import '_alerts.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
|
uri
|
||||||
twitter
|
twitter
|
||||||
github
|
github
|
||||||
facebook
|
|
||||||
bitcoin
|
bitcoin
|
||||||
ether
|
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;
|
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 {
|
.thanks header {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: var(--spacer);
|
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 {
|
.title {
|
||||||
|
text-align: center;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: calc(var(--spacer) * 2);
|
margin-bottom: calc(var(--spacer) * 2);
|
||||||
font-size: var(--font-size-h2);
|
font-size: var(--font-size-h2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.web3 {
|
.subTitle {
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-top: calc(var(--spacer) / 2);
|
font-size: var(--font-size-base);
|
||||||
margin-bottom: calc(var(--spacer) * 2);
|
color: var(--text-color-light);
|
||||||
padding-bottom: var(--spacer);
|
|
||||||
}
|
|
||||||
|
|
||||||
.web3 small {
|
|
||||||
/* color: darken($alert-info, 60%); */
|
|
||||||
margin-top: -1rem;
|
|
||||||
display: block;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.coins {
|
.coins {
|
||||||
|
composes: container from '../components/Layout.module.css';
|
||||||
|
max-width: 20rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
border-top: 1px solid var(--border-color);
|
||||||
|
|
||||||
@media (min-width: 40rem) {
|
|
||||||
.coins {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.coin {
|
.coin {
|
||||||
margin-top: var(--spacer);
|
margin-top: var(--spacer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 40rem) {
|
.titleCoin {
|
||||||
.coin {
|
margin-bottom: 0;
|
||||||
width: 48%;
|
font-size: var(--font-size-base);
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading {
|
.code {
|
||||||
|
position: relative;
|
||||||
|
padding: 0;
|
||||||
|
padding-right: 2rem;
|
||||||
width: 100%;
|
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 React, { ReactElement } from 'react'
|
||||||
import loadable from '@loadable/component'
|
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
import { Web3ReactProvider } from '@web3-react/core'
|
|
||||||
import { Author } from '../@types/Site'
|
|
||||||
import { useSiteMetadata } from '../hooks/use-site-metadata'
|
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 Icon from '../components/atoms/Icon'
|
||||||
import {
|
import {
|
||||||
thanks,
|
thanks,
|
||||||
title,
|
title,
|
||||||
web3,
|
|
||||||
loading,
|
|
||||||
coins as styleCoins,
|
coins as styleCoins,
|
||||||
coin,
|
coin,
|
||||||
buttonBack
|
code,
|
||||||
|
buttonBack,
|
||||||
|
titleCoin,
|
||||||
|
subTitle
|
||||||
} from './thanks.module.css'
|
} 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(
|
function Coin({ address, title }: { address: string; title: string }) {
|
||||||
() => import('../components/molecules/Web3Donation')
|
return (
|
||||||
)
|
<div className={coin}>
|
||||||
|
<h4 className={titleCoin}>{title}</h4>
|
||||||
const Coin = ({ address, author }: { address: string; author: Author }) => (
|
<pre className={code}>
|
||||||
<div className={coin}>
|
<code>{address}</code>
|
||||||
<Qr title={address} address={(author as any)[address]} />
|
<Copy text={address} />
|
||||||
</div>
|
</pre>
|
||||||
)
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const BackButton = () => (
|
const BackButton = () => (
|
||||||
<button
|
<button
|
||||||
@ -38,8 +41,8 @@ const BackButton = () => (
|
|||||||
|
|
||||||
export default function Thanks(): ReactElement {
|
export default function Thanks(): ReactElement {
|
||||||
const { author } = useSiteMetadata()
|
const { author } = useSiteMetadata()
|
||||||
const coins = Object.keys(author).filter(
|
const coins = Object.entries(author).filter(
|
||||||
(key) => key === 'bitcoin' || key === 'ether'
|
([key]) => key === 'bitcoin' || key === 'ether'
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -55,28 +58,19 @@ export default function Thanks(): ReactElement {
|
|||||||
<h1 className={title}>Say Thanks</h1>
|
<h1 className={title}>Say Thanks</h1>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div className={web3}>
|
<WagmiProvider client={wagmiClient}>
|
||||||
<header>
|
<RainbowKitProvider chains={chains} theme={theme}>
|
||||||
<h2>With Web3 Wallet</h2>
|
<Web3Donation address={author.ether} />
|
||||||
<p>Send Ether with MetaMask or Brave.</p>
|
</RainbowKitProvider>
|
||||||
</header>
|
</WagmiProvider>
|
||||||
|
|
||||||
<Web3ReactProvider getLibrary={getLibrary}>
|
|
||||||
<LazyWeb3Donation
|
|
||||||
fallback={<div className={loading}>Loading...</div>}
|
|
||||||
address={author.ether}
|
|
||||||
/>
|
|
||||||
</Web3ReactProvider>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styleCoins}>
|
<div className={styleCoins}>
|
||||||
<header>
|
<h3 className={subTitle}>
|
||||||
<h2>With Any Other Wallet</h2>
|
Send Bitcoin or ERC-20 tokens from any wallet.
|
||||||
<p>Send Bitcoin or Ether from any wallet.</p>
|
</h3>
|
||||||
</header>
|
|
||||||
|
|
||||||
{coins.map((address: string) => (
|
{coins.map(([key, value]) => (
|
||||||
<Coin key={address} address={address} author={author} />
|
<Coin key={key} title={key} address={value} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
Loading…
Reference in New Issue
Block a user