From 795d214fd32acbe81915f8dee9012b105ccb1aad Mon Sep 17 00:00:00 2001 From: Fadi George Date: Mon, 22 Sep 2025 12:06:00 -0700 Subject: [PATCH 1/2] add logic to determine which country code to use --- src/page/slidedown/ChannelCaptureContainer.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/page/slidedown/ChannelCaptureContainer.ts b/src/page/slidedown/ChannelCaptureContainer.ts index 7c344e244..4f37ebd60 100644 --- a/src/page/slidedown/ChannelCaptureContainer.ts +++ b/src/page/slidedown/ChannelCaptureContainer.ts @@ -30,6 +30,19 @@ interface TypeSpecificVariablePayload { tabIndex: number; } +export function getCountryCodeFromLocale( + callback: (countryCode: string) => void, +): void { + try { + const locale = navigator.language || 'en-US'; + const parts = locale.split('-'); + const countryCode = parts[parts.length - 1]?.toLowerCase(); // handle things like en-US / zh-Hans-CN + callback(countryCode || 'us'); + } catch (error) { + callback('us'); + } +} + export default class ChannelCaptureContainer { public smsInputFieldIsValid = true; public emailInputFieldIsValid = true; @@ -194,6 +207,8 @@ export default class ChannelCaptureContainer { { autoPlaceholder: 'off', separateDialCode: true, + initialCountry: 'auto', + geoIpLookup: getCountryCodeFromLocale, }, ); } else { From 683264bb1427185aa0bc4eac4da0bc4f9307bf82 Mon Sep 17 00:00:00 2001 From: Fadi George Date: Mon, 22 Sep 2025 13:21:26 -0700 Subject: [PATCH 2/2] add tests for country code --- package-lock.json | 164 ++++++++---------- package.json | 6 +- .../slidedown/ChannelCaptureContainer.test.ts | 107 ++++++++++++ src/page/slidedown/ChannelCaptureContainer.ts | 39 +++-- 4 files changed, 210 insertions(+), 106 deletions(-) create mode 100644 src/page/slidedown/ChannelCaptureContainer.test.ts diff --git a/package-lock.json b/package-lock.json index bea740c35..7c80bef6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@size-limit/file": "^11.2.0", - "@types/body-parser": "*", + "@types/body-parser": "latest", "@types/express": "^4.17.17", "@types/intl-tel-input": "^18.1.4", "@types/jsdom": "^21.1.7", @@ -23,7 +23,7 @@ "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^5.36.1", "@typescript-eslint/parser": "^5.36.1", - "@vitest/coverage-v8": "4.0.0-beta.10", + "@vitest/coverage-v8": "4.0.0-beta.12", "concurrently": "^9.2.0", "deepmerge": "^4.2.2", "eslint": "^8.23.0", @@ -42,7 +42,7 @@ "vite-bundle-analyzer": "^1.1.0", "vite-plugin-mkcert": "^1.17.8", "vite-tsconfig-paths": "^5.1.4", - "vitest": "4.0.0-beta.10" + "vitest": "4.0.0-beta.12" } }, "node_modules/@asamuzakjp/css-color": { @@ -1768,30 +1768,30 @@ "license": "ISC" }, "node_modules/@vitest/coverage-v8": { - "version": "4.0.0-beta.10", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.0-beta.10.tgz", - "integrity": "sha512-euKtmmJPQBAzOQkS60sj9CwKHLxbVIAudybGY2Ah9yzsrZ8LL91loElv7lLd3u3OgYZWjASMDxIS0h4GriYxFA==", + "version": "4.0.0-beta.12", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.0-beta.12.tgz", + "integrity": "sha512-ar+nlPjwm2skYS3hkOiaXk8fBqVF93LWpLSTwyi8SQmBKxFj2iWjHV50qw65wbG4ESkJUU3fEA3mXQQhvSQ3kQ==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.0.0-beta.10", - "ast-v8-to-istanbul": "^0.3.4", - "debug": "^4.4.1", + "@vitest/utils": "4.0.0-beta.12", + "ast-v8-to-istanbul": "^0.3.5", + "debug": "^4.4.3", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-lib-source-maps": "^5.0.6", "istanbul-reports": "^3.2.0", "magicast": "^0.3.5", "std-env": "^3.9.0", - "tinyrainbow": "^2.0.0" + "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "4.0.0-beta.10", - "vitest": "4.0.0-beta.10" + "@vitest/browser": "4.0.0-beta.12", + "vitest": "4.0.0-beta.12" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -1800,32 +1800,32 @@ } }, "node_modules/@vitest/expect": { - "version": "4.0.0-beta.10", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.0-beta.10.tgz", - "integrity": "sha512-03GDRo1imUrqG2ASfC+smzjl81MX5Q8+HDiYrSm0ULs5FJoKOiz4tN62/9BNFzflmsveTp5KCjuN80i18gCDng==", + "version": "4.0.0-beta.12", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.0-beta.12.tgz", + "integrity": "sha512-3r4Gk7Cb7YPNaOETcmlFez3+1opQdKaU5cRnk3hLk13UbYsXxYuqp1wi689kwPa6Yp6BZtbddDIk/D6fPdwcdg==", "dev": true, "license": "MIT", "dependencies": { "@types/chai": "^5.2.2", - "@vitest/spy": "4.0.0-beta.10", - "@vitest/utils": "4.0.0-beta.10", + "@vitest/spy": "4.0.0-beta.12", + "@vitest/utils": "4.0.0-beta.12", "chai": "^6.0.1", - "tinyrainbow": "^2.0.0" + "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "4.0.0-beta.10", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.0-beta.10.tgz", - "integrity": "sha512-xjMA9qPCx2WnwX/72UyF1L8cJR6w/OD+HgTLpwcV6RhwFRk3ipdDTLBbTTngnyuaFh/IVXn04zJ7DKdPx1kV3A==", + "version": "4.0.0-beta.12", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.0-beta.12.tgz", + "integrity": "sha512-UVlIb5A0Qf9ZBBOB1MJNRVmNNbn4EyMhjS+XSR1DBBiTxlCFMXjt2UdncH+s04DIwWdv4obFceffjepw9CPK1w==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.0.0-beta.10", + "@vitest/spy": "4.0.0-beta.12", "estree-walker": "^3.0.3", - "magic-string": "^0.30.18" + "magic-string": "^0.30.19" }, "funding": { "url": "https://opencollective.com/vitest" @@ -1844,42 +1844,41 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.0.0-beta.10", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.0-beta.10.tgz", - "integrity": "sha512-WHQcKjG+LPfeu4R74gjJqUgrH7y0Gj2oWyQ4rscf8u3nKQS9z+D3lxyrYdwysmX5952ayHBI0x4GJ5MXmM6LCA==", + "version": "4.0.0-beta.12", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.0-beta.12.tgz", + "integrity": "sha512-zIlDOlEBxUn62/TFum2sQ2/vwQLBIYMdEDM6hMVGIn+MwDD/N0dXmpjDAuG5HDA9K3okF3624x+Einusxlio+Q==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^2.0.0" + "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "4.0.0-beta.10", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.0-beta.10.tgz", - "integrity": "sha512-bHJcMmhS3x9nFhwapm6DNuTbZMQSGEgYyKFm8GNZ35ixpYuCcmRDaA+rm8VwebraoSjkphVrwfdJ/dspaN6Gxw==", + "version": "4.0.0-beta.12", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.0-beta.12.tgz", + "integrity": "sha512-MVIvqTKcMFgfT65pHPpCVGPftXC3hT2Ovbmu0bGnEKNI7yirE3pgf5X4ZY3f5oAHjIOyggAcxkmCDNlzdl01+w==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.0.0-beta.10", - "pathe": "^2.0.3", - "strip-literal": "^3.0.0" + "@vitest/utils": "4.0.0-beta.12", + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "4.0.0-beta.10", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.0-beta.10.tgz", - "integrity": "sha512-yHpiggeAt9Fg082/5M8FGhgqp2Pv3nVeOHPZTirl9GbULEiBQiSO6pIkDZRh5gPGyKGbjXj/F/9h07BmtvOXtg==", + "version": "4.0.0-beta.12", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.0-beta.12.tgz", + "integrity": "sha512-vizO/9xz6I9b1AdwTGsrG9KiYkXicRKbLo9WMnJnYd8GJHbM+SnMS1NKBgn2uMbpG1fQoiOarjr6yEGPVoJZOw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.0.0-beta.10", - "magic-string": "^0.30.18", + "@vitest/pretty-format": "4.0.0-beta.12", + "magic-string": "^0.30.19", "pathe": "^2.0.3" }, "funding": { @@ -1887,9 +1886,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.0.0-beta.10", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.0-beta.10.tgz", - "integrity": "sha512-dz6qAHX+PqMOCq5Hq6nht2L8v9hszdzZM8x2KM9xGC8WESKBJwIdTqPVqLn+DyVzFMtRSAzeujXixDGeDFsrkQ==", + "version": "4.0.0-beta.12", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.0-beta.12.tgz", + "integrity": "sha512-PICUKy+sYYGCZoAGsTaUJsQnxZVpfvewSLOfmqdVoVKpicbBb+gMtJA2CR8vGhGLc4Uh/bMrr0vNRmQOA+U7jg==", "dev": true, "license": "MIT", "funding": { @@ -1897,15 +1896,14 @@ } }, "node_modules/@vitest/utils": { - "version": "4.0.0-beta.10", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.0-beta.10.tgz", - "integrity": "sha512-jKjXM3KR2FUIoU6QOZsFtJG9cNAwszmVg2WWsY/jlD6jhw1ngqoesxUwp3xbbSTeKh/Ii1P1qTaufa5g0oboMA==", + "version": "4.0.0-beta.12", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.0-beta.12.tgz", + "integrity": "sha512-3cP3XmmaLhyuQ5OXirsPB2B5/ZBfkqujBuFmuxi+C5710vz7h6wp9Y+eQqIYDloMHaIe1dVJTm+Q3klpNXqPrg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.0.0-beta.10", - "loupe": "^3.2.1", - "tinyrainbow": "^2.0.0" + "@vitest/pretty-format": "4.0.0-beta.12", + "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" @@ -2378,9 +2376,9 @@ } }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { @@ -3912,13 +3910,6 @@ "node": ">=8" } }, - "node_modules/loupe": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", - "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", - "dev": true, - "license": "MIT" - }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", @@ -3927,9 +3918,9 @@ "license": "ISC" }, "node_modules/magic-string": { - "version": "0.30.18", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", - "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", "dev": true, "license": "MIT", "dependencies": { @@ -5182,19 +5173,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-literal": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", - "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^9.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, "node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -5367,9 +5345,9 @@ } }, "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", "dev": true, "license": "MIT", "engines": { @@ -5736,31 +5714,31 @@ } }, "node_modules/vitest": { - "version": "4.0.0-beta.10", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.0-beta.10.tgz", - "integrity": "sha512-lVVa/PjrRMCkFHd7J95UkqNEL+MkLuJoZBoS5B7Y9tqSpOzupVTAgck9HzO5ezxn2SpvIyO0e5o7uBRFW/MZVQ==", + "version": "4.0.0-beta.12", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.0-beta.12.tgz", + "integrity": "sha512-q8b6TSUfeypyYQwC2SBOVbArKLWPbuvRqKw+OlZcStTXI3D2SSg9K9iJNugxjk+1H6i4as/6i4VzV6NmN82MZQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.0.0-beta.10", - "@vitest/mocker": "4.0.0-beta.10", - "@vitest/pretty-format": "^4.0.0-beta.10", - "@vitest/runner": "4.0.0-beta.10", - "@vitest/snapshot": "4.0.0-beta.10", - "@vitest/spy": "4.0.0-beta.10", - "@vitest/utils": "4.0.0-beta.10", - "debug": "^4.4.1", + "@vitest/expect": "4.0.0-beta.12", + "@vitest/mocker": "4.0.0-beta.12", + "@vitest/pretty-format": "4.0.0-beta.12", + "@vitest/runner": "4.0.0-beta.12", + "@vitest/snapshot": "4.0.0-beta.12", + "@vitest/spy": "4.0.0-beta.12", + "@vitest/utils": "4.0.0-beta.12", + "debug": "^4.4.3", "es-module-lexer": "^1.7.0", "expect-type": "^1.2.2", - "magic-string": "^0.30.18", + "magic-string": "^0.30.19", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.14", + "tinyglobby": "^0.2.15", "tinypool": "^2.0.0", - "tinyrainbow": "^2.0.0", + "tinyrainbow": "^3.0.3", "vite": "^6.0.0 || ^7.0.0-0", "why-is-node-running": "^2.3.0" }, @@ -5768,7 +5746,7 @@ "vitest": "vitest.mjs" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -5777,8 +5755,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "4.0.0-beta.10", - "@vitest/ui": "4.0.0-beta.10", + "@vitest/browser": "4.0.0-beta.12", + "@vitest/ui": "4.0.0-beta.12", "happy-dom": "*", "jsdom": "*" }, diff --git a/package.json b/package.json index 2de58ce6e..f9eadd2f8 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^5.36.1", "@typescript-eslint/parser": "^5.36.1", - "@vitest/coverage-v8": "4.0.0-beta.10", + "@vitest/coverage-v8": "4.0.0-beta.12", "concurrently": "^9.2.0", "deepmerge": "^4.2.2", "eslint": "^8.23.0", @@ -74,7 +74,7 @@ "vite-bundle-analyzer": "^1.1.0", "vite-plugin-mkcert": "^1.17.8", "vite-tsconfig-paths": "^5.1.4", - "vitest": "4.0.0-beta.10" + "vitest": "4.0.0-beta.12" }, "size-limit": [ { @@ -84,7 +84,7 @@ }, { "path": "./build/releases/OneSignalSDK.page.es6.js", - "limit": "50.29 kB", + "limit": "50.46 kB", "gzip": true }, { diff --git a/src/page/slidedown/ChannelCaptureContainer.test.ts b/src/page/slidedown/ChannelCaptureContainer.test.ts new file mode 100644 index 000000000..022b35eae --- /dev/null +++ b/src/page/slidedown/ChannelCaptureContainer.test.ts @@ -0,0 +1,107 @@ +import { http, HttpResponse } from 'msw'; +import { server } from '../../../__test__/support/mocks/server'; +import { getItem, removeItem } from '../modules/timedStorage'; +import { getCountryCode } from './ChannelCaptureContainer'; + +describe('getCountryCode', () => { + beforeEach(() => { + // Clear cached country code before each test + removeItem('countryCode'); + + server.use( + http.get('https://ipapi.co/json', () => { + return HttpResponse.json({ + country_code: 'US', + }); + }), + http.get('https://free.freeipapi.com/api/json/', () => { + return HttpResponse.json({ + countryCode: 'US', + }); + }), + http.get('https://api.country.is/', () => { + return HttpResponse.json({ + country: 'US', + }); + }), + ); + }); + + test('API can reutrn country code', async () => { + const callback = vi.fn(); + getCountryCode(callback); + + // Wait for the async operation to complete + await vi.waitFor(() => { + expect(callback).toHaveBeenCalledWith('us'); + }); + + // should store country code in local storage + expect(getItem('countryCode')).toBe('us'); + callback.mockClear(); + getCountryCode(callback); + expect(callback).toHaveBeenCalledWith('us'); + }); + + test('should use fallback api when first/second api fails', async () => { + server.use( + http.get('https://api.country.is/', () => { + return HttpResponse.error(); + }), + http.get('https://free.freeipapi.com/api/json/', () => { + return HttpResponse.json({ + countryCode: 'CA', + }); + }), + ); + + const callback = vi.fn(); + getCountryCode(callback); + + await vi.waitFor(() => { + expect(callback).toHaveBeenCalledWith('ca'); + }); + + server.use( + http.get('https://free.freeipapi.com/api/json/', () => { + return HttpResponse.error(); + }), + http.get('https://ipapi.co/json', () => { + return HttpResponse.json({ + country_code: 'GB', + }); + }), + ); + + callback.mockClear(); + removeItem('countryCode'); + getCountryCode(callback); + + await vi.waitFor(() => { + expect(callback).toHaveBeenCalledWith('gb'); + }); + }); + + test('should return "us" country code when all api fails', async () => { + server.use( + http.get('https://api.country.is/', () => { + return HttpResponse.error(); + }), + http.get('https://free.freeipapi.com/api/json/', () => { + return HttpResponse.error(); + }), + http.get('https://ipapi.co/json', () => { + return HttpResponse.error(); + }), + ); + + const callback = vi.fn(); + getCountryCode(callback); + + await vi.waitFor(() => { + expect(callback).toHaveBeenCalledWith('us'); + }); + + expect(callback).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/page/slidedown/ChannelCaptureContainer.ts b/src/page/slidedown/ChannelCaptureContainer.ts index 4f37ebd60..356b8be34 100644 --- a/src/page/slidedown/ChannelCaptureContainer.ts +++ b/src/page/slidedown/ChannelCaptureContainer.ts @@ -15,6 +15,7 @@ import { DANGER_ICON, SLIDEDOWN_CSS_IDS, } from '../../shared/slidedown/constants'; +import { getItem, setItem } from '../modules/timedStorage'; import { ItiScriptURLHashes, ItiScriptURLs, @@ -30,17 +31,35 @@ interface TypeSpecificVariablePayload { tabIndex: number; } -export function getCountryCodeFromLocale( +const COUNTRYCODE_TIME_TO_LIVE = 30; + +export async function getCountryCode( callback: (countryCode: string) => void, -): void { - try { - const locale = navigator.language || 'en-US'; - const parts = locale.split('-'); - const countryCode = parts[parts.length - 1]?.toLowerCase(); // handle things like en-US / zh-Hans-CN - callback(countryCode || 'us'); - } catch (error) { - callback('us'); +): Promise { + if (getItem('countryCode')) { + callback(getItem('countryCode')); + return; + } + + const urls = [ + ['https://api.country.is/', 'country'], // 600 req / min + ['https://free.freeipapi.com/api/json/', 'countryCode'], // 60 req / min + ['https://ipapi.co/json', 'country_code'], // 45 req / min + ]; + + for (const [url, field] of urls) { + try { + const res = await fetch(url); + const data = await res.json(); + const countryCode = data[field]?.toLowerCase() || 'us'; + setItem('countryCode', countryCode, COUNTRYCODE_TIME_TO_LIVE); + callback(countryCode); + return; // Success - exit function + } catch { + continue; // Try next API + } } + callback('us'); } export default class ChannelCaptureContainer { @@ -208,7 +227,7 @@ export default class ChannelCaptureContainer { autoPlaceholder: 'off', separateDialCode: true, initialCountry: 'auto', - geoIpLookup: getCountryCodeFromLocale, + geoIpLookup: getCountryCode, }, ); } else {