mirror of
https://github.com/n8n-io/n8n.git
synced 2025-02-21 02:56:40 -08:00
feat(editor): Create call to action tooltip for trying the new canvas (no-changelog) (#11230)
This commit is contained in:
parent
3c93ec88cd
commit
ba2827e7bb
|
@ -70,6 +70,10 @@ Cypress.Commands.add('signin', ({ email, password }) => {
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
Cypress.env('currentUserId', response.body.data.id);
|
Cypress.env('currentUserId', response.body.data.id);
|
||||||
|
|
||||||
|
cy.window().then((win) => {
|
||||||
|
win.localStorage.setItem('NodeView.switcher.discovered', 'true'); // @TODO Remove this once the switcher is removed
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -117,6 +117,9 @@ defineExpose({ open, close });
|
||||||
<span :class="$style.label">
|
<span :class="$style.label">
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</span>
|
</span>
|
||||||
|
<span v-if="item.badge">
|
||||||
|
<N8nBadge theme="primary" size="xsmall">{{ item.badge }}</N8nBadge>
|
||||||
|
</span>
|
||||||
<N8nKeyboardShortcut
|
<N8nKeyboardShortcut
|
||||||
v-if="item.shortcut"
|
v-if="item.shortcut"
|
||||||
v-bind="item.shortcut"
|
v-bind="item.shortcut"
|
||||||
|
|
|
@ -3,6 +3,7 @@ import type { KeyboardShortcut } from 'n8n-design-system/types/keyboardshortcut'
|
||||||
export interface ActionDropdownItem {
|
export interface ActionDropdownItem {
|
||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
|
badge?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
divided?: boolean;
|
divided?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
|
|
@ -55,7 +55,7 @@ import { useI18n } from '@/composables/useI18n';
|
||||||
import { useTelemetry } from '@/composables/useTelemetry';
|
import { useTelemetry } from '@/composables/useTelemetry';
|
||||||
import type { BaseTextKey } from '@/plugins/i18n';
|
import type { BaseTextKey } from '@/plugins/i18n';
|
||||||
import { useNpsSurveyStore } from '@/stores/npsSurvey.store';
|
import { useNpsSurveyStore } from '@/stores/npsSurvey.store';
|
||||||
import { useLocalStorage } from '@vueuse/core';
|
import { useNodeViewVersionSwitcher } from '@/composables/useNodeViewVersionSwitcher';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
readOnly?: boolean;
|
readOnly?: boolean;
|
||||||
|
@ -99,16 +99,14 @@ const importFileRef = ref<HTMLInputElement | undefined>();
|
||||||
const tagsEventBus = createEventBus();
|
const tagsEventBus = createEventBus();
|
||||||
const sourceControlModalEventBus = createEventBus();
|
const sourceControlModalEventBus = createEventBus();
|
||||||
|
|
||||||
const nodeViewSwitcher = useLocalStorage('NodeView.switcher', '');
|
const {
|
||||||
const nodeViewVersion = useLocalStorage('NodeView.version', '1');
|
nodeViewVersion,
|
||||||
|
nodeViewSwitcherDiscovered,
|
||||||
const isNodeViewSwitcherEnabled = computed(() => {
|
isNodeViewDiscoveryTooltipVisible,
|
||||||
return (
|
switchNodeViewVersion,
|
||||||
import.meta.env.DEV ||
|
setNodeViewSwitcherDropdownOpened,
|
||||||
nodeViewSwitcher.value === 'true' ||
|
setNodeViewSwitcherDiscovered,
|
||||||
settingsStore.deploymentType === 'n8n-internal'
|
} = useNodeViewVersionSwitcher();
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const hasChanged = (prev: string[], curr: string[]) => {
|
const hasChanged = (prev: string[], curr: string[]) => {
|
||||||
if (prev.length !== curr.length) {
|
if (prev.length !== curr.length) {
|
||||||
|
@ -189,16 +187,17 @@ const workflowMenuItems = computed<ActionDropdownItem[]>(() => {
|
||||||
disabled: !onWorkflowPage.value || isNewWorkflow.value,
|
disabled: !onWorkflowPage.value || isNewWorkflow.value,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isNodeViewSwitcherEnabled.value) {
|
actions.push({
|
||||||
actions.push({
|
id: WORKFLOW_MENU_ACTIONS.SWITCH_NODE_VIEW_VERSION,
|
||||||
id: WORKFLOW_MENU_ACTIONS.SWITCH_NODE_VIEW_VERSION,
|
...(nodeViewSwitcherDiscovered.value
|
||||||
label:
|
? {}
|
||||||
nodeViewVersion.value === '2'
|
: { badge: locale.baseText('menuActions.badge.new') }),
|
||||||
? locale.baseText('menuActions.switchToOldNodeViewVersion')
|
label:
|
||||||
: locale.baseText('menuActions.switchToNewNodeViewVersion'),
|
nodeViewVersion.value === '2'
|
||||||
disabled: !onWorkflowPage.value,
|
? locale.baseText('menuActions.switchToOldNodeViewVersion')
|
||||||
});
|
: locale.baseText('menuActions.switchToNewNodeViewVersion'),
|
||||||
}
|
disabled: !onWorkflowPage.value,
|
||||||
|
});
|
||||||
|
|
||||||
if ((workflowPermissions.value.delete && !props.readOnly) || isNewWorkflow.value) {
|
if ((workflowPermissions.value.delete && !props.readOnly) || isNewWorkflow.value) {
|
||||||
actions.push({
|
actions.push({
|
||||||
|
@ -399,6 +398,10 @@ async function handleFileImport(): Promise<void> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onWorkflowMenuOpen() {
|
||||||
|
setNodeViewSwitcherDropdownOpened();
|
||||||
|
}
|
||||||
|
|
||||||
async function onWorkflowMenuSelect(action: WORKFLOW_MENU_ACTIONS): Promise<void> {
|
async function onWorkflowMenuSelect(action: WORKFLOW_MENU_ACTIONS): Promise<void> {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case WORKFLOW_MENU_ACTIONS.DUPLICATE: {
|
case WORKFLOW_MENU_ACTIONS.DUPLICATE: {
|
||||||
|
@ -499,6 +502,8 @@ async function onWorkflowMenuSelect(action: WORKFLOW_MENU_ACTIONS): Promise<void
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WORKFLOW_MENU_ACTIONS.SWITCH_NODE_VIEW_VERSION: {
|
case WORKFLOW_MENU_ACTIONS.SWITCH_NODE_VIEW_VERSION: {
|
||||||
|
setNodeViewSwitcherDiscovered();
|
||||||
|
|
||||||
if (uiStore.stateIsDirty) {
|
if (uiStore.stateIsDirty) {
|
||||||
const confirmModal = await message.confirm(
|
const confirmModal = await message.confirm(
|
||||||
locale.baseText('generic.unsavedWork.confirmMessage.message'),
|
locale.baseText('generic.unsavedWork.confirmMessage.message'),
|
||||||
|
@ -522,11 +527,7 @@ async function onWorkflowMenuSelect(action: WORKFLOW_MENU_ACTIONS): Promise<void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodeViewVersion.value === '1') {
|
switchNodeViewVersion();
|
||||||
nodeViewVersion.value = '2';
|
|
||||||
} else {
|
|
||||||
nodeViewVersion.value = '1';
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -734,11 +735,17 @@ function showCreateWorkflowSuccessToast(id?: string) {
|
||||||
data-test-id="workflow-import-input"
|
data-test-id="workflow-import-input"
|
||||||
@change="handleFileImport()"
|
@change="handleFileImport()"
|
||||||
/>
|
/>
|
||||||
<N8nActionDropdown
|
<N8nTooltip :visible="isNodeViewDiscoveryTooltipVisible">
|
||||||
:items="workflowMenuItems"
|
<N8nActionDropdown
|
||||||
data-test-id="workflow-menu"
|
:items="workflowMenuItems"
|
||||||
@select="onWorkflowMenuSelect"
|
data-test-id="workflow-menu"
|
||||||
/>
|
@select="onWorkflowMenuSelect"
|
||||||
|
@visible-change="onWorkflowMenuOpen"
|
||||||
|
/>
|
||||||
|
<template #content>
|
||||||
|
{{ $locale.baseText('menuActions.nodeViewDiscovery.tooltip') }}
|
||||||
|
</template>
|
||||||
|
</N8nTooltip>
|
||||||
</div>
|
</div>
|
||||||
</PushConnectionTracker>
|
</PushConnectionTracker>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { useLocalStorage } from '@vueuse/core';
|
||||||
|
import { useSettingsStore } from '@/stores/settings.store';
|
||||||
|
import { useTelemetry } from '@/composables/useTelemetry';
|
||||||
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
|
import { debouncedRef } from '@vueuse/core';
|
||||||
|
|
||||||
|
export function useNodeViewVersionSwitcher() {
|
||||||
|
const workflowsStore = useWorkflowsStore();
|
||||||
|
const settingsStore = useSettingsStore();
|
||||||
|
const telemetry = useTelemetry();
|
||||||
|
|
||||||
|
const isNewUser = computed(() => workflowsStore.activeWorkflows.length === 0);
|
||||||
|
|
||||||
|
const nodeViewVersion = useLocalStorage(
|
||||||
|
'NodeView.version',
|
||||||
|
settingsStore.deploymentType === 'n8n-internal' ? '2' : '1',
|
||||||
|
);
|
||||||
|
|
||||||
|
const nodeViewSwitcherDropdownOpened = ref(false);
|
||||||
|
function setNodeViewSwitcherDropdownOpened() {
|
||||||
|
nodeViewSwitcherDropdownOpened.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodeViewSwitcherDiscovered = useLocalStorage('NodeView.switcher.discovered', false);
|
||||||
|
function setNodeViewSwitcherDiscovered() {
|
||||||
|
nodeViewSwitcherDiscovered.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isNodeViewDiscoveryTooltipVisibleRaw = computed(
|
||||||
|
() =>
|
||||||
|
nodeViewVersion.value !== '2' &&
|
||||||
|
!(
|
||||||
|
isNewUser.value ||
|
||||||
|
nodeViewSwitcherDropdownOpened.value ||
|
||||||
|
nodeViewSwitcherDiscovered.value
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const isNodeViewDiscoveryTooltipVisible = debouncedRef(
|
||||||
|
isNodeViewDiscoveryTooltipVisibleRaw,
|
||||||
|
3000,
|
||||||
|
);
|
||||||
|
|
||||||
|
function switchNodeViewVersion() {
|
||||||
|
const toVersion = nodeViewVersion.value === '1' ? '2' : '1';
|
||||||
|
|
||||||
|
telemetry.track('User switched canvas version', {
|
||||||
|
to_version: toVersion,
|
||||||
|
});
|
||||||
|
|
||||||
|
nodeViewVersion.value = toVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
nodeViewVersion,
|
||||||
|
nodeViewSwitcherDiscovered,
|
||||||
|
isNodeViewDiscoveryTooltipVisible,
|
||||||
|
setNodeViewSwitcherDropdownOpened,
|
||||||
|
setNodeViewSwitcherDiscovered,
|
||||||
|
switchNodeViewVersion,
|
||||||
|
};
|
||||||
|
}
|
|
@ -906,6 +906,8 @@
|
||||||
"menuActions.delete": "Delete",
|
"menuActions.delete": "Delete",
|
||||||
"menuActions.switchToNewNodeViewVersion": "Switch to new canvas",
|
"menuActions.switchToNewNodeViewVersion": "Switch to new canvas",
|
||||||
"menuActions.switchToOldNodeViewVersion": "Switch to old canvas",
|
"menuActions.switchToOldNodeViewVersion": "Switch to old canvas",
|
||||||
|
"menuActions.badge.new": "NEW",
|
||||||
|
"menuActions.nodeViewDiscovery.tooltip": "Try our new, more performant canvas",
|
||||||
"multipleParameter.addItem": "Add item",
|
"multipleParameter.addItem": "Add item",
|
||||||
"multipleParameter.currentlyNoItemsExist": "Currently no items exist",
|
"multipleParameter.currentlyNoItemsExist": "Currently no items exist",
|
||||||
"multipleParameter.deleteItem": "Delete item",
|
"multipleParameter.deleteItem": "Delete item",
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useLocalStorage } from '@vueuse/core';
|
|
||||||
import { computed, watch } from 'vue';
|
import { computed, watch } from 'vue';
|
||||||
import { onBeforeRouteLeave, useRoute, useRouter } from 'vue-router';
|
import { onBeforeRouteLeave, useRoute, useRouter } from 'vue-router';
|
||||||
import NodeViewV1 from '@/views/NodeView.vue';
|
import NodeViewV1 from '@/views/NodeView.vue';
|
||||||
|
@ -9,20 +8,16 @@ import { MAIN_HEADER_TABS, PLACEHOLDER_EMPTY_WORKFLOW_ID, VIEWS } from '@/consta
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
|
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
|
||||||
import { useSourceControlStore } from '@/stores/sourceControl.store';
|
import { useSourceControlStore } from '@/stores/sourceControl.store';
|
||||||
import { useSettingsStore } from '@/stores/settings.store';
|
import { useNodeViewVersionSwitcher } from '@/composables/useNodeViewVersionSwitcher';
|
||||||
|
|
||||||
const workflowsStore = useWorkflowsStore();
|
const workflowsStore = useWorkflowsStore();
|
||||||
const sourceControlStore = useSourceControlStore();
|
const sourceControlStore = useSourceControlStore();
|
||||||
const settingsStore = useSettingsStore();
|
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const workflowHelpers = useWorkflowHelpers({ router });
|
const workflowHelpers = useWorkflowHelpers({ router });
|
||||||
|
|
||||||
const nodeViewVersion = useLocalStorage(
|
const { nodeViewVersion } = useNodeViewVersionSwitcher();
|
||||||
'NodeView.version',
|
|
||||||
settingsStore.deploymentType === 'n8n-internal' ? '2' : '1',
|
|
||||||
);
|
|
||||||
|
|
||||||
const workflowId = computed<string>(() => route.params.name as string);
|
const workflowId = computed<string>(() => route.params.name as string);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue