render the avatar upload status
All checks were successful
Dotnet build and test / build (pull_request) Successful in 8m18s
All checks were successful
Dotnet build and test / build (pull_request) Successful in 8m18s
This commit is contained in:
parent
343538a7ea
commit
2c6ab134f5
3 changed files with 82 additions and 4 deletions
|
|
@ -357,7 +357,7 @@ public class YavscApiClient : IYavscApiClient, IAsyncDisposable
|
||||||
/// single multipart file named <c>file</c> and validates the image
|
/// single multipart file named <c>file</c> and validates the image
|
||||||
/// content type before persisting it.
|
/// content type before persisting it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task SetAvatarAsync(
|
public async Task<string> SetAvatarAsync(
|
||||||
Stream imageStream,
|
Stream imageStream,
|
||||||
string fileName,
|
string fileName,
|
||||||
string? contentType = null,
|
string? contentType = null,
|
||||||
|
|
@ -400,6 +400,27 @@ public class YavscApiClient : IYavscApiClient, IAsyncDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
await EnsureSuccessOrThrowAsync(response, ct).ConfigureAwait(false);
|
await EnsureSuccessOrThrowAsync(response, ct).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var payload = await response.Content.ReadAsStringAsync(ct).ConfigureAwait(false);
|
||||||
|
if (string.IsNullOrWhiteSpace(payload))
|
||||||
|
return "Avatar mis à jour.";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var json = JsonDocument.Parse(payload);
|
||||||
|
if (json.RootElement.TryGetProperty("message", out var msgEl))
|
||||||
|
{
|
||||||
|
var message = msgEl.GetString();
|
||||||
|
if (!string.IsNullOrWhiteSpace(message))
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (JsonException)
|
||||||
|
{
|
||||||
|
// Keep a user-friendly fallback when the API payload is not JSON.
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Avatar mis à jour.";
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LogoutAsync()
|
public async Task LogoutAsync()
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,18 @@
|
||||||
<TextBlock Grid.Row="10" Text="Dark mode"/>
|
<TextBlock Grid.Row="10" Text="Dark mode"/>
|
||||||
<CheckBox Grid.Row="11" x:Name="DarkModeCheckBox" IsChecked="{Binding DarkMode, Mode=TwoWay}"/>
|
<CheckBox Grid.Row="11" x:Name="DarkModeCheckBox" IsChecked="{Binding DarkMode, Mode=TwoWay}"/>
|
||||||
|
|
||||||
<StackPanel Grid.Row="12" Orientation="Horizontal" HorizontalAlignment="Right">
|
<StackPanel Grid.Row="12" Spacing="8">
|
||||||
<Button Content="Choisir l'avatar"
|
<TextBlock Text="Avatar: PNG, JPG/JPEG, WEBP, GIF. Taille max 2 MB."
|
||||||
|
Opacity="0.8"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
|
<TextBlock x:Name="AvatarUploadStatusText"
|
||||||
|
Text=""
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||||
|
<Button x:Name="ChooseAvatarButton"
|
||||||
|
Content="Choisir l'avatar"
|
||||||
Click="ChooseAvatar_Click"
|
Click="ChooseAvatar_Click"
|
||||||
Margin="0,0,8,0"/>
|
Margin="0,0,8,0"/>
|
||||||
|
|
||||||
|
|
@ -62,6 +72,7 @@
|
||||||
<Button Content="Sauver"
|
<Button Content="Sauver"
|
||||||
Command="{Binding Save}"
|
Command="{Binding Save}"
|
||||||
IsEnabled="{Binding IsDirty}"/>
|
IsEnabled="{Binding IsDirty}"/>
|
||||||
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Media;
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using PostIt.Services;
|
using PostIt.Services;
|
||||||
|
|
@ -11,6 +14,11 @@ using PostIt.Services;
|
||||||
namespace PostIt.Views;
|
namespace PostIt.Views;
|
||||||
public partial class SettingsPage: ContentPage
|
public partial class SettingsPage: ContentPage
|
||||||
{
|
{
|
||||||
|
private const int MaxAvatarSizeMegabytes = 2;
|
||||||
|
private const string AcceptedAvatarFormats = "PNG, JPG/JPEG, WEBP, GIF";
|
||||||
|
|
||||||
|
private int _avatarStatusVersion;
|
||||||
|
|
||||||
public SettingsPage()
|
public SettingsPage()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
@ -18,9 +26,15 @@ public partial class SettingsPage: ContentPage
|
||||||
|
|
||||||
private async void ChooseAvatar_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
private async void ChooseAvatar_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
var statusVersion = Interlocked.Increment(ref _avatarStatusVersion);
|
||||||
|
ChooseAvatarButton.IsEnabled = false;
|
||||||
|
SetAvatarStatus("Sélection d'un fichier avatar...", Brushes.Gray);
|
||||||
|
|
||||||
var topLevel = TopLevel.GetTopLevel(this);
|
var topLevel = TopLevel.GetTopLevel(this);
|
||||||
if (topLevel is null)
|
if (topLevel is null)
|
||||||
{
|
{
|
||||||
|
SetAvatarStatus("Impossible d'accéder à la fenêtre active.", Brushes.IndianRed);
|
||||||
|
ChooseAvatarButton.IsEnabled = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,19 +54,51 @@ public partial class SettingsPage: ContentPage
|
||||||
var file = files.FirstOrDefault();
|
var file = files.FirstOrDefault();
|
||||||
if (file is null)
|
if (file is null)
|
||||||
{
|
{
|
||||||
|
SetAvatarStatus("Upload annulé.", Brushes.Gray);
|
||||||
|
ChooseAvatarButton.IsEnabled = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
SetAvatarStatus("Upload avatar en cours...", Brushes.Gray);
|
||||||
|
|
||||||
await using var stream = await file.OpenReadAsync();
|
await using var stream = await file.OpenReadAsync();
|
||||||
var api = ((App)App.Current!).ServiceProvider!.GetRequiredService<YavscApiClient>();
|
var api = ((App)App.Current!).ServiceProvider!.GetRequiredService<YavscApiClient>();
|
||||||
await api.SetAvatarAsync(stream, file.Name, GetMimeType(file.Name));
|
var message = await api.SetAvatarAsync(stream, file.Name, GetMimeType(file.Name));
|
||||||
|
SetAvatarStatus(string.IsNullOrWhiteSpace(message)
|
||||||
|
? "Avatar mis à jour."
|
||||||
|
: message, Brushes.ForestGreen);
|
||||||
|
|
||||||
|
_ = ClearSuccessStatusLaterAsync(statusVersion);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
SetAvatarStatus($"Échec upload avatar: {ex.Message}", Brushes.IndianRed);
|
||||||
Console.Error.WriteLine($"🩎 Avatar upload failed: {ex.Message}");
|
Console.Error.WriteLine($"🩎 Avatar upload failed: {ex.Message}");
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ChooseAvatarButton.IsEnabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ClearSuccessStatusLaterAsync(int statusVersion)
|
||||||
|
{
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(true);
|
||||||
|
if (statusVersion != _avatarStatusVersion)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (AvatarUploadStatusText.Foreground == Brushes.ForestGreen)
|
||||||
|
{
|
||||||
|
SetAvatarStatus($"Avatar prêt. Formats supportés: {AcceptedAvatarFormats}. Taille max: {MaxAvatarSizeMegabytes} MB.", Brushes.Gray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetAvatarStatus(string message, IBrush color)
|
||||||
|
{
|
||||||
|
AvatarUploadStatusText.Foreground = color;
|
||||||
|
AvatarUploadStatusText.Text = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetMimeType(string fileName)
|
private static string GetMimeType(string fileName)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue