Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/Languages/lang_en.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@
"Enable Software Catalog in Experimental settings and developer options, then restart UniGetUI.": "Enable Software Catalog in Experimental settings and developer options, then restart UniGetUI.",
"The software catalog could not be loaded. Ask an administrator to check {0}, then try again.": "The software catalog could not be loaded. Ask an administrator to check {0}, then try again.",
"Edit software catalog": "Edit software catalog",
"Back to {0}": "Back to {0}",
"Create a catalog": "Create a catalog",
"Create a new catalog or open a catalog file": "Create a new catalog or open a catalog file",
"Enable the Catalog Editor": "Enable the Catalog Editor",
"Catalog Editor is disabled": "Catalog Editor is disabled",
"Enable the Catalog Editor in Experimental settings and developer options, then restart UniGetUI.": "Enable the Catalog Editor in Experimental settings and developer options, then restart UniGetUI.",
"Software Catalog": "Software Catalog",
"Software selected for you, ready to install.": "Software selected for you, ready to install.",
"Refresh software catalog": "Refresh software catalog",
Expand Down
13 changes: 10 additions & 3 deletions src/UniGetUI.Avalonia/Infrastructure/AvaloniaBootstrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,14 @@ private static IpcAppInfo GetAppInfo()
CanNavigate = window is not null,
CanQuit = true,
CurrentPage = window is null ? "" : IpcAppPages.ToPageName(window.CurrentPage.ToString()),
SupportedPages = window is { SoftwareCatalogEnabled: true }
? IpcAppPages.SupportedPages
: IpcAppPages.SupportedPages.Where(page => page is not ("catalog" or "catalog-editor")).ToArray(),
SupportedPages = IpcAppPages.SupportedPages
.Where(page => page switch
{
"catalog" => window is { SoftwareCatalogEnabled: true },
"catalog-editor" => window is { CatalogEditorEnabled: true },
_ => true,
})
.ToArray(),
SystemRestartPending = PendingRebootStore.HasPending,
SystemRestartPendingPackages = PendingRebootStore.PendingCount,
};
Expand Down Expand Up @@ -298,6 +303,8 @@ private static IpcCommandResult NavigateApp(IpcAppNavigateRequest request)
case "catalog-editor":
if (!window.SoftwareCatalogEnabled)
throw new InvalidOperationException("Software Catalog is disabled. Enable it in Experimental settings and developer options, then restart UniGetUI.");
if (page == "catalog-editor" && !window.CatalogEditorEnabled)
throw new InvalidOperationException("Catalog Editor is disabled. Enable it in Experimental settings and developer options, then restart UniGetUI.");
window.Navigate(page == "catalog-editor" ? PageType.CatalogEditor : PageType.Catalog);
break;
case "settings":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace UniGetUI.Avalonia.Infrastructure;
internal static class NavigationPageOrder
{
public static PageType[] DefaultPages(bool catalogEnabled) => catalogEnabled
? [PageType.Catalog, PageType.Discover, PageType.Updates, PageType.Installed, PageType.Bundles, PageType.CatalogEditor]
? [PageType.Catalog, PageType.Discover, PageType.Updates, PageType.Installed, PageType.Bundles]
: [PageType.Discover, PageType.Updates, PageType.Installed, PageType.Bundles];

public static PageType[] Get(bool catalogEnabled)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public partial class CatalogEditorViewModel : ViewModelBase
private string _query = "";

[ObservableProperty]
private string _filePath = SoftwareCatalog.FilePath;
private string _filePath = "";

private string _message = "";

Expand Down Expand Up @@ -110,14 +110,15 @@ public Task<bool> EnsureLoadedAsync()
return _initialLoad ??= LoadOrStartAsync(FilePath);
}

