From b2b7478d4e11b815432507a7502e7b395d35f41b Mon Sep 17 00:00:00 2001 From: Copilot Date: Fri, 12 Jun 2026 17:08:32 -0500 Subject: [PATCH 1/2] feat(gui): Nexus update badges and check-for-updates menu action Surface ModUpdateCheckService results in the mod list and add a common-menu action so users can refresh Nexus version metadata without touching downloads. --- docs/knowledgebase/README.md | 4 +- docs/knowledgebase/update-checking.md | 7 ++- .../2026-06-14-122-update-badges-plan.md | 37 ++++++++++++ .../vortex-mo2-feature-parity-living-plan.md | 50 ++++++++++++++++ src/ModSync.Core/ModComponent.cs | 25 ++++++++ src/ModSync.GUI/Controls/ModListItem.axaml | 29 +++++++-- .../Services/MenuBuilderService.cs | 60 +++++++++++++++++++ .../ModUpdateCheckServiceTests.cs | 19 ++++++ 8 files changed, 222 insertions(+), 9 deletions(-) create mode 100644 docs/plans/2026-06-14-122-update-badges-plan.md create mode 100644 docs/plans/vortex-mo2-feature-parity-living-plan.md diff --git a/docs/knowledgebase/README.md b/docs/knowledgebase/README.md index 045fe4cb..156f3a88 100644 --- a/docs/knowledgebase/README.md +++ b/docs/knowledgebase/README.md @@ -44,7 +44,7 @@ Use these when citing findings in plans, PRs, or audits: - [Install lifecycle](install-lifecycle.md) — wizard page order, `InstallationService`, checkpoints, widescreen, CLI flags - [Download system](download-system.md) — ResourceRegistry, handler order, `DownloadCacheService`, GUI vs CLI -- [Mod update checking](update-checking.md) — `NexusApiClient`, `ResourceMetadata` version fields, `ModUpdateCheckService` +- [Mod update checking](update-checking.md) — `NexusApiClient`, `ModUpdateCheckService`, mod list update badges, menu check action - [Install profiles](install-profiles.md) — named loadouts, `ProfileService` capture/apply, `ProfileManagerDialog` - [Managed deployment engine](managed-deployment.md) — hardlink deploy, per-component manifests, uninstall/purge (Phase 4 slice 1) - [File-level conflicts](file-conflicts.md) — `FileConflictAnalyzer` dry-run VFS attribution and `ConflictsDialog` results UI @@ -84,7 +84,7 @@ Use these when citing findings in plans, PRs, or audits: ### Plans (implementation history) -- [docs/plans/](../plans/) — dated plans with requirements and status frontmatter +- [docs/plans/](../plans/) — dated plans with requirements and status frontmatter; active parity tracker: [vortex-mo2-feature-parity-living-plan.md](../plans/vortex-mo2-feature-parity-living-plan.md) #### June 2026 arcs (rebrand closure + MainWindow extraction) diff --git a/docs/knowledgebase/update-checking.md b/docs/knowledgebase/update-checking.md index 0a409a3a..210f50ec 100644 --- a/docs/knowledgebase/update-checking.md +++ b/docs/knowledgebase/update-checking.md @@ -1,6 +1,6 @@ # Mod update checking -How ModSync knows whether a tracked Nexus Mods component has a newer version. Core-only as of plan 117; no GUI surface yet. +How ModSync knows whether a tracked Nexus Mods component has a newer version available. ## Components @@ -9,6 +9,9 @@ How ModSync knows whether a tracked Nexus Mods component has a newer version. Co | `NexusApiClient` | `src/ModSync.Core/Services/Download/NexusApiClient.cs` | `[REPO]` Standalone typed client for the Nexus REST API (`/games/{domain}/mods/{id}.json`, `files.json`, `md5_search`, `/users/validate.json`). Handles 429 Retry-After (single retry) and tracks `X-RL-Daily-Remaining` / `X-RL-Hourly-Remaining` as `DailyRemaining` / `HourlyRemaining`. | | `ResourceMetadata` version fields | `src/ModSync.Core/ModComponent.cs` | `[REPO]` `ModVersion` (baseline), `LatestKnownVersion`, `LastUpdateCheck`, `UpdateAvailable` — plain serializable properties on each `ResourceRegistry` entry. | | `ModUpdateCheckService` | `src/ModSync.Core/Services/ModUpdateCheckService.cs` | `[REPO]` Batch checker: extracts `(gameDomain, modId)` from `nexusmods.com/{domain}/mods/{id}` URLs in each component's `ResourceRegistry`, fetches mod info once per unique mod across all components, writes results onto the metadata, stops early when the rate-limit budget hits zero. | +| `ModComponent.NexusUpdateAvailable` | `src/ModSync.Core/ModComponent.cs` | `[REPO]` Computed flag for list binding; call `NotifyNexusUpdateStateChanged()` after mutating metadata. | +| Mod list badge | `src/ModSync.GUI/Controls/ModListItem.axaml` | `[REPO]` "Update" badge when `NexusUpdateAvailable` is true. | +| Menu action | `src/ModSync.GUI/Services/MenuBuilderService.cs` | `[REPO]` **Check for Nexus Updates** runs the batch checker (requires API key) and refreshes badges. | ## Semantics @@ -22,7 +25,7 @@ How ModSync knows whether a tracked Nexus Mods component has a newer version. Co - `ModComponent.ResourceRegistry` getter returns a defensive copy of the dictionary, but the `ResourceMetadata` values are shared references; the service mutates them in place — that is the supported persistence path. `[REPO]` - `ModSync.Core` compiles as C# 7.3 and multi-targets net48; keep additions to these classes within that language level. `[REPO]` -- `NexusModsDownloadHandler` still has its own inline API code; migrating it onto `NexusApiClient` is a planned follow-up, as are endorsements and GUI update badges (see `docs/plans/2026-06-12-117-nexus-update-checks-plan.md`). `[REPO]` +- `NexusModsDownloadHandler` still has its own inline API code; migrating it onto `NexusApiClient` is a planned follow-up, as are endorsements and persisting check results via `DownloadCacheService`. `[REPO]` ## Tests diff --git a/docs/plans/2026-06-14-122-update-badges-plan.md b/docs/plans/2026-06-14-122-update-badges-plan.md new file mode 100644 index 00000000..795438b8 --- /dev/null +++ b/docs/plans/2026-06-14-122-update-badges-plan.md @@ -0,0 +1,37 @@ +--- +title: "feat: Nexus update badges and check-for-updates menu action" +type: feat +status: completed +date: 2026-06-14 +origin: docs/plans/2026-06-12-117-nexus-update-checks-plan.md (Phase 2, GUI slice) +branch: feat/update-badges-gui +--- + +# feat: Nexus update badges and check-for-updates menu action + +Second sub-slice of Phase 2 (mod version tracking + update checks) of the Vortex/MO2 +feature-parity roadmap. + +## Goal + +Surface `ModUpdateCheckService` results in the mod list and expose a user-triggered +"Check for Nexus Updates" action without changing download or install pipelines. + +## Landed + +- `ModComponent.NexusUpdateAvailable` + `NotifyNexusUpdateStateChanged()` for list binding. +- `ModListItem` "Update" badge when any tracked Nexus resource has `UpdateAvailable`. +- `MenuBuilderService` common menu item runs `ModUpdateCheckService` and refreshes badges. +- Knowledgebase and living-plan delta updated. + +## Out of scope + +- Sidebar-only badges, endorsement UI, persisting check results via `DownloadCacheService`. +- Migrating `NexusModsDownloadHandler` onto `NexusApiClient`. +- Desktop GUI validation of badge rendering (headless agent skipped). + +## Tests + +```bash +dotnet test src/ModSync.Tests/ModSync.Tests.csproj --filter "FullyQualifiedName~ModUpdateCheck" +``` diff --git a/docs/plans/vortex-mo2-feature-parity-living-plan.md b/docs/plans/vortex-mo2-feature-parity-living-plan.md new file mode 100644 index 00000000..61f06ac9 --- /dev/null +++ b/docs/plans/vortex-mo2-feature-parity-living-plan.md @@ -0,0 +1,50 @@ +--- +title: "Vortex/MO2 feature parity — living plan" +type: living-plan +status: active +date: 2026-06-14 +origin: Vortex/MO2 feature-parity roadmap (phases 1–7) +--- + +# Vortex/MO2 feature parity — living plan + +Single authoritative tracker for parity work. Individual slice plans under +`docs/plans/` remain the implementation record; this file holds status and next steps only. + +## Phase status + +| Phase | Area | Status | +|-------|------|--------| +| 1 | nxm protocol handler | Merged (#155–#164) | +| 2 | Nexus update checks | Core merged (#156); **GUI badges merged** (plan 122) | +| 3 | Profiles | Merged (#157) | +| 4 | Managed deployment | PR #158 rebased; **merge when CI green**; install wiring deferred | +| 5 | File conflicts | Core #160 + GUI #165 merged | +| 6 | FOMOD | Parser + installer dialog merged (#166); archive hook deferred | +| 7 | (roadmap tail) | Per slice plans | + +## Delta update (2026-06-14) + +### Landed + +- FOMOD installer dialog + parser stack (#166). +- Conflicts analysis GUI (#165). +- Nexus update check core (#156). +- **Update badges + "Check for Nexus Updates" menu action** (plan 122, branch `feat/update-badges-gui`). + +### Partial + +- Managed deployment engine (#158): rebased on master, CI pending → merge when green; no install-path wiring yet. +- FOMOD: no automatic archive enumeration hook in download flow. +- Update checking: no endorsement UI, no cache persistence of check results. + +### Next + +1. Merge PR #158 when CI is green. +2. Wire `DeploymentService` into install execution + optional GUI toggle (plan 116 slice 2). +3. FOMOD archive discovery hook in download/archive enumeration. +4. Migrate `NexusModsDownloadHandler` to `NexusApiClient` when download handler branch is stable. + +## Superseded + +- PR #162 closed; superseded by #166 (FOMOD dialog includes parser stack). diff --git a/src/ModSync.Core/ModComponent.cs b/src/ModSync.Core/ModComponent.cs index 9e3eda11..6409c15c 100644 --- a/src/ModSync.Core/ModComponent.cs +++ b/src/ModSync.Core/ModComponent.cs @@ -738,6 +738,31 @@ public bool IsValidating OnPropertyChanged(); } } + + /// + /// True when any entry has + /// set after a Nexus update check. + /// + [JsonIgnore] + public bool NexusUpdateAvailable + { + get + { + foreach (ResourceMetadata metadata in _resourceRegistry.Values) + { + if (metadata != null && metadata.UpdateAvailable) + { + return true; + } + } + + return false; + } + } + + /// Raises for after metadata mutation. + public void NotifyNexusUpdateStateChanged() => OnPropertyChanged(nameof(NexusUpdateAvailable)); + public bool WidescreenOnly { get => _widescreenOnly; diff --git a/src/ModSync.GUI/Controls/ModListItem.axaml b/src/ModSync.GUI/Controls/ModListItem.axaml index 34da9b98..8c03b064 100644 --- a/src/ModSync.GUI/Controls/ModListItem.axaml +++ b/src/ModSync.GUI/Controls/ModListItem.axaml @@ -43,7 +43,7 @@ - + + + + + + @@ -98,7 +117,7 @@