222 lines
8.7 KiB
Markdown
222 lines
8.7 KiB
Markdown
# 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
|
|
|
|
```mermaid
|
|
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.
|
|
|