diff --git a/src/routes/postBranding.ts b/src/routes/postBranding.ts index e38418b..16a8f5c 100644 --- a/src/routes/postBranding.ts +++ b/src/routes/postBranding.ts @@ -88,7 +88,7 @@ export async function postBranding(req: Request, res: Response) { await handleExistingVotes(BrandingType.Title, videoID, hashedUserID, UUID, hashedIP, voteType); if (existingUUID) { - await updateVoteTotals(BrandingType.Title, UUID, shouldLock, !!downvote); + await updateVoteTotals(BrandingType.Title, UUID, hashedUserID, shouldLock, !!downvote); } else { if (downvote) { throw new Error("Title submission doesn't exist"); @@ -134,7 +134,7 @@ export async function postBranding(req: Request, res: Response) { await handleExistingVotes(BrandingType.Thumbnail, videoID, hashedUserID, UUID, hashedIP, voteType); if (existingUUID) { - await updateVoteTotals(BrandingType.Thumbnail, UUID, shouldLock, !!downvote); + await updateVoteTotals(BrandingType.Thumbnail, UUID, hashedUserID, shouldLock, !!downvote); } else { if (downvote) { throw new Error("Thumbnail submission doesn't exist"); @@ -185,7 +185,9 @@ export async function postBranding(req: Request, res: Response) { async function handleExistingVotes(type: BrandingType, videoID: VideoID, hashedUserID: HashedUserID, UUID: BrandingUUID, hashedIP: HashedIP, voteType: BrandingVoteType) { const table = type === BrandingType.Title ? `"titleVotes"` : `"thumbnailVotes"`; + const table2 = type === BrandingType.Title ? `"titles"` : `"thumbnails"`; + const isUsersSubmission = (await db.prepare("get", `SELECT "userID" FROM ${table2} WHERE "UUID" = ?`, [UUID]))?.userID === hashedUserID; const idsDealtWith: BrandingUUID[] = []; // Either votes of the same type, or on the same submission (undo a downvote) @@ -204,15 +206,33 @@ async function handleExistingVotes(type: BrandingType, videoID: VideoID, await privateDB.prepare("run", `DELETE FROM ${table} WHERE "id" = ?`, [existingVote.id]); break; - case BrandingVoteType.Downvote: + case BrandingVoteType.Downvote: { // Undoing a downvote now that it is being upvoted - await db.prepare("run", `UPDATE ${table} SET "downvotes" = "downvotes" - 1 WHERE "UUID" = ?`, [existingVote.UUID]); + + // Only undo downvote if it is not their submission + if (!isUsersSubmission) { + await db.prepare("run", `UPDATE ${table} SET "downvotes" = "downvotes" - 1 WHERE "UUID" = ?`, [existingVote.UUID]); + } + await privateDB.prepare("run", `DELETE FROM ${table} WHERE "id" = ?`, [existingVote.id]); break; + } + } + } + } else if (isUsersSubmission) { + // Treat like upvoting another submission (undoing upvote) + let dealtWith = false; + for (const existingVote of existingVotes) { + if (existingVote.type === BrandingVoteType.Upvote && existingVote.UUID === UUID) { + if (!dealtWith) { + dealtWith = true; + await db.prepare("run", `UPDATE ${table} SET "votes" = "votes" - 1 WHERE "UUID" = ?`, [UUID]); + } + + await privateDB.prepare("run", `DELETE FROM ${table} WHERE "id" = ?`, [existingVote.id]); } } } - } await privateDB.prepare("run", `INSERT INTO ${table} ("videoID", "UUID", "userID", "hashedIP", "type") VALUES (?, ?, ?, ?, ?)`, @@ -223,11 +243,16 @@ async function handleExistingVotes(type: BrandingType, videoID: VideoID, * Only called if an existing vote exists. * Will update public vote totals and locked status. */ -async function updateVoteTotals(type: BrandingType, UUID: BrandingUUID, shouldLock: boolean, downvote: boolean): Promise { +async function updateVoteTotals(type: BrandingType, UUID: BrandingUUID, userID: HashedUserID, shouldLock: boolean, downvote: boolean): Promise { const table = type === BrandingType.Title ? `"titleVotes"` : `"thumbnailVotes"`; + const table2 = type === BrandingType.Title ? `"titles"` : `"thumbnails"`; if (downvote) { - await db.prepare("run", `UPDATE ${table} SET "downvotes" = "downvotes" + 1 WHERE "UUID" = ?`, [UUID]); + // Only downvote if it is not their submission + const isUsersSubmission = (await db.prepare("get", `SELECT "userID" FROM ${table2} WHERE "UUID" = ?`, [UUID]))?.userID === userID; + if (!isUsersSubmission) { + await db.prepare("run", `UPDATE ${table} SET "downvotes" = "downvotes" + 1 WHERE "UUID" = ?`, [UUID]); + } } else { await db.prepare("run", `UPDATE ${table} SET "votes" = "votes" + 1 WHERE "UUID" = ?`, [UUID]); } diff --git a/test/cases/postBranding.ts b/test/cases/postBranding.ts index f4d606d..d2afcd2 100644 --- a/test/cases/postBranding.ts +++ b/test/cases/postBranding.ts @@ -317,6 +317,51 @@ describe("postBranding", () => { } }); + it("Downvote your own title and thumbnail", async () => { + const videoID = "postBrand5"; + const title = { + title: "Some other title", + original: false + }; + const thumbnail = { + timestamp: 13.42, + original: false + }; + + const res = await postBranding({ + title, + thumbnail, + userID: userID4, + service: Service.YouTube, + videoID, + downvote: true + }); + + assert.strictEqual(res.status, 200); + const dbTitles = await queryTitleByVideo(videoID, true); + for (const dbTitle of dbTitles) { + if (dbTitle.title === title.title) { + const dbTitleVotes = await queryTitleVotesByUUID(dbTitle.UUID); + assert.strictEqual(dbTitleVotes.votes, -1); + assert.strictEqual(dbTitleVotes.downvotes, 1); + assert.strictEqual(dbTitleVotes.locked, 0); + assert.strictEqual(dbTitleVotes.shadowHidden, 0); + } + } + + const dbThumbnails = await queryThumbnailByVideo(videoID, true); + for (const dbThumbnail of dbThumbnails) { + if (dbThumbnail.timestamp === thumbnail.timestamp) { + const dbThumbnailVotes = await queryThumbnailVotesByUUID(dbThumbnail.UUID); + + assert.strictEqual(dbThumbnailVotes.votes, -1); + assert.strictEqual(dbThumbnailVotes.downvotes, 1); + assert.strictEqual(dbThumbnailVotes.locked, 0); + assert.strictEqual(dbThumbnailVotes.shadowHidden, 0); + } + } + }); + it("Downvote another title and thumbnail", async () => { const videoID = "postBrand5"; const title = { @@ -340,21 +385,25 @@ describe("postBranding", () => { assert.strictEqual(res.status, 200); const dbTitles = await queryTitleByVideo(videoID, true); for (const dbTitle of dbTitles) { - const dbTitleVotes = await queryTitleVotesByUUID(dbTitle.UUID); - assert.strictEqual(dbTitleVotes.votes, 0); - assert.strictEqual(dbTitleVotes.downvotes, 1); - assert.strictEqual(dbTitleVotes.locked, 0); - assert.strictEqual(dbTitleVotes.shadowHidden, 0); + if (dbTitle.title === title.title) { + const dbTitleVotes = await queryTitleVotesByUUID(dbTitle.UUID); + assert.strictEqual(dbTitleVotes.votes, 0); + assert.strictEqual(dbTitleVotes.downvotes, 1); + assert.strictEqual(dbTitleVotes.locked, 0); + assert.strictEqual(dbTitleVotes.shadowHidden, 0); + } } const dbThumbnails = await queryThumbnailByVideo(videoID, true); for (const dbThumbnail of dbThumbnails) { const dbThumbnailVotes = await queryThumbnailVotesByUUID(dbThumbnail.UUID); - assert.strictEqual(dbThumbnailVotes.votes, 0); - assert.strictEqual(dbThumbnailVotes.downvotes, 1); - assert.strictEqual(dbThumbnailVotes.locked, 0); - assert.strictEqual(dbThumbnailVotes.shadowHidden, 0); + if (dbThumbnail.timestamp === thumbnail.timestamp) { + assert.strictEqual(dbThumbnailVotes.votes, 0); + assert.strictEqual(dbThumbnailVotes.downvotes, 1); + assert.strictEqual(dbThumbnailVotes.locked, 0); + assert.strictEqual(dbThumbnailVotes.shadowHidden, 0); + } } }); @@ -427,7 +476,7 @@ describe("postBranding", () => { for (const dbTitle of dbTitles) { if (dbTitle.title === title.title) { const dbTitleVotes = await queryTitleVotesByUUID(dbTitle.UUID); - assert.strictEqual(dbTitleVotes.votes, 1); + assert.strictEqual(dbTitleVotes.votes, 0); assert.strictEqual(dbTitleVotes.downvotes, 0); assert.strictEqual(dbTitleVotes.locked, 0); assert.strictEqual(dbTitleVotes.shadowHidden, 0); @@ -439,7 +488,7 @@ describe("postBranding", () => { if (dbThumbnail.timestamp === thumbnail.timestamp) { const dbThumbnailVotes = await queryThumbnailVotesByUUID(dbThumbnail.UUID); - assert.strictEqual(dbThumbnailVotes.votes, 1); + assert.strictEqual(dbThumbnailVotes.votes, 0); assert.strictEqual(dbThumbnailVotes.downvotes, 0); assert.strictEqual(dbThumbnailVotes.locked, 0); assert.strictEqual(dbThumbnailVotes.shadowHidden, 0);