Conversation
When a streak freeze is used overnight to save a user's streak, they now receive a notification letting them know: - Their streak was saved - How many freezes they have remaining Also adds lastStreakFreezeTime to user data for tracking when the freeze was used (will be used for frozen visual in a follow-up commit).
Warns users 2-3 days before their membership expires if: - Auto-renew is off (they cancelled) - Auto-renew is on but they don't have enough mana to renew Runs daily at 10 AM UTC (2 hours after the renewal job).
When a streak freeze is used to save a streak, the UI now shows: - Ice cube emoji (🧊) instead of fire - "Frozen" label instead of "Streak" - Blue/icy color filter on the display - Explanatory message in the modal This helps users understand their streak was saved and encourages them to make a prediction to continue their streak.
…lity systems Type extensions for shop items: - Achievement requirements (streak, profit, loss, volume, donations, referrals) - Team mutual exclusivity (red/green items disable opposite team) - Seasonal date-gating (items available only during window around event date) Backend validation in shop-purchase.ts and shop-toggle.ts. UI support in shop.tsx for badges and disabled states.
New shop items: - Custom YES Button (M$5000): Choose from PAMPU, BULLISH, LFG, SEND IT, etc. - Custom NO Button (M$5000): Choose from DUMPU, BEARISH, GUH, NAH, etc. Implementation: - Add metadata field to UserEntitlement for storing selections - New shop-update-metadata API endpoint for changing selections - Update bet-panel and feed-bet-button to use getCustomYesButtonText/getCustomNoButtonText - Shop UI shows selector dropdown for owned custom button items - Legacy PAMPU skin still works (maps to custom YES = PAMPU)
…crophone) Adds 6 new avatar overlay items with full rendering in avatar component and shop preview. Includes propeller-spin animation for shop/hovercard contexts, dark mode support for all hats, and restructured custom button cycling arrows to prevent layout shift.
Restores the admin-only "Return All Cosmetics" button that was removed during the subscription refactor. Refund now excludes supporter/subscription entitlements. Adds metadata jsonb column to user_entitlements for storing custom data like selected button text.
- Remove 'skin' from EXCLUSIVE_CATEGORIES so YES + NO buttons can be enabled simultaneously - Add 'conflicts' field to ShopItem for explicit mutual exclusivity (pampu-skin ↔ custom-yes-button) - Handle conflicts in shop-purchase, shop-toggle, and frontend optimistic updates - Replace owned-state dropdown with arrow cycling + debounced metadata API (800ms) for smooth UX - Remove useEffect that caused snap-back bug during rapid cycling - Skip confetti for consumable and skin category purchases - Document admin refund button disable procedure for prod deployment
- Mana Aura (M$75k): Purple-blue mystical energy glow - Black Hole (M$200k): Dark swirling void with conic gradient - Fire Ring (M$150k): Blazing flames, streak-gated (100 days) - Bad Aura (M$25k): Crimson red glow (dark version of golden glow)
Avatar accessories (new category): - Monocle (M$35k): Large gold monocle centered on avatar - Crystal Ball (M$75k): Mystical orb in bottom-right corner - YES/NO Thought Bubbles (M$25k each): Speech bubbles above avatar - Stonks Up/Down (M$50k each): Meme-style stock charts, profit/loss gated Visual improvements: - Black hole: Gemini-generated swirling accretion disk with particles - Fire ring: Gemini-generated swirling flames in red/orange/yellow - Monocle: Bigger, thicker, more centered on profile - Crystal ball: Positioned in corner, overlapping avatar - Stonks: Mini stock chart panels instead of plain arrows
- Add avatar-accessory to display-config.ts EntitlementGroup - Enable accessories in all relevant display contexts - Improve accessory positioning (all in corners now) - Update fire ring with dramatic flame tongues - Update black hole with bright accretion disk - Simplify stonks to circular badge with arrow - Update shop previews to match actual appearance
- Rename fire-ring to fire-item across all files (id, types, components) - Change category from avatar-border to avatar-accessory (combinable with auras) - Replace grey smoke glow with fiery orange/red radial gradient - Add fiery light cast overlay on avatar - Fix z-index so profile expand chevron renders above flames
Adds /shop-preview page for iterating on team cap designs. Contains two working designs: - Classic Rally: Front view with rounded brim - Tilted Rally: 3/4 angle view with visible side panel
…ents Charity Champion Trophy: - Add claim-charity-champion API endpoint for #1 ticket buyer to claim trophy - Update get-charity-giveaway to return champion and trophy holder data - Add CharityChampionCard UI component with claim/toggle functionality - Add CharityChampionBadge display in user-link Hovercard Enhancements: - Add royal velvet curtain border effect (red velvet + gold trim) - Add mana printer background SVG asset - Improve hovercard background rendering Avatar System: - Expand avatar decoration rendering with new items - Add trump-style cap SVG experiments for team caps Shop Improvements: - Add new shop items and helpers - Improve purchase/toggle conflict handling - Update follow button styling
…ckgrounds - Charity champion: claim/toggle model with previousHolder tracking, former-champion permanent entitlement, mini leaderboard, provably fair nonce - Hovercard backgrounds: champions-legacy, trading-floor overlays, floating trophy for current champion - Black hole: softer glow (no disc on light mode), subtle purple border - Shop: halo preview matches live SVG, 6 black hole design variants (E–I) - Giveaway card: top-3 leaderboard, winner display, charity stats
…lish - Crown: change to unique slot (combines with everything), add 3 position options (Right/Left/Center) with directional cycling UI - Angel wings: change to unique slot, add hover tilt animation - Avatar z-index layering: background effects (-3), angel wings (-2), halo back half (-1), avatar image (0), halo front half (auto) - Jester hat: lighter purple flap, gold headband base - Shop cards: remove pairing labels, keep slot pill badge only - Profile avatar: clip blur to avatar circle dimensions
Replace the hue-rotate-180/brightness-110 CSS filter for frozen streaks with an ice cube emoji (🧊) swap instead. Add ?previewFrozen=true query param for testing the frozen state without an actual freeze.
…cards - Notify previous trophy holder when dethroned by a new claimant - Notify new #1 ticket buyer that they're eligible to claim the trophy - Add UserHovercard + profile link for trophy holder and previous holder - Crystal ball now requires 3 seasons finished at Platinum or higher - Add seasonsPlatinum requirement type to shop system
…r thinned - Add former-charity-champion as toggleable shop card (locked for non-holders) - Redesign champions legacy overlay: higher quality trophy symbols, laurel wreaths, star accents, better spacing - Reduce royal border inner red thickness (inset -5px → -3px)
…ools - Add hidden field to ShopItem, mark 24 items as hidden (not ready for release) - Remove team system entirely (types, helpers, purchase/toggle/UI logic) - Delete team border items, keep red/green caps as regular hats - Full price audit of all visible items - Rename Jester Hat → Coolfold Jester Hat, Fedora → Bowler Hat - Unhide Disguise (loan-gated), Flames now requires 100-day streak - Add admin toggle to show/reveal hidden items with amber badges - Add pink SEASONAL badge for seasonal items - Update default shop item ordering - Delete shop-preview.tsx dev page - Re-enable admin testing tools - Fix hover jitter with card wrapper padding - Update Champion's Legacy locked text
- Add Printful merch integration (hidden): AGGC T-Shirt with size selector, shipping flow, and multi-step purchase modal - Add shop-purchase-merch and shop-shipping-rates API endpoints - Giveaway card now has 3 states: LIVE (active), ENDED (awaiting draw), and ENDED+winner (drawn) — each with mini leaderboard - Update hidden item prices (halo, wings, bowler hat, mic, aura, monocle, thought bubbles, arrows, stonks) - Rename Fedora → Bowler Hat, unhide Disguise item - Flames now requires 100-day streak - Add SEASONAL badge for seasonal items - Champion's Legacy locked text updated - Merch section in shop with admin hidden visibility
- Add White Logo Cap and Purple Logo Cap (hidden, M$3000 each) - Add merchImages field for dynamic image carousels per merch item - Auto-select size for single-variant items (caps), hide size picker - Dynamic filter tabs: only show tabs with visible items - Add Merch and Seasonal filter tabs (seasonal gets pink gradient) - Admin showHidden toggle reveals all filter tabs - Supporter discount now applies to merch purchases (backend + frontend) - Shop cards stretch to equal height in grid rows - Rename header to "Cosmetics & goods", update discount text everywhere - Fix get-printful-variants script to accept token as CLI argument
…ID collisions Bull horns: thicker curve with dual-bezier return edge, 4-stop gradient (tan→golden brown→dark brown→near-black), unique gradient IDs via useId(). Cat ears: squat wide viewBox (24x18), further apart, shorter profile. Tinfoil hat: conical shape with jagged crinkly brim, fold lines, metallic glint. Santa hat gradient ID also made unique to prevent collisions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
hasCrown was missing from needsRelativeWrapper check, so the parent div never got position:relative when crown was the only decoration.
- Green Cap and Black Cap marked hidden - NEW sidebar badge dismisses on click via localStorage, bump SHOP_NEW_VERSION to re-show - Merch items changed to one-time limit with backend enforcement - Limit 1 per customer label with info tooltip on merch cards - LEGENDARY badge restricted to halo, wings, crown only
- Top Hat: M15k -> M12.5k - Coolfold Jester Hat: M25k -> M7.5k (shown as 50% off M15k, by Strutheo) - Royal Velvet Border: M25k -> M12k - Added originalPrice field to ShopItem type for sale/strikethrough pricing - SALE badge + strikethrough in shop card and confirm modal
… endpoints - shop-implementation-plan.md: marked implemented features, added severity-ranked audit findings (C1-C4, H1-H5, M1-M5) with fix descriptions and implementation order - SHOP_SYSTEM.md: added shop-purchase-merch, shop-shipping-rates, claim-charity-champion, shop-update-metadata endpoint documentation with known issue cross-references
|
Nice! Going to tackle this bit by bit, is there any reason the svgs in avatar and shop can't be shared code rather than duplicated in 2 places? |
There were some quirks to this but I'm looking into it now and consolidating, I'll let you know if there's anything that actually blocks me from consolidating it all but it should be doable. It was mostly an artifact of a lazy method to side-by-side preview the changes as I iterated on the designs |
…le bug and halo positioning Extract 18 duplicated SVG definitions from avatar.tsx and shop.tsx into shared canonical components in item-svgs.tsx. Both files now import the same SVG art and wrap it with their own positioning/sizing — eliminating ~1,100 lines of duplicated code while keeping visuals identical. Shared components: BlackHoleSvg, FireFlamesSvg, AngelWingSvg, MonocleSvg, CrystalBallSvg, DisguiseSvg, ArrowBadgeSvg, StonksMemeArrowSvg, BullHornSvg, BearEarSvg, CatEarSvg, SantaHatSvg, BunnyEarSvg, WizardHatSvg, TinfoilHatSvg, JesterHatSvg, FedoraSvg, DevilHornSvg. Also consolidates TrophySvg (used by user-hovercard and charity-champion-card). All gradient IDs use React useId() to prevent SVG ID collisions when multiple instances render on the same page. Additional fixes: - Fix shop-toggle crash: SELECT id → SELECT 1 (user_entitlements has no id column, uses composite PK). This broke ALL item toggling. - Fix halo flying away when equipped alone: add hasHalo to needsRelativeWrapper so the absolute-positioned halo always has a relative parent container. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move halo back half before the avatar image in the render tree so it naturally layers behind. Previously it rendered after the avatar and relied on zIndex: -1 which didn't work (wrapper div wasn't positioned). Also always split halo into back/front halves even when no hat is equipped, so the back arc goes behind the avatar in all cases.
IanPhilips
left a comment
There was a problem hiding this comment.
Mini review: all places where you are debiting the user's mana I think should probably be in a SERIAL_MODE transaction, ie use runTransactionWithRetries. I think this makes sure that the user's balance that we read at the beginning of the transaction is the same at the end. This is what we use in place-bet, etc.
| @@ -20,11 +22,12 @@ export const buyCharityGiveawayTickets: APIHandler< | |||
|
|
|||
| return await pg.tx(async (tx) => { | |||
There was a problem hiding this comment.
Should this be using SERIAL_MODE?
There was a problem hiding this comment.
This makes sense! I'll have to try and remember that as a check when working on mana debits. I believe this is addressed now for the files within the PR.
Addresses PR review: all places debiting user mana now use runTransactionWithRetries (SERIAL_MODE) to prevent balance race conditions on concurrent requests. Files converted from pg.tx() to runTransactionWithRetries(): - shop-purchase.ts (item purchases) - shop-purchase-merch.ts (merch charge + refund) - shop-reset-all.ts (admin refund) - buy-charity-giveaway-tickets.ts (ticket purchases) - process-membership-renewals.ts (auto-renewal debits) Also moved side effects (notifications, log counters) outside retryable callbacks to prevent duplicate sends on tx retry.
IanPhilips
left a comment
There was a problem hiding this comment.
Looking good! Just a few changes needed. I only reviewed the backend code, I trust any frontend bugs can get worked out later.
shop-implementation-plan.md
Outdated
There was a problem hiding this comment.
do we need to keep this around? seems like maybe SHOP_SYSTEM.md is enough?
There was a problem hiding this comment.
Fair enough, I moved the remaining useful stuff across to the shop_system doc. I have double checked to make sure but everything appears to be addressed properly
|
Also, you know how to run the migrations once we merge this PR in? |
- get-charity-giveaway: Promise.all → pg.multi for single round trip - shop-purchase-merch: betsQueue.enqueueFn + runTxnOutsideBetQueue - shop-purchase: same bet queue pattern, remove redundant FOR UPDATE - shop-reset-all: same bet queue pattern - shop-shipping-rates: deduplicate PRINTFUL_API_URL to common/shop/items - shop-update-metadata: add FOR UPDATE (plain pg.tx, no bet queue) - get-printful-variants: rewrite with runScript pattern - Delete shop-implementation-plan.md, merge into SHOP_SYSTEM.md
Yes, I ran the SQL on prod after we discussed in discord. Good to go I think! |
|
I don't see some of the changes yet! maybe haven't pushed? |
all changes were pushed together here dc2ad96, I double checked each thing 1by1 as I resolved them, lmk if anything wasn't addressed |
There was a problem hiding this comment.
Nice! a tiny number of extra changes and then you're good to go. for some reason the diff inspector on here was showing me old changes so i pulled the newest branch and inspected things from the ide. I did see 2 changes missing and one new change needed.
|
Great, looks good to go, then! Yeah still saw some out of date stuff on the online diff viewer |
SQL Migrations (4 files, I need to run them manually on prod still)
2026020501 — Adds metadata jsonb column to user_entitlements
2026020801 — Enables RLS on user_entitlements (public read) and user_bans (no public access)
2026022401 — Index on shop_orders(user_id, item_id) for purchase limit checks
2026022402 — RLS on shop_orders (users see own orders only), index on user_entitlements(entitlement_id), unique partial index enforcing one-time merch limits at DB level
New API Endpoints
shop-purchase-merch — Printful integration, charges mana + calls external API, auto-refunds on Printful failure. Uses PRINTFUL_API_KEY secret.
shop-shipping-rates — Calls Printful for shipping quotes
shop-update-metadata — Updates entitlement metadata (e.g. custom button text). Validated: max 10 keys, max 500 char values.
claim-charity-champion — Trophy claim/toggle for giveaway winners
Notifications (new types)
betting_streak_freeze_used — Fires when streak freeze is consumed
membership_subscription → expiring_soon — New subtype for sub expiry warning
charity_champion_dethroned / charity_champion_eligible — Giveaway trophy notifications
Backend Logic Changes
shop-purchase.ts — Achievement gating (checkItemRequirement) queries DB for loan balance, bet count, league history, etc. Tier downgrade credit calculation.
check-subscription-expiry.ts — New scheduled job, sends notifications when subs are expiring
reset-betting-streaks.ts — Modified to support streak freeze item consumption (changes the streak flame to show as ice if a streak was used, and send notification "freeze saved your x-day streak, y freezes left.)
get-charity-giveaway.ts — Expanded: champion data, leaderboard, trophy holder tracking
buy-charity-giveaway-tickets.ts — Ticket count now validated as .int()