Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

NotificationEntry

Persistent notification archive — the storage and data-model layer backing NotificationLog, NotificationCenterButton, and NotificationLogDialog.

Public types

KindName
structNotificationEntry — A single archived notification entry rendered by NotificationLog and persisted under NotificationArchive::Persistent
structNotificationUpdate — One in-place mutation applied when a Toast with the same id as an existing entry is presented again
enumArchivedActionStyle — Visual presentation of an archived action button
structArchivedAction — A single action stored alongside an archived notification entry
constDEFAULT_ARCHIVE_LIMIT — Default per-archive entry cap
constARCHIVE_FILE_NAME — File-name (without extension) used for the persistent archive
constUPDATE_HISTORY_LIMIT — How many NotificationUpdate records one row keeps
enumNotificationArchive — Storage mode for the notification archive
structNotificationArchiveModel — Shared model — clones share state
structNotificationLogDialog — One-liner modal preset around NotificationLog

Public functions

NotificationArchive

NotificationArchiveModel

NotificationLogDialog

Detailed description

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_memory directly 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());

API reference

📖 Full rustdoc API for this module

pub struct NotificationEntry

Import: use teksilo::prelude::NotificationEntry; — in teksilo::prelude::*.

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 */ }
}

Fields

  • id: u64 — Stable per-archive id (separate from the runtime Toast::id dedup key — that one lives in dedup_id below). Assigned by the archive on first push; never reused.
  • severity: BannerSeverity — Severity of the notice as it now stands: the latest in-place update's, else the original push's. Drives the log's row glyph + severity-chip filter.
  • priority: ToastPriority
  • title: String — Resolved title (LocalizedString::resolve_now() snapshot).
  • body: Option<String>
  • actions: Vec<ArchivedAction> — The actions the notice offers as it now stands. An in-place update replaces them; one that offers none leaves none.
  • timestamp: jiff::Timestamp — Wall-clock time the notice was last raised: its first push, or the latest time a notice with the same dedup_id was raised again after the first had left the screen. In-place updates of a notice still on screen leave it alone. The log's day-bucket computation runs against this in the user's local timezone.
  • group: Option<String> — Optional grouping key for the log's visual section headers.
  • source: Option<String> — Optional originating-feature tag (e.g. "build", "sync"). Surfaced as a chip in the log row.
  • read: bool — Flipped when the user opens the log popover. Drives the bell badge's unread_count signal.
  • dedup_id: Option<String> — Toast::id(...) value, if any — used for update-in-place merge logic. New entries with a matching dedup_id merge into the existing row rather than creating a separate one (see NotificationArchiveModel::push and push_update).
  • updates: Vec<NotificationUpdate> — Later presents of the same Toast::id(...): one per in-place update that changed what the row shows, and one per time the notice was raised again. Holds the most recent UPDATE_HISTORY_LIMIT. Empty on a freshly-pushed entry.
  • route: ToastRoute — Mirrored from the originating LiveEntry::route (see ToastRegistry::entry_to_archive) — drives which bell(s) show this entry and which bell's "mark all read" / "clear" affects it. Defaults to ToastRoute::Broadcast on deserialization when the field is absent (a notifications.toml written before this feature existed): treating pre-upgrade history as broadcast keeps it visible in every window's bell, rather than it silently vanishing from all of them the moment routing scopes are introduced.

pub struct NotificationUpdate

Import: use teksilo::widgets::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 */ }
}

Fields

  • timestamp: jiff::Timestamp
  • title: Option<String>
  • body: Option<String>
  • progress: Option<f32>

pub enum ArchivedActionStyle

Import: use teksilo::widgets::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

Import: use teksilo::widgets::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 */ }
}

Fields

  • label: String — Resolved label snapshot.
  • intent_name: Option<String> — Intent name for archive replay through ctx.send_intent(...). None for closure-only actions; the log renders these as non-clickable past-action tags.
  • style: ArchivedActionStyle
  • closes_on_invoke: bool — Mirrors the live action's closes_toast flag. The log uses it informationally only — a replayed Intent fires and the archive row itself doesn't dismiss (since it's not a live toast).

pub const DEFAULT_ARCHIVE_LIMIT

Import: none — DEFAULT_ARCHIVE_LIMIT is public in a private module of teksilo-widgets and nothing re-exports it.

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

Import: none — ARCHIVE_FILE_NAME is public in a private module of teksilo-widgets and nothing re-exports it.

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 const UPDATE_HISTORY_LIMIT

Import: none — UPDATE_HISTORY_LIMIT is public in a private module of teksilo-widgets and nothing re-exports it.

How many NotificationUpdate records one row keeps. A progress notice that reports each step in its wording records one update per step, and an id an app reuses for every run of an operation gathers them from every run, so a row keeps the most recent ones and drops the oldest. The row itself always shows the notice as it now stands, whatever the history kept.

#![allow(unused)]
fn main() {
pub const UPDATE_HISTORY_LIMIT: usize = 20;
}

pub enum NotificationArchive

Import: none — NotificationArchive is public in a private module of teksilo-widgets and nothing re-exports it.

Storage mode for the notification archive. Passed inside ToastInstallOptions::archive to the install helper.

#![allow(unused)]
fn main() {
pub enum NotificationArchive { /* variants */ }
}

Variants

  • InMemory { limit: usize } — Session-only — entries live in a ListModel for the running session. Cheap, no disk I/O. Default for apps that don't install a SettingsBundle.
  • Persistent { file_name: String, limit: usize } — File-backed via PersistedListModel. The path is built at install time from AppPaths::config_file using the configured file_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

Import: none — NotificationArchiveModel is public in a private module of teksilo-widgets and nothing re-exports it.

Shared model — clones share state. Constructed by the install helper from NotificationArchive + AppPaths; apps reach it via ctx.app_state::<Rc<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, entry: NotificationEntry)

Push a notice that has just been raised. 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 row, the notice is that row raised again: an operation that failed this morning and fails again now, under the same Toast::id. No new row is inserted. The row moves to the front, takes the entry's timestamp, records a NotificationUpdate and takes the entry's read state, which makes it unread again, even when the notice says exactly what it said before: the log and the bell show that it happened again. Every other field is merged as push_update describes.

pub fn push_update(&self, entry: NotificationEntry)

Push an in-place update of a notice that is still on screen: a progress notice's next step, or its result. This is what the toast registry calls when a Toast::id matches a live toast.

If entry.dedup_id matches an existing row, that row is updated in place: same position, and it keeps the time the notice was raised. The row then shows the notice as it now stands, the way the live toast it mirrors does:

  • title, body, severity, priority, actions and route are the update's. An update offering no actions leaves the row offering none: a progress notice's Cancel must not outlive the notice that said the work was cancelled, nor stand in for the Open and See report a finished one offers.
  • group and source are the update's when it names them, and stay as they were when it does not.
  • id, timestamp (when the notice was raised, which places the row in the log) and dedup_id stay.
  • A NotificationUpdate is appended when the update changes what the row shows (its wording, severity or actions), carrying the update's timestamp and whichever of title and body changed. A row keeps the most recent UPDATE_HISTORY_LIMIT of them. An update that repeats the row exactly records nothing and leaves its read state alone.
  • A change gives the row the update's read state, which for a toast is unread. unread_count counts rows, so it grows only when the row had been read.

With no matching row (the user cleared it, or it was evicted), the update is pushed as a new row, like push.

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 closes.

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

Import: none — NotificationLogDialog is public in a private module of teksilo-widgets and nothing re-exports it.

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).