add ln addr + lnurl pay qr code to profile pages
This commit is contained in:
parent
4ba1227605
commit
aa4ac2ecc9
|
@ -868,7 +868,7 @@ function newTimedOrderByWeightedSats (num) {
|
|||
return `
|
||||
GROUP BY "Item".id
|
||||
ORDER BY (SUM(CASE WHEN "ItemAct".act = 'VOTE' AND "Item"."userId" <> "ItemAct"."userId" THEN users.trust ELSE 0 END)/POWER(EXTRACT(EPOCH FROM ($${num} - "Item".created_at))/3600+2, 1.3) +
|
||||
GREATEST(SUM(CASE WHEN "ItemAct".act = 'BOOST' THEN "ItemAct".sats ELSE 0 END)-1000+5, 0)/POWER(EXTRACT(EPOCH FROM ($${num} - "Item".created_at))/3600+2, 5)) DESC NULLS LAST, "Item".id DESC`
|
||||
GREATEST(SUM(CASE WHEN "ItemAct".act = 'BOOST' THEN "ItemAct".sats ELSE 0 END)-1000+5, 0)/POWER(EXTRACT(EPOCH FROM ($${num} - "Item".created_at))/3600+2, 3)) DESC NULLS LAST, "Item".id DESC`
|
||||
}
|
||||
|
||||
const TOP_ORDER_BY_SATS = 'GROUP BY "Item".id ORDER BY (SUM(CASE WHEN "ItemAct".act = \'VOTE\' AND "Item"."userId" <> "ItemAct"."userId" THEN users.trust ELSE 0 END)) DESC NULLS LAST, "Item".created_at DESC'
|
||||
|
|
|
@ -32,11 +32,7 @@ export default {
|
|||
return null
|
||||
}
|
||||
|
||||
models.user.update(
|
||||
{ where: { id: me.id }, data: { lastSeenAt: new Date() } }
|
||||
).catch(console.log)
|
||||
|
||||
return await models.user.findUnique({ where: { id: me.id } })
|
||||
return await models.user.update({ where: { id: me.id }, data: { lastSeenAt: new Date() } })
|
||||
},
|
||||
user: async (parent, { name }, { models }) => {
|
||||
return await models.user.findUnique({ where: { name } })
|
||||
|
@ -48,7 +44,9 @@ export default {
|
|||
throw new AuthenticationError('you must be logged in')
|
||||
}
|
||||
|
||||
return me.name?.toUpperCase() === name?.toUpperCase() || !(await models.user.findUnique({ where: { name } }))
|
||||
const user = await models.user.findUnique({ where: { id: me.id } })
|
||||
|
||||
return user.name?.toUpperCase() === name?.toUpperCase() || !(await models.user.findUnique({ where: { name } }))
|
||||
},
|
||||
topUsers: async (parent, { cursor, within, userType }, { models, me }) => {
|
||||
const decodedCursor = decodeCursor(cursor)
|
||||
|
@ -136,7 +134,7 @@ export default {
|
|||
} else {
|
||||
([item] = await serialize(models,
|
||||
models.$queryRaw(`${SELECT} FROM create_bio($1, $2, $3) AS "Item"`,
|
||||
`@${me.name}'s bio`, bio, Number(me.id))))
|
||||
`@${user.name}'s bio`, bio, Number(me.id))))
|
||||
}
|
||||
|
||||
await createMentions(item, models)
|
||||
|
|
|
@ -178,9 +178,11 @@ export default {
|
|||
throw new UserInputError('amount must be positive', { argumentName: 'amount' })
|
||||
}
|
||||
|
||||
const user = await models.user.findUnique({ where: { id: me.id } })
|
||||
|
||||
// set expires at to 3 hours into future
|
||||
const expiresAt = new Date(new Date().setHours(new Date().getHours() + 3))
|
||||
const description = `${amount} sats for @${me.name} on stacker.news`
|
||||
const description = `${amount} sats for @${user.name} on stacker.news`
|
||||
try {
|
||||
const invoice = await createInvoice({
|
||||
description,
|
||||
|
@ -271,10 +273,12 @@ async function createWithdrawal (parent, { invoice, maxFee }, { me, models, lnd
|
|||
|
||||
const msatsFee = Number(maxFee) * 1000
|
||||
|
||||
const user = await models.user.findUnique({ where: { id: me.id } })
|
||||
|
||||
// create withdrawl transactionally (id, bolt11, amount, fee)
|
||||
const [withdrawl] = await serialize(models,
|
||||
models.$queryRaw`SELECT * FROM create_withdrawl(${decoded.id}, ${invoice},
|
||||
${Number(decoded.mtokens)}, ${msatsFee}, ${me.name})`)
|
||||
${Number(decoded.mtokens)}, ${msatsFee}, ${user.name})`)
|
||||
|
||||
payViaPaymentRequest({
|
||||
lnd,
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import { useState } from 'react'
|
||||
import { Modal } from 'react-bootstrap'
|
||||
|
||||
export default function ModalButton ({ children, clicker }) {
|
||||
const [show, setShow] = useState()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
show={show}
|
||||
onHide={() => setShow(false)}
|
||||
>
|
||||
<div className='modal-close' onClick={() => setShow(false)}>X</div>
|
||||
<Modal.Body>
|
||||
{children}
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
<div className='pointer' onClick={() => setShow(true)}>{clicker}</div>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -10,9 +10,11 @@ import { gql, useApolloClient, useMutation } from '@apollo/client'
|
|||
import styles from './user-header.module.css'
|
||||
import { useMe } from './me'
|
||||
import { NAME_MUTATION, NAME_QUERY } from '../fragments/users'
|
||||
import Image from 'next/image'
|
||||
// import Image from 'next/image'
|
||||
import QRCode from 'qrcode.react'
|
||||
import LightningIcon from '../svgs/bolt.svg'
|
||||
import ModalButton from './modal-button'
|
||||
import { encodeLNUrl } from '../lib/lnurl'
|
||||
|
||||
export default function UserHeader ({ user }) {
|
||||
const [editting, setEditting] = useState(false)
|
||||
|
@ -22,7 +24,7 @@ export default function UserHeader ({ user }) {
|
|||
const [setName] = useMutation(NAME_MUTATION)
|
||||
|
||||
const isMe = me?.name === user.name
|
||||
const Satistics = () => <div className={`mb-4 ${styles.username} text-success`}>{isMe ? `${user.sats} sats \\ ` : ''}{user.stacked} stacked</div>
|
||||
const Satistics = () => <div className={`mb-2 ${styles.username} text-success`}>{isMe ? `${user.sats} sats \\ ` : ''}{user.stacked} stacked</div>
|
||||
|
||||
const UserSchema = Yup.object({
|
||||
name: Yup.string()
|
||||
|
@ -40,14 +42,16 @@ export default function UserHeader ({ user }) {
|
|||
})
|
||||
})
|
||||
|
||||
const lnurlp = encodeLNUrl(new URL(`https://stacker.news/.well-known/lnurlp/${user.name}`))
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='d-flex align-items-center mt-2 flex-wrap'>
|
||||
<Image
|
||||
src='/dorian400.jpg' width='200' height='166' layout='fixed'
|
||||
{/* <Image
|
||||
src='/dorian400.jpg' width='135' height='135' layout='fixed'
|
||||
className={styles.userimg}
|
||||
/>
|
||||
<div className='ml-3'>
|
||||
/> */}
|
||||
<div>
|
||||
{editting
|
||||
? (
|
||||
<Form
|
||||
|
@ -85,7 +89,7 @@ export default function UserHeader ({ user }) {
|
|||
setEditting(false)
|
||||
}}
|
||||
>
|
||||
<div className='d-flex align-items-center mb-1'>
|
||||
<div className='d-flex align-items-center mb-2'>
|
||||
<Input
|
||||
prepend=<InputGroup.Text>@</InputGroup.Text>
|
||||
name='name'
|
||||
|
@ -98,13 +102,15 @@ export default function UserHeader ({ user }) {
|
|||
</Form>
|
||||
)
|
||||
: (
|
||||
<div className='d-flex align-items-center mb-1'>
|
||||
<div className='d-flex align-items-center mb-2'>
|
||||
<div className={styles.username}>@{user.name}</div>
|
||||
{isMe &&
|
||||
<Button className='py-0' variant='link' onClick={() => setEditting(true)}>edit nym</Button>}
|
||||
</div>
|
||||
)}
|
||||
<Satistics user={user} />
|
||||
<ModalButton
|
||||
clicker={
|
||||
<Button className='font-weight-bold'>
|
||||
<LightningIcon
|
||||
width={20}
|
||||
|
@ -112,8 +118,14 @@ export default function UserHeader ({ user }) {
|
|||
className='mr-1'
|
||||
/>{user.name}@stacker.news
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<a className='d-flex m-auto p-3' style={{ background: 'white', width: 'fit-content' }} href={`lightning:${lnurlp}`}>
|
||||
<QRCode className='d-flex m-auto' value={lnurlp} renderAs='svg' size={300} />
|
||||
</a>
|
||||
<div className='text-center font-weight-bold text-muted mt-3'>click or scan</div>
|
||||
</ModalButton>
|
||||
</div>
|
||||
<QRCode className='ml-auto' value='fdsajfkldsajlkfjdlksajfkldjsalkjfdklsa' renderAs='svg' size={166} />
|
||||
</div>
|
||||
<Nav
|
||||
className={styles.nav}
|
||||
|
|
16
lib/lnurl.js
16
lib/lnurl.js
|
@ -1,19 +1,9 @@
|
|||
import { randomBytes, createHash } from 'crypto'
|
||||
import { createHash } from 'crypto'
|
||||
import { bech32 } from 'bech32'
|
||||
|
||||
export function lnurlAuth (params) {
|
||||
// generate secret (32 random bytes)
|
||||
const secret = Buffer.from(randomBytes(32), 'hex')
|
||||
// create url
|
||||
const url = new URL(process.env.LNAUTH_URL)
|
||||
url.searchParams = new URLSearchParams({
|
||||
...params,
|
||||
k1: secret
|
||||
})
|
||||
// bech32 encode url
|
||||
export function encodeLNUrl (url) {
|
||||
const words = bech32.toWords(Buffer.from(url.toString(), 'utf8'))
|
||||
const encodedUrl = bech32.encode('lnurl', words, 1023)
|
||||
return { secret, encodedUrl }
|
||||
return bech32.encode('lnurl', words, 1023)
|
||||
}
|
||||
|
||||
export function lnurlPayMetadataString (username) {
|
||||
|
|
|
@ -22,13 +22,6 @@ const options = {
|
|||
token.id = user.id
|
||||
}
|
||||
|
||||
// XXX We need to update the user name incase they update it ... kind of hacky
|
||||
// better if we use user id everywhere an ignore the username ...
|
||||
if (token?.id) {
|
||||
const { name } = await prisma.user.findUnique({ where: { id: token.id } })
|
||||
token.name = name
|
||||
}
|
||||
|
||||
// sign them up for the newsletter
|
||||
if (isNewUser && profile.email) {
|
||||
fetch(process.env.LIST_MONK_URL + '/api/subscribers', {
|
||||
|
@ -52,7 +45,6 @@ const options = {
|
|||
async session (session, token) {
|
||||
// we need to add additional session params here
|
||||
session.user.id = token.id
|
||||
session.user.name = token.name
|
||||
return session
|
||||
}
|
||||
},
|
||||
|
|
|
@ -38,7 +38,7 @@ const apolloServer = new ApolloServer({
|
|||
models,
|
||||
lnd,
|
||||
me: session
|
||||
? await models.user.findUnique({ where: { id: session.user?.id } })
|
||||
? session.user
|
||||
: null,
|
||||
search
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue