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
82 changes: 82 additions & 0 deletions src/UniGetUI.Avalonia/Infrastructure/MemoryTrimmer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Avalonia.Threading;
using UniGetUI.Core.Logging;

namespace UniGetUI.Avalonia.Infrastructure;

// Collects and returns the working set to the OS once loading has settled — neither .NET nor the
// native allocator hand freed memory back on their own. Debounced so it never runs mid-load.
// Windows-only, best-effort.
internal static class MemoryTrimmer
{
private static readonly TimeSpan SettleDelay = TimeSpan.FromSeconds(3);
private static DispatcherTimer? _timer;

public static void RequestTrimAfterIdle()
{
if (!OperatingSystem.IsWindows()) return;
if (!Dispatcher.UIThread.CheckAccess())
{
Dispatcher.UIThread.Post(RequestTrimAfterIdle);
return;
}

_timer ??= CreateTimer();
_timer.Stop();
_timer.Start();
}

public static void CancelPending()
{
if (!OperatingSystem.IsWindows()) return;
if (!Dispatcher.UIThread.CheckAccess())
{
Dispatcher.UIThread.Post(CancelPending);
return;
}

_timer?.Stop();
}

private static DispatcherTimer CreateTimer()
{
var timer = new DispatcherTimer { Interval = SettleDelay };
timer.Tick += (_, _) =>
{
timer.Stop();
Trim();
};
return timer;
}

private static void Trim() => _ = Task.Run(() =>
{
try
{
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, blocking: true);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, blocking: true);
NativeMethods.SetProcessWorkingSetSize(NativeMethods.GetCurrentProcess(), -1, -1);
}
catch (Exception ex)
{
Logger.Warn($"Post-load working-set trim failed: {ex.Message}");
}
});

private static class NativeMethods
{
[DllImport("kernel32.dll")]
public static extern nint GetCurrentProcess();

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetProcessWorkingSetSize(
nint hProcess,
nint dwMinimumWorkingSetSize,
nint dwMaximumWorkingSetSize
);
}
}
138 changes: 118 additions & 20 deletions src/UniGetUI.Avalonia/Models/PackageCollections.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Net.Http;
using Avalonia;
using Avalonia.Collections;
using Avalonia.Media.Imaging;
using Avalonia.Threading;
Expand All @@ -27,8 +27,66 @@
{
Timeout = TimeSpan.FromSeconds(8),
};
private static readonly ConcurrentDictionary<long, Bitmap?> _iconCache = new();
private static readonly SemaphoreSlim _iconLoadSemaphore = new(4, 4);
private static readonly SemaphoreSlim _iconLoadSemaphore = new(8, 8);

// Cap decoded icon size; the list shows icons at ≤64px (128 covers 2x DPI).
private const int MaxIconSide = 128;

// Bounded LRU by package hash. Evicted entries aren't disposed: a visible row may still
// reference the bitmap, so dropping it here only makes it GC-eligible.
private const int MaxIconCacheEntries = 512;
private static readonly object _iconCacheLock = new();
private static readonly Dictionary<long, LinkedListNode<(long Hash, Bitmap? Bitmap)>> _iconCache = new();
private static readonly LinkedList<(long Hash, Bitmap? Bitmap)> _iconCacheOrder = new();

private static bool TryGetCachedIcon(long hash, out Bitmap? bitmap)
{
lock (_iconCacheLock)
{
if (_iconCache.TryGetValue(hash, out var node))
{
_iconCacheOrder.Remove(node);
_iconCacheOrder.AddFirst(node);
bitmap = node.Value.Bitmap;
return true;
}
}
bitmap = null;
return false;
}

private static void CacheIcon(long hash, Bitmap? bitmap)
{
lock (_iconCacheLock)
{
if (_iconCache.TryGetValue(hash, out var existing))
{
existing.Value = (hash, bitmap);
_iconCacheOrder.Remove(existing);
_iconCacheOrder.AddFirst(existing);
return;
}

var node = new LinkedListNode<(long, Bitmap?)>((hash, bitmap));
_iconCache[hash] = node;
_iconCacheOrder.AddFirst(node);

while (_iconCache.Count > MaxIconCacheEntries && _iconCacheOrder.Last is { } last)
{
_iconCacheOrder.RemoveLast();
_iconCache.Remove(last.Value.Hash);
}
}
}

public static void ClearIconCache()
{
lock (_iconCacheLock)
{
_iconCache.Clear();
_iconCacheOrder.Clear();
}
}

public IPackage Package { get; }
public PackageWrapper Self => this;
Expand Down Expand Up @@ -73,6 +131,8 @@
public string InstallerHostChangeTooltip { get; private set; } = "";

private CancellationTokenSource? _installerHostCheckCts;
// Cancels this row's queued/in-flight icon load on disposal so it stops rooting the wrapper.
private readonly CancellationTokenSource _lifetimeCts = new();

public string SourceIconPath => IconTypeToSvgPath(Package.Source.IconId);

Expand Down Expand Up @@ -102,19 +162,27 @@
Package.PropertyChanged += Package_PropertyChanged;
UpdateDisplayState();

if (!Settings.Get(Settings.K.DisableIconsOnPackageLists))
_ = LoadIconAsync();

// Icons load lazily per visible row (see EnsureIconLoaded), not eagerly for every result.
MaybeStartInstallerHostCheck();
}

private int _iconLoadStarted;

/// <summary>Loads this row's icon at most once; called when the row becomes visible.</summary>
public void EnsureIconLoaded()
{
if (Settings.Get(Settings.K.DisableIconsOnPackageLists)) return;
if (Interlocked.Exchange(ref _iconLoadStarted, 1) != 0) return;
_ = LoadIconAsync();
}

/// <summary>
/// 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.
/// </summary>
private void MaybeStartInstallerHostCheck()

Check warning on line 185 in src/UniGetUI.Avalonia/Models/PackageCollections.cs

View workflow job for this annotation

GitHub Actions / Linux (Avalonia)

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

Check warning on line 185 in src/UniGetUI.Avalonia/Models/PackageCollections.cs

View workflow job for this annotation

GitHub Actions / Linux (NativeAOT)

Member 'MaybeStartInstallerHostCheck' does not access instance data and can be marked as static (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1822)
{
#if WINDOWS
if (!Package.IsUpgradable) return;
Expand Down Expand Up @@ -183,8 +251,9 @@

private async Task LoadIconAsync()
{
CancellationToken token = _lifetimeCts.Token;
long hash = Package.GetHash();
if (_iconCache.TryGetValue(hash, out Bitmap? cached))
if (TryGetCachedIcon(hash, out Bitmap? cached))
{
if (cached is not null)
IconBitmap = cached;
Expand All @@ -193,12 +262,12 @@

try
{
await _iconLoadSemaphore.WaitAsync().ConfigureAwait(false);
await _iconLoadSemaphore.WaitAsync(token).ConfigureAwait(false);
Bitmap bitmap;
try
{
var uri = await Task.Run(Package.GetIconUrlIfAny).ConfigureAwait(false);
if (uri is null) { _iconCache[hash] = null; return; }
var uri = await Task.Run(Package.GetIconUrlIfAny, token).ConfigureAwait(false);
if (uri is null) { CacheIcon(hash, null); return; }

Bitmap? decoded;
if (uri.IsFile)
Expand All @@ -207,30 +276,35 @@
{
// Avalonia's Bitmap (Skia) can't decode SVG/AVIF/ICO/TIFF — the
// icon cache may produce those. Reject upfront so we don't throw.
_iconCache[hash] = null;
CacheIcon(hash, null);
return;
}
decoded = await Task.Run(() => TryDecodeIcon(uri.LocalPath)).ConfigureAwait(false);
decoded = await Task.Run(() => TryDecodeIcon(uri.LocalPath), token).ConfigureAwait(false);
}
else if (uri.Scheme is "http" or "https")
{
var bytes = await _iconHttpClient.GetByteArrayAsync(uri).ConfigureAwait(false);
var bytes = await _iconHttpClient.GetByteArrayAsync(uri, token).ConfigureAwait(false);
decoded = TryDecodeIcon(bytes, uri.Host);
}
else { _iconCache[hash] = null; return; }
else { CacheIcon(hash, null); return; }

if (decoded is null) { _iconCache[hash] = null; return; }
if (decoded is null) { CacheIcon(hash, null); return; }
bitmap = decoded;
_iconCache[hash] = bitmap;
CacheIcon(hash, bitmap);
}
finally
{
_iconLoadSemaphore.Release();
}

await Dispatcher.UIThread.InvokeAsync(() => IconBitmap = bitmap);
if (token.IsCancellationRequested) return;
await Dispatcher.UIThread.InvokeAsync(() =>
{
if (!token.IsCancellationRequested) IconBitmap = bitmap;
});
}
catch { _iconCache[hash] = null; }
catch (OperationCanceledException) { /* row discarded before its icon finished loading */ }
catch { CacheIcon(hash, null); }
}

// Icons come from a shared on-disk cache that can hold empty or partial entries after an
Expand All @@ -239,18 +313,41 @@
{
var info = new FileInfo(filePath);
if (!info.Exists || info.Length == 0) return null;
return TryDecodeIcon(() => new Bitmap(filePath), filePath);
return TryDecodeIcon(() => { using var fs = File.OpenRead(filePath); return DecodeDownscaled(fs); }, filePath);
}

private static Bitmap? TryDecodeIcon(byte[] bytes, string source)
=> bytes.Length == 0 ? null : TryDecodeIcon(() => new Bitmap(new MemoryStream(bytes)), source);
=> bytes.Length == 0 ? null : TryDecodeIcon(() => { using var ms = new MemoryStream(bytes); return DecodeDownscaled(ms); }, source);

private static Bitmap? TryDecodeIcon(Func<Bitmap> decode, string source)
{
try { return decode(); }
catch (Exception ex) { Logger.Debug($"Discarding undecodable icon '{source}': {ex.Message}"); return null; }
}

// Downscales oversized icons to MaxIconSide; small icons pass through (never upscaled).
private static Bitmap DecodeDownscaled(Stream stream)
{
var bitmap = new Bitmap(stream);
PixelSize size = bitmap.PixelSize;
if (size.Width <= MaxIconSide && size.Height <= MaxIconSide)
return bitmap;

double scale = (double)MaxIconSide / Math.Max(size.Width, size.Height);
var target = new PixelSize(
Math.Max(1, (int)Math.Round(size.Width * scale)),
Math.Max(1, (int)Math.Round(size.Height * scale)));

try
{
return bitmap.CreateScaledBitmap(target, BitmapInterpolationMode.HighQuality);
}
finally
{
bitmap.Dispose();
}
}

private static bool IsSkiaDecodableExtension(string path)
{
string ext = Path.GetExtension(path);
Expand Down Expand Up @@ -314,6 +411,7 @@
_installerHostCheckCts?.Cancel();
_installerHostCheckCts?.Dispose();
_installerHostCheckCts = null;
_lifetimeCts.Cancel(); // not disposed: a background load may still hold the token
}
}

Expand Down
6 changes: 6 additions & 0 deletions src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
<PublishReadyToRun>false</PublishReadyToRun>
</PropertyGroup>

<ItemGroup>
<!-- Let the GC decommit unused heap back to the OS; the app is idle most of the time. -->
<RuntimeHostConfigurationOption Include="System.GC.ConserveMemory" Value="5" Trim="false" />
</ItemGroup>

<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('Windows'))">
<!-- Parameterized windows/base views are directly constructed in code, not through the runtime XAML loader. -->
<NoWarn>$(NoWarn);MVVMTK0045;AVLN3001</NoWarn>
Expand Down Expand Up @@ -229,6 +234,7 @@
<Compile Include="Infrastructure\AppShortcutAumidStamper.cs" />
<Compile Include="Infrastructure\TrayService.cs" />
<Compile Include="Infrastructure\MicaWindowHelper.cs" />
<Compile Include="Infrastructure\MemoryTrimmer.cs" />
<Compile Include="Infrastructure\EntrancePageTransition.cs" />
<Compile Include="Infrastructure\DirectionalSlideTransition.cs" />
<Compile Include="Infrastructure\MacOsNotificationBridge.cs" />
Expand Down
7 changes: 7 additions & 0 deletions src/UniGetUI.Avalonia/ViewModels/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,16 +156,16 @@
if (e.PropertyName == nameof(PackagesPageViewModel.GlobalQueryText) && sender is PackagesPageViewModel vm)
{
_syncingSearch = true;
GlobalSearchText = vm.GlobalQueryText;

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

View workflow job for this annotation

GitHub Actions / Linux (Avalonia)

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

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

View workflow job for this annotation

GitHub Actions / Linux (NativeAOT)

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

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

View workflow job for this annotation

GitHub Actions / Windows (Avalonia)

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

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

View workflow job for this annotation

GitHub Actions / Windows (Avalonia)

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

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

View workflow job for this annotation

GitHub Actions / Windows (NativeAOT)

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

Check warning on line 159 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)

