2021-08-17 23:07:52 +00:00
|
|
|
import { AuthenticationError } from 'apollo-server-micro'
|
2021-09-06 22:36:08 +00:00
|
|
|
import { decodeCursor, LIMIT, nextCursorEncoded } from '../../lib/cursor'
|
2021-08-17 18:15:24 +00:00
|
|
|
|
|
|
|
export default {
|
|
|
|
Query: {
|
|
|
|
notifications: async (parent, { cursor }, { me, models }) => {
|
|
|
|
const decodedCursor = decodeCursor(cursor)
|
2021-08-17 23:59:22 +00:00
|
|
|
if (!me) {
|
|
|
|
throw new AuthenticationError('you must be logged in')
|
|
|
|
}
|
2021-08-17 18:15:24 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
So that we can cursor over results, we union notifications together ...
|
|
|
|
this requires we have the same number of columns in all results
|
|
|
|
|
|
|
|
select "Item".id, NULL as earnedSats, "Item".created_at as created_at from
|
|
|
|
"Item" JOIN "Item" p ON "Item"."parentId" = p.id AND p."userId" = 622 AND
|
|
|
|
"Item"."userId" <> 622 UNION ALL select "Item".id, "Vote".sats as earnedSats,
|
|
|
|
"Vote".created_at as created_at FROM "Item" LEFT JOIN "Vote" on
|
|
|
|
"Vote"."itemId" = "Item".id AND "Vote"."userId" <> 622 AND "Vote".boost = false
|
|
|
|
WHERE "Item"."userId" = 622 ORDER BY created_at DESC;
|
|
|
|
|
|
|
|
Because we want to "collapse" time adjacent votes in the result
|
|
|
|
|
|
|
|
select vote.id, sum(vote."earnedSats") as "earnedSats", max(vote.voted_at)
|
|
|
|
as "createdAt" from (select "Item".*, "Vote".sats as "earnedSats",
|
|
|
|
"Vote".created_at as voted_at, ROW_NUMBER() OVER(ORDER BY "Vote".created_at) -
|
|
|
|
ROW_NUMBER() OVER(PARTITION BY "Item".id ORDER BY "Vote".created_at) as island
|
|
|
|
FROM "Item" LEFT JOIN "Vote" on "Vote"."itemId" = "Item".id AND
|
|
|
|
"Vote"."userId" <> 622 AND "Vote".boost = false WHERE "Item"."userId" = 622)
|
|
|
|
as vote group by vote.id, vote.island order by max(vote.voted_at) desc;
|
|
|
|
|
|
|
|
We can also "collapse" votes occuring within 1 hour intervals of each other
|
|
|
|
(I haven't yet combined with the above collapsing method .. but might be
|
|
|
|
overkill)
|
|
|
|
|
|
|
|
select "Item".id, sum("Vote".sats) as earnedSats, max("Vote".created_at)
|
|
|
|
as created_at, ROW_NUMBER() OVER(ORDER BY max("Vote".created_at)) - ROW_NUMBER()
|
|
|
|
OVER(PARTITION BY "Item".id ORDER BY max("Vote".created_at)) as island FROM
|
|
|
|
"Item" LEFT JOIN "Vote" on "Vote"."itemId" = "Item".id AND "Vote"."userId" <> 622
|
|
|
|
AND "Vote".boost = false WHERE "Item"."userId" = 622 group by "Item".id,
|
|
|
|
date_trunc('hour', "Vote".created_at) order by created_at desc;
|
|
|
|
|
2021-10-28 22:22:19 +00:00
|
|
|
island approach we used to take
|
2021-08-20 00:13:32 +00:00
|
|
|
(SELECT ${ITEM_SUBQUERY_FIELDS}, max(subquery.voted_at) as "sortTime",
|
2021-09-02 22:22:00 +00:00
|
|
|
sum(subquery.sats) as "earnedSats", false as mention
|
|
|
|
FROM
|
2021-09-08 21:51:23 +00:00
|
|
|
(SELECT ${ITEM_FIELDS}, "ItemAct".created_at as voted_at, "ItemAct".sats,
|
|
|
|
ROW_NUMBER() OVER(ORDER BY "ItemAct".created_at) -
|
|
|
|
ROW_NUMBER() OVER(PARTITION BY "Item".id ORDER BY "ItemAct".created_at) as island
|
|
|
|
FROM "ItemAct"
|
|
|
|
JOIN "Item" on "ItemAct"."itemId" = "Item".id
|
|
|
|
WHERE "ItemAct"."userId" <> $1
|
|
|
|
AND "ItemAct".created_at <= $2
|
|
|
|
AND "ItemAct".act <> 'BOOST'
|
2021-09-02 22:22:00 +00:00
|
|
|
AND "Item"."userId" = $1) subquery
|
2021-10-07 03:20:59 +00:00
|
|
|
GROUP BY ${ITEM_SUBQUERY_FIELDS}, subquery.island
|
|
|
|
ORDER BY max(subquery.voted_at) desc
|
|
|
|
LIMIT ${LIMIT}+$3)
|
2021-10-28 22:22:19 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
// 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
|
|
|
|
let notifications = await models.$queryRaw(`
|
|
|
|
(SELECT ${ITEM_FIELDS}, "Item".created_at as "sortTime", NULL as "earnedSats",
|
|
|
|
false as mention
|
|
|
|
FROM "Item"
|
|
|
|
JOIN "Item" p ON "Item"."parentId" = p.id
|
|
|
|
WHERE p."userId" = $1
|
|
|
|
AND "Item"."userId" <> $1 AND "Item".created_at <= $2
|
|
|
|
ORDER BY "Item".created_at desc
|
|
|
|
LIMIT ${LIMIT}+$3)
|
|
|
|
UNION ALL
|
|
|
|
(SELECT ${ITEM_FIELDS}, max("ItemAct".created_at) as "sortTime",
|
|
|
|
sum("ItemAct".sats) as "earnedSats", false as mention
|
|
|
|
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_GROUP_FIELDS}
|
|
|
|
ORDER BY max("ItemAct".created_at) desc
|
|
|
|
LIMIT ${LIMIT}+$3)
|
2021-08-18 23:00:54 +00:00
|
|
|
UNION ALL
|
2021-08-20 00:13:32 +00:00
|
|
|
(SELECT ${ITEM_FIELDS}, "Mention".created_at as "sortTime", NULL as "earnedSats",
|
2021-09-02 22:22:00 +00:00
|
|
|
true as mention
|
|
|
|
FROM "Mention"
|
|
|
|
JOIN "Item" on "Mention"."itemId" = "Item".id
|
2021-09-24 23:05:29 +00:00
|
|
|
LEFT JOIN "Item" p on "Item"."parentId" = p.id
|
2021-09-02 22:22:00 +00:00
|
|
|
WHERE "Mention"."userId" = $1
|
|
|
|
AND "Mention".created_at <= $2
|
|
|
|
AND "Item"."userId" <> $1
|
2021-10-07 03:20:59 +00:00
|
|
|
AND (p."userId" IS NULL OR p."userId" <> $1)
|
2021-10-07 03:45:38 +00:00
|
|
|
ORDER BY "Mention".created_at desc
|
2021-10-07 03:20:59 +00:00
|
|
|
LIMIT ${LIMIT}+$3)
|
|
|
|
ORDER BY "sortTime" DESC
|
|
|
|
OFFSET $3
|
|
|
|
LIMIT ${LIMIT}`, me.id, decodedCursor.time, decodedCursor.offset)
|
2021-08-17 18:15:24 +00:00
|
|
|
|
|
|
|
notifications = notifications.map(n => {
|
|
|
|
n.item = { ...n }
|
|
|
|
return n
|
|
|
|
})
|
2021-08-17 23:59:22 +00:00
|
|
|
|
2021-08-20 00:13:32 +00:00
|
|
|
const { checkedNotesAt } = await models.user.findUnique({ where: { id: me.id } })
|
2021-09-06 22:36:08 +00:00
|
|
|
if (decodedCursor.offset === 0) {
|
|
|
|
await models.user.update({ where: { id: me.id }, data: { checkedNotesAt: new Date() } })
|
|
|
|
}
|
2021-08-17 23:59:22 +00:00
|
|
|
|
2021-08-17 18:15:24 +00:00
|
|
|
return {
|
2021-08-20 00:13:32 +00:00
|
|
|
lastChecked: checkedNotesAt,
|
2021-08-17 18:15:24 +00:00
|
|
|
cursor: notifications.length === LIMIT ? nextCursorEncoded(decodedCursor) : null,
|
|
|
|
notifications
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Notification: {
|
|
|
|
__resolveType: async (notification, args, { models }) =>
|
2021-08-18 23:00:54 +00:00
|
|
|
notification.earnedSats ? 'Votification' : (notification.mention ? 'Mention' : 'Reply')
|
2021-08-17 18:15:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-28 22:22:19 +00:00
|
|
|
// const ITEM_SUBQUERY_FIELDS =
|
|
|
|
// `subquery.id, subquery."createdAt", subquery."updatedAt", subquery.title, subquery.text,
|
|
|
|
// subquery.url, subquery."userId", subquery."parentId", subquery.path`
|
|
|
|
|
|
|
|
const ITEM_GROUP_FIELDS =
|
|
|
|
`"Item".id, "Item".created_at, "Item".updated_at, "Item".title,
|
|
|
|
"Item".text, "Item".url, "Item"."userId", "Item"."parentId", ltree2text("Item"."path")`
|
2021-08-17 18:15:24 +00:00
|
|
|
|
|
|
|
const ITEM_FIELDS =
|
|
|
|
`"Item".id, "Item".created_at as "createdAt", "Item".updated_at as "updatedAt", "Item".title,
|
2021-08-17 23:07:52 +00:00
|
|
|
"Item".text, "Item".url, "Item"."userId", "Item"."parentId", ltree2text("Item"."path") AS path`
|