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) => {
|
||||
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">
|
||||
{{ item.label }}
|
||||
</span>
|
||||
<span v-if="item.badge">
|
||||
<N8nBadge theme="primary" size="xsmall">{{ item.badge }}</N8nBadge>
|
||||
</span>
|
||||
<N8nKeyboardShortcut
|
||||
v-if="item.shortcut"
|
||||
v-bind="item.shortcut"
|
||||
|
|
|
@ -3,6 +3,7 @@ import type { KeyboardShortcut } from 'n8n-design-system/types/keyboardshortcut'
|
|||
export interface ActionDropdownItem {
|
||||
id: string;
|
||||
label: string;
|
||||
badge?: string;
|
||||
icon?: string;
|
||||
divided?: boolean;
|
||||
disabled?: boolean;
|
||||
|
|
|
@ -55,7 +55,7 @@ import { useI18n } from '@/composables/useI18n';
|
|||
import { useTelemetry } from '@/composables/useTelemetry';
|
||||
import type { BaseTextKey } from '@/plugins/i18n';
|
||||
import { useNpsSurveyStore } from '@/stores/npsSurvey.store';
|
||||
import { useLocalStorage } from '@vueuse/core';
|
||||
import { useNodeViewVersionSwitcher } from '@/composables/useNodeViewVersionSwitcher';
|
||||
|
||||
const props = defineProps<{
|
||||
readOnly?: boolean;
|
||||
|
@ -99,16 +99,14 @@ const importFileRef = ref<HTMLInputElement | undefined>();
|
|||
const tagsEventBus = createEventBus();
|
||||
const sourceControlModalEventBus = createEventBus();
|
||||
|
||||
const nodeViewSwitcher = useLocalStorage('NodeView.switcher', '');
|
||||
const nodeViewVersion = useLocalStorage('NodeView.version', '1');
|
||||
|
||||
const isNodeViewSwitcherEnabled = computed(() => {
|
||||
return (
|
||||
import.meta.env.DEV ||
|
||||
nodeViewSwitcher.value === 'true' ||
|
||||
settingsStore.deploymentType === 'n8n-internal'
|
||||
);
|
||||
});
|
||||
const {
|
||||
nodeViewVersion,
|
||||
nodeViewSwitcherDiscovered,
|
||||
isNodeViewDiscoveryTooltipVisible,
|
||||
switchNodeViewVersion,
|
||||
setNodeViewSwitcherDropdownOpened,
|
||||
setNodeViewSwitcherDiscovered,
|
||||
} = useNodeViewVersionSwitcher();
|
||||
|
||||
const hasChanged = (prev: string[], curr: string[]) => {
|
||||
if (prev.length !== curr.length) {
|
||||
|
@ -189,16 +187,17 @@ const workflowMenuItems = computed<ActionDropdownItem[]>(() => {
|
|||
disabled: !onWorkflowPage.value || isNewWorkflow.value,
|
||||
});
|
||||
|
||||
if (isNodeViewSwitcherEnabled.value) {
|
||||
actions.push({
|
||||
id: WORKFLOW_MENU_ACTIONS.SWITCH_NODE_VIEW_VERSION,
|
||||
label:
|
||||
nodeViewVersion.value === '2'
|
||||
? locale.baseText('menuActions.switchToOldNodeViewVersion')
|
||||
: locale.baseText('menuActions.switchToNewNodeViewVersion'),
|
||||
disabled: !onWorkflowPage.value,
|
||||
});
|
||||
}
|
||||
actions.push({
|
||||
id: WORKFLOW_MENU_ACTIONS.SWITCH_NODE_VIEW_VERSION,
|
||||
...(nodeViewSwitcherDiscovered.value
|
||||
? {}
|
||||
: { badge: locale.baseText('menuActions.badge.new') }),
|
||||
label:
|
||||
nodeViewVersion.value === '2'
|
||||
? locale.baseText('menuActions.switchToOldNodeViewVersion')
|
||||
: locale.baseText('menuActions.switchToNewNodeViewVersion'),
|
||||
disabled: !onWorkflowPage.value,
|
||||
});
|
||||
|
||||
if ((workflowPermissions.value.delete && !props.readOnly) || isNewWorkflow.value) {
|
||||
actions.push({
|
||||
|
@ -399,6 +398,10 @@ async function handleFileImport(): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
function onWorkflowMenuOpen() {
|
||||
setNodeViewSwitcherDropdownOpened();
|
||||
}
|
||||
|
||||
async function onWorkflowMenuSelect(action: WORKFLOW_MENU_ACTIONS): Promise<void> {
|
||||
switch (action) {
|
||||
case WORKFLOW_MENU_ACTIONS.DUPLICATE: {
|
||||
|
@ -499,6 +502,8 @@ async function onWorkflowMenuSelect(action: WORKFLOW_MENU_ACTIONS): Promise<void
|
|||
break;
|
||||
}
|
||||
case WORKFLOW_MENU_ACTIONS.SWITCH_NODE_VIEW_VERSION: {
|
||||
setNodeViewSwitcherDiscovered();
|
||||
|
||||
if (uiStore.stateIsDirty) {
|
||||
const confirmModal = await message.confirm(
|
||||
locale.baseText('generic.unsavedWork.confirmMessage.message'),
|
||||
|
@ -522,11 +527,7 @@ async function onWorkflowMenuSelect(action: WORKFLOW_MENU_ACTIONS): Promise<void
|
|||
}
|
||||
}
|
||||
|
||||
if (nodeViewVersion.value === '1') {
|
||||
nodeViewVersion.value = '2';
|
||||
} else {
|
||||
nodeViewVersion.value = '1';
|
||||
}
|
||||
switchNodeViewVersion();
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -734,11 +735,17 @@ function showCreateWorkflowSuccessToast(id?: string) {
|
|||
data-test-id="workflow-import-input"
|
||||
@change="handleFileImport()"
|
||||
/>
|
||||
<N8nActionDropdown
|
||||
:items="workflowMenuItems"
|
||||
data-test-id="workflow-menu"
|
||||
@select="onWorkflowMenuSelect"
|
||||
/>
|
||||
<N8nTooltip :visible="isNodeViewDiscoveryTooltipVisible">
|
||||
<N8nActionDropdown
|
||||
:items="workflowMenuItems"
|
||||
data-test-id="workflow-menu"
|
||||
@select="onWorkflowMenuSelect"
|
||||
@visible-change="onWorkflowMenuOpen"
|
||||
/>
|
||||
<template #content>
|
||||
{{ $locale.baseText('menuActions.nodeViewDiscovery.tooltip') }}
|
||||
</template>
|
||||
</N8nTooltip>
|
||||
</div>
|
||||
</PushConnectionTracker>
|
||||
</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.switchToNewNodeViewVersion": "Switch to new 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.currentlyNoItemsExist": "Currently no items exist",
|
||||
"multipleParameter.deleteItem": "Delete item",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<script lang="ts" setup>
|
||||
import { useLocalStorage } from '@vueuse/core';
|
||||
import { computed, watch } from 'vue';
|
||||
import { onBeforeRouteLeave, useRoute, useRouter } from 'vue-router';
|
||||
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 { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
|
||||
import { useSourceControlStore } from '@/stores/sourceControl.store';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { useNodeViewVersionSwitcher } from '@/composables/useNodeViewVersionSwitcher';
|
||||
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
const sourceControlStore = useSourceControlStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const workflowHelpers = useWorkflowHelpers({ router });
|
||||
|
||||
const nodeViewVersion = useLocalStorage(
|
||||
'NodeView.version',
|
||||
settingsStore.deploymentType === 'n8n-internal' ? '2' : '1',
|
||||
);
|
||||
const { nodeViewVersion } = useNodeViewVersionSwitcher();
|
||||
|
||||
const workflowId = computed<string>(() => route.params.name as string);
|
||||
|
||||
|
|
Loading…
Reference in a new issue