Skip to content
Client v5: BLE, BLE Hosting, HTTP, Jobs - Linux, MacOS, & Blazor Support! Full AOT, RX on BLE only & MANY other features! Power up!

ChatView | Scenarios

Every ChatView scenario is the same two moving parts: an IChatSessionProvider and a SessionId. What changes is the permissions you return from Info and a couple of control flags. The snippets below assume a provider like the one in The Provider Interface.

Show history with no composer. Hide the input bar and return None permissions.

<shiny:ChatView Provider="{Binding Provider}"
SessionId="{Binding SessionId}"
IsInputBarVisible="False" />
public ChatSessionInfo BuildInfo() => new(
SessionId, SessionName, Users,
PermittedEmojis: [], // no reactions
BodyPermissions: MessageBodyPermissions.None,
Permissions: ChatSessionPermissions.None,
CreatedAt, LastReadDate, UnreadMessageCount: 0
);

Paging, live MessageReceived, and image taps still work — only composing is removed.

A 1:1 conversation where the customer can send text and images and react, but not edit/delete or manage the session.

public ChatSessionInfo BuildInfo() => new(
SessionId, "Support", Users,
PermittedEmojis: ["👍", "❤️", "🎉"],
BodyPermissions: MessageBodyPermissions.Bold | MessageBodyPermissions.Italics | MessageBodyPermissions.Links,
Permissions: ChatSessionPermissions.CanSendMessages
| ChatSessionPermissions.CanSendImages
| ChatSessionPermissions.CanReactToMessages,
CreatedAt, LastReadDate, UnreadMessageCount
);

Drive delivery/read status from your backend by sending Sent/Delivered/Read statuses and raising MessageUpdated(MessageChangeKind.StatusChanged). Show the agent’s typing indicator via UserTyping, and surface connectivity with ConnectionStateChanged (see Typing & Connection).

Treat the assistant as another participant. The provider sends the user’s message, then streams or posts the assistant’s reply via MessageReceived (optionally after a UserTyping(true) “thinking” burst, like the demo’s SimulateReplyAsync).

public Task<ChatMessage> SendMessageAsync(OutgoingMessage message, CancellationToken ct = default)
{
var stored = /* persist the user's message with the same ClientMessageId */;
_ = this.RunAssistantAsync(message.Body); // raise UserTyping then MessageReceived
return Task.FromResult(stored);
}

For action cards (approve/decline, suggestions), stamp Identifier/Metadata and render with a message template.

Grant the management permissions; the control surfaces invite/leave/rename affordances automatically.

public ChatSessionInfo BuildInfo() => new(
SessionId, "Controls Crew", Users, // Users[] includes avatars + per-user BubbleColor
PermittedEmojis: null, // default emoji set
BodyPermissions: MessageBodyPermissions.All,
Permissions: ChatSessionPermissions.All,
CreatedAt, LastReadDate, UnreadMessageCount
);

Implement the management methods and announce membership/name changes:

public Task InviteUserAsync(string userId, CancellationToken ct = default)
{
var joined = this.AddUser(userId);
this.UserJoined?.Invoke(this, joined);
this.SessionUpdated?.Invoke(this, this.Info); // refresh Info first, then announce
return Task.CompletedTask;
}
public Task RenameAsync(string sessionName, CancellationToken ct = default)
{
this.store.Rename(sessionName);
this.SessionUpdated?.Invoke(this, this.Info);
return Task.CompletedTask;
}
public Task LeaveAsync(CancellationToken ct = default) => this.RemoveSelfAsync();

Per-user read receipts and per-participant bubble colors come from ReadReceipts and each ChatSessionUserInfo.BubbleColor.

Bind SessionId to the selected conversation; the control disposes the old session and resolves the new one on change. One provider can host many sessions.

@inject IChatSessionProvider ChatProvider
<select @onchange="OnPick">
@foreach (var c in contacts) { <option value="@c.Id">@c.Name</option> }
</select>
<div style="height: 600px;">
<ChatView Provider="ChatProvider" SessionId="@sessionId" />
</div>
@code {
string sessionId = "alice";
void OnPick(ChangeEventArgs e) => sessionId = e.Value?.ToString() ?? sessionId;
}

When ChatView lives inside a FloatingPanel, set AdjustForKeyboard="False" — otherwise the panel’s keyboard handling and the chat’s fight each other and the entry drops keystrokes.

<shiny:FloatingPanel>
<shiny:ChatView Provider="{Binding Provider}"
SessionId="{Binding SessionId}"
AdjustForKeyboard="False" />
</shiny:FloatingPanel>

If GetSessionAsync (or the initial load) throws ChatSessionException — unknown session or no access — the control renders an error state instead of the chat. Throw it from the provider:

public Task<IChatSession> GetSessionAsync(string sessionId, CancellationToken ct = default)
{
if (!this.stores.TryGetValue(sessionId, out var store))
throw new ChatSessionException($"Chat session '{sessionId}' was not found.");
return Task.FromResult<IChatSession>(new InMemoryChatSession(store));
}