aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Dominik Guzei <dominik.guzei@gmail.com>2019-07-31 17:08:29 +0200
committerLibravatar Dominik Guzei <dominik.guzei@gmail.com>2019-07-31 17:08:29 +0200
commitbd49d59008d64db13e3f37277ec873a3a464ef9e (patch)
tree7d4f4a943477e543abbcacd2ceb2ac0002054910
parentMerge branch 'develop' into feature/todos (diff)
downloadferdium-app-bd49d59008d64db13e3f37277ec873a3a464ef9e.tar.gz
ferdium-app-bd49d59008d64db13e3f37277ec873a3a464ef9e.tar.zst
ferdium-app-bd49d59008d64db13e3f37277ec873a3a464ef9e.zip
MVP for service <-> todos integration
-rw-r--r--src/features/todos/actions.js11
-rw-r--r--src/features/todos/components/TodosWebview.js20
-rw-r--r--src/features/todos/containers/TodosScreen.js3
-rw-r--r--src/features/todos/preload.js21
-rw-r--r--src/features/todos/store.js29
-rw-r--r--src/i18n/messages/src/lib/Menu.json157
-rw-r--r--src/index.html11
-rw-r--r--src/lib/Menu.js16
-rw-r--r--src/stores/ServicesStore.js3
-rw-r--r--src/webview/contextMenu.js17
10 files changed, 210 insertions, 78 deletions
diff --git a/src/features/todos/actions.js b/src/features/todos/actions.js
index 673ce8531..696568f7f 100644
--- a/src/features/todos/actions.js
+++ b/src/features/todos/actions.js
@@ -5,6 +5,17 @@ export const todoActions = createActionsFromDefinitions({
5 resize: { 5 resize: {
6 width: PropTypes.number.isRequired, 6 width: PropTypes.number.isRequired,
7 }, 7 },
8 setTodosWebview: {
9 webview: PropTypes.element.isRequired,
10 },
11 handleHostMessage: {
12 action: PropTypes.string.isRequired,
13 data: PropTypes.object,
14 },
15 handleClientMessage: {
16 action: PropTypes.string.isRequired,
17 data: PropTypes.object,
18 },
8}, PropTypes.checkPropTypes); 19}, PropTypes.checkPropTypes);
9 20
10export default todoActions; 21export default todoActions;
diff --git a/src/features/todos/components/TodosWebview.js b/src/features/todos/components/TodosWebview.js
index 1d99b9388..1f4730094 100644
--- a/src/features/todos/components/TodosWebview.js
+++ b/src/features/todos/components/TodosWebview.js
@@ -35,6 +35,8 @@ class TodosWebview extends Component {
35 static propTypes = { 35 static propTypes = {
36 classes: PropTypes.object.isRequired, 36 classes: PropTypes.object.isRequired,
37 authToken: PropTypes.string.isRequired, 37 authToken: PropTypes.string.isRequired,
38 handleClientMessage: PropTypes.func.isRequired,
39 setTodosWebview: PropTypes.func.isRequired,
38 resize: PropTypes.func.isRequired, 40 resize: PropTypes.func.isRequired,
39 width: PropTypes.number.isRequired, 41 width: PropTypes.number.isRequired,
40 minWidth: PropTypes.number.isRequired, 42 minWidth: PropTypes.number.isRequired,
@@ -43,7 +45,7 @@ class TodosWebview extends Component {
43 state = { 45 state = {
44 isDragging: false, 46 isDragging: false,
45 width: 300, 47 width: 300,
46 } 48 };
47 49
48 componentWillMount() { 50 componentWillMount() {
49 const { width } = this.props; 51 const { width } = this.props;
@@ -65,7 +67,7 @@ class TodosWebview extends Component {
65 initialPos: event.clientX, 67 initialPos: event.clientX,
66 delta: 0, 68 delta: 0,
67 }); 69 });
68 } 70 };
69 71
70 resizePanel(e) { 72 resizePanel(e) {
71 const { minWidth } = this.props; 73 const { minWidth } = this.props;
@@ -113,10 +115,15 @@ class TodosWebview extends Component {
113 } 115 }
114 } 116 }
115 117
118 startListeningToIpcMessages() {
119 const { handleClientMessage } = this.props;
120 if (!this.webview) return;
121 this.webview.addEventListener('ipc-message', e => handleClientMessage(e.args[0]));
122 }
123
116 render() { 124 render() {
117 const { authToken, classes } = this.props; 125 const { authToken, classes } = this.props;
118 const { width, delta, isDragging } = this.state; 126 const { width, delta, isDragging } = this.state;
119
120 return ( 127 return (
121 <> 128 <>
122 <div 129 <div
@@ -138,6 +145,13 @@ class TodosWebview extends Component {
138 )} 145 )}
139 <Webview 146 <Webview
140 className={classes.webview} 147 className={classes.webview}
148 onDidAttach={() => {
149 this.props.setTodosWebview(this.webview);
150 this.startListeningToIpcMessages();
151 }}
152 partition="persist:todos"
153 preload="./features/todos/preload.js"
154 ref={(webview) => { this.webview = webview ? webview.view : null; }}
141 src={`${environment.TODOS_FRONTEND}?authToken=${authToken}`} 155 src={`${environment.TODOS_FRONTEND}?authToken=${authToken}`}
142 /> 156 />
143 </div> 157 </div>
diff --git a/src/features/todos/containers/TodosScreen.js b/src/features/todos/containers/TodosScreen.js
index 0759c22db..5b7c4531b 100644
--- a/src/features/todos/containers/TodosScreen.js
+++ b/src/features/todos/containers/TodosScreen.js
@@ -18,6 +18,7 @@ class TodosScreen extends Component {
18 actions: PropTypes.shape({ 18 actions: PropTypes.shape({
19 todos: PropTypes.shape({ 19 todos: PropTypes.shape({
20 resize: PropTypes.func.isRequired, 20 resize: PropTypes.func.isRequired,
21 handleIPCMessage: PropTypes.func.isRequired,
21 }), 22 }),
22 }).isRequired, 23 }).isRequired,
23 }; 24 };
@@ -33,6 +34,8 @@ class TodosScreen extends Component {
33 <ErrorBoundary> 34 <ErrorBoundary>
34 <TodosWebview 35 <TodosWebview
35 authToken={stores.user.authToken} 36 authToken={stores.user.authToken}
37 handleClientMessage={actions.todos.handleClientMessage}
38 setTodosWebview={webview => actions.todos.setTodosWebview({ webview })}
36 width={stores.todos.width} 39 width={stores.todos.width}
37 minWidth={TODOS_MIN_WIDTH} 40 minWidth={TODOS_MIN_WIDTH}
38 resize={width => actions.todos.resize({ width })} 41 resize={width => actions.todos.resize({ width })}
diff --git a/src/features/todos/preload.js b/src/features/todos/preload.js
new file mode 100644
index 000000000..533c9aea3
--- /dev/null
+++ b/src/features/todos/preload.js
@@ -0,0 +1,21 @@
1import { ipcRenderer } from 'electron';
2
3const debug = require('debug')('Franz:feature:todos:preload');
4
5debug('Preloading Todos Webview');
6
7let hostMessageListener = () => {};
8
9window.franz = {
10 onInitialize(ipcHostMessageListener) {
11 hostMessageListener = ipcHostMessageListener;
12 },
13 sendToHost(message) {
14 ipcRenderer.sendToHost('clientMessage', message);
15 },
16};
17
18ipcRenderer.on('hostMessage', (event, message) => {
19 debug('Received host message', event, message);
20 hostMessageListener(message);
21});
diff --git a/src/features/todos/store.js b/src/features/todos/store.js
index e7e13b37f..737902946 100644
--- a/src/features/todos/store.js
+++ b/src/features/todos/store.js
@@ -18,6 +18,8 @@ export default class TodoStore extends FeatureStore {
18 18
19 @observable isFeatureActive = false; 19 @observable isFeatureActive = false;
20 20
21 webview = null;
22
21 @computed get width() { 23 @computed get width() {
22 const width = this.settings.width || DEFAULT_TODOS_WIDTH; 24 const width = this.settings.width || DEFAULT_TODOS_WIDTH;
23 25
@@ -39,6 +41,9 @@ export default class TodoStore extends FeatureStore {
39 41
40 this._registerActions(createActionBindings([ 42 this._registerActions(createActionBindings([
41 [todoActions.resize, this._resize], 43 [todoActions.resize, this._resize],
44 [todoActions.setTodosWebview, this._setTodosWebview],
45 [todoActions.handleHostMessage, this._handleHostMessage],
46 [todoActions.handleClientMessage, this._handleClientMessage],
42 ])); 47 ]));
43 48
44 // REACTIONS 49 // REACTIONS
@@ -76,6 +81,30 @@ export default class TodoStore extends FeatureStore {
76 }); 81 });
77 }; 82 };
78 83
84 @action _setTodosWebview = ({ webview }) => {
85 debug('_setTodosWebview', webview);
86 this.webview = webview;
87 };
88
89 @action _handleHostMessage = (message) => {
90 debug('_handleHostMessage', message);
91 if (message.action === 'create:todo') {
92 console.log(this.webview);
93 this.webview.send('hostMessage', message);
94 }
95 };
96
97 @action _handleClientMessage = (message) => {
98 debug('_handleClientMessage', message);
99 if (message.action === 'goToService') {
100 const { url, serviceId } = message.data;
101 if (url) {
102 this.stores.services.one(serviceId).webview.loadURL(url);
103 }
104 this.actions.service.setActive({ serviceId });
105 }
106 };
107
79 // Reactions 108 // Reactions
80 109
81 _setFeatureEnabledReaction = () => { 110 _setFeatureEnabledReaction = () => {
diff --git a/src/i18n/messages/src/lib/Menu.json b/src/i18n/messages/src/lib/Menu.json
index 6f878cbd1..fa9509cbf 100644
--- a/src/i18n/messages/src/lib/Menu.json
+++ b/src/i18n/messages/src/lib/Menu.json
@@ -273,15 +273,28 @@
273 } 273 }
274 }, 274 },
275 { 275 {
276 "id": "menu.view.toggleTodosDevTools",
277 "defaultMessage": "!!!Toggle Todos Developer Tools",
278 "file": "src/lib/Menu.js",
279 "start": {
280 "line": 99,
281 "column": 23
282 },
283 "end": {
284 "line": 102,
285 "column": 3
286 }
287 },
288 {
276 "id": "menu.view.toggleServiceDevTools", 289 "id": "menu.view.toggleServiceDevTools",
277 "defaultMessage": "!!!Toggle Service Developer Tools", 290 "defaultMessage": "!!!Toggle Service Developer Tools",
278 "file": "src/lib/Menu.js", 291 "file": "src/lib/Menu.js",
279 "start": { 292 "start": {
280 "line": 99, 293 "line": 103,
281 "column": 25 294 "column": 25
282 }, 295 },
283 "end": { 296 "end": {
284 "line": 102, 297 "line": 106,
285 "column": 3 298 "column": 3
286 } 299 }
287 }, 300 },
@@ -290,11 +303,11 @@
290 "defaultMessage": "!!!Reload Service", 303 "defaultMessage": "!!!Reload Service",
291 "file": "src/lib/Menu.js", 304 "file": "src/lib/Menu.js",
292 "start": { 305 "start": {
293 "line": 103, 306 "line": 107,
294 "column": 17 307 "column": 17
295 }, 308 },
296 "end": { 309 "end": {
297 "line": 106, 310 "line": 110,
298 "column": 3 311 "column": 3
299 } 312 }
300 }, 313 },
@@ -303,11 +316,11 @@
303 "defaultMessage": "!!!Reload Franz", 316 "defaultMessage": "!!!Reload Franz",
304 "file": "src/lib/Menu.js", 317 "file": "src/lib/Menu.js",
305 "start": { 318 "start": {
306 "line": 107, 319 "line": 111,
307 "column": 15 320 "column": 15
308 }, 321 },
309 "end": { 322 "end": {
310 "line": 110, 323 "line": 114,
311 "column": 3 324 "column": 3
312 } 325 }
313 }, 326 },
@@ -316,11 +329,11 @@
316 "defaultMessage": "!!!Minimize", 329 "defaultMessage": "!!!Minimize",
317 "file": "src/lib/Menu.js", 330 "file": "src/lib/Menu.js",
318 "start": { 331 "start": {
319 "line": 111, 332 "line": 115,
320 "column": 12 333 "column": 12
321 }, 334 },
322 "end": { 335 "end": {
323 "line": 114, 336 "line": 118,
324 "column": 3 337 "column": 3
325 } 338 }
326 }, 339 },
@@ -329,11 +342,11 @@
329 "defaultMessage": "!!!Close", 342 "defaultMessage": "!!!Close",
330 "file": "src/lib/Menu.js", 343 "file": "src/lib/Menu.js",
331 "start": { 344 "start": {
332 "line": 115, 345 "line": 119,
333 "column": 9 346 "column": 9
334 }, 347 },
335 "end": { 348 "end": {
336 "line": 118, 349 "line": 122,
337 "column": 3 350 "column": 3
338 } 351 }
339 }, 352 },
@@ -342,11 +355,11 @@
342 "defaultMessage": "!!!Learn More", 355 "defaultMessage": "!!!Learn More",
343 "file": "src/lib/Menu.js", 356 "file": "src/lib/Menu.js",
344 "start": { 357 "start": {
345 "line": 119, 358 "line": 123,
346 "column": 13 359 "column": 13
347 }, 360 },
348 "end": { 361 "end": {
349 "line": 122, 362 "line": 126,
350 "column": 3 363 "column": 3
351 } 364 }
352 }, 365 },
@@ -355,11 +368,11 @@
355 "defaultMessage": "!!!Changelog", 368 "defaultMessage": "!!!Changelog",
356 "file": "src/lib/Menu.js", 369 "file": "src/lib/Menu.js",
357 "start": { 370 "start": {
358 "line": 123, 371 "line": 127,
359 "column": 13 372 "column": 13
360 }, 373 },
361 "end": { 374 "end": {
362 "line": 126, 375 "line": 130,
363 "column": 3 376 "column": 3
364 } 377 }
365 }, 378 },
@@ -368,11 +381,11 @@
368 "defaultMessage": "!!!Support", 381 "defaultMessage": "!!!Support",
369 "file": "src/lib/Menu.js", 382 "file": "src/lib/Menu.js",
370 "start": { 383 "start": {
371 "line": 127, 384 "line": 131,
372 "column": 11 385 "column": 11
373 }, 386 },
374 "end": { 387 "end": {
375 "line": 130, 388 "line": 134,
376 "column": 3 389 "column": 3
377 } 390 }
378 }, 391 },
@@ -381,11 +394,11 @@
381 "defaultMessage": "!!!Copy Debug Information", 394 "defaultMessage": "!!!Copy Debug Information",
382 "file": "src/lib/Menu.js", 395 "file": "src/lib/Menu.js",
383 "start": { 396 "start": {
384 "line": 131, 397 "line": 135,
385 "column": 13 398 "column": 13
386 }, 399 },
387 "end": { 400 "end": {
388 "line": 134, 401 "line": 138,
389 "column": 3 402 "column": 3
390 } 403 }
391 }, 404 },
@@ -394,11 +407,11 @@
394 "defaultMessage": "!!!Franz Debug Information", 407 "defaultMessage": "!!!Franz Debug Information",
395 "file": "src/lib/Menu.js", 408 "file": "src/lib/Menu.js",
396 "start": { 409 "start": {
397 "line": 135, 410 "line": 139,
398 "column": 27 411 "column": 27
399 }, 412 },
400 "end": { 413 "end": {
401 "line": 138, 414 "line": 142,
402 "column": 3 415 "column": 3
403 } 416 }
404 }, 417 },
@@ -407,11 +420,11 @@
407 "defaultMessage": "!!!Your Debug Information has been copied to your clipboard.", 420 "defaultMessage": "!!!Your Debug Information has been copied to your clipboard.",
408 "file": "src/lib/Menu.js", 421 "file": "src/lib/Menu.js",
409 "start": { 422 "start": {
410 "line": 139, 423 "line": 143,
411 "column": 23 424 "column": 23
412 }, 425 },
413 "end": { 426 "end": {
414 "line": 142, 427 "line": 146,
415 "column": 3 428 "column": 3
416 } 429 }
417 }, 430 },
@@ -420,11 +433,11 @@
420 "defaultMessage": "!!!Terms of Service", 433 "defaultMessage": "!!!Terms of Service",
421 "file": "src/lib/Menu.js", 434 "file": "src/lib/Menu.js",
422 "start": { 435 "start": {
423 "line": 143, 436 "line": 147,
424 "column": 7 437 "column": 7
425 }, 438 },
426 "end": { 439 "end": {
427 "line": 146, 440 "line": 150,
428 "column": 3 441 "column": 3
429 } 442 }
430 }, 443 },
@@ -433,11 +446,11 @@
433 "defaultMessage": "!!!Privacy Statement", 446 "defaultMessage": "!!!Privacy Statement",
434 "file": "src/lib/Menu.js", 447 "file": "src/lib/Menu.js",
435 "start": { 448 "start": {
436 "line": 147, 449 "line": 151,
437 "column": 11 450 "column": 11
438 }, 451 },
439 "end": { 452 "end": {
440 "line": 150, 453 "line": 154,
441 "column": 3 454 "column": 3
442 } 455 }
443 }, 456 },
@@ -446,11 +459,11 @@
446 "defaultMessage": "!!!File", 459 "defaultMessage": "!!!File",
447 "file": "src/lib/Menu.js", 460 "file": "src/lib/Menu.js",
448 "start": { 461 "start": {
449 "line": 151, 462 "line": 155,
450 "column": 8 463 "column": 8
451 }, 464 },
452 "end": { 465 "end": {
453 "line": 154, 466 "line": 158,
454 "column": 3 467 "column": 3
455 } 468 }
456 }, 469 },
@@ -459,11 +472,11 @@
459 "defaultMessage": "!!!View", 472 "defaultMessage": "!!!View",
460 "file": "src/lib/Menu.js", 473 "file": "src/lib/Menu.js",
461 "start": { 474 "start": {
462 "line": 155, 475 "line": 159,
463 "column": 8 476 "column": 8
464 }, 477 },
465 "end": { 478 "end": {
466 "line": 158, 479 "line": 162,
467 "column": 3 480 "column": 3
468 } 481 }
469 }, 482 },
@@ -472,11 +485,11 @@
472 "defaultMessage": "!!!Services", 485 "defaultMessage": "!!!Services",
473 "file": "src/lib/Menu.js", 486 "file": "src/lib/Menu.js",
474 "start": { 487 "start": {
475 "line": 159, 488 "line": 163,
476 "column": 12 489 "column": 12
477 }, 490 },
478 "end": { 491 "end": {
479 "line": 162, 492 "line": 166,
480 "column": 3 493 "column": 3
481 } 494 }
482 }, 495 },
@@ -485,11 +498,11 @@
485 "defaultMessage": "!!!Window", 498 "defaultMessage": "!!!Window",
486 "file": "src/lib/Menu.js", 499 "file": "src/lib/Menu.js",
487 "start": { 500 "start": {
488 "line": 163, 501 "line": 167,
489 "column": 10 502 "column": 10
490 }, 503 },
491 "end": { 504 "end": {
492 "line": 166, 505 "line": 170,
493 "column": 3 506 "column": 3
494 } 507 }
495 }, 508 },
@@ -498,11 +511,11 @@
498 "defaultMessage": "!!!Help", 511 "defaultMessage": "!!!Help",
499 "file": "src/lib/Menu.js", 512 "file": "src/lib/Menu.js",
500 "start": { 513 "start": {
501 "line": 167, 514 "line": 171,
502 "column": 8 515 "column": 8
503 }, 516 },
504 "end": { 517 "end": {
505 "line": 170, 518 "line": 174,
506 "column": 3 519 "column": 3
507 } 520 }
508 }, 521 },
@@ -511,11 +524,11 @@
511 "defaultMessage": "!!!About Franz", 524 "defaultMessage": "!!!About Franz",
512 "file": "src/lib/Menu.js", 525 "file": "src/lib/Menu.js",
513 "start": { 526 "start": {
514 "line": 171, 527 "line": 175,
515 "column": 9 528 "column": 9
516 }, 529 },
517 "end": { 530 "end": {
518 "line": 174, 531 "line": 178,
519 "column": 3 532 "column": 3
520 } 533 }
521 }, 534 },
@@ -524,11 +537,11 @@
524 "defaultMessage": "!!!What's new?", 537 "defaultMessage": "!!!What's new?",
525 "file": "src/lib/Menu.js", 538 "file": "src/lib/Menu.js",
526 "start": { 539 "start": {
527 "line": 175, 540 "line": 179,
528 "column": 16 541 "column": 16
529 }, 542 },
530 "end": { 543 "end": {
531 "line": 178, 544 "line": 182,
532 "column": 3 545 "column": 3
533 } 546 }
534 }, 547 },
@@ -537,11 +550,11 @@
537 "defaultMessage": "!!!Settings", 550 "defaultMessage": "!!!Settings",
538 "file": "src/lib/Menu.js", 551 "file": "src/lib/Menu.js",
539 "start": { 552 "start": {
540 "line": 179, 553 "line": 183,
541 "column": 12 554 "column": 12
542 }, 555 },
543 "end": { 556 "end": {
544 "line": 182, 557 "line": 186,
545 "column": 3 558 "column": 3
546 } 559 }
547 }, 560 },
@@ -550,11 +563,11 @@
550 "defaultMessage": "!!!Check for updates", 563 "defaultMessage": "!!!Check for updates",
551 "file": "src/lib/Menu.js", 564 "file": "src/lib/Menu.js",
552 "start": { 565 "start": {
553 "line": 183, 566 "line": 187,
554 "column": 19 567 "column": 19
555 }, 568 },
556 "end": { 569 "end": {
557 "line": 186, 570 "line": 190,
558 "column": 3 571 "column": 3
559 } 572 }
560 }, 573 },
@@ -563,11 +576,11 @@
563 "defaultMessage": "!!!Hide", 576 "defaultMessage": "!!!Hide",
564 "file": "src/lib/Menu.js", 577 "file": "src/lib/Menu.js",
565 "start": { 578 "start": {
566 "line": 187, 579 "line": 191,
567 "column": 8 580 "column": 8
568 }, 581 },
569 "end": { 582 "end": {
570 "line": 190, 583 "line": 194,
571 "column": 3 584 "column": 3
572 } 585 }
573 }, 586 },
@@ -576,11 +589,11 @@
576 "defaultMessage": "!!!Hide Others", 589 "defaultMessage": "!!!Hide Others",
577 "file": "src/lib/Menu.js", 590 "file": "src/lib/Menu.js",
578 "start": { 591 "start": {
579 "line": 191, 592 "line": 195,
580 "column": 14 593 "column": 14
581 }, 594 },
582 "end": { 595 "end": {
583 "line": 194, 596 "line": 198,
584 "column": 3 597 "column": 3
585 } 598 }
586 }, 599 },
@@ -589,11 +602,11 @@
589 "defaultMessage": "!!!Unhide", 602 "defaultMessage": "!!!Unhide",
590 "file": "src/lib/Menu.js", 603 "file": "src/lib/Menu.js",
591 "start": { 604 "start": {
592 "line": 195, 605 "line": 199,
593 "column": 10 606 "column": 10
594 }, 607 },
595 "end": { 608 "end": {
596 "line": 198, 609 "line": 202,
597 "column": 3 610 "column": 3
598 } 611 }
599 }, 612 },
@@ -602,11 +615,11 @@
602 "defaultMessage": "!!!Quit", 615 "defaultMessage": "!!!Quit",
603 "file": "src/lib/Menu.js", 616 "file": "src/lib/Menu.js",
604 "start": { 617 "start": {
605 "line": 199, 618 "line": 203,
606 "column": 8 619 "column": 8
607 }, 620 },
608 "end": { 621 "end": {
609 "line": 202, 622 "line": 206,
610 "column": 3 623 "column": 3
611 } 624 }
612 }, 625 },
@@ -615,11 +628,11 @@
615 "defaultMessage": "!!!Add New Service...", 628 "defaultMessage": "!!!Add New Service...",
616 "file": "src/lib/Menu.js", 629 "file": "src/lib/Menu.js",
617 "start": { 630 "start": {
618 "line": 203, 631 "line": 207,
619 "column": 17 632 "column": 17
620 }, 633 },
621 "end": { 634 "end": {
622 "line": 206, 635 "line": 210,
623 "column": 3 636 "column": 3
624 } 637 }
625 }, 638 },
@@ -628,11 +641,11 @@
628 "defaultMessage": "!!!Add New Workspace...", 641 "defaultMessage": "!!!Add New Workspace...",
629 "file": "src/lib/Menu.js", 642 "file": "src/lib/Menu.js",
630 "start": { 643 "start": {
631 "line": 207, 644 "line": 211,
632 "column": 19 645 "column": 19
633 }, 646 },
634 "end": { 647 "end": {
635 "line": 210, 648 "line": 214,
636 "column": 3 649 "column": 3
637 } 650 }
638 }, 651 },
@@ -641,11 +654,11 @@
641 "defaultMessage": "!!!Open workspace drawer", 654 "defaultMessage": "!!!Open workspace drawer",
642 "file": "src/lib/Menu.js", 655 "file": "src/lib/Menu.js",
643 "start": { 656 "start": {
644 "line": 211, 657 "line": 215,
645 "column": 23 658 "column": 23
646 }, 659 },
647 "end": { 660 "end": {
648 "line": 214, 661 "line": 218,
649 "column": 3 662 "column": 3
650 } 663 }
651 }, 664 },
@@ -654,11 +667,11 @@
654 "defaultMessage": "!!!Close workspace drawer", 667 "defaultMessage": "!!!Close workspace drawer",
655 "file": "src/lib/Menu.js", 668 "file": "src/lib/Menu.js",
656 "start": { 669 "start": {
657 "line": 215, 670 "line": 219,
658 "column": 24 671 "column": 24
659 }, 672 },
660 "end": { 673 "end": {
661 "line": 218, 674 "line": 222,
662 "column": 3 675 "column": 3
663 } 676 }
664 }, 677 },
@@ -667,11 +680,11 @@
667 "defaultMessage": "!!!Activate next service...", 680 "defaultMessage": "!!!Activate next service...",
668 "file": "src/lib/Menu.js", 681 "file": "src/lib/Menu.js",
669 "start": { 682 "start": {
670 "line": 219, 683 "line": 223,
671 "column": 23 684 "column": 23
672 }, 685 },
673 "end": { 686 "end": {
674 "line": 222, 687 "line": 226,
675 "column": 3 688 "column": 3
676 } 689 }
677 }, 690 },
@@ -680,11 +693,11 @@
680 "defaultMessage": "!!!Activate previous service...", 693 "defaultMessage": "!!!Activate previous service...",
681 "file": "src/lib/Menu.js", 694 "file": "src/lib/Menu.js",
682 "start": { 695 "start": {
683 "line": 223, 696 "line": 227,
684 "column": 27 697 "column": 27
685 }, 698 },
686 "end": { 699 "end": {
687 "line": 226, 700 "line": 230,
688 "column": 3 701 "column": 3
689 } 702 }
690 }, 703 },
@@ -693,11 +706,11 @@
693 "defaultMessage": "!!!Disable notifications & audio", 706 "defaultMessage": "!!!Disable notifications & audio",
694 "file": "src/lib/Menu.js", 707 "file": "src/lib/Menu.js",
695 "start": { 708 "start": {
696 "line": 227, 709 "line": 231,
697 "column": 11 710 "column": 11
698 }, 711 },
699 "end": { 712 "end": {
700 "line": 230, 713 "line": 234,
701 "column": 3 714 "column": 3
702 } 715 }
703 }, 716 },
@@ -706,11 +719,11 @@
706 "defaultMessage": "!!!Enable notifications & audio", 719 "defaultMessage": "!!!Enable notifications & audio",
707 "file": "src/lib/Menu.js", 720 "file": "src/lib/Menu.js",
708 "start": { 721 "start": {
709 "line": 231, 722 "line": 235,
710 "column": 13 723 "column": 13
711 }, 724 },
712 "end": { 725 "end": {
713 "line": 234, 726 "line": 238,
714 "column": 3 727 "column": 3
715 } 728 }
716 }, 729 },
@@ -719,11 +732,11 @@
719 "defaultMessage": "!!!Workspaces", 732 "defaultMessage": "!!!Workspaces",
720 "file": "src/lib/Menu.js", 733 "file": "src/lib/Menu.js",
721 "start": { 734 "start": {
722 "line": 235, 735 "line": 239,
723 "column": 14 736 "column": 14
724 }, 737 },
725 "end": { 738 "end": {
726 "line": 238, 739 "line": 242,
727 "column": 3 740 "column": 3
728 } 741 }
729 }, 742 },
@@ -732,11 +745,11 @@
732 "defaultMessage": "!!!Default", 745 "defaultMessage": "!!!Default",
733 "file": "src/lib/Menu.js", 746 "file": "src/lib/Menu.js",
734 "start": { 747 "start": {
735 "line": 239, 748 "line": 243,
736 "column": 20 749 "column": 20
737 }, 750 },
738 "end": { 751 "end": {
739 "line": 242, 752 "line": 246,
740 "column": 3 753 "column": 3
741 } 754 }
742 } 755 }
diff --git a/src/index.html b/src/index.html
index f29aa2686..198c1ab7b 100644
--- a/src/index.html
+++ b/src/index.html
@@ -35,11 +35,16 @@
35 const originalReloadBehaviour = window._onLiveReloadFileChanged; 35 const originalReloadBehaviour = window._onLiveReloadFileChanged;
36 36
37 window._onLiveReloadFileChanged = (file) => { 37 window._onLiveReloadFileChanged = (file) => {
38 if (!file.path.includes('/build/webview/') && !file.path.includes('/build/index.js') && !file.path.includes('/build/electron/')) { 38 const isTodoPreloadPath = file.path.includes('/build/features/todos/preload.js');
39 if (!file.path.includes('/build/webview/') && !file.path.includes('/build/index.js') && !file.path.includes('/build/electron/') && !isTodoPreloadPath) {
39 originalReloadBehaviour(file); 40 originalReloadBehaviour(file);
40 } else { 41 } else {
41 if (file.path.includes('/build/webview/')) { 42 if (isTodoPreloadPath) {
42 debug('Livereload: Reloading all webvies'); 43 debug('Livereload: Reloading all webviews');
44 const webview = document.querySelector('webview[partition="persist:todos"]');
45 if (webview) webview.reload();
46 } else if (file.path.includes('/build/webview/')) {
47 debug('Livereload: Reloading all webviews');
43 const webviews = document.querySelectorAll('webview').forEach(webview => webview.reload()); 48 const webviews = document.querySelectorAll('webview').forEach(webview => webview.reload());
44 } else { 49 } else {
45 debug('Livereload: skip reload as only main process files have changed'); 50 debug('Livereload: skip reload as only main process files have changed');
diff --git a/src/lib/Menu.js b/src/lib/Menu.js
index 22d788918..9e491e151 100644
--- a/src/lib/Menu.js
+++ b/src/lib/Menu.js
@@ -96,6 +96,10 @@ const menuItems = defineMessages({
96 id: 'menu.view.toggleDevTools', 96 id: 'menu.view.toggleDevTools',
97 defaultMessage: '!!!Toggle Developer Tools', 97 defaultMessage: '!!!Toggle Developer Tools',
98 }, 98 },
99 toggleTodosDevTools: {
100 id: 'menu.view.toggleTodosDevTools',
101 defaultMessage: '!!!Toggle Todos Developer Tools',
102 },
99 toggleServiceDevTools: { 103 toggleServiceDevTools: {
100 id: 'menu.view.toggleServiceDevTools', 104 id: 'menu.view.toggleServiceDevTools',
101 defaultMessage: '!!!Toggle Service Developer Tools', 105 defaultMessage: '!!!Toggle Service Developer Tools',
@@ -240,6 +244,7 @@ const menuItems = defineMessages({
240 id: 'menu.workspaces.defaultWorkspace', 244 id: 'menu.workspaces.defaultWorkspace',
241 defaultMessage: '!!!Default', 245 defaultMessage: '!!!Default',
242 }, 246 },
247
243}); 248});
244 249
245function getActiveWebview() { 250function getActiveWebview() {
@@ -620,6 +625,17 @@ export default class FranzMenu {
620 enabled: this.stores.user.isLoggedIn && this.stores.services.enabled.length > 0, 625 enabled: this.stores.user.isLoggedIn && this.stores.services.enabled.length > 0,
621 }); 626 });
622 627
628 if (this.stores.features.features.isTodosEnabled) {
629 tpl[1].submenu.push({
630 label: intl.formatMessage(menuItems.toggleTodosDevTools),
631 accelerator: `${cmdKey}+Shift+Alt+O`,
632 click: () => {
633 const webview = document.querySelector('webview[partition="persist:todos"]');
634 if (webview) webview.openDevTools();
635 },
636 });
637 }
638
623 tpl[1].submenu.unshift({ 639 tpl[1].submenu.unshift({
624 label: intl.formatMessage(menuItems.reloadService), 640 label: intl.formatMessage(menuItems.reloadService),
625 id: 'reloadService', // TODO: needed? 641 id: 'reloadService', // TODO: needed?
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js
index 109ac5cd7..b13425249 100644
--- a/src/stores/ServicesStore.js
+++ b/src/stores/ServicesStore.js
@@ -440,6 +440,9 @@ export default class ServicesStore extends Store {
440 redirect: false, 440 redirect: false,
441 }); 441 });
442 } 442 }
443 } else if (channel === 'feature:todos') {
444 Object.assign(args[0].data, { serviceId });
445 this.actions.todos.handleHostMessage(args[0]);
443 } 446 }
444 } 447 }
445 448
diff --git a/src/webview/contextMenu.js b/src/webview/contextMenu.js
index a4a6ab899..663b1ebca 100644
--- a/src/webview/contextMenu.js
+++ b/src/webview/contextMenu.js
@@ -39,6 +39,23 @@ const buildMenuTpl = (props, suggestions, isSpellcheckEnabled, defaultSpellcheck
39 { 39 {
40 type: 'separator', 40 type: 'separator',
41 }, { 41 }, {
42 id: 'createTodo',
43 label: `Create todo: "${textSelection.length > 15 ? `${textSelection.slice(0, 15)}...` : textSelection}"`,
44 visible: hasText,
45 click() {
46 debug('Create todo from selected text', textSelection);
47 ipcRenderer.sendToHost('feature:todos', {
48 action: 'create:todo',
49 data: {
50 title: textSelection,
51 url: window.location.href,
52 },
53 });
54 },
55 },
56 {
57 type: 'separator',
58 }, {
42 id: 'lookup', 59 id: 'lookup',
43 label: `Look Up "${textSelection.length > 15 ? `${textSelection.slice(0, 15)}...` : textSelection}"`, 60 label: `Look Up "${textSelection.length > 15 ? `${textSelection.slice(0, 15)}...` : textSelection}"`,
44 visible: isMac && props.mediaType === 'none' && hasText, 61 visible: isMac && props.mediaType === 'none' && hasText,