Browse Source

Content UI

Krzysztof Krysiński 4 tháng trước cách đây
mục cha
commit
6eaf16df73

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

@@ -162,6 +162,7 @@
             <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>
+            <system:String x:Key="icon-checkTick">&#xE9A1;</system:String>
 
         </ResourceDictionary>
     </Styles.Resources>

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

@@ -156,6 +156,7 @@ public static class PixiPerfectIcons
     public const string ChartSpline = "\ue99e";
     public const string Logout = "\ue99f";
     public const string User = "\ue9a0";
+    public const string CheckTick = "\ue9a1";
 
     public static Stream GetFontStream()
     {

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


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

@@ -1055,6 +1055,7 @@
   "INSTALL": "Install",
   "MANAGE_ACCOUNT": "Manage",
   "OWNED_PRODUCTS": "Owned Content",
+  "INSTALLING": "Installing",
   "INSTALLED": "Installed",
   "ACCOUNT_PROVIDER_INFO": "Account handled by"
 }

+ 4 - 1
src/PixiEditor/ViewModels/SubViewModels/ExtensionsViewModel.cs

@@ -15,8 +15,11 @@ internal class ExtensionsViewModel : SubViewModel<ViewModelMain>
     public ExtensionsViewModel(ViewModelMain owner, ExtensionLoader loader) : base(owner)
     {
         ExtensionLoader = loader;
-        WindowProvider windowProvider = (WindowProvider)Owner.Services.GetService<IWindowProvider>();
+    }
 
+    public void Init()
+    {
+        WindowProvider windowProvider = (WindowProvider)Owner.Services.GetService<IWindowProvider>();
         RegisterCoreWindows(windowProvider);
         Owner.OnStartupEvent += Owner_OnStartupEvent;
     }

+ 24 - 2
src/PixiEditor/ViewModels/SubViewModels/UserViewModel.cs

@@ -12,6 +12,7 @@ using PixiEditor.PixiAuth;
 using PixiEditor.PixiAuth.Exceptions;
 using PixiEditor.PixiAuth.Utils;
 using PixiEditor.Platform;
+using PixiEditor.ViewModels.User;
 
 namespace PixiEditor.ViewModels.SubViewModels;
 
@@ -67,8 +68,8 @@ internal class UserViewModel : SubViewModel<ViewModelMain>
         }
     }
 
-    public ObservableCollection<ProductData> OwnedProducts =>
-        new(IdentityProvider?.User?.OwnedProducts ?? new List<ProductData>());
+    public ObservableCollection<OwnedProductViewModel> OwnedProducts { get; } =
+        new ObservableCollection<OwnedProductViewModel>();
 
     private string currentEmail = string.Empty;
 
@@ -88,6 +89,8 @@ internal class UserViewModel : SubViewModel<ViewModelMain>
 
     public string? AvatarUrl => IdentityProvider.User?.AvatarUrl;
 
+    public bool NonDefaultIdentityProvider => IdentityProvider is not PixiAuthIdentityProvider;
+
     public UserViewModel(ViewModelMain owner) : base(owner)
     {
         IdentityProvider = IPlatform.Current.IdentityProvider;
@@ -108,6 +111,11 @@ internal class UserViewModel : SubViewModel<ViewModelMain>
             pixiAuth.LoginTimeout += PixiAuthOnLoginTimeout;
             pixiAuth.LoggedOut += PixiAuthOnLoggedOut;
         }
+
+        if (IdentityProvider?.User != null)
+        {
+            IdentityProviderOnOwnedProductsUpdated(IdentityProvider.User.OwnedProducts);
+        }
     }
 
     private void IdentityProviderOnUsernameUpdated(string newUsername)
@@ -117,6 +125,19 @@ internal class UserViewModel : SubViewModel<ViewModelMain>
 
     private void IdentityProviderOnOwnedProductsUpdated(List<ProductData> products)
     {
+        OwnedProducts.Clear();
+        if (products == null)
+        {
+            return;
+        }
+
+        foreach (ProductData product in products)
+        {
+            bool isInstalled = IsInstalled(product.Id);
+
+            OwnedProducts.Add(new OwnedProductViewModel(product, isInstalled, InstallContentCommand, IsInstalled));
+        }
+
         NotifyProperties();
     }
 
@@ -291,6 +312,7 @@ internal class UserViewModel : SubViewModel<ViewModelMain>
             {
                 Owner.ExtensionsSubViewModel.LoadExtensionAdHoc(extensionPath);
             }
