diff --git a/src/Quarrel.Client/QuarrelClient.Methods.cs b/src/Quarrel.Client/QuarrelClient.Methods.cs index a18d0425f..dd55abb01 100644 --- a/src/Quarrel.Client/QuarrelClient.Methods.cs +++ b/src/Quarrel.Client/QuarrelClient.Methods.cs @@ -136,17 +136,24 @@ public IPrivateChannel[] GetPrivateChannels() } } + // Nullability is improperly accessed here +#pragma warning disable CS8629 Array.Resize(ref privateChannels, i); Array.Sort(privateChannels, Comparer.Create((item1, item2) => { - if (!item2.LastMessageId.HasValue) return -1; - if (!item1.LastMessageId.HasValue) return 1; + bool i1Null = !item1.LastMessageId.HasValue; + bool i2Null = !item2.LastMessageId.HasValue; + + if (i1Null && i2Null) return 0; + if (i2Null) return -1; + if (i1Null) return 1; long compare = (long)item2.LastMessageId.Value - (long)item1.LastMessageId.Value; if (compare < 0) return -1; if (compare > 0) return 1; return 0; })); +#pragma warning restore CS8629 return privateChannels; } diff --git a/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableChannel.cs b/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableChannel.cs index 2878a5c20..fe9923a06 100644 --- a/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableChannel.cs +++ b/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableChannel.cs @@ -1,5 +1,6 @@ // Quarrel © 2022 +using Discord.API.Models.Enums.Channels; using Microsoft.Toolkit.Mvvm.Messaging; using Quarrel.Bindables.Abstract; using Quarrel.Bindables.Channels.Interfaces; @@ -45,6 +46,9 @@ internal BindableChannel( /// public ulong Id => Channel.Id; + /// + public ChannelType Type => Channel.Type; + /// public virtual string? Name => _channel.Name; diff --git a/src/Quarrel.ViewModels/Bindables/Channels/BindableTextChannel.cs b/src/Quarrel.ViewModels/Bindables/Channels/BindableTextChannel.cs index 3c85b476d..c7aa0d814 100644 --- a/src/Quarrel.ViewModels/Bindables/Channels/BindableTextChannel.cs +++ b/src/Quarrel.ViewModels/Bindables/Channels/BindableTextChannel.cs @@ -35,6 +35,9 @@ internal BindableTextChannel( /// public IMessageChannel MessageChannel => (IMessageChannel)Channel; + + /// + public GuildTextChannel TextChannel => (GuildTextChannel)Channel; /// protected override void AckUpdate() diff --git a/src/Quarrel.ViewModels/Bindables/Channels/Interfaces/IBindableChannel.cs b/src/Quarrel.ViewModels/Bindables/Channels/Interfaces/IBindableChannel.cs index f20cd6f3a..e57cbeaf9 100644 --- a/src/Quarrel.ViewModels/Bindables/Channels/Interfaces/IBindableChannel.cs +++ b/src/Quarrel.ViewModels/Bindables/Channels/Interfaces/IBindableChannel.cs @@ -1,5 +1,6 @@ // Quarrel © 2022 +using Discord.API.Models.Enums.Channels; using Quarrel.Client.Models.Channels.Interfaces; namespace Quarrel.Bindables.Channels.Interfaces @@ -14,6 +15,11 @@ public interface IBindableChannel /// public ulong Id { get; } + /// + /// Gets the channel type. + /// + public ChannelType Type { get; } + /// /// Gets the id of the guild the channel belongs to, or null if a DM. /// diff --git a/src/Quarrel.ViewModels/Services/Analytics/Enums/LoggedEvent.cs b/src/Quarrel.ViewModels/Services/Analytics/Enums/LoggedEvent.cs index 952f20368..bf4f6df1e 100644 --- a/src/Quarrel.ViewModels/Services/Analytics/Enums/LoggedEvent.cs +++ b/src/Quarrel.ViewModels/Services/Analytics/Enums/LoggedEvent.cs @@ -21,6 +21,18 @@ public enum LoggedEvent /// [StringValue("Message Sent")] MessageSent, + + /// + /// Opened a channel. + /// + [StringValue("Guild Opened")] + GuildOpened, + + /// + /// Opened a channel. + /// + [StringValue("Channel Opened")] + ChannelOpened, #endregion #region Login diff --git a/src/Quarrel.ViewModels/ViewModels/Panels/ChannelsViewModel.cs b/src/Quarrel.ViewModels/ViewModels/Panels/ChannelsViewModel.cs index dddbb2cf1..6e5013fcf 100644 --- a/src/Quarrel.ViewModels/ViewModels/Panels/ChannelsViewModel.cs +++ b/src/Quarrel.ViewModels/ViewModels/Panels/ChannelsViewModel.cs @@ -6,7 +6,10 @@ using Quarrel.Bindables.Channels.Interfaces; using Quarrel.Bindables.Guilds.Interfaces; using Quarrel.Messages.Navigation; +using Quarrel.Services.Analytics; +using Quarrel.Services.Analytics.Enums; using Quarrel.Services.Discord; +using System; using System.Collections.Generic; namespace Quarrel.ViewModels.Panels @@ -16,6 +19,7 @@ namespace Quarrel.ViewModels.Panels /// public partial class ChannelsViewModel : ObservableRecipient { + private readonly IAnalyticsService _analyticsService; private readonly IMessenger _messenger; private readonly IDiscordService _discordService; @@ -27,8 +31,9 @@ public partial class ChannelsViewModel : ObservableRecipient /// /// Initializes a new instance of the class. /// - public ChannelsViewModel(IMessenger messenger, IDiscordService discordService) + public ChannelsViewModel(IAnalyticsService analyticsService, IMessenger messenger, IDiscordService discordService) { + _analyticsService = analyticsService; _messenger = messenger; _discordService = discordService; @@ -55,6 +60,10 @@ public IBindableSelectableChannel? SelectedChannel { value.IsSelected = true; _currentGuild.SelectedChannelId = value.Id; + + _analyticsService.Log(LoggedEvent.ChannelOpened, + ("Type", $"{_selectedChannel.Type}")); + _messenger.Send(new NavigateToChannelMessage(value)); } } diff --git a/src/Quarrel.ViewModels/ViewModels/Panels/CommandBarViewModel.cs b/src/Quarrel.ViewModels/ViewModels/Panels/CommandBarViewModel.cs new file mode 100644 index 000000000..8e59ff9d5 --- /dev/null +++ b/src/Quarrel.ViewModels/ViewModels/Panels/CommandBarViewModel.cs @@ -0,0 +1,35 @@ +// Quarrel © 2022 + +using Microsoft.Toolkit.Mvvm.ComponentModel; +using Microsoft.Toolkit.Mvvm.Messaging; +using Quarrel.Bindables.Channels.Interfaces; +using Quarrel.Messages.Navigation; +using Quarrel.Services.Analytics; +using Quarrel.Services.Discord; + +namespace Quarrel.ViewModels.Panels +{ + public class CommandBarViewModel : ObservableRecipient + { + private readonly IAnalyticsService _analyticsService; + private readonly IMessenger _messenger; + private readonly IDiscordService _discordService; + + private IBindableSelectableChannel? _selectedChannel; + + public CommandBarViewModel(IAnalyticsService analyticsService, IMessenger messenger, IDiscordService discordService) + { + _analyticsService = analyticsService; + _messenger = messenger; + _discordService = discordService; + + _messenger.Register>(this, (_, m) => SelectedChannel = m.Channel); + } + + public IBindableSelectableChannel? SelectedChannel + { + get => _selectedChannel; + set => SetProperty(ref _selectedChannel, value); + } + } +} diff --git a/src/Quarrel.ViewModels/ViewModels/Panels/GuildsViewModel.cs b/src/Quarrel.ViewModels/ViewModels/Panels/GuildsViewModel.cs index b7c2de149..394fd382b 100644 --- a/src/Quarrel.ViewModels/ViewModels/Panels/GuildsViewModel.cs +++ b/src/Quarrel.ViewModels/ViewModels/Panels/GuildsViewModel.cs @@ -6,6 +6,8 @@ using Quarrel.Bindables.Guilds.Interfaces; using Quarrel.Messages; using Quarrel.Messages.Navigation; +using Quarrel.Services.Analytics; +using Quarrel.Services.Analytics.Enums; using Quarrel.Services.Discord; using Quarrel.Services.Dispatcher; using Quarrel.Services.Localization; @@ -19,6 +21,7 @@ namespace Quarrel.ViewModels /// public partial class GuildsViewModel : ObservableRecipient { + private readonly IAnalyticsService _analyticsService; private readonly IMessenger _messenger; private readonly ILocalizationService _localizationService; private readonly IDiscordService _discordService; @@ -30,8 +33,9 @@ public partial class GuildsViewModel : ObservableRecipient /// /// Initializes a new instance of the class. /// - public GuildsViewModel(IMessenger messenger, ILocalizationService localizationService, IDiscordService discordService, IDispatcherService dispatcherService) + public GuildsViewModel(IAnalyticsService analyticsService, IMessenger messenger, ILocalizationService localizationService, IDiscordService discordService, IDispatcherService dispatcherService) { + _analyticsService = analyticsService; _messenger = messenger; _localizationService = localizationService; _discordService = discordService; @@ -58,6 +62,7 @@ public IBindableSelectableGuildItem? SelectedGuild if (SetProperty(ref _selectedGuild, value) && value is not null) { value.IsSelected = true; + _analyticsService.Log(LoggedEvent.GuildOpened); _messenger.Send(new NavigateToGuildMessage(value)); } } diff --git a/src/Quarrel.ViewModels/ViewModels/Panels/MessageBoxViewModel.cs b/src/Quarrel.ViewModels/ViewModels/Panels/MessageBoxViewModel.cs index 68f004a32..0fa0b2b93 100644 --- a/src/Quarrel.ViewModels/ViewModels/Panels/MessageBoxViewModel.cs +++ b/src/Quarrel.ViewModels/ViewModels/Panels/MessageBoxViewModel.cs @@ -7,7 +7,6 @@ using Quarrel.Messages.Navigation; using Quarrel.Services.Discord; using Quarrel.Services.Dispatcher; -using System.Collections.Generic; namespace Quarrel.ViewModels.Panels { diff --git a/src/Quarrel.ViewModels/ViewModels/Panels/MessagesViewModel.cs b/src/Quarrel.ViewModels/ViewModels/Panels/MessagesViewModel.cs index 9ee972465..cee04ea70 100644 --- a/src/Quarrel.ViewModels/ViewModels/Panels/MessagesViewModel.cs +++ b/src/Quarrel.ViewModels/ViewModels/Panels/MessagesViewModel.cs @@ -10,9 +10,7 @@ using Quarrel.Messages.Navigation; using Quarrel.Services.Discord; using Quarrel.Services.Dispatcher; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Linq; namespace Quarrel.ViewModels.Panels { @@ -96,10 +94,13 @@ private void LoadInitialMessages(IBindableMessageChannel? channel) // Load messages var messages = await _discordService.GetChannelMessagesAsync(channel); BindableMessage[] bindableMessages = new BindableMessage[messages.Length]; - bindableMessages[0] = new BindableMessage(_messenger, _discordService, _dispatcherService, messages[messages.Length-1]); - for (int i = 1; i < messages.Length; i++) + if (bindableMessages.Length > 0) { - bindableMessages[i] = new BindableMessage(_messenger, _discordService, _dispatcherService, messages[messages.Length-1-i], messages[messages.Length-i]); + 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]); + } } // Add messages to the UI and mark loading as finished diff --git a/src/Quarrel/App.Services.cs b/src/Quarrel/App.Services.cs index ee7abfbb5..3faed2625 100644 --- a/src/Quarrel/App.Services.cs +++ b/src/Quarrel/App.Services.cs @@ -57,6 +57,7 @@ private IServiceProvider ConfigureServices() services.AddTransient(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/Quarrel/Attached/TextHelpers/CharacterCasing.cs b/src/Quarrel/Attached/TextHelpers/CharacterCasing.cs index 0cea5b51c..ddfc886fe 100644 --- a/src/Quarrel/Attached/TextHelpers/CharacterCasing.cs +++ b/src/Quarrel/Attached/TextHelpers/CharacterCasing.cs @@ -1,6 +1,6 @@ // Quarrel © 2022 -using Quarrel.Converters; +using Quarrel.Converters.Common.Text; using System; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; diff --git a/src/Quarrel/Controls/Shell/QuarrelCommandBar.xaml b/src/Quarrel/Controls/Shell/QuarrelCommandBar.xaml index 9c2953efa..af23d5e2e 100644 --- a/src/Quarrel/Controls/Shell/QuarrelCommandBar.xaml +++ b/src/Quarrel/Controls/Shell/QuarrelCommandBar.xaml @@ -6,6 +6,9 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:qc="using:Quarrel.Controls" + xmlns:bchannel="using:Quarrel.Bindables.Channels" + xmlns:cselector="using:Quarrel.Selectors.Channels" + xmlns:vconvert="using:Quarrel.Converters.Common.Visible" mc:Ignorable="d" Background="Transparent" d:DesignHeight="56" @@ -19,12 +22,35 @@ + + + + + + + + + + + + + + + + + - + + @@ -34,8 +60,12 @@ Click="HamburgerClicked"> + + - diff --git a/src/Quarrel/SubPages/LoginPage.xaml b/src/Quarrel/SubPages/LoginPage.xaml index abd8e6c6d..528378dee 100644 --- a/src/Quarrel/SubPages/LoginPage.xaml +++ b/src/Quarrel/SubPages/LoginPage.xaml @@ -7,7 +7,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:qb="using:Quarrel.Behaviors" xmlns:qc="using:Quarrel.Controls" - xmlns:convert="using:Quarrel.Converters" + xmlns:vconvert="using:Quarrel.Converters.Common.Visible" xmlns:spvm="using:Quarrel.ViewModels.SubPages" xmlns:ui="using:Microsoft.Toolkit.Uwp.UI" xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" @@ -145,7 +145,7 @@ + Visibility="{x:Bind vconvert:VisibleIfNotEqualConverter.Convert(ViewModel.AppVersionType, versionenums:VersionType.Release)}"/> + Visibility="{x:Bind vconvert:NotBoolToVisibilityConverter.Convert(ViewModel.IsNeutralLanguage), Mode=OneWay}"/> @@ -96,7 +96,7 @@ + Visibility="{x:Bind vconvert:VisibleIfNotEqualConverter.Convert(ViewModel.AppVersionType, versionenums:VersionType.Release)}">