From 4d02744dfab8a49075b82a5ddbdc02e08c7e8a66 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sat, 23 Oct 2021 19:16:01 +0500 Subject: 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 --- src/actions/service.ts | 4 ++++ src/models/Service.js | 2 ++ src/stores/ServicesStore.js | 21 ++++++++++++++++++++- src/webview/dialogTitle.ts | 33 +++++++++++++++++++++++++++++++++ src/webview/lib/RecipeWebview.js | 24 ++++++++++++++++++++++-- src/webview/lib/Userscript.js | 10 ++++++++++ src/webview/recipe.js | 5 +++++ 7 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 src/webview/dialogTitle.ts 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 { serviceId: PropTypes.string.isRequired, count: PropTypes.object.isRequired, }, + setDialogTitle: { + serviceId: PropTypes.string.isRequired, + dialogTitle: PropTypes.string.isRequired, + }, setWebviewReference: { serviceId: PropTypes.string.isRequired, 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 { @observable unreadIndirectMessageCount = 0; + @observable dialogTitle = ''; + @observable order = DEFAULT_SERVICE_ORDER; @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 { this.actions.service.setUnreadMessageCount.listen( this._setUnreadMessageCount.bind(this), ); + this.actions.service.setDialogTitle.listen(this._setDialogTitle.bind(this)); this.actions.service.openWindow.listen(this._openWindow.bind(this)); this.actions.service.filter.listen(this._filter.bind(this)); this.actions.service.resetFilter.listen(this._resetFilter.bind(this)); @@ -646,6 +647,12 @@ export default class ServicesStore extends Store { service.unreadIndirectMessageCount = count.indirect; } + @action _setDialogTitle({ serviceId, dialogTitle }) { + const service = this.one(serviceId); + + service.dialogTitle = dialogTitle; + } + @action _setWebviewReference({ serviceId, webview }) { const service = this.one(serviceId); @@ -741,6 +748,16 @@ export default class ServicesStore extends Store { break; } + case 'active-dialog-title': { + debug(`Received active dialog title from '${serviceId}'`, args[0]); + + this.actions.service.setDialogTitle({ + serviceId, + dialogTitle: args[0], + }); + + break; + } case 'notification': { const { options } = args[0]; @@ -1063,7 +1080,9 @@ export default class ServicesStore extends Store { const service = this.active; if (service) { this.actions.service.focusService({ serviceId: service.id }); - document.title = `Ferdi - ${service.name}`; + document.title = `Ferdi - ${service.name} ${ + service.dialogTitle ? ` - ${service.dialogTitle}` : '' + }`; } else { debug('No service is active'); } 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 @@ +import { ipcRenderer } from 'electron'; + +const debug = require('debug')('Ferdi:Plugin:DialogTitleHandler'); + +export class DialogTitleHandler { + titleCache: { title: string }; + + constructor() { + this.titleCache = { + title: '', + }; + } + + safeGetTitle(title: string | undefined | null) { + if (!title) { + return ''; + } + + return title; + } + + setDialogTitle(title: string | undefined | null) { + const newTitle = this.safeGetTitle(title); + if (this.titleCache.title === newTitle) { + return; + } + + debug('Sending active dialog title to host %s', newTitle); + ipcRenderer.sendToHost('active-dialog-title', newTitle); + + this.titleCache.title = newTitle; + } +} 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'; const debug = require('debug')('Ferdi:Plugin:RecipeWebview'); class RecipeWebview { - constructor(badgeHandler, notificationsHandler, sessionHandler) { + constructor( + badgeHandler, + dialogTitleHandler, + notificationsHandler, + sessionHandler, + ) { this.badgeHandler = badgeHandler; + this.dialogTitleHandler = dialogTitleHandler; this.notificationsHandler = notificationsHandler; this.sessionHandler = sessionHandler; @@ -58,6 +64,17 @@ class RecipeWebview { this.badgeHandler.setBadge(direct, indirect); } + /** + * Set the active dialog title to the app title + * + * @param {string | undefined | null} title Set the active dialog title + * to the app title + * eg. WhatsApp contact name + */ + setDialogTitle(title) { + this.dialogTitleHandler.setDialogTitle(title); + } + /** * Safely parse the given text into an integer * @@ -127,7 +144,10 @@ class RecipeWebview { } clearStorageData(serviceId, targetsToClear) { - ipcRenderer.send('clear-storage-data', { serviceId, targetsToClear }); + ipcRenderer.send('clear-storage-data', { + serviceId, + targetsToClear, + }); } 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 @@ -59,6 +59,16 @@ export default class Userscript { } } + /** + * Set active dialog title to the app title + * @param {*} title Dialog title + */ + setDialogTitle(title) { + if (this.recipe && this.recipe.setDialogTitle) { + this.recipe.setDialogTitle(title); + } + } + /** * Inject CSS files into the current page * 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'; import Userscript from './lib/Userscript'; import { BadgeHandler } from './badge'; +import { DialogTitleHandler } from './dialogTitle'; import { SessionHandler } from './sessionHandler'; import contextMenu from './contextMenu'; import { @@ -51,6 +52,8 @@ const debug = require('debug')('Ferdi:Plugin'); const badgeHandler = new BadgeHandler(); +const dialogTitleHandler = new DialogTitleHandler(); + const sessionHandler = new SessionHandler(); const notificationsHandler = new NotificationsHandler(); @@ -106,6 +109,7 @@ contextBridge.exposeInMainWorld('ferdi', { open: window.open, setBadge: (direct, indirect) => badgeHandler.setBadge(direct, indirect), safeParseInt: text => badgeHandler.safeParseInt(text), + setDialogTitle: title => dialogTitleHandler.setDialogTitle(title), displayNotification: (title, options) => notificationsHandler.displayNotification(title, options), getDisplayMediaSelector, @@ -200,6 +204,7 @@ class RecipeController { try { this.recipe = new RecipeWebview( badgeHandler, + dialogTitleHandler, notificationsHandler, sessionHandler, ); -- cgit v1.2.3-54-g00ecf