diff --git a/src/Artemis.UI/Screens/Workshop/CurrentUser/CurrentUserViewModel.cs b/src/Artemis.UI/Screens/Workshop/CurrentUser/CurrentUserViewModel.cs
index 388ebe130..7b652d289 100644
--- a/src/Artemis.UI/Screens/Workshop/CurrentUser/CurrentUserViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/CurrentUser/CurrentUserViewModel.cs
@@ -34,8 +34,11 @@ public class CurrentUserViewModel : ActivatableViewModelBase
_windowService = windowService;
Login = ReactiveCommand.CreateFromTask(ExecuteLogin);
- this.WhenActivated(d => ReactiveCommand.CreateFromTask(ExecuteAutoLogin).Execute().Subscribe().DisposeWith(d));
- this.WhenActivated(d => _authenticationService.IsLoggedIn.Subscribe(_ => LoadCurrentUser().DisposeWith(d)));
+ this.WhenActivated(d =>
+ {
+ Task.Run(AutoLogin);
+ _authenticationService.IsLoggedIn.Subscribe(_ => Task.Run(LoadCurrentUser)).DisposeWith(d);
+ });
}
public bool Loading
@@ -86,7 +89,7 @@ public class CurrentUserViewModel : ActivatableViewModelBase
await LoadCurrentUser();
}
- private async Task ExecuteAutoLogin(CancellationToken cancellationToken)
+ private async Task AutoLogin()
{
try
{
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/WelcomeStepView.axaml b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/WelcomeStepView.axaml
index 7341a2307..6e273c4af 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/WelcomeStepView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/WelcomeStepView.axaml
@@ -2,10 +2,51 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:steps="clr-namespace:Artemis.UI.Screens.Workshop.SubmissionWizard.Steps"
+ xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
- x:Class="Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.WelcomeStepView">
+ x:Class="Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.WelcomeStepView"
+ x:DataType="steps:WelcomeStepViewModel">
- Welcome to the Workshop Submission Wizard 🧙
+ Welcome to the Workshop Submission Wizard 🧙
Here we'll take you, step by step, through the process of uploading your submission to the workshop.
+
+
+ First things first
+
+ In order to submit anything to the workshop you must be logged in.
+
+
+ Creating an account is free and we'll not bother you with a newsletter or crap like that. You can also log in with Google or Discord.
+
+
+
+
+
+
+ Confirm email address
+
+ Before you can continue, please confirm your email address. ()
+
+ You'll find the confirmation mail in your inbox.
+
+
+ Don't see an email? Check your spam box!
+
+
+
+
+ PS: We take this step to avoid the workshop getting flooded with bogus content.
+
-
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/WelcomeStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/WelcomeStepViewModel.cs
index cf70f6410..47d59e6e0 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/WelcomeStepViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/WelcomeStepViewModel.cs
@@ -1,11 +1,51 @@
+using System;
using System.Reactive;
+using System.Reactive.Disposables;
+using System.Security.Claims;
+using System.Threading;
+using System.Threading.Tasks;
+using Artemis.UI.Screens.Workshop.CurrentUser;
+using Artemis.UI.Shared.Services;
+using Artemis.WebClient.Workshop.Services;
+using IdentityModel;
using ReactiveUI;
+using Timer = System.Timers.Timer;
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
public class WelcomeStepViewModel : SubmissionViewModel
{
- #region Overrides of SubmissionViewModel
+ private readonly IAuthenticationService _authenticationService;
+ private readonly ObservableAsPropertyHelper _showMissingVerification;
+ private readonly IWindowService _windowService;
+ private ObservableAsPropertyHelper? _email;
+ private ObservableAsPropertyHelper? _emailVerified;
+ private ObservableAsPropertyHelper? _isLoggedIn;
+
+ public WelcomeStepViewModel(IAuthenticationService authenticationService, IWindowService windowService)
+ {
+ _authenticationService = authenticationService;
+ _windowService = windowService;
+ _showMissingVerification = this.WhenAnyValue(vm => vm.IsLoggedIn, vm => vm.EmailVerified, (l, v) => l && (v == null || v.Value == "false")).ToProperty(this, vm => vm.ShowMissingVerification);
+
+ ShowGoBack = false;
+ Continue = ReactiveCommand.Create(ExecuteContinue);
+ Login = ReactiveCommand.CreateFromTask(ExecuteLogin);
+ Refresh = ReactiveCommand.CreateFromTask(ExecuteRefresh);
+
+ this.WhenActivated(d =>
+ {
+ _isLoggedIn = authenticationService.IsLoggedIn.ToProperty(this, vm => vm.IsLoggedIn).DisposeWith(d);
+ _emailVerified = authenticationService.GetClaim(JwtClaimTypes.EmailVerified).ToProperty(this, vm => vm.EmailVerified).DisposeWith(d);
+ _email = authenticationService.GetClaim(JwtClaimTypes.Email).ToProperty(this, vm => vm.Email).DisposeWith(d);
+
+ Timer updateTimer = new(TimeSpan.FromSeconds(15));
+ updateTimer.Elapsed += (_, _) => Task.Run(Update);
+ updateTimer.Start();
+
+ updateTimer.DisposeWith(d);
+ });
+ }
///
public override ReactiveCommand Continue { get; }
@@ -13,10 +53,43 @@ public class WelcomeStepViewModel : SubmissionViewModel
///
public override ReactiveCommand GoBack { get; } = null!;
- public WelcomeStepViewModel()
+ public ReactiveCommand Login { get; }
+ public ReactiveCommand Refresh { get; }
+
+ public bool ShowMissingVerification => _showMissingVerification.Value;
+ public bool IsLoggedIn => _isLoggedIn?.Value ?? false;
+ public Claim? EmailVerified => _emailVerified?.Value;
+ public Claim? Email => _email?.Value;
+
+ private async Task Update()
{
- ShowGoBack = false;
+ if (EmailVerified?.Value == "true")
+ return;
+
+ try
+ {
+ // Use the refresh token to login again, updating claims
+ await _authenticationService.AutoLogin(true);
+ }
+ catch (Exception)
+ {
+ // ignored, meh
+ }
}
- #endregion
+ private void ExecuteContinue()
+ {
+ throw new NotImplementedException();
+ }
+
+ private async Task ExecuteLogin(CancellationToken ct)
+ {
+ await _windowService.CreateContentDialog().WithViewModel(out WorkshopLoginViewModel _).WithTitle("Workshop login").ShowAsync();
+ }
+
+ private async Task ExecuteRefresh(CancellationToken ct)
+ {
+ await Update();
+ await Task.Delay(1000, ct);
+ }
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/SubmissionWizardViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/SubmissionWizardViewModel.cs
index fed2c3241..857aea286 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/SubmissionWizardViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/SubmissionWizardViewModel.cs
@@ -10,9 +10,9 @@ public class SubmissionWizardViewModel : DialogViewModelBase
{
private SubmissionViewModel _screen;
- public SubmissionWizardViewModel(CurrentUserViewModel currentUserViewModel)
+ public SubmissionWizardViewModel(CurrentUserViewModel currentUserViewModel, WelcomeStepViewModel welcomeStepViewModel)
{
- _screen = new WelcomeStepViewModel();
+ _screen = welcomeStepViewModel;
CurrentUserViewModel = currentUserViewModel;
}
diff --git a/src/Artemis.WebClient.Workshop/Services/AuthenticationService.cs b/src/Artemis.WebClient.Workshop/Services/AuthenticationService.cs
index adec69370..a9a7ac379 100644
--- a/src/Artemis.WebClient.Workshop/Services/AuthenticationService.cs
+++ b/src/Artemis.WebClient.Workshop/Services/AuthenticationService.cs
@@ -8,6 +8,7 @@ using System.Security.Cryptography;
using System.Text;
using Artemis.Core;
using Artemis.WebClient.Workshop.Repositories;
+using DynamicData;
using IdentityModel;
using IdentityModel.Client;
@@ -18,7 +19,7 @@ internal class AuthenticationService : CorePropertyChanged, IAuthenticationServi
private const string CLIENT_ID = "artemis.desktop";
private readonly IAuthenticationRepository _authenticationRepository;
private readonly SemaphoreSlim _authLock = new(1);
- private readonly ObservableCollection _claims = new();
+ private readonly SourceList _claims;
private readonly IDiscoveryCache _discoveryCache;
private readonly IHttpClientFactory _httpClientFactory;
@@ -32,7 +33,9 @@ internal class AuthenticationService : CorePropertyChanged, IAuthenticationServi
_discoveryCache = discoveryCache;
_authenticationRepository = authenticationRepository;
- Claims = new ReadOnlyObservableCollection(_claims);
+ _claims = new SourceList();
+ _claims.Connect().Bind(out ReadOnlyObservableCollection claims).Subscribe();
+ Claims = claims;
}
private async Task GetDiscovery()
@@ -54,9 +57,11 @@ internal class AuthenticationService : CorePropertyChanged, IAuthenticationServi
if (token == null)
throw new ArtemisWebClientException("Failed to read JWT token");
- _claims.Clear();
- foreach (Claim responseClaim in token.Claims)
- _claims.Add(responseClaim);
+ _claims.Edit(c =>
+ {
+ c.Clear();
+ c.AddRange(token.Claims);
+ });
_isLoggedInSubject.OnNext(true);
}
@@ -96,6 +101,15 @@ internal class AuthenticationService : CorePropertyChanged, IAuthenticationServi
///
public ReadOnlyObservableCollection Claims { get; }
+ ///
+ public IObservable GetClaim(string type)
+ {
+ return _claims.Connect()
+ .Filter(c => c.Type == JwtClaimTypes.Email)
+ .ToCollection()
+ .Select(f => f.FirstOrDefault());
+ }
+
public async Task GetBearer()
{
await _authLock.WaitAsync();
@@ -122,13 +136,13 @@ internal class AuthenticationService : CorePropertyChanged, IAuthenticationServi
}
///
- public async Task AutoLogin()
+ public async Task AutoLogin(bool force = false)
{
await _authLock.WaitAsync();
try
{
- if (_isLoggedInSubject.Value)
+ if (!force && _isLoggedInSubject.Value)
return true;
string? refreshToken = _authenticationRepository.GetRefreshToken();
diff --git a/src/Artemis.WebClient.Workshop/Services/IAuthenticationService.cs b/src/Artemis.WebClient.Workshop/Services/IAuthenticationService.cs
index 16b05ed5e..c3a3686ba 100644
--- a/src/Artemis.WebClient.Workshop/Services/IAuthenticationService.cs
+++ b/src/Artemis.WebClient.Workshop/Services/IAuthenticationService.cs
@@ -9,8 +9,9 @@ public interface IAuthenticationService : IProtectedArtemisService
IObservable IsLoggedIn { get; }
ReadOnlyObservableCollection Claims { get; }
+ IObservable GetClaim(string type);
Task GetBearer();
- Task AutoLogin();
+ Task AutoLogin(bool force = false);
Task Login(CancellationToken cancellationToken);
void Logout();
}
\ No newline at end of file