mirror of
https://github.com/Stone-Red-Code/Decho.git
synced 2026-09-04 00:46:11 +02:00
Auto load more messages when scrolling up
This commit is contained in:
@@ -111,4 +111,20 @@ public sealed class ChannelViewModel(ChannelModel model) : ViewModelBase
|
||||
Model.Messages.Add(message);
|
||||
Messages.Add(new MessageViewModel(message));
|
||||
}
|
||||
|
||||
public void InsertMessages(IList<MessageModel> olderMessages)
|
||||
{
|
||||
HashSet<string> existingIds = Messages.Select(m => m.Model.Id).ToHashSet();
|
||||
List<MessageModel> fresh = olderMessages.Where(m => !existingIds.Contains(m.Id)).ToList();
|
||||
if (fresh.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (MessageModel msg in fresh)
|
||||
{
|
||||
Model.Messages.Insert(0, msg);
|
||||
Messages.Insert(0, new MessageViewModel(msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@ namespace Decho.ViewModels;
|
||||
|
||||
public sealed class ChatViewModel : ViewModelBase
|
||||
{
|
||||
public Action? LoadMoreRequested;
|
||||
|
||||
public ObservableCollection<MessageViewModel> Messages
|
||||
{
|
||||
get;
|
||||
@@ -57,6 +59,17 @@ public sealed class ChatViewModel : ViewModelBase
|
||||
|
||||
public string CurrentChannelName { get; private set; } = string.Empty;
|
||||
|
||||
public bool IsLoadingMore
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
if (field == value) return;
|
||||
field = value;
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public ChatViewModel()
|
||||
{
|
||||
Composer = new MessageComposerViewModel();
|
||||
|
||||
@@ -57,6 +57,7 @@ public sealed class MainWindowViewModel : ViewModelBase
|
||||
Chat.Composer.SendRequested += HandleSendRequested;
|
||||
Chat.Composer.CommandRequested += HandleCommandAsync;
|
||||
Chat.Composer.FileUploadRequested += HandleFileUploadRequested;
|
||||
Chat.LoadMoreRequested += HandleLoadMoreRequested;
|
||||
|
||||
WireCommandHandlerEvents();
|
||||
WireConnectionServiceEvents();
|
||||
@@ -879,6 +880,43 @@ public sealed class MainWindowViewModel : ViewModelBase
|
||||
}
|
||||
}
|
||||
|
||||
private async void HandleLoadMoreRequested()
|
||||
{
|
||||
string serverUrl = Chat.CurrentServerUrl;
|
||||
string channelName = Chat.CurrentChannelName;
|
||||
if (string.IsNullOrEmpty(serverUrl) || string.IsNullOrEmpty(channelName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ChannelViewModel? channel = FindChannelViewModel(serverUrl, channelName);
|
||||
if (channel is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Chat.IsLoadingMore = true;
|
||||
|
||||
try
|
||||
{
|
||||
int offset = channel.Messages.Count;
|
||||
List<MessageModel> older = await ConnectionService.GetHistoryAsync(serverUrl, channelName, HubConstants.DefaultHistoryCount, offset);
|
||||
|
||||
if (older.Count > 0)
|
||||
{
|
||||
channel.InsertMessages(older);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// silently ignore
|
||||
}
|
||||
finally
|
||||
{
|
||||
Chat.IsLoadingMore = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleChannelSelected(ChannelViewModel? channel)
|
||||
{
|
||||
if (channel is null)
|
||||
|
||||
@@ -31,4 +31,4 @@ public sealed partial class ImageViewerWindow : Window
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Threading;
|
||||
|
||||
@@ -12,6 +13,8 @@ public partial class MessageListView : UserControl
|
||||
private ChatViewModel? _chat;
|
||||
private bool _hasPendingScroll;
|
||||
private bool _wasAtBottom;
|
||||
private bool _loadingMore;
|
||||
private double _loadingMoreExtent;
|
||||
|
||||
public MessageListView()
|
||||
{
|
||||
@@ -25,6 +28,7 @@ public partial class MessageListView : UserControl
|
||||
{
|
||||
_chat.Messages.CollectionChanged -= OnMessagesChanged;
|
||||
_chat.PropertyChanged -= OnViewModelPropertyChanged;
|
||||
_chat.LoadMoreRequested -= OnLoadMoreRequested;
|
||||
}
|
||||
|
||||
ScrollViewer? scroll = this.FindControl<ScrollViewer>("MessageScrollViewer");
|
||||
@@ -35,10 +39,20 @@ public partial class MessageListView : UserControl
|
||||
{
|
||||
_chat.Messages.CollectionChanged += OnMessagesChanged;
|
||||
_chat.PropertyChanged += OnViewModelPropertyChanged;
|
||||
_chat.LoadMoreRequested += OnLoadMoreRequested;
|
||||
scroll?.ScrollChanged += OnScrollChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLoadMoreRequested()
|
||||
{
|
||||
ScrollViewer? scroll = this.FindControl<ScrollViewer>("MessageScrollViewer");
|
||||
if (scroll is null) return;
|
||||
|
||||
_loadingMore = true;
|
||||
_loadingMoreExtent = scroll.Extent.Height;
|
||||
}
|
||||
|
||||
private void OnScrollChanged(object? sender, ScrollChangedEventArgs e)
|
||||
{
|
||||
if (e.ExtentDelta.Y > 0 && _wasAtBottom)
|
||||
@@ -48,6 +62,14 @@ public partial class MessageListView : UserControl
|
||||
}
|
||||
|
||||
ScrollViewer scrollViewer = (ScrollViewer)sender!;
|
||||
|
||||
if (!_loadingMore && _chat is not null && !_chat.IsLoadingMore
|
||||
&& scrollViewer.Offset.Y <= 0
|
||||
&& scrollViewer.Extent.Height > scrollViewer.Viewport.Height)
|
||||
{
|
||||
_chat.LoadMoreRequested?.Invoke();
|
||||
}
|
||||
|
||||
_wasAtBottom = scrollViewer.Offset.Y + scrollViewer.Viewport.Height >= scrollViewer.Extent.Height - 30;
|
||||
}
|
||||
|
||||
@@ -58,6 +80,10 @@ public partial class MessageListView : UserControl
|
||||
_chat!.Messages.CollectionChanged += OnMessagesChanged;
|
||||
ScrollToBottom();
|
||||
}
|
||||
else if (e.PropertyName == nameof(ChatViewModel.IsLoadingMore) && !_chat!.IsLoadingMore && _loadingMore)
|
||||
{
|
||||
RestoreScrollPosition();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMessagesChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
@@ -68,6 +94,19 @@ public partial class MessageListView : UserControl
|
||||
}
|
||||
}
|
||||
|
||||
private void RestoreScrollPosition()
|
||||
{
|
||||
ScrollViewer? scroll = this.FindControl<ScrollViewer>("MessageScrollViewer");
|
||||
if (scroll is null) return;
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
double delta = scroll.Extent.Height - _loadingMoreExtent;
|
||||
scroll.Offset = new Vector(scroll.Offset.X, scroll.Offset.Y + delta);
|
||||
_loadingMore = false;
|
||||
}, DispatcherPriority.Background);
|
||||
}
|
||||
|
||||
private void TryAutoScroll()
|
||||
{
|
||||
ScrollViewer? scroll = this.FindControl<ScrollViewer>("MessageScrollViewer");
|
||||
|
||||
Reference in New Issue
Block a user