import { useAuth } from '../contexts/AuthContext' import { Link, useNavigate } from 'react-router-dom' import { useState, useRef, useEffect } from 'react' import { createEntry, updateUserProfile } from '../lib/api' import { encryptEntry } from '../lib/crypto' import BottomNav from '../components/BottomNav' import WelcomeModal from '../components/WelcomeModal' import { SaveBookAnimation } from '../components/SaveBookAnimation' import { useOnboardingTour } from '../hooks/useOnboardingTour' import { PageLoader } from '../components/PageLoader' const AFFIRMATIONS = [ 'You showed up for yourself today 🌱', 'Another moment beautifully captured ✨', 'Gratitude logged. Keep growing 🌿', 'Small moments, big growth 🍃', "You're building something beautiful 💚", 'One more grateful moment preserved 🌸', 'Your thoughts are safe and stored 🔒', ] const SAVE_LEAVES = [ { left: -80, dx: -30, rot: -25, delay: 0.0, emoji: '🌱' }, { left: -45, dx: -12, rot: 15, delay: 0.08, emoji: '🌿' }, { left: -15, dx: -22, rot: -10, delay: 0.04, emoji: '🌱' }, { left: 8, dx: 18, rot: 20, delay: 0.12, emoji: '🍃' }, { left: 38, dx: 27, rot: -18, delay: 0.06, emoji: '🌿' }, { left: 68, dx: 38, rot: 12, delay: 0.18, emoji: '🌱' }, { left: -62, dx: -33, rot: 28, delay: 0.22, emoji: '🍃' }, { left: 25, dx: 15, rot: -22, delay: 0.28, emoji: '🌿' }, ] export default function HomePage() { const { user, userId, mongoUser, secretKey, loading } = useAuth() const navigate = useNavigate() const [entry, setEntry] = useState('') const [title, setTitle] = useState('') const [phase, setPhase] = useState<'idle' | 'saving' | 'book' | 'celebrate'>('idle') const [affirmation, setAffirmation] = useState('') const [message, setMessage] = useState<{ type: 'error'; text: string } | null>(null) const [showWelcome, setShowWelcome] = useState(false) const titleInputRef = useRef(null) const contentTextareaRef = useRef(null) const { startTour } = useOnboardingTour() // Check if onboarding should be shown after login useEffect(() => { if (!loading && user && userId && mongoUser && !mongoUser.tutorial) { setShowWelcome(true) } }, [loading, user, userId, mongoUser]) async function markTutorialDone() { if (!user || !userId) return const token = await user.getIdToken() updateUserProfile(userId, { tutorial: true }, token).catch(console.error) } const handleStartTour = () => { setShowWelcome(false) markTutorialDone() startTour() } const handleSkipTour = () => { setShowWelcome(false) markTutorialDone() } if (loading) { return } if (!user) { return (

Grateful Journal

Sign in to start your journal.

Go to login
) } // Format date: "THURSDAY, OCT 24" const today = new Date() const dateString = today .toLocaleDateString('en-US', { weekday: 'long', month: 'short', day: 'numeric' }) .toUpperCase() const handleBookDone = () => { setPhase('celebrate') setTimeout(() => navigate('/history'), 2500) } const handleTitleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && title.trim()) { e.preventDefault() contentTextareaRef.current?.focus() } } const handleWrite = async () => { if (!userId || !title.trim() || !entry.trim()) { setMessage({ type: 'error', text: 'Please add a title and entry content' }) return } if (!secretKey) { setMessage({ type: 'error', text: 'Encryption key not available. Please log in again.' }) return } setPhase('saving') setMessage(null) try { const token = await user.getIdToken() // Combine title and content for encryption const contentToEncrypt = `${title.trim()}\n\n${entry.trim()}` // Encrypt the entry with master key const { ciphertext, nonce } = await encryptEntry( contentToEncrypt, secretKey ) // Send encrypted data to backend // Note: title and content are null for encrypted entries await createEntry( userId, { title: undefined, content: undefined, isPublic: false, encryption: { encrypted: true, ciphertext, nonce, algorithm: 'XSalsa20-Poly1305', }, }, token ) setTitle('') setEntry('') setAffirmation(AFFIRMATIONS[Math.floor(Math.random() * AFFIRMATIONS.length)]) setPhase('book') } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Failed to save entry' setMessage({ type: 'error', text: errorMessage }) setPhase('idle') } } return (
{showWelcome && ( )}
{dateString}

What are you grateful for today?

setTitle(e.target.value)} onKeyDown={handleTitleKeyDown} enterKeyHint="next" ref={titleInputRef} disabled={phase !== 'idle'} />