@page "/chat" @page "/chat/{InitialSessionId}" @rendermode InteractiveServer @inject IChatSessionService ChatSessionService @inject NavigationManager NavigationManager @using Alchegos.HCI.Models @using Alchegos.HCI.Services @implements IAsyncDisposable Chat

Chat Session

@if (!string.IsNullOrEmpty(activeSessionId)) { Active Session: @activeSessionId }
@if (isLoading) {

Loading session...

} else if (currentSession != null) {
@foreach (var message in currentSession.Messages) {
@(message.IsUser ? "You" : "Server"): @message.Content @message.Timestamp.ToLocalTime()
} @if (currentSession.IsWaitingForResponse) {

Waiting for response...

}
} else if (sessionLoadAttempted) {

Enter a Session ID and click "Load / Start Session" or generate a new one.

} @code { [Parameter] public string? InitialSessionId { get; set; } private string currentSessionIdInput { get; set; } = string.Empty; private string activeSessionId { get; set; } = string.Empty; private ChatSession? currentSession; private string newMessage { get; set; } = string.Empty; private bool isLoading = false; private bool sessionLoadAttempted = false; private string externalWebhookUrl { get; set; } = string.Empty; private string? _previousSessionId = null; protected override async Task OnParametersSetAsync() { Console.WriteLine($"--- OnParametersSetAsync START. InitialSessionId: {InitialSessionId} ---"); if (InitialSessionId != _previousSessionId && !string.IsNullOrEmpty(InitialSessionId)) { isLoading = true; sessionLoadAttempted = true; if (!string.IsNullOrEmpty(_previousSessionId) && _previousSessionId != InitialSessionId) { Console.WriteLine($"Parameter changed from {_previousSessionId} to {InitialSessionId}. Unregistering old callbacks for {_previousSessionId}."); var oldId = _previousSessionId; _ = Task.Run(() => ChatSessionService.UnregisterSessionCallbacks(oldId)); } activeSessionId = InitialSessionId; _previousSessionId = InitialSessionId; Console.WriteLine($"Loading session from parameter: {activeSessionId}"); currentSession = ChatSessionService.GetOrCreateSession(activeSessionId); if (currentSession != null) { Console.WriteLine($"Session obtained: {currentSession.SessionId}"); await RegisterCallbacksAsync(); } else Console.WriteLine("!!! Failed to get/create session in OnParametersSetAsync!"); isLoading = false; Console.WriteLine("--- OnParametersSetAsync END. isLoading set to false ---"); } else if (string.IsNullOrEmpty(InitialSessionId) && _previousSessionId != null) { Console.WriteLine("--- OnParametersSetAsync: InitialSessionId became null/empty. Clearing session. ---"); await UnregisterCallbacksAsync(); currentSession = null; activeSessionId = string.Empty; _previousSessionId = null; currentSessionIdInput = string.Empty; sessionLoadAttempted = false; } else if (InitialSessionId == _previousSessionId) { Console.WriteLine($"--- OnParametersSetAsync: InitialSessionId '{InitialSessionId}' did not change. Skipping load. ---"); } else { Console.WriteLine($"--- OnParametersSetAsync: InitialSessionId '{InitialSessionId}' is null/empty and no previous ID. Initial state. ---"); } } protected override void OnInitialized() { if (!string.IsNullOrEmpty(InitialSessionId)) currentSessionIdInput = InitialSessionId; } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender && !string.IsNullOrEmpty(activeSessionId)) await RegisterCallbacksAsync(); } private void GenerateSessionId() { var newId = Guid.NewGuid().ToString("N"); currentSessionIdInput = newId; Console.WriteLine($"--- GenerateSessionId: Generated {newId}, Navigating... ---"); NavigationManager.NavigateTo($"/chat/{newId}"); } private void LoadSession() { if (string.IsNullOrWhiteSpace(currentSessionIdInput)) { Console.WriteLine("--- LoadSession Clicked: Input empty, doing nothing. ---"); return; } var sessionIdToLoad = currentSessionIdInput; var targetUrl = $"/chat/{sessionIdToLoad}"; string currentPathAndQuery = new Uri(NavigationManager.Uri).PathAndQuery; if (!currentPathAndQuery.Equals(targetUrl, StringComparison.OrdinalIgnoreCase)) { Console.WriteLine($"--- LoadSession Clicked: Navigating to {targetUrl}... ---"); NavigationManager.NavigateTo(targetUrl); } else { Console.WriteLine($"--- LoadSession Clicked: Already at {targetUrl}. No navigation needed. ---"); } } private async Task SendMessage() { if (currentSession == null || string.IsNullOrWhiteSpace(newMessage) || currentSession.IsWaitingForResponse || String.IsNullOrWhiteSpace(externalWebhookUrl)) return; var userMessage = new ChatMessage { IsUser = true, Content = newMessage }; ChatSessionService.AddMessage(activeSessionId, userMessage); ChatSessionService.SetWaitingStatus(activeSessionId, true); var messageToSend = newMessage; newMessage = string.Empty; StateHasChanged(); await ChatSessionService.SendMessageToExternalWebhook(externalWebhookUrl, activeSessionId, messageToSend); } private async Task HandleKeyDown(KeyboardEventArgs e) { if (e.Key == "Enter" && !currentSession.IsWaitingForResponse && !string.IsNullOrWhiteSpace(newMessage)) await SendMessage(); } [JSInvokable] public async Task HandleMessageReceived(ChatMessage message) { await InvokeAsync(StateHasChanged); } [JSInvokable] public async Task HandleSessionClosed() { await InvokeAsync(() => { if (currentSession != null && currentSession.SessionId == activeSessionId) { Console.WriteLine($"UI notified session closed: {activeSessionId}"); currentSession = null; activeSessionId = string.Empty; currentSessionIdInput = string.Empty; NavigationManager.NavigateTo("/chat", forceLoad: false); StateHasChanged(); } }); } private async Task RegisterCallbacksAsync() { if (currentSession != null) { _ = Task.Run(() => { ChatSessionService.RegisterSessionCallbacks( currentSession.SessionId, HandleMessageReceived, HandleSessionClosed ); }); await Task.CompletedTask; } } private async Task UnregisterCallbacksAsync() { if (!string.IsNullOrEmpty(activeSessionId)) { var capturedSessionId = activeSessionId; _ = Task.Run(() => ChatSessionService.UnregisterSessionCallbacks(capturedSessionId)); await Task.CompletedTask; } } public async ValueTask DisposeAsync() { await UnregisterCallbacksAsync(); } }