diff --git a/src/Artemis.UI.Shared/Styles/Sidebar.axaml b/src/Artemis.UI.Shared/Styles/Sidebar.axaml
index 605ba8e40..5fbfb48d5 100644
--- a/src/Artemis.UI.Shared/Styles/Sidebar.axaml
+++ b/src/Artemis.UI.Shared/Styles/Sidebar.axaml
@@ -22,6 +22,6 @@
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Styles/TextBlock.axaml b/src/Artemis.UI.Shared/Styles/TextBlock.axaml
index 9d3cf6ab4..8a5099cde 100644
--- a/src/Artemis.UI.Shared/Styles/TextBlock.axaml
+++ b/src/Artemis.UI.Shared/Styles/TextBlock.axaml
@@ -10,6 +10,15 @@
This is heading 5
This is heading 6
This is a subtitle
+
+ This is heading 1
+ This is heading 2
+ This is heading 3
+ This is heading 4
+ This is heading 5
+ This is heading 6
+ This is a subtitle
+
@@ -50,4 +59,26 @@
+
+
+
+
+
+
+
+
diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj
index 4e71ac252..5da497f13 100644
--- a/src/Artemis.UI/Artemis.UI.csproj
+++ b/src/Artemis.UI/Artemis.UI.csproj
@@ -55,4 +55,11 @@
+
+
+
+ ProfileListEntryView.axaml
+ Code
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Categories/CategoriesViewModel.cs b/src/Artemis.UI/Screens/Workshop/Categories/CategoriesViewModel.cs
index f131958ae..e3b1e0464 100644
--- a/src/Artemis.UI/Screens/Workshop/Categories/CategoriesViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Categories/CategoriesViewModel.cs
@@ -1,11 +1,12 @@
using System;
-using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Disposables;
using System.Threading;
using System.Threading.Tasks;
using Artemis.UI.Shared;
using Artemis.WebClient.Workshop;
+using DynamicData;
using ReactiveUI;
using Serilog;
using StrawberryShake;
@@ -16,21 +17,21 @@ public class CategoriesViewModel : ActivatableViewModelBase
{
private readonly IWorkshopClient _client;
private readonly ILogger _logger;
- private IReadOnlyList _categories;
+ public readonly SourceList _categories;
public CategoriesViewModel(ILogger logger, IWorkshopClient client)
{
_logger = logger;
_client = client;
-
+ _categories = new SourceList();
+ _categories.Connect().Bind(out ReadOnlyObservableCollection categoryViewModels).Subscribe();
+
+ Categories = categoryViewModels;
this.WhenActivated(d => ReactiveCommand.CreateFromTask(GetCategories).Execute().Subscribe().DisposeWith(d));
}
- public IReadOnlyList Categories
- {
- get => _categories;
- set => RaiseAndSetIfChanged(ref _categories, value);
- }
+ public ReadOnlyObservableCollection Categories { get; }
+
private async Task GetCategories(CancellationToken cancellationToken)
{
@@ -40,7 +41,12 @@ public class CategoriesViewModel : ActivatableViewModelBase
if (result.IsErrorResult())
_logger.Warning("Failed to retrieve categories {Error}", result.Errors);
- Categories = result.Data?.Categories.Select(c => new CategoryViewModel(c)).ToList() ?? new List();
+ _categories.Edit(l =>
+ {
+ l.Clear();
+ if (result.Data?.Categories != null)
+ l.AddRange(result.Data.Categories.Select(c => new CategoryViewModel(c)));
+ });
}
catch (Exception e)
{
diff --git a/src/Artemis.UI/Screens/Workshop/CurrentUser/CurrentUserView.axaml b/src/Artemis.UI/Screens/Workshop/CurrentUser/CurrentUserView.axaml
index 57a245c81..9487ac4eb 100644
--- a/src/Artemis.UI/Screens/Workshop/CurrentUser/CurrentUserView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/CurrentUser/CurrentUserView.axaml
@@ -9,9 +9,9 @@
x:Class="Artemis.UI.Screens.Workshop.CurrentUser.CurrentUserView"
x:DataType="currentUser:CurrentUserViewModel">
-
+
-
+
-
+
diff --git a/src/Artemis.UI/Screens/Workshop/CurrentUser/CurrentUserViewModel.cs b/src/Artemis.UI/Screens/Workshop/CurrentUser/CurrentUserViewModel.cs
index 7df2e3150..c204c3165 100644
--- a/src/Artemis.UI/Screens/Workshop/CurrentUser/CurrentUserViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/CurrentUser/CurrentUserViewModel.cs
@@ -10,38 +10,33 @@ using Artemis.WebClient.Workshop.Services;
using Avalonia.Media.Imaging;
using Flurl.Http;
using ReactiveUI;
+using Serilog;
namespace Artemis.UI.Screens.Workshop.CurrentUser;
public class CurrentUserViewModel : ActivatableViewModelBase
{
+ private readonly ILogger _logger;
private readonly IAuthenticationService _authenticationService;
- private ObservableAsPropertyHelper? _isLoggedIn;
-
- private string? _userId;
- private string? _name;
- private string? _email;
+ private bool _loading = true;
private Bitmap? _avatar;
+ private string? _email;
+ private string? _name;
+ private string? _userId;
- public CurrentUserViewModel(IAuthenticationService authenticationService)
+ public CurrentUserViewModel(ILogger logger, IAuthenticationService authenticationService)
{
+ _logger = logger;
_authenticationService = authenticationService;
Login = ReactiveCommand.CreateFromTask(ExecuteLogin);
- this.WhenActivated(d => _isLoggedIn = _authenticationService.WhenAnyValue(s => s.IsLoggedIn).ToProperty(this, vm => vm.IsLoggedIn).DisposeWith(d));
- this.WhenActivated(d =>
- {
- Task.Run(async () =>
- {
- await _authenticationService.AutoLogin();
- await LoadCurrentUser();
- }).DisposeWith(d);
- });
+ this.WhenActivated(d => ReactiveCommand.CreateFromTask(ExecuteAutoLogin).Execute().Subscribe().DisposeWith(d));
}
- public void Logout()
+ public bool Loading
{
- _authenticationService.Logout();
+ get => _loading;
+ set => RaiseAndSetIfChanged(ref _loading, value);
}
public string? UserId
@@ -69,18 +64,38 @@ public class CurrentUserViewModel : ActivatableViewModelBase
}
public ReactiveCommand Login { get; }
- public bool IsLoggedIn => _isLoggedIn?.Value ?? false;
+
+ public void Logout()
+ {
+ _authenticationService.Logout();
+ }
private async Task ExecuteLogin(CancellationToken cancellationToken)
{
await _authenticationService.Login();
await LoadCurrentUser();
- Console.WriteLine(_authenticationService.Claims);
+ }
+
+ private async Task ExecuteAutoLogin(CancellationToken cancellationToken)
+ {
+ try
+ {
+ await _authenticationService.AutoLogin();
+ await LoadCurrentUser();
+ }
+ catch (Exception e)
+ {
+ _logger.Warning(e, "Failed to load the current user");
+ }
+ finally
+ {
+ Loading = false;
+ }
}
private async Task LoadCurrentUser()
{
- if (!IsLoggedIn)
+ if (!_authenticationService.IsLoggedIn)
return;
UserId = _authenticationService.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntryListView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/EntryListView.axaml
new file mode 100644
index 000000000..e7450f685
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Entries/EntryListView.axaml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+ by
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ downloads
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntryListView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Entries/EntryListView.axaml.cs
new file mode 100644
index 000000000..42a7b86ad
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Entries/EntryListView.axaml.cs
@@ -0,0 +1,24 @@
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Markup.Xaml;
+using Avalonia.ReactiveUI;
+
+namespace Artemis.UI.Screens.Workshop.Entries;
+
+public partial class EntryListView : ReactiveUserControl
+{
+ public EntryListView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ private async void InputElement_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
+ {
+ await ViewModel.NavigateToEntry();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntryListViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/EntryListViewModel.cs
new file mode 100644
index 000000000..7a318fc27
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Entries/EntryListViewModel.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Threading.Tasks;
+using Artemis.UI.Shared;
+using Artemis.UI.Shared.Routing;
+using Artemis.WebClient.Workshop;
+
+namespace Artemis.UI.Screens.Workshop.Entries;
+
+public class EntryListViewModel : ViewModelBase
+{
+ private readonly IRouter _router;
+
+ public EntryListViewModel(IGetEntries_Entries_Nodes entry, IRouter router)
+ {
+ _router = router;
+ Entry = entry;
+ }
+
+ public IGetEntries_Entries_Nodes Entry { get; }
+
+ public async Task NavigateToEntry()
+ {
+ switch (Entry.EntryType)
+ {
+ case EntryType.Layout:
+ await _router.Navigate($"workshop/layouts/{Entry.Id}");
+ break;
+ case EntryType.Profile:
+ await _router.Navigate($"workshop/profiles/{Entry.Id}");
+ break;
+ case EntryType.Plugin:
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileListEntryView.axaml b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListEntryView.axaml
deleted file mode 100644
index 13395d7b7..000000000
--- a/src/Artemis.UI/Screens/Workshop/Profile/ProfileListEntryView.axaml
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Downloads
- Last updated
-
-
-
-
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileListEntryView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListEntryView.axaml.cs
deleted file mode 100644
index 288da66ae..000000000
--- a/src/Artemis.UI/Screens/Workshop/Profile/ProfileListEntryView.axaml.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Markup.Xaml;
-
-namespace Artemis.UI.Screens.Workshop.Profile;
-
-public partial class ProfileListEntryView : UserControl
-{
- public ProfileListEntryView()
- {
- InitializeComponent();
- }
-
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
-}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileListEntryViewModel.cs b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListEntryViewModel.cs
deleted file mode 100644
index 67786214d..000000000
--- a/src/Artemis.UI/Screens/Workshop/Profile/ProfileListEntryViewModel.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using Artemis.UI.Shared;
-using Artemis.WebClient.Workshop;
-
-namespace Artemis.UI.Screens.Workshop.Profile;
-
-public class ProfileListEntryViewModel : ViewModelBase
-{
- public ProfileListEntryViewModel(IGetEntries_Entries_Nodes entry)
- {
- Entry = entry;
- }
-
- public IGetEntries_Entries_Nodes Entry { get; }
-}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileListViewModel.cs b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListViewModel.cs
index 76f1fa762..ecbdb28ff 100644
--- a/src/Artemis.UI/Screens/Workshop/Profile/ProfileListViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListViewModel.cs
@@ -1,15 +1,18 @@
using System;
+using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Disposables;
using System.Threading;
using System.Threading.Tasks;
using Artemis.UI.Screens.Workshop.Categories;
+using Artemis.UI.Screens.Workshop.Entries;
using Artemis.UI.Screens.Workshop.Parameters;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Routing;
using Artemis.WebClient.Workshop;
using DynamicData;
+using DynamicData.Alias;
using ReactiveUI;
using StrawberryShake;
@@ -21,21 +24,31 @@ public class ProfileListViewModel : RoutableScreen();
_entries.Connect()
- .Transform(e => new ProfileListEntryViewModel(e))
- .Bind(out ReadOnlyObservableCollection observableEntries)
+ .Transform(e => new EntryListViewModel(e, router))
+ .Bind(out ReadOnlyObservableCollection observableEntries)
.Subscribe();
+
+ this.WhenActivated(d =>
+ {
+ CategoriesViewModel._categories.Connect()
+ .AutoRefresh(c => c.IsSelected)
+ .Filter(e => e.IsSelected)
+ .Select(e => e.Id)
+ .Subscribe(_ => ReactiveCommand.CreateFromTask(GetEntries).Execute().Subscribe())
+ .DisposeWith(d);
+ });
Entries = observableEntries;
}
public CategoriesViewModel CategoriesViewModel { get; }
- public ReadOnlyObservableCollection Entries { get; set; }
+ public ReadOnlyObservableCollection Entries { get; set; }
public int Page
{
@@ -64,7 +77,13 @@ public class ProfileListViewModel : RoutableScreen categories = CategoriesViewModel.Categories.Where(c => c.IsSelected).Select(c => (int?) c.Id).ToList();
+ if (categories.Any())
+ filter.Categories = new ListFilterInputTypeOfCategoryFilterInput {All = new CategoryFilterInput {Id = new IntOperationFilterInput {In = categories}}};
+
+ return filter;
}
public EntryType? EntryType => null;
diff --git a/src/Artemis.UI/Screens/Workshop/Search/SearchView.axaml b/src/Artemis.UI/Screens/Workshop/Search/SearchView.axaml
index c4b5f5063..e46ea18ac 100644
--- a/src/Artemis.UI/Screens/Workshop/Search/SearchView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/Search/SearchView.axaml
@@ -16,13 +16,13 @@
MaxWidth="500"
Watermark="Search"
Margin="0 5"
- ValueMemberBinding="{CompiledBinding Name, DataType=workshop:ISearchEntries_Entries_Nodes}"
+ ValueMemberBinding="{CompiledBinding Name, DataType=workshop:ISearchEntries_SearchEntries}"
AsyncPopulator="{CompiledBinding SearchAsync}"
SelectedItem="{CompiledBinding SelectedEntry}"
FilterMode="None"
windowing:AppWindow.AllowInteractionInTitleBar="True">
-
+
diff --git a/src/Artemis.UI/Screens/Workshop/Search/SearchViewModel.cs b/src/Artemis.UI/Screens/Workshop/Search/SearchViewModel.cs
index 9d66eb0ba..b3370ee8b 100644
--- a/src/Artemis.UI/Screens/Workshop/Search/SearchViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Search/SearchViewModel.cs
@@ -18,7 +18,7 @@ public class SearchViewModel : ViewModelBase
private readonly IRouter _router;
private readonly IWorkshopClient _workshopClient;
private EntryType? _entryType;
- private ISearchEntries_Entries_Nodes? _selectedEntry;
+ private ISearchEntries_SearchEntries? _selectedEntry;
public SearchViewModel(IWorkshopClient workshopClient, IRouter router, CurrentUserViewModel currentUserViewModel)
{
@@ -32,7 +32,7 @@ public class SearchViewModel : ViewModelBase
public Func>> SearchAsync { get; }
- public ISearchEntries_Entries_Nodes? SelectedEntry
+ public ISearchEntries_SearchEntries? SelectedEntry
{
get => _selectedEntry;
set => RaiseAndSetIfChanged(ref _selectedEntry, value);
@@ -44,7 +44,7 @@ public class SearchViewModel : ViewModelBase
set => RaiseAndSetIfChanged(ref _entryType, value);
}
- private void NavigateToEntry(ISearchEntries_Entries_Nodes entry)
+ private void NavigateToEntry(ISearchEntries_SearchEntries entry)
{
string? url = null;
if (entry.EntryType == WebClient.Workshop.EntryType.Profile)
@@ -60,21 +60,8 @@ public class SearchViewModel : ViewModelBase
{
if (string.IsNullOrWhiteSpace(input))
return new List