From 084c002c45d31df50c7bf58c5282de99d323256d Mon Sep 17 00:00:00 2001
From: SkateSpots
Date: Sun, 4 Oct 2026 18:01:49 +0700
Subject: [PATCH] Fix mobile grid overflow + add a Top Shops leaderboard
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The /lab profile card and /stats Top Mappers were overflowing off the right
edge: Tailwind `grid md:grid-cols-2` (no base column) renders one implicit
`auto` track on mobile that can exceed the container when a name is long, so the
whole card got pushed off-screen. Added the base `grid-cols-1` repo-wide (plus
`min-w-0` on truncating flex/grid children).
/stats also gained a "Top Shops — most shops added" leaderboard (kind 30017)
beside Top Mappers (spots): `topShops` in useGrowthStats, a generalized
MapperRow (count + unit), and EN/ES copy. PERSIST_BUSTER → v6 (new persisted
field).
---
AGENTS.md | 8 +++
src/components/auth/SignupMethodDialog.tsx | 2 +-
src/hooks/useGrowthStats.ts | 16 +++++
src/i18n/en.ts | 4 ++
src/i18n/es.ts | 4 ++
src/lib/queryPersister.ts | 2 +-
src/pages/AboutPage.tsx | 6 +-
src/pages/CommunityStatsPage.tsx | 68 +++++++++++++++++++---
src/pages/LabPage.tsx | 8 +--
src/pages/MarketplacePage.tsx | 2 +-
src/pages/OnboardingPage.tsx | 6 +-
src/pages/PrivacyPage.tsx | 2 +-
src/pages/RiderCardPage.tsx | 2 +-
13 files changed, 106 insertions(+), 24 deletions(-)
diff --git a/AGENTS.md b/AGENTS.md
index 4fefe00..ad42bb2 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1257,6 +1257,14 @@ session with **no other memory of this project**. So:
with the QR; the `bunker://` paste stays as the fallback. ⚠️ **NOT verified
against a real signer — needs an on-device check with Amber** (scan → pair →
signed in); only the URI shape was verified in Node.
+- **Mobile grid overflow gotcha (2026-10-04):** a Tailwind grid **must** declare a
+ base column — `grid grid-cols-1 md:grid-cols-2`, **not** `grid md:grid-cols-2`.
+ Without the base, mobile renders one implicit `auto` track that can exceed the
+ container, so a long name (e.g. a `/lab` `ProfileCard`, or `/stats` Top Mappers)
+ pushes the whole card off the right edge and clips the values. Fixed repo-wide;
+ also add `min-w-0` to truncating flex/grid children. `/stats` also gained a
+ **Top Shops** leaderboard (`topShops` in `useGrowthStats`; `PERSIST_BUSTER` → v6)
+ alongside Top Mappers (spots).
- **Community stats (`/stats`, 2026-10-01):** `useGrowthStats` + `CommunityStatsPage`.
- ⚠️ **A relay outage used to render a board of ZEROS as truth** — every
sub-query ended in `.catch(() => [])`. The queryFn now counts failures (a
diff --git a/src/components/auth/SignupMethodDialog.tsx b/src/components/auth/SignupMethodDialog.tsx
index 9e1c164..030aafa 100644
--- a/src/components/auth/SignupMethodDialog.tsx
+++ b/src/components/auth/SignupMethodDialog.tsx
@@ -32,7 +32,7 @@ const SignupMethodDialog: React.FC = ({
-
+
{/* Mnemonic Option (Recommended) */}
diff --git a/src/hooks/useGrowthStats.ts b/src/hooks/useGrowthStats.ts
index de3b43d..ec49cba 100644
--- a/src/hooks/useGrowthStats.ts
+++ b/src/hooks/useGrowthStats.ts
@@ -54,6 +54,11 @@ export interface TopMapper {
totalContent: number;
}
+export interface TopShop {
+ pubkey: string;
+ shopCount: number;
+}
+
export interface GrowthStats {
// ── Totals ──────────────────────────────────────────────────────────────
totalSpots: number;
@@ -92,6 +97,8 @@ export interface GrowthStats {
// ── Leaderboard ─────────────────────────────────────────────────────────
topMappers: TopMapper[];
+ /** Top shops by shop count (kind 30017). */
+ topShops: TopShop[];
// ── Marketplace ──────────────────────────────────────────────────────────
/** Live NIP-99 listings (stash "showcase-only" items excluded). */
@@ -485,6 +492,14 @@ export function useGrowthStats() {
totalContent: mapperTotal.get(pubkey) ?? spotCount,
}));
+ // ── Top shops by shop count ──────────────────────────────────────────
+ const mapperShops = new Map();
+ for (const e of shops) { mapperShops.set(e.pubkey, (mapperShops.get(e.pubkey) ?? 0) + 1); }
+ const topShops: TopShop[] = Array.from(mapperShops.entries())
+ .sort((a, b) => b[1] - a[1])
+ .slice(0, 10)
+ .map(([pubkey, shopCount]) => ({ pubkey, shopCount }));
+
// ── Unique mappers ───────────────────────────────────────────────────
const allPubkeys = new Set(allContent.map(e => e.pubkey));
@@ -552,6 +567,7 @@ export function useGrowthStats() {
monthly,
cumulativeSpots,
topMappers,
+ topShops,
totalListings: listings.length,
totalTrades: trades.length,
tradedSats,
diff --git a/src/i18n/en.ts b/src/i18n/en.ts
index 38d9ccc..7903f83 100644
--- a/src/i18n/en.ts
+++ b/src/i18n/en.ts
@@ -6337,6 +6337,10 @@ walletUi: {
topMappers: 'Top Mappers',
mostSpots: '— most spots added',
noMapperData: 'No mapper data yet',
+ topShops: 'Top Shops',
+ mostShops: '— most shops added',
+ shopCount: '{{count}} shops',
+ noShopData: 'No shop data yet',
supporters: 'Community Supporters',
fundedTitle: 'Funded by the Community',
fundedDesc1: 'Decentralized Bitcoin crowdfunding via Angor.',
diff --git a/src/i18n/es.ts b/src/i18n/es.ts
index 3acfbc5..2cb071b 100644
--- a/src/i18n/es.ts
+++ b/src/i18n/es.ts
@@ -6337,6 +6337,10 @@ walletUi: {
topMappers: 'Mejores mapeadores',
mostSpots: '— más spots añadidos',
noMapperData: 'Aún no hay datos de mapeadores',
+ topShops: 'Mejores tiendas',
+ mostShops: '— más tiendas añadidas',
+ shopCount: '{{count}} tiendas',
+ noShopData: 'Aún no hay datos de tiendas',
supporters: 'Patrocinadores de la comunidad',
fundedTitle: 'Financiado por la comunidad',
fundedDesc1: 'Crowdfunding descentralizado en Bitcoin vía Angor.',
diff --git a/src/lib/queryPersister.ts b/src/lib/queryPersister.ts
index 1bd0ec3..b3fc13b 100644
--- a/src/lib/queryPersister.ts
+++ b/src/lib/queryPersister.ts
@@ -40,7 +40,7 @@ import type { Query } from '@tanstack/react-query';
const IDB_KEY = 'skatespots:react-query-cache';
/** Bump when the persisted data shape changes to invalidate old caches. */
-export const PERSIST_BUSTER = 'v5';
+export const PERSIST_BUSTER = 'v6';
/** Discard any persisted cache older than this (ms). */
export const PERSIST_MAX_AGE = 24 * 60 * 60 * 1000; // 24h
diff --git a/src/pages/AboutPage.tsx b/src/pages/AboutPage.tsx
index b65cb40..dd57ee9 100644
--- a/src/pages/AboutPage.tsx
+++ b/src/pages/AboutPage.tsx
@@ -113,7 +113,7 @@ Version: v0.1.0`;
-