Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion apps/antalmanac/src/stores/PatchNotesStore.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { getLocalStoragePatchNotesKey } from '$lib/localStorage';
import { tourShouldRun } from '$lib/TutorialHelpers';
import { differenceInCalendarDays, parse, startOfDay } from 'date-fns';
import { create } from 'zustand';

/**
Expand All @@ -10,13 +11,28 @@ import { create } from 'zustand';
*/
export const LATEST_PATCH_NOTES_UPDATE = '20260130';

const PATCH_NOTES_ADDED_ON = parse(LATEST_PATCH_NOTES_UPDATE, 'yyyyMMdd', new Date());

/** Stop auto-opening the patch notes modal after this many calendar days since release. */
const PATCH_NOTES_MAX_AGE_CALENDAR_DAYS = 30;

function arePatchNotesStale(now: Date = new Date()) {
return (
differenceInCalendarDays(startOfDay(now), startOfDay(PATCH_NOTES_ADDED_ON)) > PATCH_NOTES_MAX_AGE_CALENDAR_DAYS
);
}

export interface PatchNotesStoreProps {
showPatchNotes: boolean;
setShowPatchNotes: (value: boolean | ((prev: boolean) => boolean)) => void;
}

export function shouldShowPatchNotes() {
return getLocalStoragePatchNotesKey() !== LATEST_PATCH_NOTES_UPDATE && !tourShouldRun();
return (
!arePatchNotesStale() &&
getLocalStoragePatchNotesKey() !== LATEST_PATCH_NOTES_UPDATE &&
!tourShouldRun()
);
}

export const usePatchNotesStore = create<PatchNotesStoreProps>((set) => {
Expand Down
54 changes: 46 additions & 8 deletions apps/antalmanac/tests/patch-notes.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,24 @@ import {
setLocalStorageTourHasRun,
} from '$lib/localStorage';
import { LATEST_PATCH_NOTES_UPDATE, shouldShowPatchNotes, usePatchNotesStore } from '$stores/PatchNotesStore';
import { render, screen, act } from '@testing-library/react';
import { describe, expect, test } from 'vitest';
import { render, screen, act, waitFor } from '@testing-library/react';
import { afterEach, describe, expect, test, vi } from 'vitest';

describe('patch notes', () => {
/**
* A date that's guaranteed to be outdated.
*/
const outdatedPatchNotes = '00000000';

afterEach(() => {
vi.useRealTimers();
});

describe('patch notes displays appropriately', () => {
test('displays when latest patch notes is outdated ', () => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-02-15T12:00:00.000Z'));

setLocalStoragePatchNotesKey(outdatedPatchNotes);
setLocalStorageTourHasRun('true');
usePatchNotesStore.setState({ showPatchNotes: shouldShowPatchNotes() });
Expand All @@ -26,17 +33,46 @@ describe('patch notes', () => {
});

test('no display when latest patch notes is up to date', () => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-02-15T12:00:00.000Z'));

setLocalStoragePatchNotesKey(LATEST_PATCH_NOTES_UPDATE);
usePatchNotesStore.setState({ showPatchNotes: shouldShowPatchNotes() });

render(<PatchNotes />);

expect(screen.queryByTestId(dialogTestId)).toBeFalsy();
});

test('no auto display when patch notes are older than 30 calendar days', () => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-03-02T12:00:00.000Z'));

setLocalStoragePatchNotesKey(outdatedPatchNotes);
setLocalStorageTourHasRun('true');
usePatchNotesStore.setState({ showPatchNotes: shouldShowPatchNotes() });

render(<PatchNotes />);

expect(screen.queryByTestId(dialogTestId)).toBeFalsy();
});

test('auto display still allowed on the 30th calendar day after release', () => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-03-01T12:00:00.000Z'));

setLocalStoragePatchNotesKey(outdatedPatchNotes);
setLocalStorageTourHasRun('true');
usePatchNotesStore.setState({ showPatchNotes: shouldShowPatchNotes() });

render(<PatchNotes />);

expect(screen.queryByTestId(dialogTestId)).toBeTruthy();
});
});

describe('close patch notes with button', () => {
test('clicking the button closes the dialog', () => {
test('clicking the button closes the dialog', async () => {
usePatchNotesStore.setState({ showPatchNotes: true });

render(<PatchNotes />);
Expand All @@ -45,7 +81,9 @@ describe('patch notes', () => {
screen.getByTestId(closeButtonTestId).click();
});

expect(screen.queryByTitle(dialogTestId)).toBeFalsy();
await waitFor(() => {
expect(screen.queryByTestId(dialogTestId)).toBeNull();
});
});

test('the latest patch notes is saved to local storage', () => {
Expand All @@ -63,7 +101,7 @@ describe('patch notes', () => {
});

describe('closing the dialog by clicking the backdrop ', () => {
test('clicking the backdrop closes the dialog', () => {
test('clicking the backdrop closes the dialog', async () => {
usePatchNotesStore.setState({ showPatchNotes: true });

render(<PatchNotes />);
Expand All @@ -72,9 +110,9 @@ describe('patch notes', () => {
screen.getByTestId(backdropTestId).click();
});

const dialog = screen.queryByTitle(dialogTestId);

expect(dialog).toBeFalsy();
await waitFor(() => {
expect(screen.queryByTestId(dialogTestId)).toBeNull();
});
});

test('the latest patch notes is saved to local storage', () => {
Expand Down
Loading