feat/welcome-letter integration #2
Binary file not shown.
|
After Width: | Height: | Size: 129 KiB |
@@ -1,6 +1,6 @@
|
|||||||
import { AnimatePresence, motion } from "framer-motion";
|
import { AnimatePresence, motion } from "framer-motion";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { getWelcomeContent } from "../../config/welcomeLetter";
|
import { getWelcomeLetterContent } from "../../config/welcomeLetter";
|
||||||
import { formatDate } from "../../utils/dateFormat";
|
import { formatDate } from "../../utils/dateFormat";
|
||||||
import { type CanvasTools, ComposeCanvas } from "../editor/ComposeCanvas";
|
import { type CanvasTools, ComposeCanvas } from "../editor/ComposeCanvas";
|
||||||
import { EnvelopeReveal } from "../reader/EnvelopeReveal";
|
import { EnvelopeReveal } from "../reader/EnvelopeReveal";
|
||||||
@@ -21,7 +21,7 @@ export function WelcomeLetterOverlay({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (revealState === "REVEALED" && canvasRef.current) {
|
if (revealState === "REVEALED" && canvasRef.current) {
|
||||||
const welcomeContent = getWelcomeContent(userName);
|
const welcomeContent = getWelcomeLetterContent(userName);
|
||||||
canvasRef.current.loadData(welcomeContent);
|
canvasRef.current.loadData(welcomeContent);
|
||||||
}
|
}
|
||||||
}, [revealState, userName]);
|
}, [revealState, userName]);
|
||||||
|
|||||||
@@ -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 { MemoryRouter } from "react-router-dom";
|
||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { mockUser } from "../../test/fixtures/user.fixture";
|
import { mockUser } from "../../test/fixtures/user.fixture";
|
||||||
@@ -7,6 +7,19 @@ import { useAuthStore } from "../store/useAuthStore";
|
|||||||
import Drawer from "./Drawer";
|
import Drawer from "./Drawer";
|
||||||
|
|
||||||
vi.mock("../hooks/useLetters");
|
vi.mock("../hooks/useLetters");
|
||||||
|
vi.mock("../components/drawer/WelcomeLetterOverlay", () => ({
|
||||||
|
WelcomeLetterOverlay: ({ onComplete }: any) => (
|
||||||
|
<div data-testid="welcome-letter-overlay">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-testid="overlay-exit-button"
|
||||||
|
onClick={onComplete}
|
||||||
|
>
|
||||||
|
I'll see you
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
|
||||||
describe("Drawer Page", () => {
|
describe("Drawer Page", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -75,9 +88,36 @@ describe("Drawer Page", () => {
|
|||||||
</MemoryRouter>,
|
</MemoryRouter>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(screen.getByText(/You've been away a while./i)).toBeInTheDocument();
|
||||||
screen.getByText(/You've been away a while./i),
|
|
||||||
).toBeInTheDocument();
|
|
||||||
expect(screen.getByPlaceholderText(/password/i)).toBeInTheDocument();
|
expect(screen.getByPlaceholderText(/password/i)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("renders the welcome letter when firstTime state is present", () => {
|
||||||
|
render(
|
||||||
|
<MemoryRouter
|
||||||
|
initialEntries={[{ pathname: "/drawer", state: { firstTime: true } }]}
|
||||||
|
>
|
||||||
|
<Drawer />
|
||||||
|
</MemoryRouter>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByTestId("welcome-letter-overlay")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders the drawer content when the letter is closed", () => {
|
||||||
|
render(
|
||||||
|
<MemoryRouter
|
||||||
|
initialEntries={[{ pathname: "/drawer", state: { firstTime: true } }]}
|
||||||
|
>
|
||||||
|
<Drawer />
|
||||||
|
</MemoryRouter>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const completeButton = screen.getByTestId("overlay-exit-button");
|
||||||
|
fireEvent.click(completeButton);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.queryByTestId("welcome-letter-overlay"),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { FeatherIcon } from "@phosphor-icons/react";
|
import { FeatherIcon } from "@phosphor-icons/react";
|
||||||
import { useState } from "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 { DrawerSection } from "../components/drawer/DrawerSection.tsx";
|
||||||
import { LetterItem } from "../components/drawer/LetterItem.tsx";
|
import { LetterItem } from "../components/drawer/LetterItem.tsx";
|
||||||
import { PasskeyModal } from "../components/drawer/PasskeyModal.tsx";
|
import { PasskeyModal } from "../components/drawer/PasskeyModal.tsx";
|
||||||
|
import { WelcomeLetterOverlay } from "../components/drawer/WelcomeLetterOverlay.tsx";
|
||||||
import Logo from "../components/Logo";
|
import Logo from "../components/Logo";
|
||||||
import Saajan from "../components/ui/Saajan.tsx";
|
import Saajan from "../components/ui/Saajan.tsx";
|
||||||
import { PATHS } from "../config/routes";
|
import { PATHS } from "../config/routes";
|
||||||
@@ -19,6 +20,10 @@ export default function Drawer() {
|
|||||||
|
|
||||||
const [openSection, setOpenSection] = useState<string | null>(null);
|
const [openSection, setOpenSection] = useState<string | null>(null);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const location = useLocation();
|
||||||
|
const [showWelcomeLetter, setShowWelcomeLetter] = useState(
|
||||||
|
!!location.state?.firstTime,
|
||||||
|
);
|
||||||
const { drafts, kept, sent, vault, loading, isAuthRequired } = useLetters();
|
const { drafts, kept, sent, vault, loading, isAuthRequired } = useLetters();
|
||||||
|
|
||||||
if (!user) return null;
|
if (!user) return null;
|
||||||
@@ -30,6 +35,16 @@ export default function Drawer() {
|
|||||||
<div className="min-h-screen w-full bg-base-100 text-base-content flex flex-col items-center py-12 px-5 pb-32 font-serif transition-colors">
|
<div className="min-h-screen w-full bg-base-100 text-base-content flex flex-col items-center py-12 px-5 pb-32 font-serif transition-colors">
|
||||||
<div className="fixed inset-0 bg-vig pointer-events-none z-0" />
|
<div className="fixed inset-0 bg-vig pointer-events-none z-0" />
|
||||||
|
|
||||||
|
{showWelcomeLetter && (
|
||||||
|
<WelcomeLetterOverlay
|
||||||
|
userName={user.full_name}
|
||||||
|
onComplete={() => {
|
||||||
|
setShowWelcomeLetter(false);
|
||||||
|
navigate(location.pathname, { replace: true, state: {} });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{isAuthRequired && <PasskeyModal />}
|
{isAuthRequired && <PasskeyModal />}
|
||||||
<header className="text-center mb-12 z-10 animate-in fade-in slide-in-from-top-4 duration-500">
|
<header className="text-center mb-12 z-10 animate-in fade-in slide-in-from-top-4 duration-500">
|
||||||
<Logo />
|
<Logo />
|
||||||
@@ -166,12 +181,14 @@ export default function Drawer() {
|
|||||||
<footer className="mt-25 font-sans text-[0.6rem] tracking-widester uppercase text-base-content/10 z-10">
|
<footer className="mt-25 font-sans text-[0.6rem] tracking-widester uppercase text-base-content/10 z-10">
|
||||||
For your unsaid.
|
For your unsaid.
|
||||||
</footer>
|
</footer>
|
||||||
|
{!showWelcomeLetter && (
|
||||||
<div className="absolute bottom-0 z-50 font-sans">
|
<div className="absolute bottom-0 z-50 font-sans">
|
||||||
<Saajan
|
<Saajan
|
||||||
message={`Good to see you again, ${user.full_name}.\nWhat's on your mind today?`}
|
message={`Good to see you again, ${user.full_name}.\nWhat's on your mind today?`}
|
||||||
position="top"
|
position="top"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user