From 0d8ab8413a6044a3be14b3b2041bff05d24de5fb Mon Sep 17 00:00:00 2001 From: "Lin, Hung-Shin" Date: Thu, 23 Nov 2023 15:04:39 +0800 Subject: [PATCH 01/10] feat: add leaderboard declarations --- app/src/router.js | 6 ++++++ app/src/views/LeaderboardPage.vue | 6 ++++++ server/src/api/controllers/leaderboardController.js | 5 +++++ server/src/api/routes/index.js | 3 +++ 4 files changed, 20 insertions(+) create mode 100644 app/src/views/LeaderboardPage.vue create mode 100644 server/src/api/controllers/leaderboardController.js diff --git a/app/src/router.js b/app/src/router.js index 76f8280..1d3b420 100644 --- a/app/src/router.js +++ b/app/src/router.js @@ -4,6 +4,7 @@ import LoginPage from './views/LoginPage.vue' import RegisterPage from './views/RegisterPage.vue' import ChooseUsernamePage from './views/ChooseUsernamePage.vue' import QuestMapPage from './views/QuestMapPage.vue' +import LeaderboardPage from './views/LeaderboardPage.vue' import { isLoggedIn } from './utils/auth' const routes = [ @@ -45,6 +46,11 @@ const routes = [ component: QuestMapPage, meta: { requiresAuth: true }, }, + { + path: '/leaderboard', + name: 'leaderboard', + component: LeaderboardPage, + }, ] const router = createRouter({ diff --git a/app/src/views/LeaderboardPage.vue b/app/src/views/LeaderboardPage.vue new file mode 100644 index 0000000..55daefd --- /dev/null +++ b/app/src/views/LeaderboardPage.vue @@ -0,0 +1,6 @@ + diff --git a/server/src/api/controllers/leaderboardController.js b/server/src/api/controllers/leaderboardController.js new file mode 100644 index 0000000..1d351e3 --- /dev/null +++ b/server/src/api/controllers/leaderboardController.js @@ -0,0 +1,5 @@ +import { asyncHandler } from '../../middleware/error.js' + +export default asyncHandler(async (req, res) => { + res.json({ message: 'leaderboard' }) +}) diff --git a/server/src/api/routes/index.js b/server/src/api/routes/index.js index 427cf91..2cfd5ec 100644 --- a/server/src/api/routes/index.js +++ b/server/src/api/routes/index.js @@ -7,6 +7,7 @@ import authRouter from './authRouter.js' import authRequired from '../../middleware/authRequired.js' import userRouter from './userRouter.js' import config from '../../config.js' +import leaderboardController from '../controllers/leaderboardController.js' const router = Router() @@ -48,4 +49,6 @@ router.get('/bug-report', (req, res) => { res.redirect(config.bugReportUrl) }) +router.get('/leaderboard', leaderboardController) + export default router From 196187d5c967d3c68b8c957a801db7a6b3ee0654 Mon Sep 17 00:00:00 2001 From: wancat Date: Thu, 23 Nov 2023 15:42:31 +0800 Subject: [PATCH 02/10] feat: add leaderboard api --- .../api/controllers/leaderboardController.js | 9 +++- server/src/models/userManager.js | 43 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/server/src/api/controllers/leaderboardController.js b/server/src/api/controllers/leaderboardController.js index 1d351e3..e3cb7ee 100644 --- a/server/src/api/controllers/leaderboardController.js +++ b/server/src/api/controllers/leaderboardController.js @@ -1,5 +1,12 @@ import { asyncHandler } from '../../middleware/error.js' +import { leaderboard } from '../../models/userManager.js' export default asyncHandler(async (req, res) => { - res.json({ message: 'leaderboard' }) + const users = await leaderboard() + const response = users.map((user) => ({ + username: user.user.username, + completedQuests: user.completedQuests, + numberOfCompletedQuests: user.numberOfCompletedQuests, + })) + res.json(response) }) diff --git a/server/src/models/userManager.js b/server/src/models/userManager.js index 46943dd..8168eb5 100644 --- a/server/src/models/userManager.js +++ b/server/src/models/userManager.js @@ -22,3 +22,46 @@ export async function createUser(username, email, { password, socialLogin }) { return user } + +export function leaderboard() { + return UserProfile.aggregate([ + { + $project: { + user: true, // Include the user field in the projection + completedQuests: { + $map: { + input: { + $filter: { + input: { $objectToArray: '$progress' }, // Convert the progress map to an array + as: 'prog', + cond: { $eq: ['$$prog.v.completed', true] }, // Filter for completed quests + }, + }, + as: 'completedProg', + in: '$$completedProg.k', // Extract the quest IDs + }, + }, + }, + }, + { + $addFields: { + numberOfCompletedQuests: { $size: '$completedQuests' }, // Calculate the size of the completedQuests array + }, + }, + { + $match: { + numberOfCompletedQuests: { $gt: 0 }, // Filter out users who haven't completed any quests + }, + }, + { $sort: { numberOfCompletedQuests: -1 } }, + { + $lookup: { + from: 'users', + localField: 'user', + foreignField: '_id', + as: 'user', + }, + }, + { $unwind: '$user' }, + ]) +} From 1a5655d7ced5f097ca4f6596b78c1a1b730f5fdf Mon Sep 17 00:00:00 2001 From: wancat Date: Thu, 23 Nov 2023 15:49:06 +0800 Subject: [PATCH 03/10] feat: rename numberOfCompletedQuests to score --- server/src/api/controllers/leaderboardController.js | 2 +- server/src/models/userManager.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/src/api/controllers/leaderboardController.js b/server/src/api/controllers/leaderboardController.js index e3cb7ee..4f497a1 100644 --- a/server/src/api/controllers/leaderboardController.js +++ b/server/src/api/controllers/leaderboardController.js @@ -6,7 +6,7 @@ export default asyncHandler(async (req, res) => { const response = users.map((user) => ({ username: user.user.username, completedQuests: user.completedQuests, - numberOfCompletedQuests: user.numberOfCompletedQuests, + score: user.score * 1000, })) res.json(response) }) diff --git a/server/src/models/userManager.js b/server/src/models/userManager.js index 8168eb5..d2f5faf 100644 --- a/server/src/models/userManager.js +++ b/server/src/models/userManager.js @@ -45,15 +45,15 @@ export function leaderboard() { }, { $addFields: { - numberOfCompletedQuests: { $size: '$completedQuests' }, // Calculate the size of the completedQuests array + score: { $size: '$completedQuests' }, // Calculate the size of the completedQuests array }, }, { $match: { - numberOfCompletedQuests: { $gt: 0 }, // Filter out users who haven't completed any quests + score: { $gt: 0 }, // Filter out users who haven't completed any quests }, }, - { $sort: { numberOfCompletedQuests: -1 } }, + { $sort: { score: -1 } }, { $lookup: { from: 'users', From a90b0a3c83b487f78695bef79b38da3a6674e3f3 Mon Sep 17 00:00:00 2001 From: wancat Date: Thu, 23 Nov 2023 15:49:20 +0800 Subject: [PATCH 04/10] feat: load leadboard data in frontend --- app/src/views/LeaderboardPage.vue | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/app/src/views/LeaderboardPage.vue b/app/src/views/LeaderboardPage.vue index 55daefd..779b066 100644 --- a/app/src/views/LeaderboardPage.vue +++ b/app/src/views/LeaderboardPage.vue @@ -1,6 +1,33 @@ + + From 790e09b3153d523389d8760364761d0caa96b8e6 Mon Sep 17 00:00:00 2001 From: wancat Date: Thu, 23 Nov 2023 16:28:47 +0800 Subject: [PATCH 05/10] wip: design leaderboard --- app/src/components/GameHeaderPart.vue | 9 ++- app/src/views/LeaderboardPage.vue | 58 +++++++++++++++++-- .../api/controllers/leaderboardController.js | 7 ++- 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/app/src/components/GameHeaderPart.vue b/app/src/components/GameHeaderPart.vue index 3cbf961..3c93ed9 100644 --- a/app/src/components/GameHeaderPart.vue +++ b/app/src/components/GameHeaderPart.vue @@ -69,10 +69,17 @@ onMounted(async () => { class="text-text-tertiary h-full w-full" /> --> -

+ Linux Odyssey + +

{{ title }}

diff --git a/app/src/views/LeaderboardPage.vue b/app/src/views/LeaderboardPage.vue index 779b066..86ff408 100644 --- a/app/src/views/LeaderboardPage.vue +++ b/app/src/views/LeaderboardPage.vue @@ -1,6 +1,8 @@