diff --git a/src/control/content/app.js b/src/control/content/app.js index 6c7bdce..eecf156 100644 --- a/src/control/content/app.js +++ b/src/control/content/app.js @@ -14,6 +14,11 @@ EXTERNAL_BROWSER: 'External browser' }; + $scope.viewSubtype = { + IFRAME: 'iFrame', + NATIVE_WEBVIEW: 'Native webview' + }; + buildfire.datastore.get(function(err, result) { if (err) return console.error('Error: ', err); @@ -32,11 +37,17 @@ $scope.data.content.view = $scope.viewType.IN_APP_POPUP; } } + + // Backward compatibility: If viewType.NATIVE_IN_APP was selected before, default to viewSubtype.IFRAME + if ($scope.data.content.view === $scope.viewType.NATIVE_IN_APP && !$scope.data.content.viewSubtype) { + $scope.data.content.viewSubtype = $scope.viewSubtype.IFRAME; + } } else { $scope.data = { content: { url: '', - view: $scope.viewType.IN_APP_POPUP + view: $scope.viewType.IN_APP_POPUP, + viewSubtype: null // Initialize viewSubtype } }; } @@ -118,22 +129,26 @@ }; $scope.changeViewType = function() { - dataChanged = true; - - if ($scope.frmMain.$invalid) return; + dataChanged = true; + + if ($scope.frmMain.$invalid) return; - var data = $scope.data; + if ($scope.data.content.view === $scope.viewType.NATIVE_IN_APP && !$scope.data.content.viewSubtype) { + $scope.data.content.viewSubtype = $scope.viewSubtype.IFRAME; + } - if (data.content.openInApp != undefined) { - data.content.openInApp = null; - } - buildfire.datastore.save(data, function(err, result) { - if (err || !result) { - $log.error('Error saving the widget details: ', err); - } else { - $log.info('Widget details saved'); - } - }); + var data = $scope.data; + + if (data.content.openInApp != undefined) { + data.content.openInApp = null; + } + buildfire.datastore.save(data, function(err, result) { + if (err || !result) { + $log.error('Error saving the widget details: ', err); + } else { + $log.info('Widget details saved'); + } + }); }; $scope.openMethodChanged = function() { diff --git a/src/control/content/index.html b/src/control/content/index.html index 78945f5..4bf7081 100644 --- a/src/control/content/index.html +++ b/src/control/content/index.html @@ -17,12 +17,19 @@ display: block !important; padding-bottom: 5px; } - p.info-note { + .note { + padding: 12px 20px; margin-top: 15px; + margin-bottom: 15px; + background: #F5F5F5; } - p.info-note.text-warning { - color: var(--c-warning); - background-color: var(--c-gray1); + .note .bold { + font-weight: 600; + } + .image-container img { + margin: 10px; + border-radius: 8px; + box-shadow: 0 0 8px 2px rgba(0,0,0,0.10); } @@ -73,6 +80,20 @@
+
+ Native Webview Preview + Popup Window Preview + External Browser Preview +
-

- Some websites may not function properly in the in-feature WebView due to Apple's restrictions on cross-origin iframes, which can affect login and state management, or due to internal policies of certain sites like Google and Amazon that prevent them from being displayed in the app’s native component. If your web page does not appear or work as expected, we recommend using one of the alternative methods available. -

+
+
+ + +
+
+ + +
+
+
+ Some websites may not work correctly when opened inside the app, especially in the built-in browser view. This is often due to Apple’s security rules or website settings from companies like Google and Amazon that block their pages from being shown in this way. If your webpage doesn’t load or work as expected, we recommend trying one of the other viewing options available. +
+
+ Native Webview is a new feature that works around iFrame issues. If the iFrame option isn’t working, try using Native Webview instead. +
Note: This feature will only work for apps with builds submitted to Apple App Store and Google Play Store after the 5, August 2025. For older builds the app will default to "In Popup Window" option.
+
-
- -

Note 1: For the best user experience, link to - mobile optimized content.
(Disclaimer: Some websites will show - mobile version of site only on mobile devices.)

- -

Note 2: If you are taking payments through - this web page, you must open the the URL outside of the app or the app - will get rejected by Apple in the submission review process.

- -

Note 3: It is best to use secure websites (Https) some operating systems require it.

- -

Hint: If you are taking payments, use your - app or content DeepLink URL to redirect back to the app after a payment - is made.

+
+
For the best experience, link to mobile-optimized and secure (HTTPS) content. If your webpage includes payment processing, it must open outside of the app—otherwise, Apple may reject your app during review. Also, be aware that some websites only display their mobile layout on actual mobile devices.
+
Note: If you’re accepting payments, use a Deep Link to redirect users back into your app after the transaction is complete.
+
+
diff --git a/src/resources/external-browser.jpg b/src/resources/external-browser.jpg new file mode 100644 index 0000000..29a46cb Binary files /dev/null and b/src/resources/external-browser.jpg differ diff --git a/src/resources/native-webview.jpg b/src/resources/native-webview.jpg new file mode 100644 index 0000000..3f8ad52 Binary files /dev/null and b/src/resources/native-webview.jpg differ diff --git a/src/resources/popup-window.jpg b/src/resources/popup-window.jpg new file mode 100644 index 0000000..0f125fe Binary files /dev/null and b/src/resources/popup-window.jpg differ diff --git a/src/widget/index.html b/src/widget/index.html index 1727ddf..2d546cf 100755 --- a/src/widget/index.html +++ b/src/widget/index.html @@ -29,6 +29,13 @@ .modal-header, .cancel-confirmation { display: none; } + #webviewReload { + display: none; + width: 100%; + height: 100%; + align-items: center; + justify-content: center; + } @@ -42,5 +49,9 @@

