ソースを参照

feat(webview props) onLoadProgresss property (#53)

* Added the onLoadProgress property to the iOS wkwebview and Android webview
黎明 5 年 前
コミット
b5aaf5c800

+ 1
- 0
README.md ファイルの表示

@@ -37,6 +37,7 @@ class MyWebComponent extends Component {
37 37
       <WebView
38 38
         source={{ uri: 'https://infinite.red/react-native' }}
39 39
         style={{ marginTop: 20 }}
40
+        onLoadProgress={e=>console.log(e.nativeEvent.progress)}
40 41
       />
41 42
     );
42 43
   }

+ 27
- 1
android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java ファイルの表示

@@ -54,6 +54,7 @@ import com.reactnativecommunity.webview.events.TopLoadingErrorEvent;
54 54
 import com.reactnativecommunity.webview.events.TopLoadingFinishEvent;
55 55
 import com.reactnativecommunity.webview.events.TopLoadingStartEvent;
56 56
 import com.reactnativecommunity.webview.events.TopMessageEvent;
57
+import com.reactnativecommunity.webview.events.TopLoadingProgressEvent;
57 58
 import java.io.UnsupportedEncodingException;
58 59
 import java.util.ArrayList;
59 60
 import java.util.HashMap;
@@ -74,7 +75,8 @@ import org.json.JSONObject;
74 75
  * {@link WebView} instances could emit following direct events:
75 76
  *  - topLoadingFinish
76 77
  *  - topLoadingStart
77
- *  - topLoadingError
78
+ *  - topLoadingStart
79
+ *  - topLoadingProgress
78 80
  *
79 81
  * Each event will carry the following properties:
80 82
  *  - target - view's react tag
@@ -409,6 +411,23 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
409 411
         return true;
410 412
       }
411 413
 
414
+
415
+    @Override
416
+    public void onProgressChanged(WebView webView, int newProgress) {
417
+        super.onProgressChanged(webView, newProgress);
418
+        WritableMap event = Arguments.createMap();
419
+        event.putDouble("target", webView.getId());
420
+        event.putString("title", webView.getTitle());
421
+        event.putBoolean("canGoBack", webView.canGoBack());
422
+        event.putBoolean("canGoForward", webView.canGoForward());
423
+        event.putDouble("progress", (float)newProgress/100);
424
+        dispatchEvent(
425
+                  webView,
426
+                  new TopLoadingProgressEvent(
427
+                      webView.getId(),
428
+                      event));
429
+    }
430
+
412 431
       @Override
413 432
       public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
414 433
         callback.invoke(origin, true, false);
@@ -623,6 +642,13 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
623 642
     view.setWebViewClient(new RNCWebViewClient());
624 643
   }
625 644
 
645
+  @Override
646
+  public Map getExportedCustomDirectEventTypeConstants() {
647
+    MapBuilder.Builder builder = MapBuilder.builder();
648
+    builder.put("topLoadingProgress", MapBuilder.of("registrationName", "onLoadingProgress"));
649
+    return builder.build();
650
+  }
651
+
626 652
   @Override
627 653
   public @Nullable Map<String, Integer> getCommandsMap() {
628 654
     return MapBuilder.of(

+ 36
- 0
android/src/main/java/com/reactnativecommunity/webview/events/TopLoadingProgressEvent.java ファイルの表示

@@ -0,0 +1,36 @@
1
+package com.reactnativecommunity.webview.events;
2
+
3
+import com.facebook.react.bridge.WritableMap;
4
+import com.facebook.react.uimanager.events.Event;
5
+import com.facebook.react.uimanager.events.RCTEventEmitter;
6
+
7
+public class TopLoadingProgressEvent extends Event<TopLoadingProgressEvent> {
8
+    public static final String EVENT_NAME = "topLoadingProgress";
9
+    private WritableMap mEventData;
10
+
11
+    public TopLoadingProgressEvent(int viewId, WritableMap eventData) {
12
+        super(viewId);
13
+        mEventData = eventData;
14
+    }
15
+
16
+    @Override
17
+    public String getEventName() {
18
+        return EVENT_NAME;
19
+    }
20
+
21
+    @Override
22
+    public boolean canCoalesce() {
23
+        return false;
24
+    }
25
+
26
+    @Override
27
+    public short getCoalescingKey() {
28
+        // All events for a given view can be coalesced.
29
+        return 0;
30
+    }
31
+
32
+    @Override
33
+    public void dispatch(RCTEventEmitter rctEventEmitter) {
34
+        rctEventEmitter.receiveEvent(getViewTag(), getEventName(), mEventData);
35
+    }
36
+}

+ 44
- 28
docs/Reference.md ファイルの表示

@@ -16,6 +16,7 @@ This document lays out the current public properties and methods for the React N
16 16
 - [`onLoad`](Reference.md#onload)
17 17
 - [`onLoadEnd`](Reference.md#onloadend)
18 18
 - [`onLoadStart`](Reference.md#onloadstart)
19
+- [`onLoadProgress`](Reference.md#onloadprogress)
19 20
 - [`onMessage`](Reference.md#onmessage)
20 21
 - [`onNavigationStateChange`](Reference.md#onnavigationstatechange)
21 22
 - [`originWhitelist`](Reference.md#originwhitelist)
@@ -44,11 +45,11 @@ This document lays out the current public properties and methods for the React N
44 45
 
45 46
 ## Methods Index
46 47
 
47
-* [`extraNativeComponentConfig`](Reference.md#extranativecomponentconfig)
48
-* [`goForward`](Reference.md#goforward)
49
-* [`goBack`](Reference.md#goback)
50
-* [`reload`](Reference.md#reload)
51
-* [`stopLoading`](Reference.md#stoploading)
48
+- [`extraNativeComponentConfig`](Reference.md#extranativecomponentconfig)
49
+- [`goForward`](Reference.md#goforward)
50
+- [`goBack`](Reference.md#goback)
51
+- [`reload`](Reference.md#reload)
52
+- [`stopLoading`](Reference.md#stoploading)
52 53
 
53 54
 ---
54 55
 
@@ -64,17 +65,17 @@ The object passed to `source` can have either of the following shapes:
64 65
 
65 66
 **Load uri**
66 67
 
67
-* `uri` (string) - The URI to load in the `WebView`. Can be a local or remote file.
68
-* `method` (string) - The HTTP Method to use. Defaults to GET if not specified. On Android, the only supported methods are GET and POST.
69
-* `headers` (object) - Additional HTTP headers to send with the request. On Android, this can only be used with GET requests.
70
-* `body` (string) - The HTTP body to send with the request. This must be a valid UTF-8 string, and will be sent exactly as specified, with no additional encoding (e.g. URL-escaping or base64) applied. On Android, this can only be used with POST requests.
68
+- `uri` (string) - The URI to load in the `WebView`. Can be a local or remote file.
69
+- `method` (string) - The HTTP Method to use. Defaults to GET if not specified. On Android, the only supported methods are GET and POST.
70
+- `headers` (object) - Additional HTTP headers to send with the request. On Android, this can only be used with GET requests.
71
+- `body` (string) - The HTTP body to send with the request. This must be a valid UTF-8 string, and will be sent exactly as specified, with no additional encoding (e.g. URL-escaping or base64) applied. On Android, this can only be used with POST requests.
71 72
 
72 73
 **Static HTML**
73 74
 
74 75
 _Note that using static HTML requires the WebView property [originWhiteList](Reference.md#originWhiteList) to `['*']`._
75 76
 
76
-* `html` (string) - A static HTML page to display in the WebView.
77
-* `baseUrl` (string) - The base URL to be used for any relative links in the HTML.
77
+- `html` (string) - A static HTML page to display in the WebView.
78
+- `baseUrl` (string) - The base URL to be used for any relative links in the HTML.
78 79
 
79 80
 | Type   | Required |
80 81
 | ------ | -------- |
@@ -128,9 +129,9 @@ Override the native component used to render the WebView. Enables a custom nativ
128 129
 
129 130
 The `nativeConfig` prop expects an object with the following keys:
130 131
 
131
-* `component` (any)
132
-* `props` (object)
133
-* `viewManager` (object)
132
+- `component` (any)
133
+- `props` (object)
134
+- `viewManager` (object)
134 135
 
135 136
 | Type   | Required |
136 137
 | ------ | -------- |
@@ -178,6 +179,21 @@ Function that is invoked when the `WebView` starts loading.
178 179
 
179 180
 ---
180 181
 
182
+### `onLoadProgress`
183
+
184
+Function that is invoked when the `WebView` is loading.
185
+
186
+> **_Note_**
187
+>
188
+> On iOS, when useWebKit=false, this prop will not work.
189
+> On android, You can't get the url property, meaning that `event.nativeEvent.url` will be null.
190
+
191
+| Type     | Required |
192
+| -------- | -------- |
193
+| function | No       |
194
+
195
+---
196
+
181 197
 ### `onMessage`
182 198
 
183 199
 A function that is invoked when the webview calls `window.postMessage`. Setting this property will inject a `postMessage` global into your webview, but will still call pre-existing values of `postMessage`.
@@ -266,8 +282,8 @@ Boolean value that forces the `WebView` to show the loading view on the first lo
266 282
 
267 283
 A floating-point number that determines how quickly the scroll view decelerates after the user lifts their finger. You may also use the string shortcuts `"normal"` and `"fast"` which match the underlying iOS settings for `UIScrollViewDecelerationRateNormal` and `UIScrollViewDecelerationRateFast` respectively:
268 284
 
269
-* normal: 0.998
270
-* fast: 0.99 (the default for iOS web view)
285
+- normal: 0.998
286
+- fast: 0.99 (the default for iOS web view)
271 287
 
272 288
 | Type   | Required | Platform |
273 289
 | ------ | -------- | -------- |
@@ -301,9 +317,9 @@ Specifies the mixed content mode. i.e WebView will allow a secure origin to load
301 317
 
302 318
 Possible values for `mixedContentMode` are:
303 319
 
304
-* `never` (default) - WebView will not allow a secure origin to load content from an insecure origin.
305
-* `always` - WebView will allow a secure origin to load content from any other origin, even if that origin is insecure.
306
-* `compatibility` - WebView will attempt to be compatible with the approach of a modern web browser with regard to mixed content.
320
+- `never` (default) - WebView will not allow a secure origin to load content from an insecure origin.
321
+- `always` - WebView will allow a secure origin to load content from any other origin, even if that origin is insecure.
322
+- `compatibility` - WebView will attempt to be compatible with the approach of a modern web browser with regard to mixed content.
307 323
 
308 324
 | Type   | Required | Platform |
309 325
 | ------ | -------- | -------- |
@@ -373,18 +389,18 @@ You can provide one type or an array of many types.
373 389
 
374 390
 Possible values for `dataDetectorTypes` are:
375 391
 
376
-* `phoneNumber`
377
-* `link`
378
-* `address`
379
-* `calendarEvent`
380
-* `none`
381
-* `all`
392
+- `phoneNumber`
393
+- `link`
394
+- `address`
395
+- `calendarEvent`
396
+- `none`
397
+- `all`
382 398
 
383 399
 With the [new WebKit](Reference.md#usewebkit) implementation, we have three new values:
384 400
 
385
-* `trackingNumber`
386
-* `flightNumber`
387
-* `lookupSuggestion`
401
+- `trackingNumber`
402
+- `flightNumber`
403
+- `lookupSuggestion`
388 404
 
389 405
 | Type             | Required | Platform |
390 406
 | ---------------- | -------- | -------- |

+ 17
- 1
ios/RNCWKWebView.m ファイルの表示

@@ -15,6 +15,7 @@ static NSString *const MessageHanderName = @"ReactNative";
15 15
 @property (nonatomic, copy) RCTDirectEventBlock onLoadingStart;
16 16
 @property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish;
17 17
 @property (nonatomic, copy) RCTDirectEventBlock onLoadingError;
18
+@property (nonatomic, copy) RCTDirectEventBlock onLoadingProgress;
18 19
 @property (nonatomic, copy) RCTDirectEventBlock onShouldStartLoadWithRequest;
19 20
 @property (nonatomic, copy) RCTDirectEventBlock onMessage;
20 21
 @property (nonatomic, copy) WKWebView *webView;
@@ -27,7 +28,9 @@ static NSString *const MessageHanderName = @"ReactNative";
27 28
 
28 29
 - (void)dealloc
29 30
 {
30
-
31
+    if(_webView){
32
+        [_webView removeObserver:self forKeyPath:@"estimatedProgress"];
33
+    }
31 34
 }
32 35
 
33 36
 /**
@@ -85,6 +88,7 @@ static NSString *const MessageHanderName = @"ReactNative";
85 88
     _webView.navigationDelegate = self;
86 89
     _webView.scrollView.scrollEnabled = _scrollEnabled;
87 90
     _webView.scrollView.bounces = _bounces;
91
+    [_webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
88 92
 
89 93
 #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
90 94
     if ([_webView.scrollView respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) {
@@ -100,6 +104,18 @@ static NSString *const MessageHanderName = @"ReactNative";
100 104
   }
101 105
 }
102 106
 
107
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
108
+    if ([keyPath isEqual:@"estimatedProgress"] && object == self.webView) {
109
+        if(_onLoadingProgress){
110
+             NSMutableDictionary<NSString *, id> *event = [self baseEvent];
111
+            [event addEntriesFromDictionary:@{@"progress":[NSNumber numberWithDouble:self.webView.estimatedProgress]}];
112
+            _onLoadingProgress(event);
113
+        }
114
+    }else{
115
+        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
116
+    }
117
+}
118
+
103 119
 - (void)setBackgroundColor:(UIColor *)backgroundColor
104 120
 {
105 121
   _savedBackgroundColor = backgroundColor;

+ 1
- 0
ios/RNCWKWebViewManager.m ファイルの表示

@@ -33,6 +33,7 @@ RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary)
33 33
 RCT_EXPORT_VIEW_PROPERTY(onLoadingStart, RCTDirectEventBlock)
34 34
 RCT_EXPORT_VIEW_PROPERTY(onLoadingFinish, RCTDirectEventBlock)
35 35
 RCT_EXPORT_VIEW_PROPERTY(onLoadingError, RCTDirectEventBlock)
36
+RCT_EXPORT_VIEW_PROPERTY(onLoadingProgress, RCTDirectEventBlock)
36 37
 RCT_EXPORT_VIEW_PROPERTY(onShouldStartLoadWithRequest, RCTDirectEventBlock)
37 38
 RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString)
38 39
 RCT_EXPORT_VIEW_PROPERTY(allowsInlineMediaPlayback, BOOL)

+ 7
- 0
js/WebView.android.js ファイルの表示

@@ -35,6 +35,7 @@ import type {
35 35
   WebViewNavigationEvent,
36 36
   WebViewSharedProps,
37 37
   WebViewSource,
38
+  WebViewProgressEvent,
38 39
 } from './WebViewTypes';
39 40
 
40 41
 const resolveAssetSource = Image.resolveAssetSource;
@@ -152,6 +153,7 @@ class WebView extends React.Component<WebViewSharedProps, State> {
152 153
         onLoadingStart={this.onLoadingStart}
153 154
         onLoadingFinish={this.onLoadingFinish}
154 155
         onLoadingError={this.onLoadingError}
156
+        onLoadingProgress={this.onLoadingProgress}
155 157
         testID={this.props.testID}
156 158
         geolocationEnabled={this.props.geolocationEnabled}
157 159
         mediaPlaybackRequiresUserAction={
@@ -280,6 +282,11 @@ class WebView extends React.Component<WebViewSharedProps, State> {
280 282
     const { onMessage } = this.props;
281 283
     onMessage && onMessage(event);
282 284
   };
285
+  
286
+  onLoadingProgress = (event: WebViewProgressEvent) => {
287
+    const { onLoadProgress} = this.props;
288
+    onLoadProgress && onLoadProgress(event);
289
+  }
283 290
 }
284 291
 
285 292
 const RNCWebView = requireNativeComponent('RNCWebView');

+ 7
- 0
js/WebView.ios.js ファイルの表示

@@ -34,6 +34,7 @@ import type {
34 34
   WebViewNavigationEvent,
35 35
   WebViewSharedProps,
36 36
   WebViewSource,
37
+  WebViewProgressEvent,
37 38
 } from './WebViewTypes';
38 39
 
39 40
 const resolveAssetSource = Image.resolveAssetSource;
@@ -263,6 +264,7 @@ class WebView extends React.Component<WebViewSharedProps, State> {
263 264
         onLoadingStart={this._onLoadingStart}
264 265
         onLoadingFinish={this._onLoadingFinish}
265 266
         onLoadingError={this._onLoadingError}
267
+        onLoadingProgress={this._onLoadingProgress}
266 268
         messagingEnabled={messagingEnabled}
267 269
         onMessage={this._onMessage}
268 270
         onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
@@ -420,6 +422,11 @@ class WebView extends React.Component<WebViewSharedProps, State> {
420 422
     onMessage && onMessage(event);
421 423
   };
422 424
 
425
+  _onLoadingProgress = (event: WebViewProgressEvent) => {
426
+    const {onLoadProgress} = this.props;
427
+    onLoadProgress && onLoadProgress(event);
428
+  }
429
+
423 430
   componentDidUpdate(prevProps: WebViewSharedProps) {
424 431
     if (!(prevProps.useWebKit && this.props.useWebKit)) {
425 432
       return;

+ 10
- 0
js/WebViewTypes.js ファイルの表示

@@ -25,6 +25,11 @@ export type WebViewNativeEvent = $ReadOnly<{|
25 25
   canGoForward: boolean,
26 26
 |}>;
27 27
 
28
+export type WebViewProgressEvent = $ReadOnly<{|
29
+    ...WebViewNativeEvent,
30
+    progress: number,
31
+|}>
32
+
28 33
 export type WebViewNavigation = $ReadOnly<{|
29 34
   ...WebViewNativeEvent,
30 35
   navigationType:
@@ -365,6 +370,11 @@ export type WebViewSharedProps =  $ReadOnly<{|
365 370
    */
366 371
   onMessage?: (event: WebViewMessageEvent) => mixed,
367 372
 
373
+  /**
374
+   * Function that is invoked when the `WebView` is loading.
375
+   */
376
+  onLoadProgress?: (event: WebViewProgressEvent) => mixed,
377
+
368 378
   /**
369 379
    * Boolean value that forces the `WebView` to show the loading view
370 380
    * on the first load.