From 2a9ae762e890325a8fe6ea597d85dcb08f349fdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 18 Jun 2026 09:02:09 -0400 Subject: [PATCH 1/8] Make release build Avalonia-only Remove the WinUI frontend from the active solution and release payload, move shared assets into neutral paths, and publish Avalonia as the Windows app while preserving existing executable/artifact names. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/build-release.yml | 14 +- .github/workflows/cli-headless-e2e.yml | 4 +- .github/workflows/dotnet-test.yml | 5 +- AGENTS.md | 19 +- CONTRIBUTING.md | 2 +- UniGetUI.iss | 12 +- scripts/build.ps1 | 18 +- scripts/package-linux.ps1 | 2 +- src/Directory.Build.props | 2 +- src/Shared/AutoUpdater.Helpers.cs | 156 ++ src/Shared/SharedPreUiCommandDispatcher.cs | 101 +- .../Assets/Images/admin_color.png | Bin .../Assets/Images/agreement.png | Bin .../Assets/Images/alert_laptop.png | Bin .../Assets/Images/cancel.png | Bin .../Assets/Images/cargo_color.png | Bin .../Assets/Images/checked_laptop.png | Bin .../Assets/Images/choco_color.png | Bin .../Assets/Images/coffee.png | Bin .../Assets/Images/console_color.png | Bin .../Assets/Images/desktop_download.png | Bin .../Assets/Images/dotnet_color.png | Bin .../Assets/Images/finish.png | Bin .../Assets/Images/github.png | Bin .../Assets/Images/hacker.png | Bin .../Assets/Images/icon.bmp | Bin .../Assets/Images/icon.ico | Bin .../Assets/Images/icon.png | Bin .../Assets/Images/icon_unsquare.png | Bin .../Assets/Images/infocolor.png | Bin .../Assets/Images/kofi.png | Bin .../Assets/Images/node_color.png | Bin .../Assets/Images/package_color.png | Bin .../Assets/Images/pin_color.png | Bin .../Assets/Images/pip_color.png | Bin .../Assets/Images/powershell_color.png | Bin .../Assets/Images/restart_color.png | Bin .../Assets/Images/rocket.png | Bin .../Assets/Images/save.png | Bin .../Assets/Images/scoop_color.png | Bin .../Assets/Images/settings_gear.png | Bin .../Assets/Images/shield_green.png | Bin .../Assets/Images/shield_question.png | Bin .../Assets/Images/shield_red.png | Bin .../Assets/Images/shield_reload.png | Bin .../Assets/Images/shield_yellow.png | Bin .../Assets/Images/simple_user.png | Bin .../Assets/Images/tick.png | Bin .../Assets/Images/tray_blue_black_legacy.ico | Bin .../Assets/Images/tray_blue_white_legacy.ico | Bin .../Assets/Images/tray_empty_black_legacy.ico | Bin .../Assets/Images/tray_empty_white_legacy.ico | Bin .../Assets/Images/tray_green_black_legacy.ico | Bin .../Assets/Images/tray_green_white_legacy.ico | Bin .../Images/tray_orange_black_legacy.ico | Bin .../Images/tray_orange_white_legacy.ico | Bin .../Images/tray_turquoise_black_legacy.ico | Bin .../Images/tray_turquoise_white_legacy.ico | Bin .../Assets/Images/update_pc_color.png | Bin .../Assets/Images/vcpkg_color.png | Bin .../Assets/Images/warn.png | Bin .../Assets/Images/winget_color.png | Bin .../Assets/Images/workstation.png | Bin .../Assets/Images/youtube.png | Bin .../Assets/SplashScreen.png | Bin .../Assets/SplashScreen.svg | 0 .../Assets/SplashScreen.theme-dark.png | Bin .../Assets/SplashScreen.theme-dark.svg | 0 .../Assets/Square150x150Logo.png | Bin .../Assets/Square44x44Logo.png | Bin .../Assets/StoreLogo.png | Bin .../Assets/Symbols/DiscoverPackage.svg | 0 .../Assets/Symbols/Filter.svg | 0 .../Assets/Symbols/Font/Read Me.txt | 0 .../Assets/Symbols/Font/demo-files/demo.css | 0 .../Assets/Symbols/Font/demo-files/demo.js | 0 .../Assets/Symbols/Font/demo.html | 0 .../Symbols/Font/fonts/UniGetUI-Symbols.eot | Bin .../Symbols/Font/fonts/UniGetUI-Symbols.svg | 0 .../Symbols/Font/fonts/UniGetUI-Symbols.ttf | Bin .../Symbols/Font/fonts/UniGetUI-Symbols.woff | Bin .../Assets/Symbols/Font/selection.json | 0 .../Assets/Symbols/Font/style.css | 0 .../Assets/Symbols/InstalledPackages.svg | 0 .../Assets/Symbols/PackagesBundle.svg | 0 .../Assets/Symbols/Sources.svg | 0 .../Assets/Symbols/add_to.svg | 0 .../Assets/Symbols/android.svg | 0 .../Assets/Symbols/apt.svg | 0 .../Assets/Symbols/backward.svg | 0 .../Assets/Symbols/bucket.svg | 0 .../Assets/Symbols/buggy.svg | 0 .../Assets/Symbols/bun.svg | 0 .../Assets/Symbols/checksum.svg | 0 .../Assets/Symbols/choco.svg | 0 .../Assets/Symbols/clipboard_list.svg | 0 .../Assets/Symbols/close_round.svg | 0 .../Assets/Symbols/collapse.svg | 0 .../Assets/Symbols/console.svg | 0 .../Assets/Symbols/copy.svg | 0 .../Assets/Symbols/cross.svg | 0 .../Assets/Symbols/delete.svg | 0 .../Assets/Symbols/disk.svg | 0 .../Assets/Symbols/dnf.svg | 0 .../Assets/Symbols/dotnet.svg | 0 .../Assets/Symbols/download.svg | 0 .../Assets/Symbols/empty.svg | 0 .../Assets/Symbols/expand.svg | 0 .../Assets/Symbols/experimental.svg | 0 .../Assets/Symbols/flatpak.svg | 0 .../Assets/Symbols/forward.svg | 0 .../Assets/Symbols/gog.svg | 0 .../Assets/Symbols/help.svg | 0 .../Assets/Symbols/history.svg | 0 .../Assets/Symbols/home.svg | 0 .../Assets/Symbols/homebrew.svg | 0 .../Assets/Symbols/icomoon-project.json | 0 .../Assets/Symbols/id.svg | 0 .../Assets/Symbols/info_round.svg | 0 .../Assets/Symbols/installed.svg | 0 .../Assets/Symbols/installed_filled.svg | 0 .../Assets/Symbols/interactive.svg | 0 .../Assets/Symbols/launch.svg | 0 .../Assets/Symbols/loading.svg | 0 .../Assets/Symbols/loading_filled.svg | 0 .../Assets/Symbols/local_pc.svg | 0 .../Assets/Symbols/megaphone.svg | 0 .../Assets/Symbols/ms_store.svg | 0 .../Assets/Symbols/node.svg | 0 .../Assets/Symbols/open_folder.svg | 0 .../Assets/Symbols/options.svg | 0 .../Assets/Symbols/package.svg | 0 .../Assets/Symbols/pacman.svg | 0 .../Assets/Symbols/pin.svg | 0 .../Assets/Symbols/pin_filled.svg | 0 .../Assets/Symbols/powershell.svg | 0 .../Assets/Symbols/python.svg | 0 .../Assets/Symbols/reload.svg | 0 .../Assets/Symbols/rust.svg | 0 .../Assets/Symbols/sandclock.svg | 0 .../Assets/Symbols/save_as.svg | 0 .../Assets/Symbols/scoop.svg | 0 .../Assets/Symbols/search.svg | 0 .../Assets/Symbols/search_filled.svg | 0 .../Assets/Symbols/settings.svg | 0 .../Assets/Symbols/share.svg | 0 .../Assets/Symbols/skip.svg | 0 .../Assets/Symbols/snap.svg | 0 .../Assets/Symbols/steam.svg | 0 .../Assets/Symbols/sys_tray.svg | 0 .../Assets/Symbols/uac.svg | 0 .../Assets/Symbols/undelete.svg | 0 .../Assets/Symbols/update.svg | 0 .../Assets/Symbols/upgradable.svg | 0 .../Assets/Symbols/upgradable_filled.svg | 0 .../Assets/Symbols/uplay.svg | 0 .../Assets/Symbols/vcpkg.svg | 0 .../Assets/Symbols/version.svg | 0 .../Assets/Symbols/warning.svg | 0 .../Assets/Symbols/warning_filled.svg | 0 .../Assets/Symbols/warning_round.svg | 0 .../Assets/Symbols/winget.svg | 0 .../Assets/Utilities/install_scoop.cmd | 0 .../Assets/Utilities/install_scoop.ps1 | 0 .../Assets/Utilities/scoop_cleanup.cmd | 0 .../Assets/Utilities/uninstall_scoop.cmd | 0 .../Assets/Wide310x150Logo.png | Bin src/{UniGetUI => SharedAssets}/icon.ico | Bin src/UniGetUI.Avalonia/AvaloniaCliHandler.cs | 6 +- .../Infrastructure/AvaloniaAutoUpdater.cs | 3 +- .../UniGetUI.Avalonia.csproj | 68 +- .../SettingsPages/Interface_PViewModel.cs | 7 - .../Pages/SettingsPages/Interface_P.axaml | 9 - src/UniGetUI.Core.Data/CoreData.cs | 4 +- .../SettingsEngine_Names.cs | 4 - src/UniGetUI.Tests/AutoUpdaterTests.cs | 25 +- src/UniGetUI.Tests/CLIHandlerTests.cs | 61 +- src/UniGetUI.Tests/ModernAppLauncherTests.cs | 184 -- src/UniGetUI.Tests/UniGetUI.Tests.csproj | 4 +- src/UniGetUI.Windows.slnx | 4 - src/UniGetUI/.vscode/launch.json | 15 - src/UniGetUI/.vscode/settings.json | 6 - src/UniGetUI/.vscode/tasks.json | 22 - src/UniGetUI/App.xaml | 985 --------- src/UniGetUI/App.xaml.cs | 750 ------- src/UniGetUI/AppOperationHelper.cs | 554 ------ src/UniGetUI/AutoUpdater.Helpers.cs | 326 --- src/UniGetUI/AutoUpdater.cs | 972 --------- src/UniGetUI/CLIHandler.cs | 255 --- src/UniGetUI/Controls/CustomNavViewItem.cs | 110 -- src/UniGetUI/Controls/DialogCloseButton.xaml | 35 - .../Controls/DialogCloseButton.xaml.cs | 22 - src/UniGetUI/Controls/LocalIcon.cs | 81 - src/UniGetUI/Controls/MenuForPackage.cs | 129 -- .../Controls/ObservablePackageCollection.cs | 132 -- .../OperationWidgets/OperationBadge.cs | 34 - .../OperationWidgets/OperationControl.cs | 815 -------- src/UniGetUI/Controls/PackageItemContainer.cs | 81 - src/UniGetUI/Controls/PackageWrapper.cs | 362 ---- .../Controls/SettingsWidgets/ButtonCard.cs | 49 - .../SettingsWidgets/CheckboxButtonCard.cs | 110 -- .../Controls/SettingsWidgets/CheckboxCard.cs | 179 -- .../Controls/SettingsWidgets/ComboboxCard.cs | 94 - .../SettingsWidgets/SecureCheckboxCard.cs | 157 -- .../SettingsWidgets/SettingsPageButton.cs | 55 - .../Controls/SettingsWidgets/TextboxCard.cs | 84 - src/UniGetUI/Controls/SourceManager.xaml | 140 -- src/UniGetUI/Controls/SourceManager.xaml.cs | 232 --- .../Controls/TranslatedTextBlock.xaml | 21 - .../Controls/TranslatedTextBlock.xaml.cs | 75 - src/UniGetUI/CrashHandler.cs | 250 --- src/UniGetUI/CrashReportWindow.xaml | 68 - src/UniGetUI/CrashReportWindow.xaml.cs | 91 - src/UniGetUI/EntryPoint.cs | 176 -- src/UniGetUI/InternalsVisibleTo.cs | 3 - src/UniGetUI/MainWindow.xaml | 178 -- src/UniGetUI/MainWindow.xaml.cs | 1104 ----------- src/UniGetUI/ModernAppLauncher.cs | 122 -- src/UniGetUI/Package.appxmanifest | 51 - .../Pages/AboutPages/AboutUniGetUI.xaml | 76 - .../Pages/AboutPages/AboutUniGetUI.xaml.cs | 28 - .../Pages/AboutPages/Contributors.xaml | 70 - .../Pages/AboutPages/Contributors.xaml.cs | 32 - .../Pages/AboutPages/ThirdPartyLicenses.xaml | 70 - .../AboutPages/ThirdPartyLicenses.xaml.cs | 46 - .../Pages/AboutPages/Translators.xaml | 86 - .../Pages/AboutPages/Translators.xaml.cs | 27 - .../Pages/DialogPages/AboutUniGetUI.xaml | 60 - .../Pages/DialogPages/AboutUniGetUI.xaml.cs | 61 - .../Pages/DialogPages/DesktopShortcuts.xaml | 208 -- .../DialogPages/DesktopShortcuts.xaml.cs | 190 -- .../Pages/DialogPages/DialogHelper_Generic.cs | 750 ------- .../DialogHelper_Infrastructure.cs | 307 --- .../DialogPages/DialogHelper_Operations.cs | 37 - .../DialogPages/DialogHelper_Packages.cs | 348 ---- .../Pages/DialogPages/IgnoredUpdates.xaml | 157 -- .../Pages/DialogPages/IgnoredUpdates.xaml.cs | 188 -- .../DialogPages/InstallOptions_Manager.xaml | 233 --- .../InstallOptions_Manager.xaml.cs | 337 ---- .../DialogPages/InstallOptions_Package.xaml | 735 ------- .../InstallOptions_Package.xaml.cs | 605 ------ .../DialogPages/OperationFailedDialog.xaml | 50 - .../DialogPages/OperationFailedDialog.xaml.cs | 118 -- .../DialogPages/OperationLiveLogPage.xaml | 43 - .../DialogPages/OperationLiveLogPage.xaml.cs | 85 - .../Pages/DialogPages/PackageDetailsPage.xaml | 325 --- .../DialogPages/PackageDetailsPage.xaml.cs | 780 -------- .../Pages/DialogPages/ReleaseNotes.xaml | 41 - .../Pages/DialogPages/ReleaseNotes.xaml.cs | 48 - src/UniGetUI/Pages/HelpPage.xaml | 105 - src/UniGetUI/Pages/HelpPage.xaml.cs | 120 -- src/UniGetUI/Pages/LogPages/LogPage.xaml | 73 - src/UniGetUI/Pages/LogPages/LogPage.xaml.cs | 109 - .../Pages/LogPages/ManagerLogsPage.cs | 91 - .../Pages/LogPages/OperationHistoryPage.cs | 33 - .../Pages/LogPages/UniGetUILogPage.cs | 135 -- src/UniGetUI/Pages/MainView.xaml | 465 ----- src/UniGetUI/Pages/MainView.xaml.cs | 644 ------ .../PageInterfaces/IEnterLeaveListener.cs | 7 - .../PageInterfaces/IInnerNavigationPage.cs | 7 - .../IKeyboardShortcutListener.cs | 24 - .../Pages/PageInterfaces/ISearchBoxPage.cs | 16 - .../GeneralPages/Administrator.xaml | 140 -- .../GeneralPages/Administrator.xaml.cs | 62 - .../SettingsPages/GeneralPages/Backup.xaml | 232 --- .../SettingsPages/GeneralPages/Backup.xaml.cs | 360 ---- .../GeneralPages/Experimental.xaml | 107 - .../GeneralPages/Experimental.xaml.cs | 34 - .../SettingsPages/GeneralPages/General.xaml | 160 -- .../GeneralPages/General.xaml.cs | 152 -- .../GeneralPages/Interface_P.xaml | 100 - .../GeneralPages/Interface_P.xaml.cs | 113 -- .../SettingsPages/GeneralPages/Internet.xaml | 179 -- .../GeneralPages/Internet.xaml.cs | 146 -- .../GeneralPages/Notifications.xaml | 84 - .../GeneralPages/Notifications.xaml.cs | 59 - .../GeneralPages/Operations.xaml | 112 -- .../GeneralPages/Operations.xaml.cs | 65 - .../GeneralPages/SettingsHomepage.xaml | 136 -- .../GeneralPages/SettingsHomepage.xaml.cs | 62 - .../SettingsPages/GeneralPages/Updates.xaml | 160 -- .../GeneralPages/Updates.xaml.cs | 193 -- .../Pages/SettingsPages/ISettingsPage.cs | 12 - .../ManagersPages/ManagersHomepage.xaml | 24 - .../ManagersPages/ManagersHomepage.xaml.cs | 192 -- .../ManagersPages/PackageManager.xaml | 220 --- .../ManagersPages/PackageManager.xaml.cs | 784 -------- .../Pages/SettingsPages/SettingsBasePage.xaml | 83 - .../SettingsPages/SettingsBasePage.xaml.cs | 166 -- .../SoftwarePages/AbstractPackagesPage.xaml | 1310 ------------ .../AbstractPackagesPage.xaml.cs | 1753 ----------------- .../SoftwarePages/DiscoverSoftwarePage.cs | 341 ---- .../SoftwarePages/InstalledPackagesPage.cs | 522 ----- .../Pages/SoftwarePages/PackageBundlesPage.cs | 1058 ---------- .../SoftwarePages/SoftwareUpdatesPage.cs | 673 ------- .../PublishProfiles/win10-x64.pubxml | 20 - src/UniGetUI/Properties/Resources.Designer.cs | 63 - src/UniGetUI/Properties/Resources.resx | 101 - src/UniGetUI/Properties/launchSettings.json | 11 - src/UniGetUI/Services/BackgroundLoginApi.cs | 102 - src/UniGetUI/Services/GitHubAuthService.cs | 260 --- src/UniGetUI/Services/GitHubBackupService.cs | 141 -- src/UniGetUI/Services/Secrets.cs | 19 - src/UniGetUI/Services/UserAvatar.cs | 314 --- src/UniGetUI/Services/generate-secrets.ps1 | 38 - src/UniGetUI/Themes/Generic.xaml | 20 - src/UniGetUI/UniGetUI.csproj | 482 ----- src/UniGetUI/UniGetUI.csproj.user | 61 - src/UniGetUI/WinUiHeadlessHost.cs | 22 - src/UniGetUI/app.manifest | 23 - .../automation/cli-e2e.manifest.windows.json | 6 +- testing/automation/run-cli-e2e.ps1 | 4 +- 312 files changed, 428 insertions(+), 28662 deletions(-) create mode 100644 src/Shared/AutoUpdater.Helpers.cs rename src/{UniGetUI => SharedAssets}/Assets/Images/admin_color.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/agreement.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/alert_laptop.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/cancel.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/cargo_color.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/checked_laptop.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/choco_color.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/coffee.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/console_color.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/desktop_download.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/dotnet_color.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/finish.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/github.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/hacker.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/icon.bmp (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/icon.ico (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/icon.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/icon_unsquare.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/infocolor.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/kofi.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/node_color.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/package_color.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/pin_color.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/pip_color.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/powershell_color.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/restart_color.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/rocket.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/save.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/scoop_color.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/settings_gear.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/shield_green.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/shield_question.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/shield_red.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/shield_reload.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/shield_yellow.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/simple_user.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/tick.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/tray_blue_black_legacy.ico (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/tray_blue_white_legacy.ico (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/tray_empty_black_legacy.ico (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/tray_empty_white_legacy.ico (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/tray_green_black_legacy.ico (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/tray_green_white_legacy.ico (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/tray_orange_black_legacy.ico (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/tray_orange_white_legacy.ico (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/tray_turquoise_black_legacy.ico (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/tray_turquoise_white_legacy.ico (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/update_pc_color.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/vcpkg_color.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/warn.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/winget_color.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/workstation.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Images/youtube.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/SplashScreen.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/SplashScreen.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/SplashScreen.theme-dark.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/SplashScreen.theme-dark.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Square150x150Logo.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Square44x44Logo.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/StoreLogo.png (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/DiscoverPackage.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/Filter.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/Font/Read Me.txt (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/Font/demo-files/demo.css (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/Font/demo-files/demo.js (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/Font/demo.html (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/Font/fonts/UniGetUI-Symbols.eot (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/Font/fonts/UniGetUI-Symbols.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/Font/fonts/UniGetUI-Symbols.ttf (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/Font/fonts/UniGetUI-Symbols.woff (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/Font/selection.json (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/Font/style.css (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/InstalledPackages.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/PackagesBundle.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/Sources.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/add_to.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/android.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/apt.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/backward.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/bucket.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/buggy.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/bun.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/checksum.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/choco.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/clipboard_list.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/close_round.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/collapse.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/console.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/copy.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/cross.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/delete.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/disk.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/dnf.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/dotnet.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/download.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/empty.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/expand.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/experimental.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/flatpak.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/forward.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/gog.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/help.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/history.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/home.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/homebrew.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/icomoon-project.json (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/id.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/info_round.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/installed.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/installed_filled.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/interactive.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/launch.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/loading.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/loading_filled.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/local_pc.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/megaphone.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/ms_store.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/node.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/open_folder.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/options.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/package.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/pacman.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/pin.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/pin_filled.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/powershell.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/python.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/reload.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/rust.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/sandclock.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/save_as.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/scoop.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/search.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/search_filled.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/settings.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/share.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/skip.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/snap.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/steam.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/sys_tray.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/uac.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/undelete.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/update.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/upgradable.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/upgradable_filled.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/uplay.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/vcpkg.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/version.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/warning.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/warning_filled.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/warning_round.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Symbols/winget.svg (100%) rename src/{UniGetUI => SharedAssets}/Assets/Utilities/install_scoop.cmd (100%) rename src/{UniGetUI => SharedAssets}/Assets/Utilities/install_scoop.ps1 (100%) rename src/{UniGetUI => SharedAssets}/Assets/Utilities/scoop_cleanup.cmd (100%) rename src/{UniGetUI => SharedAssets}/Assets/Utilities/uninstall_scoop.cmd (100%) rename src/{UniGetUI => SharedAssets}/Assets/Wide310x150Logo.png (100%) rename src/{UniGetUI => SharedAssets}/icon.ico (100%) delete mode 100644 src/UniGetUI.Tests/ModernAppLauncherTests.cs delete mode 100644 src/UniGetUI/.vscode/launch.json delete mode 100644 src/UniGetUI/.vscode/settings.json delete mode 100644 src/UniGetUI/.vscode/tasks.json delete mode 100644 src/UniGetUI/App.xaml delete mode 100644 src/UniGetUI/App.xaml.cs delete mode 100644 src/UniGetUI/AppOperationHelper.cs delete mode 100644 src/UniGetUI/AutoUpdater.Helpers.cs delete mode 100644 src/UniGetUI/AutoUpdater.cs delete mode 100644 src/UniGetUI/CLIHandler.cs delete mode 100644 src/UniGetUI/Controls/CustomNavViewItem.cs delete mode 100644 src/UniGetUI/Controls/DialogCloseButton.xaml delete mode 100644 src/UniGetUI/Controls/DialogCloseButton.xaml.cs delete mode 100644 src/UniGetUI/Controls/LocalIcon.cs delete mode 100644 src/UniGetUI/Controls/MenuForPackage.cs delete mode 100644 src/UniGetUI/Controls/ObservablePackageCollection.cs delete mode 100644 src/UniGetUI/Controls/OperationWidgets/OperationBadge.cs delete mode 100644 src/UniGetUI/Controls/OperationWidgets/OperationControl.cs delete mode 100644 src/UniGetUI/Controls/PackageItemContainer.cs delete mode 100644 src/UniGetUI/Controls/PackageWrapper.cs delete mode 100644 src/UniGetUI/Controls/SettingsWidgets/ButtonCard.cs delete mode 100644 src/UniGetUI/Controls/SettingsWidgets/CheckboxButtonCard.cs delete mode 100644 src/UniGetUI/Controls/SettingsWidgets/CheckboxCard.cs delete mode 100644 src/UniGetUI/Controls/SettingsWidgets/ComboboxCard.cs delete mode 100644 src/UniGetUI/Controls/SettingsWidgets/SecureCheckboxCard.cs delete mode 100644 src/UniGetUI/Controls/SettingsWidgets/SettingsPageButton.cs delete mode 100644 src/UniGetUI/Controls/SettingsWidgets/TextboxCard.cs delete mode 100644 src/UniGetUI/Controls/SourceManager.xaml delete mode 100644 src/UniGetUI/Controls/SourceManager.xaml.cs delete mode 100644 src/UniGetUI/Controls/TranslatedTextBlock.xaml delete mode 100644 src/UniGetUI/Controls/TranslatedTextBlock.xaml.cs delete mode 100644 src/UniGetUI/CrashHandler.cs delete mode 100644 src/UniGetUI/CrashReportWindow.xaml delete mode 100644 src/UniGetUI/CrashReportWindow.xaml.cs delete mode 100644 src/UniGetUI/EntryPoint.cs delete mode 100644 src/UniGetUI/InternalsVisibleTo.cs delete mode 100644 src/UniGetUI/MainWindow.xaml delete mode 100644 src/UniGetUI/MainWindow.xaml.cs delete mode 100644 src/UniGetUI/ModernAppLauncher.cs delete mode 100644 src/UniGetUI/Package.appxmanifest delete mode 100644 src/UniGetUI/Pages/AboutPages/AboutUniGetUI.xaml delete mode 100644 src/UniGetUI/Pages/AboutPages/AboutUniGetUI.xaml.cs delete mode 100644 src/UniGetUI/Pages/AboutPages/Contributors.xaml delete mode 100644 src/UniGetUI/Pages/AboutPages/Contributors.xaml.cs delete mode 100644 src/UniGetUI/Pages/AboutPages/ThirdPartyLicenses.xaml delete mode 100644 src/UniGetUI/Pages/AboutPages/ThirdPartyLicenses.xaml.cs delete mode 100644 src/UniGetUI/Pages/AboutPages/Translators.xaml delete mode 100644 src/UniGetUI/Pages/AboutPages/Translators.xaml.cs delete mode 100644 src/UniGetUI/Pages/DialogPages/AboutUniGetUI.xaml delete mode 100644 src/UniGetUI/Pages/DialogPages/AboutUniGetUI.xaml.cs delete mode 100644 src/UniGetUI/Pages/DialogPages/DesktopShortcuts.xaml delete mode 100644 src/UniGetUI/Pages/DialogPages/DesktopShortcuts.xaml.cs delete mode 100644 src/UniGetUI/Pages/DialogPages/DialogHelper_Generic.cs delete mode 100644 src/UniGetUI/Pages/DialogPages/DialogHelper_Infrastructure.cs delete mode 100644 src/UniGetUI/Pages/DialogPages/DialogHelper_Operations.cs delete mode 100644 src/UniGetUI/Pages/DialogPages/DialogHelper_Packages.cs delete mode 100644 src/UniGetUI/Pages/DialogPages/IgnoredUpdates.xaml delete mode 100644 src/UniGetUI/Pages/DialogPages/IgnoredUpdates.xaml.cs delete mode 100644 src/UniGetUI/Pages/DialogPages/InstallOptions_Manager.xaml delete mode 100644 src/UniGetUI/Pages/DialogPages/InstallOptions_Manager.xaml.cs delete mode 100644 src/UniGetUI/Pages/DialogPages/InstallOptions_Package.xaml delete mode 100644 src/UniGetUI/Pages/DialogPages/InstallOptions_Package.xaml.cs delete mode 100644 src/UniGetUI/Pages/DialogPages/OperationFailedDialog.xaml delete mode 100644 src/UniGetUI/Pages/DialogPages/OperationFailedDialog.xaml.cs delete mode 100644 src/UniGetUI/Pages/DialogPages/OperationLiveLogPage.xaml delete mode 100644 src/UniGetUI/Pages/DialogPages/OperationLiveLogPage.xaml.cs delete mode 100644 src/UniGetUI/Pages/DialogPages/PackageDetailsPage.xaml delete mode 100644 src/UniGetUI/Pages/DialogPages/PackageDetailsPage.xaml.cs delete mode 100644 src/UniGetUI/Pages/DialogPages/ReleaseNotes.xaml delete mode 100644 src/UniGetUI/Pages/DialogPages/ReleaseNotes.xaml.cs delete mode 100644 src/UniGetUI/Pages/HelpPage.xaml delete mode 100644 src/UniGetUI/Pages/HelpPage.xaml.cs delete mode 100644 src/UniGetUI/Pages/LogPages/LogPage.xaml delete mode 100644 src/UniGetUI/Pages/LogPages/LogPage.xaml.cs delete mode 100644 src/UniGetUI/Pages/LogPages/ManagerLogsPage.cs delete mode 100644 src/UniGetUI/Pages/LogPages/OperationHistoryPage.cs delete mode 100644 src/UniGetUI/Pages/LogPages/UniGetUILogPage.cs delete mode 100644 src/UniGetUI/Pages/MainView.xaml delete mode 100644 src/UniGetUI/Pages/MainView.xaml.cs delete mode 100644 src/UniGetUI/Pages/PageInterfaces/IEnterLeaveListener.cs delete mode 100644 src/UniGetUI/Pages/PageInterfaces/IInnerNavigationPage.cs delete mode 100644 src/UniGetUI/Pages/PageInterfaces/IKeyboardShortcutListener.cs delete mode 100644 src/UniGetUI/Pages/PageInterfaces/ISearchBoxPage.cs delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/Administrator.xaml delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/Administrator.xaml.cs delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml.cs delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/Experimental.xaml delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/Experimental.xaml.cs delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/General.xaml delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/General.xaml.cs delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/Interface_P.xaml delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/Interface_P.xaml.cs delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/Internet.xaml delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/Internet.xaml.cs delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/Notifications.xaml delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/Notifications.xaml.cs delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/Operations.xaml delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/Operations.xaml.cs delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/SettingsHomepage.xaml delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/SettingsHomepage.xaml.cs delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/Updates.xaml delete mode 100644 src/UniGetUI/Pages/SettingsPages/GeneralPages/Updates.xaml.cs delete mode 100644 src/UniGetUI/Pages/SettingsPages/ISettingsPage.cs delete mode 100644 src/UniGetUI/Pages/SettingsPages/ManagersPages/ManagersHomepage.xaml delete mode 100644 src/UniGetUI/Pages/SettingsPages/ManagersPages/ManagersHomepage.xaml.cs delete mode 100644 src/UniGetUI/Pages/SettingsPages/ManagersPages/PackageManager.xaml delete mode 100644 src/UniGetUI/Pages/SettingsPages/ManagersPages/PackageManager.xaml.cs delete mode 100644 src/UniGetUI/Pages/SettingsPages/SettingsBasePage.xaml delete mode 100644 src/UniGetUI/Pages/SettingsPages/SettingsBasePage.xaml.cs delete mode 100644 src/UniGetUI/Pages/SoftwarePages/AbstractPackagesPage.xaml delete mode 100644 src/UniGetUI/Pages/SoftwarePages/AbstractPackagesPage.xaml.cs delete mode 100644 src/UniGetUI/Pages/SoftwarePages/DiscoverSoftwarePage.cs delete mode 100644 src/UniGetUI/Pages/SoftwarePages/InstalledPackagesPage.cs delete mode 100644 src/UniGetUI/Pages/SoftwarePages/PackageBundlesPage.cs delete mode 100644 src/UniGetUI/Pages/SoftwarePages/SoftwareUpdatesPage.cs delete mode 100644 src/UniGetUI/Properties/PublishProfiles/win10-x64.pubxml delete mode 100644 src/UniGetUI/Properties/Resources.Designer.cs delete mode 100644 src/UniGetUI/Properties/Resources.resx delete mode 100644 src/UniGetUI/Properties/launchSettings.json delete mode 100644 src/UniGetUI/Services/BackgroundLoginApi.cs delete mode 100644 src/UniGetUI/Services/GitHubAuthService.cs delete mode 100644 src/UniGetUI/Services/GitHubBackupService.cs delete mode 100644 src/UniGetUI/Services/Secrets.cs delete mode 100644 src/UniGetUI/Services/UserAvatar.cs delete mode 100644 src/UniGetUI/Services/generate-secrets.ps1 delete mode 100644 src/UniGetUI/Themes/Generic.xaml delete mode 100644 src/UniGetUI/UniGetUI.csproj delete mode 100644 src/UniGetUI/UniGetUI.csproj.user delete mode 100644 src/UniGetUI/WinUiHeadlessHost.cs delete mode 100644 src/UniGetUI/app.manifest diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 775b1201c1..18c7672fa3 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -187,15 +187,23 @@ jobs: } $TargetFramework = "$PortableTargetFramework-windows$WindowsTargetPlatformVersion" - dotnet publish src/UniGetUI/UniGetUI.csproj /noLogo /p:Configuration=Release /p:Platform=$Platform -p:RuntimeIdentifier=win-$Platform -v m - if ($LASTEXITCODE -ne 0) { throw "dotnet publish WinUI failed" } + dotnet publish src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj /noLogo /p:Configuration=Release /p:Platform=$Platform -p:RuntimeIdentifier=win-$Platform -v m + if ($LASTEXITCODE -ne 0) { throw "dotnet publish Avalonia failed" } # Stage binaries - $PublishDir = "src/UniGetUI/bin/$Platform/Release/$TargetFramework/win-$Platform/publish" + $PublishDir = "src/UniGetUI.Avalonia/bin/$Platform/Release/$TargetFramework/win-$Platform/publish" if (Test-Path "unigetui_bin") { Remove-Item "unigetui_bin" -Recurse -Force } New-Item "unigetui_bin" -ItemType Directory | Out-Null Get-ChildItem $PublishDir | Move-Item -Destination "unigetui_bin" -Force + if (Test-Path "unigetui_bin/UniGetUI.Avalonia.exe") { + Move-Item "unigetui_bin/UniGetUI.Avalonia.exe" "unigetui_bin/UniGetUI.exe" -Force + } + + if (-not (Test-Path "unigetui_bin/UniGetUI.exe")) { + throw "Windows app host was not produced at unigetui_bin/UniGetUI.exe" + } + $MaxShippedPdbSizeBytes = 1MB $PdbsToRemove = Get-ChildItem "unigetui_bin" -Filter "*.pdb" -File -Recurse | Where-Object { $_.Length -gt $MaxShippedPdbSizeBytes diff --git a/.github/workflows/cli-headless-e2e.yml b/.github/workflows/cli-headless-e2e.yml index bcc2cd50ea..2790476b2c 100644 --- a/.github/workflows/cli-headless-e2e.yml +++ b/.github/workflows/cli-headless-e2e.yml @@ -21,10 +21,10 @@ jobs: fail-fast: false matrix: include: - - name: Windows (WinUI 3) + - name: Windows (Avalonia) os: windows-latest solution: UniGetUI.Windows.slnx - daemon_project: UniGetUI/UniGetUI.csproj + daemon_project: UniGetUI.Avalonia/UniGetUI.Avalonia.csproj daemon_build_args: '-p:Platform=x64' manifest: testing/automation/cli-e2e.manifest.windows.json - name: Linux (Avalonia) diff --git a/.github/workflows/dotnet-test.yml b/.github/workflows/dotnet-test.yml index f5fb5b5b74..8fdf55bb38 100644 --- a/.github/workflows/dotnet-test.yml +++ b/.github/workflows/dotnet-test.yml @@ -62,11 +62,11 @@ jobs: - name: Check whitespace formatting run: dotnet format whitespace src --folder --verify-no-changes --verbosity minimal - - name: Check code style formatting (Windows solution) + - name: Check code style formatting (Avalonia Windows solution) working-directory: src run: dotnet format style UniGetUI.Windows.slnx --no-restore --verify-no-changes --verbosity minimal - - name: Build Windows solution + - name: Build Avalonia Windows solution working-directory: src run: dotnet build UniGetUI.Windows.slnx --no-restore --verbosity minimal /p:Platform=x64 @@ -75,4 +75,3 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} run: dotnet test UniGetUI.Windows.slnx --no-restore --verbosity q --nologo /p:Platform=x64 - diff --git a/AGENTS.md b/AGENTS.md index ba589756a0..70c54919ba 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,17 +2,18 @@ ## Project Overview -UniGetUI is a WinUI 3 desktop app (C#/.NET 10, Windows App SDK) providing a GUI for CLI package managers (WinGet, Scoop, Chocolatey, Pip, Npm, .NET Tool, PowerShell Gallery, Cargo, Vcpkg). +UniGetUI is an Avalonia desktop app (C#/.NET 10) providing a GUI for CLI package managers (WinGet, Scoop, Chocolatey, Pip, Npm, .NET Tool, PowerShell Gallery, Cargo, Vcpkg). Solution entry points: -- `src/UniGetUI.Windows.slnx` - official Windows solution; builds the WinUI 3 launcher/classic app and the Avalonia app -- `src/UniGetUI.Avalonia.slnx` - experimental cross-platform Avalonia port +- `src/UniGetUI.Windows.slnx` - official Windows solution; builds the Avalonia app and Windows-specific package-manager integrations +- `src/UniGetUI.Avalonia.slnx` - cross-platform Avalonia solution ## Architecture The codebase follows a **layered, modular structure** with ~40 projects: -- **`UniGetUI/`** - WinUI 3 entry point, XAML pages, controls, and app shell (`EntryPoint.cs`, `MainWindow.xaml`) +- **`UniGetUI.Avalonia/`** - Avalonia entry point, AXAML pages, controls, and app shell (`Program.cs`, `Views/MainWindow.axaml`) +- **`SharedAssets/`** - shared app icons, symbols, splash images, and utility scripts used by packaging - **`UniGetUI.Core.*`** - Shared infrastructure: `Logger`, `Settings`, `Tools` (includes `CoreTools.Translate()`), `IconEngine`, `LanguageEngine` - **`UniGetUI.PackageEngine.Interfaces`** - Contracts: `IPackageManager`, `IPackage`, `IManagerSource`, `IPackageDetails` - **`UniGetUI.PackageEngine.PackageManagerClasses`** - Base implementations: `PackageManager` (abstract), `Package`, helpers (`BasePkgDetailsHelper`, `BasePkgOperationHelper`, `BaseSourceHelper`) @@ -41,15 +42,15 @@ The constructor sets `Capabilities`, `Properties`, and wires the helpers. See `s ```shell # Restore & test (from src/) -dotnet restore -dotnet test --verbosity q --nologo +dotnet restore UniGetUI.Windows.slnx +dotnet test UniGetUI.Windows.slnx --verbosity q --nologo /p:Platform=x64 # Publish release build -dotnet publish src/UniGetUI/UniGetUI.csproj /p:Configuration=Release /p:Platform=x64 +dotnet publish src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj /p:Configuration=Release /p:Platform=x64 -p:RuntimeIdentifier=win-x64 ``` - Target framework: `net10.0-windows10.0.26100.0` (min `10.0.19041`) -- Build generates secrets via `src/UniGetUI/Services/generate-secrets.ps1` and integrity tree via `scripts/generate-integrity-tree.ps1` +- Build generates secrets via `src/UniGetUI.Avalonia/Infrastructure/generate-secrets.ps1` and integrity tree via `scripts/generate-integrity-tree.ps1` - Self-contained, publish-trimmed (partial), Windows App SDK self-contained - Tests use **xUnit** (`[Fact]`, `Assert.*`) @@ -95,7 +96,7 @@ Use `CoreTools.Translate("text")` for all user-facing strings. Parameterized: `C | Purpose | Path | |---|---| | Windows solution | `src/UniGetUI.Windows.slnx` | -| Experimental cross-platform solution | `src/UniGetUI.Avalonia.slnx` | +| Cross-platform solution | `src/UniGetUI.Avalonia.slnx` | | Shared build props | `src/Directory.Build.props` | | Version info | `src/SharedAssemblyInfo.cs` | | Manager interface | `src/UniGetUI.PackageEngine.Interfaces/IPackageManager.cs` | diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f81bcca02a..b5cc16b1c0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -41,7 +41,7 @@ Before reading: All of the rules below are guidelines, which means that they sho ## Formatting: - Run `pwsh ./scripts/install-git-hooks.ps1` once after cloning to enable the repository pre-commit hook. - The pre-commit hook runs `dotnet format whitespace src --folder` on staged files under `src` when the `dotnet` CLI is available, and stops the commit if it had to rewrite files so you can review the changes and commit again. -- CI enforces whitespace formatting with `dotnet format whitespace src --folder --verify-no-changes` and code-style verification with `dotnet format style src/UniGetUI.Windows.slnx --no-restore --verify-no-changes` in `.github/workflows/dotnet-test.yml`. +- CI enforces whitespace formatting with `dotnet format whitespace src --folder --verify-no-changes` and code-style verification with `dotnet format style src/UniGetUI.Windows.slnx --no-restore --verify-no-changes` against the Avalonia-only Windows solution in `.github/workflows/dotnet-test.yml`. - The pre-commit hook intentionally does not run `dotnet format style` because solution loading makes it take roughly the same time for one staged C# file as for the full solution. - If you want to check the same style rules locally before pushing, run `dotnet format style src/UniGetUI.Windows.slnx --no-restore --verify-no-changes` from the repository root. - If you want to prepare a dedicated formatting-only commit, run `dotnet format whitespace src --folder` from the repository root. diff --git a/UniGetUI.iss b/UniGetUI.iss index d49d3f3289..0fc3687582 100644 --- a/UniGetUI.iss +++ b/UniGetUI.iss @@ -44,7 +44,7 @@ SignTool=azsign SignedUninstaller=yes SignedUninstallerDir=InstallerExtras\ MinVersion=10.0 -SetupIconFile=src\UniGetUI\Assets\Images\icon.ico +SetupIconFile=src\SharedAssets\Assets\Images\icon.ico UninstallDisplayIcon={app}\UniGetUI.exe Compression=lzma SolidCompression=yes @@ -266,6 +266,16 @@ Root: HKA; Subkey: "Software\Classes\UniGetUI.PackageBundle\DefaultIcon"; ValueT Root: HKA; Subkey: "Software\Classes\UniGetUI.PackageBundle\shell\open\command"; ValueType: string; ValueData: """{app}\{#MyAppExeName}"" ""%1"""; Flags: uninsdeletekey; Tasks: regularinstall; +[InstallDelete] +Type: filesandordirs; Name: "{app}\Avalonia" +Type: filesandordirs; Name: "{app}\Assets" +Type: files; Name: "{app}\UniGetUI.Avalonia.exe" +Type: files; Name: "{app}\*.dll" +Type: files; Name: "{app}\*.pdb" +Type: files; Name: "{app}\*.pri" +Type: files; Name: "{app}\*.xbf" +Type: files; Name: "{app}\*.json" + [Files] ; Deploy installer for autorepair jobs (unless disabled) Source: "{srcexe}"; DestDir: "{app}"; DestName: "UniGetUI.Installer.exe"; Flags: external ignoreversion; Tasks: regularinstall; Check: not CmdLineParamExists('/NoDeployInstaller'); diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 4fbe15eee3..3b6c6ed886 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -38,7 +38,7 @@ $ErrorActionPreference = 'Stop' $RepoRoot = Resolve-Path (Join-Path $PSScriptRoot "..") $SrcDir = Join-Path $RepoRoot "src" $WindowsSolution = Join-Path $SrcDir "UniGetUI.Windows.slnx" -$PublishProject = Join-Path $SrcDir "UniGetUI" "UniGetUI.csproj" +$PublishProject = Join-Path $SrcDir "UniGetUI.Avalonia" "UniGetUI.Avalonia.csproj" $BinDir = Join-Path $RepoRoot "unigetui_bin" $BuildPropsPath = Join-Path $SrcDir "Directory.Build.props" [xml] $BuildProps = Get-Content $BuildPropsPath @@ -50,7 +50,7 @@ if ([string]::IsNullOrWhiteSpace($PortableTargetFramework) -or [string]::IsNullO } $TargetFramework = "$PortableTargetFramework-windows$WindowsTargetPlatformVersion" -$PublishDir = Join-Path $SrcDir "UniGetUI" "bin" $Platform $Configuration $TargetFramework "win-$Platform" "publish" +$PublishDir = Join-Path $SrcDir "UniGetUI.Avalonia" "bin" $Platform $Configuration $TargetFramework "win-$Platform" "publish" # --- Version stamping --- if ($Version) { @@ -77,9 +77,9 @@ if (-not $SkipTests) { Write-Host "`n=== Publishing $Configuration|$Platform ===" -ForegroundColor Cyan dotnet clean $WindowsSolution -v m --nologo /p:Platform=$Platform -dotnet publish $PublishProject /noLogo /p:Configuration=$Configuration /p:Platform=$Platform --ignore-failed-sources -v m +dotnet publish $PublishProject /noLogo /p:Configuration=$Configuration /p:Platform=$Platform -p:RuntimeIdentifier=win-$Platform --ignore-failed-sources -v m if ($LASTEXITCODE -ne 0) { - throw "dotnet publish WinUI failed with exit code $LASTEXITCODE" + throw "dotnet publish Avalonia failed with exit code $LASTEXITCODE" } # --- Stage binaries --- @@ -88,6 +88,16 @@ New-Item $BinDir -ItemType Directory | Out-Null # Move published output into unigetui_bin Get-ChildItem $PublishDir | Move-Item -Destination $BinDir -Force +$AvaloniaAppHostPath = Join-Path $BinDir "UniGetUI.Avalonia.exe" +$WindowsAppHostPath = Join-Path $BinDir "UniGetUI.exe" +if (Test-Path $AvaloniaAppHostPath) { + Move-Item $AvaloniaAppHostPath $WindowsAppHostPath -Force +} + +if (-not (Test-Path $WindowsAppHostPath)) { + throw "Windows app host was not produced at $WindowsAppHostPath" +} + # Keep smaller symbols for useful local crash source information, and prune oversized ones. $MaxShippedPdbSizeBytes = 1MB diff --git a/scripts/package-linux.ps1 b/scripts/package-linux.ps1 index a17fc57e12..2e2ea17cd5 100644 --- a/scripts/package-linux.ps1 +++ b/scripts/package-linux.ps1 @@ -81,7 +81,7 @@ param( [string] $Url = 'https://github.com/Devolutions/UniGetUI', [string] $AppExecutableName = 'UniGetUI.Avalonia', [string] $LauncherName = 'unigetui', - [string] $IconSourcePath = (Join-Path $PSScriptRoot '..\src\UniGetUI\Assets\Images\icon.png') + [string] $IconSourcePath = (Join-Path $PSScriptRoot '..' 'src' 'SharedAssets' 'Assets' 'Images' 'icon.png') ) $ErrorActionPreference = 'Stop' diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 101ede0ac1..61c627d4b0 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -47,7 +47,7 @@ false - true + true diff --git a/src/Shared/AutoUpdater.Helpers.cs b/src/Shared/AutoUpdater.Helpers.cs new file mode 100644 index 0000000000..291b3ecca0 --- /dev/null +++ b/src/Shared/AutoUpdater.Helpers.cs @@ -0,0 +1,156 @@ +using System.Runtime.InteropServices; +using Microsoft.Win32; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Tools; + +namespace UniGetUI.Shared; + +internal static class AutoUpdaterHelpers +{ + internal static bool IsSourceUrlAllowed(string url, bool allowUnsafeUrls) + { + if (!Uri.TryCreate(url, UriKind.Absolute, out Uri? uri)) + { + return false; + } + + if (allowUnsafeUrls) + { + Logger.Warn($"Registry override enabled: allowing potentially unsafe updater URL {url}"); + return true; + } + + if (!string.Equals(uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + return uri.Host.Equals("devolutions.net", StringComparison.OrdinalIgnoreCase) + || uri.Host.EndsWith(".devolutions.net", StringComparison.OrdinalIgnoreCase) + || uri.Host.Equals("github.com", StringComparison.OrdinalIgnoreCase) + || uri.Host.Equals("objects.githubusercontent.com", StringComparison.OrdinalIgnoreCase) + || uri.Host.Equals( + "release-assets.githubusercontent.com", + StringComparison.OrdinalIgnoreCase + ); + } + + internal static ProductInfoFile SelectInstallerFile(List files) + { + string targetArch = RuntimeInformation.ProcessArchitecture switch + { + Architecture.Arm64 => "arm64", + Architecture.X64 => "x64", + _ => "x64", + }; + + ProductInfoFile? match = files.FirstOrDefault(file => + file.Type.Equals("exe", StringComparison.OrdinalIgnoreCase) + && file.Arch.Equals(targetArch, StringComparison.OrdinalIgnoreCase) + ); + + match ??= files.FirstOrDefault(file => + file.Type.Equals("exe", StringComparison.OrdinalIgnoreCase) + && file.Arch.Equals("Any", StringComparison.OrdinalIgnoreCase) + ); + + match ??= files.FirstOrDefault(file => + file.Type.Equals("msi", StringComparison.OrdinalIgnoreCase) + && file.Arch.Equals(targetArch, StringComparison.OrdinalIgnoreCase) + ); + + match ??= files.FirstOrDefault(file => + file.Type.Equals("msi", StringComparison.OrdinalIgnoreCase) + && file.Arch.Equals("Any", StringComparison.OrdinalIgnoreCase) + ); + + if (match is null) + { + throw new KeyNotFoundException( + $"No compatible installer file found in productinfo for architecture '{targetArch}'" + ); + } + + return match; + } + + internal static Version ParseVersionOrFallback(string rawVersion, Version fallbackVersion) + { + if (Version.TryParse(rawVersion, out Version? parsed)) + { + return CoreTools.NormalizeVersionForComparison(parsed); + } + + string sanitized = rawVersion.Trim().TrimStart('v', 'V'); + if (Version.TryParse(sanitized, out parsed)) + { + return CoreTools.NormalizeVersionForComparison(parsed); + } + + Logger.Warn($"Could not parse version '{rawVersion}', using fallback '{fallbackVersion}'"); + return fallbackVersion; + } + + internal static string NormalizeThumbprint(string thumbprint) + { + char[] normalized = thumbprint.ToLowerInvariant().Where(char.IsAsciiHexDigit).ToArray(); + + return new string(normalized); + } + + internal static string? GetRegistryString(RegistryKey? key, string valueName) + { +#pragma warning disable CA1416 + object? value = key?.GetValue(valueName); +#pragma warning restore CA1416 + if (value is null) + { + return null; + } + + string? parsedValue = value.ToString(); + if (string.IsNullOrWhiteSpace(parsedValue)) + { + return null; + } + + return parsedValue.Trim(); + } + +#if DEBUG + internal static bool GetRegistryBool(RegistryKey? key, string valueName) + { +#pragma warning disable CA1416 + object? value = key?.GetValue(valueName); +#pragma warning restore CA1416 + if (value is null) + { + return false; + } + + if (value is int intValue) + { + return intValue != 0; + } + + if (value is long longValue) + { + return longValue != 0; + } + + string normalized = value.ToString()?.Trim() ?? ""; + return normalized.Equals("1", StringComparison.OrdinalIgnoreCase) + || normalized.Equals("true", StringComparison.OrdinalIgnoreCase) + || normalized.Equals("yes", StringComparison.OrdinalIgnoreCase) + || normalized.Equals("on", StringComparison.OrdinalIgnoreCase); + } +#endif + + internal sealed class ProductInfoFile + { + public string Arch { get; set; } = string.Empty; + public string Type { get; set; } = string.Empty; + public string Url { get; set; } = string.Empty; + public string Hash { get; set; } = string.Empty; + } +} diff --git a/src/Shared/SharedPreUiCommandDispatcher.cs b/src/Shared/SharedPreUiCommandDispatcher.cs index 99178b1f4c..e3595b06fa 100644 --- a/src/Shared/SharedPreUiCommandDispatcher.cs +++ b/src/Shared/SharedPreUiCommandDispatcher.cs @@ -1,3 +1,4 @@ +using UniGetUI.Core.Logging; using UniGetUI.Core.SettingsEngine; using UniGetUI.Core.SettingsEngine.SecureSettings; using UniGetUI.Core.Tools; @@ -14,7 +15,7 @@ int UnknownSettingsKey internal static class SharedPreUiCommandDispatcher { - internal static readonly SharedPreUiCommandExitCodes WinUiExitCodes = new( + internal static readonly SharedPreUiCommandExitCodes WindowsCliExitCodes = new( Success: 0, Failed: -1, InvalidParameter: -1073741811, @@ -22,7 +23,7 @@ internal static class SharedPreUiCommandDispatcher UnknownSettingsKey: -2 ); - internal static readonly SharedPreUiCommandExitCodes AvaloniaExitCodes = new( + internal static readonly SharedPreUiCommandExitCodes PortableCliExitCodes = new( Success: 0, Failed: 1, InvalidParameter: 2, @@ -30,14 +31,17 @@ internal static class SharedPreUiCommandDispatcher UnknownSettingsKey: 4 ); - private const string HelpArgument = "--help"; - private const string ImportSettingsArgument = "--import-settings"; - private const string ExportSettingsArgument = "--export-settings"; - private const string EnableSettingArgument = "--enable-setting"; - private const string DisableSettingArgument = "--disable-setting"; - private const string SetSettingValueArgument = "--set-setting-value"; - private const string EnableSecureSettingArgument = "--enable-secure-setting"; - private const string DisableSecureSettingArgument = "--disable-secure-setting"; + internal const string HelpArgument = "--help"; + internal const string ImportSettingsArgument = "--import-settings"; + internal const string ExportSettingsArgument = "--export-settings"; + internal const string EnableSettingArgument = "--enable-setting"; + internal const string DisableSettingArgument = "--disable-setting"; + internal const string SetSettingValueArgument = "--set-setting-value"; + internal const string EnableSecureSettingArgument = "--enable-secure-setting"; + internal const string DisableSecureSettingArgument = "--disable-secure-setting"; + internal const string MigrateWingetUIToUniGetUIArgument = "--migrate-wingetui-to-unigetui"; + internal const string UninstallWingetUIArgument = "--uninstall-wingetui"; + internal const string UninstallUniGetUIArgument = "--uninstall-unigetui"; private const string EnableSecureSettingForUserArgument = SecureSettings.Args.ENABLE_FOR_USER; private const string DisableSecureSettingForUserArgument = SecureSettings.Args.DISABLE_FOR_USER; @@ -94,6 +98,16 @@ internal static class SharedPreUiCommandDispatcher return DisableSecureSettingForUser(args, exitCodes); } + if (args.Contains(MigrateWingetUIToUniGetUIArgument)) + { + return MigrateWingetUIToUniGetUI(); + } + + if (args.Contains(UninstallWingetUIArgument) || args.Contains(UninstallUniGetUIArgument)) + { + return exitCodes.Success; + } + return null; } @@ -290,6 +304,73 @@ public static int DisableSecureSettingForUser(IReadOnlyList args, Shared } } + public static int MigrateWingetUIToUniGetUI() + { + try + { + string[] basePaths = + [ + Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), + Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), + Environment.GetFolderPath(Environment.SpecialFolder.CommonDesktopDirectory), + Environment.GetFolderPath(Environment.SpecialFolder.CommonStartMenu), + ]; + + foreach (string path in basePaths) + { + foreach ( + string oldWingetUIIcon in new[] + { + "WingetUI.lnk", + "WingetUI .lnk", + "UniGetUI (formerly WingetUI) .lnk", + "UniGetUI (formerly WingetUI).lnk", + } + ) + { + try + { + string oldFile = Path.Join(path, oldWingetUIIcon); + string newFile = Path.Join(path, "UniGetUI.lnk"); + if (!File.Exists(oldFile)) + { + continue; + } + + if (File.Exists(newFile)) + { + Logger.Info( + "Deleting shortcut " + + oldFile + + " since new shortcut already exists" + ); + File.Delete(oldFile); + } + else + { + Logger.Info("Moving shortcut to " + newFile); + File.Move(oldFile, newFile); + } + } + catch (Exception ex) + { + Logger.Warn( + $"An error occurred while migrating the shortcut {Path.Join(path, oldWingetUIIcon)}" + ); + Logger.Warn(ex); + } + } + } + + return 0; + } + catch (Exception ex) + { + Logger.Error(ex); + return ex.HResult; + } + } + private static bool TryGetValueAfterArgument( IReadOnlyList args, string argument, diff --git a/src/UniGetUI/Assets/Images/admin_color.png b/src/SharedAssets/Assets/Images/admin_color.png similarity index 100% rename from src/UniGetUI/Assets/Images/admin_color.png rename to src/SharedAssets/Assets/Images/admin_color.png diff --git a/src/UniGetUI/Assets/Images/agreement.png b/src/SharedAssets/Assets/Images/agreement.png similarity index 100% rename from src/UniGetUI/Assets/Images/agreement.png rename to src/SharedAssets/Assets/Images/agreement.png diff --git a/src/UniGetUI/Assets/Images/alert_laptop.png b/src/SharedAssets/Assets/Images/alert_laptop.png similarity index 100% rename from src/UniGetUI/Assets/Images/alert_laptop.png rename to src/SharedAssets/Assets/Images/alert_laptop.png diff --git a/src/UniGetUI/Assets/Images/cancel.png b/src/SharedAssets/Assets/Images/cancel.png similarity index 100% rename from src/UniGetUI/Assets/Images/cancel.png rename to src/SharedAssets/Assets/Images/cancel.png diff --git a/src/UniGetUI/Assets/Images/cargo_color.png b/src/SharedAssets/Assets/Images/cargo_color.png similarity index 100% rename from src/UniGetUI/Assets/Images/cargo_color.png rename to src/SharedAssets/Assets/Images/cargo_color.png diff --git a/src/UniGetUI/Assets/Images/checked_laptop.png b/src/SharedAssets/Assets/Images/checked_laptop.png similarity index 100% rename from src/UniGetUI/Assets/Images/checked_laptop.png rename to src/SharedAssets/Assets/Images/checked_laptop.png diff --git a/src/UniGetUI/Assets/Images/choco_color.png b/src/SharedAssets/Assets/Images/choco_color.png similarity index 100% rename from src/UniGetUI/Assets/Images/choco_color.png rename to src/SharedAssets/Assets/Images/choco_color.png diff --git a/src/UniGetUI/Assets/Images/coffee.png b/src/SharedAssets/Assets/Images/coffee.png similarity index 100% rename from src/UniGetUI/Assets/Images/coffee.png rename to src/SharedAssets/Assets/Images/coffee.png diff --git a/src/UniGetUI/Assets/Images/console_color.png b/src/SharedAssets/Assets/Images/console_color.png similarity index 100% rename from src/UniGetUI/Assets/Images/console_color.png rename to src/SharedAssets/Assets/Images/console_color.png diff --git a/src/UniGetUI/Assets/Images/desktop_download.png b/src/SharedAssets/Assets/Images/desktop_download.png similarity index 100% rename from src/UniGetUI/Assets/Images/desktop_download.png rename to src/SharedAssets/Assets/Images/desktop_download.png diff --git a/src/UniGetUI/Assets/Images/dotnet_color.png b/src/SharedAssets/Assets/Images/dotnet_color.png similarity index 100% rename from src/UniGetUI/Assets/Images/dotnet_color.png rename to src/SharedAssets/Assets/Images/dotnet_color.png diff --git a/src/UniGetUI/Assets/Images/finish.png b/src/SharedAssets/Assets/Images/finish.png similarity index 100% rename from src/UniGetUI/Assets/Images/finish.png rename to src/SharedAssets/Assets/Images/finish.png diff --git a/src/UniGetUI/Assets/Images/github.png b/src/SharedAssets/Assets/Images/github.png similarity index 100% rename from src/UniGetUI/Assets/Images/github.png rename to src/SharedAssets/Assets/Images/github.png diff --git a/src/UniGetUI/Assets/Images/hacker.png b/src/SharedAssets/Assets/Images/hacker.png similarity index 100% rename from src/UniGetUI/Assets/Images/hacker.png rename to src/SharedAssets/Assets/Images/hacker.png diff --git a/src/UniGetUI/Assets/Images/icon.bmp b/src/SharedAssets/Assets/Images/icon.bmp similarity index 100% rename from src/UniGetUI/Assets/Images/icon.bmp rename to src/SharedAssets/Assets/Images/icon.bmp diff --git a/src/UniGetUI/Assets/Images/icon.ico b/src/SharedAssets/Assets/Images/icon.ico similarity index 100% rename from src/UniGetUI/Assets/Images/icon.ico rename to src/SharedAssets/Assets/Images/icon.ico diff --git a/src/UniGetUI/Assets/Images/icon.png b/src/SharedAssets/Assets/Images/icon.png similarity index 100% rename from src/UniGetUI/Assets/Images/icon.png rename to src/SharedAssets/Assets/Images/icon.png diff --git a/src/UniGetUI/Assets/Images/icon_unsquare.png b/src/SharedAssets/Assets/Images/icon_unsquare.png similarity index 100% rename from src/UniGetUI/Assets/Images/icon_unsquare.png rename to src/SharedAssets/Assets/Images/icon_unsquare.png diff --git a/src/UniGetUI/Assets/Images/infocolor.png b/src/SharedAssets/Assets/Images/infocolor.png similarity index 100% rename from src/UniGetUI/Assets/Images/infocolor.png rename to src/SharedAssets/Assets/Images/infocolor.png diff --git a/src/UniGetUI/Assets/Images/kofi.png b/src/SharedAssets/Assets/Images/kofi.png similarity index 100% rename from src/UniGetUI/Assets/Images/kofi.png rename to src/SharedAssets/Assets/Images/kofi.png diff --git a/src/UniGetUI/Assets/Images/node_color.png b/src/SharedAssets/Assets/Images/node_color.png similarity index 100% rename from src/UniGetUI/Assets/Images/node_color.png rename to src/SharedAssets/Assets/Images/node_color.png diff --git a/src/UniGetUI/Assets/Images/package_color.png b/src/SharedAssets/Assets/Images/package_color.png similarity index 100% rename from src/UniGetUI/Assets/Images/package_color.png rename to src/SharedAssets/Assets/Images/package_color.png diff --git a/src/UniGetUI/Assets/Images/pin_color.png b/src/SharedAssets/Assets/Images/pin_color.png similarity index 100% rename from src/UniGetUI/Assets/Images/pin_color.png rename to src/SharedAssets/Assets/Images/pin_color.png diff --git a/src/UniGetUI/Assets/Images/pip_color.png b/src/SharedAssets/Assets/Images/pip_color.png similarity index 100% rename from src/UniGetUI/Assets/Images/pip_color.png rename to src/SharedAssets/Assets/Images/pip_color.png diff --git a/src/UniGetUI/Assets/Images/powershell_color.png b/src/SharedAssets/Assets/Images/powershell_color.png similarity index 100% rename from src/UniGetUI/Assets/Images/powershell_color.png rename to src/SharedAssets/Assets/Images/powershell_color.png diff --git a/src/UniGetUI/Assets/Images/restart_color.png b/src/SharedAssets/Assets/Images/restart_color.png similarity index 100% rename from src/UniGetUI/Assets/Images/restart_color.png rename to src/SharedAssets/Assets/Images/restart_color.png diff --git a/src/UniGetUI/Assets/Images/rocket.png b/src/SharedAssets/Assets/Images/rocket.png similarity index 100% rename from src/UniGetUI/Assets/Images/rocket.png rename to src/SharedAssets/Assets/Images/rocket.png diff --git a/src/UniGetUI/Assets/Images/save.png b/src/SharedAssets/Assets/Images/save.png similarity index 100% rename from src/UniGetUI/Assets/Images/save.png rename to src/SharedAssets/Assets/Images/save.png diff --git a/src/UniGetUI/Assets/Images/scoop_color.png b/src/SharedAssets/Assets/Images/scoop_color.png similarity index 100% rename from src/UniGetUI/Assets/Images/scoop_color.png rename to src/SharedAssets/Assets/Images/scoop_color.png diff --git a/src/UniGetUI/Assets/Images/settings_gear.png b/src/SharedAssets/Assets/Images/settings_gear.png similarity index 100% rename from src/UniGetUI/Assets/Images/settings_gear.png rename to src/SharedAssets/Assets/Images/settings_gear.png diff --git a/src/UniGetUI/Assets/Images/shield_green.png b/src/SharedAssets/Assets/Images/shield_green.png similarity index 100% rename from src/UniGetUI/Assets/Images/shield_green.png rename to src/SharedAssets/Assets/Images/shield_green.png diff --git a/src/UniGetUI/Assets/Images/shield_question.png b/src/SharedAssets/Assets/Images/shield_question.png similarity index 100% rename from src/UniGetUI/Assets/Images/shield_question.png rename to src/SharedAssets/Assets/Images/shield_question.png diff --git a/src/UniGetUI/Assets/Images/shield_red.png b/src/SharedAssets/Assets/Images/shield_red.png similarity index 100% rename from src/UniGetUI/Assets/Images/shield_red.png rename to src/SharedAssets/Assets/Images/shield_red.png diff --git a/src/UniGetUI/Assets/Images/shield_reload.png b/src/SharedAssets/Assets/Images/shield_reload.png similarity index 100% rename from src/UniGetUI/Assets/Images/shield_reload.png rename to src/SharedAssets/Assets/Images/shield_reload.png diff --git a/src/UniGetUI/Assets/Images/shield_yellow.png b/src/SharedAssets/Assets/Images/shield_yellow.png similarity index 100% rename from src/UniGetUI/Assets/Images/shield_yellow.png rename to src/SharedAssets/Assets/Images/shield_yellow.png diff --git a/src/UniGetUI/Assets/Images/simple_user.png b/src/SharedAssets/Assets/Images/simple_user.png similarity index 100% rename from src/UniGetUI/Assets/Images/simple_user.png rename to src/SharedAssets/Assets/Images/simple_user.png diff --git a/src/UniGetUI/Assets/Images/tick.png b/src/SharedAssets/Assets/Images/tick.png similarity index 100% rename from src/UniGetUI/Assets/Images/tick.png rename to src/SharedAssets/Assets/Images/tick.png diff --git a/src/UniGetUI/Assets/Images/tray_blue_black_legacy.ico b/src/SharedAssets/Assets/Images/tray_blue_black_legacy.ico similarity index 100% rename from src/UniGetUI/Assets/Images/tray_blue_black_legacy.ico rename to src/SharedAssets/Assets/Images/tray_blue_black_legacy.ico diff --git a/src/UniGetUI/Assets/Images/tray_blue_white_legacy.ico b/src/SharedAssets/Assets/Images/tray_blue_white_legacy.ico similarity index 100% rename from src/UniGetUI/Assets/Images/tray_blue_white_legacy.ico rename to src/SharedAssets/Assets/Images/tray_blue_white_legacy.ico diff --git a/src/UniGetUI/Assets/Images/tray_empty_black_legacy.ico b/src/SharedAssets/Assets/Images/tray_empty_black_legacy.ico similarity index 100% rename from src/UniGetUI/Assets/Images/tray_empty_black_legacy.ico rename to src/SharedAssets/Assets/Images/tray_empty_black_legacy.ico diff --git a/src/UniGetUI/Assets/Images/tray_empty_white_legacy.ico b/src/SharedAssets/Assets/Images/tray_empty_white_legacy.ico similarity index 100% rename from src/UniGetUI/Assets/Images/tray_empty_white_legacy.ico rename to src/SharedAssets/Assets/Images/tray_empty_white_legacy.ico diff --git a/src/UniGetUI/Assets/Images/tray_green_black_legacy.ico b/src/SharedAssets/Assets/Images/tray_green_black_legacy.ico similarity index 100% rename from src/UniGetUI/Assets/Images/tray_green_black_legacy.ico rename to src/SharedAssets/Assets/Images/tray_green_black_legacy.ico diff --git a/src/UniGetUI/Assets/Images/tray_green_white_legacy.ico b/src/SharedAssets/Assets/Images/tray_green_white_legacy.ico similarity index 100% rename from src/UniGetUI/Assets/Images/tray_green_white_legacy.ico rename to src/SharedAssets/Assets/Images/tray_green_white_legacy.ico diff --git a/src/UniGetUI/Assets/Images/tray_orange_black_legacy.ico b/src/SharedAssets/Assets/Images/tray_orange_black_legacy.ico similarity index 100% rename from src/UniGetUI/Assets/Images/tray_orange_black_legacy.ico rename to src/SharedAssets/Assets/Images/tray_orange_black_legacy.ico diff --git a/src/UniGetUI/Assets/Images/tray_orange_white_legacy.ico b/src/SharedAssets/Assets/Images/tray_orange_white_legacy.ico similarity index 100% rename from src/UniGetUI/Assets/Images/tray_orange_white_legacy.ico rename to src/SharedAssets/Assets/Images/tray_orange_white_legacy.ico diff --git a/src/UniGetUI/Assets/Images/tray_turquoise_black_legacy.ico b/src/SharedAssets/Assets/Images/tray_turquoise_black_legacy.ico similarity index 100% rename from src/UniGetUI/Assets/Images/tray_turquoise_black_legacy.ico rename to src/SharedAssets/Assets/Images/tray_turquoise_black_legacy.ico diff --git a/src/UniGetUI/Assets/Images/tray_turquoise_white_legacy.ico b/src/SharedAssets/Assets/Images/tray_turquoise_white_legacy.ico similarity index 100% rename from src/UniGetUI/Assets/Images/tray_turquoise_white_legacy.ico rename to src/SharedAssets/Assets/Images/tray_turquoise_white_legacy.ico diff --git a/src/UniGetUI/Assets/Images/update_pc_color.png b/src/SharedAssets/Assets/Images/update_pc_color.png similarity index 100% rename from src/UniGetUI/Assets/Images/update_pc_color.png rename to src/SharedAssets/Assets/Images/update_pc_color.png diff --git a/src/UniGetUI/Assets/Images/vcpkg_color.png b/src/SharedAssets/Assets/Images/vcpkg_color.png similarity index 100% rename from src/UniGetUI/Assets/Images/vcpkg_color.png rename to src/SharedAssets/Assets/Images/vcpkg_color.png diff --git a/src/UniGetUI/Assets/Images/warn.png b/src/SharedAssets/Assets/Images/warn.png similarity index 100% rename from src/UniGetUI/Assets/Images/warn.png rename to src/SharedAssets/Assets/Images/warn.png diff --git a/src/UniGetUI/Assets/Images/winget_color.png b/src/SharedAssets/Assets/Images/winget_color.png similarity index 100% rename from src/UniGetUI/Assets/Images/winget_color.png rename to src/SharedAssets/Assets/Images/winget_color.png diff --git a/src/UniGetUI/Assets/Images/workstation.png b/src/SharedAssets/Assets/Images/workstation.png similarity index 100% rename from src/UniGetUI/Assets/Images/workstation.png rename to src/SharedAssets/Assets/Images/workstation.png diff --git a/src/UniGetUI/Assets/Images/youtube.png b/src/SharedAssets/Assets/Images/youtube.png similarity index 100% rename from src/UniGetUI/Assets/Images/youtube.png rename to src/SharedAssets/Assets/Images/youtube.png diff --git a/src/UniGetUI/Assets/SplashScreen.png b/src/SharedAssets/Assets/SplashScreen.png similarity index 100% rename from src/UniGetUI/Assets/SplashScreen.png rename to src/SharedAssets/Assets/SplashScreen.png diff --git a/src/UniGetUI/Assets/SplashScreen.svg b/src/SharedAssets/Assets/SplashScreen.svg similarity index 100% rename from src/UniGetUI/Assets/SplashScreen.svg rename to src/SharedAssets/Assets/SplashScreen.svg diff --git a/src/UniGetUI/Assets/SplashScreen.theme-dark.png b/src/SharedAssets/Assets/SplashScreen.theme-dark.png similarity index 100% rename from src/UniGetUI/Assets/SplashScreen.theme-dark.png rename to src/SharedAssets/Assets/SplashScreen.theme-dark.png diff --git a/src/UniGetUI/Assets/SplashScreen.theme-dark.svg b/src/SharedAssets/Assets/SplashScreen.theme-dark.svg similarity index 100% rename from src/UniGetUI/Assets/SplashScreen.theme-dark.svg rename to src/SharedAssets/Assets/SplashScreen.theme-dark.svg diff --git a/src/UniGetUI/Assets/Square150x150Logo.png b/src/SharedAssets/Assets/Square150x150Logo.png similarity index 100% rename from src/UniGetUI/Assets/Square150x150Logo.png rename to src/SharedAssets/Assets/Square150x150Logo.png diff --git a/src/UniGetUI/Assets/Square44x44Logo.png b/src/SharedAssets/Assets/Square44x44Logo.png similarity index 100% rename from src/UniGetUI/Assets/Square44x44Logo.png rename to src/SharedAssets/Assets/Square44x44Logo.png diff --git a/src/UniGetUI/Assets/StoreLogo.png b/src/SharedAssets/Assets/StoreLogo.png similarity index 100% rename from src/UniGetUI/Assets/StoreLogo.png rename to src/SharedAssets/Assets/StoreLogo.png diff --git a/src/UniGetUI/Assets/Symbols/DiscoverPackage.svg b/src/SharedAssets/Assets/Symbols/DiscoverPackage.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/DiscoverPackage.svg rename to src/SharedAssets/Assets/Symbols/DiscoverPackage.svg diff --git a/src/UniGetUI/Assets/Symbols/Filter.svg b/src/SharedAssets/Assets/Symbols/Filter.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/Filter.svg rename to src/SharedAssets/Assets/Symbols/Filter.svg diff --git a/src/UniGetUI/Assets/Symbols/Font/Read Me.txt b/src/SharedAssets/Assets/Symbols/Font/Read Me.txt similarity index 100% rename from src/UniGetUI/Assets/Symbols/Font/Read Me.txt rename to src/SharedAssets/Assets/Symbols/Font/Read Me.txt diff --git a/src/UniGetUI/Assets/Symbols/Font/demo-files/demo.css b/src/SharedAssets/Assets/Symbols/Font/demo-files/demo.css similarity index 100% rename from src/UniGetUI/Assets/Symbols/Font/demo-files/demo.css rename to src/SharedAssets/Assets/Symbols/Font/demo-files/demo.css diff --git a/src/UniGetUI/Assets/Symbols/Font/demo-files/demo.js b/src/SharedAssets/Assets/Symbols/Font/demo-files/demo.js similarity index 100% rename from src/UniGetUI/Assets/Symbols/Font/demo-files/demo.js rename to src/SharedAssets/Assets/Symbols/Font/demo-files/demo.js diff --git a/src/UniGetUI/Assets/Symbols/Font/demo.html b/src/SharedAssets/Assets/Symbols/Font/demo.html similarity index 100% rename from src/UniGetUI/Assets/Symbols/Font/demo.html rename to src/SharedAssets/Assets/Symbols/Font/demo.html diff --git a/src/UniGetUI/Assets/Symbols/Font/fonts/UniGetUI-Symbols.eot b/src/SharedAssets/Assets/Symbols/Font/fonts/UniGetUI-Symbols.eot similarity index 100% rename from src/UniGetUI/Assets/Symbols/Font/fonts/UniGetUI-Symbols.eot rename to src/SharedAssets/Assets/Symbols/Font/fonts/UniGetUI-Symbols.eot diff --git a/src/UniGetUI/Assets/Symbols/Font/fonts/UniGetUI-Symbols.svg b/src/SharedAssets/Assets/Symbols/Font/fonts/UniGetUI-Symbols.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/Font/fonts/UniGetUI-Symbols.svg rename to src/SharedAssets/Assets/Symbols/Font/fonts/UniGetUI-Symbols.svg diff --git a/src/UniGetUI/Assets/Symbols/Font/fonts/UniGetUI-Symbols.ttf b/src/SharedAssets/Assets/Symbols/Font/fonts/UniGetUI-Symbols.ttf similarity index 100% rename from src/UniGetUI/Assets/Symbols/Font/fonts/UniGetUI-Symbols.ttf rename to src/SharedAssets/Assets/Symbols/Font/fonts/UniGetUI-Symbols.ttf diff --git a/src/UniGetUI/Assets/Symbols/Font/fonts/UniGetUI-Symbols.woff b/src/SharedAssets/Assets/Symbols/Font/fonts/UniGetUI-Symbols.woff similarity index 100% rename from src/UniGetUI/Assets/Symbols/Font/fonts/UniGetUI-Symbols.woff rename to src/SharedAssets/Assets/Symbols/Font/fonts/UniGetUI-Symbols.woff diff --git a/src/UniGetUI/Assets/Symbols/Font/selection.json b/src/SharedAssets/Assets/Symbols/Font/selection.json similarity index 100% rename from src/UniGetUI/Assets/Symbols/Font/selection.json rename to src/SharedAssets/Assets/Symbols/Font/selection.json diff --git a/src/UniGetUI/Assets/Symbols/Font/style.css b/src/SharedAssets/Assets/Symbols/Font/style.css similarity index 100% rename from src/UniGetUI/Assets/Symbols/Font/style.css rename to src/SharedAssets/Assets/Symbols/Font/style.css diff --git a/src/UniGetUI/Assets/Symbols/InstalledPackages.svg b/src/SharedAssets/Assets/Symbols/InstalledPackages.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/InstalledPackages.svg rename to src/SharedAssets/Assets/Symbols/InstalledPackages.svg diff --git a/src/UniGetUI/Assets/Symbols/PackagesBundle.svg b/src/SharedAssets/Assets/Symbols/PackagesBundle.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/PackagesBundle.svg rename to src/SharedAssets/Assets/Symbols/PackagesBundle.svg diff --git a/src/UniGetUI/Assets/Symbols/Sources.svg b/src/SharedAssets/Assets/Symbols/Sources.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/Sources.svg rename to src/SharedAssets/Assets/Symbols/Sources.svg diff --git a/src/UniGetUI/Assets/Symbols/add_to.svg b/src/SharedAssets/Assets/Symbols/add_to.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/add_to.svg rename to src/SharedAssets/Assets/Symbols/add_to.svg diff --git a/src/UniGetUI/Assets/Symbols/android.svg b/src/SharedAssets/Assets/Symbols/android.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/android.svg rename to src/SharedAssets/Assets/Symbols/android.svg diff --git a/src/UniGetUI/Assets/Symbols/apt.svg b/src/SharedAssets/Assets/Symbols/apt.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/apt.svg rename to src/SharedAssets/Assets/Symbols/apt.svg diff --git a/src/UniGetUI/Assets/Symbols/backward.svg b/src/SharedAssets/Assets/Symbols/backward.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/backward.svg rename to src/SharedAssets/Assets/Symbols/backward.svg diff --git a/src/UniGetUI/Assets/Symbols/bucket.svg b/src/SharedAssets/Assets/Symbols/bucket.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/bucket.svg rename to src/SharedAssets/Assets/Symbols/bucket.svg diff --git a/src/UniGetUI/Assets/Symbols/buggy.svg b/src/SharedAssets/Assets/Symbols/buggy.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/buggy.svg rename to src/SharedAssets/Assets/Symbols/buggy.svg diff --git a/src/UniGetUI/Assets/Symbols/bun.svg b/src/SharedAssets/Assets/Symbols/bun.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/bun.svg rename to src/SharedAssets/Assets/Symbols/bun.svg diff --git a/src/UniGetUI/Assets/Symbols/checksum.svg b/src/SharedAssets/Assets/Symbols/checksum.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/checksum.svg rename to src/SharedAssets/Assets/Symbols/checksum.svg diff --git a/src/UniGetUI/Assets/Symbols/choco.svg b/src/SharedAssets/Assets/Symbols/choco.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/choco.svg rename to src/SharedAssets/Assets/Symbols/choco.svg diff --git a/src/UniGetUI/Assets/Symbols/clipboard_list.svg b/src/SharedAssets/Assets/Symbols/clipboard_list.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/clipboard_list.svg rename to src/SharedAssets/Assets/Symbols/clipboard_list.svg diff --git a/src/UniGetUI/Assets/Symbols/close_round.svg b/src/SharedAssets/Assets/Symbols/close_round.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/close_round.svg rename to src/SharedAssets/Assets/Symbols/close_round.svg diff --git a/src/UniGetUI/Assets/Symbols/collapse.svg b/src/SharedAssets/Assets/Symbols/collapse.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/collapse.svg rename to src/SharedAssets/Assets/Symbols/collapse.svg diff --git a/src/UniGetUI/Assets/Symbols/console.svg b/src/SharedAssets/Assets/Symbols/console.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/console.svg rename to src/SharedAssets/Assets/Symbols/console.svg diff --git a/src/UniGetUI/Assets/Symbols/copy.svg b/src/SharedAssets/Assets/Symbols/copy.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/copy.svg rename to src/SharedAssets/Assets/Symbols/copy.svg diff --git a/src/UniGetUI/Assets/Symbols/cross.svg b/src/SharedAssets/Assets/Symbols/cross.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/cross.svg rename to src/SharedAssets/Assets/Symbols/cross.svg diff --git a/src/UniGetUI/Assets/Symbols/delete.svg b/src/SharedAssets/Assets/Symbols/delete.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/delete.svg rename to src/SharedAssets/Assets/Symbols/delete.svg diff --git a/src/UniGetUI/Assets/Symbols/disk.svg b/src/SharedAssets/Assets/Symbols/disk.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/disk.svg rename to src/SharedAssets/Assets/Symbols/disk.svg diff --git a/src/UniGetUI/Assets/Symbols/dnf.svg b/src/SharedAssets/Assets/Symbols/dnf.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/dnf.svg rename to src/SharedAssets/Assets/Symbols/dnf.svg diff --git a/src/UniGetUI/Assets/Symbols/dotnet.svg b/src/SharedAssets/Assets/Symbols/dotnet.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/dotnet.svg rename to src/SharedAssets/Assets/Symbols/dotnet.svg diff --git a/src/UniGetUI/Assets/Symbols/download.svg b/src/SharedAssets/Assets/Symbols/download.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/download.svg rename to src/SharedAssets/Assets/Symbols/download.svg diff --git a/src/UniGetUI/Assets/Symbols/empty.svg b/src/SharedAssets/Assets/Symbols/empty.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/empty.svg rename to src/SharedAssets/Assets/Symbols/empty.svg diff --git a/src/UniGetUI/Assets/Symbols/expand.svg b/src/SharedAssets/Assets/Symbols/expand.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/expand.svg rename to src/SharedAssets/Assets/Symbols/expand.svg diff --git a/src/UniGetUI/Assets/Symbols/experimental.svg b/src/SharedAssets/Assets/Symbols/experimental.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/experimental.svg rename to src/SharedAssets/Assets/Symbols/experimental.svg diff --git a/src/UniGetUI/Assets/Symbols/flatpak.svg b/src/SharedAssets/Assets/Symbols/flatpak.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/flatpak.svg rename to src/SharedAssets/Assets/Symbols/flatpak.svg diff --git a/src/UniGetUI/Assets/Symbols/forward.svg b/src/SharedAssets/Assets/Symbols/forward.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/forward.svg rename to src/SharedAssets/Assets/Symbols/forward.svg diff --git a/src/UniGetUI/Assets/Symbols/gog.svg b/src/SharedAssets/Assets/Symbols/gog.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/gog.svg rename to src/SharedAssets/Assets/Symbols/gog.svg diff --git a/src/UniGetUI/Assets/Symbols/help.svg b/src/SharedAssets/Assets/Symbols/help.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/help.svg rename to src/SharedAssets/Assets/Symbols/help.svg diff --git a/src/UniGetUI/Assets/Symbols/history.svg b/src/SharedAssets/Assets/Symbols/history.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/history.svg rename to src/SharedAssets/Assets/Symbols/history.svg diff --git a/src/UniGetUI/Assets/Symbols/home.svg b/src/SharedAssets/Assets/Symbols/home.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/home.svg rename to src/SharedAssets/Assets/Symbols/home.svg diff --git a/src/UniGetUI/Assets/Symbols/homebrew.svg b/src/SharedAssets/Assets/Symbols/homebrew.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/homebrew.svg rename to src/SharedAssets/Assets/Symbols/homebrew.svg diff --git a/src/UniGetUI/Assets/Symbols/icomoon-project.json b/src/SharedAssets/Assets/Symbols/icomoon-project.json similarity index 100% rename from src/UniGetUI/Assets/Symbols/icomoon-project.json rename to src/SharedAssets/Assets/Symbols/icomoon-project.json diff --git a/src/UniGetUI/Assets/Symbols/id.svg b/src/SharedAssets/Assets/Symbols/id.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/id.svg rename to src/SharedAssets/Assets/Symbols/id.svg diff --git a/src/UniGetUI/Assets/Symbols/info_round.svg b/src/SharedAssets/Assets/Symbols/info_round.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/info_round.svg rename to src/SharedAssets/Assets/Symbols/info_round.svg diff --git a/src/UniGetUI/Assets/Symbols/installed.svg b/src/SharedAssets/Assets/Symbols/installed.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/installed.svg rename to src/SharedAssets/Assets/Symbols/installed.svg diff --git a/src/UniGetUI/Assets/Symbols/installed_filled.svg b/src/SharedAssets/Assets/Symbols/installed_filled.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/installed_filled.svg rename to src/SharedAssets/Assets/Symbols/installed_filled.svg diff --git a/src/UniGetUI/Assets/Symbols/interactive.svg b/src/SharedAssets/Assets/Symbols/interactive.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/interactive.svg rename to src/SharedAssets/Assets/Symbols/interactive.svg diff --git a/src/UniGetUI/Assets/Symbols/launch.svg b/src/SharedAssets/Assets/Symbols/launch.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/launch.svg rename to src/SharedAssets/Assets/Symbols/launch.svg diff --git a/src/UniGetUI/Assets/Symbols/loading.svg b/src/SharedAssets/Assets/Symbols/loading.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/loading.svg rename to src/SharedAssets/Assets/Symbols/loading.svg diff --git a/src/UniGetUI/Assets/Symbols/loading_filled.svg b/src/SharedAssets/Assets/Symbols/loading_filled.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/loading_filled.svg rename to src/SharedAssets/Assets/Symbols/loading_filled.svg diff --git a/src/UniGetUI/Assets/Symbols/local_pc.svg b/src/SharedAssets/Assets/Symbols/local_pc.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/local_pc.svg rename to src/SharedAssets/Assets/Symbols/local_pc.svg diff --git a/src/UniGetUI/Assets/Symbols/megaphone.svg b/src/SharedAssets/Assets/Symbols/megaphone.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/megaphone.svg rename to src/SharedAssets/Assets/Symbols/megaphone.svg diff --git a/src/UniGetUI/Assets/Symbols/ms_store.svg b/src/SharedAssets/Assets/Symbols/ms_store.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/ms_store.svg rename to src/SharedAssets/Assets/Symbols/ms_store.svg diff --git a/src/UniGetUI/Assets/Symbols/node.svg b/src/SharedAssets/Assets/Symbols/node.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/node.svg rename to src/SharedAssets/Assets/Symbols/node.svg diff --git a/src/UniGetUI/Assets/Symbols/open_folder.svg b/src/SharedAssets/Assets/Symbols/open_folder.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/open_folder.svg rename to src/SharedAssets/Assets/Symbols/open_folder.svg diff --git a/src/UniGetUI/Assets/Symbols/options.svg b/src/SharedAssets/Assets/Symbols/options.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/options.svg rename to src/SharedAssets/Assets/Symbols/options.svg diff --git a/src/UniGetUI/Assets/Symbols/package.svg b/src/SharedAssets/Assets/Symbols/package.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/package.svg rename to src/SharedAssets/Assets/Symbols/package.svg diff --git a/src/UniGetUI/Assets/Symbols/pacman.svg b/src/SharedAssets/Assets/Symbols/pacman.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/pacman.svg rename to src/SharedAssets/Assets/Symbols/pacman.svg diff --git a/src/UniGetUI/Assets/Symbols/pin.svg b/src/SharedAssets/Assets/Symbols/pin.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/pin.svg rename to src/SharedAssets/Assets/Symbols/pin.svg diff --git a/src/UniGetUI/Assets/Symbols/pin_filled.svg b/src/SharedAssets/Assets/Symbols/pin_filled.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/pin_filled.svg rename to src/SharedAssets/Assets/Symbols/pin_filled.svg diff --git a/src/UniGetUI/Assets/Symbols/powershell.svg b/src/SharedAssets/Assets/Symbols/powershell.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/powershell.svg rename to src/SharedAssets/Assets/Symbols/powershell.svg diff --git a/src/UniGetUI/Assets/Symbols/python.svg b/src/SharedAssets/Assets/Symbols/python.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/python.svg rename to src/SharedAssets/Assets/Symbols/python.svg diff --git a/src/UniGetUI/Assets/Symbols/reload.svg b/src/SharedAssets/Assets/Symbols/reload.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/reload.svg rename to src/SharedAssets/Assets/Symbols/reload.svg diff --git a/src/UniGetUI/Assets/Symbols/rust.svg b/src/SharedAssets/Assets/Symbols/rust.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/rust.svg rename to src/SharedAssets/Assets/Symbols/rust.svg diff --git a/src/UniGetUI/Assets/Symbols/sandclock.svg b/src/SharedAssets/Assets/Symbols/sandclock.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/sandclock.svg rename to src/SharedAssets/Assets/Symbols/sandclock.svg diff --git a/src/UniGetUI/Assets/Symbols/save_as.svg b/src/SharedAssets/Assets/Symbols/save_as.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/save_as.svg rename to src/SharedAssets/Assets/Symbols/save_as.svg diff --git a/src/UniGetUI/Assets/Symbols/scoop.svg b/src/SharedAssets/Assets/Symbols/scoop.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/scoop.svg rename to src/SharedAssets/Assets/Symbols/scoop.svg diff --git a/src/UniGetUI/Assets/Symbols/search.svg b/src/SharedAssets/Assets/Symbols/search.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/search.svg rename to src/SharedAssets/Assets/Symbols/search.svg diff --git a/src/UniGetUI/Assets/Symbols/search_filled.svg b/src/SharedAssets/Assets/Symbols/search_filled.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/search_filled.svg rename to src/SharedAssets/Assets/Symbols/search_filled.svg diff --git a/src/UniGetUI/Assets/Symbols/settings.svg b/src/SharedAssets/Assets/Symbols/settings.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/settings.svg rename to src/SharedAssets/Assets/Symbols/settings.svg diff --git a/src/UniGetUI/Assets/Symbols/share.svg b/src/SharedAssets/Assets/Symbols/share.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/share.svg rename to src/SharedAssets/Assets/Symbols/share.svg diff --git a/src/UniGetUI/Assets/Symbols/skip.svg b/src/SharedAssets/Assets/Symbols/skip.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/skip.svg rename to src/SharedAssets/Assets/Symbols/skip.svg diff --git a/src/UniGetUI/Assets/Symbols/snap.svg b/src/SharedAssets/Assets/Symbols/snap.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/snap.svg rename to src/SharedAssets/Assets/Symbols/snap.svg diff --git a/src/UniGetUI/Assets/Symbols/steam.svg b/src/SharedAssets/Assets/Symbols/steam.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/steam.svg rename to src/SharedAssets/Assets/Symbols/steam.svg diff --git a/src/UniGetUI/Assets/Symbols/sys_tray.svg b/src/SharedAssets/Assets/Symbols/sys_tray.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/sys_tray.svg rename to src/SharedAssets/Assets/Symbols/sys_tray.svg diff --git a/src/UniGetUI/Assets/Symbols/uac.svg b/src/SharedAssets/Assets/Symbols/uac.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/uac.svg rename to src/SharedAssets/Assets/Symbols/uac.svg diff --git a/src/UniGetUI/Assets/Symbols/undelete.svg b/src/SharedAssets/Assets/Symbols/undelete.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/undelete.svg rename to src/SharedAssets/Assets/Symbols/undelete.svg diff --git a/src/UniGetUI/Assets/Symbols/update.svg b/src/SharedAssets/Assets/Symbols/update.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/update.svg rename to src/SharedAssets/Assets/Symbols/update.svg diff --git a/src/UniGetUI/Assets/Symbols/upgradable.svg b/src/SharedAssets/Assets/Symbols/upgradable.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/upgradable.svg rename to src/SharedAssets/Assets/Symbols/upgradable.svg diff --git a/src/UniGetUI/Assets/Symbols/upgradable_filled.svg b/src/SharedAssets/Assets/Symbols/upgradable_filled.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/upgradable_filled.svg rename to src/SharedAssets/Assets/Symbols/upgradable_filled.svg diff --git a/src/UniGetUI/Assets/Symbols/uplay.svg b/src/SharedAssets/Assets/Symbols/uplay.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/uplay.svg rename to src/SharedAssets/Assets/Symbols/uplay.svg diff --git a/src/UniGetUI/Assets/Symbols/vcpkg.svg b/src/SharedAssets/Assets/Symbols/vcpkg.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/vcpkg.svg rename to src/SharedAssets/Assets/Symbols/vcpkg.svg diff --git a/src/UniGetUI/Assets/Symbols/version.svg b/src/SharedAssets/Assets/Symbols/version.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/version.svg rename to src/SharedAssets/Assets/Symbols/version.svg diff --git a/src/UniGetUI/Assets/Symbols/warning.svg b/src/SharedAssets/Assets/Symbols/warning.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/warning.svg rename to src/SharedAssets/Assets/Symbols/warning.svg diff --git a/src/UniGetUI/Assets/Symbols/warning_filled.svg b/src/SharedAssets/Assets/Symbols/warning_filled.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/warning_filled.svg rename to src/SharedAssets/Assets/Symbols/warning_filled.svg diff --git a/src/UniGetUI/Assets/Symbols/warning_round.svg b/src/SharedAssets/Assets/Symbols/warning_round.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/warning_round.svg rename to src/SharedAssets/Assets/Symbols/warning_round.svg diff --git a/src/UniGetUI/Assets/Symbols/winget.svg b/src/SharedAssets/Assets/Symbols/winget.svg similarity index 100% rename from src/UniGetUI/Assets/Symbols/winget.svg rename to src/SharedAssets/Assets/Symbols/winget.svg diff --git a/src/UniGetUI/Assets/Utilities/install_scoop.cmd b/src/SharedAssets/Assets/Utilities/install_scoop.cmd similarity index 100% rename from src/UniGetUI/Assets/Utilities/install_scoop.cmd rename to src/SharedAssets/Assets/Utilities/install_scoop.cmd diff --git a/src/UniGetUI/Assets/Utilities/install_scoop.ps1 b/src/SharedAssets/Assets/Utilities/install_scoop.ps1 similarity index 100% rename from src/UniGetUI/Assets/Utilities/install_scoop.ps1 rename to src/SharedAssets/Assets/Utilities/install_scoop.ps1 diff --git a/src/UniGetUI/Assets/Utilities/scoop_cleanup.cmd b/src/SharedAssets/Assets/Utilities/scoop_cleanup.cmd similarity index 100% rename from src/UniGetUI/Assets/Utilities/scoop_cleanup.cmd rename to src/SharedAssets/Assets/Utilities/scoop_cleanup.cmd diff --git a/src/UniGetUI/Assets/Utilities/uninstall_scoop.cmd b/src/SharedAssets/Assets/Utilities/uninstall_scoop.cmd similarity index 100% rename from src/UniGetUI/Assets/Utilities/uninstall_scoop.cmd rename to src/SharedAssets/Assets/Utilities/uninstall_scoop.cmd diff --git a/src/UniGetUI/Assets/Wide310x150Logo.png b/src/SharedAssets/Assets/Wide310x150Logo.png similarity index 100% rename from src/UniGetUI/Assets/Wide310x150Logo.png rename to src/SharedAssets/Assets/Wide310x150Logo.png diff --git a/src/UniGetUI/icon.ico b/src/SharedAssets/icon.ico similarity index 100% rename from src/UniGetUI/icon.ico rename to src/SharedAssets/icon.ico diff --git a/src/UniGetUI.Avalonia/AvaloniaCliHandler.cs b/src/UniGetUI.Avalonia/AvaloniaCliHandler.cs index fb5a2546cb..ff19c7e7e1 100644 --- a/src/UniGetUI.Avalonia/AvaloniaCliHandler.cs +++ b/src/UniGetUI.Avalonia/AvaloniaCliHandler.cs @@ -9,9 +9,13 @@ internal static class AvaloniaCliHandler public static int? HandlePreUiArgs(string[] args) { + SharedPreUiCommandExitCodes exitCodes = OperatingSystem.IsWindows() + ? SharedPreUiCommandDispatcher.WindowsCliExitCodes + : SharedPreUiCommandDispatcher.PortableCliExitCodes; + return SharedPreUiCommandDispatcher.TryHandle( args, - SharedPreUiCommandDispatcher.AvaloniaExitCodes + exitCodes ); } } diff --git a/src/UniGetUI.Avalonia/Infrastructure/AvaloniaAutoUpdater.cs b/src/UniGetUI.Avalonia/Infrastructure/AvaloniaAutoUpdater.cs index a9b8be9926..9f08c42e0e 100644 --- a/src/UniGetUI.Avalonia/Infrastructure/AvaloniaAutoUpdater.cs +++ b/src/UniGetUI.Avalonia/Infrastructure/AvaloniaAutoUpdater.cs @@ -1497,7 +1497,8 @@ private static bool IsSourceUrlAllowed(string url, bool allowUnsafe) return false; } - return uri.Host.EndsWith("devolutions.net", StringComparison.OrdinalIgnoreCase) + return uri.Host.Equals("devolutions.net", StringComparison.OrdinalIgnoreCase) + || uri.Host.EndsWith(".devolutions.net", StringComparison.OrdinalIgnoreCase) || uri.Host.Equals("github.com", StringComparison.OrdinalIgnoreCase) || uri.Host.Equals("objects.githubusercontent.com", StringComparison.OrdinalIgnoreCase) || uri.Host.Equals("release-assets.githubusercontent.com", StringComparison.OrdinalIgnoreCase); diff --git a/src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj b/src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj index 59692b67f4..9ff3118b8f 100644 --- a/src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj +++ b/src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj @@ -18,7 +18,7 @@ WinExe UniGetUI.Avalonia UniGetUI.Avalonia - ..\UniGetUI\icon.ico + ..\SharedAssets\icon.ico true app.manifest true @@ -53,6 +53,14 @@ $(PingetCliPackageNativePath)\pinget.exe + + arm64 + x64 + $(PkgDevolutions_UniGetUI_Elevator)\runtimes\win-$(ElevatorPackageArchitecture)\native + $(ElevatorPackageNativePath)\UniGetUI Elevator.exe + $(ElevatorPackageNativePath)\getfilesiginforedist.dll + + @@ -111,6 +119,21 @@ /> + + + + + + @@ -160,29 +184,35 @@ - - + - - - - - - + + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/Interface_PViewModel.cs b/src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/Interface_PViewModel.cs index 242ba5c62c..c85d3d372a 100644 --- a/src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/Interface_PViewModel.cs +++ b/src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/Interface_PViewModel.cs @@ -6,7 +6,6 @@ using UniGetUI.Avalonia.Views; using UniGetUI.Core.Data; using UniGetUI.Core.Logging; -using UniGetUI.Core.SettingsEngine; using UniGetUI.Core.Tools; namespace UniGetUI.Avalonia.ViewModels.Pages.SettingsPages; @@ -15,12 +14,6 @@ public partial class Interface_PViewModel : ViewModelBase { public bool IsWindows { get; } = OperatingSystem.IsWindows(); - /// - /// True when the user is enrolled in the beta program. In that case the modern UI is forced - /// and the classic-mode toggle should be disabled. - /// - public bool IsBetaTester { get; } = Settings.Get(Settings.K.EnableUniGetUIBeta); - [ObservableProperty] private string _iconCacheSizeText = ""; public event EventHandler? RestartRequired; diff --git a/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Interface_P.axaml b/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Interface_P.axaml index ad2506ceec..98135bc3e7 100644 --- a/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Interface_P.axaml +++ b/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Interface_P.axaml @@ -27,15 +27,6 @@ - - directoryExists ) { - return fileExists(Path.Join(directory, ClassicExecutableName)) + return fileExists(Path.Join(directory, WindowsExecutableName)) || fileExists(Path.Join(directory, BundledPingetExecutableName)) || fileExists(Path.Join(directory, "IntegrityTree.json")) || directoryExists(Path.Join(directory, "Assets", "Utilities")) diff --git a/src/UniGetUI.Core.Settings/SettingsEngine_Names.cs b/src/UniGetUI.Core.Settings/SettingsEngine_Names.cs index a6047f3f9d..5e6be7aa78 100644 --- a/src/UniGetUI.Core.Settings/SettingsEngine_Names.cs +++ b/src/UniGetUI.Core.Settings/SettingsEngine_Names.cs @@ -90,8 +90,6 @@ public enum K WinGetCliToolPreference, WinGetComApiPolicy, WinGetDownloadFullManifest, - DisableClassicMode, - UseClassicMode, DisableInstallerHostChangeWarning, BunPreferLatestVersions, RedactUsernameInLog, @@ -198,8 +196,6 @@ public static string ResolveKey(K key) K.WinGetCliToolPreference => "WinGetCliToolPreference", K.WinGetComApiPolicy => "WinGetComApiPolicy", K.WinGetDownloadFullManifest => "WinGetDownloadFullManifest", - K.DisableClassicMode => "DisableClassicMode", - K.UseClassicMode => "UseClassicMode", K.DisableInstallerHostChangeWarning => "DisableInstallerHostChangeWarning", K.BunPreferLatestVersions => "BunPreferLatestVersions", K.RedactUsernameInLog => "RedactUsernameInLog", diff --git a/src/UniGetUI.Tests/AutoUpdaterTests.cs b/src/UniGetUI.Tests/AutoUpdaterTests.cs index 6a4d3ca847..c6e75c1cd5 100644 --- a/src/UniGetUI.Tests/AutoUpdaterTests.cs +++ b/src/UniGetUI.Tests/AutoUpdaterTests.cs @@ -1,5 +1,6 @@ using System.Runtime.InteropServices; using Microsoft.Win32; +using UniGetUI.Shared; namespace UniGetUI.Tests; @@ -18,7 +19,7 @@ public void IsSourceUrlAllowed_RestrictsUnsafeOrUnexpectedHosts( bool expected ) { - Assert.Equal(expected, AutoUpdater.IsSourceUrlAllowed(url, allowUnsafeUrls)); + Assert.Equal(expected, AutoUpdaterHelpers.IsSourceUrlAllowed(url, allowUnsafeUrls)); } [Fact] @@ -30,7 +31,7 @@ public void SelectInstallerFile_PrefersExecutableForCurrentArchitecture() Architecture.X64 => "x64", _ => "x64", }; - var preferred = new AutoUpdater.ProductInfoFile + var preferred = new AutoUpdaterHelpers.ProductInfoFile { Arch = targetArch, Type = "exe", @@ -38,16 +39,16 @@ public void SelectInstallerFile_PrefersExecutableForCurrentArchitecture() Hash = "hash-exe", }; - var selected = AutoUpdater.SelectInstallerFile( + var selected = AutoUpdaterHelpers.SelectInstallerFile( [ - new AutoUpdater.ProductInfoFile + new AutoUpdaterHelpers.ProductInfoFile { Arch = "Any", Type = "exe", Url = "https://example.test/any.exe", Hash = "hash-any", }, - new AutoUpdater.ProductInfoFile + new AutoUpdaterHelpers.ProductInfoFile { Arch = targetArch, Type = "msi", @@ -66,14 +67,14 @@ public void ParseVersionOrFallback_ParsesTrimmedVersionsAndFallsBackForInvalidIn { Version fallback = new(9, 9, 9, 9); - Assert.Equal(new Version(1, 2, 3), AutoUpdater.ParseVersionOrFallback("v1.2.3", fallback)); - Assert.Equal(fallback, AutoUpdater.ParseVersionOrFallback("not-a-version", fallback)); + Assert.Equal(new Version(1, 2, 3), AutoUpdaterHelpers.ParseVersionOrFallback("v1.2.3", fallback)); + Assert.Equal(fallback, AutoUpdaterHelpers.ParseVersionOrFallback("not-a-version", fallback)); } [Fact] public void NormalizeThumbprint_RemovesNonHexCharactersAndLowercases() { - Assert.Equal("abcdef1234", AutoUpdater.NormalizeThumbprint("AB:CD ef-12_34")); + Assert.Equal("abcdef1234", AutoUpdaterHelpers.NormalizeThumbprint("AB:CD ef-12_34")); } #if DEBUG @@ -87,10 +88,10 @@ public void RegistryHelpers_ParseTrimmedStringsAndTruthyValues() try { - Assert.Equal("https://devolutions.net/custom.json", AutoUpdater.GetRegistryString(key, "ProductInfoUrl")); - Assert.True(AutoUpdater.GetRegistryBool(key, "AllowUnsafe")); - Assert.Null(AutoUpdater.GetRegistryString(key, "Missing")); - Assert.False(AutoUpdater.GetRegistryBool(key, "Missing")); + Assert.Equal("https://devolutions.net/custom.json", AutoUpdaterHelpers.GetRegistryString(key, "ProductInfoUrl")); + Assert.True(AutoUpdaterHelpers.GetRegistryBool(key, "AllowUnsafe")); + Assert.Null(AutoUpdaterHelpers.GetRegistryString(key, "Missing")); + Assert.False(AutoUpdaterHelpers.GetRegistryBool(key, "Missing")); } finally { diff --git a/src/UniGetUI.Tests/CLIHandlerTests.cs b/src/UniGetUI.Tests/CLIHandlerTests.cs index 4f011be11e..350ad2bcd7 100644 --- a/src/UniGetUI.Tests/CLIHandlerTests.cs +++ b/src/UniGetUI.Tests/CLIHandlerTests.cs @@ -2,6 +2,7 @@ using UniGetUI.Core.Data; using UniGetUI.Core.SettingsEngine; using UniGetUI.Core.SettingsEngine.SecureSettings; +using UniGetUI.Shared; namespace UniGetUI.Tests; @@ -38,7 +39,10 @@ public void Dispose() [Fact] public void ImportSettings_ReturnsNoSuchFileWhenInputIsMissing() { - int result = CLIHandler.ImportSettings(["unigetui", CLIHandler.IMPORT_SETTINGS, Path.Combine(_testRoot, "missing.json")]); + int result = SharedPreUiCommandDispatcher.ImportSettings( + ["unigetui", SharedPreUiCommandDispatcher.ImportSettingsArgument, Path.Combine(_testRoot, "missing.json")], + SharedPreUiCommandDispatcher.WindowsCliExitCodes + ); Assert.Equal(-1073741809, result); } @@ -50,12 +54,24 @@ public void ExportAndImportSettings_RoundTripConfiguration() Settings.Set(Settings.K.FreshBoolSetting, true); Settings.SetValue(Settings.K.FreshValue, "before-export"); - Assert.Equal(0, CLIHandler.ExportSettings(["unigetui", CLIHandler.EXPORT_SETTINGS, exportPath])); + Assert.Equal( + 0, + SharedPreUiCommandDispatcher.ExportSettings( + ["unigetui", SharedPreUiCommandDispatcher.ExportSettingsArgument, exportPath], + SharedPreUiCommandDispatcher.WindowsCliExitCodes + ) + ); Settings.Set(Settings.K.FreshBoolSetting, false); Settings.SetValue(Settings.K.FreshValue, "after-export"); - Assert.Equal(0, CLIHandler.ImportSettings(["unigetui", CLIHandler.IMPORT_SETTINGS, exportPath])); + Assert.Equal( + 0, + SharedPreUiCommandDispatcher.ImportSettings( + ["unigetui", SharedPreUiCommandDispatcher.ImportSettingsArgument, exportPath], + SharedPreUiCommandDispatcher.WindowsCliExitCodes + ) + ); Assert.True(Settings.Get(Settings.K.FreshBoolSetting)); Assert.Equal("before-export", Settings.GetValue(Settings.K.FreshValue)); @@ -67,13 +83,36 @@ public void ExportAndImportSettings_RoundTripConfiguration() [Fact] public void EnableDisableAndSetValue_MutateSettings() { - Assert.Equal(0, CLIHandler.EnableSetting(["unigetui", CLIHandler.ENABLE_SETTING, nameof(Settings.K.Test1)])); + Assert.Equal( + 0, + SharedPreUiCommandDispatcher.EnableSetting( + ["unigetui", SharedPreUiCommandDispatcher.EnableSettingArgument, nameof(Settings.K.Test1)], + SharedPreUiCommandDispatcher.WindowsCliExitCodes + ) + ); Assert.True(Settings.Get(Settings.K.Test1)); - Assert.Equal(0, CLIHandler.SetSettingsValue(["unigetui", CLIHandler.SET_SETTING_VAL, nameof(Settings.K.FreshValue), "cli-value"])); + Assert.Equal( + 0, + SharedPreUiCommandDispatcher.SetSettingValue( + [ + "unigetui", + SharedPreUiCommandDispatcher.SetSettingValueArgument, + nameof(Settings.K.FreshValue), + "cli-value", + ], + SharedPreUiCommandDispatcher.WindowsCliExitCodes + ) + ); Assert.Equal("cli-value", Settings.GetValue(Settings.K.FreshValue)); - Assert.Equal(0, CLIHandler.DisableSetting(["unigetui", CLIHandler.DISABLE_SETTING, nameof(Settings.K.Test1)])); + Assert.Equal( + 0, + SharedPreUiCommandDispatcher.DisableSetting( + ["unigetui", SharedPreUiCommandDispatcher.DisableSettingArgument, nameof(Settings.K.Test1)], + SharedPreUiCommandDispatcher.WindowsCliExitCodes + ) + ); Assert.False(Settings.Get(Settings.K.Test1)); } @@ -85,8 +124,9 @@ public void EnableAndDisableSecureSettingForUser_MutateSecureSettings() Assert.Equal( 0, - CLIHandler.EnableSecureSettingForUser( - ["unigetui", CLIHandler.ENABLE_SECURE_SETTING_FOR_USER, user, setting] + SharedPreUiCommandDispatcher.EnableSecureSettingForUser( + ["unigetui", SecureSettings.Args.ENABLE_FOR_USER, user, setting], + SharedPreUiCommandDispatcher.WindowsCliExitCodes ) ); Assert.True( @@ -101,8 +141,9 @@ public void EnableAndDisableSecureSettingForUser_MutateSecureSettings() Assert.Equal( 0, - CLIHandler.DisableSecureSettingForUser( - ["unigetui", CLIHandler.DISABLE_SECURE_SETTING_FOR_USER, user, setting] + SharedPreUiCommandDispatcher.DisableSecureSettingForUser( + ["unigetui", SecureSettings.Args.DISABLE_FOR_USER, user, setting], + SharedPreUiCommandDispatcher.WindowsCliExitCodes ) ); Assert.False( diff --git a/src/UniGetUI.Tests/ModernAppLauncherTests.cs b/src/UniGetUI.Tests/ModernAppLauncherTests.cs deleted file mode 100644 index f8d8b4c8ae..0000000000 --- a/src/UniGetUI.Tests/ModernAppLauncherTests.cs +++ /dev/null @@ -1,184 +0,0 @@ -using System.Runtime.InteropServices; -using UniGetUI.Core.Data; -using UniGetUI.Core.SettingsEngine; - -namespace UniGetUI.Tests; - -public sealed class ModernAppLauncherTests : IDisposable -{ - private readonly string _testRoot = Path.Combine( - Path.GetTempPath(), - nameof(ModernAppLauncherTests), - Guid.NewGuid().ToString("N") - ); - - public ModernAppLauncherTests() - { - Directory.CreateDirectory(_testRoot); - CoreData.TEST_DataDirectoryOverride = Path.Combine(_testRoot, "Data"); - Directory.CreateDirectory(CoreData.UniGetUIUserConfigurationDirectory); - Settings.ResetSettings(); - } - - public void Dispose() - { - Settings.ResetSettings(); - CoreData.TEST_DataDirectoryOverride = null; - if (Directory.Exists(_testRoot)) - Directory.Delete(_testRoot, recursive: true); - } - - [Fact] - public void ClassicModeDefaultsToDisabled() - { - Assert.False(ModernAppLauncher.IsClassicModeEnabled()); - - Settings.Set(Settings.K.UseClassicMode, true); - - Assert.True(ModernAppLauncher.IsClassicModeEnabled()); - } - - [Fact] - public void BetaTestersAlwaysUseModernUI() - { - Settings.Set(Settings.K.UseClassicMode, true); - - Assert.True(ModernAppLauncher.IsClassicModeEnabled()); - - Settings.Set(Settings.K.EnableUniGetUIBeta, true); - - Assert.False(ModernAppLauncher.IsClassicModeEnabled()); - } - - [Fact] - public void LegacyDisableClassicModeSettingDoesNotEnableClassicMode() - { - Settings.Set(Settings.K.DisableClassicMode, true); - - Assert.False(ModernAppLauncher.IsClassicModeEnabled()); - } - - [Fact] - public void ResolveModernExecutablePath_PrefersRootExecutable() - { - string baseDirectory = Path.Combine(_testRoot, "Launcher"); - Directory.CreateDirectory(baseDirectory); - - string expected = Path.Combine(baseDirectory, ModernAppLauncher.ModernAppExecutableName); - File.WriteAllText(expected, ""); - - string avaloniaDirectory = Path.Combine(baseDirectory, ModernAppLauncher.ModernAppDirectoryName); - Directory.CreateDirectory(avaloniaDirectory); - File.WriteAllText(Path.Combine(avaloniaDirectory, ModernAppLauncher.ModernAppExecutableName), ""); - - Assert.Equal(expected, ModernAppLauncher.ResolveModernExecutablePath(baseDirectory)); - } - - [Fact] - public void ResolveModernExecutablePath_FallsBackToAvaloniaSubdirectory() - { - string baseDirectory = Path.Combine(_testRoot, "Launcher"); - string avaloniaDirectory = Path.Combine(baseDirectory, ModernAppLauncher.ModernAppDirectoryName); - Directory.CreateDirectory(avaloniaDirectory); - - string expected = Path.Combine( - avaloniaDirectory, - ModernAppLauncher.ModernAppExecutableName - ); - File.WriteAllText(expected, ""); - - Assert.Equal(expected, ModernAppLauncher.ResolveModernExecutablePath(baseDirectory)); - } - - [Fact] - public void ResolveModernExecutablePath_FindsDevelopmentBuildOutput() - { - string baseDirectory = Path.Combine( - _testRoot, - "UniGetUI", - "bin", - "x64", - "Debug", - "net10.0-windows10.0.26100.0" - ); - Directory.CreateDirectory(baseDirectory); - - string expected = Path.Combine( - _testRoot, - "UniGetUI.Avalonia", - "bin", - "x64", - "Debug", - "net10.0-windows10.0.26100.0", - ModernAppLauncher.ModernAppExecutableName - ); - Directory.CreateDirectory(Path.GetDirectoryName(expected)!); - File.WriteAllText(expected, ""); - - Assert.Equal(expected, ModernAppLauncher.ResolveModernExecutablePath(baseDirectory)); - } - - [Fact] - public void ResolveModernExecutablePath_PrefersCurrentArchitectureDevelopmentBuildOutput() - { - string baseDirectory = Path.Combine( - _testRoot, - "UniGetUI", - "bin", - "x64", - "Debug", - "net10.0-windows10.0.26100.0", - "win-x64" - ); - Directory.CreateDirectory(baseDirectory); - - string currentRuntimeIdentifier = RuntimeInformation.ProcessArchitecture switch - { - Architecture.X64 => "win-x64", - Architecture.Arm64 => "win-arm64", - Architecture.X86 => "win-x86", - Architecture.Arm => "win-arm", - _ => "win-x64", - }; - string otherRuntimeIdentifier = currentRuntimeIdentifier == "win-x64" - ? "win-arm64" - : "win-x64"; - - string expected = CreateAvaloniaDevelopmentExecutable(currentRuntimeIdentifier); - string other = CreateAvaloniaDevelopmentExecutable(otherRuntimeIdentifier); - - File.SetLastWriteTimeUtc(expected, DateTime.UtcNow.AddMinutes(-5)); - File.SetLastWriteTimeUtc(other, DateTime.UtcNow); - - Assert.Equal(expected, ModernAppLauncher.ResolveModernExecutablePath(baseDirectory)); - } - - private string CreateAvaloniaDevelopmentExecutable(string runtimeIdentifier) - { - string path = Path.Combine( - _testRoot, - "UniGetUI.Avalonia", - "bin", - "Debug", - "net10.0-windows10.0.26100.0", - runtimeIdentifier, - ModernAppLauncher.ModernAppExecutableName - ); - Directory.CreateDirectory(Path.GetDirectoryName(path)!); - File.WriteAllText(path, ""); - return path; - } - - [Fact] - public void CreateStartInfo_PreservesArguments() - { - string executable = Path.Combine(_testRoot, ModernAppLauncher.ModernAppExecutableName); - string[] args = ["--daemon", "--set-setting-value", "FreshValue", "value with spaces"]; - - var startInfo = ModernAppLauncher.CreateStartInfo(executable, args); - - Assert.Equal(executable, startInfo.FileName); - Assert.Equal(_testRoot, startInfo.WorkingDirectory); - Assert.Equal(args, startInfo.ArgumentList); - } -} diff --git a/src/UniGetUI.Tests/UniGetUI.Tests.csproj b/src/UniGetUI.Tests/UniGetUI.Tests.csproj index 7a3b4a57e6..c7ca3d7dfb 100644 --- a/src/UniGetUI.Tests/UniGetUI.Tests.csproj +++ b/src/UniGetUI.Tests/UniGetUI.Tests.csproj @@ -45,8 +45,6 @@ - - - + diff --git a/src/UniGetUI.Windows.slnx b/src/UniGetUI.Windows.slnx index d3ecc29b0e..05479163f1 100644 --- a/src/UniGetUI.Windows.slnx +++ b/src/UniGetUI.Windows.slnx @@ -229,8 +229,4 @@ - - - - diff --git a/src/UniGetUI/.vscode/launch.json b/src/UniGetUI/.vscode/launch.json deleted file mode 100644 index 53a6648256..0000000000 --- a/src/UniGetUI/.vscode/launch.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "configurations": [ - { - "name": "UniGetUI (Unpackaged)", - "type": "coreclr", - "request": "launch", - "preLaunchTask": "build UniGetUI (Debug x64)", - "program": "${workspaceFolder}/bin/x64/Debug/net10.0-windows10.0.26100.0/UniGetUI.exe", - "args": [], - "cwd": "${workspaceFolder}/bin/x64/Debug/net10.0-windows10.0.26100.0/", - "console": "integratedTerminal", - "stopAtEntry": false - } - ] -} \ No newline at end of file diff --git a/src/UniGetUI/.vscode/settings.json b/src/UniGetUI/.vscode/settings.json deleted file mode 100644 index daadc0ccd0..0000000000 --- a/src/UniGetUI/.vscode/settings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "cSpell.words": [ - "nupkg", - "windowspowershell" - ] -} \ No newline at end of file diff --git a/src/UniGetUI/.vscode/tasks.json b/src/UniGetUI/.vscode/tasks.json deleted file mode 100644 index a8075f1791..0000000000 --- a/src/UniGetUI/.vscode/tasks.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "label": "build UniGetUI (Debug x64)", - "type": "process", - "command": "dotnet", - "args": [ - "build", - "${workspaceFolder}/UniGetUI.csproj", - "--configuration", - "Debug", - "/p:Platform=x64" - ], - "group": { - "kind": "build", - "isDefault": true - }, - "problemMatcher": "$msCompile" - } - ] -} \ No newline at end of file diff --git a/src/UniGetUI/App.xaml b/src/UniGetUI/App.xaml deleted file mode 100644 index 0766abe55c..0000000000 --- a/src/UniGetUI/App.xaml +++ /dev/null @@ -1,985 +0,0 @@ - - - - - - - - - - - - - - - /Assets/Symbols/Font/fonts/UniGetUI-Symbols.ttf#UniGetUI-Symbols - - - - - - - - - - - diff --git a/src/UniGetUI/App.xaml.cs b/src/UniGetUI/App.xaml.cs deleted file mode 100644 index 1791460763..0000000000 --- a/src/UniGetUI/App.xaml.cs +++ /dev/null @@ -1,750 +0,0 @@ -using System.Diagnostics; -using System.Text.RegularExpressions; -using CommunityToolkit.WinUI.Helpers; -using Microsoft.UI.Dispatching; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.Windows.AppLifecycle; -using Microsoft.Windows.AppNotifications; -using UniGetUI.Core.Data; -using UniGetUI.Core.IconEngine; -using UniGetUI.Core.Logging; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.SettingsEngine.SecureSettings; -using UniGetUI.Core.Tools; -using UniGetUI.Interface; -using UniGetUI.Interface.Telemetry; -using UniGetUI.PackageEngine; -using UniGetUI.PackageEngine.Classes.Manager.Classes; -using UniGetUI.PackageEngine.Enums; -using UniGetUI.PackageEngine.Interfaces; -using UniGetUI.PackageOperations; -using UniGetUI.Pages.DialogPages; -using UniGetUI.Services; -using Windows.ApplicationModel.Activation; -using LaunchActivatedEventArgs = Microsoft.UI.Xaml.LaunchActivatedEventArgs; - -namespace UniGetUI -{ - public partial class MainApp - { - public static DispatcherQueue Dispatcher = null!; - - public static class Tooltip - { - private static int _errors_occurred; - public static int ErrorsOccurred - { - get => _errors_occurred; - set - { - _errors_occurred = value; - Instance?.MainWindow?.UpdateSystemTrayStatus(); - } - } - - private static bool _restart_required; - public static bool RestartRequired - { - get => _restart_required; - set - { - _restart_required = value; - Instance?.MainWindow?.UpdateSystemTrayStatus(); - } - } - - private static int _available_updates; - public static int AvailableUpdates - { - get => _available_updates; - set - { - _available_updates = value; - Instance?.MainWindow?.UpdateSystemTrayStatus(); - } - } - } - - public bool RaiseExceptionAsFatal = true; - private int _isQuitting; - - public MainWindow MainWindow = null!; - public ThemeListener ThemeListener = null!; - - private readonly IpcServer IpcApi = new() - { - SessionKind = IpcTransportOptions.GuiSessionKind, - }; - public static MainApp Instance = null!; - - public MainApp() - { - try - { - Instance = this; - Dispatcher = DispatcherQueue.GetForCurrentThread(); - InitializeComponent(); - ApplyThemeToApp(); - _ = LoadComponentsAsync(); - } - catch (Exception e) - { - CrashHandler.ReportFatalException(e); - } - } - - /// - /// This method must be called during the constructor, because - /// setting RequestedTheme on runtime will crash the app - /// - private void ApplyThemeToApp() - { - string preferredTheme = Settings.GetValue(Settings.K.PreferredTheme); - if (preferredTheme == "dark") - { - RequestedTheme = ApplicationTheme.Dark; - } - else if (preferredTheme == "light") - { - RequestedTheme = ApplicationTheme.Light; - } - ThemeListener = new ThemeListener(); - } - - internal static async Task LoadGSudoAsync() - { - try - { - if (Settings.Get(Settings.K.ProhibitElevation)) - { - Logger.Warn( - "UniGetUI Elevator has been disabled since elevation is prohibited!" - ); - } - - if (SecureSettings.Get(SecureSettings.K.ForceUserGSudo)) - { - var res = await CoreTools.WhichAsync("gsudo.exe"); - if (res.Item1) - { - CoreData.ElevatorPath = res.Item2; - Logger.Warn( - $"Using user GSudo (forced by user) at {CoreData.ElevatorPath}" - ); - return; - } - } - -#if DEBUG - Logger.Warn( - $"Using bundled GSudo at {CoreData.ElevatorPath} since UniGetUI Elevator is not available!" - ); - CoreData.ElevatorPath = (await CoreTools.WhichAsync("gsudo.exe")).Item2; -#else - CoreData.ElevatorPath = Path.Join( - CoreData.UniGetUIExecutableDirectory, - "Assets", - "Utilities", - "UniGetUI Elevator.exe" - ); - Logger.Debug($"Using built-in UniGetUI Elevator at {CoreData.ElevatorPath}"); -#endif - } - catch (Exception ex) - { - Logger.Error("Elevator/GSudo failed to be loaded!"); - Logger.Error(ex); - } - } - - private void RegisterErrorHandling() - { - try - { - UnhandledException += (_, e) => - { - if (Debugger.IsAttached) - Debugger.Break(); - string message = $"Unhandled Exception raised: {e.Message}"; - string stackTrace = $"Stack Trace: \n{e.Exception.StackTrace}"; - Logger.Error(" -"); - Logger.Error(" -"); - Logger.Error(" ⚠️⚠️⚠️ START OF UNHANDLED ERROR TRACE ⚠️⚠️⚠️"); - Logger.Error(message); - Logger.Error(stackTrace); - Logger.Error(" ⚠️⚠️⚠️ END OF UNHANDLED ERROR TRACE ⚠️⚠️⚠️"); - Logger.Error(" -"); - Logger.Error(" -"); - if ( - Environment.GetCommandLineArgs().Contains("--report-all-errors") - || RaiseExceptionAsFatal - || MainWindow is null - ) - { - CrashHandler.ReportFatalException(e.Exception); - } - else - { - MainWindow.ErrorBanner.Title = CoreTools.Translate("Something went wrong"); - MainWindow.ErrorBanner.Message = CoreTools.Translate( - "An interal error occurred. Please view the log for further details." - ); - MainWindow.ErrorBanner.IsOpen = true; - Button button = new() { Content = CoreTools.Translate("UniGetUI Log") }; - button.Click += (sender, args) => - MainWindow.NavigationPage.UniGetUILogs_Click(sender, args); - MainWindow.ErrorBanner.ActionButton = button; - DialogHelper.HideAllLoadingDialogs(); - e.Handled = true; - } - }; - } - catch (Exception ex) - { - if (Debugger.IsAttached) - Debugger.Break(); - Logger.Error(ex); - } - - try - { - TaskScheduler.UnobservedTaskException += (sender, args) => - { - Logger.Error( - $"An unhandled exception occurred in a Task (sender: {sender?.GetType().ToString() ?? "null"})" - ); - Exception? e = args.Exception.InnerException; - Logger.Error(args.Exception); - while (e is not null) - { - Logger.Error("------------------------------"); - Logger.Error(e); - e = e.InnerException; - } - - if (Debugger.IsAttached) - Debugger.Break(); - - Dispatcher.TryEnqueue(() => - { - if (MainWindow is null) - return; // MainWindow could have not been loaded yet - try - { - MainWindow.ErrorBanner.Title = CoreTools.Translate( - "Something went wrong" - ); - MainWindow.ErrorBanner.Message = CoreTools.Translate( - "An interal error occurred. Please view the log for further details." - ); - MainWindow.ErrorBanner.IsOpen = true; - Button button = new() { Content = CoreTools.Translate("UniGetUI Log") }; - if (MainWindow.NavigationPage is not null) - { // MainWindow.NavigationPage could have not been loaded yet - button.Click += (s, a) => - MainWindow.NavigationPage.UniGetUILogs_Click(s, a); - } - MainWindow.ErrorBanner.ActionButton = button; - DialogHelper.HideAllLoadingDialogs(); - } - catch (Exception ex) - { - Logger.Error(ex); - } - }); - - args.SetObserved(); - }; - } - catch (Exception ex) - { - if (Debugger.IsAttached) - Debugger.Break(); - Logger.Error(ex); - } - } - - private static void SetUpWebViewUserDataFolder() - { - try - { - string WebViewPath = Path.Join(Path.GetTempPath(), "UniGetUI", "WebView"); - if (!Directory.Exists(WebViewPath)) - { - Directory.CreateDirectory(WebViewPath); - } - - Environment.SetEnvironmentVariable("WEBVIEW2_USER_DATA_FOLDER", WebViewPath); - } - catch (Exception e) - { - Logger.Warn("Could not set up data folder for WebView2"); - Logger.Warn(e); - } - } - - /// - /// Initialize the main window - /// - private void InitializeMainWindow() - { - MainWindow = new MainWindow { BlockLoading = true }; - MainWindow.Closed += (_, _) => DisposeAndQuit(0); - - nint hWnd = MainWindow.GetWindowHandle(); - var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hWnd); - var appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId); - - appWindow?.Closing += MainWindow.HandleClosingEvent; - - MainWindow.DoEntryTextAnimationAsync(); - } - - /// - /// Register the notification activation event - /// - private void RegisterNotificationService() - { - try - { - AppNotificationManager.Default.NotificationInvoked += (_, args) => - { - MainWindow.DispatcherQueue.TryEnqueue(() => - { - MainWindow.HandleNotificationActivation(args); - }); - }; - AppNotificationManager.Default.Register(); - } - catch (Exception ex) - { - Logger.Error("Could not register notification event"); - Logger.Error(ex); - } - } - - /// - /// Background component loader - /// - private async Task LoadComponentsAsync() - { - try - { - RegisterErrorHandling(); - - // MainWindow depends on this - await Task.Run(PEInterface.LoadLoaders); - - // Create MainWindow - InitializeMainWindow(); - if (!CoreData.WasDaemon) - MainWindow.Activate(); - - // Show crash report from the previous session on top of the loading - // screen and wait for the user to dismiss it before continuing. - if (File.Exists(CrashHandler.PendingCrashFile)) - { - try - { - // In daemon mode the DWM/XAML threads are suspended; resume them - // temporarily so the crash window can render, then re-suspend. - bool resumedForCrash = CoreData.WasDaemon; - if (resumedForCrash) - { - DWMThreadHelper.ChangeState_DWM(false); - DWMThreadHelper.ChangeState_XAML(false); - } - - string report = File.ReadAllText(CrashHandler.PendingCrashFile); - File.Delete(CrashHandler.PendingCrashFile); - var tcs = new TaskCompletionSource(); - var crashWindow = new CrashReportWindow(report); - crashWindow.Closed += (_, _) => tcs.TrySetResult(); - crashWindow.Activate(); - await tcs.Task; - - if (resumedForCrash) - { - DWMThreadHelper.ChangeState_DWM(true); - DWMThreadHelper.ChangeState_XAML(true); - } - } - catch { /* must not prevent normal startup */ } - } - - IEnumerable iniTasks = - [ - Task.Run(PEInterface.LoadManagers), // Takes most of the time - Task.Run(SetUpWebViewUserDataFolder), - Task.Run(IconDatabase.Instance.LoadFromCacheAsync), - Task.Run(RegisterNotificationService), - Task.Run(LoadGSudoAsync), - Task.Run(InitializeBackgroundAPI), - ]; - - // Load essential components - await Task.WhenAll(iniTasks); - - // Load non-essential components - TelemetryHandler.Configure( - Secrets.GetOpenSearchUsername(), - Secrets.GetOpenSearchPassword()); - AbstractOperation.QueueDrained += (_, _) => _ = TelemetryHandler.FlushPackageEventsAsync(); - _ = TelemetryHandler.InitializeAsync(); - _ = IconDatabase.Instance.LoadIconAndScreenshotsDatabaseAsync(); - - // Load interface - Logger.Info( - "LoadComponentsAsync finished executing. All managers loaded. Proceeding to interface." - ); - MainWindow.SwitchToInterface(); - - RaiseExceptionAsFatal = false; - - // Process any remaining command-line arguments - MainWindow.ProcessCommandLineParameters(); - MainWindow.ParametersToProcess.ItemEnqueued += (_, _) => - { - MainWindow.DispatcherQueue.TryEnqueue(MainWindow.ProcessCommandLineParameters); - }; - - _ = CheckForMissingDependencies(); - - var i = await Task.Run(() => IntegrityTester.CheckIntegrity(allowRetry: true)); - if (!i.Passed && !Settings.Get(Settings.K.DisableIntegrityChecks)) - { - _ = DialogHelper.ShowIntegrityResult(); - } - } - catch (Exception e) - { - CrashHandler.ReportFatalException(e); - } - } - - private async Task InitializeBackgroundAPI() - { - // Bind the IPC API to the main interface - try - { - if (Settings.Get(Settings.K.DisableApi)) - return; - - IpcApi.AppInfoProvider = () => RunOnUiThread(GetAppInfo); - IpcApi.ShowAppHandler = () => RunOnUiThread(ShowApp); - IpcApi.NavigateAppHandler = request => RunOnUiThread(() => NavigateApp(request)); - IpcApi.QuitAppHandler = () => RunOnUiThread(QuitApp); - IpcApi.ShowPackageHandler = request => RunOnUiThread(() => ShowPackage(request)); - - IpcApi.OnUpgradeAll += (_, _) => - MainWindow.DispatcherQueue.TryEnqueue(() => - { - _ = Operations.UpdateAll(); - }); - - IpcApi.OnUpgradeAllForManager += (s, managerName) => - MainWindow.DispatcherQueue.TryEnqueue(() => - { - _ = Operations.UpdateAllForManager(managerName); - }); - - await IpcApi.Start(); - } - catch (Exception ex) - { - Logger.Error("Could not initialize IPC API:"); - Logger.Error(ex); - } - } - - private static T RunOnUiThread(Func action) - { - if (Instance.MainWindow.DispatcherQueue.HasThreadAccess) - { - return action(); - } - - var completion = new TaskCompletionSource( - TaskCreationOptions.RunContinuationsAsynchronously - ); - if (!Instance.MainWindow.DispatcherQueue.TryEnqueue(() => - { - try - { - completion.SetResult(action()); - } - catch (Exception ex) - { - completion.SetException(ex); - } - })) - { - throw new InvalidOperationException("Failed to dispatch the app automation request."); - } - - return completion.Task.GetAwaiter().GetResult(); - } - - private IpcAppInfo GetAppInfo() - { - return new IpcAppInfo - { - Headless = false, - WindowAvailable = MainWindow is not null, - WindowVisible = MainWindow?.IsInterfaceVisible ?? false, - CanShowWindow = MainWindow is not null, - CanNavigate = MainWindow?.NavigationPage is not null, - CanQuit = true, - CurrentPage = MainWindow?.NavigationPage is null - ? "" - : IpcAppPages.ToPageName(MainWindow.NavigationPage.CurrentPage.ToString()), - SupportedPages = IpcAppPages.SupportedPages, - }; - } - - private IpcCommandResult ShowApp() - { - MainWindow.ShowFromTray(); - return IpcCommandResult.Success("show-app"); - } - - private IpcCommandResult NavigateApp(IpcAppNavigateRequest request) - { - string page = IpcAppPages.NormalizePageName(request.Page); - IPackageManager? manager = ResolveManager(request.ManagerName); - - switch (page) - { - case "discover": - MainWindow.NavigationPage.NavigateTo(PageType.Discover); - break; - case "updates": - MainWindow.NavigationPage.NavigateTo(PageType.Updates); - break; - case "installed": - MainWindow.NavigationPage.NavigateTo(PageType.Installed); - break; - case "bundles": - MainWindow.NavigationPage.NavigateTo(PageType.Bundles); - break; - case "settings": - MainWindow.NavigationPage.NavigateTo(PageType.Settings); - break; - case "managers": - MainWindow.NavigationPage.OpenManagerSettings(manager); - break; - case "own-log": - MainWindow.NavigationPage.NavigateTo(PageType.OwnLog); - break; - case "manager-log": - MainWindow.NavigationPage.OpenManagerLogs(manager); - break; - case "operation-history": - MainWindow.NavigationPage.NavigateTo(PageType.OperationHistory); - break; - case "help": - MainWindow.NavigationPage.ShowHelp(request.HelpAttachment ?? ""); - break; - case "release-notes": - _ = DialogHelper.ShowReleaseNotes(); - break; - case "about": - _ = DialogHelper.ShowAboutUniGetUI(); - break; - default: - throw new InvalidOperationException( - $"Unsupported app page \"{request.Page}\"." - ); - } - - MainWindow.ShowFromTray(); - return IpcCommandResult.Success("navigate-app"); - } - - private IpcCommandResult ShowPackage(IpcPackageActionRequest request) - { - IPackage package = IpcPackageApi.ResolvePackage(request); - MainWindow.ShowFromTray(); - _ = DialogHelper.ShowPackageDetails( - package, - OperationType.Install, - TEL_InstallReferral.DIRECT_SEARCH - ); - return IpcCommandResult.Success("show-package"); - } - - private IpcCommandResult QuitApp() - { - _ = Task.Run(async () => - { - await Task.Delay(150); - RunOnUiThread(() => - { - DisposeAndQuit(); - return true; - }); - }); - return IpcCommandResult.Success("quit-app"); - } - - private static IPackageManager? ResolveManager(string? managerName) - { - if (string.IsNullOrWhiteSpace(managerName)) - { - return null; - } - - return PEInterface.Managers.FirstOrDefault(manager => - manager.Id.Equals(managerName, StringComparison.OrdinalIgnoreCase)) - ?? throw new InvalidOperationException($"Unknown manager \"{managerName}\"."); - } - - private async Task CheckForMissingDependencies() - { - // Check for missing dependencies on package managers - List missing_deps = []; - foreach (IPackageManager manager in PEInterface.Managers) - { - if (!manager.IsReady()) - { - continue; - } - - foreach (ManagerDependency dependency in manager.Dependencies) - { - bool isInstalled = true; - try - { - isInstalled = await dependency.IsInstalled(); - } - catch (Exception ex) - { - Logger.Error( - $"An error occurred while checking if dependency {dependency.Name} was installed:" - ); - Logger.Error(ex); - } - - if (!isInstalled) - { - if ( - Settings.GetDictionaryItem( - Settings.K.DependencyManagement, - dependency.Name - ) == "skipped" - ) - { - Logger.Error( - $"Dependency {dependency.Name} was not found, and the user set it to not be reminded of the missing dependency" - ); - } - else - { - Logger.Warn( - $"Dependency {dependency.Name} was not found for manager {manager.Name}, marking to prompt..." - ); - missing_deps.Add(dependency); - } - } - else - { - Logger.Info( - $"Dependency {dependency.Name} for manager {manager.Name} is present" - ); - } - } - } - await MainWindow.HandleMissingDependencies(missing_deps); - } - - protected override void OnLaunched(LaunchActivatedEventArgs args) - { - if (!CoreData.WasDaemon) - MainWindow?.Activate(); - } - - public async Task ShowMainWindowFromRedirectAsync(AppActivationArguments rawArgs) - { - while (MainWindow is null) - await Task.Delay(100); - - ExtendedActivationKind kind = rawArgs.Kind; - if (kind is ExtendedActivationKind.Launch) - { - if (rawArgs.Data is ILaunchActivatedEventArgs launchArguments) - { - // If the app redirection event comes from a launch, extract - // the CLI arguments and redirect them to the ParameterProcessor - foreach ( - Match argument in Regex.Matches( - launchArguments.Arguments, - "([^ \"']+|\"[^\"]+\"|'[^']+')" - ) - ) - { - MainWindow.ParametersToProcess.Enqueue(argument.Value); - } - } - else - { - Logger.Error( - "REDIRECTOR ACTIVATOR: args.Data was null when casted to ILaunchActivatedEventArgs" - ); - } - } - else - { - Logger.Warn("REDIRECTOR ACTIVATOR: args.Kind is not Launch but rather " + kind); - } - - if (kind is ExtendedActivationKind.Launch or ExtendedActivationKind.Protocol) - { - MainWindow.DispatcherQueue.TryEnqueue(MainWindow.Activate); - } - } - - public void DisposeAndQuit(int outputCode = 0) - { - if (Interlocked.Exchange(ref _isQuitting, 1) == 1) - { - return; - } - - Logger.Warn("Quitting UniGetUI"); - DWMThreadHelper.ChangeState_DWM(false); - DWMThreadHelper.ChangeState_XAML(false); - MainWindow?.Close(); - _ = StopIpcAndExitAsync(outputCode); - // await Task.Delay(100); - // Environment.Exit(outputCode); - } - - public bool IsQuitting => Interlocked.CompareExchange(ref _isQuitting, 0, 0) == 1; - - private async Task StopIpcAndExitAsync(int outputCode) - { - try - { - await IpcApi.Stop().WaitAsync(TimeSpan.FromSeconds(5)); - } - catch (TimeoutException ex) - { - Logger.Warn("Timed out while stopping IPC API during shutdown"); - Logger.Warn(ex); - } - catch (Exception ex) - { - Logger.Error(ex); - } - - Environment.Exit(outputCode); - } - - public void KillAndRestart() - { - CoreTools.ScheduleRelaunchAfterExit(); - DisposeAndQuit(); - } - } -} diff --git a/src/UniGetUI/AppOperationHelper.cs b/src/UniGetUI/AppOperationHelper.cs deleted file mode 100644 index f4cc6d7806..0000000000 --- a/src/UniGetUI/AppOperationHelper.cs +++ /dev/null @@ -1,554 +0,0 @@ -using System.Collections.Concurrent; -using System.Collections.ObjectModel; -using Microsoft.UI.Xaml.Controls; -using UniGetUI.Controls.OperationWidgets; -using UniGetUI.Core.Logging; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; -using UniGetUI.Interface; -using UniGetUI.Interface.Enums; -using UniGetUI.Interface.Telemetry; -using UniGetUI.PackageEngine; -using UniGetUI.PackageEngine.Enums; -using UniGetUI.PackageEngine.Interfaces; -using UniGetUI.PackageEngine.Managers.PowerShellManager; -using UniGetUI.PackageEngine.Managers.WingetManager; -using UniGetUI.PackageEngine.Operations; -using UniGetUI.PackageEngine.PackageClasses; -using UniGetUI.PackageEngine.PackageLoader; -using UniGetUI.PackageOperations; -using UniGetUI.Pages.DialogPages; -using Windows.Storage; -using Windows.Storage.Pickers; - -namespace UniGetUI; - -public partial class MainApp -{ - public static class Operations - { - public static bool AreThereRunningOperations() - { - return _operationList.Any() - && _operationList.Any(x => - x.Operation.Status is OperationStatus.Running or OperationStatus.InQueue - ); - } - - public static ObservableCollection _operationList = new(); - - public static OperationControl Add(AbstractOperation op) - { - IpcOperationApi.Track(op); - var control = new OperationControl(op); - _operationList.Add(control); - return control; - } - - public static void Remove(OperationControl control) => _operationList.Remove(control); - - public static void Remove(AbstractOperation op) - { - foreach (var control in _operationList.Where(x => x.Operation == op).ToArray()) - { - _operationList.Remove(control); - } - } - - /* - * - * OPERATION CREATION HELPERS - * - */ - public static async Task AskLocationAndDownload( - IPackage? package, - TEL_InstallReferral referral - ) - { - if (package is null) - return null; - - if ( - package.Manager is WinGet - && Settings.Get(Settings.K.WinGetDownloadFullManifest) - ) - { - return await AskFolderAndDownloadWinGetManifest(package, referral); - } - - int loadingId = DialogHelper.ShowLoadingDialog(CoreTools.Translate("Please wait...")); - try - { - var details = package.Details; - await details.Load(); - - if (details.InstallerUrl is null) - { - DialogHelper.HideLoadingDialog(loadingId); - var dialog = new ContentDialog - { - Title = CoreTools.Translate("Download failed"), - Content = CoreTools.Translate( - "No applicable installer was found for the package {0}", - package.Name - ), - PrimaryButtonText = CoreTools.Translate("Ok"), - DefaultButton = ContentDialogButton.Primary, - XamlRoot = Instance.MainWindow.Content.XamlRoot, - }; - await DialogHelper.ShowDialogAsync(dialog); - return null; - } - - FileSavePicker savePicker = new(); - MainWindow window = Instance.MainWindow; - IntPtr hWnd = WinRT.Interop.WindowNative.GetWindowHandle(window); - WinRT.Interop.InitializeWithWindow.Initialize(savePicker, hWnd); - savePicker.SuggestedStartLocation = PickerLocationId.Downloads; - - string name = await package.GetInstallerFileName() ?? ""; - string extension; - if (!name.Where(x => x == '.').Any()) - { // As a last resort, we need an extension for the file picker to work - extension = "unknown"; - name = name + "." + extension; - } - else - { - extension = CoreTools.MakeValidFileName(name.Split('.')[^1]); - } - - savePicker.SuggestedFileName = name; - - if (package.Manager is BaseNuGet) - { - extension = "nupkg"; - savePicker.FileTypeChoices.Add("NuGet package", [".nupkg"]); - } - - savePicker.FileTypeChoices.Add("Automatic", [$".{extension}"]); - savePicker.FileTypeChoices.Add("Executable", [".exe"]); - savePicker.FileTypeChoices.Add("MSI", [".msi"]); - savePicker.FileTypeChoices.Add("Compressed file", [".zip"]); - savePicker.FileTypeChoices.Add("MSIX", [".msix"]); - savePicker.FileTypeChoices.Add("APPX", [".appx"]); - savePicker.FileTypeChoices.Add("Tarball", [".tar"]); - savePicker.FileTypeChoices.Add("Compressed Tarball", [".tgz"]); - - StorageFile file = await savePicker.PickSaveFileAsync(); - - DialogHelper.HideLoadingDialog(loadingId); - if (file is not null) - { - var op = new DownloadOperation(package, file.Path); - op.OperationSucceeded += (_, _) => - TelemetryHandler.DownloadPackage(package, TEL_OP_RESULT.SUCCESS, referral); - op.OperationFailed += (_, _) => - TelemetryHandler.DownloadPackage(package, TEL_OP_RESULT.FAILED, referral); - Add(op); - Instance.MainWindow.UpdateSystemTrayStatus(); - return op; - } - - return null; - } - catch (Exception ex) - { - Logger.Error( - $"An error occurred while downloading the installer for the package {package.Id}" - ); - Logger.Error(ex); - DialogHelper.HideLoadingDialog(loadingId); - return null; - } - } - - private static async Task AskFolderAndDownloadWinGetManifest( - IPackage package, - TEL_InstallReferral referral - ) - { - try - { - var hWnd = Instance.MainWindow.GetWindowHandle(); - var picker = new ExternalLibraries.Pickers.FolderPicker(hWnd); - var outputPath = await Task.Run(picker.Show); - if (string.IsNullOrEmpty(outputPath)) - return null; - - var op = new WinGetManifestDownloadOperation(package, outputPath); - op.OperationSucceeded += (_, _) => - TelemetryHandler.DownloadPackage(package, TEL_OP_RESULT.SUCCESS, referral); - op.OperationFailed += (_, _) => - TelemetryHandler.DownloadPackage(package, TEL_OP_RESULT.FAILED, referral); - Add(op); - Instance.MainWindow.UpdateSystemTrayStatus(); - return op; - } - catch (Exception ex) - { - Logger.Error( - $"An error occurred while downloading the WinGet manifest for package {package.Id}" - ); - Logger.Error(ex); - return null; - } - } - - public static async Task Download( - IEnumerable packages, - TEL_InstallReferral referral - ) - { - try - { - if (!packages.Any()) - return; - - var hWnd = MainApp.Instance.MainWindow.GetWindowHandle(); - var a = new ExternalLibraries.Pickers.FolderPicker(hWnd); - var outputPath = await Task.Run(a.Show); - if (outputPath == "") - return; - - bool fullManifest = Settings.Get(Settings.K.WinGetDownloadFullManifest); - foreach (var package in packages) - { - if ( - package.Source.IsVirtualManager - || !package.Manager.Capabilities.CanDownloadInstaller - ) - { - Logger.Warn($"Package {package.Id} cannot have its installer downloaded."); - continue; - } - - AbstractOperation op = - fullManifest && package.Manager is WinGet - ? new WinGetManifestDownloadOperation(package, outputPath) - : new DownloadOperation(package, outputPath); - op.OperationSucceeded += (_, _) => - TelemetryHandler.DownloadPackage(package, TEL_OP_RESULT.SUCCESS, referral); - op.OperationFailed += (_, _) => - TelemetryHandler.DownloadPackage(package, TEL_OP_RESULT.FAILED, referral); - Add(op); - } - } - catch (Exception ex) - { - Logger.Error("An error occurred while attempting to bulk-download packages:"); - Logger.Error(ex); - } - } - - /* - * - * - * PACKAGE INSTALLATION - * - * - */ - public static async Task Install( - IPackage? package, - TEL_InstallReferral referral, - bool? elevated = null, - bool? interactive = null, - bool? no_integrity = null, - bool ignoreParallel = false, - AbstractOperation? req = null - ) - { - if (package is null) - return null; - - var options = await InstallOptionsFactory.LoadApplicableAsync( - package, - elevated, - interactive, - no_integrity - ); - var operation = new InstallPackageOperation(package, options, ignoreParallel, req); - operation.OperationSucceeded += (_, _) => - TelemetryHandler.InstallPackage(package, TEL_OP_RESULT.SUCCESS, referral); - operation.OperationFailed += (_, _) => - TelemetryHandler.InstallPackage(package, TEL_OP_RESULT.FAILED, referral); - Add(operation); - Instance.MainWindow.UpdateSystemTrayStatus(); - return operation; - } - - public static void Install( - IReadOnlyList packages, - TEL_InstallReferral referral, - bool? elevated = null, - bool? interactive = null, - bool? no_integrity = null - ) - { - foreach (var package in packages) - { - _ = Install(package, referral, elevated, interactive, no_integrity); - } - } - - public static async Task UninstallThenReinstall( - IPackage? package, - TEL_InstallReferral referral - ) - { - if (package is null) - return null; - - var options = await InstallOptionsFactory.LoadApplicableAsync(package); - - var uninstallOp = new UninstallPackageOperation(package, options); - uninstallOp.OperationSucceeded += (_, _) => - TelemetryHandler.UninstallPackage(package, TEL_OP_RESULT.SUCCESS); - uninstallOp.OperationFailed += (_, _) => - TelemetryHandler.UninstallPackage(package, TEL_OP_RESULT.FAILED); - - var installOp = new InstallPackageOperation(package, options, req: uninstallOp); - installOp.OperationSucceeded += (_, _) => - TelemetryHandler.InstallPackage(package, TEL_OP_RESULT.SUCCESS, referral); - installOp.OperationFailed += (_, _) => - TelemetryHandler.InstallPackage(package, TEL_OP_RESULT.FAILED, referral); - - Add(installOp); - Instance.MainWindow.UpdateSystemTrayStatus(); - return installOp; - } - - /* - * - * - * PACKAGE UPDATE - * - * - */ - public static async Task Update( - IPackage? package, - bool? elevated = null, - bool? interactive = null, - bool? no_integrity = null, - bool ignoreParallel = false, - AbstractOperation? req = null - ) - { - if (package is null) - return null; - if (package.NewerVersionIsInstalled()) - { - Logger.Warn( - $"A newer version of {package.Id} has been detected, the update will not be performed!" - ); - UpgradablePackagesLoader.Instance.Remove(package); - foreach (var eq in InstalledPackagesLoader.Instance.GetEquivalentPackages(package)) - { // Remove upgradable tag from all installed packages - eq.Tag = PackageTag.Default; - } - - return null; - } - - var options = await InstallOptionsFactory.LoadApplicableAsync( - package, - elevated, - interactive, - no_integrity - ); - var operation = new UpdatePackageOperation(package, options, ignoreParallel, req); - operation.OperationSucceeded += (_, _) => - TelemetryHandler.UpdatePackage(package, TEL_OP_RESULT.SUCCESS); - operation.OperationFailed += (_, _) => - TelemetryHandler.UpdatePackage(package, TEL_OP_RESULT.FAILED); - Add(operation); - Instance.MainWindow.UpdateSystemTrayStatus(); - return operation; - } - - public static void Update( - IReadOnlyList packages, - bool? elevated = null, - bool? interactive = null, - bool? no_integrity = null - ) - { - foreach (var package in packages) - { - _ = Update(package, elevated, interactive, no_integrity); - } - } - - private static bool AlreadyBeingUpdated(IPackage package) - { - // Check for duplicate operations already in the queue (prevents duplicates during unattended updates) - return _operationList.Any(x => - x.Operation is UpdatePackageOperation updateOp - && (x.Operation.Status is OperationStatus.InQueue or OperationStatus.Running) - && updateOp.Package.GetHash() == package.GetHash() - ); - } - - public static async Task UpdateAll() - { - foreach (IPackage package in UpgradablePackagesLoader.Instance.Packages) - { // First check will only work if the package has not been reloaded, deeper check is needed, hence AlreadyBeingUpdated - if ( - package.Tag is PackageTag.BeingProcessed or PackageTag.OnQueue - || AlreadyBeingUpdated(package) - ) - { - Logger.Warn( - $"Update operation for package {package.Id} is already queued or running. Skipping duplicate in UpdateAll." - ); - continue; - } - - await Update(package); - } - } - - public static async Task UpdateAllForManager(string managerName) - { - foreach (IPackage package in UpgradablePackagesLoader.Instance.Packages) - { - if ( - package.Manager.Id != managerName - ) - continue; // Package not from the desired package manager - - // First check will only work if the package has not been reloaded, deeper check is needed, hence AlreadyBeingUpdated - if ( - package.Tag is PackageTag.BeingProcessed or PackageTag.OnQueue - || AlreadyBeingUpdated(package) - ) - { - Logger.Warn( - $"Update operation for package {package.Id} is already queued or running. Skipping duplicate in UpdateAll." - ); - continue; - } - - await Update(package); - } - } - - public static async Task UpdateForId(string packageId) - { - foreach (IPackage package in UpgradablePackagesLoader.Instance.Packages) - { - if (package.Id == packageId) - { - Logger.Info($"[WIDGETS] Updating package with id {packageId}"); - await Update(package); - return; - } - } - - Logger.Warn($"[WIDGETS] No package with id={packageId} was found"); - } - - public static async Task UninstallThenUpdate(IPackage? package) - { - if (package is null) - return null; - - var options = await InstallOptionsFactory.LoadApplicableAsync(package); - - var uninstallOp = new UninstallPackageOperation(package, options); - uninstallOp.OperationSucceeded += (_, _) => - TelemetryHandler.UninstallPackage(package, TEL_OP_RESULT.SUCCESS); - uninstallOp.OperationFailed += (_, _) => - TelemetryHandler.UninstallPackage(package, TEL_OP_RESULT.FAILED); - - options.Version = package.NewVersionString; - options.OverridesNextLevelOpts = true; - var installOp = new InstallPackageOperation(package, options, req: uninstallOp); - installOp.OperationSucceeded += (_, _) => - TelemetryHandler.UpdatePackage(package, TEL_OP_RESULT.SUCCESS); - installOp.OperationFailed += (_, _) => - TelemetryHandler.UpdatePackage(package, TEL_OP_RESULT.FAILED); - - Add(installOp); - Instance.MainWindow.UpdateSystemTrayStatus(); - return installOp; - } - - /* - * - * - * PACKAGE UNINSTALL - * - * - */ - - public static async Task ConfirmAndUninstall( - IReadOnlyList packages, - bool? elevated = null, - bool? interactive = null, - bool? remove_data = null - ) - { - if (!await DialogHelper.ConfirmUninstallation(packages)) - return; - - await Uninstall(packages, elevated, interactive, remove_data); - } - - public static async Task ConfirmAndUninstall( - IPackage? package, - bool? elevated = null, - bool? interactive = null, - bool? remove_data = null - ) - { - if (package is null) - return; - if (!await DialogHelper.ConfirmUninstallation(package)) - return; - - await Uninstall(package, elevated, interactive, remove_data); - } - - public static async Task Uninstall( - IPackage? package, - bool? elevated = null, - bool? interactive = null, - bool? remove_data = null, - bool ignoreParallel = false, - AbstractOperation? req = null - ) - { - if (package is null) - return null; - - var options = await InstallOptionsFactory.LoadApplicableAsync( - package, - elevated, - interactive, - remove_data: remove_data - ); - var operation = new UninstallPackageOperation(package, options, ignoreParallel, req); - operation.OperationSucceeded += (_, _) => - TelemetryHandler.UninstallPackage(package, TEL_OP_RESULT.SUCCESS); - operation.OperationFailed += (_, _) => - TelemetryHandler.UninstallPackage(package, TEL_OP_RESULT.FAILED); - Add(operation); - Instance.MainWindow.UpdateSystemTrayStatus(); - return operation; - } - - public static async Task Uninstall( - IReadOnlyList packages, - bool? elevated = null, - bool? interactive = null, - bool? remove_data = null - ) - { - foreach (var package in packages) - { - await Uninstall(package, elevated, interactive, remove_data); - } - } - } -} diff --git a/src/UniGetUI/AutoUpdater.Helpers.cs b/src/UniGetUI/AutoUpdater.Helpers.cs deleted file mode 100644 index 5ba5cfa242..0000000000 --- a/src/UniGetUI/AutoUpdater.Helpers.cs +++ /dev/null @@ -1,326 +0,0 @@ -using System.Globalization; -using System.Runtime.InteropServices; -using System.Text.Json; -using System.Text.Json.Serialization; -using Microsoft.Win32; -using UniGetUI.Core.Logging; -using UniGetUI.Core.Tools; - -namespace UniGetUI; - -public partial class AutoUpdater -{ - private const string INSTALLER_VALIDATION_FAILURE_MARKER_SUFFIX = ".validation-failed"; - - private const string REGISTRY_PATH = @"Software\Devolutions\UniGetUI"; - private const string DEFAULT_PRODUCTINFO_URL = "https://devolutions.net/productinfo.json"; - private const string DEFAULT_PRODUCTINFO_KEY = "Devolutions.UniGetUI"; - - private const string REG_PRODUCTINFO_URL = "UpdaterProductInfoUrl"; - private const string REG_PRODUCTINFO_KEY = "UpdaterProductKey"; - private const string REG_ALLOW_UNSAFE_URLS = "UpdaterAllowUnsafeUrls"; - private const string REG_SKIP_HASH_VALIDATION = "UpdaterSkipHashValidation"; - private const string REG_SKIP_SIGNER_THUMBPRINT_CHECK = "UpdaterSkipSignerThumbprintCheck"; - private const string REG_DISABLE_TLS_VALIDATION = "UpdaterDisableTlsValidation"; - -#if !DEBUG - private static readonly string[] RELEASE_IGNORED_REGISTRY_VALUES = - [ - REG_PRODUCTINFO_KEY, - REG_ALLOW_UNSAFE_URLS, - REG_SKIP_HASH_VALIDATION, - REG_SKIP_SIGNER_THUMBPRINT_CHECK, - REG_DISABLE_TLS_VALIDATION, - ]; -#endif - - private static HttpClientHandler CreateHttpClientHandler(UpdaterOverrides updaterOverrides) - { - HttpClientHandler handler = CoreTools.GenericHttpClientParameters; - if (updaterOverrides.DisableTlsValidation) - { - Logger.Warn( - "Registry override enabled: TLS certificate validation is disabled for updater requests." - ); - handler.ServerCertificateCustomValidationCallback = static (_, _, _, _) => true; - } - - return handler; - } - - internal static bool IsSourceUrlAllowed(string url, bool allowUnsafeUrls) - { - if (!Uri.TryCreate(url, UriKind.Absolute, out Uri? uri)) - { - return false; - } - - if (allowUnsafeUrls) - { - Logger.Warn($"Registry override enabled: allowing potentially unsafe updater URL {url}"); - return true; - } - - if (!string.Equals(uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - return uri.Host.Equals("devolutions.net", StringComparison.OrdinalIgnoreCase) - || uri.Host.EndsWith(".devolutions.net", StringComparison.OrdinalIgnoreCase) - || uri.Host.Equals("github.com", StringComparison.OrdinalIgnoreCase) - || uri.Host.Equals("objects.githubusercontent.com", StringComparison.OrdinalIgnoreCase) - || uri.Host.Equals( - "release-assets.githubusercontent.com", - StringComparison.OrdinalIgnoreCase - ); - } - - internal static ProductInfoFile SelectInstallerFile(List files) - { - string targetArch = RuntimeInformation.ProcessArchitecture switch - { - Architecture.Arm64 => "arm64", - Architecture.X64 => "x64", - _ => "x64", - }; - - ProductInfoFile? match = files.FirstOrDefault(file => - file.Type.Equals("exe", StringComparison.OrdinalIgnoreCase) - && file.Arch.Equals(targetArch, StringComparison.OrdinalIgnoreCase) - ); - - match ??= files.FirstOrDefault(file => - file.Type.Equals("exe", StringComparison.OrdinalIgnoreCase) - && file.Arch.Equals("Any", StringComparison.OrdinalIgnoreCase) - ); - - match ??= files.FirstOrDefault(file => - file.Type.Equals("msi", StringComparison.OrdinalIgnoreCase) - && file.Arch.Equals(targetArch, StringComparison.OrdinalIgnoreCase) - ); - - match ??= files.FirstOrDefault(file => - file.Type.Equals("msi", StringComparison.OrdinalIgnoreCase) - && file.Arch.Equals("Any", StringComparison.OrdinalIgnoreCase) - ); - - if (match is null) - { - throw new KeyNotFoundException( - $"No compatible installer file found in productinfo for architecture '{targetArch}'" - ); - } - - return match; - } - - internal static Version ParseVersionOrFallback(string rawVersion, Version fallbackVersion) - { - if (Version.TryParse(rawVersion, out Version? parsed)) - { - return CoreTools.NormalizeVersionForComparison(parsed); - } - - string sanitized = rawVersion.Trim().TrimStart('v', 'V'); - if (Version.TryParse(sanitized, out parsed)) - { - return CoreTools.NormalizeVersionForComparison(parsed); - } - - Logger.Warn($"Could not parse version '{rawVersion}', using fallback '{fallbackVersion}'"); - return fallbackVersion; - } - - // Normalize trailing zero components so "2026.1.11" and "2026.1.11.0" compare equal. - internal static bool VersionsMatch(string a, string b) - { - string sa = a.Trim().TrimStart('v', 'V'); - string sb = b.Trim().TrimStart('v', 'V'); - - if (Version.TryParse(sa, out Version? va) && Version.TryParse(sb, out Version? vb)) - { - return CoreTools.NormalizeVersionForComparison(va) - .Equals(CoreTools.NormalizeVersionForComparison(vb)); - } - - return string.Equals(sa, sb, StringComparison.OrdinalIgnoreCase); - } - - internal static string NormalizeThumbprint(string thumbprint) - { - char[] normalized = thumbprint.ToLowerInvariant().Where(char.IsAsciiHexDigit).ToArray(); - - return new string(normalized); - } - - internal static string GetInstallerValidationFailureMarkerPath(string installerPath) - { - return installerPath + INSTALLER_VALIDATION_FAILURE_MARKER_SUFFIX; - } - - private static void ClearInstallerValidationFailure(string markerPath) - { - DeleteFileIfExists(markerPath, "installer validation failure marker"); - } - - private static void DeleteFileIfExists(string path, string description) - { - if (!File.Exists(path)) - { - return; - } - - try - { - File.Delete(path); - } - catch (Exception ex) - { - Logger.Warn($"Could not delete {description} at '{path}': {ex.Message}"); - } - } - - private static UpdaterOverrides LoadUpdaterOverrides() - { - using RegistryKey? key = Registry.LocalMachine.OpenSubKey(REGISTRY_PATH); - -#if DEBUG - if (key is not null) - { - Logger.Info($"Updater registry overrides loaded from HKLM\\{REGISTRY_PATH}"); - } - - return new UpdaterOverrides( - GetRegistryString(key, REG_PRODUCTINFO_URL) ?? DEFAULT_PRODUCTINFO_URL, - GetRegistryString(key, REG_PRODUCTINFO_KEY) ?? DEFAULT_PRODUCTINFO_KEY, - GetRegistryBool(key, REG_ALLOW_UNSAFE_URLS), - GetRegistryBool(key, REG_SKIP_HASH_VALIDATION), - GetRegistryBool(key, REG_SKIP_SIGNER_THUMBPRINT_CHECK), - GetRegistryBool(key, REG_DISABLE_TLS_VALIDATION) - ); -#else - LogIgnoredReleaseOverrides(key); - string productInfoUrl = - GetRegistryString(key, REG_PRODUCTINFO_URL) ?? DEFAULT_PRODUCTINFO_URL; - - return new UpdaterOverrides( - productInfoUrl, - DEFAULT_PRODUCTINFO_KEY, - false, - false, - false, - false - ); -#endif - } - -#if !DEBUG - private static void LogIgnoredReleaseOverrides(RegistryKey? key) - { - if (key is null) - { - return; - } - - foreach (string valueName in RELEASE_IGNORED_REGISTRY_VALUES) - { - if (key.GetValue(valueName) is not null) - { - Logger.Warn( - $"Release build is ignoring updater registry value HKLM\\{REGISTRY_PATH}\\{valueName}." - ); - } - } - } -#endif - - internal static string? GetRegistryString(RegistryKey? key, string valueName) - { -#pragma warning disable CA1416 - object? value = key?.GetValue(valueName); -#pragma warning restore CA1416 - if (value is null) - { - return null; - } - - string? parsedValue = value.ToString(); - if (string.IsNullOrWhiteSpace(parsedValue)) - { - return null; - } - - return parsedValue.Trim(); - } - -#if DEBUG - internal static bool GetRegistryBool(RegistryKey? key, string valueName) - { -#pragma warning disable CA1416 - object? value = key?.GetValue(valueName); -#pragma warning restore CA1416 - if (value is null) - { - return false; - } - - if (value is int intValue) - { - return intValue != 0; - } - - if (value is long longValue) - { - return longValue != 0; - } - - string normalized = value.ToString()?.Trim() ?? ""; - return normalized.Equals("1", StringComparison.OrdinalIgnoreCase) - || normalized.Equals("true", StringComparison.OrdinalIgnoreCase) - || normalized.Equals("yes", StringComparison.OrdinalIgnoreCase) - || normalized.Equals("on", StringComparison.OrdinalIgnoreCase); - } -#endif - - private sealed record UpdateCandidate( - bool IsUpgradable, - string VersionName, - string InstallerHash, - string InstallerDownloadUrl, - string SourceName - ); - - private sealed record UpdaterOverrides( - string ProductInfoUrl, - string ProductInfoProductKey, - bool AllowUnsafeUrls, - bool SkipHashValidation, - bool SkipSignerThumbprintCheck, - bool DisableTlsValidation - ); - - private sealed class ProductInfoProduct - { - public ProductInfoChannel? Current { get; set; } - public ProductInfoChannel? Beta { get; set; } - } - - internal sealed class ProductInfoChannel - { - public string Version { get; set; } = string.Empty; - public List Files { get; set; } = []; - } - - internal sealed class ProductInfoFile - { - public string Arch { get; set; } = string.Empty; - public string Type { get; set; } = string.Empty; - public string Url { get; set; } = string.Empty; - public string Hash { get; set; } = string.Empty; - } - - [JsonSourceGenerationOptions(AllowTrailingCommas = true)] - [JsonSerializable(typeof(Dictionary))] - private sealed partial class AutoUpdaterJsonContext : JsonSerializerContext { } -} diff --git a/src/UniGetUI/AutoUpdater.cs b/src/UniGetUI/AutoUpdater.cs deleted file mode 100644 index 84c62427df..0000000000 --- a/src/UniGetUI/AutoUpdater.cs +++ /dev/null @@ -1,972 +0,0 @@ -using System.Diagnostics; -using System.Globalization; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Text.Json; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.Windows.AppNotifications; -using Microsoft.Windows.AppNotifications.Builder; -using UniGetUI.Core.Data; -using UniGetUI.Core.Logging; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; -using UniGetUI.Interface.Enums; - -namespace UniGetUI; - -public partial class AutoUpdater -{ - private static readonly string[] DEVOLUTIONS_CERT_THUMBPRINTS = - [ - "3f5202a9432d54293bdfe6f7e46adb0a6f8b3ba6", - "8db5a43bb8afe4d2ffb92da9007d8997a4cc4e13", - "50f753333811ff11f1920274afde3ffd4468b210", - ]; - - private static readonly AutoUpdaterJsonContext ProductInfoJsonContext = new( - new JsonSerializerOptions(SerializationHelpers.DefaultOptions) - ); - - public static Window Window = null!; - public static InfoBar Banner = null!; - - //------------------------------------------------------------------------------------------------------------------ - public static bool ReleaseLockForAutoupdate_Notification; - public static bool ReleaseLockForAutoupdate_Window; - public static bool ReleaseLockForAutoupdate_UpdateBanner; - public static bool UpdateReadyToBeInstalled { get; private set; } - - // ------------------------------------------------------------------ per-attempt log - // Captures auto-updater log entries for the current update attempt. We keep a - // dedicated buffer (in addition to the global session log) so the "View log" - // banner button can show the user only the entries relevant to their failed - // update, instead of dumping the entire noisy session log. - private static readonly Lock _updateLogLock = new(); - private static StringBuilder? _updateLogBuilder; - private static readonly string _updateLogPath = Path.Combine( - Path.GetTempPath(), - "UniGetUI", - "last-update-attempt.log" - ); - - private static void ResetUpdateLog(bool manualCheck, bool autoLaunch) - { - lock (_updateLogLock) - { - _updateLogBuilder = new StringBuilder() - .AppendLine($"=== UniGetUI update attempt started at {DateTime.Now:yyyy-MM-dd HH:mm:ss} ===") - .AppendLine($"Current version: {CoreData.VersionName} (build {CoreData.BuildNumber})") - .AppendLine($"Manual check: {manualCheck}") - .AppendLine($"Auto-launch: {autoLaunch}") - .AppendLine($"UI: WinUI") - .AppendLine(); - FlushUpdateLogToDiskNoLock(); - } - } - - private static void AppendToUpdateLog(string severity, string message) - { - lock (_updateLogLock) - { - if (_updateLogBuilder is null) return; - _updateLogBuilder.AppendLine($"[{DateTime.Now:HH:mm:ss}] [{severity}] {Logger.Redact(message)}"); - FlushUpdateLogToDiskNoLock(); - } - } - - // Tmp + rename so a kill mid-flush (installer terminates us during file replacement) can't leave a 0-byte file. - private static void FlushUpdateLogToDiskNoLock() - { - if (_updateLogBuilder is null) return; - try - { - Directory.CreateDirectory(Path.GetDirectoryName(_updateLogPath)!); - string tempPath = _updateLogPath + ".tmp"; - File.WriteAllText(tempPath, _updateLogBuilder.ToString()); - File.Move(tempPath, _updateLogPath, overwrite: true); - } - catch { } - } - - private const string AttemptFinishedMarker = "=== Attempt finished:"; - - // Appends a structured line indicating the update flow reached a terminal state. - // The presence/absence of this marker on disk lets a subsequent app launch tell - // whether the previous attempt completed cleanly or was killed mid-flow. - private static void MarkAttemptFinished(string outcome) - { - lock (_updateLogLock) - { - if (_updateLogBuilder is null) return; - _updateLogBuilder - .AppendLine() - .AppendLine($"{AttemptFinishedMarker} {outcome} at {DateTime.Now:yyyy-MM-dd HH:mm:ss} ==="); - FlushUpdateLogToDiskNoLock(); - } - } - - private static void RecordTargetVersion(string version) - { - lock (_updateLogLock) - { - _updateLogBuilder?.AppendLine($"Target version: {version}"); - FlushUpdateLogToDiskNoLock(); - } - } - - /// - /// On app startup, detects an interrupted update attempt — the log file - /// from the previous attempt has no , - /// indicating the app was killed mid-flow (almost always because the - /// installer terminated us during file replacement). - /// Caller must have already assigned and - /// so the banner can render. - /// - public static void CheckForOrphanedUpdateAttempt() - { - try - { - if (!File.Exists(_updateLogPath)) return; - - var info = new FileInfo(_updateLogPath); - if ((DateTime.Now - info.LastWriteTime).TotalMinutes > 10) - return; - - string content = File.ReadAllText(_updateLogPath); - if (content.Contains(AttemptFinishedMarker)) - return; - - string currentVer = CoreData.VersionName; - string? targetVer = null; - foreach (string line in content.Split('\n')) - { - if (line.StartsWith("Target version: ")) - { - targetVer = line["Target version: ".Length..].Trim(); - break; - } - } - - if (targetVer is null) - { - Logger.Info("Update log has no recorded target version; skipping orphan-attempt banner."); - return; - } - - if (VersionsMatch(targetVer, currentVer)) - { - Logger.Info($"Previous update attempt killed mid-flow but install succeeded (running version {currentVer} matches target {targetVer}). Marking as finished."); - try - { - File.AppendAllText( - _updateLogPath, - $"{Environment.NewLine}{AttemptFinishedMarker} installer succeeded (detected on next launch — running version is {currentVer}) at {DateTime.Now:yyyy-MM-dd HH:mm:ss} ==={Environment.NewLine}"); - } - catch { /* swallow */ } - return; - } - - Logger.Warn($"Detected interrupted update attempt. Running={currentVer}, Target={targetVer}"); - - ShowMessage_ThreadSafe( - CoreTools.Translate("Your last update attempt did not complete."), - CoreTools.Translate("UniGetUI could not confirm whether the update succeeded. Open the log to see what happened."), - InfoBarSeverity.Warning, - true, - CreateViewLogButton() - ); - } - catch (Exception ex) - { - Logger.Warn($"Could not check for orphaned update attempt: {ex.Message}"); - } - } - - private static void LogUpdateInfo(string message, [System.Runtime.CompilerServices.CallerMemberName] string caller = "") - { - Logger.Info(message, caller); - AppendToUpdateLog("INFO ", message); - } - - private static void LogUpdateWarn(string message, [System.Runtime.CompilerServices.CallerMemberName] string caller = "") - { - Logger.Warn(message, caller); - AppendToUpdateLog("WARN ", message); - } - - private static void LogUpdateWarn(Exception ex, [System.Runtime.CompilerServices.CallerMemberName] string caller = "") - { - Logger.Warn(ex, caller); - AppendToUpdateLog("WARN ", ex.ToString()); - } - - private static void LogUpdateError(string message, [System.Runtime.CompilerServices.CallerMemberName] string caller = "") - { - Logger.Error(message, caller); - AppendToUpdateLog("ERROR", message); - } - - private static void LogUpdateError(Exception ex, [System.Runtime.CompilerServices.CallerMemberName] string caller = "") - { - Logger.Error(ex, caller); - AppendToUpdateLog("ERROR", ex.ToString()); - } - - private static void LogUpdateDebug(string message, [System.Runtime.CompilerServices.CallerMemberName] string caller = "") - { - Logger.Debug(message, caller); - AppendToUpdateLog("DEBUG", message); - } - - private static void OpenUpdateLog() - { - // The buffer is flushed to disk on every append/reset, so the file should - // already be current. Only fall back to the full session log if no flow - // has ever run (button shouldn't appear in that case, but be defensive). - string pathToOpen = File.Exists(_updateLogPath) - ? _updateLogPath - : Logger.GetSessionLogPath(); - - try - { - Process.Start(new ProcessStartInfo - { - FileName = pathToOpen, - UseShellExecute = true, - }); - } - catch (Exception ex) - { - Logger.Warn($"Could not open log file '{pathToOpen}': {ex.Message}"); - } - } - - /// - /// Translates an Inno Setup installer exit code into a short human-readable - /// reason. The codes come from the Inno Setup documentation - /// (https://jrsoftware.org/ishelp/index.php?topic=setupexitcodes). - /// - private static string DescribeInstallerExitCode(int code) => code switch - { - 0 => CoreTools.Translate("The installer reported success but did not restart UniGetUI."), - 1 => CoreTools.Translate("The installer failed to initialize."), - 2 => CoreTools.Translate("Setup was canceled before installation began."), - 3 => CoreTools.Translate("A fatal error occurred during the preparation phase."), - 4 => CoreTools.Translate("A fatal error occurred during installation."), - 5 => CoreTools.Translate("Installation was canceled while in progress."), - 6 => CoreTools.Translate("The installer was terminated by another process."), - 7 => CoreTools.Translate("The preparation phase determined the installation cannot proceed."), - 8 => CoreTools.Translate("The installer could not start. UniGetUI may already be running, or you do not have permission to install."), - _ => CoreTools.Translate("Unexpected installer error."), - }; - - private static Button CreateViewLogButton() - { - var btn = new Button { Content = CoreTools.Translate("View log") }; - btn.Click += (_, _) => OpenUpdateLog(); - return btn; - } - - public static async Task UpdateCheckLoop(Window window, InfoBar banner) - { - Window = window; - Banner = banner; - - // If the previous update attempt was killed mid-flow (typically by the - // installer terminating us during file replacement), surface a banner - // before either entering or short-circuiting the auto-update loop. - CheckForOrphanedUpdateAttempt(); - - if (Settings.Get(Settings.K.DisableAutoUpdateWingetUI)) - { - Logger.Warn("User has disabled updates"); - return; - } - - bool IsFirstLaunch = true; - - await CoreTools.WaitForInternetConnection(); - while (true) - { - // User could have disabled updates on runtime - if (Settings.Get(Settings.K.DisableAutoUpdateWingetUI)) - { - Logger.Warn("User has disabled updates"); - return; - } - await CheckAndInstallUpdates( - window, - banner, - false, - IsFirstLaunch - ); - IsFirstLaunch = false; - await Task.Delay(UpdaterDownloadEngine.DefaultAutomaticUpdateCheckInterval); - } - } - - /// - /// Performs the entire update process, and returns true/false whether the process finished successfully; - /// - public static async Task CheckAndInstallUpdates( - Window window, - InfoBar banner, - bool Verbose, - bool AutoLaunch = false, - bool ManualCheck = false - ) - { - Window = window; - Banner = banner; - bool WasCheckingForUpdates = true; - ResetUpdateLog(ManualCheck, AutoLaunch); - UpdaterOverrides updaterOverrides = LoadUpdaterOverrides(); - - try - { - if (Verbose) - ShowMessage_ThreadSafe( - CoreTools.Translate("We are checking for updates."), - CoreTools.Translate("Please wait"), - InfoBarSeverity.Informational, - false - ); - - // Check for updates - UpdateCandidate updateCandidate = await GetUpdateCandidate(updaterOverrides); - LogUpdateInfo( - $"Updater source '{updateCandidate.SourceName}' returned version {updateCandidate.VersionName} (upgradable={updateCandidate.IsUpgradable})" - ); - - if (updateCandidate.IsUpgradable) - { - WasCheckingForUpdates = false; - RecordTargetVersion(updateCandidate.VersionName); - LogUpdateInfo( - $"An update to UniGetUI version {updateCandidate.VersionName} is available" - ); - string InstallerPath = Path.Join( - CoreData.UniGetUIDataDirectory, - "UniGetUI Updater.exe" - ); - UpdaterDownloadIdentity installerIdentity = new( - updateCandidate.VersionName, - updateCandidate.InstallerHash, - updateCandidate.InstallerDownloadUrl - ); - string installerValidationFailureMarkerPath = - GetInstallerValidationFailureMarkerPath(InstallerPath); - string installerDownloadFailureStatePath = - UpdaterDownloadEngine.GetFailureStatePath(InstallerPath); - - if ( - File.Exists(InstallerPath) - && await CheckInstallerHash( - InstallerPath, - updateCandidate.InstallerHash, - updaterOverrides - ) - && CheckInstallerSignerThumbprint(InstallerPath, updaterOverrides) - ) - { - ClearInstallerValidationFailure(installerValidationFailureMarkerPath); - UpdaterDownloadEngine.ClearFailureState( - installerDownloadFailureStatePath, - message => LogUpdateWarn(message) - ); - LogUpdateInfo($"A cached valid installer was found, launching update process..."); - return await PrepairToLaunchInstaller( - InstallerPath, - updateCandidate.VersionName, - AutoLaunch, - ManualCheck - ); - } - - if (File.Exists(InstallerPath)) - { - LogUpdateWarn( - "Cached installer is invalid; it will be kept until a replacement is validated." - ); - } - - if ( - !ManualCheck - && UpdaterDownloadEngine.IsFailureBackoffActive( - installerDownloadFailureStatePath, - installerIdentity, - DateTime.UtcNow, - out TimeSpan remainingBackoff, - out UpdaterDownloadFailureState? failureState, - message => LogUpdateWarn(message) - ) - ) - { - LogUpdateWarn( - $"Skipping installer download for version {updateCandidate.VersionName}; previous {failureState?.FailureClass ?? "download"} failure is backed off for {remainingBackoff:g}." - ); - MarkAttemptFinished("installer download skipped by failure backoff"); - return true; - } - - ShowMessage_ThreadSafe( - CoreTools.Translate( - "UniGetUI version {0} is being downloaded.", - updateCandidate.VersionName.ToString(CultureInfo.InvariantCulture) - ), - CoreTools.Translate("This may take a minute or two"), - InfoBarSeverity.Informational, - false - ); - - // Download the installer - string downloadedInstallerPath; - try - { - downloadedInstallerPath = await DownloadInstaller( - updateCandidate.InstallerDownloadUrl, - InstallerPath, - updaterOverrides, - installerIdentity - ); - } - catch (Exception ex) - { - UpdaterDownloadEngine.RecordFailure( - installerDownloadFailureStatePath, - installerIdentity, - "download", - DateTime.UtcNow, - message => LogUpdateWarn(message) - ); - LogUpdateWarn($"Installer download failed: {ex.Message}"); - throw; - } - - if ( - await CheckInstallerHash( - downloadedInstallerPath, - updateCandidate.InstallerHash, - updaterOverrides - ) && CheckInstallerSignerThumbprint(downloadedInstallerPath, updaterOverrides) - ) - { - UpdaterDownloadEngine.PromotePartialDownload( - InstallerPath, - message => LogUpdateWarn(message) - ); - ClearInstallerValidationFailure(installerValidationFailureMarkerPath); - UpdaterDownloadEngine.ClearFailureState( - installerDownloadFailureStatePath, - message => LogUpdateWarn(message) - ); - LogUpdateInfo("The downloaded installer is valid, launching update process..."); - return await PrepairToLaunchInstaller( - InstallerPath, - updateCandidate.VersionName, - AutoLaunch, - ManualCheck - ); - } - - UpdaterDownloadEngine.RecordFailure( - installerDownloadFailureStatePath, - installerIdentity, - "validation", - DateTime.UtcNow, - message => LogUpdateWarn(message) - ); - UpdaterDownloadEngine.DeletePartialDownload( - InstallerPath, - message => LogUpdateWarn(message) - ); - - ShowMessage_ThreadSafe( - CoreTools.Translate("The installer authenticity could not be verified."), - CoreTools.Translate("The update process has been aborted."), - InfoBarSeverity.Error, - true, - CreateViewLogButton() - ); - MarkAttemptFinished("authenticity verification failed"); - return !ManualCheck; - } - - if (Verbose) - ShowMessage_ThreadSafe( - CoreTools.Translate("Great! You are on the latest version."), - CoreTools.Translate("There are no new UniGetUI versions to be installed"), - InfoBarSeverity.Success, - true - ); - MarkAttemptFinished("no update available"); - return true; - } - catch (Exception e) - { - LogUpdateError("An error occurred while checking for updates: "); - LogUpdateError(e); - // We don't want an error popping if updates can't - if (Verbose || !WasCheckingForUpdates) - ShowMessage_ThreadSafe( - CoreTools.Translate("An error occurred when checking for updates: "), - e.Message, - InfoBarSeverity.Error, - true, - CreateViewLogButton() - ); - MarkAttemptFinished($"exception: {e.Message}"); - return false; - } - } - - private static async Task GetUpdateCandidate(UpdaterOverrides updaterOverrides) - { - return await CheckForUpdatesFromProductInfo(updaterOverrides); - } - - /// - /// Default update source using Devolutions productinfo.json - /// - private static async Task CheckForUpdatesFromProductInfo( - UpdaterOverrides updaterOverrides - ) - { - LogUpdateDebug( - $"Begin check for updates on productinfo source {updaterOverrides.ProductInfoUrl}" - ); - - if (!IsSourceUrlAllowed(updaterOverrides.ProductInfoUrl, updaterOverrides.AllowUnsafeUrls)) - { - throw new InvalidOperationException( - $"Productinfo URL is not allowed: {updaterOverrides.ProductInfoUrl}" - ); - } - - string productInfo; - using (HttpClient client = new(CreateHttpClientHandler(updaterOverrides))) - { - client.Timeout = TimeSpan.FromSeconds(600); - client.DefaultRequestHeaders.UserAgent.ParseAdd(CoreData.UserAgentString); - productInfo = await client.GetStringAsync(updaterOverrides.ProductInfoUrl); - } - - Dictionary? productInfoRoot = - JsonSerializer.Deserialize( - productInfo, - typeof(Dictionary), - ProductInfoJsonContext - ) as Dictionary; - if (productInfoRoot is null || productInfoRoot.Count == 0) - { - throw new FormatException("productinfo.json content is empty or invalid"); - } - - if ( - !productInfoRoot.TryGetValue( - updaterOverrides.ProductInfoProductKey, - out ProductInfoProduct? product - ) - ) - { - throw new KeyNotFoundException( - $"Product '{updaterOverrides.ProductInfoProductKey}' was not found in productinfo.json" - ); - } - - ProductInfoChannel? channel = Settings.Get(Settings.K.EnableUniGetUIBeta) - ? product.Beta - : product.Current; - if (channel is null) - { - string missingChannel = Settings.Get(Settings.K.EnableUniGetUIBeta) - ? "Beta" - : "Current"; - throw new KeyNotFoundException( - $"Channel '{missingChannel}' was not found for product '{updaterOverrides.ProductInfoProductKey}'" - ); - } - - ProductInfoFile installerFile = SelectInstallerFile(channel.Files); - if (!IsSourceUrlAllowed(installerFile.Url, updaterOverrides.AllowUnsafeUrls)) - { - throw new InvalidOperationException( - $"Installer URL is not allowed: {installerFile.Url}" - ); - } - - Version currentVersion = ParseVersionOrFallback( - CoreData.VersionName, - new Version(0, 0, 0, CoreData.BuildNumber) - ); - Version availableVersion = ParseVersionOrFallback(channel.Version, new Version(0, 0, 0, 0)); - - bool isUpgradable = availableVersion > currentVersion; - LogUpdateDebug( - $"Productinfo check result: current={currentVersion}, available={availableVersion}, upgradable={isUpgradable}" - ); - - return new UpdateCandidate( - isUpgradable, - channel.Version, - installerFile.Hash, - installerFile.Url, - "ProductInfo" - ); - } - - /// - /// Checks whether the downloaded updater matches the hash. - /// - private static async Task CheckInstallerHash( - string installerLocation, - string expectedHash, - UpdaterOverrides updaterOverrides - ) - { - if (updaterOverrides.SkipHashValidation) - { - LogUpdateWarn("Registry override enabled: skipping updater hash validation."); - return true; - } - - LogUpdateDebug($"Checking updater hash on location {installerLocation}"); - using (FileStream stream = File.OpenRead(installerLocation)) - { - string hash = Convert - .ToHexString(await SHA256.Create().ComputeHashAsync(stream)) - .ToLower(); - if (hash == expectedHash.ToLower()) - { - LogUpdateDebug($"The hashes match ({hash})"); - return true; - } - LogUpdateWarn($"Hash mismatch.\nExpected: {expectedHash}\nGot: {hash}"); - return false; - } - } - - private static bool CheckInstallerSignerThumbprint( - string installerLocation, - UpdaterOverrides updaterOverrides - ) - { - if (updaterOverrides.SkipSignerThumbprintCheck) - { - LogUpdateWarn( - "Registry override enabled: skipping updater signer thumbprint validation." - ); - return true; - } - - try - { -#pragma warning disable SYSLIB0057 - X509Certificate signerCertificate = X509Certificate.CreateFromSignedFile( - installerLocation - ); -#pragma warning restore SYSLIB0057 - using X509Certificate2 cert = new(signerCertificate); - - string signerThumbprint = NormalizeThumbprint(cert.Thumbprint ?? string.Empty); - if (string.IsNullOrWhiteSpace(signerThumbprint)) - { - LogUpdateWarn( - $"Could not read signer thumbprint for installer '{installerLocation}'" - ); - return false; - } - - if ( - DEVOLUTIONS_CERT_THUMBPRINTS.Contains( - signerThumbprint, - StringComparer.OrdinalIgnoreCase - ) - ) - { - LogUpdateDebug($"Installer signer thumbprint is trusted: {signerThumbprint}"); - return true; - } - - LogUpdateWarn($"Installer signer thumbprint is not trusted. Got: {signerThumbprint}"); - return false; - } - catch (Exception ex) - { - LogUpdateWarn("Could not validate installer signer thumbprint"); - LogUpdateWarn(ex); - return false; - } - } - - /// - /// Downloads the given installer to the given location - /// - private static async Task DownloadInstaller( - string downloadUrl, - string installerLocation, - UpdaterOverrides updaterOverrides, - UpdaterDownloadIdentity identity - ) - { - if (!IsSourceUrlAllowed(downloadUrl, updaterOverrides.AllowUnsafeUrls)) - { - throw new InvalidOperationException($"Download URL is not allowed: {downloadUrl}"); - } - - LogUpdateDebug($"Downloading installer from {downloadUrl} to {installerLocation}"); - using (HttpClient client = new(CreateHttpClientHandler(updaterOverrides))) - { - client.Timeout = TimeSpan.FromSeconds(600); - client.DefaultRequestHeaders.UserAgent.ParseAdd(CoreData.UserAgentString); - using CancellationTokenSource downloadTimeout = new(TimeSpan.FromSeconds(600)); - UpdaterDownloadResult result = await UpdaterDownloadEngine.DownloadInstallerPartAsync( - client, - identity, - installerLocation, - message => LogUpdateDebug(message), - downloadTimeout.Token - ); - LogUpdateDebug("The download has finished successfully"); - return result.PartialPath; - } - } - - /// - /// Waits for the window to be closed if it is open and launches the updater - /// - private static async Task PrepairToLaunchInstaller( - string installerLocation, - string NewVersion, - bool AutoLaunch, - bool ManualCheck - ) - { - LogUpdateDebug("Starting the process to launch the installer."); - UpdateReadyToBeInstalled = true; - ReleaseLockForAutoupdate_Window = false; - ReleaseLockForAutoupdate_Notification = false; - ReleaseLockForAutoupdate_UpdateBanner = false; - - // Check if the user has disabled updates - if (!ManualCheck && Settings.Get(Settings.K.DisableAutoUpdateWingetUI)) - { - // Banner is a UI element; always touch it from the UI thread. - Window.DispatcherQueue.TryEnqueue(() => Banner.IsOpen = false); - LogUpdateWarn("User disabled updates!"); - MarkAttemptFinished("aborted - auto-update disabled before launch"); - return true; - } - - Window.DispatcherQueue.TryEnqueue(() => - { - // Set the banner to Restart UniGetUI to update - var UpdateNowButton = new Button { Content = CoreTools.Translate("Update now") }; - UpdateNowButton.Click += (_, _) => ReleaseLockForAutoupdate_UpdateBanner = true; - ShowMessage_ThreadSafe( - CoreTools.Translate("UniGetUI {0} is ready to be installed.", NewVersion), - CoreTools.Translate("The update process will start after closing UniGetUI"), - InfoBarSeverity.Success, - true, - UpdateNowButton - ); - - // Show a toast notification - AppNotificationBuilder builder = new AppNotificationBuilder() - .SetScenario(AppNotificationScenario.Default) - .SetTag(CoreData.UniGetUICanBeUpdated.ToString()) - .AddText( - CoreTools.Translate("{0} can be updated to version {1}", "UniGetUI", NewVersion) - ) - .SetAttributionText( - CoreTools.Translate( - "You have currently version {0} installed", - CoreData.VersionName - ) - ) - .AddArgument("action", NotificationArguments.Show) - .AddButton( - new AppNotificationButton(CoreTools.Translate("Update now")).AddArgument( - "action", - NotificationArguments.ReleaseSelfUpdateLock - ) - ); - AppNotification notification = builder.BuildNotification(); - notification.ExpiresOnReboot = true; - AppNotificationManager.Default.Show(notification); - }); - - if (AutoLaunch && !Window.Visible) - { - LogUpdateDebug("AutoLaunch is enabled and the Window is hidden, launching installer..."); - } - else - { - LogUpdateDebug( - "Waiting for mainWindow to be closed or for user to trigger the update from the notification..." - ); - while ( - !(ReleaseLockForAutoupdate_Window && !ManualCheck) - && !ReleaseLockForAutoupdate_Notification - && !ReleaseLockForAutoupdate_UpdateBanner - ) - { - await Task.Delay(100); - } - LogUpdateDebug("Autoupdater lock released, launching installer..."); - } - - if (!ManualCheck && Settings.Get(Settings.K.DisableAutoUpdateWingetUI)) - { - LogUpdateWarn("User has disabled updates"); - MarkAttemptFinished("aborted - auto-update disabled while waiting"); - return true; - } - - await LaunchInstallerAndQuit(installerLocation); - return true; - } - - /// - /// Launches the installer located on the installerLocation argument. The installer - /// is expected to terminate UniGetUI before file replacement; if it returns control - /// to us, we surface the exit code so the user has something concrete to act on. - /// - private static async Task LaunchInstallerAndQuit(string installerLocation) - { - LogUpdateInfo($"Launching installer: {installerLocation}"); - using Process p = new() - { - StartInfo = new() - { - FileName = installerLocation, - Arguments = - "/SILENT /SUPPRESSMSGBOXES /NORESTART /SP- /NoVCRedist /NoEdgeWebView /NoWinGet", - UseShellExecute = true, - CreateNoWindow = true, - }, - }; - - bool started; - try - { - started = p.Start(); - } - catch (Exception ex) - { - LogUpdateError("Process.Start threw while launching the installer:"); - LogUpdateError(ex); - ShowMessage_ThreadSafe( - CoreTools.Translate("The updater could not be launched."), - ex.Message, - InfoBarSeverity.Error, - true, - CreateViewLogButton() - ); - MarkAttemptFinished($"installer launch threw: {ex.Message}"); - return; - } - - if (!started) - { - LogUpdateError("Failed to start installer process (Process.Start returned false)."); - ShowMessage_ThreadSafe( - CoreTools.Translate("The updater could not be launched."), - CoreTools.Translate("The operating system did not start the installer process."), - InfoBarSeverity.Error, - true, - CreateViewLogButton() - ); - MarkAttemptFinished("Process.Start returned false"); - return; - } - - LogUpdateInfo($"Installer process started (PID {p.Id}). The installer is expected to terminate UniGetUI before file replacement."); - - ShowMessage_ThreadSafe( - CoreTools.Translate("UniGetUI is being updated..."), - CoreTools.Translate("This may take a minute or two"), - InfoBarSeverity.Informational, - false - ); - - await p.WaitForExitAsync(); - - // If we reach here, the installer exited without terminating this process. - // Distinguish two cases: - // - Exit code 0: installer succeeded; the new version IS installed at the - // install location, but the running copy was not replaced (almost always - // because UniGetUI is running from outside the install location — typically - // a development build). This is not really an error. - // - Any other code: installer reported a failure; the update did not apply. - int exitCode = p.ExitCode; - string reason = DescribeInstallerExitCode(exitCode); - - if (exitCode == 0) - { - string runningPath = Environment.ProcessPath ?? "(unknown)"; - LogUpdateWarn($"Installer reported success (exit code 0) but did not replace this running copy. Running from: {runningPath}"); - - ShowMessage_ThreadSafe( - CoreTools.Translate("Update installed."), - CoreTools.Translate("UniGetUI was updated successfully, but this running copy was not replaced. This usually means you are running a development build. Close this copy and start the newly-installed version to finish."), - InfoBarSeverity.Warning, - true, - CreateViewLogButton() - ); - MarkAttemptFinished("installer succeeded but did not replace running copy"); - return; - } - - LogUpdateError($"Installer exited with code {exitCode} ({reason}) without restarting UniGetUI."); - - ShowMessage_ThreadSafe( - CoreTools.Translate("The update could not be applied."), - CoreTools.Translate("Installer exit code {0}: {1}", exitCode, reason), - InfoBarSeverity.Error, - true, - CreateViewLogButton() - ); - MarkAttemptFinished($"installer failed with code {exitCode}"); - } - - private static void ShowMessage_ThreadSafe( - string Title, - string Message, - InfoBarSeverity MessageSeverity, - bool BannerClosable, - Button? ActionButton = null - ) - { - try - { - if (Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread() is null) - { - Window.DispatcherQueue.TryEnqueue(() => - ShowMessage_ThreadSafe( - Title, - Message, - MessageSeverity, - BannerClosable, - ActionButton - ) - ); - return; - } - - Banner.Title = Title; - Banner.Message = Message; - Banner.Severity = MessageSeverity; - Banner.IsClosable = BannerClosable; - Banner.ActionButton = ActionButton; - Banner.IsOpen = true; - } - catch (Exception ex) - { - Logger.Error(ex); - } - } - -} diff --git a/src/UniGetUI/CLIHandler.cs b/src/UniGetUI/CLIHandler.cs deleted file mode 100644 index 59702e26fa..0000000000 --- a/src/UniGetUI/CLIHandler.cs +++ /dev/null @@ -1,255 +0,0 @@ -using System.Text.Json; -using UniGetUI.Core.Data; -using UniGetUI.Core.Logging; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.SettingsEngine.SecureSettings; -using UniGetUI.Core.Tools; -using UniGetUI.Interface; -using UniGetUI.Shared; - -namespace UniGetUI; - -public static class CLIHandler -{ - public const string HELP = "--help"; - public const string DAEMON = "--daemon"; - public const string MIGRATE_WINGETUI_TO_UNIGETUI = "--migrate-wingetui-to-unigetui"; - public const string UNINSTALL_WINGETUI = "--uninstall-wingetui"; - public const string UNINSTALL_UNIGETUI = "--uninstall-unigetui"; - public const string NO_CORRUPT_DIALOG = "--no-corrupt-dialog"; - - public const string IMPORT_SETTINGS = "--import-settings"; - public const string EXPORT_SETTINGS = "--export-settings"; - - public const string ENABLE_SETTING = "--enable-setting"; - public const string DISABLE_SETTING = "--disable-setting"; - public const string SET_SETTING_VAL = "--set-setting-value"; - - public const string ENABLE_SECURE_SETTING = "--enable-secure-setting"; - public const string DISABLE_SECURE_SETTING = "--disable-secure-setting"; - public const string ENABLE_SECURE_SETTING_FOR_USER = SecureSettings.Args.ENABLE_FOR_USER; - public const string DISABLE_SECURE_SETTING_FOR_USER = SecureSettings.Args.DISABLE_FOR_USER; - public const string HEADLESS = "--headless"; - - private enum HRESULT - { - SUCCESS = 0, - STATUS_FAILED = -1, - STATUS_INVALID_PARAMETER = -1073741811, - STATUS_NO_SUCH_FILE = -1073741809, - STATUS_UNKNOWN__SETTINGS_KEY = -2, - STATUS_BACKGROUND_API_UNAVAILABLE = -3, - STATUS_UNKNOWN_AUTOMATION_COMMAND = -4, - } - - public static int Help() - { - return SharedPreUiCommandDispatcher.Help(); - } - - public static int ImportSettings() - { - return ImportSettings(Environment.GetCommandLineArgs()); - } - - internal static int ImportSettings(IReadOnlyList args) - { - return SharedPreUiCommandDispatcher.ImportSettings( - args, - SharedPreUiCommandDispatcher.WinUiExitCodes - ); - } - - public static int ExportSettings() - { - return ExportSettings(Environment.GetCommandLineArgs()); - } - - internal static int ExportSettings(IReadOnlyList args) - { - return SharedPreUiCommandDispatcher.ExportSettings( - args, - SharedPreUiCommandDispatcher.WinUiExitCodes - ); - } - - public static int EnableSetting() - { - return EnableSetting(Environment.GetCommandLineArgs()); - } - - internal static int EnableSetting(IReadOnlyList args) - { - return SharedPreUiCommandDispatcher.EnableSetting( - args, - SharedPreUiCommandDispatcher.WinUiExitCodes - ); - } - - public static int DisableSetting() - { - return DisableSetting(Environment.GetCommandLineArgs()); - } - - internal static int DisableSetting(IReadOnlyList args) - { - return SharedPreUiCommandDispatcher.DisableSetting( - args, - SharedPreUiCommandDispatcher.WinUiExitCodes - ); - } - - public static int SetSettingsValue() - { - return SetSettingsValue(Environment.GetCommandLineArgs()); - } - - internal static int SetSettingsValue(IReadOnlyList args) - { - return SharedPreUiCommandDispatcher.SetSettingValue( - args, - SharedPreUiCommandDispatcher.WinUiExitCodes - ); - } - - public static int WingetUIToUniGetUIMigrator() - { - try - { - string[] BasePaths = - [ - // User desktop icon - Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), - // User start menu icon - Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), - // Common desktop icon - Environment.GetFolderPath(Environment.SpecialFolder.CommonDesktopDirectory), - // User start menu icon - Environment.GetFolderPath(Environment.SpecialFolder.CommonStartMenu), - ]; - - foreach (string path in BasePaths) - { - foreach ( - string old_wingetui_icon in new[] - { - "WingetUI.lnk", - "WingetUI .lnk", - "UniGetUI (formerly WingetUI) .lnk", - "UniGetUI (formerly WingetUI).lnk", - } - ) - { - try - { - string old_file = Path.Join(path, old_wingetui_icon); - string new_file = Path.Join(path, "UniGetUI.lnk"); - if (!File.Exists(old_file)) - { - continue; - } - - if (File.Exists(old_file) && File.Exists(new_file)) - { - Logger.Info( - "Deleting shortcut " - + old_file - + " since new shortcut already exists" - ); - File.Delete(old_file); - } - else if (File.Exists(old_file) && !File.Exists(new_file)) - { - Logger.Info("Moving shortcut to " + new_file); - File.Move(old_file, new_file); - } - } - catch (Exception ex) - { - Logger.Warn( - $"An error occurred while migrating the shortcut {Path.Join(path, old_wingetui_icon)}" - ); - Logger.Warn(ex); - } - } - } - - return 0; - } - catch (Exception ex) - { - Logger.Error(ex); - return ex.HResult; - } - } - - public static int UninstallUniGetUI() - { - // There is currently no uninstall logic. However, this needs to be maintained, or otherwhise UniGetUI will launch on uninstall - return 0; - } - - public static int EnableSecureSetting() - { - return EnableSecureSetting(Environment.GetCommandLineArgs()); - } - - internal static int EnableSecureSetting(IReadOnlyList args) - { - return SharedPreUiCommandDispatcher.EnableSecureSetting( - args, - SharedPreUiCommandDispatcher.WinUiExitCodes - ); - } - - public static int DisableSecureSetting() - { - return DisableSecureSetting(Environment.GetCommandLineArgs()); - } - - internal static int DisableSecureSetting(IReadOnlyList args) - { - return SharedPreUiCommandDispatcher.DisableSecureSetting( - args, - SharedPreUiCommandDispatcher.WinUiExitCodes - ); - } - - public static int EnableSecureSettingForUser() - { - return EnableSecureSettingForUser(Environment.GetCommandLineArgs()); - } - - internal static int EnableSecureSettingForUser(IReadOnlyList args) - { - return SharedPreUiCommandDispatcher.EnableSecureSettingForUser( - args, - SharedPreUiCommandDispatcher.WinUiExitCodes - ); - } - - public static int DisableSecureSettingForUser() - { - return DisableSecureSettingForUser(Environment.GetCommandLineArgs()); - } - - internal static int DisableSecureSettingForUser(IReadOnlyList args) - { - return SharedPreUiCommandDispatcher.DisableSecureSettingForUser( - args, - SharedPreUiCommandDispatcher.WinUiExitCodes - ); - } - - public static int Automation() - { - return Automation(Environment.GetCommandLineArgs()); - } - - internal static int Automation(IReadOnlyList args) - { - return IpcCliCommandRunner.RunAsync(args, Console.Out, Console.Error) - .GetAwaiter() - .GetResult(); - } -} diff --git a/src/UniGetUI/Controls/CustomNavViewItem.cs b/src/UniGetUI/Controls/CustomNavViewItem.cs deleted file mode 100644 index 9a38ce9bef..0000000000 --- a/src/UniGetUI/Controls/CustomNavViewItem.cs +++ /dev/null @@ -1,110 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Automation; -using Microsoft.UI.Xaml.Controls; -using UniGetUI.Core.Tools; -using UniGetUI.Interface; -using UniGetUI.Interface.Enums; -using UniGetUI.Interface.Widgets; - -namespace UniGetUI.Controls; - -internal sealed partial class CustomNavViewItem : NavigationViewItem -{ - int _iconSize = 20; - public IconType LocalIcon - { - set => base.Icon = new LocalIcon(value); - } - public string GlyphIcon - { - set => base.Icon = new FontIcon() { Glyph = value }; - } - public new IconElement Icon - { - set => base.Icon = value; - } - - public bool IsLoading - { - set - { - if (value) - _ = increaseMargins(); - else - _ = decreaseMargins(); - _progressRing.Visibility = value ? Visibility.Visible : Visibility.Collapsed; - } - } - - public int IconSize - { - set => Resources["NavigationViewItemOnLeftIconBoxHeight"] = _iconSize = value; - } - - public string Text - { - set - { - string text = CoreTools.Translate(value); - _textBlock.Text = text; - ToolTipService.SetToolTip(this, text); - AutomationProperties.SetName(this, text); - } - } - - private readonly TextBlock _textBlock; - private readonly ProgressRing _progressRing; - - private PageType _pageType; - public new PageType Tag - { - set => _pageType = value; - get => _pageType; - } - - public CustomNavViewItem() - { - Height = 48; - Resources["NavigationViewItemOnLeftIconBoxHeight"] = _iconSize; - Resources["NavigationViewItemContentPresenterMargin"] = new Thickness(0); - - var grid = new Grid { Height = 44 }; - - _progressRing = new ProgressRing - { - Width = 20, - Height = 20, - Margin = new Thickness(-32, 0, 0, 0), - HorizontalAlignment = HorizontalAlignment.Left, - VerticalAlignment = VerticalAlignment.Center, - IsIndeterminate = true, - Visibility = Visibility.Collapsed, - }; - - _textBlock = new TextBlock { VerticalAlignment = VerticalAlignment.Center }; - - grid.Children.Add(_progressRing); - grid.Children.Add(_textBlock); - base.Content = grid; - } - - public async Task increaseMargins() - { - for (int i = (int)base.Icon.Margin.Left; i < 6; i += 2) - { - base.Icon.Margin = new Thickness(i); - await Task.Delay(15); - } - base.Icon.Margin = new Thickness(6); - } - - public async Task decreaseMargins() - { - for (int i = (int)base.Icon.Margin.Left; i > 0; i -= 2) - { - base.Icon.Margin = new Thickness(i); - await Task.Delay(15); - } - base.Icon.Margin = new Thickness(0); - } -} diff --git a/src/UniGetUI/Controls/DialogCloseButton.xaml b/src/UniGetUI/Controls/DialogCloseButton.xaml deleted file mode 100644 index 63919052f2..0000000000 --- a/src/UniGetUI/Controls/DialogCloseButton.xaml +++ /dev/null @@ -1,35 +0,0 @@ - - - - diff --git a/src/UniGetUI/Controls/DialogCloseButton.xaml.cs b/src/UniGetUI/Controls/DialogCloseButton.xaml.cs deleted file mode 100644 index 30647aba6a..0000000000 --- a/src/UniGetUI/Controls/DialogCloseButton.xaml.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Interface.Widgets; - -public sealed partial class DialogCloseButton : UserControl -{ - public event EventHandler? Click; - - public DialogCloseButton() - { - this.InitializeComponent(); - } - - private void CloseButton_Click(object sender, RoutedEventArgs e) - { - Click?.Invoke(sender, e); - } -} diff --git a/src/UniGetUI/Controls/LocalIcon.cs b/src/UniGetUI/Controls/LocalIcon.cs deleted file mode 100644 index cc0335412e..0000000000 --- a/src/UniGetUI/Controls/LocalIcon.cs +++ /dev/null @@ -1,81 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Media; -using UniGetUI.Interface.Enums; - -namespace UniGetUI.Interface.Widgets -{ - public partial class LocalIcon : FontIcon - { - public static FontFamily font = (FontFamily)Application.Current.Resources["SymbolFont"]; - - public IconType Icon - { - set => Glyph = $"{(char)value}"; - } - - public LocalIcon() - { - FontFamily = font; - } - - public LocalIcon(IconType icon) - : this() - { - Glyph = $"{(char)icon}"; - } - } - - public static class IconBuilder - { - private static FontFamily customFont = null!; - private static FontFamily symbolFont = null!; - - public static IconType SetIcon(this TextBlock block, IconType icon) - { - customFont ??= (FontFamily)Application.Current.Resources["SymbolFont"]; - block.Text = $"{(char)icon}"; - block.FontFamily = customFont; - return icon; - } - - public static IconType GetIcon(this TextBlock block) - { - return IconType.Help; - } - - public static string SetGlyph(this TextBlock block, string glyph) - { - symbolFont ??= new FontFamily("Segoe Fluent Icons,Segoe MDL2 Assets"); - block.Text = glyph; - block.FontFamily = symbolFont; - return glyph; - } - - public static string GetGlyph(this TextBlock block) - { - return block.Text; - } - } - - public partial class LocalIconSource : FontIconSource - { - public static FontFamily font = (FontFamily)Application.Current.Resources["SymbolFont"]; - - public IconType Icon - { - set => Glyph = $"{(char)value}"; - } - - public LocalIconSource() - { - FontFamily = font; - } - - public LocalIconSource(IconType icon) - : this() - { - Glyph = $"{(char)icon}"; - } - } -} diff --git a/src/UniGetUI/Controls/MenuForPackage.cs b/src/UniGetUI/Controls/MenuForPackage.cs deleted file mode 100644 index 9d8d563c73..0000000000 --- a/src/UniGetUI/Controls/MenuForPackage.cs +++ /dev/null @@ -1,129 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Media; -using UniGetUI.Core.Tools; -using UniGetUI.Interface.Enums; - -namespace UniGetUI.Interface.Widgets -{ - public partial class BetterMenu : MenuFlyout - { - private readonly Style menuyStyle = (Style) - Application.Current.Resources["BetterContextMenu"]; - - public BetterMenu() - { - MenuFlyoutPresenterStyle = menuyStyle; - } - } - - public partial class BetterMenuItem : MenuFlyoutItem - { - private readonly Style menuStyle = (Style)Application.Current.Resources["BetterMenuItem"]; - - public IconType IconName - { - set - { - var icon = new LocalIcon(value) { FontSize = 24 }; - Icon = icon; - } - } - - public string UntranslatedText - { - set => base.Text = value; - } - public new string Text - { - set => base.Text = CoreTools.Translate(value); - } - - public BetterMenuItem() - { - Style = menuStyle; - } - } - - public partial class BetterToggleMenuItem : ToggleMenuFlyoutItem - { - private readonly Style menuStyle = (Style) - Application.Current.Resources["BetterToggleMenuItem"]; - - public IconType IconName - { - set - { - var icon = new LocalIcon(value) { FontSize = 24 }; - Icon = icon; - } - } - - public new string Text - { - set => base.Text = CoreTools.Translate(value); - } - - public BetterToggleMenuItem() - { - Style = menuStyle; - } - } - - public partial class BetterTabViewItem : TabViewItem - { - string line1 = ""; - string line2 = ""; - public string Line1 - { - set - { - line1 = value; - LoadText(); - } - } - public string Line2 - { - set - { - line2 = value; - LoadText(); - } - } - - public IconType IconName - { - set => IconSource = new LocalIconSource(value); - } - - public BetterTabViewItem() - { - IsClosable = false; - CanDrag = false; - } - - public void LoadText() - { - string text = ""; - // The invisible U+200E character here is used to prevent the text from being - // trimmed in the TabViewItem header, adding a little bit of padding at the end. - if (line1 != "") - text += CoreTools.Translate(line1) + " ‎‎ "; - if (line2 != "") - text += (text.Length > 0 ? "\n" : "") + CoreTools.Translate(line2) + " ‎ "; - Header = text; - } - } - - public partial class BetterFlyout : Flyout - { - public BetterFlyout() - : base() - { - ShouldConstrainToRootBounds = false; - SystemBackdrop = new DesktopAcrylicBackdrop(); - FlyoutPresenterStyle = (Style) - Application.Current.Resources["BetterFlyoutPresenterStyle"]; - } - } -} diff --git a/src/UniGetUI/Controls/ObservablePackageCollection.cs b/src/UniGetUI/Controls/ObservablePackageCollection.cs deleted file mode 100644 index 4ec5698d8a..0000000000 --- a/src/UniGetUI/Controls/ObservablePackageCollection.cs +++ /dev/null @@ -1,132 +0,0 @@ -using UniGetUI.Core.Classes; -using UniGetUI.PackageEngine.Interfaces; - -namespace UniGetUI.PackageEngine.PackageClasses -{ - /// - /// A special ObservableCollection designed to work with Package objects - /// - public partial class ObservablePackageCollection : SortableObservableCollection - { - public enum Sorter - { - Checked, - Name, - Id, - Version, - NewVersion, - Source, - } - - public Sorter CurrentSorter { get; private set; } - - public ObservablePackageCollection() - { - CurrentSorter = Sorter.Name; - SortingSelector = x => x.Package.Name; - } - - public void FromRange(IReadOnlyList packages) - { - BlockSorting = true; - - // Clear the list - Clear(); - - // Add all packages - foreach (var w in packages) - Add(w); - - BlockSorting = false; - Sort(); - } - - /// - /// Sets the property with which to filter the package and sorts the collection - /// - /// The field with which to sort the collection - /// - public void SetSorter(Sorter field) - { - CurrentSorter = field; - switch (field) - { - case Sorter.Checked: - SortingSelector = x => x.Package.IsChecked; - break; - - case Sorter.Name: - SortingSelector = x => x.Package.Name; - break; - - case Sorter.Id: - SortingSelector = x => x.Package.Id; - break; - - case Sorter.Version: - SortingSelector = x => x.Package.NormalizedVersion; - break; - - case Sorter.NewVersion: - SortingSelector = x => x.Package.NormalizedNewVersion; - break; - - case Sorter.Source: - SortingSelector = x => x.Package.Source.AsString_DisplayName; - break; - } - } - - /// - /// Returns a list containing the packages in this collection - /// - public List GetPackages() - { - List packages = []; - foreach (PackageWrapper wrapper in this) - { - packages.Add(wrapper.Package); - } - - return packages; - } - - /// - /// Returns a list containing the checked packages on this collection - /// - public List GetCheckedPackages() - { - List packages = []; - foreach (PackageWrapper wrapper in this) - { - if (wrapper.Package.IsChecked) - { - packages.Add(wrapper.Package); - } - } - return packages; - } - - /// - /// Mark all packages as checked - /// - public void SelectAll() - { - foreach (PackageWrapper wrapper in this) - { - wrapper.IsChecked = true; - } - } - - /// - /// Mark all packages as unchecked - /// - public void ClearSelection() - { - foreach (PackageWrapper wrapper in this) - { - wrapper.IsChecked = false; - } - } - } -} diff --git a/src/UniGetUI/Controls/OperationWidgets/OperationBadge.cs b/src/UniGetUI/Controls/OperationWidgets/OperationBadge.cs deleted file mode 100644 index b1a2e62b80..0000000000 --- a/src/UniGetUI/Controls/OperationWidgets/OperationBadge.cs +++ /dev/null @@ -1,34 +0,0 @@ -using UniGetUI.Interface.Enums; - -namespace UniGetUI.Controls.OperationWidgets; - -public class OperationBadge -{ - public string Tooltip; - public string PrimaryBanner; - public string SecondaryBanner; - public bool SecondaryBannerVisible; - public IconType Icon; - - public OperationBadge( - string tooltip, - IconType icon, - string primaryBanner, - string? secondaryBanner = null - ) - { - Tooltip = tooltip; - Icon = icon; - PrimaryBanner = primaryBanner; - if (secondaryBanner is null || secondaryBanner == String.Empty) - { - SecondaryBannerVisible = false; - SecondaryBanner = ""; - } - else - { - SecondaryBanner = secondaryBanner; - SecondaryBannerVisible = true; - } - } -} diff --git a/src/UniGetUI/Controls/OperationWidgets/OperationControl.cs b/src/UniGetUI/Controls/OperationWidgets/OperationControl.cs deleted file mode 100644 index c895bb1db0..0000000000 --- a/src/UniGetUI/Controls/OperationWidgets/OperationControl.cs +++ /dev/null @@ -1,815 +0,0 @@ -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using CommunityToolkit.WinUI; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Media; -using Microsoft.Windows.AppNotifications; -using Microsoft.Windows.AppNotifications.Builder; -using UniGetUI.Core.Logging; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; -using UniGetUI.Interface; -using UniGetUI.Interface.Enums; -using UniGetUI.Interface.Telemetry; -using UniGetUI.Interface.Widgets; -using UniGetUI.PackageEngine; -using UniGetUI.PackageEngine.Classes.Packages.Classes; -using UniGetUI.PackageEngine.Enums; -using UniGetUI.PackageEngine.Operations; -using UniGetUI.PackageEngine.PackageLoader; -using UniGetUI.PackageOperations; -using UniGetUI.Pages.DialogPages; -using Windows.UI; - -namespace UniGetUI.Controls.OperationWidgets; - -public partial class OperationControl : INotifyPropertyChanged -{ - public AbstractOperation Operation; - public BetterMenu OpMenu; - public OperationStatus? MenuStateOnLoaded; - public ObservableCollection Badges = []; - private int _errorCount; - - public OperationControl(AbstractOperation operation) - { - OpMenu = new BetterMenu(); - Operation = operation; - Operation.LogLineAdded += (_, values) => LiveLine = values.Item1; - Operation.StatusChanged += OnOperationStatusChanged; - Operation.OperationStarting += OnOperationStarting; - Operation.OperationFinished += OnOperationFinished; - Operation.OperationFailed += OnOperationFailed; - Operation.OperationSucceeded += OnOperationSucceeded; - - Operation.BadgesChanged += (_, badges) => - { - Badges.Clear(); - if (badges.AsAdministrator) - Badges.Add( - new( - CoreTools.Translate("Administrator privileges"), - IconType.UAC, - CoreTools.Translate( - "This operation is running with administrator privileges." - ), - "" - ) - ); - - if (badges.Interactive) - Badges.Add( - new( - CoreTools.Translate("Interactive operation"), - IconType.Interactive, - CoreTools.Translate("This operation is running interactively."), - CoreTools.Translate("You will likely need to interact with the installer.") - ) - ); - - if (badges.SkipHashCheck) - Badges.Add( - new( - CoreTools.Translate("Integrity checks skipped"), - IconType.Checksum, - CoreTools.Translate( - "Integrity checks will not be performed during this operation" - ), - CoreTools.Translate("This is not recommended.") - + " " - + CoreTools.Translate("Proceed at your own risk.") - ) - ); - - /*if (badges.Scope is not null) - { - if (badges.Scope is PackageScope.Local) - Badges.Add(new( - CoreTools.Translate ("Local operation"), - IconType.Home, - CoreTools.Translate ("The changes performed by this operation will affect only the current user."), - "" - )); - else - Badges.Add(new( - CoreTools.Translate ("Global operation"), - IconType.LocalPc, - CoreTools.Translate ("The changes performed by this operation may affect other users on this machine."), - "" - )); - }*/ - }; - - _title = Operation.Metadata.Title; - _liveLine = operation.GetOutput().Any() - ? operation.GetOutput()[operation.GetOutput().Count - 1].Item1 - : CoreTools.Translate("Please wait..."); - _buttonText = ""; - OnOperationStatusChanged(this, operation.Status); - _ = LoadIcon(); - if (!operation.Started) - _ = operation.MainThread(); - } - - private void OnOperationStarting(object? sender, EventArgs e) - { - ShowProgressToast(); - if (MainApp.Instance.MainWindow.NavigationPage.OperationList.Items.Contains(this)) - { - MainApp.Instance.MainWindow.NavigationPage.OperationList.SmoothScrollIntoViewWithItemAsync( - this - ); - } - } - - private void OnOperationSucceeded(object? sender, EventArgs e) => _ = _onOperationSucceeded(); - - private async Task _onOperationSucceeded() - { - // Success notification - ShowSuccessToast(); - - // Clean succesful operation from list - if ( - !Settings.Get(Settings.K.MaintainSuccessfulInstalls) - && Operation is not DownloadOperation - ) - { - await TimeoutAndClose(); - } - } - - private void OnOperationFailed(object? sender, EventArgs e) - { - ShowErrorToast(); - } - - private void OnOperationFinished(object? sender, EventArgs e) => _ = _onOperationFinished(); - - private async Task _onOperationFinished() - { - // Remove progress notification (if any) - _ = AppNotificationManager.Default.RemoveByTagAsync( - Operation.Metadata.Identifier + "progress" - ); - - if (Operation.Status is OperationStatus.Failed) - { - _errorCount++; - MainApp.Tooltip.ErrorsOccurred++; - } - else - { - MainApp.Tooltip.ErrorsOccurred -= _errorCount; - _errorCount = 0; - } - MainApp.Instance.MainWindow.UpdateSystemTrayStatus(); - - // Generate process output - List rawOutput = - [ - " ", - "▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄", - ]; - foreach (var line in Operation.GetOutput()) - { - rawOutput.Add(line.Item1); - } - - string[] oldHistory = Settings.GetValue(Settings.K.OperationHistory).Split("\n"); - if (oldHistory.Length > 300) - oldHistory = oldHistory.Take(300).ToArray(); - - List newHistory = [.. rawOutput, .. oldHistory]; - Settings.SetValue(Settings.K.OperationHistory, string.Join('\n', newHistory)); - rawOutput.Add(""); - rawOutput.Add(""); - rawOutput.Add(""); - - // Handle UAC for batches - if (Settings.Get(Settings.K.DoCacheAdminRightsForBatches)) - { - if (!MainApp.Operations.AreThereRunningOperations()) - { - Logger.Info("Clearing UAC prompt since there are no remaining operations"); - await CoreTools.ResetUACForCurrentProcess(); - } - } - - // Handle newly created shortcuts - if ( - Settings.Get(Settings.K.AskToDeleteNewDesktopShortcuts) - && !MainApp.Operations.AreThereRunningOperations() - && DesktopShortcutsDatabase.GetUnknownShortcuts().Any() - ) - { - _ = DialogHelper.HandleNewDesktopShortcuts(); - } - } - - private async Task LoadIcon() - { - Icon = await Operation.GetOperationIcon(); - } - - private void OnOperationStatusChanged(object? sender, OperationStatus newStatus) - { - switch (newStatus) - { - case OperationStatus.InQueue: - ProgressIndeterminate = false; - ProgressValue = 0; - ProgressForeground = (SolidColorBrush) - Application.Current.Resources["SystemFillColorNeutralBrush"]; - Background = (SolidColorBrush) - Application.Current.Resources["SystemFillColorNeutralBackgroundBrush"]; - ButtonText = CoreTools.Translate("Cancel"); - break; - case OperationStatus.Running: - ProgressIndeterminate = true; - ButtonText = CoreTools.Translate("Cancel"); - ProgressForeground = (SolidColorBrush) - Application.Current.Resources["SystemFillColorAttentionBrush"]; - Background = (SolidColorBrush) - Application.Current.Resources["SystemFillColorAttentionBackgroundBrush"]; - break; - case OperationStatus.Succeeded: - ProgressIndeterminate = false; - ProgressValue = 100; - ProgressForeground = (SolidColorBrush) - Application.Current.Resources["SystemFillColorSuccessBrush"]; - Background = (SolidColorBrush) - Application.Current.Resources["SystemFillColorNeutralBackgroundBrush"]; - ButtonText = CoreTools.Translate("Close"); - break; - case OperationStatus.Failed: - ProgressIndeterminate = false; - ProgressValue = 100; - ProgressForeground = (SolidColorBrush) - Application.Current.Resources["SystemFillColorCriticalBrush"]; - Background = (SolidColorBrush) - Application.Current.Resources["SystemFillColorCriticalBackgroundBrush"]; - ButtonText = CoreTools.Translate("Close"); - break; - case OperationStatus.Canceled: - ProgressIndeterminate = false; - ProgressValue = 100; - ProgressForeground = (SolidColorBrush) - Application.Current.Resources["SystemFillColorCautionBrush"]; - Background = (SolidColorBrush) - Application.Current.Resources["SystemFillColorNeutralBackgroundBrush"]; - ButtonText = CoreTools.Translate("Close"); - break; - default: - throw new ArgumentOutOfRangeException(nameof(newStatus), newStatus, null); - } - } - - public void LiveLineClick() => _ = LiveLineClickAsync(); - - private async Task LiveLineClickAsync() - { - if (Operation.Status is OperationStatus.Failed or OperationStatus.Canceled) - { - await DialogHelper.ShowOperationFailedDialog(Operation, this); - } - else - { - await DialogHelper.ShowLiveLogDialog(Operation); - } - } - - public void ButtonClick() - { - if (Operation.Status is OperationStatus.Running or OperationStatus.InQueue) - { - Operation.Cancel(); - } - else - { - Close(); - } - } - - public void LoadMenu() - { - if (MenuStateOnLoaded == Operation.Status) - return; - MenuStateOnLoaded = Operation.Status; - - // Reset menu - OpMenu.Items.Clear(); - - // Load operation-specific entries - var normalOptions = GetOperationOptions(); - if (normalOptions.Count != 0) - { - foreach (var item in normalOptions) - { - OpMenu.Items.Add(item); - } - - OpMenu.Items.Add(new MenuFlyoutSeparator()); - } - - if (Operation.Status is OperationStatus.InQueue) - { - var skipQueue = new BetterMenuItem - { - Text = CoreTools.Translate("Run now"), - Icon = new FontIcon { Glyph = "\uE768" }, - }; - skipQueue.Click += (_, _) => Operation.SkipQueue(); - OpMenu.Items.Add(skipQueue); - - var putNext = new BetterMenuItem - { - Text = CoreTools.Translate("Run next"), - Icon = new FontIcon { Glyph = "\uEB9D" }, - }; - putNext.Click += (_, _) => Operation.RunNext(); - OpMenu.Items.Add(putNext); - - var putLast = new BetterMenuItem - { - Text = CoreTools.Translate("Run last"), - Icon = new FontIcon { Glyph = "\uEB9E" }, - }; - putLast.Click += (_, _) => Operation.BackOfTheQueue(); - OpMenu.Items.Add(putLast); - - OpMenu.Items.Add(new MenuFlyoutSeparator()); - } - - // Create Cancel/Retry buttons - if (Operation.Status is OperationStatus.InQueue or OperationStatus.Running) - { - var cancel = new BetterMenuItem - { - Text = CoreTools.Translate("Cancel"), - IconName = IconType.Cross, - }; - cancel.Click += (_, _) => Operation.Cancel(); - OpMenu.Items.Add(cancel); - } - else - { - var retry = new BetterMenuItem - { - Text = CoreTools.Translate("Retry"), - IconName = IconType.Reload, - }; - retry.Click += (_, _) => Operation.Retry(AbstractOperation.RetryMode.Retry); - OpMenu.Items.Add(retry); - - // Add extra retry options, if applicable - var extraRetry = GetRetryOptions(() => { }); - if (extraRetry.Count != 0) - { - OpMenu.Items.Add(new MenuFlyoutSeparator()); - - foreach (var item in extraRetry) - { - OpMenu.Items.Add(item); - } - } - } - } - - private async Task TimeoutAndClose() - { - var oldStatus = Operation.Status; - await Task.Delay(5000); - - if (Operation.Status == oldStatus) - Close(); - } - - public void Close() - { - MainApp.Tooltip.ErrorsOccurred -= _errorCount; - _errorCount = 0; - MainApp.Instance.MainWindow.UpdateSystemTrayStatus(); - - MainApp.Operations._operationList.Remove(this); - while (AbstractOperation.OperationQueue.Remove(Operation)) - ; - if (Operation.Status is not (OperationStatus.InQueue or OperationStatus.Running)) - { - IpcOperationApi.ForgetTracking(Operation.Metadata.Identifier); - } - } - - private string _buttonText; - public string ButtonText - { - get => _buttonText; - set - { - _buttonText = value; - OnPropertyChanged(); - } - } - - private string _liveLine; - public string LiveLine - { - get => _liveLine; - set - { - _liveLine = value; - OnPropertyChanged(); - } - } - - private string _title; - public string Title - { - get => _title; - set - { - _title = value; - OnPropertyChanged(); - } - } - - private bool _progressIndeterminate; - public bool ProgressIndeterminate - { - get => _progressIndeterminate; - set - { - _progressIndeterminate = value; - OnPropertyChanged(); - } - } - - private int _progressValue; - public int ProgressValue - { - get => _progressValue; - set - { - _progressValue = value; - OnPropertyChanged(); - } - } - - private Uri _icon = new("ms-appx:///Assets/images/package_color.png"); - public Uri Icon - { - get => _icon; - set - { - _icon = value; - OnPropertyChanged(); - } - } - - private SolidColorBrush _background = new(Color.FromArgb(0, 0, 0, 0)); - public SolidColorBrush Background - { - get => _background; - set - { - _background = value; - OnPropertyChanged(); - } - } - - private SolidColorBrush _progressForeground = new(Color.FromArgb(0, 0, 0, 0)); - public SolidColorBrush ProgressForeground - { - get => _progressForeground; - set - { - _progressForeground = value; - OnPropertyChanged(); - } - } - - public event PropertyChangedEventHandler? PropertyChanged; - - protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) - { - MainApp.Dispatcher.TryEnqueue(() => - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)) - ); - } - - private void ShowProgressToast() - { - if (Settings.AreProgressNotificationsDisabled()) - return; - - try - { - _ = AppNotificationManager.Default.RemoveByTagAsync( - Operation.Metadata.Identifier + "progress" - ); - AppNotificationBuilder builder = new AppNotificationBuilder() - .SetScenario(AppNotificationScenario.Default) - .SetTag(Operation.Metadata.Identifier + "progress") - .AddProgressBar( - new AppNotificationProgressBar() - .SetStatus(CoreTools.Translate("Please wait...")) - .SetValueStringOverride("\u2003") - .SetTitle(Operation.Metadata.Status) - .SetValue(1.0) - ) - .AddArgument("action", NotificationArguments.Show); - AppNotification notification = builder.BuildNotification(); - notification.ExpiresOnReboot = true; - notification.SuppressDisplay = true; - AppNotificationManager.Default.Show(notification); - } - catch (Exception ex) - { - Logger.Error("Failed to show toast notification"); - Logger.Error(ex); - } - } - - private void ShowSuccessToast() - { - if (Settings.AreSuccessNotificationsDisabled()) - return; - - try - { - _ = AppNotificationManager.Default.RemoveByTagAsync(Operation.Metadata.Identifier); - AppNotificationBuilder builder = new AppNotificationBuilder() - .SetScenario(AppNotificationScenario.Default) - .SetTag(Operation.Metadata.Identifier) - .AddText(Operation.Metadata.SuccessTitle) - .AddText(Operation.Metadata.SuccessMessage) - .AddArgument("action", NotificationArguments.Show); - AppNotification notification = builder.BuildNotification(); - notification.ExpiresOnReboot = true; - AppNotificationManager.Default.Show(notification); - } - catch (Exception ex) - { - Logger.Error("Failed to show toast notification"); - Logger.Error(ex); - } - } - - private void ShowErrorToast() - { - if (Settings.AreErrorNotificationsDisabled()) - return; - - try - { - _ = AppNotificationManager.Default.RemoveByTagAsync(Operation.Metadata.Identifier); - AppNotificationBuilder builder = new AppNotificationBuilder() - .SetScenario(AppNotificationScenario.Urgent) - .SetTag(Operation.Metadata.Identifier) - .AddText(Operation.Metadata.FailureTitle) - .AddText(Operation.Metadata.FailureMessage) - .AddArgument("action", NotificationArguments.Show); - AppNotification notification = builder.BuildNotification(); - notification.ExpiresOnReboot = true; - AppNotificationManager.Default.Show(notification); - } - catch (Exception ex) - { - Logger.Error("Failed to show toast notification"); - Logger.Error(ex); - } - } - - public List GetRetryOptions(Action callback) - { - var retryOptionsMenu = new List(); - - if (Operation is SourceOperation sourceOp && !sourceOp.ForceAsAdministrator) - { - var adminButton = new BetterMenuItem - { - Text = CoreTools.Translate("Retry as administrator"), - IconName = IconType.UAC, - }; - adminButton.Click += (_, _) => - { - callback(); - Operation.Retry(AbstractOperation.RetryMode.Retry_AsAdmin); - }; - retryOptionsMenu.Add(adminButton); - } - else if (Operation is PackageOperation packageOp) - { - if ( - !packageOp.Options.RunAsAdministrator - && packageOp.Package.Manager.Capabilities.CanRunAsAdmin - ) - { - var adminButton = new BetterMenuItem - { - Text = CoreTools.Translate("Retry as administrator"), - IconName = IconType.UAC, - }; - adminButton.Click += (_, _) => - { - callback(); - Operation.Retry(AbstractOperation.RetryMode.Retry_AsAdmin); - }; - retryOptionsMenu.Add(adminButton); - } - - if ( - !packageOp.Options.InteractiveInstallation - && packageOp.Package.Manager.Capabilities.CanRunInteractively - ) - { - var interactiveButton = new BetterMenuItem - { - Text = CoreTools.Translate("Retry interactively"), - IconName = IconType.Interactive, - }; - interactiveButton.Click += (_, _) => - { - callback(); - Operation.Retry(AbstractOperation.RetryMode.Retry_Interactive); - }; - retryOptionsMenu.Add(interactiveButton); - } - - if ( - !packageOp.Options.SkipHashCheck - && packageOp.Package.Manager.Capabilities.CanSkipIntegrityChecks - ) - { - var skiphashButton = new BetterMenuItem - { - Text = CoreTools.Translate("Retry skipping integrity checks"), - IconName = IconType.Checksum, - }; - skiphashButton.Click += (_, _) => - { - callback(); - Operation.Retry(AbstractOperation.RetryMode.Retry_SkipIntegrity); - }; - retryOptionsMenu.Add(skiphashButton); - } - - if ( - packageOp is UpdatePackageOperation - && packageOp.Status is OperationStatus.Failed or OperationStatus.Canceled - ) - { - retryOptionsMenu.Add(new MenuFlyoutSeparator()); - - var reinstall = new BetterMenuItem() - { - Text = CoreTools.Translate("Reinstall package"), - }; - reinstall.IconName = IconType.Download; - reinstall.Click += async (_, _) => - { - callback(); - this.Close(); - _ = MainApp.Operations.Install( - packageOp.Package, - TEL_InstallReferral.ALREADY_INSTALLED, - ignoreParallel: true - ); - }; - retryOptionsMenu.Add(reinstall); - - var uninstallReinstall = new BetterMenuItem() - { - Text = CoreTools.Translate("Uninstall package, then reinstall it"), - }; - uninstallReinstall.IconName = IconType.Undelete; - uninstallReinstall.Click += async (_, _) => - { - callback(); - this.Close(); - var op = await MainApp.Operations.Uninstall( - packageOp.Package, - ignoreParallel: true - ); - _ = MainApp.Operations.Install( - packageOp.Package, - TEL_InstallReferral.ALREADY_INSTALLED, - ignoreParallel: true, - req: op - ); - }; - retryOptionsMenu.Add(uninstallReinstall); - - retryOptionsMenu.Add(new MenuFlyoutSeparator()); - - var skipThisVersion = new BetterMenuItem() - { - Text = CoreTools.Translate("Skip this version"), - }; - skipThisVersion.IconName = IconType.Skip; - skipThisVersion.Click += async (_, _) => - { - callback(); - await packageOp.Package.AddToIgnoredUpdatesAsync( - packageOp.Package.NewVersionString - ); - UpgradablePackagesLoader.Instance.Remove(packageOp.Package); - Close(); - }; - retryOptionsMenu.Add(skipThisVersion); - - var ignoreUpdates = new BetterMenuItem() - { - Text = CoreTools.Translate("Ignore updates for this package"), - }; - ignoreUpdates.IconName = IconType.Pin; - ignoreUpdates.Click += async (_, _) => - { - callback(); - await packageOp.Package.AddToIgnoredUpdatesAsync(); - UpgradablePackagesLoader.Instance.Remove(packageOp.Package); - Close(); - }; - retryOptionsMenu.Add(ignoreUpdates); - } - } - - return retryOptionsMenu; - } - - public List GetOperationOptions() - { - var optionsMenu = new List(); - if (Operation is PackageOperation packageOp) - { - var details = new BetterMenuItem - { - Text = CoreTools.Translate("Package details"), - IconName = IconType.Info_Round, - IsEnabled = !packageOp.Package.Source.IsVirtualManager, - }; - details.Click += (_, _) => - { - _ = DialogHelper.ShowPackageDetails( - packageOp.Package, - OperationType.None, - TEL_InstallReferral.DIRECT_SEARCH - ); - }; - optionsMenu.Add(details); - - var installationSettings = new BetterMenuItem - { - Text = CoreTools.Translate("Installation options"), - IconName = IconType.Options, - IsEnabled = !packageOp.Package.Source.IsVirtualManager, - }; - installationSettings.Click += (_, _) => - { - _ = DialogHelper.ShowInstallatOptions_Continue( - packageOp.Package, - OperationType.None - ); - }; - optionsMenu.Add(installationSettings); - - string? location = packageOp.Package.Manager.DetailsHelper.GetInstallLocation( - packageOp.Package - ); - var openLocation = new BetterMenuItem - { - Text = CoreTools.Translate("Open install location"), - IconName = IconType.OpenFolder, - }; - openLocation.Click += (_, _) => CoreTools.Launch(location); - openLocation.IsEnabled = location is not null && Directory.Exists(location); - optionsMenu.Add(openLocation); - } - else if (Operation is DownloadOperation downloadOp) - { - var launchInstaller = new BetterMenuItem - { - Text = CoreTools.Translate("Open"), - IconName = IconType.Launch, - }; - launchInstaller.Click += (_, _) => CoreTools.Launch(downloadOp.DownloadLocation); - launchInstaller.IsEnabled = downloadOp.Status is OperationStatus.Succeeded; - optionsMenu.Add(launchInstaller); - - var showFileInExplorer = new BetterMenuItem - { - Text = CoreTools.Translate("Show in explorer"), - IconName = IconType.OpenFolder, - }; - showFileInExplorer.Click += (_, _) => - _ = CoreTools.ShowFileOnExplorer(downloadOp.DownloadLocation); - showFileInExplorer.IsEnabled = downloadOp.Status is OperationStatus.Succeeded; - optionsMenu.Add(showFileInExplorer); - } - - return optionsMenu; - } -} diff --git a/src/UniGetUI/Controls/PackageItemContainer.cs b/src/UniGetUI/Controls/PackageItemContainer.cs deleted file mode 100644 index 5d440ab1c3..0000000000 --- a/src/UniGetUI/Controls/PackageItemContainer.cs +++ /dev/null @@ -1,81 +0,0 @@ -using Microsoft.UI.Xaml.Automation; -using Microsoft.UI.Xaml.Automation.Peers; -using Microsoft.UI.Xaml.Automation.Provider; -using Microsoft.UI.Xaml.Controls; -using UniGetUI.PackageEngine.Interfaces; -using UniGetUI.PackageEngine.PackageClasses; - -namespace UniGetUI.Interface.Widgets -{ - public partial class PackageItemContainer : ItemContainer - { - public IPackage? Package { get; set; } - - private PackageWrapper _wrapper = null!; - public PackageWrapper Wrapper - { - get => _wrapper; - set - { - _wrapper?.PropertyChanged -= Wrapper_PropertyChanged; - _wrapper = value; - _wrapper?.PropertyChanged += Wrapper_PropertyChanged; - } - } - - private void Wrapper_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) - { - if (e.PropertyName == nameof(PackageWrapper.IsChecked)) - { - var peer = FrameworkElementAutomationPeer.FromElement(this) as PackageItemContainerAutomationPeer; - if (peer != null) - { - ToggleState oldState = !Wrapper.IsChecked ? ToggleState.On : ToggleState.Off; - ToggleState newState = Wrapper.IsChecked ? ToggleState.On : ToggleState.Off; - peer.RaiseToggleStatePropertyChanged(oldState, newState); - } - } - } - - protected override AutomationPeer OnCreateAutomationPeer() - { - return new PackageItemContainerAutomationPeer(this); - } - } - - public partial class PackageItemContainerAutomationPeer : FrameworkElementAutomationPeer, IToggleProvider - { - private readonly PackageItemContainer _owner; - - public PackageItemContainerAutomationPeer(PackageItemContainer owner) : base(owner) - { - _owner = owner; - } - - protected override AutomationControlType GetAutomationControlTypeCore() - { - return AutomationControlType.CheckBox; - } - - protected override object GetPatternCore(PatternInterface patternInterface) - { - if (patternInterface == PatternInterface.Toggle) - { - return this; - } - return base.GetPatternCore(patternInterface); - } - - public ToggleState ToggleState => (_owner.Wrapper != null && _owner.Wrapper.IsChecked) ? ToggleState.On : ToggleState.Off; - - public void Toggle() - { - _owner.Wrapper?.IsChecked = !_owner.Wrapper.IsChecked; - } - - public void RaiseToggleStatePropertyChanged(ToggleState oldValue, ToggleState newValue) - { - RaisePropertyChangedEvent(TogglePatternIdentifiers.ToggleStateProperty, oldValue, newValue); - } - } -} diff --git a/src/UniGetUI/Controls/PackageWrapper.cs b/src/UniGetUI/Controls/PackageWrapper.cs deleted file mode 100644 index 8761fc4f09..0000000000 --- a/src/UniGetUI/Controls/PackageWrapper.cs +++ /dev/null @@ -1,362 +0,0 @@ -using System.Collections.Concurrent; -using System.ComponentModel; -using System.Runtime.InteropServices; -using Microsoft.UI.Dispatching; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Media.Imaging; -using UniGetUI.Core.Classes; -using UniGetUI.Core.Logging; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; -using UniGetUI.Interface; -using UniGetUI.Interface.Enums; -using UniGetUI.PackageEngine.Interfaces; -using UniGetUI.PackageEngine.Managers.WingetManager; - -namespace UniGetUI.PackageEngine.PackageClasses -{ - /// - /// A wrapper for packages to be able to show in ItemCollections - /// - public partial class PackageWrapper : IIndexableListItem, INotifyPropertyChanged, IDisposable - { - private static readonly ConcurrentDictionary CachedPackageIcons = new(); - - public static void ResetIconCache() - { - CachedPackageIcons.Clear(); - } - - public bool IsChecked - { - get => Package.IsChecked; - set - { - Package.IsChecked = value; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsChecked))); - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CheckedStatus))); - _page.UpdatePackageCount(); - } - } - - public string CheckedStatus => IsChecked ? CoreTools.Translate("Checked") : CoreTools.Translate("Unchecked"); - - public bool IconWasLoaded; - public bool AlternateIdIconVisible; - public bool ShowCustomPackageIcon; - public bool ShowDefaultPackageIcon = true; - public string VersionComboString; - public IconType MainIconId = IconType.Id; - public IconType AlternateIconId = IconType.Id; - public ImageSource? MainIconSource; - - public Uri? PackageIcon - { - set - { - CachedPackageIcons[Package.GetHash()] = value; - UpdatePackageIcon(); - } - } - - public string ListedNameTooltip = ""; - public readonly string ExtendedTooltip = ""; - public float ListedOpacity = 1.0f; - - public bool InstallerHostChanged { get; private set; } - public string InstallerHostChangeTooltip { get; private set; } = ""; - - public bool ShowUpgradeDownloadIcon => Package.IsUpgradable && !InstallerHostChanged; - - private CancellationTokenSource? _installerHostCheckCts; - - public int NewVersionLabelWidth - { - get => Package.IsUpgradable ? 125 : 0; - } - public int NewVersionIconWidth - { - get => Package.IsUpgradable ? 24 : 0; - } - - public int Index { get; set; } - public event PropertyChangedEventHandler? PropertyChanged; - - public IPackage Package { get; private set; } - public PackageWrapper Self { get; private set; } - - private readonly AbstractPackagesPage _page; - - public PackageWrapper(IPackage package, AbstractPackagesPage page) - { - Package = package; - Self = this; - _page = page; - WhenTagHasChanged(); - Package.PropertyChanged += Package_PropertyChanged; - UpdatePackageIcon(); - VersionComboString = package.IsUpgradable - ? $"{package.VersionString} -> {package.NewVersionString}" - : package.VersionString; - - if (package.Name.ToLower() != package.Id.ToLower()) - ExtendedTooltip = - $"{package.Name} ({package.Id} from {package.Source.AsString_DisplayName})"; - else - ExtendedTooltip = $"{package.Name} (from {package.Source.AsString_DisplayName})"; - - MaybeStartInstallerHostCheck(); - } - - /// - /// For upgradable WinGet packages, asynchronously fetches the installer URL host for - /// both the installed and the new version, and flags the row when the hosts differ. - /// See issue #4617 — defense-in-depth signal that an upgrade may be redirecting the - /// download to a different domain than the user originally trusted. - /// - private void MaybeStartInstallerHostCheck() - { - if (!Package.IsUpgradable) return; - if (Package.Manager is not WinGet) return; - if (Settings.Get(Settings.K.DisableInstallerHostChangeWarning)) return; - - string installedVersion = Package.VersionString; - string newVersion = Package.NewVersionString; - if (string.IsNullOrWhiteSpace(installedVersion) || string.IsNullOrWhiteSpace(newVersion)) - return; - if (installedVersion == newVersion) return; - - DispatcherQueue? dispatcher = _page.DispatcherQueue - ?? DispatcherQueue.GetForCurrentThread(); - if (dispatcher is null) return; - - _installerHostCheckCts?.Cancel(); - _installerHostCheckCts = new CancellationTokenSource(); - CancellationToken token = _installerHostCheckCts.Token; - - Task.Run(() => - { - try - { - if (token.IsCancellationRequested) return; - var oldHosts = WinGet.TryGetInstallerHostsForVersion(Package, installedVersion); - if (token.IsCancellationRequested) return; - var newHosts = WinGet.TryGetInstallerHostsForVersion(Package, newVersion); - if (token.IsCancellationRequested) return; - - if (oldHosts is null || newHosts is null) return; - // Only flag when the two host sets are fully disjoint. If they share even - // one host, the publisher hasn't moved hosting — adding/removing CDN mirrors - // or architectures shouldn't trigger the warning. - if (oldHosts.Overlaps(newHosts)) return; - - string tooltip = CoreTools.Translate( - "Installer host changed since the installed version.\n" - + "Old: {0}\n" - + "New: {1}\n\n" - + "This is usually harmless (the publisher moved hosting), " - + "but can also indicate a hijacked package manifest. " - + "Verify the new source before upgrading.", - string.Join(", ", oldHosts), - string.Join(", ", newHosts) - ); - - dispatcher.TryEnqueue(() => - { - if (token.IsCancellationRequested) return; - InstallerHostChanged = true; - InstallerHostChangeTooltip = tooltip; - PropertyChanged?.Invoke( - this, - new PropertyChangedEventArgs(nameof(InstallerHostChanged)) - ); - PropertyChanged?.Invoke( - this, - new PropertyChangedEventArgs(nameof(InstallerHostChangeTooltip)) - ); - PropertyChanged?.Invoke( - this, - new PropertyChangedEventArgs(nameof(ShowUpgradeDownloadIcon)) - ); - }); - } - catch (Exception ex) - { - Logger.Warn( - $"Installer-host check failed for {Package.Id}: {ex.Message}" - ); - } - }, token); - } - - public void PackageItemContainer_DoubleTapped( - object sender, - DoubleTappedRoutedEventArgs e - ) => _page.PackageItemContainer_DoubleTapped(sender, e); - - public void PackageItemContainer_PreviewKeyDown(object sender, KeyRoutedEventArgs e) => - _page.PackageItemContainer_PreviewKeyDown(sender, e); - - public void PackageItemContainer_RightTapped(object sender, RightTappedRoutedEventArgs e) => - _page.PackageItemContainer_RightTapped(sender, e); - - public void RightClick() => _ = RightClickAsync(); - - private async Task RightClickAsync() - { - await _page.ShowContextMenu(this); - } - - public void Package_PropertyChanged(object? sender, PropertyChangedEventArgs e) - { - try - { - if (e.PropertyName == nameof(Package.Tag)) - { - WhenTagHasChanged(); - PropertyChanged?.Invoke( - this, - new PropertyChangedEventArgs(nameof(ListedOpacity)) - ); - PropertyChanged?.Invoke( - this, - new PropertyChangedEventArgs(nameof(AlternateIconId)) - ); - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MainIconId))); - PropertyChanged?.Invoke( - this, - new PropertyChangedEventArgs(nameof(AlternateIdIconVisible)) - ); - PropertyChanged?.Invoke( - this, - new PropertyChangedEventArgs(nameof(ListedNameTooltip)) - ); - } - else if (e.PropertyName == nameof(Package.IsChecked)) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsChecked))); - } - else - { - PropertyChanged?.Invoke(this, e); - } - } - catch (COMException) - { - // ignore - } - } - - public void Dispose() - { - Package.PropertyChanged -= Package_PropertyChanged; - _installerHostCheckCts?.Cancel(); - _installerHostCheckCts?.Dispose(); - _installerHostCheckCts = null; - } - - /// - /// Updates the fields that change how the item template is rendered. - /// - public void WhenTagHasChanged() - { - MainIconId = Package.Tag switch - { - PackageTag.Default => IconType.Id, - PackageTag.AlreadyInstalled => IconType.Installed, - PackageTag.IsUpgradable => IconType.Upgradable, - PackageTag.Pinned => IconType.Pin, - PackageTag.OnQueue => IconType.SandClock, - PackageTag.BeingProcessed => IconType.Loading, - PackageTag.Failed => IconType.Warning, - PackageTag.Unavailable => IconType.Help, - _ => throw new ArgumentException($"Unknown tag {Package.Tag}"), - }; - - AlternateIconId = Package.Tag switch - { - PackageTag.Default => IconType.Empty, - PackageTag.AlreadyInstalled => IconType.Installed_Filled, - PackageTag.IsUpgradable => IconType.Upgradable_Filled, - PackageTag.Pinned => IconType.Pin_Filled, - PackageTag.OnQueue => IconType.Empty, - PackageTag.BeingProcessed => IconType.Loading_Filled, - PackageTag.Failed => IconType.Warning_Filled, - PackageTag.Unavailable => IconType.Empty, - _ => throw new ArgumentException($"Unknown tag {Package.Tag}"), - }; - AlternateIdIconVisible = AlternateIconId != IconType.Empty; - - ListedNameTooltip = - Package.Tag switch - { - PackageTag.Default => "", - PackageTag.AlreadyInstalled => CoreTools.Translate( - "This package is already installed" - ) + " - ", - PackageTag.IsUpgradable => CoreTools.Translate( - "This package can be upgraded to version {0}", - Package.GetUpgradablePackage()?.NewVersionString ?? "-1" - ) + " - ", - PackageTag.Pinned => CoreTools.Translate("Updates for this package are ignored") - + " - ", - PackageTag.OnQueue => CoreTools.Translate( - "This package is on the queue" + " - " - ), - PackageTag.BeingProcessed => CoreTools.Translate( - "This package is being processed" - ) + " - ", - PackageTag.Failed => CoreTools.Translate( - "An error occurred while processing this package" - ) + " - ", - PackageTag.Unavailable => CoreTools.Translate("This package is not available") - + " - ", - _ => throw new ArgumentException($"Unknown tag {Package.Tag}"), - } + Package.Name; - - ListedOpacity = Package.Tag switch - { - PackageTag.Default => 1, - PackageTag.AlreadyInstalled => 1, - PackageTag.IsUpgradable => 1, - PackageTag.Pinned => 1, - PackageTag.OnQueue => .5F, - PackageTag.BeingProcessed => .5F, - PackageTag.Failed => 1, - PackageTag.Unavailable => .5F, - _ => throw new ArgumentException($"Unknown tag {Package.Tag}"), - }; -#pragma warning restore CS8524 - } - - public void UpdatePackageIcon() - { - if (CachedPackageIcons.TryGetValue(Package.GetHash(), out Uri? icon)) - { - MainIconSource = new BitmapImage - { - UriSource = icon, - DecodePixelWidth = 64, - DecodePixelType = DecodePixelType.Logical, - }; - ShowCustomPackageIcon = true; - ShowDefaultPackageIcon = false; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MainIconSource))); - } - else - { - ShowCustomPackageIcon = false; - ShowDefaultPackageIcon = true; - } - PropertyChanged?.Invoke( - this, - new PropertyChangedEventArgs(nameof(ShowCustomPackageIcon)) - ); - PropertyChanged?.Invoke( - this, - new PropertyChangedEventArgs(nameof(ShowDefaultPackageIcon)) - ); - } - } -} diff --git a/src/UniGetUI/Controls/SettingsWidgets/ButtonCard.cs b/src/UniGetUI/Controls/SettingsWidgets/ButtonCard.cs deleted file mode 100644 index 55cfbb012d..0000000000 --- a/src/UniGetUI/Controls/SettingsWidgets/ButtonCard.cs +++ /dev/null @@ -1,49 +0,0 @@ -using CommunityToolkit.WinUI.Controls; -using Microsoft.UI.Xaml.Controls; -using UniGetUI.Core.Tools; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Interface.Widgets -{ - public sealed partial class ButtonCard : SettingsCard - { - private readonly Button _button = new(); - - public string ButtonText - { - set => _button.Content = CoreTools.Translate(value); - } - - private string _text = ""; - public string Text - { - set - { - _text = CoreTools.Translate(value); - Header = _text; - Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _text); - Microsoft.UI.Xaml.Automation.AutomationProperties.SetLocalizedControlType(this, "grouping"); - } - } - - public new event EventHandler? Click; - - public ButtonCard() - { - _button.MinWidth = 200; - _button.Click += (_, _) => - { - Click?.Invoke(this, EventArgs.Empty); - }; - Content = _button; - - Loaded += (s, e) => - { - Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _text); - Microsoft.UI.Xaml.Automation.AutomationProperties.SetLocalizedControlType(this, "grouping"); - }; - } - } -} diff --git a/src/UniGetUI/Controls/SettingsWidgets/CheckboxButtonCard.cs b/src/UniGetUI/Controls/SettingsWidgets/CheckboxButtonCard.cs deleted file mode 100644 index d9357dc029..0000000000 --- a/src/UniGetUI/Controls/SettingsWidgets/CheckboxButtonCard.cs +++ /dev/null @@ -1,110 +0,0 @@ -using CommunityToolkit.WinUI.Controls; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Media; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; -using Windows.UI.Text; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Interface.Widgets -{ - public sealed partial class CheckboxButtonCard : SettingsCard - { - public ToggleSwitch _checkbox; - public TextBlock _textblock; - public ButtonBase Button; - private bool IS_INVERTED; - - private Settings.K setting_name = Settings.K.Unset; - public Settings.K SettingName - { - set - { - setting_name = value; - IS_INVERTED = Settings.ResolveKey(value).StartsWith("Disable"); - _checkbox.IsOn = Settings.Get(setting_name) ^ IS_INVERTED ^ ForceInversion; - _textblock.Opacity = _checkbox.IsOn ? 1 : 0.7; - Button.IsEnabled = (_checkbox.IsOn) || _buttonAlwaysOn; - } - } - - public bool ForceInversion { get; set; } - - public bool Checked - { - get => _checkbox.IsOn; - } - public event EventHandler? StateChanged; - public new event EventHandler? Click; - - public string CheckboxText - { - set - { - _textblock.Text = CoreTools.Translate(value); - Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _textblock.Text); - Microsoft.UI.Xaml.Automation.AutomationProperties.SetLocalizedControlType(this, "grouping"); - Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(_checkbox, _textblock.Text); - } - } - - public string ButtonText - { - set => Button.Content = CoreTools.Translate(value); - } - - private bool _buttonAlwaysOn; - public bool ButtonAlwaysOn - { - set - { - _buttonAlwaysOn = value; - Button.IsEnabled = (_checkbox.IsOn) || _buttonAlwaysOn; - } - } - - public CheckboxButtonCard() - { - Button = new Button() { Margin = new Thickness(0, 8, 0, 0) }; - _checkbox = new ToggleSwitch() - { - Margin = new Thickness(0, 0, 8, 0), - OnContent = new TextBlock() { Text = CoreTools.Translate("Enabled") }, - OffContent = new TextBlock() { Text = CoreTools.Translate("Disabled") }, - }; - _textblock = new TextBlock() - { - Margin = new Thickness(2, 0, 0, 0), - VerticalAlignment = VerticalAlignment.Center, - TextWrapping = TextWrapping.Wrap, - Style = (Style)Application.Current.Resources["BaseTextBlockStyle"], - FontWeight = new FontWeight(450), - Foreground = (SolidColorBrush)Application.Current.Resources["ButtonForeground"], - }; - IS_INVERTED = false; - - Content = _checkbox; - Header = _textblock; - Description = Button; - _checkbox.Toggled += (_, _) => - { - Settings.Set(setting_name, _checkbox.IsOn ^ IS_INVERTED ^ ForceInversion); - StateChanged?.Invoke(this, EventArgs.Empty); - Button.IsEnabled = _checkbox.IsOn ? true : _buttonAlwaysOn; - _textblock.Opacity = _checkbox.IsOn ? 1 : 0.7; - }; - - Button.Click += (s, e) => Click?.Invoke(s, e); - - Loaded += (s, e) => - { - Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _textblock.Text); - Microsoft.UI.Xaml.Automation.AutomationProperties.SetLocalizedControlType(this, "grouping"); - }; - } - } -} diff --git a/src/UniGetUI/Controls/SettingsWidgets/CheckboxCard.cs b/src/UniGetUI/Controls/SettingsWidgets/CheckboxCard.cs deleted file mode 100644 index 5384a51502..0000000000 --- a/src/UniGetUI/Controls/SettingsWidgets/CheckboxCard.cs +++ /dev/null @@ -1,179 +0,0 @@ -using CommunityToolkit.WinUI.Controls; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Media; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Interface.Widgets -{ - public partial class CheckboxCard : SettingsCard - { - public ToggleSwitch _checkbox; - public TextBlock _textblock; - public TextBlock _warningBlock; - protected bool IS_INVERTED; - - private Settings.K setting_name = Settings.K.Unset; - public Settings.K SettingName - { - set - { - setting_name = value; - IS_INVERTED = Settings.ResolveKey(value).StartsWith("Disable"); - _checkbox.IsOn = Settings.Get(setting_name) ^ IS_INVERTED ^ ForceInversion; - _textblock.Opacity = _checkbox.IsOn ? 1 : 0.7; - } - } - - public bool ForceInversion { get; set; } - - public bool Checked - { - get => _checkbox.IsOn; - } - public virtual event EventHandler? StateChanged; - - public string Text - { - set - { - _textblock.Text = CoreTools.Translate(value); - Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _textblock.Text); - Microsoft.UI.Xaml.Automation.AutomationProperties.SetLocalizedControlType(this, "grouping"); - Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(_checkbox, _textblock.Text); - } - } - - public string WarningText - { - set - { - _warningBlock.Text = CoreTools.Translate(value); - _warningBlock.Visibility = value.Any() ? Visibility.Visible : Visibility.Collapsed; - } - } - - public Brush WarningForeground - { - set => _warningBlock.Foreground = value; - } - - public double WarningOpacity - { - set => _warningBlock.Opacity = value; - } - - public CheckboxCard() - { - _checkbox = new ToggleSwitch() - { - Margin = new Thickness(0, 0, 8, 0), - OnContent = new TextBlock() { Text = CoreTools.Translate("Enabled") }, - OffContent = new TextBlock() { Text = CoreTools.Translate("Disabled") }, - }; - _textblock = new TextBlock() - { - VerticalAlignment = VerticalAlignment.Center, - Margin = new Thickness(0, 0, 0, 0), - TextWrapping = TextWrapping.Wrap, - }; - _warningBlock = new TextBlock() - { - VerticalAlignment = VerticalAlignment.Center, - Margin = new Thickness(0, 0, 0, 0), - TextWrapping = TextWrapping.Wrap, - FontSize = 12, - Opacity = 0.7, - Visibility = Visibility.Collapsed, - }; - IS_INVERTED = false; - Content = _checkbox; - Header = new StackPanel() - { - Spacing = 4, - Orientation = Orientation.Vertical, - Children = { _textblock, _warningBlock }, - }; - - _checkbox.HorizontalAlignment = HorizontalAlignment.Stretch; - _checkbox.Toggled += _checkbox_Toggled; - - Loaded += (s, e) => - { - Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _textblock.Text); - Microsoft.UI.Xaml.Automation.AutomationProperties.SetLocalizedControlType(this, "grouping"); - }; - } - - protected virtual void _checkbox_Toggled(object sender, RoutedEventArgs e) - { - Settings.Set(setting_name, _checkbox.IsOn ^ IS_INVERTED ^ ForceInversion); - StateChanged?.Invoke(this, EventArgs.Empty); - _textblock.Opacity = _checkbox.IsOn ? 1 : 0.7; - } - } - - public partial class CheckboxCard_Dict : CheckboxCard - { - public override event EventHandler? StateChanged; - - private Settings.K _dictName = Settings.K.Unset; - private bool _disableStateChangedEvent; - - private string _keyName = ""; - public string KeyName - { - set - { - _keyName = value; - if (_dictName != Settings.K.Unset && _keyName.Any()) - { - _disableStateChangedEvent = true; - _checkbox.IsOn = - Settings.GetDictionaryItem(_dictName, _keyName) - ^ IS_INVERTED - ^ ForceInversion; - _textblock.Opacity = _checkbox.IsOn ? 1 : 0.7; - _disableStateChangedEvent = false; - } - } - } - - public Settings.K DictionaryName - { - set - { - _dictName = value; - IS_INVERTED = Settings.ResolveKey(value).StartsWith("Disable"); - if (_dictName != Settings.K.Unset && _keyName.Any()) - { - _checkbox.IsOn = - Settings.GetDictionaryItem(_dictName, _keyName) - ^ IS_INVERTED - ^ ForceInversion; - _textblock.Opacity = _checkbox.IsOn ? 1 : 0.7; - } - } - } - - public CheckboxCard_Dict() - : base() { } - - protected override void _checkbox_Toggled(object sender, RoutedEventArgs e) - { - if (_disableStateChangedEvent) - return; - Settings.SetDictionaryItem( - _dictName, - _keyName, - _checkbox.IsOn ^ IS_INVERTED ^ ForceInversion - ); - StateChanged?.Invoke(this, EventArgs.Empty); - _textblock.Opacity = _checkbox.IsOn ? 1 : 0.7; - } - } -} diff --git a/src/UniGetUI/Controls/SettingsWidgets/ComboboxCard.cs b/src/UniGetUI/Controls/SettingsWidgets/ComboboxCard.cs deleted file mode 100644 index 97a75feb6d..0000000000 --- a/src/UniGetUI/Controls/SettingsWidgets/ComboboxCard.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System.Collections.ObjectModel; -using CommunityToolkit.WinUI.Controls; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Data; -using UniGetUI.Core.Logging; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Interface.Widgets -{ - public sealed partial class ComboboxCard : SettingsCard - { - private readonly ComboBox _combobox = new(); - private readonly ObservableCollection _elements = []; - private readonly Dictionary _values_ref = []; - private readonly Dictionary _inverted_val_ref = []; - - private Settings.K settings_name = Settings.K.Unset; - public Settings.K SettingName - { - set { settings_name = value; } - } - - public string Text - { - set => Header = CoreTools.Translate(value); - } - - public event EventHandler? ValueChanged; - - public ComboboxCard() - { - _combobox.MinWidth = 200; - _combobox.SetBinding( - ItemsControl.ItemsSourceProperty, - new Binding { Source = _elements } - ); - Content = _combobox; - } - - public void AddItem(string name, string value) - { - AddItem(name, value, true); - } - - public void AddItem(string name, string value, bool translate) - { - if (translate) - { - name = CoreTools.Translate(name); - } - - _elements.Add(name); - _values_ref.Add(name, value); - _inverted_val_ref.Add(value, name); - } - - public void ShowAddedItems() - { - try - { - string savedItem = Settings.GetValue(settings_name); - _combobox.SelectedIndex = _elements.IndexOf(_inverted_val_ref[savedItem]); - } - catch - { - _combobox.SelectedIndex = 0; - } - _combobox.SelectionChanged += (_, _) => - { - try - { - Settings.SetValue( - settings_name, - _values_ref[_combobox.SelectedItem?.ToString() ?? ""] - ); - ValueChanged?.Invoke(this, EventArgs.Empty); - } - catch (Exception ex) - { - Logger.Warn(ex); - } - }; - } - - public string SelectedValue() => - _combobox.SelectedValue.ToString() ?? throw new InvalidCastException(); - - public void SelectIndex(int index) => _combobox.SelectedIndex = index; - } -} diff --git a/src/UniGetUI/Controls/SettingsWidgets/SecureCheckboxCard.cs b/src/UniGetUI/Controls/SettingsWidgets/SecureCheckboxCard.cs deleted file mode 100644 index ce0b7bc75f..0000000000 --- a/src/UniGetUI/Controls/SettingsWidgets/SecureCheckboxCard.cs +++ /dev/null @@ -1,157 +0,0 @@ -using CommunityToolkit.WinUI.Controls; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Media; -using UniGetUI.Core.Logging; -using UniGetUI.Core.SettingsEngine.SecureSettings; -using UniGetUI.Core.Tools; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Interface.Widgets -{ - public partial class SecureCheckboxCard : SettingsCard - { - public ToggleSwitch _checkbox; - public TextBlock _textblock; - public TextBlock _warningBlock; - public ProgressRing _loading; - private bool IS_INVERTED; - - private SecureSettings.K setting_name = SecureSettings.K.Unset; - public SecureSettings.K SettingName - { - set - { - _checkbox.IsEnabled = false; - setting_name = value; - IS_INVERTED = SecureSettings.ResolveKey(value).StartsWith("Disable"); - _checkbox.IsOn = SecureSettings.Get(setting_name) ^ IS_INVERTED ^ ForceInversion; - _textblock.Opacity = _checkbox.IsOn ? 1 : 0.7; - _checkbox.IsEnabled = true; - } - } - - public new bool IsEnabled - { - set - { - base.IsEnabled = value; - _warningBlock.Opacity = value ? 1 : 0.2; - } - get => base.IsEnabled; - } - - public bool ForceInversion { get; set; } - - public bool Checked - { - get => _checkbox.IsOn; - } - public virtual event EventHandler? StateChanged; - - public string Text - { - set - { - _textblock.Text = CoreTools.Translate(value); - Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _textblock.Text); - Microsoft.UI.Xaml.Automation.AutomationProperties.SetLocalizedControlType(this, "grouping"); - Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(_checkbox, _textblock.Text); - } - } - - public string WarningText - { - set - { - _warningBlock.Text = CoreTools.Translate(value); - _warningBlock.Visibility = value.Any() ? Visibility.Visible : Visibility.Collapsed; - } - } - - public SecureCheckboxCard() - { - _checkbox = new ToggleSwitch() - { - Margin = new Thickness(0, 0, 8, 0), - OnContent = new TextBlock() { Text = CoreTools.Translate("Enabled") }, - OffContent = new TextBlock() { Text = CoreTools.Translate("Disabled") }, - }; - - _loading = new ProgressRing() - { - IsIndeterminate = true, - Visibility = Visibility.Collapsed, - }; - _textblock = new TextBlock() - { - VerticalAlignment = VerticalAlignment.Center, - Margin = new Thickness(0, 0, 0, 0), - TextWrapping = TextWrapping.Wrap, - }; - _warningBlock = new TextBlock() - { - VerticalAlignment = VerticalAlignment.Center, - Margin = new Thickness(0, 0, 0, 0), - TextWrapping = TextWrapping.Wrap, - Foreground = (SolidColorBrush) - Application.Current.Resources["SystemControlErrorTextForegroundBrush"], - FontSize = 12, - Visibility = Visibility.Collapsed, - }; - IS_INVERTED = false; - Content = new StackPanel() - { - Spacing = 4, - Orientation = Orientation.Horizontal, - Children = { _loading, _checkbox }, - }; - //Header = _textblock; - Header = new StackPanel() - { - Spacing = 4, - Orientation = Orientation.Vertical, - Children = { _textblock, _warningBlock }, - }; - - _checkbox.HorizontalAlignment = HorizontalAlignment.Stretch; - _checkbox.Toggled += (s, e) => _ = _checkbox_Toggled(); - - Loaded += (s, e) => - { - Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _textblock.Text); - Microsoft.UI.Xaml.Automation.AutomationProperties.SetLocalizedControlType(this, "grouping"); - }; - } - - protected virtual async Task _checkbox_Toggled() - { - try - { - if (_checkbox.IsEnabled is false) - return; - - _loading.Visibility = Visibility.Visible; - _checkbox.IsEnabled = false; - await SecureSettings.TrySet( - setting_name, - _checkbox.IsOn ^ IS_INVERTED ^ ForceInversion - ); - StateChanged?.Invoke(this, EventArgs.Empty); - _textblock.Opacity = _checkbox.IsOn ? 1 : 0.7; - _checkbox.IsOn = SecureSettings.Get(setting_name) ^ IS_INVERTED ^ ForceInversion; - _loading.Visibility = Visibility.Collapsed; - _checkbox.IsEnabled = true; - } - catch (Exception ex) - { - Logger.Warn(ex); - _checkbox.IsOn = SecureSettings.Get(setting_name) ^ IS_INVERTED ^ ForceInversion; - _loading.Visibility = Visibility.Collapsed; - _checkbox.IsEnabled = true; - } - } - } -} diff --git a/src/UniGetUI/Controls/SettingsWidgets/SettingsPageButton.cs b/src/UniGetUI/Controls/SettingsWidgets/SettingsPageButton.cs deleted file mode 100644 index 9280c59168..0000000000 --- a/src/UniGetUI/Controls/SettingsWidgets/SettingsPageButton.cs +++ /dev/null @@ -1,55 +0,0 @@ -using CommunityToolkit.WinUI.Controls; -using Microsoft.UI.Xaml; -using UniGetUI.Core.Tools; -using UniGetUI.Interface.Enums; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Interface.Widgets -{ - public partial class SettingsPageButton : SettingsCard - { - private string _text = ""; - public string Text - { - set - { - _text = CoreTools.Translate(value); - Header = _text; - Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _text); - } - } - - private string _underText = ""; - public string UnderText - { - set - { - _underText = CoreTools.Translate(value); - Description = _underText; - Microsoft.UI.Xaml.Automation.AutomationProperties.SetHelpText(this, _underText); - } - } - - public IconType Icon - { - set => HeaderIcon = new LocalIcon(value); - } - - public SettingsPageButton() - { - CornerRadius = new CornerRadius(8); - HorizontalAlignment = HorizontalAlignment.Stretch; - IsClickEnabled = true; - - Loaded += (s, e) => - { - if (!string.IsNullOrEmpty(_text)) - Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _text); - if (!string.IsNullOrEmpty(_underText)) - Microsoft.UI.Xaml.Automation.AutomationProperties.SetHelpText(this, _underText); - }; - } - } -} diff --git a/src/UniGetUI/Controls/SettingsWidgets/TextboxCard.cs b/src/UniGetUI/Controls/SettingsWidgets/TextboxCard.cs deleted file mode 100644 index 6167e7966d..0000000000 --- a/src/UniGetUI/Controls/SettingsWidgets/TextboxCard.cs +++ /dev/null @@ -1,84 +0,0 @@ -using CommunityToolkit.WinUI.Controls; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Interface.Widgets -{ - public sealed partial class TextboxCard : SettingsCard - { - private readonly TextBox _textbox; - private readonly HyperlinkButton _helpbutton; - - private Settings.K setting_name = Settings.K.Unset; - public Settings.K SettingName - { - set - { - setting_name = value; - _textbox.Text = Settings.GetValue(setting_name); - _textbox.TextChanged += (_, _) => SaveValue(); - } - } - - public string Placeholder - { - set => _textbox.PlaceholderText = CoreTools.Translate(value); - } - - public string Text - { - set => Header = CoreTools.Translate(value); - } - - public Uri HelpUrl - { - set - { - _helpbutton.NavigateUri = value; - _helpbutton.Visibility = Visibility.Visible; - _helpbutton.Content = CoreTools.Translate("More info"); - } - } - - public event EventHandler? ValueChanged; - - public TextboxCard() - { - _helpbutton = new HyperlinkButton { Visibility = Visibility.Collapsed }; - - _textbox = new TextBox { MinWidth = 200, MaxWidth = 300 }; - - StackPanel s = new() { Orientation = Orientation.Horizontal }; - s.Children.Add(_helpbutton); - s.Children.Add(_textbox); - - Content = s; - } - - public void SaveValue() - { - string SanitizedText = _textbox.Text; - - if (Settings.ResolveKey(setting_name).Contains("File")) - { - SanitizedText = CoreTools.MakeValidFileName(SanitizedText); - } - - if (SanitizedText != "") - { - Settings.SetValue(setting_name, SanitizedText); - } - else - { - Settings.Set(setting_name, false); - } - - ValueChanged?.Invoke(this, EventArgs.Empty); - } - } -} diff --git a/src/UniGetUI/Controls/SourceManager.xaml b/src/UniGetUI/Controls/SourceManager.xaml deleted file mode 100644 index d759b3461d..0000000000 --- a/src/UniGetUI/Controls/SourceManager.xaml +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Controls/SourceManager.xaml.cs b/src/UniGetUI/Controls/SourceManager.xaml.cs deleted file mode 100644 index ffeaee3f9b..0000000000 --- a/src/UniGetUI/Controls/SourceManager.xaml.cs +++ /dev/null @@ -1,232 +0,0 @@ -using System.Collections.ObjectModel; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using UniGetUI.Controls.OperationWidgets; -using UniGetUI.Core.Logging; -using UniGetUI.Core.Tools; -using UniGetUI.PackageEngine.Classes.Manager; -using UniGetUI.PackageEngine.Interfaces; -using UniGetUI.PackageEngine.Operations; -using UniGetUI.Pages.DialogPages; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Interface.Widgets -{ - public class SourceItem - { - public SourceManager Parent; - public IManagerSource Source; - - public SourceItem(SourceManager Parent, IManagerSource Source) - { - this.Parent = Parent; - this.Source = Source; - } - - public void Remove(object sender, RoutedEventArgs e) - { - var op = MainApp.Operations.Add(new RemoveSourceOperation(Source)); - op.Operation.OperationSucceeded += (_, _) => - { - Parent.RemoveSourceItem(this); - }; - } - } - - public sealed partial class SourceManager : UserControl - { - private IPackageManager Manager { get; set; } - - // ReSharper disable once FieldCanBeMadeReadOnly.Local - private ObservableCollection Sources = []; - - private ListView _datagrid { get; set; } - - public SourceManager(IPackageManager Manager) - { - this.Manager = Manager; - InitializeComponent(); - - if (!Manager.Capabilities.SupportsCustomSources) - { - throw new InvalidOperationException( - $"Attempted to create a SourceManager class from Manager {Manager.Name}, which does not support custom sources" - ); - } - - Header.Text = CoreTools.Translate("Manage {0} sources", Manager.DisplayName); - AddSourceButton.Content = CoreTools.Translate("Add source"); - AddSourceButton.Click += async (sender, e) => - { - try - { - ContentDialog d = new() { Title = CoreTools.Translate("Add source") }; - - ComboBox SourcesCombo = new(); - Dictionary NameSourceRef = []; - foreach (IManagerSource source in Manager.Properties.KnownSources) - { - SourcesCombo.Items.Add(source.Name); - NameSourceRef.Add(source.Name, source); - } - - d.Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style; - StackPanel p = new() { Spacing = 8 }; - p.Children.Add( - new TextBlock - { - Text = CoreTools.Translate("Select the source you want to add:"), - } - ); - p.Children.Add(SourcesCombo); - - TextBox SourceNameTextBox = new() - { - HorizontalAlignment = HorizontalAlignment.Stretch, - Width = 400, - }; - TextBox SourceUrlTextBox = new() - { - HorizontalAlignment = HorizontalAlignment.Stretch, - }; - - StackPanel p1 = new() - { - Spacing = 2, - HorizontalAlignment = HorizontalAlignment.Stretch, - }; - p1.Children.Add( - new TextBlock - { - Text = CoreTools.Translate("Source name:"), - VerticalAlignment = VerticalAlignment.Center, - } - ); - p1.Children.Add(SourceNameTextBox); - - StackPanel p2 = new() - { - Spacing = 2, - HorizontalAlignment = HorizontalAlignment.Stretch, - }; - p2.Children.Add( - new TextBlock - { - Text = CoreTools.Translate("Source URL:"), - VerticalAlignment = VerticalAlignment.Center, - } - ); - p2.Children.Add(SourceUrlTextBox); - - p.Children.Add(p1); - p.Children.Add(p2); - - SourcesCombo.Items.Add(CoreTools.Translate("Other")); - SourcesCombo.HorizontalAlignment = HorizontalAlignment.Stretch; - SourcesCombo.SelectionChanged += (_, _) => - { - if (SourcesCombo.SelectedValue.ToString() == CoreTools.Translate("Other")) - { - SourceUrlTextBox.IsEnabled = SourceNameTextBox.IsEnabled = true; - SourceUrlTextBox.Text = SourceNameTextBox.Text = ""; - } - else - { - string? sourceName = SourcesCombo.SelectedValue.ToString(); - if (sourceName is not null) - { - SourceUrlTextBox.IsEnabled = SourceNameTextBox.IsEnabled = false; - SourceUrlTextBox.Text = NameSourceRef[sourceName].Url.ToString(); - SourceNameTextBox.Text = NameSourceRef[sourceName].Name; - } - else - { - Logger.Warn( - "SourcesCombo.SelectedValue.ToString() was null on SourceManager.SourceManager" - ); - } - } - }; - SourcesCombo.SelectedIndex = 0; - - d.XamlRoot = XamlRoot; - d.Content = p; - d.PrimaryButtonText = CoreTools.Translate("Add"); - d.SecondaryButtonText = CoreTools.Translate("Cancel"); - d.DefaultButton = ContentDialogButton.Primary; - - if (await DialogHelper.ShowDialogAsync(d) == ContentDialogResult.Primary) - { - PackageOperations.AbstractOperation op; - if (CoreTools.Translate("Other") != SourcesCombo.SelectedValue.ToString()) - op = new AddSourceOperation( - NameSourceRef[SourcesCombo.SelectedValue.ToString() ?? ""] - ); - else - op = new AddSourceOperation( - new ManagerSource( - this.Manager, - SourceNameTextBox.Text, - new Uri(SourceUrlTextBox.Text) - ) - ); - - MainApp.Operations.Add(op); - op.OperationSucceeded += (_, _) => _ = LoadSources(); - } - } - catch (Exception ex) - { - ContentDialog d = new() - { - XamlRoot = XamlRoot, - Title = CoreTools.Translate("An error occurred"), - Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style, - Content = - CoreTools.Translate("An error occurred when adding the source: ") - + ex.Message, - }; - _ = DialogHelper.ShowDialogAsync(d, HighPriority: true); - d.PrimaryButtonText = CoreTools.Translate("Close"); - Logger.Error("An error occurred when adding the source"); - Logger.Error(ex); - } - }; - this.Manager = Manager; - _datagrid = DataList; - DataList.ItemTemplate = (DataTemplate)Resources["ManagerSourceTemplate"]; - _ = LoadSources(); - } - - public async Task LoadSources() - { - if (!Manager.IsReady()) - { - return; - } - - LoadingBar.Visibility = Visibility.Visible; - Sources.Clear(); - foreach (IManagerSource source in await Task.Run(Manager.SourcesHelper.GetSources)) - { - Sources.Add(new SourceItem(this, source)); - } - - if (Sources.Count > 0) - { - _datagrid.SelectedIndex = 0; - } - - LoadingBar.Visibility = Visibility.Collapsed; - } - - public void RemoveSourceItem(SourceItem Item) - { - Sources.Remove(Item); - } - - private void ReloadButton_Click(object sender, RoutedEventArgs e) => _ = LoadSources(); - } -} diff --git a/src/UniGetUI/Controls/TranslatedTextBlock.xaml b/src/UniGetUI/Controls/TranslatedTextBlock.xaml deleted file mode 100644 index aa8d47674f..0000000000 --- a/src/UniGetUI/Controls/TranslatedTextBlock.xaml +++ /dev/null @@ -1,21 +0,0 @@ - - - - diff --git a/src/UniGetUI/Controls/TranslatedTextBlock.xaml.cs b/src/UniGetUI/Controls/TranslatedTextBlock.xaml.cs deleted file mode 100644 index d71d3706f4..0000000000 --- a/src/UniGetUI/Controls/TranslatedTextBlock.xaml.cs +++ /dev/null @@ -1,75 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using UniGetUI.Core.Logging; -using UniGetUI.Core.Tools; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Interface.Widgets -{ - public sealed partial class TranslatedTextBlock : UserControl - { - public string __text = ""; - public string Text - { - set => ApplyText(value); - } - - public string __suffix = ""; - public string Suffix - { - set - { - __suffix = value; - ApplyText(null); - } - } - public string __prefix = ""; - public string Prefix - { - set - { - __prefix = value; - ApplyText(null); - } - } - - public TextWrapping WrappingMode - { - set => _textBlock.TextWrapping = value; - } - - public TranslatedTextBlock() - { - InitializeComponent(); - Loaded += (s, e) => - { - if (Parent is Microsoft.UI.Xaml.Controls.Primitives.ButtonBase parentBtn && string.IsNullOrEmpty(Microsoft.UI.Xaml.Automation.AutomationProperties.GetName(parentBtn))) - { - Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(parentBtn, _textBlock.Text); - } - }; - } - - public void ApplyText(string? text) - { - try - { - if (text is not null) - __text = CoreTools.Translate(text); - _textBlock.Text = __prefix + __text + __suffix; - Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _textBlock.Text); - - if (IsLoaded && Parent is Microsoft.UI.Xaml.Controls.Primitives.ButtonBase parentBtn) - { - Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(parentBtn, _textBlock.Text); - } - } - catch (Exception ex) - { - Logger.Error(ex); - } - } - } -} diff --git a/src/UniGetUI/CrashHandler.cs b/src/UniGetUI/CrashHandler.cs deleted file mode 100644 index b62a304428..0000000000 --- a/src/UniGetUI/CrashHandler.cs +++ /dev/null @@ -1,250 +0,0 @@ -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Text; -using UniGetUI.Core.Data; -using UniGetUI.Core.Logging; -using UniGetUI.Core.Tools; - -namespace UniGetUI; - -public static class CrashHandler -{ - public static readonly string PendingCrashFile = - Path.Combine(Path.GetTempPath(), "UniGetUI_pending_crash.txt"); - - private const uint MB_ICONSTOP = 0x00000010; - private const uint MB_OKCANCEL = 0x00000001; - private const uint MB_YESNOCANCEL = 0x00000003; - private const int IDOK = 1; - private const int IDYES = 6; - private const int IDNO = 7; - - [DllImport("user32.dll", CharSet = CharSet.Unicode)] - private static extern int MessageBox(IntPtr hWnd, string lpText, string lpCaption, uint uType); - - // ── Missing-files handler ───────────────────────────────────────────────── - private static void _reportMissingFiles(out bool showDetailedReport) - { - try - { - string installerPath = Path.Join( - CoreData.UniGetUIExecutableDirectory, - "UniGetUI.Installer.exe" - ); - bool canAutoRepair = File.Exists(installerPath); - - var title = "UniGetUI - Missing Files"; - - if (canAutoRepair) - { - var errorMessage = - "UniGetUI has detected that some required files are missing." - + "\n\nThis might be caused by an incomplete installation or corrupted files. Please reinstall UniGetUI." - + "\n\nPress YES to reinstall UniGetUI right now." - + "\nPress NO to close this prompt." - + "\nPress CANCEL to get more details about the crash."; - - var msgboxResult = MessageBox( - IntPtr.Zero, - errorMessage, - title, - MB_ICONSTOP | MB_YESNOCANCEL - ); - if (msgboxResult is IDYES) - { - Process.Start(installerPath, "/silent /NoDeployInstaller"); - } - - if (msgboxResult is IDYES or IDNO) - showDetailedReport = false; - else - showDetailedReport = true; // msgboxResult is IDCANCEL - } - else - { - var errorMessage = - "UniGetUI has detected that some required files are missing." - + "\n\nThis might be caused by an incomplete installation or corrupted files. Please reinstall UniGetUI." - + "\n\nPress OK to close this prompt." - + "\nPress CANCEL to get more details about the crash."; - - var msgboxResult = MessageBox( - IntPtr.Zero, - errorMessage, - title, - MB_ICONSTOP | MB_OKCANCEL - ); - if (msgboxResult is IDOK) - showDetailedReport = false; - else - showDetailedReport = true; // msgboxResult is IDCANCEL - } - } - catch - { - showDetailedReport = false; - } - } - - public static void ReportFatalException(Exception e) - { - Debugger.Break(); - - if (!Environment.GetCommandLineArgs().Contains(CLIHandler.NO_CORRUPT_DIALOG)) - { - Exception? fileEx = e; - while (fileEx is not null) - { - if ((uint)fileEx.HResult is 0x80070002 or 0x8007007E or 0x802B000A) - { - _reportMissingFiles(out bool showDetailedReport); - if (!showDetailedReport) - { - Environment.Exit(1); - } - } - fileEx = fileEx.InnerException; - } - } - - string LangName = "Unknown"; - try - { - LangName = CoreTools.GetCurrentLocale(); - } - catch - { - // ignored - } - - static string GetExceptionData(Exception e) - { - try - { - StringBuilder b = new(); - foreach (var key in e.Data.Keys) - { - b.AppendLine($"{key}: {e.Data[key]}"); - } - - string r = b.ToString(); - return r.Any() ? r : "No extra data was provided"; - } - catch (Exception ex) - { - return $"Failed to get exception Data with exception {ex.Message}"; - } - } - - // Run the integrity check on a background thread with a tight timeout. - // Running it synchronously on the UI thread can block for 20-30 s on slow - // disks, and calling Environment.Exit while the UI thread holds WinRT locks - // causes a native crash in coreclr!ProcessCLRException (null read @ 0x0). - string iReport; - try - { - var integrityTask = Task.Run(() => IntegrityTester.CheckIntegrity(false)); - if (integrityTask.Wait(TimeSpan.FromSeconds(5))) - { - iReport = IntegrityTester.GetReadableReport(integrityTask.Result); - } - else - { - iReport = "Integrity check timed out (> 5 s) — skipped in crash report"; - } - } - catch (Exception ex) - { - iReport = "Failed to compute integrity report: "; - iReport += ex.GetType() + ": " + ex.Message; - } - - string Error_String = $$""" - Environment details: - Windows version: {{Environment.OSVersion.VersionString}} - Language: {{LangName}} - APP Version: {{CoreData.VersionName}} - APP Build number: {{CoreData.BuildNumber}} - Executable: {{Environment.ProcessPath}} - Command-line arguments: {{Environment.CommandLine}} - - Integrity report: - {{iReport.Replace("\n", "\n ")}} - - Exception type: {{e.GetType()?.Name}} ({{e.GetType()}}) - Crash HResult: 0x{{(uint)e.HResult:X}} ({{(uint)e.HResult}}, {{e.HResult}}) - Crash Message: {{e.Message}} - - Crash Data: - {{GetExceptionData(e).Replace("\n", "\n ")}} - - Crash Trace: - {{e.StackTrace?.Replace("\n", "\n ")}} - """; - - Exception originalException = e; - - try - { - int i = 0; - while (e.InnerException is not null) - { - i++; - e = e.InnerException; - Error_String += - "\n\n\n\n" - + $$""" - ——————————————————————————————————————————————————————————— - Inner exception details (depth level: {{i}}) - Crash HResult: 0x{{(uint)e.HResult:X}} ({{(uint) - e.HResult}}, {{e.HResult}}) - Crash Message: {{e.Message}} - - Crash Data: - {{GetExceptionData(e).Replace("\n", "\n ")}} - - Crash Traceback: - {{e.StackTrace?.Replace("\n", "\n ")}} - """; - } - - if (i == 0) - { - Error_String += $"\n\n\nNo inner exceptions found"; - } - } - catch - { - // ignore - } - - // Authoritative fallback: ToString() recurses through every inner exception (and all of an - // AggregateException's inners) with their stack traces. The walk above only follows the single - // .InnerException chain, so it can drop the real cause of e.g. a TypeInitializationException. - try - { - Error_String += "\n\n\n———————————————————————————————————————————————————————————\n" - + "Full exception detail (ToString):\n" + originalException; - } - catch - { - // ignore - } - - Error_String = Logger.Redact(Error_String); - - Console.WriteLine(Error_String); - - // Persist crash data so the next normal app launch can show the report. - try - { - File.WriteAllText(PendingCrashFile, Error_String, Encoding.UTF8); - } - catch - { - // If we can't write the file, nothing more we can do — just exit. - } - - Environment.Exit(1); - } -} diff --git a/src/UniGetUI/CrashReportWindow.xaml b/src/UniGetUI/CrashReportWindow.xaml deleted file mode 100644 index 0ded6bb9ee..0000000000 --- a/src/UniGetUI/CrashReportWindow.xaml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/DialogPages/DesktopShortcuts.xaml.cs b/src/UniGetUI/Pages/DialogPages/DesktopShortcuts.xaml.cs deleted file mode 100644 index f6d2cfb0da..0000000000 --- a/src/UniGetUI/Pages/DialogPages/DesktopShortcuts.xaml.cs +++ /dev/null @@ -1,190 +0,0 @@ -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; -using UniGetUI.PackageEngine.Classes.Packages.Classes; -using UniGetUI.Pages.DialogPages; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Interface -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class DesktopShortcutsManager : Page - { - public event EventHandler? Close; - private readonly ObservableCollection Shortcuts = []; - - public DesktopShortcutsManager() - { - InitializeComponent(); - DeletableDesktopShortcutsList.ItemsSource = Shortcuts; - - AutoDeleteShortcutsCheckbox.IsChecked = Settings.Get( - Settings.K.RemoveAllDesktopShortcuts - ); - AutoDeleteShortcutsCheckbox.Checked += HandleAllDesktop_Checked; - AutoDeleteShortcutsCheckbox.Unchecked += HandleAllDesktop_Unchecked; - } - - public void LoadShortcuts(IReadOnlyList NewShortcuts) - { - Shortcuts.Clear(); - List items = new(); - foreach (var shortcut in NewShortcuts) - { - var status = DesktopShortcutsDatabase.GetStatus(shortcut); - var entry = new ShortcutEntry( - shortcut, - status is DesktopShortcutsDatabase.Status.Delete - ); - entry.OnReset += (_, _) => Shortcuts.Remove(entry); - items.Add(entry); - } - - foreach (var item in items.OrderBy(s => s.Name)) - { - Shortcuts.Add(item); - } - } - - /*private async void ManualScanButton_Click(object sender, RoutedEventArgs e) - { - SaveChanges(); - var shortcutsOnDesktop = DesktopShortcutsDatabase.GetShortcutsOnDisk(); - List UnknownShortcuts = new(); - - foreach (var shortcut in shortcutsOnDesktop) - { - if(DesktopShortcutsDatabase.GetStatus(shortcut) is DesktopShortcutsDatabase.Status.Unknown) - { - UnknownShortcuts.Add(shortcut); - } - } - if (UnknownShortcuts.Any()) - { - LoadShortcuts(UnknownShortcuts); - ManualScanFlyout.Hide(); - } - else - { - ManualScanFlyout.Hide(); - Close?.Invoke(this, EventArgs.Empty); - await DialogHelper.ManualScanDidNotFoundNewShortcuts(); - _ = DialogHelper.ManageDesktopShortcuts(); - } - }*/ - - private void CloseButton_Click(object sender, RoutedEventArgs e) - { - Close?.Invoke(this, EventArgs.Empty); - } - - private void YesResetButton_Click(object sender, RoutedEventArgs e) - { - foreach (ShortcutEntry shortcut in Shortcuts.ToArray()) - { - shortcut.ResetShortcut(); - } - ConfirmResetFlyout.Hide(); - } - - private void NoResetButton_Click(object sender, RoutedEventArgs e) - { - ConfirmResetFlyout.Hide(); - } - - private void HandleAllDesktop_Checked(object sender, RoutedEventArgs e) - { - SaveChanges(); - Close?.Invoke(this, new()); - _ = DialogHelper.ConfirmSetDeleteAllShortcutsSetting(); - } - - private void HandleAllDesktop_Unchecked(object sender, RoutedEventArgs e) - { - Settings.Set(Settings.K.RemoveAllDesktopShortcuts, false); - } - - public void SaveChanges() - { - foreach (var shortcut in Shortcuts) - { - DesktopShortcutsDatabase.AddToDatabase( - shortcut.Path, - shortcut.IsDeletable - ? DesktopShortcutsDatabase.Status.Delete - : DesktopShortcutsDatabase.Status.Maintain - ); - DesktopShortcutsDatabase.RemoveFromUnknownShortcuts(shortcut.Path); - - if (shortcut.IsDeletable && File.Exists(shortcut.Path)) - { - DesktopShortcutsDatabase.DeleteFromDisk(shortcut.Path); - } - } - } - - private void CloseSaveButton_Click(object sender, RoutedEventArgs e) - { - SaveChanges(); - Close?.Invoke(this, EventArgs.Empty); - } - } - - public partial class ShortcutEntry : INotifyPropertyChanged - { - public event EventHandler? OnReset; - - public string Path { get; } - public string Name { get; } - private bool _deletable; - - public bool IsDeletable - { - get => _deletable; - set - { - _deletable = value; - OnPropertyChanged(); - } - } - - public bool ExistsOnDisk - { - get => File.Exists(Path); - } - - public ShortcutEntry(string path, bool isDeletable) - { - Path = path; - Name = string.Join('.', path.Split("\\")[^1].Split('.')[..^1]); - IsDeletable = isDeletable; - } - - public void OpenShortcutPath() => _ = CoreTools.ShowFileOnExplorer(Path); - - public void ResetShortcut() - { - DesktopShortcutsDatabase.AddToDatabase( - this.Path, - DesktopShortcutsDatabase.Status.Unknown - ); - OnReset?.Invoke(this, EventArgs.Empty); - } - - public event PropertyChangedEventHandler? PropertyChanged; - - protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - } -} diff --git a/src/UniGetUI/Pages/DialogPages/DialogHelper_Generic.cs b/src/UniGetUI/Pages/DialogPages/DialogHelper_Generic.cs deleted file mode 100644 index 4b5037bcc4..0000000000 --- a/src/UniGetUI/Pages/DialogPages/DialogHelper_Generic.cs +++ /dev/null @@ -1,750 +0,0 @@ -using System.Diagnostics; -using Microsoft.UI; -using Microsoft.UI.Text; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Documents; -using Microsoft.UI.Xaml.Media; -using Microsoft.Windows.AppNotifications; -using Microsoft.Windows.AppNotifications.Builder; -using UniGetUI.Core.Data; -using UniGetUI.Core.Logging; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; -using UniGetUI.Interface; -using UniGetUI.Interface.Dialogs; -using UniGetUI.Interface.Enums; -using UniGetUI.PackageEngine; -using UniGetUI.PackageEngine.Classes.Packages.Classes; -using UniGetUI.PackageEngine.PackageLoader; -using Windows.UI; - -namespace UniGetUI.Pages.DialogPages; - -public static partial class DialogHelper -{ - public static async Task ShowMissingDependency( - string dep_name, - string exe_name, - string exe_args, - string fancy_command, - int current, - int total - ) - { - if ( - Settings.GetDictionaryItem(Settings.K.DependencyManagement, dep_name) - == "skipped" - ) - { - Logger.Error( - $"Dependency {dep_name} was not found, and the user set it to not be reminded of the missing dependency" - ); - return; - } - - bool NotFirstTime = - Settings.GetDictionaryItem(Settings.K.DependencyManagement, dep_name) - == "attempted"; - Settings.SetDictionaryItem(Settings.K.DependencyManagement, dep_name, "attempted"); - - var dialog = DialogFactory.Create(); - dialog.Title = - CoreTools.Translate("Missing dependency") + (total > 1 ? $" ({current}/{total})" : ""); - dialog.SecondaryButtonText = CoreTools.Translate("Not right now"); - dialog.PrimaryButtonText = CoreTools.Translate("Install {0}", dep_name); - dialog.DefaultButton = ContentDialogButton.Primary; - - bool has_installed = false; - bool block_closing = false; - - StackPanel p = new(); - - p.Children.Add( - new TextBlock - { - Text = CoreTools.Translate( - "UniGetUI requires {0} to operate, but it was not found on your system.", - dep_name - ), - TextWrapping = TextWrapping.Wrap, - Margin = new Thickness(0, 0, 0, 5), - } - ); - - TextBlock infotext = new() - { - Text = CoreTools.Translate( - "Click on Install to begin the installation process. If you skip the installation, UniGetUI may not work as expected." - ), - TextWrapping = TextWrapping.Wrap, - Margin = new Thickness(0, 0, 0, 10), - Opacity = .7F, - FontStyle = Windows.UI.Text.FontStyle.Italic, - }; - p.Children.Add(infotext); - - TextBlock commandInfo = new() - { - Text = CoreTools.Translate( - "Alternatively, you can also install {0} by running the following command in a Windows PowerShell prompt:", - dep_name - ), - TextWrapping = TextWrapping.Wrap, - Margin = new Thickness(0, 0, 0, 4), - Opacity = .7F, - }; - p.Children.Add(commandInfo); - - TextBlock manualInstallCommand = new() - { - Text = fancy_command, - TextWrapping = TextWrapping.Wrap, - Margin = new Thickness(0, 0, 0, 4), - Opacity = .7F, - IsTextSelectionEnabled = true, - FontFamily = new FontFamily("Consolas"), - }; - p.Children.Add(manualInstallCommand); - - CheckBox c = new(); - if (NotFirstTime) - { - c.Content = CoreTools.Translate("Do not show this dialog again for {0}", dep_name); - c.IsChecked = false; - c.Checked += (_, _) => - Settings.SetDictionaryItem(Settings.K.DependencyManagement, dep_name, "skipped"); - c.Unchecked += (_, _) => - Settings.SetDictionaryItem(Settings.K.DependencyManagement, dep_name, "attempted"); - p.Children.Add(c); - } - - ProgressBar progress = new() { IsIndeterminate = false, Opacity = .0F }; - p.Children.Add(progress); - - dialog.PrimaryButtonClick += async (_, _) => - { - if (!has_installed) - { - // Begin installing the dependency - try - { - progress.Opacity = 1.0F; - progress.IsIndeterminate = true; - block_closing = true; - c.IsEnabled = false; - dialog.IsPrimaryButtonEnabled = false; - dialog.IsSecondaryButtonEnabled = false; - dialog.SecondaryButtonText = ""; - dialog.PrimaryButtonText = CoreTools.Translate("Please wait"); - infotext.Text = CoreTools.Translate( - "Please wait while {0} is being installed. A black window may show up. Please wait until it closes.", - dep_name - ); - Process install_dep_p = new() - { - StartInfo = new ProcessStartInfo - { - FileName = exe_name, - Arguments = exe_args, - }, - }; - install_dep_p.Start(); - await install_dep_p.WaitForExitAsync(); - dialog.IsPrimaryButtonEnabled = true; - dialog.IsSecondaryButtonEnabled = true; - if (current < total) - { - // When finished, but more dependencies need to be installed - infotext.Text = - CoreTools.Translate("{0} has been installed successfully.", dep_name) - + " " - + CoreTools.Translate( - "Please click on \"Continue\" to continue", - dep_name - ); - dialog.SecondaryButtonText = ""; - dialog.PrimaryButtonText = CoreTools.Translate("Continue"); - } - else - { - // When finished, and no more dependencies need to be installed - infotext.Text = CoreTools.Translate( - "{0} has been installed successfully. It is recommended to restart UniGetUI to finish the installation", - dep_name - ); - dialog.SecondaryButtonText = CoreTools.Translate("Restart later"); - dialog.PrimaryButtonText = CoreTools.Translate("Restart UniGetUI"); - } - } - catch (Exception ex) - { - // If an error occurs - Logger.Error(ex); - dialog.IsPrimaryButtonEnabled = true; - dialog.IsSecondaryButtonEnabled = true; - infotext.Text = - CoreTools.Translate("An error occurred:") - + " " - + ex.Message - + "\n" - + CoreTools.Translate("Please click on \"Continue\" to continue"); - dialog.SecondaryButtonText = ""; - dialog.PrimaryButtonText = - (current < total) - ? CoreTools.Translate("Continue") - : CoreTools.Translate("Close"); - } - - has_installed = true; - progress.Opacity = .0F; - progress.IsIndeterminate = false; - } - else - { - // If this is the last dependency - if (current == total) - { - block_closing = true; - MainApp.Instance.KillAndRestart(); - } - } - }; - - dialog.Closing += (_, e) => - { - e.Cancel = block_closing; - block_closing = false; - }; - dialog.Content = p; - await ShowDialogAsync(dialog); - } - - public static async Task ManageIgnoredUpdates() - { - ContentDialog dialog = DialogFactory.Create_AsWindow(true); - - dialog.Title = CoreTools.Translate("Manage ignored updates"); - - IgnoredUpdatesManager IgnoredUpdatesPage = new(); - dialog.Content = IgnoredUpdatesPage; - IgnoredUpdatesPage.Close += (_, _) => dialog.Hide(); - await ShowDialogAsync(dialog); - } - - public static async Task ManageDesktopShortcuts(IReadOnlyList? NewShortucts = null) - { - ContentDialog dialog = DialogFactory.Create_AsWindow(true); - - DesktopShortcutsManager DesktopShortcutsPage = new(); - DesktopShortcutsPage.LoadShortcuts( - NewShortucts ?? DesktopShortcutsDatabase.GetAllShortcuts() - ); - DesktopShortcutsPage.Close += (_, _) => dialog.Hide(); - - dialog.Title = CoreTools.Translate("Automatic desktop shortcut remover"); - dialog.Content = DesktopShortcutsPage; - - await ShowDialogAsync(dialog); - } - - public static async Task HandleNewDesktopShortcuts() - { - var unknownShortcuts = DesktopShortcutsDatabase.GetUnknownShortcuts(); - - if (!Settings.AreNotificationsDisabled()) - { - await AppNotificationManager.Default.RemoveByTagAsync( - CoreData.NewShortcutsNotificationTag.ToString() - ); - AppNotification notification; - - if (unknownShortcuts.Count == 1) - { - AppNotificationBuilder builder = new AppNotificationBuilder() - .SetScenario(AppNotificationScenario.Default) - .SetTag(CoreData.NewShortcutsNotificationTag.ToString()) - .AddText(CoreTools.Translate("Desktop shortcut created")) - .AddText( - CoreTools.Translate( - "UniGetUI has detected a new desktop shortcut that can be deleted automatically." - ) - ) - .SetAttributionText(unknownShortcuts.First().Split("\\").Last()) - .AddButton( - new AppNotificationButton( - CoreTools.Translate("Open UniGetUI").Replace("'", "´") - ).AddArgument("action", NotificationArguments.Show) - ) - .AddArgument("action", NotificationArguments.Show); - - notification = builder.BuildNotification(); - } - else - { - string attribution = ""; - foreach (string shortcut in unknownShortcuts) - { - attribution += shortcut.Split("\\").Last() + ", "; - } - - attribution = attribution.TrimEnd(' ').TrimEnd(','); - - AppNotificationBuilder builder = new AppNotificationBuilder() - .SetScenario(AppNotificationScenario.Default) - .SetTag(CoreData.NewShortcutsNotificationTag.ToString()) - .AddText( - CoreTools.Translate("{0} desktop shortcuts created", unknownShortcuts.Count) - ) - .AddText( - CoreTools.Translate( - "UniGetUI has detected {0} new desktop shortcuts that can be deleted automatically.", - unknownShortcuts.Count - ) - ) - .SetAttributionText(attribution) - .AddButton( - new AppNotificationButton( - CoreTools.Translate("Open UniGetUI").Replace("'", "´") - ).AddArgument("action", NotificationArguments.ShowOnUpdatesTab) - ) - .AddArgument("action", NotificationArguments.ShowOnUpdatesTab); - - notification = builder.BuildNotification(); - } - - notification.ExpiresOnReboot = true; - AppNotificationManager.Default.Show(notification); - } - - await ManageDesktopShortcuts(unknownShortcuts); - } - - public static async Task WarnAboutAdminRights() - { - ContentDialog AdminDialog = new() - { - Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style, - }; - - while (Window.XamlRoot is null) - { - await Task.Delay(100); - } - - AdminDialog.XamlRoot = Window.XamlRoot; - AdminDialog.PrimaryButtonText = CoreTools.Translate("I understand"); - AdminDialog.DefaultButton = ContentDialogButton.Primary; - AdminDialog.Title = CoreTools.Translate("Administrator privileges"); - AdminDialog.Content = CoreTools.Translate( - "UniGetUI has been ran as administrator, which is not recommended. When running UniGetUI as administrator, EVERY operation launched from UniGetUI will have administrator privileges. You can still use the program, but we highly recommend not running UniGetUI with administrator privileges." - ); - - await ShowDialogAsync(AdminDialog); - } - - public static async Task WarnAboutChocolateyMigration() - { - ContentDialog dialog = new() - { - Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style, - }; - - while (Window.XamlRoot is null) - { - await Task.Delay(100); - } - - dialog.XamlRoot = Window.XamlRoot; - dialog.PrimaryButtonText = CoreTools.Translate("I understand"); - dialog.DefaultButton = ContentDialogButton.Primary; - dialog.Title = CoreTools.Translate("Chocolatey setup changed"); - dialog.Content = CoreTools.Translate( - "UniGetUI no longer includes its own private Chocolatey installation. A legacy UniGetUI-managed Chocolatey installation was detected for this user profile, but system Chocolatey was not found. Chocolatey will stay unavailable in UniGetUI until Chocolatey is installed on the system and UniGetUI is restarted. Applications previously installed through UniGetUI's bundled Chocolatey may still appear under other sources such as Local PC or WinGet." - ); - - await ShowDialogAsync(dialog); - } - - public static async Task ShowAboutUniGetUI() - { - ContentDialog AboutDialog = DialogFactory.Create_AsWindow(false, false); - AboutUniGetUI AboutPage = new(); - AboutDialog.Content = AboutPage; - AboutPage.Close += (_, _) => AboutDialog.Hide(); - - await ShowDialogAsync(AboutDialog); - } - - public static async Task ShowReleaseNotes() - { - ContentDialog NotesDialog = DialogFactory.Create_AsWindow(true); - - NotesDialog.Title = CoreTools.Translate("Release notes"); - ReleaseNotes notes = new(); - notes.Close += (_, _) => NotesDialog.Hide(); - NotesDialog.Content = notes; - await ShowDialogAsync(NotesDialog); - notes.Dispose(); - } - - public static async Task HandleBrokenWinGet() - { - bool bannerWasOpen = false; - try - { - int loadingId = ShowLoadingDialog( - "Attempting to repair WinGet...", - "WinGet is being repaired. Please wait until the process finishes." - ); - bannerWasOpen = Window.WinGetWarningBanner.IsOpen; - Window.WinGetWarningBanner.IsOpen = false; - using Process p = new Process - { - StartInfo = new() - { - FileName = CoreData.PowerShell5, - Arguments = - "-ExecutionPolicy Bypass -NoLogo -NoProfile -Command \"& {" - + "cmd.exe /C \"rmdir /Q /S `\"%temp%\\WinGet`\"\"; " - + "cmd.exe /C \"`\"%localappdata%\\Microsoft\\WindowsApps\\winget.exe`\" source reset --force\"; " - + "taskkill /im winget.exe /f; " - + "taskkill /im WindowsPackageManagerServer.exe /f; " - + "Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force; " - + "Install-Module Microsoft.WinGet.Client -Force -AllowClobber; " - + "Import-Module Microsoft.WinGet.Client; " - + "Repair-WinGetPackageManager -Force -Latest; " - + "Get-AppxPackage -Name 'Microsoft.DesktopAppInstaller' | Reset-AppxPackage; " - + "}\"", - UseShellExecute = true, - Verb = "runas", - }, - }; - p.Start(); - await p.WaitForExitAsync(); - HideLoadingDialog(loadingId); - - var c = DialogFactory.Create(); - c.Title = CoreTools.Translate("WinGet was repaired successfully"); - c.Content = - CoreTools.Translate( - "It is recommended to restart UniGetUI after WinGet has been repaired" - ) - + "\n\n" - + CoreTools.Translate( - "NOTE: This troubleshooter can be disabled from UniGetUI Settings, on the WinGet section" - ); - c.PrimaryButtonText = CoreTools.Translate("Close"); - c.SecondaryButtonText = CoreTools.Translate("Restart"); - c.DefaultButton = ContentDialogButton.Secondary; - - // Restart UniGetUI or reload packages depending on the user's choice - if (await ShowDialogAsync(c) == ContentDialogResult.Secondary) - { - MainApp.Instance.KillAndRestart(); - } - else - { - _ = UpgradablePackagesLoader.Instance.ReloadPackages(); - _ = InstalledPackagesLoader.Instance.ReloadPackages(); - } - } - catch (Exception ex) - { - // Show an error message if something goes wrong - Window.WinGetWarningBanner.IsOpen = bannerWasOpen; - Logger.Error("An error occurred while trying to repair WinGet"); - Logger.Error(ex); - HideAllLoadingDialogs(); - - var c = DialogFactory.Create(); - c.Title = CoreTools.Translate("WinGet could not be repaired"); - c.Content = - CoreTools.Translate( - "An unexpected issue occurred while attempting to repair WinGet. Please try again later" - ) - + "\n\n" - + ex.Message - + "\n\n" - + CoreTools.Translate( - "NOTE: This troubleshooter can be disabled from UniGetUI Settings, on the WinGet section" - ); - c.PrimaryButtonText = CoreTools.Translate("Close"); - c.DefaultButton = ContentDialogButton.None; - await ShowDialogAsync(c); - } - } - - public static async Task ShowTelemetryDialog() - { - var dialog = DialogFactory.Create(); - dialog.Title = CoreTools.Translate("Share anonymous usage data"); - - var MessageBlock = new RichTextBlock(); - dialog.Content = MessageBlock; - - var p = new Paragraph(); - MessageBlock.Blocks.Add(p); - - p.Inlines.Add( - new Run - { - Text = CoreTools.Translate( - "UniGetUI collects anonymous usage data with the sole purpose of understanding and improving the user experience." - ), - } - ); - p.Inlines.Add(new LineBreak()); - p.Inlines.Add( - new Run - { - Text = CoreTools.Translate( - "No personal information is collected nor sent, and the collected data is anonimized, so it can't be back-tracked to you." - ), - } - ); - p.Inlines.Add(new LineBreak()); - p.Inlines.Add(new LineBreak()); - var link = new Hyperlink - { - NavigateUri = new Uri("https://devolutions.net/legal/"), - }; - link.Inlines.Add( - new Run - { - Text = CoreTools.Translate( - "More details about the shared data and how it will be processed" - ), - } - ); - - p.Inlines.Add(link); - p.Inlines.Add(new LineBreak()); - p.Inlines.Add(new LineBreak()); - p.Inlines.Add( - new Run - { - Text = CoreTools.Translate( - "Do you accept that UniGetUI collects and sends anonymous usage statistics, with the sole purpose of understanding and improving the user experience?" - ), - FontWeight = FontWeights.SemiBold, - } - ); - - dialog.SecondaryButtonText = CoreTools.Translate("Decline"); - dialog.PrimaryButtonText = CoreTools.Translate("Accept"); - dialog.DefaultButton = ContentDialogButton.Primary; - dialog.Closing += (_, e) => - { - if (e.Result == ContentDialogResult.None) - e.Cancel = true; - }; - - var res = await ShowDialogAsync(dialog); - - if (res is ContentDialogResult.Primary) - { - Settings.Set(Settings.K.DisableTelemetry, false); - } - else - { - Settings.Set(Settings.K.DisableTelemetry, true); - } - } - - public static void ShowTelemetryBanner() - { - Window.TelemetryWarner.Title = CoreTools.Translate("Share anonymous usage data"); - Window.TelemetryWarner.Message = CoreTools.Translate( - "UniGetUI collects anonymous usage data in order to improve the user experience." - ); - Window.TelemetryWarner.IsOpen = true; - - Window.TelemetryWarner.IsClosable = true; - Window.TelemetryWarner.Visibility = Visibility.Visible; - - var AcceptBtn = new Button() - { - Content = CoreTools.Translate("Accept"), - Style = Application.Current.Resources["AccentButtonStyle"] as Style, - }; - AcceptBtn.Click += (_, _) => - { - Window.TelemetryWarner.Visibility = Visibility.Collapsed; - Window.TelemetryWarner.IsOpen = false; - Settings.Set(Settings.K.ShownTelemetryBanner, true); - }; - - var SettingsBtn = new Button() { Content = CoreTools.Translate("Settings") }; - SettingsBtn.Click += (_, _) => - { - Window.TelemetryWarner.Visibility = Visibility.Collapsed; - Window.TelemetryWarner.IsOpen = false; - _ = ShowTelemetryDialog(); - Settings.Set(Settings.K.ShownTelemetryBanner, true); - }; - - StackPanel btns = new() - { - Margin = new Thickness(4, 0, 4, 0), - Spacing = 4, - Orientation = Orientation.Horizontal, - }; - btns.Children.Add(AcceptBtn); - btns.Children.Add(SettingsBtn); - - var mainButton = Window.TelemetryWarner.ActionButton = new HyperlinkButton() - { - Padding = new Thickness(0), - Content = btns, - Background = new SolidColorBrush(Colors.Transparent), - BorderBrush = new SolidColorBrush(Colors.Transparent), - }; - mainButton.Resources["HyperlinkButtonBackgroundPointerOver"] = new SolidColorBrush( - Color.FromArgb(0, 0, 0, 0) - ); - - Window.TelemetryWarner.CloseButtonClick += (_, _) => - Settings.Set(Settings.K.ShownTelemetryBanner, true); - } - - public static async Task ConfirmSetDeleteAllShortcutsSetting() - { - var dialog = DialogFactory.Create(); - dialog.Title = CoreTools.Translate("Are you sure you want to delete all shortcuts?"); - dialog.Content = - CoreTools.Translate( - "Any new shorcuts created during an install or an update operation will be deleted automatically, instead of showing a confirmation prompt the first time they are detected." - ) - + " " - + CoreTools.Translate( - "Any shorcuts created or modified outside of UniGetUI will be ignored. You will be able to add them via the {0} button.", - $"\"{CoreTools.Translate("Manual scan")}\"" - ) - + " " - + CoreTools.Translate("Are you really sure you want to enable this feature?"); - dialog.PrimaryButtonText = CoreTools.Translate("Yes"); - dialog.CloseButtonText = CoreTools.Translate("No"); - dialog.DefaultButton = ContentDialogButton.Close; - if (await ShowDialogAsync(dialog) is ContentDialogResult.Primary) - { - Settings.Set(Settings.K.RemoveAllDesktopShortcuts, true); - } - _ = ManageDesktopShortcuts(); - } - - /*public static async Task ManualScanDidNotFoundNewShortcuts() - { - var dialog = DialogFactory.Create(); - dialog.Title = CoreTools.Translate("Manual scan"); - dialog.Content = CoreTools.Translate("No new shortcuts were found during the scan."); - dialog.PrimaryButtonText = CoreTools.Translate("Ok"); - dialog.DefaultButton = ContentDialogButton.Primary; - await ShowDialogAsync(dialog); - }*/ - - public static async Task HowToAddPackagesToBundle() - { - var dialog = DialogFactory.Create(); - dialog.Title = CoreTools.Translate("How to add packages to a bundle"); - dialog.Content = - CoreTools.Translate("In order to add packages to a bundle, you will need to: ") - + "\n " - + CoreTools.Translate( - "1. Navigate to the \"{0}\" or \"{1}\" page.", - CoreTools.Translate("Discover packages"), - CoreTools.Translate("Installed packages") - ) - + "\n " - + CoreTools.Translate( - "2. Locate the package(s) you want to add to the bundle, and select their leftmost checkbox." - ) - + "\n " - + CoreTools.Translate( - "3. When the packages you want to add to the bundle are selected, find and click the option \"{0}\" on the toolbar.", - CoreTools.Translate("Add selection to bundle") - ) - + "\n " - + CoreTools.Translate( - "4. Your packages will have been added to the bundle. You can continue adding packages, or export the bundle." - ); - dialog.PrimaryButtonText = CoreTools.Translate("Discover packages"); - dialog.SecondaryButtonText = CoreTools.Translate("Installed packages"); - dialog.CloseButtonText = CoreTools.Translate("Close"); - dialog.DefaultButton = ContentDialogButton.None; - var result = await ShowDialogAsync(dialog); - if (result is ContentDialogResult.Primary) - Window.NavigationPage.NavigateTo(PageType.Discover); - else if (result is ContentDialogResult.Secondary) - Window.NavigationPage.NavigateTo(PageType.Installed); - } - - public static void ShowDismissableBalloon(string title, string message) - { - Window.DismissableNotification.Title = title; - Window.DismissableNotification.Content = new TextBlock() - { - Text = message, - TextWrapping = TextWrapping.Wrap, - }; - Window.DismissableNotification.IsOpen = true; - } - - public static async Task AskForBackupSelection(IEnumerable availableBackups) - { - var dialog = DialogFactory.Create(); - dialog.Title = CoreTools.Translate("Which backup do you want to open?"); - dialog.PrimaryButtonText = CoreTools.Translate("Open"); - dialog.SecondaryButtonText = CoreTools.Translate("Cancel"); - dialog.DefaultButton = ContentDialogButton.Primary; - dialog.IsPrimaryButtonEnabled = false; - - RadioButtons buttons = new RadioButtons(); - foreach (var name in availableBackups) - buttons.Items.Add(name); - buttons.SelectionChanged += (_, _) => dialog.IsPrimaryButtonEnabled = true; - - dialog.Content = new StackPanel() - { - Orientation = Orientation.Vertical, - Spacing = 4, - Children = - { - new TextBlock() - { - Text = CoreTools.Translate( - "Select the backup you want to open. Later, you will be able to review which packages you want to install." - ), - TextWrapping = TextWrapping.Wrap, - }, - new ScrollViewer() - { - Content = buttons, - HorizontalScrollMode = ScrollMode.Disabled, - }, - }, - }; - - if (await ShowDialogAsync(dialog) is ContentDialogResult.Primary) - return buttons.SelectedItem.ToString() ?? null; - - return null; - } - - /// - /// Asks the user whether to quit or not (there are running operations) - /// - /// True if the user wants to quit, false otherwhise - public static async Task AskContinueClosing_RunningOps() - { - var d = DialogFactory.Create(); - d.Title = CoreTools.Translate("Operation in progress"); - d.Content = CoreTools.Translate( - "There are ongoing operations. Quitting UniGetUI may cause them to fail. Do you want to continue?" - ); - d.PrimaryButtonText = CoreTools.Translate("Quit"); - d.SecondaryButtonText = CoreTools.Translate("Cancel"); - d.DefaultButton = ContentDialogButton.Secondary; - return await ShowDialogAsync(d) is ContentDialogResult.Primary; - } -} diff --git a/src/UniGetUI/Pages/DialogPages/DialogHelper_Infrastructure.cs b/src/UniGetUI/Pages/DialogPages/DialogHelper_Infrastructure.cs deleted file mode 100644 index 2bcc0302ee..0000000000 --- a/src/UniGetUI/Pages/DialogPages/DialogHelper_Infrastructure.cs +++ /dev/null @@ -1,307 +0,0 @@ -using System.Diagnostics; -using System.Runtime.InteropServices; -using Microsoft.UI; -using Microsoft.UI.Text; -using Microsoft.UI.Windowing; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Documents; -using Microsoft.UI.Xaml.Media; -using Microsoft.Windows.AppNotifications; -using Microsoft.Windows.AppNotifications.Builder; -using UniGetUI.Core.Data; -using UniGetUI.Core.Logging; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; -using UniGetUI.Interface; -using UniGetUI.Interface.Dialogs; -using UniGetUI.Interface.Enums; -using UniGetUI.PackageEngine; -using UniGetUI.PackageEngine.Classes.Packages.Classes; -using Windows.UI; -using Windows.UI.Text; - -namespace UniGetUI.Pages.DialogPages; - -public static partial class DialogHelper -{ - internal static class DialogFactory - { - public static ContentDialog Create() - { - var dialog = new ContentDialog() - { - XamlRoot = Window.MainContentGrid.XamlRoot, - Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style, - }; - return dialog; - } - - public static ContentDialog Create_AsWindow(bool hasTitle, bool hasButtons = false) - { - var dialog = Create(); - dialog.Resources["ContentDialogMaxWidth"] = 8192; - dialog.Resources["ContentDialogMaxHeight"] = 4096; - dialog.SizeChanged += (_, _) => - { - if (dialog.Content is FrameworkElement page) - { - double maxW, - maxH; - int tresholdW = 1300, - tresholdH = 1300; - if (Window.NavigationPage.ActualWidth < tresholdW) - maxW = 100; - else if (Window.NavigationPage.ActualWidth >= tresholdW + 200) - maxW = 300; - else - maxW = Window.NavigationPage.ActualWidth - (tresholdW - 100); - - if (Window.NavigationPage.ActualHeight < tresholdH) - maxH = (hasTitle ? 104 : 64) + (hasButtons ? 80 : 0); - else if (Window.NavigationPage.ActualHeight >= tresholdH + 200) - maxH = (hasTitle ? 320 : 280) + (hasButtons ? 80 : 0); - else - maxH = - Window.NavigationPage.ActualHeight - - (tresholdH - (hasTitle ? 120 : 80)) - + (hasButtons ? 80 : 0); - - page.Width = Math.Min(Math.Abs(Window.NavigationPage.ActualWidth - maxW), 8192); - page.Height = Math.Min( - Math.Abs(Window.NavigationPage.ActualHeight - maxH), - 4096 - ); - } - }; - return dialog; - } - } - - public static MainWindow Window { get; set; } = null!; - - public struct LoadingDialog - { - public readonly int Id; - public readonly string Title; - public readonly string Text; - private static readonly Random r = new(); - - public LoadingDialog(string title, string text) - { - Title = title; - Text = text; - Id = r.Next(); - } - } - - private static class NativeHelpers - { - [ComImport] - [Guid("3A3DCD6C-3EAB-43DC-BCDE-45671CE800C8")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public interface IDataTransferManagerInterop - { - IntPtr GetForWindow([In] IntPtr appWindow, [In] ref Guid riid); - void ShowShareUIForWindow(IntPtr appWindow); - } - - public static readonly Guid _dtm_iid = new( - 0xa5caee9b, - 0x8708, - 0x49d1, - 0x8d, - 0x36, - 0x67, - 0xd2, - 0x5a, - 0x8d, - 0xa0, - 0x0c - ); - } - - private static readonly List _loadingDialogQueue = new(); - private static readonly List _dialogQueue = []; - private static int _currentLoadingDialogId; - private static ContentDialog? _currentLoadingDialog; - - public static int ShowLoadingDialog(string text) => ShowLoadingDialog(text, ""); - - public static int ShowLoadingDialog(string title, string description) - { - var dialogData = new LoadingDialog(title, description); - _loadingDialogQueue.Add(dialogData); - _showNextLoadingDialogIfPossible(); - return dialogData.Id; - } - - public static void HideLoadingDialog(int id) - { - _loadingDialogQueue.RemoveAll(d => d.Id == id); - if (_currentLoadingDialogId == id) - { - _currentLoadingDialog?.Hide(); - _currentLoadingDialog = null; - _currentLoadingDialogId = 0; - } - - _showNextLoadingDialogIfPossible(); - } - - public static void HideAllLoadingDialogs() - { - _loadingDialogQueue.Clear(); - _currentLoadingDialog?.Hide(); - _currentLoadingDialog = null; - _currentLoadingDialogId = 0; - } - - public static void _showNextLoadingDialogIfPossible() - { - if (!_loadingDialogQueue.Any()) - return; - var data = _loadingDialogQueue.First(); - - if (Window.LoadingDialogCount == 0 && _dialogQueue.Count == 0) - { - _currentLoadingDialogId = data.Id; - _currentLoadingDialog = DialogFactory.Create(); - _currentLoadingDialog.Title = data.Title; - _currentLoadingDialog.Content = new StackPanel() - { - // Width = 400, - Orientation = Orientation.Vertical, - VerticalAlignment = VerticalAlignment.Stretch, - HorizontalAlignment = HorizontalAlignment.Stretch, - Spacing = 20, - Children = - { - new TextBlock() - { - HorizontalAlignment = HorizontalAlignment.Stretch, - TextWrapping = TextWrapping.Wrap, - Text = data.Text, - }, - new ProgressRing() - { - IsIndeterminate = true, - HorizontalAlignment = HorizontalAlignment.Center, - VerticalAlignment = VerticalAlignment.Center, - }, - }, - }; - _ = ShowDialogAsync(_currentLoadingDialog, HighPriority: true); - } - } - - public static async Task ShowDialogAsync( - ContentDialog dialog, - bool HighPriority = false - ) - { - try - { - if (HighPriority && _dialogQueue.Count >= 1) - { - _dialogQueue.Insert(1, dialog); - } - else - { - _dialogQueue.Add(dialog); - } - - while (_dialogQueue[0] != dialog) - { - await Task.Delay(100); - } - - dialog.RequestedTheme = Window.MainContentGrid.RequestedTheme; - Window.AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Standard; - ContentDialogResult result = await dialog.ShowAsync(); - Window.AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Tall; - _dialogQueue.Remove(dialog); - if (!_dialogQueue.Any()) - DialogHelper._showNextLoadingDialogIfPossible(); - return result; - } - catch (Exception e) - { - Logger.Error("An error occurred while showing a ContentDialog via ShowDialogAsync()"); - Logger.Error(e); - _dialogQueue.Remove(dialog); - return ContentDialogResult.None; - } - } - - public static async Task ShowIntegrityResult() - { - var dialog = DialogFactory.Create(); - - dialog.Title = CoreTools.Translate("Integrity violation"); - dialog.Content = new ScrollView() - { - Content = new StackPanel() - { - Orientation = Orientation.Vertical, - Spacing = 8, - Children = - { - new TextBlock() - { - Text = - CoreTools.Translate( - "UniGetUI or some of its components are missing or corrupt." - ) - + " " - + CoreTools.Translate( - "It is strongly recommended to reinstall UniGetUI to adress the situation." - ), - FontWeight = new FontWeight(600), - TextWrapping = TextWrapping.Wrap, - Foreground = - Application.Current.Resources["SystemControlErrorTextForegroundBrush"] - as Brush, - }, - new TextBlock() - { - Text = - " ● " - + CoreTools.Translate( - "Refer to the UniGetUI Logs to get more details regarding the affected file(s)" - ), - TextWrapping = TextWrapping.Wrap, - }, - new TextBlock() - { - Text = - " ● " - + CoreTools.Translate( - "Integrity checks can be disabled from the Experimental Settings" - ), - TextWrapping = TextWrapping.Wrap, - }, - }, - }, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - }; - - string installerPath = Path.Join( - CoreData.UniGetUIExecutableDirectory, - "UniGetUI.Installer.exe" - ); - if (File.Exists(installerPath)) - { - dialog.SecondaryButtonText = CoreTools.Translate("Repair UniGetUI"); - dialog.DefaultButton = ContentDialogButton.Secondary; - dialog.SecondaryButtonClick += (_, _) => - { - Process.Start(installerPath, "/silent /NoDeployInstaller"); - }; - } - - dialog.PrimaryButtonText = CoreTools.Translate("Close"); - await ShowDialogAsync(dialog); - } -} diff --git a/src/UniGetUI/Pages/DialogPages/DialogHelper_Operations.cs b/src/UniGetUI/Pages/DialogPages/DialogHelper_Operations.cs deleted file mode 100644 index f41b2e1b23..0000000000 --- a/src/UniGetUI/Pages/DialogPages/DialogHelper_Operations.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Microsoft.UI.Xaml.Controls; -using UniGetUI.Controls.OperationWidgets; -using UniGetUI.Core.Tools; -using UniGetUI.PackageOperations; - -namespace UniGetUI.Pages.DialogPages; - -public static partial class DialogHelper -{ - public static async Task ShowOperationFailedDialog( - AbstractOperation operation, - OperationControl opControl - ) - { - ContentDialog dialog = DialogFactory.Create_AsWindow(true, true); - dialog.Title = operation.Metadata.FailureTitle; - var contents = new OperationFailedDialog(operation, opControl); - - dialog.Content = contents; - contents.Close += (_, _) => dialog.Hide(); - - await ShowDialogAsync(dialog); - } - - public static async Task ShowLiveLogDialog(AbstractOperation operation) - { - ContentDialog OutputDialog = DialogFactory.Create_AsWindow(hasTitle: true); - var viewer = new OperationLiveLogPage(operation); - viewer.Close += (_, _) => OutputDialog.Hide(); - OutputDialog.Title = CoreTools.Translate("Live output"); - OutputDialog.Content = viewer; - - operation.LogLineAdded += viewer.AddLine_ThreadSafe; - await DialogHelper.ShowDialogAsync(OutputDialog); - operation.LogLineAdded -= viewer.AddLine_ThreadSafe; - } -} diff --git a/src/UniGetUI/Pages/DialogPages/DialogHelper_Packages.cs b/src/UniGetUI/Pages/DialogPages/DialogHelper_Packages.cs deleted file mode 100644 index 461f09f2fe..0000000000 --- a/src/UniGetUI/Pages/DialogPages/DialogHelper_Packages.cs +++ /dev/null @@ -1,348 +0,0 @@ -using ABI.Microsoft.UI.Text; -using Microsoft.UI; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Documents; -using Microsoft.UI.Xaml.Media; -using UniGetUI.Core.Logging; -using UniGetUI.Core.Tools; -using UniGetUI.Interface.Dialogs; -using UniGetUI.Interface.Enums; -using UniGetUI.Interface.Telemetry; -using UniGetUI.PackageEngine; -using UniGetUI.PackageEngine.Enums; -using UniGetUI.PackageEngine.Interfaces; -using UniGetUI.PackageEngine.PackageClasses; -using UniGetUI.PackageEngine.PackageLoader; -using UniGetUI.PackageEngine.Serializable; -using UniGetUI.Pages.SettingsPages.GeneralPages; -using Windows.UI.Text; - -namespace UniGetUI.Pages.DialogPages; - -public static partial class DialogHelper -{ - /// - /// Will update the Installation Options for the given Package, and will return whether the user choose to continue - /// - public static async Task ShowInstallatOptions_Continue( - IPackage package, - OperationType operation - ) - { - var options = await InstallOptionsFactory.LoadForPackageAsync(package); - var (dialogOptions, dialogResult) = await ShowInstallOptions(package, operation, options); - - if (dialogResult is not ContentDialogResult.None) - { - Logger.Debug($"Saving updated options for package {package.Id}"); - await InstallOptionsFactory.SaveForPackageAsync(dialogOptions, package); - } - else - { - Logger.Debug( - $"Install options dialog for {package.Id} was canceled, no changes will be saved" - ); - } - - return dialogResult is ContentDialogResult.Secondary; - } - - /// - /// Will update the Installation Options for the given imported package - /// - public static async Task ShowInstallOptions_ImportedPackage( - ImportedPackage importedPackage - ) - { - var (options, dialogResult) = await ShowInstallOptions( - importedPackage, - OperationType.None, - importedPackage.installation_options.Copy() - ); - - if (dialogResult != ContentDialogResult.None) - { - importedPackage.installation_options = options; - importedPackage.FirePackageVersionChangedEvent(); - } - - return dialogResult; - } - - private static async Task<(InstallOptions, ContentDialogResult)> ShowInstallOptions( - IPackage package, - OperationType operation, - InstallOptions options - ) - { - InstallOptionsPage OptionsPage = new(package, operation, options); - - ContentDialog OptionsDialog = DialogFactory.Create_AsWindow(true, true); - - OptionsDialog.SecondaryButtonText = operation switch - { - OperationType.Install => CoreTools.Translate("Install"), - OperationType.Uninstall => CoreTools.Translate("Uninstall"), - OperationType.Update => CoreTools.Translate("Update"), - _ => "", - }; - OptionsDialog.PrimaryButtonText = CoreTools.Translate("Save and close"); - OptionsDialog.DefaultButton = ContentDialogButton.Secondary; - // OptionsDialog.Title = CoreTools.Translate("{0} installation options", package.Name); - OptionsDialog.Content = OptionsPage; - - OptionsPage.Close += (_, _) => - { - OptionsDialog.Hide(); - }; - - ContentDialogResult result = await ShowDialogAsync(OptionsDialog); - return (await OptionsPage.GetUpdatedOptions(), result); - } - - public static async Task ShowPackageDetails( - IPackage package, - OperationType operation, - TEL_InstallReferral referral - ) - { - PackageDetailsPage DetailsPage = new(package, operation, referral); - - ContentDialog DetailsDialog = DialogFactory.Create_AsWindow(false); - DetailsDialog.Content = DetailsPage; - DetailsPage.Close += (_, _) => - { - DetailsDialog.Hide(); - }; - - await ShowDialogAsync(DetailsDialog); - } - - public static async Task ConfirmUninstallation(IPackage package) - { - ContentDialog dialog = DialogFactory.Create(); - dialog.Title = CoreTools.Translate("Are you sure?"); - dialog.PrimaryButtonText = CoreTools.Translate("Yes"); - dialog.SecondaryButtonText = CoreTools.Translate("No"); - dialog.DefaultButton = ContentDialogButton.Secondary; - dialog.Content = CoreTools.Translate("Do you really want to uninstall {0}?", package.Name); - - return await ShowDialogAsync(dialog) is ContentDialogResult.Primary; - } - - public static async Task ConfirmUninstallation(IReadOnlyList packages) - { - if (!packages.Any()) - { - return false; - } - - if (packages.Count == 1) - { - return await ConfirmUninstallation(packages[0]); - } - - ContentDialog dialog = DialogFactory.Create(); - dialog.Title = CoreTools.Translate("Are you sure?"); - dialog.PrimaryButtonText = CoreTools.Translate("Yes"); - dialog.SecondaryButtonText = CoreTools.Translate("No"); - dialog.DefaultButton = ContentDialogButton.Secondary; - - StackPanel p = new(); - p.Children.Add( - new TextBlock - { - Text = CoreTools.Translate( - "Do you really want to uninstall the following {0} packages?", - packages.Count - ), - Margin = new Thickness(0, 0, 0, 5), - } - ); - - string pkgList = ""; - foreach (IPackage package in packages) - { - pkgList += " ● " + package.Name + "\x0a"; - } - - TextBlock PackageListTextBlock = new() - { - FontFamily = new FontFamily("Consolas"), - Text = pkgList, - }; - p.Children.Add(new ScrollView { Content = PackageListTextBlock, MaxHeight = 200 }); - - dialog.Content = p; - - return await ShowDialogAsync(dialog) is ContentDialogResult.Primary; - } - - public static async Task ShowBundleSecurityReport( - Dictionary> packageReport - ) - { - var dialog = DialogFactory.Create_AsWindow(true, true); - Brush bad = new SolidColorBrush(Colors.PaleVioletRed); - Brush good = new SolidColorBrush(Colors.Gold); - - if (Window.NavigationPage.ActualTheme is ElementTheme.Light) - { - bad = new SolidColorBrush(Colors.Red); - good = new SolidColorBrush(Colors.DarkGoldenrod); - } - - var title = CoreTools.Translate("Bundle security report"); - dialog.Title = title; - Hyperlink a; - Paragraph p = new(); - - foreach (var pair in packageReport) - { - p.Inlines.Add( - new Run() - { - Text = $" - {CoreTools.Translate("Package")}: {pair.Key}:\n", - FontFamily = new("Consolas"), - } - ); - - foreach (var issue in pair.Value) - { - p.Inlines.Add( - new Run() - { - Text = $" * {issue.Line}\n", - FontFamily = new("Consolas"), - Foreground = issue.Allowed ? bad : good, - } - ); - } - p.Inlines.Add(new LineBreak()); - } - - dialog.Content = new ScrollViewer() - { - MaxWidth = 800, - Background = (Brush) - Application.Current.Resources["ApplicationPageBackgroundThemeBrush"], - CornerRadius = new CornerRadius(8), - Padding = new Thickness(16), - Content = new RichTextBlock() - { - Blocks = - { - new Paragraph() - { - Inlines = - { - new Run() - { - Text = CoreTools.Translate( - "This package bundle had some settings that are potentially dangerous, and may be ignored by default." - ), - }, - new Run() - { - Text = - "\n - " - + CoreTools.Translate( - "Entries that show in YELLOW will be IGNORED." - ), - Foreground = good, - }, - new Run() - { - Text = - "\n - " - + CoreTools.Translate( - "Entries that show in RED will be IMPORTED." - ), - Foreground = bad, - }, - new Run() - { - Text = - "\n" - + CoreTools.Translate( - "You can change this behavior on UniGetUI security settings." - ) - + " ", - }, - ( - a = new Hyperlink - { - Inlines = - { - new Run() - { - Text = CoreTools.Translate( - "Open UniGetUI security settings" - ), - }, - }, - } - ), - new LineBreak(), - new Run() - { - Text = CoreTools.Translate( - "Should you modify the security settings, you will need to open the bundle again for the changes to take effect." - ), - }, - new LineBreak(), - new LineBreak(), - new Run() { Text = CoreTools.Translate("Details of the report:") }, - new LineBreak(), - }, - }, - p, - }, - }, - }; - a.Click += (_, _) => - { - dialog.Hide(); - Window.NavigationPage.OpenSettingsPage(typeof(Administrator)); - }; - dialog.SecondaryButtonText = CoreTools.Translate("Close"); - await ShowDialogAsync(dialog); - } - - /// - /// Returns true if the user confirms to lose unsaved changes, and wants to proceed with the creation of a new bundle - /// - /// - public static async Task AskLoseChangesAndCreateBundle() - { - RichTextBlock rtb = new(); - var p = new Paragraph(); - rtb.Blocks.Add(p); - p.Inlines.Add( - new Run - { - Text = CoreTools.Translate( - "Are you sure you want to create a new package bundle? " - ), - } - ); - p.Inlines.Add(new LineBreak()); - p.Inlines.Add( - new Run - { - Text = CoreTools.Translate("Any unsaved changes will be lost"), - FontWeight = new FontWeight(600), - } - ); - - ContentDialog dialog = DialogFactory.Create(); - dialog.Title = CoreTools.Translate("Warning!"); - dialog.Content = rtb; - dialog.DefaultButton = ContentDialogButton.Secondary; - dialog.PrimaryButtonText = CoreTools.Translate("Yes"); - dialog.SecondaryButtonText = CoreTools.Translate("No"); - - return await DialogHelper.ShowDialogAsync(dialog) is ContentDialogResult.Primary; - } -} diff --git a/src/UniGetUI/Pages/DialogPages/IgnoredUpdates.xaml b/src/UniGetUI/Pages/DialogPages/IgnoredUpdates.xaml deleted file mode 100644 index fb029c64a4..0000000000 --- a/src/UniGetUI/Pages/DialogPages/IgnoredUpdates.xaml +++ /dev/null @@ -1,157 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/DialogPages/IgnoredUpdates.xaml.cs b/src/UniGetUI/Pages/DialogPages/IgnoredUpdates.xaml.cs deleted file mode 100644 index 2b36a9f82e..0000000000 --- a/src/UniGetUI/Pages/DialogPages/IgnoredUpdates.xaml.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System.Collections.ObjectModel; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using UniGetUI.Core.Tools; -using UniGetUI.Interface.Enums; -using UniGetUI.PackageEngine; -using UniGetUI.PackageEngine.Classes.Packages.Classes; -using UniGetUI.PackageEngine.Interfaces; -using UniGetUI.PackageEngine.Managers.WingetManager; -using UniGetUI.PackageEngine.PackageLoader; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Interface -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class IgnoredUpdatesManager : Page - { - public event EventHandler? Close; - private readonly ObservableCollection ignoredPackages = []; - - public IgnoredUpdatesManager() - { - UpdateData(); - InitializeComponent(); - IgnoredUpdatesList.ItemsSource = ignoredPackages; - } - - private void UpdateData() - { - Dictionary ManagerNameReference = []; - - foreach (IPackageManager Manager in PEInterface.Managers) - { - ManagerNameReference.Add(Manager.Name.ToLower(), Manager); - } - - ignoredPackages.Clear(); - - var rawIgnoredPackages = IgnoredUpdatesDatabase.GetDatabase(); - - foreach (var (ignoredId, version) in rawIgnoredPackages) - { - IPackageManager? manager = PEInterface.WinGet; // Manager by default - if (ManagerNameReference.ContainsKey(ignoredId.Split("\\")[0])) - { - manager = ManagerNameReference[ignoredId.Split("\\")[0]]; - } - - if (manager is null) - continue; - - ignoredPackages.Add( - new IgnoredPackageEntry( - ignoredId.Split("\\")[^1], - version, - manager, - ignoredPackages - ) - ); - } - } - - /*private async void IgnoredUpdatesList_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e) - { - if (IgnoredUpdatesList.SelectedItem is IgnoredPackageEntry package) - { - await package.RemoveFromIgnoredUpdates(); - } - }*/ - - private void CloseButton_Click(object sender, RoutedEventArgs e) - { - Close?.Invoke(this, EventArgs.Empty); - } - - private void YesResetButton_Click(object sender, RoutedEventArgs e) => - _ = _yesResetButton_Click(); - - private async Task _yesResetButton_Click() - { - foreach (IgnoredPackageEntry package in ignoredPackages.ToArray()) - { - await package.RemoveFromIgnoredUpdatesAsync(); - } - ConfirmResetFlyout.Hide(); - } - - private void NoResetButton_Click(object sender, RoutedEventArgs e) - { - ConfirmResetFlyout.Hide(); - } - } - - public class IgnoredPackageEntry - { - public string Id { get; } - public string Name { get; } - public string Version { get; } - public string NewVersion { get; } - public IPackageManager Manager { get; } - private ObservableCollection List { get; } - - public IgnoredPackageEntry( - string id, - string version, - IPackageManager manager, - ObservableCollection list - ) - { - Id = id; - - if (manager is WinGet && id.Contains('.')) - Name = String.Join(' ', id.Split('.')[1..]); - else - Name = CoreTools.FormatAsName(id); - - if (version == "*") - { - Version = CoreTools.Translate("All versions"); - } - else - { - Version = version; - } - - string CurrentVersion = - InstalledPackagesLoader.Instance.GetPackageForId(id)?.VersionString ?? "Unknown"; - - if ( - UpgradablePackagesLoader.Instance.IgnoredPackages.TryGetValue( - Id, - out IPackage? package - ) - && package.NewVersionString != package.VersionString - ) - { - NewVersion = CurrentVersion + " \u27a4 " + package.NewVersionString; - } - else if (CurrentVersion != "Unknown") - { - NewVersion = CoreTools.Translate("Up to date") + $" ({CurrentVersion})"; - ; - } - else - { - NewVersion = CoreTools.Translate("Unknown"); - } - - Manager = manager; - List = list; - } - - public void RemoveFromIgnoredUpdates() => _ = RemoveFromIgnoredUpdatesAsync(); - - public async Task RemoveFromIgnoredUpdatesAsync() - { - string ignoredId = $"{Manager.Properties.Name.ToLower()}\\{Id}"; - await Task.Run(() => IgnoredUpdatesDatabase.Remove(ignoredId)); - - // If possible, add the package to the software updates tab again - if ( - UpgradablePackagesLoader.Instance.IgnoredPackages.TryRemove( - Id, - out IPackage? nativePackage - ) - && nativePackage.NewVersionString != nativePackage.VersionString - ) - { - await UpgradablePackagesLoader.Instance.AddForeign(nativePackage); - } - - foreach (IPackage package in InstalledPackagesLoader.Instance.Packages) - { - if (Manager == package.Manager && package.Id == Id) - { - package.SetTag(PackageTag.Default); - break; - } - } - - List.Remove(this); - } - } -} diff --git a/src/UniGetUI/Pages/DialogPages/InstallOptions_Manager.xaml b/src/UniGetUI/Pages/DialogPages/InstallOptions_Manager.xaml deleted file mode 100644 index b29e83cb4e..0000000000 --- a/src/UniGetUI/Pages/DialogPages/InstallOptions_Manager.xaml +++ /dev/null @@ -1,233 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/DialogPages/InstallOptions_Package.xaml.cs b/src/UniGetUI/Pages/DialogPages/InstallOptions_Package.xaml.cs deleted file mode 100644 index 648cd32448..0000000000 --- a/src/UniGetUI/Pages/DialogPages/InstallOptions_Package.xaml.cs +++ /dev/null @@ -1,605 +0,0 @@ -using System.Collections.ObjectModel; -using System.Diagnostics; -using CommunityToolkit.WinUI.Controls; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Media.Imaging; -using UniGetUI.Core.Language; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.SettingsEngine.SecureSettings; -using UniGetUI.Core.Tools; -using UniGetUI.PackageEngine.Enums; -using UniGetUI.PackageEngine.Interfaces; -using UniGetUI.PackageEngine.PackageClasses; -using UniGetUI.PackageEngine.Serializable; -using UniGetUI.Pages.SettingsPages.GeneralPages; -using Architecture = UniGetUI.PackageEngine.Enums.Architecture; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Interface.Dialogs -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class InstallOptionsPage : Page - { - public InstallOptions Options; - public IPackage Package; - public event EventHandler? Close; - private readonly OperationType Operation; - private readonly string packageInstallLocation; - private bool UiLoaded { get; set; } - - public ObservableCollection ProcessesToKill { get; set; } = new(); - private readonly ObservableCollection _runningProcesses = new(); - public ObservableCollection SuggestedProcesses { get; set; } = new(); - - public InstallOptionsPage(IPackage package, InstallOptions options) - : this(package, OperationType.None, options) { } - - public InstallOptionsPage(IPackage package, OperationType operation, InstallOptions options) - { - Package = package; - InitializeComponent(); - Operation = operation; - Options = options; - - KillProcessesThatWontDie.IsChecked = Settings.Get( - Settings.K.KillProcessesThatRefuseToDie - ); - - ProfileComboBox.Items.Add(CoreTools.Translate("Install")); - ProfileComboBox.Items.Add(CoreTools.Translate("Update")); - ProfileComboBox.Items.Add(CoreTools.Translate("Uninstall")); - ProfileComboBox.SelectedIndex = operation switch - { - OperationType.Update => 1, - OperationType.Uninstall => 2, - _ => 0, - }; - ProfileComboBox.SelectionChanged += (_, _) => - { - EnableDisableControls( - ProfileComboBox.SelectedIndex switch - { - 1 => OperationType.Update, - 2 => OperationType.Uninstall, - _ => OperationType.Install, - } - ); - }; - - FollowGlobalOptionsSwitch.IsOn = !options.OverridesNextLevelOpts; - FollowGlobalOptionsSwitch.Toggled += (_, _) => - { - EnableDisableControls( - ProfileComboBox.SelectedIndex switch - { - 1 => OperationType.Update, - 2 => OperationType.Uninstall, - _ => OperationType.Install, - } - ); - }; - - var iconSource = new BitmapImage() - { - UriSource = package.GetIconUrl(), - DecodePixelHeight = 32, - DecodePixelWidth = 32, - DecodePixelType = DecodePixelType.Logical, - }; - - PackageIcon.Source = iconSource; - async Task LoadImage() - { - iconSource.UriSource = await Task.Run(package.GetIconUrl); - } - _ = LoadImage(); - DialogTitle.Text = CoreTools.Translate("{0} installation options", package.Name); - PlaceholderText.Text = CoreTools.Translate( - "{0} Install options are currently locked because {0} follows the default install options.", - package.Name - ); - - KillProcessesLabel.Text = CoreTools.Translate( - "Select the processes that should be closed before this package is installed, updated or uninstalled." - ); - KillProcessesBox.PlaceholderText = CoreTools.Translate( - "Write here the process names here, separated by commas (,)" - ); - - packageInstallLocation = - Package.Manager.DetailsHelper.GetInstallLocation(package) - ?? CoreTools.Translate("Unset or unknown"); - - AdminCheckBox.IsChecked = Options.RunAsAdministrator; - InteractiveCheckBox.IsChecked = Options.InteractiveInstallation; - HashCheckbox.IsChecked = Options.SkipHashCheck; - UninstallPreviousOnUpdate.IsChecked = Options.UninstallPreviousVersionsOnUpdate; - - ArchitectureComboBox.Items.Add(CoreTools.Translate("Default")); - ArchitectureComboBox.SelectedIndex = 0; - - if (Package.Manager.Capabilities.SupportsCustomArchitectures) - { - foreach (string arch in Package.Manager.Capabilities.SupportedCustomArchitectures) - { - ArchitectureComboBox.Items.Add(arch); - if (Options.Architecture == arch) - { - ArchitectureComboBox.SelectedValue = arch; - } - } - } - - VersionComboBox.SelectionChanged += (_, _) => - { - IgnoreUpdatesCheckbox.IsChecked = !( - new[] - { - CoreTools.Translate("Latest"), - CoreTools.Translate("PreRelease"), - "", - }.Contains(VersionComboBox.SelectedValue.ToString()) - ); - }; - AutoUpdatePackageCheckbox.IsChecked = Options.AutoUpdatePackage; - - VersionComboBox.Items.Add(CoreTools.Translate("Latest")); - VersionComboBox.SelectedIndex = 0; - if (package.Manager.Capabilities.SupportsPreRelease) - { - VersionComboBox.Items.Add(CoreTools.Translate("PreRelease")); - if (Options.PreRelease) - { - VersionComboBox.SelectedValue = CoreTools.Translate("PreRelease"); - } - } - - SkipMinorUpdatesCheckbox.IsChecked = Options.SkipMinorUpdates; - - if (Package.Manager.Capabilities.SupportsCustomVersions) - { - _ = LoadVersions(); - } - else - { - VersionProgress.Visibility = Visibility.Collapsed; - } - - ScopeCombo.Items.Add(CoreTools.Translate("Default")); - ScopeCombo.SelectedIndex = 0; - if (package.Manager.Capabilities.SupportsCustomScopes) - { - ScopeCombo.Items.Add( - CoreTools.Translate(CommonTranslations.ScopeNames[PackageScope.Local]) - ); - if (Options.InstallationScope == PackageScope.Local) - { - ScopeCombo.SelectedValue = CommonTranslations.ScopeNames[PackageScope.Local]; - } - - ScopeCombo.Items.Add( - CoreTools.Translate(CommonTranslations.ScopeNames[PackageScope.Global]) - ); - if (Options.InstallationScope == PackageScope.Global) - { - ScopeCombo.SelectedValue = CommonTranslations.ScopeNames[PackageScope.Global]; - } - } - - foreach (var p in Options.KillBeforeOperation) - { - ProcessesToKill.Add(new(p)); - } - - if (Options.CustomInstallLocation == "") - CustomInstallLocation.Text = packageInstallLocation; - else - CustomInstallLocation.Text = Options.CustomInstallLocation; - - CustomParameters1.Text = string.Join(' ', Options.CustomParameters_Install); - CustomParameters2.Text = string.Join(' ', Options.CustomParameters_Update); - CustomParameters3.Text = string.Join(' ', Options.CustomParameters_Uninstall); - - PreInstallCommandBox.Text = Options.PreInstallCommand; - PostInstallCommandBox.Text = Options.PostInstallCommand; - PreUpdateCommandBox.Text = Options.PreUpdateCommand; - PostUpdateCommandBox.Text = Options.PostUpdateCommand; - PreUninstallCommandBox.Text = Options.PreUninstallCommand; - PostUninstallCommandBox.Text = Options.PostUninstallCommand; - AbortInsFailedCheck.IsChecked = Options.AbortOnPreInstallFail; - AbortUpdFailedCheck.IsChecked = Options.AbortOnPreUpdateFail; - AbortUniFailedCheck.IsChecked = Options.AbortOnPreUninstallFail; - - UiLoaded = true; - EnableDisableControls(operation); - _ = LoadIgnoredUpdates(); - _ = _loadProcesses(); - } - - private async Task _loadProcesses() - { - var processNames = await Task.Run(() => - Process.GetProcesses().Select(p => p.ProcessName).Distinct().ToList() - ); - - _runningProcesses.Clear(); - foreach (var name in processNames) - { - if (name.Any()) - _runningProcesses.Add(new(name + ".exe")); - } - } - - private void EnableDisableControls(OperationType operation) - { - if (FollowGlobalOptionsSwitch.IsOn) - { - OptionsPanel0.Opacity = 0.3; - SettingsSwitchPresenter.Opacity = 0.3; - SettingsTabBar.Opacity = 0.3; - OptionsPanelBase.IsEnabled = false; - PlaceholderBanner.Visibility = Visibility.Visible; - } - else - { - OptionsPanel0.Opacity = 1; - SettingsSwitchPresenter.Opacity = 1; - SettingsTabBar.Opacity = 1; - OptionsPanelBase.IsEnabled = true; - PlaceholderBanner.Visibility = Visibility.Collapsed; - - AdminCheckBox.IsEnabled = Package.Manager.Capabilities.CanRunAsAdmin; - InteractiveCheckBox.IsEnabled = Package.Manager.Capabilities.CanRunInteractively; - HashCheckbox.IsEnabled = - operation is not OperationType.Uninstall - && Package.Manager.Capabilities.CanSkipIntegrityChecks; - - UninstallPreviousOnUpdate.IsEnabled = Package - .Manager - .Capabilities - .CanUninstallPreviousVersionsAfterUpdate; - UninstallPreviousOnUpdate.Visibility = Package - .Manager - .Capabilities - .CanUninstallPreviousVersionsAfterUpdate - ? Visibility.Visible - : Visibility.Collapsed; - - ArchitectureComboBox.IsEnabled = - operation is not OperationType.Uninstall - && Package.Manager.Capabilities.SupportsCustomArchitectures; - - VersionComboBox.IsEnabled = - operation is OperationType.Install or OperationType.None - && ( - Package.Manager.Capabilities.SupportsCustomVersions - || Package.Manager.Capabilities.SupportsPreRelease - ); - ScopeCombo.IsEnabled = Package.Manager.Capabilities.SupportsCustomScopes; - ResetDir.IsEnabled = Package.Manager.Capabilities.SupportsCustomLocations; - SelectDir.IsEnabled = Package.Manager.Capabilities.SupportsCustomLocations; - } - - bool IsCLIEnabled = SecureSettings.Get(SecureSettings.K.AllowCLIArguments); - CustomParameters1.IsEnabled = IsCLIEnabled; - CustomParameters2.IsEnabled = IsCLIEnabled; - CustomParameters3.IsEnabled = IsCLIEnabled; - CustomParametersLabel1.Opacity = IsCLIEnabled ? 1 : 0.5; - CustomParametersLabel2.Opacity = IsCLIEnabled ? 1 : 0.5; - CustomParametersLabel3.Opacity = IsCLIEnabled ? 1 : 0.5; - GoToCLISettings.Visibility = IsCLIEnabled ? Visibility.Collapsed : Visibility.Visible; - CLIDisabled.Visibility = IsCLIEnabled ? Visibility.Collapsed : Visibility.Visible; - - bool IsPrePostOpEnabled = SecureSettings.Get(SecureSettings.K.AllowPrePostOpCommand); - PreInstallCommandBox.IsEnabled = IsPrePostOpEnabled; - PostInstallCommandBox.IsEnabled = IsPrePostOpEnabled; - AbortInsFailedCheck.IsEnabled = IsPrePostOpEnabled; - PreUpdateCommandBox.IsEnabled = IsPrePostOpEnabled; - PostUpdateCommandBox.IsEnabled = IsPrePostOpEnabled; - AbortUpdFailedCheck.IsEnabled = IsPrePostOpEnabled; - PreUninstallCommandBox.IsEnabled = IsPrePostOpEnabled; - PostUninstallCommandBox.IsEnabled = IsPrePostOpEnabled; - AbortUniFailedCheck.IsEnabled = IsPrePostOpEnabled; - PeInsLabel.Opacity = IsPrePostOpEnabled ? 1 : 0.5; - PoInsLabel.Opacity = IsPrePostOpEnabled ? 1 : 0.5; - PeUpdLabel.Opacity = IsPrePostOpEnabled ? 1 : 0.5; - PoUpdLabel.Opacity = IsPrePostOpEnabled ? 1 : 0.5; - PeUniLabel.Opacity = IsPrePostOpEnabled ? 1 : 0.5; - PoUniLabel.Opacity = IsPrePostOpEnabled ? 1 : 0.5; - CustomCommandsHeaderExplainer.Opacity = IsPrePostOpEnabled ? 1 : 0.5; - GoToPrePostSettings.Visibility = IsPrePostOpEnabled - ? Visibility.Collapsed - : Visibility.Visible; - PrePostDisabled.Visibility = IsPrePostOpEnabled - ? Visibility.Collapsed - : Visibility.Visible; - - _ = GenerateCommand(); - } - - private async Task LoadIgnoredUpdates() - { - IgnoreUpdatesCheckbox.IsChecked = await Package.HasUpdatesIgnoredAsync(); - } - - private async Task LoadVersions() - { - VersionComboBox.IsEnabled = false; - - IReadOnlyList versions = await Task.Run(() => - Package.Manager.DetailsHelper.GetVersions(Package) - ); - foreach (string ver in versions) - { - VersionComboBox.Items.Add(ver); - if (Options.Version == ver) - { - VersionComboBox.SelectedValue = ver; - } - } - IgnoreUpdatesCheckbox.IsChecked = await Package.HasUpdatesIgnoredAsync(); - - VersionComboBox.IsEnabled = - Operation is OperationType.Install or OperationType.None - && ( - Package.Manager.Capabilities.SupportsCustomVersions - || Package.Manager.Capabilities.SupportsPreRelease - ); - VersionProgress.Visibility = Visibility.Collapsed; - } - - public async Task GetUpdatedOptions(bool updateDetachedOptions = true) - { - InstallOptions options = new(); - options.RunAsAdministrator = AdminCheckBox?.IsChecked ?? false; - options.InteractiveInstallation = InteractiveCheckBox?.IsChecked ?? false; - options.SkipHashCheck = HashCheckbox?.IsChecked ?? false; - options.UninstallPreviousVersionsOnUpdate = - UninstallPreviousOnUpdate?.IsChecked ?? false; - options.OverridesNextLevelOpts = !FollowGlobalOptionsSwitch.IsOn; - options.AutoUpdatePackage = AutoUpdatePackageCheckbox.IsChecked ?? false; - - options.Architecture = ""; - var userSelection = ArchitectureComboBox.SelectedValue?.ToString() ?? ""; - if (Architecture.ValidValues.Contains(userSelection)) - { - options.Architecture = userSelection; - } - - options.InstallationScope = ""; - userSelection = ScopeCombo.SelectedValue?.ToString() ?? ""; - if (CommonTranslations.InvertedScopeNames.TryGetValue(userSelection, out string? value)) - { - options.InstallationScope = value; - } - - if (CustomInstallLocation.Text == packageInstallLocation) - options.CustomInstallLocation = ""; - else - options.CustomInstallLocation = CustomInstallLocation.Text; - - options.CustomParameters_Install = CustomParameters1.Text.Split(' ').ToList(); - options.CustomParameters_Update = CustomParameters2.Text.Split(' ').ToList(); - options.CustomParameters_Uninstall = CustomParameters3.Text.Split(' ').ToList(); - options.PreRelease = - VersionComboBox.SelectedValue.ToString() == CoreTools.Translate("PreRelease"); - - options.PreInstallCommand = PreInstallCommandBox.Text; - options.PostInstallCommand = PostInstallCommandBox.Text; - options.PreUpdateCommand = PreUpdateCommandBox.Text; - options.PostUpdateCommand = PostUpdateCommandBox.Text; - options.PreUninstallCommand = PreUninstallCommandBox.Text; - options.PostUninstallCommand = PostUninstallCommandBox.Text; - options.AbortOnPreInstallFail = AbortInsFailedCheck.IsChecked ?? true; - options.AbortOnPreUpdateFail = AbortUpdFailedCheck.IsChecked ?? true; - options.AbortOnPreUninstallFail = AbortUniFailedCheck.IsChecked ?? true; - - options.KillBeforeOperation.Clear(); - foreach (var p in ProcessesToKill) - options.KillBeforeOperation.Add(p.Name); - - if ( - VersionComboBox.SelectedValue.ToString() != CoreTools.Translate("PreRelease") - && VersionComboBox.SelectedValue.ToString() != CoreTools.Translate("Latest") - ) - { - options.Version = VersionComboBox.SelectedValue.ToString() ?? ""; - } - else - { - options.Version = ""; - } - options.SkipMinorUpdates = SkipMinorUpdatesCheckbox?.IsChecked ?? false; - - if (updateDetachedOptions) - { - Settings.Set( - Settings.K.KillProcessesThatRefuseToDie, - KillProcessesThatWontDie.IsChecked ?? false - ); - - if (IgnoreUpdatesCheckbox?.IsChecked ?? false) - { - await Package.AddToIgnoredUpdatesAsync(version: "*"); - } - else - { - if (await Package.GetIgnoredUpdatesVersionAsync() == "*") - { - await Package.RemoveFromIgnoredUpdatesAsync(); - } - } - } - return options; - } - - private void SelectDir_Click(object sender, RoutedEventArgs e) - { - ExternalLibraries.Pickers.FolderPicker openPicker = new( - MainApp.Instance.MainWindow.GetWindowHandle() - ); - string folder = openPicker.Show(); - if (folder != string.Empty) - { - CustomInstallLocation.Text = folder; - } - _ = GenerateCommand(); - } - - private void ResetDir_Click(object sender, RoutedEventArgs e) - { - CustomInstallLocation.Text = packageInstallLocation; - _ = GenerateCommand(); - } - - private void CloseButton_Click(object sender, RoutedEventArgs e) - { - Close?.Invoke(this, EventArgs.Empty); - } - - private void CustomParameters_TextChanged(object sender, TextChangedEventArgs e) => - _ = GenerateCommand(); - - private void ScopeCombo_SelectionChanged(object sender, SelectionChangedEventArgs e) => - _ = GenerateCommand(); - - private void VersionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) => - _ = GenerateCommand(); - - private void AdminCheckBox_Click(object sender, RoutedEventArgs e) => _ = GenerateCommand(); - - private void InteractiveCheckBox_Click(object sender, RoutedEventArgs e) => - _ = GenerateCommand(); - - private void HashCheckbox_Click(object sender, RoutedEventArgs e) => _ = GenerateCommand(); - - private void ArchitectureComboBox_SelectionChanged( - object sender, - SelectionChangedEventArgs e - ) => _ = GenerateCommand(); - - private async Task GenerateCommand() - { - if (!UiLoaded) - return; - InstallOptions options = await GetUpdatedOptions(updateDetachedOptions: false); - options = await InstallOptionsFactory.LoadApplicableAsync( - this.Package, - overridePackageOptions: options - ); - - var op = ProfileComboBox.SelectedIndex switch - { - 1 => OperationType.Update, - 2 => OperationType.Uninstall, - _ => OperationType.Install, - }; - var commandline = await Task.Run(() => - Package.Manager.OperationHelper.GetParameters(Package, options, op) - ); - CommandBox.Text = - Package.Manager.Properties.ExecutableFriendlyName - + " " - + string.Join(" ", commandline); - } - - private void LayoutGrid_SizeChanged(object sender, SizeChangedEventArgs e) - { - if (LayoutGrid.ActualSize.Y > 1 && LayoutGrid.ActualSize.Y < double.PositiveInfinity) - MaxHeight = LayoutGrid.ActualSize.Y; - } - - private void UnlockSettingsButton_Click(object sender, RoutedEventArgs e) - { - FollowGlobalOptionsSwitch.IsOn = false; - } - - private void GoToDefaultOptionsSettings_Click(object sender, RoutedEventArgs e) - { - Close?.Invoke(this, EventArgs.Empty); - MainApp.Instance.MainWindow.NavigationPage.OpenManagerSettings(Package.Manager); - } - - private void GoToSecureSettings_Click(object sender, RoutedEventArgs e) - { - Close?.Invoke(this, EventArgs.Empty); - MainApp.Instance.MainWindow.NavigationPage.OpenSettingsPage(typeof(Administrator)); - } - - private void KillProcessesBox_TokenItemAdding( - TokenizingTextBox sender, - TokenItemAddingEventArgs args - ) - { - args.Item = _runningProcesses.FirstOrDefault( - (item) => item.Name.Contains(args.TokenText) - ); - if (args.Item is null) - { - string text = args.TokenText; - if (!text.EndsWith(".exe")) - text += ".exe"; - args.Item = new IOP_Proc(text); - } - } - - private void KillProcessesBox_TextChanged( - AutoSuggestBox sender, - AutoSuggestBoxTextChangedEventArgs args - ) => _ = _killProcessesBox_TextChanged(); - - private async Task _killProcessesBox_TextChanged() - { - var text = KillProcessesBox.Text; - await Task.Delay(100); - if (text != KillProcessesBox.Text) - return; - - SuggestedProcesses.Clear(); - if (text.Trim() != "") - { - if (!text.EndsWith(".exe")) - text = text.Trim() + ".exe"; - SuggestedProcesses.Add(new(text)); - foreach ( - var item in _runningProcesses.Where(x => x.Name.Contains(KillProcessesBox.Text)) - ) - { - SuggestedProcesses.Add(item); - } - } - } - - private void SettingsTabBar_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - CommandLineViewBox.Visibility = - SettingsTabBar.SelectedIndex < 3 ? Visibility.Visible : Visibility.Collapsed; - } - - public void HideCloseButton() - { - CloseButton.Visibility = Visibility.Collapsed; - CloseButton.IsEnabled = false; - } - - internal void HideHeaderBar() - { - HeaderBar.Visibility = Visibility.Collapsed; - } - } - - public class IOP_Proc - { - public readonly string Name; - - public IOP_Proc(string name) - { - Name = name; - } - } -} diff --git a/src/UniGetUI/Pages/DialogPages/OperationFailedDialog.xaml b/src/UniGetUI/Pages/DialogPages/OperationFailedDialog.xaml deleted file mode 100644 index ad937f1f9b..0000000000 --- a/src/UniGetUI/Pages/DialogPages/OperationFailedDialog.xaml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/DialogPages/OperationFailedDialog.xaml.cs b/src/UniGetUI/Pages/DialogPages/OperationFailedDialog.xaml.cs deleted file mode 100644 index f55db0948e..0000000000 --- a/src/UniGetUI/Pages/DialogPages/OperationFailedDialog.xaml.cs +++ /dev/null @@ -1,118 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Documents; -using Microsoft.UI.Xaml.Media; -using UniGetUI.Controls.OperationWidgets; -using UniGetUI.Core.Tools; -using UniGetUI.Interface.Widgets; -using UniGetUI.PackageOperations; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Pages.DialogPages; - -/// -/// An empty page that can be used on its own or navigated to within a Frame. -/// -public sealed partial class OperationFailedDialog : Page -{ - public event EventHandler? Close; - private readonly Paragraph par; - - private static SolidColorBrush errorColor = null!; - private static SolidColorBrush debugColor = null!; - - public OperationFailedDialog(AbstractOperation operation, OperationControl opControl) - { - this.InitializeComponent(); - - errorColor ??= (SolidColorBrush) - Application.Current.Resources["SystemFillColorCriticalBrush"]; - debugColor ??= (SolidColorBrush) - Application.Current.Resources["SystemFillColorNeutralBrush"]; - - headerContent.Text = - $"{operation.Metadata.FailureMessage}.\n" - + CoreTools.Translate( - "Please see the Command-line Output or refer to the Operation History for further information about the issue." - ); - - par = new Paragraph(); - foreach (var line in operation.GetOutput()) - { - if (line.Item2 is AbstractOperation.LineType.Information) - { - par.Inlines.Add(new Run { Text = line.Item1 + "\x0a" }); - } - else if (line.Item2 is AbstractOperation.LineType.VerboseDetails) - { - par.Inlines.Add(new Run { Text = line.Item1 + "\x0a", Foreground = debugColor }); - } - else - { - par.Inlines.Add(new Run { Text = line.Item1 + "\x0a", Foreground = errorColor }); - } - } - - CommandLineOutput.Blocks.Add(par); - - var CloseButton = new Button - { - Content = CoreTools.Translate("Close"), - HorizontalAlignment = HorizontalAlignment.Stretch, - Height = 30, - }; - CloseButton.Click += (_, _) => Close?.Invoke(this, EventArgs.Empty); - - Control _retryButton; - - var retryOptions = opControl.GetRetryOptions(() => Close?.Invoke(this, EventArgs.Empty)); - if (retryOptions.Count != 0) - { - SplitButton RetryButton = new SplitButton - { - Content = CoreTools.Translate("Retry"), - HorizontalAlignment = HorizontalAlignment.Stretch, - Height = 30, - }; - RetryButton.Click += (_, _) => - { - operation.Retry(AbstractOperation.RetryMode.Retry); - Close?.Invoke(this, EventArgs.Empty); - }; - BetterMenu menu = new(); - RetryButton.Flyout = menu; - foreach (var opt in retryOptions) - { - menu.Items.Add(opt); - } - - _retryButton = RetryButton; - } - else - { - var RetryButton = new Button - { - Content = CoreTools.Translate("Retry"), - HorizontalAlignment = HorizontalAlignment.Stretch, - Height = 30, - }; - RetryButton.Click += (_, _) => - { - operation.Retry(AbstractOperation.RetryMode.Retry); - Close?.Invoke(this, EventArgs.Empty); - }; - _retryButton = RetryButton; - } - - ButtonsLayout.Children.Add(CloseButton); - ButtonsLayout.Children.Add(_retryButton); - Grid.SetColumn(CloseButton, 1); - } - - private void CloseButton_Click(object sender, RoutedEventArgs e) - { - Close?.Invoke(this, EventArgs.Empty); - } -} diff --git a/src/UniGetUI/Pages/DialogPages/OperationLiveLogPage.xaml b/src/UniGetUI/Pages/DialogPages/OperationLiveLogPage.xaml deleted file mode 100644 index 955a520c44..0000000000 --- a/src/UniGetUI/Pages/DialogPages/OperationLiveLogPage.xaml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/DialogPages/OperationLiveLogPage.xaml.cs b/src/UniGetUI/Pages/DialogPages/OperationLiveLogPage.xaml.cs deleted file mode 100644 index 9b65eb7520..0000000000 --- a/src/UniGetUI/Pages/DialogPages/OperationLiveLogPage.xaml.cs +++ /dev/null @@ -1,85 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Documents; -using Microsoft.UI.Xaml.Media; -using UniGetUI.PackageOperations; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Pages.DialogPages; - -/// -/// An empty page that can be used on its own or navigated to within a Frame. -/// -public sealed partial class OperationLiveLogPage : Page -{ - public event EventHandler? Close; - private readonly Paragraph par; - private static SolidColorBrush errorColor = null!; - private static SolidColorBrush debugColor = null!; - private bool LastLineWasProgress; - - public OperationLiveLogPage(AbstractOperation operation) - { - this.InitializeComponent(); - errorColor ??= (SolidColorBrush) - Application.Current.Resources["SystemFillColorCriticalBrush"]; - debugColor ??= (SolidColorBrush) - Application.Current.Resources["SystemFillColorNeutralBrush"]; - par = new Paragraph() { LineHeight = 4.8 }; - TextBlock.Blocks.Clear(); - TextBlock.Blocks.Add(par); - - foreach (var line in operation.GetOutput()) - { - if (line.Item2 is AbstractOperation.LineType.Information) - { - par.Inlines.Add(new Run { Text = line.Item1 + "\x0a" }); - } - else if (line.Item2 is AbstractOperation.LineType.VerboseDetails) - { - par.Inlines.Add(new Run { Text = line.Item1 + "\x0a", Foreground = debugColor }); - } - else - { - par.Inlines.Add(new Run { Text = line.Item1 + "\x0a", Foreground = errorColor }); - } - } - } - - public void AddLine_ThreadSafe(object? sender, (string, AbstractOperation.LineType) line) => - MainApp.Dispatcher.TryEnqueue(() => - { - if (LastLineWasProgress) - par.Inlines.RemoveAt(par.Inlines.Count - 1); - - LastLineWasProgress = false; - if (line.Item2 is AbstractOperation.LineType.Information) - { - par.Inlines.Add(new Run { Text = line.Item1 + "\x0a" }); - } - else if (line.Item2 is AbstractOperation.LineType.VerboseDetails) - { - par.Inlines.Add(new Run { Text = line.Item1 + "\x0a", Foreground = debugColor }); - } - else if (line.Item2 is AbstractOperation.LineType.Error) - { - par.Inlines.Add(new Run { Text = line.Item1 + "\x0a", Foreground = errorColor }); - } - else - { - LastLineWasProgress = true; - par.Inlines.Add(new Run { Text = line.Item1 + "\x0a" }); - } - - this.TextBlock.Blocks.Clear(); - this.TextBlock.Blocks.Add(par); - this.ScrollBar.ScrollToVerticalOffset(this.ScrollBar.ScrollableHeight); - }); - - private void CloseButton_Click(object sender, RoutedEventArgs e) - { - Close?.Invoke(this, EventArgs.Empty); - } -} diff --git a/src/UniGetUI/Pages/DialogPages/PackageDetailsPage.xaml b/src/UniGetUI/Pages/DialogPages/PackageDetailsPage.xaml deleted file mode 100644 index 2673e2fb46..0000000000 --- a/src/UniGetUI/Pages/DialogPages/PackageDetailsPage.xaml +++ /dev/null @@ -1,325 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - HOMEPAGE_LABEL: - Helo - - PUBLISHER_LABEL: - Helo - - AUTHOR_LABEL: - Helo - - LICENSE_LABEL: - Helo - (Helo) - - Update date: - Helo - - SOURCE_LABEL: - Helo - - - - - - - - - - - - - - - - - - - - - - - - - - - - Package Id: - Helo - - Manifest: : - Helo - - (Installed) Version: - Helo - - - Installer type: - Helo - - Installer Url: - (Helo) - - Installer hash: - Helo - - - Download installer - - - - - Dependencies: - - - - - Release Notes: - Helo - - Release notes Url: - (Helo) - - - - - - - - diff --git a/src/UniGetUI/Pages/DialogPages/PackageDetailsPage.xaml.cs b/src/UniGetUI/Pages/DialogPages/PackageDetailsPage.xaml.cs deleted file mode 100644 index aa7938673d..0000000000 --- a/src/UniGetUI/Pages/DialogPages/PackageDetailsPage.xaml.cs +++ /dev/null @@ -1,780 +0,0 @@ -using System.Collections.ObjectModel; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Documents; -using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Media.Imaging; -using UniGetUI.Core.Logging; -using UniGetUI.Core.Tools; -using UniGetUI.Interface.Enums; -using UniGetUI.Interface.Telemetry; -using UniGetUI.Interface.Widgets; -using UniGetUI.PackageEngine; -using UniGetUI.PackageEngine.Enums; -using UniGetUI.PackageEngine.Interfaces; -using UniGetUI.PackageEngine.PackageClasses; -using UniGetUI.Pages.DialogPages; -using Windows.UI; -using Windows.UI.Text; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Interface.Dialogs -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class PackageDetailsPage : Page - { - public IPackage Package; - public IPackage? AvailablePackage; - public IPackage? UpgradablePackage; - public IPackage? InstalledPackage; - - private readonly InstallOptionsPage InstallOptionsPage; - public event EventHandler? Close; - private readonly OperationType OperationRole; - - private bool PackageHasScreenshots; - public ObservableCollection ShowableTags = []; - - private enum LayoutMode - { - Normal, - Wide, - Unset, - } - - private readonly TEL_InstallReferral InstallReferral; - - private LayoutMode __layout_mode = LayoutMode.Unset; - - public PackageDetailsPage( - IPackage package, - OperationType role, - TEL_InstallReferral referral - ) - { - if (role == OperationType.None) - role = OperationType.Install; - - InstallReferral = referral; - OperationRole = role; - Package = package; - - InitializeComponent(); - SizeChanged += PackageDetailsPage_SizeChanged; - - PackageName.Text = package.Name; - LoadingIndicator.Visibility = Visibility.Visible; - string LoadingString = CoreTools.Translate("Loading..."); - - // Basic details section - SetTextToItem(DescriptionContent, LoadingString); - SetTextToItem(HomepageUrl_Label, CoreTools.Translate("Homepage") + ": "); - SetTextToItem(HomepageUrl_Content, LoadingString); - SetTextToItem(Author_Label, CoreTools.Translate("Author") + ": "); - SetTextToItem(Author_Content, LoadingString); - SetTextToItem(Publisher_Label, CoreTools.Translate("Publisher") + ": "); - SetTextToItem(Publisher_Content, LoadingString); - SetTextToItem(License_Label, CoreTools.Translate("License") + ": "); - SetTextToItem(License_Content_Text, LoadingString); - SetTextToItem(License_Content_Uri, ""); - SetTextToItem(Source_Label, CoreTools.Translate("Package Manager") + ": "); - SetTextToItem(Source_Content, Package.Source.AsString_DisplayName); - - // Extended details section - SetTextToItem(PackageId_Label, CoreTools.Translate("Package ID") + ": "); - SetTextToItem(PackageId_Content, package.Id); - SetTextToItem(ManifestUrl_Label, CoreTools.Translate("Manifest") + ": "); - SetTextToItem(ManifestUrl_Content, LoadingString); - - SetTextToItem(InstallerType_Label, CoreTools.Translate("Installer Type") + ": "); - SetTextToItem(InstallerType_Content, LoadingString); - SetTextToItem(InstallerHash_Label, CoreTools.Translate("Installer SHA256") + ": "); - SetTextToItem(InstallerHash_Content, LoadingString); - SetTextToItem(InstallerUrl_Label, CoreTools.Translate("Installer URL") + ": "); - SetTextToItem(InstallerUrl_Content, LoadingString); - DownloadInstaller_Button.Click += DownloadInstallerButton_Click; - SetTextToItem(DownloadInstaller_Button, CoreTools.Translate("Download installer")); - SetTextToItem(UpdateDate_Label, CoreTools.Translate("Last updated:") + " "); - SetTextToItem(UpdateDate_Content, LoadingString); - SetTextToItem(Dependencies_Label, CoreTools.Translate("Dependencies:") + " "); - DependenciesParagraph.Inlines.Clear(); - DependenciesParagraph.Inlines.Add( - new Run() - { - Text = LoadingString, - Foreground = new SolidColorBrush(color: Color.FromArgb(255, 127, 127, 127)), - } - ); - SetTextToItem(ReleaseNotes_Label, CoreTools.Translate("Release notes") + ": "); - SetTextToItem(ReleaseNotes_Content, LoadingString); - SetTextToItem(ReleaseNotesUrl_Label, CoreTools.Translate("Release notes URL") + ": "); - SetTextToItem(ReleaseNotesUrl_Content, LoadingString); - - AvailablePackage = Package.GetAvailablePackage(); - UpgradablePackage = Package.GetUpgradablePackage(); - InstalledPackage = UpgradablePackage?.GetInstalledPackages().FirstOrDefault(); - - var options = InstallOptionsFactory.LoadForPackage(package); - InstallOptionsPage = new InstallOptionsPage(package, OperationRole, options); - InstallOptionsPage.Close += (_, _) => Close?.Invoke(this, EventArgs.Empty); - InstallOptionsPage.HideCloseButton(); - InstallOptionsPage.HideHeaderBar(); - InstallOptionsPage.MaxWidth = double.PositiveInfinity; - InstallOptionsPage.HorizontalAlignment = HorizontalAlignment.Stretch; - InstallOptionsPage.Margin = new(0, -16, 0, 0); - InstallOptionsExpander.Content = InstallOptionsPage; - - MainActionButton.Padding = new Thickness(0); - var textBlock = new TextBlock() - { - TextWrapping = TextWrapping.WrapWholeWords, - Padding = new Thickness(0), - TextAlignment = TextAlignment.Center, - }; - MainActionButton.Content = textBlock; - if (OperationRole is OperationType.Install) - { - textBlock.Text = CoreTools.Translate("Install"); - SetTextToItem(Version_Label, CoreTools.Translate("Version")); - SetTextToItem(Version_Content, AvailablePackage?.VersionString ?? "NULL"); - SetUpActionButtonAsInstall(); - } - else if (OperationRole is OperationType.Update) - { - textBlock.Text = CoreTools.Translate( - "Update to version {0}", - UpgradablePackage?.NewVersionString ?? "NULL" - ); - SetTextToItem(Version_Label, CoreTools.Translate("Installed Version")); - SetTextToItem( - Version_Content, - (UpgradablePackage?.VersionString ?? "NULL") - + $" - {CoreTools.Translate("Update to {0} available", UpgradablePackage?.NewVersionString ?? "NULL")}" - ); - SetUpActionButtonAsUpdate(); - } - else /* OperationRole is OperationType.Uninstall */ - { - textBlock.Text = CoreTools.Translate("Uninstall"); - SetTextToItem(Version_Label, CoreTools.Translate("Installed Version")); - SetTextToItem(Version_Content, InstalledPackage?.VersionString ?? "NULL"); - SetUpActionButtonAsUninstall(); - } - _ = LoadInformation(); - - TelemetryHandler.PackageDetails(package, referral.ToString()); - } - - public void SetUpActionButtonAsInstall() - { - var AsAdmin = new BetterMenuItem - { - Text = CoreTools.Translate("Install as administrator"), - IconName = IconType.UAC, - IsEnabled = Package.Manager.Capabilities.CanRunAsAdmin, - }; - var Interactive = new BetterMenuItem - { - Text = CoreTools.Translate("Interactive installation"), - IconName = IconType.Interactive, - IsEnabled = Package.Manager.Capabilities.CanRunInteractively, - }; - var SkipHash = new BetterMenuItem - { - Text = CoreTools.Translate("Skip hash check"), - IconName = IconType.Checksum, - IsEnabled = Package.Manager.Capabilities.CanSkipIntegrityChecks, - }; - - AsAdmin.Click += (_, _) => _ = DoAction(Package, OperationType.Install, AsAdmin: true); - Interactive.Click += (_, _) => - _ = DoAction(Package, OperationType.Install, Interactive: true); - SkipHash.Click += (_, _) => - _ = DoAction(Package, OperationType.Install, SkipHash: true); - - ExtendedActionsMenu.Items.Add(AsAdmin); - ExtendedActionsMenu.Items.Add(Interactive); - ExtendedActionsMenu.Items.Add(SkipHash); - - if (UpgradablePackage is not null) - { - ExtendedActionsMenu.Items.Add(new MenuFlyoutSeparator()); - var Upgrade = new BetterMenuItem - { - Text = CoreTools.Translate( - "Update to version {0}", - UpgradablePackage.NewVersionString - ), - IconName = IconType.Update, - }; - Upgrade.Click += (_, _) => _ = DoAction(UpgradablePackage, OperationType.Update); - ExtendedActionsMenu.Items.Add(Upgrade); - } - - if (InstalledPackage is not null) - { - ExtendedActionsMenu.Items.Add(new MenuFlyoutSeparator()); - var Uninstall = new BetterMenuItem - { - Text = CoreTools.Translate("Uninstall"), - IconName = IconType.Delete, - }; - Uninstall.Click += (_, _) => - _ = DoAction(InstalledPackage, OperationType.Uninstall); - ExtendedActionsMenu.Items.Add(Uninstall); - } - } - - public void SetUpActionButtonAsUpdate() - { - var AsAdmin = new BetterMenuItem - { - Text = CoreTools.Translate("Update as administrator"), - IconName = IconType.UAC, - IsEnabled = Package.Manager.Capabilities.CanRunAsAdmin, - }; - var Interactive = new BetterMenuItem - { - Text = CoreTools.Translate("Interactive update"), - IconName = IconType.Interactive, - IsEnabled = Package.Manager.Capabilities.CanRunInteractively, - }; - var SkipHash = new BetterMenuItem - { - Text = CoreTools.Translate("Skip hash check"), - IconName = IconType.Checksum, - IsEnabled = Package.Manager.Capabilities.CanSkipIntegrityChecks, - }; - - AsAdmin.Click += (_, _) => _ = DoAction(Package, OperationType.Update, AsAdmin: true); - Interactive.Click += (_, _) => - _ = DoAction(Package, OperationType.Update, Interactive: true); - SkipHash.Click += (_, _) => _ = DoAction(Package, OperationType.Update, SkipHash: true); - - ExtendedActionsMenu.Items.Add(AsAdmin); - ExtendedActionsMenu.Items.Add(Interactive); - ExtendedActionsMenu.Items.Add(SkipHash); - - if (InstalledPackage is not null) - { - ExtendedActionsMenu.Items.Add(new MenuFlyoutSeparator()); - var Uninstall = new BetterMenuItem - { - Text = CoreTools.Translate("Uninstall"), - IconName = IconType.Delete, - }; - Uninstall.Click += (_, _) => - _ = DoAction(InstalledPackage, OperationType.Uninstall); - ExtendedActionsMenu.Items.Add(Uninstall); - } - - ExtendedActionsMenu.Items.Add(new MenuFlyoutSeparator()); - var Reinstall = new BetterMenuItem - { - Text = CoreTools.Translate("Reinstall"), - IconName = IconType.Download, - }; - Reinstall.Click += (_, _) => _ = DoAction(Package, OperationType.Install); - ExtendedActionsMenu.Items.Add(Reinstall); - } - - public void SetUpActionButtonAsUninstall() - { - var AsAdmin = new BetterMenuItem - { - Text = CoreTools.Translate("Uninstall as administrator"), - IconName = IconType.UAC, - IsEnabled = Package.Manager.Capabilities.CanRunAsAdmin, - }; - var Interactive = new BetterMenuItem - { - Text = CoreTools.Translate("Interactive uninstall"), - IconName = IconType.Interactive, - IsEnabled = Package.Manager.Capabilities.CanRunInteractively, - }; - var RemoveData = new BetterMenuItem - { - Text = CoreTools.Translate("Uninstall and remove data"), - IconName = IconType.Close_Round, - IsEnabled = Package.Manager.Capabilities.CanRemoveDataOnUninstall, - }; - - AsAdmin.Click += (_, _) => - _ = DoAction(Package, OperationType.Uninstall, AsAdmin: true); - Interactive.Click += (_, _) => - _ = DoAction(Package, OperationType.Uninstall, Interactive: true); - RemoveData.Click += (_, _) => - _ = DoAction(Package, OperationType.Uninstall, RemoveData: true); - - ExtendedActionsMenu.Items.Add(AsAdmin); - ExtendedActionsMenu.Items.Add(Interactive); - ExtendedActionsMenu.Items.Add(RemoveData); - - if (UpgradablePackage is not null) - { - ExtendedActionsMenu.Items.Add(new MenuFlyoutSeparator()); - var Upgrade = new BetterMenuItem - { - Text = CoreTools.Translate( - "Update to version {0}", - UpgradablePackage.NewVersionString - ), - IconName = IconType.Update, - }; - Upgrade.Click += (_, _) => _ = DoAction(UpgradablePackage, OperationType.Update); - ExtendedActionsMenu.Items.Add(Upgrade); - } - - ExtendedActionsMenu.Items.Add(new MenuFlyoutSeparator()); - var Reinstall = new BetterMenuItem - { - Text = CoreTools.Translate("Reinstall"), - IconName = IconType.Download, - }; - Reinstall.Click += (_, _) => _ = DoAction(Package, OperationType.Install); - ExtendedActionsMenu.Items.Add(Reinstall); - } - - public async Task LoadInformation() - { - LoadingIndicator.Visibility = Visibility.Visible; - - _ = LoadIcon(); - _ = LoadScreenshots(); - - IPackageDetails details = Package.Details; - if (!details.IsPopulated) - { - await details.Load(); - } - - LoadingIndicator.Visibility = Visibility.Collapsed; - - // Basic details section - SetTextToItem(DescriptionContent, details.Description); - SetTextToItem(HomepageUrl_Content, details.HomepageUrl); - SetTextToItem(Author_Content, details.Author); - SetTextToItem(Publisher_Content, details.Publisher); - - if (details.License is not null && details.LicenseUrl is not null) - { - SetTextToItem(License_Content_Text, details.License); - SetTextToItem(License_Content_Uri, details.LicenseUrl, "(", ")"); - } - else if (details.License is not null && details.LicenseUrl is null) - { - SetTextToItem(License_Content_Text, details.License); - SetTextToItem(License_Content_Uri, ""); - } - else if (details.License is null && details.LicenseUrl is not null) - { - SetTextToItem(License_Content_Text, ""); - SetTextToItem(License_Content_Uri, details.LicenseUrl); - } - else - { - SetTextToItem(License_Content_Text, null); - SetTextToItem(License_Content_Uri, details.LicenseUrl); - } - - // Extended details section - SetTextToItem(ManifestUrl_Content, details.ManifestUrl); - if (Package.Manager == PEInterface.Chocolatey) - { - SetTextToItem(InstallerHash_Label, CoreTools.Translate("Installer SHA512") + ": "); - } - else - { - SetTextToItem(InstallerHash_Label, CoreTools.Translate("Installer SHA256") + ": "); - } - - SetTextToItem(InstallerHash_Content, details.InstallerHash); - if (Package.Manager.Capabilities.CanDownloadInstaller) - { - SetTextToItem( - InstallerSize_Content, - details.InstallerSize > 0 - ? $" ({CoreTools.FormatAsSize(details.InstallerSize, 2)})" - : $" ({CoreTools.Translate("Unknown size")})" - ); - SetTextToItem(DownloadInstaller_Button, CoreTools.Translate("Download installer")); - } - else - { - SetTextToItem(InstallerSize_Content, ""); - SetTextToItem( - DownloadInstaller_Button, - CoreTools.Translate("Installer not available") - ); - } - SetTextToItem(InstallerUrl_Content, details.InstallerUrl); - SetTextToItem(InstallerType_Content, details.InstallerType); - SetTextToItem(UpdateDate_Content, details.UpdateDate); - SetTextToItem(ReleaseNotes_Content, details.ReleaseNotes); - SetTextToItem(ReleaseNotesUrl_Content, details.ReleaseNotesUrl); - - if (!details.Package.Manager.Capabilities.CanListDependencies) - { - DependenciesParagraph.Inlines.Clear(); - DependenciesParagraph.Inlines.Add( - new Run() - { - Text = CoreTools.Translate("Not available"), - Foreground = new SolidColorBrush(color: Color.FromArgb(255, 127, 127, 127)), - } - ); - } - else if (details.Dependencies.Any()) - { - DependenciesParagraph.Inlines.Clear(); - - foreach (var dep in details.Dependencies) - { - DependenciesParagraph.Inlines.Add( - new Run() - { - Text = $"  • {dep.Name}", - FontStyle = dep.Mandatory ? FontStyle.Normal : FontStyle.Italic, - FontWeight = new FontWeight(600), - } - ); - - string line = $" ("; - if (dep.Version.Any()) - line += CoreTools.Translate("Version:") + $" {dep.Version}, "; - line += - $"{(dep.Mandatory ? CoreTools.Translate("mandatory") : CoreTools.Translate("optional"))})"; - - DependenciesParagraph.Inlines.Add( - new Run() - { - Text = line, - FontStyle = dep.Mandatory ? FontStyle.Normal : FontStyle.Italic, - } - ); - DependenciesParagraph.Inlines.Add(new LineBreak()); - } - if ( - DependenciesParagraph.Inlines.Any() - && DependenciesParagraph.Inlines.Last() is LineBreak - ) - DependenciesParagraph.Inlines.RemoveAt(DependenciesParagraph.Inlines.Count - 1); - } - else - { - DependenciesParagraph.Inlines.Clear(); - DependenciesParagraph.Inlines.Add( - new Run() - { - Text = "\t" + CoreTools.Translate("No dependencies specified"), - Foreground = new SolidColorBrush(color: Color.FromArgb(255, 127, 127, 127)), - } - ); - } - - ShowableTags.Clear(); - foreach (string tag in details.Tags) - { - ShowableTags.Add( - new TextBlock - { - Text = tag, - VerticalAlignment = VerticalAlignment.Center, - TextLineBounds = TextLineBounds.Tight, - } - ); - } - } - - public void SetTextToItem(Run r, string? s) - { - if (s is null) - { - r.Text = CoreTools.Translate("Not available"); - r.Foreground = new SolidColorBrush(color: Color.FromArgb(255, 127, 127, 127)); - } - else - { - r.Text = s; - r.ClearValue(TextElement.ForegroundProperty); - } - } - - public void SetTextToItem(Hyperlink h, Uri? u, string prefix = "", string suffix = "") - { - if (u is null) - { - h.Inlines.Clear(); - h.Inlines.Add( - new Run - { - Text = CoreTools.Translate("Not available"), - TextDecorations = TextDecorations.None, - Foreground = new SolidColorBrush(color: Color.FromArgb(255, 127, 127, 127)), - } - ); - h.NavigateUri = u; - } - else - { - h.Inlines.Clear(); - h.Inlines.Add(new Run { Text = prefix + u.ToString() + suffix }); - h.NavigateUri = u; - } - } - - public void SetTextToItem(Hyperlink h, string s) - { - h.Inlines.Clear(); - h.Inlines.Add(new Run { Text = s }); - h.NavigateUri = null; - } - - public async Task LoadIcon() - { - PackageIcon.Source = new BitmapImage { UriSource = await Task.Run(Package.GetIconUrl) }; - } - - public async Task LoadScreenshots() - { - IReadOnlyList screenshots = await Task.Run(Package.GetScreenshots); - PackageHasScreenshots = screenshots.Any(); - if (PackageHasScreenshots) - { - PackageHasScreenshots = true; - IconsExtraBanner.Visibility = Visibility.Visible; - ScreenshotsCarroussel.Items.Clear(); - foreach (Uri image in screenshots) - { - ScreenshotsCarroussel.Items.Add(new Image { Source = new BitmapImage(image) }); - } - } - - __layout_mode = LayoutMode.Unset; - PackageDetailsPage_SizeChanged(); - } - - public void DownloadInstallerButton_Click(object sender, RoutedEventArgs e) - { - if (!Package.Manager.Capabilities.CanDownloadInstaller) - return; - Close?.Invoke(this, EventArgs.Empty); - _ = MainApp.Operations.AskLocationAndDownload(Package, InstallReferral); - } - - public void CloseButton_Click(object sender, RoutedEventArgs e) - { - Close?.Invoke(this, EventArgs.Empty); - } - - public void PackageDetailsPage_SizeChanged( - object? sender = null, - SizeChangedEventArgs? e = null - ) - { - if (ActualWidth < 950) - { - if (__layout_mode != LayoutMode.Normal) - { - __layout_mode = LayoutMode.Normal; - - MainGrid.ColumnDefinitions.Clear(); - MainGrid.ColumnDefinitions.Add( - new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) } - ); - Grid.SetColumn(TitlePanel, 0); - Grid.SetColumn(BasicInfoPanelText, 0); - Grid.SetColumn(ScreenshotsPanel, 0); - Grid.SetColumn(ActionsPanel, 0); - Grid.SetColumn(InstallOptionsBorder, 0); - Grid.SetColumn(DetailsPanelText, 0); - - MainGrid.RowDefinitions.Clear(); - MainGrid.RowDefinitions.Add( - new RowDefinition { Height = new GridLength(1, GridUnitType.Auto) } - ); - MainGrid.RowDefinitions.Add( - new RowDefinition { Height = new GridLength(1, GridUnitType.Auto) } - ); - MainGrid.RowDefinitions.Add( - new RowDefinition { Height = new GridLength(1, GridUnitType.Auto) } - ); - MainGrid.RowDefinitions.Add( - new RowDefinition { Height = new GridLength(1, GridUnitType.Auto) } - ); - MainGrid.RowDefinitions.Add( - new RowDefinition { Height = new GridLength(1, GridUnitType.Auto) } - ); - MainGrid.RowDefinitions.Add( - new RowDefinition { Height = new GridLength(1, GridUnitType.Auto) } - ); - MainGrid.RowDefinitions.Add( - new RowDefinition { Height = new GridLength(1, GridUnitType.Auto) } - ); - Grid.SetRow(TitlePanel, 0); - Grid.SetRow(DescriptionPanel, 1); - Grid.SetRow(BasicInfoPanelText, 2); - Grid.SetRow(ActionsPanel, 3); - Grid.SetRow(InstallOptionsBorder, 4); - Grid.SetRow(ScreenshotsPanel, 5); - Grid.SetRow(DetailsPanelText, 6); - - LeftPanel.Children.Clear(); - RightPanel.Children.Clear(); - MainGrid.Children.Clear(); - MainGrid.Children.Add(TitlePanel); - MainGrid.Children.Add(DescriptionPanel); - MainGrid.Children.Add(BasicInfoPanelText); - MainGrid.Children.Add(ScreenshotsPanel); - MainGrid.Children.Add(ActionsPanel); - MainGrid.Children.Add(InstallOptionsBorder); - MainGrid.Children.Add(DetailsPanelText); - ScreenshotsCarroussel.Height = PackageHasScreenshots ? 225 : 150; - - InstallOptionsExpander.IsExpanded = false; - } - } - else - { - if (__layout_mode != LayoutMode.Wide) - { - __layout_mode = LayoutMode.Wide; - - MainGrid.ColumnDefinitions.Clear(); - MainGrid.ColumnDefinitions.Add( - new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) } - ); - MainGrid.ColumnDefinitions.Add( - new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) } - ); - Grid.SetColumn(LeftPanel, 0); - Grid.SetColumn(RightPanel, 1); - Grid.SetColumn(TitlePanel, 0); - Grid.SetColumnSpan(TitlePanel, 1); - - MainGrid.RowDefinitions.Clear(); - MainGrid.RowDefinitions.Add( - new RowDefinition { Height = new GridLength(1, GridUnitType.Auto) } - ); - Grid.SetRow(LeftPanel, 0); - Grid.SetRow(RightPanel, 0); - - LeftPanel.Children.Clear(); - RightPanel.Children.Clear(); - MainGrid.Children.Clear(); - - Grid.SetRow(TitlePanel, 0); - Grid.SetRow(DescriptionPanel, 1); - Grid.SetRow(BasicInfoPanelText, 2); - Grid.SetRow(ActionsPanel, 3); - Grid.SetRow(InstallOptionsBorder, 4); - - LeftPanel.Children.Add(TitlePanel); - LeftPanel.Children.Add(DescriptionPanel); - LeftPanel.Children.Add(BasicInfoPanelText); - LeftPanel.Children.Add(ActionsPanel); - LeftPanel.Children.Add(InstallOptionsBorder); - - Grid.SetRow(ScreenshotsPanel, 0); - Grid.SetRow(DetailsPanelText, 1); - - RightPanel.Children.Add(ScreenshotsPanel); - RightPanel.Children.Add(DetailsPanelText); - ScreenshotsCarroussel.Height = PackageHasScreenshots ? 400 : 150; - - InstallOptionsExpander.IsExpanded = true; - - MainGrid.Children.Add(LeftPanel); - MainGrid.Children.Add(RightPanel); - } - } - } - - public void ActionButton_Click(object sender, RoutedEventArgs e) - { - _ = DoAction(Package, OperationRole); - } - - public async Task DoAction( - IPackage package, - OperationType action, - bool? AsAdmin = null, - bool? Interactive = null, - bool? SkipHash = null, - bool? RemoveData = null - ) - { - Close?.Invoke(this, EventArgs.Empty); - - var newOptions = await InstallOptionsPage.GetUpdatedOptions(); - await InstallOptionsFactory.SaveForPackageAsync(newOptions, package); - - if (AsAdmin is not null) - newOptions.RunAsAdministrator = (bool)AsAdmin; - if (Interactive is not null) - newOptions.InteractiveInstallation = (bool)Interactive; - if (SkipHash is not null) - newOptions.SkipHashCheck = (bool)SkipHash; - if (RemoveData is not null) - newOptions.RemoveDataOnUninstall = (bool)RemoveData; - - if (action is OperationType.Install) - { - _ = MainApp.Operations.Install( - package, - InstallReferral, - AsAdmin, - Interactive, - SkipHash - ); - } - else if (action is OperationType.Uninstall) - { - _ = MainApp.Operations.ConfirmAndUninstall( - package, - AsAdmin, - Interactive, - RemoveData - ); - } - else if (action is OperationType.Update) - { - _ = MainApp.Operations.Update(package, AsAdmin, Interactive, SkipHash); - } - else - { - throw new ArgumentException( - "PackageDetailsPage.DoAction should never be called with action=None" - ); - } - } - - private void SaveInstallOptionsButton_Click(object sender, RoutedEventArgs e) => - _ = _saveInstallOptionsButton_Click(); - - private async Task _saveInstallOptionsButton_Click() - { - try - { - SaveInstallOptionsButton.IsEnabled = false; - SaveInstallOptionsButton.Content = new FontIcon { Glyph = "\uE9F5" }; - var options = await InstallOptionsPage.GetUpdatedOptions(); - await InstallOptionsFactory.SaveForPackageAsync(options, Package); - await Task.Delay(400); // Give feedback to the user that things are being done - SaveInstallOptionsButton.Content = new FontIcon { Glyph = "\uE73E" }; - SaveInstallOptionsButton.IsEnabled = true; - await Task.Delay(2000); - SaveInstallOptionsButton.Content = CoreTools.Translate("Save"); - } - catch (Exception ex) - { - Logger.Error("An error occurred while saving install options"); - Logger.Error(ex); - } - } - } -} diff --git a/src/UniGetUI/Pages/DialogPages/ReleaseNotes.xaml b/src/UniGetUI/Pages/DialogPages/ReleaseNotes.xaml deleted file mode 100644 index 5a9998a3d6..0000000000 --- a/src/UniGetUI/Pages/DialogPages/ReleaseNotes.xaml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/DialogPages/ReleaseNotes.xaml.cs b/src/UniGetUI/Pages/DialogPages/ReleaseNotes.xaml.cs deleted file mode 100644 index a54c06fd8e..0000000000 --- a/src/UniGetUI/Pages/DialogPages/ReleaseNotes.xaml.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using UniGetUI.Core.Data; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Interface.Dialogs -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class ReleaseNotes : Page, IDisposable - { - public event EventHandler? Close; - - public ReleaseNotes() - { - InitializeComponent(); - _ = InitializeWebView(); - - WebView.NavigationStarting += (_, _) => - { - ProgressBar.Visibility = Visibility.Visible; - }; - WebView.NavigationCompleted += (_, _) => - { - ProgressBar.Visibility = Visibility.Collapsed; - }; - } - - private async Task InitializeWebView() - { - await WebView.EnsureCoreWebView2Async(); - WebView.Source = new Uri(CoreData.ReleaseNotesUrl); - } - - public void Dispose() - { - WebView.Close(); - } - - private void CloseButton_Click(object sender, RoutedEventArgs e) - { - Close?.Invoke(this, EventArgs.Empty); - } - } -} diff --git a/src/UniGetUI/Pages/HelpPage.xaml b/src/UniGetUI/Pages/HelpPage.xaml deleted file mode 100644 index d2f7150bfb..0000000000 --- a/src/UniGetUI/Pages/HelpPage.xaml +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/HelpPage.xaml.cs b/src/UniGetUI/Pages/HelpPage.xaml.cs deleted file mode 100644 index b16d3374ee..0000000000 --- a/src/UniGetUI/Pages/HelpPage.xaml.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System.Diagnostics; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using UniGetUI.Core.Tools; -using UniGetUI.Interface.Pages; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Interface.Dialogs -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class HelpPage : Page, IDisposable, IEnterLeaveListener - { - private static readonly Uri HelpUri = new("https://github.com/Devolutions/UniGetUI"); - private bool Initialized; - private WebView2? webView; - private Uri? lastUri; - - public HelpPage() - { - InitializeComponent(); - _ = InitializeWebView(); - } - - private async Task InitializeWebView() - { - webView = new(); - WebViewBorder.Child = webView; - webView.NavigationStarting += (_, e) => - { - ProgressBar.Visibility = Visibility.Visible; - lastUri = new Uri(e.Uri); - }; - webView.NavigationCompleted += (_, _) => - { - ProgressBar.Visibility = Visibility.Collapsed; - }; - - await webView.EnsureCoreWebView2Async(); - NavigateTo("", skipWait: true); - Initialized = true; - } - - public void NavigateTo(string piece, bool skipWait = false) => - _ = _navigateTo(piece, skipWait); - - private async Task _navigateTo(string piece, bool skipWait) - { - while (!Initialized && !skipWait) - await Task.Delay(50); - ArgumentNullException.ThrowIfNull(webView); - webView.Source = HelpUri; - } - - private void BackButton_Click(object sender, RoutedEventArgs e) - { - if (Initialized && webView is not null && webView.CanGoBack) - { - webView.GoBack(); - } - } - - private void RightButton_Click(object sender, RoutedEventArgs e) - { - if (Initialized && webView is not null && webView.CanGoForward) - { - webView.GoForward(); - } - } - - private void HomeButton_Click(object sender, RoutedEventArgs e) - { - if (!Initialized || webView is null) - return; - - webView.Source = HelpUri; - } - - private void ReloadButton_Click(object sender, RoutedEventArgs e) - { - if (!Initialized || webView is null) - return; - - webView.Reload(); - } - - private void BrowserButton_Click(object sender, RoutedEventArgs e) - { - if (!Initialized || webView is null) - return; - - CoreTools.Launch(webView.Source.ToString()); - } - - public void Dispose() - { - webView?.Close(); - WebViewBorder.Child = new UserControl(); - webView = null; - Initialized = false; - } - - public void OnEnter() - { - if (webView is null) - _ = InitializeWebView(); - } - - public void OnLeave() - { - webView?.Close(); - WebViewBorder.Child = new UserControl(); - webView = null; - Initialized = false; - } - } -} diff --git a/src/UniGetUI/Pages/LogPages/LogPage.xaml b/src/UniGetUI/Pages/LogPages/LogPage.xaml deleted file mode 100644 index 7bdea35cc9..0000000000 --- a/src/UniGetUI/Pages/LogPages/LogPage.xaml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/LogPages/LogPage.xaml.cs b/src/UniGetUI/Pages/LogPages/LogPage.xaml.cs deleted file mode 100644 index 42fc0b3d72..0000000000 --- a/src/UniGetUI/Pages/LogPages/LogPage.xaml.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System.Diagnostics; -using ExternalLibraries.Clipboard; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using UniGetUI.Core.Tools; -using Windows.Storage; -using Windows.Storage.Pickers; -using Windows.UI; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Interface.Pages -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public abstract partial class BaseLogPage : IKeyboardShortcutListener, IEnterLeaveListener - { - protected int LOG_LEVEL = 4; - - protected abstract void LoadLogLevels(); - public abstract void LoadLog(bool isReload = false); - - public BaseLogPage(bool log_level_enabled) - { - InitializeComponent(); - LogLevelPane.Visibility = log_level_enabled ? Visibility.Visible : Visibility.Collapsed; - if (log_level_enabled) - LoadLogLevels(); - - ActualThemeChanged += (_, _) => LoadLog(); - } - - protected void SelectLogLevelByName(string name) - { - LogLevelCombo.SelectedValue = name; - } - - public void ReloadTriggered() => LoadLog(isReload: true); - - public void SelectAllTriggered() => LogTextBox.SelectAll(); - - public void SearchTriggered() { } - - // Dark theme colors - protected Color DARK_GREY = Color.FromArgb(255, 130, 130, 130); - protected Color DARK_LIGHT_GREY = Color.FromArgb(255, 190, 190, 190); - protected Color DARK_WHITE = Color.FromArgb(255, 250, 250, 250); - protected Color DARK_YELLOW = Color.FromArgb(255, 255, 255, 90); - protected Color DARK_RED = Color.FromArgb(255, 255, 80, 80); - protected Color DARK_GREEN = Color.FromArgb(255, 80, 255, 80); - protected Color DARK_BLUE = Color.FromArgb(255, 120, 120, 255); - - // Light theme colors - protected Color LIGHT_GREY = Color.FromArgb(255, 125, 125, 225); - protected Color LIGHT_LIGHT_GREY = Color.FromArgb(255, 50, 50, 150); - protected Color LIGHT_WHITE = Color.FromArgb(255, 0, 0, 0); - protected Color LIGHT_YELLOW = Color.FromArgb(255, 150, 150, 0); - protected Color LIGHT_RED = Color.FromArgb(255, 205, 0, 0); - protected Color LIGHT_GREEN = Color.FromArgb(255, 0, 205, 0); - protected Color LIGHT_BLUE = Color.FromArgb(255, 0, 0, 205); - - public void CopyButton_Click(object sender, RoutedEventArgs e) - { - LogTextBox.SelectAll(); - WindowsClipboard.SetText(LogTextBox.SelectedText); - LogTextBox.Select(LogTextBox.SelectionStart, LogTextBox.SelectionStart); - } - - public void ExportButton_Click(object sender, RoutedEventArgs e) => - _ = _exportButton_Click(); - - public async Task _exportButton_Click() - { - FileSavePicker savePicker = new() - { - SuggestedStartLocation = PickerLocationId.DocumentsLibrary, - }; - WinRT.Interop.InitializeWithWindow.Initialize( - savePicker, - WinRT.Interop.WindowNative.GetWindowHandle(MainApp.Instance.MainWindow) - ); - savePicker.FileTypeChoices.Add(CoreTools.Translate("Text"), [".txt"]); - savePicker.SuggestedFileName = CoreTools.Translate("UniGetUI Log"); - - StorageFile file = await savePicker.PickSaveFileAsync(); - if (file is not null) - { - LogTextBox.SelectAll(); - await File.WriteAllTextAsync(file.Path, LogTextBox.SelectedText); - LogTextBox.Select(LogTextBox.SelectionStart, LogTextBox.SelectionStart); - await CoreTools.ShowFileOnExplorer(file.Path); - } - } - - public void ReloadButton_Click(object sender, RoutedEventArgs e) => LoadLog(); - - private void LogLevelCombo_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - LOG_LEVEL = LogLevelCombo.SelectedIndex + 1; - LoadLog(); - } - - public void OnEnter() => LoadLog(); - - public void OnLeave() => LogTextBox.Blocks.Clear(); - } -} diff --git a/src/UniGetUI/Pages/LogPages/ManagerLogsPage.cs b/src/UniGetUI/Pages/LogPages/ManagerLogsPage.cs deleted file mode 100644 index 261870eafa..0000000000 --- a/src/UniGetUI/Pages/LogPages/ManagerLogsPage.cs +++ /dev/null @@ -1,91 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Documents; -using Microsoft.UI.Xaml.Media; -using UniGetUI.Core.Tools; -using UniGetUI.PackageEngine; -using UniGetUI.PackageEngine.Interfaces; -using UniGetUI.PackageEngine.ManagerClasses.Classes; - -namespace UniGetUI.Interface.Pages.LogPage -{ - public partial class ManagerLogsPage : BaseLogPage - { - public ManagerLogsPage() - : base(true) { } - - public void LoadForManager(IPackageManager manager) - { - bool IS_DARK = this.ActualTheme == ElementTheme.Dark; - bool verbose = - LogLevelCombo.SelectedValue?.ToString()?.Contains(CoreTools.Translate("Verbose")) - ?? false; - - if (!verbose) - SelectLogLevelByName(manager.DisplayName); - - IManagerLogger TaskLogger = manager.TaskLogger; - LogTextBox.Blocks.Clear(); - Paragraph versionParagraph = new(); - versionParagraph.Inlines.Add( - new Run { Text = $"Manager {manager.DisplayName} with version:\n" } - ); - versionParagraph.Inlines.Add(new Run { Text = manager.Status.Version }); - versionParagraph.Inlines.Add( - new Run { Text = "\n\n——————————————————————————————————————————\n\n" } - ); - LogTextBox.Blocks.Add(versionParagraph); - - foreach (ITaskLogger operation in TaskLogger.Operations) - { - Paragraph p = new(); - foreach (string line in operation.AsColoredString(verbose)) - { - Brush color = line[0] switch - { - '0' => new SolidColorBrush { Color = IS_DARK ? DARK_WHITE : LIGHT_WHITE }, - '1' => new SolidColorBrush - { - Color = IS_DARK ? DARK_LIGHT_GREY : LIGHT_LIGHT_GREY, - }, - '2' => new SolidColorBrush { Color = IS_DARK ? DARK_RED : LIGHT_RED }, - '3' => new SolidColorBrush { Color = IS_DARK ? DARK_BLUE : LIGHT_BLUE }, - '4' => new SolidColorBrush { Color = IS_DARK ? DARK_GREEN : LIGHT_GREEN }, - '5' => new SolidColorBrush { Color = IS_DARK ? DARK_YELLOW : LIGHT_YELLOW }, - _ => new SolidColorBrush { Color = IS_DARK ? DARK_YELLOW : LIGHT_YELLOW }, - }; - p.Inlines.Add(new Run { Text = line[1..] + "\n", Foreground = color }); - } - ((Run)p.Inlines[^1]).Text = ((Run)p.Inlines[^1]).Text.TrimEnd(); - LogTextBox.Blocks.Add(p); - } - } - - public override void LoadLog(bool isReload = false) - { - foreach (IPackageManager manager in PEInterface.Managers) - { - if (LogLevelCombo.SelectedValue?.ToString()?.Contains(manager.DisplayName) ?? false) - { - LoadForManager(manager); - break; - } - - if (isReload) - MainScroller.ScrollToVerticalOffset(MainScroller.ScrollableHeight); - } - } - - protected override void LoadLogLevels() - { - LogLevelCombo.Items.Clear(); - foreach (IPackageManager manager in PEInterface.Managers) - { - LogLevelCombo.Items.Add(manager.DisplayName); - LogLevelCombo.Items.Add( - $"{manager.DisplayName} ({CoreTools.Translate("Verbose")})" - ); - } - LogLevelCombo.SelectedIndex = 0; - } - } -} diff --git a/src/UniGetUI/Pages/LogPages/OperationHistoryPage.cs b/src/UniGetUI/Pages/LogPages/OperationHistoryPage.cs deleted file mode 100644 index 6be9bd8208..0000000000 --- a/src/UniGetUI/Pages/LogPages/OperationHistoryPage.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.UI.Xaml.Documents; -using UniGetUI.Core.SettingsEngine; - -namespace UniGetUI.Interface.Pages.LogPage -{ - public partial class OperationHistoryPage : BaseLogPage - { - public OperationHistoryPage() - : base(false) { } - - public override void LoadLog(bool isReload = false) - { - Paragraph paragraph = new(); - foreach (string line in Settings.GetValue(Settings.K.OperationHistory).Split("\n")) - { - if (line.Replace("\r", "").Replace("\n", "").Trim() == "") - { - continue; - } - - paragraph.Inlines.Add(new Run { Text = line.Replace("\r", "").Replace("\n", "") }); - paragraph.Inlines.Add(new LineBreak()); - } - LogTextBox.Blocks.Clear(); - LogTextBox.Blocks.Add(paragraph); - } - - protected override void LoadLogLevels() - { - throw new NotImplementedException(); - } - } -} diff --git a/src/UniGetUI/Pages/LogPages/UniGetUILogPage.cs b/src/UniGetUI/Pages/LogPages/UniGetUILogPage.cs deleted file mode 100644 index be7bff406d..0000000000 --- a/src/UniGetUI/Pages/LogPages/UniGetUILogPage.cs +++ /dev/null @@ -1,135 +0,0 @@ -using Microsoft.UI.Xaml.Documents; -using Microsoft.UI.Xaml.Media; -using UniGetUI.Core.Logging; -using UniGetUI.Core.Tools; - -namespace UniGetUI.Interface.Pages.LogPage -{ - public sealed partial class UniGetUILogPage : BaseLogPage - { - public UniGetUILogPage() - : base(true) { } - - protected override void LoadLogLevels() - { - LogLevelCombo.Items.Clear(); - LogLevelCombo.Items.Add(CoreTools.Translate("1 - Errors")); - LogLevelCombo.Items.Add(CoreTools.Translate("2 - Warnings")); - LogLevelCombo.Items.Add(CoreTools.Translate("3 - Information (less)")); - LogLevelCombo.Items.Add(CoreTools.Translate("4 - Information (more)")); - LogLevelCombo.Items.Add(CoreTools.Translate("5 - information (debug)")); - LogLevelCombo.SelectedIndex = 3; -#if DEBUG - LogLevelCombo.SelectedIndex = 4; -#endif - } - - public override void LoadLog(bool isReload = false) - { - bool IS_DARK = ActualTheme == Microsoft.UI.Xaml.ElementTheme.Dark; - - LogEntry[] logs = Logger.GetLogs(); - LogTextBox.Blocks.Clear(); - foreach (LogEntry log_entry in logs) - { - Paragraph p = new(); - if (log_entry.Content == "") - { - continue; - } - - if ( - LOG_LEVEL == 1 - && ( - log_entry.Severity == LogEntry.SeverityLevel.Debug - || log_entry.Severity == LogEntry.SeverityLevel.Info - || log_entry.Severity == LogEntry.SeverityLevel.Success - || log_entry.Severity == LogEntry.SeverityLevel.Warning - ) - ) - { - continue; - } - - if ( - LOG_LEVEL == 2 - && ( - log_entry.Severity == LogEntry.SeverityLevel.Debug - || log_entry.Severity == LogEntry.SeverityLevel.Info - || log_entry.Severity == LogEntry.SeverityLevel.Success - ) - ) - { - continue; - } - - if ( - LOG_LEVEL == 3 - && ( - log_entry.Severity == LogEntry.SeverityLevel.Debug - || log_entry.Severity == LogEntry.SeverityLevel.Info - ) - ) - { - continue; - } - - if (LOG_LEVEL == 4 && (log_entry.Severity == LogEntry.SeverityLevel.Debug)) - { - continue; - } - - Brush color = log_entry.Severity switch - { - LogEntry.SeverityLevel.Debug => new SolidColorBrush - { - Color = IS_DARK ? DARK_GREY : LIGHT_GREY, - }, - LogEntry.SeverityLevel.Info => new SolidColorBrush - { - Color = IS_DARK ? DARK_LIGHT_GREY : LIGHT_LIGHT_GREY, - }, - LogEntry.SeverityLevel.Success => new SolidColorBrush - { - Color = IS_DARK ? DARK_WHITE : LIGHT_WHITE, - }, - LogEntry.SeverityLevel.Warning => new SolidColorBrush - { - Color = IS_DARK ? DARK_YELLOW : LIGHT_YELLOW, - }, - LogEntry.SeverityLevel.Error => new SolidColorBrush - { - Color = IS_DARK ? DARK_RED : LIGHT_RED, - }, - _ => new SolidColorBrush { Color = IS_DARK ? DARK_GREY : LIGHT_GREY }, - }; - string[] lines = log_entry.Content.Split('\n'); - int date_length = -1; - foreach (string line in lines) - { - if (date_length == -1) - { - p.Inlines.Add( - new Run { Text = $"[{log_entry.Time}] {line}\n", Foreground = color } - ); - date_length = $"[{log_entry.Time}] ".Length; - } - else - { - p.Inlines.Add( - new Run - { - Text = new string(' ', date_length) + line + "\n", - Foreground = color, - } - ); - } - } - ((Run)p.Inlines[^1]).Text = ((Run)p.Inlines[^1]).Text.TrimEnd(); - LogTextBox.Blocks.Add(p); - } - if (isReload) - MainScroller.ScrollToVerticalOffset(MainScroller.ScrollableHeight); - } - } -} diff --git a/src/UniGetUI/Pages/MainView.xaml b/src/UniGetUI/Pages/MainView.xaml deleted file mode 100644 index 8b2259e85c..0000000000 --- a/src/UniGetUI/Pages/MainView.xaml +++ /dev/null @@ -1,465 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0,0,0,0 - 0,0,0,0 - 0,0,0,0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/MainView.xaml.cs b/src/UniGetUI/Pages/MainView.xaml.cs deleted file mode 100644 index 5b81f70732..0000000000 --- a/src/UniGetUI/Pages/MainView.xaml.cs +++ /dev/null @@ -1,644 +0,0 @@ -using Microsoft.UI.Input; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using UniGetUI.Controls; -using UniGetUI.Controls.OperationWidgets; -using UniGetUI.Core.Data; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; -using UniGetUI.Interface.Dialogs; -using UniGetUI.Interface.Pages; -using UniGetUI.Interface.Pages.LogPage; -using UniGetUI.Interface.SoftwarePages; -using UniGetUI.PackageEngine; -using UniGetUI.PackageEngine.Enums; -using UniGetUI.PackageEngine.Interfaces; -using UniGetUI.PackageEngine.Managers.ChocolateyManager; -using UniGetUI.PackageEngine.PackageLoader; -using UniGetUI.PackageOperations; -using UniGetUI.Pages.DialogPages; -using UniGetUI.Pages.PageInterfaces; -using UniGetUI.Pages.SettingsPages; -using Windows.System; -using Windows.UI.Core; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Interface -{ - public enum PageType - { - Discover, - Updates, - Installed, - Bundles, - Settings, - Managers, - OwnLog, - ManagerLog, - OperationHistory, - Help, - Null, // Used for initializers - } - - public sealed partial class MainView : UserControl - { - private readonly DiscoverSoftwarePage DiscoverPage; - private readonly SoftwareUpdatesPage UpdatesPage; - private readonly InstalledPackagesPage InstalledPage; - private readonly PackageBundlesPage BundlesPage; - private SettingsBasePage? SettingsPage; - private SettingsBasePage? ManagersPage; - private UniGetUILogPage? UniGetUILogPage; - private ManagerLogsPage? ManagerLogPage; - private OperationHistoryPage? OperationHistoryPage; - private HelpPage? HelpPage; - - private PageType OldPage_t = PageType.Null; - private PageType CurrentPage_t = PageType.Null; - private readonly List NavigationHistory = new(); - public PageType CurrentPage => CurrentPage_t; - - private readonly AutoSuggestBox MainTextBlock; - public event EventHandler? CanGoBackChanged; - - public MainView(AutoSuggestBox mainTextBlock) - { - InitializeComponent(); - MainTextBlock = mainTextBlock; - OperationList.ItemContainerTransitions = null; - OperationList.ItemsSource = MainApp.Operations._operationList; - DiscoverPage = new DiscoverSoftwarePage(); - UpdatesPage = new SoftwareUpdatesPage(); - InstalledPage = new InstalledPackagesPage(); - BundlesPage = new PackageBundlesPage(); - - MoreNavButtonMenu.Closed += (_, _) => SelectNavButtonForPage(CurrentPage_t); - KeyDown += (s, e) => - { - if (e.KeyStatus.WasKeyDown) - { - // ignore repeated KeyDown events when pressing and holding a key - return; - } - - bool IS_CONTROL_PRESSED = InputKeyboardSource - .GetKeyStateForCurrentThread(VirtualKey.Control) - .HasFlag(CoreVirtualKeyStates.Down); - bool IS_SHIFT_PRESSED = InputKeyboardSource - .GetKeyStateForCurrentThread(VirtualKey.Shift) - .HasFlag(CoreVirtualKeyStates.Down); - - Page currentPage = GetPageForType(CurrentPage_t); - if (e.Key is VirtualKey.Tab && IS_CONTROL_PRESSED) - { - NavigateTo( - IS_SHIFT_PRESSED - ? GetPreviousPage(CurrentPage_t) - : GetNextPage(CurrentPage_t) - ); - } - else if (!IS_CONTROL_PRESSED && !IS_SHIFT_PRESSED && e.Key == VirtualKey.F1) - { - NavigateTo(PageType.Help); - } - else if ((e.Key is VirtualKey.Q or VirtualKey.W) && IS_CONTROL_PRESSED) - { - MainApp.Instance.MainWindow.Close(); - } - else if (e.Key is VirtualKey.F5 || (e.Key is VirtualKey.R && IS_CONTROL_PRESSED)) - { - (currentPage as IKeyboardShortcutListener)?.ReloadTriggered(); - } - else if (e.Key is VirtualKey.F && IS_CONTROL_PRESSED) - { - (currentPage as IKeyboardShortcutListener)?.SearchTriggered(); - } - else if (e.Key is VirtualKey.A && IS_CONTROL_PRESSED) - { - (currentPage as IKeyboardShortcutListener)?.SelectAllTriggered(); - } - }; - - /* - * Connect different loaders and UI Sections to bundles page - */ - foreach ( - var pair in new Dictionary - { - { DiscoverNavBtn, DiscoverablePackagesLoader.Instance }, - { UpdatesNavBtn, UpgradablePackagesLoader.Instance }, - { InstalledNavBtn, InstalledPackagesLoader.Instance }, - } - ) - { - pair.Value.FinishedLoading += (_, _) => - MainApp.Dispatcher.TryEnqueue(() => pair.Key.IsLoading = false); - pair.Value.StartedLoading += (_, _) => - MainApp.Dispatcher.TryEnqueue(() => pair.Key.IsLoading = true); - pair.Key.IsLoading = pair.Value.IsLoading; - } - - UpgradablePackagesLoader.Instance.PackagesChanged += (_, _) => - MainApp.Dispatcher.TryEnqueue(() => - { - UpdatesBadge.Value = UpgradablePackagesLoader.Instance.Count(); - UpdatesBadge.Visibility = - UpdatesBadge.Value > 0 ? Visibility.Visible : Visibility.Collapsed; - }); - UpdatesBadge.Value = UpgradablePackagesLoader.Instance.Count(); - UpdatesBadge.Visibility = - UpdatesBadge.Value > 0 ? Visibility.Visible : Visibility.Collapsed; - - BundlesPage.UnsavedChangesStateChanged += (_, _) => - MainApp.Dispatcher.TryEnqueue(() => - { - BundlesBadge.Visibility = BundlesPage.HasUnsavedChanges - ? Visibility.Visible - : Visibility.Collapsed; - }); - BundlesBadge.Visibility = BundlesPage.HasUnsavedChanges - ? Visibility.Visible - : Visibility.Collapsed; - - /* - * End connecting stuff together - */ - - LoadDefaultPage(); - - if (CoreTools.IsAdministrator() && !Settings.Get(Settings.K.AlreadyWarnedAboutAdmin)) - { - Settings.Set(Settings.K.AlreadyWarnedAboutAdmin, true); - _ = DialogHelper.WarnAboutAdminRights(); - } - - if ( - PEInterface.Chocolatey is { Status.Found: false } - && Chocolatey.HasLegacyBundledInstallation() - && !Settings.Get(Settings.K.AlreadyWarnedAboutChocolateyMigration) - ) - { - Settings.Set(Settings.K.AlreadyWarnedAboutChocolateyMigration, true); - _ = DialogHelper.WarnAboutChocolateyMigration(); - } - - UpdateOperationsLayout(); - MainApp.Operations._operationList.CollectionChanged += (_, e) => - { - if (e.NewItems is not null) - foreach (OperationControl c in e.NewItems) - AddToBatch(c.Operation); - UpdateOperationsLayout(); - }; - - if (!Settings.Get(Settings.K.ShownTelemetryBanner)) - { - DialogHelper.ShowTelemetryBanner(); - } - - ShowReleaseNotesIfUpdated(); - - if (!Settings.Get(Settings.K.CollapseNavMenuOnWideScreen)) - { - NavView.IsPaneOpen = true; - } - } - - public void LoadDefaultPage() - { - PageType type = Settings.GetValue(Settings.K.StartupPage) switch - { - "discover" => PageType.Discover, - "updates" => PageType.Updates, - "installed" => PageType.Installed, - "bundles" => PageType.Bundles, - "settings" => PageType.Settings, - _ => MainApp.Tooltip.AvailableUpdates > 0 ? PageType.Updates : PageType.Discover, - }; - NavigateTo(type); - } - - private Page GetPageForType(PageType type) => - type switch - { - PageType.Discover => DiscoverPage, - PageType.Updates => UpdatesPage, - PageType.Installed => InstalledPage, - PageType.Bundles => BundlesPage, - PageType.Settings => SettingsPage ??= new SettingsBasePage(false), - PageType.Managers => ManagersPage ??= new SettingsBasePage(true), - PageType.OwnLog => UniGetUILogPage ??= new UniGetUILogPage(), - PageType.ManagerLog => ManagerLogPage ??= new ManagerLogsPage(), - PageType.OperationHistory => OperationHistoryPage ??= new OperationHistoryPage(), - PageType.Help => HelpPage ??= new HelpPage(), - PageType.Null => throw new InvalidCastException("Page type is Null"), - _ => throw new InvalidDataException($"Unknown page type {type}"), - }; - - private static PageType GetNextPage(PageType type) => - type switch - { - // Default loop - PageType.Discover => PageType.Updates, - PageType.Updates => PageType.Installed, - PageType.Installed => PageType.Bundles, - PageType.Bundles => PageType.Settings, - PageType.Settings => PageType.Managers, - PageType.Managers => PageType.Discover, - - // "Extra" pages - PageType.OperationHistory => PageType.Discover, - PageType.OwnLog => PageType.Discover, - PageType.ManagerLog => PageType.Discover, - PageType.Help => PageType.Discover, - PageType.Null => PageType.Discover, - _ => throw new InvalidDataException($"Unknown page type {type}"), - }; - - private static PageType GetPreviousPage(PageType type) => - type switch - { - // Default loop - PageType.Discover => PageType.Settings, - PageType.Updates => PageType.Discover, - PageType.Installed => PageType.Updates, - PageType.Bundles => PageType.Installed, - PageType.Settings => PageType.Bundles, - PageType.Managers => PageType.Settings, - - // "Extra" pages - PageType.OperationHistory => PageType.Discover, - PageType.OwnLog => PageType.Discover, - PageType.ManagerLog => PageType.Discover, - PageType.Help => PageType.Discover, - PageType.Null => PageType.Discover, - _ => throw new InvalidDataException($"Unknown page type {type}"), - }; - - private void SettingsNavButton_Click(object sender, EventArgs e) => - NavigateTo(PageType.Settings); - - private void ManagersNavButton_Click(object sender, EventArgs e) => - NavigateTo(PageType.Managers); - - private bool _lastNavItemSelectionWasAuto; - - private void SelectNavButtonForPage(PageType page) - { - _lastNavItemSelectionWasAuto = true; - NavView.SelectedItem = page switch - { - PageType.Discover => DiscoverNavBtn, - PageType.Updates => UpdatesNavBtn, - PageType.Installed => InstalledNavBtn, - PageType.Bundles => BundlesNavBtn, - PageType.Settings => SettingsNavBtn, - PageType.Managers => ManagersNavBtn, - _ => MoreNavBtn, - }; - _lastNavItemSelectionWasAuto = false; - } - - private void AboutNavButton_Click(object sender, RoutedEventArgs e) => - _ = _aboutNavButton_Click(); - - private async Task _aboutNavButton_Click() - { - SelectNavButtonForPage(PageType.Null); - await DialogHelper.ShowAboutUniGetUI(); - SelectNavButtonForPage(CurrentPage_t); - } - - public void NavigateTo(PageType NewPage_t, bool toHistory = true) - { - SelectNavButtonForPage(NewPage_t); - if (CurrentPage_t == NewPage_t) - return; - - Page NewPage = GetPageForType(NewPage_t); - Page? oldPage = ContentFrame.Content as Page; - ContentFrame.Content = NewPage; - - OldPage_t = CurrentPage_t; - CurrentPage_t = NewPage_t; - - (oldPage as IEnterLeaveListener)?.OnLeave(); - if (oldPage is ISearchBoxPage oldSPage) - { - MainTextBlock.TextChanged -= oldSPage.SearchBox_TextChanged; - MainTextBlock.QuerySubmitted -= oldSPage.SearchBox_QuerySubmitted; - oldSPage.QueryBackup = MainTextBlock.Text; - } - - if (toHistory && OldPage_t is not PageType.Null) - { - NavigationHistory.Add(OldPage_t); - CanGoBackChanged?.Invoke(this, true); - } - - (NewPage as AbstractPackagesPage)?.FocusPackageList(); - (NewPage as AbstractPackagesPage)?.FilterPackages(); - (NewPage as IEnterLeaveListener)?.OnEnter(); - - if (NewPage is ISearchBoxPage newSPage) - { - MainTextBlock.TextChanged += newSPage.SearchBox_TextChanged; - MainTextBlock.QuerySubmitted += newSPage.SearchBox_QuerySubmitted; - MainTextBlock.Text = newSPage.QueryBackup; - MainTextBlock.PlaceholderText = newSPage.SearchBoxPlaceholder; - MainTextBlock.IsEnabled = true; - } - else - { - MainTextBlock.Text = ""; - MainTextBlock.PlaceholderText = ""; - MainTextBlock.IsEnabled = false; - } - } - - public void NavigateBack() - { - if (ContentFrame.Content is IInnerNavigationPage navPage && navPage.CanGoBack()) - { - navPage.GoBack(); - } - else - { - NavigateTo(NavigationHistory.Last(), toHistory: false); - NavigationHistory.RemoveAt(NavigationHistory.Count - 1); - CanGoBackChanged?.Invoke( - this, - NavigationHistory.Any() - || ((ContentFrame.Content as IInnerNavigationPage)?.CanGoBack() ?? false) - ); - } - } - - private void ReleaseNotesMenu_Click(object sender, RoutedEventArgs e) => - _ = DialogHelper.ShowReleaseNotes(); - - // Show the changelog the first time the app runs after being updated to a newer build - private static void ShowReleaseNotesIfUpdated() - { - _ = int.TryParse(Settings.GetValue(Settings.K.LastKnownBuildNumber), out int lastBuild); - - if (lastBuild != 0 && lastBuild < CoreData.BuildNumber - && !Settings.Get(Settings.K.DisableReleaseNotesOnUpdate)) - { - _ = DialogHelper.ShowReleaseNotes(); - } - - if (lastBuild != CoreData.BuildNumber) - { - Settings.SetValue(Settings.K.LastKnownBuildNumber, CoreData.BuildNumber.ToString()); - } - } - - private void CheckForUpdates_Click(object sender, RoutedEventArgs e) - { - var mainWindow = MainApp.Instance.MainWindow; - _ = AutoUpdater.CheckAndInstallUpdates(mainWindow, mainWindow.UpdatesBanner, true, false, true); - } - - private void OperationHistoryMenu_Click(object sender, RoutedEventArgs e) => - NavigateTo(PageType.OperationHistory); - - private void ManagerLogsMenu_Click(object sender, RoutedEventArgs e) => OpenManagerLogs(); - - public void OpenManagerLogs(IPackageManager? manager = null) - { - NavigateTo(PageType.ManagerLog); - if (manager is not null) - ManagerLogPage?.LoadForManager(manager); - } - - public void OpenManagerSettings(IPackageManager? manager = null) - { - NavigateTo(PageType.Managers); - if (manager is not null) - ManagersPage?.NavigateTo(manager); - } - - public void OpenSettingsPage(Type page) - { - NavigateTo(PageType.Settings); - SettingsPage?.NavigateTo(page); - } - - public void UniGetUILogs_Click(object sender, RoutedEventArgs e) => - NavigateTo(PageType.OwnLog); - - private void HelpMenu_Click(object sender, RoutedEventArgs e) => ShowHelp(); - - public void ShowHelp(string uriAttachment = "") - { - NavigateTo(PageType.Help); - HelpPage?.NavigateTo(uriAttachment); - } - - private void QuitUniGetUI_Click(object sender, RoutedEventArgs e) => - MainApp.Instance.DisposeAndQuit(); - - private bool ResizingOPLayout; - private int OpListChanges; - - bool isCollapsed; - - private void UpdateOperationsLayout() - { - OpListChanges++; - - ResizingOPLayout = true; - int OpCount = MainApp.Operations._operationList.Count; - int maxHeight = Math.Max((OpCount * 58) - 7, 0); - - ContentGrid.RowDefinitions[2].MaxHeight = maxHeight; - - if (OpCount > 0) - { - if (isCollapsed) - { - ContentGrid.RowDefinitions[2].Height = new GridLength(0); - ContentGrid.RowDefinitions[1].Height = new GridLength(16); - OperationSplitter.Visibility = Visibility.Visible; - OperationSplitterMenuButton.Visibility = Visibility.Visible; - OperationSplitter.IsEnabled = false; - } - else - { - ContentGrid.RowDefinitions[2].Height = new GridLength(Math.Min(maxHeight, 200)); - ContentGrid.RowDefinitions[1].Height = new GridLength(16); - OperationSplitter.Visibility = Visibility.Visible; - OperationSplitterMenuButton.Visibility = Visibility.Visible; - OperationSplitter.IsEnabled = true; - } - } - else - { - ContentGrid.RowDefinitions[1].Height = new GridLength(0); - ContentGrid.RowDefinitions[2].Height = new GridLength(0); - OperationSplitter.Visibility = Visibility.Collapsed; - OperationSplitterMenuButton.Visibility = Visibility.Collapsed; - } - ResizingOPLayout = false; - UpdateOperationCount(); - } - - private readonly List _operationBatch = new(); - - private void AddToBatch(AbstractOperation op) - { - if (_operationBatch.Count > 0 - && _operationBatch.All(o => o.Status is not (OperationStatus.InQueue or OperationStatus.Running))) - { - foreach (var old in _operationBatch) - old.StatusChanged -= OnAnyOperationStatusChanged; - _operationBatch.Clear(); - } - - if (!_operationBatch.Contains(op)) - { - _operationBatch.Add(op); - op.StatusChanged += OnAnyOperationStatusChanged; - } - } - - private void OnAnyOperationStatusChanged(object? sender, OperationStatus e) - => DispatcherQueue.TryEnqueue(UpdateOperationCount); - - private void UpdateOperationCount() - { - int total = _operationBatch.Count; - int completed = _operationBatch.Count(o => - o.Status is OperationStatus.Succeeded or OperationStatus.Failed or OperationStatus.Canceled); - OperationCountLabel.Text = - total > 0 ? CoreTools.Translate("{0} of {1} operations completed", completed, total) : ""; - } - - private void OperationScrollView_SizeChanged(object sender, SizeChangedEventArgs e) - { - if (ResizingOPLayout) - return; - - if (OpListChanges > 0) - { - OpListChanges--; - } - } - - private void OperationSplitterMenuButton_Click(object sender, RoutedEventArgs e) - { - OperationListMenu.ShowAt( - OperationSplitterMenuButton, - new FlyoutShowOptions { ShowMode = FlyoutShowMode.Standard } - ); - } - - private void ExpandCollapseOpList_Click(object sender, RoutedEventArgs e) - { - if (isCollapsed) - { - isCollapsed = false; - ExpandCollapseOpList.Content = new FontIcon { Glyph = "\uE96E", FontSize = 14 }; - UpdateOperationsLayout(); - } - else - { - isCollapsed = true; - ExpandCollapseOpList.Content = new FontIcon { Glyph = "\uE96D", FontSize = 14 }; - UpdateOperationsLayout(); - } - } - - private void CancellAllOps_Click(object sender, RoutedEventArgs e) - { - foreach (var widget in MainApp.Operations._operationList) - { - var operation = widget.Operation; - if (operation.Status is OperationStatus.InQueue or OperationStatus.Running) - operation.Cancel(); - } - } - - private void RetryFailedOps_Click(object sender, RoutedEventArgs e) - { - foreach (var widget in MainApp.Operations._operationList) - { - var operation = widget.Operation; - if (operation.Status is OperationStatus.Failed) - operation.Retry(AbstractOperation.RetryMode.Retry); - } - } - - private void ClearSuccessfulOps_Click(object sender, RoutedEventArgs e) - { - foreach (var widget in MainApp.Operations._operationList.ToArray()) - { - var operation = widget.Operation; - if (operation.Status is OperationStatus.Succeeded) - widget.Close(); - } - } - - private void NavigationView_SelectionChanged( - NavigationView sender, - NavigationViewSelectionChangedEventArgs args - ) - { - if (_lastNavItemSelectionWasAuto) - return; - - if (args.SelectedItem is CustomNavViewItem item && item.Tag is not PageType.Null) - { - NavigateTo(item.Tag); - } - } - - private void MoreNavBtn_Tapped( - object sender, - Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e - ) - { - (VersionMenuItem as MenuFlyoutItem).Text = CoreTools.Translate( - "UniGetUI Version {0}", - CoreData.VersionName - ); - MoreNavButtonMenu.ShowAt(sender as FrameworkElement); - } - - internal void LoadBundleFromFile(string param) - { - NavigateTo(PageType.Bundles); - BundlesPage?.OpenFromFile(param); - } - - internal void LoadBundleFromString( - string payload, - BundleFormatType format, - string source, - int loadingId - ) - { - NavigateTo(PageType.Bundles); - BundlesPage?.OpenFromString(payload, format, source, loadingId); - } - - private void ClearAllFinished_OnClick(object sender, RoutedEventArgs e) - { - foreach (var widget in MainApp.Operations._operationList.ToArray()) - { - var operation = widget.Operation; - if ( - operation.Status - is OperationStatus.Succeeded - or OperationStatus.Failed - or OperationStatus.Canceled - ) - widget.Close(); - } - } - } -} diff --git a/src/UniGetUI/Pages/PageInterfaces/IEnterLeaveListener.cs b/src/UniGetUI/Pages/PageInterfaces/IEnterLeaveListener.cs deleted file mode 100644 index b4bd7b8195..0000000000 --- a/src/UniGetUI/Pages/PageInterfaces/IEnterLeaveListener.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace UniGetUI.Interface.Pages; - -public interface IEnterLeaveListener -{ - public void OnEnter(); - public void OnLeave(); -} diff --git a/src/UniGetUI/Pages/PageInterfaces/IInnerNavigationPage.cs b/src/UniGetUI/Pages/PageInterfaces/IInnerNavigationPage.cs deleted file mode 100644 index 99e86149ed..0000000000 --- a/src/UniGetUI/Pages/PageInterfaces/IInnerNavigationPage.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace UniGetUI.Interface.Pages; - -public interface IInnerNavigationPage -{ - public bool CanGoBack(); - public void GoBack(); -} diff --git a/src/UniGetUI/Pages/PageInterfaces/IKeyboardShortcutListener.cs b/src/UniGetUI/Pages/PageInterfaces/IKeyboardShortcutListener.cs deleted file mode 100644 index 85eb3606fc..0000000000 --- a/src/UniGetUI/Pages/PageInterfaces/IKeyboardShortcutListener.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace UniGetUI.Interface.Pages -{ - /// - /// Any object that can perform any of the following listed actions should - /// implement this class, to allow proper keyboard bindings on the interface. - /// - internal interface IKeyboardShortcutListener - { - /// - /// Handles when a search-like automation was triggered (Ctrl+F, etc.) - /// - public void SearchTriggered(); - - /// - /// Handles when a reload/refresh-like automation was triggered (F5, Ctrl+R, etc) - /// - public void ReloadTriggered(); - - /// - /// Handles when a select-like automation was triggered (Ctrl+A, etc.) - /// - public void SelectAllTriggered(); - } -} diff --git a/src/UniGetUI/Pages/PageInterfaces/ISearchBoxPage.cs b/src/UniGetUI/Pages/PageInterfaces/ISearchBoxPage.cs deleted file mode 100644 index fb7f75ce00..0000000000 --- a/src/UniGetUI/Pages/PageInterfaces/ISearchBoxPage.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Input; - -namespace UniGetUI.Pages.PageInterfaces; - -internal interface ISearchBoxPage -{ - public string QueryBackup { get; set; } - public string SearchBoxPlaceholder { get; } - public void SearchBox_TextChanged(object? sender, AutoSuggestBoxTextChangedEventArgs args); - public void SearchBox_QuerySubmitted( - object? sender, - AutoSuggestBoxQuerySubmittedEventArgs args - ); -} diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Administrator.xaml b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Administrator.xaml deleted file mode 100644 index 4c1162b987..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Administrator.xaml +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Administrator.xaml.cs b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Administrator.xaml.cs deleted file mode 100644 index da97560ecb..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Administrator.xaml.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Microsoft.UI.Xaml.Controls; -using UniGetUI.Core.Tools; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Pages.SettingsPages.GeneralPages -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class Administrator : Page, ISettingsPage - { - public Administrator() - { - this.InitializeComponent(); - - if (DoCacheAdminRights.Checked && DoCacheAdminRightsForBatches.Checked) - { - DoCacheAdminRights.IsEnabled = true; - DoCacheAdminRightsForBatches.IsEnabled = true; - } - - WarningTitlebar.Title = CoreTools.Translate("Warning") + "!"; - WarningTitlebar.Message = - CoreTools.Translate( - "The following settings may pose a security risk, hence they are disabled by default." - ) - + " " - + CoreTools.Translate( - "Enable the settings below if and only if you fully understand what they do, and the implications they may have." - ) - + "\n\n" - + CoreTools.Translate( - "The settings will list, in their descriptions, the potential security issues they may have." - ) - + " "; - - AllowCustomManagerPaths.StateChanged += (_, _) => - RestartRequired?.Invoke(this, EventArgs.Empty); - - // The following settings may pose a security risk, hence they are disabled by default. Enable them ONLY if you undertsand what you are doing. Some of those settings will show a UAC prompt before being enabled." - } - - public bool CanGoBack => true; - public string ShortTitle => - CoreTools.Translate("Administrator rights and other dangerous settings"); - - public event EventHandler? RestartRequired; - public event EventHandler? NavigationRequested - { - add { } - remove { } - } - - public void ShowRestartBanner(object sender, EventArgs e) => - RestartRequired?.Invoke(this, e); - - public void RestartCache(object sender, EventArgs e) => - _ = CoreTools.ResetUACForCurrentProcess(); - } -} diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml deleted file mode 100644 index b083ae07f9..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml +++ /dev/null @@ -1,232 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml.cs b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml.cs deleted file mode 100644 index 8a03146a10..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml.cs +++ /dev/null @@ -1,360 +0,0 @@ -using System.Data; -using System.Diagnostics; -using System.Security.Authentication; -using System.Security.Cryptography; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Media.Imaging; -using UniGetUI.Core.Data; -using UniGetUI.Core.Logging; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; -using UniGetUI.Interface.SoftwarePages; -using UniGetUI.PackageEngine.Enums; -using UniGetUI.Pages.DialogPages; -using UniGetUI.Services; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Pages.SettingsPages.GeneralPages -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class Backup : Page, ISettingsPage - { - private readonly GitHubAuthService _authService; - private readonly GitHubBackupService _backupService; - private bool _isLoggedIn; - private bool _isLoading; - - public Backup() - { - this.InitializeComponent(); - - _authService = new GitHubAuthService(); - _backupService = new GitHubBackupService(_authService); - - EnablePackageBackupUI(Settings.Get(Settings.K.EnablePackageBackup_LOCAL)); - ResetBackupDirectory.Content = CoreTools.Translate("Reset"); - OpenBackupDirectory.Content = CoreTools.Translate("Open"); - - GitHubAuthService.AuthStatusChanged += (_, _) => _ = UpdateGitHubLoginStatus(); - EnablePackageBackupCheckBox_CLOUD.StateChanged += - EnablePackageBackupCheckBox_CLOUD_StateChanged; - _ = UpdateGitHubLoginStatus(); - } - - public bool CanGoBack => true; - - public string ShortTitle => CoreTools.Translate("Backup and Restore"); - - public event EventHandler? RestartRequired; - public event EventHandler? NavigationRequested - { - add { } - remove { } - } - - public void ShowRestartBanner(object? sender, EventArgs e) => - RestartRequired?.Invoke(this, e); - - private void ChangeBackupDirectory_Click(object sender, EventArgs e) - { - ExternalLibraries.Pickers.FolderPicker openPicker = new( - MainApp.Instance.MainWindow.GetWindowHandle() - ); - string folder = openPicker.Show(); - if (folder != string.Empty) - { - Settings.SetValue(Settings.K.ChangeBackupOutputDirectory, folder); - BackupDirectoryLabel.Text = folder; - ResetBackupDirectory.IsEnabled = true; - } - } - - public void EnablePackageBackupUI(bool enabled) - { - EnableBackupTimestampingCheckBox.IsEnabled = enabled; - ChangeBackupFileNameTextBox.IsEnabled = enabled; - ChangeBackupDirectory.IsEnabled = enabled; - BackupNowButton_LOCAL.IsEnabled = enabled; - - if (enabled) - { - if (!Settings.Get(Settings.K.ChangeBackupOutputDirectory)) - { - BackupDirectoryLabel.Text = CoreData.UniGetUI_DefaultBackupDirectory; - ResetBackupDirectory.IsEnabled = false; - } - else - { - BackupDirectoryLabel.Text = Settings.GetValue( - Settings.K.ChangeBackupOutputDirectory - ); - ResetBackupDirectory.IsEnabled = true; - } - } - } - - private void ResetBackupPath_Click(object sender, RoutedEventArgs e) - { - BackupDirectoryLabel.Text = CoreData.UniGetUI_DefaultBackupDirectory; - Settings.Set(Settings.K.ChangeBackupOutputDirectory, false); - ResetBackupDirectory.IsEnabled = false; - } - - private void OpenBackupPath_Click(object sender, RoutedEventArgs e) - { - string directory = Settings.GetValue(Settings.K.ChangeBackupOutputDirectory); - if (directory == "") - directory = CoreData.UniGetUI_DefaultBackupDirectory; - - directory = directory.Replace("/", "\\"); - if (!Directory.Exists(directory)) - Directory.CreateDirectory(directory); - - CoreTools.Launch(directory); - } - - private void DoBackup_LOCAL_Click(object sender, EventArgs e) => - _ = _doBackup_LOCAL_Click(); - - private static async Task _doBackup_LOCAL_Click() - { - int loadingId = DialogHelper.ShowLoadingDialog( - CoreTools.Translate("Performing backup, please wait...") - ); - await InstalledPackagesPage.BackupPackages_LOCAL(); - DialogHelper.HideLoadingDialog(loadingId); - } - - /* - * - * BEGIN CLOUD BACKUP METHODS - * - */ - private async Task UpdateGitHubLoginStatus() - { - GitHubAuthService authService = new(); - if (authService.IsAuthenticated()) - { - try - { - await GenerateLogoutUI(authService); - } - catch (Exception ex) - { - Logger.Error( - "An error occurred while attempting to generate settings login UI: " - ); - Logger.Error(ex); - GenerateLoginUI(); - } - } - else - { - GenerateLoginUI(); - } - UpdateCloudControlsEnabled(); - } - - private void GenerateLoginUI() - { - _isLoggedIn = false; - LogInButton.Visibility = Visibility.Visible; - LogOutButton.Visibility = Visibility.Collapsed; - GitHubUserTitle.Text = CoreTools.Translate("Current status: Not logged in"); - GitHubUserSubtitle.Text = CoreTools.Translate("Log in to enable cloud backup"); - GitHubImage.ProfilePicture = null; - } - - private async Task GenerateLogoutUI(GitHubAuthService authService) - { - var client = authService.CreateGitHubClient(); - if (client is null) - throw new AuthenticationException( - "How can it be authenticated and fail to create a client?" - ); - var user = await client.User.Current(); - - _isLoggedIn = true; - LogInButton.Visibility = Visibility.Collapsed; - LogOutButton.Visibility = Visibility.Visible; - GitHubUserTitle.Text = CoreTools.Translate( - "You are logged in as {0} (@{1})", - user.Name, - user.Login - ); - GitHubUserSubtitle.Text = CoreTools.Translate( - "Nice! Backups will be uploaded to a private gist on your account" - ); - GitHubImage.Initials = ""; - GitHubImage.ProfilePicture = new BitmapImage(new Uri(user.AvatarUrl)); - } - - private void UpdateCloudControlsEnabled() - { - LogInButton.IsEnabled = !_isLoading; - LogOutButton.IsEnabled = !_isLoading; - if (_isLoggedIn && !_isLoading) - { - EnablePackageBackupCheckBox_CLOUD.IsEnabled = true; - RestorePackagesFromGitHubButton.IsEnabled = true; - BackupNowButton_Cloud.IsEnabled = Settings.Get( - Settings.K.EnablePackageBackup_CLOUD - ); - } - else - { - EnablePackageBackupCheckBox_CLOUD.IsEnabled = false; - BackupNowButton_Cloud.IsEnabled = false; - RestorePackagesFromGitHubButton.IsEnabled = false; - } - } - - private void LoginWithGitHubButton_Click(object sender, RoutedEventArgs e) => - _ = _loginWithGitHubButton_Click(); - - private async Task _loginWithGitHubButton_Click() - { - _isLoading = true; - UpdateCloudControlsEnabled(); - - bool success = await _authService.SignInAsync(); - if (!success && !_authService.LoginWasCancelled) - { - DialogHelper.ShowDismissableBalloon( - CoreTools.Translate("Failed"), - CoreTools.Translate("An error occurred while logging in: ") - ); - } - _isLoading = false; - UpdateCloudControlsEnabled(); - } - - private void LogoutGitHubButton_Click(object sender, RoutedEventArgs e) - { - _isLoading = true; - UpdateCloudControlsEnabled(); - - _authService.SignOut(); - - _isLoading = false; - UpdateCloudControlsEnabled(); - } - - private void RestoreFromGitHubButton_Click(object sender, EventArgs e) => - _ = _restoreFromGitHubButton_Click(); - - private async Task _restoreFromGitHubButton_Click() - { - RestorePackagesFromGitHubButton.IsEnabled = false; - try - { - int loadingId = DialogHelper.ShowLoadingDialog( - CoreTools.Translate("Fetching available backups...") - ); - var availableBackups = await _backupService.GetAvailableBackups(); - DialogHelper.HideLoadingDialog(loadingId); - - var selectedBackup = await DialogHelper.AskForBackupSelection(availableBackups); - if (selectedBackup is null) - { - RestorePackagesFromGitHubButton.IsEnabled = true; - return; - } - selectedBackup = selectedBackup.Split(' ')[0]; - - loadingId = DialogHelper.ShowLoadingDialog( - CoreTools.Translate("Downloading backup...") - ); - var backupContents = await _backupService.GetBackupContents(selectedBackup); - // DialogHelper.HideLoadingDialog(loadingId); - await Task.Delay(500); // Prevent race conditions with dialogs - - if (backupContents is null) - throw new DataException( - $"The backupContents for backup {selectedBackup} returned null" - ); - - Logger.Info("Successfully loaded package bundle from GitHub Gist."); - DialogHelper.ShowDismissableBalloon( - CoreTools.Translate("Done!"), - CoreTools.Translate("The cloud backup has been loaded successfully.") - ); - - MainApp.Instance.MainWindow.NavigationPage.LoadBundleFromString( - backupContents, - BundleFormatType.UBUNDLE, - $"GitHub Gist {selectedBackup}", - loadingId - ); - } - catch (Exception ex) - { - Logger.Error("An error occurred while loading a backup:"); - Logger.Error(ex); - - DialogHelper.HideAllLoadingDialogs(); - var errorDialog = DialogHelper.DialogFactory.Create(); - errorDialog.Title = CoreTools.Translate("An error occurred"); - errorDialog.Content = - CoreTools.Translate("An error occurred while loading a backup: ") + ex.Message; - errorDialog.PrimaryButtonText = CoreTools.Translate("OK"); - errorDialog.DefaultButton = ContentDialogButton.Primary; - await DialogHelper.ShowDialogAsync(errorDialog); - } - } - - private void BackupToGitHubButton_Click(object sender, EventArgs e) => - _ = _backupToGitHubButton_Click(); - - private async Task _backupToGitHubButton_Click() - { - int loadingId = DialogHelper.ShowLoadingDialog( - CoreTools.Translate("Backing up packages to GitHub Gist...") - ); - - var packagesContent = await InstalledPackagesPage.GenerateBackupContents(); - - try - { - await _backupService.UploadPackageBundle(packagesContent); - DialogHelper.HideLoadingDialog(loadingId); - Logger.Info("Successfully backed up packages to GitHub Gist."); - DialogHelper.ShowDismissableBalloon( - CoreTools.Translate("Backup Successful"), - CoreTools.Translate("The cloud backup completed successfully.") - ); - } - catch (Exception ex) - { - DialogHelper.HideLoadingDialog(loadingId); - - Logger.Error("An error occurred while uploading the backup:"); - Logger.Error(ex); - - var dialog = DialogHelper.DialogFactory.Create(); - dialog.Title = CoreTools.Translate("Backup Failed"); - dialog.Content = - CoreTools.Translate("Could not back up packages to GitHub Gist: ") + ex.Message; - dialog.PrimaryButtonText = CoreTools.Translate("OK"); - dialog.DefaultButton = ContentDialogButton.Primary; - await DialogHelper.ShowDialogAsync(dialog); - } - } - - private void EnablePackageBackupCheckBox_CLOUD_StateChanged(object? sender, EventArgs e) - { - ShowRestartBanner(sender, e); - UpdateCloudControlsEnabled(); - } - - private void MoreInfoBtn_OnClick(object sender, RoutedEventArgs e) - { - CoreTools.Launch("https://devolutions.net/unigetui"); - } - } -} diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Experimental.xaml b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Experimental.xaml deleted file mode 100644 index 2393d9b2ca..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Experimental.xaml +++ /dev/null @@ -1,107 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Experimental.xaml.cs b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Experimental.xaml.cs deleted file mode 100644 index ffa26dabcd..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Experimental.xaml.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Microsoft.UI.Xaml.Controls; -using UniGetUI.Core.Tools; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Pages.SettingsPages.GeneralPages -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class Experimental : Page, ISettingsPage - { - public Experimental() - { - this.InitializeComponent(); - } - - public bool CanGoBack => true; - - public string ShortTitle => - CoreTools.Translate("Experimental settings and developer options"); - - public event EventHandler? RestartRequired; - public event EventHandler? NavigationRequested - { - add { } - remove { } - } - - public void ShowRestartBanner(object sender, EventArgs e) => - RestartRequired?.Invoke(this, e); - } -} diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/General.xaml b/src/UniGetUI/Pages/SettingsPages/GeneralPages/General.xaml deleted file mode 100644 index 8a260484e2..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/General.xaml +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/General.xaml.cs b/src/UniGetUI/Pages/SettingsPages/GeneralPages/General.xaml.cs deleted file mode 100644 index f9c50f8971..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/General.xaml.cs +++ /dev/null @@ -1,152 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using UniGetUI.Core.Language; -using UniGetUI.Core.Logging; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; -using UniGetUI.Pages.DialogPages; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Pages.SettingsPages.GeneralPages -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class General : Page, ISettingsPage - { - public General() - { - this.InitializeComponent(); - - Dictionary lang_dict = new( - LanguageData.LanguageReference.AsEnumerable() - ); - - foreach (string key in lang_dict.Keys) - { - if ( - key != "en" - && LanguageData.TranslationPercentages.TryGetValue( - key, - out var translationPercentage - ) - ) - { - lang_dict[key] = lang_dict[key] + " (" + translationPercentage + ")"; - } - } - - bool isFirst = true; - foreach (KeyValuePair entry in lang_dict) - { - LanguageSelector.AddItem(entry.Value, entry.Key, isFirst); - isFirst = false; - } - - LanguageSelector.ShowAddedItems(); - } - - public bool CanGoBack => true; - public string ShortTitle => CoreTools.Translate("General preferences"); - - public event EventHandler? RestartRequired; - public event EventHandler? NavigationRequested; - - public void ShowRestartBanner(object sender, EventArgs e) => - RestartRequired?.Invoke(this, e); - - private void ForceUpdateUniGetUI_OnClick(object sender, RoutedEventArgs e) - { - var mainWindow = MainApp.Instance.MainWindow; - _ = AutoUpdater.CheckAndInstallUpdates( - mainWindow, - mainWindow.UpdatesBanner, - true, - false, - true - ); - } - - private void ManageTelemetrySettings_Click(object sender, EventArgs e) => - _ = DialogHelper.ShowTelemetryDialog(); - - private void RedactUsername_StateChanged(object sender, EventArgs e) - { - Logger.RedactUsername = RedactUsernameCheckbox.Checked; - ShowRestartBanner(this, e); - } - - private void ImportSettings_Click(object sender, EventArgs e) => _ = _importSettings(); - - private async Task _importSettings() - { - ExternalLibraries.Pickers.FileOpenPicker picker = new( - MainApp.Instance.MainWindow.GetWindowHandle() - ); - string file = picker.Show(["*.json"]); - - if (file != string.Empty) - { - int loadingId = DialogHelper.ShowLoadingDialog( - CoreTools.Translate("Please wait...") - ); - await Task.Run(() => Settings.ImportFromFile_JSON(file)); - DialogHelper.HideLoadingDialog(loadingId); - ShowRestartBanner(this, new()); - } - } - - private void ExportSettings_Click(object sender, EventArgs e) => _ = _exportSettings(); - - private static async Task _exportSettings() - { - try - { - ExternalLibraries.Pickers.FileSavePicker picker = new( - MainApp.Instance.MainWindow.GetWindowHandle() - ); - string file = picker.Show( - ["*.json"], - CoreTools.Translate("UniGetUI Settings") + ".json" - ); - - if (file != string.Empty) - { - int loadingId = DialogHelper.ShowLoadingDialog( - CoreTools.Translate("Please wait...") - ); - await Task.Run(() => Settings.ExportToFile_JSON(file)); - DialogHelper.HideLoadingDialog(loadingId); - _ = CoreTools.ShowFileOnExplorer(file); - } - } - catch (Exception ex) - { - DialogHelper.HideAllLoadingDialogs(); - Logger.Error("An error occurred when exporting settings"); - Logger.Error(ex); - } - } - - private void ResetWingetUI(object sender, EventArgs e) - { - try - { - Settings.ResetSettings(); - } - catch (Exception ex) - { - Logger.Error("An error occurred when resetting UniGetUI"); - Logger.Error(ex); - } - ShowRestartBanner(this, new()); - } - - private void InterfaceSettingsButton_Click(object sender, RoutedEventArgs e) - { - NavigationRequested?.Invoke(this, typeof(Interface_P)); - } - } -} diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Interface_P.xaml b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Interface_P.xaml deleted file mode 100644 index ba58da05c8..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Interface_P.xaml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Interface_P.xaml.cs b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Interface_P.xaml.cs deleted file mode 100644 index f4eb0ad747..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Interface_P.xaml.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System.Diagnostics; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Navigation; -using UniGetUI.Core.Data; -using UniGetUI.Core.Logging; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Pages.SettingsPages.GeneralPages -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class Interface_P : Page, ISettingsPage - { - public Interface_P() - { - this.InitializeComponent(); - - if (Settings.GetValue(Settings.K.PreferredTheme) == "") - { - Settings.SetValue(Settings.K.PreferredTheme, "auto"); - } - - ThemeSelector.AddItem(CoreTools.AutoTranslated("Light"), "light"); - ThemeSelector.AddItem(CoreTools.AutoTranslated("Dark"), "dark"); - ThemeSelector.AddItem(CoreTools.AutoTranslated("Follow system color scheme"), "auto"); - ThemeSelector.ShowAddedItems(); - - StartupPageSelector.AddItem(CoreTools.AutoTranslated("Default"), "default"); - StartupPageSelector.AddItem(CoreTools.AutoTranslated("Discover Packages"), "discover"); - StartupPageSelector.AddItem(CoreTools.AutoTranslated("Software Updates"), "updates"); - StartupPageSelector.AddItem( - CoreTools.AutoTranslated("Installed Packages"), - "installed" - ); - StartupPageSelector.AddItem(CoreTools.AutoTranslated("Package Bundles"), "bundles"); - StartupPageSelector.AddItem(CoreTools.AutoTranslated("Settings"), "settings"); - StartupPageSelector.ShowAddedItems(); - } - - protected override void OnNavigatedTo(NavigationEventArgs e) - { - base.OnNavigatedTo(e); - _ = LoadIconCacheSize(); - } - - public bool CanGoBack => true; - - public string ShortTitle => CoreTools.Translate("User interface preferences"); - - public event EventHandler? RestartRequired; - public event EventHandler? NavigationRequested - { - add { } - remove { } - } - - public void ShowRestartBanner(object sender, EventArgs e) => - RestartRequired?.Invoke(this, e); - - private void ResetIconCache_OnClick(object sender, EventArgs e) - { - try - { - Directory.Delete(CoreData.UniGetUICacheDirectory_Icons, true); - } - catch (Exception ex) - { - Logger.Error("An error occurred while deleting icon cache"); - Logger.Error(ex); - } - ShowRestartBanner(this, new()); - // PackageWrapper.ResetIconCache(); - // Package.ResetIconCache(); - _ = LoadIconCacheSize(); - } - - private async Task LoadIconCacheSize() - { - double realSize = - ( - await Task.Run(() => - { - return Directory - .GetFiles( - CoreData.UniGetUICacheDirectory_Icons, - "*", - SearchOption.AllDirectories - ) - .Sum(file => new FileInfo(file).Length); - }) - ) / 1048576d; - double roundedSize = ((int)(realSize * 100)) / 100d; - ResetIconCache.Header = CoreTools.Translate( - "The local icon cache currently takes {0} MB", - roundedSize - ); - } - - private void DisableSystemTray_StateChanged(object sender, EventArgs e) => - MainApp.Instance.MainWindow.UpdateSystemTrayStatus(); - - private void ThemeSelector_ValueChanged(object sender, EventArgs e) => - MainApp.Instance.MainWindow.ApplyTheme(); - - private void EditAutostartSettings_Click(object sender, EventArgs e) => - CoreTools.Launch("ms-settings:startupapps"); - } -} diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Internet.xaml b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Internet.xaml deleted file mode 100644 index 056e0c070b..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Internet.xaml +++ /dev/null @@ -1,179 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Internet.xaml.cs b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Internet.xaml.cs deleted file mode 100644 index 21cfb1e0ec..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Internet.xaml.cs +++ /dev/null @@ -1,146 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Media; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; -using UniGetUI.Interface; -using UniGetUI.PackageEngine; -using UniGetUI.PackageEngine.ManagerClasses.Manager; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Pages.SettingsPages.GeneralPages -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class Internet : Page, ISettingsPage - { - public Internet() - { - this.InitializeComponent(); - - UsernameBox.PlaceholderText = CoreTools.Translate("Username"); - PasswordBox.PlaceholderText = CoreTools.Translate("Password"); - - var creds = Settings.GetProxyCredentials(); - if (creds is not null) - { - UsernameBox.Text = creds.UserName; - PasswordBox.Password = creds.Password; - } - - Brush SUCCESS_BG = (Brush) - Application.Current.Resources["SystemFillColorSuccessBackgroundBrush"]; - Brush WARN_BG = (Brush) - Application.Current.Resources["SystemFillColorCautionBackgroundBrush"]; - Brush ERROR_BG = (Brush) - Application.Current.Resources["SystemFillColorCriticalBackgroundBrush"]; - Brush BORDER_FG = (Brush)Application.Current.Resources["CardStrokeColorDefaultBrush"]; - Brush TEXT_FG = (Brush)Application.Current.Resources["TextFillColorPrimaryBrush"]; - - string no = CoreTools.Translate("No"); - string yes = CoreTools.Translate("Yes"); - string part = CoreTools.Translate("Partially"); - - foreach (var manager in PEInterface.Managers) - { - ManagersPanel.Children.Add( - new TextBlock() - { - Text = manager.DisplayName, - TextAlignment = TextAlignment.Center, - Padding = new Thickness(3), - } - ); - - var level = manager.Capabilities.SupportsProxy; - ProxyPanel.Children.Add( - new Border() - { - CornerRadius = new CornerRadius(4), - Padding = new Thickness(2), - Background = - level is ProxySupport.No - ? ERROR_BG - : (level is ProxySupport.Partially ? WARN_BG : SUCCESS_BG), - BorderBrush = BORDER_FG, - BorderThickness = new Thickness(1), - Child = new TextBlock() - { - Text = ( - level is ProxySupport.No - ? no - : (level is ProxySupport.Partially ? part : yes) - ), - TextAlignment = TextAlignment.Center, - Foreground = TEXT_FG, - }, - } - ); - - AuthPanel.Children.Add( - new Border() - { - CornerRadius = new CornerRadius(4), - Padding = new Thickness(2), - Background = manager.Capabilities.SupportsProxyAuth ? SUCCESS_BG : ERROR_BG, - BorderBrush = BORDER_FG, - BorderThickness = new Thickness(1), - Child = new TextBlock() - { - Text = manager.Capabilities.SupportsProxyAuth ? yes : no, - TextAlignment = TextAlignment.Center, - Foreground = TEXT_FG, - }, - } - ); - } - } - - public bool CanGoBack => true; - public string ShortTitle => CoreTools.Translate("Internet connection settings"); - - public event EventHandler? RestartRequired; - public event EventHandler? NavigationRequested - { - add { } - remove { } - } - - public void ShowRestartBanner(object sender, EventArgs e) => - RestartRequired?.Invoke(this, e); - - private void UsernameBox_TextChanged(object sender, RoutedEventArgs e) => - _ = _usernameBox_TextChanged(); - - private async Task _usernameBox_TextChanged() - { - SavingUserName.Opacity = 1; - string oldusername = UsernameBox.Text; - string oldpassword = PasswordBox.Password; - await Task.Delay(500); - if (oldusername != UsernameBox.Text) - return; - if (oldpassword != PasswordBox.Password) - return; - Settings.SetProxyCredentials(UsernameBox.Text, PasswordBox.Password); - MainWindow.ApplyProxyVariableToProcess(); - SavingUserName.Opacity = 0; - } - - private void UsernameBox_TextChanged(object sender, TextChangedEventArgs e) => - UsernameBox_TextChanged(sender, new RoutedEventArgs()); - - private void EnableProxy_OnStateChanged(object? sender, EventArgs e) - { - MainWindow.ApplyProxyVariableToProcess(); - } - - private void TextboxCard_OnValueChanged(object? sender, EventArgs e) - { - MainWindow.ApplyProxyVariableToProcess(); - } - } -} diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Notifications.xaml b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Notifications.xaml deleted file mode 100644 index 3a293047f8..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Notifications.xaml +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Notifications.xaml.cs b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Notifications.xaml.cs deleted file mode 100644 index 8e5667c162..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Notifications.xaml.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Navigation; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Pages.SettingsPages.GeneralPages -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class Notifications : Page, ISettingsPage - { - public Notifications() - { - this.InitializeComponent(); - } - - protected override void OnNavigatedTo(NavigationEventArgs e) - { - if (Settings.Get(Settings.K.DisableSystemTray)) - { - ToolbarText.Visibility = Visibility.Visible; - DisableNotifications.IsEnabled = false; - DisableUpdatesNotifications.IsEnabled = false; - DisableErrorNotifications.IsEnabled = false; - DisableSuccessNotifications.IsEnabled = false; - DisableProgressNotifications.IsEnabled = false; - } - else - { - ToolbarText.Visibility = Visibility.Collapsed; - DisableNotifications.IsEnabled = true; - DisableUpdatesNotifications.IsEnabled = true; - DisableErrorNotifications.IsEnabled = true; - DisableSuccessNotifications.IsEnabled = true; - DisableProgressNotifications.IsEnabled = true; - } - base.OnNavigatedTo(e); - } - - public bool CanGoBack => true; - public string ShortTitle => CoreTools.Translate("Notification preferences"); - - public event EventHandler? RestartRequired - { - add { } - remove { } - } - public event EventHandler? NavigationRequested - { - add { } - remove { } - } - } -} diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Operations.xaml b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Operations.xaml deleted file mode 100644 index 406f49bc2b..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Operations.xaml +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Operations.xaml.cs b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Operations.xaml.cs deleted file mode 100644 index f4f5dafc78..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Operations.xaml.cs +++ /dev/null @@ -1,65 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using UniGetUI.Core.Tools; -using UniGetUI.PackageOperations; -using UniGetUI.Pages.DialogPages; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Pages.SettingsPages.GeneralPages -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class Operations : Page, ISettingsPage - { - public Operations() - { - this.InitializeComponent(); - - for (int i = 1; i <= 10; i++) - { - ParallelOperationCount.AddItem(i.ToString(), i.ToString(), false); - } - - ParallelOperationCount.AddItem("15", "15", false); - ParallelOperationCount.AddItem("20", "20", false); - ParallelOperationCount.AddItem("30", "30", false); - ParallelOperationCount.AddItem("50", "50", false); - ParallelOperationCount.AddItem("75", "75", false); - ParallelOperationCount.AddItem("100", "100", false); - ParallelOperationCount.ShowAddedItems(); - } - - public void ShowRestartBanner(object sender, EventArgs e) => - RestartRequired?.Invoke(this, e); - - private void ManageDesktopShortcutsButton_Click(object sender, RoutedEventArgs e) => - _ = DialogHelper.ManageDesktopShortcuts(); - - public bool CanGoBack => true; - public string ShortTitle => CoreTools.Translate("Package operation preferences"); - - public event EventHandler? RestartRequired; - public event EventHandler? NavigationRequested; - - private void ParallelOperationCount_OnValueChanged(object sender, EventArgs e) - { - if (int.TryParse(ParallelOperationCount.SelectedValue(), out int value)) - { - AbstractOperation.MAX_OPERATIONS = value; - } - } - - private void UpdatesSettingsButton_Click(object sender, RoutedEventArgs e) - { - NavigationRequested?.Invoke(this, typeof(Updates)); - } - - private void AdminButton_Click(object sender, RoutedEventArgs e) - { - NavigationRequested?.Invoke(this, typeof(Administrator)); - } - } -} diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/SettingsHomepage.xaml b/src/UniGetUI/Pages/SettingsPages/GeneralPages/SettingsHomepage.xaml deleted file mode 100644 index bd5b7ba151..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/SettingsHomepage.xaml +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/SettingsHomepage.xaml.cs b/src/UniGetUI/Pages/SettingsPages/GeneralPages/SettingsHomepage.xaml.cs deleted file mode 100644 index 8438debc17..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/SettingsHomepage.xaml.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using UniGetUI.Core.Tools; -using UniGetUI.Pages.SettingsPages.GeneralPages; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Pages.SettingsPages -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class SettingsHomepage : Page, ISettingsPage - { - public bool CanGoBack => false; - public string ShortTitle => CoreTools.Translate("UniGetUI Settings"); - - public event EventHandler? RestartRequired - { - add { } - remove { } - } - - public event EventHandler? NavigationRequested; - - public SettingsHomepage() - { - this.InitializeComponent(); - } - - public void Administrator(object s, RoutedEventArgs e) => - NavigationRequested?.Invoke(this, typeof(Administrator)); - - public void Backup(object s, RoutedEventArgs e) => - NavigationRequested?.Invoke(this, typeof(Backup)); - - public void Experimental(object s, RoutedEventArgs e) => - NavigationRequested?.Invoke(this, typeof(Experimental)); - - public void General(object s, RoutedEventArgs e) => - NavigationRequested?.Invoke(this, typeof(General)); - - public void Interface(object s, RoutedEventArgs e) => - NavigationRequested?.Invoke(this, typeof(Interface_P)); - - public void Notifications(object s, RoutedEventArgs e) => - NavigationRequested?.Invoke(this, typeof(Notifications)); - - public void Operations(object s, RoutedEventArgs e) => - NavigationRequested?.Invoke(this, typeof(Operations)); - - public void Startup(object s, RoutedEventArgs e) => - NavigationRequested?.Invoke(this, typeof(Updates)); - - private void Internet(object sender, RoutedEventArgs e) => - NavigationRequested?.Invoke(this, typeof(Internet)); - - private void ManagersShortcut(object sender, RoutedEventArgs e) => - NavigationRequested?.Invoke(this, typeof(ManagersHomepage)); - } -} diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Updates.xaml b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Updates.xaml deleted file mode 100644 index 17f81a0957..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Updates.xaml +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Updates.xaml.cs b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Updates.xaml.cs deleted file mode 100644 index 43d1edf7a8..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Updates.xaml.cs +++ /dev/null @@ -1,193 +0,0 @@ -using CommunityToolkit.WinUI.Controls; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Media; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; -using UniGetUI.PackageEngine; -using UniGetUI.PackageEngine.ManagerClasses.Manager; -using Windows.UI; -using Windows.UI.Text; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Pages.SettingsPages.GeneralPages -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class Updates : Page, ISettingsPage - { - public Updates() - { - this.InitializeComponent(); - - Dictionary updates_dict = new() - { - { CoreTools.Translate("{0} minutes", 10), "600" }, - { CoreTools.Translate("{0} minutes", 30), "1800" }, - { CoreTools.Translate("1 hour"), "3600" }, - { CoreTools.Translate("{0} hours", 2), "7200" }, - { CoreTools.Translate("{0} hours", 4), "14400" }, - { CoreTools.Translate("{0} hours", 8), "28800" }, - { CoreTools.Translate("{0} hours", 12), "43200" }, - { CoreTools.Translate("1 day"), "86400" }, - { CoreTools.Translate("{0} days", 2), "172800" }, - { CoreTools.Translate("{0} days", 3), "259200" }, - { CoreTools.Translate("1 week"), "604800" }, - }; - - foreach (KeyValuePair entry in updates_dict) - { - UpdatesCheckIntervalSelector.AddItem(entry.Key, entry.Value, false); - } - - UpdatesCheckIntervalSelector.ShowAddedItems(); - - // Minimum age for updates - MinimumUpdateAgeSelector.Description = CoreTools.Translate( - "Only show updates that are at least the specified number of days old"); - - Dictionary minimum_age_dict = new() - { - { CoreTools.Translate("No minimum age"), "0" }, - { CoreTools.Translate("1 day"), "1" }, - { CoreTools.Translate("{0} days", 3), "3" }, - { CoreTools.Translate("{0} days", 7), "7" }, - { CoreTools.Translate("{0} days", 14), "14" }, - { CoreTools.Translate("{0} days", 30), "30" }, - { CoreTools.Translate("Custom..."), "custom" }, - }; - - foreach (KeyValuePair entry in minimum_age_dict) - MinimumUpdateAgeSelector.AddItem(entry.Key, entry.Value, false); - - MinimumUpdateAgeSelector.ShowAddedItems(); - MinimumUpdateAgeSelector.ValueChanged += (_, _) => RefreshMinimumAgeLayout(); - RefreshMinimumAgeLayout(); - - MinimumUpdateAgeCustomInput.PlaceholderText = CoreTools.Translate("e.g. 10"); - MinimumUpdateAgeCustomInput.Text = Settings.GetValue(Settings.K.MinimumUpdateAgeCustom); - MinimumUpdateAgeCustomInput.TextChanged += (_, _) => - { - string current = MinimumUpdateAgeCustomInput.Text ?? ""; - string filtered = new string(current.Where(char.IsDigit).ToArray()); - if (filtered != current) - { - MinimumUpdateAgeCustomInput.Text = filtered; - return; - } - if (filtered.Length > 0) - Settings.SetValue(Settings.K.MinimumUpdateAgeCustom, filtered); - else - Settings.Set(Settings.K.MinimumUpdateAgeCustom, false); - }; - - ReleaseDateCompatTableHolder.Content = BuildReleaseDateCompatTable(); - } - - private void RefreshMinimumAgeLayout() - { - bool isCustom = Settings.GetValue(Settings.K.MinimumUpdateAge) == "custom"; - MinimumUpdateAgeCustomCard.Visibility = isCustom ? Visibility.Visible : Visibility.Collapsed; - } - - private static UIElement BuildReleaseDateCompatTable() - { - var managers = PEInterface.Managers.ToList(); - - var table = new Grid { ColumnSpacing = 24, RowSpacing = 8 }; - table.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); - table.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); - for (int i = 0; i <= managers.Count; i++) - table.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto }); - - var h1 = new TextBlock - { - Text = CoreTools.Translate("Package manager"), - FontWeight = new FontWeight(600), - }; - var h2 = new TextBlock - { - Text = CoreTools.Translate("Supports release dates"), - FontWeight = new FontWeight(600), - HorizontalAlignment = HorizontalAlignment.Center, - }; - Grid.SetRow(h1, 0); Grid.SetColumn(h1, 0); - Grid.SetRow(h2, 0); Grid.SetColumn(h2, 1); - table.Children.Add(h1); - table.Children.Add(h2); - - for (int i = 0; i < managers.Count; i++) - { - var manager = managers[i]; - int row = i + 1; - var name = new TextBlock { Text = manager.DisplayName, VerticalAlignment = VerticalAlignment.Center }; - Grid.SetRow(name, row); Grid.SetColumn(name, 0); - var badge = MakeStatusBadge(manager.Capabilities.KnowsPackageReleaseDate); - Grid.SetRow(badge, row); Grid.SetColumn(badge, 1); - table.Children.Add(name); - table.Children.Add(badge); - } - - var centerPanel = new StackPanel - { - Orientation = Orientation.Horizontal, - HorizontalAlignment = HorizontalAlignment.Center, - Margin = new Thickness(0, 16, 0, 0), - }; - centerPanel.Children.Add(table); - - var card = new SettingsCard - { - CornerRadius = new CornerRadius(8), - HorizontalAlignment = HorizontalAlignment.Stretch, - HorizontalContentAlignment = HorizontalAlignment.Stretch, - }; - card.Header = CoreTools.Translate("Release date support per package manager"); - card.Description = centerPanel; - return card; - } - - private static Border MakeStatusBadge(PackageReleaseDateSupport support) - { - (string text, Color baseColor) = support switch - { - PackageReleaseDateSupport.Yes => (CoreTools.Translate("Yes"), Color.FromArgb(255, 0, 180, 0)), - PackageReleaseDateSupport.Partial => (CoreTools.Translate("Partial"), Color.FromArgb(255, 224, 168, 0)), - _ => (CoreTools.Translate("No"), Color.FromArgb(255, 224, 82, 82)), - }; - - return new Border - { - CornerRadius = new CornerRadius(4), - Padding = new Thickness(4, 2, 4, 2), - BorderThickness = new Thickness(1), - HorizontalAlignment = HorizontalAlignment.Stretch, - Background = new SolidColorBrush(Color.FromArgb(60, baseColor.R, baseColor.G, baseColor.B)), - BorderBrush = new SolidColorBrush(Color.FromArgb(120, baseColor.R, baseColor.G, baseColor.B)), - Child = new TextBlock { Text = text, TextAlignment = TextAlignment.Center }, - }; - } - - public bool CanGoBack => true; - public string ShortTitle => CoreTools.Translate("Package update preferences"); - - public event EventHandler? RestartRequired; - public event EventHandler? NavigationRequested; - - public void ShowRestartBanner(object sender, EventArgs e) => - RestartRequired?.Invoke(this, e); - - private void OperationsSettingsButton_Click(object sender, RoutedEventArgs e) - { - NavigationRequested?.Invoke(this, typeof(Operations)); - } - - private void AdminButton_Click(object sender, RoutedEventArgs e) - { - NavigationRequested?.Invoke(this, typeof(Administrator)); - } - } -} diff --git a/src/UniGetUI/Pages/SettingsPages/ISettingsPage.cs b/src/UniGetUI/Pages/SettingsPages/ISettingsPage.cs deleted file mode 100644 index 33a1942148..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/ISettingsPage.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace UniGetUI.Pages.SettingsPages -{ - interface ISettingsPage - { - public bool CanGoBack { get; } - public string ShortTitle { get; } - - public event EventHandler? RestartRequired; - - public event EventHandler? NavigationRequested; - } -} diff --git a/src/UniGetUI/Pages/SettingsPages/ManagersPages/ManagersHomepage.xaml b/src/UniGetUI/Pages/SettingsPages/ManagersPages/ManagersHomepage.xaml deleted file mode 100644 index 958f83c109..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/ManagersPages/ManagersHomepage.xaml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - diff --git a/src/UniGetUI/Pages/SettingsPages/ManagersPages/ManagersHomepage.xaml.cs b/src/UniGetUI/Pages/SettingsPages/ManagersPages/ManagersHomepage.xaml.cs deleted file mode 100644 index faadb6e64f..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/ManagersPages/ManagersHomepage.xaml.cs +++ /dev/null @@ -1,192 +0,0 @@ -using CommunityToolkit.WinUI; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Navigation; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; -using UniGetUI.Interface.Widgets; -using UniGetUI.PackageEngine; -using UniGetUI.Pages.DialogPages; -using UniGetUI.Pages.SettingsPages.GeneralPages; -using Windows.UI.Text; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Pages.SettingsPages -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class ManagersHomepage : Page, ISettingsPage - { - public bool CanGoBack => false; - public string ShortTitle => CoreTools.Translate("Package manager preferences"); - - public event EventHandler? RestartRequired - { - add { } - remove { } - } - - public event EventHandler? NavigationRequested; - - private readonly List managerControls = new(); - - private bool _isLoadingToggles; - - public ManagersHomepage() - { - this.InitializeComponent(); - - bool first = true; - foreach (var manager in PEInterface.Managers) - { - var button = new SettingsPageButton() - { - Text = manager.DisplayName, - Description = manager.Properties.Description.Split("
")[0], - HeaderIcon = new LocalIcon(manager.Properties.IconId), - Padding = new Thickness(16, 2, 16, 2), - }; - button.CornerRadius = first ? new CornerRadius(8, 8, 0, 0) : new CornerRadius(0); - button.BorderThickness = first ? new Thickness(1) : new Thickness(1, 0, 1, 1); - button.Click += (_, _) => NavigationRequested?.Invoke(this, manager.GetType()); - - var statusIcon = new FontIcon() - { - FontSize = 12, - VerticalAlignment = VerticalAlignment.Center, - }; - var statusText = new TextBlock() - { - FontSize = 12, - FontWeight = new FontWeight(600), - VerticalAlignment = VerticalAlignment.Center, - }; - var statusBorder = new Border() - { - CornerRadius = new CornerRadius(4), - Padding = new Thickness(6, 3, 6, 3), - }; - - void loadStatusBadge() - { - if (!manager.IsEnabled()) - { - statusText.Text = CoreTools.Translate("Disabled"); - statusIcon.Glyph = "\uE814"; - statusIcon.Foreground = (Brush) - Application.Current.Resources["SystemFillColorCautionBrush"]; - statusBorder.Background = (Brush) - Application.Current.Resources["SystemFillColorCautionBackgroundBrush"]; - } - else if (manager.Status.Found) - { - statusText.Text = CoreTools.Translate("Ready"); - statusIcon.Glyph = "\uEC61"; - statusIcon.Foreground = (Brush) - Application.Current.Resources["SystemFillColorSuccessBrush"]; - statusBorder.Background = (Brush) - Application.Current.Resources["SystemFillColorSuccessBackgroundBrush"]; - } - else - { - statusText.Text = CoreTools.Translate("Not found"); - statusIcon.Glyph = "\uEB90"; - statusIcon.Foreground = (Brush) - Application.Current.Resources["SystemFillColorCriticalBrush"]; - statusBorder.Background = (Brush) - Application.Current.Resources["SystemFillColorCriticalBackgroundBrush"]; - } - } - - var toggle = new ToggleSwitch() - { - Height = 22, - OnContent = "", - HorizontalAlignment = HorizontalAlignment.Right, - OffContent = "", - Margin = new Thickness(-10, 0, 0, 0), - }; - toggle.Loaded += (_, _) => loadStatusBadge(); - toggle.Toggled += async (_, _) => - { - if (_isLoadingToggles) - return; - - bool disabled = !toggle.IsOn; - int loadingId = DialogHelper.ShowLoadingDialog( - CoreTools.Translate("Please wait...") - ); - Settings.SetDictionaryItem(Settings.K.DisabledManagers, manager.Name, disabled); - await Task.Run(manager.Initialize); - loadStatusBadge(); - DialogHelper.HideLoadingDialog(loadingId); - }; - - var status = new StackPanel() - { - Orientation = Orientation.Horizontal, - Spacing = 4, - HorizontalAlignment = HorizontalAlignment.Center, - Children = { statusIcon, statusText }, - }; - statusBorder.Child = status; - button.Content = new StackPanel() - { - Orientation = Orientation.Vertical, - Spacing = 4, - Children = { toggle, statusBorder }, - }; - - first = false; - SettingsEntries.Children.Add(button); - managerControls.Add(button); - } - var last = (SettingsPageButton)SettingsEntries.Children[^1]; - last.CornerRadius = new CornerRadius(0, 0, 8, 8); - } - - protected override void OnNavigatedTo(NavigationEventArgs e) - { - _isLoadingToggles = true; - for (int i = 0; i < managerControls.Count; i++) - { - var toggle = (ToggleSwitch) - ((StackPanel)managerControls[i].Content).Children.First(); - toggle.IsOn = !Settings.GetDictionaryItem( - Settings.K.DisabledManagers, - PEInterface.Managers[i].Name - ); - } - _isLoadingToggles = false; - base.OnNavigatedTo(e); - } - - public void Administrator(object s, RoutedEventArgs e) => - NavigationRequested?.Invoke(this, typeof(Administrator)); - - public void Backup(object s, RoutedEventArgs e) => - NavigationRequested?.Invoke(this, typeof(Backup)); - - public void Experimental(object s, RoutedEventArgs e) => - NavigationRequested?.Invoke(this, typeof(Experimental)); - - public void General(object s, RoutedEventArgs e) => - NavigationRequested?.Invoke(this, typeof(General)); - - public void Interface(object s, RoutedEventArgs e) => - NavigationRequested?.Invoke(this, typeof(Interface_P)); - - public void Notifications(object s, RoutedEventArgs e) => - NavigationRequested?.Invoke(this, typeof(Notifications)); - - public void Operations(object s, RoutedEventArgs e) => - NavigationRequested?.Invoke(this, typeof(Operations)); - - public void Startup(object s, RoutedEventArgs e) => - NavigationRequested?.Invoke(this, typeof(Updates)); - } -} diff --git a/src/UniGetUI/Pages/SettingsPages/ManagersPages/PackageManager.xaml b/src/UniGetUI/Pages/SettingsPages/ManagersPages/PackageManager.xaml deleted file mode 100644 index 3bebba72bd..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/ManagersPages/PackageManager.xaml +++ /dev/null @@ -1,220 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/SettingsPages/ManagersPages/PackageManager.xaml.cs b/src/UniGetUI/Pages/SettingsPages/ManagersPages/PackageManager.xaml.cs deleted file mode 100644 index 7c854741a5..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/ManagersPages/PackageManager.xaml.cs +++ /dev/null @@ -1,784 +0,0 @@ -using System.Diagnostics; -using CommunityToolkit.WinUI.Controls; -using ExternalLibraries.Clipboard; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Navigation; -using UniGetUI.Core.Data; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.SettingsEngine.SecureSettings; -using UniGetUI.Core.Tools; -using UniGetUI.Interface.Widgets; -using UniGetUI.PackageEngine; -using UniGetUI.PackageEngine.Interfaces; -using UniGetUI.PackageEngine.ManagerClasses.Manager; -using UniGetUI.PackageEngine.Managers.BunManager; -using UniGetUI.PackageEngine.Managers.CargoManager; -using UniGetUI.PackageEngine.Managers.ChocolateyManager; -using UniGetUI.PackageEngine.Managers.DotNetManager; -using UniGetUI.PackageEngine.Managers.NpmManager; -using UniGetUI.PackageEngine.Managers.PipManager; -using UniGetUI.PackageEngine.Managers.PowerShell7Manager; -using UniGetUI.PackageEngine.Managers.PowerShellManager; -using UniGetUI.PackageEngine.Managers.ScoopManager; -using UniGetUI.PackageEngine.Managers.VcpkgManager; -using UniGetUI.PackageEngine.Managers.WingetManager; -using UniGetUI.PackageEngine.PackageLoader; -using UniGetUI.Pages.DialogPages; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Pages.SettingsPages.GeneralPages -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class PackageManagerPage : Page, ISettingsPage - { - IPackageManager? Manager; - public event EventHandler? RestartRequired; - public event EventHandler? NavigationRequested - { - add { } - remove { } - } - public event EventHandler? ReapplyProperties; - public bool CanGoBack => true; - public string ShortTitle => - Manager is null ? "" : CoreTools.Translate("{0} settings", Manager.DisplayName); - private bool _isLoading; - - public PackageManagerPage() - { - this.InitializeComponent(); - } - - protected override void OnNavigatedTo(NavigationEventArgs e) - { - Manager = null; - if (e.Parameter is not Type Manager_T) - throw new InvalidDataException("The passed parameter was not a type"); - // Can't do switch with types - if (Manager_T == typeof(WinGet)) - Manager = PEInterface.WinGet; - else if (Manager_T == typeof(Chocolatey)) - Manager = PEInterface.Chocolatey; - else if (Manager_T == typeof(Scoop)) - Manager = PEInterface.Scoop; - else if (Manager_T == typeof(Npm)) - Manager = PEInterface.Npm; - else if (Manager_T == typeof(Pip)) - Manager = PEInterface.Pip; - else if (Manager_T == typeof(PowerShell)) - Manager = PEInterface.PowerShell; - else if (Manager_T == typeof(PowerShell7)) - Manager = PEInterface.PowerShell7; - else if (Manager_T == typeof(Cargo)) - Manager = PEInterface.Cargo; - else if (Manager_T == typeof(Bun)) - Manager = PEInterface.Bun; - else if (Manager_T == typeof(Vcpkg)) - Manager = PEInterface.Vcpkg; - else if (Manager_T == typeof(DotNet)) - Manager = PEInterface.DotNet; - else - throw new InvalidCastException("The specified type was not a package manager!"); - - // The manager failed to construct at startup (see PEInterface): there is nothing to configure. - if (Manager is null) - return; - - ReapplyProperties?.Invoke(this, new()); - - ApplyManagerState(); - EnableManager.KeyName = Manager.Name; - EnableManager.Text = CoreTools - .Translate("Enable {pm}") - .Replace("{pm}", Manager.DisplayName); - InstallOptionsTitle.Text = CoreTools.Translate( - "Default installation options for {0} packages", - Manager.DisplayName - ); - - SettingsTitle.Text = CoreTools.Translate("{0} settings", Manager.DisplayName); - StatusTitle.Text = CoreTools.Translate("{0} status", Manager.DisplayName); - - var DisableNotifsCard = new CheckboxCard_Dict() - { - Text = CoreTools - .Translate( - "Ignore packages from {pm} when showing a notification about updates" - ) - .Replace("{pm}", Manager.DisplayName), - DictionaryName = Settings.K.DisabledPackageManagerNotifications, - ForceInversion = true, - KeyName = Manager.Name, - }; - - ManagerLogsLabel.Text = CoreTools.Translate("View {0} logs", Manager.DisplayName); - - // ----------------- EXECUTABLE FILE PICKER ----------------- - ExeFileWarningText.Visibility = SecureSettings.Get( - SecureSettings.K.AllowCustomManagerPaths - ) - ? Visibility.Collapsed - : Visibility.Visible; - GoToSecureSettingsBtn.Visibility = SecureSettings.Get( - SecureSettings.K.AllowCustomManagerPaths - ) - ? Visibility.Collapsed - : Visibility.Visible; - ExecutableComboBox.IsEnabled = SecureSettings.Get( - SecureSettings.K.AllowCustomManagerPaths - ); - BrowseExecutableButton.IsEnabled = SecureSettings.Get( - SecureSettings.K.AllowCustomManagerPaths - ); - - InstallOptionsPanel.Description = new InstallOptions_Manager(Manager); - InstallOptionsPanel.Padding = new(18, 24, 18, 24); - - // ----------------------- SOURCES CONTROL ------------------- - - ExtraControls.Children.Clear(); - - if (Manager.Capabilities.SupportsCustomSources && Manager is not Vcpkg) - { - SettingsCard SourceManagerCard = new() - { - CornerRadius = new CornerRadius(8), - Margin = new Thickness(0, 0, 0, 16), - Padding = new(24, 24, 0, 24), - }; - var man = new SourceManager(Manager); - SourceManagerCard.Description = man; - ExtraControls.Children.Add(SourceManagerCard); - - ExtraControls.Children.Add( - new TextBlock() - { - Margin = new(4, 24, 4, 8), - FontWeight = new Windows.UI.Text.FontWeight(600), - Text = CoreTools.Translate("Advanced options"), - } - ); - } - - // ------------------------- WINGET EXTRA SETTINGS ----------------------- - - if (Manager is WinGet) - { - DisableNotifsCard.CornerRadius = new CornerRadius(8, 8, 0, 0); - DisableNotifsCard.BorderThickness = new Thickness(1, 1, 1, 0); - ExtraControls.Children.Add(DisableNotifsCard); - - ComboboxCard WinGet_CliToolPreference = new() - { - Text = "WinGet command-line tool", - Description = CoreTools.Translate( - "Choose which command-line tool UniGetUI uses for WinGet operations when the COM API is not used" - ), - SettingName = Settings.K.WinGetCliToolPreference, - CornerRadius = new CornerRadius(0), - BorderThickness = new Thickness(1, 0, 1, 1), - }; - WinGet_CliToolPreference.AddItem("Default", "default"); - WinGet_CliToolPreference.AddItem("WinGet", "winget", false); - WinGet_CliToolPreference.AddItem("Pinget", "pinget", false); - WinGet_CliToolPreference.ShowAddedItems(); - WinGet_CliToolPreference.ValueChanged += (_, _) => _ = ReloadPackageManager(); - ExtraControls.Children.Add(WinGet_CliToolPreference); - - ComboboxCard WinGet_ComApiPolicy = new() - { - Text = "WinGet COM API", - Description = CoreTools.Translate( - "Choose whether UniGetUI can use the WinGet COM API before falling back to the command-line tool" - ), - SettingName = Settings.K.WinGetComApiPolicy, - CornerRadius = new CornerRadius(0), - BorderThickness = new Thickness(1, 0, 1, 1), - }; - WinGet_ComApiPolicy.AddItem("Default", "default"); - WinGet_ComApiPolicy.AddItem("Enabled", "enabled"); - WinGet_ComApiPolicy.AddItem("Disabled", "disabled"); - WinGet_ComApiPolicy.ShowAddedItems(); - WinGet_ComApiPolicy.ValueChanged += (_, _) => _ = ReloadPackageManager(); - ExtraControls.Children.Add(WinGet_ComApiPolicy); - - ButtonCard WinGet_ResetWindowsIPackageManager = new() - { - Text = - CoreTools.Translate("Reset WinGet") - + $" ({CoreTools.Translate("This may help if no packages are listed")})", - ButtonText = CoreTools.AutoTranslated("Reset"), - CornerRadius = new CornerRadius(0), - }; - WinGet_ResetWindowsIPackageManager.Click += (_, _) => - _ = DialogHelper.HandleBrokenWinGet(); - ExtraControls.Children.Add(WinGet_ResetWindowsIPackageManager); - - CheckboxCard WinGet_ForceLocationWhenUpdating = new() - { - Text = CoreTools.Translate( - "Force install location parameter when updating packages with custom locations" - ), - SettingName = Settings.K.WinGetForceLocationOnUpdate, - CornerRadius = new CornerRadius(0), - BorderThickness = new Thickness(1, 0, 1, 1), - }; - ExtraControls.Children.Add(WinGet_ForceLocationWhenUpdating); - - CheckboxCard WinGet_DownloadFullManifest = new() - { - Text = CoreTools.Translate( - "Download full package manifest alongside the installer" - ), - SettingName = Settings.K.WinGetDownloadFullManifest, - CornerRadius = new CornerRadius(0), - BorderThickness = new Thickness(1, 0, 1, 1), - }; - ExtraControls.Children.Add(WinGet_DownloadFullManifest); - - CheckboxCard WinGet_EnableTroubleshooter = new() - { - Text = CoreTools.Translate("Enable the automatic WinGet troubleshooter"), - SettingName = Settings.K.DisableWinGetMalfunctionDetector, - CornerRadius = new CornerRadius(0, 0, 8, 8), - BorderThickness = new Thickness(1, 0, 1, 1), - }; - WinGet_EnableTroubleshooter.StateChanged += (_, _) => - { - MainApp.Instance.MainWindow.WinGetWarningBanner.IsOpen = false; - _ = InstalledPackagesLoader.Instance.ReloadPackages(); - }; - ExtraControls.Children.Add(WinGet_EnableTroubleshooter); - - /*CheckboxCard WinGet_HideNonApplicableUpdates = new() - { - Text = CoreTools.Translate("Add updates that fail with a 'no applicable update found' to the ignored updates list"), - SettingName = Settings.K.IgnoreUpdatesNotApplicable, - CornerRadius = new CornerRadius(0, 0, 8, 8) - }; - ExtraControls.Children.Add(WinGet_HideNonApplicableUpdates);*/ - } - // ---------------------------- SCOOP EXTRA SETTINGS ------------------------- - - else if (Manager is Scoop) - { - DisableNotifsCard.CornerRadius = new CornerRadius(8, 8, 0, 0); - DisableNotifsCard.BorderThickness = new Thickness(1, 1, 1, 0); - ExtraControls.Children.Add(DisableNotifsCard); - - ButtonCard Scoop_Install = new() - { - Text = CoreTools.AutoTranslated("Install Scoop"), - ButtonText = CoreTools.AutoTranslated("Install"), - CornerRadius = new CornerRadius(0), - }; - Scoop_Install.Click += (_, _) => - { - _ = CoreTools.LaunchBatchFile( - Path.Join( - CoreData.UniGetUIExecutableDirectory, - "Assets", - "Utilities", - "install_scoop.cmd" - ), - CoreTools.Translate("Scoop Installer - UniGetUI") - ); - RestartRequired?.Invoke(this, new()); - }; - ExtraControls.Children.Add(Scoop_Install); - - ButtonCard Scoop_Uninstall = new() - { - Text = CoreTools.AutoTranslated("Uninstall Scoop (and its packages)"), - ButtonText = CoreTools.AutoTranslated("Uninstall"), - CornerRadius = new CornerRadius(0), - BorderThickness = new Thickness(1, 0, 1, 0), - }; - Scoop_Uninstall.Click += (_, _) => - { - _ = CoreTools.LaunchBatchFile( - Path.Join( - CoreData.UniGetUIExecutableDirectory, - "Assets", - "Utilities", - "uninstall_scoop.cmd" - ), - CoreTools.Translate("Scoop Uninstaller - UniGetUI") - ); - RestartRequired?.Invoke(this, new()); - }; - ExtraControls.Children.Add(Scoop_Uninstall); - - ButtonCard Scoop_ResetAppCache = new() - { - Text = CoreTools.AutoTranslated("Run cleanup and clear cache"), - ButtonText = CoreTools.AutoTranslated("Run"), - CornerRadius = new CornerRadius(0), - }; - Scoop_ResetAppCache.Click += (_, _) => - { - _ = CoreTools.LaunchBatchFile( - Path.Join( - CoreData.UniGetUIExecutableDirectory, - "Assets", - "Utilities", - "scoop_cleanup.cmd" - ), - CoreTools.Translate("Clearing Scoop cache - UniGetUI"), - RunAsAdmin: true - ); - }; - ExtraControls.Children.Add(Scoop_ResetAppCache); - - CheckboxCard Scoop_CleanupOnStart = new() - { - CornerRadius = new CornerRadius(0, 0, 8, 8), - BorderThickness = new Thickness(1, 0, 1, 1), - SettingName = Settings.K.EnableScoopCleanup, - Text = "Enable Scoop cleanup on launch", - }; - ExtraControls.Children.Add(Scoop_CleanupOnStart); - } - // -------------------------------- BUN EXTRA SETTINGS ---------------------------------- - - else if (Manager is Bun) - { - DisableNotifsCard.CornerRadius = new CornerRadius(8, 8, 0, 0); - DisableNotifsCard.BorderThickness = new Thickness(1, 1, 1, 0); - ExtraControls.Children.Add(DisableNotifsCard); - - CheckboxCard Bun_PreferLatestVersions = new() - { - CornerRadius = new CornerRadius(0, 0, 8, 8), - BorderThickness = new Thickness(1, 0, 1, 1), - SettingName = Settings.K.BunPreferLatestVersions, - Text = CoreTools.Translate("Prefer latest versions (may include breaking changes) instead of recommended safe updates"), - }; - ExtraControls.Children.Add(Bun_PreferLatestVersions); - } - // -------------------------------- VCPKG EXTRA SETTINGS -------------------------------------- - - else if (Manager is Vcpkg) - { - DisableNotifsCard.CornerRadius = new CornerRadius(8, 8, 0, 0); - DisableNotifsCard.BorderThickness = new Thickness(1, 1, 1, 0); - ExtraControls.Children.Add(DisableNotifsCard); - - Settings.SetValue(Settings.K.DefaultVcpkgTriplet, Vcpkg.GetDefaultTriplet()); - ComboboxCard Vcpkg_DefaultTriplet = new() - { - Text = CoreTools.Translate("Default vcpkg triplet"), - SettingName = Settings.K.DefaultVcpkgTriplet, - CornerRadius = new CornerRadius(0), - }; - foreach (string triplet in Vcpkg.GetSystemTriplets()) - { - Vcpkg_DefaultTriplet.AddItem(triplet, triplet); - } - - Vcpkg_DefaultTriplet.ShowAddedItems(); - ExtraControls.Children.Add(Vcpkg_DefaultTriplet); - - ButtonCard Vcpkg_CustomVcpkgRoot = new() - { - Text = "Change vcpkg root location", - ButtonText = "Select", - CornerRadius = new CornerRadius(0, 0, 8, 8), - BorderThickness = new Thickness(1, 0, 1, 1), - }; - StackPanel p = new() { Orientation = Orientation.Horizontal, Spacing = 5 }; - var VcPkgRootLabel = new TextBlock { VerticalAlignment = VerticalAlignment.Center }; - var ResetVcPkgRootLabel = new HyperlinkButton - { - Content = CoreTools.Translate("Reset"), - }; - var OpenVcPkgRootLabel = new HyperlinkButton - { - Content = CoreTools.Translate("Open"), - }; - - VcPkgRootLabel.Text = Settings.Get(Settings.K.CustomVcpkgRoot) - ? Settings.GetValue(Settings.K.CustomVcpkgRoot) - : "%VCPKG_ROOT%"; - OpenVcPkgRootLabel.IsEnabled = Settings.Get(Settings.K.CustomVcpkgRoot); - ResetVcPkgRootLabel.IsEnabled = Settings.Get(Settings.K.CustomVcpkgRoot); - - ResetVcPkgRootLabel.Click += (_, _) => - { - VcPkgRootLabel.Text = "%VCPKG_ROOT%"; - Settings.Set(Settings.K.CustomVcpkgRoot, false); - ResetVcPkgRootLabel.IsEnabled = false; - OpenVcPkgRootLabel.IsEnabled = false; - }; - - OpenVcPkgRootLabel.Click += (_, _) => - { - string directory = Settings - .GetValue(Settings.K.CustomVcpkgRoot) - .Replace("/", "\\"); - if (directory.Any()) - CoreTools.Launch(directory); - }; - - Vcpkg_CustomVcpkgRoot.Click += (_, _) => - { - ExternalLibraries.Pickers.FolderPicker openPicker = new( - MainApp.Instance.MainWindow.GetWindowHandle() - ); - string folder = openPicker.Show(); - if (folder != string.Empty) - { - Settings.SetValue(Settings.K.CustomVcpkgRoot, folder); - VcPkgRootLabel.Text = folder; - ResetVcPkgRootLabel.IsEnabled = true; - OpenVcPkgRootLabel.IsEnabled = true; - } - }; - - p.Children.Add(VcPkgRootLabel); - p.Children.Add(ResetVcPkgRootLabel); - p.Children.Add(OpenVcPkgRootLabel); - Vcpkg_CustomVcpkgRoot.Description = p; - Vcpkg_CustomVcpkgRoot.Click += (_, _) => _ = ReloadPackageManager(); - ExtraControls.Children.Add(Vcpkg_CustomVcpkgRoot); - } - // -------------------------------- DEFAULT EXTRA SETTINGS -------------------------------------- - else - { - DisableNotifsCard.CornerRadius = new CornerRadius(8); - DisableNotifsCard.BorderThickness = new Thickness(1, 1, 1, 1); - ExtraControls.Children.Add(DisableNotifsCard); - } - - // ── Per-manager minimum update age - ExtraControls.Children.Add(new TextBlock - { - Margin = new Thickness(4, 24, 4, 8), - FontWeight = new Windows.UI.Text.FontWeight(600), - Text = CoreTools.Translate("Update security"), - }); - - (string Label, string Value)[] ageItems = - [ - (CoreTools.Translate("Use global setting"), ""), - (CoreTools.Translate("No minimum age"), "0"), - (CoreTools.Translate("1 day"), "1"), - (CoreTools.Translate("{0} days", 3), "3"), - (CoreTools.Translate("{0} days", 7), "7"), - (CoreTools.Translate("{0} days", 14), "14"), - (CoreTools.Translate("{0} days", 30), "30"), - (CoreTools.Translate("Custom..."), "custom"), - ]; - - var ageCombo = new ComboBox { MinWidth = 200 }; - foreach (var (label, _) in ageItems) - ageCombo.Items.Add(label); - - string? savedAgeVal = Settings.GetDictionaryItem( - Settings.K.PerManagerMinimumUpdateAge, Manager.Name); - int savedAgeIdx = Array.FindIndex(ageItems, i => i.Value == (savedAgeVal ?? "")); - ageCombo.SelectedIndex = savedAgeIdx >= 0 ? savedAgeIdx : 0; - - var customAgeInput = new TextBox - { - MinWidth = 200, - PlaceholderText = CoreTools.Translate("e.g. 10"), - Text = Settings.GetDictionaryItem( - Settings.K.PerManagerMinimumUpdateAgeCustom, Manager.Name) ?? "", - }; - customAgeInput.TextChanged += (_, _) => - { - string current = customAgeInput.Text ?? ""; - string filtered = new string(current.Where(char.IsDigit).ToArray()); - if (filtered != current) - { - customAgeInput.Text = filtered; - return; - } - if (filtered.Length > 0) - Settings.SetDictionaryItem( - Settings.K.PerManagerMinimumUpdateAgeCustom, Manager.Name, filtered); - else - Settings.RemoveDictionaryKey( - Settings.K.PerManagerMinimumUpdateAgeCustom, Manager.Name); - }; - - bool initiallyCustomAge = savedAgeVal == "custom"; - var releaseDateSupport = Manager.Capabilities.KnowsPackageReleaseDate; - bool ageSupported = releaseDateSupport != PackageReleaseDateSupport.No; - - object ageCardDescription = releaseDateSupport switch - { - PackageReleaseDateSupport.No => new TextBlock - { - Text = CoreTools.Translate( - "{pm} does not provide release dates for its packages, so this setting will have no effect") - .Replace("{pm}", Manager.DisplayName), - Foreground = new Microsoft.UI.Xaml.Media.SolidColorBrush( - Windows.UI.Color.FromArgb(255, 224, 82, 82)), - TextWrapping = TextWrapping.Wrap, - FontSize = 12, - }, - PackageReleaseDateSupport.Partial => new TextBlock - { - Text = CoreTools.Translate( - "{pm} only provides release dates for some of its packages, so this setting will only apply to those packages") - .Replace("{pm}", Manager.DisplayName), - Foreground = new Microsoft.UI.Xaml.Media.SolidColorBrush( - Windows.UI.Color.FromArgb(255, 224, 168, 0)), - TextWrapping = TextWrapping.Wrap, - FontSize = 12, - }, - _ => (object)CoreTools.Translate("Override the global minimum update age for this package manager"), - }; - - ageCombo.IsEnabled = ageSupported; - customAgeInput.IsEnabled = ageSupported; - - var minimumAgeCard = new SettingsCard - { - Header = CoreTools.Translate("Minimum age for updates"), - Description = ageCardDescription, - Content = ageCombo, - CornerRadius = initiallyCustomAge ? new CornerRadius(8, 8, 0, 0) : new CornerRadius(8), - BorderThickness = new Thickness(1), - }; - var customAgeCard = new SettingsCard - { - Header = CoreTools.Translate("Custom minimum age (days)"), - Content = customAgeInput, - Visibility = initiallyCustomAge ? Visibility.Visible : Visibility.Collapsed, - CornerRadius = new CornerRadius(0, 0, 8, 8), - BorderThickness = new Thickness(1, 0, 1, 1), - }; - - ageCombo.SelectionChanged += (_, _) => - { - int idx = ageCombo.SelectedIndex; - if (idx < 0) return; - string val = ageItems[idx].Value; - - bool isCustom = val == "custom"; - customAgeCard.Visibility = isCustom ? Visibility.Visible : Visibility.Collapsed; - minimumAgeCard.CornerRadius = isCustom - ? new CornerRadius(8, 8, 0, 0) - : new CornerRadius(8); - - if (string.IsNullOrEmpty(val)) - Settings.RemoveDictionaryKey( - Settings.K.PerManagerMinimumUpdateAge, Manager.Name); - else - Settings.SetDictionaryItem( - Settings.K.PerManagerMinimumUpdateAge, Manager.Name, val); - }; - - ExtraControls.Children.Add(minimumAgeCard); - ExtraControls.Children.Add(customAgeCard); - - // Hide the AppExecutionAliasWarning element if Manager is not Pip - if (Manager is Pip) - { - ManagerLogs.CornerRadius = new CornerRadius(8, 8, 0, 0); - AppExecutionAliasWarningLabel.Text = - "If Python cannot be found or is not listing packages but is installed on the system, you may need to disable the \"python.exe\" App Execution Alias in the settings."; - } - else - { - AppExecutionAliasWarning.Visibility = Visibility.Collapsed; - } - - base.OnNavigatedTo(e); - } - - private void ShowVersionHyperlink_Click(object sender, RoutedEventArgs e) => - ApplyManagerState(true); - - void ApplyManagerState(bool ShowVersion = false) - { - if (Manager is null) - throw new InvalidDataException(); - - // Load version and manager path - ShowVersionHyperlink.Visibility = Visibility.Collapsed; - LongVersionTextBlock.Visibility = Visibility.Collapsed; - LongVersionTextBlock.Text = Manager.Status.Version + "\n"; - LocationLabel.Text = - Manager.Status.ExecutablePath + " " + Manager.Status.ExecutableCallArgs.Trim(); - if (Manager.Status.ExecutablePath == "") - LocationLabel.Text = CoreTools.Translate( - "The executable file for {0} was not found", - Manager.DisplayName - ); - - // Load executable selection - ExecutableComboBox.SelectionChanged -= ExecutableComboBox_SelectionChanged; - ExecutableComboBox.Items.Clear(); - foreach (var path in Manager.FindCandidateExecutableFiles()) - { - AddExecutableComboBoxItem(path); - } - string configuredValue = - Settings.GetDictionaryItem(Settings.K.ManagerPaths, Manager.Name) - ?? ""; - string selectedValue = configuredValue; - if (!string.IsNullOrEmpty(configuredValue) && File.Exists(configuredValue)) - { - AddExecutableComboBoxItem(configuredValue); - } - if (string.IsNullOrEmpty(selectedValue)) - { - var exe = Manager.GetExecutableFile(); - selectedValue = exe.Item1 ? exe.Item2 : ""; - } - else if (!File.Exists(selectedValue)) - { - var exe = Manager.GetExecutableFile(); - selectedValue = exe.Item1 ? exe.Item2 : ""; - } - - ExecutableComboBox.SelectedValue = selectedValue; - ExecutableComboBox.SelectionChanged += ExecutableComboBox_SelectionChanged; - - // Load version block text and style - if (_isLoading) - { - ManagerStatusBar.Severity = InfoBarSeverity.Informational; - ManagerStatusBar.Title = CoreTools.Translate("Please wait..."); - ManagerStatusBar.Message = ""; - } - else if (!Manager.IsEnabled()) - { - ManagerStatusBar.Severity = InfoBarSeverity.Warning; - ManagerStatusBar.Title = CoreTools - .Translate("{pm} is disabled") - .Replace("{pm}", Manager.DisplayName); - ManagerStatusBar.Message = CoreTools - .Translate("Enable it to install packages from {pm}.") - .Replace("{pm}", Manager.DisplayName); - } - else if (Manager.Status.Found) - { - ManagerStatusBar.Severity = InfoBarSeverity.Success; - ManagerStatusBar.Title = CoreTools - .Translate("{pm} is enabled and ready to go") - .Replace("{pm}", Manager.DisplayName); - if (!Manager.Status.Version.Contains('\n')) - { - ManagerStatusBar.Message = - CoreTools.Translate("{pm} version:").Replace("{pm}", Manager.DisplayName) - + $" {Manager.Status.Version}"; - } - else if (ShowVersion) - { - ManagerStatusBar.Message = CoreTools - .Translate("{pm} version:") - .Replace("{pm}", Manager.DisplayName); - LongVersionTextBlock.Visibility = Visibility.Visible; - } - else - { - ManagerStatusBar.Message = ""; - ShowVersionHyperlink.Visibility = Visibility.Visible; - } - } - else // manager was not found - { - ManagerStatusBar.Severity = InfoBarSeverity.Error; - ManagerStatusBar.Title = CoreTools - .Translate("{pm} was not found!") - .Replace("{pm}", Manager.DisplayName); - ManagerStatusBar.Message = CoreTools - .Translate("You may need to install {pm} in order to use it with UniGetUI.") - .Replace("{pm}", Manager.DisplayName); - } - } - - private void ManagerPath_Click(object sender, RoutedEventArgs e) => - _ = _managerPath_Click(); - - private async Task _managerPath_Click() - { - WindowsClipboard.SetText(LocationLabel.Text); - CopyButtonIcon.Symbol = Symbol.Accept; - await Task.Delay(1000); - CopyButtonIcon.Symbol = Symbol.Copy; - } - - private void ManagerLogs_Click(object sender, RoutedEventArgs e) - { - MainApp.Instance.MainWindow.NavigationPage.OpenManagerLogs(Manager); - } - - private void ExecutableComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (ExecutableComboBox.SelectedValue?.ToString() is not { Length: > 0 } selectedValue) - return; - - Settings.SetDictionaryItem( - Settings.K.ManagerPaths, - Manager!.Name, - selectedValue - ); - _ = ReloadPackageManager(); - } - - private void BrowseExecutableButton_Click(object sender, RoutedEventArgs e) => - _ = _browseExecutableButton_Click(); - - private async Task _browseExecutableButton_Click() - { - if (Manager is null) - return; - - ExternalLibraries.Pickers.FileOpenPicker picker = new( - MainApp.Instance.MainWindow.GetWindowHandle() - ); - string file = picker.Show(["*.exe"]); - if (file == string.Empty) - return; - - Settings.SetDictionaryItem(Settings.K.ManagerPaths, Manager.Name, file); - await ReloadPackageManager(); - } - - private void AddExecutableComboBoxItem(string path) - { - if (string.IsNullOrWhiteSpace(path)) - return; - - foreach (object? item in ExecutableComboBox.Items) - { - if (string.Equals(item?.ToString(), path, StringComparison.OrdinalIgnoreCase)) - return; - } - - ExecutableComboBox.Items.Add(path); - } - - private void GoToSecureSettingsBtn_Click(object sender, RoutedEventArgs e) - { - MainApp.Instance.MainWindow.NavigationPage.OpenSettingsPage(typeof(Administrator)); - } - - private void EnableManager_OnStateChanged(object? sender, EventArgs e) => - _ = ReloadPackageManager(); - - private async Task ReloadPackageManager() - { - if (Manager is null) - return; - int loadingId = DialogHelper.ShowLoadingDialog(CoreTools.Translate("Please wait...")); - _isLoading = true; - ApplyManagerState(); - await Task.Run(Manager.Initialize); - _isLoading = false; - ApplyManagerState(); - DialogHelper.HideLoadingDialog(loadingId); - } - } -} diff --git a/src/UniGetUI/Pages/SettingsPages/SettingsBasePage.xaml b/src/UniGetUI/Pages/SettingsPages/SettingsBasePage.xaml deleted file mode 100644 index 3c7e9e3640..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/SettingsBasePage.xaml +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/SettingsPages/SettingsBasePage.xaml.cs b/src/UniGetUI/Pages/SettingsPages/SettingsBasePage.xaml.cs deleted file mode 100644 index add452cf30..0000000000 --- a/src/UniGetUI/Pages/SettingsPages/SettingsBasePage.xaml.cs +++ /dev/null @@ -1,166 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Media.Animation; -using Microsoft.UI.Xaml.Navigation; -using UniGetUI.Core.Tools; -using UniGetUI.Interface.Pages; -using UniGetUI.PackageEngine.Interfaces; -using UniGetUI.PackageEngine.ManagerClasses.Manager; -using UniGetUI.Pages.SettingsPages.GeneralPages; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Pages.SettingsPages -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class SettingsBasePage : Page, IEnterLeaveListener, IInnerNavigationPage - { - private readonly bool IsManagers; - - public SettingsBasePage(bool isManagers) - { - IsManagers = isManagers; - this.InitializeComponent(); - BackButton.Click += (_, _) => - { - if (MainNavigationFrame.Content is ManagersHomepage or SettingsHomepage) - MainApp.Instance.MainWindow.GoBack(); - else if (MainNavigationFrame.CanGoBack) - MainNavigationFrame.GoBack(); - else - MainNavigationFrame.Navigate( - isManagers ? typeof(ManagersHomepage) : typeof(SettingsHomepage), - null, - new DrillInNavigationTransitionInfo() - ); - }; - MainNavigationFrame.Navigated += MainNavigationFrame_Navigated; - MainNavigationFrame.Navigating += MainNavigationFrame_Navigating; - MainNavigationFrame.Navigate( - isManagers ? typeof(ManagersHomepage) : typeof(SettingsHomepage), - null, - new DrillInNavigationTransitionInfo() - ); - - RestartRequired.Message = CoreTools.Translate( - "Restart UniGetUI to fully apply changes" - ); - var RestartButton = new Button - { - HorizontalAlignment = HorizontalAlignment.Right, - Content = CoreTools.Translate("Restart UniGetUI"), - }; - RestartButton.Click += (_, _) => MainApp.Instance.KillAndRestart(); - RestartRequired.ActionButton = RestartButton; - } - - private void MainNavigationFrame_Navigating(object sender, NavigatingCancelEventArgs e) - { - if (MainNavigationFrame.Content is null) - return; - var page = MainNavigationFrame.Content as ISettingsPage; - if (page is null) - throw new InvalidCastException("Settings page does not inherit from ISettingsPage"); - - page.NavigationRequested -= Page_NavigationRequested; - page.RestartRequired -= Page_RestartRequired; - if (page is PackageManagerPage pmpage) - pmpage.ReapplyProperties -= SettingsBasePage_ReapplyProperties; - } - - private void MainNavigationFrame_Navigated(object sender, NavigationEventArgs e) - { - var page = e.Content as ISettingsPage; - if (page is null) - throw new InvalidCastException("Settings page does not inherit from ISettingsPage"); - - BackButton.Visibility = Visibility.Visible; - SettingsTitle.Text = page.ShortTitle; - page.NavigationRequested += Page_NavigationRequested; - page.RestartRequired += Page_RestartRequired; - if (page is PackageManagerPage pmpage) - pmpage.ReapplyProperties += SettingsBasePage_ReapplyProperties; - } - - private void SettingsBasePage_ReapplyProperties(object? sender, EventArgs e) - { - BackButton.Visibility = - ((MainNavigationFrame.Content as ISettingsPage)?.CanGoBack ?? true) - ? Visibility.Visible - : Visibility.Collapsed; - SettingsTitle.Text = - (MainNavigationFrame.Content as ISettingsPage)?.ShortTitle - ?? "INVALID CONTENT PAGE!"; - } - - private void Page_RestartRequired(object? sender, EventArgs e) - { - RestartRequired.IsOpen = true; - } - - private void Page_NavigationRequested(object? sender, Type e) - { - if (e == typeof(ManagersHomepage)) - { - MainApp.Instance.MainWindow.NavigationPage.NavigateTo(Interface.PageType.Managers); - } - if (e.IsSubclassOf(typeof(PackageManager))) - { - MainNavigationFrame.Navigate( - typeof(PackageManagerPage), - e, - new SlideNavigationTransitionInfo() - { - Effect = SlideNavigationTransitionEffect.FromRight, - } - ); - } - else - { - MainNavigationFrame.Navigate( - e, - null, - new SlideNavigationTransitionInfo() - { - Effect = SlideNavigationTransitionEffect.FromRight, - } - ); - } - } - - public void NavigateTo(IPackageManager manager) - { - Page_NavigationRequested(this, manager.GetType()); - } - - public void NavigateTo(Type e) - { - MainNavigationFrame.Navigate(e, null, new DrillInNavigationTransitionInfo()); - } - - public void OnEnter() => - MainNavigationFrame.Navigate( - IsManagers ? typeof(ManagersHomepage) : typeof(SettingsHomepage), - null, - new DrillInNavigationTransitionInfo() - ); - - public void OnLeave() { } - - public bool CanGoBack() => - MainNavigationFrame.CanGoBack - && MainNavigationFrame.Content is not SettingsHomepage - && MainNavigationFrame.Content is not ManagersHomepage; - - public void GoBack() - { - if (CanGoBack()) - MainNavigationFrame.GoBack(); - else - MainApp.Instance.MainWindow.GoBack(); - } - } -} diff --git a/src/UniGetUI/Pages/SoftwarePages/AbstractPackagesPage.xaml b/src/UniGetUI/Pages/SoftwarePages/AbstractPackagesPage.xaml deleted file mode 100644 index f473535fc1..0000000000 --- a/src/UniGetUI/Pages/SoftwarePages/AbstractPackagesPage.xaml +++ /dev/null @@ -1,1310 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/SoftwarePages/AbstractPackagesPage.xaml.cs b/src/UniGetUI/Pages/SoftwarePages/AbstractPackagesPage.xaml.cs deleted file mode 100644 index aed8c4ab79..0000000000 --- a/src/UniGetUI/Pages/SoftwarePages/AbstractPackagesPage.xaml.cs +++ /dev/null @@ -1,1753 +0,0 @@ -using System.Collections.Concurrent; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Globalization; -using CommunityToolkit.WinUI; -using Microsoft.UI; -using Microsoft.UI.Input; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; -using UniGetUI.Interface.Enums; -using UniGetUI.Interface.Pages; -using UniGetUI.Interface.Telemetry; -using UniGetUI.Interface.Widgets; -using UniGetUI.PackageEngine.Enums; -using UniGetUI.PackageEngine.Interfaces; -using UniGetUI.PackageEngine.PackageClasses; -using UniGetUI.PackageEngine.PackageLoader; -using UniGetUI.Pages.DialogPages; -using UniGetUI.Pages.PageInterfaces; -using Windows.System; -using Windows.UI; -using Windows.UI.Core; -using DispatcherQueuePriority = Microsoft.UI.Dispatching.DispatcherQueuePriority; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace UniGetUI.Interface -{ - public abstract partial class AbstractPackagesPage - : IKeyboardShortcutListener, - IEnterLeaveListener, - ISearchBoxPage - { - protected struct PackagesPageData - { - public bool DisableAutomaticPackageLoadOnStart; - public bool MegaQueryBlockEnabled; - public bool PackagesAreCheckedByDefault; - public bool ShowLastLoadTime; - public bool DisableSuggestedResultsRadio; - public bool DisableFilterOnQueryChange; - public bool DisableReload; - - public OperationType PageRole; - public AbstractPackageLoader Loader; - - public string PageName; - public string PageTitle; - public string Glyph; - - public string NoPackages_BackgroundText; - public string NoPackages_SourcesText; - public string NoPackages_SubtitleText_Base; - public string MainSubtitle_StillLoading; - public string NoMatches_BackgroundText; - } - - protected enum ReloadReason - { - FirstRun, - Automated, - Manual, - External, - } - - static class FilterHelpers - { - public static string NormalizeCase(string input) => input.ToLower(); - - public static string NormalizeSpecialCharacters(string input) - { - input = input - .Replace("-", "") - .Replace("_", "") - .Replace(" ", "") - .Replace("@", "") - .Replace("\t", "") - .Replace(".", "") - .Replace(",", "") - .Replace(":", ""); - foreach ( - KeyValuePair entry in new Dictionary - { - { 'a', "àáäâ" }, - { 'e', "èéëê" }, - { 'i', "ìíïî" }, - { 'o', "òóöô" }, - { 'u', "ùúüû" }, - { 'y', "ýÿ" }, - { 'c', "ç" }, - { 'ñ', "n" }, - } - ) - { - foreach (char InvalidChar in entry.Value) - { - input = input.Replace(InvalidChar, entry.Key); - } - } - return input; - } - - public static bool NameContains( - IPackage pkg, - string query, - List> filters - ) - { - string treatedName = pkg.Name; - foreach (var filter in filters) - treatedName = filter(treatedName); - return treatedName.Contains(query); - } - - public static bool IdContains( - IPackage pkg, - string query, - List> filters - ) - { - string treatedId = pkg.Id; - foreach (var filter in filters) - treatedId = filter(treatedId); - return treatedId.Contains(query); - } - - public static bool NameOrIdContains( - IPackage pkg, - string query, - List> filters - ) => NameContains(pkg, query, filters) || IdContains(pkg, query, filters); - - public static bool NameOrIdExactMatch( - IPackage pkg, - string query, - List> filters - ) - { - string treatedId = pkg.Id; - foreach (var filter in filters) - treatedId = filter(treatedId); - if (query == treatedId) - return true; - - string treatedName = pkg.Name; - foreach (var filter in filters) - treatedName = filter(treatedName); - return query == treatedName; - } - } - - protected readonly bool DISABLE_AUTOMATIC_PACKAGE_LOAD_ON_START; - protected readonly bool MEGA_QUERY_BOX_ENABLED; - protected readonly bool SHOW_LAST_CHECKED_TIME; - protected readonly bool DISABLE_FILTER_ON_QUERY_CHANGE; - protected readonly bool DISABLE_RELOAD; - protected readonly string PAGE_NAME; - public readonly bool RoleIsUpdateLike; - protected AppBarButton ReloadButton = new(); - protected DateTime LastPackageLoadTime { get; private set; } - protected readonly OperationType PAGE_ROLE; - - protected AutoSuggestBox QueryBlock - { - get => MainApp.Instance.MainWindow.GlobalSearchBox; - } - - protected IPackage? SelectedItem - { - get - { - if (CurrentPackageList.SelectedItem is PackageWrapper w) - return w.Package; - - var fp = FilteredPackages.GetCheckedPackages(); - if (fp.Count == 1) - return fp.First(); - DialogHelper.ShowDismissableBalloon( - CoreTools.Translate("Invalid selection"), - fp.Count == 0 - ? CoreTools.Translate("No package was selected") - : CoreTools.Translate("More than 1 package was selected") - ); - return null; - } - } - - protected ItemsView CurrentPackageList - { - get => - ( - ViewModeSelector.SelectedIndex switch - { - 1 => PackageList_Grid, - 2 => PackageList_Icons, - _ => PackageList_List, - } - ); - } - - protected AbstractPackageLoader Loader; - - public readonly ObservablePackageCollection FilteredPackages = []; - private readonly ObservableCollection WrappedPackages = []; - private IEnumerable? LastQueryResult; - - protected List UsedManagers = []; - protected ConcurrentDictionary< - IPackageManager, - List - > UsedSourcesForManager = []; - protected ConcurrentDictionary RootNodeForManager = []; - protected ConcurrentDictionary NodesForSources = []; - private readonly TreeViewNode LocalPackagesNode = new(); - - public readonly int NewVersionLabelWidth; - public readonly int NewVersionIconWidth; - private SplitViewDisplayMode _filterPanelCurrentMode = SplitViewDisplayMode.CompactInline; - - protected abstract void WhenPackagesLoaded(ReloadReason reason); - protected abstract void WhenPackageCountUpdated(); - protected abstract void WhenShowingContextMenu(IPackage package); - public abstract void GenerateToolBar(); - public abstract BetterMenu GenerateContextMenu(); - - protected readonly string NoPackages_BackgroundText; - protected readonly string NoPackages_SourcesText; - protected readonly string MainSubtitle_StillLoading; - protected readonly string NoPackages_SubtitleText_Base; - protected readonly string NoMatches_BackgroundText; - - protected string NoPackages_SubtitleText - { - get => - NoPackages_SubtitleText_Base - + ( - SHOW_LAST_CHECKED_TIME - ? " " - + CoreTools.Translate( - "(Last checked: {0})", - LastPackageLoadTime.ToString(CultureInfo.CurrentCulture) - ) - : "" - ); - } - - public string QueryBackup { get; set; } = ""; - - private readonly string _searchPlaceholder; - public string SearchBoxPlaceholder => _searchPlaceholder; - - private string TypeQuery = ""; - private int LastKeyDown; - private readonly int QUERY_SEPARATION_TIME = 1000; // 500ms between keypresses starts a new query - - protected AbstractPackagesPage(PackagesPageData data) - { - // Load page attributes - PAGE_NAME = data.PageName; - DISABLE_AUTOMATIC_PACKAGE_LOAD_ON_START = data.DisableAutomaticPackageLoadOnStart; - DISABLE_FILTER_ON_QUERY_CHANGE = data.DisableFilterOnQueryChange; - MEGA_QUERY_BOX_ENABLED = data.MegaQueryBlockEnabled; - SHOW_LAST_CHECKED_TIME = data.ShowLastLoadTime; - DISABLE_RELOAD = data.DisableReload; - - PAGE_ROLE = data.PageRole; - RoleIsUpdateLike = PAGE_ROLE == OperationType.Update; - NewVersionLabelWidth = RoleIsUpdateLike ? 125 : 0; - NewVersionIconWidth = RoleIsUpdateLike ? 24 : 0; - - NoPackages_BackgroundText = data.NoPackages_BackgroundText; - NoPackages_SourcesText = data.NoPackages_SourcesText; - NoPackages_SubtitleText_Base = data.NoPackages_SubtitleText_Base; - MainSubtitle_StillLoading = data.MainSubtitle_StillLoading; - - NoMatches_BackgroundText = data.NoMatches_BackgroundText; - Loader = data.Loader; - - // Load UI - InitializeComponent(); - - // Selection of grid view mode - int viewMode = Settings.GetDictionaryItem( - Settings.K.PackageListViewMode, - PAGE_NAME - ); - if (viewMode < 0 || viewMode >= ViewModeSelector.Items.Count) - viewMode = 0; - ViewModeSelector.SelectedIndex = viewMode; - GenerateHeaderBarTitles(); - - ToolTipService.SetToolTip(Selector_List, CoreTools.Translate("List")); - ToolTipService.SetToolTip(Selector_Grid, CoreTools.Translate("Grid")); - ToolTipService.SetToolTip(Selector_Icons, CoreTools.Translate("Icons")); - - MainTitle.Text = data.PageTitle; - HeaderIcon.Glyph = data.Glyph; - - SelectAllCheckBox.IsChecked = data.PackagesAreCheckedByDefault; - QuerySimilarResultsRadio.IsEnabled = !data.DisableSuggestedResultsRadio; - QueryOptionsGroup.SelectedIndex = 1; - QueryOptionsGroup.SelectedIndex = 2; - QueryOptionsGroup.SelectedItem = QueryBothRadio; - - Loader.StartedLoading += Loader_StartedLoading; - Loader.FinishedLoading += Loader_FinishedLoading; - Loader.PackagesChanged += Loader_PackagesChanged; - - // Clear cached filtering result - WrappedPackages.CollectionChanged += (_, _) => LastQueryResult = null; - - if (Loader.IsLoading) - { - Loader_StartedLoading(this, EventArgs.Empty); - } - else - { - Loader_FinishedLoading(this, EventArgs.Empty); - FilterPackages(); - } - Loader_PackagesChanged(this, new(false, [], [])); - - LastPackageLoadTime = DateTime.Now; - LocalPackagesNode.Content = CoreTools.Translate("Local"); - LocalPackagesNode.IsExpanded = false; - - ReloadButton.Click += async (_, _) => await LoadPackages(); - ReloadButton.Visibility = DISABLE_RELOAD ? Visibility.Collapsed : Visibility.Visible; - - // Handle the Enter Pressed event on the MegaQueryBlock - MegaQueryBlock.KeyUp += (_, e) => - { - if (e.Key != VirtualKey.Enter) - return; - - MegaQueryBlockGrid.Visibility = Visibility.Collapsed; - QueryBlock.Text = MegaQueryBlock.Text.Trim(); - if (!DISABLE_FILTER_ON_QUERY_CHANGE) - FilterPackages(true); - }; - - // Hande the MegaQueryBlock search button click - MegaFindButton.Click += (_, _) => - { - MegaQueryBlockGrid.Visibility = Visibility.Collapsed; - QueryBlock.Text = MegaQueryBlock.Text.Trim(); - FilterPackages(true); - }; - - // Handle when a source is clicked - SourcesTreeView.Tapped += (_, e) => - { - TreeViewNode? node = - (e.OriginalSource as FrameworkElement)?.DataContext as TreeViewNode; - if (node is null) - { - return; - } - - if (SourcesTreeView.SelectedNodes.Contains(node)) - { - SourcesTreeView.SelectedNodes.Remove(node); - } - else - { - SourcesTreeView.SelectedNodes.Add(node); - } - - FilterPackages(); - }; - - // Handle when a source is double-clicked - SourcesTreeView.RightTapped += (_, e) => - { - TreeViewNode? node = - (e.OriginalSource as FrameworkElement)?.DataContext as TreeViewNode; - if (node is null) - { - return; - } - - SourcesTreeView.SelectedNodes.Clear(); - SourcesTreeView.SelectedNodes.Add(node); - FilterPackages(); - }; - - if (MEGA_QUERY_BOX_ENABLED) - { - MegaQueryBlockGrid.Visibility = Visibility.Visible; - MegaQueryBlock.Focus(FocusState.Programmatic); - BackgroundText.Visibility = Visibility.Collapsed; - } - - _searchPlaceholder = CoreTools.Translate("Search for packages"); - MegaQueryBlock.PlaceholderText = _searchPlaceholder; - InstantSearchCheckbox.IsChecked = !Settings.GetDictionaryItem( - Settings.K.DisableInstantSearch, - PAGE_NAME - ); - - HeaderIcon.FontWeight = new Windows.UI.Text.FontWeight(700); - - NameHeader.Click += (_, _) => SortPackagesBy(ObservablePackageCollection.Sorter.Name); - IdHeader.Click += (_, _) => SortPackagesBy(ObservablePackageCollection.Sorter.Id); - VersionHeader.Click += (_, _) => - SortPackagesBy(ObservablePackageCollection.Sorter.Version); - NewVersionHeader.Click += (_, _) => - SortPackagesBy(ObservablePackageCollection.Sorter.NewVersion); - SourceHeader.Click += (_, _) => - SortPackagesBy(ObservablePackageCollection.Sorter.Source); - - OrderByName_Menu.Click += (_, _) => - SortPackagesBy(ObservablePackageCollection.Sorter.Name); - OrderById_Menu.Click += (_, _) => SortPackagesBy(ObservablePackageCollection.Sorter.Id); - OrderByVer_Menu.Click += (_, _) => - SortPackagesBy(ObservablePackageCollection.Sorter.Version); - OrderByNewVer_Menu.Click += (_, _) => - SortPackagesBy(ObservablePackageCollection.Sorter.NewVersion); - OrderBySrc_Menu.Click += (_, _) => - SortPackagesBy(ObservablePackageCollection.Sorter.Source); - - OrderAsc_Menu.Click += (_, _) => SortPackagesBy(ascendent: true); - OrderDesc_Menu.Click += (_, _) => SortPackagesBy(ascendent: false); - - ReloadButton.Icon = new LocalIcon(IconType.Reload); - ReloadButton.Label = CoreTools.Translate("Reload"); - ToolBar.PrimaryCommands.Add(ReloadButton); - ToolBar.PrimaryCommands.Add(new AppBarSeparator()); - - GenerateToolBar(); - var menu = GenerateContextMenu(); - - PackageList_List.ContextFlyout = menu; - PackageList_Grid.ContextFlyout = menu; - PackageList_Icons.ContextFlyout = menu; - - Loaded += (_, _) => ChangeFilteringPaneLayout(); - UpdateSortingMenu(); - } - - private void GenerateHeaderBarTitles() - { - if (ViewModeSelector.SelectedIndex == 0) - { - NameHeader.Content = CoreTools.Translate("Package Name"); - IdHeader.Content = CoreTools.Translate("Package ID"); - VersionHeader.Content = CoreTools.Translate("Version"); - NewVersionHeader.Content = CoreTools.Translate("New version"); - SourceHeader.Content = CoreTools.Translate("Source"); - } - else - { - NameHeader.Content = ""; - IdHeader.Content = ""; - VersionHeader.Content = ""; - NewVersionHeader.Content = ""; - SourceHeader.Content = ""; - } - } - - private void Loader_PackagesChanged( - object? sender, - PackagesChangedEvent packagesChangedEvent - ) - { - // Ensure we are in the UI thread - if (Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread() is null) - { - DispatcherQueue.TryEnqueue(() => - Loader_PackagesChanged(sender, packagesChangedEvent) - ); - return; - } - - // Procedural package upgrade - if (packagesChangedEvent.ProceduralChange) - { - // Add added packages - foreach (var package in packagesChangedEvent.AddedPackages) - { - if (WrappedPackages.Where(w => w.Package.Equals(package)).Any()) - continue; - - WrappedPackages.Add(new PackageWrapper(package, this)); - AddPackageToSourcesList(package); - } - - // Remove removed packages - var toRemove = new List(); - foreach (var package in packagesChangedEvent.RemovedPackages) - foreach (var match in WrappedPackages.Where(w => w.Package.Equals(package))) - { - toRemove.Add(match); - } - - foreach (var wrapper in toRemove) - { - wrapper.Dispose(); - WrappedPackages.Remove(wrapper); - } - } - else - { - // Reset internal package cache, and update from loader - foreach (var wrapper in WrappedPackages) - wrapper.Dispose(); - WrappedPackages.Clear(); - ClearSourcesList(); - foreach (var package in Loader.Packages) - { - WrappedPackages.Add(new PackageWrapper(package, this)); - AddPackageToSourcesList(package); - } - } - FilterPackages(); - } - - private void Loader_FinishedLoading(object? sender, EventArgs e) - { - // Ensure we are in the UI thread - if (Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread() is null) - { - DispatcherQueue.TryEnqueue(() => Loader_FinishedLoading(sender, e)); - return; - } - - LoadingProgressBar.Visibility = Visibility.Collapsed; - // Required to update UI labels - LastPackageLoadTime = DateTime.Now; - ToolTipService.SetToolTip( - ReloadButton, - CoreTools.Translate("Last checked: {0}", LastPackageLoadTime.ToString(CultureInfo.CurrentCulture)) - ); - UpdatePackageCount(); - WhenPackagesLoaded(ReloadReason.External); - } - - private void Loader_StartedLoading(object? sender, EventArgs e) - { - // Ensure we are in the UI thread - if (Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread() is null) - { - DispatcherQueue.TryEnqueue(() => Loader_StartedLoading(sender, e)); - return; - } - LoadingProgressBar.Visibility = Visibility.Visible; - UpdatePackageCount(); - } - - public void SearchTriggered() - { - QueryBlock.Focus(FocusState.Pointer); - } - - public void ReloadTriggered() - { - if (DISABLE_RELOAD) - return; - _ = LoadPackages(ReloadReason.Manual); - } - - public void SelectAllTriggered() - { - if (QueryBlock.FocusState == FocusState.Unfocused) - { - if (!SelectAllCheckBox.IsChecked ?? false) - { - SelectAllCheckBox.IsChecked = true; - FilteredPackages.SelectAll(); - } - else - { - SelectAllCheckBox.IsChecked = false; - FilteredPackages.ClearSelection(); - } - } - } - - protected void AddPackageToSourcesList(IPackage package) - { - IManagerSource source = package.Source; - if (!UsedManagers.Contains(source.Manager)) - { - UsedManagers.Add(source.Manager); - var node = new TreeViewNode - { - Content = - source.Manager.DisplayName - + " .", - IsExpanded = false, - }; - SourcesTreeView.RootNodes.Add(node); - - // Smart way to decide whether to check a source or not. - // - Always check a source by default if no sources are present - // - Otherwise, Check a source only if half of the sources have already been checked - if (SourcesTreeView.RootNodes.Count == 0) - { - SourcesTreeView.SelectedNodes.Add(node); - } - else if (SourcesTreeView.SelectedNodes.Count >= SourcesTreeView.RootNodes.Count / 2) - { - SourcesTreeView.SelectedNodes.Add(node); - } - - RootNodeForManager.TryAdd(source.Manager, node); - UsedSourcesForManager.TryAdd(source.Manager, []); - SourcesPlaceholderText.Visibility = Visibility.Collapsed; - SourcesTreeViewGrid.Visibility = Visibility.Visible; - } - - if ( - ( - !UsedSourcesForManager.ContainsKey(source.Manager) - || !UsedSourcesForManager[source.Manager].Contains(source) - ) && source.Manager.Capabilities.SupportsCustomSources - ) - { - UsedSourcesForManager[source.Manager].Add(source); - TreeViewNode item = new() - { - Content = - source.Name - + " .", - }; - NodesForSources.TryAdd(source, item); - - if (source.IsVirtualManager) - { - LocalPackagesNode.Children.Add(item); - if (!SourcesTreeView.RootNodes.Contains(LocalPackagesNode)) - { - SourcesTreeView.RootNodes.Add(LocalPackagesNode); - SourcesTreeView.SelectedNodes.Add(LocalPackagesNode); - } - } - else - { - RootNodeForManager[source.Manager].Children.Add(item); - } - } - } - - private void FilterOptionsChanged(object sender, RoutedEventArgs e) - { - if (QueryBothRadio is null) - { - return; - } - - FilterPackages(true); - } - - private void InstantSearchValueChanged(object sender, RoutedEventArgs e) => - Settings.SetDictionaryItem( - Settings.K.DisableInstantSearch, - PAGE_NAME, - !InstantSearchCheckbox.IsChecked - ); - - private void SourcesTreeView_SelectionChanged( - TreeView sender, - TreeViewSelectionChangedEventArgs args - ) => FilterPackages(); - - public virtual async Task LoadPackages() => await LoadPackages(ReloadReason.External); - - protected void ClearSourcesList() - { - UsedManagers.Clear(); - SourcesTreeView?.RootNodes?.Clear(); - UsedSourcesForManager.Clear(); - RootNodeForManager.Clear(); - NodesForSources.Clear(); - LocalPackagesNode?.Children?.Clear(); - } - - /// - /// Reload the packages for this Page - /// Calling this method will trigger a reload on the associated PackageLoader, unless it is already loading packages. - /// - protected async Task LoadPackages(ReloadReason reason) - { - if ( - !Loader.IsLoading - && ( - !Loader.IsLoaded - || reason == ReloadReason.External - || reason == ReloadReason.Manual - || reason == ReloadReason.Automated - ) - ) - { - await Loader.ReloadPackages(); - } - } - - private void SelectAndScrollTo(int index, bool focus) - { - if (index < 0 || index >= FilteredPackages.Count) - return; - - CurrentPackageList.Select(index); - - double position; - if (CurrentPackageList.Layout is StackLayout) - { - position = index * 39; - } - else if (CurrentPackageList.Layout is UniformGridLayout gl) - { - int columnCount = (int)( - (CurrentPackageList.ActualWidth - 8) / (gl.MinItemWidth + 8) - ); - int row = index / Math.Max(columnCount, 1); - position = Math.Max(row - 1, 0) * (gl.MinItemHeight + 8); - Debug.WriteLine($"pos: {position}, colCount:{columnCount}, {row}"); - } - else - { - throw new InvalidCastException("The layout was not recognized"); - } - - if ( - position < CurrentPackageList.ScrollView.VerticalOffset - || position - > CurrentPackageList.ScrollView.VerticalOffset - + CurrentPackageList.ScrollView.ActualHeight - ) - { - CurrentPackageList.ScrollView.ScrollTo( - 0, - position, - new ScrollingScrollOptions( - ScrollingAnimationMode.Disabled, - ScrollingSnapPointsMode.Ignore - ) - ); - } - - if (focus) - Focus(FilteredPackages[index].Package); - } - - private void Focus(IPackage packageToFocus, int retryCount = 0) - { - if (retryCount > 20) - return; - - DispatcherQueue.TryEnqueue( - DispatcherQueuePriority.Low, - () => - { - PackageItemContainer? containerToFocus = - CurrentPackageList.FindDescendant(c => - c.Package?.Equals(packageToFocus) == true - ); - if (containerToFocus == null) - { - Focus(packageToFocus, ++retryCount); - return; - } - - if (!containerToFocus.IsSelected) - { - PackageItemContainer? selectedContainer = - CurrentPackageList.FindDescendant(c => - c.IsSelected - ); - if (selectedContainer?.Package?.Equals(packageToFocus) == true) - containerToFocus = selectedContainer; - else - { - Focus(packageToFocus, ++retryCount); - return; - } - } - containerToFocus.Focus(FocusState.Keyboard); - } - ); - } - - public void PackageList_CharacterReceived(object sender, CharacterReceivedRoutedEventArgs e) - { - char ch = Char.ToLower(e.Character); - - if (('a' <= ch && ch <= 'z') || ('0' <= ch && ch <= '9')) - { - if (Environment.TickCount - LastKeyDown > QUERY_SEPARATION_TIME) - { - TypeQuery = ch.ToString(); - } - else - { - TypeQuery += ch.ToString(); - } - - int IdQueryIndex = -1; - int NameSimilarityIndex = -1; - int IdSimilarityIndex = -1; - bool SelectedPackage = false; - for (int i = 0; i < FilteredPackages.Count; i++) - { - if (FilteredPackages[i].Package.Name.ToLower().StartsWith(TypeQuery)) - { - SelectAndScrollTo(i, true); - SelectedPackage = true; - break; - } - // To avoid jumping back high up because an ID matched it (prevent typing "wi" focusing id:"WildfireGames.0AD" instead of name:"Windows") - if ( - IdQueryIndex == -1 - && FilteredPackages[i].Package.Id.ToLower().StartsWith(TypeQuery) - ) - { - IdQueryIndex = i; - } - if ( - NameSimilarityIndex == -1 - && FilteredPackages[i].Package.Name.ToLower().Contains(TypeQuery) - ) - { - NameSimilarityIndex = i; - } - if ( - IdSimilarityIndex == -1 - && FilteredPackages[i].Package.Id.ToLower().Contains(TypeQuery) - ) - { - IdSimilarityIndex = i; - } - } - int QueryIndex = - IdQueryIndex > -1 - ? IdQueryIndex - : (NameSimilarityIndex > -1 ? NameSimilarityIndex : IdSimilarityIndex); - if (!SelectedPackage) - { - bool SameChars = true; - char LastChar = TypeQuery.ToCharArray()[0]; - foreach (var c in TypeQuery) - { - if (c != LastChar) - { - SameChars = false; - break; - } - LastChar = c; - } - - if (SameChars) - { - int IndexOffset = TypeQuery.Length - 1; - int FirstIdx = -1; - int LastIdx = -1; - for (int idx = 0; idx < FilteredPackages.Count; idx++) - { - if (FilteredPackages[idx].Package.Name.ToLower().StartsWith(LastChar)) - { - if (FirstIdx == -1) - FirstIdx = idx; - LastIdx = idx; - } - else if (FirstIdx > -1) - { - // Break after the LastIdx has been set - break; - } - } - - SelectAndScrollTo( - FirstIdx + (IndexOffset % (LastIdx - FirstIdx + 1)), - true - ); - } - else if (QueryIndex > -1) - { - SelectAndScrollTo(QueryIndex, true); - } - } - } - LastKeyDown = Environment.TickCount; - } - - /// - /// Applies labels, tooltips and icons to all the menubar items - /// - /// - /// - /// - protected void ApplyTextAndIconsToToolbar( - IDictionary Labels, - IDictionary Icons - ) - { - foreach (DependencyObject item in Labels.Keys) - { - string text = Labels[item].Trim(); - ToolTipService.SetToolTip(item, text); - if (item is AppBarButton toolButton) - { - toolButton.IsCompact = Labels[toolButton][0] == ' '; - if (toolButton.IsCompact) - { - toolButton.LabelPosition = CommandBarLabelPosition.Collapsed; - } - toolButton.Label = text; - } - else if (item is BetterMenuItem menuItem) - { - menuItem.UntranslatedText = text; - } - else - throw new InvalidCastException( - "item must be of type AppBarButton or MenuFlyoutButton" - ); - } - - foreach (DependencyObject item in Icons.Keys) - { - var icon = Icons[item]; - if (item is AppBarButton barButton) - { - barButton.Icon = new LocalIcon(icon); - } - else if (item is BetterMenuItem menuItem) - { - menuItem.IconName = icon; - } - else - throw new InvalidCastException( - "item must be of type AppBarButton or MenuFlyoutButton" - ); - } - } - - /// - /// Will filter the packages with the query on QueryBlock.Text and put the - /// resulting packages on the ItemsView - /// - public void FilterPackages(bool forceQueryUpdate = false) - { - var previousSelection = CurrentPackageList.SelectedItem as PackageWrapper; - - List visibleSources = []; - List visibleManagers = []; - - if (SourcesTreeView.SelectedNodes.Count > 0) - { - foreach (TreeViewNode node in SourcesTreeView.SelectedNodes) - { - if (NodesForSources.Values.Contains(node)) - { - visibleSources.Add(NodesForSources.First(x => x.Value == node).Key); - } - else if (RootNodeForManager.Values.Contains(node)) - { - IPackageManager manager = RootNodeForManager - .First(x => x.Value == node) - .Key; - visibleManagers.Add(manager); - if (!manager.Capabilities.SupportsCustomSources) - continue; - - foreach ( - IManagerSource source in manager.SourcesHelper.Factory.GetAvailableSources() - ) - if (!visibleSources.Contains(source)) - visibleSources.Add(source); - } - } - } - - // Filter only by query when needed - if (forceQueryUpdate || LastQueryResult is null) - { - // Load applied filters and prepare query - List> appliedFilters = []; - if (UpperLowerCaseCheckbox.IsChecked is false) - appliedFilters.Add(FilterHelpers.NormalizeCase); - if (IgnoreSpecialCharsCheckbox.IsChecked is true) - appliedFilters.Add(FilterHelpers.NormalizeSpecialCharacters); - - string treatedQuery = QueryBlock.Text.Trim(); - foreach (var filter in appliedFilters) - treatedQuery = filter(treatedQuery); - // treatedQuery now has the appropiate content - - if (QueryIdRadio.IsChecked is true) - LastQueryResult = WrappedPackages.Where(wrapper => - FilterHelpers.NameContains(wrapper.Package, treatedQuery, appliedFilters) - ); - else if (QueryNameRadio.IsChecked is true) - LastQueryResult = WrappedPackages.Where(wrapper => - FilterHelpers.IdContains(wrapper.Package, treatedQuery, appliedFilters) - ); - else if (QueryBothRadio.IsChecked is true) - LastQueryResult = WrappedPackages.Where(wrapper => - FilterHelpers.NameOrIdContains( - wrapper.Package, - treatedQuery, - appliedFilters - ) - ); - else if (QueryExactMatch.IsChecked == true) - LastQueryResult = WrappedPackages.Where(wrapper => - FilterHelpers.NameOrIdExactMatch( - wrapper.Package, - treatedQuery, - appliedFilters - ) - ); - else // QuerySimilarResultsRadio == true - LastQueryResult = WrappedPackages; - } - - List matchingList_selectedSources = []; - - foreach (var match in LastQueryResult) - { - if ( - visibleSources.Contains(match.Package.Source) - || ( - !match.Package.Manager.Capabilities.SupportsCustomSources - && visibleManagers.Contains(match.Package.Manager) - ) - ) - { - matchingList_selectedSources.Add(match); - } - } - - FilteredPackages.FromRange(matchingList_selectedSources); - UpdatePackageCount(); - - if (previousSelection is not null) - { - for (int i = 0; i < FilteredPackages.Count; i++) - { - if (FilteredPackages[i].Package.Equals(previousSelection.Package)) - { - SelectAndScrollTo(i, false); - break; - } - } - } - else - { - _ = ForceRedrawByScroll(); - } - - if (!Settings.Get(Settings.K.DisableIconsOnPackageLists)) - _ = LoadIconsForNewPackages(); - } - - /// - /// Updates the UI to reflect the current amount of packages - /// - public void UpdatePackageCount() - { - var selected = FilteredPackages.Where(p => p.IsChecked).Count(); - var unSelected = FilteredPackages.Where(p => !p.IsChecked).Count(); - if (selected is 0 && unSelected is not 0) - SelectAllCheckBox.IsChecked = false; - else if (selected is not 0 && unSelected is 0) - SelectAllCheckBox.IsChecked = true; - else if (selected is not 0 && unSelected is not 0) - SelectAllCheckBox.IsChecked = null; - - string GetSubtitleText() - { - string r = - CoreTools.Translate( - "{0} packages were found, {1} of which match the specified filters.", - FilteredPackages.Count, - Loader.Count() - ) - + " (" - + CoreTools.Translate("{0} selected", selected) - + ")"; - - if (SHOW_LAST_CHECKED_TIME) - r += - " " - + CoreTools.Translate( - "(Last checked: {0})", - LastPackageLoadTime.ToString(CultureInfo.CurrentCulture) - ); - return r; - } - - if (FilteredPackages.Any()) - { - BackgroundText.Text = NoPackages_BackgroundText; - BackgroundText.Visibility = Loader.Any() - ? Visibility.Collapsed - : Visibility.Visible; - MainSubtitle.Text = GetSubtitleText(); - } - else - { - if (LoadingProgressBar.Visibility is Visibility.Collapsed) - { - if (Loader.Any()) - { - BackgroundText.Text = NoMatches_BackgroundText; - SourcesPlaceholderText.Visibility = Visibility.Collapsed; - MainSubtitle.Text = GetSubtitleText(); - } - else - { - BackgroundText.Text = NoPackages_BackgroundText; - SourcesPlaceholderText.Text = NoPackages_SourcesText; - SourcesPlaceholderText.Visibility = Visibility.Visible; - MainSubtitle.Text = NoPackages_SubtitleText; - } - - BackgroundText.Visibility = Visibility.Visible; - } - else - { - BackgroundText.Visibility = Loader.Any() - ? Visibility.Collapsed - : Visibility.Visible; - BackgroundText.Text = MainSubtitle_StillLoading; - SourcesPlaceholderText.Visibility = Loader.Any() - ? Visibility.Collapsed - : Visibility.Visible; - SourcesPlaceholderText.Text = MainSubtitle_StillLoading; - MainSubtitle.Text = MainSubtitle_StillLoading; - } - } - - if (MegaQueryBlockGrid.Visibility == Visibility.Visible) - { - BackgroundText.Visibility = Visibility.Collapsed; - } - WhenPackageCountUpdated(); - } - - /// - /// Changes how the packages are sorted - /// - /// The information with which to sort the packages - public void SortPackagesBy(ObservablePackageCollection.Sorter sorter) - { - if (sorter == FilteredPackages.CurrentSorter) - FilteredPackages.Descending = !FilteredPackages.Descending; - FilteredPackages.SetSorter(sorter); - FilteredPackages.Sort(); - UpdateSortingMenu(); - } - - public void SortPackagesBy(bool ascendent) - { - FilteredPackages.Descending = !ascendent; - FilteredPackages.Sort(); - UpdateSortingMenu(); - } - - private void UpdateSortingMenu() - { - OrderByName_Menu.IsChecked = - FilteredPackages.CurrentSorter is ObservablePackageCollection.Sorter.Name; - OrderById_Menu.IsChecked = - FilteredPackages.CurrentSorter is ObservablePackageCollection.Sorter.Id; - OrderByVer_Menu.IsChecked = - FilteredPackages.CurrentSorter is ObservablePackageCollection.Sorter.Version; - OrderByNewVer_Menu.IsChecked = - FilteredPackages.CurrentSorter is ObservablePackageCollection.Sorter.NewVersion; - OrderBySrc_Menu.IsChecked = - FilteredPackages.CurrentSorter is ObservablePackageCollection.Sorter.Source; - - OrderAsc_Menu.IsChecked = !FilteredPackages.Descending; - OrderDesc_Menu.IsChecked = FilteredPackages.Descending; - - OrderByButton.Content = FilteredPackages.CurrentSorter switch - { - ObservablePackageCollection.Sorter.Name => CoreTools.Translate("Name"), - ObservablePackageCollection.Sorter.Id => CoreTools.Translate("Id"), - ObservablePackageCollection.Sorter.Version => CoreTools.Translate("Version"), - ObservablePackageCollection.Sorter.NewVersion => CoreTools.Translate("New version"), - ObservablePackageCollection.Sorter.Source => CoreTools.Translate("Source"), - _ => throw new InvalidDataException(), - }; - } - - protected void SelectAllSourcesButton_Click(object sender, RoutedEventArgs e) - { - SourcesTreeView.SelectAll(); - } - - protected void ClearSourceSelectionButton_Click(object sender, RoutedEventArgs e) - { - SourcesTreeView.SelectedItems.Clear(); - FilterPackages(); - } - - protected void ShowDetailsForPackage(IPackage? package, TEL_InstallReferral referral) - { - if (package is null) - return; - - if (package.Source.IsVirtualManager || package is InvalidImportedPackage) - { - DialogHelper.ShowDismissableBalloon( - CoreTools.Translate("Something went wrong"), - CoreTools.Translate( - "\"{0}\" is a local package and does not have available details", - package.Name - ) - ); - return; - } - - _ = DialogHelper.ShowPackageDetails(package, PAGE_ROLE, referral); - } - - protected void OpenPackageInstallLocation(IPackage? package) - { - string? path = package?.Manager.DetailsHelper.GetInstallLocation(package); - CoreTools.Launch(path); - } - - protected async Task ShowInstallationOptionsForPackage(IPackage? package) - { - if (package is null) - return; - - if (package.Source.IsVirtualManager || package is InvalidImportedPackage) - { - DialogHelper.ShowDismissableBalloon( - CoreTools.Translate("Something went wrong"), - CoreTools.Translate( - "\"{0}\" is a local package and is not compatible with this feature", - package.Name - ) - ); - return; - } - - if (await DialogHelper.ShowInstallatOptions_Continue(package, PAGE_ROLE)) - { - PerformMainPackageAction(package); - } - } - - private void SidepanelWidth_SizeChanged(object sender, SizeChangedEventArgs e) - { - int rawWidth = (int)e.NewSize.Width; - if (rawWidth == (int)(e.NewSize.Width / 10) || rawWidth == 25) - { - return; - } - - int newWidth = Math.Clamp(rawWidth, 0, 600); - - if (newWidth < 100) - { - HideFilteringPane(); - Settings.SetDictionaryItem(Settings.K.SidepanelWidths, PAGE_NAME, 250); - } - else - { - FilteringPanel.OpenPaneLength = newWidth; - ToggleFiltersButtonWidth.MinWidth = newWidth; - Settings.SetDictionaryItem(Settings.K.SidepanelWidths, PAGE_NAME, newWidth); - } - } - - protected void PerformMainPackageAction(IPackage? package) - { - if (package is null) - { - return; - } - - if (PAGE_ROLE == OperationType.Install) - { - _ = MainApp.Operations.Install(package, TEL_InstallReferral.DIRECT_SEARCH); - } - else if (PAGE_ROLE == OperationType.Update) - { - _ = MainApp.Operations.Update(package); - } - else // if (PageRole == OperationType.Uninstall) - { - _ = MainApp.Operations.ConfirmAndUninstall(package); - } - } - - public void FocusPackageList() - { - if (MEGA_QUERY_BOX_ENABLED) - DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, - () => MegaQueryBlock.Focus(FocusState.Programmatic)); - else - CurrentPackageList.Focus(FocusState.Programmatic); - } - - public async Task ShowContextMenu(PackageWrapper wrapper) - { - CurrentPackageList.Select(wrapper.Index); - await Task.Delay(20); - if (_lastContextMenuButtonTapped is not null) - (CurrentPackageList.ContextFlyout as BetterMenu)?.ShowAt( - _lastContextMenuButtonTapped, - new FlyoutShowOptions { Placement = FlyoutPlacementMode.RightEdgeAlignedTop } - ); - WhenShowingContextMenu(wrapper.Package); - } - - public void PackageItemContainer_RightTapped(object sender, RightTappedRoutedEventArgs e) - { - if (sender is PackageItemContainer container && container.Package is not null) - { - CurrentPackageList.Select(container.Wrapper.Index); - container.Focus(FocusState.Keyboard); - WhenShowingContextMenu(container.Package); - } - } - - public void PackageItemContainer_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e) - { - if (sender is PackageItemContainer container && container.Package is not null) - { - CurrentPackageList.Select(container.Wrapper.Index); - container.Focus(FocusState.Keyboard); - - TEL_InstallReferral referral = TEL_InstallReferral.ALREADY_INSTALLED; - if (PAGE_NAME == "Bundles") - referral = TEL_InstallReferral.FROM_BUNDLE; - if (PAGE_NAME == "Discover") - referral = TEL_InstallReferral.DIRECT_SEARCH; - ShowDetailsForPackage(container.Package, referral); - } - } - - private void SelectAllCheckBox_ValueChanged(object sender, RoutedEventArgs e) - { - if (SelectAllCheckBox.IsChecked == true) - { - FilteredPackages.SelectAll(); - } - else - { - FilteredPackages.ClearSelection(); - } - } - - private async Task ForceRedrawByScroll() - { - if (CurrentPackageList is not null) - { - CurrentPackageList.ScrollView?.ScrollBy(0, 1); - await Task.Delay(10); - CurrentPackageList.ScrollView?.ScrollBy(0, -1); - } - } - - private void ToggleFiltersButton_Click(object sender, RoutedEventArgs e) - { - if (FilteringPanel.DisplayMode is SplitViewDisplayMode.Inline) - { - Settings.SetDictionaryItem( - Settings.K.HideToggleFilters, - PAGE_NAME, - !ToggleFiltersButton.IsChecked ?? false - ); - } - - if (ToggleFiltersButton.IsChecked ?? false) - { - ShowFilteringPane(); - } - else - { - HideFilteringPane(); - } - } - - private void HideFilteringPane() - { - FilteringPanel.IsPaneOpen = false; - PackagesListGrid.Margin = new Thickness(0, 0, 0, 0); - ToggleFiltersButtonWidth.MinWidth = 4; - } - - private void ShowFilteringPane() - { - if (FilteringPanel.DisplayMode is SplitViewDisplayMode.Inline) - { - int finalWidth = 250; - try - { - finalWidth = Settings.GetDictionaryItem( - Settings.K.SidepanelWidths, - PAGE_NAME - ); - if (finalWidth == 0) - finalWidth = 250; - } - catch - { - Settings.SetDictionaryItem(Settings.K.SidepanelWidths, PAGE_NAME, 250); - } - FilteringPanel.OpenPaneLength = finalWidth; - ToggleFiltersButtonWidth.MinWidth = finalWidth; - PackagesListGrid.Margin = new Thickness(12, 0, 0, 0); - } - else - { - FilteringPanel.OpenPaneLength = 250; - ToggleFiltersButtonWidth.MinWidth = 4; - - if (this.ActualTheme is ElementTheme.Dark) - { - SidePanel.Background = new AcrylicBrush() - { - TintColor = Color.FromArgb(0, 20, 20, 20), - TintOpacity = 0.4, - FallbackColor = Color.FromArgb(0, 20, 20, 20), - TintLuminosityOpacity = 0.8, - }; - } - else - { - SidePanel.Background = new AcrylicBrush() - { - TintColor = Color.FromArgb(0, 250, 250, 250), - TintOpacity = 0.4, - FallbackColor = Color.FromArgb(0, 250, 250, 250), - TintLuminosityOpacity = 0.8, - }; - } - } - FilteringPanel.IsPaneOpen = true; - ToggleFiltersButton.IsChecked = true; - } - - private async Task LoadIconsForNewPackages() - { - var PackagesWithoutIcon = new List(); - // Get the packages to be updated. - foreach (var wrapper in FilteredPackages) - { - if (wrapper.IconWasLoaded) - continue; - wrapper.IconWasLoaded = true; - PackagesWithoutIcon.Add(wrapper); - } - - // Load their icons, one at a time. - foreach (var wrapper in PackagesWithoutIcon) - { - var icon = await Task.Run(wrapper.Package.GetIconUrlIfAny); - if (icon is not null) - wrapper.PackageIcon = icon; - } - } - - public void OnEnter() - { - Visibility = Visibility.Visible; - IsEnabled = true; - } - - public void OnLeave() - { - Visibility = Visibility.Collapsed; - IsEnabled = false; - } - - public void PackageItemContainer_PreviewKeyDown(object sender, KeyRoutedEventArgs e) - { - if ( - e.Key - is not ( - VirtualKey.Up - or VirtualKey.Down - or VirtualKey.Home - or VirtualKey.End - or VirtualKey.Enter - or VirtualKey.Space - ) - || sender is not PackageItemContainer packageItemContainer - ) - { - return; - } - - int index = FilteredPackages.IndexOf(packageItemContainer.Wrapper); - switch (e.Key) - { - case VirtualKey.Up when index > 0: - SelectAndScrollTo(index - 1, true); - e.Handled = true; - break; - case VirtualKey.Down when index < FilteredPackages.Count - 1: - SelectAndScrollTo(index + 1, true); - e.Handled = true; - break; - case VirtualKey.Home when index > 0: - SelectAndScrollTo(0, true); - e.Handled = true; - break; - case VirtualKey.End when index < FilteredPackages.Count - 1: - SelectAndScrollTo(FilteredPackages.Count - 1, true); - e.Handled = true; - break; - } - - if (e.KeyStatus.WasKeyDown) - { - // ignore repeated KeyDown events when pressing and holding a key - return; - } - - IPackage? package = packageItemContainer.Package; - - bool IS_CONTROL_PRESSED = InputKeyboardSource - .GetKeyStateForCurrentThread(VirtualKey.Control) - .HasFlag(CoreVirtualKeyStates.Down); - //bool IS_SHIFT_PRESSED = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down); - bool IS_ALT_PRESSED = InputKeyboardSource - .GetKeyStateForCurrentThread(VirtualKey.LeftMenu) - .HasFlag(CoreVirtualKeyStates.Down); - IS_ALT_PRESSED |= InputKeyboardSource - .GetKeyStateForCurrentThread(VirtualKey.RightMenu) - .HasFlag(CoreVirtualKeyStates.Down); - - if (e.Key == VirtualKey.Enter && package is not null) - { - if (IS_ALT_PRESSED) - { - _ = ShowInstallationOptionsForPackage(package); - e.Handled = true; - } - else if (IS_CONTROL_PRESSED) - { - if (package is not InvalidImportedPackage) - { - PerformMainPackageAction(package); - e.Handled = true; - } - } - else - { - TEL_InstallReferral referral = TEL_InstallReferral.ALREADY_INSTALLED; - if (PAGE_NAME == "Bundles") - referral = TEL_InstallReferral.FROM_BUNDLE; - if (PAGE_NAME == "Discover") - referral = TEL_InstallReferral.DIRECT_SEARCH; - ShowDetailsForPackage(package, referral); - e.Handled = true; - } - } - else if (e.Key == VirtualKey.Space && package is not null) - { - package.IsChecked = !package.IsChecked; - e.Handled = true; - } - } - - private void SetFilterMode_Overlay() - { - if (_filterPanelCurrentMode == SplitViewDisplayMode.Overlay) - return; - - _filterPanelCurrentMode = SplitViewDisplayMode.Overlay; - FilteringPanel.DisplayMode = SplitViewDisplayMode.Overlay; - HideFilteringPane(); - FiltersResizer.Opacity = 0; - ToggleFiltersButton.IsChecked = false; - - // await Task.Delay(200); - FilteringPanel.Shadow = new ThemeShadow(); - SidePanel.BorderThickness = new Thickness(0, 1, 1, 1); - - if (FilteringPanel.Pane is ScrollViewer filters) - { - filters.Padding = new Thickness(8); - filters.Margin = new Thickness(0, 1, 0, 1); - } - } - - private void SetFilterMode_Inline() - { - if (_filterPanelCurrentMode == SplitViewDisplayMode.Inline) - return; - - _filterPanelCurrentMode = SplitViewDisplayMode.Inline; - FilteringPanel.DisplayMode = SplitViewDisplayMode.Inline; - SidePanel.Background = new SolidColorBrush(Colors.Transparent); - FiltersResizer.Opacity = 1; - SidePanel.BorderThickness = new Thickness(0); - - if (FilteringPanel.Pane is ScrollViewer filters) - { - filters.Padding = new Thickness(0); - filters.Margin = new Thickness(0); - } - - if (!Settings.GetDictionaryItem(Settings.K.HideToggleFilters, PAGE_NAME)) - { - ShowFilteringPane(); - } - } - - private void ChangeFilteringPaneLayout() - { - if (FilteringPanel.ActualWidth <= 0) - { - // Nothing, panel is not loaded yet - } - else if (FilteringPanel.ActualWidth < 1000) - { - SetFilterMode_Overlay(); - } - else /*(FilteringPanel.ActualWidth >= 1000)*/ - { - SetFilterMode_Inline(); - } - } - - private void FilteringPanel_SizeChanged(object sender, SizeChangedEventArgs e) - { - ChangeFilteringPaneLayout(); - } - - private void FilteringPanel_PaneClosing( - SplitView sender, - SplitViewPaneClosingEventArgs args - ) - { - ToggleFiltersButton.IsChecked = false; - HideFilteringPane(); - } - - private void ViewModeSelector_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - Settings.SetDictionaryItem( - Settings.K.PackageListViewMode, - PAGE_NAME, - ViewModeSelector.SelectedIndex - ); - GenerateHeaderBarTitles(); - } - - FrameworkElement _lastContextMenuButtonTapped = null!; - - private void ContextMenuButton_Tapped(object sender, TappedRoutedEventArgs e) - { - if (sender is FrameworkElement el) - _lastContextMenuButtonTapped = el; - } - - private bool? _pageIsWide; - private bool? _titleHidden; - private bool? _toolbarLabelsHidden; - - private void ABSTRACT_PAGE_SizeChanged(object sender, SizeChangedEventArgs e) - { - if (ActualWidth < 500) - { - if (_titleHidden != false) - { - _titleHidden = false; - MainSubtitle.Visibility = Visibility.Collapsed; - } - } - else - { - if (_titleHidden != true) - { - _titleHidden = true; - MainSubtitle.Visibility = Visibility.Visible; - } - } - - if (ActualWidth < 700) - { - if (_pageIsWide != false) - { - _pageIsWide = false; - MainTitle.FontSize = 18; - } - } - else - { - if (_pageIsWide != true) - { - MainTitle.FontSize = 24; - _pageIsWide = true; - } - } - - // Collapse the menu bar labels to icon-only on narrow windows so the buttons - // stay reachable instead of being clipped off the right edge. - if (ActualWidth < 900) - { - if (_toolbarLabelsHidden != true) - { - _toolbarLabelsHidden = true; - ToolBar.DefaultLabelPosition = CommandBarDefaultLabelPosition.Collapsed; - } - } - else - { - if (_toolbarLabelsHidden != false) - { - _toolbarLabelsHidden = false; - ToolBar.DefaultLabelPosition = CommandBarDefaultLabelPosition.Right; - } - } - } - - public void SearchBox_TextChanged(object? sender, AutoSuggestBoxTextChangedEventArgs args) - { - if (InstantSearchCheckbox.IsChecked is true && !DISABLE_FILTER_ON_QUERY_CHANGE) - FilterPackages(true); - - if (MEGA_QUERY_BOX_ENABLED && QueryBlock.Text.Trim() == "") - { - MegaQueryBlockGrid.Visibility = Visibility.Visible; - Loader.StopLoading(); - BackgroundText.Visibility = Visibility.Collapsed; - ClearSourcesList(); - WrappedPackages.Clear(); - FilterPackages(true); - MegaQueryBlock.Focus(FocusState.Programmatic); - MegaQueryBlock.Text = ""; - } - } - - public virtual void SearchBox_QuerySubmitted( - object? sender, - AutoSuggestBoxQuerySubmittedEventArgs args - ) - { - // Handle Enter pressed or search button pressed - MegaQueryBlockGrid.Visibility = Visibility.Collapsed; - if (!DISABLE_FILTER_ON_QUERY_CHANGE) - FilterPackages(true); - } - } -} diff --git a/src/UniGetUI/Pages/SoftwarePages/DiscoverSoftwarePage.cs b/src/UniGetUI/Pages/SoftwarePages/DiscoverSoftwarePage.cs deleted file mode 100644 index e894585d43..0000000000 --- a/src/UniGetUI/Pages/SoftwarePages/DiscoverSoftwarePage.cs +++ /dev/null @@ -1,341 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Input; -using UniGetUI.Core.Logging; -using UniGetUI.Core.Tools; -using UniGetUI.Interface.Enums; -using UniGetUI.Interface.Telemetry; -using UniGetUI.Interface.Widgets; -using UniGetUI.PackageEngine; -using UniGetUI.PackageEngine.Enums; -using UniGetUI.PackageEngine.Interfaces; -using UniGetUI.PackageEngine.PackageLoader; -using UniGetUI.Pages.DialogPages; -using Windows.System; -using Windows.UI.Text; - -namespace UniGetUI.Interface.SoftwarePages -{ - public partial class DiscoverSoftwarePage : AbstractPackagesPage - { - private BetterMenuItem? MenuAsAdmin; - private BetterMenuItem? MenuInteractive; - private BetterMenuItem? MenuSkipHash; - private BetterMenuItem? MenuDownloadInstaller; - - public DiscoverSoftwarePage() - : base( - new PackagesPageData - { - DisableAutomaticPackageLoadOnStart = true, - DisableFilterOnQueryChange = true, - MegaQueryBlockEnabled = true, - PackagesAreCheckedByDefault = false, - ShowLastLoadTime = false, - DisableReload = false, - DisableSuggestedResultsRadio = false, - PageName = "Discover", - - Loader = DiscoverablePackagesLoader.Instance, - PageRole = OperationType.Install, - - NoPackages_BackgroundText = CoreTools.Translate( - "No results were found matching the input criteria" - ), - NoPackages_SourcesText = CoreTools.Translate("No packages were found"), - NoPackages_SubtitleText_Base = CoreTools.Translate("No packages were found"), - MainSubtitle_StillLoading = CoreTools.Translate("Loading packages"), - NoMatches_BackgroundText = CoreTools.Translate( - "No results were found matching the input criteria" - ), - - PageTitle = CoreTools.Translate("Discover Packages"), - Glyph = "\uF6FA", - } - ) - { - InstantSearchCheckbox.IsEnabled = false; - InstantSearchCheckbox.Visibility = Visibility.Collapsed; - - MegaFindButton.Click += Event_SearchPackages; - MegaQueryBlock.KeyUp += (s, e) => - { - if (e.Key == VirtualKey.Enter) - { - Event_SearchPackages(s, e); - } - }; - } - - public override void SearchBox_QuerySubmitted( - object? sender, - AutoSuggestBoxQuerySubmittedEventArgs args - ) - { - base.SearchBox_QuerySubmitted(sender, args); - Event_SearchPackages(sender, new()); - } - - public override BetterMenu GenerateContextMenu() - { - BetterMenu menu = new(); - - BetterMenuItem menuInstall = new() - { - Text = CoreTools.AutoTranslated("Install"), - IconName = IconType.Download, - KeyboardAcceleratorTextOverride = "Ctrl+Enter", - }; - menuInstall.Click += MenuInstall_Invoked; - menu.Items.Add(menuInstall); - - menu.Items.Add(new MenuFlyoutSeparator { Height = 5 }); - - BetterMenuItem menuInstallSettings = new() - { - Text = CoreTools.AutoTranslated("Install options"), - IconName = IconType.Options, - KeyboardAcceleratorTextOverride = "Alt+Enter", - }; - menuInstallSettings.Click += MenuInstallSettings_Invoked; - menu.Items.Add(menuInstallSettings); - - menu.Items.Add(new MenuFlyoutSeparator { Height = 5 }); - - MenuAsAdmin = new BetterMenuItem - { - Text = CoreTools.AutoTranslated("Install as administrator"), - IconName = IconType.UAC, - }; - MenuAsAdmin.Click += MenuAsAdmin_Invoked; - menu.Items.Add(MenuAsAdmin); - - MenuInteractive = new BetterMenuItem - { - Text = CoreTools.AutoTranslated("Interactive installation"), - IconName = IconType.Interactive, - }; - MenuInteractive.Click += MenuInteractive_Invoked; - menu.Items.Add(MenuInteractive); - - MenuSkipHash = new BetterMenuItem - { - Text = CoreTools.AutoTranslated("Skip hash check"), - IconName = IconType.Checksum, - }; - MenuSkipHash.Click += MenuSkipHash_Invoked; - menu.Items.Add(MenuSkipHash); - - MenuDownloadInstaller = new BetterMenuItem - { - Text = CoreTools.AutoTranslated("Download installer"), - IconName = IconType.Download, - }; - MenuDownloadInstaller.Click += (_, _) => - _ = MainApp.Operations.AskLocationAndDownload( - SelectedItem, - TEL_InstallReferral.DIRECT_SEARCH - ); - menu.Items.Add(MenuDownloadInstaller); - - menu.Items.Add(new MenuFlyoutSeparator { Height = 5 }); - - BetterMenuItem menuDetails = new() - { - Text = CoreTools.AutoTranslated("Package details"), - IconName = IconType.Info_Round, - KeyboardAcceleratorTextOverride = "Enter", - }; - menuDetails.Click += MenuDetails_Invoked; - menu.Items.Add(menuDetails); - - return menu; - } - - public override void GenerateToolBar() - { - BetterMenuItem InstallAsAdmin = new(); - BetterMenuItem InstallSkipHash = new(); - BetterMenuItem InstallInteractive = new(); - BetterMenuItem DownloadInstallers = new(); - - MainToolbarButtonDropdown.Flyout = new BetterMenu() - { - Items = - { - InstallAsAdmin, - InstallSkipHash, - InstallInteractive, - new MenuFlyoutSeparator(), - DownloadInstallers, - }, - Placement = FlyoutPlacementMode.Bottom, - }; - MainToolbarButtonIcon.Icon = IconType.Download; - MainToolbarButtonText.Text = CoreTools.Translate("Install selection"); - - AppBarButton InstallationSettings = new(); - - AppBarButton PackageDetails = new(); - - AppBarButton ExportSelection = new(); - - ToolBar.PrimaryCommands.Add(new AppBarSeparator()); - ToolBar.PrimaryCommands.Add(InstallationSettings); - ToolBar.PrimaryCommands.Add(new AppBarSeparator()); - ToolBar.PrimaryCommands.Add(PackageDetails); - ToolBar.PrimaryCommands.Add(new AppBarSeparator()); - ToolBar.PrimaryCommands.Add(ExportSelection); - - Dictionary Labels = new() - { // Entries with a trailing space are collapsed - // Their texts will be used as the tooltip - { InstallAsAdmin, CoreTools.Translate("Install as administrator") }, - { InstallSkipHash, CoreTools.Translate("Skip integrity checks") }, - { InstallInteractive, CoreTools.Translate("Interactive installation") }, - { DownloadInstallers, CoreTools.Translate("Download selected installers") }, - { InstallationSettings, CoreTools.Translate("Install options") }, - { PackageDetails, " " + CoreTools.Translate("Package details") }, - { ExportSelection, CoreTools.Translate("Add selection to bundle") }, - }; - - Dictionary Icons = new() - { - { InstallAsAdmin, IconType.UAC }, - { InstallSkipHash, IconType.Checksum }, - { InstallationSettings, IconType.Options }, - { DownloadInstallers, IconType.Download }, - { InstallInteractive, IconType.Interactive }, - { PackageDetails, IconType.Info_Round }, - { ExportSelection, IconType.AddTo }, - }; - - ApplyTextAndIconsToToolbar(Labels, Icons); - - PackageDetails.Click += (_, _) => - ShowDetailsForPackage(SelectedItem, TEL_InstallReferral.DIRECT_SEARCH); - ExportSelection.Click += ExportSelection_Click; - InstallationSettings.Click += (_, _) => - _ = ShowInstallationOptionsForPackage(SelectedItem); - - MainToolbarButton.Click += (_, _) => - MainApp.Operations.Install( - FilteredPackages.GetCheckedPackages(), - TEL_InstallReferral.DIRECT_SEARCH - ); - InstallAsAdmin.Click += (_, _) => - MainApp.Operations.Install( - FilteredPackages.GetCheckedPackages(), - TEL_InstallReferral.DIRECT_SEARCH, - elevated: true - ); - InstallSkipHash.Click += (_, _) => - MainApp.Operations.Install( - FilteredPackages.GetCheckedPackages(), - TEL_InstallReferral.DIRECT_SEARCH, - no_integrity: true - ); - InstallInteractive.Click += (_, _) => - MainApp.Operations.Install( - FilteredPackages.GetCheckedPackages(), - TEL_InstallReferral.DIRECT_SEARCH, - interactive: true - ); - DownloadInstallers.Click += (_, _) => - _ = MainApp.Operations.Download( - FilteredPackages.GetCheckedPackages(), - TEL_InstallReferral.DIRECT_SEARCH - ); - - } - - public override async Task LoadPackages() - { - if (QueryBlock.Text.Trim() != "") - { - await LoadPackages(ReloadReason.External); - } - } - - private void Event_SearchPackages(object? sender, RoutedEventArgs e) - { - if (QueryBlock.Text.Trim() != "") - { - _ = (Loader as DiscoverablePackagesLoader)?.ReloadPackages(QueryBlock.Text.Trim()); - } - else - { - Loader.StopLoading(); - } - } - - protected override void WhenPackageCountUpdated() { } - - protected override void WhenPackagesLoaded(ReloadReason reason) { } - - protected override void WhenShowingContextMenu(IPackage package) - { - if ( - MenuAsAdmin is null - || MenuInteractive is null - || MenuSkipHash is null - || MenuDownloadInstaller is null - ) - { - Logger.Warn("MenuItems are null on DiscoverPackagesPage"); - return; - } - - MenuAsAdmin.IsEnabled = package.Manager.Capabilities.CanRunAsAdmin; - MenuInteractive.IsEnabled = package.Manager.Capabilities.CanRunInteractively; - MenuSkipHash.IsEnabled = package.Manager.Capabilities.CanSkipIntegrityChecks; - MenuDownloadInstaller.IsEnabled = package.Manager.Capabilities.CanDownloadInstaller; - } - - private void ExportSelection_Click(object sender, RoutedEventArgs e) => - _ = _exportSelection_Click(); - - private async Task _exportSelection_Click() - { - MainApp.Instance.MainWindow.NavigationPage.NavigateTo(PageType.Bundles); - int loadingId = DialogHelper.ShowLoadingDialog(CoreTools.Translate("Please wait...")); - await PackageBundlesLoader.Instance.AddPackagesAsync( - FilteredPackages.GetCheckedPackages() - ); - DialogHelper.HideLoadingDialog(loadingId); - } - - private void MenuDetails_Invoked(object sender, RoutedEventArgs e) - { - ShowDetailsForPackage(SelectedItem, TEL_InstallReferral.DIRECT_SEARCH); - } - - private void MenuInstall_Invoked(object sender, RoutedEventArgs e) => - _ = MainApp.Operations.Install(SelectedItem, TEL_InstallReferral.DIRECT_SEARCH); - - private void MenuSkipHash_Invoked(object sender, RoutedEventArgs e) => - _ = MainApp.Operations.Install( - SelectedItem, - TEL_InstallReferral.DIRECT_SEARCH, - no_integrity: true - ); - - private void MenuInteractive_Invoked(object sender, RoutedEventArgs e) => - _ = MainApp.Operations.Install( - SelectedItem, - TEL_InstallReferral.DIRECT_SEARCH, - interactive: true - ); - - private void MenuAsAdmin_Invoked(object sender, RoutedEventArgs e) => - _ = MainApp.Operations.Install( - SelectedItem, - TEL_InstallReferral.DIRECT_SEARCH, - elevated: true - ); - - private void MenuInstallSettings_Invoked(object sender, RoutedEventArgs e) => - _ = ShowInstallationOptionsForPackage(SelectedItem); - } -} diff --git a/src/UniGetUI/Pages/SoftwarePages/InstalledPackagesPage.cs b/src/UniGetUI/Pages/SoftwarePages/InstalledPackagesPage.cs deleted file mode 100644 index e8c6404e34..0000000000 --- a/src/UniGetUI/Pages/SoftwarePages/InstalledPackagesPage.cs +++ /dev/null @@ -1,522 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using UniGetUI.Core.Data; -using UniGetUI.Core.Logging; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; -using UniGetUI.Interface.Enums; -using UniGetUI.Interface.Telemetry; -using UniGetUI.Interface.Widgets; -using UniGetUI.PackageEngine; -using UniGetUI.PackageEngine.Enums; -using UniGetUI.PackageEngine.Interfaces; -using UniGetUI.PackageEngine.Managers.WingetManager; -using UniGetUI.PackageEngine.PackageLoader; -using UniGetUI.Pages.DialogPages; -using UniGetUI.Services; -using Windows.UI.Text; - -namespace UniGetUI.Interface.SoftwarePages -{ - public partial class InstalledPackagesPage : AbstractPackagesPage - { - private static bool HasDoneBackup; - - private BetterMenuItem? MenuAsAdmin; - private BetterMenuItem? MenuInteractive; - private BetterMenuItem? MenuRemoveData; - private BetterMenuItem? MenuInstallationOptions; - private BetterMenuItem? MenuReinstallPackage; - private BetterMenuItem? MenuUninstallThenReinstall; - private BetterMenuItem? MenuIgnoreUpdates; - private BetterMenuItem? MenuPackageDetails; - private BetterMenuItem? MenuOpenInstallLocation; - private BetterMenuItem? MenuDownloadInstaller; - - public InstalledPackagesPage() - : base( - new PackagesPageData - { - DisableAutomaticPackageLoadOnStart = false, - DisableFilterOnQueryChange = false, - MegaQueryBlockEnabled = false, - ShowLastLoadTime = false, - DisableReload = false, - PackagesAreCheckedByDefault = false, - DisableSuggestedResultsRadio = true, - PageName = "Installed", - - Loader = InstalledPackagesLoader.Instance, - PageRole = OperationType.Uninstall, - - NoPackages_BackgroundText = CoreTools.Translate( - "No results were found matching the input criteria" - ), - NoPackages_SourcesText = CoreTools.Translate("No packages were found"), - NoPackages_SubtitleText_Base = CoreTools.Translate("No packages were found"), - MainSubtitle_StillLoading = CoreTools.Translate("Loading packages"), - NoMatches_BackgroundText = CoreTools.Translate( - "No results were found matching the input criteria" - ), - - PageTitle = CoreTools.Translate("Installed Packages"), - Glyph = "\uE977", - } - ) - { } - - public override BetterMenu GenerateContextMenu() - { - BetterMenu menu = new(); - BetterMenuItem menuUninstall = new() - { - Text = CoreTools.AutoTranslated("Uninstall"), - IconName = IconType.Delete, - KeyboardAcceleratorTextOverride = "Ctrl+Enter", - }; - menuUninstall.Click += MenuUninstall_Invoked; - menu.Items.Add(menuUninstall); - - menu.Items.Add(new MenuFlyoutSeparator { Height = 5 }); - - MenuInstallationOptions = new() - { - Text = CoreTools.AutoTranslated("Uninstall options"), - IconName = IconType.Options, - KeyboardAcceleratorTextOverride = "Alt+Enter", - }; - MenuInstallationOptions.Click += MenuInstallSettings_Invoked; - menu.Items.Add(MenuInstallationOptions); - - MenuOpenInstallLocation = new() - { - Text = CoreTools.AutoTranslated("Open install location"), - IconName = IconType.Launch, - }; - MenuOpenInstallLocation.Click += (_, _) => OpenPackageInstallLocation(SelectedItem); - ; - menu.Items.Add(MenuOpenInstallLocation); - - menu.Items.Add(new MenuFlyoutSeparator()); - - MenuAsAdmin = new BetterMenuItem - { - Text = CoreTools.AutoTranslated("Uninstall as administrator"), - IconName = IconType.UAC, - }; - MenuAsAdmin.Click += MenuAsAdmin_Invoked; - menu.Items.Add(MenuAsAdmin); - - MenuInteractive = new BetterMenuItem - { - Text = CoreTools.AutoTranslated("Interactive uninstall"), - IconName = IconType.Interactive, - }; - MenuInteractive.Click += MenuInteractive_Invoked; - menu.Items.Add(MenuInteractive); - - MenuRemoveData = new BetterMenuItem - { - Text = CoreTools.AutoTranslated("Uninstall and remove data"), - IconName = IconType.Close_Round, - }; - MenuRemoveData.Click += MenuRemoveData_Invoked; - menu.Items.Add(MenuRemoveData); - - menu.Items.Add(new MenuFlyoutSeparator()); - - MenuDownloadInstaller = new BetterMenuItem - { - Text = CoreTools.AutoTranslated("Download installer"), - IconName = IconType.Download, - }; - MenuDownloadInstaller.Click += (_, _) => - _ = MainApp.Operations.AskLocationAndDownload( - SelectedItem, - TEL_InstallReferral.ALREADY_INSTALLED - ); - menu.Items.Add(MenuDownloadInstaller); - - menu.Items.Add(new MenuFlyoutSeparator()); - - MenuReinstallPackage = new() - { - Text = CoreTools.AutoTranslated("Reinstall package"), - IconName = IconType.Download, - }; - MenuReinstallPackage.Click += MenuReinstall_Invoked; - menu.Items.Add(MenuReinstallPackage); - - MenuUninstallThenReinstall = new() - { - Text = CoreTools.AutoTranslated("Uninstall package, then reinstall it"), - IconName = IconType.Undelete, - }; - MenuUninstallThenReinstall.Click += MenuUninstallThenReinstall_Invoked; - menu.Items.Add(MenuUninstallThenReinstall); - menu.Items.Add(new MenuFlyoutSeparator()); - - MenuIgnoreUpdates = new() - { - Text = CoreTools.AutoTranslated("Ignore updates for this package"), - IconName = IconType.Pin, - }; - MenuIgnoreUpdates.Click += MenuIgnorePackage_Invoked; - menu.Items.Add(MenuIgnoreUpdates); - - menu.Items.Add(new MenuFlyoutSeparator()); - - MenuPackageDetails = new() - { - Text = CoreTools.AutoTranslated("Package details"), - IconName = IconType.Info_Round, - KeyboardAcceleratorTextOverride = "Enter", - }; - MenuPackageDetails.Click += MenuDetails_Invoked; - menu.Items.Add(MenuPackageDetails); - - return menu; - } - - public override void GenerateToolBar() - { - BetterMenuItem UninstallAsAdmin = new(); - BetterMenuItem UninstallInteractive = new(); - BetterMenuItem DownloadInstallers = new(); - - MainToolbarButtonDropdown.Flyout = new BetterMenu() - { - Items = - { - UninstallAsAdmin, - UninstallInteractive, - new MenuFlyoutSeparator(), - DownloadInstallers, - }, - Placement = FlyoutPlacementMode.Bottom, - }; - MainToolbarButtonIcon.Icon = IconType.Delete; - MainToolbarButtonText.Text = CoreTools.Translate("Uninstall selection"); - - AppBarButton InstallationSettings = new(); - - AppBarButton PackageDetails = new(); - - AppBarButton IgnoreSelected = new(); - AppBarButton ManageIgnored = new(); - AppBarButton ExportSelection = new(); - - ToolBar.PrimaryCommands.Add(new AppBarSeparator()); - ToolBar.PrimaryCommands.Add(InstallationSettings); - ToolBar.PrimaryCommands.Add(new AppBarSeparator()); - ToolBar.PrimaryCommands.Add(PackageDetails); - ToolBar.PrimaryCommands.Add(new AppBarSeparator()); - ToolBar.PrimaryCommands.Add(IgnoreSelected); - ToolBar.PrimaryCommands.Add(ManageIgnored); - ToolBar.PrimaryCommands.Add(new AppBarSeparator()); - ToolBar.PrimaryCommands.Add(ExportSelection); - - Dictionary Labels = new() - { // Entries with a trailing space are collapsed - // Their texts will be used as the tooltip - { UninstallAsAdmin, CoreTools.Translate("Uninstall as administrator") }, - { UninstallInteractive, CoreTools.Translate("Interactive uninstall") }, - { DownloadInstallers, CoreTools.Translate("Download selected installers") }, - { InstallationSettings, " " + CoreTools.Translate("Uninstall options") }, - { PackageDetails, " " + CoreTools.Translate("Package details") }, - { IgnoreSelected, CoreTools.Translate("Ignore selected packages") }, - { ManageIgnored, CoreTools.Translate("Manage ignored updates") }, - { ExportSelection, CoreTools.Translate("Add selection to bundle") }, - }; - - Dictionary Icons = new() - { - { UninstallAsAdmin, IconType.UAC }, - { UninstallInteractive, IconType.Interactive }, - { DownloadInstallers, IconType.Download }, - { InstallationSettings, IconType.Options }, - { PackageDetails, IconType.Info_Round }, - { IgnoreSelected, IconType.Pin }, - { ManageIgnored, IconType.ClipboardList }, - { ExportSelection, IconType.AddTo }, - }; - - ApplyTextAndIconsToToolbar(Labels, Icons); - - PackageDetails.Click += (_, _) => - ShowDetailsForPackage(SelectedItem, TEL_InstallReferral.ALREADY_INSTALLED); - - ExportSelection.Click += ExportSelection_Click; - InstallationSettings.Click += (_, _) => - _ = ShowInstallationOptionsForPackage(SelectedItem); - ManageIgnored.Click += async (_, _) => await DialogHelper.ManageIgnoredUpdates(); - IgnoreSelected.Click += async (_, _) => - { - foreach (IPackage package in FilteredPackages.GetCheckedPackages()) - { - if (!package.Source.IsVirtualManager) - { - UpgradablePackagesLoader.Instance.Remove(package); - await package.AddToIgnoredUpdatesAsync(); - } - } - }; - - MainToolbarButton.Click += (_, _) => - _ = MainApp.Operations.ConfirmAndUninstall(FilteredPackages.GetCheckedPackages()); - UninstallAsAdmin.Click += (_, _) => - _ = MainApp.Operations.ConfirmAndUninstall( - FilteredPackages.GetCheckedPackages(), - elevated: true - ); - UninstallInteractive.Click += (_, _) => - _ = MainApp.Operations.ConfirmAndUninstall( - FilteredPackages.GetCheckedPackages(), - interactive: true - ); - DownloadInstallers.Click += (_, _) => - _ = MainApp.Operations.Download( - FilteredPackages.GetCheckedPackages(), - TEL_InstallReferral.ALREADY_INSTALLED - ); - } - - protected override void WhenPackageCountUpdated() - { - return; - } - - protected override void WhenPackagesLoaded(ReloadReason reason) - { - if (!HasDoneBackup) - { - if (Settings.Get(Settings.K.EnablePackageBackup_LOCAL)) - { - _ = BackupPackages_LOCAL(); - } - - if (Settings.Get(Settings.K.EnablePackageBackup_CLOUD)) - { - _ = BackupPackages_CLOUD(); - } - } - - var infoBar = MainApp.Instance.MainWindow.WinGetWarningBanner; - if ( - WinGet.NO_PACKAGES_HAVE_BEEN_LOADED - && !Settings.Get(Settings.K.DisableWinGetMalfunctionDetector) - ) - { - infoBar.IsOpen = true; - infoBar.Title = CoreTools.Translate("WinGet malfunction detected"); - infoBar.Message = CoreTools.Translate( - "It looks like WinGet is not working properly. Do you want to attempt to repair WinGet?" - ); - var button = new Button { Content = CoreTools.Translate("Repair WinGet") }; - infoBar.ActionButton = button; - button.Click += (_, _) => _ = DialogHelper.HandleBrokenWinGet(); - } - else - { - infoBar.IsOpen = false; - infoBar.ActionButton = null; - } - } - - protected override void WhenShowingContextMenu(IPackage package) => - _ = _whenShowingContextMenu(package); - - private async Task _whenShowingContextMenu(IPackage package) - { - if ( - MenuAsAdmin is null - || MenuInteractive is null - || MenuRemoveData is null - || MenuInstallationOptions is null - || MenuUninstallThenReinstall is null - || MenuReinstallPackage is null - || MenuIgnoreUpdates is null - || MenuPackageDetails is null - || MenuOpenInstallLocation is null - || MenuDownloadInstaller is null - ) - { - Logger.Error("Menu items are null on InstalledPackagesTab"); - return; - } - - MenuAsAdmin.IsEnabled = package.Manager.Capabilities.CanRunAsAdmin; - MenuInteractive.IsEnabled = package.Manager.Capabilities.CanRunInteractively; - MenuRemoveData.IsEnabled = package.Manager.Capabilities.CanRemoveDataOnUninstall; - - bool IS_LOCAL = package.Source.IsVirtualManager; - - MenuInstallationOptions.IsEnabled = !IS_LOCAL; - MenuReinstallPackage.IsEnabled = !IS_LOCAL; - MenuUninstallThenReinstall.IsEnabled = !IS_LOCAL; - MenuIgnoreUpdates.IsEnabled = false; // Will be set on the lines below; - MenuPackageDetails.IsEnabled = !IS_LOCAL; - MenuDownloadInstaller.IsEnabled = - !IS_LOCAL && package.Manager.Capabilities.CanDownloadInstaller; - ; - - MenuOpenInstallLocation.IsEnabled = - package.Manager.DetailsHelper.GetInstallLocation(package) is not null; - if (!IS_LOCAL) - { - if (await package.HasUpdatesIgnoredAsync()) - { - MenuIgnoreUpdates.Text = CoreTools.Translate( - "Do not ignore updates for this package anymore" - ); - MenuIgnoreUpdates.Icon = new FontIcon { Glyph = "\uE77A" }; - } - else - { - MenuIgnoreUpdates.Text = CoreTools.Translate("Ignore updates for this package"); - MenuIgnoreUpdates.Icon = new FontIcon { Glyph = "\uE718" }; - } - MenuIgnoreUpdates.IsEnabled = true; - } - } - - private void ExportSelection_Click(object sender, RoutedEventArgs e) => - _ = _exportSelection_Click(); - - private async Task _exportSelection_Click() - { - MainApp.Instance.MainWindow.NavigationPage.NavigateTo(PageType.Bundles); - int loadingId = DialogHelper.ShowLoadingDialog(CoreTools.Translate("Please wait...")); - await PackageBundlesLoader.Instance.AddPackagesAsync( - FilteredPackages.GetCheckedPackages() - ); - DialogHelper.HideLoadingDialog(loadingId); - } - - public static Task GenerateBackupContents() - { - Logger.Debug("Starting package backup"); - List packagesToExport = []; - foreach (IPackage package in InstalledPackagesLoader.Instance.Packages) - { - packagesToExport.Add(package); - } - - return PackageBundlesPage.CreateBundle(packagesToExport.ToArray()); - } - - public static async Task BackupPackages_CLOUD() - { - try - { - await CoreTools.WaitForInternetConnection(); - string backupContents = await GenerateBackupContents(); - var authService = new GitHubAuthService(); - var backupService = new GitHubBackupService(authService); - await backupService.UploadPackageBundle(backupContents); - Logger.ImportantInfo("Cloud backup succeeded"); - } - catch (Exception ex) - { - Logger.Error("An error occurred while performing a CLOUD backup"); - Logger.Error(ex); - } - } - - public static async Task BackupPackages_LOCAL() - { - try - { - string backupContents = await GenerateBackupContents(); - string dirName = Settings.GetValue(Settings.K.ChangeBackupOutputDirectory); - if (dirName == "") - { - dirName = CoreData.UniGetUI_DefaultBackupDirectory; - } - - if (!Directory.Exists(dirName)) - { - Directory.CreateDirectory(dirName); - } - - string fileName = Settings.GetValue(Settings.K.ChangeBackupFileName); - if (fileName == "") - { - fileName = CoreTools.Translate( - "{pcName} installed packages", - new Dictionary { { "pcName", Environment.MachineName } } - ); - } - - if (Settings.Get(Settings.K.EnableBackupTimestamping)) - { - fileName += " " + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss"); - } - - fileName += ".ubundle"; - - string filePath = Path.Combine(dirName, fileName); - await File.WriteAllTextAsync(filePath, backupContents); - HasDoneBackup = true; - Logger.ImportantInfo("Backup saved to " + filePath); - } - catch (Exception ex) - { - Logger.Error("An error occurred while performing a LOCAL backup"); - Logger.Error(ex); - } - } - - private void MenuUninstall_Invoked(object sender, RoutedEventArgs args) => - _ = MainApp.Operations.ConfirmAndUninstall(SelectedItem); - - private void MenuAsAdmin_Invoked(object sender, RoutedEventArgs args) => - _ = MainApp.Operations.ConfirmAndUninstall(SelectedItem, elevated: true); - - private void MenuInteractive_Invoked(object sender, RoutedEventArgs args) => - _ = MainApp.Operations.ConfirmAndUninstall(SelectedItem, interactive: true); - - private void MenuRemoveData_Invoked(object sender, RoutedEventArgs args) => - _ = MainApp.Operations.ConfirmAndUninstall(SelectedItem, remove_data: true); - - private void MenuReinstall_Invoked(object sender, RoutedEventArgs args) => - _ = MainApp.Operations.Install(SelectedItem, TEL_InstallReferral.ALREADY_INSTALLED); - - private void MenuUninstallThenReinstall_Invoked(object sender, RoutedEventArgs args) => - _ = MainApp.Operations.UninstallThenReinstall( - SelectedItem, - TEL_InstallReferral.ALREADY_INSTALLED - ); - - private void MenuIgnorePackage_Invoked(object sender, RoutedEventArgs args) => - _ = _menuIgnorePackage_Invoked(); - - private async Task _menuIgnorePackage_Invoked() - { - IPackage? package = SelectedItem; - if (package is null) - return; - - if (await package.HasUpdatesIgnoredAsync()) - { - await package.RemoveFromIgnoredUpdatesAsync(); - } - else - { - await package.AddToIgnoredUpdatesAsync(); - UpgradablePackagesLoader.Instance.Remove(package); - } - } - - private void MenuDetails_Invoked(object sender, RoutedEventArgs args) - { - ShowDetailsForPackage(SelectedItem, TEL_InstallReferral.ALREADY_INSTALLED); - } - - private void MenuInstallSettings_Invoked(object sender, RoutedEventArgs e) - { - _ = ShowInstallationOptionsForPackage(SelectedItem); - } - } -} diff --git a/src/UniGetUI/Pages/SoftwarePages/PackageBundlesPage.cs b/src/UniGetUI/Pages/SoftwarePages/PackageBundlesPage.cs deleted file mode 100644 index 694958ccd7..0000000000 --- a/src/UniGetUI/Pages/SoftwarePages/PackageBundlesPage.cs +++ /dev/null @@ -1,1058 +0,0 @@ -using System.Diagnostics; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Xml.Serialization; -using ExternalLibraries.Pickers; -using Microsoft.UI.Text; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Documents; -using UniGetUI.Core.Data; -using UniGetUI.Core.Logging; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.SettingsEngine.SecureSettings; -using UniGetUI.Core.Tools; -using UniGetUI.Interface.Enums; -using UniGetUI.Interface.Telemetry; -using UniGetUI.Interface.Widgets; -using UniGetUI.PackageEngine; -using UniGetUI.PackageEngine.Classes.Serializable; -using UniGetUI.PackageEngine.Enums; -using UniGetUI.PackageEngine.Interfaces; -using UniGetUI.PackageEngine.PackageClasses; -using UniGetUI.PackageEngine.PackageLoader; -using UniGetUI.Pages.DialogPages; -using Windows.UI.Text; - -namespace UniGetUI.Interface.SoftwarePages -{ - public partial class PackageBundlesPage : AbstractPackagesPage - { - private BetterMenuItem? MenuInstallOptions; - private BetterMenuItem? MenuInstall; - private BetterMenuItem? MenuDetails; - private BetterMenuItem? MenuAsAdmin; - private BetterMenuItem? MenuInteractive; - private BetterMenuItem? MenuSkipHash; - private BetterMenuItem? MenuDownloadInstaller; - - public event EventHandler? UnsavedChangesStateChanged; - - private bool _hasUnsavedChanges; - public bool HasUnsavedChanges - { - get => _hasUnsavedChanges; - private set - { - UnsavedChangesStateChanged?.Invoke(this, EventArgs.Empty); - _hasUnsavedChanges = value; - } - } - - public PackageBundlesPage() - : base( - new PackagesPageData - { - DisableAutomaticPackageLoadOnStart = true, - DisableFilterOnQueryChange = false, - MegaQueryBlockEnabled = false, - ShowLastLoadTime = false, - DisableReload = true, - PackagesAreCheckedByDefault = false, - DisableSuggestedResultsRadio = true, - PageName = "Bundles", - - Loader = PackageBundlesLoader.Instance, - PageRole = OperationType.Install, - - NoPackages_BackgroundText = CoreTools.Translate( - "Add packages or open an existing package bundle" - ), - NoPackages_SourcesText = CoreTools.Translate("Add packages to start"), - NoPackages_SubtitleText_Base = CoreTools.Translate( - "The current bundle has no packages. Add some packages to get started" - ), - MainSubtitle_StillLoading = CoreTools.Translate("Loading packages"), - NoMatches_BackgroundText = CoreTools.Translate( - "No results were found matching the input criteria" - ), - - PageTitle = CoreTools.Translate("Package Bundles"), - Glyph = "\uF133", - } - ) - { - Loader.PackagesChanged += (_, _) => - { - HasUnsavedChanges = true; - }; - } - - public override BetterMenu GenerateContextMenu() - { - BetterMenu menu = new(); - MenuInstall = new() - { - Text = CoreTools.AutoTranslated("Install"), - IconName = IconType.Download, - KeyboardAcceleratorTextOverride = "Ctrl+Enter", - }; - MenuInstall.Click += MenuInstall_Invoked; - menu.Items.Add(MenuInstall); - - menu.Items.Add(new MenuFlyoutSeparator { Height = 5 }); - - MenuInstallOptions = new() - { - Text = CoreTools.AutoTranslated("Install options"), - IconName = IconType.Options, - KeyboardAcceleratorTextOverride = "Alt+Enter", - }; - MenuInstallOptions.Click += MenuInstallSettings_Invoked; - menu.Items.Add(MenuInstallOptions); - - menu.Items.Add(new MenuFlyoutSeparator()); - - MenuAsAdmin = new BetterMenuItem - { - Text = CoreTools.AutoTranslated("Install as administrator"), - IconName = IconType.UAC, - }; - MenuAsAdmin.Click += MenuAsAdmin_Invoked; - menu.Items.Add(MenuAsAdmin); - - MenuInteractive = new BetterMenuItem - { - Text = CoreTools.AutoTranslated("Interactive installation"), - IconName = IconType.Interactive, - }; - MenuInteractive.Click += MenuInteractive_Invoked; - menu.Items.Add(MenuInteractive); - - MenuSkipHash = new BetterMenuItem - { - Text = CoreTools.AutoTranslated("Skip hash checks"), - IconName = IconType.Checksum, - }; - MenuSkipHash.Click += MenuSkipHash_Invoked; - menu.Items.Add(MenuSkipHash); - - MenuDownloadInstaller = new BetterMenuItem - { - Text = CoreTools.AutoTranslated("Download installer"), - IconName = IconType.Download, - }; - MenuDownloadInstaller.Click += (_, _) => - _ = MainApp.Operations.AskLocationAndDownload( - SelectedItem, - TEL_InstallReferral.FROM_BUNDLE - ); - menu.Items.Add(MenuDownloadInstaller); - - menu.Items.Add(new MenuFlyoutSeparator()); - - BetterMenuItem menuRemoveFromList = new() - { - Text = CoreTools.AutoTranslated("Remove from list"), - IconName = IconType.Delete, - }; - menuRemoveFromList.Click += MenuRemoveFromList_Invoked; - menu.Items.Add(menuRemoveFromList); - menu.Items.Add(new MenuFlyoutSeparator()); - - MenuDetails = new() - { - Text = CoreTools.AutoTranslated("Package details"), - IconName = IconType.Info_Round, - KeyboardAcceleratorTextOverride = "Enter", - }; - MenuDetails.Click += MenuDetails_Invoked; - menu.Items.Add(MenuDetails); - - return menu; - } - - public override void GenerateToolBar() - { - AppBarButton OpenBundle = new(); - AppBarButton NewBundle = new(); - - BetterMenuItem InstallAsAdmin = new(); - BetterMenuItem InstallSkipHash = new(); - BetterMenuItem InstallInteractive = new(); - BetterMenuItem DownloadInstallers = new(); - - MainToolbarButtonDropdown.Flyout = new BetterMenu() - { - Items = - { - InstallAsAdmin, - InstallSkipHash, - InstallInteractive, - new MenuFlyoutSeparator(), - DownloadInstallers, - }, - Placement = FlyoutPlacementMode.Bottom, - }; - MainToolbarButtonIcon.Icon = IconType.Download; - MainToolbarButtonText.Text = CoreTools.Translate("Install selection"); - - AppBarButton RemoveSelected = new(); - AppBarButton SaveBundle = new(); - AppBarButton ToBatchScript = new(); - AppBarButton AddPackagesToBundle = new(); - AppBarButton PackageDetails = new(); - - ToolBar.PrimaryCommands.Add(new AppBarSeparator()); - ToolBar.PrimaryCommands.Add(NewBundle); - ToolBar.PrimaryCommands.Add(OpenBundle); - ToolBar.PrimaryCommands.Add(SaveBundle); - ToolBar.PrimaryCommands.Add(new AppBarSeparator()); - ToolBar.PrimaryCommands.Add(ToBatchScript); - ToolBar.PrimaryCommands.Add(new AppBarSeparator()); - ToolBar.PrimaryCommands.Add(AddPackagesToBundle); - ToolBar.PrimaryCommands.Add(RemoveSelected); - ToolBar.PrimaryCommands.Add(new AppBarSeparator()); - ToolBar.PrimaryCommands.Add(PackageDetails); - - Dictionary Labels = new() - { // Entries with a trailing space are collapsed - // Their texts will be used as the tooltip - { NewBundle, CoreTools.Translate("New") }, - { InstallAsAdmin, CoreTools.Translate("Install as administrator") }, - { InstallInteractive, CoreTools.Translate("Interactive installation") }, - { InstallSkipHash, CoreTools.Translate("Skip integrity checks") }, - { DownloadInstallers, CoreTools.Translate("Download selected installers") }, - { OpenBundle, CoreTools.Translate("Open") }, - { ToBatchScript, CoreTools.Translate("Create .ps1 script") }, - { RemoveSelected, CoreTools.Translate("Remove selection from bundle") }, - { SaveBundle, CoreTools.Translate("Save as") }, - { AddPackagesToBundle, CoreTools.Translate("Add packages to bundle") }, - { PackageDetails, " " + CoreTools.Translate("Package details") }, - }; - - Dictionary Icons = new() - { - { NewBundle, IconType.AddTo }, - { InstallAsAdmin, IconType.UAC }, - { InstallInteractive, IconType.Interactive }, - { InstallSkipHash, IconType.Checksum }, - { DownloadInstallers, IconType.Download }, - { OpenBundle, IconType.OpenFolder }, - { ToBatchScript, IconType.Console }, - { RemoveSelected, IconType.Delete }, - { SaveBundle, IconType.SaveAs }, - { AddPackagesToBundle, IconType.AddTo }, - { PackageDetails, IconType.Info_Round }, - }; - - ApplyTextAndIconsToToolbar(Labels, Icons); - - PackageDetails.Click += (_, _) => - { - if (SelectedItem is null) - return; - - if (SelectedItem.Source.IsVirtualManager || SelectedItem is InvalidImportedPackage) - { - DialogHelper.ShowDismissableBalloon( - CoreTools.Translate("Something went wrong"), - CoreTools.Translate( - "\"{0}\" is a local package and is not compatible with this feature", - SelectedItem.Name - ) - ); - return; - } - - _ = DialogHelper.ShowPackageDetails( - SelectedItem, - OperationType.None, - TEL_InstallReferral.FROM_BUNDLE - ); - }; - - NewBundle.Click += async (s, e) => await AskForNewBundle(); - - RemoveSelected.Click += (_, _) => - { - HasUnsavedChanges = true; - PackageBundlesLoader.Instance.RemoveRange(FilteredPackages.GetCheckedPackages()); - }; - - IReadOnlyList GetCheckedNonInstalledPackages() - { - if (Settings.Get(Settings.K.InstallInstalledPackagesBundlesPage)) - return FilteredPackages.GetCheckedPackages().ToList(); - else - return FilteredPackages - .GetCheckedPackages() - .Where(p => p.Tag is not PackageTag.AlreadyInstalled) - .ToList(); - } - - MainToolbarButton.Click += async (_, _) => - await ImportAndInstallPackage(GetCheckedNonInstalledPackages()); - InstallSkipHash.Click += async (_, _) => - await ImportAndInstallPackage(GetCheckedNonInstalledPackages(), skiphash: true); - InstallInteractive.Click += async (_, _) => - await ImportAndInstallPackage(GetCheckedNonInstalledPackages(), interactive: true); - InstallAsAdmin.Click += async (_, _) => - await ImportAndInstallPackage(GetCheckedNonInstalledPackages(), elevated: true); - DownloadInstallers.Click += (_, _) => - _ = MainApp.Operations.Download( - FilteredPackages.GetCheckedPackages(), - TEL_InstallReferral.FROM_BUNDLE - ); - OpenBundle.Click += async (_, _) => await AskOpenFromFile(); - SaveBundle.Click += async (_, _) => await SaveFile(); - ToBatchScript.Click += (_, _) => _ = CreateBatchScript(); - - AddPackagesToBundle.Click += (_, _) => _ = DialogHelper.HowToAddPackagesToBundle(); - } - - public async Task AskForNewBundle() - { - if ( - !Loader.Any() - || !HasUnsavedChanges - || await DialogHelper.AskLoseChangesAndCreateBundle() - ) - { - // Need to call ClearPackages, this method also clears internal caches - Loader.ClearPackages(); - HasUnsavedChanges = false; - return true; - } - - return false; - } - - public async Task ImportAndInstallPackage( - IReadOnlyList packages, - bool? elevated = null, - bool? interactive = null, - bool? skiphash = null - ) - { - int loadingId = DialogHelper.ShowLoadingDialog( - CoreTools.Translate("Preparing packages, please wait...") - ); - List packages_to_install = []; - foreach (IPackage package in packages) - { - if (package is ImportedPackage imported) - { - Logger.ImportantInfo( - $"Registering package {imported.Id} from manager {imported.Source.AsString}" - ); - packages_to_install.Add(await imported.RegisterAndGetPackageAsync()); - } - else - { - Logger.Warn( - $"Attempted to install an invalid/incompatible package with Id={package.Id}" - ); - } - } - - DialogHelper.HideLoadingDialog(loadingId); - MainApp.Operations.Install( - packages_to_install, - TEL_InstallReferral.FROM_BUNDLE, - elevated, - interactive, - skiphash - ); - } - - protected override void WhenPackageCountUpdated() { } - - protected override void WhenPackagesLoaded(ReloadReason reason) { } - - protected override void WhenShowingContextMenu(IPackage package) - { - if ( - MenuAsAdmin is null - || MenuInteractive is null - || MenuSkipHash is null - || MenuDetails is null - || MenuInstall is null - || MenuInstallOptions is null - || MenuDownloadInstaller is null - ) - { - Logger.Error("Menu items are null on InstalledPackagesTab"); - return; - } - - bool IS_VALID = package as InvalidImportedPackage is null; - - MenuAsAdmin.IsEnabled = IS_VALID && package.Manager.Capabilities.CanRunAsAdmin; - MenuInteractive.IsEnabled = - IS_VALID && package.Manager.Capabilities.CanRunInteractively; - MenuSkipHash.IsEnabled = - IS_VALID && package.Manager.Capabilities.CanSkipIntegrityChecks; - MenuDetails.IsEnabled = IS_VALID; - MenuInstall.IsEnabled = IS_VALID; - MenuInstallOptions.IsEnabled = IS_VALID; - MenuDownloadInstaller.IsEnabled = - IS_VALID && package.Manager.Capabilities.CanDownloadInstaller; - } - - private void MenuInstall_Invoked(object sender, RoutedEventArgs args) - { - if (SelectedItem is null) - return; - _ = ImportAndInstallPackage([SelectedItem]); - } - - private void MenuAsAdmin_Invoked(object sender, RoutedEventArgs args) - { - if (SelectedItem is null) - return; - _ = ImportAndInstallPackage([SelectedItem], elevated: true); - } - - private void MenuInteractive_Invoked(object sender, RoutedEventArgs args) - { - if (SelectedItem is null) - return; - _ = ImportAndInstallPackage([SelectedItem], interactive: true); - } - - private void MenuSkipHash_Invoked(object sender, RoutedEventArgs args) - { - if (SelectedItem is null) - return; - _ = ImportAndInstallPackage([SelectedItem], skiphash: true); - } - - private void MenuDetails_Invoked(object sender, RoutedEventArgs args) - { - ShowDetailsForPackage(SelectedItem, TEL_InstallReferral.FROM_BUNDLE); - } - - private void MenuInstallSettings_Invoked(object sender, RoutedEventArgs e) - { - IPackage? package = SelectedItem; - if (package is ImportedPackage imported) - { - HasUnsavedChanges = true; - _ = DialogHelper.ShowInstallOptions_ImportedPackage(imported); - } - } - - private void MenuRemoveFromList_Invoked(object sender, RoutedEventArgs args) - { - IPackage? package = SelectedItem; - if (package is null) - return; - - HasUnsavedChanges = true; - Loader.Remove(package); - } - - public async Task OpenFromString( - string payload, - BundleFormatType format, - string source, - int? loadingId - ) - { - if (await AskForNewBundle() is false) - return; - - loadingId ??= DialogHelper.ShowLoadingDialog( - CoreTools.Translate("Loading packages, please wait...") - ); - - var (open_version, report) = await AddFromBundle(payload, format); - TelemetryHandler.ImportBundle(format); - HasUnsavedChanges = false; - - if ((int)(open_version * 10) != (int)(SerializableBundle.ExpectedVersion * 10)) - { // Check only up to first decimal digit, prevent floating point precision error. - Logger.Warn( - $"The loaded bundle \"{source}\" is based on schema version {open_version}, " - + $"while this UniGetUI build expects version {SerializableBundle.ExpectedVersion}." - + $"\nThis should not be a problem if packages show up, but be careful" - ); - } - - DialogHelper.HideLoadingDialog(loadingId.Value); - if (!report.IsEmpty) - { - await DialogHelper.ShowBundleSecurityReport(report.Contents); - } - } - - public async Task OpenFromFile(string file) - { - int loadingId = DialogHelper.ShowLoadingDialog( - CoreTools.Translate("Loading packages, please wait...") - ); - try - { - BundleFormatType formatType; - string EXT = file.Split('.')[^1].ToLower(); - if (EXT == "yaml") - formatType = BundleFormatType.YAML; - else if (EXT == "xml") - formatType = BundleFormatType.XML; - else if (EXT == "json") - formatType = BundleFormatType.JSON; - else if (EXT == "ubundle") - formatType = BundleFormatType.UBUNDLE; - else - formatType = BundleFormatType.UBUNDLE; - - string fileContent = await File.ReadAllTextAsync(file); - await OpenFromString(fileContent, formatType, file, loadingId); - } - catch (Exception ex) - { - Logger.Error("An error occurred while attempting to open a bundle"); - Logger.Error(ex); - var warningDialog = new ContentDialog - { - Title = CoreTools.Translate("The package bundle is not valid"), - Content = - CoreTools.Translate( - "The bundle you are trying to load appears to be invalid. Please check the file and try again." - ) - + "\n\n" - + ex.Message, - CloseButtonText = CoreTools.Translate("Ok"), - DefaultButton = ContentDialogButton.Close, - XamlRoot = MainApp.Instance.MainWindow.Content.XamlRoot, // Ensure the dialog is shown in the correct context - }; - - DialogHelper.HideLoadingDialog(loadingId); - await DialogHelper.ShowDialogAsync(warningDialog); - } - } - - public async Task AskOpenFromFile() - { - if (await AskForNewBundle() is false) - return; - - FileOpenPicker picker = new(MainApp.Instance.MainWindow.GetWindowHandle()); - string file = picker.Show(["*.ubundle", "*.json", "*.yaml", "*.xml"]); - if (file == String.Empty) - return; - - await OpenFromFile(file); - } - - public async Task SaveFile() - { - try - { - // Get file - string defaultName = CoreTools.Translate("Package bundle") + ".ubundle"; - string file = ( - new FileSavePicker(MainApp.Instance.MainWindow.GetWindowHandle()) - ).Show(["*.ubundle", "*.json"], defaultName); - if (file != String.Empty) - { - // Loading dialog - int loadingId = DialogHelper.ShowLoadingDialog( - CoreTools.Translate("Saving packages, please wait...") - ); - - // Select appropriate format - BundleFormatType formatType; - string EXT = file.Split('.')[^1].ToLower(); - if (EXT == "json") - formatType = BundleFormatType.JSON; - else if (EXT == "ubundle") - formatType = BundleFormatType.UBUNDLE; - else - formatType = BundleFormatType.UBUNDLE; - - // Save serialized data - await File.WriteAllTextAsync(file, await CreateBundle(Loader.Packages)); - TelemetryHandler.ExportBundle(formatType); - - DialogHelper.HideLoadingDialog(loadingId); - - DialogHelper.ShowDismissableBalloon( - CoreTools.Translate("Success!"), - CoreTools.Translate("The bundle was created successfully on {0}", file) - ); - - await CoreTools.ShowFileOnExplorer(file); - HasUnsavedChanges = false; - } - } - catch (Exception ex) - { - Logger.Error("An error occurred when saving packages to a file"); - Logger.Error(ex); - - var warningDialog = new ContentDialog - { - Title = CoreTools.Translate("Could not create bundle"), - Content = - CoreTools.Translate( - "The package bundle could not be created due to an error." - ) - + "\n\n" - + ex.Message, - CloseButtonText = CoreTools.Translate("Ok"), - DefaultButton = ContentDialogButton.Close, - XamlRoot = MainApp.Instance.MainWindow.Content.XamlRoot, // Ensure the dialog is shown in the correct context - }; - - DialogHelper.HideAllLoadingDialogs(); - await DialogHelper.ShowDialogAsync(warningDialog); - } - } - - public static async Task CreateBundle(IReadOnlyList unsorted_packages) - { - SerializableBundle exportableData = new(); - - List packages = unsorted_packages.ToList(); - packages.Sort(Comparison); - - static int Comparison(IPackage x, IPackage y) - { - if (x.Id != y.Id) - return String.Compare(x.Id, y.Id, StringComparison.Ordinal); - if (x.Name != y.Name) - return String.Compare(x.Name, y.Name, StringComparison.Ordinal); - return (x.NormalizedVersion > y.NormalizedVersion) ? -1 : 1; - } - - foreach (IPackage package in packages) - { - if (package is Package && !package.Source.IsVirtualManager) - exportableData.packages.Add(await package.AsSerializableAsync()); - else - exportableData.incompatible_packages.Add(package.AsSerializable_Incompatible()); - } - - Logger.Debug("Finished loading serializable objects."); - string exportablePayload = exportableData.AsJsonString(); - Logger.Debug("Serialization finished successfully"); - return exportablePayload; - } - - public async Task<(double, BundleReport)> AddFromBundle( - string content, - BundleFormatType format - ) - { - // Deserialize data - SerializableBundle? DeserializedData; - - if (format is BundleFormatType.YAML) - { - // Dynamic convert to JSON - content = await SerializationHelpers.YAML_to_JSON(content); - Logger.ImportantInfo("YAML bundle was converted to JSON before deserialization"); - } - - if (format is BundleFormatType.XML) - { - // Dynamic convert to JSON - content = await SerializationHelpers.XML_to_JSON(content); - Logger.ImportantInfo( - "XML payload was converted to JSON dynamically before deserialization" - ); - } - - DeserializedData = await Task.Run(() => - { - return new SerializableBundle( - JsonNode.Parse(content) - ?? throw new JsonException("Could not parse JSON object") - ); - }); - - List packages = []; - - var report = new BundleReport(); - report.IsEmpty = true; - - bool AllowCLIParameters = - SecureSettings.Get(SecureSettings.K.AllowCLIArguments) - && SecureSettings.Get(SecureSettings.K.AllowImportingCLIArguments); - - bool AllowPrePostOps = - SecureSettings.Get(SecureSettings.K.AllowPrePostOpCommand) - && SecureSettings.Get(SecureSettings.K.AllowImportPrePostOpCommands); - - foreach (var pkg in DeserializedData.packages) - { - var opts = pkg.InstallationOptions; - - if (opts.CustomParameters_Install.Where(x => x.Any()).Any()) - { - report.IsEmpty = false; - if (!report.Contents.ContainsKey(pkg.Id)) - report.Contents[pkg.Id] = new(); - report - .Contents[pkg.Id] - .Add( - new( - $"Custom install arguments: [{string.Join(", ", opts.CustomParameters_Install)}]", - AllowCLIParameters - ) - ); - if (!AllowCLIParameters) - opts.CustomParameters_Install.Clear(); - } - if (opts.CustomParameters_Update.Where(x => x.Any()).Any()) - { - report.IsEmpty = false; - if (!report.Contents.ContainsKey(pkg.Id)) - report.Contents[pkg.Id] = new(); - report - .Contents[pkg.Id] - .Add( - new( - $"Custom update arguments: [{string.Join(", ", opts.CustomParameters_Update)}]", - AllowCLIParameters - ) - ); - if (!AllowCLIParameters) - opts.CustomParameters_Update.Clear(); - } - if (opts.CustomParameters_Uninstall.Where(x => x.Any()).Any()) - { - report.IsEmpty = false; - if (!report.Contents.ContainsKey(pkg.Id)) - report.Contents[pkg.Id] = new(); - report - .Contents[pkg.Id] - .Add( - new( - $"Custom uninstall arguments: [{string.Join(", ", opts.CustomParameters_Uninstall)}]", - AllowCLIParameters - ) - ); - if (!AllowCLIParameters) - opts.CustomParameters_Uninstall.Clear(); - } - - if (opts.PreInstallCommand.Any()) - { - report.IsEmpty = false; - if (!report.Contents.ContainsKey(pkg.Id)) - report.Contents[pkg.Id] = new(); - report - .Contents[pkg.Id] - .Add( - new($"Pre-install command: {opts.PreInstallCommand}", AllowPrePostOps) - ); - if (!AllowPrePostOps) - opts.PreInstallCommand = ""; - } - if (opts.PostInstallCommand.Any()) - { - report.IsEmpty = false; - if (!report.Contents.ContainsKey(pkg.Id)) - report.Contents[pkg.Id] = new(); - report - .Contents[pkg.Id] - .Add( - new($"Post-install command: {opts.PostInstallCommand}", AllowPrePostOps) - ); - if (!AllowPrePostOps) - opts.PostInstallCommand = ""; - } - if (opts.PreUpdateCommand.Any()) - { - report.IsEmpty = false; - if (!report.Contents.ContainsKey(pkg.Id)) - report.Contents[pkg.Id] = new(); - report - .Contents[pkg.Id] - .Add(new($"Pre-update command: {opts.PreUpdateCommand}", AllowPrePostOps)); - if (!AllowPrePostOps) - opts.PreUpdateCommand = ""; - } - if (opts.PostUpdateCommand.Any()) - { - report.IsEmpty = false; - if (!report.Contents.ContainsKey(pkg.Id)) - report.Contents[pkg.Id] = new(); - report - .Contents[pkg.Id] - .Add( - new($"Post-update command: {opts.PostUpdateCommand}", AllowPrePostOps) - ); - if (!AllowPrePostOps) - opts.PostUpdateCommand = ""; - } - if (opts.PreUninstallCommand.Any()) - { - report.IsEmpty = false; - if (!report.Contents.ContainsKey(pkg.Id)) - report.Contents[pkg.Id] = new(); - report - .Contents[pkg.Id] - .Add( - new( - $"Pre-uninstall command: {opts.PreUninstallCommand}", - AllowPrePostOps - ) - ); - if (!AllowPrePostOps) - opts.PreUninstallCommand = ""; - } - if (opts.PostUninstallCommand.Any()) - { - report.IsEmpty = false; - if (!report.Contents.ContainsKey(pkg.Id)) - report.Contents[pkg.Id] = new(); - report - .Contents[pkg.Id] - .Add( - new( - $"Post-uninstall command: {opts.PostUninstallCommand}", - AllowPrePostOps - ) - ); - if (!AllowPrePostOps) - opts.PostUninstallCommand = ""; - } - - pkg.InstallationOptions = opts; - packages.Add(DeserializePackage(pkg)); - } - - foreach (var pkg in DeserializedData.incompatible_packages) - packages.Add(DeserializeIncompatiblePackage(pkg, NullSource.Instance)); - - await PackageBundlesLoader.Instance.AddPackagesAsync(packages); - - return (DeserializedData.export_version, report); - } - - public static IPackage DeserializePackage(SerializablePackage raw_package) - { - IPackageManager? manager = null; - IManagerSource? source; - - foreach (var possible_manager in PEInterface.Managers) - { - if ( - possible_manager.Id == raw_package.ManagerName - || possible_manager.Name == raw_package.ManagerName - || possible_manager.DisplayName == raw_package.ManagerName - ) - { - manager = possible_manager; - break; - } - } - - if (manager?.Capabilities.SupportsCustomSources == true) - { - if (raw_package.Source.Contains(": ")) // Add compatibility with previons 2.0 bundles - // where SourceName is $"{ManagerName}: {SourceName}" - raw_package.Source = raw_package.Source.Split(": ")[^1]; - - source = manager?.SourcesHelper?.Factory.GetSourceIfExists(raw_package.Source); - } - else - source = manager?.DefaultSource; - - if (manager is null || source is null) - { - return DeserializeIncompatiblePackage( - raw_package.GetInvalidEquivalent(), - NullSource.Instance - ); - } - - return new ImportedPackage(raw_package, manager, source); - } - - public static IPackage DeserializeIncompatiblePackage( - SerializableIncompatiblePackage raw_package, - IManagerSource source - ) - { - return new InvalidImportedPackage(raw_package, source); - } - - public async Task CreateBatchScript() - { - try - { - string defaultName = CoreTools.Translate("Install script") + ".ps1"; - string file = await Task.Run(() => - (new FileSavePicker(MainApp.Instance.MainWindow.GetWindowHandle())).Show( - ["*.ps1"], - defaultName - ) - ); - if (file != String.Empty) - { - int loadingId = DialogHelper.ShowLoadingDialog( - CoreTools.Translate("Saving packages, please wait...") - ); - - var packages = new List(); - var commands = new List(); - - var forceKill = Settings.Get(Settings.K.KillProcessesThatRefuseToDie); - foreach (var _p in Loader.Packages) - { - if (_p is ImportedPackage package) - { - packages.Add(package.Name + " from " + package.Manager.DisplayName); - - foreach ( - var process in package.installation_options.KillBeforeOperation - ) - { // Kill required processes, if any. Forcekill if the user has enable said option - commands.Add($"taskkill /im \"{process}\"" + (forceKill ? " /f" : "")); - } - - if (package.installation_options.PreInstallCommand != "") - { // Add pre-operation - commands.Add(package.installation_options.PreInstallCommand); - } - - // Add install command - var exeName = package.Manager.Properties.ExecutableFriendlyName; - var param = package.Manager.OperationHelper.GetParameters( - package, - package.installation_options, - OperationType.Install - ); - commands.Add($"{exeName} {string.Join(' ', param)}"); - - if (package.installation_options.PostInstallCommand != "") - { // Add post-operation - commands.Add(package.installation_options.PostInstallCommand); - } - } - } - - await File.WriteAllTextAsync(file, GenerateCommandString(packages, commands)); - - DialogHelper.HideLoadingDialog(loadingId); - DialogHelper.ShowDismissableBalloon( - CoreTools.Translate("Success!"), - CoreTools.Translate("The installation script saved to {0}", file) - ); - - TelemetryHandler.ExportBatch(); - - await CoreTools.ShowFileOnExplorer(file); - } - } - catch (Exception ex) - { - DialogHelper.HideAllLoadingDialogs(); - Logger.Error("An error occurred while attempting to export an installation script"); - Logger.Error(ex); - DialogHelper.ShowDismissableBalloon( - CoreTools.Translate("An error occurred"), - CoreTools.Translate( - "An error occurred while attempting to create an installation script:" - ) - + " " - + ex.Message - ); - } - } - - private static string GenerateCommandString( - IReadOnlyList names, - IReadOnlyList commands - ) - { - return $$""" - Clear-Host - Write-Host "" - Write-Host "========================================================" - Write-Host "" - Write-Host " __ __ _ ______ __ __ ______" -ForegroundColor Cyan - Write-Host " / / / /___ (_) ____/__ / /_/ / / / _/" -ForegroundColor Cyan - Write-Host " / / / / __ \/ / / __/ _ \/ __/ / / // /" -ForegroundColor Cyan - Write-Host " / /_/ / / / / / /_/ / __/ /_/ /_/ // /" -ForegroundColor Cyan - Write-Host " \____/_/ /_/_/\____/\___/\__/\____/___/" -ForegroundColor Cyan - Write-Host " UniGetUI Package Installer Script" - Write-Host " Created with UniGetUI Version {{CoreData.VersionName}}" - Write-Host "" - Write-Host "========================================================" - Write-Host "" - Write-Host "NOTES:" -ForegroundColor Yellow - Write-Host " - The install process will not be as reliable as importing a bundle with UniGetUI. Expect issues and errors." -ForegroundColor Yellow - Write-Host " - Packages will be installed with the install options specified at the time of creation of this script." -ForegroundColor Yellow - Write-Host " - Error/Sucess detection may not be 100% accurate." -ForegroundColor Yellow - Write-Host " - Some of the packages may require elevation. Some of them may ask for permission, but others may fail. Consider running this script elevated." -ForegroundColor Yellow - Write-Host " - You can skip confirmation prompts by running this script with the parameter `/DisablePausePrompts` " -ForegroundColor Yellow - Write-Host "" - Write-Host "" - if ($args[0] -ne "/DisablePausePrompts") { pause } - Write-Host "" - Write-Host "This script will attempt to install the following packages:" - {{string.Join('\n', names.Select(x => $"Write-Host \" - {x}\""))}} - Write-Host "" - if ($args[0] -ne "/DisablePausePrompts") { pause } - Clear-Host - - $success_count=0 - $failure_count=0 - $commands_run=0 - $results="" - - $commands= @( - {{string.Join( - ",\n ", - commands.Select(x => $"'{x.Replace("'", "''")}'") - )}} - ) - - foreach ($command in $commands) { - Write-Host "Running: $command" -ForegroundColor Yellow - cmd.exe /C $command - if ($LASTEXITCODE -eq 0) { - Write-Host "[ OK ] $command" -ForegroundColor Green - $success_count++ - $results += "$([char]0x1b)[32m[ OK ] $command`n" - } - else { - Write-Host "[ FAIL ] $command" -ForegroundColor Red - $failure_count++ - $results += "$([char]0x1b)[31m[ FAIL ] $command`n" - } - $commands_run++ - Write-Host "" - } - - Write-Host "========================================================" - Write-Host " OPERATION SUMMARY" - Write-Host "========================================================" - Write-Host "Total commands run: $commands_run" - Write-Host "Successful: $success_count" - Write-Host "Failed: $failure_count" - Write-Host "" - Write-Host "Details:" - Write-Host "$results$([char]0x1b)[37m" - Write-Host "========================================================" - - if ($failure_count -gt 0) { - Write-Host "Some commands failed. Please check the log above." -ForegroundColor Yellow - } - else { - Write-Host "All commands executed successfully!" -ForegroundColor Green - } - Write-Host "" - if ($args[0] -ne "/DisablePausePrompts") { pause } - exit $failure_count - """; - } - } -} diff --git a/src/UniGetUI/Pages/SoftwarePages/SoftwareUpdatesPage.cs b/src/UniGetUI/Pages/SoftwarePages/SoftwareUpdatesPage.cs deleted file mode 100644 index 6287b6e11a..0000000000 --- a/src/UniGetUI/Pages/SoftwarePages/SoftwareUpdatesPage.cs +++ /dev/null @@ -1,673 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.Windows.AppNotifications; -using Microsoft.Windows.AppNotifications.Builder; -using Microsoft.Windows.System.Power; -using UniGetUI.Core.Data; -using UniGetUI.Core.Logging; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Core.Tools; -using UniGetUI.Interface.Enums; -using UniGetUI.Interface.Telemetry; -using UniGetUI.Interface.Widgets; -using UniGetUI.PackageEngine; -using UniGetUI.PackageEngine.Classes.Packages.Classes; -using UniGetUI.PackageEngine.Enums; -using UniGetUI.PackageEngine.Interfaces; -using UniGetUI.PackageEngine.PackageClasses; -using UniGetUI.PackageEngine.PackageLoader; -using UniGetUI.Pages.DialogPages; -using Windows.Networking.Connectivity; -using Windows.UI.Text; - -namespace UniGetUI.Interface.SoftwarePages -{ - public partial class SoftwareUpdatesPage : AbstractPackagesPage - { - private BetterMenuItem? MenuAsAdmin; - private BetterMenuItem? MenuInteractive; - private BetterMenuItem? MenuskipHash; - private BetterMenuItem? MenuDownloadInstaller; - private BetterMenuItem? MenuOpenInstallLocation; - - public SoftwareUpdatesPage() - : base( - new PackagesPageData - { - DisableAutomaticPackageLoadOnStart = false, - DisableFilterOnQueryChange = false, - MegaQueryBlockEnabled = false, - ShowLastLoadTime = true, - DisableReload = false, - PackagesAreCheckedByDefault = true, - DisableSuggestedResultsRadio = true, - PageName = "Updates", - - Loader = UpgradablePackagesLoader.Instance, - PageRole = OperationType.Update, - - NoPackages_BackgroundText = CoreTools.Translate( - "Hooray! No updates were found." - ), - NoPackages_SourcesText = CoreTools.Translate("Everything is up to date"), - NoPackages_SubtitleText_Base = CoreTools.Translate("Everything is up to date"), - MainSubtitle_StillLoading = CoreTools.Translate("Loading packages"), - NoMatches_BackgroundText = CoreTools.Translate( - "No results were found matching the input criteria" - ), - - PageTitle = CoreTools.Translate("Software Updates"), - Glyph = "\uE895", - } - ) - { } - - public override BetterMenu GenerateContextMenu() - { - BetterMenu ContextMenu = new(); - - BetterMenuItem menuInstall = new() - { - Text = CoreTools.AutoTranslated("Update"), - IconName = IconType.Update, - KeyboardAcceleratorTextOverride = "Ctrl+Enter", - }; - menuInstall.Click += MenuInstall_Invoked; - - BetterMenuItem menuInstallSettings = new() - { - Text = CoreTools.AutoTranslated("Update options"), - IconName = IconType.Options, - KeyboardAcceleratorTextOverride = "Alt+Enter", - }; - menuInstallSettings.Click += (_, _) => - _ = ShowInstallationOptionsForPackage(SelectedItem); - - MenuOpenInstallLocation = new() - { - Text = CoreTools.AutoTranslated("Open install location"), - IconName = IconType.Launch, - }; - MenuOpenInstallLocation.Click += (_, _) => OpenPackageInstallLocation(SelectedItem); - - MenuAsAdmin = new BetterMenuItem - { - Text = CoreTools.AutoTranslated("Update as administrator"), - IconName = IconType.UAC, - }; - MenuAsAdmin.Click += MenuAsAdmin_Invoked; - - MenuInteractive = new BetterMenuItem - { - Text = CoreTools.AutoTranslated("Interactive update"), - IconName = IconType.Interactive, - }; - MenuInteractive.Click += MenuInteractive_Invoked; - - MenuskipHash = new BetterMenuItem - { - Text = CoreTools.AutoTranslated("Skip hash check"), - IconName = IconType.Checksum, - }; - MenuskipHash.Click += MenuSkipHash_Invoked; - - MenuDownloadInstaller = new BetterMenuItem - { - Text = CoreTools.AutoTranslated("Download installer"), - IconName = IconType.Download, - }; - MenuDownloadInstaller.Click += (_, _) => - _ = MainApp.Operations.AskLocationAndDownload( - SelectedItem, - TEL_InstallReferral.ALREADY_INSTALLED - ); - - BetterMenuItem menuUpdateAfterUninstall = new() - { - Text = CoreTools.AutoTranslated("Uninstall package, then update it"), - IconName = IconType.Undelete, - }; - menuUpdateAfterUninstall.Click += MenuUpdateAfterUninstall_Invoked; - - BetterMenuItem menuUninstall = new() - { - Text = CoreTools.AutoTranslated("Uninstall package"), - IconName = IconType.Delete, - }; - menuUninstall.Click += MenuUninstall_Invoked; - - BetterMenuItem menuIgnorePackage = new() - { - Text = CoreTools.AutoTranslated("Ignore updates for this package"), - IconName = IconType.Pin, - }; - menuIgnorePackage.Click += MenuIgnorePackage_Invoked; - - BetterMenuItem menuSkipVersion = new() - { - Text = CoreTools.AutoTranslated("Skip this version"), - IconName = IconType.Skip, - }; - menuSkipVersion.Click += MenuSkipVersion_Invoked; - - BetterMenuItem menuDetails = new() - { - Text = CoreTools.AutoTranslated("Package details"), - IconName = IconType.Info_Round, - KeyboardAcceleratorTextOverride = "Enter", - }; - menuDetails.Click += (_, _) => - ShowDetailsForPackage(SelectedItem, TEL_InstallReferral.ALREADY_INSTALLED); - - MenuFlyoutSubItem menuPause = new() - { - Text = CoreTools.Translate("Pause updates for"), - Icon = new FontIcon { Glyph = "\uE769" }, - }; - foreach ( - IgnoredUpdatesDatabase.PauseTime menuTime in new List - { - new() { Days = 1 }, - new() { Days = 3 }, - new() { Weeks = 1 }, - new() { Weeks = 2 }, - new() { Weeks = 4 }, - new() { Months = 3 }, - new() { Months = 6 }, - new() { Months = 12 }, - } - ) - { - BetterMenuItem menuItem = new() { Text = menuTime.StringRepresentation() }; - menuItem.Click += (_, _) => - { - if (SelectedItem != null) - { - SelectedItem.AddToIgnoredUpdatesAsync("<" + menuTime.GetDateFromNow()); - UpgradablePackagesLoader.Instance.IgnoredPackages[SelectedItem.Id] = - SelectedItem; - Loader.Remove(SelectedItem); - } - }; - menuPause.Items.Add(menuItem); - } - - ContextMenu.Items.Add(menuInstall); - ContextMenu.Items.Add(new MenuFlyoutSeparator()); - ContextMenu.Items.Add(menuInstallSettings); - ContextMenu.Items.Add(MenuOpenInstallLocation); - ContextMenu.Items.Add(new MenuFlyoutSeparator()); - ContextMenu.Items.Add(MenuAsAdmin); - ContextMenu.Items.Add(MenuInteractive); - ContextMenu.Items.Add(MenuskipHash); - ContextMenu.Items.Add(MenuDownloadInstaller); - ContextMenu.Items.Add(new MenuFlyoutSeparator()); - ContextMenu.Items.Add(menuUpdateAfterUninstall); - ContextMenu.Items.Add(menuUninstall); - ContextMenu.Items.Add(new MenuFlyoutSeparator()); - ContextMenu.Items.Add(menuIgnorePackage); - ContextMenu.Items.Add(menuSkipVersion); - ContextMenu.Items.Add(menuPause); - ContextMenu.Items.Add(new MenuFlyoutSeparator()); - ContextMenu.Items.Add(menuDetails); - - return ContextMenu; - } - - protected override void WhenShowingContextMenu(IPackage package) - { - if ( - MenuAsAdmin is null - || MenuInteractive is null - || MenuskipHash is null - || MenuDownloadInstaller is null - || MenuOpenInstallLocation is null - ) - { - Logger.Error("Menu items are null on SoftwareUpdatesTab"); - return; - } - - MenuAsAdmin.IsEnabled = package.Manager.Capabilities.CanRunAsAdmin; - MenuInteractive.IsEnabled = package.Manager.Capabilities.CanRunInteractively; - MenuskipHash.IsEnabled = package.Manager.Capabilities.CanSkipIntegrityChecks; - MenuDownloadInstaller.IsEnabled = package.Manager.Capabilities.CanDownloadInstaller; - - MenuOpenInstallLocation.IsEnabled = - package.Manager.DetailsHelper.GetInstallLocation(package) is not null; - } - - public override void GenerateToolBar() - { - BetterMenuItem UpdateAsAdmin = new(); - BetterMenuItem UpdateSkipHash = new(); - BetterMenuItem UpdateInteractive = new(); - BetterMenuItem DownloadInstallers = new(); - BetterMenuItem UninstallSelection = new(); - - MainToolbarButtonDropdown.Flyout = new BetterMenu() - { - Items = - { - UpdateAsAdmin, - UpdateSkipHash, - UpdateInteractive, - new MenuFlyoutSeparator(), - DownloadInstallers, - new MenuFlyoutSeparator(), - UninstallSelection, - }, - Placement = FlyoutPlacementMode.Bottom, - }; - MainToolbarButtonIcon.Icon = IconType.Update; - MainToolbarButtonText.Text = CoreTools.Translate("Update selection"); - - AppBarButton InstallationSettings = new(); - - AppBarButton PackageDetails = new(); - - AppBarButton IgnoreSelected = new(); - AppBarButton ManageIgnored = new(); - - ToolBar.PrimaryCommands.Add(new AppBarSeparator()); - ToolBar.PrimaryCommands.Add(InstallationSettings); - ToolBar.PrimaryCommands.Add(new AppBarSeparator()); - ToolBar.PrimaryCommands.Add(PackageDetails); - ToolBar.PrimaryCommands.Add(new AppBarSeparator()); - ToolBar.PrimaryCommands.Add(IgnoreSelected); - ToolBar.PrimaryCommands.Add(ManageIgnored); - - Dictionary Labels = new() - { // Entries with a leading space are collapsed - // Their texts will be used as the tooltip - { UpdateAsAdmin, CoreTools.Translate("Update as administrator") }, - { UpdateSkipHash, CoreTools.Translate("Skip integrity checks") }, - { UpdateInteractive, CoreTools.Translate("Interactive update") }, - { DownloadInstallers, CoreTools.Translate("Download selected installers") }, - { UninstallSelection, CoreTools.Translate("Uninstall selected packages") }, - { InstallationSettings, " " + CoreTools.Translate("Update options") }, - { PackageDetails, " " + CoreTools.Translate("Package details") }, - { IgnoreSelected, CoreTools.Translate("Ignore selected packages") }, - { ManageIgnored, CoreTools.Translate("Manage ignored updates") }, - }; - - Dictionary Icons = new() - { - { UpdateAsAdmin, IconType.UAC }, - { UpdateSkipHash, IconType.Checksum }, - { UpdateInteractive, IconType.Interactive }, - { InstallationSettings, IconType.Options }, - { DownloadInstallers, IconType.Download }, - { UninstallSelection, IconType.Delete }, - { PackageDetails, IconType.Info_Round }, - { IgnoreSelected, IconType.Pin }, - { ManageIgnored, IconType.ClipboardList }, - }; - - ApplyTextAndIconsToToolbar(Labels, Icons); - - PackageDetails.Click += (_, _) => - ShowDetailsForPackage(SelectedItem, TEL_InstallReferral.ALREADY_INSTALLED); - InstallationSettings.Click += (_, _) => - _ = ShowInstallationOptionsForPackage(SelectedItem); - ManageIgnored.Click += async (_, _) => await DialogHelper.ManageIgnoredUpdates(); - IgnoreSelected.Click += async (_, _) => - { - foreach (IPackage package in FilteredPackages.GetCheckedPackages()) - { - await package.AddToIgnoredUpdatesAsync(); - UpgradablePackagesLoader.Instance.Remove(package); - UpgradablePackagesLoader.Instance.IgnoredPackages[package.Id] = package; - } - }; - - MainToolbarButton.Click += (_, _) => - MainApp.Operations.Update(FilteredPackages.GetCheckedPackages()); - UpdateAsAdmin.Click += (_, _) => - MainApp.Operations.Update(FilteredPackages.GetCheckedPackages(), elevated: true); - UpdateSkipHash.Click += (_, _) => - MainApp.Operations.Update( - FilteredPackages.GetCheckedPackages(), - no_integrity: true - ); - UpdateInteractive.Click += (_, _) => - MainApp.Operations.Update(FilteredPackages.GetCheckedPackages(), interactive: true); - DownloadInstallers.Click += (_, _) => - _ = MainApp.Operations.Download( - FilteredPackages.GetCheckedPackages(), - TEL_InstallReferral.ALREADY_INSTALLED - ); - UninstallSelection.Click += (_, _) => - _ = MainApp.Operations.ConfirmAndUninstall(FilteredPackages.GetCheckedPackages()); - } - - protected override void WhenPackageCountUpdated() - { - MainApp.Tooltip.AvailableUpdates = Loader.Count(); - } - - protected override async void WhenPackagesLoaded(ReloadReason reason) - { - try - { - List upgradablePackages = []; - foreach (IPackage package in Loader.Packages) - { - if (package.Tag is not PackageTag.OnQueue and not PackageTag.BeingProcessed) - upgradablePackages.Add(package); - } - - if (upgradablePackages.Count == 0) - return; - - if ( - Settings.Get(Settings.K.DisableAUPOnMeteredConnections) - && NetworkInformation - .GetInternetConnectionProfile() - ?.GetConnectionCost() - .NetworkCostType - is NetworkCostType.Fixed - or NetworkCostType.Variable - ) - { - Logger.Warn( - "Updates will not be installed automatically because the current internet connection is metered." - ); - await ShowAvailableUpdatesNotification(upgradablePackages); - } - else if ( - Settings.Get(Settings.K.DisableAUPOnBattery) - && PowerManager.PowerSupplyStatus is PowerSupplyStatus.NotPresent - ) - { - Logger.Warn( - "Updates will not be installed automatically because the device is on battery." - ); - await ShowAvailableUpdatesNotification(upgradablePackages); - } - else if ( - Settings.Get(Settings.K.DisableAUPOnBatterySaver) - && PowerManager.EnergySaverStatus is EnergySaverStatus.On - ) - { - Logger.Warn( - "Updates will not be installed automatically because battery saver is enabled." - ); - await ShowAvailableUpdatesNotification(upgradablePackages); - } - else if (Settings.Get(Settings.K.AutomaticallyUpdatePackages)) - { - _ = MainApp.Operations.UpdateAll(); - await ShowUpgradingPackagesNotification(upgradablePackages); - } - else if (Environment.GetCommandLineArgs().Contains("--updateapps")) - { - _ = MainApp.Operations.UpdateAll(); - await ShowUpgradingPackagesNotification(upgradablePackages); - Logger.Warn( - "Automatic install of updates has been enabled via Command Line (user settings have been overriden)" - ); - } - else - { - foreach (var package in upgradablePackages) - { - if ( - ( - await InstallOptionsFactory.LoadApplicableAsync(package) - ).AutoUpdatePackage - ) - { - await MainApp.Operations.Update(package); - } - } - await ShowAvailableUpdatesNotification(upgradablePackages); - } - } - catch (Exception ex) - { - Logger.Error(ex); - } - } - - static async Task ShowAvailableUpdatesNotification( - IReadOnlyList upgradablePackages - ) - { - if (Settings.AreUpdatesNotificationsDisabled()) - return; - - bool SendNotification = false; - foreach (var Package in upgradablePackages) - { - // This allows to disable update notifications only for certain package managers - if ( - !Settings.GetDictionaryItem( - Settings.K.DisabledPackageManagerNotifications, - Package.Manager.Name - ) - ) - { - SendNotification = true; - break; - } - } - - if (!SendNotification) - return; - - await AppNotificationManager.Default.RemoveByTagAsync( - CoreData.UpdatesAvailableNotificationTag.ToString() - ); - - AppNotification notification; - if (upgradablePackages.Count == 1) - { - AppNotificationBuilder builder = new AppNotificationBuilder() - .SetScenario(AppNotificationScenario.Default) - .SetTag(CoreData.UpdatesAvailableNotificationTag.ToString()) - .AddText(CoreTools.Translate("An update was found!")) - .AddText( - CoreTools.Translate( - "{0} can be updated to version {1}", - upgradablePackages[0].Name, - upgradablePackages[0].NewVersionString - ) - ) - .SetAttributionText( - CoreTools.Translate( - "You have currently version {0} installed", - upgradablePackages[0].VersionString - ) - ) - .AddArgument("action", NotificationArguments.ShowOnUpdatesTab) // Believe it or not, the `'` character is broken - .AddButton( - new AppNotificationButton( - CoreTools.Translate("View on UniGetUI").Replace("'", "´") - ).AddArgument("action", NotificationArguments.ShowOnUpdatesTab) - ) - .AddButton( - new AppNotificationButton(CoreTools.Translate("Update")).AddArgument( - "action", - NotificationArguments.UpdateAllPackages - ) - ); - notification = builder.BuildNotification(); - } - else - { - string attribution = ""; - foreach (IPackage package in upgradablePackages) - { - if ( - !Settings.GetDictionaryItem( - Settings.K.DisabledPackageManagerNotifications, - package.Manager.Name - ) - ) - attribution += package.Name + ", "; - } - - attribution = attribution.TrimEnd(' ').TrimEnd(','); - - AppNotificationBuilder builder = new AppNotificationBuilder() - .SetScenario(AppNotificationScenario.Default) - .SetTag(CoreData.UpdatesAvailableNotificationTag.ToString()) - .AddText(CoreTools.Translate("Updates found!")) - .AddText( - CoreTools.Translate("{0} packages can be updated", upgradablePackages.Count) - ) - .SetAttributionText(attribution) - // Believe it or not, the `'` character is broken - .AddButton( - new AppNotificationButton( - CoreTools.Translate("Open UniGetUI").Replace("'", "´") - ).AddArgument("action", NotificationArguments.ShowOnUpdatesTab) - ) - .AddButton( - new AppNotificationButton(CoreTools.Translate("Update all")).AddArgument( - "action", - NotificationArguments.UpdateAllPackages - ) - ) - .AddArgument("action", NotificationArguments.ShowOnUpdatesTab); - notification = builder.BuildNotification(); - } - - notification.ExpiresOnReboot = true; - AppNotificationManager.Default.Show(notification); - } - - static async Task ShowUpgradingPackagesNotification( - IReadOnlyList upgradablePackages - ) - { - if (Settings.AreUpdatesNotificationsDisabled()) - return; - - bool SendNotification = false; - foreach (var Package in upgradablePackages) - { - if ( - !Settings.GetDictionaryItem( - Settings.K.DisabledPackageManagerNotifications, - Package.Manager.Name - ) - ) - { - SendNotification = true; - break; - } - } - - if (!SendNotification) - return; - - await AppNotificationManager.Default.RemoveByTagAsync( - CoreData.UpdatesAvailableNotificationTag.ToString() - ); - - AppNotification notification; - if (upgradablePackages.Count == 1) - { - AppNotificationBuilder builder = new AppNotificationBuilder() - .SetScenario(AppNotificationScenario.Default) - .SetTag(CoreData.UpdatesAvailableNotificationTag.ToString()) - .AddText(CoreTools.Translate("An update was found!")) - .AddText( - CoreTools.Translate( - "{0} is being updated to version {1}", - upgradablePackages[0].Name, - upgradablePackages[0].NewVersionString - ) - ) - .SetAttributionText( - CoreTools.Translate( - "You have currently version {0} installed", - upgradablePackages[0].VersionString - ) - ) - .AddArgument("action", NotificationArguments.ShowOnUpdatesTab); - notification = builder.BuildNotification(); - } - else - { - string attribution = ""; - foreach (IPackage package in upgradablePackages) - { - if ( - !Settings.GetDictionaryItem( - Settings.K.DisabledPackageManagerNotifications, - package.Manager.Name - ) - ) - attribution += package.Name + ", "; - } - - attribution = attribution.TrimEnd(' ').TrimEnd(','); - - AppNotificationBuilder builder = new AppNotificationBuilder() - .SetScenario(AppNotificationScenario.Default) - .SetTag(CoreData.UpdatesAvailableNotificationTag.ToString()) - .AddText( - CoreTools.Translate( - "{0} packages are being updated", - upgradablePackages.Count - ) - ) - .SetAttributionText(attribution) - .AddText(CoreTools.Translate("Updates found!")) - .AddArgument("action", NotificationArguments.ShowOnUpdatesTab); - notification = builder.BuildNotification(); - } - - notification.ExpiresOnReboot = true; - AppNotificationManager.Default.Show(notification); - } - - private void MenuInstall_Invoked(object sender, RoutedEventArgs e) => - _ = MainApp.Operations.Update(SelectedItem); - - private void MenuSkipHash_Invoked(object sender, RoutedEventArgs e) => - _ = MainApp.Operations.Update(SelectedItem, no_integrity: true); - - private void MenuInteractive_Invoked(object sender, RoutedEventArgs e) => - _ = MainApp.Operations.Update(SelectedItem, interactive: true); - - private void MenuAsAdmin_Invoked(object sender, RoutedEventArgs e) => - _ = MainApp.Operations.Update(SelectedItem, elevated: true); - - private void MenuUpdateAfterUninstall_Invoked(object sender, RoutedEventArgs e) => - _ = MainApp.Operations.UninstallThenUpdate(SelectedItem); - - private void MenuUninstall_Invoked(object sender, RoutedEventArgs e) => - _ = MainApp.Operations.Uninstall(SelectedItem); - - private void MenuIgnorePackage_Invoked(object sender, RoutedEventArgs e) - { - IPackage? package = SelectedItem; - if (package is null) - { - return; - } - - _ = package.AddToIgnoredUpdatesAsync(); - UpgradablePackagesLoader.Instance.Remove(package); - UpgradablePackagesLoader.Instance.IgnoredPackages[package.Id] = package; - } - - private void MenuSkipVersion_Invoked(object sender, RoutedEventArgs e) - { - IPackage? package = SelectedItem; - if (package is null) - { - return; - } - - _ = package.AddToIgnoredUpdatesAsync(package.NewVersionString); - UpgradablePackagesLoader.Instance.Remove(package); - UpgradablePackagesLoader.Instance.IgnoredPackages[package.Id] = package; - } - } -} diff --git a/src/UniGetUI/Properties/PublishProfiles/win10-x64.pubxml b/src/UniGetUI/Properties/PublishProfiles/win10-x64.pubxml deleted file mode 100644 index 26ea7e55c1..0000000000 --- a/src/UniGetUI/Properties/PublishProfiles/win10-x64.pubxml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - FileSystem - x64 - win10-x64 - bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ - true - False - False - True - - - \ No newline at end of file diff --git a/src/UniGetUI/Properties/Resources.Designer.cs b/src/UniGetUI/Properties/Resources.Designer.cs deleted file mode 100644 index ebe9181866..0000000000 --- a/src/UniGetUI/Properties/Resources.Designer.cs +++ /dev/null @@ -1,63 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace UniGetUI.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.MainApp.Instance.StronglyTypedResourceBuilder", "17.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ModernWindow.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - } -} diff --git a/src/UniGetUI/Properties/Resources.resx b/src/UniGetUI/Properties/Resources.resx deleted file mode 100644 index 4fdb1b6aff..0000000000 --- a/src/UniGetUI/Properties/Resources.resx +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/src/UniGetUI/Properties/launchSettings.json b/src/UniGetUI/Properties/launchSettings.json deleted file mode 100644 index 7108a3ce55..0000000000 --- a/src/UniGetUI/Properties/launchSettings.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "profiles": { - "UniGetUI (Package)": { - "commandName": "MsixPackage" - }, - "UniGetUI (Unpackaged)": { - "commandName": "Project", - "commandLineArgs": "" - } - } -} diff --git a/src/UniGetUI/Services/BackgroundLoginApi.cs b/src/UniGetUI/Services/BackgroundLoginApi.cs deleted file mode 100644 index 669c582ad1..0000000000 --- a/src/UniGetUI/Services/BackgroundLoginApi.cs +++ /dev/null @@ -1,102 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Hosting; -using UniGetUI.Core.Logging; - -namespace UniGetUI.Services; - -public class GHAuthApiRunner : IDisposable -{ - public event EventHandler? OnLogin; - public event EventHandler? OnCancelled; - private IHost? _host; - - public GHAuthApiRunner() { } - - public async Task Start() - { - var builder = Host.CreateDefaultBuilder(); - builder.ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseKestrel(); - webBuilder.SuppressStatusMessages(true); - webBuilder.Configure(app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapGet("/", LOGIN_CollectGitHubToken); - }); - }); - webBuilder.UseUrls("http://localhost:58642"); - }); - _host = builder.Build(); - await _host.StartAsync(); - Logger.Info("Api running on http://localhost:58642"); - } - - private async Task LOGIN_CollectGitHubToken(HttpContext context) - { - var code = context.Request.Query["code"]; - if (!string.IsNullOrEmpty(code)) - { - await context.Response.WriteAsync(ResultPage("Authentication successful", "You can now close this window and return to UniGetUI")); - Logger.ImportantInfo($"[AUTH API] Received authentication token {code} from GitHub"); - OnLogin?.Invoke(this, code.ToString()); - return; - } - - var error = context.Request.Query["error"]; - if (!string.IsNullOrEmpty(error)) - { - // GitHub redirects here with an "error" parameter when the user cancels/denies the authorization. - await context.Response.WriteAsync(ResultPage("Authentication cancelled", "You can now close this window and return to UniGetUI")); - Logger.Warn($"[AUTH API] GitHub authentication was cancelled or failed (error: {error})"); - OnCancelled?.Invoke(this, error.ToString()); - return; - } - - // Not an OAuth callback (e.g. a favicon request or a probe): ignore it. - context.Response.StatusCode = 400; - } - - private static string ResultPage(string title, string message) => - $$""" -
- UniGetUI authentication -

{{title}}

-

{{message}}

-
- """; - - public async Task Stop() - { - try - { - ArgumentNullException.ThrowIfNull(_host); - await _host.StopAsync(); - } - catch (Exception ex) - { - Logger.Error(ex); - } - } - - public void Dispose() - { - _host?.Dispose(); - } -} diff --git a/src/UniGetUI/Services/GitHubAuthService.cs b/src/UniGetUI/Services/GitHubAuthService.cs deleted file mode 100644 index e67d1a3dd4..0000000000 --- a/src/UniGetUI/Services/GitHubAuthService.cs +++ /dev/null @@ -1,260 +0,0 @@ -using System.Net; -using System.Text; -using Octokit; -using UniGetUI.Core.Data; -using UniGetUI.Core.Logging; -using UniGetUI.Core.SecureSettings; -using UniGetUI.Core.SettingsEngine; -using UniGetUI.Interface; -using Windows.System; - -namespace UniGetUI.Services -{ - public class GitHubAuthService - { - private const string MissingClientId = "CLIENT_ID_UNSET"; - private const string MissingClientSecret = "CLIENT_SECRET_UNSET"; - private static readonly TimeSpan LoginTimeout = TimeSpan.FromMinutes(2); - private readonly string GitHubClientId = Secrets.GetGitHubClientId(); - private readonly string GitHubClientSecret = Secrets.GetGitHubClientSecret(); - private const string RedirectUri = "http://127.0.0.1:58642/"; - private readonly GitHubClient _client; - - public static event EventHandler? AuthStatusChanged; - - public GitHubAuthService() - { - _client = new GitHubClient(new ProductHeaderValue("UniGetUI", CoreData.VersionName)); - } - - public GitHubClient? CreateGitHubClient() - { - var token = SecureGHTokenManager.GetToken(); - if (string.IsNullOrEmpty(token)) - { - Logger.Error( - "GitHub access token is not available. Cannot perform Gist operation." - ); - return null; - } - - return new GitHubClient(new ProductHeaderValue("UniGetUI", CoreData.VersionName)) - { - Credentials = new Credentials(token), - }; - } - - private GHAuthApiRunner? loginBackend; - - public async Task SignInAsync() - { - try - { - if (!HasConfiguredOAuthClient()) - { - Logger.Error( - "GitHub sign-in is not configured for this build. Missing OAuth client ID or client secret." - ); - AuthStatusChanged?.Invoke(this, EventArgs.Empty); - return false; - } - - Logger.Info("Initiating GitHub sign-in process using loopback redirect..."); - - var request = new OauthLoginRequest(GitHubClientId) - { - Scopes = { "read:user", "gist" }, - RedirectUri = new Uri(RedirectUri), - }; - - var oauthLoginUrl = _client.Oauth.GetGitHubLoginUrl(request); - - codeFromAPI = null; - LoginWasCancelled = false; - if (loginBackend is not null) - { - try - { - await loginBackend.Stop(); - loginBackend.Dispose(); - loginBackend = null; - } - catch (Exception ex) - { - Logger.Warn(ex); - } - } - loginBackend = new GHAuthApiRunner(); - loginBackend.OnLogin += BackgroundApiOnOnLogin; - loginBackend.OnCancelled += BackgroundApiOnCancelled; - await loginBackend.Start(); - - bool launchSucceeded = await Launcher.LaunchUriAsync(oauthLoginUrl); - if (!launchSucceeded) - { - Logger.Error("Failed to launch the browser for GitHub sign-in."); - AuthStatusChanged?.Invoke(this, EventArgs.Empty); - return false; - } - - DateTime timeoutAt = DateTime.UtcNow.Add(LoginTimeout); - while (codeFromAPI is null && !LoginWasCancelled && DateTime.UtcNow < timeoutAt) - await Task.Delay(100); - - if (LoginWasCancelled) - { - Logger.Warn("GitHub sign-in was cancelled by the user."); - AuthStatusChanged?.Invoke(this, EventArgs.Empty); - return false; - } - - if (string.IsNullOrEmpty(codeFromAPI)) - { - Logger.Error("GitHub sign-in timed out before the loopback callback was received."); - AuthStatusChanged?.Invoke(this, EventArgs.Empty); - return false; - } - - return await _completeSignInAsync(codeFromAPI); - } - catch (Exception ex) - { - Logger.Error("Exception during GitHub sign-in process:"); - Logger.Error(ex); - ClearAuthenticatedUserData(); - AuthStatusChanged?.Invoke(this, EventArgs.Empty); - return false; - } - finally - { - if (loginBackend is not null) - { - try - { - loginBackend.OnLogin -= BackgroundApiOnOnLogin; - loginBackend.OnCancelled -= BackgroundApiOnCancelled; - await loginBackend.Stop(); - loginBackend.Dispose(); - } - catch (Exception ex) - { - Logger.Warn(ex); - } - finally - { - loginBackend = null; - } - } - } - } - - private string? codeFromAPI; - - /// - /// True when the most recent ended because the user cancelled the - /// authorization on GitHub (as opposed to an error). Callers use this to avoid showing an - /// error message for a deliberate cancellation. - /// - public bool LoginWasCancelled { get; private set; } - - private void BackgroundApiOnOnLogin(object? sender, string c) - { - codeFromAPI = c; - } - - private void BackgroundApiOnCancelled(object? sender, string error) - { - LoginWasCancelled = true; - } - - private bool HasConfiguredOAuthClient() - { - return !string.IsNullOrWhiteSpace(GitHubClientId) - && !string.IsNullOrWhiteSpace(GitHubClientSecret) - && !string.Equals(GitHubClientId, MissingClientId, StringComparison.Ordinal) - && !string.Equals( - GitHubClientSecret, - MissingClientSecret, - StringComparison.Ordinal - ); - } - - private async Task _completeSignInAsync(string code) - { - try - { - var tokenRequest = new OauthTokenRequest(GitHubClientId, GitHubClientSecret, code) - { - RedirectUri = new Uri(RedirectUri), // The same redirect_uri must be sent - }; - var token = await _client.Oauth.CreateAccessToken(tokenRequest); - - if (string.IsNullOrEmpty(token.AccessToken)) - { - Logger.Error("Failed to obtain GitHub access token."); - AuthStatusChanged?.Invoke(this, EventArgs.Empty); - return false; - } - - Logger.Info("GitHub login successful. Storing access token."); - SecureGHTokenManager.StoreToken(token.AccessToken); - - var userClient = new GitHubClient(new ProductHeaderValue("UniGetUI")) - { - Credentials = new Credentials(token.AccessToken), - }; - - var user = await userClient.User.Current(); - if (user != null) - { - Settings.SetValue(Settings.K.GitHubUserLogin, user.Login); - Logger.Info($"Logged in as GitHub user: {user.Login}"); - } - else - { - Logger.Warn("Could not retrieve GitHub user information after login."); - } - - AuthStatusChanged?.Invoke(this, EventArgs.Empty); - return true; - } - catch (Exception ex) - { - Logger.Error("Exception during GitHub token exchange:"); - Logger.Error(ex); - ClearAuthenticatedUserData(); - AuthStatusChanged?.Invoke(this, EventArgs.Empty); - return false; - } - } - - public void SignOut() - { - Logger.Info("Signing out from GitHub..."); - try - { - ClearAuthenticatedUserData(); - } - catch (Exception ex) - { - Logger.Error("Failed to log out:"); - Logger.Error(ex); - } - - AuthStatusChanged?.Invoke(this, EventArgs.Empty); - Logger.Info("GitHub sign-out complete."); - } - - private static void ClearAuthenticatedUserData() - { - Settings.SetValue(Settings.K.GitHubUserLogin, ""); // Clear stored username - SecureGHTokenManager.DeleteToken(); - } - - public bool IsAuthenticated() - { - var token = SecureGHTokenManager.GetToken(); - return !string.IsNullOrEmpty(token); - } - } -} diff --git a/src/UniGetUI/Services/GitHubBackupService.cs b/src/UniGetUI/Services/GitHubBackupService.cs deleted file mode 100644 index adde230987..0000000000 --- a/src/UniGetUI/Services/GitHubBackupService.cs +++ /dev/null @@ -1,141 +0,0 @@ -using Octokit; -using UniGetUI.Core.Logging; -using UniGetUI.Core.Tools; - -namespace UniGetUI.Services -{ - public class GitHubBackupService - { - private const string GistDescription_EndingKey = "@[UNIGETUI_BACKUP_V1]"; - private const string PackageBackup_StartingKey = "@[PACKAGES]"; - - private const string GistDescription = - $"UniGetUI package backups - DO NOT RENAME OR MODIFY {GistDescription_EndingKey}"; - private const string ReadMeContents = - "" - + "This special Gist is used by UniGetUI to store your package backups. \n" - + "Please DO NOT EDIT the contents or the description of this gist, or unexpected behaviours may occur.\n" - + "Learn more about UniGetUI at https://github.com/Devolutions/UniGetUI\n"; - - private readonly GitHubAuthService _authService; - - private readonly string GistFileKey; - - public GitHubBackupService(GitHubAuthService authService) - { - _authService = authService; - string deviceUserUniqueIdentifier = - $"{Environment.MachineName}\\{Environment.UserName}".Replace(" ", ""); - GistFileKey = $"{PackageBackup_StartingKey} {deviceUserUniqueIdentifier}"; - } - - /// - /// Assuming authentication is set up, upload the given bundleContents to GitHub - /// - public async Task UploadPackageBundle(string bundleContents) - { - var GHClient = _authService.CreateGitHubClient(); - if (GHClient is null) - throw new InvalidOperationException("The GitHub user is not authenticated"); - - User user = await GHClient.User.Current(); - - var candidates = await GHClient.Gist.GetAllForUser(user.Login); - Gist? existingBackup = candidates?.FirstOrDefault(g => - g?.Description?.EndsWith(GistDescription_EndingKey) ?? false - ); - - if (existingBackup is null) - { - Logger.Warn( - $"No matching gist was found as a valid backup, a new gist will be created..." - ); - existingBackup = await _createBackupGistAsync(GHClient); - } - - await _updateBackupGistAsync(GHClient, existingBackup, bundleContents); - Logger.Info( - $"Cloud backup completed successfully to gist {user.Login}/{existingBackup.Id}" - ); - } - - /// - /// Upload the given payload to the given gist. - /// Updates the existing file if GistFileKey exists, creates a new one otherwhise. - /// - private async Task _updateBackupGistAsync(GitHubClient client, Gist gist, string payload) - { - var update = new GistUpdate { Description = GistDescription }; - if (update.Files.ContainsKey(GistFileKey)) - { - update.Files[GistFileKey] = new GistFileUpdate { Content = payload }; - } - else - { - update.Files.Add(GistFileKey, new GistFileUpdate { Content = payload }); - } - await client.Gist.Edit(gist.Id, update); - Logger.Info($"Successfully updated Gist ID: {gist.Id}"); - } - - /// - /// Creates a new Gist, prepared to be detectable by UniGetUI, and with the base readme file - /// - private static Task _createBackupGistAsync(GitHubClient client) - { - var newGist = new NewGist { Description = GistDescription, Public = false }; - newGist.Files.Add("- UniGetUI Package Backups", ReadMeContents); - return client.Gist.Create(newGist); - } - - /// - /// Retrieves a list of available backups to import - /// - public async Task> GetAvailableBackups() - { - var GHClient = _authService.CreateGitHubClient(); - if (GHClient is null) - throw new InvalidOperationException("The GitHub user is not authenticated"); - - User user = await GHClient.User.Current(); - - var candidates = await GHClient.Gist.GetAllForUser(user.Login); - Gist? existingBackup = candidates?.FirstOrDefault(g => - g?.Description?.EndsWith(GistDescription_EndingKey) ?? false - ); - - return existingBackup - ?.Files.Where(f => f.Key.StartsWith(PackageBackup_StartingKey)) - .Select(f => $"{f.Key.Split(' ')[^1]} ({CoreTools.FormatAsSize(f.Value.Size)})") - ?? []; - } - - /// - /// For the given backupName, retrieve the backup contents - /// - public async Task GetBackupContents(string backupName) - { - var GHClient = _authService.CreateGitHubClient(); - if (GHClient is null) - throw new InvalidOperationException("The GitHub user is not authenticated"); - - User user = await GHClient.User.Current(); - - var candidates = await GHClient.Gist.GetAllForUser(user.Login); - Gist? existingBackup = candidates?.FirstOrDefault(g => - g?.Description?.EndsWith(GistDescription_EndingKey) ?? false - ); - if (existingBackup is null) - throw new KeyNotFoundException( - $"The backup {backupName} was not found, yet this name was passed by argument" - ); - - existingBackup = await GHClient.Gist.Get(existingBackup.Id); - return existingBackup - .Files.FirstOrDefault(f => - f.Key.StartsWith(PackageBackup_StartingKey) && f.Key.EndsWith(backupName) - ) - .Value.Content; - } - } -} diff --git a/src/UniGetUI/Services/Secrets.cs b/src/UniGetUI/Services/Secrets.cs deleted file mode 100644 index fd77d1af9b..0000000000 --- a/src/UniGetUI/Services/Secrets.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace UniGetUI.Services -{ - internal static partial class Secrets - { - /* ---------------------------------------------------------------- - * W A R N I N G !!! - * - * Seeing errors? Build the project (maybe twice) - */ - public static partial string GetGitHubClientId(); - - public static partial string GetGitHubClientSecret(); - - public static partial string GetOpenSearchUsername(); - - public static partial string GetOpenSearchPassword(); - /* ------------------------------------------------------------------------ */ - } -} diff --git a/src/UniGetUI/Services/UserAvatar.cs b/src/UniGetUI/Services/UserAvatar.cs deleted file mode 100644 index b7127a6115..0000000000 --- a/src/UniGetUI/Services/UserAvatar.cs +++ /dev/null @@ -1,314 +0,0 @@ -using Microsoft.UI; -using Microsoft.UI.Input; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Media.Imaging; -using Octokit; -using UniGetUI.Core.Logging; -using UniGetUI.Core.Tools; -using UniGetUI.Interface.Widgets; -using UniGetUI.Pages.DialogPages; -using UniGetUI.Pages.SettingsPages.GeneralPages; -using Windows.UI; - -namespace UniGetUI.Services -{ - public partial class PointButton : Button - { - public PointButton() - { - ProtectedCursor = InputSystemCursor.Create(InputSystemCursorShape.Hand); - } - } - - public partial class UserAvatar : UserControl - { - public UserAvatar() - { - VerticalContentAlignment = VerticalAlignment.Center; - HorizontalContentAlignment = HorizontalAlignment.Center; - Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, CoreTools.Translate("User profile")); - ToolTipService.SetToolTip(this, CoreTools.Translate("User profile")); - _ = RefreshStatus(); - GitHubAuthService.AuthStatusChanged += GitHubAuthService_AuthStatusChanged; - } - - private void GitHubAuthService_AuthStatusChanged(object? sender, EventArgs e) - { - _ = RefreshStatus(); - } - - public async Task RefreshStatus() - { - SetLoading(); - var client = new GitHubAuthService(); - // await Task.Delay(1000); - if (client.IsAuthenticated()) - { - Content = await GenerateLogoutControl(); - } - else - { - Content = GenerateLoginControl(); - } - } - - private void LoginButton_Click(object sender, RoutedEventArgs e) => - _ = _loginButton_Click(); - - private async Task _loginButton_Click() - { - SetLoading(); - try - { - var client = new GitHubAuthService(); - if (client.IsAuthenticated()) - { - Logger.Warn("Login invoked when the client was already logged in!"); - return; - } - - bool success = await client.SignInAsync(); - if (!success && !client.LoginWasCancelled) - { - DialogHelper.ShowDismissableBalloon( - CoreTools.Translate("Failed"), - CoreTools.Translate("An error occurred while logging in: ") - ); - } - } - catch (Exception ex) - { - DialogHelper.ShowDismissableBalloon( - CoreTools.Translate("Error"), - CoreTools.Translate("Log in failed: ") + ex.Message - ); - } - } - - private void LogoutButton_Click(object sender, RoutedEventArgs e) - { - SetLoading(); - try - { - var client = new GitHubAuthService(); - if (client.IsAuthenticated()) - { - client.SignOut(); - } - } - catch (Exception ex) - { - DialogHelper.ShowDismissableBalloon( - CoreTools.Translate("Error"), - CoreTools.Translate("Log out failed: ") + ex.Message - ); - } - } - - private void SetLoading() - { - this.Content = new ProgressRing() - { - IsIndeterminate = true, - Width = 24, - Height = 24, - HorizontalAlignment = HorizontalAlignment.Center, - VerticalAlignment = VerticalAlignment.Center, - }; - } - - private PointButton GenerateLoginControl() - { - var personPicture = new PersonPicture { Width = 32, Height = 32 }; - - var translatedTextBlock = new TextBlock - { - Margin = new Thickness(4), - TextWrapping = TextWrapping.Wrap, - Text = CoreTools.Translate("Log in with GitHub to enable cloud package backup."), - }; - - var hyperlinkButton = new HyperlinkButton - { - Padding = new Thickness(0), - HorizontalAlignment = HorizontalAlignment.Stretch, - Content = new TextBlock() - { - Text = CoreTools.Translate("More details"), - TextWrapping = TextWrapping.Wrap, - }, - FontSize = 12, - }; - hyperlinkButton.Click += (_, _) => - CoreTools.Launch("https://devolutions.net/unigetui"); - - var loginButton = new PointButton - { - HorizontalAlignment = HorizontalAlignment.Stretch, - Content = CoreTools.Translate("Log in"), - }; - loginButton.Click += LoginButton_Click; - - var stackPanel = new StackPanel - { - MaxWidth = 200, - Margin = new Thickness(-8), - Orientation = Orientation.Vertical, - Spacing = 8, - }; - stackPanel.Children.Add(translatedTextBlock); - stackPanel.Children.Add(hyperlinkButton); - stackPanel.Children.Add(loginButton); - - var flyout = new BetterFlyout() - { - LightDismissOverlayMode = LightDismissOverlayMode.Off, - Placement = FlyoutPlacementMode.Bottom, - Content = stackPanel, - }; - - var btn = new PointButton - { - Margin = new Thickness(0), - Padding = new Thickness(4), - Background = new SolidColorBrush(Colors.Transparent), - BorderThickness = new Thickness(0), - CornerRadius = new CornerRadius(100), - Content = personPicture, - Flyout = flyout, - }; - Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(btn, CoreTools.Translate("User profile")); - ToolTipService.SetToolTip(btn, CoreTools.Translate("User profile")); - return btn; - } - - private async Task GenerateLogoutControl() - { - await Task.Run(CoreTools.WaitForInternetConnection); - User user; - try - { - var authClient = new GitHubAuthService(); - var GHClient = authClient.CreateGitHubClient(); - if (GHClient is null) - { - Logger.Error("Client did not report valid authentication"); - return GenerateLoginControl(); - } - - user = await GHClient.User.Current(); - } - catch (Exception ex) - { - Logger.Error("An error occurred while retrieving user's logged in data."); - Logger.Error(ex); - return GenerateLoginControl(); - } - - var personPicture = new PersonPicture - { - Width = 32, - Height = 32, - ProfilePicture = new BitmapImage(new Uri(user.AvatarUrl)), - }; - - var text1 = new TextBlock - { - Margin = new Thickness(4), - TextWrapping = TextWrapping.Wrap, - Text = CoreTools.Translate( - "You are logged in as {0} (@{1})", - user.Name, - user.Login - ), - }; - - var text2 = new TextBlock - { - Margin = new Thickness(4), - TextWrapping = TextWrapping.Wrap, - FontSize = 12, - FontWeight = new(500), - Text = CoreTools.Translate( - "If you have cloud backup enabled, it will be saved as a GitHub Gist on this account" - ), - }; - - var hyperlinkButton = new HyperlinkButton - { - Padding = new Thickness(0), - HorizontalAlignment = HorizontalAlignment.Stretch, - Content = new TextBlock() - { - Text = CoreTools.Translate("More details"), - TextWrapping = TextWrapping.Wrap, - }, - FontSize = 12, - }; - hyperlinkButton.Click += (_, _) => - CoreTools.Launch("https://devolutions.net/unigetui"); - - var hyperlinkButton2 = new HyperlinkButton - { - Padding = new Thickness(0), - HorizontalAlignment = HorizontalAlignment.Stretch, - Content = new TextBlock() - { - Text = CoreTools.Translate("Package backup settings"), - TextWrapping = TextWrapping.Wrap, - }, - FontSize = 12, - }; - hyperlinkButton2.Click += (_, _) => - MainApp.Instance.MainWindow.NavigationPage.OpenSettingsPage(typeof(Backup)); - - var loginButton = new PointButton - { - HorizontalAlignment = HorizontalAlignment.Stretch, - Content = CoreTools.Translate("Log out"), - Background = new SolidColorBrush( - ActualTheme is ElementTheme.Dark ? Colors.DarkRed : Colors.PaleVioletRed - ), - BorderThickness = new(0), - }; - loginButton.Click += LogoutButton_Click; - - var stackPanel = new StackPanel - { - MaxWidth = 200, - Margin = new Thickness(-8), - Orientation = Orientation.Vertical, - Spacing = 8, - }; - stackPanel.Children.Add(text1); - stackPanel.Children.Add(text2); - stackPanel.Children.Add(hyperlinkButton); - stackPanel.Children.Add(hyperlinkButton2); - stackPanel.Children.Add(loginButton); - - var flyout = new BetterFlyout() - { - LightDismissOverlayMode = LightDismissOverlayMode.Off, - Placement = FlyoutPlacementMode.Bottom, - Content = stackPanel, - }; - - var btn = new PointButton - { - Margin = new Thickness(0), - Padding = new Thickness(4), - Background = new SolidColorBrush(Colors.Transparent), - BorderThickness = new Thickness(0), - CornerRadius = new CornerRadius(100), - Content = personPicture, - Flyout = flyout, - }; - Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(btn, CoreTools.Translate("User profile")); - ToolTipService.SetToolTip(btn, CoreTools.Translate("User profile")); - return btn; - } - } -} diff --git a/src/UniGetUI/Services/generate-secrets.ps1 b/src/UniGetUI/Services/generate-secrets.ps1 deleted file mode 100644 index 8681eef882..0000000000 --- a/src/UniGetUI/Services/generate-secrets.ps1 +++ /dev/null @@ -1,38 +0,0 @@ -param ( - [string]$OutputPath = "obj\Generated" -) - -# Ensure directory exists -if (-not (Test-Path -Path "$OutputPath\Generated Files")) { - New-Item -ItemType Directory -Path "$OutputPath\Generated Files" -Force | Out-Null -} - -if (-not (Test-Path -Path "Generated Files")) { - New-Item -ItemType Directory -Path "Generated Files" -Force | Out-Null -} - - -$clientId = $env:UNIGETUI_GITHUB_CLIENT_ID -$clientSecret = $env:UNIGETUI_GITHUB_CLIENT_SECRET -$openSearchUsername = $env:UNIGETUI_OPENSEARCH_USERNAME -$openSearchPassword = $env:UNIGETUI_OPENSEARCH_PASSWORD - -if (-not $clientId) { $clientId = "CLIENT_ID_UNSET" } -if (-not $clientSecret) { $clientSecret = "CLIENT_SECRET_UNSET" } -if (-not $openSearchUsername) { $openSearchUsername = "OPENSEARCH_USERNAME_UNSET" } -if (-not $openSearchPassword) { $openSearchPassword = "OPENSEARCH_PASSWORD_UNSET" } - -@" -// Auto-generated file - do not modify -namespace UniGetUI.Services -{ - internal static partial class Secrets - { - public static partial string GetGitHubClientId() => `"$clientId`"; - public static partial string GetGitHubClientSecret() => `"$clientSecret`"; - public static partial string GetOpenSearchUsername() => `"$openSearchUsername`"; - public static partial string GetOpenSearchPassword() => `"$openSearchPassword`"; - } -} -"@ | Set-Content -Encoding UTF8 "Generated Files\Secrets.Generated.cs" -Copy-Item "Generated Files\Secrets.Generated.cs" "$OutputPath\Generated Files\Secrets.Generated.cs" diff --git a/src/UniGetUI/Themes/Generic.xaml b/src/UniGetUI/Themes/Generic.xaml deleted file mode 100644 index 2cc18ed4e2..0000000000 --- a/src/UniGetUI/Themes/Generic.xaml +++ /dev/null @@ -1,20 +0,0 @@ - - - diff --git a/src/UniGetUI/UniGetUI.csproj b/src/UniGetUI/UniGetUI.csproj deleted file mode 100644 index af340680ae..0000000000 --- a/src/UniGetUI/UniGetUI.csproj +++ /dev/null @@ -1,482 +0,0 @@ - - - $(WindowsTargetFramework) - - WinExe - UniGetUI - app.manifest - true - true - UniGetUI.EntryPoint - False - icon.ico - - - true - None - true - win-x64;win-arm64 - win-$(Platform) - - - true - partial - true - - - - arm64 - x64 - $(PkgDevolutions_UniGetUI_Elevator)\runtimes\win-$(ElevatorPackageArchitecture)\native - $(ElevatorPackageNativePath)\UniGetUI Elevator.exe - $(ElevatorPackageNativePath)\getfilesiginforedist.dll - - - - $(RuntimeIdentifier) - win-arm64 - win-x64 - $(PkgDevolutions_Pinget_Cli_Rust)\runtimes\$(PingetCliRuntimeIdentifier)\native - $(PingetCliPackageNativePath)\pinget.exe - - - - true - Avalonia - $(MSBuildThisFileDirectory)..\UniGetUI.Avalonia\UniGetUI.Avalonia.csproj - $(RuntimeIdentifier) - win-arm64 - win-x64 - $(MSBuildProjectDirectory)\obj\$(Platform)\$(Configuration)\BundledAvalonia\$(AvaloniaRuntimeIdentifier)\ - $(AvaloniaPublishDir)UniGetUI.Avalonia.exe - - - - $(IntermediateOutputPath)\Generated Files\Secrets.Generated.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - $([MSBuild]::NormalizeDirectory('$(MSBuildProjectDirectory)', '$(PublishDir)', '$(ModernAppDirectoryName)')) - $(BundledAvaloniaPublishDir)UniGetUI.Avalonia.exe - - - - - - - - - - - - - - - - - - - - Designer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - $(DefaultXamlRuntime) - Designer - - - $(DefaultXamlRuntime) - Designer - - - $(DefaultXamlRuntime) - - - $(DefaultXamlRuntime) - Designer - - - Designer - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - true - - - $(DefineConstants);DISABLE_XAML_GENERATED_MAIN - - - $(DefineConstants);DISABLE_XAML_GENERATED_MAIN - - - $(DefineConstants);DISABLE_XAML_GENERATED_MAIN - - - $(DefineConstants);DISABLE_XAML_GENERATED_MAIN - - - - - - - - - - - - PreserveNewest - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - MSBuild:Compile - - - MSBuild:Compile - - - MSBuild:Compile - - - MSBuild:Compile - - - MSBuild:Compile - - - MSBuild:Compile - - - MSBuild:Compile - - - MSBuild:Compile - - - MSBuild:Compile - - - MSBuild:Compile - - - MSBuild:Compile - - - MSBuild:Compile - - - MSBuild:Compile - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Always - - - PreserveNewest - PreserveNewest - false - false - - - PreserveNewest - PreserveNewest - false - false - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/UniGetUI.csproj.user b/src/UniGetUI/UniGetUI.csproj.user deleted file mode 100644 index 0cd5b6bf1c..0000000000 --- a/src/UniGetUI/UniGetUI.csproj.user +++ /dev/null @@ -1,61 +0,0 @@ - - - - ProjectDebugger - - - ProjectDebugger - - - UniGetUI (Unpackaged) - - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - - - Designer - - - \ No newline at end of file diff --git a/src/UniGetUI/WinUiHeadlessHost.cs b/src/UniGetUI/WinUiHeadlessHost.cs deleted file mode 100644 index 3f658bcbf1..0000000000 --- a/src/UniGetUI/WinUiHeadlessHost.cs +++ /dev/null @@ -1,22 +0,0 @@ -using UniGetUI.Core.Tools; -using UniGetUI.Interface; -using UniGetUI.PackageEngine; - -namespace UniGetUI; - -internal static class WinUiHeadlessHost -{ - public static Task RunAsync(string[] args) - { - return HeadlessIpcHost.RunAsync(async () => - { - CoreTools.ReloadLanguageEngineInstance(); - UniGetUI.Interface.MainWindow.ApplyProxyVariableToProcess(); - PEInterface.LoadLoaders(); - await Task.WhenAll( - Task.Run(PEInterface.LoadManagers), - MainApp.LoadGSudoAsync() - ); - }); - } -} diff --git a/src/UniGetUI/app.manifest b/src/UniGetUI/app.manifest deleted file mode 100644 index 9c8c1daa57..0000000000 --- a/src/UniGetUI/app.manifest +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - PerMonitorV2 - - - \ No newline at end of file diff --git a/testing/automation/cli-e2e.manifest.windows.json b/testing/automation/cli-e2e.manifest.windows.json index d561d42b40..92bd59bbd9 100644 --- a/testing/automation/cli-e2e.manifest.windows.json +++ b/testing/automation/cli-e2e.manifest.windows.json @@ -1,9 +1,9 @@ { "name": "windows-headless-ipc", "daemon": { - "kind": "winui-exe", - "project": "UniGetUI\\UniGetUI.csproj", - "assemblyName": "UniGetUI" + "kind": "avalonia-dll", + "project": "UniGetUI.Avalonia\\UniGetUI.Avalonia.csproj", + "assemblyName": "UniGetUI.Avalonia" }, "transport": { "kind": "named-pipe", diff --git a/testing/automation/run-cli-e2e.ps1 b/testing/automation/run-cli-e2e.ps1 index 63962cd73f..332787f736 100644 --- a/testing/automation/run-cli-e2e.ps1 +++ b/testing/automation/run-cli-e2e.ps1 @@ -210,7 +210,7 @@ function Get-DaemonCommand { } switch ([string]$manifest.daemon.kind) { - 'winui-exe' { + 'windows-exe' { $daemonExe = if ($env:UNIGETUI_DAEMON_EXE) { $env:UNIGETUI_DAEMON_EXE } @@ -218,7 +218,7 @@ function Get-DaemonCommand { Find-BuiltArtifact -ProjectDirectory (Split-Path $daemonProject -Parent) -FileName "$($manifest.daemon.assemblyName).exe" } if ([string]::IsNullOrWhiteSpace($daemonExe) -or -not (Test-Path $daemonExe)) { - throw "WinUI headless executable was not found. Expected $($manifest.daemon.assemblyName).exe under $(Split-Path $daemonProject -Parent)\bin\$configuration" + throw "Windows headless executable was not found. Expected $($manifest.daemon.assemblyName).exe under $(Split-Path $daemonProject -Parent)\bin\$configuration" } return @{ From 2655ae5042d347c547d5ce6fdf011703b44a6118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 18 Jun 2026 09:39:57 -0400 Subject: [PATCH 2/8] Drop legacy WingetUI executable alias Stop copying WingetUI.exe into Windows release payloads now that Avalonia ships as UniGetUI.exe, and remove stale alias files during installer upgrades. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/build-release.yml | 3 --- UniGetUI.iss | 1 + scripts/build.ps1 | 3 --- src/SharedAssets/Assets/Utilities/install_scoop.cmd | 8 +++----- src/SharedAssets/Assets/Utilities/uninstall_scoop.cmd | 6 +++--- 5 files changed, 7 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 18c7672fa3..2701ce18f3 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -215,9 +215,6 @@ jobs: Write-Host ("Removed {0} oversized PDBs above {1:N2} MiB ({2:N2} MiB total)." -f $PdbsToRemove.Count, ($MaxShippedPdbSizeBytes / 1MB), ($RemovedPdbBytes / 1MB)) } - # Backward-compat alias - Copy-Item "unigetui_bin/UniGetUI.exe" "unigetui_bin/WingetUI.exe" -Force - - name: Code-sign binaries if: ${{ fromJSON(needs.preflight.outputs.dry-run) == false }} shell: pwsh diff --git a/UniGetUI.iss b/UniGetUI.iss index 0fc3687582..86ce86a19d 100644 --- a/UniGetUI.iss +++ b/UniGetUI.iss @@ -269,6 +269,7 @@ Root: HKA; Subkey: "Software\Classes\UniGetUI.PackageBundle\shell\open\command"; [InstallDelete] Type: filesandordirs; Name: "{app}\Avalonia" Type: filesandordirs; Name: "{app}\Assets" +Type: files; Name: "{app}\WingetUI.exe" Type: files; Name: "{app}\UniGetUI.Avalonia.exe" Type: files; Name: "{app}\*.dll" Type: files; Name: "{app}\*.pdb" diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 3b6c6ed886..f734422345 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -111,9 +111,6 @@ if ($PdbsToRemove.Count -gt 0) { Write-Host ("Removed {0} oversized PDBs above {1:N2} MiB ({2:N2} MiB total)." -f $PdbsToRemove.Count, ($MaxShippedPdbSizeBytes / 1MB), ($RemovedPdbBytes / 1MB)) } -# WingetUI.exe alias for backward compat -Copy-Item (Join-Path $BinDir "UniGetUI.exe") (Join-Path $BinDir "WingetUI.exe") -Force - # --- Package output --- if (Test-Path $OutputPath) { Remove-Item $OutputPath -Recurse -Force } New-Item $OutputPath -ItemType Directory | Out-Null diff --git a/src/SharedAssets/Assets/Utilities/install_scoop.cmd b/src/SharedAssets/Assets/Utilities/install_scoop.cmd index 615e8b649c..fa90e52c68 100644 --- a/src/SharedAssets/Assets/Utilities/install_scoop.cmd +++ b/src/SharedAssets/Assets/Utilities/install_scoop.cmd @@ -1,5 +1,5 @@ @echo off -SET unigetuipath=%~dp0..\wingetui.exe +SET unigetuipath=%~dp0..\..\UniGetUI.exe set pwsh=C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe echo Scoop Installer Assistant - UniGetUI echo This script will install Scoop and its dependencies, since it appears that they are not installed on your machine. @@ -10,12 +10,10 @@ cls if %errorlevel% equ 0 ( echo UniGetUI will be restarted to continue. pause - taskkill /im wingetui.exe /f taskkill /im unigetui.exe /f - start /b "%unigetuipath%" /i + start "" /b "%unigetuipath%" /i ) else ( pause - taskkill /im wingetui.exe /f taskkill /im unigetui.exe /f - start /b "%unigetuipath%" /i + start "" /b "%unigetuipath%" /i ) diff --git a/src/SharedAssets/Assets/Utilities/uninstall_scoop.cmd b/src/SharedAssets/Assets/Utilities/uninstall_scoop.cmd index 5b353edbb8..4720fbad03 100644 --- a/src/SharedAssets/Assets/Utilities/uninstall_scoop.cmd +++ b/src/SharedAssets/Assets/Utilities/uninstall_scoop.cmd @@ -1,5 +1,5 @@ @echo off -SET unigetuipath=%~dp0..\unigetui.exe +SET unigetuipath=%~dp0..\..\UniGetUI.exe echo This script will remove scoop from your machine. echo Removing Scoop implies removing all Scoop installed packages, buckets and preferences, and might also erase valuable user data related to the affected packages echo|set/p="Press to continue or CLOSE this window to abort this process"&runas/u: "">NUL @@ -13,9 +13,9 @@ if %errorlevel% equ 0 ( echo UniGetUI will be restarted to continue. pause taskkill /im unigetui.exe /f - start /b "%unigetuipath%" /i + start "" /b "%unigetuipath%" /i ) else ( pause taskkill /im unigetui.exe /f - start /b "%unigetuipath%" /i + start "" /b "%unigetuipath%" /i ) From 26d71cf86172c71934b79fc204cbfd2fbfa03454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 18 Jun 2026 09:51:26 -0400 Subject: [PATCH 3/8] Use MSBuild target framework in release workflow Resolve the Avalonia publish directory from MSBuild evaluation instead of parsing Directory.Build.props in PowerShell. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/build-release.yml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 2701ce18f3..34b5db6ea1 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -178,15 +178,20 @@ jobs: shell: pwsh run: | $Platform = '${{ matrix.platform }}' - [xml]$BuildProps = Get-Content "src/Directory.Build.props" - $PortableTargetFramework = @($BuildProps.Project.PropertyGroup | Where-Object { $_.PortableTargetFramework } | Select-Object -First 1).PortableTargetFramework - $WindowsTargetPlatformVersion = @($BuildProps.Project.PropertyGroup | Where-Object { $_.WindowsTargetPlatformVersion } | Select-Object -First 1).WindowsTargetPlatformVersion - - if ([string]::IsNullOrWhiteSpace($PortableTargetFramework) -or [string]::IsNullOrWhiteSpace($WindowsTargetPlatformVersion)) { - throw "Could not resolve the target framework from src/Directory.Build.props" + $TargetFramework = ( + dotnet msbuild src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj ` + /nologo ` + -getProperty:TargetFramework ` + /p:Configuration=Release ` + /p:Platform=$Platform ` + /p:RuntimeIdentifier=win-$Platform | + Select-Object -Last 1 + ).Trim() + + if ([string]::IsNullOrWhiteSpace($TargetFramework)) { + throw "Could not resolve the Avalonia target framework from MSBuild" } - $TargetFramework = "$PortableTargetFramework-windows$WindowsTargetPlatformVersion" dotnet publish src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj /noLogo /p:Configuration=Release /p:Platform=$Platform -p:RuntimeIdentifier=win-$Platform -v m if ($LASTEXITCODE -ne 0) { throw "dotnet publish Avalonia failed" } From 5208b2f8b769a68ffa480a35a9146f9df81d6a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 18 Jun 2026 10:17:13 -0400 Subject: [PATCH 4/8] Build Avalonia app as UniGetUI executable Set the Avalonia assembly name to UniGetUI so publishing emits UniGetUI.exe directly, remove apphost rename steps, and update Avalonia resource URIs plus E2E manifests for the new assembly name. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/build-release.yml | 4 --- scripts/build.ps1 | 5 ---- src/UniGetUI.Avalonia/App.axaml | 2 +- src/UniGetUI.Avalonia/App.axaml.cs | 2 +- .../Infrastructure/TrayService.cs | 2 +- .../Models/PackageCollections.cs | 4 +-- .../UniGetUI.Avalonia.csproj | 2 +- .../DialogPages/InstallOptionsViewModel.cs | 2 +- .../ManageIgnoredUpdatesViewModel.cs | 2 +- .../DialogPages/OperationViewModel.cs | 10 +++---- .../DialogPages/PackageDetailsViewModel.cs | 2 +- .../SoftwarePages/PackagesPageViewModel.cs | 4 +-- .../Views/Controls/InfoBar.axaml | 2 +- .../Views/Controls/Settings/SettingsCard.cs | 2 +- .../Controls/Settings/SettingsPageButton.cs | 2 +- .../ManageDesktopShortcutsWindow.axaml | 8 ++--- .../ManageIgnoredUpdatesWindow.axaml | 10 +++---- .../DialogPages/PackageDetailsWindow.axaml | 2 +- src/UniGetUI.Avalonia/Views/MainWindow.axaml | 2 +- .../Views/Pages/HelpPage.axaml | 8 ++--- .../SettingsPages/PackageManagerPage.axaml.cs | 2 +- .../SettingsPages/SettingsBasePage.axaml | 2 +- .../SettingsPages/SourceManagerCard.axaml | 6 ++-- src/UniGetUI.Avalonia/Views/SidebarView.axaml | 30 +++++++++---------- .../SoftwarePages/AbstractPackagesPage.axaml | 28 ++++++++--------- .../AbstractPackagesPage.axaml.cs | 4 +-- .../Views/SplashWindow.axaml.cs | 4 +-- .../automation/cli-e2e.manifest.linux.json | 2 +- .../automation/cli-e2e.manifest.windows.json | 2 +- 29 files changed, 74 insertions(+), 83 deletions(-) diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 34b5db6ea1..342732110b 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -201,10 +201,6 @@ jobs: New-Item "unigetui_bin" -ItemType Directory | Out-Null Get-ChildItem $PublishDir | Move-Item -Destination "unigetui_bin" -Force - if (Test-Path "unigetui_bin/UniGetUI.Avalonia.exe") { - Move-Item "unigetui_bin/UniGetUI.Avalonia.exe" "unigetui_bin/UniGetUI.exe" -Force - } - if (-not (Test-Path "unigetui_bin/UniGetUI.exe")) { throw "Windows app host was not produced at unigetui_bin/UniGetUI.exe" } diff --git a/scripts/build.ps1 b/scripts/build.ps1 index f734422345..fe1dbe6ed8 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -88,12 +88,7 @@ New-Item $BinDir -ItemType Directory | Out-Null # Move published output into unigetui_bin Get-ChildItem $PublishDir | Move-Item -Destination $BinDir -Force -$AvaloniaAppHostPath = Join-Path $BinDir "UniGetUI.Avalonia.exe" $WindowsAppHostPath = Join-Path $BinDir "UniGetUI.exe" -if (Test-Path $AvaloniaAppHostPath) { - Move-Item $AvaloniaAppHostPath $WindowsAppHostPath -Force -} - if (-not (Test-Path $WindowsAppHostPath)) { throw "Windows app host was not produced at $WindowsAppHostPath" } diff --git a/src/UniGetUI.Avalonia/App.axaml b/src/UniGetUI.Avalonia/App.axaml index 0090c8aa0b..3b30430900 100644 --- a/src/UniGetUI.Avalonia/App.axaml +++ b/src/UniGetUI.Avalonia/App.axaml @@ -12,6 +12,6 @@ - + diff --git a/src/UniGetUI.Avalonia/App.axaml.cs b/src/UniGetUI.Avalonia/App.axaml.cs index 29bb3200e6..8a1758287c 100644 --- a/src/UniGetUI.Avalonia/App.axaml.cs +++ b/src/UniGetUI.Avalonia/App.axaml.cs @@ -47,7 +47,7 @@ private void ApplyWindowsMicaStyling() { Resources.MergedDictionaries.Add(new ResourceInclude((Uri?)null) { - Source = new Uri("avares://UniGetUI.Avalonia/Assets/Styles/Styles.WindowsMica.axaml") + Source = new Uri("avares://UniGetUI/Assets/Styles/Styles.WindowsMica.axaml") }); // Give flyouts/menus/tooltips a native acrylic backdrop (DWM) so they blur + tint // from behind and adapt to the theme. diff --git a/src/UniGetUI.Avalonia/Infrastructure/TrayService.cs b/src/UniGetUI.Avalonia/Infrastructure/TrayService.cs index ea401d7107..c1311c2f12 100644 --- a/src/UniGetUI.Avalonia/Infrastructure/TrayService.cs +++ b/src/UniGetUI.Avalonia/Infrastructure/TrayService.cs @@ -81,7 +81,7 @@ public void UpdateStatus() bool light = IsTaskbarLight(); string tone = light ? "_black" : "_white"; - string uri = $"avares://UniGetUI.Avalonia/Assets/tray_{status}{tone}_legacy.ico"; + string uri = $"avares://UniGetUI/Assets/tray_{status}{tone}_legacy.ico"; if (_lastIconUri == uri) return; _lastIconUri = uri; diff --git a/src/UniGetUI.Avalonia/Models/PackageCollections.cs b/src/UniGetUI.Avalonia/Models/PackageCollections.cs index 597d48a8fa..eb8e5f286c 100644 --- a/src/UniGetUI.Avalonia/Models/PackageCollections.cs +++ b/src/UniGetUI.Avalonia/Models/PackageCollections.cs @@ -89,7 +89,7 @@ private static string IconTypeToSvgPath(IconType icon) IconType.AddTo => "add_to", _ => icon.ToString().ToLowerInvariant(), }; - return $"avares://UniGetUI.Avalonia/Assets/Symbols/{name}.svg"; + return $"avares://UniGetUI/Assets/Symbols/{name}.svg"; } public PackageWrapper(IPackage package, PackagesPageViewModel page) @@ -282,7 +282,7 @@ private void UpdateDisplayState() }; TagIconVisible = tagName.Length > 0; TagIconPath = TagIconVisible - ? $"avares://UniGetUI.Avalonia/Assets/Symbols/{tagName}.svg" + ? $"avares://UniGetUI/Assets/Symbols/{tagName}.svg" : ""; } diff --git a/src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj b/src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj index 9ff3118b8f..232496a12b 100644 --- a/src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj +++ b/src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj @@ -17,7 +17,7 @@ WinExe UniGetUI.Avalonia - UniGetUI.Avalonia + UniGetUI ..\SharedAssets\icon.ico true app.manifest diff --git a/src/UniGetUI.Avalonia/ViewModels/DialogPages/InstallOptionsViewModel.cs b/src/UniGetUI.Avalonia/ViewModels/DialogPages/InstallOptionsViewModel.cs index 82d4ab383d..d5f9c6dd0f 100644 --- a/src/UniGetUI.Avalonia/ViewModels/DialogPages/InstallOptionsViewModel.cs +++ b/src/UniGetUI.Avalonia/ViewModels/DialogPages/InstallOptionsViewModel.cs @@ -356,7 +356,7 @@ private async Task RefreshCommandPreviewAsync() private static readonly HttpClient _iconHttp = new(CoreTools.GenericHttpClientParameters); private static readonly Uri _fallbackIconUri = - new("avares://UniGetUI.Avalonia/Assets/package_color.png"); + new("avares://UniGetUI/Assets/package_color.png"); private async Task LoadIconAsync() { diff --git a/src/UniGetUI.Avalonia/ViewModels/DialogPages/ManageIgnoredUpdatesViewModel.cs b/src/UniGetUI.Avalonia/ViewModels/DialogPages/ManageIgnoredUpdatesViewModel.cs index bdd6e8db54..78ac769cc0 100644 --- a/src/UniGetUI.Avalonia/ViewModels/DialogPages/ManageIgnoredUpdatesViewModel.cs +++ b/src/UniGetUI.Avalonia/ViewModels/DialogPages/ManageIgnoredUpdatesViewModel.cs @@ -129,7 +129,7 @@ private static string ResolveManagerIcon(string managerKey) "pacman" => "pacman", _ => "ms_store", }; - return $"avares://UniGetUI.Avalonia/Assets/Symbols/{name}.svg"; + return $"avares://UniGetUI/Assets/Symbols/{name}.svg"; } } diff --git a/src/UniGetUI.Avalonia/ViewModels/DialogPages/OperationViewModel.cs b/src/UniGetUI.Avalonia/ViewModels/DialogPages/OperationViewModel.cs index 84b6b7c126..3dd727de64 100644 --- a/src/UniGetUI.Avalonia/ViewModels/DialogPages/OperationViewModel.cs +++ b/src/UniGetUI.Avalonia/ViewModels/DialogPages/OperationViewModel.cs @@ -54,7 +54,7 @@ public sealed partial class OperationViewModel : ViewModelBase [ObservableProperty] private IImage? _packageIcon; private static readonly Uri _fallbackIconUri = - new("avares://UniGetUI.Avalonia/Assets/package_color.png"); + new("avares://UniGetUI/Assets/package_color.png"); public OperationViewModel(AbstractOperation operation) { @@ -86,21 +86,21 @@ public OperationViewModel(AbstractOperation operation) if (badges.AsAdministrator) Badges.Add(new( CoreTools.Translate("Administrator privileges"), - "avares://UniGetUI.Avalonia/Assets/Symbols/uac.svg", + "avares://UniGetUI/Assets/Symbols/uac.svg", CoreTools.Translate("This operation is running with administrator privileges."), "" )); if (badges.Interactive) Badges.Add(new( CoreTools.Translate("Interactive operation"), - "avares://UniGetUI.Avalonia/Assets/Symbols/interactive.svg", + "avares://UniGetUI/Assets/Symbols/interactive.svg", CoreTools.Translate("This operation is running interactively."), CoreTools.Translate("You will likely need to interact with the installer.") )); if (badges.SkipHashCheck) Badges.Add(new( CoreTools.Translate("Integrity checks skipped"), - "avares://UniGetUI.Avalonia/Assets/Symbols/checksum.svg", + "avares://UniGetUI/Assets/Symbols/checksum.svg", CoreTools.Translate("Integrity checks will not be performed during this operation."), CoreTools.Translate("Proceed at your own risk.") )); @@ -286,7 +286,7 @@ private static MenuItem Item(string translationKey, string svgName, bool enabled Command = new SyncCommand(action), Icon = new SvgIcon { - Path = $"avares://UniGetUI.Avalonia/Assets/Symbols/{svgName}", + Path = $"avares://UniGetUI/Assets/Symbols/{svgName}", Width = 16, Height = 16, }, diff --git a/src/UniGetUI.Avalonia/ViewModels/DialogPages/PackageDetailsViewModel.cs b/src/UniGetUI.Avalonia/ViewModels/DialogPages/PackageDetailsViewModel.cs index 24ca94c93b..5fb3020820 100644 --- a/src/UniGetUI.Avalonia/ViewModels/DialogPages/PackageDetailsViewModel.cs +++ b/src/UniGetUI.Avalonia/ViewModels/DialogPages/PackageDetailsViewModel.cs @@ -319,7 +319,7 @@ private async Task LoadIconAsync() try { using var stream = AssetLoader.Open( - new Uri("avares://UniGetUI.Avalonia/Assets/package_color.png")); + new Uri("avares://UniGetUI/Assets/package_color.png")); PackageIcon = new Bitmap(stream); } catch { } diff --git a/src/UniGetUI.Avalonia/ViewModels/SoftwarePages/PackagesPageViewModel.cs b/src/UniGetUI.Avalonia/ViewModels/SoftwarePages/PackagesPageViewModel.cs index 4d37388618..b306d878a3 100644 --- a/src/UniGetUI.Avalonia/ViewModels/SoftwarePages/PackagesPageViewModel.cs +++ b/src/UniGetUI.Avalonia/ViewModels/SoftwarePages/PackagesPageViewModel.cs @@ -208,7 +208,7 @@ public PackagesPageViewModel(PackagesPageData data) { PageName = data.PageName; PageTitle = data.PageTitle; - PageIconPath = $"avares://UniGetUI.Avalonia/Assets/Symbols/{data.IconName}.svg"; + PageIconPath = $"avares://UniGetUI/Assets/Symbols/{data.IconName}.svg"; DisableFilterOnQueryChange = data.DisableFilterOnQueryChange; MegaQueryBoxEnabled = data.MegaQueryBlockEnabled; DisableReload = data.DisableReload; @@ -275,7 +275,7 @@ public Button AddToolbarButton(string svgName, string label, Action onClick, boo { var icon = new SvgIcon { - Path = $"avares://UniGetUI.Avalonia/Assets/Symbols/{svgName}.svg", + Path = $"avares://UniGetUI/Assets/Symbols/{svgName}.svg", Width = 20, Height = 20, VerticalAlignment = VerticalAlignment.Center, diff --git a/src/UniGetUI.Avalonia/Views/Controls/InfoBar.axaml b/src/UniGetUI.Avalonia/Views/Controls/InfoBar.axaml index 01fca7b2c2..a4f33a00a7 100644 --- a/src/UniGetUI.Avalonia/Views/Controls/InfoBar.axaml +++ b/src/UniGetUI.Avalonia/Views/Controls/InfoBar.axaml @@ -86,7 +86,7 @@ Background="Transparent" BorderThickness="0" CornerRadius="4"> - diff --git a/src/UniGetUI.Avalonia/Views/Controls/Settings/SettingsCard.cs b/src/UniGetUI.Avalonia/Views/Controls/Settings/SettingsCard.cs index 4f1f7e48f4..97bbba8caa 100644 --- a/src/UniGetUI.Avalonia/Views/Controls/Settings/SettingsCard.cs +++ b/src/UniGetUI.Avalonia/Views/Controls/Settings/SettingsCard.cs @@ -181,7 +181,7 @@ public SettingsCard() _chevron = new SvgIcon { - Path = "avares://UniGetUI.Avalonia/Assets/Symbols/forward.svg", + Path = "avares://UniGetUI/Assets/Symbols/forward.svg", Width = 16, Height = 16, VerticalAlignment = VerticalAlignment.Center, diff --git a/src/UniGetUI.Avalonia/Views/Controls/Settings/SettingsPageButton.cs b/src/UniGetUI.Avalonia/Views/Controls/Settings/SettingsPageButton.cs index 34978f3934..0c772f6d0d 100644 --- a/src/UniGetUI.Avalonia/Views/Controls/Settings/SettingsPageButton.cs +++ b/src/UniGetUI.Avalonia/Views/Controls/Settings/SettingsPageButton.cs @@ -59,6 +59,6 @@ private static string IconTypeToPath(IconType icon) IconType.ClipboardList => "clipboard_list", _ => icon.ToString().ToLower(), }; - return $"avares://UniGetUI.Avalonia/Assets/Symbols/{name}.svg"; + return $"avares://UniGetUI/Assets/Symbols/{name}.svg"; } } diff --git a/src/UniGetUI.Avalonia/Views/DialogPages/ManageDesktopShortcutsWindow.axaml b/src/UniGetUI.Avalonia/Views/DialogPages/ManageDesktopShortcutsWindow.axaml index bbd878834c..69dd3430b5 100644 --- a/src/UniGetUI.Avalonia/Views/DialogPages/ManageDesktopShortcutsWindow.axaml +++ b/src/UniGetUI.Avalonia/Views/DialogPages/ManageDesktopShortcutsWindow.axaml @@ -127,7 +127,7 @@ - - - @@ -183,7 +183,7 @@ Command="{Binding RemoveCommand}" ToolTip.Tip="{t:Translate Text='Remove from list'}" automation:AutomationProperties.Name="{t:Translate Text='Remove from list'}"> - diff --git a/src/UniGetUI.Avalonia/Views/DialogPages/ManageIgnoredUpdatesWindow.axaml b/src/UniGetUI.Avalonia/Views/DialogPages/ManageIgnoredUpdatesWindow.axaml index c7e8182267..6cc3040631 100644 --- a/src/UniGetUI.Avalonia/Views/DialogPages/ManageIgnoredUpdatesWindow.axaml +++ b/src/UniGetUI.Avalonia/Views/DialogPages/ManageIgnoredUpdatesWindow.axaml @@ -120,7 +120,7 @@ - @@ -136,7 +136,7 @@ - @@ -152,7 +152,7 @@ - @@ -168,7 +168,7 @@ - @@ -206,7 +206,7 @@ Command="{Binding RemoveCommand}" automation:AutomationProperties.Name="{Binding RemoveAutomationName}" ToolTip.Tip="{t:Translate Text='Remove from list'}"> - diff --git a/src/UniGetUI.Avalonia/Views/DialogPages/PackageDetailsWindow.axaml b/src/UniGetUI.Avalonia/Views/DialogPages/PackageDetailsWindow.axaml index de84f78426..e10b117dc4 100644 --- a/src/UniGetUI.Avalonia/Views/DialogPages/PackageDetailsWindow.axaml +++ b/src/UniGetUI.Avalonia/Views/DialogPages/PackageDetailsWindow.axaml @@ -218,7 +218,7 @@ - - diff --git a/src/UniGetUI.Avalonia/Views/Pages/HelpPage.axaml b/src/UniGetUI.Avalonia/Views/Pages/HelpPage.axaml index 61c46b61b4..53e02c0bcc 100644 --- a/src/UniGetUI.Avalonia/Views/Pages/HelpPage.axaml +++ b/src/UniGetUI.Avalonia/Views/Pages/HelpPage.axaml @@ -42,7 +42,7 @@ CornerRadius="4,0,0,4" automation:AutomationProperties.Name="{t:Translate Go back}" Click="BackButton_Click"> - @@ -53,7 +53,7 @@ CornerRadius="0" automation:AutomationProperties.Name="{t:Translate Go forward}" Click="ForwardButton_Click"> - @@ -63,7 +63,7 @@ CornerRadius="0" automation:AutomationProperties.Name="{t:Translate Go to home page}" Click="HomeButton_Click"> - @@ -73,7 +73,7 @@ CornerRadius="0,4,4,0" automation:AutomationProperties.Name="{t:Translate Reload page}" Click="ReloadButton_Click"> - diff --git a/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/PackageManagerPage.axaml.cs b/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/PackageManagerPage.axaml.cs index 5f0c54e948..4a843de006 100644 --- a/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/PackageManagerPage.axaml.cs +++ b/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/PackageManagerPage.axaml.cs @@ -184,7 +184,7 @@ private void BuildPage() // ── Current path card var copyIcon = new SvgIcon { - Path = "avares://UniGetUI.Avalonia/Assets/Symbols/copy.svg", + Path = "avares://UniGetUI/Assets/Symbols/copy.svg", Width = 24, Height = 24, }; diff --git a/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/SettingsBasePage.axaml b/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/SettingsBasePage.axaml index 61ab64b61b..3492150602 100644 --- a/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/SettingsBasePage.axaml +++ b/src/UniGetUI.Avalonia/Views/Pages/SettingsPages/SettingsBasePage.axaml @@ -26,7 +26,7 @@ BorderThickness="0" automation:AutomationProperties.Name="{t:Translate Go back}"> + Path="avares://UniGetUI/Assets/Symbols/backward.svg"/> - @@ -73,7 +73,7 @@ Spacing="8" Margin="10,0,4,0" VerticalAlignment="Center"> - - diff --git a/src/UniGetUI.Avalonia/Views/SidebarView.axaml b/src/UniGetUI.Avalonia/Views/SidebarView.axaml index 0ecc44b0a0..7969bed440 100644 --- a/src/UniGetUI.Avalonia/Views/SidebarView.axaml +++ b/src/UniGetUI.Avalonia/Views/SidebarView.axaml @@ -31,7 +31,7 @@ ToolTip.Tip="{t:Translate Discover Packages}"> - + - + - + - + - + @@ -193,7 +193,7 @@ automation:AutomationProperties.AcceleratorKey="Ctrl+6" ToolTip.Tip="{t:Translate Package Managers}"> - + @@ -216,34 +216,34 @@ - + - + - + - + - + - + - + - + - + diff --git a/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml b/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml index f09cc7714a..6f043eb766 100644 --- a/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml +++ b/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml @@ -111,7 +111,7 @@ automation:AutomationProperties.Name="{t:Translate Filters}" IsChecked="{Binding IsFilterPaneOpen, Mode=TwoWay}"> - + @@ -193,7 +193,7 @@ Background="{DynamicResource SettingsCardBackground}"> - + @@ -312,7 +312,7 @@ Background="{DynamicResource SettingsCardBackground}"> - + @@ -344,7 +344,7 @@ Background="{DynamicResource SettingsCardBackground}"> - + @@ -474,7 +474,7 @@ - - @@ -506,7 +506,7 @@ - @@ -520,10 +520,10 @@ - - - - - - - + diff --git a/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml.cs b/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml.cs index 93101467c7..dc3f279e58 100644 --- a/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml.cs +++ b/src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml.cs @@ -211,7 +211,7 @@ protected virtual void WhenShowingContextMenu(IPackage package) { } // ─── Helper: create a 16×16 SvgIcon for use as a menu item icon ─────────── protected static SvgIcon LoadMenuIcon(string svgName) => new() { - Path = $"avares://UniGetUI.Avalonia/Assets/Symbols/{svgName}.svg", + Path = $"avares://UniGetUI/Assets/Symbols/{svgName}.svg", Width = 16, Height = 16, }; @@ -220,7 +220,7 @@ protected virtual void WhenShowingContextMenu(IPackage package) { } /// Sets the icon and text of the primary action button. protected void SetMainButton(string svgName, string label, Action onClick) { - MainToolbarButtonIcon.Path = $"avares://UniGetUI.Avalonia/Assets/Symbols/{svgName}.svg"; + MainToolbarButtonIcon.Path = $"avares://UniGetUI/Assets/Symbols/{svgName}.svg"; MainToolbarButtonText.Text = label; AutomationProperties.SetName(MainToolbarButton, label); MainToolbarButton.Click += (_, _) => onClick(); diff --git a/src/UniGetUI.Avalonia/Views/SplashWindow.axaml.cs b/src/UniGetUI.Avalonia/Views/SplashWindow.axaml.cs index 7913455fff..be6cc8b4ce 100644 --- a/src/UniGetUI.Avalonia/Views/SplashWindow.axaml.cs +++ b/src/UniGetUI.Avalonia/Views/SplashWindow.axaml.cs @@ -17,8 +17,8 @@ public SplashWindow() bool isDark = Application.Current?.ActualThemeVariant == ThemeVariant.Dark; string uri = isDark - ? "avares://UniGetUI.Avalonia/Assets/SplashScreen.theme-dark.png" - : "avares://UniGetUI.Avalonia/Assets/SplashScreen.png"; + ? "avares://UniGetUI/Assets/SplashScreen.theme-dark.png" + : "avares://UniGetUI/Assets/SplashScreen.png"; SplashImage.Source = new Bitmap(AssetLoader.Open(new Uri(uri))); TaglineText.Text = CoreTools.Translate("Package management made easy"); diff --git a/testing/automation/cli-e2e.manifest.linux.json b/testing/automation/cli-e2e.manifest.linux.json index a4a5f0db67..5d20967706 100644 --- a/testing/automation/cli-e2e.manifest.linux.json +++ b/testing/automation/cli-e2e.manifest.linux.json @@ -3,7 +3,7 @@ "daemon": { "kind": "avalonia-dll", "project": "UniGetUI.Avalonia\\UniGetUI.Avalonia.csproj", - "assemblyName": "UniGetUI.Avalonia" + "assemblyName": "UniGetUI" }, "transport": { "kind": "named-pipe", diff --git a/testing/automation/cli-e2e.manifest.windows.json b/testing/automation/cli-e2e.manifest.windows.json index 92bd59bbd9..28329bac9e 100644 --- a/testing/automation/cli-e2e.manifest.windows.json +++ b/testing/automation/cli-e2e.manifest.windows.json @@ -3,7 +3,7 @@ "daemon": { "kind": "avalonia-dll", "project": "UniGetUI.Avalonia\\UniGetUI.Avalonia.csproj", - "assemblyName": "UniGetUI.Avalonia" + "assemblyName": "UniGetUI" }, "transport": { "kind": "named-pipe", From 9c04e60eab789d6efe061ef1676f0e80d4c5c410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 18 Jun 2026 10:30:25 -0400 Subject: [PATCH 5/8] Use UniGetUI executable in macOS packaging Align macOS bundle packaging with the Avalonia assembly now emitting UniGetUI instead of UniGetUI.Avalonia. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/build-release.yml | 2 +- scripts/macos/Info.plist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 342732110b..4ade2fd6fa 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -397,7 +397,7 @@ jobs: set -euo pipefail VERSION='${{ needs.preflight.outputs.package-version }}' APP_BUNDLE="UniGetUI.app" - APP_EXECUTABLE="UniGetUI.Avalonia" + APP_EXECUTABLE="UniGetUI" DRY_RUN='${{ needs.preflight.outputs.dry-run }}' rm -rf "${APP_BUNDLE}" output diff --git a/scripts/macos/Info.plist b/scripts/macos/Info.plist index 978935131f..8500518ee9 100644 --- a/scripts/macos/Info.plist +++ b/scripts/macos/Info.plist @@ -9,7 +9,7 @@ CFBundleDisplayName UniGetUI CFBundleExecutable - UniGetUI.Avalonia + UniGetUI CFBundleIconName From d8e72ad507b8ec290f1e5280ae38898ae2cf4c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 18 Jun 2026 10:59:19 -0400 Subject: [PATCH 6/8] Avoid CI compiler output file lock Run Windows solution build and test paths serially without shared compilation to avoid intermittent CS2012 locks on package manager project outputs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/build-release.yml | 6 ++++-- .github/workflows/dotnet-test.yml | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 4ade2fd6fa..59da2faff7 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -166,11 +166,13 @@ jobs: working-directory: src shell: pwsh run: | + dotnet build-server shutdown # Retry once to handle flaky tests (e.g. TaskRecyclerTests uses Random) - dotnet test UniGetUI.Windows.slnx --no-restore --verbosity q --nologo /p:Platform=x64 + dotnet test UniGetUI.Windows.slnx --no-restore --verbosity q --nologo /p:Platform=x64 /p:UseSharedCompilation=false /m:1 if ($LASTEXITCODE -ne 0) { Write-Host "::warning::First test run failed, retrying..." - dotnet test UniGetUI.Windows.slnx --no-restore --verbosity q --nologo /p:Platform=x64 + dotnet build-server shutdown + dotnet test UniGetUI.Windows.slnx --no-restore --verbosity q --nologo /p:Platform=x64 /p:UseSharedCompilation=false /m:1 if ($LASTEXITCODE -ne 0) { exit 1 } } diff --git a/.github/workflows/dotnet-test.yml b/.github/workflows/dotnet-test.yml index 8fdf55bb38..04a8728697 100644 --- a/.github/workflows/dotnet-test.yml +++ b/.github/workflows/dotnet-test.yml @@ -68,10 +68,12 @@ jobs: - name: Build Avalonia Windows solution working-directory: src - run: dotnet build UniGetUI.Windows.slnx --no-restore --verbosity minimal /p:Platform=x64 + run: | + dotnet build-server shutdown + dotnet build UniGetUI.Windows.slnx --no-restore --verbosity minimal /p:Platform=x64 /p:UseSharedCompilation=false /m:1 - name: Run Tests working-directory: src env: GITHUB_TOKEN: ${{ github.token }} - run: dotnet test UniGetUI.Windows.slnx --no-restore --verbosity q --nologo /p:Platform=x64 + run: dotnet test UniGetUI.Windows.slnx --no-restore --verbosity q --nologo /p:Platform=x64 /p:UseSharedCompilation=false /m:1 From f1718d9af17fc2e84c7bf1c29c3b67a810d69ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 18 Jun 2026 11:10:41 -0400 Subject: [PATCH 7/8] Serialize CLI E2E daemon build Apply the same no-shared-compilation single-process build settings to the headless daemon build to avoid intermittent CS2012 output locks on Windows CI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/cli-headless-e2e.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cli-headless-e2e.yml b/.github/workflows/cli-headless-e2e.yml index 2790476b2c..eb950635cb 100644 --- a/.github/workflows/cli-headless-e2e.yml +++ b/.github/workflows/cli-headless-e2e.yml @@ -83,11 +83,14 @@ jobs: '--configuration', '${{ env.CONFIGURATION }}', '--verbosity', - 'minimal' + 'minimal', + '/p:UseSharedCompilation=false', + '/m:1' ) if ('${{ matrix.daemon_build_args }}') { $args += '${{ matrix.daemon_build_args }}' } + dotnet build-server shutdown dotnet @args - name: Upgrade pip tooling From 197626469cafabcb59e7857c8a216e1ff07a0ebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 18 Jun 2026 11:27:00 -0400 Subject: [PATCH 8/8] Allow slower Windows headless startup in CI Give the Windows CLI E2E daemon more time to load package managers before the named-pipe IPC readiness check. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- testing/automation/run-cli-e2e.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testing/automation/run-cli-e2e.ps1 b/testing/automation/run-cli-e2e.ps1 index 332787f736..ba7c3cd59a 100644 --- a/testing/automation/run-cli-e2e.ps1 +++ b/testing/automation/run-cli-e2e.ps1 @@ -253,6 +253,7 @@ function Get-DaemonCommand { $pipeName = "UniGetUI.CI.$([Guid]::NewGuid().ToString('N'))" $transportArgs = @('--transport', 'named-pipe', '--pipe-name', $pipeName) $daemonExtraArgs = @('--headless', '--ipc-api-transport', 'named-pipe', '--ipc-api-pipe-name', $pipeName) +$daemonStartupTimeoutSeconds = if ($runningOnWindows) { 300 } else { 120 } $daemonCommand = Get-DaemonCommand $cliCommand = $daemonCommand $process = $null @@ -534,7 +535,7 @@ try { -Arguments @('status') ` -Condition { param($response) $response.running -and $response.transport -eq 'named-pipe' } ` -FailureMessage 'Headless daemon never became ready over named-pipe IPC.' ` - -TimeoutSeconds 120 ` + -TimeoutSeconds $daemonStartupTimeoutSeconds ` -DelaySeconds 2 Write-Stage 'Status and headless transport'