From 000ba485ea38a49071e892b98bdeff43e3145e41 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 15:44:11 +0000 Subject: [PATCH 01/58] chore(deps): bump next from 15.1.7 to 15.2.3 in /examples Bumps [next](https://github.com/vercel/next.js) from 15.1.7 to 15.2.3. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v15.1.7...v15.2.3) --- updated-dependencies: - dependency-name: next dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- examples/yarn.lock | 143 +++++++++++++++++++++++---------------------- 1 file changed, 74 insertions(+), 69 deletions(-) diff --git a/examples/yarn.lock b/examples/yarn.lock index f552196c00..242aa59880 100644 --- a/examples/yarn.lock +++ b/examples/yarn.lock @@ -383,10 +383,10 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@next/env@15.1.7": - version "15.1.7" - resolved "https://registry.yarnpkg.com/@next/env/-/env-15.1.7.tgz#14e2678f893aec50ff2dcb7a6665092fb9e1263d" - integrity sha512-d9jnRrkuOH7Mhi+LHav2XW91HOgTAWHxjMPkXMGBc9B2b7614P7kjt8tAplRvJpbSt4nbO1lugcT/kAaWzjlLQ== +"@next/env@15.2.3": + version "15.2.3" + resolved "https://registry.yarnpkg.com/@next/env/-/env-15.2.3.tgz#037ee37c4d61fcbdbb212694cc33d7dcf6c7975a" + integrity sha512-a26KnbW9DFEUsSxAxKBORR/uD9THoYoKbkpFywMN/AFvboTt94b8+g/07T8J6ACsdLag8/PDU60ov4rPxRAixw== "@next/eslint-plugin-next@15.0.3": version "15.0.3" @@ -395,45 +395,45 @@ dependencies: fast-glob "3.3.1" -"@next/swc-darwin-arm64@15.1.7": - version "15.1.7" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.1.7.tgz#ecc6eacf174df36a6c73f7c319ed864ec6e08079" - integrity sha512-hPFwzPJDpA8FGj7IKV3Yf1web3oz2YsR8du4amKw8d+jAOHfYHYFpMkoF6vgSY4W6vB29RtZEklK9ayinGiCmQ== - -"@next/swc-darwin-x64@15.1.7": - version "15.1.7" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.1.7.tgz#d25b4c131d13439ea4b263dbcd0fd518a835f31c" - integrity sha512-2qoas+fO3OQKkU0PBUfwTiw/EYpN+kdAx62cePRyY1LqKtP09Vp5UcUntfZYajop5fDFTjSxCHfZVRxzi+9FYQ== - -"@next/swc-linux-arm64-gnu@15.1.7": - version "15.1.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.1.7.tgz#b19abc7b56918042b5309f55f7010e7932ee4967" - integrity sha512-sKLLwDX709mPdzxMnRIXLIT9zaX2w0GUlkLYQnKGoXeWUhcvpCrK+yevcwCJPdTdxZEUA0mOXGLdPsGkudGdnA== - -"@next/swc-linux-arm64-musl@15.1.7": - version "15.1.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.1.7.tgz#cb2ac35d3024e9d46ce0d4ff03bf491e0773519f" - integrity sha512-zblK1OQbQWdC8fxdX4fpsHDw+VSpBPGEUX4PhSE9hkaWPrWoeIJn+baX53vbsbDRaDKd7bBNcXRovY1hEhFd7w== - -"@next/swc-linux-x64-gnu@15.1.7": - version "15.1.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.1.7.tgz#cf6e338a1fbb1c9b019c158a76a7ab4f143929ce" - integrity sha512-GOzXutxuLvLHFDAPsMP2zDBMl1vfUHHpdNpFGhxu90jEzH6nNIgmtw/s1MDwpTOiM+MT5V8+I1hmVFeAUhkbgQ== - -"@next/swc-linux-x64-musl@15.1.7": - version "15.1.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.1.7.tgz#94c9117ece8e5851e7e6674f12d6b82b435e3a6f" - integrity sha512-WrZ7jBhR7ATW1z5iEQ0ZJfE2twCNSXbpCSaAunF3BKcVeHFADSI/AW1y5Xt3DzTqPF1FzQlwQTewqetAABhZRQ== - -"@next/swc-win32-arm64-msvc@15.1.7": - version "15.1.7" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.1.7.tgz#4947f3b7f41c7347114985bf3c91e2eacddfe124" - integrity sha512-LDnj1f3OVbou1BqvvXVqouJZKcwq++mV2F+oFHptToZtScIEnhNRJAhJzqAtTE2dB31qDYL45xJwrc+bLeKM2Q== - -"@next/swc-win32-x64-msvc@15.1.7": - version "15.1.7" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.1.7.tgz#0adb399deb15291b61be94909a97e0d6ce1f61fa" - integrity sha512-dC01f1quuf97viOfW05/K8XYv2iuBgAxJZl7mbCKEjMgdQl5JjAKJ0D2qMKZCgPWDeFbFT0Q0nYWwytEW0DWTQ== +"@next/swc-darwin-arm64@15.2.3": + version "15.2.3" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.2.3.tgz#2688c185651ef7a16e5642c85048cc4e151159fa" + integrity sha512-uaBhA8aLbXLqwjnsHSkxs353WrRgQgiFjduDpc7YXEU0B54IKx3vU+cxQlYwPCyC8uYEEX7THhtQQsfHnvv8dw== + +"@next/swc-darwin-x64@15.2.3": + version "15.2.3" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.2.3.tgz#3e802259b2c9a4e2ad55ff827f41f775b726fc7d" + integrity sha512-pVwKvJ4Zk7h+4hwhqOUuMx7Ib02u3gDX3HXPKIShBi9JlYllI0nU6TWLbPT94dt7FSi6mSBhfc2JrHViwqbOdw== + +"@next/swc-linux-arm64-gnu@15.2.3": + version "15.2.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.2.3.tgz#315d7b54b89153f125bdc3e40bcb7ccf94ef124b" + integrity sha512-50ibWdn2RuFFkOEUmo9NCcQbbV9ViQOrUfG48zHBCONciHjaUKtHcYFiCwBVuzD08fzvzkWuuZkd4AqbvKO7UQ== + +"@next/swc-linux-arm64-musl@15.2.3": + version "15.2.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.2.3.tgz#a1a458eb7cf19c59d2014ee388a7305e9a77973f" + integrity sha512-2gAPA7P652D3HzR4cLyAuVYwYqjG0mt/3pHSWTCyKZq/N/dJcUAEoNQMyUmwTZWCJRKofB+JPuDVP2aD8w2J6Q== + +"@next/swc-linux-x64-gnu@15.2.3": + version "15.2.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.2.3.tgz#a3cf22eda7601536ccd68e8ba4c1bfb4a1a33460" + integrity sha512-ODSKvrdMgAJOVU4qElflYy1KSZRM3M45JVbeZu42TINCMG3anp7YCBn80RkISV6bhzKwcUqLBAmOiWkaGtBA9w== + +"@next/swc-linux-x64-musl@15.2.3": + version "15.2.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.2.3.tgz#0e33c1224c76aa3078cc2249c80ef583f9d7a943" + integrity sha512-ZR9kLwCWrlYxwEoytqPi1jhPd1TlsSJWAc+H/CJHmHkf2nD92MQpSRIURR1iNgA/kuFSdxB8xIPt4p/T78kwsg== + +"@next/swc-win32-arm64-msvc@15.2.3": + version "15.2.3" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.2.3.tgz#4e0583fb981b931915a9ad22e579f9c9d5b803dd" + integrity sha512-+G2FrDcfm2YDbhDiObDU/qPriWeiz/9cRR0yMWJeTLGGX6/x8oryO3tt7HhodA1vZ8r2ddJPCjtLcpaVl7TE2Q== + +"@next/swc-win32-x64-msvc@15.2.3": + version "15.2.3" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.2.3.tgz#727b90c7dcc2279344115a94b99d93d452956f02" + integrity sha512-gHYS9tc+G2W0ZC8rBL+H6RdtXIyk40uLiaos0yj5US85FNhbFEndMA2nW3z47nzOWiSvXTZ5kBClc3rD0zJg0w== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -3066,11 +3066,11 @@ negotiator@0.6.3: integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== next@^15: - version "15.1.7" - resolved "https://registry.yarnpkg.com/next/-/next-15.1.7.tgz#e814845e7cdb0294aee88ceab0bb962de83e8e6f" - integrity sha512-GNeINPGS9c6OZKCvKypbL8GTsT5GhWPp4DM0fzkXJuXMilOO2EeFxuAY6JZbtk6XIl6Ws10ag3xRINDjSO5+wg== + version "15.2.3" + resolved "https://registry.yarnpkg.com/next/-/next-15.2.3.tgz#1ac803c08076d47eb5b431cb625135616c6bec7e" + integrity sha512-x6eDkZxk2rPpu46E1ZVUWIBhYCLszmUY6fvHBFcbzJ9dD+qRX6vcHusaqqDlnY+VngKzKbAiG2iRCkPbmi8f7w== dependencies: - "@next/env" "15.1.7" + "@next/env" "15.2.3" "@swc/counter" "0.1.3" "@swc/helpers" "0.5.15" busboy "1.6.0" @@ -3078,14 +3078,14 @@ next@^15: postcss "8.4.31" styled-jsx "5.1.6" optionalDependencies: - "@next/swc-darwin-arm64" "15.1.7" - "@next/swc-darwin-x64" "15.1.7" - "@next/swc-linux-arm64-gnu" "15.1.7" - "@next/swc-linux-arm64-musl" "15.1.7" - "@next/swc-linux-x64-gnu" "15.1.7" - "@next/swc-linux-x64-musl" "15.1.7" - "@next/swc-win32-arm64-msvc" "15.1.7" - "@next/swc-win32-x64-msvc" "15.1.7" + "@next/swc-darwin-arm64" "15.2.3" + "@next/swc-darwin-x64" "15.2.3" + "@next/swc-linux-arm64-gnu" "15.2.3" + "@next/swc-linux-arm64-musl" "15.2.3" + "@next/swc-linux-x64-gnu" "15.2.3" + "@next/swc-linux-x64-musl" "15.2.3" + "@next/swc-win32-arm64-msvc" "15.2.3" + "@next/swc-win32-x64-msvc" "15.2.3" sharp "^0.33.5" node-releases@^2.0.19: @@ -3374,16 +3374,7 @@ postcss@8.4.31: picocolors "^1.0.0" source-map-js "^1.0.2" -postcss@^8, postcss@^8.4.43, postcss@^8.4.47: - version "8.5.1" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.1.tgz#e2272a1f8a807fafa413218245630b5db10a3214" - integrity sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ== - dependencies: - nanoid "^3.3.8" - picocolors "^1.1.1" - source-map-js "^1.2.1" - -postcss@^8.4.41: +postcss@^8, postcss@^8.4.41, postcss@^8.4.43, postcss@^8.4.47: version "8.5.3" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.3.tgz#1463b6f1c7fb16fe258736cba29a2de35237eafb" integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A== @@ -3849,8 +3840,16 @@ streamsearch@^1.1.0: resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: - name string-width-cjs +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -3936,8 +3935,14 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: - name strip-ansi-cjs +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== From a2726d028117011b0af99ac4aac9d40f21099bcf Mon Sep 17 00:00:00 2001 From: matt423 Date: Tue, 25 Mar 2025 16:33:54 +0000 Subject: [PATCH 02/58] chore: Update Inkeep to cxkit 0.5.36 Upgrade Inkeep to latest version and apply migration changes. --- .env.example | 2 - .../create-graphql-schema-customization.ts | 2 - gatsby-config.ts | 2 - .../GlobalLoading/GlobalLoading.tsx | 2 - src/external-scripts/index.ts | 6 +- src/external-scripts/inkeep.test.ts | 12 +- src/external-scripts/inkeep.ts | 201 ++++++++++++------ src/hooks/use-site-metadata.ts | 2 - 8 files changed, 141 insertions(+), 88 deletions(-) diff --git a/.env.example b/.env.example index 5df5a8df0d..a2f969f036 100644 --- a/.env.example +++ b/.env.example @@ -11,8 +11,6 @@ ASSET_PREFIX= INKEEP_CHAT_ENABLED= INKEEP_CHAT_API_KEY= -INKEEP_CHAT_INTEGRATION_ID= -INKEEP_CHAT_ORGANIZATION_ID= INSIGHTS_ENABLED=false INSIGHTS_DEBUG=true diff --git a/data/onCreateNode/create-graphql-schema-customization.ts b/data/onCreateNode/create-graphql-schema-customization.ts index 39e493d1c5..50ef4ee23c 100644 --- a/data/onCreateNode/create-graphql-schema-customization.ts +++ b/data/onCreateNode/create-graphql-schema-customization.ts @@ -51,8 +51,6 @@ export const createSchemaCustomization: GatsbyNode['createSchemaCustomization'] oneTrustTest: String inkeepEnabled: String inkeepApiKey: String - inkeepIntegrationId: String - inkeepOrganizationId: String insightsEnabled: Boolean insightsDebug: Boolean mixpanelApiKey: String diff --git a/gatsby-config.ts b/gatsby-config.ts index 3385e6acfa..3fc16a2060 100644 --- a/gatsby-config.ts +++ b/gatsby-config.ts @@ -26,8 +26,6 @@ export const siteMetadata = { oneTrustTest: process.env.ONE_TRUST_TEST, inkeepEnabled: process.env.INKEEP_CHAT_ENABLED, inkeepApiKey: process.env.INKEEP_CHAT_API_KEY, - inkeepIntegrationId: process.env.INKEEP_CHAT_INTEGRATION_ID, - inkeepOrganizationId: process.env.INKEEP_CHAT_ORGANIZATION_ID, insightsEnabled: !!process.env.INSIGHTS_ENABLED, insightsDebug: process.env.INSIGHTS_DEBUG === 'true', mixpanelApiKey: process.env.MIXPANEL_API_KEY, diff --git a/src/components/GlobalLoading/GlobalLoading.tsx b/src/components/GlobalLoading/GlobalLoading.tsx index be6c6d8268..1de4ee76ce 100644 --- a/src/components/GlobalLoading/GlobalLoading.tsx +++ b/src/components/GlobalLoading/GlobalLoading.tsx @@ -31,8 +31,6 @@ const GlobalLoading: FC = ({ children, template }) => { announcementEnabled inkeepEnabled inkeepApiKey - inkeepIntegrationId - inkeepOrganizationId insightsEnabled insightsDebug mixpanelApiKey diff --git a/src/external-scripts/index.ts b/src/external-scripts/index.ts index 14ed826e4f..9605a2caeb 100644 --- a/src/external-scripts/index.ts +++ b/src/external-scripts/index.ts @@ -26,8 +26,6 @@ type ExternalScriptsData = { headwayAccountId?: string; inkeepEnabled?: string; inkeepApiKey?: string; - inkeepIntegrationId?: string; - inkeepOrganizationId?: string; insightsEnabled?: boolean; }; @@ -37,8 +35,6 @@ const injectScripts = ({ announcementEnabled, inkeepEnabled, inkeepApiKey, - inkeepIntegrationId, - inkeepOrganizationId, }: ExternalScriptsData = {}) => { if (announcementEnabled) { announcement(); @@ -49,7 +45,7 @@ const injectScripts = ({ } if (inkeepEnabled) { - inkeepChat(inkeepApiKey, inkeepIntegrationId, inkeepOrganizationId); + inkeepChat(inkeepApiKey); } if (!document.querySelector('div[data-scripts-loaded="true"]')) { diff --git a/src/external-scripts/inkeep.test.ts b/src/external-scripts/inkeep.test.ts index 04ee44a19a..77ef183f3a 100644 --- a/src/external-scripts/inkeep.test.ts +++ b/src/external-scripts/inkeep.test.ts @@ -11,7 +11,7 @@ describe('inkeepChat', () => { it('does not load Inkeep when configuration is not present', () => { const spy = jest.spyOn(scriptLoader, 'scriptLoader'); - inkeepChat(undefined, 'integrationId', 'organizationId'); + inkeepChat(undefined); expect(spy).not.toHaveBeenCalled(); }); @@ -19,7 +19,7 @@ describe('inkeepChat', () => { it('load Inkeep when configuration is present', () => { const spy = jest.spyOn(scriptLoader, 'scriptLoader'); - inkeepChat('apiKey', 'integrationId', 'organizationId'); + inkeepChat('apiKey'); expect(spy).toHaveBeenCalled(); }); @@ -28,12 +28,12 @@ describe('inkeepChat', () => { let spy; beforeEach(() => { - global.inkeepWidget = { render: jest.fn }; - spy = jest.spyOn(global.inkeepWidget, 'render'); + global.inkeepWidget = { update: jest.fn }; + spy = jest.spyOn(global.inkeepWidget, 'update'); }); it('returns when the user is undefined', () => { - inkeepChatIdentifyUser({ account: { uuid: '123' } }); + inkeepChatIdentifyUser({}); expect(spy).not.toHaveBeenCalled(); }); @@ -41,7 +41,7 @@ describe('inkeepChat', () => { it('sets the Inkeep userId', () => { inkeepChatIdentifyUser({ user: { uuid: '123' } }); - expect(spy).toHaveBeenCalledWith({ baseSettings: { userId: '123' } }); + expect(spy).toHaveBeenCalledWith({ baseSettings: { userProperties: { id: '123' } } }); }); }); }); diff --git a/src/external-scripts/inkeep.ts b/src/external-scripts/inkeep.ts index 90c56bbe81..4ced4c0744 100644 --- a/src/external-scripts/inkeep.ts +++ b/src/external-scripts/inkeep.ts @@ -1,20 +1,50 @@ import { scriptLoader } from './utils'; -const inkeepChat = (apiKey, integrationId, organizationId) => { - if (!(apiKey && integrationId && organizationId)) { +const inkeepChat = (apiKey: string) => { + if (!apiKey) { return; } - scriptLoader(document, 'https://unpkg.com/@inkeep/uikit-js@0.3.19/dist/embed.js', { + scriptLoader(document, 'https://unpkg.com/@inkeep/cxkit-js@0.5.36/dist/embed.js', { defer: true, async: false, type: 'module', + crossOrigin: 'anonymous', onload: () => { - inkeepOnLoad(apiKey, integrationId, organizationId); + inkeepOnLoad(apiKey); }, }); }; +declare global { + interface Window { + HubSpotConversations?: { + widget: { + load: () => void; + open: () => void; + remove: () => void; + }; + on: (event: string, callback: () => void) => void; + off: (event: string, callback: (() => void) | null) => void; + }; + inkeepWidgetConfig?: object; + inkeepWidget?: { + update: (config: object) => void; + }; + Inkeep: { + ChatButton: (config: object) => { + update: (config: object) => void; + }; + SearchBar: ( + element: string, + config: object, + ) => { + update: (config: object) => void; + }; + }; + } +} + const openHubSpotConversations = () => { window.HubSpotConversations?.widget.load(); window.HubSpotConversations?.widget.open(); @@ -26,89 +56,124 @@ const openHubSpotConversations = () => { }; const aiChatSettings = { - aiChatSettings: { - actionButtonLabels: { - getHelpButtonLabel: 'More Help', - }, - shouldShowCopyChatButton: true, - chatSubjectName: 'Ably', - botAvatarSrcUrl: 'https://storage.googleapis.com/organization-image-assets/ably-botAvatarSrcUrl-1721406747144.png', - getHelpCallToActions: [ - { - name: 'Request a meeting', + organizationDisplayName: 'Ably', + aiAssistantAvatar: 'https://storage.googleapis.com/organization-image-assets/ably-botAvatarSrcUrl-1721406747144.png', + getHelpOptions: [ + { + name: 'Request a meeting', + action: { + type: 'open_link', url: 'https://meetings.hubspot.com/cameron-michie/ably-demo', - - type: 'OPEN_LINK', - pinToToolbar: true, - shouldCloseModal: false, }, - { - name: 'Chat with support', - url: 'https://ably.com/support', - icon: { - builtIn: 'IoHelpBuoyOutline', - }, - type: 'INVOKE_CALLBACK', + isPinnedToToolbar: true, + }, + { + name: 'Chat with support', + action: { + type: 'invoke_callback', callback: () => { openHubSpotConversations(); }, - pinToToolbar: true, shouldCloseModal: true, }, - ], - quickQuestions: ['What is a channel?', 'How do I authenticate with Ably?', 'How do I manage user status?'], - }, + isPinnedToToolbar: true, + }, + ], + exampleQuestions: ['What is a channel?', 'How do I authenticate with Ably?', 'How to manage user status?'], + shouldShowCopyChatButton: true, }; -export const inkeepOnLoad = (apiKey, integrationId, organizationId) => { - window.inkeepBase = Inkeep({ +interface SourceItem { + id?: string; + title: string | undefined; + url: string; + description: string | undefined; + breadcrumbs: string[]; + type: string; + contentType?: string; + tag?: string; + tabs?: string[]; +} + +const transformSource = (source: SourceItem) => { + const { url, breadcrumbs } = source; + if (!url) { + return source; + } + try { + const { pathname } = new URL(url); + const urlPatterns = [ + { pattern: '/docs', tabName: 'Docs' }, + { pattern: '/blog', tabName: 'Blog' }, + { pattern: 'faqs.ably.com', tabName: 'FAQs' }, + ]; + + const matchingPattern = urlPatterns.find(({ pattern }) => pathname.startsWith(pattern) || url.includes(pattern)); + + if (matchingPattern) { + const { tabName } = matchingPattern; + return { + ...source, + tabs: [ + [ + tabName, + { + breadcrumbs: breadcrumbs[0] === tabName ? breadcrumbs.slice(1) : breadcrumbs, + }, + ], + ], + }; + } + return source; + } catch (error) { + return source; + } +}; + +const prefilledSearchText = () => { + if (typeof window === 'undefined') { + return ''; + } + + return new URLSearchParams(window.location.search).get('q') || ''; +}; + +const searchSettings = { + defaultQuery: prefilledSearchText(), + placeholder: 'Search', + tabs: ['All', 'Docs', 'Blog', 'FAQs', 'GitHub'], +}; + +export const inkeepOnLoad = (apiKey: string) => { + const baseSettings = { apiKey, - integrationId, - organizationId, - theme: { - components: { - SearchBarTrigger: { - defaultProps: { - size: 'expand', - variant: 'emphasize', - }, - }, - }, - }, - }); + transformSource, + }; - const searchSettings = { - placeholder: 'Search', - tabSettings: { - rootBreadcrumbsToUseAsTabs: ['Docs', 'Blog', 'Ably FAQs'], - }, + const config = { + baseSettings, + label: 'Ask Ably', + aiChatSettings, + searchSettings, }; - window.inkeepWidget = inkeepBase.embed({ - componentType: 'ChatButton', - properties: { - chatButtonType: 'PILL', - chatButtonText: 'Ask Ably', - ...aiChatSettings, - searchSettings, - }, + window.inkeepWidget = window.Inkeep.ChatButton({ + ...config, }); - loadInkeepSearch(searchSettings); + loadInkeepSearch(config); }; -const loadInkeepSearch = (searchSettings) => { +const loadInkeepSearch = (config: object) => { const searchBar = document.getElementById('inkeep-search'); if (!searchBar) { return; } - window.inkeepBase.embed({ - componentType: 'SearchBar', - targetElement: searchBar, - properties: { - searchSettings, - ...aiChatSettings, + window.Inkeep.SearchBar(`#${searchBar.id}`, { + ...config, + modalSettings: { + defaultView: 'SEARCH', }, }); }; @@ -122,9 +187,11 @@ export const inkeepChatIdentifyUser = ({ user }: { user?: InkeepUser }) => { return; } - window.inkeepWidget.render({ + window.inkeepWidget.update({ baseSettings: { - userId: user.uuid, + userProperties: { + id: user.uuid, + }, }, }); }; diff --git a/src/hooks/use-site-metadata.ts b/src/hooks/use-site-metadata.ts index dc5f77ae64..4f360807d5 100644 --- a/src/hooks/use-site-metadata.ts +++ b/src/hooks/use-site-metadata.ts @@ -21,8 +21,6 @@ export const useSiteMetadata = () => { announcementEnabled inkeepEnabled inkeepApiKey - inkeepIntegrationId - inkeepOrganizationId insightsEnabled insightsDebug mixpanelApiKey From e79ecf8f6ca732aad031979fe5c95a4db19a3d4a Mon Sep 17 00:00:00 2001 From: Jamie Henson Date: Fri, 28 Mar 2025 14:30:04 +0000 Subject: [PATCH 03/58] fix: use character encodings for double quotes in subscribe textile to avoid processing --- content/metadata-stats/metadata/subscribe.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/metadata-stats/metadata/subscribe.textile b/content/metadata-stats/metadata/subscribe.textile index e15b4d7a8b..3e536886b8 100644 --- a/content/metadata-stats/metadata/subscribe.textile +++ b/content/metadata-stats/metadata/subscribe.textile @@ -36,7 +36,7 @@ It is never possible to publish or be present on a metachannel, however you can The following is an example of a capability that provides access to subscribe to all metachannels: ```[json] -{"[meta]*":["subscribe"]} +{"[meta]*":["subscribe"]} ``` h2(#connection-lifecycle). Connection lifecycle events From b5fdb2ebb1f3c90d1d5a37c10a420cc0b510d3be Mon Sep 17 00:00:00 2001 From: Jamie Henson Date: Fri, 28 Mar 2025 10:27:52 +0000 Subject: [PATCH 04/58] fix: cap redoc max width to viewport --- src/components/Redoc/Loader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Redoc/Loader.tsx b/src/components/Redoc/Loader.tsx index 3c1ca638a4..092539ebdd 100644 --- a/src/components/Redoc/Loader.tsx +++ b/src/components/Redoc/Loader.tsx @@ -57,7 +57,7 @@ export const Loader = ({ specUrl }: { specUrl: string }) => { <> {specUrl ? ( -
+
) : (
Missing {specUrl} metadata From ee739e98b11a29853942cd321ac94f164100eb53 Mon Sep 17 00:00:00 2001 From: Jamie Henson Date: Fri, 28 Mar 2025 11:12:15 +0000 Subject: [PATCH 05/58] fix: modify code block truncation to only match real api key structure --- src/components/blocks/software/Code/MultilineCodeContent.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/blocks/software/Code/MultilineCodeContent.tsx b/src/components/blocks/software/Code/MultilineCodeContent.tsx index 62336d223c..aa5af7a72d 100644 --- a/src/components/blocks/software/Code/MultilineCodeContent.tsx +++ b/src/components/blocks/software/Code/MultilineCodeContent.tsx @@ -7,6 +7,7 @@ import languagesRegistry from '@ably/ui/core/utils/syntax-highlighter-registry'; registerDefaultLanguages(languagesRegistry); const TRUNCATION_CHARACTER_THRESHOLD = 20; +const truncationRegex = new RegExp(`^[A-Za-z0-9_-]{6}.[A-Za-z0-9_-]{6}:[A-Za-z0-9_-]{43}$`, 'gu'); const chooseString = (condition: boolean, firstString: string, secondString: string) => condition ? firstString : secondString; @@ -48,8 +49,8 @@ export const MultilineCodeContent = ({ contentWithObfuscatedKey, ); - const truncatedContent = renderedContent.replace( - new RegExp(`'([a-zA-Z0-9\\p{P}]{${TRUNCATION_CHARACTER_THRESHOLD},})'`, 'gu'), + const truncatedContent = renderedContent.replaceAll( + truncationRegex, (_, p1) => `'${truncate({ length: TRUNCATION_CHARACTER_THRESHOLD }, p1)}'`, ); From 7494c39571400a1e8ab40e86269f5e4ff26ffbf4 Mon Sep 17 00:00:00 2001 From: Jamie Henson Date: Tue, 25 Feb 2025 17:09:18 +0000 Subject: [PATCH 06/58] feat: expose current page product and language in layout context --- .../LanguageButton/LanguageButton.tsx | 8 +-- src/components/Layout/utils/nav.ts | 13 +++- src/components/blocks/software/Pre.tsx | 7 ++- src/contexts/layout-context.tsx | 60 +++++++++++++++++-- 4 files changed, 75 insertions(+), 13 deletions(-) diff --git a/src/components/LanguageButton/LanguageButton.tsx b/src/components/LanguageButton/LanguageButton.tsx index 3f7c53f277..455d9a1b18 100644 --- a/src/components/LanguageButton/LanguageButton.tsx +++ b/src/components/LanguageButton/LanguageButton.tsx @@ -4,14 +4,14 @@ import cn from '@ably/ui/core/utils/cn'; import { createLanguageHrefFromDefaults, getLanguageDefaults, getTrimmedLanguage } from 'src/components'; import { LanguageNavigationComponentProps } from '../Menu/LanguageNavigation'; import { button, isActive } from '../Menu/MenuItemButton/MenuItemButton.module.css'; -import { usePageLanguage } from 'src/contexts'; import { languageInfo } from 'src/data/languages'; import { LanguageKey } from 'src/data/languages/types'; +import { useLayoutContext } from 'src/contexts/layout-context'; const LanguageButton: FC = ({ language, selectedLocalLanguage }) => { - const { currentLanguage: pageLanguage, setPreferredLanguage } = usePageLanguage(); + const { activePage, setLanguage } = useLayoutContext(); const selectedLanguage = getTrimmedLanguage(language); - const { isLanguageDefault, isPageLanguageDefault } = getLanguageDefaults(selectedLanguage, pageLanguage); + const { isLanguageDefault, isPageLanguageDefault } = getLanguageDefaults(selectedLanguage, activePage.language); /* separate the isLanguageActive variable because we will pass a pageLanguage value that is not always from the useContext(PageLanguageContext), so if the useContext(PageLanguageContext) is not present in the languages we will pass the first language of the languages @@ -23,7 +23,7 @@ const LanguageButton: FC = ({ language, select const href = createLanguageHrefFromDefaults(isPageLanguageDefault, isLanguageDefault, selectedLanguage); if (!isPageLanguageDefault) { - setPreferredLanguage(language); + setLanguage(language); } navigate(href); }; diff --git a/src/components/Layout/utils/nav.ts b/src/components/Layout/utils/nav.ts index 0cf19c92cf..133a792389 100644 --- a/src/components/Layout/utils/nav.ts +++ b/src/components/Layout/utils/nav.ts @@ -4,10 +4,17 @@ import { HEADER_HEIGHT, componentMaxHeight } from '@ably/ui/core/utils/heights'; import { ProductData, ProductKey } from 'src/data/types'; import { NavProductContent, NavProductPage, NavProductPages } from 'src/data/nav/types'; import { LanguageKey } from 'src/data/languages/types'; +import { DEFAULT_LANGUAGE } from 'src/contexts/layout-context'; export type PageTreeNode = { index: number; page: NavProductPage }; -export type ActivePage = { tree: PageTreeNode[]; page: NavProductPage; languages: LanguageKey[] }; +export type ActivePage = { + tree: PageTreeNode[]; + page: NavProductPage; + languages: LanguageKey[]; + language: LanguageKey; + product: ProductKey | null; +}; /** * Determines the active page based on the provided target link. @@ -64,6 +71,8 @@ export const determineActivePage = (data: ProductData, targetLink: string): Acti tree: [{ index: Object.keys(data).indexOf(key), page: { name, link } }], page: { name, link }, languages: [], + language: DEFAULT_LANGUAGE, + product: key, }; } @@ -94,7 +103,7 @@ export const determineActivePage = (data: ProductData, targetLink: string): Acti data[key].nav[apiResult ? 'api' : 'content'], ); - return { tree, page: page?.[0] as NavProductPage, languages: [] }; + return { tree, page: page?.[0] as NavProductPage, languages: [], language: DEFAULT_LANGUAGE, product: key }; } } } diff --git a/src/components/blocks/software/Pre.tsx b/src/components/blocks/software/Pre.tsx index b3e04a5324..b3efe293af 100644 --- a/src/components/blocks/software/Pre.tsx +++ b/src/components/blocks/software/Pre.tsx @@ -16,9 +16,9 @@ import { isString, every, reduce } from 'lodash/fp'; import { MultilineCodeContent } from './Code/MultilineCodeContent'; import { isArray, isEmpty } from 'lodash'; import { getTrimmedLanguage } from 'src/components/common'; -import { usePageLanguage } from 'src/contexts'; import { languageLabel } from 'src/data/languages'; import { LanguageKey } from 'src/data/languages/types'; +import { useLayoutContext } from 'src/contexts/layout-context'; type PreProps = HtmlComponentProps<'pre'> & { language: string; @@ -53,7 +53,10 @@ const Pre = ({ restAltData, attribs, }: PreProps): ReactElement => { - const { currentLanguage: pageLanguage } = usePageLanguage(); + const { activePage } = useLayoutContext(); + const pageLanguage = activePage.language; + + console.log('languages', languages, pageLanguage); /* selectedInterfaceTab useState */ const [selectedSDKInterfaceTab, setSelectedSDKInterfaceTab] = useState(DEFAULT_PREFERRED_INTERFACE); diff --git a/src/contexts/layout-context.tsx b/src/contexts/layout-context.tsx index fb47d935ae..79aa23fcdb 100644 --- a/src/contexts/layout-context.tsx +++ b/src/contexts/layout-context.tsx @@ -1,40 +1,89 @@ -import React, { createContext, PropsWithChildren, useContext, useMemo, useState } from 'react'; +import React, { createContext, PropsWithChildren, useContext, useEffect, useMemo, useState } from 'react'; import { useLocation } from '@reach/router'; import { ActivePage, determineActivePage } from 'src/components/Layout/utils/nav'; import { productData } from 'src/data'; import { NavProduct } from 'src/data/nav/types'; import { ProductData, ProductKey } from 'src/data/types'; import { LanguageKey } from 'src/data/languages/types'; +import { languageData } from 'src/data/languages'; /** * LayoutContext * * activePage - The navigation tree that leads to the current page, and a list of languages referenced on the page. * products - List of products with their navigation data. + * setLanguages - Set the possible languages for the current page. + * setLanguage - Set the active language for the current page. */ +export const DEFAULT_LANGUAGE = 'javascript'; + const LayoutContext = createContext<{ activePage: ActivePage; products: [ProductKey, NavProduct][]; setLanguages: (languages: LanguageKey[]) => void; + setLanguage: (language: LanguageKey) => void; }>({ - activePage: { tree: [], page: { name: '', link: '' }, languages: [] }, + activePage: { + tree: [], + page: { name: '', link: '' }, + languages: [], + language: DEFAULT_LANGUAGE, + product: null, + }, products: [], setLanguages: (languages) => { console.warn('setLanguages called without a provider', languages); }, + setLanguage: (language) => { + console.warn('setLanguage called without a provider', language); + }, }); export const LayoutProvider: React.FC = ({ children }) => { const location = useLocation(); const [languages, setLanguages] = useState([]); + const [language, setLanguage] = useState(DEFAULT_LANGUAGE); + + useEffect(() => { + const languagesSet = new Set(); + + document.querySelectorAll('.docs-language-navigation').forEach((element) => { + const languages = element.getAttribute('data-languages'); + if (languages) { + languages.split(',').forEach((language) => languagesSet.add(language as LanguageKey)); + } + }); + + setLanguages(Array.from(languagesSet)); + }, [location.pathname]); const activePage = useMemo(() => { const activePageData = determineActivePage(productData, location.pathname); return activePageData - ? { ...activePageData, languages: activePageData.page.languages ?? languages } - : { tree: [], page: { name: '', link: '' }, languages: [] }; - }, [location.pathname, languages]); + ? { + ...activePageData, + languages: activePageData.page.languages ?? languages, + language, + } + : { + tree: [], + page: { name: '', link: '' }, + languages: [], + language, + product: null, + }; + }, [location.pathname, languages, language]); + + useEffect(() => { + const params = new URLSearchParams(location.search); + + if (params.get('lang')) { + setLanguage(params.get('lang') as LanguageKey); + } else if (activePage.product) { + setLanguage(Object.keys(languageData[activePage.product])[0]); + } + }, [location.search, activePage.product]); const products = useMemo( () => @@ -51,6 +100,7 @@ export const LayoutProvider: React.FC = ({ children }) => { activePage, products, setLanguages, + setLanguage, }} > {children} From 217bf69e569b349fce446b1edbd174ae549e5186 Mon Sep 17 00:00:00 2001 From: Jamie Henson Date: Fri, 28 Mar 2025 12:42:55 +0000 Subject: [PATCH 07/58] chore: remove PageLanguage context --- .../LanguageButton/LanguageButton.tsx | 2 +- src/components/Link/LanguageLink.tsx | 7 +- .../LanguageNavigation/LanguageNavigation.tsx | 9 +- .../blocks/Html/LinkableHtmlBlock.js | 10 +- .../dividers/ApiReferenceDiv.tsx | 8 +- .../dividers/ApiReferenceSpan.tsx | 6 +- src/components/blocks/list/Dl/DlWrapper.tsx | 11 +-- src/components/blocks/software/Pre.tsx | 2 - .../ConditionalChildrenLanguageDisplay.js | 16 ++-- src/contexts/index.ts | 1 - src/contexts/page-language-context.tsx | 64 ------------- src/pages/docs/index.tsx | 7 +- src/templates/base-template.tsx | 92 ++----------------- 13 files changed, 46 insertions(+), 189 deletions(-) delete mode 100644 src/contexts/page-language-context.tsx diff --git a/src/components/LanguageButton/LanguageButton.tsx b/src/components/LanguageButton/LanguageButton.tsx index 455d9a1b18..ff3413878c 100644 --- a/src/components/LanguageButton/LanguageButton.tsx +++ b/src/components/LanguageButton/LanguageButton.tsx @@ -17,7 +17,7 @@ const LanguageButton: FC = ({ language, select so if the useContext(PageLanguageContext) is not present in the languages we will pass the first language of the languages eg: selected global language: PHP but the languages are: [js, ruby] so it will pass js now as php is not present */ - const { isLanguageActive } = getLanguageDefaults(selectedLanguage, selectedLocalLanguage); + const isLanguageActive = activePage.language === selectedLanguage; const handleClick = () => { const href = createLanguageHrefFromDefaults(isPageLanguageDefault, isLanguageDefault, selectedLanguage); diff --git a/src/components/Link/LanguageLink.tsx b/src/components/Link/LanguageLink.tsx index 93e9c9cf55..0c082cd0fc 100644 --- a/src/components/Link/LanguageLink.tsx +++ b/src/components/Link/LanguageLink.tsx @@ -1,13 +1,12 @@ import { Link } from 'gatsby'; -import { usePageLanguage } from '../../contexts/page-language-context'; import { getLanguageDefaults } from '../common/language-defaults'; import { languageLabel } from 'src/data/languages'; import { LanguageKey } from 'src/data/languages/types'; - +import { useLayoutContext } from 'src/contexts/layout-context'; const LanguageLink = ({ language }: { language: LanguageKey }) => { - const { currentLanguage: pageLanguage } = usePageLanguage(); + const { activePage } = useLayoutContext(); - const { isLanguageDefault, isPageLanguageDefault } = getLanguageDefaults(language, pageLanguage); + const { isLanguageDefault, isPageLanguageDefault } = getLanguageDefaults(language, activePage.language); const href = isPageLanguageDefault ? `./language/${language}` : `../../${isLanguageDefault ? '' : `language/${language}`}`; diff --git a/src/components/Menu/LanguageNavigation/LanguageNavigation.tsx b/src/components/Menu/LanguageNavigation/LanguageNavigation.tsx index 19cb61aee2..b5fbc503d6 100644 --- a/src/components/Menu/LanguageNavigation/LanguageNavigation.tsx +++ b/src/components/Menu/LanguageNavigation/LanguageNavigation.tsx @@ -1,5 +1,4 @@ import { Dispatch, FunctionComponent as FC, SetStateAction } from 'react'; -import { usePageLanguage } from 'src/contexts'; import { SingleValue } from 'react-select'; import { navigate } from 'gatsby'; @@ -15,6 +14,7 @@ import { Select, } from 'src/components'; import cn from '@ably/ui/core/utils/cn'; +import { useLayoutContext } from 'src/contexts/layout-context'; export interface LanguageNavigationComponentProps { language: string; @@ -65,12 +65,13 @@ const LanguageNavigation = ({ setSelectedSDKInterfaceTab, setPreviousSDKInterfaceTab, }: LanguageNavigationProps) => { - const { currentLanguage: pageLanguage, setPreferredLanguage } = usePageLanguage(); - const selectedPageLanguage = pageLanguage === DEFAULT_LANGUAGE ? DEFAULT_PREFERRED_LANGUAGE : pageLanguage; + const { activePage, setLanguage } = useLayoutContext(); + const selectedPageLanguage = + activePage.language === DEFAULT_LANGUAGE ? DEFAULT_PREFERRED_LANGUAGE : activePage.language; const options = items.map((item) => ({ label: item.content, value: item.props.language })); const value = options.find((option) => option.value === selectedPageLanguage); - const onSelectChange = changePageOnSelect(pageLanguage, setPreferredLanguage); + const onSelectChange = changePageOnSelect(activePage.language, setLanguage); const isSDKInterFacePresent = allListOfLanguages ? checkIfLanguageHasSDKInterface(allListOfLanguages, SDK_INTERFACES) diff --git a/src/components/blocks/Html/LinkableHtmlBlock.js b/src/components/blocks/Html/LinkableHtmlBlock.js index 3f09254ac2..31b4aa5f9d 100644 --- a/src/components/blocks/Html/LinkableHtmlBlock.js +++ b/src/components/blocks/Html/LinkableHtmlBlock.js @@ -1,13 +1,17 @@ import PropTypes from 'prop-types'; -import { usePageLanguage } from 'src/contexts'; import Html from '.'; import CopyLink from '../wrappers/CopyLink'; import { childOrSelfHasLanguageMatchingPageLanguageOrDefault } from '../wrappers/language-utilities'; +import { useLayoutContext } from 'src/contexts/layout-context'; const LinkableHtmlBlock = (Type, marginBottom, marginTop) => { const InnerBlock = ({ data, attribs }) => { - const { currentLanguage: pageLanguage } = usePageLanguage(); - const shouldShowBlock = childOrSelfHasLanguageMatchingPageLanguageOrDefault(pageLanguage, data, attribs?.lang); + const { activePage } = useLayoutContext(); + const shouldShowBlock = childOrSelfHasLanguageMatchingPageLanguageOrDefault( + activePage.language, + data, + attribs?.lang, + ); if (shouldShowBlock) { return ( diff --git a/src/components/blocks/api-reference/dividers/ApiReferenceDiv.tsx b/src/components/blocks/api-reference/dividers/ApiReferenceDiv.tsx index e569c06f68..d026e1905b 100644 --- a/src/components/blocks/api-reference/dividers/ApiReferenceDiv.tsx +++ b/src/components/blocks/api-reference/dividers/ApiReferenceDiv.tsx @@ -1,8 +1,8 @@ import { matchesLanguageOrDefault } from '../../wrappers/language-utilities'; -import { usePageLanguage } from 'src/contexts'; import Html from '../../Html'; import { HtmlComponentProps, HtmlComponentPropsData } from 'src/components/html-component-props'; import { isString } from 'lodash/fp'; +import { useLayoutContext } from 'src/contexts/layout-context'; const DIV_CONTENTS_WHICH_SHOULD_IGNORE_NORMAL_LANGUAGE_RULES = ['dt', 'dd']; @@ -17,9 +17,11 @@ const childExistsOfIgnoredType = (data: HtmlComponentPropsData) => : false; const ApiReferenceDiv = ({ data, attribs }: HtmlComponentProps<'div'>) => { - const { currentLanguage: pageLanguage } = usePageLanguage(); + const { activePage } = useLayoutContext(); const shouldShowBlock = - attribs?.forcedisplay || matchesLanguageOrDefault(pageLanguage, attribs?.lang) || childExistsOfIgnoredType(data); + attribs?.forcedisplay || + matchesLanguageOrDefault(activePage.language, attribs?.lang) || + childExistsOfIgnoredType(data); return shouldShowBlock ? (
diff --git a/src/components/blocks/api-reference/dividers/ApiReferenceSpan.tsx b/src/components/blocks/api-reference/dividers/ApiReferenceSpan.tsx index 44dc104662..84eeb00892 100644 --- a/src/components/blocks/api-reference/dividers/ApiReferenceSpan.tsx +++ b/src/components/blocks/api-reference/dividers/ApiReferenceSpan.tsx @@ -1,11 +1,11 @@ import { HtmlComponentProps } from 'src/components/html-component-props'; -import { usePageLanguage } from 'src/contexts'; import Html from '../../Html'; import { matchesLanguageOrDefault } from '../../wrappers/language-utilities'; +import { useLayoutContext } from 'src/contexts/layout-context'; const ApiReferenceSpan = ({ data, attribs }: HtmlComponentProps<'span'>) => { - const { currentLanguage: pageLanguage } = usePageLanguage(); - const shouldShowBlock = matchesLanguageOrDefault(pageLanguage, attribs?.lang); + const { activePage } = useLayoutContext(); + const shouldShowBlock = matchesLanguageOrDefault(activePage.language, attribs?.lang); return shouldShowBlock ? ( diff --git a/src/components/blocks/list/Dl/DlWrapper.tsx b/src/components/blocks/list/Dl/DlWrapper.tsx index 9f300d9e49..b5c62a210e 100644 --- a/src/components/blocks/list/Dl/DlWrapper.tsx +++ b/src/components/blocks/list/Dl/DlWrapper.tsx @@ -1,17 +1,16 @@ import { Children, FunctionComponent } from 'react'; -import { DEFAULT_LANGUAGE, DEFAULT_PREFERRED_LANGUAGE } from '../../../../../data/createPages/constants'; -import { usePageLanguage } from 'src/contexts'; +import { useLayoutContext, DEFAULT_LANGUAGE } from 'src/contexts/layout-context'; -export const DlWrapper: FunctionComponent = ({ children }) => { - const { currentLanguage: pageLanguage } = usePageLanguage(); +export const DlWrapper: FunctionComponent = ({ children }) => { + const { activePage } = useLayoutContext(); return Children.map(children, (child) => { const attribs = child?.props?.attribs; if (attribs?.lang) { if ( - (pageLanguage === DEFAULT_LANGUAGE && attribs?.lang === DEFAULT_PREFERRED_LANGUAGE) || - attribs?.lang === pageLanguage || + (activePage.language === DEFAULT_LANGUAGE && attribs?.lang === DEFAULT_LANGUAGE) || + attribs?.lang === activePage.language || attribs?.lang === DEFAULT_LANGUAGE ) { return child; diff --git a/src/components/blocks/software/Pre.tsx b/src/components/blocks/software/Pre.tsx index b3efe293af..8574f1edb2 100644 --- a/src/components/blocks/software/Pre.tsx +++ b/src/components/blocks/software/Pre.tsx @@ -56,8 +56,6 @@ const Pre = ({ const { activePage } = useLayoutContext(); const pageLanguage = activePage.language; - console.log('languages', languages, pageLanguage); - /* selectedInterfaceTab useState */ const [selectedSDKInterfaceTab, setSelectedSDKInterfaceTab] = useState(DEFAULT_PREFERRED_INTERFACE); const [previousSDKInterfaceTab, setPreviousSDKInterfaceTab] = useState(''); diff --git a/src/components/blocks/wrappers/ConditionalChildrenLanguageDisplay.js b/src/components/blocks/wrappers/ConditionalChildrenLanguageDisplay.js index 6ea96b3120..d8cd92b5ee 100644 --- a/src/components/blocks/wrappers/ConditionalChildrenLanguageDisplay.js +++ b/src/components/blocks/wrappers/ConditionalChildrenLanguageDisplay.js @@ -6,10 +6,10 @@ import { } from '../../../../data/createPages/constants'; import { makeGroup, assignPrimary, addToFilter, isIrrelevantForLanguageDisplay } from './language-utilities'; import { isEmpty } from 'lodash'; -import { usePageLanguage } from 'src/contexts'; +import { useLayoutContext } from 'src/contexts/layout-context'; const ConditionalChildrenLanguageDisplay = ({ children }) => { - const { currentLanguage: pageLanguage } = usePageLanguage(); + const { activePage } = useLayoutContext(); let currentGroup = false; const childLanguageGroups = []; @@ -31,7 +31,7 @@ const ConditionalChildrenLanguageDisplay = ({ children }) => { currentGroup = makeGroup(attribs.lang, index, props.data); } else { currentGroup.end = index; - currentGroup = assignPrimary(currentGroup, attribs.lang, pageLanguage, props.data, index); + currentGroup = assignPrimary(currentGroup, attribs.lang, activePage.language, props.data, index); } return; } @@ -59,11 +59,15 @@ const ConditionalChildrenLanguageDisplay = ({ children }) => { key.includes(REALTIME_SDK_INTERFACE), ); const allAltDataRest = Object.entries(relevantGroup.data).filter(([key]) => key.includes(REST_SDK_INTERFACE)); - const realtimeAltData = getCleanedSDKInterfaceAltData(allAltDataRealtime, pageLanguage, REALTIME_SDK_INTERFACE); - const restAltData = getCleanedSDKInterfaceAltData(allAltDataRest, pageLanguage, REST_SDK_INTERFACE); + const realtimeAltData = getCleanedSDKInterfaceAltData( + allAltDataRealtime, + activePage.language, + REALTIME_SDK_INTERFACE, + ); + const restAltData = getCleanedSDKInterfaceAltData(allAltDataRest, activePage.language, REST_SDK_INTERFACE); return React.cloneElement(child, { - pageLanguage, + pageLanguage: activePage.language, languages: relevantGroup.languages, altData: relevantGroup.data, isSDKInterface: !isEmpty(allAltDataRealtime) || !isEmpty(allAltDataRest), diff --git a/src/contexts/index.ts b/src/contexts/index.ts index e046a7bdac..c300d99041 100644 --- a/src/contexts/index.ts +++ b/src/contexts/index.ts @@ -1,3 +1,2 @@ -export * from './page-language-context'; export * from './pathname-context'; export * from './user-context'; diff --git a/src/contexts/page-language-context.tsx b/src/contexts/page-language-context.tsx deleted file mode 100644 index ad861825a3..0000000000 --- a/src/contexts/page-language-context.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { ReactNode, createContext, useCallback, useContext, useEffect, useState } from 'react'; -import { PREFERRED_LANGUAGE_KEY, safeWindow } from 'src/utilities'; -import { DEFAULT_LANGUAGE } from '../../data/createPages/constants'; -import { useUrlParams } from '../hooks'; - -interface PageLanguageContextProps { - currentLanguage: string; - handleCurrentLanguageChange: (lang: string) => void; - getPreferredLanguage: () => string | null | undefined; - setPreferredLanguage: (lang: string) => void; -} - -const initialState = { - currentLanguage: DEFAULT_LANGUAGE, - handleCurrentLanguageChange: () => null, - getPreferredLanguage: () => null, - setPreferredLanguage: () => null, -}; - -export const PageLanguageContext = createContext(initialState); - -export function usePageLanguage() { - const context = useContext(PageLanguageContext); - - if (context === undefined) { - throw new Error('usePageLanguage context must be used within a PageLanguageProvider'); - } - - return context; -} - -interface PageLanguageProviderProps { - children: ReactNode; - search: string; -} - -export const PageLanguageProvider: React.FC = ({ children, search }) => { - const { getParam } = useUrlParams(); - const param = getParam('lang'); - - const getPreferredLanguage = useCallback(() => safeWindow.sessionStorage.getItem(PREFERRED_LANGUAGE_KEY), []); - const setPreferredLanguage = useCallback( - (lang: string) => safeWindow.sessionStorage.setItem(PREFERRED_LANGUAGE_KEY, lang), - [], - ); - - const [currentLanguage, setCurrentLanguage] = useState((param || getPreferredLanguage()) ?? DEFAULT_LANGUAGE); - - useEffect(() => { - setCurrentLanguage((param || getPreferredLanguage()) ?? DEFAULT_LANGUAGE); - }, [param, getPreferredLanguage]); - - useEffect(() => { - setPreferredLanguage(currentLanguage); - }, [currentLanguage, setPreferredLanguage]); - - const handleCurrentLanguageChange = (lang: string) => { - setCurrentLanguage(lang ?? DEFAULT_LANGUAGE); - }; - - const value = { currentLanguage, handleCurrentLanguageChange, getPreferredLanguage, setPreferredLanguage }; - - return {children}; -}; diff --git a/src/pages/docs/index.tsx b/src/pages/docs/index.tsx index a6db694d30..94505d2195 100644 --- a/src/pages/docs/index.tsx +++ b/src/pages/docs/index.tsx @@ -4,7 +4,6 @@ import { Helmet } from 'react-helmet'; import { ImageProps } from 'src/components/Image'; import { useSiteMetadata } from 'src/hooks/use-site-metadata'; import { HomepageContent } from 'src/components/Homepage/HomepageContent'; -import { PageLanguageProvider } from 'src/contexts'; import { pageData } from 'src/data'; export const ABLY_MAIN_WEBSITE = process.env.GATSBY_ABLY_MAIN_WEBSITE ?? 'http://localhost:3000'; @@ -13,10 +12,8 @@ const IndexPage = ({ data: { allFile: { images }, }, - location: { search }, }: { data: { allFile: { images: ImageProps[] } }; - location: Location; }) => { const { sections, meta } = pageData.homepage.content; const openGraphTitle = meta.title ?? 'Ably Realtime Docs'; @@ -41,9 +38,7 @@ const IndexPage = ({ - - - + ); }; diff --git a/src/templates/base-template.tsx b/src/templates/base-template.tsx index e218a43fe3..8ec4b1d59c 100644 --- a/src/templates/base-template.tsx +++ b/src/templates/base-template.tsx @@ -1,18 +1,11 @@ -import { Script, ScriptStrategy, navigate } from 'gatsby'; -import { useEffect, useLayoutEffect, useMemo } from 'react'; +import { Script, ScriptStrategy } from 'gatsby'; +import { useLayoutEffect, useMemo } from 'react'; import { Head } from 'src/components/Head'; import Article from 'src/components/Article'; import PageTitle from 'src/components/PageTitle'; import Html from 'src/components/blocks/Html'; -import { - createLanguageHrefFromDefaults, - getLanguageDefaults, - languageIsUsable, -} from 'src/components/common/language-defaults'; -import { PageLanguageProvider, PathnameContext, usePageLanguage } from 'src/contexts'; - -import { DEFAULT_LANGUAGE, DEFAULT_PREFERRED_LANGUAGE, IGNORED_LANGUAGES } from '../../data/createPages/constants'; +import { PathnameContext } from 'src/contexts'; import { AblyDocument, AblyDocumentMeta, AblyTemplateData, ProductName } from './template-data'; import { useSiteMetadata } from 'src/hooks/use-site-metadata'; import { getMetaTitle } from 'src/components/common/meta-title'; @@ -26,7 +19,6 @@ const getMetaDataDetails = ( ) => (document?.meta?.[prop] ? document.meta[prop] : alternative); const META_DESCRIPTION_FALLBACK = `Ably provides a suite of APIs to build, extend, and deliver powerful digital experiences in realtime. Organizations like Toyota, Bloomberg, HubSpot, and Hopin depend on Ably’s platform to offload the growing complexity of business-critical realtime data synchronization at global scale.`; -const NO_LANGUAGE = 'none'; const META_PRODUCT_FALLBACK = 'pub_sub'; interface ITemplate extends AblyTemplateData { @@ -34,8 +26,8 @@ interface ITemplate extends AblyTemplateData { } const Template = ({ - location: { pathname, hash }, - pageContext: { contentOrderedList, languages, slug, script }, + location: { pathname }, + pageContext: { contentOrderedList, slug, script }, data: { document: ablyDocument }, currentProduct, }: ITemplate) => { @@ -54,15 +46,8 @@ const Template = ({ setLanguages(Array.from(languagesSet)); }, [setLanguages]); - const { - currentLanguage: currentLanguageFromContext, - handleCurrentLanguageChange, - getPreferredLanguage, - } = usePageLanguage(); - const title = getMetaDataDetails(ablyDocument, 'title') as string; const description = getMetaDataDetails(ablyDocument, 'meta_description', META_DESCRIPTION_FALLBACK) as string; - const menuLanguages = getMetaDataDetails(ablyDocument, 'languages', languages) as string[]; const { canonicalUrl } = useSiteMetadata(); const canonical = canonicalUrl(`/docs/${slug}`); @@ -70,20 +55,6 @@ const Template = ({ currentProduct ??= getMetaDataDetails(ablyDocument, 'product', META_PRODUCT_FALLBACK) as ProductName; const metaTitle = getMetaTitle(title, currentProduct) as string; - const filteredLanguages = useMemo( - () => - menuLanguages.includes(NO_LANGUAGE) - ? [] - : menuLanguages - .filter((language) => /^[^,]+$/.test(language)) - .filter((language) => !IGNORED_LANGUAGES.includes(language)), - [menuLanguages], - ); - - const pageLanguage = filteredLanguages.includes(currentLanguageFromContext) - ? currentLanguageFromContext - : DEFAULT_LANGUAGE; - const elements = useMemo( () => contentOrderedList.map( @@ -94,49 +65,6 @@ const Template = ({ [contentOrderedList], ); - useEffect(() => { - if (currentLanguageFromContext === DEFAULT_LANGUAGE || !filteredLanguages.includes(currentLanguageFromContext)) { - const preferredLanguage = getPreferredLanguage(); - - if (filteredLanguages.length <= 1) { - if (pageLanguage === DEFAULT_LANGUAGE) { - return; - } - const href = `${pathname}${hash}`; - navigate(href); - return; - } - - if (preferredLanguage) { - /** - * We must select a language specified in `pageLanguages`, otherwise - * pages such as /docs/api/realtime-sdk/push?lang=ruby will show - * broken content. - */ - const usableLanguage = languageIsUsable(preferredLanguage, filteredLanguages) - ? preferredLanguage - : languageIsUsable(DEFAULT_PREFERRED_LANGUAGE, filteredLanguages) - ? DEFAULT_PREFERRED_LANGUAGE - : filteredLanguages[0]; - const { isLanguageDefault, isPageLanguageDefault } = getLanguageDefaults(usableLanguage, pageLanguage); - - const href = createLanguageHrefFromDefaults(isPageLanguageDefault, isLanguageDefault, usableLanguage); - navigate(href); - return; - } - } else { - handleCurrentLanguageChange(pageLanguage); - } - }, [ - currentLanguageFromContext, - filteredLanguages, - getPreferredLanguage, - handleCurrentLanguageChange, - hash, - pageLanguage, - pathname, - ]); - return ( <> @@ -151,12 +79,4 @@ const Template = ({ ); }; -const TemplateWrapper = (props: ITemplate) => { - return ( - -