import { CrossIcon } from "@phosphor-icons/react"; import { useEffect, useRef, useState } from "react"; import { useLocation, useParams } from "react-router-dom"; import { api } from "../api/apiClient"; import { type CanvasJSON, type CanvasTools, ComposeCanvas, } from "../components/ui/ComposeCanvas"; import { endpoints } from "../config/endpoints"; import { CryptoUtils } from "../utils/crypto"; import { decryptCanvasImagesWithSharingKey } from "../utils/letterLogic"; interface LetterMetadata { recipient?: string; } export default function Reader() { const { public_id } = useParams(); const location = useLocation(); const sharingKey = location.hash.replace("#", ""); const canvasRef = useRef(null); const [isDecrypting, setIsDecrypting] = useState(true); const [error, setError] = useState(null); const [metadata, setMetadata] = useState(null); const [decryptedCanvasData, setDecryptedCanvasData] = useState(null); useEffect(() => { if (!sharingKey) { setError("No sharing key provided. Please check the link."); setIsDecrypting(false); return; } const loadAndDecrypt = async () => { try { const response = await api.get(`${endpoints.LETTERS}${public_id}/`); const { encrypted_content, encrypted_metadata, images } = response.data; const cryptoUtils = new CryptoUtils(); const decryptedMetadata = await cryptoUtils.decryptMetadataWithSharingKey( encrypted_metadata, sharingKey, ); setMetadata(decryptedMetadata as LetterMetadata); const decryptedContent = await cryptoUtils.decryptLetterWithSharingKey( encrypted_content, sharingKey, ); const json = JSON.parse(decryptedContent) as CanvasJSON; if (images && images.length > 0) { await decryptCanvasImagesWithSharingKey( json, images, sharingKey, cryptoUtils, ); } setDecryptedCanvasData(json); } catch (err) { const message = err instanceof Error ? err.message : "Unknown error"; setError(`Failed to load letter: ${message}`); } finally { setIsDecrypting(false); } }; loadAndDecrypt(); }, [public_id, sharingKey]); useEffect(() => { if (!isDecrypting && decryptedCanvasData && canvasRef.current) { canvasRef.current.loadData(decryptedCanvasData); } }, [isDecrypting, decryptedCanvasData]); if (isDecrypting) { return (

Decrypting...

); } if (error) { return (

{error}

); } return (
{metadata?.recipient && (

A sealed message for{" "} {metadata.recipient || "Anonymous"}

)}
); }