Created
March 13, 2022 19:29
-
-
Save john-zaprite/a4f7a18ee3b191853e6af2358bc9c787 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Box, | |
Button, | |
Heading, | |
HStack, | |
Image, | |
Input, | |
Spinner, | |
Stack, | |
Text, | |
useColorModeValue as mode, | |
} from '@chakra-ui/react' | |
import Link from 'next/link' | |
import QRCode from 'qrcode' | |
import { useEffect, useState } from 'react' | |
import { FaQrcode } from 'react-icons/fa' | |
import AppMain from '../layouts/AppMain' | |
import useSWR from 'swr' | |
import ClipboardCopyIcon from '../hooks/useClipboard copy' | |
import { useRouter } from 'next/router' | |
const Login = () => { | |
// ---------------- | |
// STATE | |
// ---------------- | |
const [isLoading, setIsLoading] = useState(false) | |
const [isGeneratingQr, setIsGeneratingQr] = useState(false) | |
const [isAuthenticating, setIsAuthenticating] = useState(false) | |
const [qrPng, setQrPng] = useState(null) | |
const [paymentLink, setPaymentLink] = useState(null) | |
const [challenge, setChallenge] = useState(null) | |
// ---------------- | |
// HANDLERS | |
// ---------------- | |
const handleLNurlAuthLogin = async () => { | |
setIsLoading(true) | |
try { | |
const { success, data } = await ( | |
await fetch('/api/auth/get-lnurl-auth', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
}) | |
).json() | |
if (success) { | |
const lnurl = data.lnurl | |
const challenge = data.challenge | |
generateQRCode(lnurl) | |
setPaymentLink(lnurl) | |
setChallenge(challenge) | |
setIsAuthenticating(true) | |
} | |
} catch { | |
console.error('LN-URL auth error.') | |
} finally { | |
setIsLoading(false) | |
} | |
} | |
const generateQRCode = async (string) => { | |
const qrUrl = `lightning:${string}` | |
setIsGeneratingQr(true) | |
const opts = { | |
errorCorrectionLevel: 'H', | |
type: 'image/webp', | |
quality: 0.75, | |
color: { | |
dark: '#0e0910', | |
light: '#ffffff', | |
}, | |
} | |
try { | |
const dataImg = await QRCode.toDataURL(string, opts).then( | |
(dataImg) => setQrPng(dataImg) | |
) | |
} catch (error) { | |
console.error('QR Code did not generate.', error) | |
} finally { | |
setIsGeneratingQr(false) | |
} | |
} | |
return ( | |
<AppMain> | |
<Stack | |
sx={{ | |
m: 'auto', | |
width: '80vw', | |
maxW: '24em', | |
spacing: '8', | |
}}> | |
<Stack | |
sx={{ | |
spacing: '6', | |
align: 'center', | |
}}> | |
<Heading | |
// size={useBreakpointValue({ base: 'xs', md: 'sm' })} | |
> | |
Log in to your account | |
</Heading> | |
</Stack> | |
<Stack | |
sx={{ | |
p: 6, | |
spacing: '6', | |
borderRadius: 2, | |
bg: mode('#ffffff', 'gray.700'), | |
}}> | |
<Box sx={{ width: '100%', aspectRatio: '1' }}> | |
{isGeneratingQr && <Spinner />} | |
{!isAuthenticating && ( | |
<Image | |
src={'/images/lnurl_placeholder@2x.png'} | |
alt={''} | |
width={'100%'} | |
/> | |
)} | |
{isAuthenticating && qrPng && ( | |
<Authenticate | |
challenge={challenge} | |
paymentLink={paymentLink} | |
qrPng={qrPng} | |
/> | |
)} | |
</Box> | |
<Button | |
isLoading={isLoading} | |
loadingText={'Authenticating...'} | |
rightIcon={<FaQrcode />} | |
colorScheme='primary' | |
sx={{ minWidth: '10em' }} | |
onClick={handleLNurlAuthLogin} | |
disabled={isAuthenticating}> | |
Authenticate | |
</Button> | |
</Stack> | |
<Box> | |
<Text> | |
Some info? Link to list of supported wallets? Icons? | |
</Text> | |
</Box> | |
</Stack> | |
</AppMain> | |
) | |
} | |
export default Login | |
const Authenticate = ({ challenge, paymentLink, qrPng }) => { | |
const router = useRouter() | |
// ---------------- | |
// DATA | |
// ---------------- | |
const { data, error } = useSWR( | |
`/api/auth/check-challenge/?challenge=${challenge}`, | |
(url) => fetch(url).then((res) => res.json()), | |
{ refreshInterval: 5000, shouldRetryOnError: false } | |
) | |
if (error) console.error('Challenge failed.', error) | |
// ---------------- | |
// EFFECTS | |
// ---------------- | |
useEffect(() => { | |
if (data) { | |
const response = data.data | |
if (data) { | |
// const status = response?.status || null | |
const sessionId = response?.data?.session_key || null | |
const userId = response?.data?.user_id || null | |
const date = new Date(response?.data?.timestamp) || new Date() | |
const expireMs = 14 * 24 * 60 * 60 * 1000 | |
date.setTime(date.getTime() + expireMs) | |
localStorage.setItem('user_id', userId) | |
document.cookie = `SESSION_ID=${sessionId}; expires=${date.toUTCString()}; SameSite=Strict; path=/` | |
if (userId && sessionId) { | |
router.push('/dashboard') | |
} | |
// TODO | |
// run function that creates a user (?) | |
} | |
} | |
}, [data]) | |
return ( | |
<Box> | |
<Link href={`lightning:${paymentLink}`} passHref> | |
<a> | |
<Image src={qrPng} alt='ln-url auth' /> | |
</a> | |
</Link> | |
<HStack mt={2} spacing='4'> | |
<Input | |
placeholder={'LN-URL auth ...'} | |
value={paymentLink} | |
readOnly | |
isTruncated | |
/> | |
<ClipboardCopyIcon value={paymentLink} /> | |
</HStack> | |
</Box> | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment