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:
parent
b097efe94b
commit
ee6aa0c6dd
6 changed files with 112 additions and 107 deletions
|
|
@ -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,7 +77,8 @@ export default function App() {
|
||||||
await Promise.all([refreshStars(), refreshVersion(), fetchData()]);
|
await Promise.all([refreshStars(), refreshVersion(), fetchData()]);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(async () => {
|
.catch(async (error) => {
|
||||||
|
if (error.name !== "CanceledError") {
|
||||||
setAutoLogin(false);
|
setAutoLogin(false);
|
||||||
if (isAuthenticated && !isLoginPage) {
|
if (isAuthenticated && !isLoginPage) {
|
||||||
getUser();
|
getUser();
|
||||||
|
|
@ -85,22 +87,34 @@ export default function App() {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
useFlowsManagerStore.setState({ isLoading: 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 () => {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
try {
|
try {
|
||||||
await getTypes();
|
await getTypes();
|
||||||
refreshFlows();
|
await refreshFlows();
|
||||||
const res = await getGlobalVariables();
|
const res = await getGlobalVariables();
|
||||||
setGlobalVariables(res);
|
setGlobalVariables(res);
|
||||||
checkHasStore();
|
checkHasStore();
|
||||||
fetchApiData();
|
fetchApiData();
|
||||||
|
resolve();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch data:", error);
|
console.error("Failed to fetch data:", error);
|
||||||
|
reject();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
);
|
|
||||||
|
|
||||||
newNode.data = {
|
|
||||||
...newNode.data,
|
|
||||||
};
|
|
||||||
|
|
||||||
newNode.data.node.template[
|
|
||||||
templateField
|
templateField
|
||||||
].value = target;
|
].value = target;
|
||||||
|
return newInputList;
|
||||||
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>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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,19 +24,15 @@ 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;
|
|
||||||
let all = flows
|
|
||||||
.filter((f) => (f.is_component ?? false) === is_component)
|
.filter((f) => (f.is_component ?? false) === is_component)
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
if (a?.updated_at && b?.updated_at) {
|
if (a?.updated_at && b?.updated_at) {
|
||||||
|
|
@ -57,10 +53,7 @@ export default function ComponentsComponent({
|
||||||
});
|
});
|
||||||
const start = (pageIndex - 1) * pageSize;
|
const start = (pageIndex - 1) * pageSize;
|
||||||
const end = start + pageSize;
|
const end = start + pageSize;
|
||||||
setData(all.slice(start, end));
|
const data: FlowType[] = 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 ? (
|
||||||
|
|
|
||||||
|
|
@ -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} />}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue