From 07b9da353bd9b1428571a00d798778eec5ccce80 Mon Sep 17 00:00:00 2001 From: keyan Date: Tue, 26 Oct 2021 15:49:37 -0500 Subject: [PATCH] smarter use of SSR and caching --- api/resolvers/item.js | 46 +++++++++++-------- api/ssrApollo.js | 27 +++++++++++ api/typeDefs/item.js | 5 +-- components/comments-flat.js | 3 +- components/comments.js | 6 +-- components/header.js | 34 +++++--------- components/items.js | 7 +-- components/notifications.js | 6 +-- components/user-header.js | 2 +- fragments/comments.js | 4 +- fragments/items.js | 22 ++++++--- fragments/users.js | 45 +++++++++++++++++-- fragments/wallet.js | 25 +++++++++++ lib/apollo.js | 18 ++++---- pages/[name]/comments.js | 30 +++++++++++++ pages/{[username] => [name]}/index.js | 29 ++---------- pages/[name]/posts.js | 29 ++++++++++++ pages/[username]/comments.js | 64 --------------------------- pages/[username]/posts.js | 64 --------------------------- pages/_app.js | 33 +++++++------- pages/index.js | 29 +++--------- pages/invites/index.js | 2 +- pages/invoices/[id].js | 34 +++++--------- pages/items/[id]/edit.js | 34 ++------------ pages/items/[id]/index.js | 35 +++------------ pages/notifications.js | 28 ++---------- pages/post.js | 6 --- pages/recent.js | 29 +++--------- pages/top/[[...within]].js | 27 ++--------- pages/wallet.js | 6 --- pages/withdrawals/[id].js | 33 +++++--------- 31 files changed, 295 insertions(+), 467 deletions(-) create mode 100644 fragments/wallet.js create mode 100644 pages/[name]/comments.js rename pages/{[username] => [name]}/index.js (83%) create mode 100644 pages/[name]/posts.js delete mode 100644 pages/[username]/comments.js delete mode 100644 pages/[username]/posts.js diff --git a/api/resolvers/item.js b/api/resolvers/item.js index f726b465..340dae54 100644 --- a/api/resolvers/item.js +++ b/api/resolvers/item.js @@ -34,19 +34,28 @@ export async function getItem (parent, { id }, { models }) { export default { Query: { - moreItems: async (parent, { sort, cursor, userId, within }, { me, models }) => { + moreItems: async (parent, { sort, cursor, name, within }, { me, models }) => { const decodedCursor = decodeCursor(cursor) - let items - let interval = 'INTERVAL ' + let items; let user; let interval = 'INTERVAL ' + switch (sort) { case 'user': + if (!name) { + throw new UserInputError('must supply name', { argumentName: 'name' }) + } + + user = await models.user.findUnique({ where: { name } }) + if (!user) { + throw new UserInputError('no user has that name', { argumentName: 'name' }) + } + items = await models.$queryRaw(` ${SELECT} FROM "Item" WHERE "userId" = $1 AND "parentId" IS NULL AND created_at <= $2 ORDER BY created_at DESC OFFSET $3 - LIMIT ${LIMIT}`, Number(userId), decodedCursor.time, decodedCursor.offset) + LIMIT ${LIMIT}`, user.id, decodedCursor.time, decodedCursor.offset) break case 'hot': // HACK we can speed hack the first hot page, by limiting our query to only @@ -67,7 +76,7 @@ export default { LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset, new Date(new Date() - 7)) } - if (decodedCursor.offset !== 0 || items.length < LIMIT) { + if (decodedCursor.offset !== 0 || items?.length < LIMIT) { items = await models.$queryRaw(` ${SELECT} FROM "Item" @@ -79,7 +88,7 @@ export default { } break case 'top': - switch (within) { + switch (within?.pop()) { case 'day': interval += "'1 day'" break @@ -119,11 +128,16 @@ export default { items } }, - moreFlatComments: async (parent, { cursor, userId }, { me, models }) => { + moreFlatComments: async (parent, { cursor, name }, { me, models }) => { const decodedCursor = decodeCursor(cursor) - if (!userId) { - throw new UserInputError('must supply userId', { argumentName: 'userId' }) + if (!name) { + throw new UserInputError('must supply name', { argumentName: 'name' }) + } + + const user = await models.user.findUnique({ where: { name } }) + if (!user) { + throw new UserInputError('no user has that name', { argumentName: 'name' }) } const comments = await models.$queryRaw(` @@ -133,7 +147,7 @@ export default { AND created_at <= $2 ORDER BY created_at DESC OFFSET $3 - LIMIT ${LIMIT}`, Number(userId), decodedCursor.time, decodedCursor.offset) + LIMIT ${LIMIT}`, user.id, decodedCursor.time, decodedCursor.offset) return { cursor: comments.length === LIMIT ? nextCursorEncoded(decodedCursor) : null, @@ -141,13 +155,6 @@ export default { } }, item: getItem, - userComments: async (parent, { userId }, { models }) => { - return await models.$queryRaw(` - ${SELECT} - FROM "Item" - WHERE "userId" = $1 AND "parentId" IS NOT NULL - ORDER BY created_at DESC`, Number(userId)) - }, pageTitle: async (parent, { url }, { models }) => { try { const response = await fetch(ensureProtocol(url), { redirect: 'follow' }) @@ -524,11 +531,12 @@ const LEFT_JOIN_SATS = JOIN "ItemAct" ON i.id = "ItemAct"."itemId" GROUP BY i.id) x ON "Item".id = x.id` +/* NOTE: because many items will have the same rank, we need to tie break with a unique field so pagination works */ function timedOrderBySats (num) { return `ORDER BY ((x.sats-1)/POWER(EXTRACT(EPOCH FROM ($${num} - "Item".created_at))/3600+2, 1.5) + - (x.boost)/POWER(EXTRACT(EPOCH FROM ($${num} - "Item".created_at))/3600+2, 5)) DESC NULLS LAST` + (x.boost)/POWER(EXTRACT(EPOCH FROM ($${num} - "Item".created_at))/3600+2, 5)) DESC NULLS LAST, "Item".id DESC` } const ORDER_BY_SATS = `ORDER BY ((x.sats-1)/POWER(EXTRACT(EPOCH FROM ((NOW() AT TIME ZONE 'UTC') - "Item".created_at))/3600+2, 1.5) + - (x.boost)/POWER(EXTRACT(EPOCH FROM ((NOW() AT TIME ZONE 'UTC') - "Item".created_at))/3600+2, 5)) DESC NULLS LAST` + (x.boost)/POWER(EXTRACT(EPOCH FROM ((NOW() AT TIME ZONE 'UTC') - "Item".created_at))/3600+2, 5)) DESC NULLS LAST, "Item".id DESC` diff --git a/api/ssrApollo.js b/api/ssrApollo.js index 625f660a..82d7d4a7 100644 --- a/api/ssrApollo.js +++ b/api/ssrApollo.js @@ -5,6 +5,7 @@ import { getSession } from 'next-auth/client' import resolvers from './resolvers' import typeDefs from './typeDefs' import models from './models' +import { print } from 'graphql' export default async function getSSRApolloClient (req) { const session = req && await getSession({ req }) @@ -23,3 +24,29 @@ export default async function getSSRApolloClient (req) { cache: new InMemoryCache() }) } + +export function getGetServerSideProps (query, variables = null) { + return async function ({ req, params }) { + const client = await getSSRApolloClient(req) + const { error, data } = await client.query({ + query, + variables: { ...params, ...variables } + }) + + if (error || !data) { + return { + notFound: true + } + } + + return { + props: { + apollo: { + query: print(query), + variables: { ...params, ...variables } + }, + data + } + } + } +} diff --git a/api/typeDefs/item.js b/api/typeDefs/item.js index 54b80f18..5200591a 100644 --- a/api/typeDefs/item.js +++ b/api/typeDefs/item.js @@ -2,10 +2,9 @@ import { gql } from 'apollo-server-micro' export default gql` extend type Query { - moreItems(sort: String!, cursor: String, userId: ID, within: String): Items - moreFlatComments(cursor: String, userId: ID): Comments + moreItems(sort: String!, cursor: String, name: String, within: [String]): Items + moreFlatComments(cursor: String, name: String!): Comments item(id: ID!): Item - userComments(userId: ID!): [Item!] pageTitle(url: String!): String } diff --git a/components/comments-flat.js b/components/comments-flat.js index bad23b49..ed766a53 100644 --- a/components/comments-flat.js +++ b/components/comments-flat.js @@ -8,8 +8,7 @@ import MoreFooter from './more-footer' export default function CommentsFlat ({ variables, comments, cursor, ...props }) { const router = useRouter() const { data, fetchMore } = useQuery(MORE_FLAT_COMMENTS, { - variables, - fetchPolicy: router.query.cache ? 'cache-first' : undefined + variables }) if (!data && !comments) { diff --git a/components/comments.js b/components/comments.js index 5c2b7c96..dfcd1929 100644 --- a/components/comments.js +++ b/components/comments.js @@ -1,5 +1,4 @@ import { useQuery } from '@apollo/client' -import { useRouter } from 'next/router' import { useEffect } from 'react' import Comment, { CommentSkeleton } from './comment' @@ -22,10 +21,7 @@ export function CommentsSkeleton () { } export function CommentsQuery ({ query, ...props }) { - const router = useRouter() - const { error, data } = useQuery(query, { - fetchPolicy: router.query.cache ? 'cache-first' : undefined - }) + const { error, data } = useQuery(query) if (error) return
Failed to load!
if (!data) { diff --git a/components/header.js b/components/header.js index 66c61bc3..477bcd4a 100644 --- a/components/header.js +++ b/components/header.js @@ -16,16 +16,6 @@ function WalletSummary ({ me }) { return `${me?.sats} \\ ${me?.stacked}` } -function RefreshableLink ({ href, children, ...props }) { - const router = useRouter() - const same = router.asPath === href - return ( - - {children} - - ) -} - export default function Header () { const router = useRouter() const path = router.asPath.split('?')[0] @@ -61,7 +51,7 @@ export default function Header () { } - + notifications {me?.hasNewNotes && @@ -69,7 +59,7 @@ export default function Header () { {' '} } - + wallet @@ -84,12 +74,12 @@ export default function Header () {
- + recent - - + + top - + {me ? ( @@ -139,18 +129,18 @@ export default function Header () { className={styles.navbarNav} activeKey={path} > - + STACKER NEWS - - + + SN - + + {sort} - + } key={`/${sort}`} id='recent-top-button' diff --git a/components/items.js b/components/items.js index 09f15ebd..f048c899 100644 --- a/components/items.js +++ b/components/items.js @@ -2,15 +2,10 @@ import { useQuery } from '@apollo/client' import Item, { ItemSkeleton } from './item' import styles from './items.module.css' import { MORE_ITEMS } from '../fragments/items' -import { useRouter } from 'next/router' import MoreFooter from './more-footer' export default function Items ({ variables, rank, items, cursor }) { - const router = useRouter() - const { data, fetchMore } = useQuery(MORE_ITEMS, { - variables, - fetchPolicy: router.query.cache ? 'cache-first' : undefined - }) + const { data, fetchMore } = useQuery(MORE_ITEMS, { variables }) if (!data && !items) { return diff --git a/components/notifications.js b/components/notifications.js index 70d86982..cdbc1d2a 100644 --- a/components/notifications.js +++ b/components/notifications.js @@ -44,11 +44,7 @@ function Notification ({ n }) { } export default function Notifications ({ notifications, cursor, lastChecked, variables }) { - const router = useRouter() - const { data, fetchMore } = useQuery(NOTIFICATIONS, { - variables, - fetchPolicy: router.query.cache ? 'cache-first' : undefined - }) + const { data, fetchMore } = useQuery(NOTIFICATIONS, { variables }) if (data) { ({ notifications: { notifications, cursor } } = data) diff --git a/components/user-header.js b/components/user-header.js index 05362081..fded93ef 100644 --- a/components/user-header.js +++ b/components/user-header.js @@ -71,7 +71,7 @@ export default function UserHeader ({ user }) { } router.replace({ pathname: router.pathname, - query: { ...router.query, username: name } + query: { ...router.query, name } }) client.writeFragment({ diff --git a/fragments/comments.js b/fragments/comments.js index 2235c586..f263c317 100644 --- a/fragments/comments.js +++ b/fragments/comments.js @@ -27,8 +27,8 @@ export const COMMENT_FIELDS = gql` export const MORE_FLAT_COMMENTS = gql` ${COMMENT_FIELDS} - query MoreFlatComments($cursor: String, $userId: ID) { - moreFlatComments(cursor: $cursor, userId: $userId) { + query MoreFlatComments($cursor: String, $name: String!) { + moreFlatComments(cursor: $cursor, name: $name) { cursor comments { ...CommentFields diff --git a/fragments/items.js b/fragments/items.js index 03008a49..b32edf41 100644 --- a/fragments/items.js +++ b/fragments/items.js @@ -28,20 +28,30 @@ export const ITEM_FIELDS = gql` export const MORE_ITEMS = gql` ${ITEM_FIELDS} - query MoreItems($sort: String!, $cursor: String, $userId: ID, $within: String) { - moreItems(sort: $sort, cursor: $cursor, userId: $userId, within: $within) { + query MoreItems($sort: String!, $cursor: String, $name: String, $within: [String]) { + moreItems(sort: $sort, cursor: $cursor, name: $name, within: $within) { cursor items { ...ItemFields } } - } ` + }` -export const ITEM_FULL = id => gql` +export const ITEM = gql` + ${ITEM_FIELDS} + + query Item($id: ID!) { + item(id: $id) { + ...ItemFields + text + } + }` + +export const ITEM_FULL = gql` ${ITEM_FIELDS} ${COMMENTS} - { - item(id: ${id}) { + query Item($id: ID!) { + item(id: $id) { ...ItemFields text comments { diff --git a/fragments/users.js b/fragments/users.js index 447865c5..ad2cc860 100644 --- a/fragments/users.js +++ b/fragments/users.js @@ -1,4 +1,5 @@ import { gql } from '@apollo/client' +import { COMMENT_FIELDS } from './comments' import { ITEM_FIELDS, ITEM_WITH_COMMENTS } from './items' export const USER_FIELDS = gql` @@ -17,14 +18,52 @@ export const USER_FIELDS = gql` } }` -export const USER_FULL = name => gql` +export const USER_FULL = gql` ${USER_FIELDS} ${ITEM_WITH_COMMENTS} - { - user(name: "${name}") { + query User($name: String!) { + user(name: $name) { ...UserFields bio { ...ItemWithComments } } }` + +export const USER_WITH_COMMENTS = gql` + ${USER_FIELDS} + ${ITEM_WITH_COMMENTS} + ${COMMENT_FIELDS} + query UserWithComments($name: String!) { + user(name: $name) { + ...UserFields + bio { + ...ItemWithComments + } + } + moreFlatComments(name: $name) { + cursor + comments { + ...CommentFields + } + } + }` + +export const USER_WITH_POSTS = gql` + ${USER_FIELDS} + ${ITEM_WITH_COMMENTS} + ${ITEM_FIELDS} + query UserWithPosts($name: String!, $sort: String!) { + user(name: $name) { + ...UserFields + bio { + ...ItemWithComments + } + } + moreItems(sort: $sort, name: $name) { + cursor + items { + ...ItemFields + } + } + }` diff --git a/fragments/wallet.js b/fragments/wallet.js new file mode 100644 index 00000000..c0766463 --- /dev/null +++ b/fragments/wallet.js @@ -0,0 +1,25 @@ +import { gql } from '@apollo/client' + +export const INVOICE = gql` + query Invoice($id: ID!) { + invoice(id: $id) { + id + bolt11 + msatsReceived + cancelled + confirmedAt + expiresAt + } + }` + +export const WITHDRAWL = gql` + query Withdrawl($id: ID!) { + withdrawl(id: $id) { + id + bolt11 + satsPaid + satsFeePaying + satsFeePaid + status + } + }` diff --git a/lib/apollo.js b/lib/apollo.js index 5474c04c..96e1e75f 100644 --- a/lib/apollo.js +++ b/lib/apollo.js @@ -7,14 +7,14 @@ const additiveLink = from([ new HttpLink({ uri: '/api/graphql' }) ]) -function isFirstPage (cursor, existing) { +function isFirstPage (cursor, existingThings) { if (cursor) { const decursor = decodeCursor(cursor) return decursor.offset === LIMIT } else { // we don't have anything cached, or our existing items are less than // or equal to a full page - return !existing || !existing.items || existing.items.length < LIMIT + return existingThings?.length < LIMIT } } @@ -26,9 +26,9 @@ export default function getApolloClient () { Query: { fields: { moreItems: { - keyArgs: ['sort', 'userId', 'within'], + keyArgs: ['sort', 'name', 'within'], merge (existing, incoming) { - if (isFirstPage(incoming.cursor, existing)) { + if (isFirstPage(incoming.cursor, existing?.items)) { return incoming } @@ -39,9 +39,9 @@ export default function getApolloClient () { } }, moreFlatComments: { - keyArgs: ['userId'], + keyArgs: ['name'], merge (existing, incoming) { - if (isFirstPage(incoming.cursor, existing)) { + if (isFirstPage(incoming.cursor, existing?.comments)) { return incoming } @@ -54,7 +54,7 @@ export default function getApolloClient () { notifications: { keyArgs: false, merge (existing, incoming) { - if (isFirstPage(incoming.cursor, existing)) { + if (isFirstPage(incoming.cursor, existing?.notifications)) { return incoming } @@ -72,11 +72,11 @@ export default function getApolloClient () { defaultOptions: { // cache-and-network allows us to refresh pages on navigation watchQuery: { - fetchPolicy: 'cache-and-network', + fetchPolicy: 'cache-only', nextFetchPolicy: 'cache-first' }, query: { - fetchPolicy: 'cache-and-network', + fetchPolicy: 'cache-only', nextFetchPolicy: 'cache-first' } } diff --git a/pages/[name]/comments.js b/pages/[name]/comments.js new file mode 100644 index 00000000..5f438157 --- /dev/null +++ b/pages/[name]/comments.js @@ -0,0 +1,30 @@ +import Layout from '../../components/layout' +import { useQuery } from '@apollo/client' +import UserHeader from '../../components/user-header' +import CommentsFlat from '../../components/comments-flat' +import Seo from '../../components/seo' +import { USER_WITH_COMMENTS } from '../../fragments/users' +import { getGetServerSideProps } from '../../api/ssrApollo' + +export const getServerSideProps = getGetServerSideProps(USER_WITH_COMMENTS) + +export default function UserComments ( + { data: { user, moreFlatComments: { comments, cursor } } }) { + const { data } = useQuery( + USER_WITH_COMMENTS, { variables: { name: user.name } }) + + if (data) { + ({ user, moreFlatComments: { comments, cursor } } = data) + } + + return ( + + + + + + ) +} diff --git a/pages/[username]/index.js b/pages/[name]/index.js similarity index 83% rename from pages/[username]/index.js rename to pages/[name]/index.js index b510cf02..433424e1 100644 --- a/pages/[username]/index.js +++ b/pages/[name]/index.js @@ -12,28 +12,10 @@ import ActionTooltip from '../../components/action-tooltip' import TextareaAutosize from 'react-textarea-autosize' import { useMe } from '../../components/me' import { USER_FULL } from '../../fragments/users' -import { useRouter } from 'next/router' import { ITEM_FIELDS } from '../../fragments/items' -import getSSRApolloClient from '../../api/ssrApollo' +import { getGetServerSideProps } from '../../api/ssrApollo' -export async function getServerSideProps ({ req, params: { username } }) { - const client = await getSSRApolloClient(req) - const { error, data } = await client.query({ - query: USER_FULL(username) - }) - - if (error || !data?.user) { - return { - notFound: true - } - } - - return { - props: { - user: data.user - } - } -} +export const getServerSideProps = getGetServerSideProps(USER_FULL) const BioSchema = Yup.object({ bio: Yup.string().required('required').trim() @@ -93,15 +75,12 @@ export function BioForm ({ handleSuccess, bio }) { ) } -export default function User ({ user }) { +export default function User ({ data: { user } }) { const [create, setCreate] = useState(false) const [edit, setEdit] = useState(false) const me = useMe() - const router = useRouter() - const { data } = useQuery(USER_FULL(user.name), { - fetchPolicy: router.query.cache ? 'cache-first' : undefined - }) + const { data } = useQuery(USER_FULL, { variables: { name: user.name } }) if (data) { ({ user } = data) diff --git a/pages/[name]/posts.js b/pages/[name]/posts.js new file mode 100644 index 00000000..016ec60b --- /dev/null +++ b/pages/[name]/posts.js @@ -0,0 +1,29 @@ +import Layout from '../../components/layout' +import { useQuery } from '@apollo/client' +import UserHeader from '../../components/user-header' +import Seo from '../../components/seo' +import Items from '../../components/items' +import { USER_WITH_POSTS } from '../../fragments/users' +import { getGetServerSideProps } from '../../api/ssrApollo' + +export const getServerSideProps = getGetServerSideProps(USER_WITH_POSTS, { sort: 'user' }) + +export default function UserPosts ({ data: { user, moreItems: { items, cursor } } }) { + const { data } = useQuery(USER_WITH_POSTS, + { variables: { name: user.name, sort: 'user' } }) + + if (data) { + ({ user, moreItems: { items, cursor } } = data) + } + + return ( + + + + + + ) +} diff --git a/pages/[username]/comments.js b/pages/[username]/comments.js deleted file mode 100644 index 6c344b85..00000000 --- a/pages/[username]/comments.js +++ /dev/null @@ -1,64 +0,0 @@ -import Layout from '../../components/layout' -import { useQuery } from '@apollo/client' -import UserHeader from '../../components/user-header' -import CommentsFlat from '../../components/comments-flat' -import Seo from '../../components/seo' -import { USER_FULL } from '../../fragments/users' -import { useRouter } from 'next/router' -import { MORE_FLAT_COMMENTS } from '../../fragments/comments' -import { getServerSideProps as headerProps } from './index' -import getSSRApolloClient from '../../api/ssrApollo' - -export async function getServerSideProps ({ req, params: { username } }) { - const { notFound, props } = await headerProps({ req, params: { username } }) - - if (notFound) { - return { - notFound - } - } - - const { user } = props - const client = await getSSRApolloClient(req) - const { data } = await client.query({ - query: MORE_FLAT_COMMENTS, - variables: { userId: user.id } - }) - - let comments, cursor - if (data) { - ({ moreFlatComments: { comments, cursor } } = data) - } - - return { - props: { - ...props, - comments, - cursor - } - } -} - -export default function UserComments ({ user, comments, cursor }) { - const router = useRouter() - - const { data } = useQuery( - USER_FULL(user.name), { - fetchPolicy: router.query.cache ? 'cache-first' : undefined - }) - - if (data) { - ({ user } = data) - } - - return ( - - - - - - ) -} diff --git a/pages/[username]/posts.js b/pages/[username]/posts.js deleted file mode 100644 index 84fb2988..00000000 --- a/pages/[username]/posts.js +++ /dev/null @@ -1,64 +0,0 @@ -import Layout from '../../components/layout' -import { useQuery } from '@apollo/client' -import UserHeader from '../../components/user-header' -import Seo from '../../components/seo' -import Items from '../../components/items' -import { useRouter } from 'next/router' -import { USER_FULL } from '../../fragments/users' -import { getServerSideProps as headerProps } from './index' -import getSSRApolloClient from '../../api/ssrApollo' -import { MORE_ITEMS } from '../../fragments/items' - -export async function getServerSideProps ({ req, params: { username } }) { - const { notFound, props } = await headerProps({ req, params: { username } }) - - if (notFound) { - return { - notFound - } - } - - const { user } = props - const client = await getSSRApolloClient(req) - const { data } = await client.query({ - query: MORE_ITEMS, - variables: { sort: 'user', userId: user.id } - }) - - let items, cursor - if (data) { - ({ moreItems: { items, cursor } } = data) - } - - return { - props: { - ...props, - items, - cursor - } - } -} - -export default function UserPosts ({ user, items, cursor }) { - const router = useRouter() - - const { data } = useQuery( - USER_FULL(user.name), { - fetchPolicy: router.query.cache ? 'cache-first' : undefined - }) - - if (data) { - ({ user } = data) - } - - return ( - - - - - - ) -} diff --git a/pages/_app.js b/pages/_app.js index 0267e949..f02f6795 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -1,33 +1,34 @@ import '../styles/globals.scss' -import { ApolloProvider } from '@apollo/client' +import { ApolloProvider, gql } from '@apollo/client' import { Provider } from 'next-auth/client' import { FundErrorModal, FundErrorProvider } from '../components/fund-error' import { MeProvider } from '../components/me' import PlausibleProvider from 'next-plausible' import { LightningProvider } from '../components/lightning' -import { useRouter } from 'next/router' -import { useEffect } from 'react' import { ItemActModal, ItemActProvider } from '../components/item-act' import getApolloClient from '../lib/apollo' function MyApp ({ Component, pageProps: { session, ...props } }) { - 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 - }) - }, []) + const client = getApolloClient() + /* + If we are on the client, we populate the apollo cache with the + ssr data + */ + if (typeof window !== 'undefined') { + const { apollo, data } = props + if (apollo) { + client.writeQuery({ + query: gql`${apollo.query}`, + data: data, + variables: apollo.variables + }) + } + } return ( - + diff --git a/pages/index.js b/pages/index.js index 88848b4a..58b248b9 100644 --- a/pages/index.js +++ b/pages/index.js @@ -1,36 +1,17 @@ import Layout from '../components/layout' import Items from '../components/items' -import { useRouter } from 'next/router' -import getSSRApolloClient from '../api/ssrApollo' +import { getGetServerSideProps } from '../api/ssrApollo' import { MORE_ITEMS } from '../fragments/items' -export async function getServerSideProps ({ req }) { - const client = await getSSRApolloClient(req) - const { data } = await client.query({ - query: MORE_ITEMS, - variables: { sort: 'hot' } - }) +const variables = { sort: 'hot' } +export const getServerSideProps = getGetServerSideProps(MORE_ITEMS, variables) - let items, cursor - if (data) { - ({ moreItems: { items, cursor } } = data) - } - - return { - props: { - items, - cursor - } - } -} - -export default function Index ({ items, cursor }) { - const router = useRouter() +export default function Index ({ data: { moreItems: { items, cursor } } }) { return ( ) diff --git a/pages/invites/index.js b/pages/invites/index.js index 621d5bb3..82b403ed 100644 --- a/pages/invites/index.js +++ b/pages/invites/index.js @@ -148,7 +148,7 @@ export default function Invites () { ...InviteFields } } - `) + `, { fetchPolicy: 'cache-and-network' }) const [active, inactive] = data && data.invites ? data.invites.reduce((result, invite) => { diff --git a/pages/invoices/[id].js b/pages/invoices/[id].js index 80daee96..909e8d4f 100644 --- a/pages/invoices/[id].js +++ b/pages/invoices/[id].js @@ -1,38 +1,24 @@ import { useQuery } from '@apollo/client' -import gql from 'graphql-tag' import { Invoice } from '../../components/invoice' import { LnQRSkeleton } from '../../components/lnqr' import LayoutCenter from '../../components/layout-center' +import { useRouter } from 'next/router' +import { INVOICE } from '../../fragments/wallet' -export async function getServerSideProps ({ params: { id } }) { - return { - props: { - id - } - } -} - -export default function FullInvoice ({ id }) { - const query = gql` - { - invoice(id: ${id}) { - id - bolt11 - msatsReceived - cancelled - confirmedAt - expiresAt - } - }` +export default function FullInvoice () { return ( - + ) } -function LoadInvoice ({ query }) { - const { loading, error, data } = useQuery(query, { pollInterval: 1000 }) +function LoadInvoice () { + const router = useRouter() + const { loading, error, data } = useQuery(INVOICE, { + pollInterval: 1000, + variables: { id: router.query.id } + }) if (error) return
error
if (!data || loading) { return diff --git a/pages/items/[id]/edit.js b/pages/items/[id]/edit.js index 5c658b9f..c79c6813 100644 --- a/pages/items/[id]/edit.js +++ b/pages/items/[id]/edit.js @@ -1,38 +1,12 @@ -import { ITEM_FIELDS } from '../../../fragments/items' -import { gql } from '@apollo/client' -import getSSRApolloClient from '../../../api/ssrApollo' +import { ITEM } from '../../../fragments/items' +import { getGetServerSideProps } from '../../../api/ssrApollo' import { DiscussionForm } from '../../../components/discussion-form' import { LinkForm } from '../../../components/link-form' import LayoutCenter from '../../../components/layout-center' -export async function getServerSideProps ({ req, params: { id } }) { - const client = await getSSRApolloClient(req) - const { error, data: { item } } = await client.query({ - query: - gql` - ${ITEM_FIELDS} - { - item(id: ${id}) { - ...ItemFields - text - } - }` - }) +export const getServerSideProps = getGetServerSideProps(ITEM) - if (error || !item) { - return { - notFound: true - } - } - - return { - props: { - item - } - } -} - -export default function PostEdit ({ item }) { +export default function PostEdit ({ data: { item } }) { const editThreshold = new Date(item.createdAt).getTime() + 10 * 60000 return ( diff --git a/pages/items/[id]/index.js b/pages/items/[id]/index.js index 64093b7a..da7625b9 100644 --- a/pages/items/[id]/index.js +++ b/pages/items/[id]/index.js @@ -2,39 +2,14 @@ import Layout from '../../../components/layout' import { ITEM_FULL } from '../../../fragments/items' import Seo from '../../../components/seo' import ItemFull from '../../../components/item-full' -import getSSRApolloClient from '../../../api/ssrApollo' +import { getGetServerSideProps } from '../../../api/ssrApollo' import { useQuery } from '@apollo/client' -import { useRouter } from 'next/router' -export async function getServerSideProps ({ req, params: { id } }) { - if (isNaN(id)) { - return { - notFound: true - } - } +export const getServerSideProps = getGetServerSideProps(ITEM_FULL) - const client = await getSSRApolloClient(req) - const { error, data } = await client.query({ - query: ITEM_FULL(id) - }) - - if (error || !data?.item) { - return { - notFound: true - } - } - - return { - props: { - item: data.item - } - } -} - -export default function AnItem ({ item }) { - const router = useRouter() - const { data } = useQuery(ITEM_FULL(item.id), { - fetchPolicy: router.query.cache ? 'cache-first' : undefined +export default function AnItem ({ data: { item } }) { + const { data } = useQuery(ITEM_FULL, { + variables: { id: item.id } }) if (data) { ({ item } = data) diff --git a/pages/notifications.js b/pages/notifications.js index e78e2ab7..49fbe839 100644 --- a/pages/notifications.js +++ b/pages/notifications.js @@ -1,36 +1,16 @@ -import { useRouter } from 'next/router' -import getSSRApolloClient from '../api/ssrApollo' +import { getGetServerSideProps } from '../api/ssrApollo' import Layout from '../components/layout' import Notifications from '../components/notifications' import { NOTIFICATIONS } from '../fragments/notifications' -export async function getServerSideProps ({ req }) { - const client = await getSSRApolloClient(req) - const { data } = await client.query({ - query: NOTIFICATIONS - }) +export const getServerSideProps = getGetServerSideProps(NOTIFICATIONS) - let notifications, cursor, lastChecked - if (data) { - ({ notifications: { notifications, cursor, lastChecked } } = data) - } - - return { - props: { - notifications, - cursor, - lastChecked - } - } -} - -export default function NotificationPage ({ notifications, cursor, lastChecked }) { - const router = useRouter() +export default function NotificationPage ({ data: { notifications: { notifications, cursor, lastChecked } } }) { return ( ) diff --git a/pages/post.js b/pages/post.js index df69dbb3..12a01b23 100644 --- a/pages/post.js +++ b/pages/post.js @@ -6,12 +6,6 @@ import { useMe } from '../components/me' import { DiscussionForm } from '../components/discussion-form' import { LinkForm } from '../components/link-form' -export async function getServerSideProps () { - return { - props: {} - } -} - export function PostForm () { const router = useRouter() const me = useMe() diff --git a/pages/recent.js b/pages/recent.js index f0480e9b..fa87db71 100644 --- a/pages/recent.js +++ b/pages/recent.js @@ -1,36 +1,17 @@ import Layout from '../components/layout' import Items from '../components/items' -import { useRouter } from 'next/router' -import getSSRApolloClient from '../api/ssrApollo' +import { getGetServerSideProps } from '../api/ssrApollo' import { MORE_ITEMS } from '../fragments/items' -export async function getServerSideProps ({ req }) { - const client = await getSSRApolloClient(req) - const { data } = await client.query({ - query: MORE_ITEMS, - variables: { sort: 'recent' } - }) +const variables = { sort: 'recent' } +export const getServerSideProps = getGetServerSideProps(MORE_ITEMS, { sort: 'recent' }) - let items, cursor - if (data) { - ({ moreItems: { items, cursor } } = data) - } - - return { - props: { - items, - cursor - } - } -} - -export default function Index ({ items, cursor }) { - const router = useRouter() +export default function Index ({ data: { moreItems: { items, cursor } } }) { return ( ) diff --git a/pages/top/[[...within]].js b/pages/top/[[...within]].js index b599ec43..4a0bbf28 100644 --- a/pages/top/[[...within]].js +++ b/pages/top/[[...within]].js @@ -1,34 +1,15 @@ import Layout from '../../components/layout' import Items from '../../components/items' import { useRouter } from 'next/router' -import getSSRApolloClient from '../../api/ssrApollo' +import { getGetServerSideProps } from '../../api/ssrApollo' import { MORE_ITEMS } from '../../fragments/items' import { Nav, Navbar } from 'react-bootstrap' import styles from '../../components/header.module.css' import Link from 'next/link' -export async function getServerSideProps ({ req, params: { within } }) { - const client = await getSSRApolloClient(req) - console.log('called') - const { data } = await client.query({ - query: MORE_ITEMS, - variables: { sort: 'top', within: within?.pop() } - }) +export const getServerSideProps = getGetServerSideProps(MORE_ITEMS, { sort: 'top'}) - let items, cursor - if (data) { - ({ moreItems: { items, cursor } } = data) - } - - return { - props: { - items, - cursor - } - } -} - -export default function Index ({ items, cursor }) { +export default function Index ({ data: { moreItems: { items, cursor } } }) { const router = useRouter() const path = router.asPath.split('?')[0] @@ -88,7 +69,7 @@ export default function Index ({ items, cursor }) { ) diff --git a/pages/wallet.js b/pages/wallet.js index 6b3f6581..2c898403 100644 --- a/pages/wallet.js +++ b/pages/wallet.js @@ -13,12 +13,6 @@ import { useEffect, useState } from 'react' import { requestProvider } from 'webln' import { Alert } from 'react-bootstrap' -export async function getServerSideProps () { - return { - props: {} - } -} - export default function Wallet () { return ( diff --git a/pages/withdrawals/[id].js b/pages/withdrawals/[id].js index 988da7bc..8171609e 100644 --- a/pages/withdrawals/[id].js +++ b/pages/withdrawals/[id].js @@ -1,32 +1,15 @@ import { useQuery } from '@apollo/client' -import gql from 'graphql-tag' import LayoutCenter from '../../components/layout-center' import { CopyInput, Input, InputSkeleton } from '../../components/form' import InputGroup from 'react-bootstrap/InputGroup' import InvoiceStatus from '../../components/invoice-status' +import { useRouter } from 'next/router' +import { WITHDRAWL } from '../../fragments/wallet' -export async function getServerSideProps ({ params: { id } }) { - return { - props: { - id - } - } -} - -export default function Withdrawl ({ id }) { - const query = gql` - { - withdrawl(id: ${id}) { - bolt11 - satsPaid - satsFeePaying - satsFeePaid - status - } - }` +export default function Withdrawl () { return ( - + ) } @@ -45,8 +28,12 @@ export function WithdrawlSkeleton ({ status }) { ) } -function LoadWithdrawl ({ query }) { - const { loading, error, data } = useQuery(query, { pollInterval: 1000 }) +function LoadWithdrawl () { + const router = useRouter() + const { loading, error, data } = useQuery(WITHDRAWL, { + variables: { id: router.query.id }, + pollInterval: 1000 + }) if (error) return
error
if (!data || loading) { return