diff --git a/SteamAccountManager.AvaloniaUI/Mappers/AccountMapper.cs b/SteamAccountManager.AvaloniaUI/Mappers/AccountMapper.cs index df1318e..c225574 100644 --- a/SteamAccountManager.AvaloniaUI/Mappers/AccountMapper.cs +++ b/SteamAccountManager.AvaloniaUI/Mappers/AccountMapper.cs @@ -5,16 +5,29 @@ using SteamAccountManager.AvaloniaUI.Common.Utils; using SteamAccountManager.AvaloniaUI.Models; using SteamAccountManager.AvaloniaUI.Services; +using SteamAccountManager.Domain.Steam.Blacklisting.Storage; namespace SteamAccountManager.AvaloniaUI.Mappers { public class AccountMapper { - private AvatarService _avatarService; + private readonly IBlacklistedAccountsStorage _blacklistedAccountsStorage; + private readonly AvatarService _avatarService; - public AccountMapper(AvatarService avatarService) + public AccountMapper(AvatarService avatarService, IBlacklistedAccountsStorage blacklistedAccountsStorage) { _avatarService = avatarService; + _blacklistedAccountsStorage = blacklistedAccountsStorage; + } + + private async Task IsBlacklisted(string steamId) + { + var blacklistedIds = await _blacklistedAccountsStorage.Get(); + + if (blacklistedIds is null) + return false; + + return blacklistedIds.BlacklistedIds.Contains(steamId); } private string GetTimePassedFormatted(long minutesPassed) @@ -62,6 +75,7 @@ public async Task FromSteamAccount(Domain.Steam.Model.Account steamAcco LastLogin = timePassedSinceLastLogin, Rank = rank, IsLoggedIn = steamAccount.IsLoggedIn, + IsBlacklisted = await IsBlacklisted(steamAccount.Id), }; } } diff --git a/SteamAccountManager.AvaloniaUI/Models/Account.cs b/SteamAccountManager.AvaloniaUI/Models/Account.cs index 9bd968c..7bf468e 100644 --- a/SteamAccountManager.AvaloniaUI/Models/Account.cs +++ b/SteamAccountManager.AvaloniaUI/Models/Account.cs @@ -1,9 +1,10 @@ using System; using Avalonia.Media.Imaging; +using ReactiveUI; namespace SteamAccountManager.AvaloniaUI.Models { - public class Account + public class Account : ReactiveObject { public IBitmap? ProfilePicture { get; set; } public Uri? ProfilePictureUrl { get; set; } @@ -15,6 +16,21 @@ public class Account public bool IsCommunityBanned { get; set; } public string LastLogin { get; set; } = string.Empty; public bool IsLoggedIn { get; set; } + private bool _isBlacklisted; + + public bool IsBlacklisted + { + get => _isBlacklisted; + set => this.RaiseAndSetIfChanged(ref _isBlacklisted, value); + } + + private bool _isBlacklistToggleVisible; + + public bool IsBlacklistToggleVisible + { + get => _isBlacklistToggleVisible; + set => this.RaiseAndSetIfChanged(ref _isBlacklistToggleVisible, value); + } public Rank Rank { get; set; } = new() { diff --git a/SteamAccountManager.AvaloniaUI/Services/InfoService.cs b/SteamAccountManager.AvaloniaUI/Services/InfoService.cs index ce1ab94..e712d07 100644 --- a/SteamAccountManager.AvaloniaUI/Services/InfoService.cs +++ b/SteamAccountManager.AvaloniaUI/Services/InfoService.cs @@ -5,11 +5,16 @@ namespace SteamAccountManager.AvaloniaUI.Services; public class InfoService { - public void ShowRepository() + private const string RetrieveApiKey = "https://steamcommunity.com/dev/apikey"; + private const string GithubRepository = "https://github.com/sahin-a/SteamAccountSwitcher/"; + + public void ShowRepository() => ShowWebPage(GithubRepository); + + public void ShowRetrieveApiKey() => ShowWebPage(RetrieveApiKey); + + private void ShowWebPage(string url) { - if (OperatingSystem.IsWindows()) - Process.Start("explorer", "https://github.com/sahin-a/SteamAccountManager/"); - else - Process.Start("xdg-open", "https://github.com/sahin-a/SteamAccountManager/"); + var targetExecutable = OperatingSystem.IsWindows() ? "explorer" : "xdg-open"; + Process.Start(targetExecutable, url); } } \ No newline at end of file diff --git a/SteamAccountManager.AvaloniaUI/SteamAccountSwitcherStyles.xaml b/SteamAccountManager.AvaloniaUI/SteamAccountSwitcherStyles.xaml index fb2c3db..84ab23b 100644 --- a/SteamAccountManager.AvaloniaUI/SteamAccountSwitcherStyles.xaml +++ b/SteamAccountManager.AvaloniaUI/SteamAccountSwitcherStyles.xaml @@ -1,13 +1,31 @@  + + + + + + + + + + + + + + diff --git a/SteamAccountManager.AvaloniaUI/ViewModels/AccountSwitcherViewModel.cs b/SteamAccountManager.AvaloniaUI/ViewModels/AccountSwitcherViewModel.cs index 292ba67..c627aee 100644 --- a/SteamAccountManager.AvaloniaUI/ViewModels/AccountSwitcherViewModel.cs +++ b/SteamAccountManager.AvaloniaUI/ViewModels/AccountSwitcherViewModel.cs @@ -9,6 +9,8 @@ using SteamAccountManager.AvaloniaUI.Services; using SteamAccountManager.AvaloniaUI.ViewModels.Commands; using SteamAccountManager.Domain.Common.EventSystem; +using SteamAccountManager.Domain.Steam.Blacklisting.Model; +using SteamAccountManager.Domain.Steam.Blacklisting.Storage; using SteamAccountManager.Domain.Steam.Configuration.Model; using SteamAccountManager.Domain.Steam.Service; using SteamAccountManager.Domain.Steam.Storage; @@ -16,8 +18,6 @@ namespace SteamAccountManager.AvaloniaUI.ViewModels { - // TODO: looks ridicilous, I should refactor all of this but I don't feel bored enough yet - // TOOD: switch to reactive commands etc. public class AccountSwitcherViewModel : RoutableViewModel { private readonly IGetAccountsWithDetailsUseCase _getAccountsUseCase; @@ -28,12 +28,26 @@ public class AccountSwitcherViewModel : RoutableViewModel private readonly EventBus _eventBus; private readonly InfoService _infoService; private readonly INotificationConfigStorage _notificationConfigStorage; + private readonly IBlacklistedAccountsStorage _blacklistedAccountsStorage; - public AdvancedObservableCollection Accounts { get; private set; } + public List AllAccounts { get; private set; } = new(); + public List WhitelistedAccounts { get; private set; } = new(); + public AdvancedObservableCollection AccountsForDisplay { get; private set; } + public ICommand AccountSelectedCommand { get; set; } public ICommand ProfileClickedCommand { get; } public ICommand RefreshAccountsCommand { get; } public ICommand ShowInfoCommand { get; } public ICommand AddAccountCommand { get; } + public ICommand BlacklistAccountCommand { get; set; } + public ICommand ToggleBlacklistingModeCommand { get; set; } + private bool _isBlacklistToggleVisible; + + public bool IsBlacklistToggleVisible + { + get => _isBlacklistToggleVisible; + set => this.RaiseAndSetIfChanged(ref _isBlacklistToggleVisible, value); + } + public VisibilityConfig Config { get; private set; } = new(); private bool _isLoading; @@ -53,6 +67,7 @@ public AccountSwitcherViewModel ILocalNotificationService notificationService, IPrivacyConfigStorage privacyConfigStorage, INotificationConfigStorage notificationConfigStorage, + IBlacklistedAccountsStorage blacklistedAccountsStorage, EventBus eventBus, InfoService infoService ) : base(screen) @@ -63,21 +78,26 @@ InfoService infoService _notificationService = notificationService; _privacyConfigStorage = privacyConfigStorage; _notificationConfigStorage = notificationConfigStorage; + _blacklistedAccountsStorage = blacklistedAccountsStorage; _eventBus = eventBus; _infoService = infoService; - Accounts = new AdvancedObservableCollection(); + AccountsForDisplay = new AdvancedObservableCollection(); ProfileClickedCommand = new ProfileClickedCommand(); RefreshAccountsCommand = new QuickCommand(LoadAccounts); ShowInfoCommand = new QuickCommand(ShowInfo); AddAccountCommand = new QuickCommand(AddAccount); + AccountSelectedCommand = ReactiveCommand.Create((Account account) => OnAccountSelected(account)); + BlacklistAccountCommand = + ReactiveCommand.Create((Account account) => ToggleBlacklistingForAccount(account)); + ToggleBlacklistingModeCommand = ReactiveCommand.Create(ToggleBlacklistingMode); - RegisterSubscriptions(); + SubscribeToEventBus(); LoadVisibilityConfig(); LoadAccounts(); } - private void RegisterSubscriptions() + private void SubscribeToEventBus() { _eventBus.Subscribe(subscriberKey: GetType().Name, Events.ACCOUNTS_UPDATED, _ => LoadAccounts() @@ -113,20 +133,66 @@ private async void LoadVisibilityConfig() } } + private void DisplayAccountsForCurrentState() + { + AccountsForDisplay.SetItems(IsBlacklistToggleVisible + ? SortAccountsForManagement(AllAccounts) + : SortAccounts(WhitelistedAccounts)); + } + + private Task ToggleBlacklistingMode() => Task.Run(() => + { + IsBlacklistToggleVisible = !IsBlacklistToggleVisible; + DisplayAccountsForCurrentState(); + }); + + private async Task WhitelistAccount(Account account, AccountBlacklist blacklist) + { + blacklist.BlacklistedIds.Remove(account.SteamId); + account.IsBlacklisted = false; + WhitelistedAccounts.Add(account); + await _blacklistedAccountsStorage.Set(blacklist); + } + + private async Task BlacklistAccount(Account account, AccountBlacklist blacklist) + { + blacklist.BlacklistedIds.Add(account.SteamId); + account.IsBlacklisted = true; + WhitelistedAccounts.Remove(account); + await _blacklistedAccountsStorage.Set(blacklist); + } + + private async Task ToggleBlacklistingForAccount(Account account) + { + var blacklist = await _blacklistedAccountsStorage.Get(new()); + + if (blacklist.BlacklistedIds.Contains(account.SteamId)) + await WhitelistAccount(account, blacklist); + else + await BlacklistAccount(account, blacklist); + } + private async void AddAccount() { await _switchAccountUseCase.Execute(string.Empty); } - private IEnumerable SortAccounts(Account[] accounts) => accounts.OrderByDescending(x => x.IsLoggedIn) + private List SortAccountsForManagement(List accounts) => accounts + .OrderBy(x => x.IsBlacklisted) + .ToList(); + + private List SortAccounts(List accounts) => accounts + .OrderByDescending(x => x.IsLoggedIn) .ThenByDescending(x => x.Rank.Level) .ThenBy(x => x.Name) - .ThenBy(x => x.IsVacBanned); + .ThenBy(x => x.IsVacBanned) + .ToList(); - private async Task> GetAccounts() + private async Task> GetAccounts() { var steamAccounts = await _getAccountsUseCase.Execute(); - var accounts = await Task.WhenAll(steamAccounts.ConvertAll(x => _accountMapper.FromSteamAccount(x))); + var accounts = (await Task.WhenAll(steamAccounts.ConvertAll(x => _accountMapper.FromSteamAccount(x)))) + .ToList(); return SortAccounts(accounts); } @@ -138,7 +204,11 @@ private async void LoadAccounts() return; IsLoading = true; - await Task.Run(async () => Accounts.SetItems((await GetAccounts()).ToList())); + var accounts = await GetAccounts(); + var whitelistedAccounts = accounts.Where(x => !x.IsBlacklisted).ToList(); + AllAccounts = accounts; + WhitelistedAccounts = whitelistedAccounts; + await Task.Run(DisplayAccountsForCurrentState); IsLoading = false; } diff --git a/SteamAccountManager.AvaloniaUI/ViewModels/SettingsViewModel.cs b/SteamAccountManager.AvaloniaUI/ViewModels/SettingsViewModel.cs index 88ca905..c59d813 100644 --- a/SteamAccountManager.AvaloniaUI/ViewModels/SettingsViewModel.cs +++ b/SteamAccountManager.AvaloniaUI/ViewModels/SettingsViewModel.cs @@ -5,6 +5,7 @@ using DynamicData; using ReactiveUI; using SteamAccountManager.AvaloniaUI.Common; +using SteamAccountManager.AvaloniaUI.Services; using SteamAccountManager.Domain.Common.EventSystem; using SteamAccountManager.Domain.Steam.Configuration.Model; using SteamAccountManager.Domain.Steam.Storage; @@ -18,13 +19,24 @@ public class SettingsViewModel : RoutableViewModel private readonly EventBus _eventBus; private readonly INotificationConfigStorage _notificationConfigStorage; private readonly IRichPresenceConfigStorage _richPresenceConfigStorage; + private readonly InfoService _infoService; public AdvancedObservableCollection AccountDetailsToggles { get; } = new(); public AdvancedObservableCollection SettingsToggles { get; } = new(); - public ICommand SaveApiKeyCommand { get; } public ICommand RichPresenceToggleCommand { get; } + public ICommand ShowRetrieveApiKeyPageCommand { get; } public bool IsRichPresenceEnabled { get; set; } - public string WebApiKey { get; set; } + private string _webApiKey = ""; + + public string WebApiKey + { + get => _webApiKey; + set + { + _webApiKey = value; + UpdateApiKey(value); + } + } public SettingsViewModel ( @@ -33,7 +45,8 @@ public SettingsViewModel IPrivacyConfigStorage privacyConfigStorage, IRichPresenceConfigStorage richPresenceConfigStorage, INotificationConfigStorage notificationConfigStorage, - EventBus eventBus + EventBus eventBus, + InfoService infoService ) : base(screen) { _steamApiKeyStorage = apiKeyStorage; @@ -41,9 +54,10 @@ EventBus eventBus _notificationConfigStorage = notificationConfigStorage; _richPresenceConfigStorage = richPresenceConfigStorage; _eventBus = eventBus; + _infoService = infoService; - SaveApiKeyCommand = ReactiveCommand.Create((string key) => UpdateApiKey(key)); RichPresenceToggleCommand = ReactiveCommand.Create(UpdateRichPresenceConfig); + ShowRetrieveApiKeyPageCommand = ReactiveCommand.Create(ShowRetrieveApiKeyPage); InitializeControls(); PrefillFields(); @@ -64,6 +78,11 @@ private void UpdateRichPresenceConfig() _eventBus.Notify(Events.RICH_PRESENCE_CONFIG_UPDATED, null); } + private void ShowRetrieveApiKeyPage() + { + _infoService.ShowRetrieveApiKey(); + } + private async void CreateSettingsToggles() { SettingsToggles.SetItems( diff --git a/SteamAccountManager.AvaloniaUI/Views/AccountSwitcherView.axaml b/SteamAccountManager.AvaloniaUI/Views/AccountSwitcherView.axaml index 127ca54..9fea685 100644 --- a/SteamAccountManager.AvaloniaUI/Views/AccountSwitcherView.axaml +++ b/SteamAccountManager.AvaloniaUI/Views/AccountSwitcherView.axaml @@ -12,10 +12,12 @@ - - + + @@ -29,99 +31,142 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + - - - - - - + + + + + + - - + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + diff --git a/SteamAccountManager.AvaloniaUI/Views/AccountSwitcherView.axaml.cs b/SteamAccountManager.AvaloniaUI/Views/AccountSwitcherView.axaml.cs index e6cdf18..fd4a239 100644 --- a/SteamAccountManager.AvaloniaUI/Views/AccountSwitcherView.axaml.cs +++ b/SteamAccountManager.AvaloniaUI/Views/AccountSwitcherView.axaml.cs @@ -1,8 +1,5 @@ -using Autofac; -using Avalonia.Controls; using Avalonia.Markup.Xaml; using Avalonia.ReactiveUI; -using SteamAccountManager.AvaloniaUI.Models; using SteamAccountManager.AvaloniaUI.ViewModels; namespace SteamAccountManager.AvaloniaUI.Views @@ -13,13 +10,5 @@ public AccountSwitcherView() { AvaloniaXamlLoader.Load(this); } - - private void AccountSelection_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - if ((sender as ListBox)?.SelectedItem is Account selectedAccount) - { - ViewModel?.OnAccountSelected(selectedAccount); - } - } } -} +} \ No newline at end of file diff --git a/SteamAccountManager.AvaloniaUI/Views/MainWindowView.axaml b/SteamAccountManager.AvaloniaUI/Views/MainWindowView.axaml index 4eff34c..f0232db 100644 --- a/SteamAccountManager.AvaloniaUI/Views/MainWindowView.axaml +++ b/SteamAccountManager.AvaloniaUI/Views/MainWindowView.axaml @@ -1,40 +1,48 @@ + Width="800" + Height="450" + MinWidth="800" + MinHeight="450"> - - - + + + - - - - - - - - - + diff --git a/SteamAccountManager.Domain/Steam/Blacklisting/Model/AccountBlacklist.cs b/SteamAccountManager.Domain/Steam/Blacklisting/Model/AccountBlacklist.cs new file mode 100644 index 0000000..b211127 --- /dev/null +++ b/SteamAccountManager.Domain/Steam/Blacklisting/Model/AccountBlacklist.cs @@ -0,0 +1,6 @@ +namespace SteamAccountManager.Domain.Steam.Blacklisting.Model; + +public class AccountBlacklist +{ + public HashSet BlacklistedIds { get; set; } = new(); +} \ No newline at end of file diff --git a/SteamAccountManager.Domain/Steam/Blacklisting/Storage/IBlacklistedAccountsStorage.cs b/SteamAccountManager.Domain/Steam/Blacklisting/Storage/IBlacklistedAccountsStorage.cs new file mode 100644 index 0000000..8934b55 --- /dev/null +++ b/SteamAccountManager.Domain/Steam/Blacklisting/Storage/IBlacklistedAccountsStorage.cs @@ -0,0 +1,8 @@ +using SteamAccountManager.Domain.Common.Storage; +using SteamAccountManager.Domain.Steam.Blacklisting.Model; + +namespace SteamAccountManager.Domain.Steam.Blacklisting.Storage; + +public interface IBlacklistedAccountsStorage : IObjectStorage +{ +} \ No newline at end of file diff --git a/SteamAccountManager.Infrastructure/Dependencies.cs b/SteamAccountManager.Infrastructure/Dependencies.cs index 428869d..ac41c6a 100644 --- a/SteamAccountManager.Infrastructure/Dependencies.cs +++ b/SteamAccountManager.Infrastructure/Dependencies.cs @@ -1,6 +1,7 @@ using System.Runtime.InteropServices; using Autofac; using SteamAccountManager.Domain.Common.EventSystem; +using SteamAccountManager.Domain.Steam.Blacklisting.Storage; using SteamAccountManager.Domain.Steam.Local.Logger; using SteamAccountManager.Domain.Steam.Local.Repository; using SteamAccountManager.Domain.Steam.Observables; @@ -70,6 +71,7 @@ private static void RegisterStorages(this ContainerBuilder builder) builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); } } } \ No newline at end of file diff --git a/SteamAccountManager.Infrastructure/Steam/Local/Storage/BlacklistedAccountsStorage.cs b/SteamAccountManager.Infrastructure/Steam/Local/Storage/BlacklistedAccountsStorage.cs new file mode 100644 index 0000000..df07125 --- /dev/null +++ b/SteamAccountManager.Infrastructure/Steam/Local/Storage/BlacklistedAccountsStorage.cs @@ -0,0 +1,15 @@ +using SteamAccountManager.Domain.Steam.Blacklisting.Model; +using SteamAccountManager.Domain.Steam.Blacklisting.Storage; +using SteamAccountManager.Domain.Steam.Local.Logger; +using SteamAccountManager.Infrastructure.Steam.Local.Dao; +using SteamAccountManager.Infrastructure.Steam.Local.DataSource; + +namespace SteamAccountManager.Infrastructure.Steam.Local.Storage; + +public class BlacklistedAccountsStorage : ObjectStorage, IBlacklistedAccountsStorage +{ + public BlacklistedAccountsStorage(ILogger logger, IFileProvider fileProvider, string name = "blacklisted_accounts") + : base(name, logger, new FileDataSource(directory: AppDataDirectory.Storages, fileProvider)) + { + } +} \ No newline at end of file diff --git a/SteamAccountManager.Infrastructure/Steam/Local/Storage/ObjectStorage.cs b/SteamAccountManager.Infrastructure/Steam/Local/Storage/ObjectStorage.cs index fcbd88a..1e01589 100644 --- a/SteamAccountManager.Infrastructure/Steam/Local/Storage/ObjectStorage.cs +++ b/SteamAccountManager.Infrastructure/Steam/Local/Storage/ObjectStorage.cs @@ -26,14 +26,8 @@ public ObjectStorage(string name, ILogger logger, IKeyValueDataSource dataSource protected virtual T? GetDefaultValue() => null; - /// - /// Retrieves the value as non-nullable - /// - /// - /// value as non-nullable - /// Will be thrown when there is no value or default value present public async Task Get(T defaultValue) => - await Get() ?? throw new NullReferenceException(message: "No value could be retrieved!"); + await Get() ?? defaultValue; public async Task Get() {