Skip to content

feat: add Library 2.0A organization and search - #10

Draft
Ayerdi wants to merge 52 commits into
mainfrom
feat/library-2a
Draft

feat: add Library 2.0A organization and search#10
Ayerdi wants to merge 52 commits into
mainfrom
feat/library-2a

Conversation

@Ayerdi

@Ayerdi Ayerdi commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Summary

Implement the first post-foundation product slice from docs/ROADMAP.md: Library 2.0A.

The change keeps tracking identity small and adds separate user-owned library state plus lightweight WPF filtering and an explicit organization workflow. The design was also reviewed against the current Ayerdi/gestor-juegos schema/API so a future Gestor-backed catalogue can remain an optional adapter rather than becoming a GameHours runtime dependency.

No online metadata, tags, fuzzy-search dependency, alternate game scanner or Gestor network request is introduced.

User-facing behavior

  • search games by title without case/diacritic sensitivity;
  • multi-word search plus lightweight title acronym matching (for example g1r -> Gothic 1 Remake);
  • quick scopes: Todos, Favoritos, En ejecución, Pendientes, Jugando, Pausados, Completados, Abandonados and Ocultos;
  • dedicated Organizar biblioteca view with visible per-game status, favorite and hide/show actions;
  • the normal Library view shows the persisted user completion state (Pendiente, Jugando, Pausado, Completado, Abandonado) as a compact chip beside the game title; Sin estado stays visually empty;
  • status/favorite/visibility changes are saved immediately and the normal library/filter projection refreshes from the same persisted preferences;
  • the shared dark ComboBox template renders both the open popup and the closed selected value with explicit GameHours foreground colors, avoiding unreadable Windows default text;
  • right-click remains available as a compact shortcut for the same organization actions;
  • hidden games keep all playtime/history and are recoverable through the Ocultos scope;
  • the existing active-first / most-recently-played ordering remains unchanged;
  • Jugando is a user library/progress state and remains distinct from En ejecución, which reflects actual live process tracking.

Architecture

  • TrackedGame remains unchanged.
  • New LibraryGamePreferences / LibraryCompletionStatus domain types own user organization state.
  • SQLite schema v8 adds sparse game_library_preferences with a game FK and cascade cleanup.
  • SqliteLibraryGamePreferencesRepository deletes rows that return to the default state rather than storing empty preferences.
  • WPF uses the existing ICollectionView: one custom sort plus one filter. No duplicate library data source or search index.
  • LibraryToolbar and LibraryOrganizerView keep library interaction out of the tracking/domain model.
  • The visible status chip is a projection of the same persisted LibraryGamePreferences; it is not copied into TrackedGame or GameRowViewModel. A small WPF binding invalidation signal makes the projection re-evaluate after load/write.
  • Preference writes are serialized and apply mutations over the latest loaded state rather than stale UI snapshots.

Optional external catalogue compatibility

  • GameHours UUID remains the authoritative tracking identity.
  • schema v8 also adds game_external_identities for provider-scoped correlation such as steam:3946950;
  • provider names are normalized, while external values remain exact so each provider owns its own ID semantics;
  • one provider identity cannot silently be attached to two GameHours UUIDs;
  • Steam/Epic/GOG discovery already exposes the source ExternalId, and GameExternalIdentityProviders provides the neutral mapping boundary for a future backfill/adapter;
  • a future Gestor adapter should correlate Steam identities with catalogo_juegos.steam_id; catalogo_juego_id remains Gestor-local adapter state and never replaces the GameHours UUID;
  • shared personal states align with Gestor: Pendiente, Jugando, Pausado, Completado and Abandonado;
  • Gestor favorito maps naturally to GameHours favorite;
  • GameHours hidden/archive is intentionally local-only;
  • Gestor completado_100 is not treated as the same concept as GameHours Completed status;
  • GameHours measured sessions/playtime remain authoritative and are never overwritten by Gestor tiempo_jugado or Steam snapshots.

The reviewed compatibility and authority rules are documented under integration/gestor-juegos/.

Research applied

  • Playnite (MIT) was used as a product/model reference for Favorite, Hidden and CompletionStatus, while intentionally not copying its large all-purpose Game model.
  • WPF's existing collection-view filtering/sorting and binding notification mechanisms are used instead of adding another state layer or search framework.
  • Search uses .NET culture-aware comparison with IgnoreCase | IgnoreNonSpace rather than a new dependency.
  • The current Ayerdi/gestor-juegos database/API was reviewed directly. Its separation between global catalogo_juegos and user-owned mis_juegos supports keeping GameHours catalogue identity and personal preferences separate as well.

No third-party source code was copied or adapted in this PR.

Tests added/updated

  • schema v8 migration, repair and old-backup restore behavior;
  • library preference round-trip, including Paused, sparse-default deletion and FK cascade;
  • provider-scoped external identity round-trip, reverse lookup, uniqueness and provider-defined value semantics;
  • discovered Steam/Epic/GOG identity mapping without inventing identity for loose processes;
  • case/accent/acronym-friendly search;
  • hidden/default scope behavior;
  • favorite/completion/paused/running scopes;
  • composition of search + scopes;
  • user-facing completion-state labels, including the empty Sin estado presentation;
  • existing active/recent ordering and active-game navigation tests remain in place.

Scope deliberately deferred

  • actual Gestor HTTP/authentication integration;
  • automatic external-identity backfill beyond the neutral mapping/persistence boundary;
  • bidirectional preference sync and conflict resolution;
  • tags and compound tag filtering;
  • richer empty states / keyboard polish;
  • online metadata/artwork provider implementation (Library 2.0C);
  • visual library-card redesign;
  • portable JSON representation of library preferences/identity links (exact SQLite backup already preserves them; portable-format evolution should be a separate reviewed change).

Validation

Validated on head 63cfd9e7f65967d96f600e3bd398317bd328b278 against current main 1a37362a53594d4bed2603f43e7c3e3693e2cc46:

  • locked restore: success;
  • Release build: 0 warnings / 0 errors;
  • GameHours.Tests: 161/161;
  • GameHours.Windows.Tests: 292/292;
  • total automated tests: 453/453;
  • self-contained win-x64 desktop publish smoke: success;
  • CodeQL Actions/C#: success;
  • real-Windows visual/interaction verification of the Library organization ComboBoxes and persisted status presentation: verified.

Velopack package/update-chain and install/uninstall recovery smokes remain intentionally skipped while the PR is draft. The PR is ready to leave draft once GitHub accepts the state transition.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant