diff --git a/src/api/projectHandling.jsx b/src/api/projectHandling.jsx index 09261d9..8d6dc2f 100644 --- a/src/api/projectHandling.jsx +++ b/src/api/projectHandling.jsx @@ -617,3 +617,71 @@ export const loadSettings = async () => { throw error; } }; + +export const syncEquationsToCloud = async ( + projectId, + equations, + { isServerConnected, isAuthenticated, userId } = {}, +) => { + if (!isServerConnected || !isAuthenticated || !userId) return; + try { + await axios.put( + `${import.meta.env.VITE_admin_server}/api/equations/${userId}/${projectId}`, + { items: equations }, + { withCredentials: true }, + ); + } catch (error) { + console.error("Failed to sync equations to cloud:", error.message); + } +}; + +export const pullEquationsFromCloud = async ( + projectId, + { isServerConnected, isAuthenticated, userId } = {}, +) => { + if (!isServerConnected || !isAuthenticated || !userId) return []; + try { + const res = await axios.get( + `${import.meta.env.VITE_admin_server}/api/equations/${userId}/${projectId}`, + { withCredentials: true }, + ); + return res.data.items || []; + } catch (error) { + console.error("Failed to pull equations from cloud:", error.message); + return []; + } +}; + +export const syncCitationsToCloud = async ( + projectId, + citations, + { isServerConnected, isAuthenticated, userId } = {}, +) => { + if (!isServerConnected || !isAuthenticated || !userId) return; + try { + await axios.put( + `${import.meta.env.VITE_admin_server}/api/citations/${userId}/${projectId}`, + { items: citations }, + { withCredentials: true }, + ); + } catch (error) { + console.error("Failed to sync citations to cloud:", error.message); + } +}; + +export const pullCitationsFromCloud = async ( + projectId, + { isServerConnected, isAuthenticated, userId } = {}, +) => { + if (!isServerConnected || !isAuthenticated || !userId) return []; + try { + const res = await axios.get( + `${import.meta.env.VITE_admin_server}/api/citations/${userId}/${projectId}`, + { withCredentials: true }, + ); + return res.data.items || []; + } catch (error) { + console.error("Failed to pull citations from cloud:", error.message); + return []; + } +}; diff --git a/src/components/citationManager.jsx b/src/components/citationManager.jsx index 4ced0f3..dd54a93 100644 --- a/src/components/citationManager.jsx +++ b/src/components/citationManager.jsx @@ -1,3 +1,7 @@ +import { + syncCitationsToCloud, + pullCitationsFromCloud, +} from "../api/projectHandling"; import React, { useState, useEffect, useCallback } from "react"; import { useAuth } from "../context/useAuth"; import { useNavigate } from "react-router-dom"; @@ -36,7 +40,7 @@ const CitationManager = ({ isModal, projectId, }) => { - const { isAuthenticated } = useAuth(); + const { isAuthenticated, user, isServerConnected } = useAuth(); const navigate = useNavigate(); const [activeTab, setActiveTab] = useState("create"); const [formData, setFormData] = useState(initialFormData); @@ -52,6 +56,11 @@ const CitationManager = ({ message: "", }); const [copiedItem, setCopiedItem] = useState(null); + const syncOptions = { + isServerConnected, + isAuthenticated, + userId: user?.userId, + }; const showAlert = (title, message) => setAlertModal({ isOpen: true, title, message }); @@ -72,11 +81,53 @@ const CitationManager = ({ `${API_BASE_URL}/api/citation/list?projectId=${projectId || ""}`, ); const data = await res.json(); - setSavedCitations(data); + let localCitations = Array.isArray(data) ? data : []; + + // --- CLOUD SYNC LOGIC --- + if ( + syncOptions.isServerConnected && + syncOptions.isAuthenticated && + syncOptions.userId + ) { + if (localCitations.length === 0) { + // Pull from cloud if local is empty + const cloudCits = await pullCitationsFromCloud( + projectId, + syncOptions, + ); + + if (cloudCits && cloudCits.length > 0) { + localCitations = cloudCits; + // Save the pulled cloud citations to the local backend + for (const cit of cloudCits) { + await fetch(`${API_BASE_URL}/api/citation/save`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + fileName: cit.fileName, + citationData: cit, + latexCode: cit.latexCode, + projectId, + }), + }); + } + } + } else { + // Push existing local citations to cloud + await syncCitationsToCloud(projectId, localCitations, syncOptions); + } + } + + setSavedCitations(localCitations); } catch (err) { console.error("Failed to load citations:", err); } - }, []); + }, [ + projectId, + syncOptions.isAuthenticated, + syncOptions.userId, + syncOptions.isServerConnected, + ]); const handleInputChange = useCallback((e) => { const { name, value } = e.target; diff --git a/src/components/easyMathInput.jsx b/src/components/easyMathInput.jsx index 5148655..fd49d2c 100644 --- a/src/components/easyMathInput.jsx +++ b/src/components/easyMathInput.jsx @@ -1,3 +1,7 @@ +import { + syncEquationsToCloud, + pullEquationsFromCloud, +} from "../api/projectHandling"; import React, { useState, useEffect, useRef } from "react"; import { useAuth } from "../context/useAuth"; import { useNavigate } from "react-router-dom"; @@ -25,7 +29,7 @@ import axios from "axios"; const API_BASE_URL = "http://localhost:5000"; const EasyMathInput = ({ onClose, onInsert, projectId }) => { - const { isAuthenticated } = useAuth(); + const { isAuthenticated, user, isServerConnected } = useAuth(); const navigate = useNavigate(); const { settings } = useSettings(); @@ -76,6 +80,12 @@ const EasyMathInput = ({ onClose, onInsert, projectId }) => { ]), ); + const syncOptions = { + isServerConnected, + isAuthenticated, + userId: user?.userId, + }; + const textareaRef = useRef(null); // --- EFFECTS --- @@ -147,15 +157,56 @@ const EasyMathInput = ({ onClose, onInsert, projectId }) => { ); if (res.ok) { const data = await res.json(); - // Ensure we set an array, otherwise default to empty [] - setSavedEquations(Array.isArray(data) ? data : []); + let localEquations = Array.isArray(data) ? data : []; + + // --- CLOUD SYNC LOGIC --- + if ( + syncOptions.isServerConnected && + syncOptions.isAuthenticated && + syncOptions.userId + ) { + if (localEquations.length === 0) { + // Pull from cloud if local is empty + const cloudEqs = await pullEquationsFromCloud( + projectId, + syncOptions, + ); + + if (cloudEqs && cloudEqs.length > 0) { + localEquations = cloudEqs; + // Save the pulled cloud equations to the local backend + for (const eq of cloudEqs) { + await fetch(`${API_BASE_URL}/api/equations/save`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ ...eq, projectId }), + }); + } + } + } else { + // Push existing local history to cloud + await syncEquationsToCloud(projectId, localEquations, syncOptions); + } + } + + setSavedEquations(localEquations); } } catch (e) { console.error(e); - setSavedEquations([]); // Fallback to empty to prevent crash + setSavedEquations([]); } }; + // Update the useEffect to include the new dependencies + useEffect(() => { + loadSavedEquations(); + }, [ + projectId, + syncOptions.isAuthenticated, + syncOptions.userId, + syncOptions.isServerConnected, + ]); + const compileLatex = async (latex, isTemp = true, fileName = "temp") => { setIsCompiling(true); try {