diff --git a/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableChannel.cs b/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableChannel.cs index a5159fdb9..d79596b46 100644 --- a/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableChannel.cs +++ b/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableChannel.cs @@ -7,6 +7,7 @@ using Quarrel.Client.Models.Users; using Quarrel.Services.Discord; using Quarrel.Services.Dispatcher; +using Quarrel.Services.Localization; using System; namespace Quarrel.Bindables.Channels.Abstract @@ -84,19 +85,20 @@ private void AckUpdateRoot(object sender, EventArgs e) /// /// Creates a new instance of a based on the type. /// - /// The discord service to pass to the . - /// The dispatcher service to pass to the . + /// The to pass to the . + /// The to pass to the . + /// The to pass to the . /// The channel to wrap. /// The current user's guild member for the channel's guild. Null if not a guild channel. /// The parent category of the channel. - public static BindableChannel? Create(IDiscordService discordService, IDispatcherService dispatcherService, IChannel channel, GuildMember? member = null, BindableCategoryChannel? parent = null) + public static BindableChannel? Create(IDiscordService discordService, ILocalizationService localizationService, IDispatcherService dispatcherService, IChannel channel, GuildMember? member = null, BindableCategoryChannel? parent = null) { if (member is null) { return channel switch { DirectChannel c => new BindableDirectChannel(discordService, dispatcherService, c), - GroupChannel c => new BindableGroupChannel(discordService, dispatcherService, c), + GroupChannel c => new BindableGroupChannel(discordService, localizationService, dispatcherService, c), _ => null }; } diff --git a/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableGuildChannel.cs b/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableGuildChannel.cs index 9d9d518a5..e9eb56cd3 100644 --- a/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableGuildChannel.cs +++ b/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindableGuildChannel.cs @@ -8,6 +8,7 @@ using Quarrel.Client.Models.Users; using Quarrel.Services.Discord; using Quarrel.Services.Dispatcher; +using Quarrel.Services.Localization; namespace Quarrel.Bindables.Channels.Abstract { @@ -57,14 +58,15 @@ internal BindableGuildChannel(IDiscordService discordService, IDispatcherService /// /// Creates a new based on the type. /// - /// The discord service to pass to the . - /// The dispatcher service to pass to the . + /// The to pass to the . + /// The to pass to the . + /// The to pass to the . /// The channel to wrap. /// The current user's guild member for the channel's guild. /// The channel's parent category. - public static BindableGuildChannel? Create(IDiscordService discordService, IDispatcherService dispatcherService, IGuildChannel channel, GuildMember member, BindableCategoryChannel? parent = null) + public static BindableGuildChannel? Create(IDiscordService discordService, ILocalizationService localizationService, IDispatcherService dispatcherService, IGuildChannel channel, GuildMember member, BindableCategoryChannel? parent = null) { - return BindableChannel.Create(discordService, dispatcherService, channel, member, parent) as BindableGuildChannel; + return BindableChannel.Create(discordService, localizationService, dispatcherService, channel, member, parent) as BindableGuildChannel; } /// diff --git a/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindablePrivateChannel.cs b/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindablePrivateChannel.cs index 08ab0add9..6d90b2077 100644 --- a/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindablePrivateChannel.cs +++ b/src/Quarrel.ViewModels/Bindables/Channels/Abstract/BindablePrivateChannel.cs @@ -5,6 +5,7 @@ using Quarrel.Client.Models.Channels.Interfaces; using Quarrel.Services.Discord; using Quarrel.Services.Dispatcher; +using Quarrel.Services.Localization; namespace Quarrel.Bindables.Channels.Abstract { @@ -27,9 +28,9 @@ internal BindablePrivateChannel(IDiscordService discordService, IDispatcherServi /// public IMessageChannel MessageChannel => (IMessageChannel)Channel; - public static BindablePrivateChannel? Create(IDiscordService discordService, IDispatcherService dispatcherService, IPrivateChannel channel) + public static BindablePrivateChannel? Create(IDiscordService discordService, ILocalizationService localizationService, IDispatcherService dispatcherService, IPrivateChannel channel) { - return BindableChannel.Create(discordService, dispatcherService, channel) as BindablePrivateChannel; + return BindableChannel.Create(discordService, localizationService, dispatcherService, channel) as BindablePrivateChannel; } } } diff --git a/src/Quarrel.ViewModels/Bindables/Channels/BindableGroupChannel.cs b/src/Quarrel.ViewModels/Bindables/Channels/BindableGroupChannel.cs index 306b9f47d..2fe2394fe 100644 --- a/src/Quarrel.ViewModels/Bindables/Channels/BindableGroupChannel.cs +++ b/src/Quarrel.ViewModels/Bindables/Channels/BindableGroupChannel.cs @@ -8,6 +8,8 @@ using Quarrel.Client.Models.Channels.Interfaces; using Quarrel.Services.Discord; using Quarrel.Services.Dispatcher; +using Quarrel.Services.Localization; +using System.Linq; namespace Quarrel.Bindables.Channels { @@ -16,9 +18,13 @@ namespace Quarrel.Bindables.Channels /// public class BindableGroupChannel : BindablePrivateChannel, IBindableMessageChannel { - internal BindableGroupChannel(IDiscordService discordService, IDispatcherService dispatcherService, GroupChannel groupChannel) : + private ILocalizationService _localizationService; + + internal BindableGroupChannel(IDiscordService discordService, ILocalizationService localizationService, IDispatcherService dispatcherService, GroupChannel groupChannel) : base(discordService, dispatcherService, groupChannel) { + _localizationService = localizationService; + Guard.IsNotNull(groupChannel.Recipients); Recipients = new BindableUser[groupChannel.Recipients.Length]; int i = 0; @@ -32,17 +38,24 @@ internal BindableGroupChannel(IDiscordService discordService, IDispatcherService } /// - public IGroupChannel DirectChannel => (IGroupChannel)Channel; + public IGroupChannel GroupChannel => (IGroupChannel)Channel; - // TODO: Formatted names /// - public override string? Name => Channel.Name; + public override string? Name => Channel.Name ?? _localizationService.CommaList(Recipients.Select(x => x.User.Username).ToArray()); - public string IconUrl => $"https://cdn.discordapp.com/channel-icons/{Channel.Id}/{DirectChannel.Icon}.png"; + /// + /// Gets the icon url of the group channel. + /// + public string? IconUrl => GroupChannel.Icon is null ? null : $"https://cdn.discordapp.com/channel-icons/{Channel.Id}/{GroupChannel.Icon}.png"; /// /// Gets the recipients of the group channel as a array. /// public BindableUser[] Recipients { get; } + + /// + /// Gets the number of members in the group channel. + /// + public int MemberCount => Recipients.Length + 1; } } diff --git a/src/Quarrel.ViewModels/Services/Discord/DiscordService.Methods.cs b/src/Quarrel.ViewModels/Services/Discord/DiscordService.Methods.cs index 221be01a0..fcbc24b97 100644 --- a/src/Quarrel.ViewModels/Services/Discord/DiscordService.Methods.cs +++ b/src/Quarrel.ViewModels/Services/Discord/DiscordService.Methods.cs @@ -134,7 +134,7 @@ public async Task GetChannelMessagesAsync(IBindableMessageCha category = categories[nestedChannel.CategoryId.Value]; } - channel = BindableGuildChannel.Create(this, _dispatcherService, nestedChannel, member, category); + channel = BindableGuildChannel.Create(this, _localizationService, _dispatcherService, nestedChannel, member, category); if (channel is not null && (channel.Channel.Id == guild.SelectedChannelId || (selectedChannel is null && channel.IsAccessible)) && channel is IBindableSelectableChannel messageChannel) @@ -156,7 +156,7 @@ public async Task GetChannelMessagesAsync(IBindableMessageCha int i = 0; foreach (var channel in rawChannels) { - channels[i] = BindablePrivateChannel.Create(this, _dispatcherService, channel); + channels[i] = BindablePrivateChannel.Create(this, _localizationService, _dispatcherService, channel); if (channels[i] is IBindableSelectableChannel selectableChannel && selectableChannel.Id == home.SelectedChannelId) diff --git a/src/Quarrel.ViewModels/Services/Discord/DiscordService.cs b/src/Quarrel.ViewModels/Services/Discord/DiscordService.cs index 42dc49e52..5a94b547a 100644 --- a/src/Quarrel.ViewModels/Services/Discord/DiscordService.cs +++ b/src/Quarrel.ViewModels/Services/Discord/DiscordService.cs @@ -11,6 +11,7 @@ using Quarrel.Services.Analytics.Enums; using Quarrel.Services.Analytics.Models; using Quarrel.Services.Dispatcher; +using Quarrel.Services.Localization; using Quarrel.Services.Storage.Accounts.Models; using System; using System.Threading.Tasks; @@ -24,15 +25,17 @@ public partial class DiscordService : IDiscordService { private readonly QuarrelClient _quarrelClient; private readonly IAnalyticsService _analyticsService; + private readonly ILocalizationService _localizationService; private readonly IDispatcherService _dispatcherService; private readonly IMessenger _messenger; /// /// Initializes a new instance of the class. /// - public DiscordService(IAnalyticsService analyticsService, IDispatcherService dispatcherService, IMessenger messenger) + public DiscordService(IAnalyticsService analyticsService, ILocalizationService localizationService, IDispatcherService dispatcherService, IMessenger messenger) { _analyticsService = analyticsService; + _localizationService = localizationService; _dispatcherService = dispatcherService; _messenger = messenger; _quarrelClient = new QuarrelClient(); diff --git a/src/Quarrel.ViewModels/Services/Localization/ILocalizationService.cs b/src/Quarrel.ViewModels/Services/Localization/ILocalizationService.cs index cd303efbf..306c7bf79 100644 --- a/src/Quarrel.ViewModels/Services/Localization/ILocalizationService.cs +++ b/src/Quarrel.ViewModels/Services/Localization/ILocalizationService.cs @@ -22,6 +22,11 @@ public interface ILocalizationService /// Localized if valid, otherwise returns an empty . string this[string key, params object[] args] { get; } + /// + /// Gets a list of items as as a string with and. + /// + string CommaList(params string[] args); + /// /// Gets a value indicating whether or not the current language is written right to left. /// diff --git a/src/Quarrel.ViewModels/ViewModels/CurrentUserViewModel.cs b/src/Quarrel.ViewModels/ViewModels/CurrentUserViewModel.cs index 4518f5dd1..fafb55235 100644 --- a/src/Quarrel.ViewModels/ViewModels/CurrentUserViewModel.cs +++ b/src/Quarrel.ViewModels/ViewModels/CurrentUserViewModel.cs @@ -5,8 +5,10 @@ using Microsoft.Toolkit.Mvvm.Messaging; using Quarrel.Bindables.Users; using Quarrel.Messages; +using Quarrel.Messages.Navigation.SubPages; using Quarrel.Services.Discord; using Quarrel.Services.Dispatcher; +using Quarrel.ViewModels.SubPages.Settings; namespace Quarrel.ViewModels { @@ -46,6 +48,7 @@ public CurrentUserViewModel(IMessenger messenger, IDiscordService discordService [ICommand] public void NavigateToSettings() { + _messenger.Send(new NavigateToSubPageMessage(typeof(UserSettingsPageViewModel))); } } } diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/Abstract/UserSettingsSubPageViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/Abstract/UserSettingsSubPageViewModel.cs new file mode 100644 index 000000000..dfefb04e8 --- /dev/null +++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/Abstract/UserSettingsSubPageViewModel.cs @@ -0,0 +1,21 @@ +// Quarrel © 2022 + +using Microsoft.Toolkit.Mvvm.ComponentModel; +using Quarrel.Services.Localization; + +namespace Quarrel.ViewModels.SubPages.UserSettings.Pages.Abstract +{ + public abstract class UserSettingsSubPageViewModel : ObservableObject + { + protected readonly ILocalizationService _localizationService; + + public UserSettingsSubPageViewModel(ILocalizationService localizationService) + { + _localizationService = localizationService; + } + + public abstract string Glyph { get; } + + public abstract string Title { get; } + } +} diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/BehaviorPageViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/BehaviorPageViewModel.cs new file mode 100644 index 000000000..b5c62f296 --- /dev/null +++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/BehaviorPageViewModel.cs @@ -0,0 +1,23 @@ +// Quarrel © 2022 + +using Quarrel.Services.Localization; +using Quarrel.ViewModels.SubPages.UserSettings.Pages.Abstract; + +namespace Quarrel.ViewModels.SubPages.UserSettings.Pages +{ + public class BehaviorPageViewModel : UserSettingsSubPageViewModel + { + private const string BehaviorResource = "UserSettings/Behavior"; + + public BehaviorPageViewModel(ILocalizationService localizationService) : + base(localizationService) + { + } + + /// + public override string Glyph => ""; + + /// + public override string Title => _localizationService[BehaviorResource]; + } +} diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/ConnectionsPageViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/ConnectionsPageViewModel.cs new file mode 100644 index 000000000..5f75c5f61 --- /dev/null +++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/ConnectionsPageViewModel.cs @@ -0,0 +1,23 @@ +// Quarrel © 2022 + +using Quarrel.Services.Localization; +using Quarrel.ViewModels.SubPages.UserSettings.Pages.Abstract; + +namespace Quarrel.ViewModels.SubPages.UserSettings.Pages +{ + public class ConnectionsPageViewModel : UserSettingsSubPageViewModel + { + private const string ConnectionsResource = "UserSettings/Connections"; + + public ConnectionsPageViewModel(ILocalizationService localizationService) : + base(localizationService) + { + } + + /// + public override string Glyph => ""; + + /// + public override string Title => _localizationService[ConnectionsResource]; + } +} diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/DisplayPageViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/DisplayPageViewModel.cs new file mode 100644 index 000000000..b0c3f30c5 --- /dev/null +++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/DisplayPageViewModel.cs @@ -0,0 +1,23 @@ +// Quarrel © 2022 + +using Quarrel.Services.Localization; +using Quarrel.ViewModels.SubPages.UserSettings.Pages.Abstract; + +namespace Quarrel.ViewModels.SubPages.UserSettings.Pages +{ + public class DisplayPageViewModel : UserSettingsSubPageViewModel + { + private const string ConnectionsResource = "UserSettings/Display"; + + public DisplayPageViewModel(ILocalizationService localizationService) : + base(localizationService) + { + } + + /// + public override string Glyph => ""; + + /// + public override string Title => _localizationService[ConnectionsResource]; + } +} diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/MyAccountPageViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/MyAccountPageViewModel.cs new file mode 100644 index 000000000..cb1cf31f9 --- /dev/null +++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/MyAccountPageViewModel.cs @@ -0,0 +1,23 @@ +// Quarrel © 2022 + +using Quarrel.Services.Localization; +using Quarrel.ViewModels.SubPages.UserSettings.Pages.Abstract; + +namespace Quarrel.ViewModels.SubPages.UserSettings.Pages +{ + public class MyAccountPageViewModel : UserSettingsSubPageViewModel + { + private const string MyAccountResource = "UserSettings/MyAccount"; + + public MyAccountPageViewModel(ILocalizationService localizationService) : + base(localizationService) + { + } + + /// + public override string Glyph => ""; + + /// + public override string Title => _localizationService[MyAccountResource]; + } +} diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/NotificationsPageViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/NotificationsPageViewModel.cs new file mode 100644 index 000000000..064ef3271 --- /dev/null +++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/NotificationsPageViewModel.cs @@ -0,0 +1,23 @@ +// Quarrel © 2022 + +using Quarrel.Services.Localization; +using Quarrel.ViewModels.SubPages.UserSettings.Pages.Abstract; + +namespace Quarrel.ViewModels.SubPages.UserSettings.Pages +{ + public class NotificationsPageViewModel : UserSettingsSubPageViewModel + { + private const string NotificationsResource = "UserSettings/Notifications"; + + public NotificationsPageViewModel(ILocalizationService localizationService) : + base(localizationService) + { + } + + /// + public override string Glyph => ""; + + /// + public override string Title => _localizationService[NotificationsResource]; + } +} diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/PrivacyPageViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/PrivacyPageViewModel.cs new file mode 100644 index 000000000..e656338eb --- /dev/null +++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/PrivacyPageViewModel.cs @@ -0,0 +1,21 @@ +// Quarrel © 2022 + +using Quarrel.Services.Localization; +using Quarrel.ViewModels.SubPages.UserSettings.Pages.Abstract; + +namespace Quarrel.ViewModels.SubPages.UserSettings.Pages +{ + public class PrivacyPageViewModel : UserSettingsSubPageViewModel + { + private const string PrivacyResource = "UserSettings/Privacy"; + + public PrivacyPageViewModel(ILocalizationService localizationService) : + base(localizationService) + { + } + + public override string Glyph => ""; + + public override string Title => _localizationService[PrivacyResource]; + } +} diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/VoicePageViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/VoicePageViewModel.cs new file mode 100644 index 000000000..2097b0d4c --- /dev/null +++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/Pages/VoicePageViewModel.cs @@ -0,0 +1,23 @@ +// Quarrel © 2022 + +using Quarrel.Services.Localization; +using Quarrel.ViewModels.SubPages.UserSettings.Pages.Abstract; + +namespace Quarrel.ViewModels.SubPages.UserSettings.Pages +{ + public class VoicePageViewModel : UserSettingsSubPageViewModel + { + private const string VoiceResource = "UserSettings/Voice"; + + public VoicePageViewModel(ILocalizationService localizationService) : + base(localizationService) + { + } + + /// + public override string Glyph => ""; + + /// + public override string Title => _localizationService[VoiceResource]; + } +} diff --git a/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/UserSettingsPageViewModel.cs b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/UserSettingsPageViewModel.cs new file mode 100644 index 000000000..857d171ad --- /dev/null +++ b/src/Quarrel.ViewModels/ViewModels/SubPages/UserSettings/UserSettingsPageViewModel.cs @@ -0,0 +1,39 @@ +// Quarrel © 2022 + +using Microsoft.Toolkit.Mvvm.ComponentModel; +using Quarrel.Services.Localization; +using Quarrel.ViewModels.SubPages.UserSettings.Pages; +using Quarrel.ViewModels.SubPages.UserSettings.Pages.Abstract; +using System.Collections.ObjectModel; + +namespace Quarrel.ViewModels.SubPages.Settings +{ + public class UserSettingsPageViewModel : ObservableObject + { + private readonly ILocalizationService _localizationService; + + private UserSettingsSubPageViewModel _selectedSubPage; + + public UserSettingsPageViewModel(ILocalizationService localizationService) + { + _localizationService = localizationService; + + Pages = new ObservableCollection(); + Pages.Add(new MyAccountPageViewModel(_localizationService)); + Pages.Add(new PrivacyPageViewModel(_localizationService)); + Pages.Add(new ConnectionsPageViewModel(_localizationService)); + Pages.Add(new DisplayPageViewModel(_localizationService)); + Pages.Add(new BehaviorPageViewModel(_localizationService)); + Pages.Add(new NotificationsPageViewModel(_localizationService)); + Pages.Add(new VoicePageViewModel(_localizationService)); + } + + public UserSettingsSubPageViewModel SelectedSubPage + { + get => _selectedSubPage; + set => SetProperty(ref _selectedSubPage, value); + } + + public ObservableCollection Pages { get; } + } +} diff --git a/src/Quarrel/App.Services.cs b/src/Quarrel/App.Services.cs index 3250ed46c..24fb2bf30 100644 --- a/src/Quarrel/App.Services.cs +++ b/src/Quarrel/App.Services.cs @@ -19,6 +19,7 @@ using Quarrel.ViewModels.SubPages.DiscordStatus; using Quarrel.ViewModels.SubPages.Host; using Quarrel.ViewModels.SubPages.Meta; +using Quarrel.ViewModels.SubPages.Settings; using System; using Windows.Storage; @@ -63,6 +64,7 @@ private IServiceProvider ConfigureServices() services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); #if DEV ApplyDitryOverrides(services); diff --git a/src/Quarrel/Controls/Shell/Panels/Channels/CurrentUserButton.xaml b/src/Quarrel/Controls/Shell/Panels/Channels/CurrentUserButton.xaml index 5784ffb77..fa23b57c6 100644 --- a/src/Quarrel/Controls/Shell/Panels/Channels/CurrentUserButton.xaml +++ b/src/Quarrel/Controls/Shell/Panels/Channels/CurrentUserButton.xaml @@ -85,10 +85,10 @@