mirror of
https://github.com/kremalicious/blog.git
synced 2024-12-22 17:23:50 +01:00
put web3 donation on page, kick out modal
This commit is contained in:
parent
eb892807eb
commit
e5adbb68c5
@ -5,6 +5,9 @@ if (typeof window.IntersectionObserver === 'undefined') {
|
||||
import('intersection-observer')
|
||||
}
|
||||
|
||||
import wrapPageElementWithLayout from './src/helpers/wrapPageElement'
|
||||
export const wrapPageElement = wrapPageElementWithLayout
|
||||
|
||||
// Display a message when a service worker updates
|
||||
// https://www.gatsbyjs.org/docs/add-offline-support-with-a-service-worker/#displaying-a-message-when-a-service-worker-updates
|
||||
export const onServiceWorkerUpdateReady = () => {
|
||||
|
2
gatsby-ssr.js
Normal file
2
gatsby-ssr.js
Normal file
@ -0,0 +1,2 @@
|
||||
import wrapPageElementWithLayout from './src/helpers/wrapPageElement'
|
||||
export const wrapPageElement = wrapPageElementWithLayout
|
@ -1,124 +0,0 @@
|
||||
@import 'variables';
|
||||
|
||||
.modal {
|
||||
position: fixed;
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 9;
|
||||
background: rgba($body-background-color, 0.95);
|
||||
backdrop-filter: blur(5px);
|
||||
animation: fadein 0.3s;
|
||||
padding: $spacer;
|
||||
|
||||
:global(.dark) & {
|
||||
background: rgba($body-background-color--dark, 0.95);
|
||||
}
|
||||
|
||||
@media (min-width: $screen-sm) {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
padding-top: 6vh;
|
||||
}
|
||||
}
|
||||
|
||||
.modalContent {
|
||||
outline: 0;
|
||||
background: $body-background-color;
|
||||
position: relative;
|
||||
border-radius: $border-radius;
|
||||
border: 1px solid rgba($brand-grey-light, 0.4);
|
||||
box-shadow: 0 5px 30px rgba($brand-grey-light, 0.2);
|
||||
padding: 0 $spacer / 2 $spacer / 2;
|
||||
max-width: 100%;
|
||||
|
||||
:global(.dark) & {
|
||||
background: $body-background-color--dark;
|
||||
box-shadow: 0 5px 30px rgba(darken($brand-main, 20%), 0.5);
|
||||
}
|
||||
|
||||
@media (min-width: $screen-md) {
|
||||
max-width: $screen-sm;
|
||||
padding: 0 $spacer $spacer;
|
||||
}
|
||||
}
|
||||
|
||||
.modalClose {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
appearance: none;
|
||||
padding: 4px;
|
||||
position: absolute;
|
||||
top: $spacer / 4;
|
||||
right: ($spacer/4);
|
||||
outline: 0;
|
||||
|
||||
svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
stroke: $brand-grey-light;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
svg {
|
||||
stroke: $brand-cyan;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.isModalOpen {
|
||||
// Prevent background scrolling when modal is open
|
||||
overflow: hidden;
|
||||
|
||||
// more cross-browser backdrop-filter
|
||||
// body > div:first-child {
|
||||
// transition: filter .85s ease-out;
|
||||
// filter: blur(5px);
|
||||
// }
|
||||
}
|
||||
|
||||
.modalTitle {
|
||||
font-size: $font-size-h4;
|
||||
margin-top: $spacer / 2;
|
||||
margin-bottom: $spacer / 2;
|
||||
margin-left: -($spacer / 2);
|
||||
margin-right: -($spacer / 2);
|
||||
border-bottom: 1px solid rgba($brand-grey-light, 0.4);
|
||||
padding: 0 $spacer;
|
||||
padding-bottom: ($spacer/2);
|
||||
|
||||
@media (min-width: $screen-md) {
|
||||
margin-left: -($spacer);
|
||||
margin-right: -($spacer);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Overlay/content animations
|
||||
//
|
||||
@keyframes fadein {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeout {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
import React from 'react'
|
||||
import { render } from '@testing-library/react'
|
||||
import Modal from './Modal'
|
||||
import ReactModal from 'react-modal'
|
||||
|
||||
describe('Modal', () => {
|
||||
it('renders without crashing', () => {
|
||||
ReactModal.setAppElement(document.createElement('div'))
|
||||
|
||||
const { rerender } = render(
|
||||
<Modal title="Hello" isOpen handleCloseModal={() => null}>
|
||||
Hello
|
||||
</Modal>
|
||||
)
|
||||
expect(document.querySelector('.ReactModalPortal')).toBeInTheDocument()
|
||||
|
||||
rerender(
|
||||
<Modal isOpen={false} handleCloseModal={() => null}>
|
||||
Hello
|
||||
</Modal>
|
||||
)
|
||||
})
|
||||
})
|
@ -1,38 +0,0 @@
|
||||
import React from 'react'
|
||||
import ReactModal from 'react-modal'
|
||||
import Icon from './Icon'
|
||||
import styles from './Modal.module.scss'
|
||||
|
||||
if (process.env.NODE_ENV !== 'test') ReactModal.setAppElement('#___gatsby')
|
||||
|
||||
export default function Modal({
|
||||
title,
|
||||
isOpen,
|
||||
handleCloseModal,
|
||||
children,
|
||||
...props
|
||||
}: {
|
||||
title?: string
|
||||
isOpen?: boolean
|
||||
handleCloseModal(): void
|
||||
children: any
|
||||
}) {
|
||||
if (!isOpen) return null
|
||||
|
||||
return (
|
||||
<ReactModal
|
||||
overlayClassName={styles.modal}
|
||||
className={styles.modalContent}
|
||||
htmlOpenClassName={styles.isModalOpen}
|
||||
shouldReturnFocusAfterClose={false}
|
||||
isOpen={isOpen}
|
||||
{...props}
|
||||
>
|
||||
{title && <h1 className={styles.modalTitle}>{title}</h1>}
|
||||
{children}
|
||||
<button className={styles.modalClose} onClick={handleCloseModal}>
|
||||
<Icon name="X" />
|
||||
</button>
|
||||
</ReactModal>
|
||||
)
|
||||
}
|
@ -5,10 +5,11 @@
|
||||
}
|
||||
|
||||
.code {
|
||||
margin: 0;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
padding-right: 2rem;
|
||||
width: fit-content;
|
||||
|
||||
code {
|
||||
padding: $spacer / 2;
|
||||
|
@ -24,6 +24,10 @@
|
||||
composes: alert;
|
||||
color: darken($alert-error, 60%);
|
||||
|
||||
:global(.dark) & {
|
||||
color: darken($alert-error, 40%);
|
||||
}
|
||||
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ export const alertMessages = (
|
||||
'Web3 detected, but no account. Are you logged into your MetaMask account?',
|
||||
noCorrectNetwork: `Please connect to <strong>Main</strong> network. You are on <strong>${networkName}</strong> right now.`,
|
||||
noWeb3:
|
||||
'No Web3 detected. Install <a href="https://metamask.io">MetaMask</a>, <a href="https://brave.com">Brave</a>, or <a href="https://github.com/ethereum/mist">Mist</a>.',
|
||||
'No Web3 detected. Install <a href="https://metamask.io">MetaMask</a> or <a href="https://brave.com">Brave</a>.',
|
||||
transaction: `<a href="https://etherscan.io/tx/${transactionHash}" target="_blank">See your transaction on etherscan.io.</a>`,
|
||||
waitingForUser: 'Waiting for your confirmation',
|
||||
waitingConfirmation: 'Waiting for network confirmation, hang on',
|
||||
|
@ -1,14 +1,11 @@
|
||||
@import 'variables';
|
||||
@import 'mixins';
|
||||
|
||||
.web3 {
|
||||
@include divider;
|
||||
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-top: $spacer / 2;
|
||||
margin-bottom: $spacer;
|
||||
padding-bottom: $spacer * 1.5;
|
||||
padding-bottom: $spacer;
|
||||
|
||||
small {
|
||||
color: darken($alert-info, 60%);
|
||||
|
@ -88,7 +88,7 @@ export default class Web3Donation extends PureComponent<
|
||||
|
||||
initAccountsPoll() {
|
||||
if (!this.interval) {
|
||||
this.interval = setInterval(this.fetchAccounts, ONE_SECOND)
|
||||
this.interval = setInterval(this.fetchAccounts, ONE_SECOND * 10)
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,8 +184,8 @@ export default class Web3Donation extends PureComponent<
|
||||
return (
|
||||
<div className={styles.web3}>
|
||||
<header>
|
||||
<h4>web3</h4>
|
||||
<p>Send Ether with MetaMask, Brave, or Mist.</p>
|
||||
<h4>Web3 Wallet</h4>
|
||||
<p>Send Ether with MetaMask or Brave.</p>
|
||||
</header>
|
||||
|
||||
<div className={styles.web3Row}>
|
||||
|
@ -1,20 +1,13 @@
|
||||
import React, { useState } from 'react'
|
||||
import React from 'react'
|
||||
import { Link } from 'gatsby'
|
||||
import Container from '../atoms/Container'
|
||||
import Icon from '../atoms/Icon'
|
||||
import Vcard from '../molecules/Vcard'
|
||||
import ThemeSwitch from '../molecules/ThemeSwitch'
|
||||
import ModalThanks from './ModalThanks'
|
||||
|
||||
import styles from './Footer.module.scss'
|
||||
import { useSiteMetadata } from '../../hooks/use-site-metadata'
|
||||
import styles from './Footer.module.scss'
|
||||
|
||||
function Copyright({
|
||||
toggleModal,
|
||||
showModal
|
||||
}: {
|
||||
toggleModal(): void
|
||||
showModal: boolean
|
||||
}) {
|
||||
function Copyright() {
|
||||
const { name, uri, bitcoin, github } = useSiteMetadata().author
|
||||
const year = new Date().getFullYear()
|
||||
|
||||
@ -30,33 +23,23 @@ function Copyright({
|
||||
<Icon name="GitHub" />
|
||||
View source
|
||||
</a>
|
||||
<button className={styles.btc} onClick={toggleModal}>
|
||||
<Link to="/thanks" className={styles.btc}>
|
||||
<Icon name="Bitcoin" />
|
||||
<code>{bitcoin}</code>
|
||||
</button>
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
{showModal && (
|
||||
<ModalThanks isOpen={showModal} handleCloseModal={toggleModal} />
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Footer() {
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
|
||||
const toggleModal = () => {
|
||||
setShowModal(!showModal)
|
||||
}
|
||||
|
||||
return (
|
||||
<footer role="contentinfo" className={styles.footer}>
|
||||
<Container>
|
||||
<ThemeSwitch />
|
||||
<Vcard />
|
||||
|
||||
<Copyright showModal={showModal} toggleModal={toggleModal} />
|
||||
<Copyright />
|
||||
</Container>
|
||||
</footer>
|
||||
)
|
||||
|
@ -1,47 +0,0 @@
|
||||
import React, { lazy, Suspense } from 'react'
|
||||
import shortid from 'shortid'
|
||||
import { Author } from '../../@types/Site'
|
||||
import { useSiteMetadata } from '../../hooks/use-site-metadata'
|
||||
import Qr from '../atoms/Qr'
|
||||
import Modal from '../atoms/Modal'
|
||||
import styles from './ModalThanks.module.scss'
|
||||
|
||||
const Web3Donation = lazy(() => import('../molecules/Web3Donation'))
|
||||
|
||||
const Coin = ({ address, author }: { address: string; author: Author }) => (
|
||||
<div className={styles.coin}>
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<Qr title={address} address={(author as any)[address]} />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default function ModalThanks(props: any) {
|
||||
const { author } = useSiteMetadata()
|
||||
const coins = Object.keys(author).filter(
|
||||
key => key === 'bitcoin' || key === 'ether'
|
||||
)
|
||||
|
||||
return (
|
||||
<Modal
|
||||
{...props}
|
||||
contentLabel="Say thanks with Bitcoin or Ether"
|
||||
title="Say thanks"
|
||||
>
|
||||
<div className={styles.modalThanks}>
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<Web3Donation address={author.ether} />
|
||||
</Suspense>
|
||||
|
||||
<header>
|
||||
<h4>Any other wallets</h4>
|
||||
<p>Send Bitcoin or Ether from any wallet.</p>
|
||||
</header>
|
||||
|
||||
{coins.map((address: string) => (
|
||||
<Coin key={shortid.generate()} address={address} author={author} />
|
||||
))}
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
8
src/helpers/wrapPageElement.tsx
Normal file
8
src/helpers/wrapPageElement.tsx
Normal file
@ -0,0 +1,8 @@
|
||||
import React from 'react'
|
||||
import Layout from '../components/Layout'
|
||||
|
||||
const wrapPageElement = ({ element, props }: { element: any; props: any }) => (
|
||||
<Layout {...props}>{element}</Layout>
|
||||
)
|
||||
|
||||
export default wrapPageElement
|
@ -1,11 +1,18 @@
|
||||
@import 'variables';
|
||||
@import 'mixins';
|
||||
|
||||
.modalThanks {
|
||||
@media (min-width: $screen-sm) {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
.buttonBack {
|
||||
svg {
|
||||
stroke: $brand-grey-light;
|
||||
display: inline-block;
|
||||
margin-bottom: -0.15rem;
|
||||
}
|
||||
}
|
||||
|
||||
.thanks {
|
||||
@include breakoutviewport;
|
||||
|
||||
min-height: 100vh;
|
||||
|
||||
h4 {
|
||||
text-align: center;
|
||||
@ -41,6 +48,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-top: 0;
|
||||
margin-bottom: $spacer * 2;
|
||||
font-size: $font-size-h2;
|
||||
}
|
||||
|
||||
.coins {
|
||||
width: 100%;
|
||||
|
||||
@media (min-width: $screen-sm) {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.coin {
|
||||
margin-top: $spacer;
|
||||
|
||||
@ -49,3 +72,8 @@
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.loading {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
69
src/pages/thanks.tsx
Normal file
69
src/pages/thanks.tsx
Normal file
@ -0,0 +1,69 @@
|
||||
import React, { lazy, Suspense } from 'react'
|
||||
import shortid from 'shortid'
|
||||
import Helmet from 'react-helmet'
|
||||
import { Author } from '../@types/Site'
|
||||
import { useSiteMetadata } from '../hooks/use-site-metadata'
|
||||
import Typekit from '../components/atoms/Typekit'
|
||||
import Qr from '../components/atoms/Qr'
|
||||
import Icon from '../components/atoms/Icon'
|
||||
import styles from './thanks.module.scss'
|
||||
|
||||
const Web3Donation = lazy(() => import('../components/molecules/Web3Donation'))
|
||||
|
||||
const Coin = ({ address, author }: { address: string; author: Author }) => (
|
||||
<div className={styles.coin}>
|
||||
<Qr title={address} address={(author as any)[address]} />
|
||||
</div>
|
||||
)
|
||||
|
||||
const BackButton = () => (
|
||||
<button
|
||||
className={`link ${styles.buttonBack}`}
|
||||
onClick={() => window.history.back()}
|
||||
>
|
||||
<Icon name="ChevronLeft" /> Go Back
|
||||
</button>
|
||||
)
|
||||
|
||||
export default function Thanks() {
|
||||
const { author } = useSiteMetadata()
|
||||
const coins = Object.keys(author).filter(
|
||||
key => key === 'bitcoin' || key === 'ether'
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Say thanks</title>
|
||||
<meta name="robots" content="noindex,nofollow" />
|
||||
</Helmet>
|
||||
|
||||
<article className={styles.thanks}>
|
||||
<BackButton />
|
||||
|
||||
<header>
|
||||
<h1 className={styles.title}>Say Thanks</h1>
|
||||
</header>
|
||||
|
||||
<Suspense fallback={<div className={styles.loading}>Loading...</div>}>
|
||||
<Web3Donation address={author.ether} />
|
||||
|
||||
<div className={styles.coins}>
|
||||
<header>
|
||||
<h4>Any other wallets</h4>
|
||||
<p>Send Bitcoin or Ether from any wallet.</p>
|
||||
</header>
|
||||
|
||||
{coins.map((address: string) => (
|
||||
<Coin
|
||||
key={shortid.generate()}
|
||||
address={address}
|
||||
author={author}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Suspense>
|
||||
</article>
|
||||
</>
|
||||
)
|
||||
}
|
@ -2,7 +2,6 @@ import React from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { Post } from '../@types/Post'
|
||||
import SEO from '../components/atoms/SEO'
|
||||
import Layout from '../components/Layout'
|
||||
import styles from './Page.module.scss'
|
||||
|
||||
export default function Page({
|
||||
@ -23,10 +22,8 @@ export default function Page({
|
||||
<Helmet title={title} />
|
||||
<SEO slug={location.pathname} postSEO post={post} />
|
||||
|
||||
<Layout location={location}>
|
||||
<h1 className={styles.pageTitle}>{title}</h1>
|
||||
{section ? <section className={section}>{children}</section> : children}
|
||||
</Layout>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React, { useState } from 'react'
|
||||
import ModalThanks from '../../components/organisms/ModalThanks'
|
||||
import { useSiteMetadata } from '../../hooks/use-site-metadata'
|
||||
import styles from './PostActions.module.scss'
|
||||
import Icon from '../../components/atoms/Icon'
|
||||
@ -38,13 +37,8 @@ export default function PostActions({
|
||||
githubLink: string
|
||||
}) {
|
||||
const { siteUrl } = useSiteMetadata()
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
const urlTwitter = `https://twitter.com/intent/tweet?text=@kremalicious&url=${siteUrl}${slug}`
|
||||
|
||||
const toggleModal = () => {
|
||||
setShowModal(!showModal)
|
||||
}
|
||||
|
||||
return (
|
||||
<aside className={styles.actions}>
|
||||
<Action
|
||||
@ -55,17 +49,13 @@ export default function PostActions({
|
||||
<Action
|
||||
title="Found something useful?"
|
||||
text="Say thanks with Bitcoin or Ether"
|
||||
onClick={toggleModal}
|
||||
url="/thanks"
|
||||
/>
|
||||
<Action
|
||||
title="Edit on GitHub"
|
||||
text="Contribute to this post on GitHub"
|
||||
url={githubLink}
|
||||
/>
|
||||
|
||||
{showModal && (
|
||||
<ModalThanks isOpen={showModal} handleCloseModal={toggleModal} />
|
||||
)}
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ import React from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { graphql } from 'gatsby'
|
||||
import { Post as PostMetadata } from '../../@types/Post'
|
||||
import Layout from '../../components/Layout'
|
||||
import Exif from '../../components/atoms/Exif'
|
||||
import SEO from '../../components/atoms/SEO'
|
||||
import RelatedPosts from '../../components/molecules/RelatedPosts'
|
||||
@ -18,11 +17,9 @@ import { Image } from '../../components/atoms/Image'
|
||||
|
||||
export default function Post({
|
||||
data,
|
||||
location,
|
||||
pageContext: { next, prev }
|
||||
}: {
|
||||
data: { post: PostMetadata }
|
||||
location: Location
|
||||
pageContext: {
|
||||
next: { title: string; slug: string }
|
||||
prev: { title: string; slug: string }
|
||||
@ -40,7 +37,6 @@ export default function Post({
|
||||
|
||||
<SEO slug={slug} post={post} postSEO />
|
||||
|
||||
<Layout location={location}>
|
||||
<article className={styles.hentry}>
|
||||
<header>
|
||||
<PostTitle type={type} linkurl={linkurl} title={title} />
|
||||
@ -69,7 +65,6 @@ export default function Post({
|
||||
<RelatedPosts photos={type === 'photo'} tags={tags} />
|
||||
|
||||
<PrevNext prev={prev} next={next} />
|
||||
</Layout>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React from 'react'
|
||||
import { Link, graphql } from 'gatsby'
|
||||
import { Post } from '../@types/Post'
|
||||
import Layout from '../components/Layout'
|
||||
import Pagination from '../components/molecules/Pagination'
|
||||
import Featured from '../components/molecules/Featured'
|
||||
import PostTitle from './Post/PostTitle'
|
||||
@ -69,7 +68,7 @@ export default function Posts({
|
||||
})
|
||||
|
||||
return (
|
||||
<Layout location={location}>
|
||||
<>
|
||||
<SEO />
|
||||
{location.pathname === '/' && <Featured />}
|
||||
{tag && (
|
||||
@ -85,7 +84,7 @@ export default function Posts({
|
||||
)}
|
||||
{PostsList}
|
||||
{numPages > 1 && <Pagination pageContext={pageContext} />}
|
||||
</Layout>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user