diff --git a/frontend/public/screenshots/train.png b/frontend/public/screenshots/train.png
new file mode 100644
index 0000000..9e60582
Binary files /dev/null and b/frontend/public/screenshots/train.png differ
diff --git a/frontend/src/components/drawer/WelcomeLetterOverlay.tsx b/frontend/src/components/drawer/WelcomeLetterOverlay.tsx
index a45d2b2..5b59420 100644
--- a/frontend/src/components/drawer/WelcomeLetterOverlay.tsx
+++ b/frontend/src/components/drawer/WelcomeLetterOverlay.tsx
@@ -1,6 +1,6 @@
import { AnimatePresence, motion } from "framer-motion";
import { useEffect, useRef, useState } from "react";
-import { getWelcomeContent } from "../../config/welcomeLetter";
+import { getWelcomeLetterContent } from "../../config/welcomeLetter";
import { formatDate } from "../../utils/dateFormat";
import { type CanvasTools, ComposeCanvas } from "../editor/ComposeCanvas";
import { EnvelopeReveal } from "../reader/EnvelopeReveal";
@@ -21,7 +21,7 @@ export function WelcomeLetterOverlay({
useEffect(() => {
if (revealState === "REVEALED" && canvasRef.current) {
- const welcomeContent = getWelcomeContent(userName);
+ const welcomeContent = getWelcomeLetterContent(userName);
canvasRef.current.loadData(welcomeContent);
}
}, [revealState, userName]);
diff --git a/frontend/src/pages/Drawer.test.tsx b/frontend/src/pages/Drawer.test.tsx
index 05feade..206ec94 100644
--- a/frontend/src/pages/Drawer.test.tsx
+++ b/frontend/src/pages/Drawer.test.tsx
@@ -1,4 +1,4 @@
-import { render, screen } from "@testing-library/react";
+import { fireEvent, render, screen } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { mockUser } from "../../test/fixtures/user.fixture";
@@ -7,77 +7,117 @@ import { useAuthStore } from "../store/useAuthStore";
import Drawer from "./Drawer";
vi.mock("../hooks/useLetters");
+vi.mock("../components/drawer/WelcomeLetterOverlay", () => ({
+ WelcomeLetterOverlay: ({ onComplete }: any) => (
+
+
+
+ ),
+}));
describe("Drawer Page", () => {
- beforeEach(() => {
- // Setup authenticated state for the test
- useAuthStore.setState({
- user: mockUser,
- accessToken: "fake-token",
- isInitializing: false,
- });
-
- vi.mocked(useLetters).mockReturnValue({
- drafts: [],
- kept: [],
- sent: [],
- vault: [],
- loading: false,
- isAuthRequired: false,
- });
+ beforeEach(() => {
+ // Setup authenticated state for the test
+ useAuthStore.setState({
+ user: mockUser,
+ accessToken: "fake-token",
+ isInitializing: false,
});
- it("renders the cabinet sections and empty state message", () => {
- render(
-
-
- ,
- );
+ vi.mocked(useLetters).mockReturnValue({
+ drafts: [],
+ kept: [],
+ sent: [],
+ vault: [],
+ loading: false,
+ isAuthRequired: false,
+ });
+ });
- expect(screen.getByText(/Drafts/i)).toBeInTheDocument();
- expect(screen.getAllByText(/Kept/i).length).toBeGreaterThanOrEqual(1);
- expect(screen.getByText(/Vault/i)).toBeInTheDocument();
- expect(screen.getByText(/This drawer remains silent/i)).toBeInTheDocument();
+ it("renders the cabinet sections and empty state message", () => {
+ render(
+
+
+ ,
+ );
+
+ expect(screen.getByText(/Drafts/i)).toBeInTheDocument();
+ expect(screen.getAllByText(/Kept/i).length).toBeGreaterThanOrEqual(1);
+ expect(screen.getByText(/Vault/i)).toBeInTheDocument();
+ expect(screen.getByText(/This drawer remains silent/i)).toBeInTheDocument();
+ });
+
+ it("renders the loading state", () => {
+ vi.mocked(useLetters).mockReturnValue({
+ drafts: [],
+ kept: [],
+ sent: [],
+ vault: [],
+ loading: true,
+ isAuthRequired: false,
});
- it("renders the loading state", () => {
- vi.mocked(useLetters).mockReturnValue({
- drafts: [],
- kept: [],
- sent: [],
- vault: [],
- loading: true,
- isAuthRequired: false,
- });
+ render(
+
+
+ ,
+ );
- render(
-
-
- ,
- );
+ expect(screen.getByText(/Opening your cabinet/i)).toBeInTheDocument();
+ });
- expect(screen.getByText(/Opening your cabinet/i)).toBeInTheDocument();
+ it("renders the authentication required modal when api requires auth", () => {
+ vi.mocked(useLetters).mockReturnValue({
+ drafts: [],
+ kept: [],
+ sent: [],
+ vault: [],
+ loading: false,
+ isAuthRequired: true,
});
- it("renders the authentication required modal when api requires auth", () => {
- vi.mocked(useLetters).mockReturnValue({
- drafts: [],
- kept: [],
- sent: [],
- vault: [],
- loading: false,
- isAuthRequired: true,
- });
+ render(
+
+
+ ,
+ );
- render(
-
-
- ,
- );
+ expect(screen.getByText(/You've been away a while./i)).toBeInTheDocument();
+ expect(screen.getByPlaceholderText(/password/i)).toBeInTheDocument();
+ });
- expect(
- screen.getByText(/You've been away a while./i),
- ).toBeInTheDocument();
- expect(screen.getByPlaceholderText(/password/i)).toBeInTheDocument();
- });
+ it("renders the welcome letter when firstTime state is present", () => {
+ render(
+
+
+ ,
+ );
+
+ expect(screen.getByTestId("welcome-letter-overlay")).toBeInTheDocument();
+ });
+
+ it("renders the drawer content when the letter is closed", () => {
+ render(
+
+
+ ,
+ );
+
+ const completeButton = screen.getByTestId("overlay-exit-button");
+ fireEvent.click(completeButton);
+
+ expect(
+ screen.queryByTestId("welcome-letter-overlay"),
+ ).not.toBeInTheDocument();
+ });
});
diff --git a/frontend/src/pages/Drawer.tsx b/frontend/src/pages/Drawer.tsx
index 5fe14f4..b21f941 100644
--- a/frontend/src/pages/Drawer.tsx
+++ b/frontend/src/pages/Drawer.tsx
@@ -1,9 +1,10 @@
import { FeatherIcon } from "@phosphor-icons/react";
import { useState } from "react";
-import { useNavigate } from "react-router-dom";
+import { useLocation, useNavigate } from "react-router-dom";
import { DrawerSection } from "../components/drawer/DrawerSection.tsx";
import { LetterItem } from "../components/drawer/LetterItem.tsx";
import { PasskeyModal } from "../components/drawer/PasskeyModal.tsx";
+import { WelcomeLetterOverlay } from "../components/drawer/WelcomeLetterOverlay.tsx";
import Logo from "../components/Logo";
import Saajan from "../components/ui/Saajan.tsx";
import { PATHS } from "../config/routes";
@@ -19,6 +20,10 @@ export default function Drawer() {
const [openSection, setOpenSection] = useState(null);
const navigate = useNavigate();
+ const location = useLocation();
+ const [showWelcomeLetter, setShowWelcomeLetter] = useState(
+ !!location.state?.firstTime,
+ );
const { drafts, kept, sent, vault, loading, isAuthRequired } = useLetters();
if (!user) return null;
@@ -30,6 +35,16 @@ export default function Drawer() {
+ {showWelcomeLetter && (
+
{
+ setShowWelcomeLetter(false);
+ navigate(location.pathname, { replace: true, state: {} });
+ }}
+ />
+ )}
+
{isAuthRequired && }
@@ -166,12 +181,14 @@ export default function Drawer() {
-
-
-
+ {!showWelcomeLetter && (
+
+
+
+ )}
);
}