Skip to content

Dynamic badges/achievements system with tiered milestones#128

Open
JanMikes wants to merge 5 commits into
mainfrom
feature/dynamic-badges-system
Open

Dynamic badges/achievements system with tiered milestones#128
JanMikes wants to merge 5 commits into
mainfrom
feature/dynamic-badges-system

Conversation

@JanMikes
Copy link
Copy Markdown
Member

Summary

  • Plug-in badge system with BadgeConditionInterface + tagged iterator — adding a new badge = one enum case + one class file
  • Five flagship tiered badges (Puzzles Solved, Pieces Solved, 500pc Speed, Streak, Team Player) with Bronze→Diamond tiers, plus existing Supporter as admin-granted single-tier
  • Async recalculation triggered on every solve-time add/edit/delete; backfill via myspeedpuzzling:recalculate-badges --backfill with DelayStamp staggering
  • Profile rendering via BadgesProfileSection Twig component (membership-gated), catalog page at /en/badges with progress bars

Closes #127

Linked feature request: https://myspeedpuzzling.com/en/feature-requests/019d1d34-8b4e-71f1-81cd-a3da75d28c38

Test plan

  • Run myspeedpuzzling:recalculate-badges --backfill in dev and verify badges are awarded + emails land in Mailpit
  • Visit a member's profile — badges section renders with tier medallions and "NEW" pills
  • Visit a non-member's profile — no badges section shown at all
  • Visit /en/badges logged in — per-badge progress bars visible
  • Visit /en/badges logged out — catalog visible without progress
  • Add a puzzle solving time near a threshold — verify async recalc triggers
  • Existing Supporter badge (if any) still renders correctly with tier=NULL

Plug-in architecture: BadgeConditionInterface + tagged iterator.
Five flagship tiered badges (Puzzles Solved, Pieces Solved, 500pc Speed,
Streak, Team Player) plus existing Supporter as admin-granted single-tier.

- BadgeEvaluator orchestrates recalculation with gap-filling (tier 3 jump
  also persists tiers 1+2) and a single TemplatedEmail per recalc pass.
- Live trigger: RecalculateBadgesForPlayer dispatched from the three
  solving-time handlers to the async messenger transport.
- Console command with --player and --backfill (DelayStamp staggering).
- Profile: BadgesProfileSection component with tier medallions + NEW pill.
- Catalog page at /en/badges with per-badge progress bars.
- SCSS: Bronze/Silver/Gold/Platinum/Diamond radial-gradient medallions.
Filter out null entries from jsonb_array_elements when team puzzler
records lack a player_id field.
GetAllPlayerIdsWithSolveTimes, GetPlayerStatsSnapshot, and GetBadges
now have DB-backed tests that verify SQL syntax, null-safety, and
return-type correctness against real fixture data.
The recalc handler was sending emails inside the doctrine_transaction
boundary — a mailer failure rolled back badge rows too. Now the handler
dispatches a separate SendBadgeNotificationEmail message to async,
so badge writes commit independently of email delivery.
Use DISTINCT ON (type) with tier DESC to return one row per badge type
— always the highest earned tier. Profile and email no longer list
intermediate tiers.
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.

Dynamic badges/achievements system with tiered milestones

1 participant