Browse Source

Added avatar toggle

Krzysztof Krysiński 4 months ago
parent
commit
290089a993

+ 3 - 0
src/PixiEditor.UI.Common/Fonts/PixiPerfectIcons.axaml

@@ -159,6 +159,9 @@
             <system:String x:Key="icon-terminal">&#xE99b;</system:String>
             <system:String x:Key="icon-cone">&#xE99c;</system:String>
             <system:String x:Key="icon-camera">&#xE99d;</system:String>
+            <system:String x:Key="icon-chart-spline">&#xE99e;</system:String>
+            <system:String x:Key="icon-logout">&#xE99f;</system:String>
+            <system:String x:Key="icon-user">&#xE9A0;</system:String>
 
         </ResourceDictionary>
     </Styles.Resources>

+ 2 - 0
src/PixiEditor.UI.Common/Fonts/PixiPerfectIcons.axaml.cs

@@ -154,6 +154,8 @@ public static class PixiPerfectIcons
     public const string Cone = "\uE99c";
     public const string Camera = "\uE99d";
     public const string ChartSpline = "\ue99e";
+    public const string Logout = "\ue99f";
+    public const string User = "\ue9a0";
 
     public static Stream GetFontStream()
     {

BIN
src/PixiEditor.UI.Common/Fonts/PixiPerfect.ttf → src/PixiEditor.UI.Common/Fonts/pixiperfect.ttf


+ 5 - 1
src/PixiEditor/Data/Localization/Languages/en.json

@@ -1033,5 +1033,9 @@
   "TOO_MANY_REQUESTS": "Too many requests. Try again in {0} seconds.",
   "SESSION_EXPIRED": "Session expired. Please log in again.",
   "CONNECTION_ERROR": "Connection error. Please check your internet connection.",
-  "FAIL_LOAD_USER_DATA": "Failed to load saved user data"
+  "FAIL_LOAD_USER_DATA": "Failed to load saved user data",
+  "LOGOUT": "Logout",
+  "LOGIN": "Login",
+  "LOGGED_IN_AS": "Logged in as",
+  "AVATAR_INFO": "Avatar taken from Gravatar, click to go to gravatar.com"
 }

+ 9 - 1
src/PixiEditor/ViewModels/Menu/MenuBarViewModel.cs

@@ -24,6 +24,7 @@ internal class MenuBarViewModel : PixiObservableObject
 {
     private AdditionalContentViewModel additionalContentViewModel;
     private UpdateViewModel updateViewModel;
+    private UserViewModel userViewModel;
 
     public AdditionalContentViewModel AdditionalContentSubViewModel
     {
@@ -37,6 +38,12 @@ internal class MenuBarViewModel : PixiObservableObject
         set => SetProperty(ref updateViewModel, value);
     }
 
+    public UserViewModel UserViewModel
+    {
+        get => userViewModel;
+        set => SetProperty(ref userViewModel, value);
+    }
+
     public ObservableCollection<MenuItem>? MenuEntries { get; set; }
     public NativeMenu? NativeMenu { get; private set; }
 
@@ -55,10 +62,11 @@ internal class MenuBarViewModel : PixiObservableObject
         { "DEBUG", 1000 },
     };
 
-    public MenuBarViewModel(AdditionalContentViewModel? additionalContentSubViewModel, UpdateViewModel? updateViewModel)
+    public MenuBarViewModel(AdditionalContentViewModel? additionalContentSubViewModel, UpdateViewModel? updateViewModel, UserViewModel? userViewModel)
     {
         AdditionalContentSubViewModel = additionalContentSubViewModel;
         UpdateViewModel = updateViewModel;
+        UserViewModel = userViewModel;
     }
 
     public void Init(IServiceProvider serviceProvider, CommandController controller)

+ 12 - 0
src/PixiEditor/ViewModels/SubViewModels/UserViewModel.cs

@@ -48,6 +48,9 @@ internal class UserViewModel : SubViewModel<ViewModelMain>
         }
     }
 
+    public string? UserGravatarUrl =>
+        User?.Email != null ? $"https://www.gravatar.com/avatar/{GetEmailHash()}?s=100&d=initials" : null;
+
     public UserViewModel(ViewModelMain owner) : base(owner)
     {
         RequestLoginCommand = new AsyncRelayCommand<string>(RequestLogin);
@@ -315,6 +318,14 @@ internal class UserViewModel : SubViewModel<ViewModelMain>
         }
     }
 
