1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | import React from 'react';
|
8 | import { View, ActivityIndicator, StyleSheet, WebView, Alert } from 'react-native';
|
9 | import PropTypes from 'prop-types';
|
10 | import renderIf from 'render-if';
|
11 |
|
12 |
|
13 | const INDEX_FILE = require(`./assets/dist/reactQuillEditor-index.html`);
|
14 | const MESSAGE_PREFIX = 'react-native-webview-quilljs';
|
15 |
|
16 | export default class WebViewQuillEditor extends React.Component {
|
17 | constructor() {
|
18 | super();
|
19 | this.webview = null;
|
20 | this.state = {
|
21 | webViewNotLoaded: true,
|
22 | };
|
23 | }
|
24 |
|
25 | createWebViewRef = (webview) => {
|
26 | this.webview = webview;
|
27 | };
|
28 |
|
29 | handleMessage = (event) => {
|
30 | let msgData;
|
31 | try {
|
32 | msgData = JSON.parse(event.nativeEvent.data);
|
33 | if (msgData.hasOwnProperty('prefix') && msgData.prefix === MESSAGE_PREFIX) {
|
34 | console.log(`WebViewQuillEditor: received message ${msgData.type}`);
|
35 | this.sendMessage('MESSAGE_ACKNOWLEDGED');
|
36 | console.log(`WebViewQuillEditor: sent MESSAGE_ACKNOWLEDGED`);
|
37 |
|
38 | switch (msgData.type) {
|
39 | case 'EDITOR_LOADED':
|
40 | this.editorLoaded();
|
41 | break;
|
42 | case 'EDITOR_SENT':
|
43 | this.props.getEditorCallback(msgData.payload.editor);
|
44 | break;
|
45 | case 'TEXT_CHANGED':
|
46 | if (this.props.onDeltaChangeCallback) this.props.onDeltaChangeCallback(msgData.payload.delta);
|
47 | break;
|
48 | case 'RECEIVE_DELTA':
|
49 | if (this.props.getDeltaCallback) this.props.getDeltaCallback(msgData.payload);
|
50 | break;
|
51 | default:
|
52 | console.warn(`WebViewQuillEditor Error: Unhandled message type received "${msgData.type}"`);
|
53 | }
|
54 | }
|
55 | } catch (err) {
|
56 | console.warn(err);
|
57 | return;
|
58 | }
|
59 | };
|
60 |
|
61 | onWebViewLoaded = () => {
|
62 | console.log('Webview loaded');
|
63 | this.setState({ webViewNotLoaded: false });
|
64 | this.sendMessage('LOAD_EDITOR');
|
65 | if (this.props.hasOwnProperty('backgroundColor')) {
|
66 | this.sendMessage('SET_BACKGROUND_COLOR', {
|
67 | backgroundColor: this.props.backgroundColor
|
68 | });
|
69 | }
|
70 | if (this.props.hasOwnProperty('onLoad')) {
|
71 | this.props.onLoad();
|
72 | }
|
73 | if (this.props.hasOwnProperty('getEditorCallback')) {
|
74 | this.sendMessage('SEND_EDITOR');
|
75 | }
|
76 | };
|
77 |
|
78 | editorLoaded = () => {
|
79 |
|
80 | if (this.props.hasOwnProperty('contentToDisplay')) {
|
81 | console.log(this.props.contentToDisplay);
|
82 | this.sendMessage('SET_CONTENTS', {
|
83 | delta: this.props.contentToDisplay
|
84 | });
|
85 | }
|
86 | if (this.props.hasOwnProperty('htmlContentToDisplay')) {
|
87 | this.sendMessage('SET_HTML_CONTENTS', {
|
88 | html: this.props.htmlContentToDisplay
|
89 | });
|
90 | }
|
91 | };
|
92 |
|
93 | sendMessage = (type, payload) => {
|
94 |
|
95 | if (this.webview) {
|
96 | console.log(`WebViewQuillEditor: sending message ${type}`);
|
97 | this.webview.postMessage(
|
98 | JSON.stringify({
|
99 | prefix: MESSAGE_PREFIX,
|
100 | type,
|
101 | payload
|
102 | }),
|
103 | '*'
|
104 | );
|
105 | }
|
106 | };
|
107 |
|
108 |
|
109 |
|
110 | getDelta = () => {
|
111 | this.sendMessage('GET_DELTA');
|
112 | };
|
113 |
|
114 | showLoadingIndicator = () => {
|
115 | return (
|
116 | <View style={styles.activityOverlayStyle}>
|
117 | <View style={styles.activityIndicatorContainer}>
|
118 | <ActivityIndicator size="large" animating={this.state.webViewNotLoaded} color="green" />
|
119 | </View>
|
120 | </View>
|
121 | );
|
122 | };
|
123 |
|
124 | onError = (error) => {
|
125 | Alert.alert('WebView onError', error, [{ text: 'OK', onPress: () => console.log('OK Pressed') }]);
|
126 | };
|
127 |
|
128 | renderError = (error) => {
|
129 | Alert.alert('WebView renderError', error, [{ text: 'OK', onPress: () => console.log('OK Pressed') }]);
|
130 | };
|
131 |
|
132 | render = () => {
|
133 | return (
|
134 | <View
|
135 | style={{
|
136 | flex: 1,
|
137 | backgroundColor: 'green'
|
138 | }}
|
139 | >
|
140 | <WebView
|
141 | style={{ ...StyleSheet.absoluteFillObject }}
|
142 | ref={this.createWebViewRef}
|
143 | source={INDEX_FILE}
|
144 | onLoadEnd={this.onWebViewLoaded}
|
145 | onMessage={this.handleMessage}
|
146 | startInLoadingState={true}
|
147 | renderLoading={this.showLoadingIndicator}
|
148 | renderError={this.renderError}
|
149 | javaScriptEnabled={true}
|
150 | onError={this.onError}
|
151 | scalesPageToFit={false}
|
152 | mixedContentMode={'always'}
|
153 | />
|
154 | </View>
|
155 | );
|
156 | };
|
157 | }
|
158 |
|
159 | WebViewQuillEditor.propTypes = {
|
160 | getDeltaCallback: PropTypes.func,
|
161 | onDeltaChangeCallback: PropTypes.func,
|
162 | backgroundColor: PropTypes.string,
|
163 | onLoad: PropTypes.func
|
164 | };
|
165 |
|
166 |
|
167 | WebViewQuillEditor.defaultProps = {
|
168 | theme: 'snow'
|
169 | };
|
170 |
|
171 | const styles = StyleSheet.create({
|
172 | activityOverlayStyle: {
|
173 | ...StyleSheet.absoluteFillObject,
|
174 | display: 'flex',
|
175 | justifyContent: 'center',
|
176 | alignContent: 'center',
|
177 | borderRadius: 0
|
178 | },
|
179 | activityIndicatorContainer: {
|
180 | backgroundColor: 'white',
|
181 | padding: 10,
|
182 | borderRadius: 50,
|
183 | alignSelf: 'center',
|
184 | shadowColor: '#000000',
|
185 | shadowOffset: {
|
186 | width: 0,
|
187 | height: 3
|
188 | },
|
189 | shadowRadius: 5,
|
190 | shadowOpacity: 1.0
|
191 | }
|
192 | });
|