From 24619ac02f3f0616f240ef2ffb6d3dc43b3ab98d Mon Sep 17 00:00:00 2001 From: Tommaso Casaburi Date: Fri, 1 May 2026 16:21:00 +0700 Subject: [PATCH] fix(android): stop intercepting app fetches --- .../app/src/main/assets/capacitor.config.json | 2 +- capacitor.config.ts | 2 +- .../__tests__/challenge-modal.test.tsx | 34 +++++++++++++++++++ .../challenge-modal/challenge-modal.tsx | 10 +++++- 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/android/app/src/main/assets/capacitor.config.json b/android/app/src/main/assets/capacitor.config.json index cb312cdc..dcf5a0b3 100644 --- a/android/app/src/main/assets/capacitor.config.json +++ b/android/app/src/main/assets/capacitor.config.json @@ -4,7 +4,7 @@ "webDir": "build", "plugins": { "CapacitorHttp": { - "enabled": true + "enabled": false }, "FileUploader": { "enabled": true diff --git a/capacitor.config.ts b/capacitor.config.ts index f9bad095..991794f8 100644 --- a/capacitor.config.ts +++ b/capacitor.config.ts @@ -6,7 +6,7 @@ const config: CapacitorConfig = { webDir: 'build', plugins: { CapacitorHttp: { - enabled: true, + enabled: false, }, FileUploader: { enabled: true, diff --git a/src/components/challenge-modal/__tests__/challenge-modal.test.tsx b/src/components/challenge-modal/__tests__/challenge-modal.test.tsx index 3f49a4ad..fb38e68e 100644 --- a/src/components/challenge-modal/__tests__/challenge-modal.test.tsx +++ b/src/components/challenge-modal/__tests__/challenge-modal.test.tsx @@ -520,6 +520,40 @@ describe('ChallengeModal', () => { expect(testState.abandonCurrentChallengeMock).not.toHaveBeenCalled(); }); + it('uses inline iframe confirmation in Android WebView even when Capacitor reports web', async () => { + testState.capacitorPlatform = 'web'; + setNavigatorValue( + 'userAgent', + 'Mozilla/5.0 (Linux; Android 15; Pixel 9 Build/AP3A.241105.007) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/147.0.7727.137 Mobile Safari/537.36 wv', + ); + testState.publicationType = 'reply'; + testState.challenges = [ + createStoredChallenge( + { + challenge: 'https://spamblocker.bitsocial.net/api/v1/iframe/session-123', + type: 'url/iframe', + }, + { + ...createPublication(), + content: '>>17\nreply from webview', + title: '', + }, + ), + ]; + + await renderModal(); + + expect(confirmSpy).not.toHaveBeenCalled(); + expect(container.textContent).toContain('mu wants to open spamblocker.bitsocial.net.\n\nFor reply: >>17 reply from webview'); + expect(container.querySelector('iframe')).toBeNull(); + + await clickButton('Open'); + + const iframe = container.querySelector('iframe'); + expect(iframe).not.toBeNull(); + expect(iframe?.getAttribute('src')).toContain('https://spamblocker.bitsocial.net/api/v1/iframe/session-123?theme=dark'); + }); + it('abandons Android iframe challenges when inline confirmation is closed', async () => { testState.capacitorPlatform = 'android'; testState.challenges = [ diff --git a/src/components/challenge-modal/challenge-modal.tsx b/src/components/challenge-modal/challenge-modal.tsx index 619ea4dd..0e350d21 100644 --- a/src/components/challenge-modal/challenge-modal.tsx +++ b/src/components/challenge-modal/challenge-modal.tsx @@ -45,6 +45,14 @@ const getDisplayCommunityAddress = (shortCommunityAddress?: string, communityAdd shortCommunityAddress || (communityAddress ? getShortAddress(communityAddress) : '') || communityAddress || ''; const iframeChallengeConfirmDecisions = new Map(); +const ANDROID_USER_AGENT_REGEX = /Android/; +const ANDROID_WEBVIEW_USER_AGENT_REGEX = /\bwv\b|Version\/\d+(?:\.\d+)?\s+Chrome\//; + +const isAndroidWebViewUserAgent = () => { + if (typeof navigator === 'undefined') return false; + return ANDROID_USER_AGENT_REGEX.test(navigator.userAgent) && ANDROID_WEBVIEW_USER_AGENT_REGEX.test(navigator.userAgent); +}; + const isIosWebKitUserAgent = () => { if (typeof navigator === 'undefined') return false; return /iPad|iPhone|iPod/.test(navigator.userAgent) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1); @@ -60,7 +68,7 @@ const isDesktopSafariUserAgent = () => { const shouldUseInlineIframeConfirm = () => { const platform = Capacitor.getPlatform(); - return platform === 'android' || platform === 'ios' || isIosWebKitUserAgent() || isDesktopSafariUserAgent(); + return platform === 'android' || platform === 'ios' || isAndroidWebViewUserAgent() || isIosWebKitUserAgent() || isDesktopSafariUserAgent(); }; const TextChallenge = ({ challenge }: { challenge: string }) =>
{challenge}
;