Optimization of PluginInstallerWindow

Refactored repository selection logic. Reduced code duplication and improving maintainability. Added caching for third-party repo list to avoid unnecessary rebuilds. Updated repo filter logic to use normalized URLs for more reliable matching.
This commit is contained in:
Jerric 2025-12-25 15:10:30 -05:00
parent 97748072f5
commit 46a5b8276a

View file

@ -127,11 +127,26 @@ internal class PluginInstallerWindow : Window, IDisposable
private bool adaptiveSort = true; private bool adaptiveSort = true;
private string? selectedRepoUrl = null; // null = All Repositories private string? selectedRepoUrl = null; // null = All Repositories
private List<string> cachedRepoUrls = new(); private string selectedRepoUrlNormalized = string.Empty;
private HashSet<string> cachedRepoUrlsNormalized = new(StringComparer.OrdinalIgnoreCase); private int repoFilterCacheKey = 0;
private readonly List<string> cachedRepoUrls = new();
private readonly HashSet<string> cachedRepoUrlsNormalized = new(StringComparer.OrdinalIgnoreCase);
private const string XivLauncherRepoKey = "XIVLauncher"; private const string XivLauncherRepoKey = "XIVLauncher";
private OperationStatus installStatus = OperationStatus.Idle; private void SetSelectedRepo(string? repoUrl)
{
if (this.selectedRepoUrl == repoUrl)
return;
this.selectedRepoUrl = repoUrl;
this.selectedRepoUrlNormalized = NormalizeRepoUrl(repoUrl);
this.UpdateCategoriesOnPluginsChange();
this.openPluginCollapsibles.Clear();
}
private OperationStatus installStatus = OperationStatus.Idle;
private OperationStatus updateStatus = OperationStatus.Idle; private OperationStatus updateStatus = OperationStatus.Idle;
private OperationStatus enableDisableStatus = OperationStatus.Idle; private OperationStatus enableDisableStatus = OperationStatus.Idle;
@ -817,17 +832,13 @@ internal class PluginInstallerWindow : Window, IDisposable
{ {
if (ImGui.Selectable("All Repositories", this.selectedRepoUrl == null)) if (ImGui.Selectable("All Repositories", this.selectedRepoUrl == null))
{ {
this.selectedRepoUrl = null; this.SetSelectedRepo(null);
this.UpdateCategoriesOnPluginsChange(); }
this.openPluginCollapsibles.Clear();
}
if (ImGui.Selectable("XIVLauncher", this.selectedRepoUrl == XivLauncherRepoKey)) if (ImGui.Selectable("XIVLauncher", this.selectedRepoUrl == XivLauncherRepoKey))
{ {
this.selectedRepoUrl = XivLauncherRepoKey; this.SetSelectedRepo(XivLauncherRepoKey);
this.UpdateCategoriesOnPluginsChange(); }
this.openPluginCollapsibles.Clear();
}
ImGui.Separator(); ImGui.Separator();
@ -839,10 +850,8 @@ internal class PluginInstallerWindow : Window, IDisposable
// Use stable unique IDs to avoid ImGui collisions with long/duplicate URLs. // Use stable unique IDs to avoid ImGui collisions with long/duplicate URLs.
if (ImGui.Selectable($"{repoUrl}##repo_{i}", selected)) if (ImGui.Selectable($"{repoUrl}##repo_{i}", selected))
{ {
this.selectedRepoUrl = repoUrl; this.SetSelectedRepo(repoUrl);
this.UpdateCategoriesOnPluginsChange(); }
this.openPluginCollapsibles.Clear();
}
} }
ImGui.EndCombo(); ImGui.EndCombo();
@ -4067,7 +4076,25 @@ internal class PluginInstallerWindow : Window, IDisposable
{ {
var config = Service<DalamudConfiguration>.Get(); var config = Service<DalamudConfiguration>.Get();
this.cachedRepoUrls.Clear(); // Avoid rebuilding every frame unless the repo list actually changed.
var newKey = 17;
unchecked
{
foreach (var repo in config.ThirdRepoList)
{
if (string.IsNullOrWhiteSpace(repo.Url))
continue;
newKey = (newKey * 31) + StringComparer.OrdinalIgnoreCase.GetHashCode(repo.Url.Trim());
}
}
if (newKey == this.repoFilterCacheKey)
return;
this.repoFilterCacheKey = newKey;
this.cachedRepoUrls.Clear();
this.cachedRepoUrlsNormalized.Clear(); this.cachedRepoUrlsNormalized.Clear();
// Main repo is represented by the "XIVLauncher" entry; here we only list third-party repos. // Main repo is represented by the "XIVLauncher" entry; here we only list third-party repos.
@ -4131,7 +4158,7 @@ internal class PluginInstallerWindow : Window, IDisposable
} }
private static string? GetRepoFilterUrl(RemotePluginManifest manifest) private static string? GetRepoFilterUrl(RemotePluginManifest manifest)
{ {
// For available manifests, RepoUrl is often the plugin's project URL (or null). // For available manifests, RepoUrl is often the plugin's project URL (or null).
// The repository identity we care about is the *source repo* (pluginmaster URL) vs XIVLauncher (main repo). // The repository identity we care about is the *source repo* (pluginmaster URL) vs XIVLauncher (main repo).
@ -4141,7 +4168,7 @@ internal class PluginInstallerWindow : Window, IDisposable
return PluginRepository.MainRepoUrl; return PluginRepository.MainRepoUrl;
} }
private static string? GetRepoFilterUrl(LocalPlugin plugin) private static string? GetRepoFilterUrl(LocalPlugin plugin)
{ {
// Installed third-party plugins store their origin in InstalledFromUrl. // Installed third-party plugins store their origin in InstalledFromUrl.
if (plugin.IsThirdParty) if (plugin.IsThirdParty)
@ -4168,15 +4195,15 @@ internal class PluginInstallerWindow : Window, IDisposable
return true; return true;
// Anything not matching the configured third-party repos is considered "XIVLauncher". // Anything not matching the configured third-party repos is considered "XIVLauncher".
return !this.cachedRepoUrlsNormalized.Any(u => RepoUrlMatches(u, normalized) || string.Equals(u, normalized, StringComparison.OrdinalIgnoreCase)); return !this.cachedRepoUrlsNormalized.Contains(normalized);
} }
return RepoUrlMatches(repoUrl, this.selectedRepoUrl); return RepoUrlMatches(repoUrl, this.selectedRepoUrlNormalized);
} }
private bool PassesRepoFilter(RemotePluginManifest manifest) => this.PassesRepoFilter(GetRepoFilterUrl(manifest)); private bool PassesRepoFilter(RemotePluginManifest manifest) => this.PassesRepoFilter(GetRepoFilterUrl(manifest));
private void DrawFontawesomeIconOutlined(FontAwesomeIcon icon, Vector4 outline, Vector4 iconColor) private void DrawFontawesomeIconOutlined(FontAwesomeIcon icon, Vector4 outline, Vector4 iconColor)
{ {
var positionOffset = ImGuiHelpers.ScaledVector2(0.0f, 1.0f); var positionOffset = ImGuiHelpers.ScaledVector2(0.0f, 1.0f);
var cursorStart = ImGui.GetCursorPos() + positionOffset; var cursorStart = ImGui.GetCursorPos() + positionOffset;