From f1152d3dbb4c6deefea168d66f15f77b7155a5fe Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Wed, 15 Mar 2023 17:26:13 +0100 Subject: Basic D-Bus API (#866) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: basic D-Bus API Expose muted state and the number of unread message over D-Bus when running on Linux. This is useful for, e.g., displaying notifications on a window manager status bar. Signed-off-by: Kristóf Marussy * docs: create docs directory Move the documentation to a separate directory so that new documentation can be added into one place. We keep the following files still in the repository root by convention: * CHANGELOG.md * CODE_OF_CONDUCT.md * CONTRIBUTING.md * LICENSE.md * README.md * SECURITY.md Signed-off-by: Kristóf Marussy * docs: D-Bus usage example Signed-off-by: Kristóf Marussy * fix: remove unneeded D-Bus signals Only notify clients that the message counts or the mute status has changed if there actually was a change. Signed-off-by: Kristóf Marussy * docs: rewrite sample bar client * docs: better unread --services help * docs: update dbus docs * docs: use ferdium-dbus in dbus bar example * docs: make command argument required in bar example --------- Signed-off-by: Kristóf Marussy Co-authored-by: Victor Bonnelle --- INTERNAL_SERVER.md | 28 ---------- MIGRATION.md | 38 ------------- README.md | 2 +- docs/DBUS.md | 20 +++++++ docs/INTERNAL_SERVER.md | 28 ++++++++++ docs/MIGRATION.md | 38 +++++++++++++ docs/dbus/ferdium_bar.py | 102 ++++++++++++++++++++++++++++++++++ docs/dbus/org.ferdium.Ferdium.xml | 80 ++++++++++++++++++++++++++ docs/dbus/requirements.txt | 1 + scripts/migration/migrate-unix.sh | 2 +- scripts/migration/migrate-windows.ps1 | 2 +- src/lib/DBus.ts | 69 ++++++++++++++++++++++- src/lib/dbus/Ferdium.ts | 88 +++++++++++++++++++++++++++++ src/stores/ServicesStore.ts | 52 ++++++++++------- 14 files changed, 458 insertions(+), 92 deletions(-) delete mode 100644 INTERNAL_SERVER.md delete mode 100644 MIGRATION.md create mode 100644 docs/DBUS.md create mode 100644 docs/INTERNAL_SERVER.md create mode 100644 docs/MIGRATION.md create mode 100644 docs/dbus/ferdium_bar.py create mode 100644 docs/dbus/org.ferdium.Ferdium.xml create mode 100644 docs/dbus/requirements.txt create mode 100644 src/lib/dbus/Ferdium.ts diff --git a/INTERNAL_SERVER.md b/INTERNAL_SERVER.md deleted file mode 100644 index 1a1284f7e..000000000 --- a/INTERNAL_SERVER.md +++ /dev/null @@ -1,28 +0,0 @@ -

- -

- -# ferdium-internal-server -Internal Ferdium Server used for storing settings/preferences without logging into an external server. - -## Differences to ferdium-server -- Doesn't contain user management (only one user) -- Doesn't require logging in -- No recipe creation -- Contains `start.js` script to allow starting the server via script -- Uses `env.ini` instead of `.env` to stay compatible with Ferdium's build script -- Only allows Ferdium clients to connect to the API - -## Configuration -`ferdium-internal-server's` configuration is saved inside the `env.ini` file. Besides AdonisJS's settings, `ferdium-internal-server` has the following custom settings: -- `CONNECT_WITH_FRANZ` (`true` or `false`, default: `true`): Whether to enable connections to the Franz server. By enabling this option, `ferdium-internal-server` can: - - Show the full Franz/Ferdi recipe library instead of only custom recipes - - Import Franz/Ferdi accounts - -## Exporting backups -Since the `ferdium-internal-server` runs a local server, there's no automatic syncing of settings possible. You can backup your settings, by clicking on `Help > Import/Export Configuration Data` which will open the running server page in your browser. Choose the option to export and save the generated file. - -## Importing your Franz/Ferdi account -`ferdium-internal-server` allows you to import your full Franz account, including all its settings. - -To import your Franz/Ferdi account, within Ferdium, click on `Help > Import/Export Configuration Data` which will open the running server page in your browser. You can then login using your Franz account details. `ferdium-internal-server` will create a new user with the same credentials and copy your Franz settings, services and workspaces. diff --git a/MIGRATION.md b/MIGRATION.md deleted file mode 100644 index bb1ca315d..000000000 --- a/MIGRATION.md +++ /dev/null @@ -1,38 +0,0 @@ -# Migrating account data - -This guide is here to show you how to export your current Ferdi setup from the online server or from an accountless session for safe-keeping, backup or transferring to a new instance using only a browser. - -Before getting into the details of using an online account vs accountless, let's quickly review what the differences are between the two of them: - -## Using Ferdi with an Account - -The main advantage of using Ferdi with an account is that you have your configuration data stored on a cloud - and thus, when moving to a different machine, once you login, all these configurations are applied to the Ferdi instance on your new machine. - -**But**, if you use a Ferdi account, you need to reach their servers. If the servers are not accessible, you sadly have no other options for getting your data as there are no local copies. So it is advised to do this as soon as there's a chance - just to be safe. - -## Using Ferdi without an account i.e. Accountless - -Accountless instances have all the same functionality as account-based ones with the exception of multi-machine syncronization. If you don't intend to use this app on multiple machines with regularly changing settings and lists of services, there's no benefit to having an online account. Having a backup of your `export.ferdi-data` whenever you make changes to your setup, provides a similarly secure (albeit slower and manual alternative) to automatic syncing that doesn't rely on a server's constant availability. - -## Exporting - -Depending on where you have your account, you have to visit separate sites to retrieve it. This has no impact on your current instances. You can follow the next steps to access and export that data irrespective of which online server holds your data. - -1. Have Ferdi running on your system. (Even if you are on the initial "Get started" page, these instructions will work.) -2. Go to `Help > Import/Export Configuration Data` which should open the corresponding url in your default browser. -3. Click on `export your data to a ".ferdi-data" file`. -4. Save the `export.ferdi-data` file to anywhere you'd like for safe-keeping/backup. - -## Importing - -1. Have Ferdium running on your system. -2. When you get to the screen that says "Get Started", go to `Help > Import/Export Configuration Data` which should open a local url in your default browser. -3. Click on `import your data from a ".ferdium-data" or ".ferdi-data" file`. -4. Using the File browser button, find the previously saved `export.ferdi-data` file on your system and click the "Import data" button. -5. Restart your application. - -_Note:_ - -1. Setting up new instances or migrating to an accountless setup will require all services to be logged-in by hand again. This is because the session information only stays on your local machine - ever. -2. Importing data does not override the list of local services if you have any, but adds them to the current list. This is normal behavior. If you do not want to deal with duplicate instances of services, you can delete your pre-existing services prior to importing from the previously exported data file. -3. Due to current risks of losing access to Ferdi's services, it is ill-advised to use their servers in the future. You should setup your own server while you wait for official Ferdium solutions (if automatic syncing is important to you). diff --git a/README.md b/README.md index 692f90943..57428c80d 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ _Find answers to frequently asked questions on [ferdium.org/faq](https://ferdium ## Migrating from Ferdi -If you are a pre-existing user of Ferdi, and are thinking of switching to Ferdium, you might want to run [the following scripts](./scripts/migration) to migrate your existing Ferdi profile such that Ferdium can pick up the configurations. (.ps1 for PowerShell/Windows users and .sh for UNIX (Linux and MacOS users). For a more detailed explanation, please see [MIGRATION.md](MIGRATION.md) +If you are a pre-existing user of Ferdi, and are thinking of switching to Ferdium, you might want to run [the following scripts](./scripts/migration) to migrate your existing Ferdi profile such that Ferdium can pick up the configurations. (.ps1 for PowerShell/Windows users and .sh for UNIX (Linux and MacOS users). For a more detailed explanation, please see [MIGRATION.md](docs/MIGRATION.md) ## Styling diff --git a/docs/DBUS.md b/docs/DBUS.md new file mode 100644 index 000000000..ffbd61f4b --- /dev/null +++ b/docs/DBUS.md @@ -0,0 +1,20 @@ +# D-Bus interface + +Ferdium exposes an inter-process communication on Linux systems via [D-Bus](https://www.freedesktop.org/wiki/Software/dbus/). +This allows integrating Ferdium with your desktop environment by displaying the number of unread notifications in a status area and muting or unmuting notations. + +## Desktop integration + +As an example integration, the [`docs/dbus`](dbus) folder contains a module for status bars written in Python. +To run the example, you'll need Python 3.11 and the [`dbus-next`](https://pypi.org/project/dbus-next/) PyPI package. + +The integration uses the [`FerdiumClient`](dbus/ferdium_client.py) client library, which is an asynchronous wrapper over the D-Bus interface. +It illustrates multiple advanced concepts, such as asynchronous communication with Ferdium via `asyncio` and polling the session D-Bus to see if Ferdium is running. + +The [`ferdium_bar.py`](dbus/ferdium_bar.py) implements a bar module to use with status bars such as waybar or polybar. See `ferdium_bar.py --help` and `ferdium_bar.py unread --help` for further indications on how to use it. + +## Low-level API + +The low-level API exposed over D-Bus is documented in [`org.ferdium.Ferdium.xml](docs/org.ferdium.Ferdium.xml) with standard D-Bus introspection syntax. + +Ferdium will take ownership of the bus name `org.ferdium.Ferdium` and expose and object implementing the `org.ferdium.Ferdium` interface at the object path `/org/ferdium`. diff --git a/docs/INTERNAL_SERVER.md b/docs/INTERNAL_SERVER.md new file mode 100644 index 000000000..9e94f9bdf --- /dev/null +++ b/docs/INTERNAL_SERVER.md @@ -0,0 +1,28 @@ +

+ +

+ +# ferdium-internal-server +Internal Ferdium Server used for storing settings/preferences without logging into an external server. + +## Differences to ferdium-server +- Doesn't contain user management (only one user) +- Doesn't require logging in +- No recipe creation +- Contains `start.js` script to allow starting the server via script +- Uses `env.ini` instead of `.env` to stay compatible with Ferdium's build script +- Only allows Ferdium clients to connect to the API + +## Configuration +`ferdium-internal-server's` configuration is saved inside the `env.ini` file. Besides AdonisJS's settings, `ferdium-internal-server` has the following custom settings: +- `CONNECT_WITH_FRANZ` (`true` or `false`, default: `true`): Whether to enable connections to the Franz server. By enabling this option, `ferdium-internal-server` can: + - Show the full Franz/Ferdi recipe library instead of only custom recipes + - Import Franz/Ferdi accounts + +## Exporting backups +Since the `ferdium-internal-server` runs a local server, there's no automatic syncing of settings possible. You can backup your settings, by clicking on `Help > Import/Export Configuration Data` which will open the running server page in your browser. Choose the option to export and save the generated file. + +## Importing your Franz/Ferdi account +`ferdium-internal-server` allows you to import your full Franz account, including all its settings. + +To import your Franz/Ferdi account, within Ferdium, click on `Help > Import/Export Configuration Data` which will open the running server page in your browser. You can then login using your Franz account details. `ferdium-internal-server` will create a new user with the same credentials and copy your Franz settings, services and workspaces. diff --git a/docs/MIGRATION.md b/docs/MIGRATION.md new file mode 100644 index 000000000..bb1ca315d --- /dev/null +++ b/docs/MIGRATION.md @@ -0,0 +1,38 @@ +# Migrating account data + +This guide is here to show you how to export your current Ferdi setup from the online server or from an accountless session for safe-keeping, backup or transferring to a new instance using only a browser. + +Before getting into the details of using an online account vs accountless, let's quickly review what the differences are between the two of them: + +## Using Ferdi with an Account + +The main advantage of using Ferdi with an account is that you have your configuration data stored on a cloud - and thus, when moving to a different machine, once you login, all these configurations are applied to the Ferdi instance on your new machine. + +**But**, if you use a Ferdi account, you need to reach their servers. If the servers are not accessible, you sadly have no other options for getting your data as there are no local copies. So it is advised to do this as soon as there's a chance - just to be safe. + +## Using Ferdi without an account i.e. Accountless + +Accountless instances have all the same functionality as account-based ones with the exception of multi-machine syncronization. If you don't intend to use this app on multiple machines with regularly changing settings and lists of services, there's no benefit to having an online account. Having a backup of your `export.ferdi-data` whenever you make changes to your setup, provides a similarly secure (albeit slower and manual alternative) to automatic syncing that doesn't rely on a server's constant availability. + +## Exporting + +Depending on where you have your account, you have to visit separate sites to retrieve it. This has no impact on your current instances. You can follow the next steps to access and export that data irrespective of which online server holds your data. + +1. Have Ferdi running on your system. (Even if you are on the initial "Get started" page, these instructions will work.) +2. Go to `Help > Import/Export Configuration Data` which should open the corresponding url in your default browser. +3. Click on `export your data to a ".ferdi-data" file`. +4. Save the `export.ferdi-data` file to anywhere you'd like for safe-keeping/backup. + +## Importing + +1. Have Ferdium running on your system. +2. When you get to the screen that says "Get Started", go to `Help > Import/Export Configuration Data` which should open a local url in your default browser. +3. Click on `import your data from a ".ferdium-data" or ".ferdi-data" file`. +4. Using the File browser button, find the previously saved `export.ferdi-data` file on your system and click the "Import data" button. +5. Restart your application. + +_Note:_ + +1. Setting up new instances or migrating to an accountless setup will require all services to be logged-in by hand again. This is because the session information only stays on your local machine - ever. +2. Importing data does not override the list of local services if you have any, but adds them to the current list. This is normal behavior. If you do not want to deal with duplicate instances of services, you can delete your pre-existing services prior to importing from the previously exported data file. +3. Due to current risks of losing access to Ferdi's services, it is ill-advised to use their servers in the future. You should setup your own server while you wait for official Ferdium solutions (if automatic syncing is important to you). diff --git a/docs/dbus/ferdium_bar.py b/docs/dbus/ferdium_bar.py new file mode 100644 index 000000000..6fd5d8c30 --- /dev/null +++ b/docs/dbus/ferdium_bar.py @@ -0,0 +1,102 @@ +import asyncio +import argparse +import html + +from ferdium_dbus import Client + + +async def toggle_window(client, args): + """Toggle window visibility""" + + await client.toggle_window() + + +async def toggle_mute(client, args): + """Toggle mute status""" + + await client.toggle_mute() + + +async def unread(client, args): + """Get unread messages count""" + + def callback(): + """Print unread count(s)""" + + # For each service + counts = {} + for service in client.unread_services: + name, direct, indirect = service + safe_name = html.escape(name) + + # If it's exactly the service we're looking for, just return the count + if safe_name == args.services: + count = direct + if not args.direct: + count += indirect + print(count) + return + + # If the service in included in the services we're looking for + if args.services in ("total", "all") or safe_name in args.services: + counts[safe_name] = direct + if not args.direct: + counts[safe_name] += indirect + + # Get total notifications + if args.services == "total": + print(sum(counts.values())) + return + + # Finally, print each service notifications on a different line + print( + "\n".join( + f"{name}: {count}" + for name, count in counts.items() + ) + ) + + # Do print counts and keep running if tail mode enabled + callback() + if args.tail: + client.on_change(callback) + await asyncio.get_running_loop().create_future() + + +async def main(): + """Main cli interface""" + + # Define commands + commands = { + "unread": unread, + "toggle-mute": toggle_mute, + "toggle-window": toggle_window, + } + + # Arguments parser + argparser = argparse.ArgumentParser(description="Script to interact with Ferdium on your bar") + subparsers = argparser.add_subparsers(dest="command", required=True) + # Unread command + argparser_unread = subparsers.add_parser("unread", help=unread.__doc__) + argparser_unread.add_argument("-s", "--services", default="total", help="Which services to get notifications from {total, all, } (the name can be a comma-separated list)") + argparser_unread.add_argument("-d", "--direct", action="store_true", default=False, help="Get only direct (mentions or DM) messages") + argparser_unread.add_argument("-t", "--tail", action="store_true", default=False, help="Keep running and print on change") + # Toggle mute and toggle window commands + argparser_toggle_mute = subparsers.add_parser("toggle-mute", help=toggle_mute.__doc__) + argparser_toggle_window = subparsers.add_parser("toggle-window", help=toggle_window.__doc__) + # Get args + args = argparser.parse_args() + + # Initialise ferdium client + client = Client() + await client.connect() + if not client.running: + print("not running") + return + + # Execute command + await commands[args.command](client, args) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/docs/dbus/org.ferdium.Ferdium.xml b/docs/dbus/org.ferdium.Ferdium.xml new file mode 100644 index 000000000..8c66a9e21 --- /dev/null +++ b/docs/dbus/org.ferdium.Ferdium.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + diff --git a/docs/dbus/requirements.txt b/docs/dbus/requirements.txt new file mode 100644 index 000000000..d08a849d9 --- /dev/null +++ b/docs/dbus/requirements.txt @@ -0,0 +1 @@ +git+https://github.com/victorbnl/ferdium-dbus-py diff --git a/scripts/migration/migrate-unix.sh b/scripts/migration/migrate-unix.sh index 37f24a533..c51227f33 100755 --- a/scripts/migration/migrate-unix.sh +++ b/scripts/migration/migrate-unix.sh @@ -65,6 +65,6 @@ else echo "WARNING: Your data was partially migrated!" echo "It was detected that your account is using Ferdi servers to sync your data." echo "Please, check this guide on how to export and import your data manually:" - echo "https://github.com/ferdium/ferdi/blob/main/MIGRATION.md" + echo "https://github.com/ferdium/ferdi/blob/main/docs/MIGRATION.md" echo "********************************************" fi diff --git a/scripts/migration/migrate-windows.ps1 b/scripts/migration/migrate-windows.ps1 index a7e67b94a..0a793077c 100644 --- a/scripts/migration/migrate-windows.ps1 +++ b/scripts/migration/migrate-windows.ps1 @@ -39,7 +39,7 @@ if (-not (Test-Path -Path $FERDIUM_PATH/server.sqlite)) { Write-Host "" Write-Host "It was detected that your account is using Ferdi servers to sync your data." Write-Host "Please, check this guide on how to export and import your data manually:" - Write-Host "https://github.com/ferdium/ferdi/blob/main/MIGRATION.md" + Write-Host "https://github.com/ferdium/ferdi/blob/main/docs/MIGRATION.md" Write-Host "" Write-Host "********************************************" diff --git a/src/lib/DBus.ts b/src/lib/DBus.ts index bbff405c4..530e30c85 100644 --- a/src/lib/DBus.ts +++ b/src/lib/DBus.ts @@ -1,28 +1,92 @@ +import { ipcMain } from 'electron'; +import { comparer } from 'mobx'; + import { MessageBus, sessionBus } from 'dbus-next'; import { isLinux } from '../environment'; import TrayIcon from './Tray'; +import Ferdium, { type UnreadServices } from './dbus/Ferdium'; export default class DBus { - bus: MessageBus | null = null; + private bus: MessageBus | null = null; trayIcon: TrayIcon; + private ferdium: Ferdium | null = null; + + muted = false; + + unreadDirectMessageCount = 0; + + unreadIndirectMessageCount = 0; + + unreadServices: UnreadServices = []; + constructor(trayIcon: TrayIcon) { this.trayIcon = trayIcon; + ipcMain.on('initialAppSettings', (_, appSettings) => { + this.updateSettings(appSettings); + }); + ipcMain.on('updateAppSettings', (_, appSettings) => { + this.updateSettings(appSettings); + }); + ipcMain.on( + 'updateDBusUnread', + ( + _, + unreadDirectMessageCount, + unreadIndirectMessageCount, + unreadServices, + ) => { + this.setUnread( + unreadDirectMessageCount, + unreadIndirectMessageCount, + unreadServices, + ); + }, + ); + } + + private updateSettings(appSettings): void { + const muted = !!appSettings.data.isAppMuted; + if (this.muted !== muted) { + this.muted = muted; + this.ferdium?.emitMutedChanged(); + } } - start() { + private setUnread( + unreadDirectMessageCount: number, + unreadIndirectMessageCount: number, + unreadServices: UnreadServices, + ): void { + if ( + this.unreadDirectMessageCount !== unreadDirectMessageCount || + this.unreadIndirectMessageCount !== unreadIndirectMessageCount || + !comparer.structural(this.unreadServices, unreadServices) + ) { + this.unreadDirectMessageCount = unreadDirectMessageCount; + this.unreadIndirectMessageCount = unreadIndirectMessageCount; + this.unreadServices = unreadServices; + this.ferdium?.emitUnreadChanged(); + } + } + + async start() { if (!isLinux || this.bus) { return; } try { this.bus = sessionBus(); + await this.bus.requestName('org.ferdium.Ferdium', 0); } catch { // Error connecting to the bus. return; } + this.ferdium = new Ferdium(this); + this.bus.export('/org/ferdium', this.ferdium); + // HACK Hook onto the MessageBus to track StatusNotifierWatchers // @ts-expect-error Property '_addMatch' does not exist on type 'MessageBus'. this.bus._addMatch( @@ -56,5 +120,6 @@ export default class DBus { this.bus.disconnect(); this.bus = null; + this.ferdium = null; } } diff --git a/src/lib/dbus/Ferdium.ts b/src/lib/dbus/Ferdium.ts new file mode 100644 index 000000000..b2a9105f4 --- /dev/null +++ b/src/lib/dbus/Ferdium.ts @@ -0,0 +1,88 @@ +import * as dbus from 'dbus-next'; + +import type DBus from '../DBus'; + +export type UnreadServices = [string, number, number][]; + +export default class Ferdium extends dbus.interface.Interface { + constructor(private readonly dbus: DBus) { + super('org.ferdium.Ferdium'); + } + + emitMutedChanged(): void { + Ferdium.emitPropertiesChanged(this, { Muted: this.dbus.muted }, []); + } + + get Muted(): boolean { + return this.dbus.muted; + } + + set Muted(muted: boolean) { + if (this.dbus.muted !== muted) { + this.ToggleMute(); + } + } + + ToggleMute(): void { + this.dbus.trayIcon.mainWindow?.webContents.send('muteApp'); + } + + ToggleWindow(): void { + this.dbus.trayIcon._toggleWindow(); + } + + emitUnreadChanged(): void { + Ferdium.emitPropertiesChanged( + this, + { + UnreadDirectMessageCount: this.dbus.unreadDirectMessageCount, + UnreadIndirectMessageCount: this.dbus.unreadIndirectMessageCount, + UnreadServices: this.dbus.unreadServices, + }, + [], + ); + } + + get UnreadDirectMessageCount(): number { + return this.dbus.unreadDirectMessageCount; + } + + get UnreadIndirectMessageCount(): number { + return this.dbus.unreadIndirectMessageCount; + } + + get UnreadServices(): UnreadServices { + return this.dbus.unreadServices; + } +} + +Ferdium.configureMembers({ + methods: { + ToggleMute: { + inSignature: '', + outSignature: '', + }, + ToggleWindow: { + inSignature: '', + outSignature: '', + }, + }, + properties: { + Muted: { + signature: 'b', + access: dbus.interface.ACCESS_READWRITE, + }, + UnreadDirectMessageCount: { + signature: 'u', + access: dbus.interface.ACCESS_READ, + }, + UnreadIndirectMessageCount: { + signature: 'u', + access: dbus.interface.ACCESS_READ, + }, + UnreadServices: { + signature: 'a(suu)', + access: dbus.interface.ACCESS_READ, + }, + }, +}); diff --git a/src/stores/ServicesStore.ts b/src/stores/ServicesStore.ts index 0ab4dbc5b..829c64d76 100644 --- a/src/stores/ServicesStore.ts +++ b/src/stores/ServicesStore.ts @@ -1,4 +1,4 @@ -import { shell } from 'electron'; +import { ipcRenderer, shell } from 'electron'; import { action, reaction, computed, observable, makeObservable } from 'mobx'; import { debounce, remove } from 'lodash'; import ms from 'ms'; @@ -23,6 +23,7 @@ import { cleanseJSObject } from '../jsUtils'; import { SPELLCHECKER_LOCALES } from '../i18n/languages'; import { ferdiumVersion } from '../environment-remote'; import TypedStore from './lib/TypedStore'; +import type { UnreadServices } from '../lib/dbus/Ferdium'; const debug = require('../preload-safe-debug')('Ferdium:ServiceStore'); @@ -1230,26 +1231,29 @@ export default class ServicesStore extends TypedStore { const { showMessageBadgeWhenMuted } = this.stores.settings.all.app; const { showMessageBadgesEvenWhenMuted } = this.stores.ui; - const unreadDirectMessageCount = this.allDisplayed - .filter( - s => - (showMessageBadgeWhenMuted || s.isNotificationEnabled) && - showMessageBadgesEvenWhenMuted && - s.isBadgeEnabled, - ) - .map(s => s.unreadDirectMessageCount) - .reduce((a, b) => a + b, 0); - - const unreadIndirectMessageCount = this.allDisplayed - .filter( - s => - showMessageBadgeWhenMuted && - showMessageBadgesEvenWhenMuted && - s.isBadgeEnabled && - s.isIndirectMessageBadgeEnabled, - ) - .map(s => s.unreadIndirectMessageCount) - .reduce((a, b) => a + b, 0); + const unreadServices: UnreadServices = []; + let unreadDirectMessageCount = 0; + let unreadIndirectMessageCount = 0; + + if (showMessageBadgesEvenWhenMuted) { + for (const s of this.allDisplayed) { + if (s.isBadgeEnabled) { + const direct = + showMessageBadgeWhenMuted || s.isNotificationEnabled + ? s.unreadDirectMessageCount + : 0; + const indirect = + showMessageBadgeWhenMuted && s.isIndirectMessageBadgeEnabled + ? s.unreadIndirectMessageCount + : 0; + unreadDirectMessageCount += direct; + unreadIndirectMessageCount += indirect; + if (direct > 0 || indirect > 0) { + unreadServices.push([s.name, direct, indirect]); + } + } + } + } // We can't just block this earlier, otherwise the mobx reaction won't be aware of the vars to watch in some cases if (showMessageBadgesEvenWhenMuted) { @@ -1257,6 +1261,12 @@ export default class ServicesStore extends TypedStore { unreadDirectMessageCount, unreadIndirectMessageCount, }); + ipcRenderer.send( + 'updateDBusUnread', + unreadDirectMessageCount, + unreadIndirectMessageCount, + unreadServices, + ); } } -- cgit v1.2.3-70-g09d2