**Bug Fix**:
This PR resolves a critical bug causing the deletion of custom component
edges during saves. The issue stemmed from miscommunication between edge
tracking and saving logic.

**Enhancement**:
Additionally, this PR enhances user experience by introducing a loading
screen during app startup. The loading screen offers visual feedback for
app initialization.
This commit is contained in:
anovazzi1 2023-08-16 17:32:38 -03:00 • committed by GitHub
commit aa44125953
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 437 additions and 65 deletions

448
poetry.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "langflow" name = "langflow"
version = "0.4.10" version = "0.4.11"
description = "A Python package with a built-in web application" description = "A Python package with a built-in web application"
authors = ["Logspace <contact@logspace.ai>"] authors = ["Logspace <contact@logspace.ai>"]
maintainers = [ maintainers = [

View file

@ -9,6 +9,7 @@ import ErrorAlert from "./alerts/error";
import NoticeAlert from "./alerts/notice"; import NoticeAlert from "./alerts/notice";
import SuccessAlert from "./alerts/success"; import SuccessAlert from "./alerts/success";
import CrashErrorComponent from "./components/CrashErrorComponent"; import CrashErrorComponent from "./components/CrashErrorComponent";
import LoadingComponent from "./components/loadingComponent";
import { alertContext } from "./contexts/alertContext"; import { alertContext } from "./contexts/alertContext";
import { locationContext } from "./contexts/locationContext"; import { locationContext } from "./contexts/locationContext";
import { TabsContext } from "./contexts/tabsContext"; import { TabsContext } from "./contexts/tabsContext";
@ -24,6 +25,7 @@ export default function App() {
setIsStackedOpen(true); setIsStackedOpen(true);
}, [location.pathname, setCurrent, setIsStackedOpen, setShowSideBar]); }, [location.pathname, setCurrent, setIsStackedOpen, setShowSideBar]);
const { hardReset } = useContext(TabsContext); const { hardReset } = useContext(TabsContext);
const { const {
errorData, errorData,
errorOpen, errorOpen,
@ -34,6 +36,7 @@ export default function App() {
successData, successData,
successOpen, successOpen,
setSuccessOpen, setSuccessOpen,
loading,
} = useContext(alertContext); } = useContext(alertContext);
// Initialize state variable for the list of alerts // Initialize state variable for the list of alerts
@ -132,7 +135,15 @@ export default function App() {
}} }}
FallbackComponent={CrashErrorComponent} FallbackComponent={CrashErrorComponent}
> >
<Router /> {loading ? (
<div className="loading-page-panel">
<LoadingComponent remSize={50} />
</div>
) : (
<>
<Router />
</>
)}
</ErrorBoundary> </ErrorBoundary>
<div></div> <div></div>
<div className="app-div" style={{ zIndex: 999 }}> <div className="app-div" style={{ zIndex: 999 }}>

View file

@ -509,3 +509,5 @@ export const URL_EXCLUDED_FROM_ERROR_RETRIES = [
"/api/v1/custom_component", "/api/v1/custom_component",
"/api/v1/validate/prompt", "/api/v1/validate/prompt",
]; ];
export const skipNodeUpdate = ["CustomComponent"];

View file

