mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-04 17:37:28 -08:00
666 lines
19 KiB
TypeScript
666 lines
19 KiB
TypeScript
import {
|
|
applyForOnboardingCall,
|
|
fetchNextOnboardingPrompt,
|
|
submitEmailOnSignup,
|
|
} from '@/api/workflow-webhooks';
|
|
import {
|
|
ABOUT_MODAL_KEY,
|
|
CHAT_EMBED_MODAL_KEY,
|
|
CHANGE_PASSWORD_MODAL_KEY,
|
|
COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY,
|
|
COMMUNITY_PACKAGE_INSTALL_MODAL_KEY,
|
|
COMMUNITY_PACKAGE_MANAGE_ACTIONS,
|
|
CONTACT_PROMPT_MODAL_KEY,
|
|
CREDENTIAL_EDIT_MODAL_KEY,
|
|
CREDENTIAL_SELECT_MODAL_KEY,
|
|
DELETE_USER_MODAL_KEY,
|
|
DUPLICATE_MODAL_KEY,
|
|
FAKE_DOOR_FEATURES,
|
|
IMPORT_CURL_MODAL_KEY,
|
|
INVITE_USER_MODAL_KEY,
|
|
LOG_STREAM_MODAL_KEY,
|
|
MFA_SETUP_MODAL_KEY,
|
|
ONBOARDING_CALL_SIGNUP_MODAL_KEY,
|
|
PERSONALIZATION_MODAL_KEY,
|
|
STORES,
|
|
TAGS_MANAGER_MODAL_KEY,
|
|
VALUE_SURVEY_MODAL_KEY,
|
|
VERSIONS_MODAL_KEY,
|
|
VIEWS,
|
|
WORKFLOW_ACTIVE_MODAL_KEY,
|
|
WORKFLOW_LM_CHAT_MODAL_KEY,
|
|
WORKFLOW_SETTINGS_MODAL_KEY,
|
|
WORKFLOW_SHARE_MODAL_KEY,
|
|
EXTERNAL_SECRETS_PROVIDER_MODAL_KEY,
|
|
SOURCE_CONTROL_PUSH_MODAL_KEY,
|
|
SOURCE_CONTROL_PULL_MODAL_KEY,
|
|
DEBUG_PAYWALL_MODAL_KEY,
|
|
N8N_PRICING_PAGE_URL,
|
|
WORKFLOW_HISTORY_VERSION_RESTORE,
|
|
SETUP_CREDENTIALS_MODAL_KEY,
|
|
GENERATE_CURL_MODAL_KEY,
|
|
} from '@/constants';
|
|
import type {
|
|
CloudUpdateLinkSourceType,
|
|
CurlToJSONResponse,
|
|
IFakeDoorLocation,
|
|
INodeUi,
|
|
IOnboardingCallPrompt,
|
|
UIState,
|
|
UTMCampaign,
|
|
XYPosition,
|
|
Modals,
|
|
NewCredentialsModal,
|
|
ThemeOption,
|
|
AppliedThemeOption,
|
|
NotificationOptions,
|
|
ModalState,
|
|
} from '@/Interface';
|
|
import { defineStore } from 'pinia';
|
|
import { useRootStore } from '@/stores/n8nRoot.store';
|
|
import { getCurlToJson } from '@/api/curlHelper';
|
|
import { useCloudPlanStore } from '@/stores/cloudPlan.store';
|
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
|
import { useSettingsStore } from '@/stores/settings.store';
|
|
import { hasPermission } from '@/utils/rbac/permissions';
|
|
import { useTelemetryStore } from '@/stores/telemetry.store';
|
|
import { useUsersStore } from '@/stores/users.store';
|
|
import { dismissBannerPermanently } from '@/api/ui';
|
|
import type { BannerName } from 'n8n-workflow';
|
|
import {
|
|
addThemeToBody,
|
|
getPreferredTheme,
|
|
getThemeOverride,
|
|
isValidTheme,
|
|
updateTheme,
|
|
} from './ui.utils';
|
|
|
|
let savedTheme: ThemeOption = 'system';
|
|
try {
|
|
const value = getThemeOverride();
|
|
if (isValidTheme(value)) {
|
|
savedTheme = value;
|
|
addThemeToBody(value);
|
|
}
|
|
} catch (e) {}
|
|
|
|
export type UiStore = ReturnType<typeof useUIStore>;
|
|
|
|
export const useUIStore = defineStore(STORES.UI, {
|
|
state: (): UIState => ({
|
|
activeActions: [],
|
|
activeCredentialType: null,
|
|
theme: savedTheme,
|
|
modals: {
|
|
...Object.fromEntries(
|
|
[
|
|
ABOUT_MODAL_KEY,
|
|
CHAT_EMBED_MODAL_KEY,
|
|
CHANGE_PASSWORD_MODAL_KEY,
|
|
CONTACT_PROMPT_MODAL_KEY,
|
|
CREDENTIAL_SELECT_MODAL_KEY,
|
|
DUPLICATE_MODAL_KEY,
|
|
ONBOARDING_CALL_SIGNUP_MODAL_KEY,
|
|
PERSONALIZATION_MODAL_KEY,
|
|
INVITE_USER_MODAL_KEY,
|
|
TAGS_MANAGER_MODAL_KEY,
|
|
VALUE_SURVEY_MODAL_KEY,
|
|
VERSIONS_MODAL_KEY,
|
|
WORKFLOW_LM_CHAT_MODAL_KEY,
|
|
WORKFLOW_SETTINGS_MODAL_KEY,
|
|
WORKFLOW_SHARE_MODAL_KEY,
|
|
WORKFLOW_ACTIVE_MODAL_KEY,
|
|
COMMUNITY_PACKAGE_INSTALL_MODAL_KEY,
|
|
MFA_SETUP_MODAL_KEY,
|
|
SOURCE_CONTROL_PUSH_MODAL_KEY,
|
|
SOURCE_CONTROL_PULL_MODAL_KEY,
|
|
EXTERNAL_SECRETS_PROVIDER_MODAL_KEY,
|
|
DEBUG_PAYWALL_MODAL_KEY,
|
|
WORKFLOW_HISTORY_VERSION_RESTORE,
|
|
SETUP_CREDENTIALS_MODAL_KEY,
|
|
].map((modalKey) => [modalKey, { open: false }]),
|
|
),
|
|
[DELETE_USER_MODAL_KEY]: {
|
|
open: false,
|
|
activeId: null,
|
|
},
|
|
[COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY]: {
|
|
open: false,
|
|
mode: '',
|
|
activeId: null,
|
|
},
|
|
[IMPORT_CURL_MODAL_KEY]: {
|
|
open: false,
|
|
data: {
|
|
curlCommand: '',
|
|
},
|
|
},
|
|
[GENERATE_CURL_MODAL_KEY]: {
|
|
open: false,
|
|
data: {
|
|
service: '',
|
|
request: '',
|
|
},
|
|
},
|
|
[LOG_STREAM_MODAL_KEY]: {
|
|
open: false,
|
|
data: undefined,
|
|
},
|
|
[CREDENTIAL_EDIT_MODAL_KEY]: {
|
|
open: false,
|
|
mode: '',
|
|
activeId: null,
|
|
showAuthSelector: false,
|
|
} as ModalState,
|
|
},
|
|
modalStack: [],
|
|
sidebarMenuCollapsed: true,
|
|
isPageLoading: true,
|
|
currentView: '',
|
|
mainPanelPosition: 0.5,
|
|
fakeDoorFeatures: [
|
|
{
|
|
id: FAKE_DOOR_FEATURES.SSO,
|
|
featureName: 'fakeDoor.settings.sso.name',
|
|
icon: 'key',
|
|
actionBoxTitle: 'fakeDoor.settings.sso.actionBox.title',
|
|
actionBoxDescription: 'fakeDoor.settings.sso.actionBox.description',
|
|
linkURL: 'https://n8n-community.typeform.com/to/l7QOrERN#f=sso',
|
|
uiLocations: ['settings/users'],
|
|
},
|
|
],
|
|
draggable: {
|
|
isDragging: false,
|
|
type: '',
|
|
data: '',
|
|
canDrop: false,
|
|
stickyPosition: null,
|
|
},
|
|
stateIsDirty: false,
|
|
lastSelectedNode: null,
|
|
lastSelectedNodeOutputIndex: null,
|
|
lastSelectedNodeEndpointUuid: null,
|
|
nodeViewOffsetPosition: [0, 0],
|
|
nodeViewMoveInProgress: false,
|
|
selectedNodes: [],
|
|
nodeViewInitialized: false,
|
|
addFirstStepOnLoad: false,
|
|
bannersHeight: 0,
|
|
bannerStack: [],
|
|
// Notifications that should show when a view is initialized
|
|
// This enables us to set a queue of notifications form outside (another component)
|
|
// and then show them when the view is initialized
|
|
pendingNotificationsForViews: {},
|
|
isCreateNodeActive: false,
|
|
}),
|
|
getters: {
|
|
appliedTheme(): AppliedThemeOption {
|
|
return this.theme === 'system' ? getPreferredTheme() : this.theme;
|
|
},
|
|
logo(): string {
|
|
const { releaseChannel } = useSettingsStore().settings;
|
|
const suffix = this.appliedTheme === 'dark' ? '-dark.svg' : '.svg';
|
|
return `static/logo/${
|
|
releaseChannel === 'stable' ? 'expanded' : `channel/${releaseChannel}`
|
|
}${suffix}`;
|
|
},
|
|
contextBasedTranslationKeys() {
|
|
const settingsStore = useSettingsStore();
|
|
const deploymentType = settingsStore.deploymentType;
|
|
|
|
let contextKey: '' | '.cloud' | '.desktop' = '';
|
|
if (deploymentType === 'cloud') {
|
|
contextKey = '.cloud';
|
|
} else if (deploymentType === 'desktop_mac' || deploymentType === 'desktop_win') {
|
|
contextKey = '.desktop';
|
|
}
|
|
|
|
return {
|
|
feature: {
|
|
unavailable: {
|
|
title: `contextual.feature.unavailable.title${contextKey}`,
|
|
},
|
|
},
|
|
credentials: {
|
|
sharing: {
|
|
unavailable: {
|
|
title: `contextual.credentials.sharing.unavailable.title${contextKey}`,
|
|
description: `contextual.credentials.sharing.unavailable.description${contextKey}`,
|
|
action: `contextual.credentials.sharing.unavailable.action${contextKey}`,
|
|
button: `contextual.credentials.sharing.unavailable.button${contextKey}`,
|
|
},
|
|
},
|
|
},
|
|
workflows: {
|
|
sharing: {
|
|
title: 'contextual.workflows.sharing.title',
|
|
unavailable: {
|
|
title: `contextual.workflows.sharing.unavailable.title${contextKey}`,
|
|
description: {
|
|
modal: `contextual.workflows.sharing.unavailable.description.modal${contextKey}`,
|
|
tooltip: `contextual.workflows.sharing.unavailable.description.tooltip${contextKey}`,
|
|
},
|
|
action: `contextual.workflows.sharing.unavailable.action${contextKey}`,
|
|
button: `contextual.workflows.sharing.unavailable.button${contextKey}`,
|
|
},
|
|
},
|
|
},
|
|
variables: {
|
|
unavailable: {
|
|
title: `contextual.variables.unavailable.title${contextKey}`,
|
|
description: 'contextual.variables.unavailable.description',
|
|
action: `contextual.variables.unavailable.action${contextKey}`,
|
|
button: `contextual.variables.unavailable.button${contextKey}`,
|
|
},
|
|
},
|
|
users: {
|
|
settings: {
|
|
unavailable: {
|
|
title: `contextual.users.settings.unavailable.title${contextKey}`,
|
|
description: `contextual.users.settings.unavailable.description${contextKey}`,
|
|
button: `contextual.users.settings.unavailable.button${contextKey}`,
|
|
},
|
|
},
|
|
},
|
|
} as const;
|
|
},
|
|
getLastSelectedNode(): INodeUi | null {
|
|
const workflowsStore = useWorkflowsStore();
|
|
if (this.lastSelectedNode) {
|
|
return workflowsStore.getNodeByName(this.lastSelectedNode);
|
|
}
|
|
return null;
|
|
},
|
|
areExpressionsDisabled(): boolean {
|
|
return this.currentView === VIEWS.DEMO;
|
|
},
|
|
isVersionsOpen(): boolean {
|
|
return this.modals[VERSIONS_MODAL_KEY].open;
|
|
},
|
|
isModalOpen() {
|
|
return (name: string) => this.modals[name].open;
|
|
},
|
|
isModalActive() {
|
|
return (name: string) => this.modalStack.length > 0 && name === this.modalStack[0];
|
|
},
|
|
getModalActiveId() {
|
|
return (name: string) => this.modals[name].activeId;
|
|
},
|
|
getModalMode() {
|
|
return (name: string) => this.modals[name].mode;
|
|
},
|
|
getModalData() {
|
|
return (name: string) => this.modals[name].data;
|
|
},
|
|
getFakeDoorByLocation() {
|
|
return (location: IFakeDoorLocation) =>
|
|
this.fakeDoorFeatures.filter((fakeDoor) => fakeDoor.uiLocations.includes(location));
|
|
},
|
|
getFakeDoorById() {
|
|
return (id: string) =>
|
|
this.fakeDoorFeatures.find((fakeDoor) => fakeDoor.id.toString() === id);
|
|
},
|
|
isReadOnlyView(): boolean {
|
|
return ![VIEWS.WORKFLOW, VIEWS.NEW_WORKFLOW, VIEWS.EXECUTION_DEBUG].includes(
|
|
this.currentView as VIEWS,
|
|
);
|
|
},
|
|
isNodeView(): boolean {
|
|
return [
|
|
VIEWS.NEW_WORKFLOW.toString(),
|
|
VIEWS.WORKFLOW.toString(),
|
|
VIEWS.WORKFLOW_EXECUTIONS.toString(),
|
|
].includes(this.currentView);
|
|
},
|
|
isActionActive() {
|
|
return (action: string) => this.activeActions.includes(action);
|
|
},
|
|
getSelectedNodes(): INodeUi[] {
|
|
const seen = new Set();
|
|
return this.selectedNodes.filter((node: INodeUi) => {
|
|
// dedupe for instances when same node is selected in different ways
|
|
if (!seen.has(node.id)) {
|
|
seen.add(node.id);
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
},
|
|
isNodeSelected() {
|
|
return (nodeName: string): boolean => {
|
|
let index;
|
|
for (index in this.selectedNodes) {
|
|
if (this.selectedNodes[index].name === nodeName) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
},
|
|
upgradeLinkUrl() {
|
|
return async (source: string, utm_campaign: string, deploymentType: string) => {
|
|
let linkUrl = '';
|
|
|
|
const searchParams = new URLSearchParams();
|
|
|
|
if (deploymentType === 'cloud' && hasPermission(['instanceOwner'])) {
|
|
const adminPanelHost = new URL(window.location.href).host.split('.').slice(1).join('.');
|
|
const { code } = await useCloudPlanStore().getAutoLoginCode();
|
|
linkUrl = `https://${adminPanelHost}/login`;
|
|
searchParams.set('code', code);
|
|
searchParams.set('returnPath', '/account/change-plan');
|
|
} else {
|
|
linkUrl = N8N_PRICING_PAGE_URL;
|
|
}
|
|
|
|
if (utm_campaign) {
|
|
searchParams.set('utm_campaign', utm_campaign);
|
|
}
|
|
|
|
if (source) {
|
|
searchParams.set('source', source);
|
|
}
|
|
return `${linkUrl}?${searchParams.toString()}`;
|
|
};
|
|
},
|
|
headerHeight() {
|
|
const style = getComputedStyle(document.body);
|
|
return Number(style.getPropertyValue('--header-height'));
|
|
},
|
|
isAnyModalOpen(): boolean {
|
|
return this.modalStack.length > 0;
|
|
},
|
|
},
|
|
actions: {
|
|
setTheme(theme: ThemeOption): void {
|
|
this.theme = theme;
|
|
updateTheme(theme);
|
|
},
|
|
setMode(name: keyof Modals, mode: string): void {
|
|
this.modals[name] = {
|
|
...this.modals[name],
|
|
mode,
|
|
};
|
|
},
|
|
setActiveId(name: keyof Modals, activeId: string): void {
|
|
this.modals[name] = {
|
|
...this.modals[name],
|
|
activeId,
|
|
};
|
|
},
|
|
setShowAuthSelector(name: keyof Modals, showAuthSelector: boolean) {
|
|
this.modals[name] = {
|
|
...this.modals[name],
|
|
showAuthSelector,
|
|
} as NewCredentialsModal;
|
|
},
|
|
setModalData(payload: { name: keyof Modals; data: Record<string, unknown> }) {
|
|
this.modals[payload.name] = {
|
|
...this.modals[payload.name],
|
|
data: payload.data,
|
|
};
|
|
},
|
|
openModal(name: keyof Modals): void {
|
|
this.modals[name] = {
|
|
...this.modals[name],
|
|
open: true,
|
|
};
|
|
this.modalStack = [name].concat(this.modalStack) as string[];
|
|
},
|
|
openModalWithData(payload: { name: keyof Modals; data: Record<string, unknown> }): void {
|
|
this.setModalData(payload);
|
|
this.openModal(payload.name);
|
|
},
|
|
closeModal(name: keyof Modals): void {
|
|
this.modals[name] = {
|
|
...this.modals[name],
|
|
open: false,
|
|
};
|
|
this.modalStack = this.modalStack.filter((openModalName: string) => {
|
|
return name !== openModalName;
|
|
});
|
|
},
|
|
draggableStartDragging(type: string, data: string): void {
|
|
this.draggable = {
|
|
isDragging: true,
|
|
type,
|
|
data,
|
|
canDrop: false,
|
|
stickyPosition: null,
|
|
};
|
|
},
|
|
draggableStopDragging(): void {
|
|
this.draggable = {
|
|
isDragging: false,
|
|
type: '',
|
|
data: '',
|
|
canDrop: false,
|
|
stickyPosition: null,
|
|
};
|
|
},
|
|
setDraggableStickyPos(position: XYPosition): void {
|
|
this.draggable = {
|
|
...this.draggable,
|
|
stickyPosition: position,
|
|
};
|
|
},
|
|
setDraggableCanDrop(canDrop: boolean): void {
|
|
this.draggable = {
|
|
...this.draggable,
|
|
canDrop,
|
|
};
|
|
},
|
|
openDeleteUserModal(id: string): void {
|
|
this.setActiveId(DELETE_USER_MODAL_KEY, id);
|
|
this.openModal(DELETE_USER_MODAL_KEY);
|
|
},
|
|
openExistingCredential(id: string): void {
|
|
this.setActiveId(CREDENTIAL_EDIT_MODAL_KEY, id);
|
|
this.setMode(CREDENTIAL_EDIT_MODAL_KEY, 'edit');
|
|
this.openModal(CREDENTIAL_EDIT_MODAL_KEY);
|
|
},
|
|
openNewCredential(type: string, showAuthOptions = false): void {
|
|
this.setActiveId(CREDENTIAL_EDIT_MODAL_KEY, type);
|
|
this.setShowAuthSelector(CREDENTIAL_EDIT_MODAL_KEY, showAuthOptions);
|
|
this.setMode(CREDENTIAL_EDIT_MODAL_KEY, 'new');
|
|
this.openModal(CREDENTIAL_EDIT_MODAL_KEY);
|
|
},
|
|
async getNextOnboardingPrompt(): Promise<IOnboardingCallPrompt | null> {
|
|
const rootStore = useRootStore();
|
|
const instanceId = rootStore.instanceId;
|
|
const { currentUser } = useUsersStore();
|
|
if (currentUser) {
|
|
return await fetchNextOnboardingPrompt(instanceId, currentUser);
|
|
}
|
|
return null;
|
|
},
|
|
async applyForOnboardingCall(email: string): Promise<string | null> {
|
|
const rootStore = useRootStore();
|
|
const instanceId = rootStore.instanceId;
|
|
const { currentUser } = useUsersStore();
|
|
if (currentUser) {
|
|
return await applyForOnboardingCall(instanceId, currentUser, email);
|
|
}
|
|
return null;
|
|
},
|
|
async submitContactEmail(email: string, agree: boolean): Promise<string | null> {
|
|
const rootStore = useRootStore();
|
|
const instanceId = rootStore.instanceId;
|
|
const { currentUser } = useUsersStore();
|
|
if (currentUser) {
|
|
return await submitEmailOnSignup(
|
|
instanceId,
|
|
currentUser,
|
|
email ?? currentUser?.email,
|
|
agree,
|
|
);
|
|
}
|
|
return null;
|
|
},
|
|
openCommunityPackageUninstallConfirmModal(packageName: string) {
|
|
this.setActiveId(COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY, packageName);
|
|
this.setMode(COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY, COMMUNITY_PACKAGE_MANAGE_ACTIONS.UNINSTALL);
|
|
this.openModal(COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY);
|
|
},
|
|
openCommunityPackageUpdateConfirmModal(packageName: string) {
|
|
this.setActiveId(COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY, packageName);
|
|
this.setMode(COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY, COMMUNITY_PACKAGE_MANAGE_ACTIONS.UPDATE);
|
|
this.openModal(COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY);
|
|
},
|
|
addActiveAction(action: string): void {
|
|
if (!this.activeActions.includes(action)) {
|
|
this.activeActions.push(action);
|
|
}
|
|
},
|
|
removeActiveAction(action: string): void {
|
|
const actionIndex = this.activeActions.indexOf(action);
|
|
if (actionIndex !== -1) {
|
|
this.activeActions.splice(actionIndex, 1);
|
|
}
|
|
},
|
|
addSelectedNode(node: INodeUi): void {
|
|
const isAlreadySelected = this.selectedNodes.some((n) => n.name === node.name);
|
|
if (!isAlreadySelected) {
|
|
this.selectedNodes.push(node);
|
|
}
|
|
},
|
|
removeNodeFromSelection(node: INodeUi): void {
|
|
let index;
|
|
for (index in this.selectedNodes) {
|
|
if (this.selectedNodes[index].name === node.name) {
|
|
this.selectedNodes.splice(parseInt(index, 10), 1);
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
resetSelectedNodes(): void {
|
|
this.selectedNodes = [];
|
|
},
|
|
setCurlCommand(payload: { name: string; command: string }): void {
|
|
this.modals[payload.name] = {
|
|
...this.modals[payload.name],
|
|
curlCommand: payload.command,
|
|
};
|
|
},
|
|
toggleSidebarMenuCollapse(): void {
|
|
this.sidebarMenuCollapsed = !this.sidebarMenuCollapsed;
|
|
},
|
|
async getCurlToJson(curlCommand: string): Promise<CurlToJSONResponse> {
|
|
const rootStore = useRootStore();
|
|
const parameters = await getCurlToJson(rootStore.getRestApiContext, curlCommand);
|
|
|
|
// Normalize placeholder values
|
|
if (parameters['parameters.url']) {
|
|
parameters['parameters.url'] = parameters['parameters.url']
|
|
.replaceAll('%7B', '{')
|
|
.replaceAll('%7D', '}');
|
|
}
|
|
|
|
return parameters;
|
|
},
|
|
async goToUpgrade(
|
|
source: CloudUpdateLinkSourceType,
|
|
utm_campaign: UTMCampaign,
|
|
mode: 'open' | 'redirect' = 'open',
|
|
): Promise<void> {
|
|
const { usageLeft, trialDaysLeft, userIsTrialing } = useCloudPlanStore();
|
|
const { executionsLeft, workflowsLeft } = usageLeft;
|
|
const deploymentType = useSettingsStore().deploymentType;
|
|
|
|
useTelemetryStore().track('User clicked upgrade CTA', {
|
|
source,
|
|
isTrial: userIsTrialing,
|
|
deploymentType,
|
|
trialDaysLeft,
|
|
executionsLeft,
|
|
workflowsLeft,
|
|
});
|
|
|
|
const upgradeLink = await this.upgradeLinkUrl(source, utm_campaign, deploymentType);
|
|
|
|
if (mode === 'open') {
|
|
window.open(upgradeLink, '_blank');
|
|
} else {
|
|
location.href = upgradeLink;
|
|
}
|
|
},
|
|
async dismissBanner(
|
|
name: BannerName,
|
|
type: 'temporary' | 'permanent' = 'temporary',
|
|
): Promise<void> {
|
|
if (type === 'permanent') {
|
|
await dismissBannerPermanently(useRootStore().getRestApiContext, {
|
|
bannerName: name,
|
|
dismissedBanners: useSettingsStore().permanentlyDismissedBanners,
|
|
});
|
|
this.removeBannerFromStack(name);
|
|
return;
|
|
}
|
|
this.removeBannerFromStack(name);
|
|
},
|
|
updateBannersHeight(newHeight: number): void {
|
|
this.bannersHeight = newHeight;
|
|
},
|
|
pushBannerToStack(name: BannerName) {
|
|
if (this.bannerStack.includes(name)) return;
|
|
this.bannerStack.push(name);
|
|
},
|
|
removeBannerFromStack(name: BannerName) {
|
|
this.bannerStack = this.bannerStack.filter((bannerName) => bannerName !== name);
|
|
},
|
|
clearBannerStack() {
|
|
this.bannerStack = [];
|
|
},
|
|
getNotificationsForView(view: VIEWS): NotificationOptions[] {
|
|
return this.pendingNotificationsForViews[view] ?? [];
|
|
},
|
|
setNotificationsForView(view: VIEWS, notifications: NotificationOptions[]) {
|
|
this.pendingNotificationsForViews[view] = notifications;
|
|
},
|
|
deleteNotificationsForView(view: VIEWS) {
|
|
delete this.pendingNotificationsForViews[view];
|
|
},
|
|
},
|
|
});
|
|
|
|
/**
|
|
* Helper function for listening to credential changes in the store
|
|
*/
|
|
export const listenForModalChanges = (opts: {
|
|
store: UiStore;
|
|
onModalOpened?: (name: keyof Modals) => void;
|
|
onModalClosed?: (name: keyof Modals) => void;
|
|
}) => {
|
|
const { store, onModalClosed, onModalOpened } = opts;
|
|
const listeningForActions = ['openModal', 'openModalWithData', 'closeModal'];
|
|
|
|
return store.$onAction((result) => {
|
|
const { name, after, args } = result;
|
|
after(async () => {
|
|
if (!listeningForActions.includes(name)) {
|
|
return;
|
|
}
|
|
|
|
switch (name) {
|
|
case 'openModal': {
|
|
const modalName = args[0];
|
|
onModalOpened?.(modalName);
|
|
break;
|
|
}
|
|
|
|
case 'openModalWithData': {
|
|
const { name: modalName } = args[0] ?? {};
|
|
onModalOpened?.(modalName);
|
|
break;
|
|
}
|
|
|
|
case 'closeModal': {
|
|
const modalName = args[0];
|
|
onModalClosed?.(modalName);
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
});
|
|
};
|