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.
Read-Only Chat / Feed
Section titled “Read-Only Chat / Feed”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.
Support / Agent Chat
Section titled “Support / Agent Chat”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).
AI Assistant
Section titled “AI Assistant”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.
Group Chat with Invite & Rename
Section titled “Group Chat with Invite & Rename”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.
Switching Conversations (Master/Detail)
Section titled “Switching Conversations (Master/Detail)”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;}Hosting Inside a FloatingPanel (MAUI)
Section titled “Hosting Inside a FloatingPanel (MAUI)”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>Error State
Section titled “Error State”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));}Next Steps
Section titled “Next Steps”- Permissions — the building block for every scenario
- The Provider Interface — the full provider/session contract
- API Reference — every member at a glance