NotificationEntry
Persistent notification archive — the storage and data-model layer
backing NotificationLog, NotificationCenterButton, and
NotificationLogDialog.
Every toast presented through the toast registry is mirrored into a
NotificationArchiveModel when archiving is enabled via
ToastInstallOptions::archive. The model is a
ListModel<NotificationEntry> plus an
unread-count signal — shaped for one-line binding to the notification
UI family. Two storage variants are available: an in-memory session-only
ring buffer (NotificationArchive::InMemory) and a file-backed
persistent store (NotificationArchive::Persistent) that survives app
restarts. Action callbacks attached via raw closures are lost on
archival; actions that should remain re-invokable from the log carry an
intent_name that the log replays through ctx.send_intent(...).
When to use
- Pair with
TeksiloAppBuilder::install_toast_default()to get the full bell-button + log + persistence stack for free. - Construct
NotificationArchiveModel::in_memorydirectly in tests or custom toast setups.
// In app boot, after install_toast:
let archive = ctx.app_state::<Rc<RefCell<NotificationArchiveModel>>>().unwrap();
let log = NotificationLog::new(archive.clone());
Builder methods at a glance
in_memory, in_memory_with_limit, persistent, persistent_with_limit, limit
API reference
📖 Full rustdoc API for this module
pub struct NotificationEntry
A single archived notification entry rendered by NotificationLog and
persisted under NotificationArchive::Persistent. Carries plain owned
fields only — no closures, no Rc<dyn Fn> — so it is Serialize-friendly.
#![allow(unused)] fn main() { pub struct NotificationEntry { /* fields */ } }
pub struct NotificationUpdate
One in-place mutation applied when a Toast with the same id as an
existing entry is presented again. The archive merges these onto the
existing row — the "Uploading 3 of 7 → Upload complete" pattern.
#![allow(unused)] fn main() { pub struct NotificationUpdate { /* fields */ } }
pub enum ArchivedActionStyle
Visual presentation of an archived action button. Maps one-to-one to
ToastActionStyle; re-declared as a self-contained Serialize-friendly
enum so the archive type does not depend on ButtonVariant.
#![allow(unused)] fn main() { pub enum ArchivedActionStyle { /* variants */ } }
Variants
Link— JetBrains-style hyperlink in the body row.PrimaryButton— Filled (primary CTA).SecondaryButton— Plain (secondary).Destructive— Destructive (red-tinted).
pub struct ArchivedAction
A single action stored alongside an archived notification entry. Only
re-invokable from NotificationLog when intent_name is set — actions
whose live closure has torn down render as inert descriptive labels.
#![allow(unused)] fn main() { pub struct ArchivedAction { /* fields */ } }
pub const DEFAULT_ARCHIVE_LIMIT
Default per-archive entry cap. IntelliJ's notification log keeps hundreds of entries with no cap visible to the user; we pick a pragmatic limit so persistent files don't grow unbounded.
#![allow(unused)] fn main() { pub const DEFAULT_ARCHIVE_LIMIT: usize = 200; }
pub const ARCHIVE_FILE_NAME
File-name (without extension) used for the persistent archive.
Resolved through AppPaths::config_file into
<config_dir>/<app>/notifications.toml.
#![allow(unused)] fn main() { pub const ARCHIVE_FILE_NAME: &str = "notifications"; }
pub enum NotificationArchive
Storage mode for the notification archive. Passed inside
ToastInstallOptions::archive to the install helper.
#![allow(unused)] fn main() { pub enum NotificationArchive { /* variants */ } }
Variants
InMemory— Session-only — entries live in aListModelfor the running session. Cheap, no disk I/O. Default for apps that don't install aSettingsBundle.Persistent— File-backed viaPersistedListModel. The path is built at install time fromAppPaths::config_fileusing the configuredfile_name.
Methods
pub fn in_memory() -> Self
In-memory archive with the default 200-entry cap.
pub fn in_memory_with_limit(limit: usize) -> Self
In-memory archive with a custom cap.
pub fn persistent(file_name: impl Into<String>) -> Self
File-backed archive resolved through AppPaths::config_file
at install time. The default file name ("notifications")
yields <config_dir>/<app>/notifications.toml. Apps that
want a different name pass it here; tests pass an arbitrary
name and use AppPaths::for_testing(tmpdir).
pub fn persistent_with_limit(file_name: impl Into<String>, limit: usize) -> Self
pub fn limit(&self) -> usize
pub struct NotificationArchiveModel
Shared model — clones share state. Constructed by the install
helper from NotificationArchive + AppPaths; apps reach it
via ctx.app_state::<Rc<RefCell<NotificationArchiveModel>>>().
NotificationLog and NotificationCenterButton
consume this model directly.
#![allow(unused)] fn main() { pub struct NotificationArchiveModel { /* fields */ } }
Methods
pub fn open( archive: &NotificationArchive, paths: &AppPaths, debounce: Duration, ) -> Result<Self, NotificationArchiveError>
Construct from a NotificationArchive config. For
Persistent mode, resolves the path through AppPaths.
Tests use AppPaths::for_testing(tmpdir) + Duration::ZERO
debounce.
pub fn in_memory() -> Self
Convenience: construct an NotificationArchive::InMemory
archive with the default cap, without going through paths.
Mostly useful for tests and apps that explicitly want no
persistence.
pub fn entries(&self) -> &ListModel<NotificationEntry>
Reactive handle on the entries. Bind to a ListView /
Repeater for live UI.
pub fn unread_count(&self) -> &Signal<usize>
Signal of the unread count. Drives the bell-button badge.
pub fn version_signal(&self) -> &Signal<u64>
Reactive handle on the archive's mutation version. Widgets
that render the archive (NotificationLog,
NotificationCenterButton) bind to this at
BindingLevel::Rebuild, in every window — one signal is enough
for N of them, see
ToastRegistry::version_signal
for the history of why that had to be said out loud.
pub fn limit(&self) -> usize
pub fn flush_now(&self) -> Result<(), SettingsFileError>
Force the persistent backing file to disk synchronously.
No-op for InMemory. Tests call this between mutations and
re-opening the file to verify persistence.
pub fn push(&self, mut entry: NotificationEntry)
Push a new entry. Inserts at index 0 (newest first), evicts
the oldest if the resulting length exceeds limit. Stamps
the entry's id field from next_id. Bumps unread_count
when the entry is unread (which is the typical case from a
toast push).
If entry.dedup_id matches an existing entry, the existing
entry is updated in place (title / body / progress collapsed
into a NotificationUpdate appended to updates) and no
new row is inserted. Unread count increments either way (an
in-place update IS new information for the user).
pub fn mark_read_where(&self, mut predicate: impl FnMut(&NotificationEntry) -> bool)
Mark every UNREAD entry matching predicate as read,
decrementing unread_count by exactly how many were flipped.
This is the scoped counterpart of mark_all_read:
a bell scoped to one window/audience must only mark ITS
entries read on close — calling the unscoped mark_all_read
from a scoped bell would incorrectly clear every OTHER
window's/audience's unread state too.
pub fn mark_all_read(&self)
Mark every archived entry as read; reset unread_count to 0.
Called by NotificationCenterButton when its popover opens.
pub fn clear(&self)
Clear the entire archive (resets unread_count to 0).
pub fn clear_where(&self, mut predicate: impl FnMut(&NotificationEntry) -> bool)
Remove every entry matching predicate, decrementing
unread_count for each removed entry that was unread. The
scoped counterpart of clear: a bell scoped to
one window/audience must only clear ITS entries — the unscoped
clear() wipes the ENTIRE shared archive (every window's
history), which would be wrong for a scoped "Clear" button.
pub fn remove_by_id(&self, id: u64)
Remove the entry with the given stable id (see
NotificationEntry::id — "assigned by the archive on first
push; never reused"). Updates unread_count if the removed entry
was unread. No-op (no version bump) when no entry has that id.
Deliberately id-based rather than index-based: an index is a
snapshot of the list's shape at the moment it was read, and is
meaningless once anything else — a concurrent peer-process reload
merged in via the live archive, another push, another remove —
has shifted rows out from under it. A caller that captured "the row
I want to dismiss" as an index earlier and replays it later against
a since-mutated list can silently remove the wrong entry; keying
off id instead re-resolves the row's current position at the
moment of removal, so it always removes the entry the caller meant.
pub struct NotificationLogDialog
One-liner modal preset around NotificationLog. Apps usually
wire this to a menu item or shortcut (e.g. "Window → Notification
Log…").
#![allow(unused)] fn main() { pub struct NotificationLogDialog; }
Methods
pub fn show(archive: Rc<NotificationArchiveModel>, ctx: &mut EventContext)
Present the dialog with the standard chrome (title + 720x520 default size, escape-or-click-outside dismissal).
pub fn show_with( archive: Rc<NotificationArchiveModel>, ctx: &mut EventContext, configure: impl FnOnce(NotificationLog) -> NotificationLog + 'static, )
Same as show, but lets the caller configure the embedded
NotificationLog (e.g. attach an on_action_invoked hook
for archive replay).