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: 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.
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.
TsgcHTMLComponent_NotificationInbox (unit sgcHTML_Component_NotificationInbox)
Bootstrap dropdown markup: a bell, an unread badge and a list
Delphi, C++ Builder, .NET
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());
The members you reach for most often.
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.
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.
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.
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.
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.
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.
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.
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.
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.
| Online HelpFull API reference and usage guide for this component. | Open | |
| All sgcHTML ComponentsBrowse the full feature matrix of 80+ components. | Open | |
| Download Free TrialThe 30-day trial ships the 60.HTML demo projects, including 17.FieldService, which uses the inbox. | Open | |
| PricingSingle, Team and Site licenses with full source code. | Open |