From 4110f610a033ec8125d901846d46e75aa5000809 Mon Sep 17 00:00:00 2001 From: Valentino Hudhra <v.hudhra@gmail.com> Date: Mon, 7 Oct 2024 15:47:14 +0200 Subject: [PATCH 1/6] googleanalytics --- .../googleanalytics/gitbook-manifest.yaml | 8 +- .../googleanalytics/src/components.tsx | 83 +++++++++++++++++++ integrations/googleanalytics/src/index.ts | 2 + 3 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 integrations/googleanalytics/src/components.tsx diff --git a/integrations/googleanalytics/gitbook-manifest.yaml b/integrations/googleanalytics/gitbook-manifest.yaml index cc77c2d2e..c103347e6 100644 --- a/integrations/googleanalytics/gitbook-manifest.yaml +++ b/integrations/googleanalytics/gitbook-manifest.yaml @@ -53,10 +53,4 @@ categories: - analytics configurations: site: - properties: - tracking_id: - type: string - title: Tracking ID - description: Look for this in your Google Analytics account. - required: - - tracking_id + componentId: config diff --git a/integrations/googleanalytics/src/components.tsx b/integrations/googleanalytics/src/components.tsx new file mode 100644 index 000000000..bf3d76c3a --- /dev/null +++ b/integrations/googleanalytics/src/components.tsx @@ -0,0 +1,83 @@ +import { createComponent, RuntimeContext, RuntimeEnvironment } from '@gitbook/runtime'; + +type GARuntimeEnvironment = RuntimeEnvironment<{}, GASiteInstallationConfiguration>; + +type GARuntimeContext = RuntimeContext<GARuntimeEnvironment>; + +type GASiteInstallationConfiguration = { + tracking_id?: string; +}; + +type GAState = GASiteInstallationConfiguration; + +type GAProps = { + siteInstallation: { + configuration?: GASiteInstallationConfiguration; + }; +}; + +export type GAAction = { action: 'save.config' }; + +export const configBlock = createComponent<GAProps, GAState, GAAction, GARuntimeContext>({ + componentId: 'config', + initialState: (props) => { + const siteInstallation = props.siteInstallation; + return { + tracking_id: siteInstallation.configuration?.tracking_id ?? '', + }; + }, + action: async (element, action, context) => { + switch (action.action) { + case 'save.config': + const { api, environment } = context; + const siteInstallation = environment.siteInstallation; + if (!siteInstallation) { + throw Error('No site installation found'); + } + + const configurationBody = { + ...siteInstallation.configuration, + tracking_id: element.state.tracking_id, + }; + await api.integrations.updateIntegrationSiteInstallation( + siteInstallation.integration, + siteInstallation.installation, + siteInstallation.site, + { + configuration: { + ...configurationBody, + }, + }, + ); + return element; + } + }, + render: async () => { + return ( + <configuration> + <input + label="Client ID" + hint={<text>The unique identifier of your GA app client.</text>} + element={<textinput state="tracking_id" placeholder="Tracking ID" />} + /> + + <divider /> + <input + label="" + hint="" + element={ + <button + style="primary" + disabled={false} + label="Save" + tooltip="Save configuration" + onPress={{ + action: 'save.config', + }} + /> + } + /> + </configuration> + ); + }, +}); diff --git a/integrations/googleanalytics/src/index.ts b/integrations/googleanalytics/src/index.ts index 40a6dfb7d..8adef5b2f 100644 --- a/integrations/googleanalytics/src/index.ts +++ b/integrations/googleanalytics/src/index.ts @@ -5,6 +5,7 @@ import { RuntimeEnvironment, } from '@gitbook/runtime'; +import { configBlock } from './components'; import script from './script.raw.js'; type GARuntimeContext = RuntimeContext< @@ -35,4 +36,5 @@ export const handleFetchEvent: FetchPublishScriptEventCallback = async ( export default createIntegration<GARuntimeContext>({ fetch_published_script: handleFetchEvent, + components: [configBlock], }); From cdf7a8af27213fadb582c3b5dd3225b5feb0498a Mon Sep 17 00:00:00 2001 From: Valentino Hudhra <v.hudhra@gmail.com> Date: Mon, 7 Oct 2024 17:30:16 +0200 Subject: [PATCH 2/6] format --- .../googleanalytics/src/components.tsx | 126 +++++++++--------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/integrations/googleanalytics/src/components.tsx b/integrations/googleanalytics/src/components.tsx index bf3d76c3a..1ee76982b 100644 --- a/integrations/googleanalytics/src/components.tsx +++ b/integrations/googleanalytics/src/components.tsx @@ -5,79 +5,79 @@ type GARuntimeEnvironment = RuntimeEnvironment<{}, GASiteInstallationConfigurati type GARuntimeContext = RuntimeContext<GARuntimeEnvironment>; type GASiteInstallationConfiguration = { - tracking_id?: string; + tracking_id?: string; }; type GAState = GASiteInstallationConfiguration; type GAProps = { - siteInstallation: { - configuration?: GASiteInstallationConfiguration; - }; + siteInstallation: { + configuration?: GASiteInstallationConfiguration; + }; }; export type GAAction = { action: 'save.config' }; export const configBlock = createComponent<GAProps, GAState, GAAction, GARuntimeContext>({ - componentId: 'config', - initialState: (props) => { - const siteInstallation = props.siteInstallation; - return { - tracking_id: siteInstallation.configuration?.tracking_id ?? '', - }; - }, - action: async (element, action, context) => { - switch (action.action) { - case 'save.config': - const { api, environment } = context; - const siteInstallation = environment.siteInstallation; - if (!siteInstallation) { - throw Error('No site installation found'); - } + componentId: 'config', + initialState: (props) => { + const siteInstallation = props.siteInstallation; + return { + tracking_id: siteInstallation.configuration?.tracking_id ?? '', + }; + }, + action: async (element, action, context) => { + switch (action.action) { + case 'save.config': + const { api, environment } = context; + const siteInstallation = environment.siteInstallation; + if (!siteInstallation) { + throw Error('No site installation found'); + } - const configurationBody = { - ...siteInstallation.configuration, - tracking_id: element.state.tracking_id, - }; - await api.integrations.updateIntegrationSiteInstallation( - siteInstallation.integration, - siteInstallation.installation, - siteInstallation.site, - { - configuration: { - ...configurationBody, - }, - }, - ); - return element; - } - }, - render: async () => { - return ( - <configuration> - <input - label="Client ID" - hint={<text>The unique identifier of your GA app client.</text>} - element={<textinput state="tracking_id" placeholder="Tracking ID" />} - /> + const configurationBody = { + ...siteInstallation.configuration, + tracking_id: element.state.tracking_id, + }; + await api.integrations.updateIntegrationSiteInstallation( + siteInstallation.integration, + siteInstallation.installation, + siteInstallation.site, + { + configuration: { + ...configurationBody, + }, + }, + ); + return element; + } + }, + render: async () => { + return ( + <configuration> + <input + label="Client ID" + hint={<text>The unique identifier of your GA app client.</text>} + element={<textinput state="tracking_id" placeholder="Tracking ID" />} + /> - <divider /> - <input - label="" - hint="" - element={ - <button - style="primary" - disabled={false} - label="Save" - tooltip="Save configuration" - onPress={{ - action: 'save.config', - }} - /> - } - /> - </configuration> - ); - }, + <divider /> + <input + label="" + hint="" + element={ + <button + style="primary" + disabled={false} + label="Save" + tooltip="Save configuration" + onPress={{ + action: 'save.config', + }} + /> + } + /> + </configuration> + ); + }, }); From d5f65fc0f733bf7c6eb1a5d112c6508a763391bf Mon Sep 17 00:00:00 2001 From: Valentino Hudhra <v.hudhra@gmail.com> Date: Fri, 11 Oct 2024 09:49:54 +0200 Subject: [PATCH 3/6] Render outputs commit --- packages/runtime/src/components.ts | 51 ++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/packages/runtime/src/components.ts b/packages/runtime/src/components.ts index 64e4b0735..963c30c1b 100644 --- a/packages/runtime/src/components.ts +++ b/packages/runtime/src/components.ts @@ -1,9 +1,9 @@ import { ContentKitBlock, - UIRenderEvent, - ContentKitRenderOutput, ContentKitContext, ContentKitDefaultAction, + ContentKitRenderOutput, + UIRenderEvent, } from '@gitbook/api'; import { RuntimeCallback, RuntimeContext } from './context'; @@ -67,15 +67,19 @@ export function createComponent< * Initial state of the component. */ initialState?: - | State - | ((props: Props, renderContext: ContentKitContext, context: Context) => State); + | State + | ((props: Props, renderContext: ContentKitContext, context: Context) => State); /** * Callback to handle a dispatched action. */ action?: RuntimeCallback< [ComponentInstance<Props, State>, ComponentAction<Action>], - Promise<{ props?: Props; state?: State } | undefined>, + Promise< + | { type?: 'element'; props?: Props; state?: State } + | { type: 'complete'; returnValue?: PlainObject } + | undefined + >, Context >; @@ -112,29 +116,42 @@ export function createComponent< dynamicState: (key) => ({ $state: key }), }; + const wrapResponse = (output: ContentKitRenderOutput) => { + return new Response(JSON.stringify(output), { + headers: { + 'Content-Type': 'application/json', + ...(cache + ? { + // @ts-ignore - I'm not sure how to fix this one with TS + 'Cache-Control': `max-age=${cache.maxAge}`, + } + : {}), + }, + }); + }; + if (action && component.action) { - instance = { ...instance, ...(await component.action(instance, action, context)) }; + const actionResult = await component.action(instance, action, context); + + // If the action is complete, return the result directly. No need to render the component. + if (actionResult?.type === 'complete') { + return wrapResponse(actionResult); + } + + instance = { ...instance, ...actionResult }; } const element = await component.render(instance, context); const output: ContentKitRenderOutput = { + // for backward compatibility always default to 'element' + type: 'element', state: instance.state, props: instance.props, element, }; - return new Response(JSON.stringify(output), { - headers: { - 'Content-Type': 'application/json', - ...(cache - ? { - // @ts-ignore - I'm not sure how to fix this one with TS - 'Cache-Control': `max-age=${cache.maxAge}`, - } - : {}), - }, - }); + return wrapResponse(output); }, }; } From 81265a6b7d762cd9e6f0f125792839252b6338ff Mon Sep 17 00:00:00 2001 From: Valentino Hudhra <v.hudhra@gmail.com> Date: Fri, 11 Oct 2024 13:58:44 +0200 Subject: [PATCH 4/6] revert runtime changes --- packages/runtime/src/components.ts | 51 ++++++++++-------------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/packages/runtime/src/components.ts b/packages/runtime/src/components.ts index 963c30c1b..64e4b0735 100644 --- a/packages/runtime/src/components.ts +++ b/packages/runtime/src/components.ts @@ -1,9 +1,9 @@ import { ContentKitBlock, + UIRenderEvent, + ContentKitRenderOutput, ContentKitContext, ContentKitDefaultAction, - ContentKitRenderOutput, - UIRenderEvent, } from '@gitbook/api'; import { RuntimeCallback, RuntimeContext } from './context'; @@ -67,19 +67,15 @@ export function createComponent< * Initial state of the component. */ initialState?: - | State - | ((props: Props, renderContext: ContentKitContext, context: Context) => State); + | State + | ((props: Props, renderContext: ContentKitContext, context: Context) => State); /** * Callback to handle a dispatched action. */ action?: RuntimeCallback< [ComponentInstance<Props, State>, ComponentAction<Action>], - Promise< - | { type?: 'element'; props?: Props; state?: State } - | { type: 'complete'; returnValue?: PlainObject } - | undefined - >, + Promise<{ props?: Props; state?: State } | undefined>, Context >; @@ -116,42 +112,29 @@ export function createComponent< dynamicState: (key) => ({ $state: key }), }; - const wrapResponse = (output: ContentKitRenderOutput) => { - return new Response(JSON.stringify(output), { - headers: { - 'Content-Type': 'application/json', - ...(cache - ? { - // @ts-ignore - I'm not sure how to fix this one with TS - 'Cache-Control': `max-age=${cache.maxAge}`, - } - : {}), - }, - }); - }; - if (action && component.action) { - const actionResult = await component.action(instance, action, context); - - // If the action is complete, return the result directly. No need to render the component. - if (actionResult?.type === 'complete') { - return wrapResponse(actionResult); - } - - instance = { ...instance, ...actionResult }; + instance = { ...instance, ...(await component.action(instance, action, context)) }; } const element = await component.render(instance, context); const output: ContentKitRenderOutput = { - // for backward compatibility always default to 'element' - type: 'element', state: instance.state, props: instance.props, element, }; - return wrapResponse(output); + return new Response(JSON.stringify(output), { + headers: { + 'Content-Type': 'application/json', + ...(cache + ? { + // @ts-ignore - I'm not sure how to fix this one with TS + 'Cache-Control': `max-age=${cache.maxAge}`, + } + : {}), + }, + }); }, }; } From 9d94330bfd03b32b324e8e26fcff1fbf9d8e73ea Mon Sep 17 00:00:00 2001 From: Valentino Hudhra <v.hudhra@gmail.com> Date: Fri, 11 Oct 2024 14:03:29 +0200 Subject: [PATCH 5/6] changeset --- .changeset/slow-keys-confess.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/slow-keys-confess.md diff --git a/.changeset/slow-keys-confess.md b/.changeset/slow-keys-confess.md new file mode 100644 index 000000000..b69c86ae7 --- /dev/null +++ b/.changeset/slow-keys-confess.md @@ -0,0 +1,5 @@ +--- +'@gitbook/integration-googleanalytics': minor +--- + +Update Google Analytics integration configuration From 65a7006118ec4c04a9fc989db187dd12f21d5397 Mon Sep 17 00:00:00 2001 From: Valentino Hudhra <v.hudhra@gmail.com> Date: Fri, 11 Oct 2024 14:18:27 +0200 Subject: [PATCH 6/6] removed scopes --- integrations/googleanalytics/gitbook-manifest.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/integrations/googleanalytics/gitbook-manifest.yaml b/integrations/googleanalytics/gitbook-manifest.yaml index c103347e6..01b9d99cd 100644 --- a/integrations/googleanalytics/gitbook-manifest.yaml +++ b/integrations/googleanalytics/gitbook-manifest.yaml @@ -13,8 +13,6 @@ script: ./src/index.ts # The following scope(s) are available only to GitBook Staff # See https://developer.gitbook.com/integrations/configurations#scopes scopes: - - space:script:inject - - space:script:cookies - site:script:inject - site:script:cookies contentSecurityPolicy: