mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-09 22:24:05 -08:00
fix(editor): Fix local storage flags defaulting to undefined string (#7603)
useStorage takes the default value `undefined` and sets it in local storage.. also returns "undefined" as string which breaks onboarding flows Github issue / Community forum post (link here to close automatically):
This commit is contained in:
parent
78b84af8d1
commit
151e60f829
|
@ -50,6 +50,7 @@ import { getActivatableTriggerNodes, getTriggerNodeServiceName } from '@/utils';
|
|||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { useStorage } from '@/composables/useStorage';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ActivationModal',
|
||||
|
@ -88,7 +89,7 @@ export default defineComponent({
|
|||
},
|
||||
handleCheckboxChange(checkboxValue: boolean) {
|
||||
this.checked = checkboxValue;
|
||||
window.localStorage.setItem(LOCAL_STORAGE_ACTIVATION_FLAG, checkboxValue.toString());
|
||||
useStorage(LOCAL_STORAGE_ACTIVATION_FLAG).value = checkboxValue.toString();
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
|
|
|
@ -41,7 +41,7 @@ import { defineComponent } from 'vue';
|
|||
import type { PropType } from 'vue';
|
||||
import { mapStores } from 'pinia';
|
||||
import { get } from 'lodash-es';
|
||||
import { useStorage } from '@vueuse/core';
|
||||
import { useStorage } from '@/composables/useStorage';
|
||||
|
||||
import type { INodeTypeDescription } from 'n8n-workflow';
|
||||
import PanelDragButton from './PanelDragButton.vue';
|
||||
|
@ -348,7 +348,6 @@ export default defineComponent({
|
|||
restorePositionData() {
|
||||
const storedPanelWidthData = useStorage(
|
||||
`${LOCAL_STORAGE_MAIN_PANEL_RELATIVE_WIDTH}_${this.currentNodePaneType}`,
|
||||
undefined,
|
||||
).value;
|
||||
|
||||
if (storedPanelWidthData) {
|
||||
|
@ -362,10 +361,8 @@ export default defineComponent({
|
|||
return false;
|
||||
},
|
||||
storePositionData() {
|
||||
window.localStorage.setItem(
|
||||
`${LOCAL_STORAGE_MAIN_PANEL_RELATIVE_WIDTH}_${this.currentNodePaneType}`,
|
||||
this.mainPanelDimensions.relativeWidth.toString(),
|
||||
);
|
||||
useStorage(`${LOCAL_STORAGE_MAIN_PANEL_RELATIVE_WIDTH}_${this.currentNodePaneType}`).value =
|
||||
this.mainPanelDimensions.relativeWidth.toString();
|
||||
},
|
||||
onDragStart() {
|
||||
this.isDragging = true;
|
||||
|
|
|
@ -170,7 +170,7 @@
|
|||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { mapStores } from 'pinia';
|
||||
import { useStorage } from '@vueuse/core';
|
||||
import { useStorage } from '@/composables/useStorage';
|
||||
import {
|
||||
CUSTOM_API_CALL_KEY,
|
||||
LOCAL_STORAGE_PIN_DATA_DISCOVERY_CANVAS_FLAG,
|
||||
|
@ -582,10 +582,7 @@ export default defineComponent({
|
|||
},
|
||||
},
|
||||
created() {
|
||||
const hasSeenPinDataTooltip = useStorage(
|
||||
LOCAL_STORAGE_PIN_DATA_DISCOVERY_CANVAS_FLAG,
|
||||
undefined,
|
||||
).value;
|
||||
const hasSeenPinDataTooltip = useStorage(LOCAL_STORAGE_PIN_DATA_DISCOVERY_CANVAS_FLAG).value;
|
||||
if (!hasSeenPinDataTooltip) {
|
||||
this.unwatchWorkflowDataItems = this.$watch('workflowDataItems', (dataItemsCount: number) => {
|
||||
this.showPinDataDiscoveryTooltip(dataItemsCount);
|
||||
|
@ -626,7 +623,7 @@ export default defineComponent({
|
|||
)
|
||||
return;
|
||||
|
||||
localStorage.setItem(LOCAL_STORAGE_PIN_DATA_DISCOVERY_CANVAS_FLAG, 'true');
|
||||
useStorage(LOCAL_STORAGE_PIN_DATA_DISCOVERY_CANVAS_FLAG).value = 'true';
|
||||
|
||||
this.pinDataDiscoveryTooltipVisible = true;
|
||||
this.unwatchWorkflowDataItems();
|
||||
|
|
|
@ -496,7 +496,7 @@
|
|||
import { defineAsyncComponent, defineComponent } from 'vue';
|
||||
import type { PropType } from 'vue';
|
||||
import { mapStores } from 'pinia';
|
||||
import { useStorage } from '@vueuse/core';
|
||||
import { useStorage } from '@/composables/useStorage';
|
||||
import { saveAs } from 'file-saver';
|
||||
import type {
|
||||
ConnectionTypes,
|
||||
|
@ -940,10 +940,7 @@ export default defineComponent({
|
|||
return;
|
||||
}
|
||||
|
||||
const pinDataDiscoveryFlag = useStorage(
|
||||
LOCAL_STORAGE_PIN_DATA_DISCOVERY_NDV_FLAG,
|
||||
undefined,
|
||||
).value;
|
||||
const pinDataDiscoveryFlag = useStorage(LOCAL_STORAGE_PIN_DATA_DISCOVERY_NDV_FLAG).value;
|
||||
|
||||
if (value && value.length > 0 && !this.isReadOnlyRoute && !pinDataDiscoveryFlag) {
|
||||
this.pinDataDiscoveryComplete();
|
||||
|
@ -963,8 +960,8 @@ export default defineComponent({
|
|||
}
|
||||
},
|
||||
pinDataDiscoveryComplete() {
|
||||
localStorage.setItem(LOCAL_STORAGE_PIN_DATA_DISCOVERY_NDV_FLAG, 'true');
|
||||
localStorage.setItem(LOCAL_STORAGE_PIN_DATA_DISCOVERY_CANVAS_FLAG, 'true');
|
||||
useStorage(LOCAL_STORAGE_PIN_DATA_DISCOVERY_NDV_FLAG).value = 'true';
|
||||
useStorage(LOCAL_STORAGE_PIN_DATA_DISCOVERY_CANVAS_FLAG).value = 'true';
|
||||
},
|
||||
enterEditMode({ origin }: EnterEditModeArgs) {
|
||||
const inputData = this.pinData
|
||||
|
|
48
packages/editor-ui/src/composables/useStorage.test.ts
Normal file
48
packages/editor-ui/src/composables/useStorage.test.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { nextTick } from 'vue';
|
||||
import { useStorage } from './useStorage';
|
||||
|
||||
describe('useStorage', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
});
|
||||
|
||||
it('should initialize with null if no value is stored in localStorage', () => {
|
||||
const key = 'test-key';
|
||||
const data = useStorage(key);
|
||||
|
||||
expect(data.value).toBeNull();
|
||||
});
|
||||
|
||||
it('should initialize with the stored value if it exists in localStorage', () => {
|
||||
const key = 'test-key';
|
||||
const value = 'test-value';
|
||||
localStorage.setItem(key, value);
|
||||
|
||||
const data = useStorage(key);
|
||||
expect(data.value).toBe(value);
|
||||
});
|
||||
|
||||
it('should update localStorage when the data ref is updated', async () => {
|
||||
const key = 'test-key';
|
||||
const value = 'test-value';
|
||||
const data = useStorage(key);
|
||||
|
||||
data.value = value;
|
||||
await nextTick();
|
||||
|
||||
expect(localStorage.getItem(key)).toBe(value);
|
||||
});
|
||||
|
||||
it('should remove the key from localStorage when the data ref is set to null', async () => {
|
||||
const key = 'test-key';
|
||||
const value = 'test-value';
|
||||
localStorage.setItem(key, value);
|
||||
|
||||
const data = useStorage(key);
|
||||
|
||||
data.value = null;
|
||||
await nextTick();
|
||||
|
||||
expect(localStorage.getItem(key)).toBeNull();
|
||||
});
|
||||
});
|
13
packages/editor-ui/src/composables/useStorage.ts
Normal file
13
packages/editor-ui/src/composables/useStorage.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { useStorage as useStorageComposable } from '@vueuse/core';
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
export function useStorage(key: string): Ref<string | null> {
|
||||
const data = useStorageComposable(key, null, undefined, { writeDefaults: false });
|
||||
|
||||
// bug in 1.15.1
|
||||
if (data.value === 'undefined') {
|
||||
data.value = null;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { defineComponent } from 'vue';
|
||||
import { mapStores } from 'pinia';
|
||||
import { useStorage } from '@vueuse/core';
|
||||
import { useStorage } from '@/composables/useStorage';
|
||||
|
||||
import { externalHooks } from '@/mixins/externalHooks';
|
||||
import { workflowHelpers } from '@/mixins/workflowHelpers';
|
||||
|
@ -120,10 +120,7 @@ export const workflowActivate = defineComponent({
|
|||
this.updatingWorkflowActivation = false;
|
||||
|
||||
if (isCurrentWorkflow) {
|
||||
if (
|
||||
newActiveState &&
|
||||
useStorage(LOCAL_STORAGE_ACTIVATION_FLAG, undefined).value !== 'true'
|
||||
) {
|
||||
if (newActiveState && useStorage(LOCAL_STORAGE_ACTIVATION_FLAG).value !== 'true') {
|
||||
this.uiStore.openModal(WORKFLOW_ACTIVE_MODAL_KEY);
|
||||
} else {
|
||||
await this.settingsStore.fetchPromptsData();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { useStorage } from '@vueuse/core';
|
||||
import { useStorage } from '@/composables/useStorage';
|
||||
|
||||
import type { RouteLocation, RouteRecordRaw } from 'vue-router';
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
import type { IPermissions } from './Interface';
|
||||
|
@ -802,7 +803,7 @@ export const routes = [
|
|||
role: [ROLE.Owner],
|
||||
},
|
||||
deny: {
|
||||
shouldDeny: () => !useStorage('audit-logs', undefined).value,
|
||||
shouldDeny: () => !useStorage('audit-logs').value,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -5,6 +5,8 @@ import { useSettingsStore } from '@/stores/settings.store';
|
|||
import { useRootStore } from '@/stores/n8nRoot.store';
|
||||
import { useTelemetryStore } from '@/stores/telemetry.store';
|
||||
import type { IN8nUISettings } from 'n8n-workflow';
|
||||
import { LOCAL_STORAGE_EXPERIMENT_OVERRIDES } from '@/constants';
|
||||
import { nextTick } from 'vue';
|
||||
|
||||
const DEFAULT_POSTHOG_SETTINGS: IN8nUISettings['posthog'] = {
|
||||
enabled: true,
|
||||
|
@ -55,7 +57,6 @@ function setup() {
|
|||
|
||||
vi.spyOn(window.posthog, 'init');
|
||||
vi.spyOn(window.posthog, 'identify');
|
||||
vi.spyOn(window.Storage.prototype, 'setItem');
|
||||
vi.spyOn(telemetryStore, 'track');
|
||||
}
|
||||
|
||||
|
@ -117,7 +118,7 @@ describe('Posthog store', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('sets override feature flags', () => {
|
||||
it('sets override feature flags', async () => {
|
||||
const TEST = 'test';
|
||||
const flags = {
|
||||
[TEST]: 'variant',
|
||||
|
@ -126,17 +127,17 @@ describe('Posthog store', () => {
|
|||
posthog.init(flags);
|
||||
|
||||
window.featureFlags?.override(TEST, 'override');
|
||||
await nextTick();
|
||||
|
||||
expect(posthog.getVariant('test')).toEqual('override');
|
||||
expect(window.posthog?.init).toHaveBeenCalled();
|
||||
expect(window.localStorage.setItem).toHaveBeenCalledWith(
|
||||
'N8N_EXPERIMENT_OVERRIDES',
|
||||
expect(window.localStorage.getItem(LOCAL_STORAGE_EXPERIMENT_OVERRIDES)).toEqual(
|
||||
JSON.stringify({ test: 'override' }),
|
||||
);
|
||||
|
||||
window.featureFlags?.override('other_test', 'override');
|
||||
expect(window.localStorage.setItem).toHaveBeenCalledWith(
|
||||
'N8N_EXPERIMENT_OVERRIDES',
|
||||
await nextTick();
|
||||
expect(window.localStorage.getItem(LOCAL_STORAGE_EXPERIMENT_OVERRIDES)).toEqual(
|
||||
JSON.stringify({ test: 'override', other_test: 'override' }),
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useStorage } from '@vueuse/core';
|
||||
import { useStorage } from '@/composables/useStorage';
|
||||
import { LOCAL_STORAGE_MAPPING_IS_ONBOARDED, STORES } from '@/constants';
|
||||
import type {
|
||||
INodeUi,
|
||||
|
@ -49,7 +49,7 @@ export const useNDVStore = defineStore(STORES.NDV, {
|
|||
canDrop: false,
|
||||
stickyPosition: null,
|
||||
},
|
||||
isMappingOnboarded: useStorage(LOCAL_STORAGE_MAPPING_IS_ONBOARDED, undefined).value === 'true',
|
||||
isMappingOnboarded: useStorage(LOCAL_STORAGE_MAPPING_IS_ONBOARDED).value === 'true',
|
||||
}),
|
||||
getters: {
|
||||
activeNode(): INodeUi | null {
|
||||
|
@ -228,7 +228,7 @@ export const useNDVStore = defineStore(STORES.NDV, {
|
|||
disableMappingHint(store = true) {
|
||||
this.isMappingOnboarded = true;
|
||||
if (store) {
|
||||
window.localStorage.setItem(LOCAL_STORAGE_MAPPING_IS_ONBOARDED, 'true');
|
||||
useStorage(LOCAL_STORAGE_MAPPING_IS_ONBOARDED).value = 'true';
|
||||
}
|
||||
},
|
||||
updateNodeParameterIssues(issues: INodeIssues): void {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { Ref } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import { defineStore } from 'pinia';
|
||||
import { useStorage } from '@vueuse/core';
|
||||
import { useStorage } from '@/composables/useStorage';
|
||||
import { useUsersStore } from '@/stores/users.store';
|
||||
import { useRootStore } from '@/stores/n8nRoot.store';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
|
@ -41,7 +41,7 @@ export const usePostHog = defineStore('posthog', () => {
|
|||
|
||||
if (!window.featureFlags) {
|
||||
// for testing
|
||||
const cachedOverrides = useStorage(LOCAL_STORAGE_EXPERIMENT_OVERRIDES, undefined).value;
|
||||
const cachedOverrides = useStorage(LOCAL_STORAGE_EXPERIMENT_OVERRIDES).value;
|
||||
if (cachedOverrides) {
|
||||
try {
|
||||
console.log('Overriding feature flags', cachedOverrides);
|
||||
|
@ -60,7 +60,7 @@ export const usePostHog = defineStore('posthog', () => {
|
|||
[name]: value,
|
||||
};
|
||||
try {
|
||||
localStorage.setItem(LOCAL_STORAGE_EXPERIMENT_OVERRIDES, JSON.stringify(overrides.value));
|
||||
useStorage(LOCAL_STORAGE_EXPERIMENT_OVERRIDES).value = JSON.stringify(overrides.value);
|
||||
} catch (e) {}
|
||||
},
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import type { AppliedThemeOption, ThemeOption } from '@/Interface';
|
||||
import { useStorage } from '@/composables/useStorage';
|
||||
import { LOCAL_STORAGE_THEME } from '@/constants';
|
||||
|
||||
const themeRef = useStorage(LOCAL_STORAGE_THEME);
|
||||
|
||||
export function addThemeToBody(theme: AppliedThemeOption) {
|
||||
window.document.body.setAttribute('data-theme', theme);
|
||||
}
|
||||
|
@ -11,7 +14,7 @@ export function isValidTheme(theme: string | null): theme is AppliedThemeOption
|
|||
|
||||
// query param allows overriding theme for demo view in preview iframe without flickering
|
||||
export function getThemeOverride() {
|
||||
return getQueryParam('theme') || localStorage.getItem(LOCAL_STORAGE_THEME);
|
||||
return getQueryParam('theme') || themeRef.value;
|
||||
}
|
||||
|
||||
function getQueryParam(paramName: string): string | null {
|
||||
|
@ -21,10 +24,10 @@ function getQueryParam(paramName: string): string | null {
|
|||
export function updateTheme(theme: ThemeOption) {
|
||||
if (theme === 'system') {
|
||||
window.document.body.removeAttribute('data-theme');
|
||||
localStorage.removeItem(LOCAL_STORAGE_THEME);
|
||||
themeRef.value = null;
|
||||
} else {
|
||||
addThemeToBody(theme);
|
||||
localStorage.setItem(LOCAL_STORAGE_THEME, theme);
|
||||
themeRef.value = theme;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue