Przeglądaj źródła

Updater changes wip

Krzysztof Krysiński 4 miesięcy temu
rodzic
commit
6294af88cb

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

@@ -989,5 +989,8 @@
   "ERR_RENDERING_FAILED": "Rendering failed",
   "ENABLE_ANALYTICS": "Send anonymous analytics",
   "ANALYTICS_INFO": "We collect anonymous usage data to improve PixiEditor. No personal data is collected.",
-  "LANGUAGE_INFO": "All translations are community-driven. Join our Discord server for more information."
+  "LANGUAGE_INFO": "All translations are community-driven. Join our Discord server for more information.",
+  "UP_TO_DATE_UNKNOWN": "Couldn't check for updates",
+  "UP_TO_DATE": "PixiEditor is up to date",
+  "UPDATE_AVAILABLE": "Update {0} is available"
 }

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

@@ -12,6 +12,7 @@ using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.UI;
 using PixiEditor.Models.Commands;
 using PixiEditor.OperatingSystem;
+using PixiEditor.ViewModels.SubViewModels;
 using PixiEditor.ViewModels.SubViewModels.AdditionalContent;
 using Command = PixiEditor.Models.Commands.Commands.Command;
 using Commands_Command = PixiEditor.Models.Commands.Commands.Command;
@@ -22,6 +23,7 @@ namespace PixiEditor.ViewModels.Menu;
 internal class MenuBarViewModel : PixiObservableObject
 {
     private AdditionalContentViewModel additionalContentViewModel;
+    private UpdateViewModel updateViewModel;
 
     public AdditionalContentViewModel AdditionalContentSubViewModel
     {
@@ -29,6 +31,12 @@ internal class MenuBarViewModel : PixiObservableObject
         set => SetProperty(ref additionalContentViewModel, value);
     }
 
+    public UpdateViewModel UpdateViewModel
+    {
+        get => updateViewModel;
+        set => SetProperty(ref updateViewModel, value);
+    }
+
     public ObservableCollection<MenuItem>? MenuEntries { get; set; }
     public NativeMenu? NativeMenu { get; private set; }
 
@@ -47,9 +55,10 @@ internal class MenuBarViewModel : PixiObservableObject
         { "DEBUG", 1000 },
     };
 
-    public MenuBarViewModel(AdditionalContentViewModel? additionalContentSubViewModel)
+    public MenuBarViewModel(AdditionalContentViewModel? additionalContentSubViewModel, UpdateViewModel? updateViewModel)
     {
         AdditionalContentSubViewModel = additionalContentSubViewModel;
+        UpdateViewModel = updateViewModel;
     }
 
     public void Init(IServiceProvider serviceProvider, CommandController controller)

+ 127 - 46
src/PixiEditor/ViewModels/SubViewModels/UpdateViewModel.cs

@@ -7,6 +7,7 @@ using System.Text;
 using System.Threading.Tasks;
 using Avalonia;
 using Avalonia.Controls.ApplicationLifetimes;
+using CommunityToolkit.Mvvm.Input;
 using PixiEditor.Views.Dialogs;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.CommonApi.UserPreferences.Settings.PixiEditor;
@@ -23,12 +24,28 @@ namespace PixiEditor.ViewModels.SubViewModels;
 internal class UpdateViewModel : SubViewModel<ViewModelMain>
 {
     public const int MaxRetryCount = 3;
-    private bool updateReadyToInstall = false;
-
     public UpdateChecker UpdateChecker { get; set; }
 
     public List<UpdateChannel> UpdateChannels { get; } = new List<UpdateChannel>();
 
+    private UpdateState _updateState = UpdateState.Checking;
+
+    public UpdateState UpdateState
+    {
+        get => _updateState;
+        set
+        {
+            _updateState = value;
+            OnPropertyChanged(nameof(UpdateState));
+            OnPropertyChanged(nameof(IsUpdateAvailable));
+            OnPropertyChanged(nameof(UpdateReadyToInstall));
+            OnPropertyChanged(nameof(IsDownloading));
+            OnPropertyChanged(nameof(IsUpToDate));
+            OnPropertyChanged(nameof(IsFailed));
+            OnPropertyChanged(nameof(UpdateStateString));
+        }
+    }
+
     private string versionText;
 
     public string VersionText
@@ -41,22 +58,52 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
         }
     }
 
-    public bool UpdateReadyToInstall
+
+    public string UpdateStateString
     {
-        get => updateReadyToInstall;
-        set
+        get => _updateState switch
         {
-            updateReadyToInstall = value;
-            OnPropertyChanged(nameof(UpdateReadyToInstall));
-            if (value)
-            {
-                VersionText =
-                    new LocalizedString("TO_INSTALL_UPDATE",
-                        UpdateChecker.LatestReleaseInfo.TagName); // Button shows "Restart" before this text
-            }
-        }
+            UpdateState.UnableToCheck => new LocalizedString("UP_TO_DATE_UNKNOWN"),
+            UpdateState.Checking => new LocalizedString("CHECKING_FOR_UPDATES"),
+            UpdateState.FailedDownload => new LocalizedString("UPDATE_FAILED_DOWNLOAD"),
+            UpdateState.ReadyToInstall => new LocalizedString("UPDATE_READY_TO_INSTALL"),
+            UpdateState.Downloading => new LocalizedString("DOWNLOADING_UPDATE"),
+            UpdateState.UpdateAvailable => new LocalizedString("UPDATE_AVAILABLE",
+                UpdateChecker.LatestReleaseInfo.TagName),
+            UpdateState.UpToDate => new LocalizedString("UP_TO_DATE"),
+            UpdateState.Failed => new LocalizedString("UPDATE_FAILED"),
+            _ => new LocalizedString("UP_TO_DATE_UNKNOWN")
+        };
     }
 
+    public bool IsUpdateAvailable
+    {
+        get => _updateState == UpdateState.UpdateAvailable;
+    }
+
+    public bool UpdateReadyToInstall
+    {
+        get => _updateState == UpdateState.ReadyToInstall;
+    }
+
+    public bool IsDownloading
+    {
+        get => _updateState == UpdateState.Downloading;
+    }
+
+    public bool IsUpToDate
+    {
+        get => _updateState == UpdateState.UpToDate;
+    }
+
+    public bool IsFailed
+    {
+        get => _updateState == UpdateState.Failed;
+    }
+
+    public AsyncRelayCommand DownloadCommand => new AsyncRelayCommand(Download);
+    public RelayCommand InstallCommand => new RelayCommand(Install);
+
     public UpdateViewModel(ViewModelMain owner)
         : base(owner)
     {
@@ -73,33 +120,42 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
         InitUpdateChecker();
     }
 
-    public async Task<bool> CheckForUpdate()
+    public async Task CheckForUpdate()
     {
         if (!IOperatingSystem.Current.IsWindows)
         {
-            return false;
+            return;
         }
 
         bool updateAvailable = await UpdateChecker.CheckUpdateAvailable();
         if (!UpdateChecker.LatestReleaseInfo.WasDataFetchSuccessful ||
             string.IsNullOrEmpty(UpdateChecker.LatestReleaseInfo.TagName))
         {
-            return false;
+            UpdateState = UpdateState.UnableToCheck;
+            return;
         }
 
+        UpdateState = updateAvailable ? UpdateState.UpdateAvailable : UpdateState.UpToDate;
+    }
+
+    public async Task Download()
+    {
         bool updateCompatible = await UpdateChecker.IsUpdateCompatible();
         bool autoUpdateFailed = CheckAutoupdateFailed();
-        bool updateFileDoesNotExists = !File.Exists(
-            Path.Join(UpdateDownloader.DownloadLocation, $"update-{UpdateChecker.LatestReleaseInfo.TagName}.zip"));
+        bool updateFileDoesNotExists = !AutoUpdateFileExists();
+        bool updateExeDoesNotExists = !UpdateInstallerFileExists();
+
+        if(!updateExeDoesNotExists || !updateFileDoesNotExists)
+        {
+            UpdateState = UpdateState.ReadyToInstall;
+            return;
+        }
 
-        bool updateExeDoesNotExists = !File.Exists(
-            Path.Join(UpdateDownloader.DownloadLocation, $"update-{UpdateChecker.LatestReleaseInfo.TagName}.exe"));
-        if (updateAvailable && (updateFileDoesNotExists && updateExeDoesNotExists) || autoUpdateFailed)
+        if ((updateFileDoesNotExists && updateExeDoesNotExists) || autoUpdateFailed)
         {
-            UpdateReadyToInstall = false;
-            VersionText = new LocalizedString("DOWNLOADING_UPDATE");
             try
             {
+                UpdateState = UpdateState.Downloading;
                 if (updateCompatible && !autoUpdateFailed)
                 {
                     await UpdateDownloader.DownloadReleaseZip(UpdateChecker.LatestReleaseInfo);
@@ -113,39 +169,46 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
                     }
                 }
 
-                UpdateReadyToInstall = true;
+                UpdateState = UpdateState.ReadyToInstall;
             }
             catch (IOException ex)
             {
-                NoticeDialog.Show("FAILED_DOWNLOADING", "FAILED_DOWNLOADING_TITLE");
-                return false;
+                UpdateState = UpdateState.FailedDownload;
             }
             catch (TaskCanceledException ex)
             {
-                return false;
+                UpdateState = UpdateState.UpdateAvailable;
+            }
+            catch (Exception ex)
+            {
+                UpdateState = UpdateState.FailedDownload;
             }
-
-            return true;
         }
+    }
 
-        return false;
+    private bool AutoUpdateFileExists()
+    {
+        string path = Path.Join(UpdateDownloader.DownloadLocation,
+            $"update-{UpdateChecker.LatestReleaseInfo.TagName}.zip");
+        return File.Exists(path);
     }
 
-    private async void Install()
+    private bool UpdateInstallerFileExists()
     {
-#if RELEASE || DEVRELEASE
-        if (!PixiEditorSettings.Update.CheckUpdatesOnStartup.Value)
-        {
-            return;
-        }
+        string path = Path.Join(UpdateDownloader.DownloadLocation,
+            $"update-{UpdateChecker.LatestReleaseInfo.TagName}.exe");
+        return File.Exists(path);
+    }
 
+    private void Install()
+    {
+#if RELEASE || DEVRELEASE
         string dir = AppDomain.CurrentDomain.BaseDirectory;
 
         UpdateDownloader.CreateTempDirectory();
         if (UpdateChecker.LatestReleaseInfo == null ||
             string.IsNullOrEmpty(UpdateChecker.LatestReleaseInfo.TagName)) return;
-        bool updateFileExists = File.Exists(
-            Path.Join(UpdateDownloader.DownloadLocation, $"update-{UpdateChecker.LatestReleaseInfo.TagName}.zip"));
+        bool updateFileExists = AutoUpdateFileExists();
         string exePath = Path.Join(UpdateDownloader.DownloadLocation,
             $"update-{UpdateChecker.LatestReleaseInfo.TagName}.exe");
 
@@ -166,8 +229,7 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
             return;
         }
 
-        ViewModelMain.Current.UpdateSubViewModel.UpdateReadyToInstall = true;
-        if (!UpdateInfoExists())
+        /*if (!UpdateInfoExists())
         {
             CreateUpdateInfo(UpdateChecker.LatestReleaseInfo.TagName);
             return;
@@ -185,7 +247,7 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
         if (!CanInstallUpdate(UpdateChecker.LatestReleaseInfo.TagName, info) && !updateExeExists)
         {
             return;
-        }
+        }*/
 
         if (updateFileExists && File.Exists(updaterPath))
         {
@@ -275,6 +337,15 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
             try
             {
                 await CheckForUpdate();
+                if (UpdateState == UpdateState.UpdateAvailable)
+                {
+                    bool updateFileExists = AutoUpdateFileExists() || UpdateInstallerFileExists();
+                    if (updateFileExists)
+                    {
+                        UpdateState = UpdateState.ReadyToInstall;
+                    }
+                }
+
                 if (UpdateChecker.LatestReleaseInfo != null && UpdateChecker.LatestReleaseInfo.TagName ==
                     VersionHelpers.GetCurrentAssemblyVersionString())
                 {
@@ -283,15 +354,13 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
             }
             catch (System.Net.Http.HttpRequestException)
             {
-                NoticeDialog.Show("COULD_NOT_CHECK_FOR_UPDATES", "UPDATE_CHECK_FAILED");
+                UpdateState = UpdateState.UnableToCheck;
             }
             catch (Exception e)
             {
+                UpdateState = UpdateState.UnableToCheck;
                 CrashHelper.SendExceptionInfoAsync(e);
-                NoticeDialog.Show("COULD_NOT_CHECK_FOR_UPDATES", "UPDATE_CHECK_FAILED");
             }
-
-            Install();
         }
     }
 