// No catalog deployed yet: start an empty document that saves to that path, so a first
// catalog can be created there instead of the editor opening disabled.
// The editor starts on an untitled, empty document rather than the deployed catalog, so nobody
// edits what this device installs from by accident; New catalog or Open file is the first step.
// A path set beforehand that does not exist yet also starts empty, and saves there.
private Task<bool> LoadOrStartAsync(string path)
{
if (!IsMissing(path)) return LoadAsync(path);
if (path.Length > 0 && !IsMissing(path)) return LoadAsync(path);
SelectedCatalog = null;
Catalogs.Clear();
FilePath = Path.GetFullPath(path);
FilePath = path.Length > 0 ? Path.GetFullPath(path) : "";
_savedCatalogs = [];
IsLoaded = true;
IsDirty = false;
Expand Down
20 changes: 18 additions & 2 deletions src/UniGetUI.Avalonia/ViewModels/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,13 +172,13 @@
private void ToggleOperationsPanel() => OperationsPanelExpanded = !OperationsPanelExpanded;

[RelayCommand]
private void RetryFailedOperations() => AvaloniaOperationRegistry.RetryFailed();

Check warning on line 175 in src/UniGetUI.Avalonia/ViewModels/MainWindowViewModel.cs

View workflow job for this annotation

GitHub Actions / test-codebase

Member 'RetryFailedOperations' does not access instance data and can be marked as static (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1822)

[RelayCommand]
private void ClearSuccessfulOperations() => AvaloniaOperationRegistry.ClearSuccessful();

Check warning on line 178 in src/UniGetUI.Avalonia/ViewModels/MainWindowViewModel.cs

View workflow job for this annotation

GitHub Actions / test-codebase

Member 'ClearSuccessfulOperations' does not access instance data and can be marked as static (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1822)

[RelayCommand]
private void ClearFinishedOperations() => AvaloniaOperationRegistry.ClearFinished();

Check warning on line 181 in src/UniGetUI.Avalonia/ViewModels/MainWindowViewModel.cs

View workflow job for this annotation

GitHub Actions / test-codebase

Member 'ClearFinishedOperations' does not access instance data and can be marked as static (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1822)

[RelayCommand]
private void CancelAllOperations() => AvaloniaOperationRegistry.CancelAll();
Expand Down Expand Up @@ -433,7 +433,7 @@
RegisterBannerToast(TelemetryWarner);
RegisterBannerToast(PortableImportBanner);

CatalogEditor = Sidebar.SoftwareCatalogEnabled ? new CatalogEditorViewModel() : null;
CatalogEditor = Sidebar.CatalogEditorEnabled ? new CatalogEditorViewModel() : null;
DiscoverPage = new DiscoverSoftwarePage(CatalogEditor);
UpdatesPage = new SoftwareUpdatesPage();
InstalledPage = new InstalledPackagesPage();
Expand Down Expand Up @@ -669,7 +669,7 @@
"installed" => PageType.Installed,
"bundles" => PageType.Bundles,
"catalog" when Sidebar.SoftwareCatalogEnabled => PageType.Catalog,
"catalog-editor" when Sidebar.SoftwareCatalogEnabled => PageType.CatalogEditor,
"catalog-editor" when Sidebar.CatalogEditorEnabled => PageType.CatalogEditor,
"settings" => PageType.Settings,
// A usable admin-curated catalog is the simplest starting point, so it wins over the default pages.
_ when Sidebar.HasUsableCatalog => PageType.Catalog,
Expand Down Expand Up @@ -743,6 +743,15 @@
MainWindow.RuntimeNotificationLevel.Error);
return false;
}
if (newPage_t is PageType.CatalogEditor && !Sidebar.CatalogEditorEnabled)
{
Logger.Warn("Catalog Editor navigation was rejected because the editor is disabled.");
MainWindow.Instance?.ShowBanner(
CoreTools.Translate("Catalog Editor is disabled"),
CoreTools.Translate("Enable the Catalog Editor in Experimental settings and developer options, then restart UniGetUI."),
MainWindow.RuntimeNotificationLevel.Error);
return false;
}
if (newPage_t is PageType.About) { _ = ShowAboutDialog(); return true; }
if (newPage_t is PageType.Quit) { MainWindow.Instance?.QuitApplication(); return true; }

