diff --git a/frontend/src/components/ui/ComposeCanvas.tsx b/frontend/src/components/ui/ComposeCanvas.tsx index b192ada..6fbe3b9 100644 --- a/frontend/src/components/ui/ComposeCanvas.tsx +++ b/frontend/src/components/ui/ComposeCanvas.tsx @@ -1,9 +1,9 @@ import * as fabric from "fabric"; -import { useEffect, useRef } from "react"; +import { forwardRef, useEffect, useImperativeHandle, useRef } from "react"; const PAD = 36; -export default function ComposeCanvas() { +export const ComposeCanvas = forwardRef((_props, ref) => { const wrapperRef = useRef(null); const canvasRef = useRef(null); const fabricRef = useRef(null); @@ -125,6 +125,22 @@ export default function ComposeCanvas() { }; }, []); + useImperativeHandle(ref, () => ({ + addImage: (url: string) => { + if (!fabricRef.current) return; + fabric.FabricImage.fromURL(url).then((img) => { + img.scaleToWidth(300); + img.set({ + left: PAD, + top: PAD, + }); + fabricRef.current?.add(img); + fabricRef.current?.setActiveObject(img); + fabricRef.current?.requestRenderAll(); + }); + }, + })); + return (
); -} +}); diff --git a/frontend/src/hooks/useAuth.ts b/frontend/src/hooks/useAuth.ts index afb7919..1e67da8 100644 --- a/frontend/src/hooks/useAuth.ts +++ b/frontend/src/hooks/useAuth.ts @@ -1,3 +1,4 @@ +import { useCallback } from "react"; import { api, publicApi } from "../api/apiClient"; import { endpoints } from "../config/endpoints"; import { useAuthStore } from "../store/useAuthStore"; @@ -9,14 +10,8 @@ interface UserProfile { } export const useAuth = () => { - const { - accessToken, - user, - isInitializing, - setAuth, - clearAuth, - setInitializing, - } = useAuthStore(); + const { accessToken, user, isInitializing, setAuth, clearAuth } = + useAuthStore(); const isAuthenticated = !!accessToken; @@ -32,8 +27,11 @@ export const useAuth = () => { } }; - const initialize = async () => { - // If we already have a session in memory, don't trigger refresh/me again + const initialize = useCallback(async () => { + const { accessToken, user, setAuth, clearAuth, setInitializing } = + useAuthStore.getState(); + + // If session in memory, don't trigger refresh/me again if (accessToken && user) { setInitializing(false); return; @@ -42,19 +40,17 @@ export const useAuth = () => { try { // try refresh const { data: refreshData } = await publicApi.post(endpoints.REFRESH); - // fetch user profile with the new access token const { data: userData } = await api.get(endpoints.ME, { headers: { Authorization: `Bearer ${refreshData.access}` }, }); - // update auth details in memory setAuth(refreshData.access, userData); } catch (err) { console.error("Initialization failed:", err); clearAuth(); } - }; + }, []); return { isAuthenticated, diff --git a/frontend/src/pages/Editor.tsx b/frontend/src/pages/Editor.tsx index 41d6176..363ca15 100644 --- a/frontend/src/pages/Editor.tsx +++ b/frontend/src/pages/Editor.tsx @@ -1,11 +1,21 @@ -import { LockIcon, TrayIcon } from "@phosphor-icons/react"; -import { useState } from "react"; -import ComposeCanvas from "../components/ui/ComposeCanvas"; +import { ImageIcon, LockIcon, TrayIcon } from "@phosphor-icons/react"; +import { useRef, useState } from "react"; +import { 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]; + if (file) { + const url = URL.createObjectURL(file); + canvasRef.current?.addImage(url); + } + }; + return (
@@ -33,7 +43,22 @@ export default function Editor() { id="writer-toolbar" className="flex items-center justify-between mb-8 h-14 bg-base-100/50 backdrop-blur-md rounded-full border border-base-content/5 px-6" > -
Toolbar Placeholder
+
+ + +
- +
);