NotificationInbox

TsgcHTMLComponent_NotificationInbox: a bell with an unread badge and a dropdown list of the notifications of one user, in Delphi, C++ Builder and .NET. Your application keeps the rows, the component renders them.

TsgcHTMLComponent_NotificationInbox

The library stores nothing. Your application answers OnLoadNotifications with the rows it keeps and is told through OnMarkRead and OnMarkAllRead what the user did, so it can persist it. The component renders UI and enforces no authorization. It works on every edition, over WebSocket or server-sent events.

Component class

TsgcHTMLComponent_NotificationInbox (unit sgcHTML_Component_NotificationInbox)

Renders

Bootstrap dropdown markup: a bell, an unread badge and a list

Languages

Delphi, C++ Builder, .NET

Create it, handle three events, route the actions

Give the inbox an InboxID, fill it from your own storage in OnLoadNotifications, persist what the user did in OnMarkRead and OnMarkAllRead, and route the posted actions into ProcessAction with the user taken from the session.

uses
  sgcHTML_Session, sgcHTML_Component_NotificationInbox;

var
  oInbox: TsgcHTMLComponent_NotificationInbox;
begin
  oInbox := TsgcHTMLComponent_NotificationInbox.Create(nil);
  try
    oInbox.InboxID := 'inbox';
    oInbox.Title := 'Notifications';
    oInbox.MaxItems := 10;
    oInbox.OnLoadNotifications := InboxLoad;
    oInbox.OnMarkRead := InboxMarkRead;
    oInbox.OnMarkAllRead := InboxMarkAllRead;

    // the user comes from the session, never from the form
    oInbox.LoadNotifications(sgcHTMLRequestSession.UserID);
    Response := oInbox.HTML;   // bell, unread badge and dropdown
  finally
    oInbox.Free;
  end;
end;

// the application owns the rows: fill the list, newest first
procedure TMain.InboxLoad(Sender: TObject; const aUserID: string;
  aList: TsgcHTMLInboxItems);
begin
  with aList.Add do
  begin
    Id := '1042';
    Title := 'New order';
    Text := 'Order #1042 was placed.';
    Timestamp := '2 min ago';
    Read := False;
  end;
end;

procedure TMain.InboxMarkRead(Sender: TObject; const aUserID,
  aNotificationID: string);
begin
  // persist it, after checking that aUserID owns aNotificationID
end;

// in the message handler that receives the actions the inbox posts
if oInbox.ProcessAction(vUser, vAction, vNotificationID) then
  oHTMX.PushFragment(vGuid, oInbox.GetListFragmentHTML +
    oInbox.GetBadgeFragmentHTML);
// includes: sgcHTML_Session.hpp, sgcHTML_Component_NotificationInbox.hpp

TsgcHTMLComponent_NotificationInbox *oInbox = new TsgcHTMLComponent_NotificationInbox(NULL);
try
{
  oInbox->InboxID = "inbox";
  oInbox->Title = "Notifications";
  oInbox->MaxItems = 10;
  oInbox->OnLoadNotifications = InboxLoad;
  oInbox->OnMarkRead = InboxMarkRead;
  oInbox->OnMarkAllRead = InboxMarkAllRead;

  // the user comes from the session, never from the form
  oInbox->LoadNotifications(sgcHTMLRequestSession()->UserID);
  String html = oInbox->HTML;   // bell, unread badge and dropdown
}
__finally
{
  delete oInbox;
}

// the application owns the rows: fill the list, newest first
void __fastcall TMain::InboxLoad(TObject *Sender, const String aUserID,
  TsgcHTMLInboxItems *aList)
{
  TsgcHTMLInboxItem *item = aList->Add();
  item->Id = "1042";
  item->Title = "New order";
  item->Text = "Order #1042 was placed.";
  item->Timestamp = "2 min ago";
  item->Read = false;
}

void __fastcall TMain::InboxMarkRead(TObject *Sender, const String aUserID,
  const String aNotificationID)
{
  // persist it, after checking that aUserID owns aNotificationID
}

// in the message handler that receives the actions the inbox posts
if (oInbox->ProcessAction(vUser, vAction, vNotificationID))
  oHTMX->PushFragment(vGuid, oInbox->GetListFragmentHTML() +
    oInbox->GetBadgeFragmentHTML());
using esegece.sgcWebSockets;

