Skip to content

Notification Center

📖

Problem

Desktop notifications appear and disappear. Users miss them. A notification center collects notification history, allowing users to review missed alerts, dismiss them, or take action.

Naive Approach

Display each notification as a toast that auto-dismisses. No history. Miss one notification and it is gone forever.

MentalModel

💡Mental Model

A notification center is an inbox: notifications arrive, are stored in a list, and can be read, dismissed, or cleared. Each notification has a source app, title, body, urgency, timestamp, and optional actions.

Idea

Use Quickshell's Notifications service to receive notifications. Store them in a ListModel. Display them in a popup window with a ListView. Support dismiss-all and per-notification dismissal.

Build It

qml
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Services.Notifications

PopupWindow {
  id: notifCenter
  width: 380
  height: 500

  ListModel { id: notificationModel }

  Connections {
    target: Notifications
    function onNotificationReceived(notif: Notification) {
      notificationModel.insert(0, {
        id: notif.id,
        appName: notif.appName,
        summary: notif.summary,
        body: notif.body,
        urgency: notif.urgency,
        timestamp: new Date()
      })
    }
  }

  Column {
    anchors.fill: parent
    padding: 8

    Row {
      width: parent.width
      Text { text: "Notifications"; font.pixelSize: 16; font.bold: true }
      Item { width: parent.width - 200; height: 1 }
      Button {
        text: "Clear All"
        onClicked: notificationModel.clear()
      }
    }

    ListView {
      width: parent.width
      height: parent.height - 40
      model: notificationModel
      delegate: Rectangle {
        width: parent.width
        height: 72
        color: "#2a2a3e"
        radius: 8
        border.color: "#45475a"

        Column {
          padding: 8
          spacing: 4
          Text { text: modelData.appName; font.pixelSize: 11; color: "#888888" }
          Text { text: modelData.summary; font.pixelSize: 14; font.bold: true }
          Text { text: modelData.body; font.pixelSize: 12; elide: Text.ElideRight; maxLines: 2 }
        }

        MouseArea {
          anchors.fill: parent
          onClicked: notificationModel.remove(index)
        }
      }
    }
  }
}

BuildIt Breakdown

Notifications service connects to the system notification daemon via D-Bus. onNotificationReceived fires when any app sends a notification. Each notification is inserted at position 0 (newest first). The ListView renders them with app name, summary, and body. Clicking a notification dismisses it. "Clear All" empties the model.

Improve It

Group notifications by app. Add urgency-based styling (critical = red border). Show notification icons. Add action buttons. Support persistent notifications that stay until manually dismissed. Implement "Do Not Disturb" mode.

CommonMistake

⚠️Common Mistake

Holding references to Notification objects after they are closed. The Notification object becomes invalid after dismissal. Copy the data you need (id, summary, body) into the model, do not store the object reference.

Under the Hood

🔧Under the Hood

Quickshell's Notifications service implements the org.freedesktop.Notifications D-Bus specification. It registers as a notification server, replacing services like mako or dunst. Notifications arrive as D-Bus method calls on the Notify interface. The service parses hints (urgency, category, resident) and emits notificationReceived. Action IDs are tracked for button callbacks.

Exercises

Exercise

Build a notification toast popup that appears for 5 seconds and is clickable.

Exercise

Add a notification count badge to the top bar that shows unread notification count.

Exercise

Implement notification grouping — collapse notifications from the same app into a single expandable item.

Recap

What You've Learned

Built with VitePress. Licensed under MIT.