Commit Graph

124 Commits

Author SHA1 Message Date
martbost 19b7770a4d Video once-per-day dedup + featured gold border & spacing
- Video watch-to-earn: a member can no longer re-earn from the same video the
  same day. New video_seen table (dual-mode); serveVideo excludes today's
  already-watched videos; videowatch double-checks before crediting.
- Featured card: equal top/bottom margin (no longer touches the stat cards) and
  a gold border on the card + the rotating link.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 08:13:09 -05:00
martbost bc95965814 Featured to top, centered, capped; banner ads capped 728px centered; slot CTA
- Featured card moved to the top of Overview, centered, max 760px; the rotating
  link is capped to 728px and centered (banner-sized, not a huge block).
- Served banner creatives cap at min(100%,728px), centered — kills the oversized
  sample banner; ad slots center their creative + label.
- Featured self-sell: shows "N of C slots open today" + "Feature your link →"
  that jumps to Campaigns with Featured preselected.
- Campaign target/image/video URL fields get autocomplete=off and clear when the
  Campaigns pane opens, so no residual (e.g. massifly) URL carries over.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 08:08:47 -05:00
martbost 3a6d9d037c Ad-report admin recipient via ADMIN_EMAIL env only (siteConfig is public)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 07:59:26 -05:00
martbost 4991fe96e8 Ad reporting + featured rotates one link + clear campaign form on submit
- Ad reporting (auto-approved ads need a safety valve): new reports.js store
  (dual-mode) + ad_reports table + POST /api/report-ad; a "⚠ report" control on
  served ad slots opens a reason prompt and notifies the admin by email
  (siteConfig.adminEmail / ADMIN_EMAIL).
- Featured links now show ONE link at a time and cycle (true rotation), instead
  of listing all links at once.
- Campaign form now clears EVERY field on submit (target/creative/title/etc.).
  A leftover target URL was carrying into the next campaign — the same reason a
  video short was saved with the wrong (massifly) CTA target.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 07:57:12 -05:00
martbost ea1b2ea0ac Campaign budget field contextual + earnings full-history link
- Hide the free-form "Budget (credits)" field for fixed-cost campaign types
  (Featured = day-slot priced, Verified visits = flat pack) where spend is derived.
- Overview "Recent payouts" gets a "full history ->" link to the Earnings pane,
  which already lists every on-chain payout/referral/purchase with verify links
  (it just wasn't discoverable from the Overview mini-chart).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 07:44:00 -05:00
martbost 958bd5ac20 Live members-area updates: SSE toasts + cha-ching/pop sounds, no refresh needed
- The members area subscribes to the chain event stream (/api/feed/live) and
  reacts to anything relevant to the member: a payout received (cha-ching + toast
  + auto-refresh), a referral qualifying, a passed-up payout (missed), a settled
  purchase, a new activation in the line. No page refresh required.
- New chat message -> "pop" sound + toast + badge; ping heartbeat (20s) now
  returns chatUnread and pops on an increase.
- Sounds are synthesized via Web Audio (no asset files, CSP-safe).
- Surge badge: nudge the name down (ribbonY .779 -> .805) to center on its ribbon.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 07:36:47 -05:00
martbost 7fbf9add37 Align downline rows into a grid (name | contact | date | action)
Each row was an independent flex line, so variable-width emails threw the date
column out of alignment across rows. Now a CSS grid with a shared template keeps
the date and Message columns lined up; long names/emails truncate; responsive
stack on narrow screens.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 07:28:20 -05:00
martbost 1363bd7b0b Fix: buy links wallet first; achievement milestones use live buyerCount
- Buying now ensures the wallet is LINKED to the account before the purchase
  (one signature if not linked). Without it, a wallet only connected for the
  faucet bought successfully but credits resolved against member id 0 and never
  showed until a manual link. Matches the "buying links your wallet" promise.
- Dashboard computed achievement milestones from buyerCount BEFORE reading it
  from chain (always 0), so Surge/Circuit/Nexus never unlocked even when
  qualification advanced. Moved the milestone block after the chain read.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 07:24:26 -05:00
martbost d275822cd4 Fix "undefined joined your line" in Overview recent-activity
The referral row read r.email but dashboard referral objects carry r.name
(username or masked email). Use r.name with an escape + fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 07:09:32 -05:00
martbost 204aa6a723 Rehearsal faucet: self-serve test-POL so members can actually buy
Fresh wallets hold 0 POL on the rehearsal chain, so nobody could complete a buy.
- Oracle: the mock POL/USD feed had gone ~49h stale (quoteWei reverted "Stale
  oracle" -> catalog costWei null -> packages showed "paused"). Refreshed it and
  installed a 15-min heartbeat cron so it stays fresh; quotes now return.
- New /api/my/faucet (rehearsal-only, rate-limited) tops a connected wallet up to
  10 test-POL via anvil_setBalance (no key). "Get test POL" card in the Wallet
  pane wires it. Members can now fund, buy, and see payouts move on the ledger.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 07:02:48 -05:00
martbost 85a6ac5bb0 Wallet: fix Trust Wallet chain-add + personal_sign ("invalid method parameters")
- wallet_addEthereumChain omits blockExplorerUrls when the config explorer is
  empty (rehearsal has none) — an empty-string URL made Trust Wallet reject the
  add with "invalid method parameters", blocking new users from adding the chain.
- personal_sign now hex-encodes the SIWE message (Trust Wallet requires hex;
  MetaMask took raw). Signed bytes are identical so server recovery is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 06:58:12 -05:00
martbost 41d3293486 Fix referral attribution (username joins) + broadcast rate-limit regression
- Dashboard "Your line" roster now matches referrals joined under the member's
  USERNAME link (invite link is /join/<username>), not just code + member id —
  username joins were invisible.
- lastBroadcastAt now counts only kind='broadcast'. Chat rides the same table,
  so chatting had been tripping the once-a-day broadcast limit ("try again in
  24h" on the first broadcast).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 06:38:27 -05:00
martbost 7caff0eea2 Unblock testing: message-your-sponsor entries, video pause-on-blur, tour polish
- Sponsor messaging is now reachable everywhere a new referral looks: a
  "Message your sponsor" card on My line, the Overview quick action, and a
  pinned "Message your sponsor" row atop the Messages drawer (even with no
  thread yet). Chat stores the sponsor from the dashboard.
- Watch-to-earn video + Shorts now PAUSE when the tab/window loses focus and
  resume on return, so a viewer must stay on the page for the watch to count.
- Welcome tour hides its frame on completion (no white about:blank panel).
  NOTE: rmcircle.team allows framing (frame-ancestors *); the blank was just
  the post-dwell blank state.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 06:34:49 -05:00
martbost 5b1a60f653 Fix profile persistence + badge ribbon alignment; 24h sessions
- BUG: /api/me never returned avatarUrl/bio/socials/lineBanner fields, so the
  Profile pane repopulated empty on reload — data WAS saved, just not surfaced.
  Now returned so avatar, bio, and social links persist visibly.
- Achievement badge: member name drops the "@" and sits on a per-badge ribbonY
  (spark .728 / surge .779 / circuit .713 / nexus .709), gold with dark outline.
- Sessions now expire in 24h (was 30d) so members re-login daily and see the
  login ad each day.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 06:13:19 -05:00
martbost a634f83a8e Shorts = portrait-only reel; Watch videos = landscape; badge name in gold
- Videos now carry client-detected pixel dimensions (videoW/videoH) captured at
  campaign create (upload or direct link). serveVideo takes an orientation filter:
  Shorts reel (/shorts) serves portrait (height>width); the Watch videos tab
  serves landscape, including legacy/unknown-dimension videos so nothing is orphaned.
- shorts.js requests the portrait feed and has a client guard that skips any
  landscape video that slips through. Create form shows the detected orientation.
- Achievement badge share image: member name now renders in gold with a dark
  outline (was low-contrast dark ink) and sits centered on the ribbon.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 05:41:17 -05:00
martbost 5e3f2540f4 Sponsor chat entry: move off the bottom-right FAB into the left sidebar
The bottom-right bubble is the site's AI support chat — only one belongs there.
Sponsor/downline messaging now lives in the lower sidebar under Team → Messages
(unread badge on the link), opening the same threads drawer. Removed the FAB.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 05:33:20 -05:00
martbost ade0a13064 Sponsor Chat: two-way member<->sponsor messaging (presence, availability, mute)
Evolves the one-way broadcast into real support threads, keeping broadcast alongside.
- messages.js: kind='chat' rides sponsor_messages; sendChat/thread/threadList/
  markChatRead/chatUnread; broadcast inbox + login modal scoped to kind='broadcast'
- accounts.js: presence (last_seen), chat availability toggle, per-member mutes;
  sponsorOf() + isDownlineOf() resolvers
- server.js: /api/my/chat/{send,thread,threads,available,mute} + /api/my/ping
  heartbeat; dashboard emits chatUnread/chatAvailable/sponsor; auth = direct
  sponsor up, any downline down, or an existing thread; email only when offline
  and not mid-chat
- my.html/my.js/site.css: slide-in chat drawer (threads list + live thread,
  4s poll), presence dot, Message buttons on direct rows, Message-my-sponsor
  quick action, availability switch in Profile
- db.js: additive migrations (kind, pair index, chat_available, chat_mutes)
- chatbot.js: canned answer + AI fact for Sponsor Chat

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 05:24:23 -05:00
martbost 23136f4ba0 Ornate achievement badges (Spark/Surge/Circuit/Nexus) — kie.ai mint-teal trophies + name overlay
- Named the milestone ladder: Spark (payouts) / Surge (first buyer) / Circuit (L2) / Nexus (L3)
- Overview badges strip now shows the ornate badge art (locked = dimmed); milestone stepper renamed
- Share button composites the badge template with the member's @username on the ribbon (JPEG, same-origin canvas)
- Badge art in public/badges/ (optimized JPG ~300KB each)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 14:11:44 -05:00
martbost 3c0e94f20f Badge emblems, profile social links, and SEO/OG social previews
- Achievement badge share-image now draws a milestone emblem (⚡🎯⭐🏆) in the medal
- Profile: Facebook/X/YouTube/Instagram/TikTok/Telegram/LinkedIn/Website links, shown on the public bio page
- OG + Twitter Card tags on homepage/ledger/contract (hero banner as share image)
- Per-member OG tags server-injected into /wall/<username> so shared bio links preview with name/bio/avatar
- robots.txt + sitemap.xml; .txt/.xml MIME types

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 12:21:53 -05:00
martbost b001df61b1 Homepage: all 7 ad formats live (add Video ads, flip Featured + Verified visits to live), refresh member benefits with earn-back, badges, bio page, messaging, banner kit
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 12:06:27 -05:00
martbost 5c99753f45 Banner kit: add crisp vector 320x50 mobile leaderboard (SVG) — full 7-size IAB set
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 10:21:27 -05:00
martbost 1912d2b251 Banner kit: add 468x60, 160x600, 120x600 (kie.ai) — 6 IAB sizes now in Promo tools
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 10:20:34 -05:00
martbost 2f27038297 DO Spaces enabled: namespace IAP video uploads under iap-uploads/ in the shared media bucket
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 09:51:43 -05:00
martbost 6423d57f60 InstantAdPay banner kit (kie.ai): hero + leaderboard + rectangle in Promo tools
- 3 brand banners generated via kie.ai (mint-on-dark Web3 aesthetic), served from /banners/
- Promo tools Banners card now shows the kit with copy-image-URL

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 09:35:06 -05:00
martbost 537e7352d4 DO Spaces adapter (feature-flagged): route video uploads to object storage off the volume
- spaces.js: zero-dep SigV4 PUT to DO Spaces, public-read; inert unless DO_SPACES_* env is set
- /api/my/upload sends video to Spaces when configured, falls back to volume otherwise
- Images stay local; Spaces URLs are https so they pass the video/media validators + CSP

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 09:24:48 -05:00
martbost cb2542fca5 Verified visits (7th ad format): guaranteed unique human-visit packs
- Buy N verified visits flat up-front; each is a distinct member, dwell + captcha verified
- Verified visits earn sub-tab: open site (new tab), dwell, human-check, earn
- Per-viewer dedup (visit_seen unique), daily cap, completes at N; single-use tokens

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 09:21:59 -05:00
martbost f30a462ca8 Featured rotation -> day-slot booking: 10 slots/day, pick your start day, live occupancy
- Per-day occupancy shown in composer (Today 1/10, Tomorrow 2/10, …), full days unpickable
- Book a specific start day + duration; every covered day must have an open slot
- Slot cap turns dilution disclosure into a hard ceiling; featured skips frame-check (new-tab)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 09:16:24 -05:00
martbost c5dd5c685b Paid shorts: full-screen vertical video feed over the video inventory, earn per watched short
- /shorts page: no-seek full-screen player, server-clock watch, credit + swipe to next
- Reuses /api/my/videos + /api/my/videowatch (same fraud model, daily cap, self-exclusion)
- Linked from Earn credits > Watch videos

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 09:02:29 -05:00
martbost b7e9fdf656 Wall view interaction: click banner, countdown, then a viewed checkmark (per-position, persisted)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 09:00:14 -05:00
martbost 37a2b97bfd Featured rotation format: duration-priced link rotation with disclosed dilution
- New 'featured' type: headline + link + 1/2/7 day run, flat up-front price (40cr/day)
- Composer discloses dilution (N links share the rotation, yours makes N+1) before buying
- Overview featured strip shows the live rotation; expires automatically

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 08:58:22 -05:00
martbost b25b4c67fe Buy more views: top up any campaign's budget, reactivates spent/paused ones (+NAS extend)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 08:53:35 -05:00
martbost 1aab52ca90 Profile build-out (avatar + bio) + shareable bio page with QR
- Profile pane: avatar upload + bio, with a link to your public page
- Wall becomes a bio page: avatar, bio, scannable join QR, line ladder, join CTA
- qrcode npm dep; /api/qr renders SVG QR server-side (CSP-clean img)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 08:49:20 -05:00
martbost 9e05711f4e Achievement badges + milestone credit bonuses
- Idempotent one-time credit bonus per milestone (payouts/firstBuyer/level2/level3): 10/25/50/100 cr
- Overview badges strip (unlockable medals) with canvas share-to-image (username overlaid)
- Newly-unlocked milestones toast the bonus on next dashboard load

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 08:43:50 -05:00
martbost 7701a29169 Sponsor to downline messaging: lineage view, daily broadcast (email+inbox), login modal
- Downline resolver (3 levels); email visible for directs only, username+ID deeper
- Broadcast composer (rich editor) to directs or full downline, 1/day cap, sends email + on-site inbox
- Upline messages inbox in My line; unmissable sign-in modal for unread, ack marks read
- messages.js dual-mode store; sponsor_messages table; sanitizeRich reused

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 08:39:06 -05:00
martbost 289538dbeb NAS: adtype is an integer column (1), not a string — fixes sponsorads insert
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 08:21:59 -05:00
martbost 57bba0111a NAS syndication adapter (feature-flagged off): push banner/text to Network Ad Space, reconcile delivery into unified credit pool
- nas.js: direct-MySQL writer mirroring CTB's proven payments+sponsorads path (approved:1, pid 1/2, catid 5, remaining counts DOWN)
- Create-time push for banner/text; pause/resume mirrors to NAS deactivate/topUp
- reconcileNas() 5-min loop: reads served, charges delta credits to the shared budget, stops NAS ad when budget dry
- Campaign row shows IAP + '+N net' network views (differentiated stats)
- Inert unless NAS_DB_* env is set; zero prod impact until enabled
- Also: rename Inbox sub-tab to 'Inbox Ads'

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 08:11:32 -05:00
martbost 860aedabf6 Video ads (watch-to-earn): duration-tiered pricing, escape-proof player, per-view charge
- New 'video' campaign type; MP4/WebM upload or direct https link + required-watch tier (10/30/60s → 3/7/12 cr per view; viewer earns 1/2/4)
- Composer: video fields, tier dropdown, live price hint
- Earn credits > Watch videos sub-tab: no-seek player, server-clock watch floor, daily cap, self-exclusion, single-use tokens
- media-src CSP for direct-link/hosted video; video skips frame-check

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 08:03:01 -05:00
martbost 49508c2a13 Banner ads: required IAB size picker (width/height stored, shown in row, maps to NAS)
Also lands dormant server-side scaffolding for video ads (type, tiers, validation) — not yet exposed in UI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 07:51:28 -05:00
martbost b3be71135d Merge solo Inbox into Earn credits as a sub-tab; unread badge on Earn menu item
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 07:41:41 -05:00
martbost 1ee0b5626e Viral welcome tour + line banner + wall, milestone stepper, rich solo editor, CTA-gated reads
- Welcome tour (3 levels x 10s) unlocks welcome credits; line banner in Profile; public /wall/<username>
- 'Your next move' redesigned as a milestone stepper
- Solo composer: BV-style rich editor (H2/H3, inline image+video, undo/redo, raw text)
- Solo read reward now requires clicking through to the advertiser, not just dwelling
- Sanitizer: inline media whitelist + script/style stripped whole

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 07:30:49 -05:00
martbost 718d7f2d0e Built-in transaction viewer: every ledger row verifiable at /tx/<hash>, explorer link when configured
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 05:37:29 -05:00
martbost 1c31679b6c Login ads as post-signin interstitials: Open Ad in new tab, timer here, Go to Dashboard
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-05 16:28:57 -05:00
martbost 2cc2855488 Rich solo composer: WYSIWYG editor, sanitized HTML bodies, media uploads, CTA labels
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-05 15:38:46 -05:00
martbost f6a3befe09 Onsite solo ads: inbox delivery with read rewards, composer, homepage format live
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-05 15:25:00 -05:00
martbost 5c945a1b09 Frame-breaking check at campaign submit: reject XFO/frame-ancestors blockers, SSRF-guarded
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-05 07:48:55 -05:00
martbost 15b036b940 Earn viewer v2: full-screen URL visits with countdown, human check, explicit return
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-05 07:23:30 -05:00
martbost fee86d296a Overview v3: plated stat cards, trend chips, payout bars, credit donut, view ring, delivery chart
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-05 06:41:01 -05:00
martbost e22021c266 Member identity, promo tools, back-office color, seeded inventory
Real people, not numbers: usernames (unique, profile pane to set them),
shown across the ledger, activity, rosters, and the sidebar chip; vanity
invite links (/join/<username>) with late chain binding intact. Promo tools
pane ships Branded Voice share posts and an email swipe personalized with
each member's link. The earn viewer excludes a member's own campaigns, so
nobody earns from their own spend. Back office gains the cyan/violet/amber
accent family over the green base. Three house campaigns seeded so every
surface shows live inventory. Assets v=20260905i.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-05 06:28:17 -05:00
martbost 28ed925443 View-fraud protection + real ad placement surfaces
Earning views now run on single-use server-issued tokens: the view only
counts when the full dwell elapses on the server clock; instant, forged,
replayed, and stale posts are all rejected, and the client countdown
pauses whenever the tab loses visibility or focus. New placements: login
ads on the sign-in screen (the per-day format's real home), a banner slot
in the back-office Overview, a text slot in the member sidebar, and a
member-ads banner on the homepage. Assets v=20260905g.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-05 06:15:27 -05:00
martbost 7331eac244 Back-office polish: Overview command center, quick actions, pane motion
Overview gains a recent-activity mini-feed (chain events + line joins) and
a quick-actions card jumping straight to buy/earn/campaigns plus one-tap
invite copy. Panes fade in (reduced-motion safe). First slice of the
back-office polish pass. Assets v=20260905f.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-05 06:09:37 -05:00