Files
gridpilot.gg/plans/media-avatars-team-league-logos-streamlining-plan.md
2025-12-31 15:39:28 +01:00

8.7 KiB

Media Streamlining Plan: Driver Avatars, Team Logos, League Logos

Goal

Create one clean, conflict-free way to represent and deliver:

  • Driver avatars (defaults now; user uploads later)
  • Team logos (seeded)
  • League logos (seeded)

So that:

  • Seeding never produces conflicting behavior across environments.
  • The UI never has to guess whether a value is a file path, an API route, a generated asset, or an uploaded asset.
  • There is exactly one place that decides the final image URL for each entity.

What exists today (inventory, by responsibility)

Driver avatars

Where they surface:

  • Driver lists, driver leaderboards, race entry lists, dashboard summaries, and social/friend UI elements.
  • API payloads sometimes include an avatar URL; other times the client constructs a URL from the driver id.
  • There are multiple fallback strategies: empty string, null, or client-side default image.

Where they come from:

  • A “default avatar set” of three files exists in the website public assets.
  • There is also a server route that can generate an avatar image for a driver id.
  • Some parts of the system treat driver avatar as a user-uploadable media setting.

Observed problems:

  • Mixed meaning of the avatar field: sometimes it is an absolute URL, sometimes a relative path, sometimes a server route string.
  • Multiple fallbacks implemented in multiple places leads to inconsistent UI and hard-to-debug “missing image” bugs.
  • Multiple “demo/fake” avatar generators exist, creating divergent behavior between environments.

Team logos

Where they surface:

  • Team cards, team leaderboards, recruiting/featured team sections.
  • Sometimes the UI uses logoUrl from the API payload; other times it falls back to a server route based on id.

Where they come from:

  • A server route can generate a team logo image for a team id.
  • Seed logic also “pre-seeds” team logos by writing route strings into an in-memory store.

Observed problems:

  • Team “logoUrl” may be an actual URL, or it may be a placeholder, or it may be a server route string stored as data.
  • Storing route strings as if they were media values creates conflicts when routes change.
  • In some persistence modes the “seeded logo store” is not truly persisted, so bootstrapping may re-trigger reseeding or create inconsistent results.

League logos

Where they surface:

  • League cards, league headers, league pages.
  • UI tends to call a client-side helper that builds a league-logo URL from id.

Where they come from:

  • A server route can generate a league logo image for a league id.
  • Seed logic also “pre-seeds” league logos by writing route strings into an in-memory store.

Observed problems:

  • Same class of conflicts as team logos.
  • There is no single authoritative rule for when a league has a “real” logo versus a generated one.

Proposed streamlined model (single canonical representation)

Canonical concept: Media Reference (not a URL)

Instead of treating stored values as “final URLs”, define a single canonical media reference for each entity image.

Media reference types:

  • System default: a fixed asset shipped with the website (driver defaults: male/female/neutral).
  • Generated: deterministically generated from an entity id and a seeded pseudo-random source (team/league logos).
  • Uploaded: a user-uploaded object managed by the media subsystem.
  • None: intentionally unset.

Key rule: only one layer resolves media references into URLs.

URL resolution responsibilities

  • Backend resolves references into final URLs for API payloads.
  • Backend also serves the image bytes for generated assets and uploaded assets.
  • Frontend treats received URLs as ready-to-render and does not invent additional fallbacks beyond a single last-resort placeholder.

Seeding strategy (cleanest route)

Teams and leagues: seeded via faker, but without storing URLs

Requirement: “seed team and league logos via faker”.

Clean approach:

  • During seed, assign each team/league a Generated media reference.
  • The generator uses faker with a seed derived from the entity id to produce a deterministic “logo identity” (colors, initials, shapes, etc.).
  • The stored value is only the reference (type + seed key), not a route string and not a URL.
  • When the UI needs to show the logo, it either receives a resolved URL in the API payload or uses a single, standardized media URL builder.

