Skip to content
This repository was archived by the owner on Apr 30, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/API/Discord.API/Rest/IChannelService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ internal interface IChannelService
[Get("/v9/channels/{channelId}/messages?limit={limit}")]
Task<JsonMessage[]> GetChannelMessages([AliasAs("channelId")] ulong channelId, [AliasAs("limit")] int limit = 50);

[Get("/v9/channels/{channelId}/messages?limit={limit}&before={before}")]
Task<JsonMessage[]> GetChannelMessagesBefore([AliasAs("channelId")] ulong channelId, [AliasAs("before")] ulong before, [AliasAs("limit")] int limit = 50);

[Post("/v9/channels/{channelId}/messages")]
Task<JsonMessage> CreateMessage([AliasAs("channelId")] ulong channelId, [Body] JsonMessageUpsert message);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Quarrel © 2022

using Quarrel.Markdown.Parsing;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Documents;

namespace Quarrel.Markdown
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// Quarrel © 2022

using Quarrel.Markdown.Parsing;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ internal UserMentionElement(UserMention mention, BindableMessage? context) : bas

}

public BindableUser User
public BindableUser? User
{
get => (BindableUser)GetValue(UserProperty);
get => (BindableUser?)GetValue(UserProperty);
set => SetValue(UserProperty, value);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,6 @@ public Style? UserMentionStyle
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var messageRenderer = (MessageRenderer)d;

if (messageRenderer.Context is null)
{
// TODO: Allow context free rendering
return;
}

var tree = Parser.ParseAST(messageRenderer.Text, true, false);
var modTree = AdjustTree(tree);
messageRenderer.RenderMarkdown(modTree);
Expand Down
1 change: 0 additions & 1 deletion src/Libs/Quarrel.Markdown/Rendering/MessageRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,6 @@ private void UpdateOverlays()

private void RenderMarkdown(IList<ASTRoot> tree)
{

if (_richBlock != null)
{
_richBlock.SizeChanged -= RichBlock_SizeChanged;
Expand Down
12 changes: 10 additions & 2 deletions src/Quarrel.Client/QuarrelClient.Methods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,19 @@ public partial class QuarrelClient
/// Gets messages in a channel.
/// </summary>
/// <param name="channelId">The channel's id.</param>
public async Task<Message[]> GetMessagesAsync(ulong channelId, ulong? guildId = null)
/// <param name="guildId">The id of the guild the channel belongs to.</param>
/// <param name="beforeId">The message to get the messages from before.</param>
public async Task<Message[]> GetMessagesAsync(ulong channelId, ulong? guildId = null, ulong? beforeId = null)
{
Guard.IsNotNull(_channelService, nameof(_channelService));

JsonMessage[]? jsonMessages = await MakeRefitRequest(() => _channelService.GetChannelMessages(channelId));
Func<Task<JsonMessage[]>> request = () => _channelService.GetChannelMessages(channelId);
if (beforeId.HasValue)
{
request = () => _channelService.GetChannelMessagesBefore(channelId, beforeId.Value);
}

JsonMessage[]? jsonMessages = await MakeRefitRequest(request);
Guard.IsNotNull(jsonMessages, nameof(jsonMessages));

Message[] messages = new Message[jsonMessages.Length];
Expand Down
5 changes: 2 additions & 3 deletions src/Quarrel.ViewModels/Bindables/Messages/BindableMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using Quarrel.Bindables.Messages.Embeds;
using Quarrel.Bindables.Users;
using Quarrel.Client.Models.Messages;
using Quarrel.Client.Models.Messages.Embeds;
using Quarrel.Messages.Discord.Messages;
using Quarrel.Services.Discord;
using Quarrel.Services.Dispatcher;
Expand Down Expand Up @@ -66,7 +65,7 @@ internal BindableMessage(
{
if (Id == e.Message.Id)
{
Message = e.Message;
_dispatcherService.RunOnUIThread(() => Message = e.Message);
}
});

Expand All @@ -88,7 +87,7 @@ public Message Message
set
{
SetProperty(ref _message, value);
_dispatcherService.RunOnUIThread(AckUpdate);
AckUpdate();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ public void InsertRange(int index, IEnumerable<T> collection, NotifyCollectionCh
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
if (changedItems.Count > 0)
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, changedItems, index));
for (int i = changedItems.Count-1; i >= 0; i--)
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<T>{ changedItems[i] }, index));
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ public BindableGuildFolder[] GetMyGuildFolders()
}

/// <inheritdoc/>
public async Task<Message[]> GetChannelMessagesAsync(IBindableMessageChannel channel)
public async Task<Message[]> GetChannelMessagesAsync(IBindableMessageChannel channel, ulong? beforeId = null)
{
var rawMessages = await _quarrelClient.GetMessagesAsync(channel.Id, channel.GuildId);
var rawMessages = await _quarrelClient.GetMessagesAsync(channel.Id, channel.GuildId, beforeId);
Guard.IsNotNull(rawMessages, nameof(rawMessages));
return rawMessages;
}
Expand Down
5 changes: 3 additions & 2 deletions src/Quarrel.ViewModels/Services/Discord/IDiscordService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,14 @@ public interface IDiscordService
/// </remarks>
/// <returns>The array of <see cref="BindableGuildFolder"/>s that the current user has.</returns>
BindableGuildFolder[] GetMyGuildFolders();

/// <summary>
/// Gets the messages in a channel.
/// </summary>
/// <param name="channel">The channel to get the messages for.</param>
/// <param name="beforeId">The if of the last message to load messages before, or null.</param>
/// <returns>An array of <see cref="BindableMessage"/>s from the channel.</returns>
Task<Message[]> GetChannelMessagesAsync(IBindableMessageChannel channel);
Task<Message[]> GetChannelMessagesAsync(IBindableMessageChannel channel, ulong? beforeId = null);

/// <summary>
/// Gets the channels in a guild.
Expand Down
99 changes: 73 additions & 26 deletions src/Quarrel.ViewModels/ViewModels/Panels/MessagesViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Quarrel.Services.Discord;
using Quarrel.Services.Dispatcher;
using System.Collections.ObjectModel;
using System.Threading;

namespace Quarrel.ViewModels.Panels
{
Expand All @@ -24,6 +25,7 @@ public partial class MessagesViewModel : ObservableRecipient
private readonly IDispatcherService _dispatcherService;

private bool _isLoading;
private SemaphoreSlim _semaphore;
private IBindableSelectableChannel? _selectedChannel;

/// <summary>
Expand All @@ -34,17 +36,16 @@ public MessagesViewModel(IMessenger messenger, IDiscordService discordService, I
_messenger = messenger;
_discordService = discordService;
_dispatcherService = dispatcherService;
_semaphore = new SemaphoreSlim(1,1);

Source = new ObservableRangeCollection<BindableMessage>();
IsLoading = false;

_messenger.Register<NavigateToChannelMessage<IBindableSelectableChannel>>(this, (_, m) => SelectedChannel = m.Channel);
_messenger.Register<MessageCreatedMessage>(this, (_, m) =>
{
if (SelectedChannel?.Id == m.Message.ChannelId)
{
AppendMessage(m.Message);
}
if (SelectedChannel?.Id != m.Message.ChannelId) return;
_dispatcherService.RunOnUIThread(() => AppendMessage(m.Message));
});
}

Expand All @@ -60,7 +61,7 @@ public IBindableSelectableChannel? SelectedChannel
{
if (SetProperty(ref _selectedChannel, value))
{
LoadChannel(value);
LoadChannel();
}
}
}
Expand All @@ -74,47 +75,93 @@ public bool IsLoading
private set => SetProperty(ref _isLoading, value);
}

