| @@ -1,8 +1,11 @@ | ||
| 1 | 1 | import { useEffect } from 'react'; |
| 2 | +import Raven from '../lib/Raven'; | |
| 3 | + | |
| 2 | 4 | import { |
| 3 | 5 | accountName, |
| 4 | 6 | adminUrl, |
| 7 | + connectionStatus, | |
| 5 | 8 | deviceId, |
| 6 | 9 | hubspotBaseUrl, |
| 7 | 10 | leadinQueryParams, |
| 8 | 11 | locale, |
| @@ -10,20 +13,83 @@ | ||
| 10 | 13 | portalDomain, |
| 11 | 14 | portalEmail, |
| 12 | 15 | portalId, |
| 13 | 16 | reviewSkippedDate, |
| 14 | - refreshToken, | |
| 15 | 17 | impactLink, |
| 16 | 18 | theme, |
| 19 | + lastAuthorizeTime, | |
| 20 | + lastDeauthorizeTime, | |
| 21 | + lastDisconnectTime, | |
| 17 | 22 | leadinPluginVersion, |
| 18 | 23 | phpVersion, |
| 19 | 24 | wpVersion, |
| 25 | + contentEmbed, | |
| 26 | + requiresContentEmbedScope, | |
| 27 | + decryptError, | |
| 28 | + LeadinConfig, | |
| 20 | 29 | } from '../constants/leadinConfig'; |
| 30 | +import { fetchAccessToken } from '../api/wordpressApiClient'; | |
| 21 | 31 | import { App, AppIframe } from './constants'; |
| 22 | 32 | import { messageMiddleware } from './messageMiddleware'; |
| 23 | 33 | import { resizeWindow, useIframeNotRendered } from '../utils/iframe'; |
| 24 | 34 | |
| 25 | -const getLeadinConfig = () => { | |
| 35 | +type PartialLeadinConfig = Pick< | |
| 36 | + LeadinConfig, | |
| 37 | + | 'accountName' | |
| 38 | + | 'adminUrl' | |
| 39 | + | 'connectionStatus' | |
| 40 | + | 'deviceId' | |
| 41 | + | 'plugins' | |
| 42 | + | 'portalDomain' | |
| 43 | + | 'portalEmail' | |
| 44 | + | 'portalId' | |
| 45 | + | 'reviewSkippedDate' | |
| 46 | + | 'refreshToken' | |
| 47 | + | 'impactLink' | |
| 48 | + | 'theme' | |
| 49 | + | 'trackConsent' | |
| 50 | + | 'lastAuthorizeTime' | |
| 51 | + | 'lastDeauthorizeTime' | |
| 52 | + | 'lastDisconnectTime' | |
| 53 | + | 'leadinPluginVersion' | |
| 54 | + | 'phpVersion' | |
| 55 | + | 'wpVersion' | |
| 56 | + | 'contentEmbed' | |
| 57 | + | 'requiresContentEmbedScope' | |
| 58 | + | 'decryptError' | |
| 59 | +>; | |
| 60 | + | |
| 61 | +type AppIntegrationConfig = Pick<LeadinConfig, 'adminUrl'>; | |
| 62 | + | |
| 63 | +const getIntegrationConfig = (): AppIntegrationConfig => { | |
| 64 | + return { | |
| 65 | + adminUrl: leadinQueryParams.adminUrl, | |
| 66 | + }; | |
| 67 | +}; | |
| 68 | + | |
| 69 | +/** | |
| 70 | + * A modified version of the original leadinConfig that is passed to some integrated apps. | |
| 71 | + * | |
| 72 | + * Important: | |
| 73 | + * Try not to add new fields here. | |
| 74 | + * This config is already too large and broad in scope. | |
| 75 | + * It tightly couples the apps that use it with the WordPress plugin. | |
| 76 | + * Consider instead passing new required fields as new entry to PluginAppOptions or app-specific options. | |
| 77 | + */ | |
| 78 | +type AppLeadinConfig = { | |
| 79 | + admin: string; | |
| 80 | + company: string; | |
| 81 | + email: string; | |
| 82 | + firstName: string; | |
| 83 | + irclickid: string; | |
| 84 | + justConnected: string; | |
| 85 | + lastName: string; | |
| 86 | + mpid: string; | |
| 87 | + nonce: string; | |
| 88 | + websiteName: string; | |
| 89 | +} & PartialLeadinConfig; | |
| 90 | + | |
| 91 | +const getLeadinConfig = (): AppLeadinConfig => { | |
| 26 | 92 | const utm_query_params = Object.keys(leadinQueryParams) |
| 27 | 93 | .filter(x => /^utm/.test(x)) |
| 28 | 94 | .reduce( |
| 29 | 95 | (p: { [key: string]: string }, c: string) => ({ |
| @@ -36,8 +102,9 @@ | ||
| 36 | 102 | accountName, |
| 37 | 103 | admin: leadinQueryParams.admin, |
| 38 | 104 | adminUrl, |
| 39 | 105 | company: leadinQueryParams.company, |
| 106 | + connectionStatus, | |
| 40 | 107 | deviceId, |
| 41 | 108 | email: leadinQueryParams.email, |
| 42 | 109 | firstName: leadinQueryParams.firstName, |
| 43 | 110 | irclickid: leadinQueryParams.irclickid, |
| @@ -42,8 +109,11 @@ | ||
| 42 | 109 | firstName: leadinQueryParams.firstName, |
| 43 | 110 | irclickid: leadinQueryParams.irclickid, |
| 44 | 111 | justConnected: leadinQueryParams.justConnected, |
| 45 | 112 | lastName: leadinQueryParams.lastName, |
| 113 | + lastAuthorizeTime, | |
| 114 | + lastDeauthorizeTime, | |
| 115 | + lastDisconnectTime, | |
| 46 | 116 | leadinPluginVersion, |
| 47 | 117 | mpid: leadinQueryParams.mpid, |
| 48 | 118 | nonce: leadinQueryParams.nonce, |
| 49 | 119 | phpVersion, |
| @@ -55,8 +125,11 @@ | ||
| 55 | 125 | theme, |
| 56 | 126 | trackConsent: leadinQueryParams.trackConsent, |
| 57 | 127 | websiteName: leadinQueryParams.websiteName, |
| 58 | 128 | wpVersion, |
| 129 | + contentEmbed, | |
| 130 | + requiresContentEmbedScope, | |
| 131 | + decryptError, | |
| 59 | 132 | ...utm_query_params, |
| 60 | 133 | }; |
| 61 | 134 | }; |
| 62 | 135 | |
| @@ -67,20 +140,19 @@ | ||
| 67 | 140 | LiveChatAppOptions, |
| 68 | 141 | PluginAppOptions, |
| 69 | 142 | }: any = window; |
| 70 | 143 | let options; |
| 71 | - | |
| 72 | 144 | switch (app) { |
| 73 | 145 | case App.Plugin: |
| 74 | - options = new PluginAppOptions().setLeadinConfig(getLeadinConfig()); | |
| 146 | + options = new PluginAppOptions(); | |
| 75 | 147 | break; |
| 76 | 148 | case App.PluginSettings: |
| 77 | - options = new PluginAppOptions() | |
| 78 | - .setLeadinConfig(getLeadinConfig()) | |
| 79 | - .setPluginSettingsInit(); | |
| 149 | + options = new PluginAppOptions().setPluginSettingsInit(); | |
| 80 | 150 | break; |
| 81 | 151 | case App.Forms: |
| 82 | - options = new FormsAppOptions(); | |
| 152 | + options = new FormsAppOptions().setIntegratedAppConfig( | |
| 153 | + getIntegrationConfig() | |
| 154 | + ); | |
| 83 | 155 | if (createRoute) { |
| 84 | 156 | options = options.setCreateFormAppInit(); |
| 85 | 157 | } |
| 86 | 158 | break; |
| @@ -101,18 +173,28 @@ | ||
| 101 | 173 | app: App, |
| 102 | 174 | createRoute: boolean, |
| 103 | 175 | container: HTMLElement | null |
| 104 | 176 | ) { |
| 177 | + console.info( | |
| 178 | + 'HubSpot plugin - starting app embedder for:', | |
| 179 | + AppIframe[app], | |
| 180 | + container | |
| 181 | + ); | |
| 105 | 182 | const iframeNotRendered = useIframeNotRendered(AppIframe[app]); |
| 106 | 183 | |
| 107 | 184 | useEffect(() => { |
| 108 | 185 | const { IntegratedAppEmbedder }: any = window; |
| 109 | 186 | |
| 110 | - if (IntegratedAppEmbedder) { | |
| 187 | + if (!IntegratedAppEmbedder) { | |
| 188 | + return; | |
| 189 | + } | |
| 190 | + | |
| 191 | + const createEmbedder = (accessToken = '', expiresIn = 0) => { | |
| 111 | 192 | const options = getAppOptions(app, createRoute) |
| 112 | 193 | .setLocale(locale) |
| 113 | 194 | .setDeviceId(deviceId) |
| 114 | - .setRefreshToken(refreshToken); | |
| 195 | + .setAccessToken(accessToken, expiresIn) | |
| 196 | + .setLeadinConfig(getLeadinConfig()); | |
| 115 | 197 | |
| 116 | 198 | const embedder = new IntegratedAppEmbedder( |
| 117 | 199 | AppIframe[app], |
| 118 | 200 | portalId, |
| @@ -117,17 +199,60 @@ | ||
| 117 | 199 | AppIframe[app], |
| 118 | 200 | portalId, |
| 119 | 201 | hubspotBaseUrl, |
| 120 | 202 | resizeWindow, |
| 121 | - refreshToken ? '' : impactLink | |
| 203 | + accessToken ? '' : impactLink | |
| 122 | 204 | ).setOptions(options); |
| 123 | 205 | |
| 124 | - embedder.subscribe(messageMiddleware(embedder)); | |
| 206 | + embedder.subscribe((message: any) => { | |
| 207 | + messageMiddleware(embedder)(message); | |
| 208 | + }); | |
| 209 | + embedder.setTokenRenewalCallback(fetchAccessToken); | |
| 210 | + | |
| 125 | 211 | embedder.attachTo(container, true); |
| 126 | 212 | embedder.postStartAppMessage(); // lets the app know all all data has been passed to it |
| 127 | 213 | |
| 128 | 214 | (window as any).embedder = embedder; |
| 215 | + }; | |
| 216 | + | |
| 217 | + if (connectionStatus === 'Connected') { | |
| 218 | + fetchAccessToken() | |
| 219 | + .then( | |
| 220 | + ({ | |
| 221 | + accessToken, | |
| 222 | + expiresIn, | |
| 223 | + }: { | |
| 224 | + accessToken: string; | |
| 225 | + expiresIn: number; | |
| 226 | + }) => { | |
| 227 | + createEmbedder(accessToken, expiresIn); | |
| 228 | + } | |
| 229 | + ) | |
| 230 | + .catch(() => {}); | |
| 231 | + } else { | |
| 232 | + createEmbedder(); | |
| 129 | 233 | } |
| 130 | 234 | }, []); |
| 235 | + | |
| 236 | + if (iframeNotRendered) { | |
| 237 | + console.error('HubSpot plugin Iframe not rendered', { | |
| 238 | + portalId, | |
| 239 | + container, | |
| 240 | + appName: AppIframe[app], | |
| 241 | + hasIntegratedAppEmbedder: !!(window as any).IntegratedAppEmbedder, | |
| 242 | + }); | |
| 243 | + Raven.captureException(new Error('Leadin Iframe not rendered'), { | |
| 244 | + fingerprint: ['USE_APP_EMBEDDER', 'IFRAME_SETUP_ERROR'], | |
| 245 | + extra: { | |
| 246 | + portalId, | |
| 247 | + container, | |
| 248 | + app, | |
| 249 | + hubspotBaseUrl, | |
| 250 | + impactLink, | |
| 251 | + appName: AppIframe[app], | |
| 252 | + isConnected: connectionStatus === 'Connected', | |
| 253 | + }, | |
| 254 | + }); | |
| 255 | + } | |
| 131 | 256 | |
| 132 | 257 | return iframeNotRendered; |
| 133 | 258 | } |