var inbox = new TsgcHTMLComponent_NotificationInbox();
inbox.InboxID = "inbox";
inbox.Title = "Notifications";
inbox.MaxItems = 10;
inbox.OnLoadNotifications += InboxLoad;
inbox.OnMarkRead += InboxMarkRead;
inbox.OnMarkAllRead += InboxMarkAllRead;

// the user comes from the session, never from the form
string user = sgcHTMLSessionHelpers.sgcHTMLRequestSession()?.UserID ?? "";
inbox.LoadNotifications(user);
string html = inbox.HTML;   // bell, unread badge and dropdown

// the application owns the rows: fill the list, newest first
void InboxLoad(object sender, string userID, TsgcHTMLInboxItems list)
{
    var item = list.Add();
    item.Id = "1042";
    item.Title = "New order";
    item.Text = "Order #1042 was placed.";
    item.Timestamp = "2 min ago";
    item.Read = false;
}

void InboxMarkRead(object sender, string userID, string notificationID)
{
    // persist it, after checking that userID owns notificationID
}

// in the message handler that receives the actions the inbox posts
if (inbox.ProcessAction(user, action, notificationId))
    htmx.PushFragment(guid, inbox.GetListFragmentHTML() + inbox.GetBadgeFragmentHTML());

Key properties & methods

The members you reach for most often.

Items

Items is a TsgcHTMLInboxItems collection; each TsgcHTMLInboxItem has Id, Title, Text, Timestamp, Url, Icon and Read. Everything in a row is data and is escaped when it is rendered, the Icon included: pass a literal glyph, never an HTML entity or markup. Url is sanitized and javascript: and data: links are rejected.

Storage events

The component holds only what it is rendering. OnLoadNotifications(aUserID, aList) fires with aList already cleared: fill it with the notifications of the user, newest first. OnMarkRead(aUserID, aNotificationID) fires after the item is flagged read, and OnMarkAllRead(aUserID) after every item is.

Server side actions

LoadNotifications(aUserID), MarkRead(aUserID, aNotificationID) and MarkAllRead(aUserID) do the work. ProcessAction(aUserID, aAction, aNotificationID) routes one posted action to the right one and returns False when the action is not an inbox action, so you can go on looking for its owner.

Actions it posts

The dropdown posts inboxMarkRead (fields action, inbox, id), inboxMarkAllRead and inboxRefresh (fields action, inbox) as data-sgc-ws-send forms. The inbox field carries the element id of the inbox that rendered the form, which is InboxID when you set it, and it is how a page with several inboxes routes the action to the right component.

Authorization

The component renders UI and enforces no authorization. Every action travels as data sent by the client, so a hostile client can forge any action and any notification id. No user id is written into the markup: take the acting user from the request session (sgcHTMLRequestSession), never from the form, and check that the user owns the notification id that arrived before you persist anything.

Appearance

Title is the dropdown heading, EmptyText the placeholder when there is nothing to show and BellIcon the trigger glyph, trusted markup with an HTML entity by default. MaxItems caps the visible rows (default 10, 0 shows every one), ShowMarkAllRead and ShowBadge are on by default, and AddNotification(aId, aTitle, aText, aTimestamp, aUrl, aIcon) adds a row or updates the one with the same id.

Live updates

Every notification, the badge and the list carry a stable element id. After one change, render only the affected markup with GetItemFragmentHTML(aId), GetBadgeFragmentHTML or GetListFragmentHTML and push it with TsgcHTMX_Engine_Server.PushFragment or BroadcastFragment, instead of re-rendering the page. UnreadCount counts the items whose Read is False.

Editions and channels

The unit compiles when SGC_HTML is defined, which sgcVer.inc does not do for Android and iOS. It works on every edition, over WebSocket or server-sent events. sgcHTML is a standalone pack, sold independently of sgcWebSockets.

Page or push

The inbox does not itself choose between the page and a push: that is done by TsgcHTMLComponent_Notification, see Notification and WebPush. Which channels each user wants is set in NotificationPreferences.

Keep exploring

Online HelpFull API reference and usage guide for this component.
All sgcHTML ComponentsBrowse the full feature matrix of 80+ components.
Download Free TrialThe 30-day trial ships the 60.HTML demo projects, including 17.FieldService, which uses the inbox.
PricingSingle, Team and Site licenses with full source code.
Best value: All-AccessEvery eSeGeCe product, Premium Support included, from €1,059/year.
See All-Access pricing

Ready to Get Started?

Download the free trial and start building web UIs in Delphi, C++ Builder and .NET.