feat(new prop): onHttpError callback (#885)

This commit is contained in:
Jason Chia-Hsien Ho 2019-09-20 17:43:28 -07:00 committed by Thibault Malbranche
parent 33f1ee384f
commit 552472c414
8 changed files with 150 additions and 0 deletions

View File

@ -32,6 +32,7 @@ import android.webkit.URLUtil;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
@ -58,6 +59,7 @@ import com.facebook.react.uimanager.events.ContentSizeChangeEvent;
import com.facebook.react.uimanager.events.Event;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.reactnativecommunity.webview.events.TopLoadingErrorEvent;
import com.reactnativecommunity.webview.events.TopHttpErrorEvent;
import com.reactnativecommunity.webview.events.TopLoadingFinishEvent;
import com.reactnativecommunity.webview.events.TopLoadingProgressEvent;
import com.reactnativecommunity.webview.events.TopLoadingStartEvent;
@ -512,6 +514,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
export.put(TopLoadingProgressEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingProgress"));
export.put(TopShouldStartLoadWithRequestEvent.EVENT_NAME, MapBuilder.of("registrationName", "onShouldStartLoadWithRequest"));
export.put(ScrollEventType.getJSEventName(ScrollEventType.SCROLL), MapBuilder.of("registrationName", "onScroll"));
export.put(TopHttpErrorEvent.EVENT_NAME, MapBuilder.of("registrationName", "onHttpError"));
return export;
}
@ -725,6 +728,25 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
new TopLoadingErrorEvent(webView.getId(), eventData));
}
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
public void onReceivedHttpError(
WebView webView,
WebResourceRequest request,
WebResourceResponse errorResponse) {
super.onReceivedHttpError(webView, request, errorResponse);
if (request.isForMainFrame()) {
WritableMap eventData = createWebViewEvent(webView, request.getUrl().toString());
eventData.putInt("statusCode", errorResponse.getStatusCode());
eventData.putString("description", errorResponse.getReasonPhrase());
dispatchEvent(
webView,
new TopHttpErrorEvent(webView.getId(), eventData));
}
}
protected void emitFinishEvent(WebView webView, String url) {
dispatchEvent(
webView,

View File

@ -0,0 +1,25 @@
package com.reactnativecommunity.webview.events
import com.facebook.react.bridge.WritableMap
import com.facebook.react.uimanager.events.Event
import com.facebook.react.uimanager.events.RCTEventEmitter
/**
* Event emitted when a http error is received from the server.
*/
class TopHttpErrorEvent(viewId: Int, private val mEventData: WritableMap) :
Event<TopHttpErrorEvent>(viewId) {
companion object {
const val EVENT_NAME = "topHttpError"
}
override fun getEventName(): String = EVENT_NAME
override fun canCoalesce(): Boolean = false
override fun getCoalescingKey(): Short = 0
override fun dispatch(rctEventEmitter: RCTEventEmitter) =
rctEventEmitter.receiveEvent(viewTag, eventName, mEventData)
}

View File

@ -14,6 +14,7 @@ This document lays out the current public properties and methods for the React N
- [`onLoadEnd`](Reference.md#onloadend)
- [`onLoadStart`](Reference.md#onloadstart)
- [`onLoadProgress`](Reference.md#onloadprogress)
- [`onHttpError`](Reference.md#onhttperror)
- [`onMessage`](Reference.md#onmessage)
- [`onNavigationStateChange`](Reference.md#onnavigationstatechange)
- [`originWhitelist`](Reference.md#originwhitelist)
@ -342,6 +343,46 @@ url
---
### `onHttpError`
Function that is invoked when the `WebView` receives an http error.
> **_Note_**
> Android API minimum level 23.
| Type | Required |
| -------- | -------- |
| function | No |
Example:
```jsx
<WebView
source={{ uri: 'https://facebook.github.io/react-native' }}
onHttpError={syntheticEvent => {
const { nativeEvent } = syntheticEvent;
console.warn('WebView received error status code: ', nativeEvent.statusCode);
}}
/>
```
Function passed to `onHttpError` is called with a SyntheticEvent wrapping a nativeEvent with these properties:
```
canGoBack
canGoForward
description
loading
statusCode
target
title
url
```
> **_Note_**
> Description is only used on Android
---
### `onMessage`
Function that is invoked when the webview calls `window.ReactNativeWebView.postMessage`. Setting this property will inject this global into your webview.

View File

@ -33,6 +33,7 @@ static NSURLCredential* clientAuthenticationCredential;
@property (nonatomic, copy) RCTDirectEventBlock onLoadingError;
@property (nonatomic, copy) RCTDirectEventBlock onLoadingProgress;
@property (nonatomic, copy) RCTDirectEventBlock onShouldStartLoadWithRequest;
@property (nonatomic, copy) RCTDirectEventBlock onHttpError;
@property (nonatomic, copy) RCTDirectEventBlock onMessage;
@property (nonatomic, copy) RCTDirectEventBlock onScroll;
@property (nonatomic, copy) WKWebView *webView;
@ -806,6 +807,34 @@ static NSURLCredential* clientAuthenticationCredential;
decisionHandler(WKNavigationResponsePolicyAllow);
}
/**
* Decides whether to allow or cancel a navigation after its response is known.
* @see https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview?language=objc
*/
- (void) webView:(WKWebView *)webView
decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse
decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
{
if (_onHttpError && navigationResponse.forMainFrame) {
if ([navigationResponse.response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response;
NSInteger statusCode = response.statusCode;
if (statusCode >= 400) {
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
[event addEntriesFromDictionary: @{
@"url": response.URL.absoluteString,
@"statusCode": @(statusCode)
}];
_onHttpError(event);
}
}
}
decisionHandler(WKNavigationResponsePolicyAllow);
}
/**
* Called when an error occurs while the web view is loading content.
* @see https://fburl.com/km6vqenw

View File

@ -47,6 +47,7 @@ RCT_EXPORT_VIEW_PROPERTY(onLoadingStart, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLoadingFinish, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLoadingError, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLoadingProgress, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onHttpError, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onShouldStartLoadWithRequest, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString)
RCT_EXPORT_VIEW_PROPERTY(javaScriptEnabled, BOOL)

View File

@ -20,6 +20,7 @@ import {
} from './WebViewShared';
import {
WebViewErrorEvent,
WebViewHttpErrorEvent,
WebViewMessageEvent,
WebViewNavigationEvent,
WebViewProgressEvent,
@ -178,6 +179,13 @@ class WebView extends React.Component<AndroidWebViewProps, State> {
});
};
onHttpError = (event: WebViewHttpErrorEvent) => {
const { onHttpError } = this.props;
if (onHttpError) {
onHttpError(event);
}
}
onLoadingFinish = (event: WebViewNavigationEvent) => {
const { onLoad, onLoadEnd } = this.props;
if (onLoad) {
@ -281,6 +289,7 @@ class WebView extends React.Component<AndroidWebViewProps, State> {
onLoadingFinish={this.onLoadingFinish}
onLoadingProgress={this.onLoadingProgress}
onLoadingStart={this.onLoadingStart}
onHttpError={this.onHttpError}
onMessage={this.onMessage}
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
ref={this.webViewRef}

View File

@ -18,6 +18,7 @@ import {
} from './WebViewShared';
import {
WebViewErrorEvent,
WebViewHttpErrorEvent,
WebViewMessageEvent,
WebViewNavigationEvent,
WebViewProgressEvent,
@ -207,6 +208,13 @@ class WebView extends React.Component<IOSWebViewProps, State> {
});
};
onHttpError = (event: WebViewHttpErrorEvent) => {
const { onHttpError } = this.props;
if (onHttpError) {
onHttpError(event);
}
}
onLoadingFinish = (event: WebViewNavigationEvent) => {
const { onLoad, onLoadEnd } = this.props;
if (onLoad) {
@ -321,6 +329,7 @@ class WebView extends React.Component<IOSWebViewProps, State> {
onLoadingFinish={this.onLoadingFinish}
onLoadingProgress={this.onLoadingProgress}
onLoadingStart={this.onLoadingStart}
onHttpError={this.onHttpError}
onMessage={this.onMessage}
onScroll={this.props.onScroll}
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}

View File

@ -109,6 +109,11 @@ export interface WebViewError extends WebViewNativeEvent {
description: string;
}
export interface WebViewHttpError extends WebViewNativeEvent {
description: string;
statusCode: number;
}
export type WebViewEvent = NativeSyntheticEvent<WebViewNativeEvent>;
export type WebViewProgressEvent = NativeSyntheticEvent<
@ -121,6 +126,8 @@ export type WebViewMessageEvent = NativeSyntheticEvent<WebViewMessage>;
export type WebViewErrorEvent = NativeSyntheticEvent<WebViewError>;
export type WebViewHttpErrorEvent = NativeSyntheticEvent<WebViewHttpError>;
export type DataDetectorTypes =
| 'phoneNumber'
| 'link'
@ -210,6 +217,7 @@ export interface CommonNativeWebViewProps extends ViewProps {
onLoadingFinish: (event: WebViewNavigationEvent) => void;
onLoadingProgress: (event: WebViewProgressEvent) => void;
onLoadingStart: (event: WebViewNavigationEvent) => void;
onHttpError: (event: WebViewHttpErrorEvent) => void;
onMessage: (event: WebViewMessageEvent) => void;
onShouldStartLoadWithRequest: (event: WebViewNavigationEvent) => void;
showsHorizontalScrollIndicator?: boolean;
@ -598,6 +606,12 @@ export interface WebViewSharedProps extends ViewProps {
*/
onError?: (event: WebViewErrorEvent) => void;
/**
* Function that is invoked when the `WebView` receives an error status code.
* Works on iOS and Android (minimum API level 23).
*/
onHttpError?: (event: WebViewHttpErrorEvent) => void;
/**
* Function that is invoked when the `WebView` loading starts or ends.
*/