diff options
author | Iaroslav <yavoloh@mail.ru> | 2021-10-23 19:16:01 +0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-23 19:46:01 +0530 |
commit | 4d02744dfab8a49075b82a5ddbdc02e08c7e8a66 (patch) | |
tree | dc7b08818a8f116d41886924552c92719c4d081a | |
parent | chore: upgrade npm modules (diff) | |
download | ferdium-app-4d02744dfab8a49075b82a5ddbdc02e08c7e8a66.tar.gz ferdium-app-4d02744dfab8a49075b82a5ddbdc02e08c7e8a66.tar.zst ferdium-app-4d02744dfab8a49075b82a5ddbdc02e08c7e8a66.zip |
Add active dialog title feature (#2114)
https://github.com/getferdi/ferdi/issues/1280
WhatsApp-like services can set active dialog title to the app title eg. Ferdi - WhatsApp - Contact Name
-rw-r--r-- | src/actions/service.ts | 4 | ||||
-rw-r--r-- | src/models/Service.js | 2 | ||||
-rw-r--r-- | src/stores/ServicesStore.js | 21 | ||||
-rw-r--r-- | src/webview/dialogTitle.ts | 33 | ||||
-rw-r--r-- | src/webview/lib/RecipeWebview.js | 24 | ||||
-rw-r--r-- | src/webview/lib/Userscript.js | 10 | ||||
-rw-r--r-- | src/webview/recipe.js | 5 |
7 files changed, 96 insertions, 3 deletions
diff --git a/src/actions/service.ts b/src/actions/service.ts index e56513f8f..aa02c860a 100644 --- a/src/actions/service.ts +++ b/src/actions/service.ts | |||
@@ -39,6 +39,10 @@ export default { | |||
39 | serviceId: PropTypes.string.isRequired, | 39 | serviceId: PropTypes.string.isRequired, |
40 | count: PropTypes.object.isRequired, | 40 | count: PropTypes.object.isRequired, |
41 | }, | 41 | }, |
42 | setDialogTitle: { | ||
43 | serviceId: PropTypes.string.isRequired, | ||
44 | dialogTitle: PropTypes.string.isRequired, | ||
45 | }, | ||
42 | setWebviewReference: { | 46 | setWebviewReference: { |
43 | serviceId: PropTypes.string.isRequired, | 47 | serviceId: PropTypes.string.isRequired, |
44 | webview: PropTypes.object.isRequired, | 48 | webview: PropTypes.object.isRequired, |
diff --git a/src/models/Service.js b/src/models/Service.js index d94f55692..3cf0523c3 100644 --- a/src/models/Service.js +++ b/src/models/Service.js | |||
@@ -37,6 +37,8 @@ export default class Service { | |||
37 | 37 | ||
38 | @observable unreadIndirectMessageCount = 0; | 38 | @observable unreadIndirectMessageCount = 0; |
39 | 39 | ||
40 | @observable dialogTitle = ''; | ||
41 | |||
40 | @observable order = DEFAULT_SERVICE_ORDER; | 42 | @observable order = DEFAULT_SERVICE_ORDER; |
41 | 43 | ||
42 | @observable isEnabled = true; | 44 | @observable isEnabled = true; |
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index 4f7ad7442..e52d661f3 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js | |||
@@ -82,6 +82,7 @@ export default class ServicesStore extends Store { | |||
82 | this.actions.service.setUnreadMessageCount.listen( | 82 | this.actions.service.setUnreadMessageCount.listen( |
83 | this._setUnreadMessageCount.bind(this), | 83 | this._setUnreadMessageCount.bind(this), |
84 | ); | 84 | ); |
85 | this.actions.service.setDialogTitle.listen(this._setDialogTitle.bind(this)); | ||
85 | this.actions.service.openWindow.listen(this._openWindow.bind(this)); | 86 | this.actions.service.openWindow.listen(this._openWindow.bind(this)); |
86 | this.actions.service.filter.listen(this._filter.bind(this)); | 87 | this.actions.service.filter.listen(this._filter.bind(this)); |
87 | this.actions.service.resetFilter.listen(this._resetFilter.bind(this)); | 88 | this.actions.service.resetFilter.listen(this._resetFilter.bind(this)); |
@@ -646,6 +647,12 @@ export default class ServicesStore extends Store { | |||
646 | service.unreadIndirectMessageCount = count.indirect; | 647 | service.unreadIndirectMessageCount = count.indirect; |
647 | } | 648 | } |
648 | 649 | ||
650 | @action _setDialogTitle({ serviceId, dialogTitle }) { | ||
651 | const service = this.one(serviceId); | ||
652 | |||
653 | service.dialogTitle = dialogTitle; | ||
654 | } | ||
655 | |||
649 | @action _setWebviewReference({ serviceId, webview }) { | 656 | @action _setWebviewReference({ serviceId, webview }) { |
650 | const service = this.one(serviceId); | 657 | const service = this.one(serviceId); |
651 | 658 | ||
@@ -741,6 +748,16 @@ export default class ServicesStore extends Store { | |||
741 | 748 | ||
742 | break; | 749 | break; |
743 | } | 750 | } |
751 | case 'active-dialog-title': { | ||
752 | debug(`Received active dialog title from '${serviceId}'`, args[0]); | ||
753 | |||
754 | this.actions.service.setDialogTitle({ | ||
755 | serviceId, | ||
756 | dialogTitle: args[0], | ||
757 | }); | ||
758 | |||
759 | break; | ||
760 | } | ||
744 | case 'notification': { | 761 | case 'notification': { |
745 | const { options } = args[0]; | 762 | const { options } = args[0]; |
746 | 763 | ||
@@ -1063,7 +1080,9 @@ export default class ServicesStore extends Store { | |||
1063 | const service = this.active; | 1080 | const service = this.active; |
1064 | if (service) { | 1081 | if (service) { |
1065 | this.actions.service.focusService({ serviceId: service.id }); | 1082 | this.actions.service.focusService({ serviceId: service.id }); |
1066 | document.title = `Ferdi - ${service.name}`; | 1083 | document.title = `Ferdi - ${service.name} ${ |
1084 | service.dialogTitle ? ` - ${service.dialogTitle}` : '' | ||
1085 | }`; | ||
1067 | } else { | 1086 | } else { |
1068 | debug('No service is active'); | 1087 | debug('No service is active'); |
1069 | } | 1088 | } |
diff --git a/src/webview/dialogTitle.ts b/src/webview/dialogTitle.ts new file mode 100644 index 000000000..f9a1aac6f --- /dev/null +++ b/src/webview/dialogTitle.ts | |||
@@ -0,0 +1,33 @@ | |||
1 | import { ipcRenderer } from 'electron'; | ||
2 | |||
3 | const debug = require('debug')('Ferdi:Plugin:DialogTitleHandler'); | ||
4 | |||
5 | export class DialogTitleHandler { | ||
6 | titleCache: { title: string }; | ||
7 | |||
8 | constructor() { | ||
9 | this.titleCache = { | ||
10 | title: '', | ||
11 | }; | ||
12 | } | ||
13 | |||
14 | safeGetTitle(title: string | undefined | null) { | ||
15 | if (!title) { | ||
16 | return ''; | ||
17 | } | ||
18 | |||
19 | return title; | ||
20 | } | ||
21 | |||
22 | setDialogTitle(title: string | undefined | null) { | ||
23 | const newTitle = this.safeGetTitle(title); | ||
24 | if (this.titleCache.title === newTitle) { | ||
25 | return; | ||
26 | } | ||
27 | |||
28 | debug('Sending active dialog title to host %s', newTitle); | ||
29 | ipcRenderer.sendToHost('active-dialog-title', newTitle); | ||
30 | |||
31 | this.titleCache.title = newTitle; | ||
32 | } | ||
33 | } | ||
diff --git a/src/webview/lib/RecipeWebview.js b/src/webview/lib/RecipeWebview.js index f1d493e7c..ebe88ed85 100644 --- a/src/webview/lib/RecipeWebview.js +++ b/src/webview/lib/RecipeWebview.js | |||
@@ -5,8 +5,14 @@ import { pathExistsSync, readFileSync, existsSync } from 'fs-extra'; | |||
5 | const debug = require('debug')('Ferdi:Plugin:RecipeWebview'); | 5 | const debug = require('debug')('Ferdi:Plugin:RecipeWebview'); |
6 | 6 | ||
7 | class RecipeWebview { | 7 | class RecipeWebview { |
8 | constructor(badgeHandler, notificationsHandler, sessionHandler) { | 8 | constructor( |
9 | badgeHandler, | ||
10 | dialogTitleHandler, | ||
11 | notificationsHandler, | ||
12 | sessionHandler, | ||
13 | ) { | ||
9 | this.badgeHandler = badgeHandler; | 14 | this.badgeHandler = badgeHandler; |
15 | this.dialogTitleHandler = dialogTitleHandler; | ||
10 | this.notificationsHandler = notificationsHandler; | 16 | this.notificationsHandler = notificationsHandler; |
11 | this.sessionHandler = sessionHandler; | 17 | this.sessionHandler = sessionHandler; |
12 | 18 | ||
@@ -59,6 +65,17 @@ class RecipeWebview { | |||
59 | } | 65 | } |
60 | 66 | ||
61 | /** | 67 | /** |
68 | * Set the active dialog title to the app title | ||
69 | * | ||
70 | * @param {string | undefined | null} title Set the active dialog title | ||
71 | * to the app title | ||
72 | * eg. WhatsApp contact name | ||
73 | */ | ||
74 | setDialogTitle(title) { | ||
75 | this.dialogTitleHandler.setDialogTitle(title); | ||
76 | } | ||
77 | |||
78 | /** | ||
62 | * Safely parse the given text into an integer | 79 | * Safely parse the given text into an integer |
63 | * | 80 | * |
64 | * @param {string | number | undefined | null} text to be parsed | 81 | * @param {string | number | undefined | null} text to be parsed |
@@ -127,7 +144,10 @@ class RecipeWebview { | |||
127 | } | 144 | } |
128 | 145 | ||
129 | clearStorageData(serviceId, targetsToClear) { | 146 | clearStorageData(serviceId, targetsToClear) { |
130 | ipcRenderer.send('clear-storage-data', { serviceId, targetsToClear }); | 147 | ipcRenderer.send('clear-storage-data', { |
148 | serviceId, | ||
149 | targetsToClear, | ||
150 | }); | ||
131 | } | 151 | } |
132 | 152 | ||
133 | releaseServiceWorkers() { | 153 | releaseServiceWorkers() { |
diff --git a/src/webview/lib/Userscript.js b/src/webview/lib/Userscript.js index bed2b1ff8..f7bb99206 100644 --- a/src/webview/lib/Userscript.js +++ b/src/webview/lib/Userscript.js | |||
@@ -60,6 +60,16 @@ export default class Userscript { | |||
60 | } | 60 | } |
61 | 61 | ||
62 | /** | 62 | /** |
63 | * Set active dialog title to the app title | ||
64 | * @param {*} title Dialog title | ||
65 | */ | ||
66 | setDialogTitle(title) { | ||
67 | if (this.recipe && this.recipe.setDialogTitle) { | ||
68 | this.recipe.setDialogTitle(title); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | /** | ||
63 | * Inject CSS files into the current page | 73 | * Inject CSS files into the current page |
64 | * | 74 | * |
65 | * @param {...string} files | 75 | * @param {...string} files |
diff --git a/src/webview/recipe.js b/src/webview/recipe.js index 5cab28c09..92c1ee2f0 100644 --- a/src/webview/recipe.js +++ b/src/webview/recipe.js | |||
@@ -23,6 +23,7 @@ import RecipeWebview from './lib/RecipeWebview'; | |||
23 | import Userscript from './lib/Userscript'; | 23 | import Userscript from './lib/Userscript'; |
24 | 24 | ||
25 | import { BadgeHandler } from './badge'; | 25 | import { BadgeHandler } from './badge'; |
26 | import { DialogTitleHandler } from './dialogTitle'; | ||
26 | import { SessionHandler } from './sessionHandler'; | 27 | import { SessionHandler } from './sessionHandler'; |
27 | import contextMenu from './contextMenu'; | 28 | import contextMenu from './contextMenu'; |
28 | import { | 29 | import { |
@@ -51,6 +52,8 @@ const debug = require('debug')('Ferdi:Plugin'); | |||
51 | 52 | ||
52 | const badgeHandler = new BadgeHandler(); | 53 | const badgeHandler = new BadgeHandler(); |
53 | 54 | ||
55 | const dialogTitleHandler = new DialogTitleHandler(); | ||
56 | |||
54 | const sessionHandler = new SessionHandler(); | 57 | const sessionHandler = new SessionHandler(); |
55 | 58 | ||
56 | const notificationsHandler = new NotificationsHandler(); | 59 | const notificationsHandler = new NotificationsHandler(); |
@@ -106,6 +109,7 @@ contextBridge.exposeInMainWorld('ferdi', { | |||
106 | open: window.open, | 109 | open: window.open, |
107 | setBadge: (direct, indirect) => badgeHandler.setBadge(direct, indirect), | 110 | setBadge: (direct, indirect) => badgeHandler.setBadge(direct, indirect), |
108 | safeParseInt: text => badgeHandler.safeParseInt(text), | 111 | safeParseInt: text => badgeHandler.safeParseInt(text), |
112 | setDialogTitle: title => dialogTitleHandler.setDialogTitle(title), | ||
109 | displayNotification: (title, options) => | 113 | displayNotification: (title, options) => |
110 | notificationsHandler.displayNotification(title, options), | 114 | notificationsHandler.displayNotification(title, options), |
111 | getDisplayMediaSelector, | 115 | getDisplayMediaSelector, |
@@ -200,6 +204,7 @@ class RecipeController { | |||
200 | try { | 204 | try { |
201 | this.recipe = new RecipeWebview( | 205 | this.recipe = new RecipeWebview( |
202 | badgeHandler, | 206 | badgeHandler, |
207 | dialogTitleHandler, | ||
203 | notificationsHandler, | 208 | notificationsHandler, |
204 | sessionHandler, | 209 | sessionHandler, |
205 | ); | 210 | ); |