ssr me and price

This commit is contained in:
keyan 2021-11-28 11:29:17 -06:00
parent 996d0a9352
commit c7ae5dc8ac
7 changed files with 95 additions and 65 deletions

View File

@ -7,6 +7,8 @@ import typeDefs from './typeDefs'
import models from './models' import models from './models'
import { print } from 'graphql' import { print } from 'graphql'
import lnd from './lnd' import lnd from './lnd'
import { ME } from '../fragments/users'
import { getPrice } from '../components/price'
export default async function getSSRApolloClient (req, me = null) { export default async function getSSRApolloClient (req, me = null) {
const session = req && await getSession({ req }) const session = req && await getSession({ req })
@ -41,12 +43,20 @@ export function getGetServerSideProps (query, variables = null, foundField) {
} }
} }
const { data: { me } } = await client.query({
query: ME
})
const price = await getPrice()
return { return {
props: { props: {
apollo: { apollo: {
query: print(query), query: print(query),
variables: { ...params, ...variables } variables: { ...params, ...variables }
}, },
me,
price,
data data
} }
} }

View File

@ -7,7 +7,7 @@ import { Button, Container, NavDropdown, SplitButton, Dropdown } from 'react-boo
import Price from './price' import Price from './price'
import { useMe } from './me' import { useMe } from './me'
import Head from 'next/head' import Head from 'next/head'
import { signOut, signIn, useSession } from 'next-auth/client' import { signOut, signIn } from 'next-auth/client'
import { useLightning } from './lightning' import { useLightning } from './lightning'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { randInRange } from '../lib/rand' import { randInRange } from '../lib/rand'
@ -30,10 +30,8 @@ export default function Header () {
const router = useRouter() const router = useRouter()
const path = router.asPath.split('?')[0] const path = router.asPath.split('?')[0]
const me = useMe() const me = useMe()
const [session, loading] = useSession()
const [sort, setSort] = useState('recent') const [sort, setSort] = useState('recent')
const [within, setWithin] = useState() const [within, setWithin] = useState()
const [priceReady, setPriceReady] = useState()
useEffect(() => { useEffect(() => {
setSort(localStorage.getItem('sort') || 'recent') setSort(localStorage.getItem('sort') || 'recent')
@ -127,9 +125,6 @@ export default function Header () {
</div> </div>
) )
} else { } else {
if (loading || session) {
return null
}
const strike = useLightning() const strike = useLightning()
useEffect(() => { useEffect(() => {
setTimeout(strike, randInRange(3000, 10000)) setTimeout(strike, randInRange(3000, 10000))
@ -138,8 +133,6 @@ export default function Header () {
} }
} }
const visible = ((session && me) || (!session && !loading)) && priceReady ? 'visible' : 'invisible'
return ( return (
<> <>
<Container className='px-sm-0'> <Container className='px-sm-0'>
@ -154,7 +147,7 @@ export default function Header () {
<Link href='/' passHref> <Link href='/' passHref>
<Navbar.Brand className={`${styles.brand} d-block d-sm-none`}>SN</Navbar.Brand> <Navbar.Brand className={`${styles.brand} d-block d-sm-none`}>SN</Navbar.Brand>
</Link> </Link>
<Nav.Item className={`d-md-flex d-none nav-dropdown-toggle ${visible}`}> <Nav.Item className='d-md-flex d-none nav-dropdown-toggle'>
<SplitButton <SplitButton
title={ title={
<Link href={sortLink} passHref> <Link href={sortLink} passHref>
@ -171,7 +164,7 @@ export default function Header () {
</Link> </Link>
</SplitButton> </SplitButton>
</Nav.Item> </Nav.Item>
<Nav.Item className={`d-md-flex d-none ${visible}`}> <Nav.Item className='d-md-flex d-none'>
{me {me
? ( ? (
<Link href='/post' passHref> <Link href='/post' passHref>
@ -180,15 +173,13 @@ export default function Header () {
) )
: <Nav.Link className={styles.navLink} onClick={signIn}>post</Nav.Link>} : <Nav.Link className={styles.navLink} onClick={signIn}>post</Nav.Link>}
</Nav.Item> </Nav.Item>
<Nav.Item className={`d-md-flex d-none ${visible}`}> <Nav.Item className='d-md-flex d-none'>
<Nav.Link href='https://bitcoinerjobs.co' target='_blank' className={styles.navLink}>jobs</Nav.Link> <Nav.Link href='https://bitcoinerjobs.co' target='_blank' className={styles.navLink}>jobs</Nav.Link>
</Nav.Item> </Nav.Item>
<Nav.Item className={`text-monospace ${visible}`} style={{ opacity: '.5' }}> <Nav.Item className='text-monospace' style={{ opacity: '.5' }}>
<Price onReady={() => setPriceReady(true)} /> <Price />
</Nav.Item> </Nav.Item>
<div className={visible}>
<Corner /> <Corner />
</div>
</Nav> </Nav>
</Navbar> </Navbar>
</Container> </Container>

View File

@ -1,33 +1,16 @@
import React, { useContext } from 'react' import React, { useContext } from 'react'
import { gql, useQuery } from '@apollo/client' import { useQuery } from '@apollo/client'
import { ME } from '../fragments/users'
export const MeContext = React.createContext({ export const MeContext = React.createContext({
me: null me: null
}) })
export function MeProvider ({ children }) { export function MeProvider ({ me, children }) {
const query = gql` const { data } = useQuery(ME, { pollInterval: 1000 })
{
me {
id
name
sats
stacked
freePosts
freeComments
hasNewNotes
tipDefault
bio {
id
}
hasInvites
theme
}
}`
const { data } = useQuery(query, { pollInterval: 1000 })
const contextValue = { const contextValue = {
me: data ? data.me : null me: data ? data.me : me
} }
return ( return (

View File

@ -1,29 +1,53 @@
import { useEffect, useState } from 'react' import React, { useContext, useEffect, useState } from 'react'
import { Button } from 'react-bootstrap' import { Button } from 'react-bootstrap'
import useSWR from 'swr' import useSWR from 'swr'
const fetcher = url => fetch(url).then(res => res.json()) const fetcher = url => fetch(url).then(res => res.json())
export default function Price ({ onReady }) { export const PriceContext = React.createContext({
const [asSats, setAsSats] = useState(undefined) price: null
useEffect(() => { })
setAsSats(localStorage.getItem('asSats'))
}, [])
const ENDPOINT = 'https://api.coinbase.com/v2/prices/BTC-USD/spot'
export async function getPrice () {
const data = await fetcher(ENDPOINT)
return data?.data?.amount
}
export function PriceProvider ({ price, children }) {
const { data } = useSWR( const { data } = useSWR(
'https://api.coinbase.com/v2/prices/BTC-USD/spot', ENDPOINT,
fetcher, fetcher,
{ {
refreshInterval: 30000 refreshInterval: 30000
}) })
useEffect(() => { const contextValue = {
if (onReady) { price: data?.data?.amount || price
onReady()
} }
}, [data])
if (!data || !data.data) return null return (
<PriceContext.Provider value={contextValue}>
{children}
</PriceContext.Provider>
)
}
export function usePrice () {
const { price } = useContext(PriceContext)
return price
}
export default function Price () {
const [asSats, setAsSats] = useState(undefined)
useEffect(() => {
setAsSats(localStorage.getItem('asSats'))
}, [])
const price = usePrice()
if (!price) return null
const fixed = (n, f) => Number.parseFloat(n).toFixed(f) const fixed = (n, f) => Number.parseFloat(n).toFixed(f)
const handleClick = () => { const handleClick = () => {
@ -39,14 +63,14 @@ export default function Price ({ onReady }) {
if (asSats) { if (asSats) {
return ( return (
<Button className='text-reset px-1 py-0' onClick={handleClick} variant='link'> <Button className='text-reset px-1 py-0' onClick={handleClick} variant='link'>
{fixed(100000000 / data.data.amount, 0) + ' sats/$'} {fixed(100000000 / price, 0) + ' sats/$'}
</Button> </Button>
) )
} }
return ( return (
<Button className='text-reset px-1 py-0' onClick={handleClick} variant='link'> <Button className='text-reset px-1 py-0' onClick={handleClick} variant='link'>
{'$' + fixed(data.data.amount, 2)} {'$' + fixed(price, 2)}
</Button> </Button>
) )
} }

View File

@ -2,6 +2,25 @@ import { gql } from '@apollo/client'
import { COMMENT_FIELDS } from './comments' import { COMMENT_FIELDS } from './comments'
import { ITEM_FIELDS, ITEM_WITH_COMMENTS } from './items' import { ITEM_FIELDS, ITEM_WITH_COMMENTS } from './items'
export const ME = gql`
{
me {
id
name
sats
stacked
freePosts
freeComments
hasNewNotes
tipDefault
bio {
id
}
hasInvites
theme
}
}`
export const USER_FIELDS = gql` export const USER_FIELDS = gql`
${ITEM_FIELDS} ${ITEM_FIELDS}
fragment UserFields on User { fragment UserFields on User {

View File

@ -8,6 +8,7 @@ import { LightningProvider } from '../components/lightning'
import { ItemActModal, ItemActProvider } from '../components/item-act' 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'
function MyApp ({ Component, pageProps: { session, ...props } }) { function MyApp ({ Component, pageProps: { session, ...props } }) {
const client = getApolloClient() const client = getApolloClient()
@ -27,6 +28,8 @@ function MyApp ({ Component, pageProps: { session, ...props } }) {
} }
} }
const { me, price } = props
return ( return (
<> <>
<NextNProgress <NextNProgress
@ -40,7 +43,8 @@ function MyApp ({ Component, pageProps: { session, ...props } }) {
<PlausibleProvider domain='stacker.news' trackOutboundLinks> <PlausibleProvider domain='stacker.news' trackOutboundLinks>
<Provider session={session}> <Provider session={session}>
<ApolloProvider client={client}> <ApolloProvider client={client}>
<MeProvider> <MeProvider me={me}>
<PriceProvider price={price}>
<LightningProvider> <LightningProvider>
<FundErrorProvider> <FundErrorProvider>
<FundErrorModal /> <FundErrorModal />
@ -50,6 +54,7 @@ function MyApp ({ Component, pageProps: { session, ...props } }) {
</ItemActProvider> </ItemActProvider>
</FundErrorProvider> </FundErrorProvider>
</LightningProvider> </LightningProvider>
</PriceProvider>
</MeProvider> </MeProvider>
</ApolloProvider> </ApolloProvider>
</Provider> </Provider>

View File

@ -44,8 +44,6 @@ export async function getServerSideProps ({ req, res, query: { id, error = null
return { props: {} } return { props: {} }
} }
console.log(process.env.PUBLIC_URL + req.url)
return { return {
props: { props: {
providers: await providers({ req, res }), providers: await providers({ req, res }),