Skip to content

create a built-in Notifications module for ashell #456

Merged
MalpenZibo merged 88 commits into
MalpenZibo:mainfrom
Yoimiya-Naganohara:notifications
Apr 14, 2026
Merged

create a built-in Notifications module for ashell #456
MalpenZibo merged 88 commits into
MalpenZibo:mainfrom
Yoimiya-Naganohara:notifications

Conversation

@Yoimiya-Naganohara
Copy link
Copy Markdown
Contributor

This pull request adds a new notifications module to the application, integrating notifications into the app's configuration, UI, and event handling. The changes include new configuration options, menu integration, message handling, and icon support for notifications.

Notifications Module Integration:

  • Added a new Notifications module, including NotificationsModuleConfig with customizable options such as format, timestamps, maximum notifications, body display, and view mode. (src/config.rs, src/modules/notifications.rs) [1] [2]
  • Registered Notifications in the main App struct, message enum, initialization, update, and event handling logic, ensuring the module is properly initialized and updated with configuration reloads. (src/app.rs) [1] [2] [3] [4] [5] [6] [7]
  • Integrated the notifications menu into the UI, allowing users to open the notifications menu and view notifications. (src/menu.rs, src/app.rs, src/modules/mod.rs) [1] [2] [3]

Configuration and Subscription Support:

  • Extended the main Config struct and default implementation to include notifications configuration, and updated module name parsing to recognize Notifications. (src/config.rs) [1] [2] [3] [4]
  • Added notification subscription handling to the main app, wiring up notification events through the message system and subscriptions. (src/app.rs, src/modules/mod.rs) [1] [2]

Icon Support:

  • Added new static icons for notifications (Bell and BellBadge) to be used in the UI. (src/components/icons.rs) [1] [2]

Yoimiya-Naganohara and others added 20 commits January 29, 2026 21:32
@romanstingler
Copy link
Copy Markdown
Collaborator

Notification dismiss/Clear buttons don’t actually remove entries from the notification service/daemon. Message::NotificationClicked now removes only from the module’s local self.notifications map, never from the service (self.service) or the global NOTIFICATIONS store that NotificationsService polls.
Any subsequent event will clone the service’s still-populated map back into the UI, causing “cleared” notifications to reappear.
The Clear button has the same issue (local clear() only). Users can no longer dismiss notifications persistently.

Why did you disable the DBus signals??
In my opinion, the 100ms polling adds constant CPU wakeups and copies the whole map each tick.

show_bodies = false

still shows the last empty line

xdph_screenshot_1eb1c914

@Yoimiya-Naganohara
Copy link
Copy Markdown
Contributor Author

Notification dismiss/Clear buttons don’t actually remove entries from the notification service/daemon. Message::NotificationClicked now removes only from the module’s local self.notifications map, never from the service (self.service) or the global NOTIFICATIONS store that NotificationsService polls. Any subsequent event will clone the service’s still-populated map back into the UI, causing “cleared” notifications to reappear. The Clear button has the same issue (local clear() only). Users can no longer dismiss notifications persistently.

Why did you disable the DBus signals?? In my opinion, the 100ms polling adds constant CPU wakeups and copies the whole map each tick.

show_bodies = false

still shows the last empty line
xdph_screenshot_1eb1c914

Thanks! I hadn't noticed that before. I'm working on fixing those issues now.

Copy link
Copy Markdown
Owner

@MalpenZibo MalpenZibo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice addition!! Thanks. There's some work to do, but it could be a great module.

I left a preliminary review with my main concern