+    private string GetEmailHash()
+    {
+        using var sha256 = System.Security.Cryptography.SHA256.Create();
+        byte[] inputBytes = System.Text.Encoding.UTF8.GetBytes(User?.Email.ToLower() ?? string.Empty);
+        byte[] hashBytes = sha256.ComputeHash(inputBytes);
+        return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
+    }
+
     private void NotifyProperties()
     {
         OnPropertyChanged(nameof(User));
@@ -324,6 +335,7 @@ internal class UserViewModel : SubViewModel<ViewModelMain>
         OnPropertyChanged(nameof(LastError));
         OnPropertyChanged(nameof(TimeToEndTimeout));
         OnPropertyChanged(nameof(TimeToEndTimeoutString));
+        OnPropertyChanged(nameof(UserGravatarUrl));
         ResendActivationCommand.NotifyCanExecuteChanged();
     }
 }

+ 2 - 2
src/PixiEditor/ViewModels/ViewModelMain.cs

@@ -147,8 +147,9 @@ internal partial class ViewModelMain : ViewModelBase, ICommandsHandler
         StylusSubViewModel = services.GetService<StylusViewModel>();
         RegistrySubViewModel = services.GetService<RegistryViewModel>();
 
+        UserViewModel = services.GetRequiredService<UserViewModel>();
         AdditionalContentSubViewModel = services.GetService<AdditionalContentViewModel>();
-        MenuBarViewModel = new MenuBarViewModel(AdditionalContentSubViewModel, UpdateSubViewModel);
+        MenuBarViewModel = new MenuBarViewModel(AdditionalContentSubViewModel, UpdateSubViewModel, UserViewModel);
 
         CommandController.Init(services);
         LayoutSubViewModel.LayoutManager.InitLayout(this);
@@ -168,7 +169,6 @@ internal partial class ViewModelMain : ViewModelBase, ICommandsHandler
 
         AutosaveViewModel = services.GetService<AutosaveViewModel>();
 
-        UserViewModel = services.GetRequiredService<UserViewModel>();
 
         ExtensionsSubViewModel = services.GetService<ExtensionsViewModel>(); // Must be last
 

+ 55 - 0
src/PixiEditor/Views/Auth/UserAvatarToggle.axaml

@@ -0,0 +1,55 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+             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:subViewModels="clr-namespace:PixiEditor.ViewModels.SubViewModels"
+             xmlns:pixiAuth="clr-namespace:PixiEditor.PixiAuth;assembly=PixiEditor.PixiAuth"
+             xmlns:asyncImageLoader="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia"
+             xmlns:ui="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
+             xmlns:xaml="clr-namespace:PixiEditor.Models.Commands.XAML"
+             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+             x:ClassModifier="internal"
+             x:Class="PixiEditor.Views.Auth.UserAvatarToggle">
+    <Design.DataContext>
+        <subViewModels:UserViewModel />
+    </Design.DataContext>
+
+    <Grid>
+        <Border ClipToBounds="True" IsVisible="{Binding IsLoggedIn}" CornerRadius="25">
+            <Button Name="UserAvatarButton" Padding="0"
+                    BorderThickness="0" Classes="pixi-icon">
+                <Button.Content>
+                    <Image asyncImageLoader:ImageLoader.Source="{Binding UserGravatarUrl}" />
+                </Button.Content>
+                <Button.Styles>
+                    <Style Selector="FlyoutPresenter.arrow">
+                        <Setter Property="Cursor" Value="Arrow" />
+                    </Style>
+                </Button.Styles>
+                <Button.Flyout>
+                    <Flyout>
+                        <StackPanel IsVisible="{Binding IsLoggedIn}" Margin="5" Spacing="5" Orientation="Vertical">
+                            <Border ClipToBounds="True" Width="100" Height="100" CornerRadius="100">
+                                <HyperlinkButton NavigateUri="https://gravatar.com/connect" Cursor="Hand" ui:Translator.TooltipKey="AVATAR_INFO">
+                                    <Image asyncImageLoader:ImageLoader.Source="{Binding UserGravatarUrl}" />
+                                </HyperlinkButton>
+                            </Border>
+                            <TextBlock ui:Translator.Key="LOGGED_IN_AS">
+                                <Run Text=" " />
+                                <Run Text="{Binding User.Email}" />
+                            </TextBlock>
+                            <Button HorizontalAlignment="Right" ui:Translator.TooltipKey="LOGOUT" Classes="pixi-icon"
+                                    Content="{DynamicResource icon-logout}"
+                                    Foreground="{DynamicResource ErrorOnDarkBrush}"
+                                    Command="{Binding LogoutCommand}" />
+                        </StackPanel>
+                    </Flyout>
+                </Button.Flyout>
+            </Button>
+        </Border>
+        <Button IsVisible="{Binding !IsLoggedIn}"
+                Classes="pixi-icon"
+                Content="{DynamicResource icon-user}"
+                Command="{xaml:Command Name=PixiEditor.Window.OpenLoginWindow}" />
+    </Grid>
+</UserControl>

