diff options
-rw-r--r-- | all.json | 10 | ||||
-rw-r--r-- | mastodon.tar.gz | bin | 0 -> 42658 bytes | |||
-rw-r--r-- | uncompressed/mastodon/LICENSE | 21 | ||||
-rw-r--r-- | uncompressed/mastodon/README.md | 71 | ||||
-rw-r--r-- | uncompressed/mastodon/icon.png | bin | 0 -> 40205 bytes | |||
-rw-r--r-- | uncompressed/mastodon/icon.svg | 1 | ||||
-rw-r--r-- | uncompressed/mastodon/index.js | 53 | ||||
-rw-r--r-- | uncompressed/mastodon/package.json | 17 | ||||
-rw-r--r-- | uncompressed/mastodon/webview.js | 82 |
9 files changed, 255 insertions, 0 deletions
@@ -836,6 +836,16 @@ | |||
836 | } | 836 | } |
837 | }, | 837 | }, |
838 | { | 838 | { |
839 | "author": "sharkpp <webmaster@sharkpp.net>", | ||
840 | "featured": false, | ||
841 | "id": "mastodon", | ||
842 | "name": "Mastodon", | ||
843 | "version": "1.0.1", | ||
844 | "icons": { | ||
845 | "png": "https://cdn.jsdelivr.net/gh/getferdi/recipes/uncompressed/mastodon/icon.png", | ||
846 | "svg": "https://cdn.jsdelivr.net/gh/getferdi/recipes/uncompressed/mastodon/icon.svg" | ||
847 | }, | ||
848 | { | ||
839 | "author": "Stefan Malzner <stefan@adlk.io>", | 849 | "author": "Stefan Malzner <stefan@adlk.io>", |
840 | "featured": false, | 850 | "featured": false, |
841 | "id": "nextcloud-talk", | 851 | "id": "nextcloud-talk", |
diff --git a/mastodon.tar.gz b/mastodon.tar.gz new file mode 100644 index 0000000..299c229 --- /dev/null +++ b/mastodon.tar.gz | |||
Binary files differ | |||
diff --git a/uncompressed/mastodon/LICENSE b/uncompressed/mastodon/LICENSE new file mode 100644 index 0000000..aabad64 --- /dev/null +++ b/uncompressed/mastodon/LICENSE | |||
@@ -0,0 +1,21 @@ | |||
1 | The MIT License (MIT) | ||
2 | |||
3 | Copyright (c) 2018 sharkpp. | ||
4 | |||
5 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
6 | of this software and associated documentation files (the "Software"), to deal | ||
7 | in the Software without restriction, including without limitation the rights | ||
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
9 | copies of the Software, and to permit persons to whom the Software is | ||
10 | furnished to do so, subject to the following conditions: | ||
11 | |||
12 | The above copyright notice and this permission notice shall be included in | ||
13 | all copies or substantial portions of the Software. | ||
14 | |||
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
21 | THE SOFTWARE. | ||
diff --git a/uncompressed/mastodon/README.md b/uncompressed/mastodon/README.md new file mode 100644 index 0000000..b0477b6 --- /dev/null +++ b/uncompressed/mastodon/README.md | |||
@@ -0,0 +1,71 @@ | |||
1 | # Mastodon for Franz 5 | ||
2 | |||
3 | This is the [Franz](https://meetfranz.com/) 5 recipe for Mastodon | ||
4 | |||
5 | ## REQUIRED | ||
6 | |||
7 | * [Franz](https://meetfranz.com/) 5 or later | ||
8 | Do not support older version. | ||
9 | * [Mastodon](https://joinmastodon.org/) account | ||
10 | Do not have an account? | ||
11 | Choose from "[Mastodon instances](https://instances.social/list)"! | ||
12 | |||
13 | ## HOW TO INSTALL | ||
14 | |||
15 | ### DEVELOP VERSION | ||
16 | |||
17 | 1. download the `franz-recipe-mastodon` folder. | ||
18 | 2. Open the Franz Plugins folder on your machine (note **that this `dev` directory may not exist yet, and you must create it**): | ||
19 | * Mac: `~/Library/Application Support/Franz/recipes/dev/` | ||
20 | * Windows: `%AppData%\Franz\recipes\dev\` | ||
21 | * Linux: `~/.config/Franz/recipes/dev` | ||
22 | 3. Copy the `franz-recipe-mastodon` folder into the plugins directory | ||
23 | 4. Reload Franz | ||
24 | |||
25 | See [Franz Recipe Documentation / Overview](https://github.com/meetfranz/plugins/blob/master/docs/integration.md) | ||
26 | for details. | ||
27 | |||
28 | ### STABLE VERSION | ||
29 | |||
30 | *Coming Soon ...* | ||
31 | |||
32 | ## USAGE | ||
33 | |||
34 | ### ADD NEW SERVICE | ||
35 | |||
36 | ![](docs/add-service.png) | ||
37 | |||
38 | *develop version* | ||
39 | |||
40 | ### SETTINGS | ||
41 | |||
42 | ![](docs/add-service-settings.png) | ||
43 | |||
44 | | # | field | description | | ||
45 | |-|-|-| | ||
46 | | (1) | service name | Please set freely | | ||
47 | | (2) | Mastodon host | Please enter the host on which Mastodon is running. Only https is supported. | | ||
48 | |||
49 | ### SIGN-IN | ||
50 | |||
51 | ![](docs/mstdn_jp-signin.png) | ||
52 | |||
53 | Please enter registered account information. | ||
54 | |||
55 | ### NOTIFICATION BADGE | ||
56 | |||
57 | ![](docs/notification-badge-example.png) | ||
58 | |||
59 | In order to use it, desktop notification must be enabled on *mastodon* side. | ||
60 | |||
61 | | current actived service | clear badge | | ||
62 | |-|-| | ||
63 | | this service | At the end of 10 seconds from the last notice | | ||
64 | | other service | this service actived | | ||
65 | |||
66 | |||
67 | ## LICENSE | ||
68 | |||
69 | © 2018 sharkpp | ||
70 | |||
71 | This software is licensed under a [The MIT License](http://opensource.org/licenses/MIT). | ||
diff --git a/uncompressed/mastodon/icon.png b/uncompressed/mastodon/icon.png new file mode 100644 index 0000000..a26dcd5 --- /dev/null +++ b/uncompressed/mastodon/icon.png | |||
Binary files differ | |||
diff --git a/uncompressed/mastodon/icon.svg b/uncompressed/mastodon/icon.svg new file mode 100644 index 0000000..4b72b3a --- /dev/null +++ b/uncompressed/mastodon/icon.svg | |||
@@ -0,0 +1 @@ | |||
<svg xmlns="http://www.w3.org/2000/svg" width="61.076954mm" height="65.47831mm" viewBox="0 0 216.4144 232.00976"><path d="M211.80734 139.0875c-3.18125 16.36625-28.4925 34.2775-57.5625 37.74875-15.15875 1.80875-30.08375 3.47125-45.99875 2.74125-26.0275-1.1925-46.565-6.2125-46.565-6.2125 0 2.53375.15625 4.94625.46875 7.2025 3.38375 25.68625 25.47 27.225 46.39125 27.9425 21.11625.7225 39.91875-5.20625 39.91875-5.20625l.8675 19.09s-14.77 7.93125-41.08125 9.39c-14.50875.7975-32.52375-.365-53.50625-5.91875C9.23234 213.82 1.40609 165.31125.20859 116.09125c-.365-14.61375-.14-28.39375-.14-39.91875 0-50.33 32.97625-65.0825 32.97625-65.0825C49.67234 3.45375 78.20359.2425 107.86484 0h.72875c29.66125.2425 58.21125 3.45375 74.8375 11.09 0 0 32.975 14.7525 32.975 65.0825 0 0 .41375 37.13375-4.59875 62.915" fill="#3088d4"/><path d="M177.50984 80.077v60.94125h-24.14375v-59.15c0-12.46875-5.24625-18.7975-15.74-18.7975-11.6025 0-17.4175 7.5075-17.4175 22.3525v32.37625H96.20734V85.42325c0-14.845-5.81625-22.3525-17.41875-22.3525-10.49375 0-15.74 6.32875-15.74 18.7975v59.15H38.90484V80.077c0-12.455 3.17125-22.3525 9.54125-29.675 6.56875-7.3225 15.17125-11.07625 25.85-11.07625 12.355 0 21.71125 4.74875 27.8975 14.2475l6.01375 10.08125 6.015-10.08125c6.185-9.49875 15.54125-14.2475 27.8975-14.2475 10.6775 0 19.28 3.75375 25.85 11.07625 6.36875 7.3225 9.54 17.22 9.54 29.675" fill="#fff"/></svg> | |||
diff --git a/uncompressed/mastodon/index.js b/uncompressed/mastodon/index.js new file mode 100644 index 0000000..a2ca0b7 --- /dev/null +++ b/uncompressed/mastodon/index.js | |||
@@ -0,0 +1,53 @@ | |||
1 | //'use strict'; | ||
2 | |||
3 | module.exports = Franz => class Mastodon extends Franz { | ||
4 | |||
5 | constructor(...args) { | ||
6 | let _temp; | ||
7 | // | ||
8 | let serviceCache = {}; | ||
9 | // send service store for webview | ||
10 | setInterval(() => { | ||
11 | if (!window.franz) { // not present(near equal not initialize) | ||
12 | return; | ||
13 | } | ||
14 | const services = window.franz.stores.services; | ||
15 | // filter this recipe class | ||
16 | const instancedServices = services.all.filter(service => service.recipe.constructor === this.constructor); | ||
17 | // send | ||
18 | instancedServices.forEach(service => { | ||
19 | let updated = false; | ||
20 | serviceCache[service.id] = Object.keys(service).reduce((r, key) => { | ||
21 | if (!service[key] || Object !== service[key].constructor) { | ||
22 | updated = updated || serviceCache[key] !== service[key]; | ||
23 | r[key] = service[key]; | ||
24 | } | ||
25 | return r; | ||
26 | }, serviceCache[service.id] || {}); | ||
27 | if (updated) { | ||
28 | service.webview.send('-service-update', service); | ||
29 | } | ||
30 | }); | ||
31 | }, 1000); | ||
32 | return _temp = super(...args), this.events = { | ||
33 | }, _temp; | ||
34 | } | ||
35 | |||
36 | async validateUrl(url) { | ||
37 | try { | ||
38 | const res = await window.fetch(`${url}/api/v1/instance/`, { | ||
39 | method: 'GET', | ||
40 | headers: { | ||
41 | 'Content-Type': 'application/json', | ||
42 | }, | ||
43 | }); | ||
44 | const data = await res.json(); | ||
45 | // check url field in API response | ||
46 | return Object.hasOwnProperty.call(data, 'uri'); | ||
47 | } catch (err) { | ||
48 | console.error(err); | ||
49 | } | ||
50 | return false; | ||
51 | } | ||
52 | |||
53 | }; | ||
diff --git a/uncompressed/mastodon/package.json b/uncompressed/mastodon/package.json new file mode 100644 index 0000000..84a17e8 --- /dev/null +++ b/uncompressed/mastodon/package.json | |||
@@ -0,0 +1,17 @@ | |||
1 | { | ||
2 | "id": "mastodon", | ||
3 | "name": "Mastodon", | ||
4 | "version": "1.0.1", | ||
5 | "description": "Mastodon recipe for Franz 5 or later", | ||
6 | "main": "index.js", | ||
7 | "author": "sharkpp <webmaster@sharkpp.net>", | ||
8 | "license": "MIT", | ||
9 | "repository": "https://github.com/sharkpp/franz-recipe-mastodon", | ||
10 | "config": { | ||
11 | "serviceURL": "https://{teamId}", | ||
12 | "hasTeamId": true, | ||
13 | "hasNotificationSound": true, | ||
14 | "hasIndirectMessages": false, | ||
15 | "message": "Please enter the instance address of Mastodon in the team name input box." | ||
16 | } | ||
17 | } | ||
diff --git a/uncompressed/mastodon/webview.js b/uncompressed/mastodon/webview.js new file mode 100644 index 0000000..dcf715e --- /dev/null +++ b/uncompressed/mastodon/webview.js | |||
@@ -0,0 +1,82 @@ | |||
1 | //'use strict'; | ||
2 | |||
3 | const { ipcRenderer } = require('electron'); | ||
4 | |||
5 | const BADGE_AUTO_CLEAR_DELAY = 10000; // delay time for badge clear auto | ||
6 | |||
7 | // MONKEY PATCH: | ||
8 | // fix 'Uncaught TypeError: c.addEventListener is not a function' | ||
9 | // from mastdon code | ||
10 | // see https://github.com/tootsuite/mastodon/blob/c1a41181c52216de9ebeecebf418e6d50172139b/app/javascript/mastodon/actions/notifications.js#L60 | ||
11 | if (!Notification.prototype.addEventListener) { | ||
12 | Notification.prototype.addEventListener = function(){}; | ||
13 | } | ||
14 | |||
15 | module.exports = (Franz, service_) => { | ||
16 | |||
17 | let service = service_; | ||
18 | |||
19 | // save service instance identify | ||
20 | const serviceId = service.id; | ||
21 | |||
22 | // check if this service is active | ||
23 | let activeUpdated = false; | ||
24 | let isActive = service.isActive; | ||
25 | |||
26 | ipcRenderer.on('-service-update', (sender, serviceNew) => { | ||
27 | service = serviceNew; | ||
28 | !activeUpdated && (activeUpdated = isActive != service.isActive); | ||
29 | isActive = service.isActive; | ||
30 | }); | ||
31 | |||
32 | //ipcRenderer.on('settings-update', (sender, settings) => { | ||
33 | // const nextIsActive = serviceId == settings.activeService; | ||
34 | // !activeUpdated && (activeUpdated = isActive != nextIsActive); | ||
35 | // isActive = nextIsActive; | ||
36 | //}); | ||
37 | |||
38 | let replyCount = 0; | ||
39 | let limitBadgeClear = false; | ||
40 | |||
41 | const getMessages = function getMessages() { | ||
42 | const activeUpdated_ = activeUpdated; activeUpdated = false; | ||
43 | |||
44 | // check and redirect to signin page when not loggdin | ||
45 | if (window.location && /\/about$/.test(window.location.pathname)) { | ||
46 | const hasSigninLink = !!document.querySelector('[href$="/auth/sign_in"]'); | ||
47 | if (hasSigninLink) { | ||
48 | window.location.pathname = '/auth/sign_in'; | ||
49 | return; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | // clear replay badge when ... | ||
54 | if (replyCount) { | ||
55 | // this service actived | ||
56 | if (activeUpdated_ && isActive) { | ||
57 | replyCount = 0; | ||
58 | } | ||
59 | // timeout | ||
60 | if (!isActive && limitBadgeClear) { | ||
61 | limitBadgeClear = false; | ||
62 | } | ||
63 | if (isActive && false !== limitBadgeClear && limitBadgeClear <= Date.now()) { | ||
64 | replyCount = 0; | ||
65 | limitBadgeClear = false; | ||
66 | } | ||
67 | } | ||
68 | |||
69 | Franz.setBadge(replyCount); | ||
70 | }; | ||
71 | |||
72 | Franz.loop(getMessages); | ||
73 | |||
74 | Franz.onNotify(notification => { | ||
75 | // increment reply count for badge | ||
76 | ++replyCount; | ||
77 | limitBadgeClear = Date.now() + BADGE_AUTO_CLEAR_DELAY; | ||
78 | // | ||
79 | return notification; | ||
80 | |||
81 | }); | ||
82 | }; \ No newline at end of file | ||