Expand Down Expand Up @@ -840,6 +849,13 @@
_ => CoreTools.Translate("UniGetUI"),
};

/// <summary>Opens the Catalog Editor on a new catalog file, for the Software Catalog's New catalog actions.</summary>
public async Task StartNewCatalogAsync()
{
if (!await NavigateToAsync(PageType.CatalogEditor)) return;
if (_catalogEditorPage is { } editor) await editor.StartNewCatalogAsync();
}

public void NavigateBack() => _ = NavigateBackAsync();

public async Task<bool> NavigateBackAsync(CancellationToken cancellationToken = default)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using UniGetUI.Avalonia.ViewModels;
using CoreSettings = global::UniGetUI.Core.SettingsEngine.Settings;

namespace UniGetUI.Avalonia.ViewModels.Pages.SettingsPages;

public partial class ExperimentalViewModel : ViewModelBase
{
public bool IsWindows { get; } = OperatingSystem.IsWindows();

// The Catalog Editor is a sub-option of the Software Catalog.
[ObservableProperty]
private bool _isSoftwareCatalogEnabled = CoreSettings.Get(CoreSettings.K.EnableSoftwareCatalog);

public event EventHandler? RestartRequired;

[RelayCommand]
private void ShowRestartRequired() => RestartRequired?.Invoke(this, EventArgs.Empty);

[RelayCommand]
private void SoftwareCatalogChanged()
{
IsSoftwareCatalogEnabled = CoreSettings.Get(CoreSettings.K.EnableSoftwareCatalog);
ShowRestartRequired();
}
}
4 changes: 4 additions & 0 deletions src/UniGetUI.Avalonia/ViewModels/SidebarViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ public partial class SidebarViewModel : ViewModelBase
{
public bool SoftwareCatalogEnabled { get; } = Settings.Get(Settings.K.EnableSoftwareCatalog);

// An administrator tool, opened from the catalog page rather than the sidebar; off unless asked for.
public bool CatalogEditorEnabled { get; } =
Settings.Get(Settings.K.EnableSoftwareCatalog) && Settings.Get(Settings.K.EnableCatalogEditor);

// Only a catalog with packages to offer becomes the startup page; an unset or broken one would
// open the app on an empty page. Checked once at startup, like the setting itself.
public bool HasUsableCatalog { get; } =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ partial void OnIsFilterPaneOpenChanged(bool value)
public bool SimilarSearchEnabled { get; private set; }
public bool InstallerHostColumnVisible { get; }
public bool DownloadSizeColumnVisible { get; }
public readonly string NoPackagesText;
// Settable so a page can say why it is empty; takes effect on the next list update.
public string NoPackagesText { get; set; }
public readonly string NoMatchesText;
public readonly string SearchBoxPlaceholder;
private readonly string _noPackagesSubtitleBase;
Expand All @@ -170,6 +171,19 @@ partial void OnIsFilterPaneOpenChanged(bool value)
[ObservableProperty] private string _pageIconPath = "";
[ObservableProperty] private string _subtitle = "";

// A page opened from another one (the Catalog Editor from the Software Catalog) links back to it above its title.
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(HasParentPage))]
[NotifyPropertyChangedFor(nameof(ParentPageLinkName))]
private string _parentPageTitle = "";

public bool HasParentPage => ParentPageTitle.Length > 0;
public string ParentPageLinkName => CoreTools.Translate("Back to {0}", ParentPageTitle);
public Action? ParentPageRequested { get; set; }

[RelayCommand]
private void GoToParentPage() => ParentPageRequested?.Invoke();

// Shown after the subtitle in a secondary tone: when the list was last checked, then the page suffix.
[ObservableProperty] private string _subtitleDetail = "";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@
OnPropertyChanged(nameof(BackgroundText));
OnPropertyChanged(nameof(HasBackgroundText));
OnPropertyChanged(nameof(HasBackgroundDetail));
OnPropertyChanged(nameof(ShowCreateCatalog));
}

