Files
grateful-journal/src/pages/HomePage.tsx
2026-03-10 11:01:13 +05:30

169 lines
5.1 KiB
TypeScript

import { useAuth } from '../contexts/AuthContext'
import { Link } from 'react-router-dom'
import { useState, useRef } from 'react'
import { createEntry } from '../lib/api'
import { encryptEntry } from '../lib/crypto'
import BottomNav from '../components/BottomNav'
export default function HomePage() {
const { user, userId, secretKey, loading } = useAuth()
const [entry, setEntry] = useState('')
const [title, setTitle] = useState('')
const [saving, setSaving] = useState(false)
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null)
const titleInputRef = useRef<HTMLInputElement>(null)
const contentTextareaRef = useRef<HTMLTextAreaElement>(null)
if (loading) {
return (
<div className="home-page" style={{ alignItems: 'center', justifyContent: 'center' }}>
<p style={{ color: '#9ca3af' }}>Loading</p>
</div>
)
}
if (!user) {
return (
<div className="home-page" style={{ alignItems: 'center', justifyContent: 'center', gap: '1rem' }}>
<h1 style={{ fontFamily: '"Sniglet", system-ui', color: '#1a1a1a' }}>Grateful Journal</h1>
<p style={{ color: '#6b7280' }}>Sign in to start your journal.</p>
<Link to="/login" className="home-login-link">Go to login</Link>
</div>
)
}
// Format date: "THURSDAY, OCT 24"
const today = new Date()
const dateString = today
.toLocaleDateString('en-US', { weekday: 'long', month: 'short', day: 'numeric' })
.toUpperCase()
const handleTitleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
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
}
setSaving(true)
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
)
setMessage({ type: 'success', text: 'Entry saved securely!' })
setTitle('')
setEntry('')
// Clear success message after 3 seconds
setTimeout(() => setMessage(null), 3000)
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Failed to save entry'
setMessage({ type: 'error', text: errorMessage })
} finally {
setSaving(false)
}
}
return (
<div className="home-page">
<main className="journal-container">
<div className="journal-card">
<div className="journal-date">{dateString}</div>
<h2 className="journal-prompt">What are you grateful for today?</h2>
<div className="journal-writing-area">
<input
type="text"
className="journal-title-input"
placeholder="Title your thoughts..."
value={title}
onChange={(e) => setTitle(e.target.value)}
onKeyDown={handleTitleKeyDown}
enterKeyHint="next"
ref={titleInputRef}
disabled={saving}
/>
<textarea
className="journal-entry-textarea"
placeholder="Start writing your entry here..."
value={entry}
onChange={(e) => setEntry(e.target.value)}
enterKeyHint="enter"
ref={contentTextareaRef}
disabled={saving}
/>
</div>
{message && (
<div style={{
padding: '0.75rem',
marginTop: '1rem',
borderRadius: '8px',
fontSize: '0.875rem',
backgroundColor: message.type === 'success' ? '#f0fdf4' : '#fef2f2',
color: message.type === 'success' ? '#15803d' : '#b91c1c',
textAlign: 'center',
}}>
{message.text}
</div>
)}
<div style={{ marginTop: '1.5rem', display: 'flex', gap: '0.75rem' }}>
<button
className="journal-write-btn"
onClick={handleWrite}
disabled={saving || !title.trim() || !entry.trim()}
>
{saving ? 'Saving...' : 'Save Entry'}
</button>
</div>
</div>
</main>
<BottomNav />
</div>
)
}