Quellcode durchsuchen

#WOA-2354|Resolved|Improve link dialog

Yevhen Pavliuk vor 8 Jahren
Ursprung
Commit
7961a91f28
5 geänderte Dateien mit 91 neuen und 41 gelöschten Zeilen
  1. 58
    8
      src/RichTextEditor.js
  2. 3
    1
      src/RichTextToolbar.js
  3. 7
    0
      src/WebviewMessageHandler.js
  4. 5
    1
      src/const.js
  5. 18
    31
      src/editor.html

+ 58
- 8
src/RichTextEditor.js Datei anzeigen

@@ -32,6 +32,7 @@ export default class RichTextEditor extends Component {
32 32
     this.state = {
33 33
       listeners: [],
34 34
       showLinkDialog: false,
35
+      linkInitialUrl: '',
35 36
       linkTitle: '',
36 37
       linkUrl: '',
37 38
       keyboardHeight: 0
@@ -107,6 +108,17 @@ export default class RichTextEditor extends Component {
107 108
             }
108 109
           }
109 110
           break;
111
+        case messages.SELECTED_TEXT_RESPONSE:
112
+          if (this.selectedTextResolve) {
113
+            this.selectedTextResolve(message.data);
114
+            this.selectedTextResolve = undefined;
115
+            this.selectedTextReject = undefined;
116
+            if (this.pendingSelectedText) {
117
+              clearTimeout(this.pendingSelectedText);
118
+              this.pendingSelectedText = undefined;
119
+            }
120
+          }
121
+          break;
110 122
         case messages.ZSS_INITIALIZED:
111 123
           if (this.props.customCSS) {
112 124
             this.setCustomCSS(this.props.customCSS);
@@ -117,6 +129,11 @@ export default class RichTextEditor extends Component {
117 129
           this.setContentHTML(this.props.initialContentHTML);
118 130
           this.props.editorInitializedCallback && this.props.editorInitializedCallback();
119 131
 
132
+          break;
133
+        case messages.LINK_TOUCHED:
134
+          this.prepareInsert();
135
+          const {title, url} = message.data;
136
+          this.showLinkDialog(title, url);
120 137
           break;
121 138
         case messages.LOG:
122 139
           console.log('FROM ZSS', message.data);
@@ -180,13 +197,14 @@ export default class RichTextEditor extends Component {
180 197
   _hideModal() {
181 198
     this.setState({
182 199
       showLinkDialog: false,
200
+      linkInitialUrl: '',
183 201
       linkTitle: '',
184 202
       linkUrl: ''
185 203
     })
186 204
   }
187 205
 
188 206
   _renderModalButtons() {
189
-    const insertDisabled = this.state.linkTitle.trim().length <= 0 || this.state.linkUrl.trim().length <= 0;
207
+    const insertUpdateDisabled = this.state.linkTitle.trim().length <= 0 || this.state.linkUrl.trim().length <= 0;
190 208
     const containerPlatformStyle = PlatfomIOS ? {justifyContent: 'space-between'} : {paddingTop: 15};
191 209
     const buttonPlatformStyle = PlatfomIOS ? {flex: 1, height: 45, justifyContent: 'center'} : {};
192 210
     return (
@@ -197,25 +215,37 @@ export default class RichTextEditor extends Component {
197 215
             style={buttonPlatformStyle}
198 216
         >
199 217
           <Text style={[styles.button, {paddingRight: 10}]}>
200
-            {PlatfomIOS ? 'Cancel' : 'CANCEL'}
218
+            {this._upperCaseButtonTextIfNeeded('Cancel')}
201 219
           </Text>
202 220
         </TouchableOpacity>
203 221
         <TouchableOpacity
204 222
             onPress={() => {
205
-              this.insertLink(this.state.linkUrl, this.state.linkTitle);
223
+              if (this._linkIsNew()) {
224
+                this.insertLink(this.state.linkUrl, this.state.linkTitle);
225
+              } else {
226
+                this.updateLink(this.state.linkUrl, this.state.linkTitle);
227
+              }
206 228
               this._hideModal();
207 229
             }}
208
-            disabled={insertDisabled}
230
+            disabled={insertUpdateDisabled}
209 231
             style={buttonPlatformStyle}
210 232
         >
211
-          <Text style={[styles.button, {opacity: insertDisabled ? 0.5 : 1}]}>
212
-            {PlatfomIOS ? 'Insert' : 'INSERT'}
233
+          <Text style={[styles.button, {opacity: insertUpdateDisabled ? 0.5 : 1}]}>
234
+            {this._upperCaseButtonTextIfNeeded(this._linkIsNew() ? 'Insert' : 'Update')}
213 235
           </Text>
214 236
         </TouchableOpacity>
215 237
       </View>
216 238
     );
217 239
   }
218 240
 
241
+  _linkIsNew() {
242
+    return !this.state.linkInitialUrl;
243
+  }
244
+
245
+  _upperCaseButtonTextIfNeeded(buttonText) {
246
+    return PlatfomIOS ? buttonText : buttonText.toLowerCase();
247
+  }
248
+
219 249
   render() {
220 250
     //in release build, external html files in Android can't be required, so they must be placed in the assets folder and accessed via uri
221 251
     const pageSource = PlatfomIOS ? require('./editor.html') : { uri: 'file:///android_asset/editor.html' };
@@ -259,8 +289,11 @@ export default class RichTextEditor extends Component {
259 289
   //-------------------------------------------------------------------------------
260 290
   //--------------- Public API
261 291
 
262
-  showLinkDialog() {
292
+  showLinkDialog(optionalTitle = '', optionalUrl = '') {
263 293
     this.setState({
294
+      linkInitialUrl: optionalUrl,
295
+      linkTitle: optionalTitle,
296
+      linkUrl: optionalUrl,
264 297
       showLinkDialog: true
265 298
     });
266 299
   }
@@ -364,10 +397,13 @@ export default class RichTextEditor extends Component {
364 397
   }
365 398
 
366 399
   insertLink(url, title) {
367
-
368 400
     this._sendAction(actions.insertLink, {url, title});
369 401
   }
370 402
 
403
+  updateLink(url, title) {
404
+    this._sendAction(actions.updateLink, {url, title});
405
+  }
406
+
371 407
   insertImage(url, alt) {
372 408
     this._sendAction(actions.insertImage, {url, alt});
373 409
     this.prepareInsert(); //This must be called BEFORE insertImage. But WebViewBridge uses a stack :/
@@ -471,6 +507,20 @@ export default class RichTextEditor extends Component {
471 507
     });
472 508
   }
473 509
 
510
+  async getSelectedText() {
511
+    return new Promise((resolve, reject) => {
512
+      this.selectedTextResolve = resolve;
513
+      this.selectedTextReject = reject;
514
+      this._sendAction(actions.getSelectedText);
515
+
516
+      this.pendingSelectedText = setTimeout(() => {
517
+        if (this.selectedTextReject) {
518
+          this.selectedTextReject('timeout')
519
+        }
520
+      }, 5000);
521
+    });
522
+  }
523
+
474 524
   setTitleFocusHandler(callbackHandler) {
475 525
     this.titleFocusHandler = callbackHandler;
476 526
     this._sendAction(actions.setTitleFocusHandler);

+ 3
- 1
src/RichTextToolbar.js Datei anzeigen

@@ -167,7 +167,9 @@ export default class RichTextToolbar extends Component {
167 167
         if(this.props.onPressAddLink) {
168 168
           this.props.onPressAddLink();
169 169
         } else {
170
-          this.state.editor.showLinkDialog();
170
+          this.state.editor.getSelectedText().then(selectedText => {
171
+            this.state.editor.showLinkDialog(selectedText);
172
+          });
171 173
         }
172 174
         break;
173 175
       case actions.insertImage:

+ 7
- 0
src/WebviewMessageHandler.js Datei anzeigen

@@ -73,6 +73,9 @@ export const InjectedMessageHandler = `
73 73
         case '${actions.insertLink}':
74 74
           zss_editor.insertLink(action.data.url, action.data.title);
75 75
           break;
76
+        case '${actions.updateLink}':
77
+          zss_editor.updateLink(action.data.url, action.data.title);
78
+          break;
76 79
         case '${actions.insertImage}':
77 80
           zss_editor.insertImage(action.data.url, action.data.alt);
78 81
           break;
@@ -118,6 +121,10 @@ export const InjectedMessageHandler = `
118 121
         case '${actions.setContentFocusHandler}':
119 122
           zss_editor.setContentFocusHandler();
120 123
           break;
124
+        case '${actions.getSelectedText}':
125
+          var selectedText = getSelection().toString();
126
+          WebViewBridge.send(JSON.stringify({type: '${messages.SELECTED_TEXT_RESPONSE}', data: selectedText}));
127
+          break;
121 128
         case '${actions.focusContent}':
122 129
           zss_editor.focusContent();
123 130
           break;

+ 5
- 1
src/const.js Datei anzeigen

@@ -4,6 +4,7 @@ export const actions = {
4 4
   getTitleHtml: 'GET_TITLE_HTML',
5 5
   getTitleText: 'GET_TITLE_TEXT',
6 6
   getContentHtml: 'GET_CONTENT_HTML',
7
+  getSelectedText: 'GET_SELECTED_TEXT',
7 8
   blurTitleEditor: 'BLUR_TITLE_EDITOR',
8 9
   blurContentEditor: 'BLUR_CONTENT_EDITOR',
9 10
   focusTitle: 'FOCUS_TITLE',
@@ -27,6 +28,7 @@ export const actions = {
27 28
   insertBulletsList: 'unorderedList',
28 29
   insertOrderedList: 'orderedList',
29 30
   insertLink: 'INST_LINK',
31
+  updateLink: 'UPDATE_LINK',
30 32
   insertImage: 'INST_IMAGE',
31 33
   setSubscript: 'subscript',
32 34
   setSuperscript: 'superscript',
@@ -56,5 +58,7 @@ export const messages = {
56 58
   LOG: 'LOG',
57 59
   TITLE_FOCUSED: 'TITLE_FOCUSED',
58 60
   CONTENT_FOCUSED: 'CONTENT_FOCUSED',
59
-  SELECTION_CHANGE: 'SELECTION_CHANGE'
61
+  SELECTION_CHANGE: 'SELECTION_CHANGE',
62
+  SELECTED_TEXT_RESPONSE: 'SELECTED_TEXT_RESPONSE',
63
+  LINK_TOUCHED: 'LINK_TOUCHED'
60 64
 };

+ 18
- 31
src/editor.html Datei anzeigen

@@ -851,6 +851,20 @@
851 851
 				}
852 852
 			}
853 853
 
854
+			function setCurrentEditingLinkOnTouch() {
855
+				$('#zss_editor_content').delegate('a', 'touchend', function(event) {
856
+					var anchor = $(event.currentTarget);
857
+
858
+					zss_editor.currentEditingLink = anchor;
859
+
860
+					var link = {
861
+						title: anchor.text(),
862
+						url: anchor.attr('href')
863
+					};
864
+					WebViewBridge.send(JSON.stringify({type: 'LINK_TOUCHED', data: link}))
865
+				});
866
+			}
867
+
854 868
 			function preventLinkFollowingOnTouch() {
855 869
 				$('#zss_editor_content').delegate('a', 'touchend', function(event) {
856 870
 					event.preventDefault();
@@ -886,6 +900,7 @@
886 900
 				setupTouchEndFocus('zss_editor_title');
887 901
 				setupTouchEndFocus('zss_editor_content');
888 902
 
903
+				setCurrentEditingLinkOnTouch();
889 904
 				preventLinkFollowingOnTouch();
890 905
 
891 906
 				whenPastingInsertAsPlainText('zss_editor_title');
@@ -1219,24 +1234,8 @@
1219 1234
 
1220 1235
 				zss_editor.restorerange();
1221 1236
 				var sel = document.getSelection();
1222
-				console.log(sel);
1223
-				if (sel.toString().length != 0) {
1224
-					if (sel.rangeCount) {
1225
-
1226
-						var el = document.createElement("a");
1227
-						el.setAttribute("href", url);
1228
-						el.setAttribute("title", title);
1229
-
1230
-						var range = sel.getRangeAt(0).cloneRange();
1231
-						range.surroundContents(el);
1232
-						sel.removeAllRanges();
1233
-						sel.addRange(range);
1234
-					}
1235
-				}
1236
-				else
1237
-				{
1238
-					document.execCommand("insertHTML",false,"<a href='"+url+"'>"+title+"</a>");
1239
-				}
1237
+				sel.deleteFromDocument();
1238
+				document.execCommand("insertHTML",false,"<a href='"+url+"'>"+title+"</a>");
1240 1239
 
1241 1240
 				zss_editor.enabledEditingItems();
1242 1241
 			}
@@ -1248,7 +1247,7 @@
1248 1247
 				if (zss_editor.currentEditingLink) {
1249 1248
 					var c = zss_editor.currentEditingLink;
1250 1249
 					c.attr('href', url);
1251
-					c.attr('title', title);
1250
+					c.text(title);
1252 1251
 				}
1253 1252
 				zss_editor.enabledEditingItems();
1254 1253
 
@@ -1493,18 +1492,6 @@
1493 1492
 						items.push('fonts');
1494 1493
 					}
1495 1494
 
1496
-					// Link
1497
-					if (nodeName == 'a') {
1498
-						zss_editor.currentEditingLink = t;
1499
-						var title = t.attr('title');
1500
-						items.push('link:'+t.attr('href'));
1501
-						if (t.attr('title') !== undefined) {
1502
-							items.push('link-title:'+t.attr('title'));
1503
-						}
1504
-
1505
-					} else {
1506
-						zss_editor.currentEditingLink = null;
1507
-					}
1508 1495
 					// Blockquote
1509 1496
 					if (nodeName == 'blockquote') {
1510 1497
 						items.push('indent');