diff --git a/api/resolvers/notifications.js b/api/resolvers/notifications.js index b7aaff5d..54ba9144 100644 --- a/api/resolvers/notifications.js +++ b/api/resolvers/notifications.js @@ -64,60 +64,32 @@ export default { // HACK to make notifications faster, we only return a limited sub set of the unioned // queries ... we only ever need at most LIMIT+current offset in the child queries to // have enough items to return in the union - const notifications = await models.$queryRaw( - inc === 'replies' - ? `SELECT DISTINCT "Item".id::TEXT, "Item".created_at AS "sortTime", NULL::BIGINT as "earnedSats", + + const queries = [] + + if (inc === 'replies') { + queries.push( + `SELECT DISTINCT "Item".id::TEXT, "Item".created_at AS "sortTime", NULL::BIGINT as "earnedSats", 'Reply' AS type - FROM "Item" - JOIN "Item" p ON "Item".path <@ p.path - WHERE p."userId" = $1 - AND "Item"."userId" <> $1 AND "Item".created_at <= $2 + FROM "Item" + JOIN "Item" p ON ${me.noteAllDescendants ? '"Item".path <@ p.path' : '"Item"."parentId" = p.id'} + WHERE p."userId" = $1 + AND "Item"."userId" <> $1 AND "Item".created_at <= $2` + ) + } else { + queries.push( + `(SELECT DISTINCT "Item".id::TEXT, "Item".created_at AS "sortTime", NULL::BIGINT as "earnedSats", + 'Reply' AS type + FROM "Item" + JOIN "Item" p ON ${me.noteAllDescendants ? '"Item".path <@ p.path' : '"Item"."parentId" = p.id'} + WHERE p."userId" = $1 + AND "Item"."userId" <> $1 AND "Item".created_at <= $2 ORDER BY "sortTime" DESC - OFFSET $3 - LIMIT ${LIMIT}` - : `(SELECT DISTINCT "Item".id::TEXT, "Item".created_at AS "sortTime", NULL::BIGINT as "earnedSats", - 'Reply' AS type - FROM "Item" - JOIN "Item" p ON "Item".path <@ p.path - WHERE p."userId" = $1 - AND "Item"."userId" <> $1 AND "Item".created_at <= $2 - ORDER BY "sortTime" DESC - LIMIT ${LIMIT}+$3) - UNION ALL - (SELECT "Item".id::TEXT, MAX("ItemAct".created_at) AS "sortTime", - sum("ItemAct".sats) as "earnedSats", 'Votification' AS type - FROM "Item" - JOIN "ItemAct" ON "ItemAct"."itemId" = "Item".id - WHERE "ItemAct"."userId" <> $1 - AND "ItemAct".created_at <= $2 - AND "ItemAct".act <> 'BOOST' - AND "Item"."userId" = $1 - GROUP BY "Item".id - ORDER BY "sortTime" DESC - LIMIT ${LIMIT}+$3) - UNION ALL - (SELECT "Item".id::TEXT, "Mention".created_at AS "sortTime", NULL as "earnedSats", - 'Mention' AS type - FROM "Mention" - JOIN "Item" ON "Mention"."itemId" = "Item".id - LEFT JOIN "Item" p ON "Item"."parentId" = p.id - WHERE "Mention"."userId" = $1 - AND "Mention".created_at <= $2 - AND "Item"."userId" <> $1 - AND (p."userId" IS NULL OR p."userId" <> $1) - ORDER BY "sortTime" DESC - LIMIT ${LIMIT}+$3) - UNION ALL - (SELECT "Invite".id, MAX(users.created_at) AS "sortTime", NULL as "earnedSats", - 'Invitification' AS type - FROM users JOIN "Invite" on users."inviteId" = "Invite".id - WHERE "Invite"."userId" = $1 - AND users.created_at <= $2 - GROUP BY "Invite".id - ORDER BY "sortTime" DESC - LIMIT ${LIMIT}+$3) - UNION ALL - (SELECT "Item".id::text, "Item"."statusUpdatedAt" AS "sortTime", NULL as "earnedSats", + LIMIT ${LIMIT}+$3)` + ) + + queries.push( + `(SELECT "Item".id::text, "Item"."statusUpdatedAt" AS "sortTime", NULL as "earnedSats", 'JobChanged' AS type FROM "Item" WHERE "Item"."userId" = $1 @@ -125,25 +97,83 @@ export default { AND status <> 'STOPPED' AND "statusUpdatedAt" <= $2 ORDER BY "sortTime" DESC - LIMIT ${LIMIT}+$3) - UNION ALL - (SELECT "Earn".id::text, "Earn".created_at AS "sortTime", FLOOR(msats / 1000) as "earnedSats", - 'Earn' AS type - FROM "Earn" - WHERE "Earn"."userId" = $1 - AND FLOOR(msats / 1000) > 0 - AND created_at <= $2 - ORDER BY "sortTime" DESC - LIMIT ${LIMIT}+$3) - UNION ALL - (SELECT "Invoice".id::text, "Invoice"."confirmedAt" AS "sortTime", FLOOR("msatsReceived" / 1000) as "earnedSats", - 'InvoicePaid' AS type - FROM "Invoice" - WHERE "Invoice"."userId" = $1 - AND "confirmedAt" IS NOT NULL - AND created_at <= $2 - ORDER BY "sortTime" DESC - LIMIT ${LIMIT}+$3) + LIMIT ${LIMIT}+$3)` + ) + + if (me.noteItemSats) { + queries.push( + `(SELECT "Item".id::TEXT, MAX("ItemAct".created_at) AS "sortTime", + sum("ItemAct".sats) as "earnedSats", 'Votification' AS type + FROM "Item" + JOIN "ItemAct" ON "ItemAct"."itemId" = "Item".id + WHERE "ItemAct"."userId" <> $1 + AND "ItemAct".created_at <= $2 + AND "ItemAct".act <> 'BOOST' + AND "Item"."userId" = $1 + GROUP BY "Item".id + ORDER BY "sortTime" DESC + LIMIT ${LIMIT}+$3)` + ) + } + + if (me.noteEarning) { + queries.push( + `(SELECT "Earn".id::text, "Earn".created_at AS "sortTime", FLOOR(msats / 1000) as "earnedSats", + 'Earn' AS type + FROM "Earn" + WHERE "Earn"."userId" = $1 + AND FLOOR(msats / 1000) > 0 + AND created_at <= $2 + ORDER BY "sortTime" DESC + LIMIT ${LIMIT}+$3)` + ) + } + + if (me.noteMentions) { + queries.push( + `(SELECT "Item".id::TEXT, "Mention".created_at AS "sortTime", NULL as "earnedSats", + 'Mention' AS type + FROM "Mention" + JOIN "Item" ON "Mention"."itemId" = "Item".id + LEFT JOIN "Item" p ON "Item"."parentId" = p.id + WHERE "Mention"."userId" = $1 + AND "Mention".created_at <= $2 + AND "Item"."userId" <> $1 + AND (p."userId" IS NULL OR p."userId" <> $1) + ORDER BY "sortTime" DESC + LIMIT ${LIMIT}+$3)` + ) + } + + if (me.noteDeposits) { + queries.push( + `(SELECT "Invoice".id::text, "Invoice"."confirmedAt" AS "sortTime", FLOOR("msatsReceived" / 1000) as "earnedSats", + 'InvoicePaid' AS type + FROM "Invoice" + WHERE "Invoice"."userId" = $1 + AND "confirmedAt" IS NOT NULL + AND created_at <= $2 + ORDER BY "sortTime" DESC + LIMIT ${LIMIT}+$3)` + ) + } + + if (me.noteInvites) { + queries.push( + `(SELECT "Invite".id, MAX(users.created_at) AS "sortTime", NULL as "earnedSats", + 'Invitification' AS type + FROM users JOIN "Invite" on users."inviteId" = "Invite".id + WHERE "Invite"."userId" = $1 + AND users.created_at <= $2 + GROUP BY "Invite".id + ORDER BY "sortTime" DESC + LIMIT ${LIMIT}+$3)` + ) + } + } + + const notifications = await models.$queryRaw( + `${queries.join(' UNION ALL ')} ORDER BY "sortTime" DESC OFFSET $3 LIMIT ${LIMIT}`, me.id, decodedCursor.time, decodedCursor.offset) diff --git a/api/resolvers/user.js b/api/resolvers/user.js index 259e42b0..7f7ee057 100644 --- a/api/resolvers/user.js +++ b/api/resolvers/user.js @@ -98,12 +98,12 @@ export default { throw error } }, - setSettings: async (parent, { tipDefault }, { me, models }) => { + setSettings: async (parent, data, { me, models }) => { if (!me) { throw new AuthenticationError('you must be logged in') } - await models.user.update({ where: { id: me.id }, data: { tipDefault } }) + await models.user.update({ where: { id: me.id }, data }) return true }, @@ -172,10 +172,12 @@ export default { }) return !!anInvite }, - hasNewNotes: async (user, args, { models }) => { - // check if any votes have been cast for them since checkedNotesAt + hasNewNotes: async (user, args, { me, models }) => { const lastChecked = user.checkedNotesAt || new Date(0) - const votes = await models.$queryRaw(` + + // check if any votes have been cast for them since checkedNotesAt + if (me.noteItemSats) { + const votes = await models.$queryRaw(` SELECT "ItemAct".id, "ItemAct".created_at FROM "Item" JOIN "ItemAct" on "ItemAct"."itemId" = "Item".id @@ -184,15 +186,16 @@ export default { AND "ItemAct".act <> 'BOOST' AND "Item"."userId" = $1 LIMIT 1`, user.id, lastChecked) - if (votes.length > 0) { - return true + if (votes.length > 0) { + return true + } } // check if they have any replies since checkedNotesAt const newReplies = await models.$queryRaw(` SELECT "Item".id, "Item".created_at FROM "Item" - JOIN "Item" p ON "Item".path <@ p.path + JOIN "Item" p ON ${me.noteAllDescendants ? '"Item".path <@ p.path' : '"Item"."parentId" = p.id'} WHERE p."userId" = $1 AND "Item".created_at > $2 AND "Item"."userId" <> $1 LIMIT 1`, user.id, lastChecked) @@ -201,7 +204,8 @@ export default { } // check if they have any mentions since checkedNotesAt - const newMentions = await models.$queryRaw(` + if (me.noteMentions) { + const newMentions = await models.$queryRaw(` SELECT "Item".id, "Item".created_at FROM "Mention" JOIN "Item" ON "Mention"."itemId" = "Item".id @@ -209,8 +213,9 @@ export default { AND "Mention".created_at > $2 AND "Item"."userId" <> $1 LIMIT 1`, user.id, lastChecked) - if (newMentions.length > 0) { - return true + if (newMentions.length > 0) { + return true + } } const job = await models.item.findFirst({ @@ -231,41 +236,51 @@ export default { return true } - const earn = await models.earn.findFirst({ - where: { - userId: user.id, - createdAt: { - gt: lastChecked - }, - msats: { - gte: 1000 + if (me.noteEarning) { + const earn = await models.earn.findFirst({ + where: { + userId: user.id, + createdAt: { + gt: lastChecked + }, + msats: { + gte: 1000 + } } + }) + if (earn) { + return true } - }) - if (earn) { - return true } - const invoice = await models.invoice.findFirst({ - where: { - userId: user.id, - confirmedAt: { - gt: lastChecked + if (me.noteDeposits) { + const invoice = await models.invoice.findFirst({ + where: { + userId: user.id, + confirmedAt: { + gt: lastChecked + } } + }) + if (invoice) { + return true } - }) - if (invoice) { - return true } // check if new invites have been redeemed - const newInvitees = await models.$queryRaw(` + if (me.noteInvites) { + const newInvitees = await models.$queryRaw(` SELECT "Invite".id FROM users JOIN "Invite" on users."inviteId" = "Invite".id WHERE "Invite"."userId" = $1 AND users.created_at > $2 LIMIT 1`, user.id, lastChecked) - return newInvitees.length > 0 + if (newInvitees.length > 0) { + return true + } + } + + return false } } } diff --git a/api/ssrApollo.js b/api/ssrApollo.js index 3f6b7cc2..8e7581fb 100644 --- a/api/ssrApollo.js +++ b/api/ssrApollo.js @@ -22,7 +22,9 @@ export default async function getSSRApolloClient (req, me = null) { }), context: { models, - me: session ? session.user : me, + me: session + ? await models.user.findUnique({ where: { id: session.user?.id } }) + : me, lnd, search } @@ -42,14 +44,24 @@ export function getGetServerSideProps (query, variables = null, notFoundFunc, re } } - const { error, data } = await client.query({ - query, - variables: vars - }) + let error = null; let data = null; let props = {} + if (query) { + ({ error, data } = await client.query({ + query, + variables: vars + })) - if (error || !data || (notFoundFunc && notFoundFunc(data))) { - return { - notFound: true + if (error || !data || (notFoundFunc && notFoundFunc(data))) { + return { + notFound: true + } + } + + props = { + apollo: { + query: print(query), + variables: { ...params, ...variables } + } } } @@ -61,10 +73,7 @@ export function getGetServerSideProps (query, variables = null, notFoundFunc, re return { props: { - apollo: { - query: print(query), - variables: { ...params, ...variables } - }, + ...props, me, price, data diff --git a/api/typeDefs/user.js b/api/typeDefs/user.js index 0c2ec1ea..9ae7a878 100644 --- a/api/typeDefs/user.js +++ b/api/typeDefs/user.js @@ -27,7 +27,9 @@ export default gql` extend type Mutation { setName(name: String!): Boolean - setSettings(tipDefault: Int!): Boolean + setSettings(tipDefault: Int!, noteItemSats: Boolean!, noteEarning: Boolean!, + noteAllDescendants: Boolean!, noteMentions: Boolean!, noteDeposits: Boolean!, + noteInvites:Boolean!): Boolean upsertBio(bio: String!): User! setWalkthrough(tipPopover: Boolean, upvotePopover: Boolean): Boolean } @@ -48,5 +50,11 @@ export default gql` sats: Int! upvotePopover: Boolean! tipPopover: Boolean! + noteItemSats: Boolean! + noteEarning: Boolean! + noteAllDescendants: Boolean! + noteMentions: Boolean! + noteDeposits: Boolean! + noteInvites: Boolean! } ` diff --git a/fragments/users.js b/fragments/users.js index 844b0c0a..35d21abf 100644 --- a/fragments/users.js +++ b/fragments/users.js @@ -19,6 +19,12 @@ export const ME = gql` hasInvites upvotePopover tipPopover + noteItemSats + noteEarning + noteAllDescendants + noteMentions + noteDeposits + noteInvites } }` diff --git a/pages/api/graphql.js b/pages/api/graphql.js index 2bc14047..16c0b0fa 100644 --- a/pages/api/graphql.js +++ b/pages/api/graphql.js @@ -15,7 +15,9 @@ global.apolloServer ||= new ApolloServer({ return { models, lnd, - me: session ? session.user : null, + me: session + ? await models.user.findUnique({ where: { id: session.user?.id } }) + : null, search } } diff --git a/pages/post.js b/pages/post.js index 12a01b23..041a7518 100644 --- a/pages/post.js +++ b/pages/post.js @@ -5,6 +5,9 @@ import LayoutCenter from '../components/layout-center' import { useMe } from '../components/me' import { DiscussionForm } from '../components/discussion-form' import { LinkForm } from '../components/link-form' +import { getGetServerSideProps } from '../api/ssrApollo' + +export const getServerSideProps = getGetServerSideProps() export function PostForm () { const router = useRouter() diff --git a/pages/settings.js b/pages/settings.js index f81d4d7e..7385a580 100644 --- a/pages/settings.js +++ b/pages/settings.js @@ -1,10 +1,13 @@ -import { Form, Input, SubmitButton } from '../components/form' +import { Checkbox, Form, Input, SubmitButton } from '../components/form' import * as Yup from 'yup' import { Alert, InputGroup } from 'react-bootstrap' import { useMe } from '../components/me' import LayoutCenter from '../components/layout-center' import { useState } from 'react' import { gql, useMutation } from '@apollo/client' +import { getGetServerSideProps } from '../api/ssrApollo' + +export const getServerSideProps = getGetServerSideProps() export const SettingsSchema = Yup.object({ tipDefault: Yup.number().typeError('must be a number').required('required') @@ -16,8 +19,12 @@ export default function Settings () { const [success, setSuccess] = useState() const [setSettings] = useMutation( gql` - mutation setSettings($tipDefault: Int!) { - setSettings(tipDefault: $tipDefault) + mutation setSettings($tipDefault: Int!, $noteItemSats: Boolean!, $noteEarning: Boolean!, + $noteAllDescendants: Boolean!, $noteMentions: Boolean!, $noteDeposits: Boolean!, + $noteInvites: Boolean!) { + setSettings(tipDefault: $tipDefault, noteItemSats: $noteItemSats, + noteEarning: $noteEarning, noteAllDescendants: $noteAllDescendants, + noteMentions: $noteMentions, noteDeposits: $noteDeposits, noteInvites: $noteInvites) }` ) @@ -26,11 +33,17 @@ export default function Settings () {