packages feed

status-notifier-item 0.3.2.15 → 0.3.2.16

raw patch · 4 files changed

+74/−5 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

ChangeLog.md view
@@ -2,6 +2,14 @@  ## Unreleased +## 0.3.2.16 - 2026-07-15+- Host: normalize the random `Id`, item path, and menu path generated by+  Ayatana StatusNotifierItem implementations when building logical duplicate+  keys. Multiple instances of the same application now collapse to one tray+  item even when each instance advertises a different generated identity.+- Add regression coverage for generated Ayatana identities while preserving+  distinct identities for ordinary StatusNotifierItems.+ ## 0.3.2.15 - 2026-07-02 - Bound every DBus call made during host and watcher startup with a timeout so   a single unresponsive item (a process that owns its `StatusNotifierItem` bus
src/StatusNotifier/Host/Service.hs view
@@ -23,6 +23,7 @@ import Data.Coerce import Data.Either import Data.Int+import Data.List (stripPrefix) import qualified Data.Map.Strict as Map import Data.Maybe import Data.String@@ -133,14 +134,31 @@  logicalDuplicateKey :: ItemInfo -> Maybe LogicalDuplicateKey logicalDuplicateKey ItemInfo {..} =-  let key =+  let generatedAyatanaIdentity = do+        suffix <- stripPrefix "/org/ayatana/NotificationItem/" (coerce itemServicePath :: String)+        normalizedId <- itemId >>= normalizeOptionalString+        itemMenuPath <- menuPath+        guard $ normalizedId == suffix+        guard $ (coerce itemMenuPath :: String) == (coerce itemServicePath :: String) <> "/Menu"+        pure ()+      hasGeneratedAyatanaIdentity = isJust generatedAyatanaIdentity+      key =         LogicalDuplicateKey-          { duplicateItemId = itemId >>= normalizeOptionalString,+          { duplicateItemId =+              if hasGeneratedAyatanaIdentity+                then Nothing+                else itemId >>= normalizeOptionalString,             duplicateIconTitle = normalizeOptionalString iconTitle,             duplicateIconName = normalizeOptionalString iconName,             duplicateCategory = itemCategory >>= normalizeOptionalString,-            duplicateMenuPath = menuPath,-            duplicatePath = itemServicePath,+            duplicateMenuPath =+              if hasGeneratedAyatanaIdentity+                then Nothing+                else menuPath,+            duplicatePath =+              if hasGeneratedAyatanaIdentity+                then objectPath_ "/org/ayatana/NotificationItem"+                else itemServicePath,             duplicateIsMenu = itemIsMenu           }       hasStableIdentity =
status-notifier-item.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           status-notifier-item-version:        0.3.2.15+version:        0.3.2.16 synopsis:       A wrapper over the StatusNotifierItem/libappindicator dbus specification description:    Please see the README on Github at <https://github.com/taffybar/status-notifier-item#readme> category:       Desktop
test/HostSpec.hs view
@@ -33,6 +33,49 @@  spec :: Spec spec = around withIsolatedSessionBus $ do+  describe "logicalDuplicateKey" $ do+    it "normalizes generated Ayatana identities so duplicate app instances collapse" $ \() -> do+      let localSendItem token =+            ItemInfo+              { itemServiceName = busName_ ("org.test.LocalSend." <> token),+                itemServicePath = objectPath_ ("/org/ayatana/NotificationItem/" <> token),+                itemId = Just token,+                itemStatus = Just "Active",+                itemCategory = Just "ApplicationStatus",+                itemToolTip = Nothing,+                iconTitle = "localsend_app",+                iconName = "/nix/store/example-localsend/logo.png",+                overlayIconName = Nothing,+                iconThemePath = Nothing,+                iconPixmaps = [],+                overlayIconPixmaps = [],+                menuPath = Just $ objectPath_ ("/org/ayatana/NotificationItem/" <> token <> "/Menu"),+                itemIsMenu = True+              }+      logicalDuplicateKey (localSendItem "generatedA")+        `shouldBe` logicalDuplicateKey (localSendItem "generatedB")++    it "retains non-Ayatana item ids as part of logical identity" $ \() -> do+      let itemWithId itemIdValue =+            ItemInfo+              { itemServiceName = busName_ "org.test.RegularItem",+                itemServicePath = objectPath_ defaultPath,+                itemId = Just itemIdValue,+                itemStatus = Just "Active",+                itemCategory = Just "ApplicationStatus",+                itemToolTip = Nothing,+                iconTitle = "shared-title",+                iconName = "shared-icon",+                overlayIconName = Nothing,+                iconThemePath = Nothing,+                iconPixmaps = [],+                overlayIconPixmaps = [],+                menuPath = Nothing,+                itemIsMenu = False+              }+      logicalDuplicateKey (itemWithId "first")+        `shouldNotBe` logicalDuplicateKey (itemWithId "second")+   describe "StatusNotifier.Host.Service integration" $ do     it "receives ItemAdded then ItemRemoved for a registered item" $ \() -> do       _watcher <- startWatcher