plugin updates

This commit is contained in:
Tony Volpe
2024-06-17 15:33:26 -04:00
parent 3751a5a1a6
commit e4e274a9a7
2674 changed files with 0 additions and 507851 deletions

View File

@@ -1,54 +0,0 @@
import React from 'react';
import { __ } from '@wordpress/i18n';
import { styled } from '@linaria/react';
const IframeErrorContainer = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 120px;
font-family: 'Lexend Deca', Helvetica, Arial, sans-serif;
font-weight: 400;
font-size: 14px;
font-size: 0.875rem;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-smoothing: antialiased;
line-height: 1.5rem;
`;
const ErrorHeader = styled.h1`
text-shadow: 0 0 1px transparent;
margin-bottom: 1.25rem;
color: #33475b;
font-size: 1.25rem;
`;
export const IframeErrorPage = () => (
<IframeErrorContainer>
<img
alt="Cannot find page"
width="175"
src="//static.hsappstatic.net/ui-images/static-1.14/optimized/errors/map.svg"
/>
<ErrorHeader>
{__(
'The HubSpot for WordPress plugin is not able to load pages',
'leadin'
)}
</ErrorHeader>
<p>
{__(
'Try disabling your browser extensions and ad blockers, then refresh the page',
'leadin'
)}
</p>
<p>
{__(
'Or open the HubSpot for WordPress plugin in a different browser',
'leadin'
)}
</p>
</IframeErrorContainer>
);

View File

@@ -1,15 +0,0 @@
export enum App {
Forms,
LiveChat,
Plugin,
PluginSettings,
Background,
}
export const AppIframe = {
[App.Forms]: 'integrated-form-app',
[App.LiveChat]: 'integrated-livechat-app',
[App.Plugin]: 'integrated-plugin-app',
[App.PluginSettings]: 'integrated-plugin-app',
[App.Background]: 'integrated-plugin-proxy',
} as const;

View File

@@ -1,11 +0,0 @@
export const CoreMessages = {
HandshakeReceive: 'INTEGRATED_APP_EMBEDDER_HANDSHAKE_RECEIVED',
SendRefreshToken: 'INTEGRATED_APP_EMBEDDER_SEND_REFRESH_TOKEN',
ReloadParentFrame: 'INTEGRATED_APP_EMBEDDER_RELOAD_PARENT_FRAME',
RedirectParentFrame: 'INTEGRATED_APP_EMBEDDER_REDIRECT_PARENT_FRAME',
SendLocale: 'INTEGRATED_APP_EMBEDDER_SEND_LOCALE',
SendDeviceId: 'INTEGRATED_APP_EMBEDDER_SEND_DEVICE_ID',
SendIntegratedAppConfig: 'INTEGRATED_APP_EMBEDDER_CONFIG',
} as const;
export type CoreMessageType = typeof CoreMessages[keyof typeof CoreMessages];

View File

@@ -1,5 +0,0 @@
export const FormMessages = {
CreateFormAppNavigation: 'CREATE_FORM_APP_NAVIGATION',
} as const;
export type FormMessageType = typeof FormMessages[keyof typeof FormMessages];

View File

@@ -1,18 +0,0 @@
import * as Core from './core/CoreMessages';
import * as Forms from './forms/FormsMessages';
import * as LiveChat from './livechat/LiveChatMessages';
import * as Plugin from './plugin/PluginMessages';
import * as Proxy from './proxy/ProxyMessages';
export type MessageType =
| Core.CoreMessageType
| Forms.FormMessageType
| LiveChat.LiveChatMessageType
| Plugin.PluginMessageType
| Proxy.ProxyMessageType;
export * from './core/CoreMessages';
export * from './forms/FormsMessages';
export * from './livechat/LiveChatMessages';
export * from './plugin/PluginMessages';
export * from './proxy/ProxyMessages';

View File

@@ -1,5 +0,0 @@
export const LiveChatMessages = {
CreateLiveChatAppNavigation: 'CREATE_LIVE_CHAT_APP_NAVIGATION',
} as const;
export type LiveChatMessageType = typeof LiveChatMessages[keyof typeof LiveChatMessages];

View File

@@ -1,27 +0,0 @@
export const PluginMessages = {
PluginSettingsNavigation: 'PLUGIN_SETTINGS_NAVIGATION',
PluginLeadinConfig: 'PLUGIN_LEADIN_CONFIG',
TrackConsent: 'INTEGRATED_APP_EMBEDDER_TRACK_CONSENT',
InternalTrackingFetchRequest: 'INTEGRATED_TRACKING_FETCH_REQUEST',
InternalTrackingFetchResponse: 'INTEGRATED_TRACKING_FETCH_RESPONSE',
InternalTrackingFetchError: 'INTEGRATED_TRACKING_FETCH_ERROR',
InternalTrackingChangeRequest: 'INTEGRATED_TRACKING_CHANGE_REQUEST',
InternalTrackingChangeError: 'INTEGRATED_TRACKING_CHANGE_ERROR',
BusinessUnitFetchRequest: 'BUSINESS_UNIT_FETCH_REQUEST',
BusinessUnitFetchResponse: 'BUSINESS_UNIT_FETCH_FETCH_RESPONSE',
BusinessUnitFetchError: 'BUSINESS_UNIT_FETCH_FETCH_ERROR',
BusinessUnitChangeRequest: 'BUSINESS_UNIT_CHANGE_REQUEST',
BusinessUnitChangeError: 'BUSINESS_UNIT_CHANGE_ERROR',
SkipReviewRequest: 'SKIP_REVIEW_REQUEST',
SkipReviewResponse: 'SKIP_REVIEW_RESPONSE',
SkipReviewError: 'SKIP_REVIEW_ERROR',
RemoveParentQueryParam: 'REMOVE_PARENT_QUERY_PARAM',
ContentEmbedInstallRequest: 'CONTENT_EMBED_INSTALL_REQUEST',
ContentEmbedInstallResponse: 'CONTENT_EMBED_INSTALL_RESPONSE',
ContentEmbedInstallError: 'CONTENT_EMBED_INSTALL_ERROR',
ContentEmbedActivationRequest: 'CONTENT_EMBED_ACTIVATION_REQUEST',
ContentEmbedActivationResponse: 'CONTENT_EMBED_ACTIVATION_RESPONSE',
ContentEmbedActivationError: 'CONTENT_EMBED_ACTIVATION_ERROR',
} as const;
export type PluginMessageType = typeof PluginMessages[keyof typeof PluginMessages];

View File

@@ -1,21 +0,0 @@
export const ProxyMessages = {
FetchForms: 'FETCH_FORMS',
FetchForm: 'FETCH_FORM',
CreateFormFromTemplate: 'CREATE_FORM_FROM_TEMPLATE',
FetchAuth: 'FETCH_AUTH',
FetchMeetingsAndUsers: 'FETCH_MEETINGS_AND_USERS',
FetchContactsCreateSinceActivation: 'FETCH_CONTACTS_CREATED_SINCE_ACTIVATION',
FetchOrCreateMeetingUser: 'FETCH_OR_CREATE_MEETING_USER',
ConnectMeetingsCalendar: 'CONNECT_MEETINGS_CALENDAR',
TrackFormPreviewRender: 'TRACK_FORM_PREVIEW_RENDER',
TrackFormCreatedFromTemplate: 'TRACK_FORM_CREATED_FROM_TEMPLATE',
TrackFormCreationFailed: 'TRACK_FORM_CREATION_FAILED',
TrackMeetingPreviewRender: 'TRACK_MEETING_PREVIEW_RENDER',
TrackSidebarMetaChange: 'TRACK_SIDEBAR_META_CHANGE',
TrackReviewBannerRender: 'TRACK_REVIEW_BANNER_RENDER',
TrackReviewBannerInteraction: 'TRACK_REVIEW_BANNER_INTERACTION',
TrackReviewBannerDismissed: 'TRACK_REVIEW_BANNER_DISMISSED',
TrackPluginDeactivation: 'TRACK_PLUGIN_DEACTIVATION',
} as const;
export type ProxyMessageType = typeof ProxyMessages[keyof typeof ProxyMessages];

View File

@@ -1,161 +0,0 @@
import { MessageType, PluginMessages } from '../iframe/integratedMessages';
import {
fetchDisableInternalTracking,
trackConsent,
disableInternalTracking,
getBusinessUnitId,
setBusinessUnitId,
skipReview,
} from '../api/wordpressApiClient';
import { removeQueryParamFromLocation } from '../utils/queryParams';
import { startActivation, startInstall } from '../utils/contentEmbedInstaller';
export type Message = { key: MessageType; payload?: any };
const messageMapper: Map<MessageType, Function> = new Map([
[
PluginMessages.TrackConsent,
(message: Message) => {
trackConsent(message.payload);
},
],
[
PluginMessages.InternalTrackingChangeRequest,
(message: Message, embedder: any) => {
disableInternalTracking(message.payload)
.then(() => {
embedder.postMessage({
key: PluginMessages.InternalTrackingFetchResponse,
payload: message.payload,
});
})
.catch(payload => {
embedder.postMessage({
key: PluginMessages.InternalTrackingChangeError,
payload,
});
});
},
],
[
PluginMessages.InternalTrackingFetchRequest,
(__message: Message, embedder: any) => {
fetchDisableInternalTracking()
.then(({ message: payload }) => {
embedder.postMessage({
key: PluginMessages.InternalTrackingFetchResponse,
payload,
});
})
.catch(payload => {
embedder.postMessage({
key: PluginMessages.InternalTrackingFetchError,
payload,
});
});
},
],
[
PluginMessages.BusinessUnitFetchRequest,
(__message: Message, embedder: any) => {
getBusinessUnitId()
.then(payload => {
embedder.postMessage({
key: PluginMessages.BusinessUnitFetchResponse,
payload,
});
})
.catch(payload => {
embedder.postMessage({
key: PluginMessages.BusinessUnitFetchError,
payload,
});
});
},
],
[
PluginMessages.BusinessUnitChangeRequest,
(message: Message, embedder: any) => {
setBusinessUnitId(message.payload)
.then(payload => {
embedder.postMessage({
key: PluginMessages.BusinessUnitFetchResponse,
payload,
});
})
.catch(payload => {
embedder.postMessage({
key: PluginMessages.BusinessUnitChangeError,
payload,
});
});
},
],
[
PluginMessages.SkipReviewRequest,
(__message: Message, embedder: any) => {
skipReview()
.then(payload => {
embedder.postMessage({
key: PluginMessages.SkipReviewResponse,
payload,
});
})
.catch(payload => {
embedder.postMessage({
key: PluginMessages.SkipReviewError,
payload,
});
});
},
],
[
PluginMessages.RemoveParentQueryParam,
(message: Message) => {
removeQueryParamFromLocation(message.payload);
},
],
[
PluginMessages.ContentEmbedInstallRequest,
(message: Message, embedder: any) => {
startInstall(message.payload.nonce)
.then(payload => {
embedder.postMessage({
key: PluginMessages.ContentEmbedInstallResponse,
payload: payload,
});
})
.catch(payload => {
embedder.postMessage({
key: PluginMessages.ContentEmbedInstallError,
payload,
});
});
},
],
[
PluginMessages.ContentEmbedActivationRequest,
(message: Message, embedder: any) => {
startActivation(message.payload.activateAjaxUrl)
.then(payload => {
embedder.postMessage({
key: PluginMessages.ContentEmbedActivationResponse,
payload: payload,
});
})
.catch(payload => {
embedder.postMessage({
key: PluginMessages.ContentEmbedActivationError,
payload,
});
});
},
],
]);
export const messageMiddleware = (embedder: any) => (message: Message) => {
const next = messageMapper.get(message.key);
if (next) {
next(message, embedder);
}
};

View File

@@ -1,64 +0,0 @@
import React, { Fragment } from 'react';
import ReactDOM from 'react-dom';
import { domElements } from '../constants/selectors';
import useAppEmbedder from './useAppEmbedder';
import { App } from './constants';
import { IframeErrorPage } from './IframeErrorPage';
interface PortalProps extends React.PropsWithChildren {
app: App;
createRoute: boolean;
}
const IntegratedIframePortal = (props: PortalProps) => {
const container = document.getElementById(domElements.leadinIframeContainer);
const iframeNotRendered = useAppEmbedder(
props.app,
props.createRoute,
container
);
if (container && !iframeNotRendered) {
return ReactDOM.createPortal(props.children, container);
}
return (
<Fragment>
{(!container || iframeNotRendered) && <IframeErrorPage />}
</Fragment>
);
};
const renderIframeApp = () => {
const iframeFallbackContainer = document.getElementById(
domElements.leadinIframeContainer
);
let app: App;
const queryParams = new URLSearchParams(location.search);
const page = queryParams.get('page');
const createRoute = queryParams.get('leadin_route[0]') === 'create';
switch (page) {
case 'leadin_forms':
app = App.Forms;
break;
case 'leadin_chatflows':
app = App.LiveChat;
break;
case 'leadin_settings':
app = App.PluginSettings;
break;
case 'leadin_user_guide':
default:
app = App.Plugin;
break;
}
ReactDOM.render(
<IntegratedIframePortal app={app} createRoute={createRoute} />,
iframeFallbackContainer
);
};
export default renderIframeApp;

View File

@@ -1,233 +0,0 @@
import { useEffect } from 'react';
import Raven from '../lib/Raven';
import {
accountName,
adminUrl,
connectionStatus,
deviceId,
hubspotBaseUrl,
leadinQueryParams,
locale,
plugins,
portalDomain,
portalEmail,
portalId,
reviewSkippedDate,
refreshToken,
impactLink,
theme,
lastAuthorizeTime,
lastDeauthorizeTime,
lastDisconnectTime,
leadinPluginVersion,
phpVersion,
wpVersion,
contentEmbed,
requiresContentEmbedScope,
refreshTokenError,
LeadinConfig,
} from '../constants/leadinConfig';
import { App, AppIframe } from './constants';
import { messageMiddleware } from './messageMiddleware';
import { resizeWindow, useIframeNotRendered } from '../utils/iframe';
type PartialLeadinConfig = Pick<
LeadinConfig,
| 'accountName'
| 'adminUrl'
| 'connectionStatus'
| 'deviceId'
| 'plugins'
| 'portalDomain'
| 'portalEmail'
| 'portalId'
| 'reviewSkippedDate'
| 'refreshToken'
| 'impactLink'
| 'theme'
| 'trackConsent'
| 'lastAuthorizeTime'
| 'lastDeauthorizeTime'
| 'lastDisconnectTime'
| 'leadinPluginVersion'
| 'phpVersion'
| 'wpVersion'
| 'contentEmbed'
| 'requiresContentEmbedScope'
| 'refreshTokenError'
>;
type AppIntegrationConfig = Pick<LeadinConfig, 'adminUrl'>;
const getIntegrationConfig = (): AppIntegrationConfig => {
return {
adminUrl: leadinQueryParams.adminUrl,
};
};
/**
* A modified version of the original leadinConfig that is passed to some integrated apps.
*
* Important:
* Try not to add new fields here.
* This config is already too large and broad in scope.
* It tightly couples the apps that use it with the WordPress plugin.
* Consider instead passing new required fields as new entry to PluginAppOptions or app-specific options.
*/
type AppLeadinConfig = {
admin: string;
company: string;
email: string;
firstName: string;
irclickid: string;
justConnected: string;
lastName: string;
mpid: string;
nonce: string;
websiteName: string;
} & PartialLeadinConfig;
const getLeadinConfig = (): AppLeadinConfig => {
const utm_query_params = Object.keys(leadinQueryParams)
.filter(x => /^utm/.test(x))
.reduce(
(p: { [key: string]: string }, c: string) => ({
[c]: leadinQueryParams[c],
...p,
}),
{}
);
return {
accountName,
admin: leadinQueryParams.admin,
adminUrl,
company: leadinQueryParams.company,
connectionStatus,
deviceId,
email: leadinQueryParams.email,
firstName: leadinQueryParams.firstName,
irclickid: leadinQueryParams.irclickid,
justConnected: leadinQueryParams.justConnected,
lastName: leadinQueryParams.lastName,
lastAuthorizeTime,
lastDeauthorizeTime,
lastDisconnectTime,
leadinPluginVersion,
mpid: leadinQueryParams.mpid,
nonce: leadinQueryParams.nonce,
phpVersion,
plugins,
portalDomain,
portalEmail,
portalId,
reviewSkippedDate,
theme,
trackConsent: leadinQueryParams.trackConsent,
websiteName: leadinQueryParams.websiteName,
wpVersion,
contentEmbed,
requiresContentEmbedScope,
refreshTokenError,
...utm_query_params,
};
};
const getAppOptions = (app: App, createRoute = false) => {
const {
IntegratedAppOptions,
FormsAppOptions,
LiveChatAppOptions,
PluginAppOptions,
}: any = window;
let options;
switch (app) {
case App.Plugin:
options = new PluginAppOptions().setLeadinConfig(getLeadinConfig());
break;
case App.PluginSettings:
options = new PluginAppOptions()
.setLeadinConfig(getLeadinConfig())
.setPluginSettingsInit();
break;
case App.Forms:
options = new FormsAppOptions().setIntegratedAppConfig(
getIntegrationConfig()
);
if (createRoute) {
options = options.setCreateFormAppInit();
}
break;
case App.LiveChat:
options = new LiveChatAppOptions();
if (createRoute) {
options = options.setCreateLiveChatAppInit();
}
break;
default:
options = new IntegratedAppOptions();
}
return options;
};
export default function useAppEmbedder(
app: App,
createRoute: boolean,
container: HTMLElement | null
) {
console.info(
'HubSpot plugin - starting app embedder for:',
AppIframe[app],
container
);
const iframeNotRendered = useIframeNotRendered(AppIframe[app]);
useEffect(() => {
const { IntegratedAppEmbedder }: any = window;
if (IntegratedAppEmbedder) {
const options = getAppOptions(app, createRoute)
.setLocale(locale)
.setDeviceId(deviceId)
.setRefreshToken(refreshToken);
const embedder = new IntegratedAppEmbedder(
AppIframe[app],
portalId,
hubspotBaseUrl,
resizeWindow,
refreshToken ? '' : impactLink
).setOptions(options);
embedder.subscribe(messageMiddleware(embedder));
embedder.attachTo(container, true);
embedder.postStartAppMessage(); // lets the app know all all data has been passed to it
(window as any).embedder = embedder;
}
}, []);
if (iframeNotRendered) {
console.error('HubSpot plugin Iframe not rendered', {
portalId,
container,
appName: AppIframe[app],
hasIntegratedAppEmbedder: !!(window as any).IntegratedAppEmbedder,
});
Raven.captureException(new Error('Leadin Iframe not rendered'), {
fingerprint: ['USE_APP_EMBEDDER', 'IFRAME_SETUP_ERROR'],
extra: {
portalId,
container,
app,
hubspotBaseUrl,
impactLink,
appName: AppIframe[app],
hasRefreshToken: !!refreshToken,
},
});
}
return iframeNotRendered;
}

View File

@@ -1,29 +0,0 @@
import { createContext, useContext } from 'react';
import {
deviceId,
hubspotBaseUrl,
locale,
portalId,
} from '../constants/leadinConfig';
import { Message } from './messageMiddleware';
export const BackgroudAppContext = createContext<any>(null);
export function useBackgroundAppContext() {
return useContext(BackgroudAppContext);
}
export function usePostBackgroundMessage() {
const app = useBackgroundAppContext();
return (message: Message) => {
app.postMessage(message);
};
}
export function usePostAsyncBackgroundMessage(): (
message: Message
) => Promise<any> {
const app = useBackgroundAppContext();
return (message: Message) => app.postAsyncMessage(message);
}