From 55295be558e14e7105ed0a2ca57390a9c12d5804 Mon Sep 17 00:00:00 2001 From: AmarTrebinjac Date: Thu, 16 Apr 2026 21:18:49 +0000 Subject: [PATCH] fix: hide new quest badge for users created less than 24 hours ago Users who just registered see everything as new, making the "new" quest rotation badge confusing and distracting from onboarding. This adds an account age check so the badge only appears for accounts older than 24h. Co-Authored-By: Claude Opus 4.6 --- .../src/components/quest/QuestButton.spec.tsx | 105 +++++++++++++----- .../src/components/quest/QuestButton.tsx | 6 +- 2 files changed, 83 insertions(+), 28 deletions(-) diff --git a/packages/shared/src/components/quest/QuestButton.spec.tsx b/packages/shared/src/components/quest/QuestButton.spec.tsx index 6615c0b377..56354d28ab 100644 --- a/packages/shared/src/components/quest/QuestButton.spec.tsx +++ b/packages/shared/src/components/quest/QuestButton.spec.tsx @@ -5,6 +5,7 @@ import { act, render, screen, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import type { ReactElement, ReactNode } from 'react'; import { TestBootProvider } from '../../../__tests__/helpers/boot'; +import loggedUser from '../../../__tests__/fixture/loggedUser'; import type { QuestDashboard } from '../../graphql/quests'; import { QuestRewardType, @@ -231,17 +232,25 @@ const questDashboard = { milestone: [], }; -const renderComponent = ( +const renderComponent = ({ optOutLevelSystem = false, - client: QueryClient = new QueryClient(), + client = new QueryClient(), compact = false, log = {}, -) => + auth = {}, +}: { + optOutLevelSystem?: boolean; + client?: QueryClient; + compact?: boolean; + log?: Record; + auth?: Record; +} = {}) => render( , @@ -276,7 +285,7 @@ beforeEach(() => { describe('QuestButton', () => { it('should show level progress and xp rewards when levels are enabled', async () => { - renderComponent(false); + renderComponent(); const button = screen.getByRole('button', { name: /Quests, level 7, 63% progress/i, @@ -294,7 +303,7 @@ describe('QuestButton', () => { }); it('should render a smaller trigger when compact', () => { - renderComponent(false, new QueryClient(), true); + renderComponent({ client: new QueryClient(), compact: true }); const button = screen.getByRole('button', { name: /Quests, level 7, 63% progress/i, @@ -317,7 +326,7 @@ describe('QuestButton', () => { isError: false, }); - renderComponent(false); + renderComponent(); expect( screen.getByRole('button', { @@ -327,7 +336,7 @@ describe('QuestButton', () => { }); it('should hide level progress and xp rewards when levels are disabled', async () => { - renderComponent(true); + renderComponent({ optOutLevelSystem: true }); const button = screen.getByRole('button', { name: 'Quests' }); @@ -343,7 +352,7 @@ describe('QuestButton', () => { }); it('should route feed-based quests back to the main feed', async () => { - renderComponent(false); + renderComponent(); await userEvent.click( screen.getByRole('button', { @@ -396,7 +405,7 @@ describe('QuestButton', () => { isError: false, }); - renderComponent(false); + renderComponent(); await userEvent.click( screen.getByRole('button', { @@ -444,7 +453,7 @@ describe('QuestButton', () => { isError: false, }); - renderComponent(false); + renderComponent(); await userEvent.click( screen.getByRole('button', { @@ -490,7 +499,7 @@ describe('QuestButton', () => { isError: false, }); - renderComponent(false); + renderComponent(); await userEvent.click( screen.getByRole('button', { @@ -534,7 +543,7 @@ describe('QuestButton', () => { isError: false, }); - renderComponent(false); + renderComponent(); await userEvent.click( screen.getByRole('button', { @@ -578,7 +587,7 @@ describe('QuestButton', () => { isError: false, }); - renderComponent(false); + renderComponent(); await userEvent.click( screen.getByRole('button', { @@ -598,7 +607,7 @@ describe('QuestButton', () => { it('should log when opening the quest dropdown', async () => { const logEvent = jest.fn(); - renderComponent(false, new QueryClient(), false, { logEvent }); + renderComponent({ log: { logEvent } }); await userEvent.click( screen.getByRole('button', { @@ -613,7 +622,7 @@ describe('QuestButton', () => { }); it('should not show a new indicator when the dashboard reports no new quest rotations', async () => { - renderComponent(false); + renderComponent(); expect( screen.getByRole('button', { @@ -627,6 +636,10 @@ describe('QuestButton', () => { it('should show and clear the new quest indicator after a quest rotation update', async () => { const client = new QueryClient(); + const establishedUser = { + ...loggedUser, + createdAt: new Date(Date.now() - 48 * 60 * 60 * 1000).toISOString(), + }; const subscriptions: Array<{ query: string; next?: () => unknown; @@ -666,7 +679,7 @@ describe('QuestButton', () => { ); const view = render( - + , ); @@ -698,7 +711,7 @@ describe('QuestButton', () => { ?.next?.(); view.rerender( - + , ); @@ -727,7 +740,7 @@ describe('QuestButton', () => { }); view.rerender( - + , ); @@ -921,7 +934,7 @@ describe('QuestButton', () => { }); it('should stay open when the page scrolls', async () => { - renderComponent(false); + renderComponent(); await userEvent.click( screen.getByRole('button', { @@ -969,7 +982,7 @@ describe('QuestButton', () => { isError: false, }); - renderComponent(false); + renderComponent(); await userEvent.click( screen.getByRole('button', { name: /Quests, level 7, 63% progress/i, @@ -1029,7 +1042,7 @@ describe('QuestButton', () => { isError: false, }); - renderComponent(false); + renderComponent(); await userEvent.click( screen.getByRole('button', { @@ -1123,7 +1136,7 @@ describe('QuestButton', () => { variables: undefined, }); - renderComponent(false); + renderComponent(); act(() => { screen @@ -1265,7 +1278,7 @@ describe('QuestButton', () => { variables: undefined, }); - renderComponent(false); + renderComponent(); act(() => { screen @@ -1338,7 +1351,7 @@ describe('QuestButton', () => { isError: false, }); - renderComponent(false); + renderComponent(); await userEvent.click( screen.getByRole('button', { @@ -1388,7 +1401,7 @@ describe('QuestButton', () => { isError: false, }); - renderComponent(false); + renderComponent(); await userEvent.click( screen.getByRole('button', { @@ -1435,7 +1448,7 @@ describe('QuestButton', () => { isError: false, }); - renderComponent(false); + renderComponent(); await userEvent.click( screen.getByRole('button', { @@ -1473,7 +1486,7 @@ describe('QuestButton', () => { }, ); - renderComponent(false, client); + renderComponent({ client }); expect( Array.from( @@ -1501,4 +1514,42 @@ describe('QuestButton', () => { exact: true, }); }); + + it('should not show new indicator for users created less than 24 hours ago', () => { + mockUseQuestDashboard.mockReturnValue({ + data: { ...questDashboard, hasNewQuestRotations: true }, + isPending: false, + isError: false, + }); + + const newUser = { + ...loggedUser, + createdAt: new Date(Date.now() - 12 * 60 * 60 * 1000).toISOString(), + }; + + renderComponent({ auth: { user: newUser } }); + + expect( + screen.queryByTestId('quest-button-new-indicator'), + ).not.toBeInTheDocument(); + }); + + it('should show new indicator for users created more than 24 hours ago', () => { + mockUseQuestDashboard.mockReturnValue({ + data: { ...questDashboard, hasNewQuestRotations: true }, + isPending: false, + isError: false, + }); + + const establishedUser = { + ...loggedUser, + createdAt: new Date(Date.now() - 48 * 60 * 60 * 1000).toISOString(), + }; + + renderComponent({ auth: { user: establishedUser } }); + + expect( + screen.getByTestId('quest-button-new-indicator'), + ).toBeInTheDocument(); + }); }); diff --git a/packages/shared/src/components/quest/QuestButton.tsx b/packages/shared/src/components/quest/QuestButton.tsx index 992c1aa036..36a838007a 100644 --- a/packages/shared/src/components/quest/QuestButton.tsx +++ b/packages/shared/src/components/quest/QuestButton.tsx @@ -1377,7 +1377,11 @@ export const QuestButton = ({ const triggerVisualClassName = compact ? 'size-8' : 'size-10'; const triggerLevelClassName = compact ? 'typo-caption2' : 'typo-caption1'; const [isOpen, setIsOpen] = useState(false); - const hasNewQuestRotations = data?.hasNewQuestRotations ?? false; + const isAccountOlderThan24Hours = + !!user?.createdAt && + Date.now() - new Date(user.createdAt).getTime() > 24 * 60 * 60 * 1000; + const hasNewQuestRotations = + (data?.hasNewQuestRotations ?? false) && isAccountOlderThan24Hours; const claimedStampRotationIdSet = useMemo( () => new Set(claimedStampRotationIds), [claimedStampRotationIds],