mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
feat(editor): Store focused panel state in local storage (no-changelog) (#17163)
This commit is contained in:
@@ -29,7 +29,6 @@ import { mock } from 'vitest-mock-extended';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { useCredentialsStore } from '@/stores/credentials.store';
|
||||
import { useExecutionsStore } from '@/stores/executions.store';
|
||||
import { useFocusPanelStore } from '@/stores/focusPanel.store';
|
||||
import { useNodeCreatorStore } from '@/stores/nodeCreator.store';
|
||||
import { useProjectsStore } from '@/stores/projects.store';
|
||||
import { waitFor } from '@testing-library/vue';
|
||||
@@ -2823,7 +2822,6 @@ describe('useCanvasOperations', () => {
|
||||
const workflowsStore = mockedStore(useWorkflowsStore);
|
||||
const uiStore = mockedStore(useUIStore);
|
||||
const executionsStore = mockedStore(useExecutionsStore);
|
||||
const focusPanelStore = mockedStore(useFocusPanelStore);
|
||||
|
||||
const nodeHelpers = { credentialsUpdated: { value: true } };
|
||||
|
||||
@@ -2834,7 +2832,6 @@ describe('useCanvasOperations', () => {
|
||||
workflowsStore.resetState = vi.fn();
|
||||
workflowsStore.setActiveExecutionId = vi.fn();
|
||||
uiStore.resetLastInteractedWith = vi.fn();
|
||||
focusPanelStore.reset = vi.fn();
|
||||
executionsStore.activeExecution = null;
|
||||
|
||||
workflowsStore.executionWaitingForWebhook = true;
|
||||
@@ -2872,7 +2869,6 @@ describe('useCanvasOperations', () => {
|
||||
expect(workflowsStore.resetState).toHaveBeenCalled();
|
||||
expect(workflowsStore.currentWorkflowExecutions).toEqual([]);
|
||||
expect(workflowsStore.setActiveExecutionId).toHaveBeenCalledWith(undefined);
|
||||
expect(focusPanelStore.reset).toHaveBeenCalled();
|
||||
expect(uiStore.resetLastInteractedWith).toHaveBeenCalled();
|
||||
expect(uiStore.stateIsDirty).toBe(false);
|
||||
expect(executionsStore.activeExecution).toBeNull();
|
||||
|
||||
@@ -53,7 +53,6 @@ import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { useTagsStore } from '@/stores/tags.store';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { useFocusPanelStore } from '@/stores/focusPanel.store';
|
||||
import type {
|
||||
CanvasConnection,
|
||||
CanvasConnectionCreateData,
|
||||
@@ -153,7 +152,6 @@ export function useCanvasOperations() {
|
||||
const settingsStore = useSettingsStore();
|
||||
const tagsStore = useTagsStore();
|
||||
const nodeCreatorStore = useNodeCreatorStore();
|
||||
const focusPanelStore = useFocusPanelStore();
|
||||
const executionsStore = useExecutionsStore();
|
||||
const projectsStore = useProjectsStore();
|
||||
const logsStore = useLogsStore();
|
||||
@@ -1610,8 +1608,6 @@ export function useCanvasOperations() {
|
||||
workflowsStore.currentWorkflowExecutions = [];
|
||||
workflowsStore.setActiveExecutionId(undefined);
|
||||
|
||||
focusPanelStore.reset();
|
||||
|
||||
// Reset actions
|
||||
uiStore.resetLastInteractedWith();
|
||||
uiStore.stateIsDirty = false;
|
||||
|
||||
@@ -71,11 +71,11 @@ describe('useNodeSettingsParameters', () => {
|
||||
ndvStore.activeNodeName = 'Node1';
|
||||
ndvStore.setActiveNodeName = vi.fn();
|
||||
ndvStore.resetNDVPushRef = vi.fn();
|
||||
focusPanelStore.setFocusedNodeParameter = vi.fn();
|
||||
focusPanelStore.openWithFocusedNodeParameter = vi.fn();
|
||||
focusPanelStore.focusPanelActive = false;
|
||||
});
|
||||
|
||||
it('sets focused node parameter and activates panel', () => {
|
||||
it('sets focused node parameter', () => {
|
||||
const { handleFocus } = useNodeSettingsParameters();
|
||||
const node: INodeUi = {
|
||||
id: '1',
|
||||
@@ -95,12 +95,11 @@ describe('useNodeSettingsParameters', () => {
|
||||
|
||||
handleFocus(node, path, parameter);
|
||||
|
||||
expect(focusPanelStore.setFocusedNodeParameter).toHaveBeenCalledWith({
|
||||
expect(focusPanelStore.openWithFocusedNodeParameter).toHaveBeenCalledWith({
|
||||
nodeId: node.id,
|
||||
parameterPath: path,
|
||||
parameter,
|
||||
});
|
||||
expect(focusPanelStore.focusPanelActive).toBe(true);
|
||||
|
||||
expect(ndvStore.setActiveNodeName).toHaveBeenCalledWith(null);
|
||||
expect(ndvStore.resetNDVPushRef).toHaveBeenCalled();
|
||||
@@ -118,7 +117,7 @@ describe('useNodeSettingsParameters', () => {
|
||||
|
||||
handleFocus(undefined, 'parameters.foo', parameter);
|
||||
|
||||
expect(focusPanelStore.setFocusedNodeParameter).not.toHaveBeenCalled();
|
||||
expect(focusPanelStore.openWithFocusedNodeParameter).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -233,7 +233,7 @@ export function useNodeSettingsParameters() {
|
||||
const ndvStore = useNDVStore();
|
||||
const focusPanelStore = useFocusPanelStore();
|
||||
|
||||
focusPanelStore.setFocusedNodeParameter({
|
||||
focusPanelStore.openWithFocusedNodeParameter({
|
||||
nodeId: node.id,
|
||||
parameterPath: path,
|
||||
parameter,
|
||||
@@ -243,8 +243,6 @@ export function useNodeSettingsParameters() {
|
||||
ndvStore.setActiveNodeName(null);
|
||||
ndvStore.resetNDVPushRef();
|
||||
}
|
||||
|
||||
focusPanelStore.focusPanelActive = true;
|
||||
}
|
||||
|
||||
function shouldDisplayNodeParameter(
|
||||
|
||||
@@ -26,6 +26,7 @@ import { useTelemetry } from './useTelemetry';
|
||||
import { useNodeHelpers } from './useNodeHelpers';
|
||||
import { tryToParseNumber } from '@/utils/typesUtils';
|
||||
import { useTemplatesStore } from '@/stores/templates.store';
|
||||
import { useFocusPanelStore } from '@/stores/focusPanel.store';
|
||||
|
||||
export function useWorkflowSaving({ router }: { router: ReturnType<typeof useRouter> }) {
|
||||
const uiStore = useUIStore();
|
||||
@@ -33,6 +34,7 @@ export function useWorkflowSaving({ router }: { router: ReturnType<typeof useRou
|
||||
const message = useMessage();
|
||||
const i18n = useI18n();
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
const focusPanelStore = useFocusPanelStore();
|
||||
const nodeTypesStore = useNodeTypesStore();
|
||||
const toast = useToast();
|
||||
const telemetry = useTelemetry();
|
||||
@@ -346,6 +348,8 @@ export function useWorkflowSaving({ router }: { router: ReturnType<typeof useRou
|
||||
|
||||
workflowsStore.addWorkflow(workflowData);
|
||||
|
||||
focusPanelStore.onNewWorkflowSave(workflowData.id);
|
||||
|
||||
if (openInNewWindow) {
|
||||
const routeData = router.resolve({
|
||||
name: VIEWS.WORKFLOW,
|
||||
|
||||
@@ -494,6 +494,7 @@ export const LOCAL_STORAGE_EXPERIMENTAL_DOCKED_NODE_SETTINGS =
|
||||
export const LOCAL_STORAGE_READ_WHATS_NEW_ARTICLES = 'N8N_READ_WHATS_NEW_ARTICLES';
|
||||
export const LOCAL_STORAGE_DISMISSED_WHATS_NEW_CALLOUT = 'N8N_DISMISSED_WHATS_NEW_CALLOUT';
|
||||
export const LOCAL_STORAGE_NDV_PANEL_WIDTH = 'N8N_NDV_PANEL_WIDTH';
|
||||
export const LOCAL_STORAGE_FOCUS_PANEL = 'N8N_FOCUS_PANEL';
|
||||
|
||||
export const BASE_NODE_SURVEY_URL = 'https://n8n-community.typeform.com/to/BvmzxqYv#nodename=';
|
||||
export const COMMUNITY_PLUS_DOCS_URL =
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import { STORES } from '@n8n/stores';
|
||||
import { defineStore } from 'pinia';
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import get from 'lodash/get';
|
||||
|
||||
import { type NodeParameterValueType, type INode, type INodeProperties } from 'n8n-workflow';
|
||||
import {
|
||||
type NodeParameterValueType,
|
||||
type INode,
|
||||
type INodeProperties,
|
||||
jsonParse,
|
||||
} from 'n8n-workflow';
|
||||
import { useWorkflowsStore } from './workflows.store';
|
||||
import { LOCAL_STORAGE_FOCUS_PANEL, PLACEHOLDER_EMPTY_WORKFLOW_ID } from '@/constants';
|
||||
import { useStorage } from '@/composables/useStorage';
|
||||
|
||||
type FocusedNodeParameter = {
|
||||
nodeId: string;
|
||||
@@ -17,11 +24,36 @@ export type RichFocusedNodeParameter = FocusedNodeParameter & {
|
||||
value: NodeParameterValueType;
|
||||
};
|
||||
|
||||
type FocusPanelData = {
|
||||
isActive: boolean;
|
||||
parameters: FocusedNodeParameter[];
|
||||
};
|
||||
|
||||
type FocusPanelDataByWid = Record<string, FocusPanelData>;
|
||||
|
||||
const DEFAULT_FOCUS_PANEL_DATA: FocusPanelData = { isActive: false, parameters: [] };
|
||||
|
||||
export const useFocusPanelStore = defineStore(STORES.FOCUS_PANEL, () => {
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
const focusPanelStorage = useStorage(LOCAL_STORAGE_FOCUS_PANEL);
|
||||
|
||||
const focusPanelActive = ref(false);
|
||||
const _focusedNodeParameters = ref<FocusedNodeParameter[]>([]);
|
||||
const focusPanelData = computed((): FocusPanelDataByWid => {
|
||||
const defaultValue: FocusPanelDataByWid = {
|
||||
[workflowsStore.workflowId]: DEFAULT_FOCUS_PANEL_DATA,
|
||||
};
|
||||
|
||||
return focusPanelStorage.value
|
||||
? jsonParse(focusPanelStorage.value, { fallbackValue: defaultValue })
|
||||
: defaultValue;
|
||||
});
|
||||
|
||||
const currentFocusPanelData = computed(
|
||||
(): FocusPanelData =>
|
||||
focusPanelData.value[workflowsStore.workflowId] ?? DEFAULT_FOCUS_PANEL_DATA,
|
||||
);
|
||||
|
||||
const focusPanelActive = computed(() => currentFocusPanelData.value.isActive);
|
||||
const _focusedNodeParameters = computed(() => currentFocusPanelData.value.parameters);
|
||||
|
||||
// An unenriched parameter indicates a missing nodeId
|
||||
const focusedNodeParameters = computed<Array<RichFocusedNodeParameter | FocusedNodeParameter>>(
|
||||
@@ -38,40 +70,76 @@ export const useFocusPanelStore = defineStore(STORES.FOCUS_PANEL, () => {
|
||||
}),
|
||||
);
|
||||
|
||||
const setFocusedNodeParameter = (nodeParameter: FocusedNodeParameter) => {
|
||||
_focusedNodeParameters.value = [
|
||||
nodeParameter,
|
||||
// Uncomment when tabs are implemented
|
||||
const _setOptions = ({
|
||||
parameters,
|
||||
isActive,
|
||||
wid = workflowsStore.workflowId,
|
||||
removeEmpty = false,
|
||||
}: {
|
||||
isActive?: boolean;
|
||||
parameters?: FocusedNodeParameter[];
|
||||
wid?: string;
|
||||
removeEmpty?: boolean;
|
||||
}) => {
|
||||
const focusPanelDataCurrent = focusPanelData.value;
|
||||
|
||||
if (removeEmpty && PLACEHOLDER_EMPTY_WORKFLOW_ID in focusPanelDataCurrent) {
|
||||
delete focusPanelDataCurrent[PLACEHOLDER_EMPTY_WORKFLOW_ID];
|
||||
}
|
||||
|
||||
focusPanelStorage.value = JSON.stringify({
|
||||
...focusPanelData.value,
|
||||
[wid]: {
|
||||
isActive: isActive ?? focusPanelActive.value,
|
||||
parameters: parameters ?? _focusedNodeParameters.value,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// When a new workflow is saved, we should update the focus panel data with the new workflow ID
|
||||
const onNewWorkflowSave = (wid: string) => {
|
||||
if (!currentFocusPanelData.value || !(PLACEHOLDER_EMPTY_WORKFLOW_ID in focusPanelData.value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const latestWorkflowData = focusPanelData.value[PLACEHOLDER_EMPTY_WORKFLOW_ID];
|
||||
_setOptions({
|
||||
wid,
|
||||
parameters: latestWorkflowData.parameters,
|
||||
isActive: latestWorkflowData.isActive,
|
||||
removeEmpty: true,
|
||||
});
|
||||
};
|
||||
|
||||
const openWithFocusedNodeParameter = (nodeParameter: FocusedNodeParameter) => {
|
||||
const parameters = [nodeParameter];
|
||||
// TODO: uncomment when tabs are implemented
|
||||
// ...focusedNodeParameters.value.filter((p) => p.parameterPath !== nodeParameter.parameterPath),
|
||||
];
|
||||
|
||||
_setOptions({ parameters, isActive: true });
|
||||
};
|
||||
|
||||
const closeFocusPanel = () => {
|
||||
focusPanelActive.value = false;
|
||||
_setOptions({ isActive: false });
|
||||
};
|
||||
|
||||
const toggleFocusPanel = () => {
|
||||
focusPanelActive.value = !focusPanelActive.value;
|
||||
_setOptions({ isActive: !focusPanelActive.value });
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
focusPanelActive.value = false;
|
||||
_focusedNodeParameters.value = [];
|
||||
};
|
||||
|
||||
function isRichParameter(
|
||||
const isRichParameter = (
|
||||
p: RichFocusedNodeParameter | FocusedNodeParameter,
|
||||
): p is RichFocusedNodeParameter {
|
||||
): p is RichFocusedNodeParameter => {
|
||||
return 'value' in p && 'node' in p;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
focusPanelActive,
|
||||
focusedNodeParameters,
|
||||
setFocusedNodeParameter,
|
||||
openWithFocusedNodeParameter,
|
||||
isRichParameter,
|
||||
closeFocusPanel,
|
||||
toggleFocusPanel,
|
||||
reset,
|
||||
onNewWorkflowSave,
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user