NotificationInbox
TsgcHTMLComponent_NotificationInbox: 未読バッジ付きのベルアイコンと、1 人のユーザーの通知のドロップダウンリストを、Delphi、C++ Builder、.NET で提供します。行はアプリケーションが保持し、コンポーネントがそれをレンダリングします。
TsgcHTMLComponent_NotificationInbox: 未読バッジ付きのベルアイコンと、1 人のユーザーの通知のドロップダウンリストを、Delphi、C++ Builder、.NET で提供します。行はアプリケーションが保持し、コンポーネントがそれをレンダリングします。
ライブラリは何も保存しません。アプリケーションが保持している行で OnLoadNotifications に応答し、ユーザーの操作は OnMarkRead と OnMarkAllRead を通じて通知されるので、それを永続化できます。コンポーネントは UI をレンダリングするだけで、認可は行いません。WebSocket でも Server-Sent Events でも、すべてのエディションで動作します。
TsgcHTMLComponent_NotificationInbox(ユニット sgcHTML_Component_NotificationInbox)
Bootstrap のドロップダウンマークアップ: ベルアイコン、未読バッジ、リスト
Delphi, C++ Builder, .NET
インボックスに InboxID を指定し、OnLoadNotifications で独自のストレージから内容を埋め、OnMarkRead と OnMarkAllRead でユーザーの操作を永続化し、ポストされたアクションはセッションから取得したユーザーとともに ProcessAction に振り分けます。
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());
最もよく使うメンバーです。
Items は TsgcHTMLInboxItems コレクションで、各 TsgcHTMLInboxItem は Id、Title、Text、Timestamp、Url、Icon、Read を持ちます。行内のすべての値はデータであり、レンダリング時にエスケープされます。Icon も同様で、文字そのものを渡し、HTML エンティティやマークアップは渡さないでください。Url はサニタイズされ、javascript: と data: のリンクは拒否されます。
コンポーネントが保持するのは、レンダリングしている内容だけです。OnLoadNotifications(aUserID, aList) は aList がすでにクリアされた状態で発生します。ユーザーの通知を新しい順に埋めてください。OnMarkRead(aUserID, aNotificationID) は項目が既読になった後に、OnMarkAllRead(aUserID) はすべての項目が既読になった後に発生します。
LoadNotifications(aUserID)、MarkRead(aUserID, aNotificationID)、MarkAllRead(aUserID) が実際の処理を行います。ProcessAction(aUserID, aAction, aNotificationID) は、ポストされた 1 つのアクションを適切なものに振り分け、そのアクションがインボックスのアクションでない場合は False を返すので、引き続きその所有者を探せます。
ドロップダウンは、inboxMarkRead(フィールドは action、inbox、id)、inboxMarkAllRead、inboxRefresh(フィールドは action、inbox)を data-sgc-ws-send フォームとしてポストします。inbox フィールドには、フォームをレンダリングしたインボックスの要素 ID が入り、InboxID を設定した場合はその値です。複数のインボックスがあるページでは、これによってアクションが正しいコンポーネントに振り分けられます。
コンポーネントは UI をレンダリングするだけで、認可は行いません。すべてのアクションはクライアントから送られるデータとして届くため、悪意のあるクライアントは任意のアクションと任意の通知 ID を偽造できます。マークアップにはユーザー ID を書き込みません。操作しているユーザーは、フォームではなくリクエストのセッション(sgcHTMLRequestSession)から取得し、何かを永続化する前に、届いた通知 ID をそのユーザーが所有していることを確認してください。
Title はドロップダウンの見出し、EmptyText は表示する項目がないときのプレースホルダー、BellIcon はトリガーのグリフで、既定では HTML エンティティを含む信頼されたマークアップです。MaxItems は表示する行数の上限(既定値 10、0 ならすべて表示)を決め、ShowMarkAllRead と ShowBadge は既定でオンで、AddNotification(aId, aTitle, aText, aTimestamp, aUrl, aIcon) は行を追加するか、同じ ID の行を更新します。
すべての通知、バッジ、リストには、安定した要素 ID が付いています。1 つ変更された後は、ページ全体を再レンダリングする代わりに、GetItemFragmentHTML(aId)、GetBadgeFragmentHTML、GetListFragmentHTML で影響を受けたマークアップだけをレンダリングし、TsgcHTMX_Engine_Server.PushFragment または BroadcastFragment でプッシュします。UnreadCount は、Read が False の項目を数えます。
ユニットは SGC_HTML が定義されている場合にコンパイルされ、sgcVer.inc は Android と iOS についてはそれを定義しません。WebSocket でも Server-Sent Events でも、すべてのエディションで動作します。sgcHTML はスタンドアロンのパックで、sgcWebSockets とは別に販売されています。
インボックス自体はページとプッシュのどちらかを選択しません。それを行うのは TsgcHTMLComponent_Notification です。Notification と WebPush を参照してください。各ユーザーがどのチャネルを希望するかは、NotificationPreferences で設定します。