private bool IsSelectedByFilters(CatalogTileViewModel tile) =>
Expand Down Expand Up @@ -267,22 +268,21 @@
value, global::Avalonia.Automation.AutomationLiveSetting.Assertive);
}

// Administrators who enabled the Catalog Editor can start a catalog from here; everyone else only installs.
public bool IsEditorEnabled { get; } =
Settings.Get(Settings.K.EnableSoftwareCatalog) && Settings.Get(Settings.K.EnableCatalogEditor);

// On a device with no catalog yet, the empty page offers to create one.
public bool ShowCreateCatalog => IsEditorEnabled && _notConfigured && IsEmpty;

[RelayCommand]
private void OpenEditor() => MainWindow.Instance?.Navigate(PageType.CatalogEditor);

Check warning on line 279 in src/UniGetUI.Avalonia/ViewModels/SoftwarePages/SoftwareCatalogViewModel.cs

View workflow job for this annotation

GitHub Actions / test-codebase

Member 'OpenEditor' does not access instance data and can be marked as static (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1822)

[RelayCommand]
private async Task EditAsync()
private async Task NewCatalogAsync()

Check warning on line 282 in src/UniGetUI.Avalonia/ViewModels/SoftwarePages/SoftwareCatalogViewModel.cs

View workflow job for this annotation

GitHub Actions / test-codebase

Member 'NewCatalogAsync' does not access instance data and can be marked as static (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1822)
{
try
{
var owner = MainWindow.Instance
?? throw new InvalidOperationException("The catalog editor requires an application window.");
owner.Navigate(PageType.CatalogEditor);
await Task.CompletedTask;
}
catch (Exception ex)
{
Logger.Error("Could not open the software catalog editor.");
Logger.Error(ex);
ErrorMessage = CoreTools.Translate("The catalog editor could not be opened: {0}", ex.Message);
}
if (MainWindow.Instance?.DataContext is MainWindowViewModel main)
await main.StartNewCatalogAsync();
}

