2023-01-07 00:53:09 +00:00
|
|
|
import { Checkbox, Form, Input, SubmitButton, Select, VariableInput } from '../components/form'
|
2022-06-02 22:55:23 +00:00
|
|
|
import { Alert, Button, InputGroup, Modal } from 'react-bootstrap'
|
2021-10-30 16:20:11 +00:00
|
|
|
import LayoutCenter from '../components/layout-center'
|
|
|
|
import { useState } from 'react'
|
2022-06-02 22:55:23 +00:00
|
|
|
import { gql, useMutation, useQuery } from '@apollo/client'
|
2022-04-21 22:50:02 +00:00
|
|
|
import { getGetServerSideProps } from '../api/ssrApollo'
|
2022-06-02 22:55:23 +00:00
|
|
|
import LoginButton from '../components/login-button'
|
|
|
|
import { signIn } from 'next-auth/client'
|
|
|
|
import ModalButton from '../components/modal-button'
|
2023-01-18 18:49:20 +00:00
|
|
|
import { LightningAuth, SlashtagsAuth } from '../components/lightning-auth'
|
2022-09-06 13:01:49 +00:00
|
|
|
import { SETTINGS, SET_SETTINGS } from '../fragments/users'
|
2022-06-02 22:55:23 +00:00
|
|
|
import { useRouter } from 'next/router'
|
2022-08-30 21:50:47 +00:00
|
|
|
import Info from '../components/info'
|
2022-12-01 21:31:04 +00:00
|
|
|
import Link from 'next/link'
|
2022-12-09 19:25:38 +00:00
|
|
|
import AccordianItem from '../components/accordian-item'
|
2023-02-01 15:54:08 +00:00
|
|
|
import { bech32 } from 'bech32'
|
2023-02-08 19:38:04 +00:00
|
|
|
import { NOSTR_MAX_RELAY_NUM, NOSTR_PUBKEY_BECH32 } from '../lib/nostr'
|
|
|
|
import { emailSchema, lastAuthRemovalSchema, settingsSchema } from '../lib/validate'
|
|
|
|
import { SUPPORTED_CURRENCIES } from '../lib/currency'
|
2022-04-21 22:50:02 +00:00
|
|
|
|
2022-06-02 22:55:23 +00:00
|
|
|
export const getServerSideProps = getGetServerSideProps(SETTINGS)
|
2021-10-30 16:20:11 +00:00
|
|
|
|
2023-02-01 15:54:08 +00:00
|
|
|
function bech32encode (hexString) {
|
|
|
|
return bech32.encode('npub', bech32.toWords(Buffer.from(hexString, 'hex')))
|
|
|
|
}
|
|
|
|
|
2022-06-02 22:55:23 +00:00
|
|
|
export default function Settings ({ data: { settings } }) {
|
2021-10-30 16:20:11 +00:00
|
|
|
const [success, setSuccess] = useState()
|
2022-09-06 13:01:49 +00:00
|
|
|
const [setSettings] = useMutation(SET_SETTINGS, {
|
|
|
|
update (cache, { data: { setSettings } }) {
|
|
|
|
cache.modify({
|
|
|
|
id: 'ROOT_QUERY',
|
|
|
|
fields: {
|
|
|
|
settings () {
|
|
|
|
return setSettings
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2021-10-30 16:20:11 +00:00
|
|
|
)
|
|
|
|
|
2022-06-02 22:55:23 +00:00
|
|
|
const { data } = useQuery(SETTINGS)
|
|
|
|
if (data) {
|
|
|
|
({ settings } = data)
|
|
|
|
}
|
|
|
|
|
2021-10-30 16:20:11 +00:00
|
|
|
return (
|
|
|
|
<LayoutCenter>
|
2022-06-02 22:55:23 +00:00
|
|
|
<div className='py-3 w-100'>
|
|
|
|
<h2 className='mb-2 text-left'>settings</h2>
|
|
|
|
<Form
|
|
|
|
initial={{
|
|
|
|
tipDefault: settings?.tipDefault || 21,
|
2022-12-09 19:25:38 +00:00
|
|
|
turboTipping: settings?.turboTipping,
|
2022-09-12 23:18:23 +00:00
|
|
|
fiatCurrency: settings?.fiatCurrency || 'USD',
|
2022-06-02 22:55:23 +00:00
|
|
|
noteItemSats: settings?.noteItemSats,
|
|
|
|
noteEarning: settings?.noteEarning,
|
|
|
|
noteAllDescendants: settings?.noteAllDescendants,
|
|
|
|
noteMentions: settings?.noteMentions,
|
|
|
|
noteDeposits: settings?.noteDeposits,
|
|
|
|
noteInvites: settings?.noteInvites,
|
2022-08-30 21:50:47 +00:00
|
|
|
noteJobIndicator: settings?.noteJobIndicator,
|
2023-02-01 14:44:35 +00:00
|
|
|
noteCowboyHat: settings?.noteCowboyHat,
|
2022-09-21 19:57:36 +00:00
|
|
|
hideInvoiceDesc: settings?.hideInvoiceDesc,
|
2022-12-01 21:31:04 +00:00
|
|
|
hideFromTopUsers: settings?.hideFromTopUsers,
|
2022-09-27 21:19:15 +00:00
|
|
|
wildWestMode: settings?.wildWestMode,
|
2023-01-07 00:53:09 +00:00
|
|
|
greeterMode: settings?.greeterMode,
|
2023-02-01 15:54:08 +00:00
|
|
|
nostrPubkey: settings?.nostrPubkey ? bech32encode(settings.nostrPubkey) : '',
|
2023-01-07 01:18:03 +00:00
|
|
|
nostrRelays: settings?.nostrRelays?.length ? settings?.nostrRelays : ['']
|
2022-06-02 22:55:23 +00:00
|
|
|
}}
|
2023-02-08 19:38:04 +00:00
|
|
|
schema={settingsSchema}
|
2023-01-07 00:53:09 +00:00
|
|
|
onSubmit={async ({ tipDefault, nostrPubkey, nostrRelays, ...values }) => {
|
|
|
|
if (nostrPubkey.length === 0) {
|
|
|
|
nostrPubkey = null
|
2023-02-01 15:54:08 +00:00
|
|
|
} else {
|
2023-02-08 19:38:04 +00:00
|
|
|
if (NOSTR_PUBKEY_BECH32.test(nostrPubkey)) {
|
2023-02-01 15:54:08 +00:00
|
|
|
const { words } = bech32.decode(nostrPubkey)
|
|
|
|
nostrPubkey = Buffer.from(bech32.fromWords(words)).toString('hex')
|
|
|
|
}
|
2023-01-07 00:53:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const nostrRelaysFiltered = nostrRelays?.filter(word => word.trim().length > 0)
|
|
|
|
|
|
|
|
await setSettings({
|
|
|
|
variables: {
|
|
|
|
tipDefault: Number(tipDefault),
|
|
|
|
nostrPubkey,
|
|
|
|
nostrRelays: nostrRelaysFiltered,
|
|
|
|
...values
|
|
|
|
}
|
|
|
|
})
|
2022-06-02 22:55:23 +00:00
|
|
|
setSuccess('settings saved')
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
{success && <Alert variant='info' onClose={() => setSuccess(undefined)} dismissible>{success}</Alert>}
|
|
|
|
<Input
|
|
|
|
label='tip default'
|
|
|
|
name='tipDefault'
|
2022-12-09 19:25:38 +00:00
|
|
|
groupClassName='mb-0'
|
2022-06-02 22:55:23 +00:00
|
|
|
required
|
|
|
|
autoFocus
|
|
|
|
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
|
2022-12-09 19:25:38 +00:00
|
|
|
hint={<small className='text-muted'>note: you can also press and hold the lightning bolt to tip custom amounts</small>}
|
2022-06-02 22:55:23 +00:00
|
|
|
/>
|
2022-12-09 19:25:38 +00:00
|
|
|
<div className='mb-2'>
|
|
|
|
<AccordianItem
|
|
|
|
show={settings?.turboTipping}
|
|
|
|
header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>advanced</div>}
|
|
|
|
body={<Checkbox
|
|
|
|
name='turboTipping'
|
|
|
|
label={
|
|
|
|
<div className='d-flex align-items-center'>turbo tipping
|
|
|
|
<Info>
|
|
|
|
<ul className='font-weight-bold'>
|
|
|
|
<li>Makes every additional bolt click raise your total tip to another 10x multiple of your default tip</li>
|
|
|
|
<li>e.g. if your tip default is 10 sats
|
|
|
|
<ul>
|
|
|
|
<li>1st click: 10 sats total tipped</li>
|
|
|
|
<li>2nd click: 100 sats total tipped</li>
|
|
|
|
<li>3rd click: 1000 sats total tipped</li>
|
|
|
|
<li>4th click: 10000 sats total tipped</li>
|
|
|
|
<li>and so on ...</li>
|
|
|
|
</ul>
|
|
|
|
</li>
|
|
|
|
<li>You can still custom tip via long press
|
|
|
|
<ul>
|
|
|
|
<li>the next bolt click rounds up to the next greatest 10x multiple of your default</li>
|
|
|
|
</ul>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</Info>
|
|
|
|
</div>
|
|
|
|
}
|
|
|
|
/>}
|
|
|
|
/>
|
|
|
|
</div>
|
2022-10-04 21:21:42 +00:00
|
|
|
<Select
|
2022-09-12 23:18:23 +00:00
|
|
|
label='fiat currency'
|
|
|
|
name='fiatCurrency'
|
2022-10-04 21:21:42 +00:00
|
|
|
size='sm'
|
2023-02-08 19:38:04 +00:00
|
|
|
items={SUPPORTED_CURRENCIES}
|
2022-09-18 01:45:21 +00:00
|
|
|
required
|
2022-09-12 23:18:23 +00:00
|
|
|
/>
|
2022-06-02 22:55:23 +00:00
|
|
|
<div className='form-label'>notify me when ...</div>
|
|
|
|
<Checkbox
|
|
|
|
label='I stack sats from posts and comments'
|
|
|
|
name='noteItemSats'
|
|
|
|
groupClassName='mb-0'
|
|
|
|
/>
|
|
|
|
<Checkbox
|
|
|
|
label='I get a daily airdrop'
|
|
|
|
name='noteEarning'
|
|
|
|
groupClassName='mb-0'
|
|
|
|
/>
|
|
|
|
<Checkbox
|
|
|
|
label='someone replies to someone who replied to me'
|
|
|
|
name='noteAllDescendants'
|
|
|
|
groupClassName='mb-0'
|
|
|
|
/>
|
|
|
|
<Checkbox
|
2022-12-19 22:27:52 +00:00
|
|
|
label='someone joins using my invite or referral links'
|
2022-06-02 22:55:23 +00:00
|
|
|
name='noteInvites'
|
|
|
|
groupClassName='mb-0'
|
|
|
|
/>
|
|
|
|
<Checkbox
|
|
|
|
label='sats are deposited in my account'
|
|
|
|
name='noteDeposits'
|
|
|
|
groupClassName='mb-0'
|
|
|
|
/>
|
|
|
|
<Checkbox
|
|
|
|
label='someone mentions me'
|
|
|
|
name='noteMentions'
|
|
|
|
groupClassName='mb-0'
|
|
|
|
/>
|
|
|
|
<Checkbox
|
|
|
|
label='there is a new job'
|
|
|
|
name='noteJobIndicator'
|
2023-02-01 14:44:35 +00:00
|
|
|
groupClassName='mb-0'
|
|
|
|
/>
|
|
|
|
<Checkbox
|
|
|
|
label='I find or lose a cowboy hat'
|
|
|
|
name='noteCowboyHat'
|
2022-06-02 22:55:23 +00:00
|
|
|
/>
|
2022-08-30 21:50:47 +00:00
|
|
|
<div className='form-label'>privacy</div>
|
|
|
|
<Checkbox
|
|
|
|
label={
|
2022-09-21 19:57:36 +00:00
|
|
|
<div className='d-flex align-items-center'>hide invoice descriptions
|
2022-08-30 21:50:47 +00:00
|
|
|
<Info>
|
|
|
|
<ul className='font-weight-bold'>
|
|
|
|
<li>Use this if you don't want funding sources to be linkable to your SN identity.</li>
|
|
|
|
<li>It makes your invoice descriptions blank.</li>
|
2022-09-02 16:58:16 +00:00
|
|
|
<li>This only applies to invoices you create
|
2022-08-30 21:50:47 +00:00
|
|
|
<ul>
|
2022-09-02 16:58:16 +00:00
|
|
|
<li>lnurl-pay and lightning addresses still reference your nym</li>
|
2022-08-30 21:50:47 +00:00
|
|
|
</ul>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</Info>
|
2022-09-21 19:57:36 +00:00
|
|
|
</div>
|
2022-08-30 21:50:47 +00:00
|
|
|
}
|
|
|
|
name='hideInvoiceDesc'
|
2022-12-01 21:31:04 +00:00
|
|
|
groupClassName='mb-0'
|
|
|
|
/>
|
|
|
|
<Checkbox
|
|
|
|
label={<>hide me from <Link href='/top/users' passHref><a>top users</a></Link></>}
|
|
|
|
name='hideFromTopUsers'
|
2022-08-30 21:50:47 +00:00
|
|
|
/>
|
2022-09-21 19:57:36 +00:00
|
|
|
<div className='form-label'>content</div>
|
|
|
|
<Checkbox
|
|
|
|
label={
|
|
|
|
<div className='d-flex align-items-center'>wild west mode
|
|
|
|
<Info>
|
|
|
|
<ul className='font-weight-bold'>
|
2022-09-27 21:19:15 +00:00
|
|
|
<li>don't hide flagged content</li>
|
|
|
|
<li>don't down rank flagged content</li>
|
2022-09-21 19:57:36 +00:00
|
|
|
</ul>
|
|
|
|
</Info>
|
|
|
|
</div>
|
|
|
|
}
|
|
|
|
name='wildWestMode'
|
2022-09-27 21:19:15 +00:00
|
|
|
groupClassName='mb-0'
|
|
|
|
/>
|
|
|
|
<Checkbox
|
|
|
|
label={
|
|
|
|
<div className='d-flex align-items-center'>greeter mode
|
|
|
|
<Info>
|
|
|
|
<ul className='font-weight-bold'>
|
|
|
|
<li>see and screen free posts and comments</li>
|
|
|
|
<li>help onboard users to SN and Lightning</li>
|
|
|
|
<li>you might be subject to more spam</li>
|
|
|
|
</ul>
|
|
|
|
</Info>
|
|
|
|
</div>
|
|
|
|
}
|
|
|
|
name='greeterMode'
|
2022-09-21 19:57:36 +00:00
|
|
|
/>
|
2023-01-07 00:53:09 +00:00
|
|
|
<AccordianItem
|
|
|
|
headerColor='var(--theme-color)'
|
|
|
|
show={settings?.nostrPubkey}
|
|
|
|
header={<h4 className='mb-2 text-left'>nostr <small><a href='https://github.com/nostr-protocol/nips/blob/master/05.md' target='_blank' rel='noreferrer'>NIP-05</a></small></h4>}
|
|
|
|
body={
|
|
|
|
<>
|
|
|
|
<Input
|
|
|
|
label={<>pubkey <small className='text-muted ml-2'>optional</small></>}
|
|
|
|
name='nostrPubkey'
|
|
|
|
clear
|
|
|
|
/>
|
|
|
|
<VariableInput
|
|
|
|
label={<>relays <small className='text-muted ml-2'>optional</small></>}
|
|
|
|
name='nostrRelays'
|
|
|
|
clear
|
|
|
|
min={0}
|
2023-02-08 19:38:04 +00:00
|
|
|
max={NOSTR_MAX_RELAY_NUM}
|
2023-01-07 00:53:09 +00:00
|
|
|
/>
|
|
|
|
</>
|
|
|
|
}
|
|
|
|
/>
|
2022-06-02 22:55:23 +00:00
|
|
|
<div className='d-flex'>
|
|
|
|
<SubmitButton variant='info' className='ml-auto mt-1 px-4'>save</SubmitButton>
|
|
|
|
</div>
|
|
|
|
</Form>
|
|
|
|
<div className='text-left w-100'>
|
|
|
|
<div className='form-label'>saturday newsletter</div>
|
|
|
|
<Button href='https://mail.stacker.news/subscription/form' target='_blank'>(re)subscribe</Button>
|
|
|
|
{settings?.authMethods && <AuthMethods methods={settings.authMethods} />}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</LayoutCenter>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
function AuthMethods ({ methods }) {
|
|
|
|
const router = useRouter()
|
|
|
|
const [unlinkAuth] = useMutation(
|
|
|
|
gql`
|
|
|
|
mutation unlinkAuth($authType: String!) {
|
|
|
|
unlinkAuth(authType: $authType) {
|
|
|
|
lightning
|
|
|
|
email
|
|
|
|
twitter
|
|
|
|
github
|
|
|
|
}
|
|
|
|
}`, {
|
|
|
|
update (cache, { data: { unlinkAuth } }) {
|
|
|
|
cache.modify({
|
|
|
|
id: 'ROOT_QUERY',
|
|
|
|
fields: {
|
|
|
|
settings (existing) {
|
|
|
|
return { ...existing, authMethods: { ...unlinkAuth } }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
const [obstacle, setObstacle] = useState()
|
|
|
|
|
2023-01-18 18:49:20 +00:00
|
|
|
const providers = Object.keys(methods).filter(k => k !== '__typename')
|
|
|
|
|
2022-06-02 22:55:23 +00:00
|
|
|
const unlink = async type => {
|
|
|
|
// if there's only one auth method left
|
2023-01-18 18:49:20 +00:00
|
|
|
const links = providers.reduce((t, p) => t + (methods[p] ? 1 : 0), 0)
|
2022-06-02 22:55:23 +00:00
|
|
|
if (links === 1) {
|
|
|
|
setObstacle(type)
|
|
|
|
} else {
|
|
|
|
await unlinkAuth({ variables: { authType: type } })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<Modal
|
|
|
|
show={obstacle}
|
|
|
|
onHide={() => setObstacle(null)}
|
2021-10-30 16:20:11 +00:00
|
|
|
>
|
2022-06-02 22:55:23 +00:00
|
|
|
<div className='modal-close' onClick={() => setObstacle(null)}>X</div>
|
|
|
|
<Modal.Body>
|
|
|
|
You are removing your last auth method. It is recommended you link another auth method before removing
|
|
|
|
your last auth method. If you'd like to proceed anyway, type the following below
|
|
|
|
<div className='text-danger font-weight-bold my-2'>
|
|
|
|
If I logout, even accidentally, I will never be able to access my account again
|
|
|
|
</div>
|
|
|
|
<Form
|
|
|
|
className='mt-3'
|
|
|
|
initial={{
|
|
|
|
warning: ''
|
|
|
|
}}
|
2023-02-08 19:38:04 +00:00
|
|
|
schema={lastAuthRemovalSchema}
|
2022-06-02 22:55:23 +00:00
|
|
|
onSubmit={async () => {
|
|
|
|
await unlinkAuth({ variables: { authType: obstacle } })
|
|
|
|
router.push('/settings')
|
|
|
|
setObstacle(null)
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Input
|
|
|
|
name='warning'
|
|
|
|
required
|
|
|
|
/>
|
|
|
|
<SubmitButton className='d-flex ml-auto' variant='danger'>do it</SubmitButton>
|
|
|
|
</Form>
|
|
|
|
</Modal.Body>
|
|
|
|
</Modal>
|
|
|
|
<div className='form-label mt-3'>auth methods</div>
|
2023-01-18 18:49:20 +00:00
|
|
|
{providers && providers.map(provider => {
|
|
|
|
switch (provider) {
|
|
|
|
case 'email':
|
|
|
|
return methods.email
|
|
|
|
? (
|
|
|
|
<div className='mt-2 d-flex align-items-center'>
|
|
|
|
<Input
|
|
|
|
name='email'
|
|
|
|
placeholder={methods.email}
|
|
|
|
groupClassName='mb-0'
|
|
|
|
readOnly
|
|
|
|
noForm
|
|
|
|
/>
|
|
|
|
<Button
|
|
|
|
className='ml-2' variant='secondary' onClick={
|
|
|
|
async () => {
|
|
|
|
await unlink('email')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
>Unlink Email
|
|
|
|
</Button>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
: <div className='mt-2'><EmailLinkForm /></div>
|
|
|
|
case 'lightning':
|
|
|
|
return methods.lightning
|
|
|
|
? <LoginButton
|
|
|
|
className='d-block' type='lightning' text='Unlink' onClick={
|
|
|
|
async () => {
|
|
|
|
await unlink('lightning')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
: (
|
|
|
|
<ModalButton clicker={<LoginButton className='d-block' type='lightning' text='Link' />}>
|
|
|
|
<div className='d-flex flex-column align-items-center'>
|
|
|
|
<LightningAuth />
|
|
|
|
</div>
|
|
|
|
</ModalButton>)
|
|
|
|
case 'slashtags':
|
|
|
|
return methods.slashtags
|
|
|
|
? <LoginButton
|
|
|
|
className='d-block mt-2' type='slashtags' text='Unlink' onClick={
|
|
|
|
async () => {
|
|
|
|
await unlink('slashtags')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
: (
|
|
|
|
<ModalButton clicker={<LoginButton className='d-block mt-2' type='slashtags' text='Link' />}>
|
|
|
|
<div className='d-flex flex-column align-items-center'>
|
|
|
|
<SlashtagsAuth />
|
|
|
|
</div>
|
|
|
|
</ModalButton>)
|
|
|
|
default:
|
|
|
|
return (
|
|
|
|
<LoginButton
|
|
|
|
className='mt-2 d-block'
|
|
|
|
key={provider}
|
|
|
|
type={provider.toLowerCase()}
|
|
|
|
onClick={async () => {
|
|
|
|
if (methods[provider]) {
|
|
|
|
await unlink(provider)
|
|
|
|
} else {
|
|
|
|
signIn(provider)
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
text={methods[provider] ? 'Unlink' : 'Link'}
|
|
|
|
/>
|
|
|
|
)
|
2022-06-02 22:55:23 +00:00
|
|
|
}
|
2023-01-18 18:49:20 +00:00
|
|
|
})}
|
2022-06-02 22:55:23 +00:00
|
|
|
</>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export function EmailLinkForm ({ callbackUrl }) {
|
|
|
|
const [linkUnverifiedEmail] = useMutation(
|
|
|
|
gql`
|
|
|
|
mutation linkUnverifiedEmail($email: String!) {
|
|
|
|
linkUnverifiedEmail(email: $email)
|
|
|
|
}`
|
|
|
|
)
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Form
|
|
|
|
initial={{
|
|
|
|
email: ''
|
|
|
|
}}
|
2023-02-08 19:38:04 +00:00
|
|
|
schema={emailSchema}
|
2022-06-02 22:55:23 +00:00
|
|
|
onSubmit={async ({ email }) => {
|
|
|
|
// add email to user's account
|
|
|
|
// then call signIn
|
|
|
|
const { data } = await linkUnverifiedEmail({ variables: { email } })
|
|
|
|
if (data.linkUnverifiedEmail) {
|
|
|
|
signIn('email', { email, callbackUrl })
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<div className='d-flex align-items-center'>
|
2021-10-30 16:20:11 +00:00
|
|
|
<Input
|
2022-06-02 22:55:23 +00:00
|
|
|
name='email'
|
|
|
|
placeholder='email@example.com'
|
2021-10-30 16:20:11 +00:00
|
|
|
required
|
2022-04-21 22:50:02 +00:00
|
|
|
groupClassName='mb-0'
|
|
|
|
/>
|
2022-06-02 22:55:23 +00:00
|
|
|
<SubmitButton className='ml-2' variant='secondary'>Link Email</SubmitButton>
|
|
|
|
</div>
|
|
|
|
</Form>
|
2021-10-30 16:20:11 +00:00
|
|
|
)
|
|
|
|
}
|