diff --git a/package.json b/package.json index 0553f1de..efcffbc1 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "electron-updater": "^6.3.9" }, "optionalDependencies": { - "@vencord/venmic": "^6.1.0" + "@vencord/venmic": "^6.1.0", + "@homebridge/dbus-native": "0.6.0" }, "devDependencies": { "@fal-works/esbuild-plugin-global-externals": "^2.1.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a97c567e..b8a65f9d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: specifier: ^6.3.9 version: 6.3.9 optionalDependencies: + '@homebridge/dbus-native': + specifier: 0.6.0 + version: 0.6.0 '@vencord/venmic': specifier: ^6.1.0 version: 6.1.0 @@ -464,6 +467,17 @@ packages: '@gar/promisify@1.1.3': resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} + '@homebridge/dbus-native@0.6.0': + resolution: {integrity: sha512-xObqQeYHTXmt6wsfj10+krTo4xbzR9BgUfX2aQ+edDC9nc4ojfzLScfXCh3zluAm6UCowKw+AFfXn6WLWUOPkg==} + hasBin: true + + '@homebridge/long@5.2.1': + resolution: {integrity: sha512-i5Df8R63XNPCn+Nj1OgAoRdw9e+jHUQb3CNUbvJneI2iu3j4+OtzQj+5PA1Ce+747NR1SPqZSvyvD483dOT3AA==} + + '@homebridge/put@0.0.8': + resolution: {integrity: sha512-mwxLHHqKebOmOSU0tsPEWQSBHGApPhuaqtNpCe7U+AMdsduweANiu64E9SXXUtdpyTjsOpgSMLhD1+kbLHD2gA==} + engines: {node: '>=0.3.0'} + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -1167,6 +1181,9 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -1387,6 +1404,9 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + event-stream@4.0.1: + resolution: {integrity: sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==} + expand-tilde@2.0.2: resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==} engines: {node: '>=0.10.0'} @@ -1492,6 +1512,9 @@ packages: resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==} engines: {node: '>=0.10.0'} + from@0.1.7: + resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} + fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} @@ -1666,6 +1689,11 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hexy@0.3.5: + resolution: {integrity: sha512-UCP7TIZPXz5kxYJnNOym+9xaenxCLor/JyhKieo8y8/bJWunGh9xbhy3YrgYJUQ87WwfXGm05X330DszOfINZw==} + engines: {node: '>=10.4'} + hasBin: true + homedir-polyfill@1.0.3: resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} engines: {node: '>=0.10.0'} @@ -2055,6 +2083,9 @@ packages: resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} engines: {node: '>=0.10.0'} + map-stream@0.0.7: + resolution: {integrity: sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==} + map-visit@1.0.0: resolution: {integrity: sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==} engines: {node: '>=0.10.0'} @@ -2331,6 +2362,9 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + pause-stream@0.0.11: + resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + pe-library@0.4.1: resolution: {integrity: sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==} engines: {node: '>=12', npm: '>=6'} @@ -2643,6 +2677,9 @@ packages: resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==} engines: {node: '>=0.10.0'} + split@1.0.1: + resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} + sprintf-js@1.1.3: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} @@ -2661,6 +2698,9 @@ packages: resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==} engines: {node: '>=0.10.0'} + stream-combiner@0.2.2: + resolution: {integrity: sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -2734,6 +2774,9 @@ packages: temp-file@3.4.0: resolution: {integrity: sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==} + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + tiny-typed-emitter@2.1.0: resolution: {integrity: sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==} @@ -2947,6 +2990,14 @@ packages: resolution: {integrity: sha512-U6eN5Pyrlek9ottHVpT9e8YUax75oVYXbnYxU+utzDC7i+OyWj9ynsNMiZNQZvpuazbG0O7iLAs9FkcFmzlgSA==} engines: {node: '>= 16'} + xml2js@0.6.2: + resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} + engines: {node: '>=4.0.0'} + + xmlbuilder@11.0.1: + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} + engines: {node: '>=4.0'} + xmlbuilder@15.1.1: resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==} engines: {node: '>=8.0'} @@ -3250,6 +3301,23 @@ snapshots: '@gar/promisify@1.1.3': {} + '@homebridge/dbus-native@0.6.0': + dependencies: + '@homebridge/long': 5.2.1 + '@homebridge/put': 0.0.8 + event-stream: 4.0.1 + hexy: 0.3.5 + minimist: 1.2.8 + safe-buffer: 5.2.1 + xml2js: 0.6.2 + optional: true + + '@homebridge/long@5.2.1': + optional: true + + '@homebridge/put@0.0.8': + optional: true + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -4143,6 +4211,9 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + duplexer@0.1.2: + optional: true + eastasianwidth@0.2.0: {} ejs@3.1.10: @@ -4522,6 +4593,17 @@ snapshots: esutils@2.0.3: {} + event-stream@4.0.1: + dependencies: + duplexer: 0.1.2 + from: 0.1.7 + map-stream: 0.0.7 + pause-stream: 0.0.11 + split: 1.0.1 + stream-combiner: 0.2.2 + through: 2.3.8 + optional: true + expand-tilde@2.0.2: dependencies: homedir-polyfill: 1.0.3 @@ -4632,6 +4714,9 @@ snapshots: dependencies: map-cache: 0.2.2 + from@0.1.7: + optional: true + fs-constants@1.0.0: {} fs-extra@10.1.0: @@ -4856,6 +4941,9 @@ snapshots: dependencies: function-bind: 1.1.2 + hexy@0.3.5: + optional: true + homedir-polyfill@1.0.3: dependencies: parse-passwd: 1.0.0 @@ -5248,6 +5336,9 @@ snapshots: map-cache@0.2.2: {} + map-stream@0.0.7: + optional: true + map-visit@1.0.0: dependencies: object-visit: 1.0.1 @@ -5541,6 +5632,11 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + pause-stream@0.0.11: + dependencies: + through: 2.3.8 + optional: true + pe-library@0.4.1: {} pend@1.2.0: {} @@ -5890,6 +5986,11 @@ snapshots: dependencies: extend-shallow: 3.0.2 + split@1.0.1: + dependencies: + through: 2.3.8 + optional: true + sprintf-js@1.1.3: {} ssri@9.0.1: @@ -5907,6 +6008,12 @@ snapshots: define-property: 0.2.5 object-copy: 0.1.0 + stream-combiner@0.2.2: + dependencies: + duplexer: 0.1.2 + through: 2.3.8 + optional: true + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -6004,6 +6111,9 @@ snapshots: async-exit-hook: 2.0.1 fs-extra: 10.1.0 + through@2.3.8: + optional: true + tiny-typed-emitter@2.1.0: {} tmp-promise@3.0.3: @@ -6246,6 +6356,15 @@ snapshots: xml-parser-xo@4.1.3: {} + xml2js@0.6.2: + dependencies: + sax: 1.4.1 + xmlbuilder: 11.0.1 + optional: true + + xmlbuilder@11.0.1: + optional: true + xmlbuilder@15.1.1: {} y18n@5.0.8: {} diff --git a/src/main/appBadge.ts b/src/main/appBadge.ts index 46abe1db..b87d063f 100644 --- a/src/main/appBadge.ts +++ b/src/main/appBadge.ts @@ -8,6 +8,8 @@ import { app, NativeImage, nativeImage } from "electron"; import { join } from "path"; import { BADGE_DIR } from "shared/paths"; +import { dbus, getSessionBus } from "./utils/dbus"; + const imgCache = new Map(); function loadBadge(index: number) { const cached = imgCache.get(index); @@ -24,8 +26,28 @@ let lastIndex: null | number = -1; export function setBadgeCount(count: number) { switch (process.platform) { case "linux": - if (count === -1) count = 0; - app.setBadgeCount(count); + if (typeof count !== "number") { + throw new Error("count must be a number"); // sanitize + } + + const sessionBus = getSessionBus(); + sessionBus.connection.message({ + type: dbus.messageType.signal, + serial: 1, + path: "/", + interface: "com.canonical.Unity.LauncherEntry", + member: "Update", + signature: "sa{sv}", + body: [ + process.env.container === "1" + ? "application://dev.vencord.Vesktop.desktop" // flatpak handling + : "application://vesktop.desktop", + [ + ["count", ["x", count === -1 ? 0 : count]], + ["count-visible", ["b", count !== 0]] + ] + ] + }); break; case "darwin": if (count === 0) { diff --git a/src/main/mainWindow.ts b/src/main/mainWindow.ts index cbb0def7..33a0ca49 100644 --- a/src/main/mainWindow.ts +++ b/src/main/mainWindow.ts @@ -395,6 +395,7 @@ function initStaticTitle(win: BrowserWindow) { win.setTitle("Vesktop"); win.on("page-title-updated", listener); } else { + win.setTitle(win.getTitle().replace(/^\(\d+\)\s*|•\s/, "")); win.off("page-title-updated", listener); } }); diff --git a/src/main/utils/dbus.ts b/src/main/utils/dbus.ts new file mode 100644 index 00000000..581e12e0 --- /dev/null +++ b/src/main/utils/dbus.ts @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: GPL-3.0 + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2023 Vendicated and Vencord contributors + */ + +import dbus from "@homebridge/dbus-native"; + +let sessionBus: dbus.MessageBus | null; + +export function getSessionBus(): dbus.MessageBus { + if (!sessionBus) sessionBus = dbus.sessionBus(); + return sessionBus; +} + +export { dbus }; diff --git a/src/renderer/appBadge.ts b/src/renderer/appBadge.ts index a6491c2b..59b59a24 100644 --- a/src/renderer/appBadge.ts +++ b/src/renderer/appBadge.ts @@ -12,6 +12,7 @@ import { Settings } from "./settings"; let GuildReadStateStore: any; let NotificationSettingsStore: any; +let MessageRequestStore: any; export function setBadge() { if (Settings.store.appBadge === false) return; @@ -19,10 +20,11 @@ export function setBadge() { try { const mentionCount = GuildReadStateStore.getTotalMentionCount(); const pendingRequests = RelationshipStore.getPendingCount(); + const messageRequests = MessageRequestStore.getMessageRequestsCount(); const hasUnread = GuildReadStateStore.hasAnyUnread(); const disableUnreadBadge = NotificationSettingsStore.getDisableUnreadBadge(); - let totalCount = mentionCount + pendingRequests; + let totalCount = mentionCount + pendingRequests + messageRequests; if (!totalCount && hasUnread && !disableUnreadBadge) totalCount = -1; VesktopNative.app.setBadgeCount(totalCount); @@ -45,4 +47,5 @@ function waitForAndSubscribeToStore(name: string, cb?: (m: any) => void) { waitForAndSubscribeToStore("GuildReadStateStore", store => (GuildReadStateStore = store)); waitForAndSubscribeToStore("NotificationSettingsStore", store => (NotificationSettingsStore = store)); +waitForAndSubscribeToStore("MessageRequestStore", store => (MessageRequestStore = store)); waitForAndSubscribeToStore("RelationshipStore");