Benefits:

  • Deterministic results: same team id always yields the same logo.
  • No conflicts when URLs/routes change.
  • No need to persist binary files for seeded logos.

Drivers: seeded from the 3 default avatar images

Requirement: “seed driver logos from these defaults” and later “normally these would be user uploads”.

Clean approach:

  • During seed, assign each driver a System default media reference selecting one of:
    • male-default-avatar
    • female-default-avatar
    • neutral-default-avatar
  • Selection is deterministic (based on driver id) so reseeding does not change faces randomly.
  • Later, if a user uploads an avatar, the reference switches to Uploaded and overrides the default.

Benefits:

  • No dependency on generated avatars for baseline.
  • No ambiguous meaning of the avatar field.

Contract rules (what the UI can rely on)

Field semantics

  • Every API payload that includes a driver/team/league image should provide a single resolved URL field for that image.
  • Resolved URL is either:
    • a valid URL string the UI can render immediately, or
    • null (meaning: show a generic placeholder).
  • Never send empty strings.
  • Never send “sometimes relative file path, sometimes server route” style mixed values.

Fallback rules

  • The backend resolver must guarantee a valid URL whenever it can (system default or generated).
  • The frontend uses exactly one last-resort placeholder if it receives null.
  • No per-component bespoke fallbacks.

Streamlining work items (what changes where)

1) Centralize media reference resolution

Create one “media resolver” concept used by:

  • API payload assembly for all places that include avatars/logos.
  • Image-serving routes for generated assets and uploaded assets.

This resolver is the only place that knows:

  • how to map media references to a concrete image URL
  • what the fallback is when no uploaded media exists

2) Stop storing server route strings as data

Remove the pattern where seed logic writes values like “/api/media/.../logo” into an in-memory media store.

Replace it with:

  • stored media references (generated/system-default/uploaded)
  • consistent URL resolution at response time

3) Normalize route prefixes and caching behavior

  • Choose one public URL shape for these images and apply it universally.
  • Add consistent cache headers for generated assets (deterministic) so the browser and CDN can cache safely.

4) Align frontend consumption

  • Ensure the UI always prefers the resolved URL from the API payload.
  • Where the UI only has an id (e.g. very lightweight list items), use a single shared “URL builder” instead of ad-hoc string concatenation.
  • Remove duplicate “if missing then fallback to …” logic sprinkled across components.

5) Align tests and demo fakes

  • Eliminate competing fake avatar/logo generators.
  • Ensure all test fixtures use the same deterministic rules as seed and runtime generation.
  • Ensure snapshot/contract tests treat empty string as invalid and expect null instead.

6) Make bootstrapping/reseeding conflict-proof

  • Reseed decision should be based on durable data correctness (presence of required entities) rather than transient “in-memory media store” state.
  • Ensure “missing avatar/logo” checks are aligned with the new media reference model.

7) Migration and cleanup

  • Define how existing seeded databases are handled:
    • either a one-time cleanup that rewrites old stored values into the new reference model, or
    • a documented wipe-and-reseed path for local/dev environments.
  • Ensure the migration path eliminates stored route strings.

Mermaid: Target flow

flowchart TD
  UI[Website UI] --> API[API payloads include resolved image URLs]
  API --> RES[Media resolver]
  RES --> UP[Uploaded media storage]
  RES --> GEN[Deterministic generator]
  RES --> DEF[System default assets]
  GEN --> IMG[Image response with cache headers]
  UP --> IMG
  DEF --> UI
  IMG --> UI

Acceptance criteria

  • Driver avatars always render with one of the three defaults unless an upload exists.
  • Team and league logos always render deterministically in dev/test seed, without persisting URLs.
  • No API payload returns empty string for avatar/logo.
  • No UI component constructs its own bespoke fallback logic.
  • No bootstrapping loop caused by “missing media” when media is generated or defaults are available.