Просмотр исходного кода

UI: Added option to check for updates in the background

Evan Husted 1 год назад
Родитель
Сommit
fe9fe2a10f

+ 95 - 20
src/Ryujinx/Assets/locales.json

@@ -3350,26 +3350,101 @@
     {
       "ID": "SettingsTabGeneralCheckUpdatesOnLaunch",
       "Translations": {
-        "ar_SA": "التحقق من وجود تحديثات عند التشغيل",
-        "de_DE": "Beim Start nach Updates suchen",
-        "el_GR": "Έλεγχος για Ενημερώσεις στην Εκκίνηση",
-        "en_US": "Check for Updates on Launch",
-        "es_ES": "Buscar actualizaciones al iniciar",
-        "fr_FR": "Vérifier les mises à jour au démarrage",
-        "he_IL": "בדוק אם קיימים עדכונים בהפעלה",
-        "it_IT": "Controlla aggiornamenti all'avvio",
-        "ja_JP": "起動時にアップデートを確認する",
-        "ko_KR": "시작 시, 업데이트 확인",
-        "no_NO": "Se etter oppdateringer ved oppstart",
-        "pl_PL": "Sprawdzaj aktualizacje przy uruchomieniu",
-        "pt_BR": "Verificar se há atualizações ao iniciar",
-        "ru_RU": "Проверять наличие обновлений при запуске",
-        "sv_SE": "Leta efter uppdatering vid uppstart",
-        "th_TH": "ตรวจหาการอัปเดตเมื่อเปิดโปรแกรม",
-        "tr_TR": "Her Açılışta Güncellemeleri Denetle",
-        "uk_UA": "Перевіряти наявність оновлень під час запуску",
-        "zh_CN": "启动时检查更新",
-        "zh_TW": "啟動時檢查更新"
+        "ar_SA": "",
+        "de_DE": "",
+        "el_GR": "",
+        "en_US": "Check for Updates:",
+        "es_ES": "",
+        "fr_FR": "",
+        "he_IL": "",
+        "it_IT": "",
+        "ja_JP": "",
+        "ko_KR": "",
+        "no_NO": "",
+        "pl_PL": "",
+        "pt_BR": "",
+        "ru_RU": "",
+        "sv_SE": "",
+        "th_TH": "",
+        "tr_TR": "",
+        "uk_UA": "",
+        "zh_CN": "",
+        "zh_TW": ""
+      }
+    },
+    {
+      "ID": "SettingsTabGeneralCheckUpdatesOnLaunchOff",
+      "Translations": {
+        "ar_SA": "",
+        "de_DE": "",
+        "el_GR": "",
+        "en_US": "Off",
+        "es_ES": "",
+        "fr_FR": "",
+        "he_IL": "",
+        "it_IT": "",
+        "ja_JP": "",
+        "ko_KR": "",
+        "no_NO": "",
+        "pl_PL": "",
+        "pt_BR": "",
+        "ru_RU": "",
+        "sv_SE": "",
+        "th_TH": "",
+        "tr_TR": "",
+        "uk_UA": "",
+        "zh_CN": "",
+        "zh_TW": ""
+      }
+    },
+    {
+      "ID": "SettingsTabGeneralCheckUpdatesOnLaunchPromptAtStartup",
+      "Translations": {
+        "ar_SA": "",
+        "de_DE": "",
+        "el_GR": "",
+        "en_US": "Prompt",
+        "es_ES": "",
+        "fr_FR": "",
+        "he_IL": "",
+        "it_IT": "",
+        "ja_JP": "",
+        "ko_KR": "",
+        "no_NO": "",
+        "pl_PL": "",
+        "pt_BR": "",
+        "ru_RU": "",
+        "sv_SE": "",
+        "th_TH": "",
+        "tr_TR": "",
+        "uk_UA": "",
+        "zh_CN": "",
+        "zh_TW": ""
+      }
+    },
+    {
+      "ID": "SettingsTabGeneralCheckUpdatesOnLaunchBackground",
+      "Translations": {
+        "ar_SA": "",
+        "de_DE": "",
+        "el_GR": "",
+        "en_US": "Background",
+        "es_ES": "",
+        "fr_FR": "",
+        "he_IL": "",
+        "it_IT": "",
+        "ja_JP": "",
+        "ko_KR": "",
+        "no_NO": "",
+        "pl_PL": "",
+        "pt_BR": "",
+        "ru_RU": "",
+        "sv_SE": "",
+        "th_TH": "",
+        "tr_TR": "",
+        "uk_UA": "",
+        "zh_CN": "",
+        "zh_TW": ""
       }
     },
     {

+ 4 - 0
src/Ryujinx/UI/ViewModels/SettingsViewModel.cs

@@ -13,6 +13,7 @@ using Ryujinx.Ava.UI.Models.Input;
 using Ryujinx.Ava.UI.Windows;
 using Ryujinx.Ava.Utilities.Configuration;
 using Ryujinx.Ava.Utilities.Configuration.System;
+using Ryujinx.Ava.Utilities.Configuration.UI;
 using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Configuration.Multiplayer;
 using Ryujinx.Common.GraphicsDriver;
@@ -121,6 +122,7 @@ namespace Ryujinx.Ava.UI.ViewModels
         public bool RememberWindowState { get; set; }
         public bool ShowTitleBar { get; set; }
         public int HideCursor { get; set; }
+        public int UpdateCheckerType { get; set; }
         public bool EnableDockedMode { get; set; }
         public bool EnableKeyboard { get; set; }
         public bool EnableMouse { get; set; }
@@ -476,6 +478,7 @@ namespace Ryujinx.Ava.UI.ViewModels
             RememberWindowState = config.RememberWindowState;
             ShowTitleBar = config.ShowTitleBar;
             HideCursor = (int)config.HideCursor.Value;
+            UpdateCheckerType = (int)config.UpdateCheckerType.Value;
 
             GameDirectories.Clear();
             GameDirectories.AddRange(config.UI.GameDirs.Value);
@@ -582,6 +585,7 @@ namespace Ryujinx.Ava.UI.ViewModels
             config.RememberWindowState.Value = RememberWindowState;
             config.ShowTitleBar.Value = ShowTitleBar;
             config.HideCursor.Value = (HideCursorMode)HideCursor;
+            config.UpdateCheckerType.Value = (UpdaterType)UpdateCheckerType;
 
             if (GameDirectoryChanged)
             {

+ 20 - 4
src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml

@@ -6,6 +6,7 @@
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
     xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
+    xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common"
     mc:Ignorable="d"
     x:DataType="viewModels:SettingsViewModel">
     <Design.DataContext>
@@ -30,18 +31,33 @@
                                    ToolTip.Tip="{ext:Locale ToggleDiscordTooltip}"
                                    Text="{ext:Locale SettingsTabGeneralEnableDiscordRichPresence}" />
                     </CheckBox>
-                    <CheckBox IsChecked="{Binding CheckUpdatesOnStart}">
-                        <TextBlock Text="{ext:Locale SettingsTabGeneralCheckUpdatesOnLaunch}" />
-                    </CheckBox>
                     <CheckBox IsChecked="{Binding ShowConfirmExit}">
                         <TextBlock Text="{ext:Locale SettingsTabGeneralShowConfirmExitDialog}" />
                     </CheckBox>
                     <CheckBox IsChecked="{Binding RememberWindowState}">
                         <TextBlock Text="{ext:Locale SettingsTabGeneralRememberWindowState}" />
                     </CheckBox>
-                    <CheckBox IsChecked="{Binding ShowTitleBar}" Name="ShowTitleBarBox">
+                    <CheckBox IsChecked="{Binding ShowTitleBar}" IsVisible="{x:Static helper:RunningPlatform.IsWindows}">
                         <TextBlock Text="{ext:Locale SettingsTabGeneralShowTitleBar}" />
                     </CheckBox>
+                    <StackPanel Margin="0, 15, 0, 0" Orientation="Horizontal">
+                        <TextBlock VerticalAlignment="Center"
+                                   Text="{ext:Locale SettingsTabGeneralCheckUpdatesOnLaunch}"
+                                   Width="150" />
+                        <ComboBox SelectedIndex="{Binding UpdateCheckerType}"
+                                  HorizontalContentAlignment="Left"
+                                  MinWidth="100">
+                            <ComboBoxItem>
+                                <TextBlock Text="{ext:Locale SettingsTabGeneralCheckUpdatesOnLaunchOff}" />
+                            </ComboBoxItem>
+                            <ComboBoxItem>
+                                <TextBlock Text="{ext:Locale SettingsTabGeneralCheckUpdatesOnLaunchPromptAtStartup}" />
+                            </ComboBoxItem>
+                            <ComboBoxItem>
+                                <TextBlock Text="{ext:Locale SettingsTabGeneralCheckUpdatesOnLaunchBackground}" />
+                            </ComboBoxItem>
+                        </ComboBox>
+                    </StackPanel>
                     <StackPanel Margin="0, 15, 0, 0" Orientation="Horizontal">
                         <TextBlock VerticalAlignment="Center"
                                    Text="{ext:Locale SettingsTabGeneralHideCursor}"

+ 0 - 1
src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml.cs

@@ -21,7 +21,6 @@ namespace Ryujinx.Ava.UI.Views.Settings
         public SettingsUiView()
         {
             InitializeComponent();
-            ShowTitleBarBox.IsVisible = OperatingSystem.IsWindows();
             AddGameDirButton.Command =
                 Commands.Create(() => AddDirButton(GameDirPathBox, ViewModel.GameDirectories, true));
             AddAutoloadDirButton.Command =

+ 23 - 3
src/Ryujinx/UI/Windows/MainWindow.axaml.cs

@@ -19,6 +19,7 @@ using Ryujinx.Ava.UI.ViewModels;
 using Ryujinx.Ava.Utilities;
 using Ryujinx.Ava.Utilities.AppLibrary;
 using Ryujinx.Ava.Utilities.Configuration;
+using Ryujinx.Ava.Utilities.Configuration.UI;
 using Ryujinx.Common;
 using Ryujinx.Common.Helper;
 using Ryujinx.Common.Logging;
@@ -400,10 +401,29 @@ namespace Ryujinx.Ava.UI.Windows
                 await Dispatcher.UIThread.InvokeAsync(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys));
             }
 
-            if (ConfigurationState.Instance.CheckUpdatesOnStart && !CommandLineState.HideAvailableUpdates && Updater.CanUpdate())
+            if (!Updater.CanUpdate() || CommandLineState.HideAvailableUpdates)
+                return;
+
+            switch (ConfigurationState.Instance.UpdateCheckerType.Value)
             {
-                await Updater.BeginUpdateAsync()
-                    .Catch(task => Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}"));
+                case UpdaterType.PromptAtStartup:
+                    await Updater.BeginUpdateAsync()
+                        .Catch(task => Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}"));
+                    break;
+                case UpdaterType.CheckInBackground:
+                    if ((await Updater.CheckVersionAsync()).TryGet(out (Version Current, Version Incoming) versions))
+                    {
+                        string newVersionString = ReleaseInformation.IsCanaryBuild
+                            ? $"Canary {versions.Current} -> Canary {versions.Incoming}"
+                            : $"{versions.Current} -> {versions.Incoming}";
+                    
+                        if (versions.Current < versions.Incoming)
+                            NotificationHelper.ShowInformation(
+                                title: "Update Available",
+                                text: newVersionString,
+                                onClick: () => _ = Updater.BeginUpdateAsync());
+                    }
+                    break;
             }
         }
 

+ 31 - 28
src/Ryujinx/Updater.cs

@@ -43,7 +43,18 @@ namespace Ryujinx.Ava
         private const int ConnectionCount = 4;
 
         private static string _buildVer;
-        private static string _platformExt;
+
+        private static readonly string _platformExt = 
+            RunningPlatform.IsMacOS 
+                ? "macos_universal.app.tar.gz"
+                : RunningPlatform.IsWindows
+                    ? "win_x64.zip"
+                    : RunningPlatform.IsX64Linux
+                        ? "linux_x64.tar.gz"
+                        : RunningPlatform.IsArmLinux
+                            ? "linux_arm64.tar.gz"
+                            : throw new PlatformNotSupportedException();
+        
         private static string _buildUrl;
         private static long _buildSize;
         private static bool _updateSuccessful;
@@ -51,30 +62,8 @@ namespace Ryujinx.Ava
 
         private static readonly string[] _windowsDependencyDirs = [];
 
-        public static async Task BeginUpdateAsync(bool showVersionUpToDate = false)
+        public static async Task<Optional<(Version Current, Version Incoming)>> CheckVersionAsync(bool showVersionUpToDate = false)
         {
-            if (_running)
-            {
-                return;
-            }
-
-            _running = true;
-
-            // Detect current platform
-            if (OperatingSystem.IsMacOS())
-            {
-                _platformExt = "macos_universal.app.tar.gz";
-            }
-            else if (OperatingSystem.IsWindows())
-            {
-                _platformExt = "win_x64.zip";
-            }
-            else if (OperatingSystem.IsLinux())
-            {
-                string arch = RuntimeInformation.OSArchitecture == Architecture.Arm64 ? "arm64" : "x64";
-                _platformExt = $"linux_{arch}.tar.gz";
-            }
-
             if (!Version.TryParse(Program.Version, out Version currentVersion))
             {
                 Logger.Error?.Print(LogClass.Application, $"Failed to convert the current {RyujinxApp.FullAppName} version!");
@@ -85,7 +74,7 @@ namespace Ryujinx.Ava
 
                 _running = false;
 
-                return;
+                return default;
             }
             
             Logger.Info?.Print(LogClass.Application, "Checking for updates.");
@@ -123,7 +112,7 @@ namespace Ryujinx.Ava
 
                             _running = false;
 
-                            return;
+                            return default;
                         }
 
                         break;
@@ -149,7 +138,7 @@ namespace Ryujinx.Ava
 
                     _running = false;
 
-                    return;
+                    return default;
                 }
             }
             catch (Exception exception)
@@ -161,7 +150,7 @@ namespace Ryujinx.Ava
 
                 _running = false;
 
-                return;
+                return default;
             }
 
             if (!Version.TryParse(_buildVer, out Version newVersion))
@@ -174,9 +163,23 @@ namespace Ryujinx.Ava
 
                 _running = false;
 
+                return (currentVersion, null);
+            }
+
+            return (currentVersion, newVersion);
+        }
+        
+        public static async Task BeginUpdateAsync(bool showVersionUpToDate = false)
+        {
+            if (_running)
+            {
                 return;
             }
 
+            _running = true;
+
+            (Version currentVersion, Version newVersion) = (await CheckVersionAsync(showVersionUpToDate)).OrDefault();
+
             if (newVersion <= currentVersion)
             {
                 if (showVersionUpToDate)

+ 7 - 2
src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs

@@ -15,7 +15,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
         /// <summary>
         /// The current version of the file format
         /// </summary>
-        public const int CurrentVersion = 64;
+        public const int CurrentVersion = 65;
 
         /// <summary>
         /// Version of the configuration file format
@@ -163,9 +163,14 @@ namespace Ryujinx.Ava.Utilities.Configuration
         public bool EnableDiscordIntegration { get; set; }
 
         /// <summary>
-        /// Checks for updates when Ryujinx starts when enabled
+        /// DEPRECATED: Checks for updates when Ryujinx starts when enabled
         /// </summary>
         public bool CheckUpdatesOnStart { get; set; }
+        
+        /// <summary>
+        /// Checks for updates when Ryujinx starts when enabled, either prompting when an update is found or just showing a notification.
+        /// </summary>
+        public UpdaterType UpdateCheckerType { get; set; }
 
         /// <summary>
         /// Show "Confirm Exit" Dialog

+ 3 - 1
src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs

@@ -45,6 +45,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
             
             EnableDiscordIntegration.Value = cff.EnableDiscordIntegration;
             CheckUpdatesOnStart.Value = cff.CheckUpdatesOnStart;
+            UpdateCheckerType.Value = cff.UpdateCheckerType;
             ShowConfirmExit.Value = cff.ShowConfirmExit;
             RememberWindowState.Value = cff.RememberWindowState;
             ShowTitleBar.Value = cff.ShowTitleBar;
@@ -431,7 +432,8 @@ namespace Ryujinx.Ava.Utilities.Configuration
                 }),
                 (62, static cff => cff.RainbowSpeed = 1f),
                 (63, static cff => cff.MatchSystemTime = false),
-                (64, static cff => cff.LoggingEnableAvalonia = false)
+                (64, static cff => cff.LoggingEnableAvalonia = false),
+                (65, static cff => cff.UpdateCheckerType = cff.CheckUpdatesOnStart ? UpdaterType.PromptAtStartup : UpdaterType.Off)
             );
     }
 }

+ 7 - 0
src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs

@@ -1,6 +1,7 @@
 using ARMeilleure;
 using Gommon;
 using Ryujinx.Ava.Utilities.Configuration.System;
+using Ryujinx.Ava.Utilities.Configuration.UI;
 using Ryujinx.Common;
 using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Configuration.Hid;
@@ -767,6 +768,11 @@ namespace Ryujinx.Ava.Utilities.Configuration
         /// Checks for updates when Ryujinx starts when enabled
         /// </summary>
         public ReactiveObject<bool> CheckUpdatesOnStart { get; private set; }
+        
+        /// <summary>
+        /// Checks for updates when Ryujinx starts when enabled, either prompting when an update is found or just showing a notification.
+        /// </summary>
+        public ReactiveObject<UpdaterType> UpdateCheckerType { get; private set; }
 
         /// <summary>
         /// Show "Confirm Exit" Dialog
@@ -804,6 +810,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
             Hacks = new HacksSection();
             EnableDiscordIntegration = new ReactiveObject<bool>();
             CheckUpdatesOnStart = new ReactiveObject<bool>();
