diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx index 6c5af17df..4a551f3b5 100644 --- a/src/frontend/src/App.tsx +++ b/src/frontend/src/App.tsx @@ -63,9 +63,10 @@ export default function App() { }, [dark]); useEffect(() => { + const abortController = new AbortController(); const isLoginPage = location.pathname.includes("login"); - autoLogin() + autoLogin(abortController.signal) .then(async (user) => { if (user && user["access_token"]) { user["refresh_token"] = "auto"; @@ -76,31 +77,44 @@ export default function App() { await Promise.all([refreshStars(), refreshVersion(), fetchData()]); } }) - .catch(async () => { - setAutoLogin(false); - if (isAuthenticated && !isLoginPage) { - getUser(); - await Promise.all([refreshStars(), refreshVersion(), fetchData()]); - } else { - setLoading(false); - useFlowsManagerStore.setState({ isLoading: false }); + .catch(async (error) => { + if (error.name !== "CanceledError") { + setAutoLogin(false); + if (isAuthenticated && !isLoginPage) { + getUser(); + await Promise.all([refreshStars(), refreshVersion(), fetchData()]); + } else { + setLoading(false); + useFlowsManagerStore.setState({ isLoading: false }); + } } }); - }, [isAuthenticated]); + + /* + Abort the request as it isn't needed anymore, the component being + unmounted. It helps avoid, among other things, the well-known "can't + perform a React state update on an unmounted component" warning. + */ + return () => abortController.abort(); + }, []); const fetchData = async () => { - if (isAuthenticated) { - try { - await getTypes(); - refreshFlows(); - const res = await getGlobalVariables(); - setGlobalVariables(res); - checkHasStore(); - fetchApiData(); - } catch (error) { - console.error("Failed to fetch data:", error); + return new Promise(async (resolve, reject) => { + if (isAuthenticated) { + try { + await getTypes(); + await refreshFlows(); + const res = await getGlobalVariables(); + setGlobalVariables(res); + checkHasStore(); + fetchApiData(); + resolve(); + } catch (error) { + console.error("Failed to fetch data:", error); + reject(); + } } - } + }); }; useEffect(() => { diff --git a/src/frontend/src/components/codeTabsComponent/index.tsx b/src/frontend/src/components/codeTabsComponent/index.tsx index 78c344714..0a19ec273 100644 --- a/src/frontend/src/components/codeTabsComponent/index.tsx +++ b/src/frontend/src/components/codeTabsComponent/index.tsx @@ -39,8 +39,8 @@ import { classNames } from "../../utils/utils"; import ShadTooltip from "../ShadTooltipComponent"; import DictComponent from "../dictComponent"; import IconComponent from "../genericIconComponent"; -import InputGlobalComponent from "../inputGlobalComponent"; import KeypairListComponent from "../keypairListComponent"; +import InputComponent from "../inputComponent"; export default function CodeTabsComponent({ flow, @@ -351,31 +351,38 @@ export default function CodeTabsComponent({ /> ) : ( - { - if (node.data) { - setNode( - node.data.id, - (oldNode) => { - let newNode = - cloneDeep( - oldNode - ); - - newNode.data = { - ...newNode.data, - }; - - newNode.data.node.template[ - templateField - ].value = target; - - return newNode; - } - ); - } + setData((old) => { + let newInputList = + cloneDeep(old); + newInputList![ + i + ].data.node.template[ + templateField + ].value = target; + return newInputList; + }); tweaks.buildTweakObject!( node["data"]["id"], target, @@ -384,25 +391,6 @@ export default function CodeTabsComponent({ ] ); }} - setDb={(value) => { - setNode( - node.data.id, - (oldNode) => { - let newNode = - cloneDeep(oldNode); - newNode.data = { - ...newNode.data, - }; - newNode.data.node.template[ - templateField - ].load_from_db = - value; - return newNode; - } - ); - }} - name={templateField} - data={node.data} /> )} diff --git a/src/frontend/src/components/genericIconComponent/index.tsx b/src/frontend/src/components/genericIconComponent/index.tsx index 1fce0525c..f4e6a49d6 100644 --- a/src/frontend/src/components/genericIconComponent/index.tsx +++ b/src/frontend/src/components/genericIconComponent/index.tsx @@ -5,6 +5,8 @@ import { nodeIconsLucide } from "../../utils/styleUtils"; import { cn } from "../../utils/utils"; import Loading from "../ui/loading"; +import { useEffect, useState } from "react"; + const ForwardedIconComponent = memo( forwardRef( ( @@ -18,9 +20,18 @@ const ForwardedIconComponent = memo( }: IconComponentProps, ref ) => { + const [showFallback, setShowFallback] = useState(false); + + useEffect(() => { + const timer = setTimeout(() => { + setShowFallback(true); + }, 30); + + return () => clearTimeout(timer); + }, []); + let TargetIcon = nodeIconsLucide[name]; if (!TargetIcon) { - // check if name exists in dynamicIconImports if (!dynamicIconImports[name]) { TargetIcon = nodeIconsLucide["unknown"]; } else TargetIcon = lazy(dynamicIconImports[name]); @@ -35,11 +46,15 @@ const ForwardedIconComponent = memo( if (!TargetIcon) { return null; // Render nothing until the icon is loaded } - const fallback = ( + + const fallback = showFallback ? (
+ ) : ( +
); + return ( state.uploadFlow); const removeFlow = useFlowsManagerStore((state) => state.removeFlow); const isLoading = useFlowsManagerStore((state) => state.isLoading); - const setExamples = useFlowsManagerStore((state) => state.setExamples); const flows = useFlowsManagerStore((state) => state.flows); const setSuccessData = useAlertStore((state) => state.setSuccessData); const setErrorData = useAlertStore((state) => state.setErrorData); const [pageSize, setPageSize] = useState(20); const [pageIndex, setPageIndex] = useState(1); - const [loadingScreen, setLoadingScreen] = useState(true); const navigate = useNavigate(); - useEffect(() => { - if (isLoading) return; - let all = flows - .filter((f) => (f.is_component ?? false) === is_component) - .sort((a, b) => { - if (a?.updated_at && b?.updated_at) { - return ( - new Date(b?.updated_at!).getTime() - - new Date(a?.updated_at!).getTime() - ); - } else if (a?.updated_at && !b?.updated_at) { - return 1; - } else if (!a?.updated_at && b?.updated_at) { - return -1; - } else { - return ( - new Date(b?.date_created!).getTime() - - new Date(a?.date_created!).getTime() - ); - } - }); - const start = (pageIndex - 1) * pageSize; - const end = start + pageSize; - setData(all.slice(start, end)); - }, [flows, isLoading, pageIndex, pageSize]); - - const [data, setData] = useState([]); + const all: FlowType[] = flows + .filter((f) => (f.is_component ?? false) === is_component) + .sort((a, b) => { + if (a?.updated_at && b?.updated_at) { + return ( + new Date(b?.updated_at!).getTime() - + new Date(a?.updated_at!).getTime() + ); + } else if (a?.updated_at && !b?.updated_at) { + return 1; + } else if (!a?.updated_at && b?.updated_at) { + return -1; + } else { + return ( + new Date(b?.date_created!).getTime() - + new Date(a?.date_created!).getTime() + ); + } + }); + const start = (pageIndex - 1) * pageSize; + const end = start + pageSize; + const data: FlowType[] = all.slice(start, end); const name = is_component ? "Component" : "Flow"; @@ -149,7 +142,7 @@ export default function ComponentsComponent({ resetFilter(); }} key={idx} - data={item} + data={{ is_component: item.is_component ?? false, ...item }} disabled={isLoading} button={ !is_component ? ( diff --git a/src/frontend/src/routes.tsx b/src/frontend/src/routes.tsx index b48e5147d..fde8c28f9 100644 --- a/src/frontend/src/routes.tsx +++ b/src/frontend/src/routes.tsx @@ -1,5 +1,4 @@ -import { useEffect } from "react"; -import { Route, Routes, useNavigate } from "react-router-dom"; +import { Navigate, Route, Routes } from "react-router-dom"; import { ProtectedAdminRoute } from "./components/authAdminGuard"; import { ProtectedRoute } from "./components/authGuard"; import { ProtectedLoginRoute } from "./components/authLoginGuard"; @@ -19,13 +18,6 @@ import LoginPage from "./pages/loginPage"; import SignUp from "./pages/signUpPage"; const Router = () => { - const navigate = useNavigate(); - useEffect(() => { - // Redirect from root to /flows - if (window.location.pathname === "/") { - navigate("/flows"); - } - }, [navigate]); return ( { } > + } /> }