1- import { ActivityIndicator , StyleSheet , Text , View } from 'react-native' ;
1+ import {
2+ ActivityIndicator ,
3+ Modal ,
4+ Pressable ,
5+ StyleSheet ,
6+ Text ,
7+ View ,
8+ } from 'react-native' ;
29import { WebView , type WebViewMessageEvent } from 'react-native-webview' ;
310import type { ContentpassLayerEvents } from './ContentpassLayerEvents' ;
411import buildFirstLayerUrl from './buildFirstLayerUrl' ;
5- import { useMemo , useState } from 'react' ;
12+ import { useCallback , useMemo , useState } from 'react' ;
613
714const MESSAGE_PROTOCOL = 'contentpass-first-layer' ;
815
@@ -52,18 +59,44 @@ const styles = StyleSheet.create({
5259 alignItems : 'center' ,
5360 justifyContent : 'center' ,
5461 } ,
62+ popupContainer : {
63+ flex : 1 ,
64+ backgroundColor : '#fff' ,
65+ } ,
66+ popupHeader : {
67+ flexDirection : 'row' ,
68+ justifyContent : 'flex-end' ,
69+ paddingHorizontal : 12 ,
70+ paddingVertical : 8 ,
71+ borderBottomWidth : StyleSheet . hairlineWidth ,
72+ borderBottomColor : '#ccc' ,
73+ } ,
74+ popupClose : {
75+ paddingHorizontal : 12 ,
76+ paddingVertical : 6 ,
77+ } ,
78+ popupCloseText : {
79+ fontSize : 16 ,
80+ fontWeight : '600' ,
81+ color : '#007AFF' ,
82+ } ,
83+ popupWebview : {
84+ flex : 1 ,
85+ } ,
5586} ) ;
5687
5788export default function ContentpassLayer ( {
5889 baseUrl,
5990 eventHandler,
91+ instanceId,
6092 planId,
6193 propertyId,
6294 purposesList,
6395 vendorCount,
6496} : {
6597 baseUrl : string ;
6698 eventHandler : ContentpassLayerEvents ;
99+ instanceId : string ;
67100 planId : string ;
68101 propertyId : string ;
69102 purposesList : string [ ] ;
@@ -80,6 +113,13 @@ export default function ContentpassLayer({
80113 } , [ baseUrl , planId , propertyId , purposesList , vendorCount ] ) ;
81114
82115 const [ ready , setReady ] = useState ( false ) ;
116+ const [ popupUrl , setPopupUrl ] = useState < string | null > ( null ) ;
117+
118+ const closePopup = useCallback ( ( ) => setPopupUrl ( null ) , [ ] ) ;
119+
120+ function buildFaqUrl ( ) : string {
121+ return `${ baseUrl } /auth/login?instanceId=${ encodeURIComponent ( instanceId ) } &propertyId=${ encodeURIComponent ( propertyId ) } &planId=${ encodeURIComponent ( planId ) } &route=faq` ;
122+ }
83123
84124 function handleMessage ( event : WebViewMessageEvent ) {
85125 let msg : any ;
@@ -118,6 +158,19 @@ export default function ContentpassLayer({
118158 msg . payload ?. options ?. page as 'login' | 'signup'
119159 ) ;
120160 break ;
161+ case 'faq' :
162+ setPopupUrl ( buildFaqUrl ( ) ) ;
163+ break ;
164+ case 'url' :
165+ if ( msg . payload ?. options ?. url ) {
166+ setPopupUrl ( msg . payload ?. options ?. url ) ;
167+ } else {
168+ console . warn (
169+ 'WebView message with unknown URL' ,
170+ msg . payload ?. options ?. url
171+ ) ;
172+ }
173+ break ;
121174 default :
122175 console . warn (
123176 'WebView message with unknown page' ,
@@ -167,8 +220,16 @@ export default function ContentpassLayer({
167220 handleMessage ( event ) ;
168221 } }
169222 onShouldStartLoadWithRequest = { ( request ) => {
170- console . debug ( 'WebView request' , request . url ) ;
171- return true ;
223+ // Prevent accidental redirects to external URLs
224+ const firstLayerHostname = new URL ( firstLayerUrl ) . hostname ;
225+ const requestedHostname = new URL ( request . url ) . hostname ;
226+ const allowed = requestedHostname === firstLayerHostname ;
227+ console . debug ( 'WebView request' , request . url , {
228+ allowed,
229+ firstLayerHostname,
230+ requestedHostname,
231+ } ) ;
232+ return allowed ;
172233 } }
173234 onLoadStart = { ( ) => {
174235 console . debug ( 'WebView load start' ) ;
@@ -198,6 +259,34 @@ export default function ContentpassLayer({
198259 < ActivityIndicator size = "large" />
199260 </ View >
200261 ) }
262+ < Modal
263+ visible = { popupUrl !== null }
264+ animationType = "slide"
265+ presentationStyle = "pageSheet"
266+ onRequestClose = { closePopup }
267+ >
268+ < View style = { styles . popupContainer } >
269+ < View style = { styles . popupHeader } >
270+ < Pressable onPress = { closePopup } style = { styles . popupClose } >
271+ < Text style = { styles . popupCloseText } > Close</ Text >
272+ </ Pressable >
273+ </ View >
274+ { popupUrl && (
275+ < WebView
276+ source = { { uri : popupUrl } }
277+ style = { styles . popupWebview }
278+ javaScriptEnabled
279+ domStorageEnabled
280+ onShouldStartLoadWithRequest = { ( request ) => {
281+ console . debug ( 'WebView popup request' , request . url ) ;
282+ // Allow any request to load in the popup, otherwise
283+ // we would block redirects to external URLs.
284+ return true ;
285+ } }
286+ />
287+ ) }
288+ </ View >
289+ </ Modal >
201290 </ View >
202291 ) ;
203292}
0 commit comments