maintage pagination and other state on back button

This commit is contained in:
keyan 2022-09-06 08:01:49 -05:00
parent 1a0803a594
commit 7efc86427d
9 changed files with 117 additions and 37 deletions

View File

@ -169,9 +169,7 @@ export default {
throw new AuthenticationError('you must be logged in') throw new AuthenticationError('you must be logged in')
} }
await models.user.update({ where: { id: me.id }, data }) return await models.user.update({ where: { id: me.id }, data })
return true
}, },
setWalkthrough: async (parent, { upvotePopover, tipPopover }, { me, models }) => { setWalkthrough: async (parent, { upvotePopover, tipPopover }, { me, models }) => {
if (!me) { if (!me) {

View File

@ -35,8 +35,20 @@ export default async function getSSRApolloClient (req, me = null) {
export function getGetServerSideProps (query, variables = null, notFoundFunc, requireVar) { export function getGetServerSideProps (query, variables = null, notFoundFunc, requireVar) {
return async function ({ req, query: params }) { return async function ({ req, query: params }) {
const client = await getSSRApolloClient(req) const { nodata, ...realParams } = params
const vars = { ...params, ...variables } const vars = { ...realParams, ...variables }
// we want to use client-side cache
if (nodata && query) {
return {
props: {
apollo: {
query: print(query),
variables: vars
}
}
}
}
if (requireVar && !vars[requireVar]) { if (requireVar && !vars[requireVar]) {
return { return {
@ -44,6 +56,7 @@ export function getGetServerSideProps (query, variables = null, notFoundFunc, re
} }
} }
const client = await getSSRApolloClient(req)
let error = null; let data = null; let props = {} let error = null; let data = null; let props = {}
if (query) { if (query) {
({ error, data } = await client.query({ ({ error, data } = await client.query({
@ -60,7 +73,7 @@ export function getGetServerSideProps (query, variables = null, notFoundFunc, re
props = { props = {
apollo: { apollo: {
query: print(query), query: print(query),
variables: { ...params, ...variables } variables: vars
} }
} }
} }

View File

@ -31,7 +31,7 @@ export default gql`
setName(name: String!): Boolean setName(name: String!): Boolean
setSettings(tipDefault: Int!, noteItemSats: Boolean!, noteEarning: Boolean!, setSettings(tipDefault: Int!, noteItemSats: Boolean!, noteEarning: Boolean!,
noteAllDescendants: Boolean!, noteMentions: Boolean!, noteDeposits: Boolean!, noteAllDescendants: Boolean!, noteMentions: Boolean!, noteDeposits: Boolean!,
noteInvites: Boolean!, noteJobIndicator: Boolean!, hideInvoiceDesc: Boolean!): Boolean noteInvites: Boolean!, noteJobIndicator: Boolean!, hideInvoiceDesc: Boolean!): User
setPhoto(photoId: ID!): Int! setPhoto(photoId: ID!): Int!
upsertBio(bio: String!): User! upsertBio(bio: String!): User!
setWalkthrough(tipPopover: Boolean, upvotePopover: Boolean): Boolean setWalkthrough(tipPopover: Boolean, upvotePopover: Boolean): Boolean

View File

@ -96,9 +96,11 @@ export default function UserHeader ({ user }) {
if (error) { if (error) {
throw new Error({ message: error.toString() }) throw new Error({ message: error.toString() })
} }
const { nodata, ...query } = router.query
router.replace({ router.replace({
pathname: router.pathname, pathname: router.pathname,
query: { ...router.query, name } query: { ...query, name }
}) })
client.writeFragment({ client.writeFragment({

View File

@ -54,9 +54,8 @@ export const ME_SSR = gql`
} }
}` }`
export const SETTINGS = gql` export const SETTINGS_FIELDS = gql`
{ fragment SettingsFields on User {
settings {
tipDefault tipDefault
noteItemSats noteItemSats
noteEarning noteEarning
@ -72,9 +71,31 @@ export const SETTINGS = gql`
twitter twitter
github github
} }
}`
export const SETTINGS = gql`
${SETTINGS_FIELDS}
{
settings {
...SettingsFields
} }
}` }`
export const SET_SETTINGS =
gql`
${SETTINGS_FIELDS}
mutation setSettings($tipDefault: Int!, $noteItemSats: Boolean!, $noteEarning: Boolean!,
$noteAllDescendants: Boolean!, $noteMentions: Boolean!, $noteDeposits: Boolean!,
$noteInvites: Boolean!, $noteJobIndicator: Boolean!, $hideInvoiceDesc: Boolean!) {
setSettings(tipDefault: $tipDefault, noteItemSats: $noteItemSats,
noteEarning: $noteEarning, noteAllDescendants: $noteAllDescendants,
noteMentions: $noteMentions, noteDeposits: $noteDeposits, noteInvites: $noteInvites,
noteJobIndicator: $noteJobIndicator, hideInvoiceDesc: $hideInvoiceDesc) {
...SettingsFields
}
}
`
export const NAME_QUERY = export const NAME_QUERY =
gql` gql`
query nameAvailable($name: String!) { query nameAvailable($name: String!) {
@ -164,6 +185,11 @@ export const USER_WITH_POSTS = gql`
cursor cursor
items { items {
...ItemFields ...ItemFields
position
}
pins {
...ItemFields
position
} }
} }
}` }`

View File

@ -79,7 +79,7 @@ export default function getApolloClient () {
} }
}, },
notifications: { notifications: {
keyArgs: false, keyArgs: ['inc'],
merge (existing, incoming) { merge (existing, incoming) {
if (isFirstPage(incoming.cursor, existing?.notifications)) { if (isFirstPage(incoming.cursor, existing?.notifications)) {
return incoming return incoming

View File

@ -10,7 +10,7 @@ export const getServerSideProps = getGetServerSideProps(USER_WITH_POSTS)
export default function UserPosts ({ data: { user, items: { items, cursor } } }) { export default function UserPosts ({ data: { user, items: { items, cursor } } }) {
const { data } = useQuery(USER_WITH_POSTS, const { data } = useQuery(USER_WITH_POSTS,
{ variables: { name: user.name } }) { variables: { name: user.name, sort: 'user' } })
if (data) { if (data) {
({ user, items: { items, cursor } } = data) ({ user, items: { items, cursor } } = data)

View File

@ -1,5 +1,5 @@
import '../styles/globals.scss' import '../styles/globals.scss'
import { ApolloProvider, gql } from '@apollo/client' import { ApolloProvider, gql, useQuery } 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'
@ -10,26 +10,63 @@ import getApolloClient from '../lib/apollo'
import NextNProgress from 'nextjs-progressbar' import NextNProgress from 'nextjs-progressbar'
import { PriceProvider } from '../components/price' import { PriceProvider } from '../components/price'
import Head from 'next/head' import Head from 'next/head'
import { useRouter } from 'next/dist/client/router'
import { useEffect } from 'react'
import Moon from '../svgs/moon-fill.svg'
import Layout from '../components/layout'
function CSRWrapper ({ Component, apollo, ...props }) {
const { data, error } = useQuery(gql`${apollo.query}`, { variables: apollo.variables, fetchPolicy: 'cache-first' })
if (error) {
return (
<div className='d-flex font-weight-bold justify-content-center mt-3 mb-1'>
{error.toString()}
</div>
)
}
if (!data) {
return (
<Layout>
<div className='d-flex justify-content-center mt-3 mb-1'>
<Moon className='spin fill-grey' />
</div>
</Layout>
)
}
return <Component {...props} data={data} />
}
function MyApp ({ Component, pageProps: { session, ...props } }) { function MyApp ({ Component, pageProps: { session, ...props } }) {
const client = getApolloClient() const client = getApolloClient()
const router = useRouter()
useEffect(async () => {
// HACK: 'cause there's no way to tell Next to skip SSR
// So every page load, we modify the route in browser history
// to point to the same page but without SSR, ie ?nodata=true
// this nodata var will get passed to the server on back/foward and
// 1. prevent data from reloading and 2. perserve scroll
// (2) is not possible while intercepting nav with beforePopState
router.replace({
pathname: router.pathname,
query: { ...router.query, nodata: true }
}, router.asPath, { ...router.options, scroll: false })
}, [router.asPath])
/* /*
If we are on the client, we populate the apollo cache with the If we are on the client, we populate the apollo cache with the
ssr data ssr data
*/ */
if (typeof window !== 'undefined') { const { apollo, data, me, price } = props
const { apollo, data } = props if (typeof window !== 'undefined' && apollo && data) {
if (apollo) {
client.writeQuery({ client.writeQuery({
query: gql`${apollo.query}`, query: gql`${apollo.query}`,
data: data, data: data,
variables: apollo.variables variables: apollo.variables
}) })
} }
}
const { me, price } = props
return ( return (
<> <>
@ -54,7 +91,9 @@ function MyApp ({ Component, pageProps: { session, ...props } }) {
<FundErrorModal /> <FundErrorModal />
<ItemActProvider> <ItemActProvider>
<ItemActModal /> <ItemActModal />
<Component {...props} /> {data || !apollo?.query
? <Component {...props} />
: <CSRWrapper Component={Component} {...props} />}
</ItemActProvider> </ItemActProvider>
</FundErrorProvider> </FundErrorProvider>
</LightningProvider> </LightningProvider>

View File

@ -9,7 +9,7 @@ import LoginButton from '../components/login-button'
import { signIn } from 'next-auth/client' import { signIn } from 'next-auth/client'
import ModalButton from '../components/modal-button' import ModalButton from '../components/modal-button'
import { LightningAuth } from '../components/lightning-auth' import { LightningAuth } from '../components/lightning-auth'
import { SETTINGS } from '../fragments/users' import { SETTINGS, SET_SETTINGS } from '../fragments/users'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import Info from '../components/info' import Info from '../components/info'
@ -28,16 +28,18 @@ export const WarningSchema = Yup.object({
export default function Settings ({ data: { settings } }) { export default function Settings ({ data: { settings } }) {
const [success, setSuccess] = useState() const [success, setSuccess] = useState()
const [setSettings] = useMutation( const [setSettings] = useMutation(SET_SETTINGS, {
gql` update (cache, { data: { setSettings } }) {
mutation setSettings($tipDefault: Int!, $noteItemSats: Boolean!, $noteEarning: Boolean!, cache.modify({
$noteAllDescendants: Boolean!, $noteMentions: Boolean!, $noteDeposits: Boolean!, id: 'ROOT_QUERY',
$noteInvites: Boolean!, $noteJobIndicator: Boolean!, $hideInvoiceDesc: Boolean!) { fields: {
setSettings(tipDefault: $tipDefault, noteItemSats: $noteItemSats, settings () {
noteEarning: $noteEarning, noteAllDescendants: $noteAllDescendants, return setSettings
noteMentions: $noteMentions, noteDeposits: $noteDeposits, noteInvites: $noteInvites, }
noteJobIndicator: $noteJobIndicator, hideInvoiceDesc: $hideInvoiceDesc) }
}` })
}
}
) )
const { data } = useQuery(SETTINGS) const { data } = useQuery(SETTINGS)