+
         }
         catch (Exception ex)
         {

+ 44 - 0
src/PixiEditor/ViewModels/User/OwnedProductViewModel.cs

@@ -0,0 +1,44 @@
+using System.Windows.Input;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using PixiEditor.IdentityProvider;
+
+namespace PixiEditor.ViewModels.User;
+
+public class OwnedProductViewModel : ObservableObject
+{
+    public ProductData ProductData { get; }
+
+    private bool isInstalled;
+
+    public bool IsInstalled
+    {
+        get => isInstalled;
+        set => SetProperty(ref isInstalled, value);
+    }
+
+    private bool isInstalling;
+
+    public bool IsInstalling
+    {
+        get => isInstalling;
+        set => SetProperty(ref isInstalling, value);
+    }
+
+    public IAsyncRelayCommand InstallCommand { get; }
+
+    public OwnedProductViewModel(ProductData productData, bool isInstalled,
+        IAsyncRelayCommand<string> installContentCommand, Func<string, bool> isInstalledFunc)
+    {
+        ProductData = productData;
+        IsInstalled = isInstalled;
+        InstallCommand = new AsyncRelayCommand(
+            async () =>
+        {
+            IsInstalling = true;
+            await installContentCommand.ExecuteAsync(ProductData.Id);
+            IsInstalling = false;
+            IsInstalled = isInstalledFunc(ProductData.Id);
+        }, () => !IsInstalled && !IsInstalling);
+    }
+}

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

@@ -147,6 +147,8 @@ internal partial class ViewModelMain : ViewModelBase, ICommandsHandler
         StylusSubViewModel = services.GetService<StylusViewModel>();
         RegistrySubViewModel = services.GetService<RegistryViewModel>();
 
+        ExtensionsSubViewModel = services.GetService<ExtensionsViewModel>();
+
         UserViewModel = services.GetRequiredService<UserViewModel>();
         AdditionalContentSubViewModel = services.GetService<AdditionalContentViewModel>();
         MenuBarViewModel = new MenuBarViewModel(AdditionalContentSubViewModel, UpdateSubViewModel, UserViewModel);
@@ -169,8 +171,7 @@ internal partial class ViewModelMain : ViewModelBase, ICommandsHandler
 
         AutosaveViewModel = services.GetService<AutosaveViewModel>();
 
-
-        ExtensionsSubViewModel = services.GetService<ExtensionsViewModel>(); // Must be last
+        ExtensionsSubViewModel.Init();  // Must be last
 
         DocumentManagerSubViewModel.ActiveDocumentChanged += OnActiveDocumentChanged;
         BeforeDocumentClosed += OnBeforeDocumentClosed;

+ 1 - 1
src/PixiEditor/Views/Auth/LoginForm.axaml

@@ -77,7 +77,7 @@
                     <Image asyncImageLoader:ImageLoader.Source="{Binding AvatarUrl}" />
                 </HyperlinkButton>
             </Border>
-            <TextBlock HorizontalAlignment="Center" Classes="subtext">
+            <TextBlock IsVisible="{Binding NonDefaultIdentityProvider}" HorizontalAlignment="Center" Classes="subtext">
                 <Run Text="{ui:Translate Key=ACCOUNT_PROVIDER_INFO}" />
                 <Run Text="{Binding IdentityProvider.ProviderName}" />
             </TextBlock>

+ 4 - 22
src/PixiEditor/Views/Auth/LoginPopup.axaml

@@ -9,31 +9,13 @@
                          xmlns:ui="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
                          mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
                          x:Class="PixiEditor.Views.Auth.LoginPopup"
-                         CanMinimize="False"
-                         CanResize="True"
-                         Width="320" MinHeight="190"
+                         CanMinimize="True"
+                         CanResize="False"
+                         Width="320" Height="190"
                          ui:Translator.Key="ACCOUNT_WINDOW_TITLE">
     <Design.DataContext>
         <subViewModels:UserViewModel />
     </Design.DataContext>
 
-    <StackPanel Margin="15" Spacing="5" Orientation="Vertical">
-        <auth:LoginForm DataContext="{Binding}" />
-        <Separator />
-        <TextBlock Text="{ui:Translate Key=OWNED_PRODUCTS}" Classes="h4" />
-        <ItemsControl IsVisible="{Binding !!User}" ItemsSource="{Binding OwnedProducts}">
-            <ItemsControl.ItemTemplate>
-                <DataTemplate>
-                    <StackPanel Orientation="Vertical" Spacing="5">
-                        <TextBlock Text="{Binding DisplayName}" />
-                        <Button
-                            Command="{Binding DataContext.InstallContentCommand, RelativeSource={RelativeSource AncestorType=auth:LoginPopup, Mode=FindAncestor}}"
-                            CommandParameter="{Binding Id}">
-                            <TextBlock Text="{ui:Translate Key=INSTALL}" />
-                        </Button>
-                    </StackPanel>
-                </DataTemplate>
-            </ItemsControl.ItemTemplate>
-        </ItemsControl>
-    </StackPanel>
+    <auth:LoginForm Margin="15" DataContext="{Binding}" />
 </dialogs:PixiEditorPopup>

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

@@ -7,6 +7,8 @@
              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"
+             xmlns:auth="clr-namespace:PixiEditor.Views.Auth"
+             xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
              mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
              x:ClassModifier="internal"
              x:Class="PixiEditor.Views.Auth.UserAvatarToggle">
@@ -31,21 +33,64 @@
                 </Button.Styles>
                 <Button.Flyout>
                     <Flyout>
-                        <StackPanel IsVisible="{Binding IsLoggedIn}" Margin="5" Spacing="12" Orientation="Vertical">
-                            <Border ClipToBounds="True" Width="100" Height="100" CornerRadius="100">
-                                <HyperlinkButton NavigateUri="https://gravatar.com/connect" Cursor="Hand"
-                                                 ui:Translator.TooltipKey="AVATAR_INFO">
+                        <StackPanel Width="250" IsVisible="{Binding IsLoggedIn}" Margin="15" Orientation="Vertical">
+                            <Border ClipToBounds="True" Width="50" Height="50" CornerRadius="50">
+                                <HyperlinkButton NavigateUri="{Binding IdentityProvider.EditProfileUrl}" Cursor="Hand"
+                                                 ToolTip.Tip="{Binding IdentityProvider.EditProfileUrl}">
                                     <Image asyncImageLoader:ImageLoader.Source="{Binding AvatarUrl}" />
                                 </HyperlinkButton>
                             </Border>
-                            <TextBlock HorizontalAlignment="Center" FontSize="{DynamicResource FontSizeNormal}"
-                                       ui:Translator.Key="LOGGED_IN_AS">
-                                <Run Text="" />
+                            <TextBlock Margin="0, 12, 0, 24" HorizontalAlignment="Center"
+                                       FontSize="{DynamicResource FontSizeLarge}">
+                                <Run ui:Translator.Key="LOGGED_IN_AS"/>
                                 <Run Text="{Binding Username}" />
                             </TextBlock>
-                            <Button
-                                Content="{ui:Translate Key=MANAGE_ACCOUNT}"
-                                Command="{xaml:Command Name=PixiEditor.Window.OpenAccountWindow}" />
+
+                            <TextBlock Text="{ui:Translate Key=OWNED_PRODUCTS}" FontWeight="Bold" />
+                            <ItemsControl IsVisible="{Binding !!User}" ItemsSource="{Binding OwnedProducts}">
+                                <ItemsControl.ItemsPanel>
+                                    <ItemsPanelTemplate>
+                                        <StackPanel Spacing="12" />
+                                    </ItemsPanelTemplate>
+                                </ItemsControl.ItemsPanel>
+                                <ItemsControl.ItemTemplate>
+                                    <DataTemplate>
+                                        <DockPanel>
+                                            <TextBlock DockPanel.Dock="Left" HorizontalAlignment="Left"
+                                                       Text="{Binding ProductData.DisplayName}" />
+                                            <Button DockPanel.Dock="Right" HorizontalAlignment="Right"
+                                                    Command="{Binding InstallCommand}">
+                                                <Panel Margin="4 0">
+                                                    <TextBlock IsVisible="{Binding IsInstalled}">
+                                                        <Run Classes="pixi-icon"
+                                                              Text="{DynamicResource icon-checkTick}"/>
+                                                        <Run ui:Translator.Key="INSTALLED" />
+                                                    </TextBlock>
+
+                                                    <TextBlock>
+                                                        <TextBlock.IsVisible>
+                                                            <MultiBinding Converter="{converters:AllTrueConverter}">
+                                                                <Binding Path="!IsInstalled" />
+                                                                <Binding Path="!IsInstalling" />
+                                                            </MultiBinding>
+                                                        </TextBlock.IsVisible>
+
+                                                        <Run Classes="pixi-icon" Text="{DynamicResource icon-download}"/>
+                                                        <Run ui:Translator.Key="INSTALL" />
+                                                    </TextBlock>
+
+                                                    <TextBlock IsVisible="{Binding IsInstalling}"
+                                                               ui:Translator.Key="INSTALLING" />
+                                                </Panel>
+                                            </Button>
+                                        </DockPanel>
+                                    </DataTemplate>
+                                </ItemsControl.ItemTemplate>
+                            </ItemsControl>
+
+                            <Button Margin="0, 24, 0, 0"
+                                    Content="{ui:Translate Key=LOGOUT}"
+                                    Command="{Binding LogoutCommand}" />
                         </StackPanel>
                     </Flyout>
                 </Button.Flyout>