mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-09 22:24:05 -08:00
fix(editor): Fix workflow loading after switching to executions view in new canvas (no-changelog) (#10655)
This commit is contained in:
parent
8750b287f5
commit
0f91fd2b2e
|
@ -328,6 +328,7 @@ export const routes: RouteRecordRaw[] = [
|
|||
default: NodeView,
|
||||
},
|
||||
meta: {
|
||||
nodeView: true,
|
||||
middleware: ['authenticated'],
|
||||
middlewareOptions: {
|
||||
authenticated: {
|
||||
|
|
|
@ -428,9 +428,9 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
|
|||
|
||||
async function fetchWorkflow(id: string): Promise<IWorkflowDb> {
|
||||
const rootStore = useRootStore();
|
||||
const workflow = await workflowsApi.getWorkflow(rootStore.restApiContext, id);
|
||||
addWorkflow(workflow);
|
||||
return workflow;
|
||||
const workflowData = await workflowsApi.getWorkflow(rootStore.restApiContext, id);
|
||||
addWorkflow(workflowData);
|
||||
return workflowData;
|
||||
}
|
||||
|
||||
async function getNewWorkflowData(name?: string, projectId?: string): Promise<INewWorkflowData> {
|
||||
|
|
|
@ -5,13 +5,13 @@ import {
|
|||
nextTick,
|
||||
onActivated,
|
||||
onBeforeMount,
|
||||
onBeforeUnmount,
|
||||
onDeactivated,
|
||||
onMounted,
|
||||
ref,
|
||||
useCssModule,
|
||||
watch,
|
||||
h,
|
||||
onBeforeUnmount,
|
||||
} from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import WorkflowCanvas from '@/components/canvas/WorkflowCanvas.vue';
|
||||
|
@ -53,7 +53,9 @@ import {
|
|||
MAIN_HEADER_TABS,
|
||||
MANUAL_CHAT_TRIGGER_NODE_TYPE,
|
||||
MODAL_CONFIRM,
|
||||
NEW_WORKFLOW_ID,
|
||||
NODE_CREATOR_OPEN_SOURCES,
|
||||
PLACEHOLDER_EMPTY_WORKFLOW_ID,
|
||||
START_NODE_TYPE,
|
||||
STICKY_NODE_TYPE,
|
||||
VALID_WORKFLOW_IMPORT_URL_REGEX,
|
||||
|
@ -152,7 +154,7 @@ const canvasEventBus = createEventBus<CanvasEventBusEvents>();
|
|||
const { addBeforeUnloadEventBindings, removeBeforeUnloadEventBindings } = useBeforeUnload({
|
||||
route,
|
||||
});
|
||||
const { registerCustomAction } = useGlobalLinkActions();
|
||||
const { registerCustomAction, unregisterCustomAction } = useGlobalLinkActions();
|
||||
const { runWorkflow, stopCurrentExecution, stopWaitingForWebhook } = useRunWorkflow({ router });
|
||||
const {
|
||||
updateNodePosition,
|
||||
|
@ -201,12 +203,18 @@ const isExecutionPreview = ref(false);
|
|||
const canOpenNDV = ref(true);
|
||||
const hideNodeIssues = ref(false);
|
||||
|
||||
const workflowId = computed<string>(() => route.params.name as string);
|
||||
const workflow = computed(() => workflowsStore.workflowsById[workflowId.value]);
|
||||
const initializedWorkflowId = ref<string | undefined>();
|
||||
const workflowId = computed(() => {
|
||||
const workflowIdParam = route.params.name as string;
|
||||
return [PLACEHOLDER_EMPTY_WORKFLOW_ID, NEW_WORKFLOW_ID].includes(workflowIdParam)
|
||||
? undefined
|
||||
: workflowIdParam;
|
||||
});
|
||||
|
||||
const isNewWorkflowRoute = computed(() => route.name === VIEWS.NEW_WORKFLOW);
|
||||
const isNewWorkflowRoute = computed(() => route.name === VIEWS.NEW_WORKFLOW || !workflowId.value);
|
||||
const isWorkflowRoute = computed(() => !!route?.meta?.nodeView);
|
||||
const isDemoRoute = computed(() => route.name === VIEWS.DEMO);
|
||||
const isReadOnlyRoute = computed(() => route?.meta?.readOnlyCanvas === true);
|
||||
const isReadOnlyRoute = computed(() => !!route?.meta?.readOnlyCanvas);
|
||||
const isReadOnlyEnvironment = computed(() => {
|
||||
return sourceControlStore.preferences.branchReadOnly;
|
||||
});
|
||||
|
@ -287,6 +295,10 @@ async function initializeRoute() {
|
|||
return;
|
||||
}
|
||||
|
||||
const isAlreadyInitialized =
|
||||
initializedWorkflowId.value &&
|
||||
[NEW_WORKFLOW_ID, workflowId.value].includes(initializedWorkflowId.value);
|
||||
|
||||
// This function is called on route change as well, so we need to do the following:
|
||||
// - if the redirect is blank, then do nothing
|
||||
// - if the route is the template import view, then open the template
|
||||
|
@ -296,11 +308,11 @@ async function initializeRoute() {
|
|||
} else if (route.name === VIEWS.TEMPLATE_IMPORT) {
|
||||
const templateId = route.params.id;
|
||||
await openWorkflowTemplate(templateId.toString());
|
||||
} else {
|
||||
} else if (isWorkflowRoute.value && !isAlreadyInitialized) {
|
||||
historyStore.reset();
|
||||
|
||||
// If there is no workflow id, treat it as a new workflow
|
||||
if (!workflowId.value || isNewWorkflowRoute.value) {
|
||||
if (isNewWorkflowRoute.value || !workflowId.value) {
|
||||
if (route.meta?.nodeView === true) {
|
||||
await initializeWorkspaceForNewWorkflow();
|
||||
}
|
||||
|
@ -308,14 +320,14 @@ async function initializeRoute() {
|
|||
}
|
||||
|
||||
await initializeWorkspaceForExistingWorkflow(workflowId.value);
|
||||
|
||||
nodeHelpers.updateNodesInputIssues();
|
||||
nodeHelpers.updateNodesCredentialsIssues();
|
||||
nodeHelpers.updateNodesParameterIssues();
|
||||
|
||||
await loadCredentials();
|
||||
await initializeDebugMode();
|
||||
}
|
||||
|
||||
nodeHelpers.updateNodesInputIssues();
|
||||
nodeHelpers.updateNodesCredentialsIssues();
|
||||
nodeHelpers.updateNodesParameterIssues();
|
||||
|
||||
await loadCredentials();
|
||||
await initializeDebugMode();
|
||||
}
|
||||
|
||||
async function initializeWorkspaceForNewWorkflow() {
|
||||
|
@ -325,11 +337,10 @@ async function initializeWorkspaceForNewWorkflow() {
|
|||
workflowsStore.makeNewWorkflowShareable();
|
||||
|
||||
uiStore.nodeViewInitialized = true;
|
||||
initializedWorkflowId.value = NEW_WORKFLOW_ID;
|
||||
}
|
||||
|
||||
async function initializeWorkspaceForExistingWorkflow(id: string) {
|
||||
resetWorkspace();
|
||||
|
||||
try {
|
||||
const workflowData = await workflowsStore.fetchWorkflow(id);
|
||||
|
||||
|
@ -339,7 +350,9 @@ async function initializeWorkspaceForExistingWorkflow(id: string) {
|
|||
trackOpenWorkflowFromOnboardingTemplate();
|
||||
}
|
||||
|
||||
await projectsStore.setProjectNavActiveIdByWorkflowHomeProject(workflow.value.homeProject);
|
||||
await projectsStore.setProjectNavActiveIdByWorkflowHomeProject(
|
||||
editableWorkflow.value.homeProject,
|
||||
);
|
||||
|
||||
collaborationStore.notifyWorkflowOpened(id);
|
||||
} catch (error) {
|
||||
|
@ -350,6 +363,7 @@ async function initializeWorkspaceForExistingWorkflow(id: string) {
|
|||
});
|
||||
} finally {
|
||||
uiStore.nodeViewInitialized = true;
|
||||
initializedWorkflowId.value = workflowId.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -359,7 +373,7 @@ async function initializeWorkspaceForExistingWorkflow(id: string) {
|
|||
|
||||
async function openWorkflow(data: IWorkflowDb) {
|
||||
resetWorkspace();
|
||||
titleSet(workflow.value.name, 'IDLE');
|
||||
titleSet(editableWorkflow.value.name, 'IDLE');
|
||||
|
||||
await initializeWorkspace(data);
|
||||
|
||||
|
@ -382,7 +396,7 @@ async function openWorkflow(data: IWorkflowDb) {
|
|||
|
||||
function trackOpenWorkflowFromOnboardingTemplate() {
|
||||
telemetry.track(
|
||||
`User opened workflow from onboarding template with ID ${workflow.value.meta?.onboardingId}`,
|
||||
`User opened workflow from onboarding template with ID ${editableWorkflow.value.meta?.onboardingId}`,
|
||||
{
|
||||
workflow_id: workflowId.value,
|
||||
},
|
||||
|
@ -716,8 +730,8 @@ function onClickNodeAdd(source: string, sourceHandle: string) {
|
|||
async function loadCredentials() {
|
||||
let options: { workflowId: string } | { projectId: string };
|
||||
|
||||
if (workflow.value) {
|
||||
options = { workflowId: workflow.value.id };
|
||||
if (editableWorkflow.value) {
|
||||
options = { workflowId: editableWorkflow.value.id };
|
||||
} else {
|
||||
const queryParam =
|
||||
typeof route.query?.projectId === 'string' ? route.query?.projectId : undefined;
|
||||
|
@ -917,7 +931,9 @@ function onClickConnectionAdd(connection: Connection) {
|
|||
*/
|
||||
|
||||
const workflowPermissions = computed(() => {
|
||||
return getResourcePermissions(workflowsStore.getWorkflowById(workflowId.value)?.scopes).workflow;
|
||||
return workflowId.value
|
||||
? getResourcePermissions(workflowsStore.getWorkflowById(workflowId.value)?.scopes).workflow
|
||||
: {};
|
||||
});
|
||||
|
||||
const projectPermissions = computed(() => {
|
||||
|
@ -1200,7 +1216,7 @@ async function onSourceControlPull() {
|
|||
loadCredentials(),
|
||||
]);
|
||||
|
||||
if (workflowId.value !== null && !uiStore.stateIsDirty) {
|
||||
if (workflowId.value && !uiStore.stateIsDirty) {
|
||||
const workflowData = await workflowsStore.fetchWorkflow(workflowId.value);
|
||||
if (workflowData) {
|
||||
titleSet(workflowData.name, 'IDLE');
|
||||
|
@ -1306,6 +1322,10 @@ async function onPostMessageReceived(messageEvent: MessageEvent) {
|
|||
*/
|
||||
|
||||
function checkIfEditingIsAllowed(): boolean {
|
||||
if (!initializedWorkflowId.value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (readOnlyNotification.value?.visible) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1438,6 +1458,12 @@ function registerCustomActions() {
|
|||
});
|
||||
}
|
||||
|
||||
function unregisterCustomActions() {
|
||||
unregisterCustomAction('openNodeDetail');
|
||||
unregisterCustomAction('openSelectiveNodeCreator');
|
||||
unregisterCustomAction('showNodeCreator');
|
||||
}
|
||||
|
||||
/**
|
||||
* Routing
|
||||
*/
|
||||
|
@ -1445,10 +1471,6 @@ function registerCustomActions() {
|
|||
watch(
|
||||
() => route.name,
|
||||
async () => {
|
||||
if (!checkIfEditingIsAllowed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
await initializeRoute();
|
||||
},
|
||||
);
|
||||
|
@ -1464,8 +1486,9 @@ onBeforeMount(() => {
|
|||
}
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
onMounted(() => {
|
||||
canvasStore.startLoading();
|
||||
|
||||
titleReset();
|
||||
resetWorkspace();
|
||||
|
||||
|
@ -1479,6 +1502,8 @@ onMounted(async () => {
|
|||
.finally(() => {
|
||||
isLoading.value = false;
|
||||
canvasStore.stopLoading();
|
||||
|
||||
void externalHooks.run('nodeView.mount').catch(() => {});
|
||||
});
|
||||
|
||||
void usersStore.showPersonalizationSurvey();
|
||||
|
@ -1486,34 +1511,31 @@ onMounted(async () => {
|
|||
checkIfRouteIsAllowed();
|
||||
});
|
||||
|
||||
addUndoRedoEventBindings();
|
||||
addPostMessageEventBindings();
|
||||
addSourceControlEventBindings();
|
||||
addPostMessageEventBindings();
|
||||
addWorkflowSavedEventBindings();
|
||||
addBeforeUnloadEventBindings();
|
||||
addImportEventBindings();
|
||||
addExecutionOpenedEventBindings();
|
||||
addWorkflowSavedEventBindings();
|
||||
|
||||
registerCustomActions();
|
||||
|
||||
// @TODO: This currently breaks since front-end hooks are still not updated to work with pinia store
|
||||
void externalHooks.run('nodeView.mount').catch(() => {});
|
||||
});
|
||||
|
||||
onActivated(() => {
|
||||
addBeforeUnloadEventBindings();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
removeUndoRedoEventBindings();
|
||||
removePostMessageEventBindings();
|
||||
removeSourceControlEventBindings();
|
||||
removeImportEventBindings();
|
||||
removeExecutionOpenedEventBindings();
|
||||
removeWorkflowSavedEventBindings();
|
||||
onActivated(async () => {
|
||||
addUndoRedoEventBindings();
|
||||
});
|
||||
|
||||
onDeactivated(() => {
|
||||
removeUndoRedoEventBindings();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
removeSourceControlEventBindings();
|
||||
removePostMessageEventBindings();
|
||||
removeWorkflowSavedEventBindings();
|
||||
removeBeforeUnloadEventBindings();
|
||||
removeImportEventBindings();
|
||||
removeExecutionOpenedEventBindings();
|
||||
unregisterCustomActions();
|
||||
collaborationStore.terminate();
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -8,7 +8,7 @@ import { useWorkflowsStore } from '@/stores/workflows.store';
|
|||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { NO_NETWORK_ERROR_CODE } from '@/utils/apiUtils';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { PLACEHOLDER_EMPTY_WORKFLOW_ID, VIEWS } from '@/constants';
|
||||
import { NEW_WORKFLOW_ID, PLACEHOLDER_EMPTY_WORKFLOW_ID, VIEWS } from '@/constants';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import type { ExecutionSummary } from 'n8n-workflow';
|
||||
import { useDebounce } from '@/composables/useDebounce';
|
||||
|
@ -34,15 +34,22 @@ const loadingMore = ref(false);
|
|||
const workflow = ref<IWorkflowDb | undefined>();
|
||||
|
||||
const workflowId = computed(() => {
|
||||
return (route.params.name as string) || workflowsStore.workflowId;
|
||||
const workflowIdParam = route.params.name as string;
|
||||
return [PLACEHOLDER_EMPTY_WORKFLOW_ID, NEW_WORKFLOW_ID].includes(workflowIdParam)
|
||||
? undefined
|
||||
: workflowIdParam;
|
||||
});
|
||||
|
||||
const executionId = computed(() => route.params.executionId as string);
|
||||
|
||||
const executions = computed(() => [
|
||||
...(executionsStore.currentExecutionsByWorkflowId[workflowId.value] ?? []),
|
||||
...(executionsStore.executionsByWorkflowId[workflowId.value] ?? []),
|
||||
]);
|
||||
const executions = computed(() =>
|
||||
workflowId.value
|
||||
? [
|
||||
...(executionsStore.currentExecutionsByWorkflowId[workflowId.value] ?? []),
|
||||
...(executionsStore.executionsByWorkflowId[workflowId.value] ?? []),
|
||||
]
|
||||
: [],
|
||||
);
|
||||
|
||||
const execution = computed(() => {
|
||||
return executions.value.find((e) => e.id === executionId.value) ?? currentExecution.value;
|
||||
|
@ -65,13 +72,12 @@ watch(
|
|||
);
|
||||
|
||||
onMounted(async () => {
|
||||
await nodeTypesStore.loadNodeTypesIfNotLoaded();
|
||||
await Promise.all([
|
||||
nodeTypesStore.loadNodeTypesIfNotLoaded(),
|
||||
fetchWorkflow(),
|
||||
executionsStore.initialize(workflowId.value),
|
||||
]);
|
||||
await fetchExecution();
|
||||
await Promise.all([nodeTypesStore.loadNodeTypesIfNotLoaded(), fetchWorkflow()]);
|
||||
|
||||
if (workflowId.value) {
|
||||
await Promise.all([executionsStore.initialize(workflowId.value), fetchExecution()]);
|
||||
}
|
||||
|
||||
await initializeRoute();
|
||||
document.addEventListener('visibilitychange', onDocumentVisibilityChange);
|
||||
});
|
||||
|
@ -116,19 +122,23 @@ async function initializeRoute() {
|
|||
}
|
||||
|
||||
async function fetchWorkflow() {
|
||||
// Check if the workflow already has an ID
|
||||
// In other words: are we coming from the Editor tab or browser loaded the Executions tab directly
|
||||
if (workflowsStore.workflow.id === PLACEHOLDER_EMPTY_WORKFLOW_ID) {
|
||||
try {
|
||||
await workflowsStore.fetchActiveWorkflows();
|
||||
const data = await workflowsStore.fetchWorkflow(workflowId.value);
|
||||
workflowHelpers.initState(data);
|
||||
await nodeHelpers.addNodes(data.nodes, data.connections);
|
||||
} catch (error) {
|
||||
toast.showError(error, i18n.baseText('nodeView.showError.openWorkflow.title'));
|
||||
if (workflowId.value) {
|
||||
// Check if we are loading the Executions tab directly, without having loaded the workflow
|
||||
if (workflowsStore.workflow.id === PLACEHOLDER_EMPTY_WORKFLOW_ID) {
|
||||
try {
|
||||
await workflowsStore.fetchActiveWorkflows();
|
||||
const data = await workflowsStore.fetchWorkflow(workflowId.value);
|
||||
workflowHelpers.initState(data);
|
||||
await nodeHelpers.addNodes(data.nodes, data.connections);
|
||||
} catch (error) {
|
||||
toast.showError(error, i18n.baseText('nodeView.showError.openWorkflow.title'));
|
||||
}
|
||||
}
|
||||
|
||||
workflow.value = workflowsStore.getWorkflowById(workflowId.value);
|
||||
} else {
|
||||
workflow.value = workflowsStore.workflow;
|
||||
}
|
||||
workflow.value = workflowsStore.getWorkflowById(workflowId.value);
|
||||
}
|
||||
|
||||
async function onAutoRefreshToggle(value: boolean) {
|
||||
|
|
Loading…
Reference in a new issue