From 29d37d52d512cde99e1f073a718b84f7b666e587 Mon Sep 17 00:00:00 2001 From: jaaaaavier Date: Tue, 14 Apr 2026 12:44:59 +0200 Subject: [PATCH 1/6] feat: add Firefox MV2 support --- .github/workflows/build.yml | 78 +++++++++++++++++++ src/entrypoints/background.ts | 20 ++--- .../dropdown/components/DropdownSection.tsx | 3 +- src/entrypoints/content/index.ts | 16 ++-- src/entrypoints/popup/App.tsx | 7 +- src/entrypoints/popup/proxy.service.ts | 8 +- src/entrypoints/services/storage.service.ts | 9 ++- 7 files changed, 111 insertions(+), 30 deletions(-) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..b054afc --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,78 @@ +name: Build + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + name: Build Chrome & Firefox + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Type check + run: npm run compile + + - name: Build Chrome (MV3) + run: npm run build + env: + VITE_VPN_SERVER_ADDRESS: ${{ secrets.VITE_VPN_SERVER_ADDRESS }} + VITE_VPN_SERVER_PORT: ${{ secrets.VITE_VPN_SERVER_PORT }} + VITE_VPN_USERNAME: ${{ secrets.VITE_VPN_USERNAME }} + VITE_VPN_PASSWORD: ${{ secrets.VITE_VPN_PASSWORD }} + VITE_VPN_API_URL: ${{ secrets.VITE_VPN_API_URL }} + VITE_VPN_API_URL_STAGING: ${{ secrets.VITE_VPN_API_URL_STAGING }} + VITE_VPN_API_URL_DEVELOPMENT: ${{ secrets.VITE_VPN_API_URL_DEVELOPMENT }} + VITE_IP_API_URL: ${{ secrets.VITE_IP_API_URL }} + VITE_AUTH_HOST_URL: ${{ secrets.VITE_AUTH_HOST_URL }} + VITE_AUTH_HOST_URL_STAGING: ${{ secrets.VITE_AUTH_HOST_URL_STAGING }} + VITE_AUTH_HOST_URL_DEVELOPMENT: ${{ secrets.VITE_AUTH_HOST_URL_DEVELOPMENT }} + VITE_DRIVE_API_URL: ${{ secrets.VITE_DRIVE_API_URL }} + VITE_DRIVE_API_URL_STAGING: ${{ secrets.VITE_DRIVE_API_URL_STAGING }} + VITE_DRIVE_API_URL_DEVELOPMENT: ${{ secrets.VITE_DRIVE_API_URL_DEVELOPMENT }} + + - name: Build Firefox (MV2) + run: npm run build:firefox + env: + VITE_VPN_SERVER_ADDRESS: ${{ secrets.VITE_VPN_SERVER_ADDRESS }} + VITE_VPN_SERVER_PORT: ${{ secrets.VITE_VPN_SERVER_PORT }} + VITE_VPN_USERNAME: ${{ secrets.VITE_VPN_USERNAME }} + VITE_VPN_PASSWORD: ${{ secrets.VITE_VPN_PASSWORD }} + VITE_VPN_API_URL: ${{ secrets.VITE_VPN_API_URL }} + VITE_VPN_API_URL_STAGING: ${{ secrets.VITE_VPN_API_URL_STAGING }} + VITE_VPN_API_URL_DEVELOPMENT: ${{ secrets.VITE_VPN_API_URL_DEVELOPMENT }} + VITE_IP_API_URL: ${{ secrets.VITE_IP_API_URL }} + VITE_AUTH_HOST_URL: ${{ secrets.VITE_AUTH_HOST_URL }} + VITE_AUTH_HOST_URL_STAGING: ${{ secrets.VITE_AUTH_HOST_URL_STAGING }} + VITE_AUTH_HOST_URL_DEVELOPMENT: ${{ secrets.VITE_AUTH_HOST_URL_DEVELOPMENT }} + VITE_DRIVE_API_URL: ${{ secrets.VITE_DRIVE_API_URL }} + VITE_DRIVE_API_URL_STAGING: ${{ secrets.VITE_DRIVE_API_URL_STAGING }} + VITE_DRIVE_API_URL_DEVELOPMENT: ${{ secrets.VITE_DRIVE_API_URL_DEVELOPMENT }} + + - name: Upload Chrome build + uses: actions/upload-artifact@v4 + with: + name: chrome-mv3 + path: .output/chrome-mv3/ + retention-days: 7 + + - name: Upload Firefox build + uses: actions/upload-artifact@v4 + with: + name: firefox-mv2 + path: .output/firefox-mv2/ + retention-days: 7 diff --git a/src/entrypoints/background.ts b/src/entrypoints/background.ts index 0b77067..9aa9756 100644 --- a/src/entrypoints/background.ts +++ b/src/entrypoints/background.ts @@ -1,3 +1,4 @@ +import { browser } from 'wxt/browser' import { handleUserToken } from './utils/handleUserToken' import { clearProxySettings } from './popup/proxy.service' @@ -15,13 +16,13 @@ function startInterval() { export default defineBackground(() => { const IP_API_URL = import.meta.env.VITE_IP_API_URL - chrome.runtime.onInstalled.addListener((details) => { + browser.runtime.onInstalled.addListener((details) => { if (details.reason === 'install') { - chrome.tabs.create({ url: 'https://internxt.com/vpn' }) + browser.tabs.create({ url: 'https://internxt.com/vpn' }) } }) - chrome.runtime.onMessage.addListener((message, _, sendResponse) => { + browser.runtime.onMessage.addListener((message, _, sendResponse) => { if (message === 'GET_DATA') { fetch(`${IP_API_URL}/json`, { method: 'GET', @@ -58,10 +59,9 @@ export default defineBackground(() => { } async function initializeLocalCache() { - const { userToken, connection } = await chrome.storage.local.get([ - 'userToken', - 'connection', - ]) + const result = await browser.storage.local.get(['userToken', 'connection']) + const userToken = result.userToken as { token: string } | undefined + const connection = result.connection as string | undefined console.log('INITIAL LOCAL STORAGE: ', userToken) localCache.token = userToken?.token ?? null localCache.connection = connection ?? null @@ -70,14 +70,14 @@ export default defineBackground(() => { startInterval() initializeLocalCache() - chrome.storage.onChanged.addListener((changes, areaName) => { + browser.storage.onChanged.addListener((changes, areaName) => { if (areaName === 'local') { if (changes.userToken?.newValue) { - localCache.token = changes.userToken.newValue?.token ?? null + localCache.token = (changes.userToken.newValue as { token: string })?.token ?? null startInterval() } if (changes.connection?.newValue) { - localCache.connection = changes.connection.newValue ?? null + localCache.connection = (changes.connection.newValue as string) ?? null } } }) diff --git a/src/entrypoints/components/dropdown/components/DropdownSection.tsx b/src/entrypoints/components/dropdown/components/DropdownSection.tsx index 5c0cc52..72ea7b7 100644 --- a/src/entrypoints/components/dropdown/components/DropdownSection.tsx +++ b/src/entrypoints/components/dropdown/components/DropdownSection.tsx @@ -1,3 +1,4 @@ +import { browser } from 'wxt/browser' import { translate } from '@/constants' import { SectionItemProps, SectionProps } from '../Dropdown' import { DropdownItem } from './DropdownItem' @@ -16,7 +17,7 @@ export const DropdownSection = ({ onItemClicked, }: DropdownSectionProps) => { const handleUpgradeButtonClicked = () => { - chrome.tabs.create({ url: 'https://internxt.com/pricing' }) + browser.tabs.create({ url: 'https://internxt.com/pricing' }) } return (
{ + }) + .then(() => { console.log( 'The user has been authenticated in the VPN extension', ) - }, - ) + }) } else if (eventMessage === MESSAGES.USER_LOG_OUT) { - chrome.storage.local.clear(async () => { - await chrome.runtime.sendMessage('RESET_PROXY') + browser.storage.local.clear().then(async () => { + await browser.runtime.sendMessage('RESET_PROXY') console.log('The user has been logged out from the VPN extension') }) } diff --git a/src/entrypoints/popup/App.tsx b/src/entrypoints/popup/App.tsx index de10f3a..4f7a221 100644 --- a/src/entrypoints/popup/App.tsx +++ b/src/entrypoints/popup/App.tsx @@ -1,4 +1,5 @@ import { useEffect, useState } from 'react' +import { browser } from 'wxt/browser' import { clearProxySettings, updateProxySettings } from './proxy.service' import { ConnectionDetails } from '../components/ConnectionDetails' @@ -51,7 +52,7 @@ export const App = () => { const initialAppState = async () => { try { - const storageData = (await chrome.storage.local.get([ + const storageData = (await browser.storage.local.get([ 'vpnStatus', 'userData', 'userToken', @@ -100,7 +101,7 @@ export const App = () => { const onConnectVpn = async () => { await updateProxySettings() - const userData = await chrome.runtime.sendMessage('GET_DATA') + const userData = await browser.runtime.sendMessage('GET_DATA') setUserData(userData) await storageService.saveVpnStatus('ON', userData) @@ -218,7 +219,7 @@ export const App = () => { const dropdownSections = getDropdownSections(availableLocations) return ( -
+
{/* Main section (logo, title, description) */}
{ console.log('PROXY CACHE REMOVED') }) } export async function updateProxySettings() { const proxyConfig = { - mode: 'fixed_servers', + mode: 'fixed_servers' as const, rules: { singleProxy: { - scheme: 'http', + scheme: 'http' as const, host: VPN_CONFIG.HOST, port: VPN_CONFIG.PORT, }, @@ -43,7 +43,7 @@ export async function updateProxySettings() { export async function clearProxySettings() { const proxyConfig = { - mode: 'system', + mode: 'system' as const, } browser.proxy.settings diff --git a/src/entrypoints/services/storage.service.ts b/src/entrypoints/services/storage.service.ts index 9ccd3f6..b71e43d 100644 --- a/src/entrypoints/services/storage.service.ts +++ b/src/entrypoints/services/storage.service.ts @@ -1,3 +1,4 @@ +import { browser } from 'wxt/browser' import { UserData, VPN_STATUS, VPNLocation } from '../popup/App' type TokenType = 'anonymous' | 'user' @@ -6,7 +7,7 @@ export const saveUserToken = async ( tokenType: TokenType, userToken: string ) => { - await chrome.storage.local.set({ + await browser.storage.local.set({ userToken: { token: userToken, type: tokenType, @@ -18,12 +19,12 @@ export const getUserToken = async (): Promise<{ token: string type: TokenType }> => { - const storageData = await chrome.storage.local.get('userToken') + const storageData = await browser.storage.local.get('userToken') return storageData.userToken as { token: string; type: TokenType } } export const saveUserConnection = async (connection: VPNLocation) => { - await chrome.storage.local.set({ + await browser.storage.local.set({ connection, }) } @@ -32,7 +33,7 @@ export const saveVpnStatus = async ( vpnStatus: VPN_STATUS, userData: UserData ) => { - await chrome.storage.local.set({ + await browser.storage.local.set({ vpnStatus, userData, }) From ab8c11a32de94ea781bd77a4db1d1ad09e0a7f8d Mon Sep 17 00:00:00 2001 From: jaaaaavier Date: Tue, 14 Apr 2026 12:51:36 +0200 Subject: [PATCH 2/6] Update build.yml --- .github/workflows/build.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b054afc..e02dae7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,16 +19,16 @@ jobs: uses: actions/setup-node@v4 with: node-version: 20 - cache: npm + cache: yarn - name: Install dependencies - run: npm ci + run: yarn install --frozen-lockfile - name: Type check - run: npm run compile + run: yarn compile - name: Build Chrome (MV3) - run: npm run build + run: yarn build env: VITE_VPN_SERVER_ADDRESS: ${{ secrets.VITE_VPN_SERVER_ADDRESS }} VITE_VPN_SERVER_PORT: ${{ secrets.VITE_VPN_SERVER_PORT }} @@ -46,7 +46,7 @@ jobs: VITE_DRIVE_API_URL_DEVELOPMENT: ${{ secrets.VITE_DRIVE_API_URL_DEVELOPMENT }} - name: Build Firefox (MV2) - run: npm run build:firefox + run: yarn build:firefox env: VITE_VPN_SERVER_ADDRESS: ${{ secrets.VITE_VPN_SERVER_ADDRESS }} VITE_VPN_SERVER_PORT: ${{ secrets.VITE_VPN_SERVER_PORT }} From 361fd10118a1f1fc19fa1e75608ed4bfaabe75a8 Mon Sep 17 00:00:00 2001 From: jaaaaavier Date: Tue, 14 Apr 2026 12:57:54 +0200 Subject: [PATCH 3/6] Update StatusComponent.tsx --- src/entrypoints/components/StatusComponent.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/entrypoints/components/StatusComponent.tsx b/src/entrypoints/components/StatusComponent.tsx index fb7a118..df8a9b1 100644 --- a/src/entrypoints/components/StatusComponent.tsx +++ b/src/entrypoints/components/StatusComponent.tsx @@ -1,7 +1,7 @@ -import { VPN_STATUS_SWITCH } from '../constants' +import { VPN_STATUS } from '~/entrypoints/popup/App' interface StatusComponentProps { - status: VPN_STATUS_SWITCH + status: VPN_STATUS } export const StatusComponent = ({ status }: StatusComponentProps) => { From d4d94b10064e8112a3314889ab0cb8e25ea2f95c Mon Sep 17 00:00:00 2001 From: jaaaaavier Date: Mon, 18 May 2026 17:47:07 +0200 Subject: [PATCH 4/6] feat: updates for firefox --- src/entrypoints/background.ts | 77 ++++++++++++++++-------- src/entrypoints/content/index.ts | 14 +---- src/entrypoints/popup/App.tsx | 16 ++--- src/entrypoints/popup/proxy.service.ts | 47 ++++++--------- src/entrypoints/utils/handleUserToken.ts | 6 +- wxt.config.ts | 6 +- 6 files changed, 81 insertions(+), 85 deletions(-) diff --git a/src/entrypoints/background.ts b/src/entrypoints/background.ts index 9aa9756..9501772 100644 --- a/src/entrypoints/background.ts +++ b/src/entrypoints/background.ts @@ -1,6 +1,6 @@ import { browser } from 'wxt/browser' import { handleUserToken } from './utils/handleUserToken' -import { clearProxySettings } from './popup/proxy.service' +import { clearProxySettings, updateProxySettings } from './popup/proxy.service' const FOUR_DAYS_IN_MS = 4 * 24 * 60 * 60 * 1000 let interval: NodeJS.Timeout | null = null @@ -8,7 +8,6 @@ let interval: NodeJS.Timeout | null = null function startInterval() { if (interval) clearInterval(interval) interval = setInterval(() => { - console.log('Refresh token') handleUserToken() }, FOUR_DAYS_IN_MS) } @@ -31,14 +30,21 @@ export default defineBackground(() => { .then((items) => { const { ip, city, region, country } = items const locationText = `${city}, ${region}, ${country}` - sendResponse({ location: locationText, ip, }) }) .catch(() => { - // NO OP + sendResponse(null) + }) + } else if (message === 'SET_PROXY') { + updateProxySettings() + .then(() => { + sendResponse({}) + }) + .catch(() => { + sendResponse({}) }) } else if (message === 'RESET_PROXY') { clearProxySettings() @@ -59,12 +65,13 @@ export default defineBackground(() => { } async function initializeLocalCache() { - const result = await browser.storage.local.get(['userToken', 'connection']) + const result = await browser.storage.local.get(['userToken', 'connection', 'vpnEnabled']) const userToken = result.userToken as { token: string } | undefined const connection = result.connection as string | undefined console.log('INITIAL LOCAL STORAGE: ', userToken) localCache.token = userToken?.token ?? null localCache.connection = connection ?? null + localCache.vpnEnabled = (result.vpnEnabled as boolean) ?? false } startInterval() @@ -79,29 +86,47 @@ export default defineBackground(() => { if (changes.connection?.newValue) { localCache.connection = (changes.connection.newValue as string) ?? null } + if ('vpnEnabled' in changes) { + localCache.vpnEnabled = (changes.vpnEnabled.newValue as boolean) ?? false + } } }) - browser.webRequest.onAuthRequired.addListener( - function (details) { - if (details.isProxy) { - return { - authCredentials: { - username: localCache.connection ?? 'FR', - password: localCache.token ?? '', - }, - } - } - return {} - }, - { urls: [''] }, - ['blocking'] - ) + if (import.meta.env.BROWSER === 'firefox') { + const VPN_HOST = import.meta.env.VITE_VPN_SERVER_ADDRESS + const VPN_PORT = Number(import.meta.env.VITE_VPN_SERVER_PORT) + ;(browser as any).proxy.onRequest.addListener( + (details: any) => { + if (details.tabId === -1 || details.originUrl?.startsWith('moz-extension://')) return { type: 'direct' } + if (!localCache.vpnEnabled) return { type: 'direct' } + return { type: 'http', host: VPN_HOST, port: VPN_PORT, username: localCache.connection ?? 'FR', password: localCache.token ?? '' } + }, + { urls: [''] }, + ) - browser.webRequest.onErrorOccurred.addListener( - (error) => { - console.log('[AN ERROR OCURRED]:', error) - }, - { urls: [''] } - ) + browser.webRequest.onAuthRequired.addListener( + function (details) { + if (!details.isProxy) return {} + return { authCredentials: { username: localCache.connection ?? 'FR', password: localCache.token ?? '' } } + }, + { urls: [''] }, + ['blocking'], + ) + } else { + browser.webRequest.onAuthRequired.addListener( + function (details) { + if (details.isProxy) { + return { + authCredentials: { + username: localCache.connection ?? 'FR', + password: localCache.token ?? '', + }, + } + } + return {} + }, + { urls: [''] }, + ['blocking'], + ) + } }) diff --git a/src/entrypoints/content/index.ts b/src/entrypoints/content/index.ts index b02c702..0df6a6c 100644 --- a/src/entrypoints/content/index.ts +++ b/src/entrypoints/content/index.ts @@ -52,22 +52,10 @@ export default defineContentScript({ if (eventMessage === MESSAGES.USER_TOKEN) { const token = event.data.payload.token - browser.storage.local - .set({ - userToken: { - token, - type: 'user', - }, - }) - .then(() => { - console.log( - 'The user has been authenticated in the VPN extension', - ) - }) + browser.storage.local.set({ userToken: { token, type: 'user' } }) } else if (eventMessage === MESSAGES.USER_LOG_OUT) { browser.storage.local.clear().then(async () => { await browser.runtime.sendMessage('RESET_PROXY') - console.log('The user has been logged out from the VPN extension') }) } } diff --git a/src/entrypoints/popup/App.tsx b/src/entrypoints/popup/App.tsx index 4f7a221..8450bfe 100644 --- a/src/entrypoints/popup/App.tsx +++ b/src/entrypoints/popup/App.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react' import { browser } from 'wxt/browser' -import { clearProxySettings, updateProxySettings } from './proxy.service' +import { clearProxySettings } from './proxy.service' import { ConnectionDetails } from '../components/ConnectionDetails' import { VpnStatus } from '../components/VpnStatus' import { Footer } from '../components/Footer' @@ -82,9 +82,7 @@ export const App = () => { setSelectedLocation(location) } catch (error) { - console.error(`ERROR WHILE INITIALIZING APP STATE: ${error}`) if (error instanceof UnauthorizedError) { - console.warn('Authorization error detected:', error.message) await onLogOut() } } @@ -100,11 +98,11 @@ export const App = () => { } const onConnectVpn = async () => { - await updateProxySettings() + await browser.runtime.sendMessage('SET_PROXY') const userData = await browser.runtime.sendMessage('GET_DATA') - setUserData(userData) - await storageService.saveVpnStatus('ON', userData) - + const resolvedUserData = userData ?? defaultUserDataInfo + setUserData(resolvedUserData) + await storageService.saveVpnStatus('ON', resolvedUserData) setStatus('ON') } @@ -125,9 +123,7 @@ export const App = () => { } } catch (err) { await onDisconnectVpn() - } finally { - const newStatus = status === 'OFF' ? 'ON' : 'OFF' - setStatus(newStatus) + setStatus('OFF') } } diff --git a/src/entrypoints/popup/proxy.service.ts b/src/entrypoints/popup/proxy.service.ts index 40326aa..5e1ffb8 100644 --- a/src/entrypoints/popup/proxy.service.ts +++ b/src/entrypoints/popup/proxy.service.ts @@ -5,20 +5,18 @@ const VPN_CONFIG = { PORT: Number(import.meta.env.VITE_VPN_SERVER_PORT), } +const IS_FIREFOX = import.meta.env.BROWSER === 'firefox' + async function clearProxyCache() { - const options: Record = {} - const rootDomain = VPN_CONFIG.HOST - options.origins = [] - options.origins.push('http://' + rootDomain) - options.origins.push('https://' + rootDomain) - - const types = { cookies: true } - browser.browsingData.remove(options, types).then(() => { - console.log('PROXY CACHE REMOVED') - }) + browser.browsingData.remove({}, { cookies: true }) } export async function updateProxySettings() { + if (IS_FIREFOX) { + await browser.storage.local.set({ vpnEnabled: true }) + return + } + const proxyConfig = { mode: 'fixed_servers' as const, rules: { @@ -31,30 +29,21 @@ export async function updateProxySettings() { }, } - browser.proxy.settings - .set({ value: proxyConfig, scope: 'regular' }) - .then(() => { - console.log('CONNECTED') - }) - .catch((err) => { - console.log('ERROR WHILE CONNECTING TO THE PROXY: ', err) - }) + browser.proxy.settings.set({ value: proxyConfig, scope: 'regular' }) } export async function clearProxySettings() { + if (IS_FIREFOX) { + await browser.storage.local.set({ vpnEnabled: false }) + clearProxyCache() + return + } + const proxyConfig = { mode: 'system' as const, } - browser.proxy.settings - .set({ value: proxyConfig, scope: 'regular' }) - .then(() => { - if (browser.runtime.lastError) { - console.error( - 'ERROR ADDING THE DEFAULT PROXY CONFIG: ', - browser.runtime.lastError, - ) - } - clearProxyCache() - }) + browser.proxy.settings.set({ value: proxyConfig, scope: 'regular' }).then(() => { + clearProxyCache() + }) } diff --git a/src/entrypoints/utils/handleUserToken.ts b/src/entrypoints/utils/handleUserToken.ts index 744f6d4..8f1f642 100644 --- a/src/entrypoints/utils/handleUserToken.ts +++ b/src/entrypoints/utils/handleUserToken.ts @@ -7,14 +7,12 @@ import storageService, { getUserToken } from '../services/storage.service' const refreshExistentUserToken = async (userToken: string) => { const refreshedToken = await refreshUserToken(userToken) - console.log(`User token refreshed`) - await storageService.saveUserToken('user', refreshedToken) + await storageService.saveUserToken('user', refreshedToken) } const refreshAnonymousToken = async () => { const anonymousToken = await getAnonymousToken() - console.log(`Anonymous token refreshed`) - await storageService.saveUserToken('anonymous', anonymousToken.token) + await storageService.saveUserToken('anonymous', anonymousToken.token) } export const handleUserToken = async () => { diff --git a/wxt.config.ts b/wxt.config.ts index b992cba..5120fe3 100644 --- a/wxt.config.ts +++ b/wxt.config.ts @@ -7,7 +7,7 @@ export default defineConfig({ }), modules: ['@wxt-dev/i18n/module'], srcDir: 'src', - manifest: { + manifest: ({ browser }) => ({ name: 'Internxt VPN - Free, Encrypted & Unlimited VPN', short_name: 'Internxt VPN', default_locale: 'en', @@ -25,7 +25,7 @@ export default defineConfig({ 'storage', 'proxy', 'webRequest', - 'webRequestAuthProvider', + ...(browser === 'firefox' ? ['webRequestBlocking'] : ['webRequestAuthProvider']), 'browsingData', ], web_accessible_resources: [ @@ -38,5 +38,5 @@ export default defineConfig({ action: { default_popup: 'index.html', }, - }, + }), }) From 0070e1c37e85ad06a5becb038c193eab4cf599b0 Mon Sep 17 00:00:00 2001 From: jaaaaavier Date: Thu, 21 May 2026 14:49:47 +0200 Subject: [PATCH 5/6] feat: firefox update --- .../components/RestartBrowserModal.tsx | 29 +++++++++++++++++++ src/entrypoints/popup/App.tsx | 23 ++++++++++++++- src/locales/en.json | 5 ++++ wxt.config.ts | 4 ++- 4 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 src/entrypoints/components/RestartBrowserModal.tsx diff --git a/src/entrypoints/components/RestartBrowserModal.tsx b/src/entrypoints/components/RestartBrowserModal.tsx new file mode 100644 index 0000000..14d9986 --- /dev/null +++ b/src/entrypoints/components/RestartBrowserModal.tsx @@ -0,0 +1,29 @@ +import { translate } from '@/constants' + +interface RestartBrowserModalProps { + onClose: () => void +} + +export const RestartBrowserModal = ({ onClose }: RestartBrowserModalProps) => { + return ( +
+
+
+

+ {translate('firefoxLocationModal.title')} +

+

+ {translate('firefoxLocationModal.description')} +

+
+ +
+
+ ) +} diff --git a/src/entrypoints/popup/App.tsx b/src/entrypoints/popup/App.tsx index 8450bfe..9ef1999 100644 --- a/src/entrypoints/popup/App.tsx +++ b/src/entrypoints/popup/App.tsx @@ -5,7 +5,11 @@ import { clearProxySettings } from './proxy.service' import { ConnectionDetails } from '../components/ConnectionDetails' import { VpnStatus } from '../components/VpnStatus' import { Footer } from '../components/Footer' +import { RestartBrowserModal } from '../components/RestartBrowserModal' import { translate } from '@/constants' + +const IS_FIREFOX = import.meta.env.BROWSER === 'firefox' + import { getAnonymousToken, getUserAvailableLocations, @@ -45,6 +49,8 @@ export const App = () => { const [availableLocations, setAvailableLocations] = useState([ 'FR', ]) + const [showFirefoxLocationInfo, setShowFirefoxLocationInfo] = useState(false) + const [firefoxNeedsRestart, setFirefoxNeedsRestart] = useState(false) useEffect(() => { initialAppState() @@ -114,6 +120,10 @@ export const App = () => { } const onToggleClicked = async () => { + if (IS_FIREFOX && firefoxNeedsRestart && status === 'OFF') { + setShowFirefoxLocationInfo(true) + return + } setStatus('CONNECTING') try { if (status === 'OFF') { @@ -144,6 +154,14 @@ export const App = () => { } const onChangeLocation = async (newLocation: VPNLocation) => { + if (IS_FIREFOX && status === 'ON') { + if (newLocation !== selectedLocation) { + setShowFirefoxLocationInfo(true) + setFirefoxNeedsRestart(true) + } + return + } + try { if (status === 'ON') { await onDisconnectVpn() @@ -215,7 +233,7 @@ export const App = () => { const dropdownSections = getDropdownSections(availableLocations) return ( -
+
{/* Main section (logo, title, description) */}
{
+ {showFirefoxLocationInfo && ( + setShowFirefoxLocationInfo(false)} /> + )}
) } diff --git a/src/locales/en.json b/src/locales/en.json index 35f1453..fe18fa8 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -32,6 +32,11 @@ "description": "Connect for secure browsing." } }, + "firefoxLocationModal": { + "title": "Restart browser to change location", + "description": "Close Firefox > Open a new window > Open Internxt VPN > Click preferred location", + "confirm": "Got it" + }, "footer": { "login": "Log in", "signup": "Sign up", diff --git a/wxt.config.ts b/wxt.config.ts index 5120fe3..09d68d4 100644 --- a/wxt.config.ts +++ b/wxt.config.ts @@ -25,7 +25,9 @@ export default defineConfig({ 'storage', 'proxy', 'webRequest', - ...(browser === 'firefox' ? ['webRequestBlocking'] : ['webRequestAuthProvider']), + ...(browser === 'firefox' + ? ['webRequestBlocking'] + : ['webRequestAuthProvider']), 'browsingData', ], web_accessible_resources: [ From a423bf74ced1a86c22239098cc4019846c03b1ed Mon Sep 17 00:00:00 2001 From: jaaaaavier Date: Thu, 21 May 2026 14:54:01 +0200 Subject: [PATCH 6/6] Update background.ts --- src/entrypoints/background.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/entrypoints/background.ts b/src/entrypoints/background.ts index 9501772..e9326a8 100644 --- a/src/entrypoints/background.ts +++ b/src/entrypoints/background.ts @@ -62,6 +62,7 @@ export default defineBackground(() => { const localCache = { token: null as string | null, connection: null as string | null, + vpnEnabled: false as boolean, } async function initializeLocalCache() {