diff --git a/frontend/src/components/ui/ComposeCanvas.tsx b/frontend/src/components/ui/ComposeCanvas.tsx
index 6fbe3b9..e716751 100644
--- a/frontend/src/components/ui/ComposeCanvas.tsx
+++ b/frontend/src/components/ui/ComposeCanvas.tsx
@@ -3,7 +3,11 @@ import { forwardRef, useEffect, useImperativeHandle, useRef } from "react";
const PAD = 36;
-export const ComposeCanvas = forwardRef((_props, ref) => {
+export type CanvasTools = {
+ addImage: (url: string) => void;
+};
+
+export const ComposeCanvas = forwardRef((_props, ref) => {
const wrapperRef = useRef(null);
const canvasRef = useRef(null);
const fabricRef = useRef(null);
@@ -137,6 +141,8 @@ export const ComposeCanvas = forwardRef((_props, ref) => {
fabricRef.current?.add(img);
fabricRef.current?.setActiveObject(img);
fabricRef.current?.requestRenderAll();
+
+ URL.revokeObjectURL(url); // cleanup browser upload
});
},
}));
@@ -155,3 +161,4 @@ export const ComposeCanvas = forwardRef((_props, ref) => {
);
});
+ComposeCanvas.displayName = "ComposeCanvas";
diff --git a/frontend/src/hooks/useAuth.ts b/frontend/src/hooks/useAuth.ts
index 1e67da8..1515067 100644
--- a/frontend/src/hooks/useAuth.ts
+++ b/frontend/src/hooks/useAuth.ts
@@ -1,13 +1,7 @@
import { useCallback } from "react";
import { api, publicApi } from "../api/apiClient";
import { endpoints } from "../config/endpoints";
-import { useAuthStore } from "../store/useAuthStore";
-
-interface UserProfile {
- public_id: string;
- email: string;
- full_name: string;
-}
+import { type UserProfile, useAuthStore } from "../store/useAuthStore";
export const useAuth = () => {
const { accessToken, user, isInitializing, setAuth, clearAuth } =
@@ -15,7 +9,7 @@ export const useAuth = () => {
const isAuthenticated = !!accessToken;
- const login = async (access: string, profile: UserProfile) => {
+ const login = (access: string, profile: UserProfile) => {
setAuth(access, profile);
};
diff --git a/frontend/src/pages/Editor.tsx b/frontend/src/pages/Editor.tsx
index 363ca15..651a5d1 100644
--- a/frontend/src/pages/Editor.tsx
+++ b/frontend/src/pages/Editor.tsx
@@ -1,15 +1,18 @@
import { ImageIcon, LockIcon, TrayIcon } from "@phosphor-icons/react";
import { useRef, useState } from "react";
-import { ComposeCanvas } from "../components/ui/ComposeCanvas";
+import {
+ type CanvasTools,
+ ComposeCanvas,
+} from "../components/ui/ComposeCanvas";
import DateDisplay from "../components/ui/DateDisplay";
export default function Editor() {
const [recipient, setRecipient] = useState("");
- const canvasRef = useRef(null);
- const _fileInputRef = useRef(null);
- const _handleImageUpload = (e: React.ChangeEvent) => {
- const file = e.target.files?.[0];
+ const canvasRef = useRef(null);
+ const fileInputRef = useRef(null);
+ const handleImageUpload = (e: React.ChangeEvent) => {
+ const file = e.target.files?.[0]; // pick one file at a time
if (file) {
const url = URL.createObjectURL(file);
canvasRef.current?.addImage(url);
@@ -47,14 +50,14 @@ export default function Editor() {
diff --git a/frontend/src/pages/Register.tsx b/frontend/src/pages/Register.tsx
index efb1d92..c6dfaed 100644
--- a/frontend/src/pages/Register.tsx
+++ b/frontend/src/pages/Register.tsx
@@ -9,6 +9,7 @@ import { publicApi } from "../api/apiClient";
import Logo from "../components/Logo";
import FormField from "../components/ui/FormField";
import { endpoints } from "../config/endpoints";
+import { ROUTES } from "../config/routes";
// validation logic
const registerSchema = z
@@ -47,7 +48,7 @@ export default function Register() {
email: data.email,
password: data.password,
});
- navigate("/verify-email");
+ navigate(ROUTES.VERIFY_EMAIL);
} catch (err) {
console.error("Registration error:", err);
let message = "Registration failed. Please try again.";
diff --git a/frontend/src/pages/VerifyEmail.tsx b/frontend/src/pages/VerifyEmail.tsx
index cc7d230..52d1557 100644
--- a/frontend/src/pages/VerifyEmail.tsx
+++ b/frontend/src/pages/VerifyEmail.tsx
@@ -29,13 +29,14 @@ export default function VerifyEmail() {
- window.close()}
onKeyDown={(e) => e.key === "Enter" && window.close()}
>
You can close this window now.
-
+
);
}
diff --git a/frontend/src/store/useAuthStore.ts b/frontend/src/store/useAuthStore.ts
index b805a59..d9ee990 100644
--- a/frontend/src/store/useAuthStore.ts
+++ b/frontend/src/store/useAuthStore.ts
@@ -1,6 +1,6 @@
import { create } from "zustand";
-interface UserProfile {
+export interface UserProfile {
public_id: string;
email: string;
full_name: string;
diff --git a/frontend/src/utils/crypto.ts b/frontend/src/utils/crypto.ts
index bdd6b1e..dc66b80 100644
--- a/frontend/src/utils/crypto.ts
+++ b/frontend/src/utils/crypto.ts
@@ -66,7 +66,8 @@ export async function encryptLetter(plaintext: string, masterKey: CryptoKey) {
const rawKey = await crypto.subtle.exportKey("raw", dek);
// conversion to base64 for transit
- const toBase64 = (buffer: Uint8Array) => btoa(String.fromCharCode(...buffer));
+ const toBase64 = (buf: Uint8Array) =>
+ btoa(buf.reduce((acc, b) => acc + String.fromCharCode(b), ""));
return {
// This goes to the server