summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--package-lock.json147
-rw-r--r--package.json10
-rw-r--r--src/electron/macOSPermissions.js9
-rw-r--r--src/helpers/userAgent-helpers.ts11
-rw-r--r--src/internal-server/app/Controllers/Http/ServiceController.js6
-rw-r--r--src/internal-server/app/Controllers/Http/UserController.js178
-rw-r--r--src/internal-server/app/Controllers/Http/WorkspaceController.js57
-rw-r--r--src/lib/Tray.js55
-rw-r--r--src/webview/notifications.js19
-rw-r--r--uidev/src/stories/input.stories.tsx66
-rw-r--r--uidev/src/stories/select.stories.tsx31
-rw-r--r--uidev/src/stories/textarea.stories.tsx25
-rw-r--r--uidev/src/stories/toggle.stories.tsx46
13 files changed, 314 insertions, 346 deletions
diff --git a/package-lock.json b/package-lock.json
index 4adabbe9b..8f302a8fe 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -104,13 +104,6 @@
104 "ms": "^2.1.1", 104 "ms": "^2.1.1",
105 "resetable": "^1.0.3", 105 "resetable": "^1.0.3",
106 "uuid": "^3.3.2" 106 "uuid": "^3.3.2"
107 },
108 "dependencies": {
109 "uuid": {
110 "version": "3.4.0",
111 "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
112 "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
113 }
114 } 107 }
115 }, 108 },
116 "@adonisjs/bodyparser": { 109 "@adonisjs/bodyparser": {
@@ -297,11 +290,6 @@
297 "jsonfile": "^4.0.0", 290 "jsonfile": "^4.0.0",
298 "universalify": "^0.1.0" 291 "universalify": "^0.1.0"
299 } 292 }
300 },
301 "uuid": {
302 "version": "3.4.0",
303 "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
304 "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
305 } 293 }
306 } 294 }
307 }, 295 },
@@ -316,13 +304,6 @@
316 "node-csp": "^1.0.1", 304 "node-csp": "^1.0.1",
317 "node-guard": "^1.0.0", 305 "node-guard": "^1.0.0",
318 "uuid": "^3.3.2" 306 "uuid": "^3.3.2"
319 },
320 "dependencies": {
321 "uuid": {
322 "version": "3.4.0",
323 "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
324 "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
325 }
326 } 307 }
327 }, 308 },
328 "@adonisjs/validator": { 309 "@adonisjs/validator": {
@@ -6752,9 +6733,9 @@
6752 } 6733 }
6753 }, 6734 },
6754 "@types/uuid": { 6735 "@types/uuid": {
6755 "version": "8.3.1", 6736 "version": "3.4.9",
6756 "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz", 6737 "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.9.tgz",
6757 "integrity": "sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==", 6738 "integrity": "sha512-XDwyIlt/47l2kWLTzw/mtrpLdB+GPSskR2n/PIcPn+VYhVO77rGhRncIR5GPU0KRzXuqkDO+J5qqrG0Y8P6jzQ==",
6758 "dev": true 6739 "dev": true
6759 }, 6740 },
6760 "@types/verror": { 6741 "@types/verror": {
@@ -13958,18 +13939,18 @@
13958 } 13939 }
13959 }, 13940 },
13960 "execa": { 13941 "execa": {
13961 "version": "5.1.1", 13942 "version": "4.1.0",
13962 "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", 13943 "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
13963 "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", 13944 "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
13964 "requires": { 13945 "requires": {
13965 "cross-spawn": "^7.0.3", 13946 "cross-spawn": "^7.0.0",
13966 "get-stream": "^6.0.0", 13947 "get-stream": "^5.0.0",
13967 "human-signals": "^2.1.0", 13948 "human-signals": "^1.1.1",
13968 "is-stream": "^2.0.0", 13949 "is-stream": "^2.0.0",
13969 "merge-stream": "^2.0.0", 13950 "merge-stream": "^2.0.0",
13970 "npm-run-path": "^4.0.1", 13951 "npm-run-path": "^4.0.0",
13971 "onetime": "^5.1.2", 13952 "onetime": "^5.1.0",
13972 "signal-exit": "^3.0.3", 13953 "signal-exit": "^3.0.2",
13973 "strip-final-newline": "^2.0.0" 13954 "strip-final-newline": "^2.0.0"
13974 }, 13955 },
13975 "dependencies": { 13956 "dependencies": {
@@ -13984,9 +13965,12 @@
13984 } 13965 }
13985 }, 13966 },
13986 "get-stream": { 13967 "get-stream": {
13987 "version": "6.0.1", 13968 "version": "5.2.0",
13988 "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", 13969 "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
13989 "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" 13970 "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
13971 "requires": {
13972 "pump": "^3.0.0"
13973 }
13990 }, 13974 },
13991 "path-key": { 13975 "path-key": {
13992 "version": "3.1.1", 13976 "version": "3.1.1",
@@ -16688,9 +16672,9 @@
16688 } 16672 }
16689 }, 16673 },
16690 "human-signals": { 16674 "human-signals": {
16691 "version": "2.1.0", 16675 "version": "1.1.1",
16692 "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", 16676 "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
16693 "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" 16677 "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="
16694 }, 16678 },
16695 "humanize-ms": { 16679 "humanize-ms": {
16696 "version": "1.2.1", 16680 "version": "1.2.1",
@@ -19917,11 +19901,6 @@
19917 "version": "2.0.0", 19901 "version": "2.0.0",
19918 "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 19902 "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
19919 "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 19903 "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
19920 },
19921 "uuid": {
19922 "version": "3.4.0",
19923 "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
19924 "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
19925 } 19904 }
19926 } 19905 }
19927 }, 19906 },
@@ -20541,16 +20520,23 @@
20541 } 20520 }
20542 }, 20521 },
20543 "macos-release": { 20522 "macos-release": {
20544 "version": "3.0.0", 20523 "version": "2.5.0",
20545 "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-3.0.0.tgz", 20524 "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz",
20546 "integrity": "sha512-y+uUjBt2D1YK0w8k0D19r6O8BYrCkSokWMaTJPvE68RjUOVYQmPXUD7Y4wGphM+/DEJNU+e46hl1lXOzPpvo+w==" 20525 "integrity": "sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g=="
20547 }, 20526 },
20548 "macos-version": { 20527 "macos-version": {
20549 "version": "6.0.0", 20528 "version": "5.2.1",
20550 "resolved": "https://registry.npmjs.org/macos-version/-/macos-version-6.0.0.tgz", 20529 "resolved": "https://registry.npmjs.org/macos-version/-/macos-version-5.2.1.tgz",
20551 "integrity": "sha512-O2S8voA+pMfCHhBn/TIYDXzJ1qNHpPDU32oFxglKnVdJABiYYITt45oLkV9yhwA3E2FDwn3tQqUFrTsr1p3sBQ==", 20530 "integrity": "sha512-OHJU8nTNxHYL1FQhD+nZawWgXKXAqDGr4kluLtaqKO4au3cR41y1mKuVShOU5U4rOYiuPanljq6oFGmV2B9DFA==",
20552 "requires": { 20531 "requires": {
20553 "semver": "^7.3.5" 20532 "semver": "^5.6.0"
20533 },
20534 "dependencies": {
20535 "semver": {
20536 "version": "5.7.1",
20537 "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
20538 "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
20539 }
20554 } 20540 }
20555 }, 20541 },
20556 "macroable": { 20542 "macroable": {
@@ -22352,9 +22338,9 @@
22352 "dev": true 22338 "dev": true
22353 }, 22339 },
22354 "normalize-url": { 22340 "normalize-url": {
22355 "version": "7.0.1", 22341 "version": "6.1.0",
22356 "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-7.0.1.tgz", 22342 "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
22357 "integrity": "sha512-WjLnBQVhYX5XRIectq7CEXgu7O13OB1vsj3rkVItD4c48G/JRD5dohJ1R5vVwZ5wI2/SJDNHfpdQ0Xsqbr1dhQ==" 22343 "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="
22358 }, 22344 },
22359 "normalize-version": { 22345 "normalize-version": {
22360 "version": "1.0.5", 22346 "version": "1.0.5",
@@ -22984,12 +22970,12 @@
22984 } 22970 }
22985 }, 22971 },
22986 "os-name": { 22972 "os-name": {
22987 "version": "5.0.0", 22973 "version": "4.0.1",
22988 "resolved": "https://registry.npmjs.org/os-name/-/os-name-5.0.0.tgz", 22974 "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz",
22989 "integrity": "sha512-2NNINSnda99omAve/ayz0vIOwwS7fC1jNlutqxAcAsS8NXOGU3Rdku1lCm00fDNFdOMLd1YCVTNnfUsVs5471Q==", 22975 "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==",
22990 "requires": { 22976 "requires": {
22991 "macos-release": "^3.0.0", 22977 "macos-release": "^2.5.0",
22992 "windows-release": "^5.0.0" 22978 "windows-release": "^4.0.0"
22993 } 22979 }
22994 }, 22980 },
22995 "os-tmpdir": { 22981 "os-tmpdir": {
@@ -23423,14 +23409,6 @@
23423 "normalize-url": "^6.1.0", 23409 "normalize-url": "^6.1.0",
23424 "parse-path": "^4.0.0", 23410 "parse-path": "^4.0.0",
23425 "protocols": "^1.4.0" 23411 "protocols": "^1.4.0"
23426 },
23427 "dependencies": {
23428 "normalize-url": {
23429 "version": "6.1.0",
23430 "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
23431 "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
23432 "dev": true
23433 }
23434 } 23412 }
23435 }, 23413 },
23436 "parse5": { 23414 "parse5": {
@@ -25579,11 +25557,6 @@
25579 "version": "6.5.2", 25557 "version": "6.5.2",
25580 "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 25558 "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
25581 "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 25559 "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
25582 },
25583 "uuid": {
25584 "version": "3.4.0",
25585 "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
25586 "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
25587 } 25560 }
25588 } 25561 }
25589 }, 25562 },
@@ -27549,12 +27522,6 @@
27549 "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", 27522 "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz",
27550 "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=", 27523 "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=",
27551 "dev": true 27524 "dev": true
27552 },
27553 "uuid": {
27554 "version": "3.4.0",
27555 "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
27556 "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
27557 "dev": true
27558 } 27525 }
27559 } 27526 }
27560 }, 27527 },
@@ -27566,14 +27533,6 @@
27566 "requires": { 27533 "requires": {
27567 "temp-dir": "^2.0.0", 27534 "temp-dir": "^2.0.0",
27568 "uuid": "^3.3.2" 27535 "uuid": "^3.3.2"
27569 },
27570 "dependencies": {
27571 "uuid": {
27572 "version": "3.4.0",
27573 "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
27574 "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
27575 "dev": true
27576 }
27577 } 27536 }
27578 }, 27537 },
27579 "terminal-link": { 27538 "terminal-link": {
@@ -28831,9 +28790,9 @@
28831 "dev": true 28790 "dev": true
28832 }, 28791 },
28833 "uuid": { 28792 "uuid": {
28834 "version": "8.3.2", 28793 "version": "3.3.3",
28835 "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", 28794 "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
28836 "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" 28795 "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ=="
28837 }, 28796 },
28838 "v8-compile-cache": { 28797 "v8-compile-cache": {
28839 "version": "2.3.0", 28798 "version": "2.3.0",
@@ -29847,12 +29806,6 @@
29847 "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", 29806 "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz",
29848 "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", 29807 "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==",
29849 "dev": true 29808 "dev": true
29850 },
29851 "uuid": {
29852 "version": "3.4.0",
29853 "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
29854 "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
29855 "dev": true
29856 } 29809 }
29857 } 29810 }
29858 }, 29811 },
@@ -30015,11 +29968,11 @@
30015 } 29968 }
30016 }, 29969 },
30017 "windows-release": { 29970 "windows-release": {
30018 "version": "5.0.0", 29971 "version": "4.0.0",
30019 "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-5.0.0.tgz", 29972 "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz",
30020 "integrity": "sha512-oiBC+rh4zgwD2E1WvsdcuaBlcNtO+bfoq4GT7sR36J8X+AZNJuXXf+5BjLflDqhEZty25SahBMYSEBhg/V8ikg==", 29973 "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==",
30021 "requires": { 29974 "requires": {
30022 "execa": "^5.1.1" 29975 "execa": "^4.0.2"
30023 } 29976 }
30024 }, 29977 },
30025 "winreg": { 29978 "winreg": {
diff --git a/package.json b/package.json
index 3a448bae7..7aa100668 100644
--- a/package.json
+++ b/package.json
@@ -91,7 +91,7 @@
91 "jss": "10.7.1", 91 "jss": "10.7.1",
92 "lodash": "4.17.21", 92 "lodash": "4.17.21",
93 "macos-notification-state": "1.3.5", 93 "macos-notification-state": "1.3.5",
94 "macos-version": "6.0.0", 94 "macos-version": "5.2.1",
95 "marked": "0.7.0", 95 "marked": "0.7.0",
96 "mime-types": "2.1.32", 96 "mime-types": "2.1.32",
97 "minimist": "1.2.5", 97 "minimist": "1.2.5",
@@ -103,8 +103,8 @@
103 "ms": "2.1.3", 103 "ms": "2.1.3",
104 "node-fetch": "2.6.1", 104 "node-fetch": "2.6.1",
105 "node-mac-permissions": "2.2.0", 105 "node-mac-permissions": "2.2.0",
106 "normalize-url": "7.0.1", 106 "normalize-url": "6.1.0",
107 "os-name": "5.0.0", 107 "os-name": "4.0.1",
108 "pretty-bytes": "5.6.0", 108 "pretty-bytes": "5.6.0",
109 "prop-types": "15.7.2", 109 "prop-types": "15.7.2",
110 "react": "16.14.0", 110 "react": "16.14.0",
@@ -126,7 +126,7 @@
126 "tar": "4.4.15", 126 "tar": "4.4.15",
127 "tslib": "2.3.1", 127 "tslib": "2.3.1",
128 "useragent-generator": "1.1.1-amkt-22079-finish.0", 128 "useragent-generator": "1.1.1-amkt-22079-finish.0",
129 "uuid": "8.3.2", 129 "uuid": "3.3.3",
130 "validator": "11.0.0", 130 "validator": "11.0.0",
131 "ws": "7.4.6" 131 "ws": "7.4.6"
132 }, 132 },
@@ -155,7 +155,7 @@
155 "@types/react-dom": "16.9.13", 155 "@types/react-dom": "16.9.13",
156 "@types/route-parser": "0.1.3", 156 "@types/route-parser": "0.1.3",
157 "@types/tar": "4.0.5", 157 "@types/tar": "4.0.5",
158 "@types/uuid": "8.3.1", 158 "@types/uuid": "3.4.9",
159 "@typescript-eslint/eslint-plugin": "4.29.1", 159 "@typescript-eslint/eslint-plugin": "4.29.1",
160 "@typescript-eslint/parser": "4.29.1", 160 "@typescript-eslint/parser": "4.29.1",
161 "all-contributors-cli": "6.20.0", 161 "all-contributors-cli": "6.20.0",
diff --git a/src/electron/macOSPermissions.js b/src/electron/macOSPermissions.js
index fc2f9b72d..887af2903 100644
--- a/src/electron/macOSPermissions.js
+++ b/src/electron/macOSPermissions.js
@@ -1,17 +1,14 @@
1import { systemPreferences, dialog } from 'electron'; 1import { systemPreferences, dialog } from 'electron';
2import { pathExistsSync, mkdirSync, writeFileSync } from 'fs-extra'; 2import { pathExistsSync, mkdirSync, writeFileSync } from 'fs-extra';
3import { isMacOSVersionGreaterThanOrEqualTo } from 'macos-version'; 3import macosVersion from 'macos-version';
4import { dirname } from 'path'; 4import { dirname } from 'path';
5import { askForScreenCaptureAccess } from 'node-mac-permissions'; 5import { askForScreenCaptureAccess } from 'node-mac-permissions';
6import { userDataPath } from '../environment'; 6import { userDataPath } from '../environment';
7 7
8const debug = require('debug')('Ferdi:macOSPermissions'); 8const debug = require('debug')('Ferdi:macOSPermissions');
9 9
10const isExplicitScreenCapturePermissionReqd = 10const isExplicitScreenCapturePermissionReqd = macosVersion.isGreaterThanOrEqualTo('10.15');
11 isMacOSVersionGreaterThanOrEqualTo('10.15'); 11debug(`Should check explicitly for screen-capture permissions: ${isExplicitScreenCapturePermissionReqd}`);
12debug(
13 `Should check explicitly for screen-capture permissions: ${isExplicitScreenCapturePermissionReqd}`,
14);
15 12
16const filePath = userDataPath('.has-app-requested-screen-capture-permissions'); 13const filePath = userDataPath('.has-app-requested-screen-capture-permissions');
17 14
diff --git a/src/helpers/userAgent-helpers.ts b/src/helpers/userAgent-helpers.ts
index dea49ad7e..73c8bfd03 100644
--- a/src/helpers/userAgent-helpers.ts
+++ b/src/helpers/userAgent-helpers.ts
@@ -1,17 +1,12 @@
1import os from 'os'; 1import os from 'os';
2import { macOSVersion } from 'macos-version'; 2import macosVersion from 'macos-version';
3import { chrome } from 'useragent-generator'; 3import { chrome } from 'useragent-generator';
4import { 4import {
5 chromeVersion, 5 chromeVersion, isMac, isWindows, is64Bit, osArch, osRelease,
6 isMac,
7 isWindows,
8 is64Bit,
9 osArch,
10 osRelease,
11} from '../environment'; 6} from '../environment';
12 7
13function macOS() { 8function macOS() {
14 const version = macOSVersion() || ''; 9 const version = macosVersion() || '';
15 let cpuName = os.cpus()[0].model.split(' ')[0]; 10 let cpuName = os.cpus()[0].model.split(' ')[0];
16 if (cpuName && cpuName.match(/\(/)) { 11 if (cpuName && cpuName.match(/\(/)) {
17 cpuName = cpuName.split('(')[0]; 12 cpuName = cpuName.split('(')[0];
diff --git a/src/internal-server/app/Controllers/Http/ServiceController.js b/src/internal-server/app/Controllers/Http/ServiceController.js
index dea5a888e..c76a287f7 100644
--- a/src/internal-server/app/Controllers/Http/ServiceController.js
+++ b/src/internal-server/app/Controllers/Http/ServiceController.js
@@ -2,7 +2,7 @@ const Service = use('App/Models/Service');
2const { validateAll } = use('Validator'); 2const { validateAll } = use('Validator');
3const Env = use('Env'); 3const Env = use('Env');
4 4
5const { v4: uuid } = require('uuid'); 5const uuid = require('uuid/v4');
6const path = require('path'); 6const path = require('path');
7const fs = require('fs-extra'); 7const fs = require('fs-extra');
8const { LOCAL_HOSTNAME } = require('../../../../config'); 8const { LOCAL_HOSTNAME } = require('../../../../config');
@@ -156,7 +156,9 @@ class ServiceController {
156 id, 156 id,
157 name: service.name, 157 name: service.name,
158 ...newSettings, 158 ...newSettings,
159 iconUrl: `http://${hostname}:${port}/v1/icon/${newSettings.iconId}`, 159 iconUrl: `http://${hostname}:${port}/v1/icon/${
160 newSettings.iconId
161 }`,
160 userId: 1, 162 userId: 1,
161 }, 163 },
162 status: ['updated'], 164 status: ['updated'],
diff --git a/src/internal-server/app/Controllers/Http/UserController.js b/src/internal-server/app/Controllers/Http/UserController.js
index e387e39c4..a3ad736fa 100644
--- a/src/internal-server/app/Controllers/Http/UserController.js
+++ b/src/internal-server/app/Controllers/Http/UserController.js
@@ -1,38 +1,41 @@
1const User = use('App/Models/User'); 1const User = use('App/Models/User');
2const Service = use('App/Models/Service'); 2const Service = use('App/Models/Service');
3const Workspace = use('App/Models/Workspace'); 3const Workspace = use('App/Models/Workspace');
4const { validateAll } = use('Validator'); 4const {
5 validateAll,
6} = use('Validator');
5 7
6const btoa = require('btoa'); 8const btoa = require('btoa');
7const fetch = require('node-fetch'); 9const fetch = require('node-fetch');
8const { v4: uuid } = require('uuid'); 10const uuid = require('uuid/v4');
9const crypto = require('crypto'); 11const crypto = require('crypto');
10const { DEFAULT_APP_SETTINGS } = require('../../../../environment'); 12const { DEFAULT_APP_SETTINGS } = require('../../../../environment');
11 13
12const apiRequest = (url, route, method, auth) => 14const apiRequest = (url, route, method, auth) => new Promise((resolve, reject) => {
13 new Promise((resolve, reject) => { 15 const base = `${url}/v1/`;
14 const base = `${url}/v1/`; 16 const user = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Ferdi/5.3.0-beta.1 Chrome/69.0.3497.128 Electron/4.2.4 Safari/537.36';
15 const user =
16 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Ferdi/5.3.0-beta.1 Chrome/69.0.3497.128 Electron/4.2.4 Safari/537.36';
17 17
18 try { 18 try {
19 fetch(base + route, { 19 fetch(base + route, {
20 method, 20 method,
21 headers: { 21 headers: {
22 Authorization: `Bearer ${auth}`, 22 Authorization: `Bearer ${auth}`,
23 'User-Agent': user, 23 'User-Agent': user,
24 }, 24 },
25 }) 25 })
26 .then(data => data.json()) 26 .then(data => data.json())
27 .then(json => resolve(json)); 27 .then(json => resolve(json));
28 } catch (e) { 28 } catch (e) {
29 reject(); 29 reject();
30 } 30 }
31 }); 31});
32 32
33class UserController { 33class UserController {
34 // Register a new user 34 // Register a new user
35 async signup({ request, response }) { 35 async signup({
36 request,
37 response,
38 }) {
36 // Validate user input 39 // Validate user input
37 const validation = await validateAll(request.all(), { 40 const validation = await validateAll(request.all(), {
38 firstname: 'required', 41 firstname: 'required',
@@ -49,13 +52,15 @@ class UserController {
49 52
50 return response.send({ 53 return response.send({
51 message: 'Successfully created account', 54 message: 'Successfully created account',
52 token: 55 token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJGZXJkaSBJbnRlcm5hbCBTZXJ2ZXIiLCJpYXQiOjE1NzEwNDAyMTUsImV4cCI6MjUzMzk1NDE3ODQ0LCJhdWQiOiJnZXRmZXJkaS5jb20iLCJzdWIiOiJmZXJkaUBsb2NhbGhvc3QiLCJ1c2VySWQiOiIxIn0.9_TWFGp6HROv8Yg82Rt6i1-95jqWym40a-HmgrdMC6M',
53 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJGZXJkaSBJbnRlcm5hbCBTZXJ2ZXIiLCJpYXQiOjE1NzEwNDAyMTUsImV4cCI6MjUzMzk1NDE3ODQ0LCJhdWQiOiJnZXRmZXJkaS5jb20iLCJzdWIiOiJmZXJkaUBsb2NhbGhvc3QiLCJ1c2VySWQiOiIxIn0.9_TWFGp6HROv8Yg82Rt6i1-95jqWym40a-HmgrdMC6M',
54 }); 56 });
55 } 57 }
56 58
57 // Login using an existing user 59 // Login using an existing user
58 async login({ request, response }) { 60 async login({
61 request,
62 response,
63 }) {
59 if (!request.header('Authorization')) { 64 if (!request.header('Authorization')) {
60 return response.status(401).send({ 65 return response.status(401).send({
61 message: 'Please provide authorization', 66 message: 'Please provide authorization',
@@ -65,19 +70,17 @@ class UserController {
65 70
66 return response.send({ 71 return response.send({
67 message: 'Successfully logged in', 72 message: 'Successfully logged in',
68 token: 73 token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJGZXJkaSBJbnRlcm5hbCBTZXJ2ZXIiLCJpYXQiOjE1NzEwNDAyMTUsImV4cCI6MjUzMzk1NDE3ODQ0LCJhdWQiOiJnZXRmZXJkaS5jb20iLCJzdWIiOiJmZXJkaUBsb2NhbGhvc3QiLCJ1c2VySWQiOiIxIn0.9_TWFGp6HROv8Yg82Rt6i1-95jqWym40a-HmgrdMC6M',
69 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJGZXJkaSBJbnRlcm5hbCBTZXJ2ZXIiLCJpYXQiOjE1NzEwNDAyMTUsImV4cCI6MjUzMzk1NDE3ODQ0LCJhdWQiOiJnZXRmZXJkaS5jb20iLCJzdWIiOiJmZXJkaUBsb2NhbGhvc3QiLCJ1c2VySWQiOiIxIn0.9_TWFGp6HROv8Yg82Rt6i1-95jqWym40a-HmgrdMC6M',
70 }); 74 });
71 } 75 }
72 76
73 // Return information about the current user 77 // Return information about the current user
74 async me({ response }) { 78 async me({
79 response,
80 }) {
75 const user = await User.find(1); 81 const user = await User.find(1);
76 82
77 const settings = 83 const settings = typeof user.settings === 'string' ? JSON.parse(user.settings) : user.settings;
78 typeof user.settings === 'string'
79 ? JSON.parse(user.settings)
80 : user.settings;
81 84
82 return response.send({ 85 return response.send({
83 accountType: 'individual', 86 accountType: 'individual',
@@ -91,11 +94,14 @@ class UserController {
91 isSubscriptionOwner: true, 94 isSubscriptionOwner: true,
92 lastname: 'Application', 95 lastname: 'Application',
93 locale: DEFAULT_APP_SETTINGS.fallbackLocale, 96 locale: DEFAULT_APP_SETTINGS.fallbackLocale,
94 ...(settings || {}), 97 ...settings || {},
95 }); 98 });
96 } 99 }
97 100
98 async updateMe({ request, response }) { 101 async updateMe({
102 request,
103 response,
104 }) {
99 const user = await User.find(1); 105 const user = await User.find(1);
100 106
101 let settings = user.settings || {}; 107 let settings = user.settings || {};
@@ -126,11 +132,16 @@ class UserController {
126 locale: DEFAULT_APP_SETTINGS.fallbackLocale, 132 locale: DEFAULT_APP_SETTINGS.fallbackLocale,
127 ...newSettings, 133 ...newSettings,
128 }, 134 },
129 status: ['data-updated'], 135 status: [
136 'data-updated',
137 ],
130 }); 138 });
131 } 139 }
132 140
133 async import({ request, response }) { 141 async import({
142 request,
143 response,
144 }) {
134 // Validate user input 145 // Validate user input
135 const validation = await validateAll(request.all(), { 146 const validation = await validateAll(request.all(), {
136 email: 'required|email', 147 email: 'required|email',
@@ -138,8 +149,7 @@ class UserController {
138 server: 'required', 149 server: 'required',
139 }); 150 });
140 if (validation.fails()) { 151 if (validation.fails()) {
141 let errorMessage = 152 let errorMessage = 'There was an error while trying to import your account:\n';
142 'There was an error while trying to import your account:\n';
143 for (const message of validation.messages()) { 153 for (const message of validation.messages()) {
144 if (message.validation === 'required') { 154 if (message.validation === 'required') {
145 errorMessage += `- Please make sure to supply your ${message.field}\n`; 155 errorMessage += `- Please make sure to supply your ${message.field}\n`;
@@ -152,16 +162,16 @@ class UserController {
152 return response.status(401).send(errorMessage); 162 return response.status(401).send(errorMessage);
153 } 163 }
154 164
155 const { email, password, server } = request.all(); 165 const {
166 email,
167 password,
168 server,
169 } = request.all();
156 170
157 const hashedPassword = crypto 171 const hashedPassword = crypto.createHash('sha256').update(password).digest('base64');
158 .createHash('sha256')
159 .update(password)
160 .digest('base64');
161 172
162 const base = `${server}/v1/`; 173 const base = `${server}/v1/`;
163 const userAgent = 174 const userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Ferdi/5.3.0-beta.1 Chrome/69.0.3497.128 Electron/4.2.4 Safari/537.36';
164 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Ferdi/5.3.0-beta.1 Chrome/69.0.3497.128 Electron/4.2.4 Safari/537.36';
165 175
166 // Try to get an authentication token 176 // Try to get an authentication token
167 let token; 177 let token;
@@ -178,8 +188,7 @@ class UserController {
178 const content = await rawResponse.json(); 188 const content = await rawResponse.json();
179 189
180 if (!content.message || content.message !== 'Successfully logged in') { 190 if (!content.message || content.message !== 'Successfully logged in') {
181 const errorMessage = 191 const errorMessage = 'Could not login into Franz with your supplied credentials. Please check and try again';
182 'Could not login into Franz with your supplied credentials. Please check and try again';
183 return response.status(401).send(errorMessage); 192 return response.status(401).send(errorMessage);
184 } 193 }
185 194
@@ -201,8 +210,7 @@ class UserController {
201 return response.status(401).send(errorMessage); 210 return response.status(401).send(errorMessage);
202 } 211 }
203 if (!userInf) { 212 if (!userInf) {
204 const errorMessage = 213 const errorMessage = 'Could not get your user info from Franz. Please check your credentials or try again later';
205 'Could not get your user info from Franz. Please check your credentials or try again later';
206 return response.status(401).send(errorMessage); 214 return response.status(401).send(errorMessage);
207 } 215 }
208 216
@@ -217,14 +225,9 @@ class UserController {
217 let serviceId; 225 let serviceId;
218 do { 226 do {
219 serviceId = uuid(); 227 serviceId = uuid();
220 } while ( 228 } while ((await Service.query().where('serviceId', serviceId).fetch()).rows.length > 0); // eslint-disable-line no-await-in-loop
221 // eslint-disable-next-line no-await-in-loop 229
222 (await Service.query().where('serviceId', serviceId).fetch()).rows 230 await Service.create({ // eslint-disable-line no-await-in-loop
223 .length > 0
224 );
225
226 // eslint-disable-next-line no-await-in-loop
227 await Service.create({
228 serviceId, 231 serviceId,
229 name: service.name, 232 name: service.name,
230 recipeId: service.recipeId, 233 recipeId: service.recipeId,
@@ -246,18 +249,11 @@ class UserController {
246 let workspaceId; 249 let workspaceId;
247 do { 250 do {
248 workspaceId = uuid(); 251 workspaceId = uuid();
249 } while ( 252 } while ((await Workspace.query().where('workspaceId', workspaceId).fetch()).rows.length > 0); // eslint-disable-line no-await-in-loop
250 // eslint-disable-next-line no-await-in-loop 253
251 (await Workspace.query().where('workspaceId', workspaceId).fetch()) 254 const services = workspace.services.map(service => serviceIdTranslation[service]);
252 .rows.length > 0 255
253 ); 256 await Workspace.create({ // eslint-disable-line no-await-in-loop
254
255 const services = workspace.services.map(
256 service => serviceIdTranslation[service],
257 );
258
259 // eslint-disable-next-line no-await-in-loop
260 await Workspace.create({
261 workspaceId, 257 workspaceId,
262 name: workspace.name, 258 name: workspace.name,
263 order: workspace.order, 259 order: workspace.order,
@@ -270,9 +266,7 @@ class UserController {
270 return response.status(401).send(errorMessage); 266 return response.status(401).send(errorMessage);
271 } 267 }
272 268
273 return response.send( 269 return response.send('Your account has been imported. You can now use your Franz account in Ferdi.');
274 'Your account has been imported. You can now use your Franz account in Ferdi.',
275 );
276 } 270 }
277 271
278 // Account import/export 272 // Account import/export
@@ -297,7 +291,10 @@ class UserController {
297 .send(exportData); 291 .send(exportData);
298 } 292 }
299 293
300 async importFerdi({ request, response }) { 294 async importFerdi({
295 request,
296 response,
297 }) {
301 const validation = await validateAll(request.all(), { 298 const validation = await validateAll(request.all(), {
302 file: 'required', 299 file: 'required',
303 }); 300 });
@@ -309,9 +306,7 @@ class UserController {
309 try { 306 try {
310 file = JSON.parse(request.input('file')); 307 file = JSON.parse(request.input('file'));
311 } catch (e) { 308 } catch (e) {
312 return response.send( 309 return response.send('Could not import: Invalid file, could not read file');
313 'Could not import: Invalid file, could not read file',
314 );
315 } 310 }
316 311
317 if (!file || !file.services || !file.workspaces) { 312 if (!file || !file.services || !file.workspaces) {
@@ -327,14 +322,9 @@ class UserController {
327 let serviceId; 322 let serviceId;
328 do { 323 do {
329 serviceId = uuid(); 324 serviceId = uuid();
330 } while ( 325 } while ((await Service.query().where('serviceId', serviceId).fetch()).rows.length > 0); // eslint-disable-line no-await-in-loop
331 // eslint-disable-next-line no-await-in-loop 326
332 (await Service.query().where('serviceId', serviceId).fetch()).rows 327 await Service.create({ // eslint-disable-line no-await-in-loop
333 .length > 0
334 );
335
336 // eslint-disable-next-line no-await-in-loop
337 await Service.create({
338 serviceId, 328 serviceId,
339 name: service.name, 329 name: service.name,
340 recipeId: service.recipeId, 330 recipeId: service.recipeId,
@@ -354,19 +344,13 @@ class UserController {
354 let workspaceId; 344 let workspaceId;
355 do { 345 do {
356 workspaceId = uuid(); 346 workspaceId = uuid();
357 } while ( 347 } while ((await Workspace.query().where('workspaceId', workspaceId).fetch()).rows.length > 0); // eslint-disable-line no-await-in-loop
358 // eslint-disable-next-line no-await-in-loop 348
359 (await Workspace.query().where('workspaceId', workspaceId).fetch()) 349 const services = (workspace.services && typeof (workspace.services) === 'object') ?
360 .rows.length > 0 350 workspace.services.map((service) => serviceIdTranslation[service]) :
361 ); 351 [];
362 352
363 const services = 353 await Workspace.create({ // eslint-disable-line no-await-in-loop
364 workspace.services && typeof workspace.services === 'object'
365 ? workspace.services.map(service => serviceIdTranslation[service])
366 : [];
367
368 // eslint-disable-next-line no-await-in-loop
369 await Workspace.create({
370 workspaceId, 354 workspaceId,
371 name: workspace.name, 355 name: workspace.name,
372 order: workspace.order, 356 order: workspace.order,
diff --git a/src/internal-server/app/Controllers/Http/WorkspaceController.js b/src/internal-server/app/Controllers/Http/WorkspaceController.js
index f1a5ddf2b..4189fbcdd 100644
--- a/src/internal-server/app/Controllers/Http/WorkspaceController.js
+++ b/src/internal-server/app/Controllers/Http/WorkspaceController.js
@@ -1,11 +1,16 @@
1const Workspace = use('App/Models/Workspace'); 1const Workspace = use('App/Models/Workspace');
2const { validateAll } = use('Validator'); 2const {
3 validateAll,
4} = use('Validator');
3 5
4const { v4: uuid } = require('uuid'); 6const uuid = require('uuid/v4');
5 7
6class WorkspaceController { 8class WorkspaceController {
7 // Create a new workspace for user 9 // Create a new workspace for user
8 async create({ request, response }) { 10 async create({
11 request,
12 response,
13 }) {
9 // Validate user input 14 // Validate user input
10 const validation = await validateAll(request.all(), { 15 const validation = await validateAll(request.all(), {
11 name: 'required', 16 name: 'required',
@@ -24,10 +29,7 @@ class WorkspaceController {
24 let workspaceId; 29 let workspaceId;
25 do { 30 do {
26 workspaceId = uuid(); 31 workspaceId = uuid();
27 } while ( 32 } while ((await Workspace.query().where('workspaceId', workspaceId).fetch()).rows.length > 0); // eslint-disable-line no-await-in-loop
28 (await Workspace.query().where('workspaceId', workspaceId).fetch()).rows
29 .length > 0
30 ); // eslint-disable-line no-await-in-loop
31 33
32 const order = (await Workspace.all()).rows.length; 34 const order = (await Workspace.all()).rows.length;
33 35
@@ -48,7 +50,11 @@ class WorkspaceController {
48 }); 50 });
49 } 51 }
50 52
51 async edit({ request, response, params }) { 53 async edit({
54 request,
55 response,
56 params,
57 }) {
52 // Validate user input 58 // Validate user input
53 const validation = await validateAll(request.all(), { 59 const validation = await validateAll(request.all(), {
54 name: 'required', 60 name: 'required',
@@ -63,19 +69,20 @@ class WorkspaceController {
63 } 69 }
64 70
65 const data = request.all(); 71 const data = request.all();
66 const { id } = params; 72 const {
73 id,
74 } = params;
67 75
68 // Update data in database 76 // Update data in database
69 await Workspace.query() 77 await (Workspace.query()
70 .where('workspaceId', id) 78 .where('workspaceId', id)).update({
71 .update({ 79 name: data.name,
72 name: data.name, 80 services: JSON.stringify(data.services),
73 services: JSON.stringify(data.services), 81 });
74 });
75 82
76 // Get updated row 83 // Get updated row
77 const workspace = (await Workspace.query().where('workspaceId', id).fetch()) 84 const workspace = (await Workspace.query()
78 .rows[0]; 85 .where('workspaceId', id).fetch()).rows[0];
79 86
80 return response.send({ 87 return response.send({
81 id: workspace.workspaceId, 88 id: workspace.workspaceId,
@@ -104,10 +111,13 @@ class WorkspaceController {
104 }); 111 });
105 } 112 }
106 113
107 const { id } = params; 114 const {
115 id,
116 } = params;
108 117
109 // Update data in database 118 // Update data in database
110 await Workspace.query().where('workspaceId', id).delete(); 119 await (Workspace.query()
120 .where('workspaceId', id)).delete();
111 121
112 return response.send({ 122 return response.send({
113 message: 'Successfully deleted workspace', 123 message: 'Successfully deleted workspace',
@@ -115,7 +125,9 @@ class WorkspaceController {
115 } 125 }
116 126
117 // List all workspaces a user has created 127 // List all workspaces a user has created
118 async list({ response }) { 128 async list({
129 response,
130 }) {
119 const workspaces = (await Workspace.all()).rows; 131 const workspaces = (await Workspace.all()).rows;
120 // Convert to array with all data Franz wants 132 // Convert to array with all data Franz wants
121 let workspacesArray = []; 133 let workspacesArray = [];
@@ -124,10 +136,7 @@ class WorkspaceController {
124 id: workspace.workspaceId, 136 id: workspace.workspaceId,
125 name: workspace.name, 137 name: workspace.name,
126 order: workspace.order, 138 order: workspace.order,
127 services: 139 services: typeof workspace.services === 'string' ? JSON.parse(workspace.services) : workspace.services,
128 typeof workspace.services === 'string'
129 ? JSON.parse(workspace.services)
130 : workspace.services,
131 userId: 1, 140 userId: 1,
132 })); 141 }));
133 } 142 }
diff --git a/src/lib/Tray.js b/src/lib/Tray.js
index c629e212d..f5970f7e7 100644
--- a/src/lib/Tray.js
+++ b/src/lib/Tray.js
@@ -1,14 +1,8 @@
1import { 1import {
2 app, 2 app, Menu, nativeImage, nativeTheme, systemPreferences, Tray, ipcMain,
3 Menu,
4 nativeImage,
5 nativeTheme,
6 systemPreferences,
7 Tray,
8 ipcMain,
9} from 'electron'; 3} from 'electron';
10import { join } from 'path'; 4import { join } from 'path';
11import { isMacOSVersionGreaterThanOrEqualTo } from 'macos-version'; 5import macosVersion from 'macos-version';
12import { isMac, isWindows, isLinux } from '../environment'; 6import { isMac, isWindows, isLinux } from '../environment';
13 7
14const FILE_EXTENSION = isWindows ? 'ico' : 'png'; 8const FILE_EXTENSION = isWindows ? 'ico' : 'png';
@@ -70,9 +64,7 @@ export default class TrayIcon {
70 64
71 if (appSettings.type === 'app') { 65 if (appSettings.type === 'app') {
72 const { isAppMuted } = appSettings.data; 66 const { isAppMuted } = appSettings.data;
73 this.trayMenuTemplate[1].label = isAppMuted 67 this.trayMenuTemplate[1].label = isAppMuted ? 'Enable Notifications && Audio' : 'Disable Notifications && Audio';
74 ? 'Enable Notifications && Audio'
75 : 'Disable Notifications && Audio';
76 this.trayMenu = Menu.buildFromTemplate(this.trayMenuTemplate); 68 this.trayMenu = Menu.buildFromTemplate(this.trayMenuTemplate);
77 if (isLinux) { 69 if (isLinux) {
78 this.trayIcon.setContextMenu(this.trayMenu); 70 this.trayIcon.setContextMenu(this.trayMenu);
@@ -115,12 +107,9 @@ export default class TrayIcon {
115 } 107 }
116 108
117 if (isMac) { 109 if (isMac) {
118 this.themeChangeSubscriberId = systemPreferences.subscribeNotification( 110 this.themeChangeSubscriberId = systemPreferences.subscribeNotification('AppleInterfaceThemeChangedNotification', () => {
119 'AppleInterfaceThemeChangedNotification', 111 this._refreshIcon();
120 () => { 112 });
121 this._refreshIcon();
122 },
123 );
124 } 113 }
125 } 114 }
126 115
@@ -160,8 +149,7 @@ export default class TrayIcon {
160 _getAssetFromIndicator(indicator) { 149 _getAssetFromIndicator(indicator) {
161 if (indicator === '•') { 150 if (indicator === '•') {
162 return INDICATOR_TRAY_INDIRECT; 151 return INDICATOR_TRAY_INDIRECT;
163 } 152 } if (indicator !== 0) {
164 if (indicator !== 0) {
165 return INDICATOR_TRAY_UNREAD; 153 return INDICATOR_TRAY_UNREAD;
166 } 154 }
167 return INDICATOR_TRAY_PLAIN; 155 return INDICATOR_TRAY_PLAIN;
@@ -170,16 +158,11 @@ export default class TrayIcon {
170 _refreshIcon() { 158 _refreshIcon() {
171 if (!this.trayIcon) return; 159 if (!this.trayIcon) return;
172 160
173 this.trayIcon.setImage( 161 this.trayIcon.setImage(this._getAsset('tray', this._getAssetFromIndicator(this.indicator)));
174 this._getAsset('tray', this._getAssetFromIndicator(this.indicator)),
175 );
176 162
177 if (isMac) { 163 if (isMac) {
178 this.trayIcon.setPressedImage( 164 this.trayIcon.setPressedImage(
179 this._getAsset( 165 this._getAsset('tray', `${this._getAssetFromIndicator(this.indicator)}-active`),
180 'tray',
181 `${this._getAssetFromIndicator(this.indicator)}-active`,
182 ),
183 ); 166 );
184 } 167 }
185 } 168 }
@@ -187,24 +170,12 @@ export default class TrayIcon {
187 _getAsset(type, asset) { 170 _getAsset(type, asset) {
188 let { platform } = process; 171 let { platform } = process;
189 172
190 if ( 173 if (isMac && (nativeTheme.shouldUseDarkColors || macosVersion.isGreaterThanOrEqualTo('11'))) {
191 isMac &&
192 (nativeTheme.shouldUseDarkColors ||
193 isMacOSVersionGreaterThanOrEqualTo('11'))
194 ) {
195 platform = `${platform}-dark`; 174 platform = `${platform}-dark`;
196 } 175 }
197 176
198 return nativeImage.createFromPath( 177 return nativeImage.createFromPath(join(
199 join( 178 __dirname, '..', 'assets', 'images', type, platform, `${asset}.${FILE_EXTENSION}`,
200 __dirname, 179 ));
201 '..',
202 'assets',
203 'images',
204 type,
205 platform,
206 `${asset}.${FILE_EXTENSION}`,
207 ),
208 );
209 } 180 }
210} 181}
diff --git a/src/webview/notifications.js b/src/webview/notifications.js
index 73cdb89d4..205a3220c 100644
--- a/src/webview/notifications.js
+++ b/src/webview/notifications.js
@@ -1,25 +1,22 @@
1import { ipcRenderer } from 'electron'; 1import { ipcRenderer } from 'electron';
2import { v1 as uuidV1 } from 'uuid'; 2import uuidV1 from 'uuid/v1';
3 3
4const debug = require('debug')('Ferdi:Notifications'); 4const debug = require('debug')('Ferdi:Notifications');
5 5
6export class NotificationsHandler { 6export class NotificationsHandler {
7 onNotify = data => data; 7 onNotify = (data) => data;
8 8
9 displayNotification(title, options) { 9 displayNotification(title, options) {
10 return new Promise(resolve => { 10 return new Promise((resolve) => {
11 debug('New notification', title, options); 11 debug('New notification', title, options);
12 12
13 const notificationId = uuidV1(); 13 const notificationId = uuidV1();
14 14
15 ipcRenderer.sendToHost( 15 ipcRenderer.sendToHost('notification', this.onNotify({
16 'notification', 16 title,
17 this.onNotify({ 17 options,
18 title, 18 notificationId,
19 options, 19 }));
20 notificationId,
21 }),
22 );
23 20
24 ipcRenderer.once(`notification-onclick:${notificationId}`, () => { 21 ipcRenderer.once(`notification-onclick:${notificationId}`, () => {
25 resolve(); 22 resolve();
diff --git a/uidev/src/stories/input.stories.tsx b/uidev/src/stories/input.stories.tsx
index 4136cfd4d..889539266 100644
--- a/uidev/src/stories/input.stories.tsx
+++ b/uidev/src/stories/input.stories.tsx
@@ -1,5 +1,5 @@
1import React from 'react'; 1import React from 'react';
2import { v4 as uuid } from 'uuid'; 2import uuid from 'uuid/v4';
3 3
4import { Input } from '@meetfranz/forms'; 4import { Input } from '@meetfranz/forms';
5import { storiesOf } from '../stores/stories'; 5import { storiesOf } from '../stores/stories';
@@ -10,8 +10,7 @@ const defaultProps = () => {
10 label: 'Label', 10 label: 'Label',
11 id: `test-${id}`, 11 id: `test-${id}`,
12 name: `test-${id}`, 12 name: `test-${id}`,
13 onChange: (e: React.ChangeEvent<HTMLInputElement>) => 13 onChange: (e: React.ChangeEvent<HTMLInputElement>) => console.log('changed event', e),
14 console.log('changed event', e),
15 }; 14 };
16}; 15};
17 16
@@ -22,23 +21,44 @@ const defaultPasswordProps = () => {
22 id: `test-${id}`, 21 id: `test-${id}`,
23 name: `test-${id}`, 22 name: `test-${id}`,
24 type: 'password', 23 type: 'password',
25 onChange: (e: React.ChangeEvent<HTMLInputElement>) => 24 onChange: (e: React.ChangeEvent<HTMLInputElement>) => console.log('changed event', e),
26 console.log('changed event', e),
27 }; 25 };
28}; 26};
29 27
30storiesOf('Input') 28storiesOf('Input')
31 .add('Basic', () => ( 29 .add('Basic', () => (
32 <Input {...defaultProps()} placeholder="Placeholder text" /> 30 <Input
31 {...defaultProps()}
32 placeholder="Placeholder text"
33 />
34 ))
35 .add('Without Label', () => (
36 <Input
37 {...defaultProps()}
38 showLabel={false}
39 />
40 ))
41 .add('Disabled', () => (
42 <Input {...defaultProps()} disabled />
43 ))
44 .add('With prefix', () => (
45 <Input
46 {...defaultProps()}
47 prefix="https://"
48 />
33 )) 49 ))
34 .add('Without Label', () => <Input {...defaultProps()} showLabel={false} />)
35 .add('Disabled', () => <Input {...defaultProps()} disabled />)
36 .add('With prefix', () => <Input {...defaultProps()} prefix="https://" />)
37 .add('With suffix', () => ( 50 .add('With suffix', () => (
38 <Input {...defaultProps()} suffix=".meetfranz.com" /> 51 <Input
52 {...defaultProps()}
53 suffix=".meetfranz.com"
54 />
39 )) 55 ))
40 .add('With pre-suffix', () => ( 56 .add('With pre-suffix', () => (
41 <Input {...defaultProps()} prefix="https://" suffix=".meetfranz.com" /> 57 <Input
58 {...defaultProps()}
59 prefix="https://"
60 suffix=".meetfranz.com"
61 />
42 )) 62 ))
43 .add('With error', () => ( 63 .add('With error', () => (
44 <Input 64 <Input
@@ -48,16 +68,32 @@ storiesOf('Input')
48 /> 68 />
49 )) 69 ))
50 .add('Type number with min & max', () => ( 70 .add('Type number with min & max', () => (
51 <Input {...defaultProps()} type="number" min={1} max={10} /> 71 <Input
72 {...defaultProps()}
73 type="number"
74 min={1}
75 max={10}
76 />
52 )); 77 ));
53 78
54storiesOf('Password') 79storiesOf('Password')
55 .add('Basic', () => <Input {...defaultPasswordProps()} />) 80 .add('Basic', () => (
81 <Input
82 {...defaultPasswordProps()}
83 />
84 ))
56 .add('Show password toggle', () => ( 85 .add('Show password toggle', () => (
57 <Input {...defaultPasswordProps()} showPasswordToggle /> 86 <Input
87 {...defaultPasswordProps()}
88 showPasswordToggle
89 />
58 )) 90 ))
59 .add('Score password', () => ( 91 .add('Score password', () => (
60 <Input {...defaultPasswordProps()} showPasswordToggle scorePassword /> 92 <Input
93 {...defaultPasswordProps()}
94 showPasswordToggle
95 scorePassword
96 />
61 )) 97 ))
62 .add('Score password with error', () => ( 98 .add('Score password with error', () => (
63 <Input 99 <Input
diff --git a/uidev/src/stories/select.stories.tsx b/uidev/src/stories/select.stories.tsx
index 93e072cd9..51ec6ed88 100644
--- a/uidev/src/stories/select.stories.tsx
+++ b/uidev/src/stories/select.stories.tsx
@@ -1,5 +1,5 @@
1import React from 'react'; 1import React from 'react';
2import { v4 as uuid } from 'uuid'; 2import uuid from 'uuid/v4';
3 3
4import { Select } from '@meetfranz/forms'; 4import { Select } from '@meetfranz/forms';
5import { storiesOf } from '../stores/stories'; 5import { storiesOf } from '../stores/stories';
@@ -282,18 +282,35 @@ const defaultProps = () => {
282 }, 282 },
283 actionText: 'Select country', 283 actionText: 'Select country',
284 // defaultValue: 'AT', 284 // defaultValue: 'AT',
285 onChange: (e: React.ChangeEvent<HTMLInputElement>) => 285 onChange: (e: React.ChangeEvent<HTMLInputElement>) => console.log('changed event', e),
286 console.log('changed event', e),
287 }; 286 };
288}; 287};
289 288
290storiesOf('Select') 289storiesOf('Select')
291 .add('Basic', () => <Select {...defaultProps()} />) 290 .add('Basic', () => (
291 <Select
292 {...defaultProps()}
293 />
294 ))
292 .add('With preselection', () => ( 295 .add('With preselection', () => (
293 <Select {...defaultProps()} defaultValue="AT" /> 296 <Select
297 {...defaultProps()}
298 defaultValue="AT"
299 />
300 ))
301 .add('With search', () => (
302 <Select
303 {...defaultProps()}
304 showSearch
305 />
306 ))
307 .add('Disabled', () => (
308 <Select
309 {...defaultProps()}
310 showSearch
311 disabled
312 />
294 )) 313 ))
295 .add('With search', () => <Select {...defaultProps()} showSearch />)
296 .add('Disabled', () => <Select {...defaultProps()} showSearch disabled />)
297 .add('With error', () => ( 314 .add('With error', () => (
298 <Select 315 <Select
299 {...defaultProps()} 316 {...defaultProps()}
diff --git a/uidev/src/stories/textarea.stories.tsx b/uidev/src/stories/textarea.stories.tsx
index 09b9fef70..1ab21820b 100644
--- a/uidev/src/stories/textarea.stories.tsx
+++ b/uidev/src/stories/textarea.stories.tsx
@@ -1,5 +1,5 @@
1import React from 'react'; 1import React from 'react';
2import { v4 as uuid } from 'uuid'; 2import uuid from 'uuid/v4';
3 3
4import { Textarea } from '@meetfranz/forms'; 4import { Textarea } from '@meetfranz/forms';
5import { storiesOf } from '../stores/stories'; 5import { storiesOf } from '../stores/stories';
@@ -11,8 +11,7 @@ const defaultProps = () => {
11 id: `test-${id}`, 11 id: `test-${id}`,
12 name: `test-${id}`, 12 name: `test-${id}`,
13 rows: 5, 13 rows: 5,
14 onChange: (e: React.ChangeEvent<HTMLInputElement>) => 14 onChange: (e: React.ChangeEvent<HTMLInputElement>) => console.log('changed event', e),
15 console.log('changed event', e),
16 }; 15 };
17}; 16};
18 17
@@ -23,8 +22,22 @@ storiesOf('Textarea')
23 // placeholder="Placeholder text" 22 // placeholder="Placeholder text"
24 /> 23 />
25 )) 24 ))
26 .add('10 rows', () => <Textarea {...defaultProps()} rows={10} />) 25 .add('10 rows', () => (
26 <Textarea
27 {...defaultProps()}
28 rows={10}
29 />
30 ))
27 .add('With error', () => ( 31 .add('With error', () => (
28 <Textarea {...defaultProps()} error="This is a generic error message." /> 32 <Textarea
33 {...defaultProps()}
34 error="This is a generic error message."
35 />
29 )) 36 ))
30 .add('Disabled', () => <Textarea {...defaultProps()} rows={2} disabled />); 37 .add('Disabled', () => (
38 <Textarea
39 {...defaultProps()}
40 rows={2}
41 disabled
42 />
43 ));
diff --git a/uidev/src/stories/toggle.stories.tsx b/uidev/src/stories/toggle.stories.tsx
index f54e67596..af6b282bc 100644
--- a/uidev/src/stories/toggle.stories.tsx
+++ b/uidev/src/stories/toggle.stories.tsx
@@ -1,7 +1,7 @@
1import { observable } from 'mobx'; 1import { observable } from 'mobx';
2import { observer } from 'mobx-react'; 2import { observer } from 'mobx-react';
3import React from 'react'; 3import React from 'react';
4import { v4 as uuid } from 'uuid'; 4import uuid from 'uuid/v4';
5 5
6import { Toggle } from '@meetfranz/forms'; 6import { Toggle } from '@meetfranz/forms';
7import { storiesOf } from '../stores/stories'; 7import { storiesOf } from '../stores/stories';
@@ -16,17 +16,14 @@ interface IStoreArgs {
16 error?: string; 16 error?: string;
17} 17}
18 18
19const createStore = (args?: IStoreArgs) => 19const createStore = (args?: IStoreArgs) => observable({ id: `element-${uuid()}`,
20 observable({
21 id: `element-${uuid()}`,
22 name: 'toggle', 20 name: 'toggle',
23 label: 'Label', 21 label: 'Label',
24 value: true, 22 value: true,
25 checked: false, 23 checked: false,
26 disabled: false, 24 disabled: false,
27 error: '', 25 error: '',
28 ...args, 26...args });
29 });
30 27
31const WithStoreToggle = observer(({ store }: { store: any }) => ( 28const WithStoreToggle = observer(({ store }: { store: any }) => (
32 <> 29 <>
@@ -38,40 +35,37 @@ const WithStoreToggle = observer(({ store }: { store: any }) => (
38 name={store.name} 35 name={store.name}
39 disabled={store.disabled} 36 disabled={store.disabled}
40 error={store.error} 37 error={store.error}
41 onChange={() => (store.checked = !store.checked)} 38 onChange={() => store.checked = !store.checked}
42 /> 39 />
43 </> 40 </>
44)); 41));
45 42
46storiesOf('Toggle') 43storiesOf('Toggle')
47 .add('Basic', () => <WithStoreToggle store={createStore()} />) 44 .add('Basic', () => (
45 <WithStoreToggle store={createStore()} />
46 ))
48 .add('Checked', () => ( 47 .add('Checked', () => (
49 <WithStoreToggle 48 <WithStoreToggle store={createStore({
50 store={createStore({ 49 checked: true,
51 checked: true, 50 })}
52 })}
53 /> 51 />
54 )) 52 ))
55 .add('Disabled', () => ( 53 .add('Disabled', () => (
56 <WithStoreToggle 54 <WithStoreToggle store={createStore({
57 store={createStore({ 55 checked: true,
58 checked: true, 56 disabled: true,
59 disabled: true, 57 })}
60 })}
61 /> 58 />
62 )) 59 ))
63 .add('Long label', () => ( 60 .add('Long label', () => (
64 <WithStoreToggle 61 <WithStoreToggle store={createStore({
65 store={createStore({ 62 label: 'Hello world, this is an insanely long label for this toggle. We need to make sure that it will be displayed correctly.',
66 label: 63 })}
67 'Hello world, this is an insanely long label for this toggle. We need to make sure that it will be displayed correctly.',
68 })}
69 /> 64 />
70 )) 65 ))
71 .add('With error', () => ( 66 .add('With error', () => (
72 <WithStoreToggle 67 <WithStoreToggle store={createStore({
73 store={createStore({ 68 error: 'Something went wrong',
74 error: 'Something went wrong', 69 })}
75 })}
76 /> 70 />
77 )); 71 ));