+            UpdateCheckerType = new ReactiveObject<UpdaterType>();
             ShowConfirmExit = new ReactiveObject<bool>();
             RememberWindowState = new ReactiveObject<bool>();
             ShowTitleBar = new ReactiveObject<bool>();

+ 1 - 1
src/Ryujinx/Utilities/Configuration/ConfigurationState.cs

@@ -175,7 +175,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
             System.SystemTimeOffset.Value = 0;
             System.EnableDockedMode.Value = true;
             EnableDiscordIntegration.Value = true;
-            CheckUpdatesOnStart.Value = true;
+            UpdateCheckerType.Value = UpdaterType.PromptAtStartup;
             ShowConfirmExit.Value = true;
             RememberWindowState.Value = true;
             ShowTitleBar.Value = !OperatingSystem.IsWindows();

+ 13 - 0
src/Ryujinx/Utilities/Configuration/UI/UpdaterType.cs

@@ -0,0 +1,13 @@
+using Ryujinx.Common.Utilities;
+using System.Text.Json.Serialization;
+
+namespace Ryujinx.Ava.Utilities.Configuration.UI
+{
+    [JsonConverter(typeof(TypedStringEnumConverter<UpdaterType>))]
+    public enum UpdaterType
+    {
+        Off,
+        PromptAtStartup,
+        CheckInBackground
+    }
+}