db portion of mentions

This commit is contained in:
keyan 2021-08-18 17:20:33 -05:00
parent 534e772849
commit 3370675d61
4 changed files with 115 additions and 35 deletions

View File

@ -61,30 +61,20 @@ export default {
},
moreFlatComments: async (parent, { cursor, userId }, { me, models }) => {
const decodedCursor = decodeCursor(cursor)
let comments
if (userId) {
comments = await models.$queryRaw(`
${SELECT}
FROM "Item"
WHERE "userId" = $1 AND "parentId" IS NOT NULL
AND created_at <= $2
ORDER BY created_at DESC
OFFSET $3
LIMIT ${LIMIT}`, Number(userId), decodedCursor.time, decodedCursor.offset)
} else {
// notifications ... god such spagetti
if (!me) {
throw new AuthenticationError('you must be logged in')
}
comments = await models.$queryRaw(`
${SELECT}
From "Item"
JOIN "Item" p ON "Item"."parentId" = p.id AND p."userId" = $1
AND "Item"."userId" <> $1 AND "Item".created_at <= $2
ORDER BY "Item".created_at DESC
OFFSET $3
LIMIT ${LIMIT}`, me.id, decodedCursor.time, decodedCursor.offset)
if (!userId) {
throw new UserInputError('must supply userId', { argumentName: 'userId' })
}
const comments = await models.$queryRaw(`
${SELECT}
FROM "Item"
WHERE "userId" = $1 AND "parentId" IS NOT NULL
AND created_at <= $2
ORDER BY created_at DESC
OFFSET $3
LIMIT ${LIMIT}`, Number(userId), decodedCursor.time, decodedCursor.offset)
return {
cursor: comments.length === LIMIT ? nextCursorEncoded(decodedCursor) : null,
comments
@ -144,10 +134,7 @@ export default {
throw new UserInputError('item can no longer be editted')
}
return await models.item.update({
where: { id: Number(id) },
data: { title, url: ensureProtocol(url) }
})
return await updateItem(parent, { id, data: { title, url: ensureProtocol(url) } }, { me, models })
},
createDiscussion: async (parent, { title, text }, { me, models }) => {
if (!title) {
@ -175,10 +162,7 @@ export default {
throw new UserInputError('item can no longer be editted')
}
return await models.item.update({
where: { id: Number(id) },
data: { title, text }
})
return await updateItem(parent, { id, data: { title, text } }, { me, models })
},
createComment: async (parent, { text, parentId }, { me, models }) => {
if (!text) {
@ -210,10 +194,7 @@ export default {
throw new UserInputError('comment can no longer be editted')
}
return await models.item.update({
where: { id: Number(id) },
data: { text }
})
return await updateItem(parent, { id, data: { text } }, { me, models })
},
vote: async (parent, { id, sats = 1 }, { me, models }) => {
// need to make sure we are logged in
@ -302,6 +283,56 @@ export default {
}
}
const namePattern = /\B@[\w_]+/gi
const createMentions = async (item, models) => {
// if we miss a mention, in the rare circumstance there's some kind of
// failure, it's not a big deal so we don't do it transactionally
// ideally, we probably would
if (!item.text) {
return
}
try {
const mentions = item.text.match(namePattern).map(m => m.slice(1))
if (mentions.length > 0) {
const users = await models.user.findMany({
where: {
name: { in: mentions }
}
})
users.forEach(async user => {
const data = {
itemId: item.id,
userId: user.id
}
await models.mention.upsert({
where: {
itemId_userId: data
},
update: data,
create: data
})
})
}
} catch (e) {
console.log('mention failure', e)
}
}
const updateItem = async (parent, { id, data }, { me, models }) => {
const item = await models.item.update({
where: { id: Number(id) },
data
})
await createMentions(item, models)
return item
}
const createItem = async (parent, { title, url, text, parentId }, { me, models }) => {
if (!me) {
throw new AuthenticationError('you must be logged in')
@ -310,6 +341,9 @@ const createItem = async (parent, { title, url, text, parentId }, { me, models }
const [item] = await serialize(models, models.$queryRaw(
`${SELECT} FROM create_item($1, $2, $3, $4, $5) AS "Item"`,
title, url, text, Number(parentId), me.name))
await createMentions(item, models)
item.comments = []
return item
}

View File

@ -0,0 +1,22 @@
-- CreateTable
CREATE TABLE "Mention" (
"id" SERIAL NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
"itemId" INTEGER NOT NULL,
"userId" INTEGER NOT NULL,
PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "Mention.itemId_index" ON "Mention"("itemId");
-- CreateIndex
CREATE INDEX "Mention.userId_index" ON "Mention"("userId");
-- AddForeignKey
ALTER TABLE "Mention" ADD FOREIGN KEY ("itemId") REFERENCES "Item"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Mention" ADD FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -0,0 +1,8 @@
/*
Warnings:
- A unique constraint covering the columns `[itemId,userId]` on the table `Mention` will be added. If there are existing duplicate values, this will fail.
*/
-- CreateIndex
CREATE UNIQUE INDEX "Mention.itemId_userId_unique" ON "Mention"("itemId", "userId");

View File

@ -19,6 +19,7 @@ model User {
emailVerified DateTime? @map(name: "email_verified")
image String?
items Item[]
mentions Mention[]
messages Message[]
votes Vote[]
invoices Invoice[]
@ -60,6 +61,7 @@ model Item {
parentId Int?
children Item[] @relation("ParentChildren")
votes Vote[]
mentions Mention[]
path Unsupported("LTREE")?
@@index([userId])
@ -81,6 +83,20 @@ model Vote {
@@index([userId])
}
model Mention {
id Int @id @default(autoincrement())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
item Item @relation(fields: [itemId], references: [id])
itemId Int
user User @relation(fields: [userId], references: [id])
userId Int
@@unique([itemId, userId])
@@index([itemId])
@@index([userId])
}
model Invoice {
id Int @id @default(autoincrement())
createdAt DateTime @default(now()) @map(name: "created_at")