UNPKG

6.77 kBJavaScriptView Raw
1/********************************************
2 * WebViewQuillEditor.js
3 * A Quill.js editor component for use in react-native
4 * applications that need to avoid using native code
5 *
6 */
7import React from 'react';
8import {
9 View,
10 ActivityIndicator,
11 StyleSheet,
12 WebView,
13 Platform,
14 Alert
15} from 'react-native';
16import PropTypes from 'prop-types';
17import AssetUtils from 'expo-asset-utils';
18
19// path to the file that the webview will load
20
21const EDITOR_INDEX_FILE_PATH = require(`./assets/dist/reactQuillEditor-index.html`);
22// const INDEX_FILE_ASSET_URI = Asset.fromModule(require(EDITOR_INDEX_FILE_PATH)).uri;
23const MESSAGE_PREFIX = 'react-native-webview-quilljs';
24
25export default class WebViewQuillEditor extends React.Component {
26 constructor() {
27 super();
28 this.webview = null;
29 this.state = {
30 webViewNotLoaded: true // flag to show activity indicator
31 };
32 this.editorIndexFileAsset = undefined;
33 }
34
35 componentDidMount = async () => {
36 try {
37 this.editorIndexFileAsset = await AssetUtils.resolveAsync(EDITOR_INDEX_FILE_PATH);
38 console.log(this.editorIndexFileAsset);
39 } catch (error) {
40 console.log({ error });
41 debugger;
42 }
43 };
44
45 createWebViewRef = (webview) => {
46 this.webview = webview;
47 };
48
49 handleMessage = (event) => {
50 let msgData;
51 try {
52 msgData = JSON.parse(event.nativeEvent.data);
53 if (
54 msgData.hasOwnProperty('prefix') &&
55 msgData.prefix === MESSAGE_PREFIX
56 ) {
57 console.log(`WebViewQuillEditor: received message ${msgData.type}`);
58 this.sendMessage('MESSAGE_ACKNOWLEDGED');
59 console.log(`WebViewQuillEditor: sent MESSAGE_ACKNOWLEDGED`);
60
61 switch (msgData.type) {
62 case 'EDITOR_LOADED':
63 this.editorLoaded();
64 break;
65 case 'EDITOR_SENT':
66 this.props.getEditorCallback(msgData.payload.editor);
67 break;
68 case 'TEXT_CHANGED':
69 if (this.props.onDeltaChangeCallback) {
70 delete msgData.payload.type;
71 let {
72 deltaChange,
73 delta,
74 deltaOld,
75 changeSource
76 } = msgData.payload;
77 this.props.onDeltaChangeCallback(
78 deltaChange,
79 deltaChange,
80 deltaOld,
81 changeSource
82 );
83 }
84 break;
85 case 'RECEIVE_DELTA':
86 if (this.props.getDeltaCallback)
87 this.props.getDeltaCallback(msgData.payload);
88 break;
89 default:
90 console.warn(
91 `WebViewQuillEditor Error: Unhandled message type received "${
92 msgData.type
93 }"`
94 );
95 }
96 }
97 } catch (err) {
98 console.warn(err);
99 return;
100 }
101 };
102
103 onWebViewLoaded = () => {
104 console.log('Webview loaded');
105 this.setState({ webViewNotLoaded: false });
106 this.sendMessage('LOAD_EDITOR');
107 if (this.props.hasOwnProperty('backgroundColor')) {
108 this.sendMessage('SET_BACKGROUND_COLOR', {
109 backgroundColor: this.props.backgroundColor
110 });
111 }
112 if (this.props.hasOwnProperty('onLoad')) {
113 this.props.onLoad();
114 }
115 if (this.props.hasOwnProperty('getEditorCallback')) {
116 this.sendMessage('SEND_EDITOR');
117 }
118 };
119
120 editorLoaded = () => {
121 // send the content to the editor if we have it
122 if (this.props.hasOwnProperty('contentToDisplay')) {
123 console.log(this.props.contentToDisplay);
124 this.sendMessage('SET_CONTENTS', {
125 delta: this.props.contentToDisplay
126 });
127 }
128 if (this.props.hasOwnProperty('htmlContentToDisplay')) {
129 this.sendMessage('SET_HTML_CONTENTS', {
130 html: this.props.htmlContentToDisplay
131 });
132 }
133 };
134
135 sendMessage = (type, payload) => {
136 // only send message when webview is loaded
137 if (this.webview) {
138 console.log(`WebViewQuillEditor: sending message ${type}`);
139 this.webview.postMessage(
140 JSON.stringify({
141 prefix: MESSAGE_PREFIX,
142 type,
143 payload
144 }),
145 '*'
146 );
147 }
148 };
149
150 // get the contents of the editor. The contents will be in the Delta format
151 // defined here: https://quilljs.com/docs/delta/
152 getDelta = () => {
153 this.sendMessage('GET_DELTA');
154 };
155
156 showLoadingIndicator = () => {
157 return (
158 <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
159 <ActivityIndicator color="green" />
160 </View>
161 );
162 };
163
164 onError = (error) => {
165 Alert.alert('WebView onError', error, [
166 { text: 'OK', onPress: () => console.log('OK Pressed') }
167 ]);
168 };
169
170 renderError = (error) => {
171 Alert.alert('WebView renderError', error, [
172 { text: 'OK', onPress: () => console.log('OK Pressed') }
173 ]);
174 };
175
176 render = () => {
177 return (
178 <View style={{ flex: 1, overflow: 'hidden' }}>
179 {this.editorIndexFileAsset ? (
180 <WebView
181 style={{ ...StyleSheet.absoluteFillObject }}
182 ref={this.createWebViewRef}
183 source={{ uri: this.editorIndexFileAsset.uri }}
184 onLoadEnd={this.onWebViewLoaded}
185 onMessage={this.handleMessage}
186 startInLoadingState={true}
187 renderLoading={this.showLoadingIndicator}
188 renderError={this.renderError}
189 javaScriptEnabled={true}
190 onError={this.onError}
191 scalesPageToFit={false}
192 mixedContentMode={'always'}
193 domStorageEnabled={true}
194 />
195 ) : (
196 <View
197 style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}
198 >
199 <ActivityIndicator color="red" />
200 </View>
201 )}
202 </View>
203 );
204 };
205}
206
207WebViewQuillEditor.propTypes = {
208 getDeltaCallback: PropTypes.func,
209 onDeltaChangeCallback: PropTypes.func,
210 backgroundColor: PropTypes.string,
211 onLoad: PropTypes.func
212};
213
214// Specifies the default values for props:
215WebViewQuillEditor.defaultProps = {
216 theme: 'snow'
217};
218
219const styles = StyleSheet.create({
220 activityOverlayStyle: {
221 ...StyleSheet.absoluteFillObject,
222 display: 'flex',
223 justifyContent: 'center',
224 alignContent: 'center',
225 borderRadius: 0
226 },
227 activityIndicatorContainer: {
228 backgroundColor: 'white',
229 padding: 10,
230 borderRadius: 50,
231 alignSelf: 'center',
232 shadowColor: '#000000',
233 shadowOffset: {
234 width: 0,
235 height: 3
236 },
237 shadowRadius: 5,
238 shadowOpacity: 1.0
239 }
240});