global modal + small fixes/enhancements
This commit is contained in:
		
							parent
							
								
									e2d7506ebf
								
							
						
					
					
						commit
						ae5c6c457f
					
				@ -3,7 +3,7 @@ import ArrowRight from '../svgs/arrow-right-s-fill.svg'
 | 
				
			|||||||
import ArrowDown from '../svgs/arrow-down-s-fill.svg'
 | 
					import ArrowDown from '../svgs/arrow-down-s-fill.svg'
 | 
				
			||||||
import { useEffect, useState } from 'react'
 | 
					import { useEffect, useState } from 'react'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function AccordianItem ({ header, body, className, headerColor = 'var(--theme-grey)', show }) {
 | 
					export default function AccordianItem ({ header, body, headerColor = 'var(--theme-grey)', show }) {
 | 
				
			||||||
  const [open, setOpen] = useState(show)
 | 
					  const [open, setOpen] = useState(show)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
@ -19,7 +19,6 @@ export default function AccordianItem ({ header, body, className, headerColor =
 | 
				
			|||||||
        eventKey='0'
 | 
					        eventKey='0'
 | 
				
			||||||
        style={{ cursor: 'pointer', display: 'flex', alignItems: 'center' }}
 | 
					        style={{ cursor: 'pointer', display: 'flex', alignItems: 'center' }}
 | 
				
			||||||
        onClick={() => setOpen(!open)}
 | 
					        onClick={() => setOpen(!open)}
 | 
				
			||||||
        className={className}
 | 
					 | 
				
			||||||
      >
 | 
					      >
 | 
				
			||||||
        {open
 | 
					        {open
 | 
				
			||||||
          ? <ArrowDown style={{ fill: headerColor }} height={20} width={20} />
 | 
					          ? <ArrowDown style={{ fill: headerColor }} height={20} width={20} />
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,11 @@
 | 
				
			|||||||
import { gql, useMutation } from '@apollo/client'
 | 
					import { gql, useMutation } from '@apollo/client'
 | 
				
			||||||
import { Dropdown } from 'react-bootstrap'
 | 
					import { Dropdown } from 'react-bootstrap'
 | 
				
			||||||
import MoreIcon from '../svgs/more-fill.svg'
 | 
					import MoreIcon from '../svgs/more-fill.svg'
 | 
				
			||||||
import { useFundError } from './fund-error'
 | 
					import FundError from './fund-error'
 | 
				
			||||||
 | 
					import { useShowModal } from './modal'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function DontLikeThis ({ id }) {
 | 
					export default function DontLikeThis ({ id }) {
 | 
				
			||||||
  const { setError } = useFundError()
 | 
					  const showModal = useShowModal()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [dontLikeThis] = useMutation(
 | 
					  const [dontLikeThis] = useMutation(
 | 
				
			||||||
    gql`
 | 
					    gql`
 | 
				
			||||||
@ -41,7 +42,9 @@ export default function DontLikeThis ({ id }) {
 | 
				
			|||||||
              })
 | 
					              })
 | 
				
			||||||
            } catch (error) {
 | 
					            } catch (error) {
 | 
				
			||||||
              if (error.toString().includes('insufficient funds')) {
 | 
					              if (error.toString().includes('insufficient funds')) {
 | 
				
			||||||
                setError(true)
 | 
					                showModal(onClose => {
 | 
				
			||||||
 | 
					                  return <FundError onClose={onClose} />
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,48 +1,15 @@
 | 
				
			|||||||
import { Button, Modal } from 'react-bootstrap'
 | 
					 | 
				
			||||||
import React, { useState, useCallback, useContext } from 'react'
 | 
					 | 
				
			||||||
import Link from 'next/link'
 | 
					import Link from 'next/link'
 | 
				
			||||||
 | 
					import { Button } from 'react-bootstrap'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const FundErrorContext = React.createContext({
 | 
					export default function FundError ({ onClose }) {
 | 
				
			||||||
  error: null,
 | 
					 | 
				
			||||||
  toggleError: () => {}
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function FundErrorProvider ({ children }) {
 | 
					 | 
				
			||||||
  const [error, setError] = useState(false)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const contextValue = {
 | 
					 | 
				
			||||||
    error,
 | 
					 | 
				
			||||||
    setError: useCallback(e => setError(e), [])
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <FundErrorContext.Provider value={contextValue}>
 | 
					    <>
 | 
				
			||||||
      {children}
 | 
					      <p className='font-weight-bolder'>you need more sats</p>
 | 
				
			||||||
    </FundErrorContext.Provider>
 | 
					      <div className='d-flex justify-content-end'>
 | 
				
			||||||
  )
 | 
					        <Link href='/wallet?type=fund'>
 | 
				
			||||||
}
 | 
					          <Button variant='success' onClick={onClose}>fund</Button>
 | 
				
			||||||
 | 
					        </Link>
 | 
				
			||||||
export function useFundError () {
 | 
					      </div>
 | 
				
			||||||
  const { error, setError } = useContext(FundErrorContext)
 | 
					    </>
 | 
				
			||||||
  return { error, setError }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function FundErrorModal () {
 | 
					 | 
				
			||||||
  const { error, setError } = useFundError()
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <Modal
 | 
					 | 
				
			||||||
      show={error}
 | 
					 | 
				
			||||||
      onHide={() => setError(false)}
 | 
					 | 
				
			||||||
    >
 | 
					 | 
				
			||||||
      <div className='modal-close' onClick={() => setError(false)}>X</div>
 | 
					 | 
				
			||||||
      <Modal.Body>
 | 
					 | 
				
			||||||
        <p className='font-weight-bolder'>you need more sats</p>
 | 
					 | 
				
			||||||
        <div className='d-flex justify-content-end'>
 | 
					 | 
				
			||||||
          <Link href='/wallet?type=fund'>
 | 
					 | 
				
			||||||
            <Button variant='success' onClick={() => setError(false)}>fund</Button>
 | 
					 | 
				
			||||||
          </Link>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </Modal.Body>
 | 
					 | 
				
			||||||
    </Modal>
 | 
					 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,105 +1,69 @@
 | 
				
			|||||||
import { Button, InputGroup, Modal } from 'react-bootstrap'
 | 
					import { Button, InputGroup } from 'react-bootstrap'
 | 
				
			||||||
import React, { useState, useCallback, useContext, useRef, useEffect } from 'react'
 | 
					import React, { useState, useRef, useEffect } from 'react'
 | 
				
			||||||
import * as Yup from 'yup'
 | 
					import * as Yup from 'yup'
 | 
				
			||||||
import { Form, Input, SubmitButton } from './form'
 | 
					import { Form, Input, SubmitButton } from './form'
 | 
				
			||||||
import { useMe } from './me'
 | 
					import { useMe } from './me'
 | 
				
			||||||
import UpBolt from '../svgs/bolt.svg'
 | 
					import UpBolt from '../svgs/bolt.svg'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ItemActContext = React.createContext({
 | 
					 | 
				
			||||||
  item: null,
 | 
					 | 
				
			||||||
  setItem: () => {}
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function ItemActProvider ({ children }) {
 | 
					 | 
				
			||||||
  const [item, setItem] = useState(null)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const contextValue = {
 | 
					 | 
				
			||||||
    item,
 | 
					 | 
				
			||||||
    setItem: useCallback(i => setItem(i), [])
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <ItemActContext.Provider value={contextValue}>
 | 
					 | 
				
			||||||
      {children}
 | 
					 | 
				
			||||||
    </ItemActContext.Provider>
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function useItemAct () {
 | 
					 | 
				
			||||||
  const { item, setItem } = useContext(ItemActContext)
 | 
					 | 
				
			||||||
  return { item, setItem }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const ActSchema = Yup.object({
 | 
					export const ActSchema = Yup.object({
 | 
				
			||||||
  amount: Yup.number().typeError('must be a number').required('required')
 | 
					  amount: Yup.number().typeError('must be a number').required('required')
 | 
				
			||||||
    .positive('must be positive').integer('must be whole')
 | 
					    .positive('must be positive').integer('must be whole')
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function ItemActModal () {
 | 
					export default function ItemAct ({ onClose, itemId, act, strike }) {
 | 
				
			||||||
  const { item, setItem } = useItemAct()
 | 
					 | 
				
			||||||
  const inputRef = useRef(null)
 | 
					  const inputRef = useRef(null)
 | 
				
			||||||
  const me = useMe()
 | 
					  const me = useMe()
 | 
				
			||||||
  const [oValue, setOValue] = useState()
 | 
					  const [oValue, setOValue] = useState()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    inputRef.current?.focus()
 | 
					    inputRef.current?.focus()
 | 
				
			||||||
  }, [item])
 | 
					  }, [onClose, itemId])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Modal
 | 
					    <Form
 | 
				
			||||||
      show={!!item}
 | 
					      initial={{
 | 
				
			||||||
      onHide={() => {
 | 
					        amount: me?.tipDefault,
 | 
				
			||||||
        setItem(null)
 | 
					        default: false
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					      schema={ActSchema}
 | 
				
			||||||
 | 
					      onSubmit={async ({ amount }) => {
 | 
				
			||||||
 | 
					        await act({
 | 
				
			||||||
 | 
					          variables: {
 | 
				
			||||||
 | 
					            id: itemId,
 | 
				
			||||||
 | 
					            sats: Number(amount)
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        await strike()
 | 
				
			||||||
 | 
					        onClose()
 | 
				
			||||||
      }}
 | 
					      }}
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <div className='modal-close' onClick={() => setItem(null)}>X</div>
 | 
					      <Input
 | 
				
			||||||
      <Modal.Body>
 | 
					        label='amount'
 | 
				
			||||||
        <Form
 | 
					        name='amount'
 | 
				
			||||||
          initial={{
 | 
					        innerRef={inputRef}
 | 
				
			||||||
            amount: me?.tipDefault,
 | 
					        overrideValue={oValue}
 | 
				
			||||||
            default: false
 | 
					        required
 | 
				
			||||||
          }}
 | 
					        autoFocus
 | 
				
			||||||
          schema={ActSchema}
 | 
					        append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
 | 
				
			||||||
          onSubmit={async ({ amount }) => {
 | 
					      />
 | 
				
			||||||
            await item.act({
 | 
					      <div>
 | 
				
			||||||
              variables: {
 | 
					        {[1, 10, 100, 1000, 10000].map(num =>
 | 
				
			||||||
                id: item.itemId,
 | 
					          <Button
 | 
				
			||||||
                sats: Number(amount)
 | 
					            size='sm'
 | 
				
			||||||
              }
 | 
					            className={`${num > 1 ? 'ml-2' : ''} mb-2`}
 | 
				
			||||||
            })
 | 
					            key={num}
 | 
				
			||||||
            await item.strike()
 | 
					            onClick={() => { setOValue(num) }}
 | 
				
			||||||
            setItem(null)
 | 
					          >
 | 
				
			||||||
          }}
 | 
					            <UpBolt
 | 
				
			||||||
        >
 | 
					              className='mr-1'
 | 
				
			||||||
          <Input
 | 
					              width={14}
 | 
				
			||||||
            label='amount'
 | 
					              height={14}
 | 
				
			||||||
            name='amount'
 | 
					            />{num}
 | 
				
			||||||
            innerRef={inputRef}
 | 
					          </Button>)}
 | 
				
			||||||
            overrideValue={oValue}
 | 
					      </div>
 | 
				
			||||||
            required
 | 
					      <div className='d-flex'>
 | 
				
			||||||
            autoFocus
 | 
					        <SubmitButton variant='success' className='ml-auto mt-1 px-4' value='TIP'>tip</SubmitButton>
 | 
				
			||||||
            append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
 | 
					      </div>
 | 
				
			||||||
          />
 | 
					    </Form>
 | 
				
			||||||
          <div>
 | 
					 | 
				
			||||||
            {[1, 10, 100, 1000, 10000].map(num =>
 | 
					 | 
				
			||||||
              <Button
 | 
					 | 
				
			||||||
                size='sm'
 | 
					 | 
				
			||||||
                className={`${num > 1 ? 'ml-2' : ''} mb-2`}
 | 
					 | 
				
			||||||
                key={num}
 | 
					 | 
				
			||||||
                onClick={() => { setOValue(num) }}
 | 
					 | 
				
			||||||
              >
 | 
					 | 
				
			||||||
                <UpBolt
 | 
					 | 
				
			||||||
                  className='mr-1'
 | 
					 | 
				
			||||||
                  width={14}
 | 
					 | 
				
			||||||
                  height={14}
 | 
					 | 
				
			||||||
                />{num}
 | 
					 | 
				
			||||||
              </Button>)}
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
          <div className='d-flex'>
 | 
					 | 
				
			||||||
            <SubmitButton variant='success' className='ml-auto mt-1 px-4' value='TIP'>tip</SubmitButton>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </Form>
 | 
					 | 
				
			||||||
      </Modal.Body>
 | 
					 | 
				
			||||||
    </Modal>
 | 
					 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,6 @@ import { Form, Input, SubmitButton } from '../components/form'
 | 
				
			|||||||
import * as Yup from 'yup'
 | 
					import * as Yup from 'yup'
 | 
				
			||||||
import { useState } from 'react'
 | 
					import { useState } from 'react'
 | 
				
			||||||
import Alert from 'react-bootstrap/Alert'
 | 
					import Alert from 'react-bootstrap/Alert'
 | 
				
			||||||
import LayoutCenter from '../components/layout-center'
 | 
					 | 
				
			||||||
import { useRouter } from 'next/router'
 | 
					import { useRouter } from 'next/router'
 | 
				
			||||||
import { LightningAuth } from './lightning-auth'
 | 
					import { LightningAuth } from './lightning-auth'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -56,60 +55,59 @@ export default function Login ({ providers, callbackUrl, error, text, Header, Fo
 | 
				
			|||||||
  const [errorMessage, setErrorMessage] = useState(error && (errors[error] ?? errors.default))
 | 
					  const [errorMessage, setErrorMessage] = useState(error && (errors[error] ?? errors.default))
 | 
				
			||||||
  const router = useRouter()
 | 
					  const router = useRouter()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (router.query.type === 'lightning') {
 | 
				
			||||||
 | 
					    return <LightningAuth callbackUrl={callbackUrl} text={text} />
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <LayoutCenter>
 | 
					    <div className={styles.login}>
 | 
				
			||||||
      {router.query.type === 'lightning'
 | 
					      {Header && <Header />}
 | 
				
			||||||
        ? <LightningAuth callbackUrl={callbackUrl} text={text} />
 | 
					      {errorMessage &&
 | 
				
			||||||
        : (
 | 
					        <Alert
 | 
				
			||||||
          <div className={styles.login}>
 | 
					          variant='danger'
 | 
				
			||||||
            {Header && <Header />}
 | 
					          onClose={() => setErrorMessage(undefined)}
 | 
				
			||||||
            {errorMessage &&
 | 
					          dismissible
 | 
				
			||||||
              <Alert
 | 
					        >{errorMessage}
 | 
				
			||||||
                variant='danger'
 | 
					        </Alert>}
 | 
				
			||||||
                onClose={() => setErrorMessage(undefined)}
 | 
					      <Button
 | 
				
			||||||
                dismissible
 | 
					        className={`mt-2 ${styles.providerButton}`}
 | 
				
			||||||
              >{errorMessage}
 | 
					        variant='primary'
 | 
				
			||||||
              </Alert>}
 | 
					        onClick={() => router.push({
 | 
				
			||||||
            <Button
 | 
					          pathname: router.pathname,
 | 
				
			||||||
              className={`mt-2 ${styles.providerButton}`}
 | 
					          query: { ...router.query, type: 'lightning' }
 | 
				
			||||||
              variant='primary'
 | 
					        })}
 | 
				
			||||||
              onClick={() => router.push({
 | 
					      >
 | 
				
			||||||
                pathname: router.pathname,
 | 
					        <LightningIcon
 | 
				
			||||||
                query: { ...router.query, type: 'lightning' }
 | 
					          width={20}
 | 
				
			||||||
              })}
 | 
					          height={20}
 | 
				
			||||||
            >
 | 
					          className='mr-3'
 | 
				
			||||||
              <LightningIcon
 | 
					        />{text || 'Login'} with Lightning
 | 
				
			||||||
                width={20}
 | 
					      </Button>
 | 
				
			||||||
                height={20}
 | 
					      {providers && Object.values(providers).map(provider => {
 | 
				
			||||||
                className='mr-3'
 | 
					        if (provider.name === 'Email' || provider.name === 'Lightning') {
 | 
				
			||||||
              />{text || 'Login'} with Lightning
 | 
					          return null
 | 
				
			||||||
            </Button>
 | 
					        }
 | 
				
			||||||
            {Object.values(providers).map(provider => {
 | 
					        const [variant, Icon] =
 | 
				
			||||||
              if (provider.name === 'Email' || provider.name === 'Lightning') {
 | 
					 | 
				
			||||||
                return null
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
              const [variant, Icon] =
 | 
					 | 
				
			||||||
          provider.name === 'Twitter'
 | 
					          provider.name === 'Twitter'
 | 
				
			||||||
            ? ['twitter', TwitterIcon]
 | 
					            ? ['twitter', TwitterIcon]
 | 
				
			||||||
            : ['dark', GithubIcon]
 | 
					            : ['dark', GithubIcon]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              return (
 | 
					        return (
 | 
				
			||||||
                <Button
 | 
					          <Button
 | 
				
			||||||
                  className={`mt-2 ${styles.providerButton}`}
 | 
					            className={`mt-2 ${styles.providerButton}`}
 | 
				
			||||||
                  key={provider.name}
 | 
					            key={provider.name}
 | 
				
			||||||
                  variant={variant}
 | 
					            variant={variant}
 | 
				
			||||||
                  onClick={() => signIn(provider.id, { callbackUrl })}
 | 
					            onClick={() => signIn(provider.id, { callbackUrl })}
 | 
				
			||||||
                >
 | 
					          >
 | 
				
			||||||
                  <Icon
 | 
					            <Icon
 | 
				
			||||||
                    className='mr-3'
 | 
					              className='mr-3'
 | 
				
			||||||
                  />{text || 'Login'} with {provider.name}
 | 
					            />{text || 'Login'} with {provider.name}
 | 
				
			||||||
                </Button>
 | 
					          </Button>
 | 
				
			||||||
              )
 | 
					        )
 | 
				
			||||||
            })}
 | 
					      })}
 | 
				
			||||||
            <div className='mt-2 text-center text-muted font-weight-bold'>or</div>
 | 
					      <div className='mt-2 text-center text-muted font-weight-bold'>or</div>
 | 
				
			||||||
            <EmailLoginForm text={text} callbackUrl={callbackUrl} />
 | 
					      <EmailLoginForm text={text} callbackUrl={callbackUrl} />
 | 
				
			||||||
            {Footer && <Footer />}
 | 
					      {Footer && <Footer />}
 | 
				
			||||||
          </div>)}
 | 
					    </div>
 | 
				
			||||||
    </LayoutCenter>
 | 
					 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										51
									
								
								components/modal.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								components/modal.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					import { createContext, useCallback, useContext, useMemo, useState } from 'react'
 | 
				
			||||||
 | 
					import { Modal } from 'react-bootstrap'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ShowModalContext = createContext(() => null)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function ShowModalProvider ({ children }) {
 | 
				
			||||||
 | 
					  const [modal, showModal] = useModal()
 | 
				
			||||||
 | 
					  const contextValue = showModal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <ShowModalContext.Provider value={contextValue}>
 | 
				
			||||||
 | 
					      {children}
 | 
				
			||||||
 | 
					      {modal}
 | 
				
			||||||
 | 
					    </ShowModalContext.Provider>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function useShowModal () {
 | 
				
			||||||
 | 
					  return useContext(ShowModalContext)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function useModal () {
 | 
				
			||||||
 | 
					  const [modalContent, setModalContent] = useState(null)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const onClose = useCallback(() => {
 | 
				
			||||||
 | 
					    setModalContent(null)
 | 
				
			||||||
 | 
					  }, [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const modal = useMemo(() => {
 | 
				
			||||||
 | 
					    if (modalContent === null) {
 | 
				
			||||||
 | 
					      return null
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Modal onHide={onClose} show={!!modalContent}>
 | 
				
			||||||
 | 
					        <div className='modal-close' onClick={onClose}>X</div>
 | 
				
			||||||
 | 
					        <Modal.Body>
 | 
				
			||||||
 | 
					          {modalContent}
 | 
				
			||||||
 | 
					        </Modal.Body>
 | 
				
			||||||
 | 
					      </Modal>
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }, [modalContent, onClose])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const showModal = useCallback(
 | 
				
			||||||
 | 
					    (getContent) => {
 | 
				
			||||||
 | 
					      setModalContent(getContent(onClose))
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    [onClose]
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return [modal, showModal]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -6,12 +6,13 @@ import { useMe } from './me'
 | 
				
			|||||||
import styles from './poll.module.css'
 | 
					import styles from './poll.module.css'
 | 
				
			||||||
import Check from '../svgs/checkbox-circle-fill.svg'
 | 
					import Check from '../svgs/checkbox-circle-fill.svg'
 | 
				
			||||||
import { signIn } from 'next-auth/client'
 | 
					import { signIn } from 'next-auth/client'
 | 
				
			||||||
import { useFundError } from './fund-error'
 | 
					 | 
				
			||||||
import ActionTooltip from './action-tooltip'
 | 
					import ActionTooltip from './action-tooltip'
 | 
				
			||||||
 | 
					import { useShowModal } from './modal'
 | 
				
			||||||
 | 
					import FundError from './fund-error'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Poll ({ item }) {
 | 
					export default function Poll ({ item }) {
 | 
				
			||||||
  const me = useMe()
 | 
					  const me = useMe()
 | 
				
			||||||
  const { setError } = useFundError()
 | 
					  const showModal = useShowModal()
 | 
				
			||||||
  const [pollVote] = useMutation(
 | 
					  const [pollVote] = useMutation(
 | 
				
			||||||
    gql`
 | 
					    gql`
 | 
				
			||||||
      mutation pollVote($id: ID!) {
 | 
					      mutation pollVote($id: ID!) {
 | 
				
			||||||
@ -60,7 +61,9 @@ export default function Poll ({ item }) {
 | 
				
			|||||||
                  })
 | 
					                  })
 | 
				
			||||||
                } catch (error) {
 | 
					                } catch (error) {
 | 
				
			||||||
                  if (error.toString().includes('insufficient funds')) {
 | 
					                  if (error.toString().includes('insufficient funds')) {
 | 
				
			||||||
                    setError(true)
 | 
					                    showModal(onClose => {
 | 
				
			||||||
 | 
					                      return <FundError onClose={onClose} />
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
 | 
				
			|||||||
@ -2,15 +2,16 @@ import { LightningConsumer } from './lightning'
 | 
				
			|||||||
import UpBolt from '../svgs/bolt.svg'
 | 
					import UpBolt from '../svgs/bolt.svg'
 | 
				
			||||||
import styles from './upvote.module.css'
 | 
					import styles from './upvote.module.css'
 | 
				
			||||||
import { gql, useMutation } from '@apollo/client'
 | 
					import { gql, useMutation } from '@apollo/client'
 | 
				
			||||||
import { signIn } from 'next-auth/client'
 | 
					import FundError from './fund-error'
 | 
				
			||||||
import { useFundError } from './fund-error'
 | 
					 | 
				
			||||||
import ActionTooltip from './action-tooltip'
 | 
					import ActionTooltip from './action-tooltip'
 | 
				
			||||||
import { useItemAct } from './item-act'
 | 
					import ItemAct from './item-act'
 | 
				
			||||||
import { useMe } from './me'
 | 
					import { useMe } from './me'
 | 
				
			||||||
import Rainbow from '../lib/rainbow'
 | 
					import Rainbow from '../lib/rainbow'
 | 
				
			||||||
import { useRef, useState } from 'react'
 | 
					import { useRef, useState } from 'react'
 | 
				
			||||||
import LongPressable from 'react-longpressable'
 | 
					import LongPressable from 'react-longpressable'
 | 
				
			||||||
import { Overlay, Popover } from 'react-bootstrap'
 | 
					import { Overlay, Popover } from 'react-bootstrap'
 | 
				
			||||||
 | 
					import { useShowModal } from './modal'
 | 
				
			||||||
 | 
					import { useRouter } from 'next/router'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getColor = (meSats) => {
 | 
					const getColor = (meSats) => {
 | 
				
			||||||
  if (!meSats || meSats <= 10) {
 | 
					  if (!meSats || meSats <= 10) {
 | 
				
			||||||
@ -63,8 +64,8 @@ const TipPopover = ({ target, show, handleClose }) => (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function UpVote ({ item, className }) {
 | 
					export default function UpVote ({ item, className }) {
 | 
				
			||||||
  const { setError } = useFundError()
 | 
					  const showModal = useShowModal()
 | 
				
			||||||
  const { setItem } = useItemAct()
 | 
					  const router = useRouter()
 | 
				
			||||||
  const [voteShow, _setVoteShow] = useState(false)
 | 
					  const [voteShow, _setVoteShow] = useState(false)
 | 
				
			||||||
  const [tipShow, _setTipShow] = useState(false)
 | 
					  const [tipShow, _setTipShow] = useState(false)
 | 
				
			||||||
  const ref = useRef()
 | 
					  const ref = useRef()
 | 
				
			||||||
@ -123,11 +124,14 @@ export default function UpVote ({ item, className }) {
 | 
				
			|||||||
              return existingSats + sats
 | 
					              return existingSats + sats
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            meSats (existingSats = 0) {
 | 
					            meSats (existingSats = 0) {
 | 
				
			||||||
              if (existingSats === 0) {
 | 
					              if (sats <= me.sats) {
 | 
				
			||||||
                setVoteShow(true)
 | 
					                if (existingSats === 0) {
 | 
				
			||||||
              } else {
 | 
					                  setVoteShow(true)
 | 
				
			||||||
                setTipShow(true)
 | 
					                } else {
 | 
				
			||||||
 | 
					                  setTipShow(true)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              return existingSats + sats
 | 
					              return existingSats + sats
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            upvotes (existingUpvotes = 0) {
 | 
					            upvotes (existingUpvotes = 0) {
 | 
				
			||||||
@ -183,7 +187,8 @@ export default function UpVote ({ item, className }) {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                setTipShow(false)
 | 
					                setTipShow(false)
 | 
				
			||||||
                setItem({ itemId: item.id, act, strike })
 | 
					                showModal(onClose =>
 | 
				
			||||||
 | 
					                  <ItemAct onClose={onClose} itemId={item.id} act={act} strike={strike} />)
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            onShortPress={
 | 
					            onShortPress={
 | 
				
			||||||
@ -215,13 +220,18 @@ export default function UpVote ({ item, className }) {
 | 
				
			|||||||
                    })
 | 
					                    })
 | 
				
			||||||
                  } catch (error) {
 | 
					                  } catch (error) {
 | 
				
			||||||
                    if (error.toString().includes('insufficient funds')) {
 | 
					                    if (error.toString().includes('insufficient funds')) {
 | 
				
			||||||
                      setError(true)
 | 
					                      showModal(onClose => {
 | 
				
			||||||
 | 
					                        return <FundError onClose={onClose} />
 | 
				
			||||||
 | 
					                      })
 | 
				
			||||||
                      return
 | 
					                      return
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    throw new Error({ message: error.toString() })
 | 
					                    throw new Error({ message: error.toString() })
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              : signIn
 | 
					              : async () => await router.push({
 | 
				
			||||||
 | 
					                pathname: '/signup',
 | 
				
			||||||
 | 
					                query: { callbackUrl: window.location.origin + router.asPath }
 | 
				
			||||||
 | 
					              })
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          >
 | 
					          >
 | 
				
			||||||
            <ActionTooltip notForm disable={item?.mine || fwd2me} overlayText={overlayText()}>
 | 
					            <ActionTooltip notForm disable={item?.mine || fwd2me} overlayText={overlayText()}>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,9 @@
 | 
				
			|||||||
import '../styles/globals.scss'
 | 
					import '../styles/globals.scss'
 | 
				
			||||||
import { ApolloProvider, gql, useQuery } from '@apollo/client'
 | 
					import { ApolloProvider, gql, useQuery } from '@apollo/client'
 | 
				
			||||||
import { Provider } from 'next-auth/client'
 | 
					import { Provider } from 'next-auth/client'
 | 
				
			||||||
import { FundErrorModal, FundErrorProvider } from '../components/fund-error'
 | 
					 | 
				
			||||||
import { MeProvider } from '../components/me'
 | 
					import { MeProvider } from '../components/me'
 | 
				
			||||||
import PlausibleProvider from 'next-plausible'
 | 
					import PlausibleProvider from 'next-plausible'
 | 
				
			||||||
import { LightningProvider } from '../components/lightning'
 | 
					import { LightningProvider } from '../components/lightning'
 | 
				
			||||||
import { ItemActModal, ItemActProvider } from '../components/item-act'
 | 
					 | 
				
			||||||
import getApolloClient from '../lib/apollo'
 | 
					import getApolloClient from '../lib/apollo'
 | 
				
			||||||
import NextNProgress from 'nextjs-progressbar'
 | 
					import NextNProgress from 'nextjs-progressbar'
 | 
				
			||||||
import { PriceProvider } from '../components/price'
 | 
					import { PriceProvider } from '../components/price'
 | 
				
			||||||
@ -14,6 +12,7 @@ import { useRouter } from 'next/dist/client/router'
 | 
				
			|||||||
import { useEffect } from 'react'
 | 
					import { useEffect } from 'react'
 | 
				
			||||||
import Moon from '../svgs/moon-fill.svg'
 | 
					import Moon from '../svgs/moon-fill.svg'
 | 
				
			||||||
import Layout from '../components/layout'
 | 
					import Layout from '../components/layout'
 | 
				
			||||||
 | 
					import { ShowModalProvider } from '../components/modal'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function CSRWrapper ({ Component, apollo, ...props }) {
 | 
					function CSRWrapper ({ Component, apollo, ...props }) {
 | 
				
			||||||
  const { data, error } = useQuery(gql`${apollo.query}`, { variables: apollo.variables, fetchPolicy: 'cache-first' })
 | 
					  const { data, error } = useQuery(gql`${apollo.query}`, { variables: apollo.variables, fetchPolicy: 'cache-first' })
 | 
				
			||||||
@ -89,15 +88,11 @@ function MyApp ({ Component, pageProps: { session, ...props } }) {
 | 
				
			|||||||
            <MeProvider me={me}>
 | 
					            <MeProvider me={me}>
 | 
				
			||||||
              <PriceProvider price={price}>
 | 
					              <PriceProvider price={price}>
 | 
				
			||||||
                <LightningProvider>
 | 
					                <LightningProvider>
 | 
				
			||||||
                  <FundErrorProvider>
 | 
					                  <ShowModalProvider>
 | 
				
			||||||
                    <FundErrorModal />
 | 
					                    {data || !apollo?.query
 | 
				
			||||||
                    <ItemActProvider>
 | 
					                      ? <Component {...props} />
 | 
				
			||||||
                      <ItemActModal />
 | 
					                      : <CSRWrapper Component={Component} {...props} />}
 | 
				
			||||||
                      {data || !apollo?.query
 | 
					                  </ShowModalProvider>
 | 
				
			||||||
                        ? <Component {...props} />
 | 
					 | 
				
			||||||
                        : <CSRWrapper Component={Component} {...props} />}
 | 
					 | 
				
			||||||
                    </ItemActProvider>
 | 
					 | 
				
			||||||
                  </FundErrorProvider>
 | 
					 | 
				
			||||||
                </LightningProvider>
 | 
					                </LightningProvider>
 | 
				
			||||||
              </PriceProvider>
 | 
					              </PriceProvider>
 | 
				
			||||||
            </MeProvider>
 | 
					            </MeProvider>
 | 
				
			||||||
 | 
				
			|||||||
@ -136,7 +136,8 @@ export default (req, res) => NextAuth(req, res, {
 | 
				
			|||||||
    signingKey: process.env.JWT_SIGNING_PRIVATE_KEY
 | 
					    signingKey: process.env.JWT_SIGNING_PRIVATE_KEY
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  pages: {
 | 
					  pages: {
 | 
				
			||||||
    signIn: '/login'
 | 
					    signIn: '/login',
 | 
				
			||||||
 | 
					    verifyRequest: '/email'
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										14
									
								
								pages/email.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								pages/email.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					import LayoutError from '../components/layout-error'
 | 
				
			||||||
 | 
					import { Image } from 'react-bootstrap'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function Email () {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <LayoutError>
 | 
				
			||||||
 | 
					      <div className='p-4 text-center'>
 | 
				
			||||||
 | 
					        <h1>Check your email</h1>
 | 
				
			||||||
 | 
					        <h4 className='pb-4'>A sign in link has been sent to your email address</h4>
 | 
				
			||||||
 | 
					        <Image width='500' height='376' src='/hello.gif' fluid />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </LayoutError>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -6,6 +6,7 @@ import { gql } from '@apollo/client'
 | 
				
			|||||||
import { INVITE_FIELDS } from '../../fragments/invites'
 | 
					import { INVITE_FIELDS } from '../../fragments/invites'
 | 
				
			||||||
import getSSRApolloClient from '../../api/ssrApollo'
 | 
					import getSSRApolloClient from '../../api/ssrApollo'
 | 
				
			||||||
import Link from 'next/link'
 | 
					import Link from 'next/link'
 | 
				
			||||||
 | 
					import LayoutCenter from '../../components/layout-center'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function getServerSideProps ({ req, res, query: { id, error = null } }) {
 | 
					export async function getServerSideProps ({ req, res, query: { id, error = null } }) {
 | 
				
			||||||
  const session = await getSession({ req })
 | 
					  const session = await getSession({ req })
 | 
				
			||||||
@ -78,5 +79,9 @@ function InviteHeader ({ invite }) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Invite ({ invite, ...props }) {
 | 
					export default function Invite ({ invite, ...props }) {
 | 
				
			||||||
  return <Login Header={() => <InviteHeader invite={invite} />} text='Sign up' {...props} />
 | 
					  return (
 | 
				
			||||||
 | 
					    <LayoutCenter>
 | 
				
			||||||
 | 
					      <Login Header={() => <InviteHeader invite={invite} />} text='Sign up' {...props} />
 | 
				
			||||||
 | 
					    </LayoutCenter>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
import { providers, getSession } from 'next-auth/client'
 | 
					import { providers, getSession } from 'next-auth/client'
 | 
				
			||||||
import Link from 'next/link'
 | 
					import Link from 'next/link'
 | 
				
			||||||
 | 
					import LayoutCenter from '../components/layout-center'
 | 
				
			||||||
import Login from '../components/login'
 | 
					import Login from '../components/login'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function getServerSideProps ({ req, res, query: { callbackUrl, error = null } }) {
 | 
					export async function getServerSideProps ({ req, res, query: { callbackUrl, error = null } }) {
 | 
				
			||||||
@ -30,9 +31,11 @@ function LoginFooter ({ callbackUrl }) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export default function LoginPage (props) {
 | 
					export default function LoginPage (props) {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Login
 | 
					    <LayoutCenter>
 | 
				
			||||||
      Footer={() => <LoginFooter callbackUrl={props.callbackUrl} />}
 | 
					      <Login
 | 
				
			||||||
      {...props}
 | 
					        Footer={() => <LoginFooter callbackUrl={props.callbackUrl} />}
 | 
				
			||||||
    />
 | 
					        {...props}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    </LayoutCenter>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
import { providers, getSession } from 'next-auth/client'
 | 
					import { providers, getSession } from 'next-auth/client'
 | 
				
			||||||
import Link from 'next/link'
 | 
					import Link from 'next/link'
 | 
				
			||||||
 | 
					import LayoutCenter from '../components/layout-center'
 | 
				
			||||||
import Login from '../components/login'
 | 
					import Login from '../components/login'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function getServerSideProps ({ req, res, query: { callbackUrl, error = null } }) {
 | 
					export async function getServerSideProps ({ req, res, query: { callbackUrl, error = null } }) {
 | 
				
			||||||
@ -41,11 +42,13 @@ function SignUpFooter ({ callbackUrl }) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export default function SignUp ({ ...props }) {
 | 
					export default function SignUp ({ ...props }) {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Login
 | 
					    <LayoutCenter>
 | 
				
			||||||
      Header={() => <SignUpHeader />}
 | 
					      <Login
 | 
				
			||||||
      Footer={() => <SignUpFooter callbackUrl={props.callbackUrl} />}
 | 
					        Header={() => <SignUpHeader />}
 | 
				
			||||||
      text='Sign up'
 | 
					        Footer={() => <SignUpFooter callbackUrl={props.callbackUrl} />}
 | 
				
			||||||
      {...props}
 | 
					        text='Sign up'
 | 
				
			||||||
    />
 | 
					        {...props}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    </LayoutCenter>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@
 | 
				
			|||||||
// This will help to prevent a flash if dark mode is the default.
 | 
					// This will help to prevent a flash if dark mode is the default.
 | 
				
			||||||
const COLORS = {
 | 
					const COLORS = {
 | 
				
			||||||
  light: {
 | 
					  light: {
 | 
				
			||||||
    body: '#f5f5f5',
 | 
					    body: '#f5f5f7',
 | 
				
			||||||
    color: '#212529',
 | 
					    color: '#212529',
 | 
				
			||||||
    navbarVariant: 'light',
 | 
					    navbarVariant: 'light',
 | 
				
			||||||
    navLink: 'rgba(0, 0, 0, 0.55)',
 | 
					    navLink: 'rgba(0, 0, 0, 0.55)',
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								public/hello.gif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/hello.gif
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 418 KiB  | 
@ -11,7 +11,7 @@ $theme-colors: (
 | 
				
			|||||||
  "grey-darkmode": #8c8c8c
 | 
					  "grey-darkmode": #8c8c8c
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$body-bg: #f5f5f5;
 | 
					$body-bg: #f5f5f7;
 | 
				
			||||||
$border-radius: .4rem;
 | 
					$border-radius: .4rem;
 | 
				
			||||||
$enable-transitions: false;
 | 
					$enable-transitions: false;
 | 
				
			||||||
$enable-gradients: false;
 | 
					$enable-gradients: false;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user