= {
[LeaderboardType.MostUpvoted]: MOST_UPVOTED_QUERY,
[LeaderboardType.MostReferrals]: MOST_REFERRALS_QUERY,
[LeaderboardType.MostReadingDays]: MOST_READING_DAYS_QUERY,
- [LeaderboardType.MostAchievementPoints]: MOST_ACHIEVEMENT_POINTS_QUERY,
[LeaderboardType.MostQuestsCompleted]: MOST_QUESTS_COMPLETED_QUERY,
[LeaderboardType.HighestLevel]: HIGHEST_LEVEL_QUERY,
[LeaderboardType.MostVerifiedUsers]: MOST_VERIFIED_USERS_QUERY,
diff --git a/packages/shared/src/graphql/user/achievements.ts b/packages/shared/src/graphql/user/achievements.ts
index 2c3ef321906..6b902aeec5c 100644
--- a/packages/shared/src/graphql/user/achievements.ts
+++ b/packages/shared/src/graphql/user/achievements.ts
@@ -20,7 +20,7 @@ export interface Achievement {
image: string;
type: AchievementType;
criteria?: AchievementCriteria;
- points: number;
+ xp: number;
rarity: number | null;
unit: string | null;
}
@@ -53,8 +53,9 @@ export interface AchievementSyncStatus {
}
export interface AchievementSyncResult extends AchievementSyncStatus {
- pointsGained: number;
- totalPoints: number;
+ xpGained: number;
+ /** XP from unlocked achievements only, not the user's whole balance. */
+ totalAchievementXp: number;
newlyUnlockedAchievements: UserAchievement[];
closeAchievements: UserAchievement[];
}
@@ -95,7 +96,7 @@ const ACHIEVEMENT_FRAGMENT = gql`
criteria {
targetCount
}
- points
+ xp
rarity
unit
}
@@ -194,8 +195,8 @@ export const SYNC_ACHIEVEMENTS_MUTATION = gql`
remainingSyncs
canSync
syncedAchievements
- pointsGained
- totalPoints
+ xpGained
+ totalAchievementXp
newlyUnlockedAchievements {
achievement {
...AchievementFragment
diff --git a/packages/shared/src/hooks/profile/useProfileAchievements.ts b/packages/shared/src/hooks/profile/useProfileAchievements.ts
index 6f13871f177..8b9479aeb95 100644
--- a/packages/shared/src/hooks/profile/useProfileAchievements.ts
+++ b/packages/shared/src/hooks/profile/useProfileAchievements.ts
@@ -7,7 +7,8 @@ interface UseProfileAchievementsResult {
achievements: UserAchievement[] | undefined;
unlockedCount: number;
totalCount: number;
- totalPoints: number;
+ /** XP from unlocked achievements only, not the user's whole balance. */
+ totalAchievementXp: number;
isPending: boolean;
isError: boolean;
}
@@ -38,8 +39,8 @@ export function useProfileAchievements(
const unlocked = data?.filter((a) => a.unlockedAt !== null) ?? [];
const unlockedCount = unlocked.length;
const totalCount = data?.length ?? 0;
- const totalPoints = unlocked.reduce(
- (sum, a) => sum + (a.achievement.points ?? 0),
+ const totalAchievementXp = unlocked.reduce(
+ (sum, a) => sum + (a.achievement.xp ?? 0),
0,
);
@@ -47,7 +48,7 @@ export function useProfileAchievements(
achievements: data,
unlockedCount,
totalCount,
- totalPoints,
+ totalAchievementXp,
isPending,
isError,
};
diff --git a/packages/storybook/stories/features/snapshot/ShareImages.stories.tsx b/packages/storybook/stories/features/snapshot/ShareImages.stories.tsx
index f487e95cec7..dd2a1288e93 100644
--- a/packages/storybook/stories/features/snapshot/ShareImages.stories.tsx
+++ b/packages/storybook/stories/features/snapshot/ShareImages.stories.tsx
@@ -172,7 +172,7 @@ const PLACEMENTS: Placement[] = [
name: `achievement-${index}`,
image,
}))}
- points={1240}
+ xp={1240}
seed="achievements"
total={60}
unlocked={18}
diff --git a/packages/storybook/stories/features/snapshot/SnapshotPlacements.stories.tsx b/packages/storybook/stories/features/snapshot/SnapshotPlacements.stories.tsx
index 2144687b361..d9fbf829918 100644
--- a/packages/storybook/stories/features/snapshot/SnapshotPlacements.stories.tsx
+++ b/packages/storybook/stories/features/snapshot/SnapshotPlacements.stories.tsx
@@ -693,7 +693,7 @@ const AchievementBox = ({ entry }: { entry: UserAchievement }) => {
isUnlocked ? 'text-text-primary' : 'text-text-tertiary',
)}
>
- {entry.achievement.points}
+ {entry.achievement.xp}
@@ -1007,7 +1007,7 @@ const Placements = () => {
step="Placement 7"
leads="Snapshot"
title="Achievements page — per achievement box"
- note="Snapshot leads: an unlocked achievement is status with no shareable URL. Icon-only on hover, beside the points value."
+ note="Snapshot leads: an unlocked achievement is status with no shareable URL. Icon-only on hover, beside the xp value."
>
{[ACHIEVEMENT, LOCKED_ACHIEVEMENT].map((entry) => (
diff --git a/packages/webapp/__tests__/UsersLeaderboardStaticProps.spec.ts b/packages/webapp/__tests__/UsersLeaderboardStaticProps.spec.ts
index 8658fb2207c..203300764d8 100644
--- a/packages/webapp/__tests__/UsersLeaderboardStaticProps.spec.ts
+++ b/packages/webapp/__tests__/UsersLeaderboardStaticProps.spec.ts
@@ -31,7 +31,6 @@ const baseLeaderboardResponse = {
mostUpvoted: [],
mostReferrals: [],
mostReadingDays: [],
- mostAchievementPoints: [],
mostVerifiedUsers: [],
};
diff --git a/packages/webapp/lib/gameCenter.spec.ts b/packages/webapp/lib/gameCenter.spec.ts
index 46a2cdfa432..89c562040dd 100644
--- a/packages/webapp/lib/gameCenter.spec.ts
+++ b/packages/webapp/lib/gameCenter.spec.ts
@@ -47,7 +47,7 @@ const createAchievement = (
overrides: Partial & {
id: string;
name: string;
- points?: number;
+ xp?: number;
},
): UserAchievement => ({
achievement: {
@@ -57,7 +57,7 @@ const createAchievement = (
image: 'https://daily.dev/achievement.png',
type: AchievementType.Milestone,
criteria: { targetCount: 10 },
- points: overrides.points ?? 100,
+ xp: overrides.xp ?? 100,
rarity: 10,
unit: 'posts',
},
@@ -214,13 +214,13 @@ describe('game center helpers', () => {
id: 'tracked',
name: 'Tracked',
progress: 9,
- points: 50,
+ xp: 50,
});
const rareUnlocked = createAchievement({
id: 'rare',
name: 'Rare unlocked',
unlockedAt: '2025-03-01T00:00:00.000Z',
- points: 200,
+ xp: 200,
achievement: {
id: 'rare',
name: 'Rare unlocked',
@@ -228,7 +228,7 @@ describe('game center helpers', () => {
image: 'https://daily.dev/rare.png',
type: AchievementType.Milestone,
criteria: { targetCount: 10 },
- points: 200,
+ xp: 200,
rarity: 1,
unit: 'posts',
},
@@ -237,7 +237,7 @@ describe('game center helpers', () => {
id: 'latest',
name: 'Latest unlocked',
unlockedAt: '2025-03-10T00:00:00.000Z',
- points: 120,
+ xp: 120,
});
const summary = getAchievementSummary(
@@ -247,7 +247,7 @@ describe('game center helpers', () => {
expect(summary.unlockedCount).toBe(2);
expect(summary.totalCount).toBe(3);
- expect(summary.totalPoints).toBe(320);
+ expect(summary.totalAchievementXp).toBe(320);
expect(summary.nextToUnlock?.achievement.id).toBe('tracked');
expect(summary.latestUnlocked?.achievement.id).toBe('latest');
expect(summary.rarestUnlocked?.achievement.id).toBe('rare');
diff --git a/packages/webapp/lib/gameCenter.ts b/packages/webapp/lib/gameCenter.ts
index 1ab04e5eab8..0f48aa88ce5 100644
--- a/packages/webapp/lib/gameCenter.ts
+++ b/packages/webapp/lib/gameCenter.ts
@@ -185,7 +185,8 @@ const dedupeAchievements = (
export type GameCenterAchievementSummary = {
unlockedCount: number;
totalCount: number;
- totalPoints: number;
+ /** XP from unlocked achievements only, not the user's whole balance. */
+ totalAchievementXp: number;
latestUnlocked: UserAchievement | null;
rarestUnlocked: UserAchievement | null;
nextToUnlock: UserAchievement | null;
@@ -235,7 +236,7 @@ export const getAchievementSummary = (
return right.progress - left.progress;
}
- return right.achievement.points - left.achievement.points;
+ return right.achievement.xp - left.achievement.xp;
})[0] ?? null;
const featuredAchievements = dedupeAchievements([
@@ -248,8 +249,8 @@ export const getAchievementSummary = (
return {
unlockedCount: unlocked.length,
totalCount: allAchievements.length,
- totalPoints: unlocked.reduce(
- (total, achievement) => total + (achievement.achievement.points ?? 0),
+ totalAchievementXp: unlocked.reduce(
+ (total, achievement) => total + (achievement.achievement.xp ?? 0),
0,
),
latestUnlocked,
diff --git a/packages/webapp/pages/users.tsx b/packages/webapp/pages/users.tsx
index 13d02f08183..1a46ccbbdf8 100644
--- a/packages/webapp/pages/users.tsx
+++ b/packages/webapp/pages/users.tsx
@@ -45,7 +45,6 @@ interface PageProps {
mostUpvoted: UserLeaderboard[];
mostReferrals: UserLeaderboard[];
mostReadingDays: UserLeaderboard[];
- mostAchievementPoints: UserLeaderboard[];
highestLevel: UserLeaderboard[];
isHighestLevelSupported: boolean;
mostVerifiedUsers: CompanyLeaderboard[];
@@ -77,7 +76,6 @@ const LeaderboardPage = ({
mostUpvoted,
mostReferrals,
mostReadingDays,
- mostAchievementPoints,
highestLevel,
isHighestLevelSupported,
mostVerifiedUsers,
@@ -167,14 +165,6 @@ const LeaderboardPage = ({
isLoading={isLoading}
leaderboardType={LeaderboardType.MostReadingDays}
/>
-
leaderboardType === LeaderboardType.MostQuestsCompleted
? MOST_QUESTS_COMPLETED_LIMIT
@@ -158,6 +160,17 @@ export async function getStaticProps({
> {
const { id } = params || {};
+ // Achievement points were folded into XP, so highest level is now the
+ // superset of this board. Bookmarks and shared links land here.
+ if (id === RETIRED_ACHIEVEMENT_POINTS_SLUG) {
+ return {
+ redirect: {
+ destination: `/users/${LeaderboardType.HighestLevel}`,
+ permanent: true,
+ },
+ };
+ }
+
if (!id || !Object.values(LeaderboardType).includes(id as LeaderboardType)) {
return {
notFound: true,