private bool _waitingForManagers;
Expand Down Expand Up @@ -609,7 +609,7 @@
if (SortField == field) SortAscending = !SortAscending;
else
{
_sortAscending = true;

Check warning on line 612 in src/UniGetUI.Avalonia/ViewModels/SoftwarePages/SoftwareCatalogViewModel.cs

View workflow job for this annotation

GitHub Actions / test-codebase

The field UniGetUI.Avalonia.ViewModels.Pages.SoftwareCatalogViewModel._sortAscending is annotated with
SaveSortDirection(true);
OnPropertyChanged(nameof(SortAscending));
SortField = field;
Expand Down
1 change: 1 addition & 0 deletions src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ public enum RuntimeNotificationLevel
private MainWindowViewModel ViewModel => (MainWindowViewModel)DataContext!;
public PageType CurrentPage => ViewModel.CurrentPage_t;
public bool SoftwareCatalogEnabled => ViewModel.Sidebar.SoftwareCatalogEnabled;
public bool CatalogEditorEnabled => ViewModel.Sidebar.CatalogEditorEnabled;

public MainWindow()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,15 @@
<settings:CheckboxCard x:Name="SoftwareCatalogCard" SettingName="EnableSoftwareCatalog"
Text="{t:Translate Enable Software Catalog}"
WarningText="{t:Translate Requires a machine-wide SoftwareCatalog.json file and an application restart.}"
StateChangedCommand="{Binding SoftwareCatalogChangedCommand}"
CornerRadius="8,8,0,0"/>

<settings:CheckboxCard x:Name="CatalogEditorCard" SettingName="EnableCatalogEditor"
Text="{t:Translate Enable the Catalog Editor}"
StateChangedCommand="{Binding ShowRestartRequiredCommand}"
CornerRadius="8"/>
IsEnabled="{Binding IsSoftwareCatalogEnabled}"
BorderThickness="1,0,1,1"
CornerRadius="0,0,8,8"/>

</StackPanel>
</ScrollViewer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public Interface_P()
StartupPageSelector.AddItem(CoreTools.Translate("Software Updates"), "updates");
StartupPageSelector.AddItem(CoreTools.Translate("Installed Packages"), "installed");
StartupPageSelector.AddItem(CoreTools.Translate("Package Bundles"), "bundles");
if (catalogEnabled)
if (catalogEnabled && CoreSettings.Get(CoreSettings.K.EnableCatalogEditor))
StartupPageSelector.AddItem(CoreTools.Translate("Catalog Editor"), "catalog-editor");
StartupPageSelector.AddItem(CoreTools.Translate("Settings"), "settings");
StartupPageSelector.SettingName = CoreSettings.K.StartupPage;
Expand Down
12 changes: 0 additions & 12 deletions src/UniGetUI.Avalonia/Views/SidebarView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -160,18 +160,6 @@
</Panel>
</ListBoxItem>

<ListBoxItem x:Name="CatalogEditorNavBtn" Tag="CatalogEditor" Classes="nav-item" Padding="4,2" CornerRadius="6"
automation:AutomationProperties.Name="{t:Translate Catalog Editor}"
ToolTip.Tip="{t:Translate Catalog Editor}">
<Grid ColumnDefinitions="Auto,*" ColumnSpacing="12" Margin="8,8">
<controls:SvgIcon Path="avares://UniGetUI/Assets/Symbols/catalog_add.svg"
Width="24" Height="24" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="{t:Translate Catalog Editor}" FontSize="14"
VerticalAlignment="Center"
IsVisible="{Binding $parent[local:SidebarView].ShowLabels}"/>
</Grid>
</ListBoxItem>

</ListBox>

<!-- Footer items -->
Expand Down
2 changes: 0 additions & 2 deletions src/UniGetUI.Avalonia/Views/SidebarView.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,6 @@ private void AssignShortcutKeys(PageType[] pages)
PageType.Updates => UpdatesNavBtn,
PageType.Installed => InstalledNavBtn,
PageType.Bundles => BundlesNavBtn,
PageType.CatalogEditor => CatalogEditorNavBtn,
PageType.Settings => SettingsNavBtn,
PageType.Managers => ManagersNavBtn,
_ => null,
Expand All @@ -446,7 +445,6 @@ private void SyncListBoxSelection(PageType page)
PageType.Installed => InstalledNavBtn,
PageType.Bundles => BundlesNavBtn,
PageType.Catalog => CatalogNavBtn,
PageType.CatalogEditor => CatalogEditorNavBtn,
_ => null,
};
FooterNavListBox.SelectedItem = page switch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,22 @@
Path="{Binding PageIconPath}"/>

<StackPanel Grid.Column="1" VerticalAlignment="Center" Spacing="0">
<!-- Link back to the page this one belongs to, e.g. the Catalog Editor's Software Catalog -->
<Button Classes="filter-hyperlink"
IsVisible="{Binding HasParentPage}"
Command="{Binding GoToParentPageCommand}"
HorizontalAlignment="Left"
Padding="2,1"
Margin="-3,0,0,0"
ToolTip.Tip="{Binding ParentPageLinkName}"
automation:AutomationProperties.Name="{Binding ParentPageLinkName}">
<StackPanel Orientation="Horizontal" Spacing="4">
<controls:SvgIcon Path="avares://UniGetUI/Assets/Symbols/backward.svg"
Width="12" Height="12" VerticalAlignment="Center"
automation:AutomationProperties.AccessibilityView="Raw"/>
<TextBlock Text="{Binding ParentPageTitle}" FontSize="12" VerticalAlignment="Center"/>
</StackPanel>
</Button>
<TextBlock x:Name="MainTitle"
MaxHeight="60"
HorizontalAlignment="Left"
Expand Down
Loading
Loading