LANCommander/LANCommander.UI/Components/Chat/ChatListThread.razor
Pat Hartl 66abc6e59e Implement infinite loading with chat thread
- Chat messages are now loaded based on scroll position
- Read status is now updated and set by last message read
- Reworked script importing by using ScriptProvider
2026-01-11 14:38:24 -06:00

124 lines
No EOL
3.7 KiB
Text

@using LANCommander.SDK.Services
@using LANCommander.SDK.Models
@namespace LANCommander.UI.Components
@inject IChatClient ChatClient
@implements IAsyncDisposable
<MouseArea Cursor="Cursors.Pointer" OnClick="OnClick" Class="@_cssClass">
<Flex Align="FlexAlign.Center" Gap="FlexGap.Small">
<div class="chat-list-thread-avatar">
@if (Thread.Participants.Count == 2)
{
<SafeAvatar
Shape="AvatarShape.Square"
Size="AvatarSize.Large"
Username="@Thread.Participants.First().Name" />
}
else
{
<SafeAvatarGroup
Shape="AvatarShape.Square"
Size="AvatarSize.Large"
Usernames="@Thread.Participants.Select(p => p.Name)" />
}
</div>
<div class="chat-list-thread-title">
@Thread.Title
</div>
<div class="chat-list-thread-unread">
<Badge Count="_unreadCount" OverflowCount="99" />
</div>
</Flex>
</MouseArea>
@code {
[Parameter] public required SDK.Models.ChatThread Thread { get; set; }
[Parameter] public EventCallback<SDK.Models.ChatThread> OnSelected { get; set; }
[Parameter] public bool Active { get; set; }
int _unreadCount = 0;
string _cssClass = string.Empty;
bool _isSubscribed = false;
bool _previousActive = false;
protected override async Task OnParametersSetAsync()
{
await RefreshUnreadCountAsync();
// Refresh unread count when thread becomes active (read status may have been updated)
if (Active && !_previousActive)
{
await RefreshUnreadCountAsync();
}
_previousActive = Active;
_cssClass = new ClassBuilder()
.Add("chat-list-thread")
.If(() => Active, "chat-list-thread-active")
.Build();
// Subscribe to message events if not already subscribed
if (!_isSubscribed)
{
Thread.OnMessageReceivedAsync = OnMessageReceivedAsync;
Thread.OnMessagesReceivedAsync = OnMessagesReceivedAsync;
_isSubscribed = true;
}
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// Refresh unread count when component becomes active (read status may have changed)
if (Active && firstRender)
{
await RefreshUnreadCountAsync();
}
}
async Task OnMessageReceivedAsync(SDK.Models.ChatMessage message)
{
// Update unread count when a new message is received
await RefreshUnreadCountAsync();
}
async Task OnMessagesReceivedAsync(IEnumerable<SDK.Models.ChatMessage> messages)
{
// Update unread count when messages are received
await RefreshUnreadCountAsync();
}
async Task RefreshUnreadCountAsync()
{
var newCount = await ChatClient.GetUnreadMessageCountAsync(Thread.Id);
if (newCount != _unreadCount)
{
_unreadCount = newCount;
await InvokeAsync(StateHasChanged);
}
}
async Task OnClick(MouseEventArgs args)
{
if (OnSelected.HasDelegate)
await OnSelected.InvokeAsync(Thread);
// Refresh unread count when thread is selected (read status may have been updated)
await RefreshUnreadCountAsync();
}
public async ValueTask DisposeAsync()
{
if (_isSubscribed)
{
Thread.OnMessageReceivedAsync -= OnMessageReceivedAsync;
Thread.OnMessagesReceivedAsync -= OnMessagesReceivedAsync;
_isSubscribed = false;
}
await Task.CompletedTask;
}
}