@ -23,12 +23,16 @@ type alertContextType = {
pushNotificationList: (Object: AlertItemType) => void; pushNotificationList: (Object: AlertItemType) => void;
clearNotificationList: () => void; clearNotificationList: () => void;
removeFromNotificationList: (index: string) => void; removeFromNotificationList: (index: string) => void;
loading: boolean;
setLoading: (newState: boolean) => void;
}; };
//initial values to alertContextType //initial values to alertContextType
const initialValue: alertContextType = { const initialValue: alertContextType = {
errorData: { title: "", list: [] }, errorData: { title: "", list: [] },
setErrorData: () => {}, setErrorData: () => {},
loading: true,
setLoading: () => {},
errorOpen: false, errorOpen: false,
setErrorOpen: () => {}, setErrorOpen: () => {},
noticeData: { title: "", link: "" }, noticeData: { title: "", link: "" },
@ -55,6 +59,7 @@ export function AlertProvider({ children }: { children: ReactNode }) {
list?: Array<string>; list?: Array<string>;
}>({ title: "", list: [] }); }>({ title: "", list: [] });
const [errorOpen, setErrorOpen] = useState(false); const [errorOpen, setErrorOpen] = useState(false);
const [loading, setLoading] = useState(true);
const [noticeData, setNoticeDataState] = useState<{ const [noticeData, setNoticeDataState] = useState<{
title: string; title: string;
link?: string; link?: string;
@ -141,6 +146,8 @@ export function AlertProvider({ children }: { children: ReactNode }) {
removeFromNotificationList, removeFromNotificationList,
clearNotificationList, clearNotificationList,
notificationList, notificationList,
loading,
setLoading,
pushNotificationList, pushNotificationList,
setNotificationCenter, setNotificationCenter,
notificationCenter, notificationCenter,

View file

@ -16,17 +16,17 @@ export default function ContextWrapper({ children }: { children: ReactNode }) {
<TooltipProvider> <TooltipProvider>
<ReactFlowProvider> <ReactFlowProvider>
<DarkProvider> <DarkProvider>
<TypesProvider> <AlertProvider>
<LocationProvider> <TypesProvider>
<AlertProvider> <LocationProvider>
<SSEProvider> <SSEProvider>
<TabsProvider> <TabsProvider>
<UndoRedoProvider>{children}</UndoRedoProvider> <UndoRedoProvider>{children}</UndoRedoProvider>
</TabsProvider> </TabsProvider>
</SSEProvider> </SSEProvider>
</AlertProvider> </LocationProvider>
</LocationProvider> </TypesProvider>
</TypesProvider> </AlertProvider>
</DarkProvider> </DarkProvider>
</ReactFlowProvider> </ReactFlowProvider>
</TooltipProvider> </TooltipProvider>

View file

@ -9,6 +9,7 @@ import {
} from "react"; } from "react";
import { addEdge } from "reactflow"; import { addEdge } from "reactflow";
import ShortUniqueId from "short-unique-id"; import ShortUniqueId from "short-unique-id";
import { skipNodeUpdate } from "../constants/constants";
import { import {
deleteFlowFromDatabase, deleteFlowFromDatabase,
downloadFlowsFromDatabase, downloadFlowsFromDatabase,
@ -163,6 +164,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
function processFlowNodes(flow) { function processFlowNodes(flow) {
if (!flow.data || !flow.data.nodes) return; if (!flow.data || !flow.data.nodes) return;
flow.data.nodes.forEach((node: NodeType) => { flow.data.nodes.forEach((node: NodeType) => {
if (skipNodeUpdate.includes(node.data.type)) return;
const template = templates[node.data.type]; const template = templates[node.data.type];
if (!template) { if (!template) {
setErrorData({ title: `Unknown node type: ${node.data.type}` }); setErrorData({ title: `Unknown node type: ${node.data.type}` });
@ -506,6 +508,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
const updateNodes = (nodes, edges) => { const updateNodes = (nodes, edges) => {
nodes.forEach((node) => { nodes.forEach((node) => {
if (skipNodeUpdate.includes(node.data.type)) return;
const template = templates[node.data.type]; const template = templates[node.data.type];
if (!template) { if (!template) {
setErrorData({ title: `Unknown node type: ${node.data.type}` }); setErrorData({ title: `Unknown node type: ${node.data.type}` });

View file

@ -1,8 +1,15 @@
import { createContext, ReactNode, useEffect, useState } from "react"; import {
createContext,
ReactNode,
useContext,
useEffect,
useState,
} from "react";
import { Node } from "reactflow"; import { Node } from "reactflow";
import { getAll } from "../controllers/API"; import { getAll } from "../controllers/API";
import { APIKindType } from "../types/api"; import { APIKindType } from "../types/api";
import { typesContextType } from "../types/typesContext"; import { typesContextType } from "../types/typesContext";
import { alertContext } from "./alertContext";
//context to share types adn functions from nodes to flow //context to share types adn functions from nodes to flow
@ -25,6 +32,7 @@ export function TypesProvider({ children }: { children: ReactNode }) {
const [reactFlowInstance, setReactFlowInstance] = useState(null); const [reactFlowInstance, setReactFlowInstance] = useState(null);
const [templates, setTemplates] = useState({}); const [templates, setTemplates] = useState({});
const [data, setData] = useState({}); const [data, setData] = useState({});
const { setLoading } = useContext(alertContext);
useEffect(() => { useEffect(() => {
let delay = 1000; // Start delay of 1 second let delay = 1000; // Start delay of 1 second
@ -40,6 +48,7 @@ export function TypesProvider({ children }: { children: ReactNode }) {
const result = await getAll(); const result = await getAll();
// Make sure to only update the state if the component is still mounted. // Make sure to only update the state if the component is still mounted.
if (isMounted) { if (isMounted) {
setLoading(false);
setData(result.data); setData(result.data);
setTemplates( setTemplates(
Object.keys(result.data).reduce((acc, curr) => { Object.keys(result.data).reduce((acc, curr) => {

View file

@ -192,6 +192,10 @@
@apply flex w-full; @apply flex w-full;
} }
.loading-page-panel {
@apply h-full w-full flex justify-center items-center bg-muted;
}
.main-page-panel { .main-page-panel {
@apply flex-max-width h-full flex-col overflow-auto bg-muted px-16; @apply flex-max-width h-full flex-col overflow-auto bg-muted px-16;
} }