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:
Milorad FIlipović 2022-12-27 14:51:48 +01:00 committed by GitHub
parent d113977b10
commit d0865e28ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 79 additions and 10 deletions

View file

@ -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}`);
}

View file

@ -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 });
}
}
},

View file

@ -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();

View file

@ -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",

View file

@ -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);
},