← back to CelebritySignatures
iOS: wire the 3.1.1 WebView guard + fix evolution-load side effect
fc653007205f47af2cb898a2b57e0648d9037fe1 · 2026-08-05 15:53:20 -0700 · Steve
- allowWebNav was defined but never attached to the mural-checkout WebView,
leaving the App Store 3.1.1 digital-purchase guard inert (tsc still passed).
Attach it via onShouldStartLoadWithRequest so in-WebView navigation to a
digital signature purchase is blocked and true externals bounce to Safari.
- Move the evolution-map lazy load out of a setState updater into a useEffect
(state updaters must be pure).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit fc653007205f47af2cb898a2b57e0648d9037fe1
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Aug 5 15:53:20 2026 -0700
iOS: wire the 3.1.1 WebView guard + fix evolution-load side effect
- allowWebNav was defined but never attached to the mural-checkout WebView,
leaving the App Store 3.1.1 digital-purchase guard inert (tsc still passed).
Attach it via onShouldStartLoadWithRequest so in-WebView navigation to a
digital signature purchase is blocked and true externals bounce to Safari.
- Move the evolution-map lazy load out of a setState updater into a useEffect
(state updaters must be pure).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
apps/mobile/App.tsx | 30 +++++++++++++++++++++++-------
1 file changed, 23 insertions(+), 7 deletions(-)
diff --git a/apps/mobile/App.tsx b/apps/mobile/App.tsx
index 10bb27b..446d692 100644
--- a/apps/mobile/App.tsx
+++ b/apps/mobile/App.tsx
@@ -1,6 +1,6 @@
-import React, { useCallback, useState } from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
import {
- ActivityIndicator, Modal, Pressable, SafeAreaView, StyleSheet, Text, View,
+ ActivityIndicator, Linking, Modal, Pressable, SafeAreaView, StyleSheet, Text, View,
} from 'react-native';
import { StatusBar } from 'expo-status-bar';
import * as Haptics from 'expo-haptics';
@@ -38,11 +38,26 @@ export default function App() {
const openSignature = useCallback((sig: Signature) => {
Haptics.selectionAsync().catch(() => {});
setSelected(sig);
- // Lazy-load the evolution map once, on first detail open.
- setEvolution((cur) => {
- if (!cur) api.evolution().then(setEvolution).catch(() => {});
- return cur;
- });
+ }, []);
+
+ // Lazy-load the evolution map once, the first time a detail is opened.
+ // (A useEffect, not a side effect inside a setState updater — updaters must be pure.)
+ useEffect(() => {
+ if (selected && !evolution) api.evolution().then(setEvolution).catch(() => {});
+ }, [selected, evolution]);
+
+ // App Store 3.1.1 guard: the mural checkout is a PHYSICAL print (IAP-exempt),
+ // but the site also exposes DIGITAL signature-file downloads. Block any in-
+ // WebView navigation that could reach a digital purchase, so a reviewer can't
+ // complete a digital sale without IAP. Allow the murals page, same-origin
+ // assets, and Stripe (physical checkout); kick true externals to Safari.
+ const allowWebNav = useCallback((url: string): boolean => {
+ if (/signature-(checkout|file)|\/api\/signature-/.test(url)) return false;
+ if (url.startsWith(WEB_BASE) || /^https:\/\/(checkout|js|hooks)\.stripe\.com/.test(url) || url.startsWith('about:')) {
+ return true;
+ }
+ if (/^https?:/.test(url)) { Linking.openURL(url).catch(() => {}); return false; }
+ return true;
}, []);
const orderMural = useCallback((sig: Signature) => {
@@ -101,6 +116,7 @@ export default function App() {
source={{ uri: webUrl }}
startInLoadingState
sharedCookiesEnabled
+ onShouldStartLoadWithRequest={(req) => allowWebNav(req.url)}
injectedJavaScriptBeforeContentLoaded={NO_TRACK}
renderLoading={() => (
<ActivityIndicator style={styles.webLoading} size="large" color={GLASS.accent} />
← 73d9137 iOS app: native Expo/EAS scaffold over celebsignatures.com A
·
back to CelebritySignatures
·
iOS: add on-brand app icon + splash + adaptive icon, wire in caf36ad →