Success!

+ +
+

Click here to reload

+
diff --git a/src/widget/widget.js b/src/widget/widget.js index 8ca689e..57340f3 100644 --- a/src/widget/widget.js +++ b/src/widget/widget.js @@ -4,7 +4,8 @@ const { formatSSO, formatOAuth } = require('./formatSSO'); const viewOptions = { POPUP: 'In app popup', NATIVE: 'Native In App', - EXTERNAL: 'External browser' + EXTERNAL: 'External browser', + NATIVE_WEBVIEW: 'Native webview', }; const flags = {}; @@ -12,6 +13,7 @@ const flags = {}; const setFlags = (content) => { flags.isWeb = (buildfire.context.device.platform == 'web'); flags.shouldOpenInApp = (content.view == viewOptions.NATIVE); + flags.shouldOpenPluginInboundWebview = (content.view == viewOptions.NATIVE && content.viewSubtype == viewOptions.NATIVE_WEBVIEW); flags.isLiveMode = buildfire.context.liveMode; flags.isNotCP = (flags.isLiveMode === 1 || !flags.isWeb); flags.requiresSSO = content.url && content.url.indexOf('{{SSO}}') > 0; @@ -20,37 +22,45 @@ const setFlags = (content) => { const render = (content) => { - const handleWindow = (openWindow, displayIniFrame, displaySuccessMessage) => { + const handleWindow = ({openWindow, displayIniFrame, shouldOpenPluginInboundWebview, displaySuccessMessage}) => { if(openWindow){ setTimeout(() => buildfire.navigation.goBack(), 750); if(content.view === viewOptions.POPUP){ if(flags.isWeb){ renderiFrame({url: content.url, isIOS: flags.isIOS}); - return; } else { buildfire.navigation.openWindow(content.url, "_blank"); } } else buildfire.navigation.openWindow(content.url, "_system"); - - return; - } - if(displayIniFrame){ + } else if (shouldOpenPluginInboundWebview) { + if(flags.isWeb){ + renderiFrame({url: content.url, isIOS: flags.isIOS}); + } else { + // Show the title bar and open the window + buildfire.appearance.titlebar.show(null, (err) => { + if (err) return console.error(err); + buildfire.navigation.openWindow({url: content.url, target: "_plugin", windowFeatures: "pushToHistory=true"}); + buildfire.messaging.onReceivedBroadcast = (event) => { + if (event.message === 'webview hidden') { + document.getElementById('webviewReload').style.display = 'none'; + } else if (event.message === 'webview closed') { + document.getElementById('webviewReload').style.display = 'flex'; + } + }; + }); + } + } else if(displayIniFrame){ renderiFrame({url: content.url, isIOS: flags.isIOS}); - return; - } - if(displaySuccessMessage){ + } else if(displaySuccessMessage){ if(flags.isWeb){ renderiFrame({url: content.url, isIOS: flags.isIOS}); - return; } else { window.document.getElementById('successMessage').style.display = 'block'; window.document.getElementById('targetUrl').href = content.url; - return; } - } }; @@ -59,26 +69,27 @@ const render = (content) => { setFlags(content); const displayIniFrame = flags.shouldOpenInApp; //on the device and open native const openWindow = flags.isNotCP && !flags.shouldOpenInApp; //on the device and open in pop up or native brow + const shouldOpenPluginInboundWebview = flags.isNotCP && flags.shouldOpenPluginInboundWebview; //on the device and open in webview const displaySuccessMessage = content.url && flags.isWeb && !flags.isLiveMode; if (flags.requiresSSO) { //This is an SSO webview with an access token buildfire.auth.getCurrentUser((err, result) => { if (result && result.SSO && result.SSO.accessToken) { content.url = formatSSO(content.url, JSON.stringify(result.SSO)); - handleWindow(openWindow, displayIniFrame, displaySuccessMessage); + handleWindow({openWindow, displayIniFrame, shouldOpenPluginInboundWebview, displaySuccessMessage}); } else{ if (result && result.oauthProfile && result.oauthProfile.accessToken) { content.url = formatOAuth(content.url, result.oauthProfile.accessToken); - handleWindow(openWindow, displayIniFrame, displaySuccessMessage); + handleWindow({openWindow, displayIniFrame, shouldOpenPluginInboundWebview, displaySuccessMessage}); } else{ - handleWindow(openWindow, displayIniFrame, displaySuccessMessage); + handleWindow({openWindow, displayIniFrame, shouldOpenPluginInboundWebview, displaySuccessMessage}); } } }); } else { //this is all other URLs, i.e. no SSO. - handleWindow(openWindow, displayIniFrame, displaySuccessMessage); + handleWindow({openWindow, displayIniFrame, shouldOpenPluginInboundWebview, displaySuccessMessage}); } };