@@ -397,3 +466,15 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
         return selectedChannel;
     }
 }
+
+public enum UpdateState
+{
+    Checking,
+    UnableToCheck,
+    UpdateAvailable,
+    Downloading,
+    FailedDownload,
+    ReadyToInstall,
+    UpToDate,
+    Failed,
+}

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

@@ -147,7 +147,7 @@ internal partial class ViewModelMain : ViewModelBase, ICommandsHandler
         RegistrySubViewModel = services.GetService<RegistryViewModel>();
 
         AdditionalContentSubViewModel = services.GetService<AdditionalContentViewModel>();
-        MenuBarViewModel = new MenuBarViewModel(AdditionalContentSubViewModel);
+        MenuBarViewModel = new MenuBarViewModel(AdditionalContentSubViewModel, UpdateSubViewModel);
 
         CommandController.Init(services);
         LayoutSubViewModel.LayoutManager.InitLayout(this);

+ 1 - 5
src/PixiEditor/Views/Main/ActionDisplayBar.axaml

@@ -55,15 +55,11 @@
             VerticalAlignment="Center"
             Grid.Column="2"
             Orientation="Horizontal">
-            <Button
-                IsVisible="{Binding DataContext.UpdateSubViewModel.UpdateReadyToInstall, FallbackValue=False, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}"
-                Background="{DynamicResource ThemeAccentBrush}"
-                Command="{xaml:Command PixiEditor.RestartToUpdate}" ui:Translator.Key="RESTART" />
             <TextBlock
                 VerticalAlignment="Center"
                 Padding="10, 0"
                 HorizontalAlignment="Right"
-                Foreground="White"
+                Foreground="{DynamicResource ThemeForegroundBrush}"
                 FontSize="14"
                 Text="{Binding DataContext.UpdateSubViewModel.VersionText, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}" />
         </StackPanel>

+ 30 - 5
src/PixiEditor/Views/Main/MainTitleBar.axaml

@@ -29,9 +29,9 @@
                 </Border>
             </dialogs:DialogTitleBar.AdditionalElement>
         </dialogs:DialogTitleBar>
-        <Svg DockPanel.Dock="Left" HorizontalAlignment="Left" Path="/Images/PixiEditorLogo.svg"
-             Width="20" Height="20">
-            <Svg.Margin>
+        <ToggleButton Name="LogoButton" Padding="0" BorderThickness="0" DockPanel.Dock="Left"
+                      HorizontalAlignment="Left" Width="20" Height="20">
+            <ToggleButton.Margin>
                 <OnPlatform>
                     <OnPlatform.macOS>
                         <Thickness>75, 0, 0, 0</Thickness>
@@ -40,8 +40,33 @@
                         <Thickness>10, 0, 0, 0</Thickness>
                     </OnPlatform.Default>
                 </OnPlatform>
-            </Svg.Margin>
-        </Svg>
+            </ToggleButton.Margin>
+            <ToggleButton.Background>
+                <VisualBrush>
+                    <VisualBrush.Visual>
+                        <Svg Path="/Images/PixiEditorLogo.svg" />
+                    </VisualBrush.Visual>
+                </VisualBrush>
+            </ToggleButton.Background>
+            <ToggleButton.Flyout>
+                <Flyout Placement="BottomEdgeAlignedLeft">
+                    <Flyout.Content>
+                        <StackPanel Margin="5">
+                            <TextBlock Text="PixiEditor" FontSize="20" />
+                            <TextBlock Text="{Binding UpdateViewModel.VersionText}" />
+                            <TextBlock Text="{Binding UpdateViewModel.UpdateStateString}" />
+                            <Button ui:Translator.Key="DOWNLOAD_UPDATE"
+                                    Command="{Binding UpdateViewModel.DownloadCommand}"
+                                    IsVisible="{Binding UpdateViewModel.IsUpdateAvailable}" />
+                            <Button ui:Translator.Key="SWITCH_TO_NEW_VERSION"
+                                    Command="{Binding UpdateViewModel.InstallCommand}"
+                                    IsVisible="{Binding UpdateViewModel.UpdateReadyToInstall}" />
+                        </StackPanel>
+                    </Flyout.Content>
+                </Flyout>
+            </ToggleButton.Flyout>
+        </ToggleButton>
+
         <StackPanel Orientation="Horizontal">
             <StackPanel.Margin>
                 <OnPlatform>