private void LoadChannel(IBindableSelectableChannel? channel)
/// <remarks>
/// Must be called on the UI thread.
/// </remarks>
public async void LoadOlderMessages()
{
if (channel is IBindableMessageChannel messageChannel)
if (Source.Count == 0) return;
if (IsLoading) return;
await _semaphore.WaitAsync();
IsLoading = true;
try
{
ulong beforeId = Source[0].Message.Id;
IBindableMessageChannel? channel = SelectedChannel as IBindableMessageChannel;
Guard.IsNotNull(channel, nameof(channel));

// Load messages
var messages = await _discordService.GetChannelMessagesAsync(channel, beforeId);
if (messages.Length > 0)
{
var bindableMessages = ParseMessages(messages);

// Add messages to the UI and mark loading as finished
Source.InsertRange(0, bindableMessages);
}
}
finally
{
LoadInitialMessages(messageChannel);
IsLoading = false;
_semaphore.Release();
}
}

private void LoadInitialMessages(IBindableMessageChannel? channel)
/// <remarks>
/// Must be called on the UI thread.
/// </remarks>
private async void LoadInitialMessages()
{
Guard.IsNotNull(channel, nameof(channel));
_dispatcherService.RunOnUIThread(async () =>
await _semaphore.WaitAsync();
try
{
IBindableMessageChannel? channel = SelectedChannel as IBindableMessageChannel;
Guard.IsNotNull(channel, nameof(channel));
// Clear the messages and begin loading
Source.Clear();
IsLoading = true;

// Load messages
IsLoading = true;
var messages = await _discordService.GetChannelMessagesAsync(channel);
BindableMessage[] bindableMessages = new BindableMessage[messages.Length];
if (bindableMessages.Length > 0)
{
bindableMessages[0] = new BindableMessage(_messenger, _discordService, _dispatcherService, messages[messages.Length - 1]);
for (int i = 1; i < messages.Length; i++)
{
bindableMessages[i] = new BindableMessage(_messenger, _discordService, _dispatcherService, messages[messages.Length - 1 - i], messages[messages.Length - i]);
}
}
var bindableMessages = ParseMessages(messages);

// Add messages to the UI and mark loading as finished
Source.AddRange(bindableMessages);
IsLoading = false;
});
}
finally
{
_semaphore.Release();
}
}