+ 16 - 0
src/PixiEditor/Views/Auth/UserAvatarToggle.axaml.cs

@@ -0,0 +1,16 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using PixiEditor.PixiAuth;
+using PixiEditor.ViewModels.SubViewModels;
+
+namespace PixiEditor.Views.Auth;
+
+internal partial class UserAvatarToggle : UserControl
+{
+    public UserAvatarToggle()
+    {
+        InitializeComponent();
+    }
+}
+

+ 13 - 8
src/PixiEditor/Views/Main/MainTitleBar.axaml

@@ -11,6 +11,7 @@
              xmlns:menu="clr-namespace:PixiEditor.ViewModels.Menu"
              xmlns:main="clr-namespace:PixiEditor.Views.Main"
              xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
+             xmlns:auth="clr-namespace:PixiEditor.Views.Auth"
              mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
              x:DataType="menu:MenuBarViewModel"
              x:Class="PixiEditor.Views.Main.MainTitleBar">
@@ -21,12 +22,15 @@
         <dialogs:DialogTitleBar
             DockPanel.Dock="Top">
             <dialogs:DialogTitleBar.AdditionalElement>
-                <Border BorderThickness="1" BorderBrush="{DynamicResource AccentColor}"
-                        Padding="5 0" CornerRadius="5" Height="25"
-                        IsVisible="{Binding Path=AdditionalContentSubViewModel.IsSupporterPackAvailable, FallbackValue=False}">
-                    <TextBlock VerticalAlignment="Center"
-                               ui:Translator.Key="PixiEditor.SupporterPack:AWESOME_SUPPORTER" />
-                </Border>
+                <StackPanel Margin="5 0" Spacing="5" Orientation="Horizontal" DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType=main:MainTitleBar}}">
+                    <Border BorderThickness="1" BorderBrush="{DynamicResource AccentColor}"
+                            Padding="5 0" CornerRadius="5" Height="25"
+                            IsVisible="{Binding Path=AdditionalContentSubViewModel.IsSupporterPackAvailable, FallbackValue=False}">
+                        <TextBlock VerticalAlignment="Center"
+                                   ui:Translator.Key="PixiEditor.SupporterPack:AWESOME_SUPPORTER" />
+                    </Border>
+                    <auth:UserAvatarToggle Width="26" Height="26" DataContext="{Binding Path=UserViewModel}" />
+                </StackPanel>
             </dialogs:DialogTitleBar.AdditionalElement>
         </dialogs:DialogTitleBar>
         <Panel DockPanel.Dock="Left"
@@ -84,7 +88,7 @@
                            Foreground="Yellow" FontSize="18" />
                 <TextBlock Text="{DynamicResource icon-settings}"
                            ClipToBounds="False"
-                            Width="16" Height="16"
+                           Width="16" Height="16"
                            IsVisible="{Binding UpdateViewModel.IsDownloading}"
                            Classes="pixi-icon" Foreground="DarkGray" FontSize="16">
                     <TextBlock.Styles>
@@ -104,7 +108,8 @@
                 </TextBlock>
                 <Ellipse
                     ClipToBounds="False"
-                    Fill="{DynamicResource ThemeAccent3Brush}" IsVisible="{Binding UpdateViewModel.UpdateReadyToInstall}"
+                    Fill="{DynamicResource ThemeAccent3Brush}"
+                    IsVisible="{Binding UpdateViewModel.UpdateReadyToInstall}"
                     Width="8" Height="8" />
             </Panel>
         </Panel>