Comment thread src/modules/notifications.rs Outdated
Comment thread src/modules/notifications.rs Outdated
let _ = NotificationDaemon::close_notification_by_id(id).await;
});
// Remove from local state immediately for responsive UI
self.notifications.remove(&id);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we return a task to perform the async operation we could also place this code in the task "callback"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment thread src/modules/notifications.rs Outdated
Message::ClearNotifications => {
// Close each notification through the daemon (cleans up both daemon and global state)
let notification_ids: Vec<u32> = self.notifications.keys().copied().collect();
tokio::spawn(async move {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as before

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

solved

Comment thread src/modules/notifications.rs Outdated
}

pub fn menu_view<'a>(&'a self, _id: Id, theme: &'a AshellTheme) -> Element<'a, Message> {
let mut notifications_data: Vec<_> = self.notifications.values().cloned().collect();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there isn't a way to avoid this clone? Maybe we could directly save in the state the vector

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment thread src/modules/notifications.rs Outdated
let mut notifications_data: Vec<_> = self.notifications.values().cloned().collect();

// Sort by timestamp (newest first) and limit
notifications_data.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

time consuming operation should go in the update method if it's possible

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved to the update

Comment thread src/services/notifications/dbus.rs Outdated
};

// Global static for storing notifications - accessed by service layer
pub static NOTIFICATIONS: std::sync::OnceLock<std::sync::Mutex<HashMap<u32, Notification>>> =
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could avoid this static global object. I haven't checked all the code, but if I understand correctly, you should take inspiration from the tray service

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this has been replaced

@MalpenZibo
Copy link
Copy Markdown
Owner

Sorry, it's a big change, so I will not merge it into the next release. Anyway, the toast surface makes sense. The problem right now is that iced takes some time to create and correctly render a new surface (taking into consideration the HiDPI monitor). For this reason, the menu surface is always open.

You are "correctly" creating a new surface to show a notification when necessary. I need to check if this solution will work. After the release, I will update the iced library to the latest version to see if this behavior has been improved.

@MalpenZibo
Copy link
Copy Markdown
Owner

Ok here I am! Sorry for the delay.

image

First, the layout, I'd like to have a bigger icon in the first column. Then it could be ok for now, but I'd like to have an X button on the top right to close the notification and reserve the click on notification for an optional call to action

Scaling issue

image

Sadly, as I thought, we have the same scaling issue that I had with the menu. On a HiDPI monitor, the time between the first frame and the point where iced fixes the scaling of the surface is not trivial.

I will try with the new version of iced to see if this issue is resolved:

  • If it's resolved, we could pause this PR, wait until the update (I'm already working on that), then rebase the PR after the update.
  • If it's not resolved, the only solution will be to maintain a third surface always open, like for the menu :(. In that case, we should find a strategy to check the memory consumption of this solution, and in case, try to find a way to reduce it

@MalpenZibo
Copy link
Copy Markdown
Owner

Ok, great news. The new version of iced seems to fix this issue.

This means that we can leave the surface creation logic as it is here and, in the future, resolve the menu surface problem. That's great news, we could also simplify that outputs.rs awful system 😅

@Yoimiya-Naganohara
Copy link
Copy Markdown
Contributor Author

I will improve this

@Yoimiya-Naganohara
Copy link
Copy Markdown
Contributor Author

There is an issue with the toast layer's dimensions: it's blocking mouse clicks on the underlying window. Implementing click-through functionality or resizable layer would solve this.

@MalpenZibo
Copy link
Copy Markdown
Owner

ok, so let's block this PR until the update of iced is merged. So we can easily open and close the notification surface

@romanstingler
Copy link
Copy Markdown
Collaborator

I had a look at the branch at the current state, I think it looks fine for a first merge, so people can use it and give feedback.
Sure, features like click to open should be implemented, but not necessarily now.
Idk if you @MalpenZibo want to have some of the commits stashed.

@MalpenZibo
Copy link
Copy Markdown
Owner

Yes! Now that we've merged the iced update the main blocker here is gone and we can merge it.

@Yoimiya-Naganohara are you able to fix the conflict?

@MalpenZibo
Copy link
Copy Markdown
Owner

I double-checked the code. There are a few things that I would like to change to make everything more in line with the rest of the codebase.
I can handle everything in a dedicated PR merging this as it is, if it's ok for you @Yoimiya-Naganohara. Otherwise, I can make a deep review to point out everything that I'd like to change and let you do the work. Let me know what you prefer.
I'd like the idea of working on this, but I prefer to ask you first

@MalpenZibo
Copy link
Copy Markdown
Owner

Nevermind! There are some things that I prefer to fix by myself!

I will merge this PR as soon as I can.

@Yoimiya-Naganohara
Copy link
Copy Markdown
Contributor Author

I’ve been working on other things recently, so I’ve just resolved the conflicts.

@Yoimiya-Naganohara
Copy link
Copy Markdown
Contributor Author

I double-checked the code. There are a few things that I would like to change to make everything more in line with the rest of the codebase. I can handle everything in a dedicated PR merging this as it is, if it's ok for you @Yoimiya-Naganohara. Otherwise, I can make a deep review to point out everything that I'd like to change and let you do the work. Let me know what you prefer. I'd like the idea of working on this, but I prefer to ask you first

surely

@MalpenZibo MalpenZibo merged commit ace347b into MalpenZibo:main Apr 14, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants