diff --git a/harmony/rn_webview.har b/harmony/rn_webview.har index 32f2bff3b..f97b1b934 100644 Binary files a/harmony/rn_webview.har and b/harmony/rn_webview.har differ diff --git a/harmony/rn_webview/hvigorfile.ts b/harmony/rn_webview/hvigorfile.ts index 421870714..120de5c39 100644 --- a/harmony/rn_webview/hvigorfile.ts +++ b/harmony/rn_webview/hvigorfile.ts @@ -1,6 +1 @@ -import { harTasks } from '@ohos/hvigor-ohos-plugin'; - -export default { - system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ - plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ -} +export { harTasks } from '@ohos/hvigor-ohos-plugin'; \ No newline at end of file diff --git a/harmony/rn_webview/index.ets b/harmony/rn_webview/index.ets index 01ae21bf3..8b2c9b830 100644 --- a/harmony/rn_webview/index.ets +++ b/harmony/rn_webview/index.ets @@ -1,7 +1,7 @@ -/* +/** * MIT License * - * Copyright (C) 2023 Huawei Device Co., Ltd. + * Copyright (C) 2024 Huawei Device Co., Ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,5 +22,6 @@ * SOFTWARE. */ -export * from './src/main/ets/WebView' +export * from './src/main/ets/RNCWebView' + export * from './ts' \ No newline at end of file diff --git a/harmony/rn_webview/oh-package.json5 b/harmony/rn_webview/oh-package.json5 index f9fe57693..44ba38fd3 100644 --- a/harmony/rn_webview/oh-package.json5 +++ b/harmony/rn_webview/oh-package.json5 @@ -6,8 +6,8 @@ "name": "@react-native-oh-tpl/react-native-webview", "description": "main cpai architecture", "main": "index.ets", - "version": "13.6.3-0.1.4", + "version": "13.10.2-0.0.1", "dependencies": { "@rnoh/react-native-openharmony": "file:../react_native_openharmony" } -} +} \ No newline at end of file diff --git a/harmony/rn_webview/src/main/cpp/WebViewJSIBinder.h b/harmony/rn_webview/src/main/cpp/WebViewJSIBinder.h index 7d5638328..90fa0e087 100644 --- a/harmony/rn_webview/src/main/cpp/WebViewJSIBinder.h +++ b/harmony/rn_webview/src/main/cpp/WebViewJSIBinder.h @@ -31,6 +31,7 @@ namespace rnoh { { auto object = ViewComponentJSIBinder::createNativeProps(rt); object.setProperty(rt, "newSource", "object"); + object.setProperty(rt, "bounces", "bool"); object.setProperty(rt, "javaScriptEnabled", "bool"); object.setProperty(rt, "injectedJavaScript", "string"); object.setProperty(rt, "messagingEnabled", "bool"); @@ -38,6 +39,7 @@ namespace rnoh { object.setProperty(rt, "showsVerticalScrollIndicator", "bool"); object.setProperty(rt, "textZoom", "int"); object.setProperty(rt, "cacheEnabled", "bool"); + object.setProperty(rt, "dataDetectorTypes", "Array"); object.setProperty(rt, "cacheMode", "string"); object.setProperty(rt, "domStorageEnabled", "bool"); object.setProperty(rt, "scalesPageToFit", "bool"); diff --git a/harmony/rn_webview/src/main/cpp/WebViewNapiBinder.h b/harmony/rn_webview/src/main/cpp/WebViewNapiBinder.h index d0db89d87..d4bb55b02 100644 --- a/harmony/rn_webview/src/main/cpp/WebViewNapiBinder.h +++ b/harmony/rn_webview/src/main/cpp/WebViewNapiBinder.h @@ -45,6 +45,7 @@ namespace rnoh { return objectBuilder.addProperty("newSource", sourceObject.build()) .addProperty("javaScriptEnabled", props->javaScriptEnabled) .addProperty("injectedJavaScript", props->injectedJavaScript) + .addProperty("bounces", props->bounces) .addProperty("messagingEnabled", props->messagingEnabled) .addProperty("showsHorizontalScrollIndicator", props->showsHorizontalScrollIndicator) .addProperty("showsVerticalScrollIndicator", props->showsVerticalScrollIndicator) diff --git a/harmony/rn_webview/src/main/ets/WebView.ets b/harmony/rn_webview/src/main/ets/RNCWebView.ets similarity index 52% rename from harmony/rn_webview/src/main/ets/WebView.ets rename to harmony/rn_webview/src/main/ets/RNCWebView.ets index c13ff3d04..5d55f259d 100644 --- a/harmony/rn_webview/src/main/ets/WebView.ets +++ b/harmony/rn_webview/src/main/ets/RNCWebView.ets @@ -1,7 +1,7 @@ -/* +/** * MIT License * - * Copyright (C) 2023 Huawei Device Co., Ltd. + * Copyright (C) 2024 Huawei Device Co., Ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,17 +22,22 @@ * SOFTWARE. */ -import { Descriptor, ComponentBuilderContext, RNOHContext, ViewBaseProps } from '@rnoh/react-native-openharmony' +import { Descriptor, RNOHContext, ViewBaseProps } from '@rnoh/react-native-openharmony'; import webview from '@ohos.web.webview'; import { CallbackState, ShouldRequestUrl } from './ShouldRequestUrl'; -import { RNViewBase } from '@rnoh/react-native-openharmony'; -import Logger from './Logger' +import { RNC } from '@rnoh/react-native-openharmony/generated'; +import Logger from './Logger'; + +export class WebViewNewSourceHeader { + name?: string + value?: string +} export class WebViewNewSource { - uri: string | Resource = "" + uri?: string method?: string body?: string - headers?: string + headers?: WebViewNewSourceHeader[] html?: string baseUrl?: string } @@ -59,12 +64,12 @@ export interface WebViewProps extends ViewBaseProps { shouldStartLoadWithRequestEnabled: boolean webviewDebuggingEnabled: boolean // nestedScrollEnabled: boolean + scrollEnabled: boolean } export class RNCWebViewBridge { postMessage!: (data: string) => void; } - export class WebViewEventParams { type: string url?: string @@ -80,18 +85,44 @@ export class WebViewEventParams { } } +export enum CACHE_MODE { + 'LOAD_DEFAULT' = 'LOAD_DEFAULT', + 'LOAD_CACHE_ELSE_NETWORK' = 'LOAD_CACHE_ELSE_NETWORK' , + 'LOAD_NO_CACHE' = 'LOAD_NO_CACHE' , + 'LOAD_CACHE_ONLY' = 'LOAD_CACHE_ONLY', +} + +export class ResultType { + url: string + loading: boolean + title: string + canGoBack: boolean + canGoForward: boolean + lockIdentifier: number + data: string + + constructor(url: string, loading: boolean, title: string, canGoBack: boolean, canGoForward: boolean, lockIdentifier: number, data: string) { + this.url = url + this.loading = loading + this.title = title + this.canGoBack = canGoBack + this.canGoForward = canGoForward + this.lockIdentifier = lockIdentifier + this.data = data + } +} + export type WebViewViewDescriptor = Descriptor<"RNCWebView", WebViewProps> @Component -export struct WebView { +export struct RNCWebView { + public static readonly NAME = RNC.RNCWebView.NAME ctx!: RNOHContext tag: number = 0 - @State descriptor: WebViewViewDescriptor = {} as WebViewViewDescriptor @State source: WebViewNewSource = { uri: "", method: "", body: "", - headers: "", html: "", baseUrl: "" } @@ -101,6 +132,7 @@ export struct WebView { private cleanupCommandCallback?: () => void = undefined controller: webview.WebviewController = new webview.WebviewController(); javaScriptEnable: boolean = true + overScrollMode: OverScrollMode = OverScrollMode.NEVER progress: number = 0 cacheMode: number = CacheMode.Default lockIdentifier: string = ""; @@ -110,58 +142,112 @@ export struct WebView { controllerAttached: boolean = false; // nestedScrollFlag: boolean = false; renderMode: RenderMode = RenderMode.SYNC_RENDER; + scrollEnabled = true; + private eventEmitter: RNC.RNCWebView.EventEmitter | undefined = undefined + private cleanUpCallbacks: (() => void)[] = [] + @State private descriptorWrapper: RNC.RNCWebView.DescriptorWrapper = {} as RNC.RNCWebView.DescriptorWrapper + overScrollMode: OverScrollMode = OverScrollMode.NEVER - aboutToAppear() { - this.descriptor = this.ctx.descriptorRegistry.getDescriptor(this.tag) - this.unregisterDescriptorChangesListener = this.ctx.descriptorRegistry.subscribeToDescriptorChanges(this.tag, - (newDescriptor) => { - this.descriptor = (newDescriptor as WebViewViewDescriptor) - Logger.debug(TAG, `[RNOH] newDescriptor props uri, ${JSON.stringify(this.descriptor.props.newSource.uri)}`); - this.cacheMode = this.descriptor.props.cacheEnabled ? this.transCacheMode(this.descriptor.props.cacheMode) : CacheMode.Online; - this.javaScriptEnable = this.descriptor.props.javaScriptEnabled; - this.source = this.descriptor.props.newSource - if (this.html != "" && this.html != this.source.html) { - Logger.debug(TAG, "[RNOH] html is update") - this.html = this.source.html - if (this.controllerAttached) { - try { - this.controller.loadData( - this.source.html, - "text/html", - "UTF-8", - this.source.baseUrl, - " " - ); - } catch (error) { - Logger.error(TAG, "error: " + error) - } - } - } else if (this.source.uri != "" && this.url != this.source.uri) { - Logger.debug(TAG, `[RNOH] newDescriptor props update uri: ` + this.source.uri); - this.url = this.source.uri - if (this.controllerAttached) { - this.controller.loadUrl(this.descriptor.props.newSource.uri) - } + private onDescriptorWrapperChange(descriptorWrapper: RNC.RNCWebView.DescriptorWrapper) { + this.descriptorWrapper = descriptorWrapper + + Logger.debug(TAG, `[RNOH] newDescriptor props uri, ${JSON.stringify(this.descriptorWrapper.props.newSource.uri)}`); + this.cacheMode = + this.descriptorWrapper.props.cacheEnabled ? this.transCacheMode(this.descriptorWrapper.props.cacheMode as CACHE_MODE) : CacheMode.Online; + this.javaScriptEnable = this.descriptorWrapper.props.javaScriptEnabled; + this.source = this.descriptorWrapper.props.newSource + this.scrollEnabled = this.descriptorWrapper.props.scrollEnabled; + if (this.html != "" && this.html != this.source.html) { + Logger.debug(TAG, "[RNOH] html is update") + this.html = this.source.html + if (this.controllerAttached) { + try { + this.controller.loadData( + this.source.html, + "text/html", + "UTF-8", + this.source.baseUrl, + " " + ); + } catch (error) { + Logger.error(TAG, "error: " + error) } } - ) - webview.WebviewController.setWebDebuggingAccess(this.descriptor.props.webviewDebuggingEnabled) - this.javaScriptEnable = this.descriptor.props.javaScriptEnabled; - this.cacheMode = this.descriptor.props.cacheEnabled ? this.transCacheMode(this.descriptor.props.cacheMode) : CacheMode.Online; - this.source = this.descriptor.props.newSource + } else if (this.source.uri != "" && this.url != this.source.uri) { + Logger.debug(TAG, `[RNOH] newDescriptor props update uri: ` + this.source.uri); + this.url = this.source.uri as string; + if (this.controllerAttached) { + this.controller.loadUrl(this.descriptorWrapper.props.newSource.uri) + } + } + } + + aboutToAppear() { + this.eventEmitter = new RNC.RNCWebView.EventEmitter(this.ctx.rnInstance, this.tag) + this.onDescriptorWrapperChange(this.ctx.descriptorRegistry.findDescriptorWrapperByTag(this.tag)!) + this.cleanUpCallbacks.push(this.ctx.descriptorRegistry.subscribeToDescriptorChanges(this.tag, + (_descriptor, newDescriptorWrapper) => { + this.onDescriptorWrapperChange(newDescriptorWrapper! as RNC.RNCWebView.DescriptorWrapper) + } + )) + if(this.descriptorWrapper.props.overScrollMode === 'always') { + this.overScrollMode = OverScrollMode.ALWAYS + } else if(this.descriptorWrapper.props.overScrollMode === 'never') { + this.overScrollMode = OverScrollMode.NEVER + } + + // this.unregisterDescriptorChangesListener = this.ctx.descriptorRegistry.subscribeToDescriptorChanges(this.tag, + // (newDescriptor) => { + // this.descriptor = (newDescriptor as WebViewViewDescriptor) + // Logger.debug(TAG, `[RNOH] newDescriptor props uri, ${JSON.stringify(this.descriptor.props.newSource.uri)}`); + // this.cacheMode = + // this.descriptor.props.cacheEnabled ? this.transCacheMode(this.descriptor.props.cacheMode) : CacheMode.Online; + // this.javaScriptEnable = this.descriptor.props.javaScriptEnabled; + // this.source = this.descriptor.props.newSource + // if (this.html != "" && this.html != this.source.html) { + // Logger.debug(TAG, "[RNOH] html is update") + // this.html = this.source.html + // if (this.controllerAttached) { + // try { + // this.controller.loadData( + // this.source.html, + // "text/html", + // "UTF-8", + // this.source.baseUrl, + // " " + // ); + // } catch (error) { + // Logger.error(TAG, "error: " + error) + // } + // } + // } else if (this.source.uri != "" && this.url != this.source.uri) { + // Logger.debug(TAG, `[RNOH] newDescriptor props update uri: ` + this.source.uri); + // this.url = this.source.uri + // if (this.controllerAttached) { + // this.controller.loadUrl(this.descriptor.props.newSource.uri) + // } + // } + // }) + // webview.WebviewController.setWebDebuggingAccess(this.descriptor.props.webviewDebuggingEnabled) + this.scrollEnabled = this.descriptorWrapper.props.scrollEnabled; + this.javaScriptEnable = this.descriptorWrapper.props.javaScriptEnabled; + this.cacheMode = + this.descriptorWrapper.props.cacheEnabled ? this.transCacheMode(this.descriptorWrapper.props.cacheMode as CACHE_MODE) : CacheMode.Online; + this.source = this.descriptorWrapper.props.newSource this.html = this.source.html - this.url = this.source.uri; + this.url = this.source.uri as string; // this.nestedScrollFlag = this.descriptor.props.nestedScrollEnabled; // nestedScrollEnabled true表示可嵌套滚动,不需要自适应和web统一渲染,默认是false // this.renderMode = this.nestedScrollFlag?RenderMode.ASYNC_RENDER:RenderMode.SYNC_RENDER + this.overScrollMode = this.descriptorWrapper.props.bounces ? OverScrollMode.ALWAYS : OverScrollMode.NEVER this.registerCommandCallback() } private registerPostMessage() { - if (this.messagingEnabled == this.descriptor.props.messagingEnabled) { + if (this.messagingEnabled == this.descriptorWrapper.props.messagingEnabled) { return; } - this.messagingEnabled = this.descriptor.props.messagingEnabled; + this.messagingEnabled = this.descriptorWrapper.props.messagingEnabled; if (this.messagingEnabled) { let bridge: RNCWebViewBridge = { postMessage: (data: string) => { @@ -170,7 +256,7 @@ export struct WebView { let result: WebViewEventParams = this.createWebViewEvent("onMessage") result.data = data result.lockIdentifier = 0 - this.ctx.rnInstance.emitComponentEvent(this.descriptor.tag, WEB_VIEW, result); + this.eventEmitter!.emit("message", result as ResultType); } } }; @@ -202,6 +288,7 @@ export struct WebView { this.cleanupCommandCallback?.() this.unregisterDescriptorChangesListener?.() Logger.debug(TAG, `[RNOH] aboutToDisappear`) + this.cleanUpCallbacks.forEach(cb => cb()) try { this.controller.deleteJavaScriptRegister(JAVASCRIPT_INTERFACE) this.controller.refresh() @@ -211,19 +298,19 @@ export struct WebView { } } - transCacheMode(cacheMode: number): CacheMode { + transCacheMode(cacheMode: CACHE_MODE): CacheMode { let mode = CacheMode.Default switch (cacheMode) { - case 0: + case CACHE_MODE.LOAD_DEFAULT: mode = CacheMode.Default break; - case 1: + case CACHE_MODE.LOAD_CACHE_ELSE_NETWORK: mode = CacheMode.None break; - case 2: + case CACHE_MODE.LOAD_NO_CACHE: mode = CacheMode.Online break; - case 3: + case CACHE_MODE.LOAD_CACHE_ONLY: mode = CacheMode.Only break; default: @@ -249,7 +336,7 @@ export struct WebView { break case "postMessage": Logger.debug(TAG, `[RNOH] postMessage,${JSON.stringify(args)}`) - let data = JSON.stringify({data : args[0]}) + let data = JSON.stringify({ data: args[0] }) let result: string = "(function () {" + "var event;" + "var data = " + data.toString() + ";" + @@ -279,15 +366,78 @@ export struct WebView { } } break + case "goBack": + Logger.debug(TAG, `[RNOH] goBack,${JSON.stringify(args)}`) + if (this.controllerAttached) { + try { + this.controller.backward(); + } catch (error) { + Logger.error(TAG, "error: " + error) + } + } + break + case "goForward": + Logger.debug(TAG, `[RNOH] goForward,${JSON.stringify(args)}`) + if (this.controllerAttached) { + try { + this.controller.forward(); + } catch (error) { + Logger.error(TAG, "error: " + error) + } + } + break + case "requestFocus": + Logger.debug(TAG, `[RNOH] requestFocus,${JSON.stringify(args)}`) + if (this.controllerAttached) { + try { + this.controller.requestFocus(); + } catch (error) { + Logger.error(TAG, "error: " + error) + } + } + break + case "clearCache": + Logger.debug(TAG, `[RNOH] clearCache,${JSON.stringify(args)}`) + if (this.controllerAttached) { + try { + const removeFlag = !!args[0] === true; + this.controller.removeCache(removeFlag); + } catch (error) { + Logger.error(TAG, "error: " + error) + } + } + break + case "clearHistory": + Logger.debug(TAG, `[RNOH] clearHistory,${JSON.stringify(args)}`) + if (this.controllerAttached) { + try { + this.controller.clearHistory(); + } catch (error) { + Logger.error(TAG, "error: " + error) + } + } + break default: break } }); } + onLoadingStart() { + this.eventEmitter!.emit('loadingStart', { + url: this.controller.getUrl(), + loading: this.progress != 100, + title: this.controller.getTitle(), + canGoBack: this.controller.accessBackward(), + canGoForward: this.controller.accessForward(), + lockIdentifier: 0, + navigationType: "other", + mainDocumentURL: "" + }) + } + onLoadingFinish() { - this.ctx.rnInstance.emitComponentEvent(this.descriptor.tag, WEB_VIEW, { - type: "onLoadingFinish", + this.eventEmitter!.emit('loadingFinish', { url: this.controller.getUrl(), loading: this.progress != 100, title: this.controller.getTitle(), @@ -300,8 +450,7 @@ export struct WebView { } onLoadingError(code: number, description: string) { - this.ctx.rnInstance.emitComponentEvent(this.descriptor.tag, WEB_VIEW, { - type: "onLoadingError", + this.eventEmitter!.emit('loadingError', { url: this.controller.getUrl(), loading: false, title: this.controller.getTitle(), @@ -314,9 +463,21 @@ export struct WebView { }) } + onHttpError(code: number,description: string) { + this.eventEmitter!.emit('httpError', { + url: this.controller.getUrl(), + loading: false, + title: this.controller.getTitle(), + canGoBack: this.controller.accessBackward(), + canGoForward: this.controller.accessForward(), + lockIdentifier: 0, + description: description, + statusCode: code + }) + } + onShouldStartLoadWithRequest() { - this.ctx.rnInstance.emitComponentEvent(this.descriptor.tag, WEB_VIEW, { - type: "onShouldStartLoadWithRequest", + this.eventEmitter!.emit('shouldStartLoadWithRequest', { url: this.controller.getUrl(), loading: this.progress != 100, title: this.controller.getTitle(), @@ -330,8 +491,7 @@ export struct WebView { } onShouldStartLoadCallFunction(lockIdentifier: number, data: WebResourceRequest) { - this.ctx.rnInstance.emitComponentEvent(this.descriptor.tag, WEB_VIEW, { - type: "onShouldStartLoadWithRequest", + this.eventEmitter!.emit("shouldStartLoadWithRequest", { url: data.getRequestUrl(), loading: this.progress != 100, title: this.controller.getTitle(), @@ -345,7 +505,7 @@ export struct WebView { } runInjectedJavaScript() { - let injectedJS = this.descriptor.props.injectedJavaScript + let injectedJS = this.descriptorWrapper.props.injectedJavaScript if (this.javaScriptEnable && injectedJS != "" && this.controllerAttached) { try { this.controller.runJavaScript("(function() {\n" + injectedJS + ";\n})();") @@ -364,36 +524,41 @@ export struct WebView { build() { //RNViewBase({ ctx: this.ctx, tag: this.tag }) { Stack() { - Web({ src: this.source.uri, controller: this.controller,renderMode: this.renderMode }) - .width(this.descriptor.layoutMetrics.frame.size.width) - .height(this.descriptor.layoutMetrics.frame.size.height) + Web({ src: this.source.uri, controller: this.controller, renderMode: this.renderMode }) + .width(this.descriptorWrapper.width) + .height(this.descriptorWrapper.height) .constraintSize({ minHeight: 1 }) + .overScrollMode(this.overScrollMode) .backgroundColor(Color.Transparent) .javaScriptAccess(this.javaScriptEnable) - .horizontalScrollBarAccess(this.descriptor.props.showsHorizontalScrollIndicator) - .verticalScrollBarAccess(this.descriptor.props.showsVerticalScrollIndicator) - .overviewModeAccess(this.descriptor.props.scalesPageToFit) - .textZoomRatio(this.descriptor.props.textZoom) + .horizontalScrollBarAccess(this.descriptorWrapper.props.showsHorizontalScrollIndicator) + .verticalScrollBarAccess(this.descriptorWrapper.props.showsVerticalScrollIndicator) + .overviewModeAccess(this.descriptorWrapper.props.scalesPageToFit) + .textZoomRatio(this.descriptorWrapper.props.textZoom) .cacheMode(this.cacheMode) - .domStorageAccess(this.descriptor.props.domStorageEnabled) - .zoomAccess(this.descriptor.props.scalesPageToFit) - // nestedScrollFlag 为true 表示可以在嵌套滚动中,web自己能滚动 - // .layoutMode(this.nestedScrollFlag ? WebLayoutMode.NONE : WebLayoutMode.FIT_CONTENT) - - // .nestedScroll(this.nestedScrollFlag ? { - // scrollForward: NestedScrollMode.SELF_ONLY, - // scrollBackward: NestedScrollMode.SELF_ONLY - // } : { scrollForward: NestedScrollMode.PARENT_FIRST, scrollBackward: NestedScrollMode.PARENT_FIRST }) - // .overScrollMode(OverScrollMode.NEVER) - // .onSizeChange((event)=>{ - // Logger.debug(TAG, "[RNOH] event width: " + event.width + "[RNOH] event height: " + event.height) - // }) + .domStorageAccess(this.descriptorWrapper.props.domStorageEnabled) + .zoomAccess(this.descriptorWrapper.props.scalesPageToFit)// nestedScrollFlag 为true 表示可以在嵌套滚动中,web自己能滚动 + .overScrollMode(this.overScrollMode) + // .layoutMode(this.nestedScrollFlag ? WebLayoutMode.NONE : WebLayoutMode.FIT_CONTENT) + + // .nestedScroll(this.nestedScrollFlag ? { + // scrollForward: NestedScrollMode.SELF_ONLY, + // scrollBackward: NestedScrollMode.SELF_ONLY + // } : { scrollForward: NestedScrollMode.PARENT_FIRST, scrollBackward: NestedScrollMode.PARENT_FIRST }) + // .overScrollMode(OverScrollMode.NEVER) + // .onSizeChange((event)=>{ + // Logger.debug(TAG, "[RNOH] event width: " + event.width + "[RNOH] event height: " + event.height) + // }) .onProgressChange((event) => { if (event) { this.progress = event.newProgress Logger.debug(TAG, "[RNOH] event progress: " + event.newProgress) } }) + .onPageBegin(() => { + this.onLoadingStart() + this.controller.setScrollable(this.scrollEnabled) + }) .onPageEnd(() => { Logger.debug(TAG, "[RNOH] onPageEnd") this.runInjectedJavaScript() @@ -403,18 +568,25 @@ export struct WebView { if (event) { let errorInfo: string = event.error.getErrorInfo(); let errorCode: number = event.error.getErrorCode(); - if (errorInfo == "ERR_INTERNET_DISCONNECTED" || errorInfo == "ERR_CACHE_MISS" || !event.request.isMainFrame()) { - Logger.debug(TAG, "[RNOH] ERR_INTERNET_DISCONNECTED:OR ERR_CACHE_MISS") - return - } Logger.debug(TAG, "[RNOH] errorInfo:" + errorInfo) Logger.debug(TAG, "[RNOH] errorCode:" + errorCode) this.onLoadingError(errorCode, errorInfo) } }) + .onHttpErrorReceive((event) => { + console.log('onHttpErrorReceiveevent',event) + if (event) { + let errorInfo: string = event.response.getResponseData(); + let code: number = event.response.getResponseCode() + console.log('onHttpErrorReceive',errorInfo) + Logger.debug(TAG, "[RNOH] errorInfo:" + errorInfo) + this.onHttpError(code,errorInfo) + } + }) .onLoadIntercept((event) => { - if (!this.descriptor.props.shouldStartLoadWithRequestEnabled) { - Logger.debug(TAG, "[RNOH]:shouldStartLoadWithRequestEnabled:" + this.descriptor.props.shouldStartLoadWithRequestEnabled) + if (!this.descriptorWrapper.props.shouldStartLoadWithRequestEnabled) { + Logger.debug(TAG, + "[RNOH]:shouldStartLoadWithRequestEnabled:" + this.descriptorWrapper.props.shouldStartLoadWithRequestEnabled) return false } if (this.source.html != undefined && this.source.html != '') { @@ -464,13 +636,12 @@ export struct WebView { } } else if (uri != undefined && uri != "") { let header = this.source.headers; - if (header != undefined && header != "") { + if (header != undefined) { let headers: Array = []; - JSON.parse(header,( key: string, value: string) => { - if(key && value){ - headers.push({ headerKey: key, headerValue: value }) + header.forEach(item => { + if (item.name && item.value) { + headers.push({ headerKey: item.name, headerValue: item.value }) } - return undefined; }) this.controller.loadUrl(uri, headers); } @@ -480,8 +651,8 @@ export struct WebView { } }) } - .width(this.descriptor.layoutMetrics.frame.size.width) - .height(this.descriptor.layoutMetrics.frame.size.height) - .position({x:this.descriptor.layoutMetrics.frame.origin.x,y:this.descriptor.layoutMetrics.frame.origin.y}) + .width(this.descriptorWrapper.width) + .height(this.descriptorWrapper.height) + .position({ x: this.descriptorWrapper.positionRelativeToParent.x, y: this.descriptorWrapper.positionRelativeToParent.y }) } } \ No newline at end of file diff --git a/harmony/rn_webview/src/main/ets/WebViewPackage.ts b/harmony/rn_webview/src/main/ets/RNCWebViewPackage.ts similarity index 59% rename from harmony/rn_webview/src/main/ets/WebViewPackage.ts rename to harmony/rn_webview/src/main/ets/RNCWebViewPackage.ts index be42f16cb..c01521a16 100644 --- a/harmony/rn_webview/src/main/ets/WebViewPackage.ts +++ b/harmony/rn_webview/src/main/ets/RNCWebViewPackage.ts @@ -1,7 +1,7 @@ -/* +/** * MIT License * - * Copyright (C) 2023 Huawei Device Co., Ltd. + * Copyright (C) 2024 Huawei Device Co., Ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,26 +22,17 @@ * SOFTWARE. */ -import {RNPackage,TurboModulesFactory} from '@rnoh/react-native-openharmony/ts'; -import type {TurboModule,TurboModuleContext} from '@rnoh/react-native-openharmony/ts'; -import {RNCWebViewTurboModule} from './RNCWebViewTurboModule'; +import type { + DescriptorWrapperFactoryByDescriptorType, + DescriptorWrapperFactoryByDescriptorTypeCtx +} from '@rnoh/react-native-openharmony/ts'; +import { RNPackage } from '@rnoh/react-native-openharmony/ts'; +import { RNC } from '@rnoh/react-native-openharmony/generated/ts'; -class WebViewTurboModulesFactory extends TurboModulesFactory{ - createTurboModule(name:string):TurboModule | null{ - if (name === 'RNCWebView') { - return new RNCWebViewTurboModule(this.ctx); +export class RNCWebViewPackage extends RNPackage { + createDescriptorWrapperFactoryByDescriptorType(ctx: DescriptorWrapperFactoryByDescriptorTypeCtx): DescriptorWrapperFactoryByDescriptorType { + return { + [RNC.RNCWebView.NAME]: (ctx) => new RNC.RNCWebView.DescriptorWrapper(ctx.descriptor) } - return null; - } - - hasTurboModule(name:string):boolean{ - return name === 'RNCWebView'; - - } -} - -export class WebViewPackage extends RNPackage{ - createTurboModulesFactory(ctx:TurboModuleContext):TurboModulesFactory{ - return new WebViewTurboModulesFactory(ctx); } } \ No newline at end of file diff --git a/harmony/rn_webview/src/main/ets/RNCWebViewTurboModule.ts b/harmony/rn_webview/src/main/ets/RNCWebViewTurboModule.ts deleted file mode 100644 index 832b4c3f4..000000000 --- a/harmony/rn_webview/src/main/ets/RNCWebViewTurboModule.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * MIT License - * - * Copyright (C) 2023 Huawei Device Co., Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import { TurboModule, TurboModuleContext } from '@rnoh/react-native-openharmony/ts'; -import { CallbackState, ShouldRequestUrl } from './ShouldRequestUrl'; -import Logger from './Logger' - -export class RNCWebViewTurboModule extends TurboModule { - constructor(protected ctx: TurboModuleContext) { - super(ctx); - Logger.debug('[RNOH]:RNCWebViewTurboModule constructor'); - } - - getConstants() { - Logger.debug('[RNOH]:RNCWebViewTurboModule call getConstants'); - } - - isFileUploadSupported(): Promise { - return new Promise((resolve, reject) => { - Logger.debug('[RNOH]:RNCWebViewTurboModule call isFileUploadSupported'); - resolve() - }); - } - - shouldStartLoadWithLockIdentifier(shouldStart: boolean, lockIdentifier: number) { - Logger.info("WebView",'shouldStartLoadWithLockIdentifier shouldStart: ' + shouldStart); - ShouldRequestUrl.setValue(lockIdentifier, shouldStart ? CallbackState.DO_NOT_OVERRIDE : CallbackState.SHOULD_OVERRIDE) - } -} \ No newline at end of file diff --git a/harmony/rn_webview/src/main/module.json5 b/harmony/rn_webview/src/main/module.json5 index d4da3968d..234a6bfec 100644 --- a/harmony/rn_webview/src/main/module.json5 +++ b/harmony/rn_webview/src/main/module.json5 @@ -2,6 +2,10 @@ "module":{ "name":"rn_webview", "type":"har", - deviceTypes:['default'] + "deviceTypes": [ + "default", + "tablet", + "2in1" + ] } } \ No newline at end of file diff --git a/harmony/rn_webview/ts.ts b/harmony/rn_webview/ts.ts index c026208d4..5fff7c3b0 100644 --- a/harmony/rn_webview/ts.ts +++ b/harmony/rn_webview/ts.ts @@ -1,7 +1,7 @@ -/* +/** * MIT License * - * Copyright (C) 2023 Huawei Device Co., Ltd. + * Copyright (C) 2024 Huawei Device Co., Ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,5 +22,4 @@ * SOFTWARE. */ -export * from './src/main/ets/RNCWebViewTurboModule' -export * from './src/main/ets/WebViewPackage' \ No newline at end of file +export * from './src/main/ets/RNCWebViewPackage' \ No newline at end of file diff --git a/package.json b/package.json index 5abf3388a..3cc54369d 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,31 @@ { "name": "@react-native-oh-tpl/react-native-webview", "description": "React Native WebView component for iOS, Android, macOS, and Windows", - "main": "index.js", - "main-internal": "src/index.ts", - "react-native": "src/index.ts", + "react-native": "src/index", + "source": "src/index", "typings": "index.d.ts", "author": "Jamon Holmgren ", "contributors": [ "Thibault Malbranche " ], "license": "MIT", - "version": "13.6.3-0.1.4", + "version": "13.10.2-0.0.1", "homepage": "https://github.com/react-native-webview/react-native-webview#readme", "scripts": { - "lint": "yarn tsc --noEmit && yarn eslint ./src --ext .ts,.tsx", - "build": "yarn tsc", - "prepare": "yarn build" + "android": "react-native run-android", + "ios": "react-native run-ios", + "macos": "react-native run-macos --scheme WebviewExample --project-path example/macos", + "start": "react-native start", + "windows": "install-windows-test-app --project-directory example/windows && react-native run-windows --root example --arch x64", + "ci": "CI=true && yarn lint", + "ci:publish": "yarn semantic-release", + "lint": "yarn tsc --noEmit && yarn eslint ./src --ext .ts,.tsx,.js,.jsx", + "build": "babel --extensions \".ts,.tsx\" --out-dir lib src", + "prepare:types": "tsc --noEmit false --emitDeclarationOnly --declaration --rootDir src --outDir lib", + "prepare": "yarn prepare:types && yarn build", + "appium": "appium", + "test:windows": "yarn jest --setupFiles=./jest-setups/jest.setup.js", + "add:macos": "yarn add react-native-macos@0.73.17" }, "rn-docs": { "title": "Webview", @@ -26,64 +36,70 @@ "react-native": "*" }, "dependencies": { + "react-native-webview": "13.10.2", "escape-string-regexp": "2.0.0", - "invariant": "2.2.4", - "react-native-webview": "13.6.2 - 13.6.3" + "invariant": "2.2.4" }, "devDependencies": { - "@babel/core": "^7.0.0", - "@babel/runtime": "^7.0.0", + "@babel/cli": "^7.20.0", + "@babel/core": "^7.20.0", + "@babel/runtime": "^7.20.0", + "@callstack/react-native-visionos": "0.73.8", + "@react-native/babel-preset": "0.73.21", + "@react-native/eslint-config": "0.73.2", + "@react-native/metro-config": "0.73.5", + "@react-native/typescript-config": "0.73.1", + "@rnx-kit/metro-config": "1.3.15", "@semantic-release/git": "7.0.16", "@types/invariant": "^2.2.30", - "@types/jest": "^29.4.0", - "@types/react": "18.0.27", + "@types/jest": "^29.5.12", + "@types/react": "18.2.61", "@types/selenium-webdriver": "4.0.9", - "@typescript-eslint/eslint-plugin": "2.1.0", - "@typescript-eslint/parser": "2.1.0", "appium": "1.17.0", - "eslint": "6.3.0", - "eslint-config-airbnb": "18.0.1", - "eslint-config-prettier": "6.2.0", - "eslint-plugin-import": "2.18.2", - "eslint-plugin-jsx-a11y": "6.2.3", - "eslint-plugin-react": "7.14.3", - "eslint-plugin-react-hooks": "^4.5.0", - "eslint-plugin-react-native": "3.7.0", - "jest": "^29.4.1", + "eslint": "8.57.0", + "jest": "^29.6.3", "metro-react-native-babel-preset": "0.73.7", + "prettier": "2.8.8", "react": "18.2.0", - "react-native": "0.71.12", - "react-native-macos": "0.71.33", - "react-native-test-app": "2.5.15", - "react-native-windows": "0.71.30", + "react-native": "0.73.5", + "react-native-macos": "0.73.17", + "react-native-test-app": "3.7.2", + "react-native-windows": "0.73.8", "selenium-appium": "1.0.2", "selenium-webdriver": "4.0.0-alpha.7", "semantic-release": "15.13.24", - "typescript": "^4.0.0", + "typescript": "5.1.3", "winappdriver": "^0.0.7" }, - "harmony": { - "alias": "react-native-webview" - }, "repository": { "type": "git", - "url": "https://github.com/react-native-oh-library/react-native-webview.git" + "url": "git://github.com/react-native-oh-library/react-native-webview.git" }, "files": [ - "harmony/rn_webview", - "harmony/rn_webview.har", - "lib", "src", - "index.js", - "index.d.ts" + "android", + "ios", + "harmony", + "react-native-webview.podspec", + "!android/build", + "!ios/build", + "!**/__tests__", + "!**/__fixtures__", + "!**/__mocks__" ], - "resolutions": { - "@react-native-community/cli": "10.2.4", - "@react-native-community/cli-platform-android": "10.2.0", - "@react-native-community/cli-platform-ios": "10.2.4" + "keywords": ["react-native", "ios", "android", "harmony"], + "harmony": { + "alias": "react-native-webview", + "codegenConfig": { + "specPaths": [ + "./src" + ] + } }, - "publishConfig": { - "registry": "https://registry.npmjs.org/", - "access": "public" - } + "codegenConfig": { + "name": "WebViewSpecs", + "type": "components", + "jsSrcsDir": "src" + }, + "packageManager": "yarn@1.22.19" } diff --git a/src/RNCWebViewNativeComponent.ts b/src/RNCWebViewNativeComponent.ts index 6a5754bde..6ac9ec87f 100644 --- a/src/RNCWebViewNativeComponent.ts +++ b/src/RNCWebViewNativeComponent.ts @@ -254,7 +254,7 @@ export interface NativeProps extends ViewProps { html?: string; baseUrl?: string; }>; - userAgent?: string; + userAgent?: string; } export interface NativeCommands { diff --git a/src/WebView.harmony.tsx b/src/WebView.harmony.tsx index f782071ce..23074fc68 100644 --- a/src/WebView.harmony.tsx +++ b/src/WebView.harmony.tsx @@ -1,18 +1,15 @@ -import React, { forwardRef, ReactElement, useCallback, useEffect, useImperativeHandle, useMemo, useRef } from 'react'; - -import { - Image, - View, - ImageSourcePropType, - HostComponent, -} from 'react-native'; - -import BatchedBridge from 'react-native/Libraries/BatchedBridge/BatchedBridge'; - +import React, { + forwardRef, + useCallback, + useImperativeHandle, + useRef, +} from 'react'; +import { Image, View, ImageSourcePropType, HostComponent } from 'react-native'; +import { Double } from 'react-native/Libraries/Types/CodegenTypes'; import invariant from 'invariant'; -import RNCWebView, {Commands, NativeProps} from "./RNCWebViewNativeComponent"; -import RNCWebViewModule from "react-native-webview/src/NativeRNCWebView"; +import RNCWebView, { Commands, NativeProps } from './RNCWebViewNativeComponent'; + import { defaultOriginWhitelist, defaultRenderError, @@ -20,211 +17,283 @@ import { useWebViewLogic, } from 'react-native-webview/src/WebViewShared'; import { - AndroidWebViewProps, WebViewSourceUri, + IOSWebViewProps, + DecelerationRateConstant, + WebViewSourceUri, } from 'react-native-webview/src/WebViewTypes'; import styles from 'react-native-webview/src/WebView.styles'; const { resolveAssetSource } = Image; +const processDecelerationRate = ( + decelerationRate: DecelerationRateConstant | number | undefined +) => { + let newDecelerationRate = decelerationRate; + if (newDecelerationRate === 'normal') { + newDecelerationRate = 0.998; + } else if (newDecelerationRate === 'fast') { + newDecelerationRate = 0.99; + } + return newDecelerationRate; +}; -/** - * A simple counter to uniquely identify WebView instances. Do not use this for anything else. - */ -let uniqueRef = 0; - -const WebViewComponent = forwardRef<{}, AndroidWebViewProps>(({ - overScrollMode = 'always', - javaScriptEnabled = true, - thirdPartyCookiesEnabled = true, - scalesPageToFit = true, - allowsFullscreenVideo = false, - allowFileAccess = false, - saveFormDataDisabled = false, - cacheEnabled = true, - androidLayerType = "none", - originWhitelist = defaultOriginWhitelist, - setSupportMultipleWindows = true, - setBuiltInZoomControls = true, - setDisplayZoomControls = false, - nestedScrollEnabled = false, - startInLoadingState, - onNavigationStateChange, - onLoadStart, - onError, - onLoad, - onLoadEnd, - onLoadProgress, - onHttpError: onHttpErrorProp, - onRenderProcessGone: onRenderProcessGoneProp, - onMessage: onMessageProp, - onOpenWindow: onOpenWindowProp, - renderLoading, - renderError, - style, - containerStyle, - source, - nativeConfig, - onShouldStartLoadWithRequest: onShouldStartLoadWithRequestProp, - injectedJavaScriptObject, - ...otherProps -}, ref) => { - const messagingModuleName = useRef(`WebViewMessageHandler${uniqueRef += 1}`).current; - const webViewRef = useRef> | null>(null); - - const onShouldStartLoadWithRequestCallback = useCallback((shouldStart: boolean, - url: string, - lockIdentifier?: number) => { - if (lockIdentifier) { - RNCWebViewModule.shouldStartLoadWithLockIdentifier(shouldStart, lockIdentifier); - } else if (shouldStart && webViewRef.current) { - Commands.loadUrl(webViewRef.current, url); - } - }, []); - - const { onLoadingStart, onShouldStartLoadWithRequest, onMessage, viewState, setViewState, lastErrorEvent, onHttpError, onLoadingError, onLoadingFinish, onLoadingProgress, onOpenWindow, onRenderProcessGone } = useWebViewLogic({ - onNavigationStateChange, - onLoad, - onError, - onHttpErrorProp, - onLoadEnd, - onLoadProgress, - onLoadStart, - onRenderProcessGoneProp, - onMessageProp, - onOpenWindowProp, - startInLoadingState, - originWhitelist, - onShouldStartLoadWithRequestProp, - onShouldStartLoadWithRequestCallback, - }) - - useImperativeHandle(ref, () => ({ - goForward: () => webViewRef.current && Commands.goForward(webViewRef.current), - goBack: () => webViewRef.current && Commands.goBack(webViewRef.current), - reload: () => { - setViewState( - 'LOADING', - ); - if (webViewRef.current) { - Commands.reload(webViewRef.current) - } +const useWarnIfChanges = (value: T, name: string) => { + const ref = useRef(value); + if (ref.current !== value) { + console.warn( + `Changes to property ${name} do nothing after the initial render.` + ); + ref.current = value; + } +}; + +const shouldStartLoadWithLockIdentifier:( + shouldStart: boolean, + lockIdentifier: Double +) => void = () => {} + +const WebViewComponent = forwardRef<{}, IOSWebViewProps>( + ( + { + fraudulentWebsiteWarningEnabled = true, + javaScriptEnabled = true, + cacheEnabled = true, + originWhitelist = defaultOriginWhitelist, + useSharedProcessPool = true, + textInteractionEnabled = true, + injectedJavaScript, + injectedJavaScriptBeforeContentLoaded, + injectedJavaScriptForMainFrameOnly = true, + injectedJavaScriptBeforeContentLoadedForMainFrameOnly = true, + injectedJavaScriptObject, + startInLoadingState, + onNavigationStateChange, + onLoadStart, + onError, + onLoad, + onLoadEnd, + onLoadProgress, + onContentProcessDidTerminate: onContentProcessDidTerminateProp, + onFileDownload, + onHttpError: onHttpErrorProp, + onMessage: onMessageProp, + onOpenWindow: onOpenWindowProp, + renderLoading, + renderError, + style, + containerStyle, + source, + nativeConfig, + allowsInlineMediaPlayback, + allowsAirPlayForMediaPlayback, + mediaPlaybackRequiresUserAction, + dataDetectorTypes, + incognito, + decelerationRate: decelerationRateProp, + onShouldStartLoadWithRequest: onShouldStartLoadWithRequestProp, + ...otherProps }, - stopLoading: () => webViewRef.current && Commands.stopLoading(webViewRef.current), - postMessage: (data: string) => webViewRef.current && Commands.postMessage(webViewRef.current, data), - injectJavaScript: (data: string) => webViewRef.current && Commands.injectJavaScript(webViewRef.current, data), - requestFocus: () => webViewRef.current && Commands.requestFocus(webViewRef.current), - clearFormData: () => webViewRef.current && Commands.clearFormData(webViewRef.current), - clearCache: (includeDiskFiles: boolean) => webViewRef.current && Commands.clearCache(webViewRef.current, includeDiskFiles), - clearHistory: () => webViewRef.current && Commands.clearHistory(webViewRef.current), - }), [setViewState, webViewRef]); - - const directEventCallbacks = useMemo(() => ({ - onShouldStartLoadWithRequest, - onMessage, - }), [onMessage, onShouldStartLoadWithRequest]); - - useEffect(() => { - BatchedBridge.registerCallableModule(messagingModuleName, directEventCallbacks); - }, [messagingModuleName, directEventCallbacks]) - - let otherView: ReactElement | undefined; - if (viewState === 'LOADING') { - otherView = (renderLoading || defaultRenderLoading)(); - } else if (viewState === 'ERROR') { - invariant(lastErrorEvent != null, 'lastErrorEvent expected to be non-null'); - if (lastErrorEvent) { + ref + ) => { + const webViewRef = useRef + > | null>(null); + + const onShouldStartLoadWithRequestCallback = useCallback( + (shouldStart: boolean, _url: string, lockIdentifier = 0) => { + shouldStartLoadWithLockIdentifier( + shouldStart, + lockIdentifier + ); + }, + [] + ); + + const { + onLoadingStart, + onShouldStartLoadWithRequest, + onMessage, + viewState, + setViewState, + lastErrorEvent, + onHttpError, + onLoadingError, + onLoadingFinish, + onLoadingProgress, + onOpenWindow, + onContentProcessDidTerminate, + } = useWebViewLogic({ + onNavigationStateChange, + onLoad, + onError, + onHttpErrorProp, + onLoadEnd, + onLoadProgress, + onLoadStart, + onMessageProp, + onOpenWindowProp, + startInLoadingState, + originWhitelist, + onShouldStartLoadWithRequestProp, + onShouldStartLoadWithRequestCallback, + onContentProcessDidTerminateProp, + }); + + useImperativeHandle( + ref, + () => ({ + goForward: () => + webViewRef.current && Commands.goForward(webViewRef.current), + goBack: () => webViewRef.current && Commands.goBack(webViewRef.current), + reload: () => { + setViewState('LOADING'); + if (webViewRef.current) { + Commands.reload(webViewRef.current); + } + }, + stopLoading: () => + webViewRef.current && Commands.stopLoading(webViewRef.current), + postMessage: (data: string) => + webViewRef.current && Commands.postMessage(webViewRef.current, data), + injectJavaScript: (data: string) => + webViewRef.current && + Commands.injectJavaScript(webViewRef.current, data), + requestFocus: () => + webViewRef.current && Commands.requestFocus(webViewRef.current), + clearCache: (includeDiskFiles: boolean) => + webViewRef.current && + Commands.clearCache(webViewRef.current, includeDiskFiles), + clearHistory: () => webViewRef.current && Commands.clearHistory(webViewRef.current), + loadUrl: (data: string) => webViewRef.current && Commands.loadUrl(webViewRef.current, data), + }), + [setViewState, webViewRef] + ); + + useWarnIfChanges(allowsInlineMediaPlayback, 'allowsInlineMediaPlayback'); + useWarnIfChanges( + allowsAirPlayForMediaPlayback, + 'allowsAirPlayForMediaPlayback' + ); + useWarnIfChanges(incognito, 'incognito'); + useWarnIfChanges( + mediaPlaybackRequiresUserAction, + 'mediaPlaybackRequiresUserAction' + ); + useWarnIfChanges(dataDetectorTypes, 'dataDetectorTypes'); + + let otherView = null; + if (viewState === 'LOADING') { + otherView = (renderLoading || defaultRenderLoading)(); + } else if (viewState === 'ERROR') { + invariant( + lastErrorEvent != null, + 'lastErrorEvent expected to be non-null' + ); otherView = (renderError || defaultRenderError)( - lastErrorEvent.domain, - lastErrorEvent.code, - lastErrorEvent.description, + lastErrorEvent?.domain, + lastErrorEvent?.code ?? 0, + lastErrorEvent?.description ?? '' ); + } else if (viewState !== 'IDLE') { + console.error(`RNCWebView invalid state encountered: ${viewState}`); } - } else if (viewState !== 'IDLE') { - console.error(`RNCWebView invalid state encountered: ${viewState}`); - } - const webViewStyles = [styles.container, styles.webView, style]; - const webViewContainerStyle = [styles.container, containerStyle]; + const webViewStyles = [styles.container, styles.webView, style]; + const webViewContainerStyle = [styles.container, containerStyle]; - if (typeof source !== "number" && source && 'method' in source) { - if (source.method === 'POST' && source.headers) { - console.warn( - 'WebView: `source.headers` is not supported when using POST.', - ); - } else if (source.method === 'GET' && source.body) { - console.warn('WebView: `source.body` is not supported when using GET.'); - } + const decelerationRate = processDecelerationRate(decelerationRateProp); + + const NativeWebView = + (nativeConfig?.component as typeof RNCWebView | undefined) || RNCWebView; + + const sourceResolved = resolveAssetSource(source as ImageSourcePropType); + const newSource = + typeof sourceResolved === 'object' + ? Object.entries(sourceResolved as WebViewSourceUri).reduce( + (prev, [currKey, currValue]) => { + return { + ...prev, + [currKey]: + currKey === 'headers' && + currValue && + typeof currValue === 'object' + ? Object.entries(currValue).map(([key, value]) => { + return { + name: key, + value, + }; + }) + : currValue, + }; + }, + {} + ) + : sourceResolved; + + const webView = ( + + ); + + return ( + + {webView} + {otherView} + + ); } +); - const NativeWebView - = (nativeConfig?.component as (typeof RNCWebView | undefined)) || RNCWebView; - - const sourceResolved = resolveAssetSource(source as ImageSourcePropType) - const newSource = typeof sourceResolved === "object" ? Object.entries(sourceResolved as WebViewSourceUri).reduce((prev, [currKey, currValue]) => { - return { - ...prev, - [currKey]: currKey === "headers" && currValue && typeof currValue === "object" ? Object.entries(currValue).map( - ([key, value]) => { - return { - name: key, - value - } - }) : currValue - } - }, {}) : sourceResolved - - const webView = - - return ( - - {webView} - {otherView} - - ); -}); - -// native implementation should return "true" only for Android 5+ -const { isFileUploadSupported } = RNCWebViewModule; +// no native implementation for iOS, depends only on permissions +const isFileUploadSupported: () => Promise = async () => true; const WebView = Object.assign(WebViewComponent, { isFileUploadSupported }); -export default WebView; \ No newline at end of file +export default WebView; diff --git a/src/WebView.tsx b/src/WebView.tsx index 45a50a168..e62e0a0db 100644 --- a/src/WebView.tsx +++ b/src/WebView.tsx @@ -1,18 +1,4 @@ -import React from 'react'; -import { Text, View } from 'react-native'; -import { IOSWebViewProps, AndroidWebViewProps, WindowsWebViewProps } from 'react-native-webview/src/WebViewTypes'; - -export type WebViewProps = IOSWebViewProps & AndroidWebViewProps & WindowsWebViewProps; - -// This "dummy" WebView is to render something for unsupported platforms, -// like for example Expo SDK "web" platform. -const WebView: React.FunctionComponent = () => ( - - - React Native WebView does not support this platform. - - -); +import {WebView} from 'react-native-webview'; export { WebView }; export default WebView;