Check warning on line 159 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)

Check warning on line 159 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)

Check warning on line 159 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)
_syncingSearch = false;
}
}

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

View workflow job for this annotation

GitHub Actions / Linux (Avalonia)

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

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

View workflow job for this annotation

GitHub Actions / Linux (NativeAOT)

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

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

View workflow job for this annotation

GitHub Actions / Windows (Avalonia)

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

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

View workflow job for this annotation

GitHub Actions / Windows (Avalonia)

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

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

View workflow job for this annotation

GitHub Actions / Windows (NativeAOT)

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

Check warning on line 162 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)

Check warning on line 162 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)

Check warning on line 162 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)

Check warning on line 162 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)

// ─── Title bar ───────────────────────────────────────────────────────────
// Mirrors WinUI behavior: the version appears next to "UniGetUI" only when

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

View workflow job for this annotation

GitHub Actions / Linux (Avalonia)

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

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

View workflow job for this annotation

GitHub Actions / Linux (NativeAOT)

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

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

View workflow job for this annotation

GitHub Actions / Windows (Avalonia)

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

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

View workflow job for this annotation

GitHub Actions / Windows (Avalonia)

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

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

View workflow job for this annotation

GitHub Actions / Windows (NativeAOT)

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

Check warning on line 165 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)

Check warning on line 165 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)

Check warning on line 165 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)

Check warning on line 165 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)
// the ShowVersionNumberOnTitlebar setting is enabled (the setting is gated
// on restart, so a one-shot read at construction is sufficient).
public string TitleBarText { get; } = Settings.Get(Settings.K.ShowVersionNumberOnTitlebar)

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

View workflow job for this annotation

GitHub Actions / Linux (Avalonia)

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

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

View workflow job for this annotation

GitHub Actions / Linux (NativeAOT)

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

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

View workflow job for this annotation

GitHub Actions / Windows (Avalonia)

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

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

View workflow job for this annotation

GitHub Actions / Windows (Avalonia)

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

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

View workflow job for this annotation

GitHub Actions / Windows (NativeAOT)

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

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

View workflow job for this annotation

GitHub Actions / test-codebase

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

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

View workflow job for this annotation

GitHub Actions / test-codebase

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

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

View workflow job for this annotation

GitHub Actions / test-codebase

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

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

View workflow job for this annotation

GitHub Actions / test-codebase

Member 'CancelAllOperations' does not access instance data and can be marked as static (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1822)
? $"UniGetUI {CoreTools.Translate("version {0}", CoreData.VersionName)}"
: "UniGetUI";

Expand Down Expand Up @@ -199,9 +199,16 @@
if (loader is null) continue;
var pt = pageType;
loader.FinishedLoading += (_, _) =>
{
Dispatcher.UIThread.Post(() => Sidebar.SetNavItemLoading(pt, false));
// Return the load's memory to the OS once it settles.
Infrastructure.MemoryTrimmer.RequestTrimAfterIdle();
};
loader.StartedLoading += (_, _) =>
{
Dispatcher.UIThread.Post(() => Sidebar.SetNavItemLoading(pt, true));
Infrastructure.MemoryTrimmer.CancelPending();
};
Sidebar.SetNavItemLoading(pt, loader.IsLoading);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ private async Task ResetIconCache(Visual? _)
{
try { Directory.Delete(CoreData.UniGetUICacheDirectory_Icons, true); }
catch (Exception ex) { Logger.Error(ex); }
global::UniGetUI.PackageEngine.PackageClasses.PackageWrapper.ClearIconCache();
RestartRequired?.Invoke(this, EventArgs.Empty);
await LoadIconCacheSize();
}
Expand Down
Loading
Loading