private void AppendMessage(Message message)
private void LoadChannel()
{
_dispatcherService.RunOnUIThread(() =>
if (SelectedChannel is IBindableMessageChannel)
{
Source.Add(new BindableMessage(_messenger, _discordService, _dispatcherService, message, Source.Count > 0 ? Source[Source.Count-1].Message : null));
});
LoadInitialMessages();
}
}

private BindableMessage[] ParseMessages(Message[] messages)
{
BindableMessage[] bindableMessages = new BindableMessage[messages.Length];
if (bindableMessages.Length == 0) return bindableMessages;

bindableMessages[0] = new BindableMessage(_messenger, _discordService, _dispatcherService, messages[messages.Length - 1]);
for (int i = 1; i < messages.Length; i++)
{
bindableMessages[i] = new BindableMessage(_messenger, _discordService, _dispatcherService, messages[messages.Length - 1 - i], messages[messages.Length - i]);
}
return bindableMessages;
}

/// <remarks>
/// Must be called on the UI thread.
/// </remarks>
private void AppendMessage(Message message)
{
Source.Add(new BindableMessage(_messenger, _discordService, _dispatcherService, message, Source.Count > 0 ? Source[Source.Count - 1].Message : null));
}
}
}
3 changes: 2 additions & 1 deletion src/Quarrel/Controls/Panels/Messages/MessagePanel.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
<ListView ItemsSource="{x:Bind ViewModel.Source}"
ItemTemplateSelector="{StaticResource MessageTemplateSelector}"
ItemContainerStyle="{StaticResource MessageListItemStyle}"
ShowsScrollingPlaceholders="False">
ShowsScrollingPlaceholders="False"
Loaded="ListView_Loaded">
<ListView.Header>
<ProgressBar IsIndeterminate="True" Visibility="{x:Bind ViewModel.IsLoading, Mode=OneWay}"/>
</ListView.Header>
Expand Down
27 changes: 27 additions & 0 deletions src/Quarrel/Controls/Panels/Messages/MessagePanel.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,46 @@
// Quarrel © 2022

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Toolkit.Uwp.UI;
using Quarrel.ViewModels.Panels;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace Quarrel.Controls.Panels.Messages
{
public sealed partial class MessagePanel : UserControl
{
private const double ScrollPadding = 100;

public MessagePanel()
{
this.InitializeComponent();
DataContext = App.Current.Services.GetRequiredService<MessagesViewModel>();
}

public MessagesViewModel ViewModel => (MessagesViewModel)DataContext;

private void ListView_Loaded(object sender, RoutedEventArgs e)
{
ListView messageList = (ListView)sender;
var scrollViewer = messageList.FindDescendant<ScrollViewer>();
scrollViewer.ViewChanged += OnViewChanged;
}

private void OnViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
var viewer = (ScrollViewer)sender;
if (viewer.VerticalOffset <= ScrollPadding)
{
if (!ViewModel.IsLoading)
{
ViewModel.LoadOlderMessages();
}
}
else if (viewer.VerticalOffset >= viewer.ScrollableHeight - ScrollPadding)
{
// TODO: Scrolled to bottom
}
}
}
}
4 changes: 3 additions & 1 deletion src/Quarrel/Controls/Shell/QuarrelCommandBar.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
xmlns:qc="using:Quarrel.Controls"
xmlns:bchannel="using:Quarrel.Bindables.Channels"
xmlns:cselector="using:Quarrel.Selectors.Channels"
xmlns:cconvert="using:Quarrel.Converters.Discord.Channels"
xmlns:vconvert="using:Quarrel.Converters.Common.Visible"
mc:Ignorable="d"
Background="Transparent"
Expand Down Expand Up @@ -80,7 +81,8 @@
<Button x:Uid="CommandBar/CallBTN"
ToolTipService.ToolTip="Call"
ToolTipService.Placement="Bottom"
Style="{StaticResource CommandBarButton}">
Style="{StaticResource CommandBarButton}"
Visibility="{x:Bind cconvert:IsPrivateChannelVisibleConverter.Convert(ViewModel.SelectedChannel), Mode=OneWay}">
<FontIcon Glyph="&#xE13A;" FontSize="16"/>
</Button>

Expand Down
Loading