Fixed glitches that don't affect usability (#1800)

* Fixed bug of main page showing Empty page before showing components

* Added timeout to generic icon lazy loading

* Create an abort signal for the program to not fetch twice at the beginning

* Removed global variables functionality at tweaks
This commit is contained in:
Lucas Oliveira 2024-04-30 21:33:30 +02:00 • committed by GitHub
commit ee6aa0c6dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 112 additions and 107 deletions

View file

@ -63,9 +63,10 @@ export default function App() {
}, [dark]); }, [dark]);
useEffect(() => { useEffect(() => {
const abortController = new AbortController();
const isLoginPage = location.pathname.includes("login"); const isLoginPage = location.pathname.includes("login");
autoLogin() autoLogin(abortController.signal)
.then(async (user) => { .then(async (user) => {
if (user && user["access_token"]) { if (user && user["access_token"]) {
user["refresh_token"] = "auto"; user["refresh_token"] = "auto";
@ -76,31 +77,44 @@ export default function App() {
await Promise.all([refreshStars(), refreshVersion(), fetchData()]); await Promise.all([refreshStars(), refreshVersion(), fetchData()]);
} }
}) })
.catch(async () => { .catch(async (error) => {
setAutoLogin(false); if (error.name !== "CanceledError") {
if (isAuthenticated && !isLoginPage) { setAutoLogin(false);
getUser(); if (isAuthenticated && !isLoginPage) {
await Promise.all([refreshStars(), refreshVersion(), fetchData()]); getUser();
} else { await Promise.all([refreshStars(), refreshVersion(), fetchData()]);
setLoading(false); } else {
useFlowsManagerStore.setState({ isLoading: false }); 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 () => { const fetchData = async () => {
if (isAuthenticated) { return new Promise<void>(async (resolve, reject) => {
try { if (isAuthenticated) {
await getTypes(); try {
refreshFlows(); await getTypes();
const res = await getGlobalVariables(); await refreshFlows();
setGlobalVariables(res); const res = await getGlobalVariables();
checkHasStore(); setGlobalVariables(res);
fetchApiData(); checkHasStore();
} catch (error) { fetchApiData();
console.error("Failed to fetch data:", error); resolve();
} catch (error) {
console.error("Failed to fetch data:", error);
reject();
}
} }
} });
}; };
useEffect(() => { useEffect(() => {

View file

@ -39,8 +39,8 @@ import { classNames } from "../../utils/utils";
import ShadTooltip from "../ShadTooltipComponent"; import ShadTooltip from "../ShadTooltipComponent";
import DictComponent from "../dictComponent"; import DictComponent from "../dictComponent";
import IconComponent from "../genericIconComponent"; import IconComponent from "../genericIconComponent";
import InputGlobalComponent from "../inputGlobalComponent";
import KeypairListComponent from "../keypairListComponent"; import KeypairListComponent from "../keypairListComponent";
import InputComponent from "../inputComponent";
export default function CodeTabsComponent({ export default function CodeTabsComponent({
flow, flow,
@ -351,31 +351,38 @@ export default function CodeTabsComponent({
/> />
</div> </div>
) : ( ) : (
<InputGlobalComponent <InputComponent
editNode={true} editNode={true}
disabled={false} disabled={false}
password={
node.data.node.template[
templateField
].password ?? false
}
value={
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: node.data.node
.template[
templateField
].value
}
onChange={(target) => { onChange={(target) => {
if (node.data) { setData((old) => {
setNode( let newInputList =
node.data.id, cloneDeep(old);
(oldNode) => { newInputList![
let newNode = i
cloneDeep( ].data.node.template[
oldNode templateField
); ].value = target;
return newInputList;
newNode.data = { });
...newNode.data,
};
newNode.data.node.template[
templateField
].value = target;
return newNode;
}
);
}
tweaks.buildTweakObject!( tweaks.buildTweakObject!(
node["data"]["id"], node["data"]["id"],
target, 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}
/> />
)} )}
</div> </div>

View file

@ -5,6 +5,8 @@ import { nodeIconsLucide } from "../../utils/styleUtils";
import { cn } from "../../utils/utils"; import { cn } from "../../utils/utils";
import Loading from "../ui/loading"; import Loading from "../ui/loading";
import { useEffect, useState } from "react";
const ForwardedIconComponent = memo( const ForwardedIconComponent = memo(
forwardRef( forwardRef(
( (
@ -18,9 +20,18 @@ const ForwardedIconComponent = memo(
}: IconComponentProps, }: IconComponentProps,
ref ref
) => { ) => {
const [showFallback, setShowFallback] = useState(false);
useEffect(() => {
const timer = setTimeout(() => {
setShowFallback(true);
}, 30);
return () => clearTimeout(timer);
}, []);
let TargetIcon = nodeIconsLucide[name]; let TargetIcon = nodeIconsLucide[name];
if (!TargetIcon) { if (!TargetIcon) {
// check if name exists in dynamicIconImports
if (!dynamicIconImports[name]) { if (!dynamicIconImports[name]) {
TargetIcon = nodeIconsLucide["unknown"]; TargetIcon = nodeIconsLucide["unknown"];
} else TargetIcon = lazy(dynamicIconImports[name]); } else TargetIcon = lazy(dynamicIconImports[name]);
@ -35,11 +46,15 @@ const ForwardedIconComponent = memo(
if (!TargetIcon) { if (!TargetIcon) {
return null; // Render nothing until the icon is loaded return null; // Render nothing until the icon is loaded
} }
const fallback = (
const fallback = showFallback ? (
<div className={cn(className, "flex items-center justify-center")}> <div className={cn(className, "flex items-center justify-center")}>
<Loading /> <Loading />
</div> </div>
) : (
<div className={className}></div>
); );
return ( return (
<Suspense fallback={fallback}> <Suspense fallback={fallback}>
<TargetIcon <TargetIcon

View file

@ -406,9 +406,11 @@ export async function onLogin(user: LoginType) {
} }
} }
export async function autoLogin() { export async function autoLogin(abortSignal) {
try { try {
const response = await api.get(`${BASE_URL_API}auto_login`); const response = await api.get(`${BASE_URL_API}auto_login`, {
signal: abortSignal,
});
if (response.status === 200) { if (response.status === 200) {
const data = response.data; const data = response.data;

View file

@ -1,4 +1,4 @@
import { useEffect, useState } from "react"; import { useState } from "react";
import { Link, useNavigate } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import PaginatorComponent from "../../../../components/PaginatorComponent"; import PaginatorComponent from "../../../../components/PaginatorComponent";
import CollectionCardComponent from "../../../../components/cardComponent"; import CollectionCardComponent from "../../../../components/cardComponent";
@ -24,43 +24,36 @@ export default function ComponentsComponent({
const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow); const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow);
const removeFlow = useFlowsManagerStore((state) => state.removeFlow); const removeFlow = useFlowsManagerStore((state) => state.removeFlow);
const isLoading = useFlowsManagerStore((state) => state.isLoading); const isLoading = useFlowsManagerStore((state) => state.isLoading);
const setExamples = useFlowsManagerStore((state) => state.setExamples);
const flows = useFlowsManagerStore((state) => state.flows); const flows = useFlowsManagerStore((state) => state.flows);
const setSuccessData = useAlertStore((state) => state.setSuccessData); const setSuccessData = useAlertStore((state) => state.setSuccessData);
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
const [pageSize, setPageSize] = useState(20); const [pageSize, setPageSize] = useState(20);
const [pageIndex, setPageIndex] = useState(1); const [pageIndex, setPageIndex] = useState(1);
const [loadingScreen, setLoadingScreen] = useState(true);
const navigate = useNavigate(); const navigate = useNavigate();
useEffect(() => { const all: FlowType[] = flows
if (isLoading) return; .filter((f) => (f.is_component ?? false) === is_component)
let all = flows .sort((a, b) => {
.filter((f) => (f.is_component ?? false) === is_component) if (a?.updated_at && b?.updated_at) {
.sort((a, b) => { return (
if (a?.updated_at && b?.updated_at) { new Date(b?.updated_at!).getTime() -
return ( new Date(a?.updated_at!).getTime()
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) { } else if (!a?.updated_at && b?.updated_at) {
return 1; return -1;
} else if (!a?.updated_at && b?.updated_at) { } else {
return -1; return (
} else { new Date(b?.date_created!).getTime() -
return ( new Date(a?.date_created!).getTime()
new Date(b?.date_created!).getTime() - );
new Date(a?.date_created!).getTime() }
); });
} const start = (pageIndex - 1) * pageSize;
}); const end = start + pageSize;
const start = (pageIndex - 1) * pageSize; const data: FlowType[] = all.slice(start, end);
const end = start + pageSize;
setData(all.slice(start, end));
}, [flows, isLoading, pageIndex, pageSize]);
const [data, setData] = useState<FlowType[]>([]);
const name = is_component ? "Component" : "Flow"; const name = is_component ? "Component" : "Flow";
@ -149,7 +142,7 @@ export default function ComponentsComponent({
resetFilter(); resetFilter();
}} }}
key={idx} key={idx}
data={item} data={{ is_component: item.is_component ?? false, ...item }}
disabled={isLoading} disabled={isLoading}
button={ button={
!is_component ? ( !is_component ? (

View file

@ -1,5 +1,4 @@
import { useEffect } from "react"; import { Navigate, Route, Routes } from "react-router-dom";
import { Route, Routes, useNavigate } from "react-router-dom";
import { ProtectedAdminRoute } from "./components/authAdminGuard"; import { ProtectedAdminRoute } from "./components/authAdminGuard";
import { ProtectedRoute } from "./components/authGuard"; import { ProtectedRoute } from "./components/authGuard";
import { ProtectedLoginRoute } from "./components/authLoginGuard"; import { ProtectedLoginRoute } from "./components/authLoginGuard";
@ -19,13 +18,6 @@ import LoginPage from "./pages/loginPage";
import SignUp from "./pages/signUpPage"; import SignUp from "./pages/signUpPage";
const Router = () => { const Router = () => {
const navigate = useNavigate();
useEffect(() => {
// Redirect from root to /flows
if (window.location.pathname === "/") {
navigate("/flows");
}
}, [navigate]);
return ( return (
<Routes> <Routes>
<Route <Route
@ -36,6 +28,7 @@ const Router = () => {
</ProtectedRoute> </ProtectedRoute>
} }
> >
<Route index element={<Navigate replace to={"flows"} />} />
<Route <Route
path="flows" path="flows"
element={<ComponentsComponent key="flows" is_component={false} />} element={<ComponentsComponent key="flows" is_component={false} />}