mirror of
https://github.com/n8n-io/n8n.git
synced 2025-02-21 02:56:40 -08:00
fix(editor): Fix for loading executions that are not on the current executions list (#5035)
* fix(editor): fixed executions list scroll not working on large screens * ⚡ Use limit to only load necessary number of additional executions * 🐛 Fixing loading execution that is not on the current execution list * ✔️ Fixing lint error * ⚡ Fixing more executions count logic * 📚 Updating comments * 🔥 Removing leftover `console.log`
This commit is contained in:
parent
d113977b10
commit
d0865e28ff
|
@ -27,3 +27,7 @@ export async function getCurrentExecutions(context: IRestApiContext, filter: IDa
|
|||
export async function getFinishedExecutions(context: IRestApiContext, filter: IDataObject) {
|
||||
return await makeRestApiRequest(context, 'GET', '/executions', { filter });
|
||||
}
|
||||
|
||||
export async function getExecutionData(context: IRestApiContext, executionId: string) {
|
||||
return await makeRestApiRequest(context, 'GET', `/executions/${executionId}`);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div :class="['executions-sidebar', $style.container]">
|
||||
<div :class="['executions-sidebar', $style.container]" ref="container">
|
||||
<div :class="$style.heading">
|
||||
<n8n-heading tag="h2" size="medium" color="text-dark">
|
||||
{{ $locale.baseText('generic.executions') }}
|
||||
|
@ -60,7 +60,7 @@
|
|||
</n8n-link>
|
||||
</n8n-info-tip>
|
||||
</div>
|
||||
<div :class="$style.executionList" ref="executionList" @scroll="loadMore">
|
||||
<div :class="$style.executionList" ref="executionList" @scroll="loadMore(20)">
|
||||
<div v-if="loading" class="mr-m">
|
||||
<n8n-loading :class="$style.loader" variant="p" :rows="1" />
|
||||
<n8n-loading :class="$style.loader" variant="p" :rows="1" />
|
||||
|
@ -149,12 +149,19 @@ export default Vue.extend({
|
|||
this.$router.go(-1);
|
||||
}
|
||||
},
|
||||
'workflowsStore.activeWorkflowExecution'() {
|
||||
this.checkListSize();
|
||||
this.scrollToActiveCard();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.autoRefresh = this.uiStore.executionSidebarAutoRefresh === true;
|
||||
if (this.autoRefresh) {
|
||||
this.autoRefreshInterval = setInterval(() => this.onRefresh(), 4000);
|
||||
}
|
||||
// On larger screens, we need to load more then first page of executions
|
||||
// for the scroll bar to appear and infinite scrolling is enabled
|
||||
this.checkListSize();
|
||||
this.scrollToActiveCard();
|
||||
},
|
||||
beforeDestroy() {
|
||||
|
@ -164,14 +171,14 @@ export default Vue.extend({
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
loadMore(): void {
|
||||
loadMore(limit = 20): void {
|
||||
if (!this.loading) {
|
||||
const executionsList = this.$refs.executionList as HTMLElement;
|
||||
if (executionsList) {
|
||||
const diff =
|
||||
executionsList.offsetHeight - (executionsList.scrollHeight - executionsList.scrollTop);
|
||||
if (diff > -10 && diff < 10) {
|
||||
this.$emit('loadMore');
|
||||
this.$emit('loadMore', limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -209,6 +216,23 @@ export default Vue.extend({
|
|||
status: this.filter.status,
|
||||
};
|
||||
},
|
||||
checkListSize(): void {
|
||||
const sidebarContainer = this.$refs.container as HTMLElement;
|
||||
const currentExecutionCard = this.$refs[
|
||||
`execution-${this.workflowsStore.activeWorkflowExecution?.id}`
|
||||
] as Vue[];
|
||||
|
||||
// Find out how many execution card can fit into list
|
||||
// and load more if needed
|
||||
if (sidebarContainer && currentExecutionCard) {
|
||||
const cardElement = currentExecutionCard[0].$el as HTMLElement;
|
||||
const listCapacity = Math.ceil(sidebarContainer.clientHeight / cardElement.clientHeight);
|
||||
|
||||
if (listCapacity > this.executions.length) {
|
||||
this.$emit('loadMore', listCapacity - this.executions.length);
|
||||
}
|
||||
}
|
||||
},
|
||||
scrollToActiveCard(): void {
|
||||
const executionsList = this.$refs.executionList as HTMLElement;
|
||||
const currentExecutionCard = this.$refs[
|
||||
|
@ -218,8 +242,9 @@ export default Vue.extend({
|
|||
if (executionsList && currentExecutionCard && this.workflowsStore.activeWorkflowExecution) {
|
||||
const cardElement = currentExecutionCard[0].$el as HTMLElement;
|
||||
const cardRect = cardElement.getBoundingClientRect();
|
||||
const LIST_HEADER_OFFSET = 200;
|
||||
if (cardRect.top > executionsList.offsetHeight) {
|
||||
executionsList.scrollTo({ top: cardRect.top });
|
||||
executionsList.scrollTo({ top: cardRect.top - LIST_HEADER_OFFSET });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -177,7 +177,7 @@ export default mixins(
|
|||
if (this.workflowsStore.currentWorkflowExecutions.length > 0) {
|
||||
const workflowExecutions = await this.loadExecutions();
|
||||
this.workflowsStore.addToCurrentExecutions(workflowExecutions);
|
||||
this.setActiveExecution();
|
||||
await this.setActiveExecution();
|
||||
} else {
|
||||
await this.setExecutions();
|
||||
}
|
||||
|
@ -210,7 +210,7 @@ export default mixins(
|
|||
this.callDebounced('loadMore', { debounceTime: 1000 });
|
||||
}
|
||||
},
|
||||
async loadMore(): Promise<void> {
|
||||
async loadMore(limit = 20): Promise<void> {
|
||||
if (
|
||||
this.filter.status === 'running' ||
|
||||
this.loadedFinishedExecutionsCount >= this.totalFinishedExecutionsCount
|
||||
|
@ -233,7 +233,7 @@ export default mixins(
|
|||
}
|
||||
let data: IExecutionsListResponse;
|
||||
try {
|
||||
data = await this.restApi().getPastExecutions(requestFilter, 20, lastId);
|
||||
data = await this.restApi().getPastExecutions(requestFilter, limit, lastId);
|
||||
} catch (error) {
|
||||
this.loadingMore = false;
|
||||
this.$showError(error, this.$locale.baseText('executionsList.showError.loadMore.title'));
|
||||
|
@ -316,7 +316,7 @@ export default mixins(
|
|||
async setExecutions(): Promise<void> {
|
||||
const workflowExecutions = await this.loadExecutions();
|
||||
this.workflowsStore.currentWorkflowExecutions = workflowExecutions;
|
||||
this.setActiveExecution();
|
||||
await this.setActiveExecution();
|
||||
},
|
||||
async loadAutoRefresh(): Promise<void> {
|
||||
// Most of the auto-refresh logic is taken from the `ExecutionsList` component
|
||||
|
@ -404,14 +404,17 @@ export default mixins(
|
|||
return [];
|
||||
}
|
||||
},
|
||||
setActiveExecution(): void {
|
||||
async setActiveExecution(): Promise<void> {
|
||||
const activeExecutionId = this.$route.params.executionId;
|
||||
if (activeExecutionId) {
|
||||
const execution = this.workflowsStore.getExecutionDataById(activeExecutionId);
|
||||
if (execution) {
|
||||
this.workflowsStore.activeWorkflowExecution = execution;
|
||||
} else {
|
||||
await this.tryToFindExecution(activeExecutionId);
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no execution in the route, select the first one
|
||||
if (this.workflowsStore.activeWorkflowExecution === null && this.executions.length > 0) {
|
||||
this.workflowsStore.activeWorkflowExecution = this.executions[0];
|
||||
|
@ -423,6 +426,37 @@ export default mixins(
|
|||
.catch(() => {});
|
||||
}
|
||||
},
|
||||
async tryToFindExecution(executionId: string, skipCheck = false): Promise<void> {
|
||||
// First check if executions exists in the DB at all
|
||||
if (!skipCheck) {
|
||||
const executionExists = await this.workflowsStore.fetchExecutionDataById(executionId);
|
||||
if (!executionExists) {
|
||||
this.workflowsStore.activeWorkflowExecution = null;
|
||||
this.$showError(
|
||||
new Error(
|
||||
this.$locale.baseText('executionView.notFound.message', {
|
||||
interpolate: { executionId },
|
||||
}),
|
||||
),
|
||||
this.$locale.baseText('nodeView.showError.openExecution.title'),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Fetch next batch of executions
|
||||
await this.loadMore(100);
|
||||
const execution = this.workflowsStore.getExecutionDataById(executionId);
|
||||
if (!execution) {
|
||||
// If it's not there load next until found
|
||||
await this.$nextTick();
|
||||
// But skip fetching execution data since we at this point know it exists
|
||||
await this.tryToFindExecution(executionId, true);
|
||||
} else {
|
||||
// When found set execution as active
|
||||
this.workflowsStore.activeWorkflowExecution = execution;
|
||||
return;
|
||||
}
|
||||
},
|
||||
async openWorkflow(workflowId: string): Promise<void> {
|
||||
await this.loadActiveWorkflows();
|
||||
|
||||
|
|
|
@ -487,6 +487,7 @@
|
|||
"executionSidebar.searchPlaceholder": "Search executions...",
|
||||
"executionView.onPaste.title": "Cannot paste here",
|
||||
"executionView.onPaste.message": "This view is read-only. Switch to <i>Workflow</i> tab to be able to edit the current workflow",
|
||||
"executionView.notFound.message": "Execution with id '{executionId}' could not be found!",
|
||||
"expressionEdit.anythingInside": "Anything inside",
|
||||
"expressionEdit.isJavaScript": "is JavaScript.",
|
||||
"expressionEdit.learnMore": "Learn more",
|
||||
|
|
|
@ -46,6 +46,7 @@ import { useRootStore } from './n8nRootStore';
|
|||
import {
|
||||
getActiveWorkflows,
|
||||
getCurrentExecutions,
|
||||
getExecutionData,
|
||||
getFinishedExecutions,
|
||||
getNewWorkflow,
|
||||
getWorkflows,
|
||||
|
@ -941,6 +942,10 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||
throw error;
|
||||
}
|
||||
},
|
||||
async fetchExecutionDataById(executionId: string): Promise<IExecutionResponse | null> {
|
||||
const rootStore = useRootStore();
|
||||
return await getExecutionData(rootStore.getRestApiContext, executionId);
|
||||
},
|
||||
deleteExecution(execution: IExecutionsSummary): void {
|
||||
this.currentWorkflowExecutions.splice(this.currentWorkflowExecutions.indexOf(execution), 1);
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue