cache magic - use cache and network except when a result of popstate
This commit is contained in:
parent
2250ad5571
commit
91a2061342
|
@ -1,7 +1,7 @@
|
||||||
import { UserInputError, AuthenticationError } from 'apollo-server-micro'
|
import { UserInputError, AuthenticationError } from 'apollo-server-micro'
|
||||||
import { ensureProtocol } from '../../lib/url'
|
import { ensureProtocol } from '../../lib/url'
|
||||||
import serialize from './serial'
|
import serialize from './serial'
|
||||||
import { decodeCursor, LIMIT, nextCursorEncoded } from './cursor'
|
import { decodeCursor, LIMIT, nextCursorEncoded } from '../../lib/cursor'
|
||||||
import { getMetadata, metadataRuleSets } from 'page-metadata-parser'
|
import { getMetadata, metadataRuleSets } from 'page-metadata-parser'
|
||||||
import domino from 'domino'
|
import domino from 'domino'
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { AuthenticationError } from 'apollo-server-micro'
|
import { AuthenticationError } from 'apollo-server-micro'
|
||||||
import { decodeCursor, LIMIT, nextCursorEncoded } from './cursor'
|
import { decodeCursor, LIMIT, nextCursorEncoded } from '../../lib/cursor'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Query: {
|
Query: {
|
||||||
|
@ -83,7 +83,9 @@ export default {
|
||||||
})
|
})
|
||||||
|
|
||||||
const { checkedNotesAt } = await models.user.findUnique({ where: { id: me.id } })
|
const { checkedNotesAt } = await models.user.findUnique({ where: { id: me.id } })
|
||||||
await models.user.update({ where: { id: me.id }, data: { checkedNotesAt: new Date() } })
|
if (decodedCursor.offset === 0) {
|
||||||
|
await models.user.update({ where: { id: me.id }, data: { checkedNotesAt: new Date() } })
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
lastChecked: checkedNotesAt,
|
lastChecked: checkedNotesAt,
|
||||||
|
|
|
@ -8,11 +8,12 @@ import { useRouter } from 'next/router'
|
||||||
|
|
||||||
export default function CommentsFlat ({ variables, ...props }) {
|
export default function CommentsFlat ({ variables, ...props }) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { loading, error, data, fetchMore } = useQuery(MORE_FLAT_COMMENTS, {
|
const { error, data, fetchMore } = useQuery(MORE_FLAT_COMMENTS, {
|
||||||
variables
|
variables,
|
||||||
|
fetchPolicy: router.query.cache ? 'cache-first' : undefined
|
||||||
})
|
})
|
||||||
if (error) return <div>Failed to load!</div>
|
if (error) return <div>Failed to load!</div>
|
||||||
if (loading) {
|
if (!data) {
|
||||||
return <CommentsFlatSkeleton />
|
return <CommentsFlatSkeleton />
|
||||||
}
|
}
|
||||||
const { moreFlatComments: { comments, cursor } } = data
|
const { moreFlatComments: { comments, cursor } } = data
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { useQuery } from '@apollo/client'
|
import { useQuery } from '@apollo/client'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import Comment, { CommentSkeleton } from './comment'
|
import Comment, { CommentSkeleton } from './comment'
|
||||||
|
|
||||||
|
@ -21,10 +22,13 @@ export function CommentsSkeleton () {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CommentsQuery ({ query, ...props }) {
|
export function CommentsQuery ({ query, ...props }) {
|
||||||
const { loading, error, data } = useQuery(query)
|
const router = useRouter()
|
||||||
|
const { error, data } = useQuery(query, {
|
||||||
|
fetchPolicy: router.query.cache ? 'cache-first' : undefined
|
||||||
|
})
|
||||||
|
|
||||||
if (error) return <div>Failed to load!</div>
|
if (error) return <div>Failed to load!</div>
|
||||||
if (loading) {
|
if (!data) {
|
||||||
return <CommentsSkeleton />
|
return <CommentsSkeleton />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { useRouter } from 'next/router'
|
||||||
import { Button, Container, NavDropdown } from 'react-bootstrap'
|
import { Button, Container, NavDropdown } from 'react-bootstrap'
|
||||||
import Price from './price'
|
import Price from './price'
|
||||||
import { useMe } from './me'
|
import { useMe } from './me'
|
||||||
import { useApolloClient } from '@apollo/client'
|
|
||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
import { signOut, signIn, useSession } from 'next-auth/client'
|
import { signOut, signIn, useSession } from 'next-auth/client'
|
||||||
import { useLightning } from './lightning'
|
import { useLightning } from './lightning'
|
||||||
|
@ -17,12 +16,21 @@ function WalletSummary ({ me }) {
|
||||||
return `${me.sats} \\ ${me.stacked}`
|
return `${me.sats} \\ ${me.stacked}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function RefreshableLink ({ href, children, ...props }) {
|
||||||
|
const router = useRouter()
|
||||||
|
const same = router.asPath === href
|
||||||
|
return (
|
||||||
|
<Link href={same ? `${href}?key=${Math.random()}` : href} as={href} {...props}>
|
||||||
|
{children}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default function Header () {
|
export default function Header () {
|
||||||
const [session, loading] = useSession()
|
const [session, loading] = useSession()
|
||||||
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 client = useApolloClient()
|
|
||||||
|
|
||||||
const Corner = () => {
|
const Corner = () => {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
|
@ -40,27 +48,23 @@ export default function Header () {
|
||||||
<Link href={'/' + session.user.name} passHref>
|
<Link href={'/' + session.user.name} passHref>
|
||||||
<NavDropdown.Item>profile</NavDropdown.Item>
|
<NavDropdown.Item>profile</NavDropdown.Item>
|
||||||
</Link>
|
</Link>
|
||||||
<Link href='/notifications' passHref>
|
<RefreshableLink href='/notifications' passHref>
|
||||||
<NavDropdown.Item onClick={() => {
|
<NavDropdown.Item>
|
||||||
// when it's a fresh click evict old notification cache
|
|
||||||
client.cache.evict({ id: 'ROOT_QUERY', fieldName: 'notifications' })
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
notifications
|
notifications
|
||||||
{me && me.hasNewNotes &&
|
{me && me.hasNewNotes &&
|
||||||
<div className='p-1 d-inline-block bg-danger ml-1'>
|
<div className='p-1 d-inline-block bg-danger ml-1'>
|
||||||
<span className='invisible'>{' '}</span>
|
<span className='invisible'>{' '}</span>
|
||||||
</div>}
|
</div>}
|
||||||
</NavDropdown.Item>
|
</NavDropdown.Item>
|
||||||
</Link>
|
</RefreshableLink>
|
||||||
<Link href='/wallet' passHref>
|
<Link href='/wallet' passHref>
|
||||||
<NavDropdown.Item>wallet</NavDropdown.Item>
|
<NavDropdown.Item>wallet</NavDropdown.Item>
|
||||||
</Link>
|
</Link>
|
||||||
<div>
|
<div>
|
||||||
<NavDropdown.Divider />
|
<NavDropdown.Divider />
|
||||||
<Link href='/recent' passHref>
|
<RefreshableLink href='/recent' passHref>
|
||||||
<NavDropdown.Item>recent</NavDropdown.Item>
|
<NavDropdown.Item>recent</NavDropdown.Item>
|
||||||
</Link>
|
</RefreshableLink>
|
||||||
{session
|
{session
|
||||||
? (
|
? (
|
||||||
<Link href='/post' passHref>
|
<Link href='/post' passHref>
|
||||||
|
@ -100,16 +104,16 @@ export default function Header () {
|
||||||
<Container className='px-sm-0'>
|
<Container className='px-sm-0'>
|
||||||
<Navbar className={styles.navbar}>
|
<Navbar className={styles.navbar}>
|
||||||
<Nav className='w-100 justify-content-between flex-wrap align-items-center' activeKey={path}>
|
<Nav className='w-100 justify-content-between flex-wrap align-items-center' activeKey={path}>
|
||||||
<Link href='/' passHref>
|
<RefreshableLink href='/' passHref>
|
||||||
<Navbar.Brand className={`${styles.brand} d-none d-sm-block`}>STACKER NEWS</Navbar.Brand>
|
<Navbar.Brand className={`${styles.brand} d-none d-sm-block`}>STACKER NEWS</Navbar.Brand>
|
||||||
</Link>
|
</RefreshableLink>
|
||||||
<Link href='/' passHref>
|
<RefreshableLink 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>
|
</RefreshableLink>
|
||||||
<Nav.Item className='d-md-flex d-none'>
|
<Nav.Item className='d-md-flex d-none'>
|
||||||
<Link href='/recent' passHref>
|
<RefreshableLink href='/recent' passHref>
|
||||||
<Nav.Link className={styles.navLink}>recent</Nav.Link>
|
<Nav.Link className={styles.navLink}>recent</Nav.Link>
|
||||||
</Link>
|
</RefreshableLink>
|
||||||
</Nav.Item>
|
</Nav.Item>
|
||||||
<Nav.Item className='d-md-flex d-none'>
|
<Nav.Item className='d-md-flex d-none'>
|
||||||
{session
|
{session
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
color: grey;
|
color: grey;
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
|
min-width: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.skeleton .other {
|
.skeleton .other {
|
||||||
|
|
|
@ -4,13 +4,16 @@ import Item, { ItemSkeleton } from './item'
|
||||||
import styles from './items.module.css'
|
import styles from './items.module.css'
|
||||||
import { MORE_ITEMS } from '../fragments/items'
|
import { MORE_ITEMS } from '../fragments/items'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
export default function Items ({ variables, rank }) {
|
export default function Items ({ variables, rank }) {
|
||||||
const { loading, error, data, fetchMore } = useQuery(MORE_ITEMS, {
|
const router = useRouter()
|
||||||
variables
|
const { error, data, fetchMore } = useQuery(MORE_ITEMS, {
|
||||||
|
variables,
|
||||||
|
fetchPolicy: router.query.cache ? 'cache-first' : undefined
|
||||||
})
|
})
|
||||||
if (error) return <div>Failed to load!</div>
|
if (error) return <div>Failed to load!</div>
|
||||||
if (loading) {
|
if (!data) {
|
||||||
return <ItemsSkeleton rank={rank} />
|
return <ItemsSkeleton rank={rank} />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useApolloClient, useQuery } from '@apollo/client'
|
import { useQuery } from '@apollo/client'
|
||||||
import Button from 'react-bootstrap/Button'
|
import Button from 'react-bootstrap/Button'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import Comment, { CommentSkeleton } from './comment'
|
import Comment, { CommentSkeleton } from './comment'
|
||||||
|
@ -7,25 +7,22 @@ import { NOTIFICATIONS } from '../fragments/notifications'
|
||||||
import styles from './notifications.module.css'
|
import styles from './notifications.module.css'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
function Notification ({ key, n }) {
|
function Notification ({ n }) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const client = useApolloClient()
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={key}
|
|
||||||
className={styles.clickToContext}
|
className={styles.clickToContext}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (n.__typename === 'Reply' || !n.item.title) {
|
if (n.__typename === 'Reply' || !n.item.title) {
|
||||||
// evict item from cache so that it has current state
|
|
||||||
// e.g. if they previously visited before a recent comment
|
|
||||||
client.cache.evict({ id: `Item:${n.item.parentId}` })
|
|
||||||
router.push({
|
router.push({
|
||||||
pathname: '/items/[id]',
|
pathname: '/items/[id]',
|
||||||
query: { id: n.item.parentId, commentId: n.item.id }
|
query: { id: n.item.parentId, commentId: n.item.id }
|
||||||
}, `/items/${n.item.parentId}`)
|
}, `/items/${n.item.parentId}`)
|
||||||
} else {
|
} else {
|
||||||
client.cache.evict({ id: `Item:${n.item.id}` })
|
router.push({
|
||||||
router.push(`items/${n.item.id}`)
|
pathname: '/items/[id]',
|
||||||
|
query: { id: n.item.id }
|
||||||
|
}, `/items/${n.item.id}`)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -48,11 +45,13 @@ function Notification ({ key, n }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Notifications ({ variables }) {
|
export default function Notifications ({ variables }) {
|
||||||
const { loading, error, data, fetchMore } = useQuery(NOTIFICATIONS, {
|
const router = useRouter()
|
||||||
variables
|
const { error, data, fetchMore } = useQuery(NOTIFICATIONS, {
|
||||||
|
variables,
|
||||||
|
fetchPolicy: router.query.cache ? 'cache-first' : undefined
|
||||||
})
|
})
|
||||||
if (error) return <div>Failed to load!</div>
|
if (error) return <div>Failed to load!</div>
|
||||||
if (loading) {
|
if (!data) {
|
||||||
return <CommentsFlatSkeleton />
|
return <CommentsFlatSkeleton />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { ApolloClient, InMemoryCache } from '@apollo/client'
|
||||||
|
import { isFirstPage } from './cursor'
|
||||||
|
|
||||||
|
export default new ApolloClient({
|
||||||
|
uri: '/api/graphql',
|
||||||
|
cache: new InMemoryCache({
|
||||||
|
typePolicies: {
|
||||||
|
Query: {
|
||||||
|
fields: {
|
||||||
|
moreItems: {
|
||||||
|
keyArgs: ['sort', 'userId'],
|
||||||
|
merge (existing, incoming) {
|
||||||
|
if (incoming.cursor && isFirstPage(incoming.cursor)) {
|
||||||
|
return incoming
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
cursor: incoming.cursor,
|
||||||
|
items: [...(existing?.items || []), ...incoming.items]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
moreFlatComments: {
|
||||||
|
keyArgs: ['userId'],
|
||||||
|
merge (existing, incoming) {
|
||||||
|
if (incoming.cursor && isFirstPage(incoming.cursor)) {
|
||||||
|
return incoming
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
cursor: incoming.cursor,
|
||||||
|
comments: [...(existing?.comments || []), ...incoming.comments]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
notifications: {
|
||||||
|
keyArgs: false,
|
||||||
|
merge (existing, incoming) {
|
||||||
|
if (incoming.cursor && isFirstPage(incoming.cursor)) {
|
||||||
|
return incoming
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
cursor: incoming.cursor,
|
||||||
|
notifications: [...(existing?.notifications || []), ...incoming.notifications],
|
||||||
|
lastChecked: incoming.lastChecked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
defaultOptions: {
|
||||||
|
// cache-and-network allows us to refresh pages on navigation
|
||||||
|
watchQuery: {
|
||||||
|
fetchPolicy: 'cache-and-network',
|
||||||
|
nextFetchPolicy: 'cache-first'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
|
@ -14,3 +14,8 @@ export function nextCursorEncoded (cursor) {
|
||||||
cursor.offset += LIMIT
|
cursor.offset += LIMIT
|
||||||
return Buffer.from(JSON.stringify(cursor)).toString('base64')
|
return Buffer.from(JSON.stringify(cursor)).toString('base64')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isFirstPage (cursor) {
|
||||||
|
const decursor = decodeCursor(cursor)
|
||||||
|
return decursor.offset === LIMIT
|
||||||
|
}
|
|
@ -1,80 +1,31 @@
|
||||||
import '../styles/globals.scss'
|
import '../styles/globals.scss'
|
||||||
import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client'
|
import { ApolloProvider } from '@apollo/client'
|
||||||
import { Provider } from 'next-auth/client'
|
import { Provider } from 'next-auth/client'
|
||||||
import { FundErrorModal, FundErrorProvider } from '../components/fund-error'
|
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 apolloClient from '../lib/apollo'
|
||||||
const client = new ApolloClient({
|
import { useRouter } from 'next/router'
|
||||||
uri: '/api/graphql',
|
import { useEffect } from 'react'
|
||||||
cache: new InMemoryCache({
|
|
||||||
typePolicies: {
|
|
||||||
Query: {
|
|
||||||
fields: {
|
|
||||||
moreItems: {
|
|
||||||
keyArgs: ['sort', 'userId'],
|
|
||||||
merge (existing, incoming, { readField }) {
|
|
||||||
const items = existing ? existing.items : []
|
|
||||||
return {
|
|
||||||
cursor: incoming.cursor,
|
|
||||||
items: [...items, ...incoming.items]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
read (existing) {
|
|
||||||
if (existing) {
|
|
||||||
return {
|
|
||||||
cursor: existing.cursor,
|
|
||||||
items: existing.items
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
moreFlatComments: {
|
|
||||||
keyArgs: ['userId'],
|
|
||||||
merge (existing, incoming, { readField }) {
|
|
||||||
const comments = existing ? existing.comments : []
|
|
||||||
return {
|
|
||||||
cursor: incoming.cursor,
|
|
||||||
comments: [...comments, ...incoming.comments]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
read (existing) {
|
|
||||||
if (existing) {
|
|
||||||
return {
|
|
||||||
cursor: existing.cursor,
|
|
||||||
comments: existing.comments
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
notifications: {
|
|
||||||
merge (existing, incoming, { readField }) {
|
|
||||||
const notifications = existing ? existing.notifications : []
|
|
||||||
return {
|
|
||||||
cursor: incoming.cursor,
|
|
||||||
notifications: [...notifications, ...incoming.notifications],
|
|
||||||
lastChecked: incoming.lastChecked
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
read (existing) {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
function MyApp ({ Component, pageProps }) {
|
function MyApp ({ Component, pageProps }) {
|
||||||
|
const router = useRouter()
|
||||||
|
useEffect(() => {
|
||||||
|
router.beforePopState(({ url, as, options }) => {
|
||||||
|
// we need to tell the next page to use a cache-first fetch policy ...
|
||||||
|
// so that scroll position can be maintained
|
||||||
|
const fullurl = new URL(url, 'https://stacker.news')
|
||||||
|
fullurl.searchParams.set('cache', true)
|
||||||
|
router.push(`${fullurl.pathname}${fullurl.search}`, as, options)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PlausibleProvider domain='stacker.news' trackOutboundLinks>
|
<PlausibleProvider domain='stacker.news' trackOutboundLinks>
|
||||||
<Provider session={pageProps.session}>
|
<Provider session={pageProps.session}>
|
||||||
<ApolloProvider client={client}>
|
<ApolloProvider client={apolloClient}>
|
||||||
<MeProvider>
|
<MeProvider>
|
||||||
<LightningProvider>
|
<LightningProvider>
|
||||||
<FundErrorProvider>
|
<FundErrorProvider>
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import Layout from '../components/layout'
|
import Layout from '../components/layout'
|
||||||
import Items from '../components/items'
|
import Items from '../components/items'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
export default function Index () {
|
export default function Index () {
|
||||||
|
const router = useRouter()
|
||||||
|
console.log(router)
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<Items variables={{ sort: 'hot' }} rank />
|
<Items variables={{ sort: 'hot' }} rank key={router.query.key} />
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import styles from '../../styles/item.module.css'
|
||||||
import Seo from '../../components/seo'
|
import Seo from '../../components/seo'
|
||||||
import ApolloClient from '../../api/client'
|
import ApolloClient from '../../api/client'
|
||||||
import { NOFOLLOW_LIMIT } from '../../lib/constants'
|
import { NOFOLLOW_LIMIT } from '../../lib/constants'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
// ssr the item without comments so that we can populate metatags
|
// ssr the item without comments so that we can populate metatags
|
||||||
export async function getServerSideProps ({ req, params: { id } }) {
|
export async function getServerSideProps ({ req, params: { id } }) {
|
||||||
|
@ -68,10 +69,13 @@ export default function FullItem ({ item }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function LoadItem ({ query }) {
|
function LoadItem ({ query }) {
|
||||||
const { loading, error, data } = useQuery(query)
|
const router = useRouter()
|
||||||
|
const { error, data } = useQuery(query, {
|
||||||
|
fetchPolicy: router.query.cache ? 'cache-first' : undefined
|
||||||
|
})
|
||||||
if (error) return <div>Failed to load!</div>
|
if (error) return <div>Failed to load!</div>
|
||||||
|
|
||||||
if (loading) {
|
if (!data) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ItemSkeleton>
|
<ItemSkeleton>
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
import Layout from '../components/layout'
|
import Layout from '../components/layout'
|
||||||
import Notifications from '../components/notifications'
|
import Notifications from '../components/notifications'
|
||||||
|
|
||||||
export default function NotificationPage () {
|
export default function NotificationPage () {
|
||||||
|
const router = useRouter()
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<Notifications />
|
<Notifications key={router.query.key} />
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import Layout from '../components/layout'
|
import Layout from '../components/layout'
|
||||||
import Items from '../components/items'
|
import Items from '../components/items'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
export default function Index () {
|
export default function Index () {
|
||||||
|
const router = useRouter()
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<Items variables={{ sort: 'recent' }} rank />
|
<Items variables={{ sort: 'recent' }} rank key={router.query.key} />
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue