mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-12 21:38:38 +00:00
Workshop - Fixed current user avatar not displaying
Workshop - Improve error handling while uploading new entries
This commit is contained in:
parent
c29d30d222
commit
17bd62e673
File diff suppressed because one or more lines are too long
1
src/Artemis.UI/Assets/Animations/upload.json
Normal file
1
src/Artemis.UI/Assets/Animations/upload.json
Normal file
File diff suppressed because one or more lines are too long
@ -5,6 +5,7 @@
|
|||||||
xmlns:currentUser="clr-namespace:Artemis.UI.Screens.Workshop.CurrentUser"
|
xmlns:currentUser="clr-namespace:Artemis.UI.Screens.Workshop.CurrentUser"
|
||||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
xmlns:asyncImageLoader="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.Workshop.CurrentUser.CurrentUserView"
|
x:Class="Artemis.UI.Screens.Workshop.CurrentUser.CurrentUserView"
|
||||||
x:DataType="currentUser:CurrentUserViewModel">
|
x:DataType="currentUser:CurrentUserViewModel">
|
||||||
@ -27,13 +28,16 @@
|
|||||||
</Ellipse>
|
</Ellipse>
|
||||||
|
|
||||||
<!-- Signed in -->
|
<!-- Signed in -->
|
||||||
<Ellipse Height="{CompiledBinding Bounds.Height, ElementName=Container}" Width="{CompiledBinding Bounds.Height, ElementName=Container}" IsVisible="{CompiledBinding !IsAnonymous}" Name="UserMenu">
|
<Ellipse Height="{CompiledBinding Bounds.Height, ElementName=Container}"
|
||||||
|
Width="{CompiledBinding Bounds.Height, ElementName=Container}"
|
||||||
|
IsVisible="{CompiledBinding !IsAnonymous}"
|
||||||
|
Name="UserMenu">
|
||||||
<Ellipse.ContextFlyout>
|
<Ellipse.ContextFlyout>
|
||||||
<Flyout>
|
<Flyout>
|
||||||
<Grid ColumnDefinitions="Auto,*" RowDefinitions="*,*,*" MinWidth="300">
|
<Grid ColumnDefinitions="Auto,*" RowDefinitions="*,*,*" MinWidth="300">
|
||||||
<Ellipse Grid.Column="0" Grid.RowSpan="3" Height="50" Width="50" Margin="0 0 8 0" VerticalAlignment="Top">
|
<Ellipse Grid.Column="0" Grid.RowSpan="3" Height="50" Width="50" Margin="0 0 8 0" VerticalAlignment="Top">
|
||||||
<Ellipse.Fill>
|
<Ellipse.Fill>
|
||||||
<ImageBrush Source="{CompiledBinding Avatar}" />
|
<ImageBrush asyncImageLoader:ImageBrushLoader.Source="{CompiledBinding AvatarUrl}" />
|
||||||
</Ellipse.Fill>
|
</Ellipse.Fill>
|
||||||
</Ellipse>
|
</Ellipse>
|
||||||
<TextBlock Grid.Column="1" Grid.Row="0" Text="{CompiledBinding Name}" Margin="0 4 0 0"></TextBlock>
|
<TextBlock Grid.Column="1" Grid.Row="0" Text="{CompiledBinding Name}" Margin="0 4 0 0"></TextBlock>
|
||||||
@ -51,7 +55,7 @@
|
|||||||
</Flyout>
|
</Flyout>
|
||||||
</Ellipse.ContextFlyout>
|
</Ellipse.ContextFlyout>
|
||||||
<Ellipse.Fill>
|
<Ellipse.Fill>
|
||||||
<ImageBrush Source="{CompiledBinding Avatar}" />
|
<ImageBrush asyncImageLoader:ImageBrushLoader.Source="{CompiledBinding AvatarUrl}" />
|
||||||
</Ellipse.Fill>
|
</Ellipse.Fill>
|
||||||
</Ellipse>
|
</Ellipse>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|||||||
@ -21,7 +21,6 @@ public partial class CurrentUserViewModel : ActivatableViewModelBase
|
|||||||
{
|
{
|
||||||
private readonly IAuthenticationService _authenticationService;
|
private readonly IAuthenticationService _authenticationService;
|
||||||
private readonly ObservableAsPropertyHelper<bool> _isAnonymous;
|
private readonly ObservableAsPropertyHelper<bool> _isAnonymous;
|
||||||
private readonly HttpClient _httpClient;
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
[Notify] private bool _allowLogout = true;
|
[Notify] private bool _allowLogout = true;
|
||||||
@ -30,13 +29,13 @@ public partial class CurrentUserViewModel : ActivatableViewModelBase
|
|||||||
[Notify(Setter.Private)] private bool _loading = true;
|
[Notify(Setter.Private)] private bool _loading = true;
|
||||||
[Notify(Setter.Private)] private string? _name;
|
[Notify(Setter.Private)] private string? _name;
|
||||||
[Notify(Setter.Private)] private string? _userId;
|
[Notify(Setter.Private)] private string? _userId;
|
||||||
|
[Notify(Setter.Private)] private string? _avatarUrl;
|
||||||
|
|
||||||
public CurrentUserViewModel(ILogger logger, IAuthenticationService authenticationService, IWindowService windowService)
|
public CurrentUserViewModel(ILogger logger, IAuthenticationService authenticationService, IWindowService windowService)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_authenticationService = authenticationService;
|
_authenticationService = authenticationService;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
_httpClient = new HttpClient();
|
|
||||||
Login = ReactiveCommand.CreateFromTask(ExecuteLogin);
|
Login = ReactiveCommand.CreateFromTask(ExecuteLogin);
|
||||||
|
|
||||||
_isAnonymous = this.WhenAnyValue(vm => vm.Loading, vm => vm.Name, (l, n) => l || n == null).ToProperty(this, vm => vm.IsAnonymous);
|
_isAnonymous = this.WhenAnyValue(vm => vm.Loading, vm => vm.Name, (l, n) => l || n == null).ToProperty(this, vm => vm.IsAnonymous);
|
||||||
@ -44,7 +43,7 @@ public partial class CurrentUserViewModel : ActivatableViewModelBase
|
|||||||
this.WhenActivated(d =>
|
this.WhenActivated(d =>
|
||||||
{
|
{
|
||||||
Task.Run(AutoLogin);
|
Task.Run(AutoLogin);
|
||||||
_authenticationService.IsLoggedIn.Subscribe(_ => Task.Run(LoadCurrentUser)).DisposeWith(d);
|
_authenticationService.IsLoggedIn.Subscribe(_ => LoadCurrentUser()).DisposeWith(d);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +65,7 @@ public partial class CurrentUserViewModel : ActivatableViewModelBase
|
|||||||
.ShowAsync();
|
.ShowAsync();
|
||||||
|
|
||||||
if (result == ContentDialogResult.Primary)
|
if (result == ContentDialogResult.Primary)
|
||||||
await LoadCurrentUser();
|
LoadCurrentUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AutoLogin()
|
private async Task AutoLogin()
|
||||||
@ -74,7 +73,7 @@ public partial class CurrentUserViewModel : ActivatableViewModelBase
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _authenticationService.AutoLogin();
|
await _authenticationService.AutoLogin();
|
||||||
await LoadCurrentUser();
|
LoadCurrentUser();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -86,33 +85,11 @@ public partial class CurrentUserViewModel : ActivatableViewModelBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadCurrentUser()
|
private void LoadCurrentUser()
|
||||||
{
|
{
|
||||||
UserId = _authenticationService.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;
|
UserId = _authenticationService.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;
|
||||||
Name = _authenticationService.Claims.FirstOrDefault(c => c.Type == "name")?.Value;
|
Name = _authenticationService.Claims.FirstOrDefault(c => c.Type == "name")?.Value;
|
||||||
Email = _authenticationService.Claims.FirstOrDefault(c => c.Type == "email")?.Value;
|
Email = _authenticationService.Claims.FirstOrDefault(c => c.Type == "email")?.Value;
|
||||||
|
AvatarUrl = $"{WorkshopConstants.AUTHORITY_URL}/user/avatar/{UserId}";
|
||||||
if (UserId != null)
|
|
||||||
{
|
|
||||||
await LoadAvatar(UserId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Avatar?.Dispose();
|
|
||||||
Avatar = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LoadAvatar(string userId)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Avatar?.Dispose();
|
|
||||||
Avatar = new Bitmap(await _httpClient.GetStreamAsync($"{WorkshopConstants.AUTHORITY_URL}/user/avatar/{userId}"));
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -17,21 +17,21 @@
|
|||||||
</StackPanel.Styles>
|
</StackPanel.Styles>
|
||||||
|
|
||||||
<StackPanel IsVisible="{CompiledBinding !Finished}">
|
<StackPanel IsVisible="{CompiledBinding !Finished}">
|
||||||
<Lottie Path="/Assets/Animations/busy.json" Width="250" Height="250" Margin="0 100" ></Lottie>
|
<Lottie Path="/Assets/Animations/upload.json" Width="300" Height="300" Margin="0 75" ></Lottie>
|
||||||
<TextBlock Theme="{StaticResource TitleTextBlockStyle}">Uploading your submission...</TextBlock>
|
<TextBlock Theme="{StaticResource TitleTextBlockStyle}">Uploading your submission...</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel IsVisible="{CompiledBinding Succeeded}">
|
<StackPanel IsVisible="{CompiledBinding Succeeded}">
|
||||||
<Lottie Path="/Assets/Animations/success.json" RepeatCount="1" Width="250" Height="250" Margin="0 100"></Lottie>
|
<Lottie Path="/Assets/Animations/success.json" RepeatCount="1" Width="250" Height="250" Margin="0 100"></Lottie>
|
||||||
<TextBlock Theme="{StaticResource TitleTextBlockStyle}">All done! Hit finish to view your submission.</TextBlock>
|
<TextBlock Theme="{StaticResource TitleTextBlockStyle}">All done! Hit finish to view your submission.</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel IsVisible="{CompiledBinding Failed}">
|
<StackPanel IsVisible="{CompiledBinding Failed}">
|
||||||
<TextBlock FontSize="140" Margin="0 100">😢</TextBlock>
|
<TextBlock FontSize="140" Margin="0 100">😢</TextBlock>
|
||||||
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">
|
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">
|
||||||
Unfortunately something went wrong while uploading your submission.
|
Unfortunately something went wrong while uploading your submission.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock>Hit finish to view your submission, from there you can try to upload a new release.</TextBlock>
|
<TextBlock Text="{CompiledBinding FailureMessage}"></TextBlock>
|
||||||
<TextBlock Margin="0 10" Classes="subtitle">If this keeps occuring, hit us up on Discord</TextBlock>
|
<TextBlock Margin="0 10" Classes="subtitle">If this keeps occuring, hit us up on Discord</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@ using System.Reactive.Disposables;
|
|||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Artemis.Core;
|
||||||
using Artemis.UI.Shared.Routing;
|
using Artemis.UI.Shared.Routing;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
using Artemis.UI.Shared.Utilities;
|
using Artemis.UI.Shared.Utilities;
|
||||||
@ -31,6 +32,7 @@ public partial class UploadStepViewModel : SubmissionViewModel
|
|||||||
[Notify] private bool _failed;
|
[Notify] private bool _failed;
|
||||||
[Notify] private bool _finished;
|
[Notify] private bool _finished;
|
||||||
[Notify] private bool _succeeded;
|
[Notify] private bool _succeeded;
|
||||||
|
[Notify] private string? _failureMessage;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public UploadStepViewModel(ILogger logger,
|
public UploadStepViewModel(ILogger logger,
|
||||||
@ -56,37 +58,33 @@ public partial class UploadStepViewModel : SubmissionViewModel
|
|||||||
|
|
||||||
private async Task ExecuteUpload(CancellationToken cancellationToken)
|
private async Task ExecuteUpload(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Use the existing entry or create a new one
|
|
||||||
_entryId = State.EntryId ?? await CreateEntry(cancellationToken);
|
|
||||||
|
|
||||||
// If a new entry had to be created but that failed, stop here, CreateEntry will send the user back
|
|
||||||
if (_entryId == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Use the existing entry or create a new one
|
||||||
|
_entryId = State.EntryId ?? await CreateEntry(cancellationToken);
|
||||||
|
if (_entryId == null)
|
||||||
|
{
|
||||||
|
Failed = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a release for the new entry
|
||||||
IEntryUploadHandler uploadHandler = _entryUploadHandlerFactory.CreateHandler(State.EntryType);
|
IEntryUploadHandler uploadHandler = _entryUploadHandlerFactory.CreateHandler(State.EntryType);
|
||||||
EntryUploadResult uploadResult = await uploadHandler.CreateReleaseAsync(_entryId.Value, State.EntrySource!, cancellationToken);
|
EntryUploadResult uploadResult = await uploadHandler.CreateReleaseAsync(_entryId.Value, State.EntrySource!, cancellationToken);
|
||||||
if (!uploadResult.IsSuccess)
|
if (!uploadResult.IsSuccess)
|
||||||
{
|
throw new ArtemisWorkshopException(uploadResult.Message);
|
||||||
string? message = uploadResult.Message;
|
|
||||||
if (message != null)
|
|
||||||
message += "\r\n\r\n";
|
|
||||||
else
|
|
||||||
message = "";
|
|
||||||
message += "Your submission has still been saved, you may try to upload a new release";
|
|
||||||
await _windowService.ShowConfirmContentDialog("Failed to upload workshop entry", message, "Close", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
Succeeded = true;
|
Succeeded = true;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.Error(e, "Failed to upload submission for entry {EntryId}", _entryId);
|
_logger.Error(e, "Failed to upload submission for entry {EntryId}", _entryId);
|
||||||
|
FailureMessage = e.Message;
|
||||||
// Something went wrong when creating a release :c
|
|
||||||
// We'll keep the workshop entry so that the user can make changes and try again
|
|
||||||
Failed = true;
|
Failed = true;
|
||||||
|
|
||||||
|
// If something went wrong halfway through, delete the entry
|
||||||
|
if (_entryId != null)
|
||||||
|
await _workshopClient.RemoveEntry.ExecuteAsync(_entryId.Value, CancellationToken.None);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -94,10 +92,12 @@ public partial class UploadStepViewModel : SubmissionViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<long?> CreateEntry(CancellationToken cancellationToken)
|
private async Task<long> CreateEntry(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
await Task.Delay(2000);
|
// Let the UI settle before making the thread busy
|
||||||
|
await Task.Delay(500, cancellationToken);
|
||||||
|
|
||||||
|
// Create entry
|
||||||
IOperationResult<IAddEntryResult> result = await _workshopClient.AddEntry.ExecuteAsync(new CreateEntryInput
|
IOperationResult<IAddEntryResult> result = await _workshopClient.AddEntry.ExecuteAsync(new CreateEntryInput
|
||||||
{
|
{
|
||||||
EntryType = State.EntryType,
|
EntryType = State.EntryType,
|
||||||
@ -108,18 +108,16 @@ public partial class UploadStepViewModel : SubmissionViewModel
|
|||||||
Tags = State.Tags
|
Tags = State.Tags
|
||||||
}, cancellationToken);
|
}, cancellationToken);
|
||||||
|
|
||||||
long? entryId = result.Data?.AddEntry?.Id;
|
result.EnsureNoErrors();
|
||||||
if (result.IsErrorResult() || entryId == null)
|
if (result.Data?.AddEntry == null)
|
||||||
{
|
throw new ArtemisWorkshopException("AddEntry returned result");
|
||||||
await _windowService.ShowConfirmContentDialog("Failed to create workshop entry", string.Join("\r\n", result.Errors.Select(e => e.Message)), "Close", null);
|
long entryId = result.Data.AddEntry.Id;
|
||||||
State.ChangeScreen<SubmitStepViewModel>();
|
|
||||||
return null;
|
// Upload images
|
||||||
}
|
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
foreach (ImageUploadRequest image in State.Images.ToList())
|
foreach (ImageUploadRequest image in State.Images.ToList())
|
||||||
{
|
{
|
||||||
await TryImageUpload(async () => await _workshopService.UploadEntryImage(entryId.Value, image, cancellationToken));
|
await TryImageUpload(async () => await _workshopService.UploadEntryImage(entryId, image, cancellationToken));
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,8 +125,7 @@ public partial class UploadStepViewModel : SubmissionViewModel
|
|||||||
return entryId;
|
return entryId;
|
||||||
|
|
||||||
// Upload icon
|
// Upload icon
|
||||||
await TryImageUpload(async () => await _workshopService.SetEntryIcon(entryId.Value, State.Icon, cancellationToken));
|
await TryImageUpload(async () => await _workshopService.SetEntryIcon(entryId, State.Icon, cancellationToken));
|
||||||
|
|
||||||
return entryId;
|
return entryId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +148,7 @@ public partial class UploadStepViewModel : SubmissionViewModel
|
|||||||
{
|
{
|
||||||
State.Close();
|
State.Close();
|
||||||
|
|
||||||
if (_entryId != null)
|
if (Succeeded && _entryId != null)
|
||||||
await _router.Navigate($"workshop/library/submissions/{_entryId.Value}");
|
await _router.Navigate($"workshop/library/submissions/{_entryId.Value}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,6 +45,9 @@
|
|||||||
<GraphQL Update="Mutations\UpdateEntryImage.graphql">
|
<GraphQL Update="Mutations\UpdateEntryImage.graphql">
|
||||||
<Generator>MSBuild:GenerateGraphQLCode</Generator>
|
<Generator>MSBuild:GenerateGraphQLCode</Generator>
|
||||||
</GraphQL>
|
</GraphQL>
|
||||||
|
<GraphQL Update="Mutations\CreateEntry.graphql">
|
||||||
|
<Generator>MSBuild:GenerateGraphQLCode</Generator>
|
||||||
|
</GraphQL>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user