From 42bf0c4a593f329fa79b63fe9121b8253a2d0ca3 Mon Sep 17 00:00:00 2001 From: Avvrik Date: Tue, 26 Aug 2025 11:46:28 +0500 Subject: [PATCH] add twitter lists --- app/cross-chain/TwitterLoader.tsx | 24 ---- app/cross-chain/page.tsx | 142 ++++++++++----------- app/ethereum/EthereumTimelines.tsx | 75 ----------- app/ethereum/OrnellaWeb3.md | 12 -- app/ethereum/VitalikButerin.md | 10 -- app/ethereum/ethereumJoseph.md | 12 -- app/ethereum/francescoswiss.md | 11 -- app/ethereum/owocki.md | 12 -- app/ethereum/page.tsx | 147 ++++++++++------------ app/ethereum/sassal0x.md | 12 -- app/solana/SolanaTimelines.tsx | 58 --------- app/solana/TwitterLoader.tsx | 24 ---- app/solana/page.tsx | 154 +++++++++++------------ app/solana/solana-twitter-0xmert.md | 12 -- app/solana/solana-twitter-aeyakovenko.md | 12 -- app/solana/solana-twitter-bmigliaccio.md | 12 -- app/solana/solana-twitter-foundation.md | 10 -- app/solana/solana-twitter-guibibeau.md | 11 -- app/solana/solana-twitter-turbine.md | 11 -- 19 files changed, 210 insertions(+), 551 deletions(-) delete mode 100644 app/cross-chain/TwitterLoader.tsx delete mode 100644 app/ethereum/EthereumTimelines.tsx delete mode 100644 app/ethereum/OrnellaWeb3.md delete mode 100644 app/ethereum/VitalikButerin.md delete mode 100644 app/ethereum/ethereumJoseph.md delete mode 100644 app/ethereum/francescoswiss.md delete mode 100644 app/ethereum/owocki.md delete mode 100644 app/ethereum/sassal0x.md delete mode 100644 app/solana/SolanaTimelines.tsx delete mode 100644 app/solana/TwitterLoader.tsx delete mode 100644 app/solana/solana-twitter-0xmert.md delete mode 100644 app/solana/solana-twitter-aeyakovenko.md delete mode 100644 app/solana/solana-twitter-bmigliaccio.md delete mode 100644 app/solana/solana-twitter-foundation.md delete mode 100644 app/solana/solana-twitter-guibibeau.md delete mode 100644 app/solana/solana-twitter-turbine.md diff --git a/app/cross-chain/TwitterLoader.tsx b/app/cross-chain/TwitterLoader.tsx deleted file mode 100644 index 471fcec..0000000 --- a/app/cross-chain/TwitterLoader.tsx +++ /dev/null @@ -1,24 +0,0 @@ -'use client' - -import Script from 'next/script' -import { useEffect } from 'react' - -export default function TwitterLoader() { - // Re-hydrate embeds after mount / client navigations - useEffect(() => { - // Call once after script is available - const id = setTimeout(() => { - // @ts-ignore - if (window.twttr?.widgets?.load) window.twttr.widgets.load() - }, 0) - return () => clearTimeout(id) - }, []) - - return ( - + {/* CTA */}
diff --git a/app/ethereum/EthereumTimelines.tsx b/app/ethereum/EthereumTimelines.tsx deleted file mode 100644 index a6b80b3..0000000 --- a/app/ethereum/EthereumTimelines.tsx +++ /dev/null @@ -1,75 +0,0 @@ -'use client' - -type Profile = { - handle: string - title?: string - avatar?: string - bio?: string - displayName?: string -} - -function displayNameOf(p: Profile) { - if (p.displayName && p.displayName.trim()) return p.displayName.trim() - if (p.title && !p.title.startsWith('@')) return p.title.trim() - return p.handle.replace(/^@/, '') -} - -export default function EthereumTimelines({ profiles }: { profiles: Profile[] }) { - if (!profiles || profiles.length === 0) return null - - return ( -
- {profiles.map((p) => { - const raw = p.handle.replace(/^@/, '').trim() - const handle = raw.toLowerCase() // 👈 normalize to lowercase - const name = displayNameOf(p) - const sameAsHandle = name.replace(/^@/, '').toLowerCase() === handle - - const avatar = - (p.avatar && p.avatar.trim()) || `https://unavatar.io/twitter/${handle}` - - return ( -
-
-
- {/* eslint-disable-next-line @next/next/no-img-element */} - {`@${handle} { - (e.currentTarget as HTMLImageElement).src = `https://unavatar.io/twitter/${handle}` - }} - /> -
- -
- {sameAsHandle ? ( - @{handle} - ) : ( -
- {name} - @{handle} -
- )} - - {p.bio &&

{p.bio}

} - - -
-
-
- ) - })} -
- ) -} diff --git a/app/ethereum/OrnellaWeb3.md b/app/ethereum/OrnellaWeb3.md deleted file mode 100644 index a339c58..0000000 --- a/app/ethereum/OrnellaWeb3.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: "@OrnellaWeb3" -description: "X/Twitter profile" -authors: ["@OrnellaWeb3"] -tags: ["Ethereum", "Twitter"] -languages: ["General"] -url: "https://x.com/OrnellaWeb3" -avatar: "https://x.com/OrnellaWeb3/photo" # or any image URL -bio: "I work for @ethereum at @ethereumfndn" -dateAdded: 2025-08-12 -category: "Twitter" ---- diff --git a/app/ethereum/VitalikButerin.md b/app/ethereum/VitalikButerin.md deleted file mode 100644 index b35079c..0000000 --- a/app/ethereum/VitalikButerin.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: "@VitalikButerin" -description: "X/Twitter profile" -authors: ["@VitalikButerin"] -tags: ["Ethereum", "Twitter"] -url: "https://x.com/VitalikButerin" -avatar: "https://x.com/VitalikButerin/photo" -dateAdded: 2025-08-12 -category: "Twitter" ---- diff --git a/app/ethereum/ethereumJoseph.md b/app/ethereum/ethereumJoseph.md deleted file mode 100644 index 25e8e0d..0000000 --- a/app/ethereum/ethereumJoseph.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: "@ethereumJoseph" -description: "X/Twitter profile" -authors: ["@ethereumJoseph"] -tags: ["Ethereum", "Twitter"] -languages: ["General"] -url: "https://x.com/ethereumJoseph" -avatar: "https://x.com/ethereumJoseph/photo" # or any image URL -bio: "Co-founder of @ethereum | Founder of @Consensys" -dateAdded: 2025-08-12 -category: "Twitter" ---- diff --git a/app/ethereum/francescoswiss.md b/app/ethereum/francescoswiss.md deleted file mode 100644 index d50f0b3..0000000 --- a/app/ethereum/francescoswiss.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: "@francescoswiss" -description: "X/Twitter profile" -authors: ["@francescoswiss"] -tags: ["Ethereum", "Twitter"] -url: "https://x.com/francescoswiss" -avatar: "https://x.com/francescoswiss/photo" # or any image URL -bio: "Not Forbes 30 under 30 |🇨🇭🧠 DevRel @consensys @MetaMask" -dateAdded: 2025-08-12 -category: "Twitter" ---- diff --git a/app/ethereum/owocki.md b/app/ethereum/owocki.md deleted file mode 100644 index 90f4fae..0000000 --- a/app/ethereum/owocki.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: "@owocki" -description: "X/Twitter profile" -authors: ["@owocki"] -tags: ["Ethereum", "Twitter"] -languages: ["General"] -url: "https://x.com/owocki" -avatar: "https://x.com/owocki/photo" # or any image URL -bio: "etheruem localist, DAO cartographer, EVM whisperer, shitpost artist + chaos magician @allo_capital/@gitcoin" -dateAdded: 2025-08-12 -category: "Twitter" ---- diff --git a/app/ethereum/page.tsx b/app/ethereum/page.tsx index f4fbda5..6cbd12e 100644 --- a/app/ethereum/page.tsx +++ b/app/ethereum/page.tsx @@ -5,8 +5,8 @@ import matter from 'gray-matter' import { marked } from 'marked' import Link from 'next/link' import Image from 'next/image' +import Script from 'next/script' import { ArrowLeft, Github, ExternalLink, ChevronRight } from 'lucide-react' -import EthereumTimelines from './EthereumTimelines' export const runtime = 'nodejs' export const dynamic = 'force-static' @@ -21,18 +21,6 @@ type ResourceMeta = { dateAdded?: string | Date level?: string category?: string - // twitter extras - avatar?: string - bio?: string - displayName?: string -} - -type TwitterProfile = { - handle: string - title?: string - avatar?: string - bio?: string - displayName?: string } // ---------- helpers ---------- @@ -42,9 +30,6 @@ const arrLower = (a?: unknown[]) => (Array.isArray(a) ? a.map(toLower) : []) const isEthereumTag = (tags?: unknown) => Array.isArray(tags) && tags.map(toLower).some(t => ['ethereum','eth','evm','ethereum dev'].includes(t)) -const isTwitterTag = (tags?: unknown) => - Array.isArray(tags) && tags.map(toLower).some(t => ['twitter','x','twitter profile'].includes(t)) - const isGithub = (url?: string) => { if (!url) return false try { @@ -68,8 +53,7 @@ function haystack(meta: ResourceMeta) { const containsPhrase = (hay: string, phrase: string) => hay.includes(` ${phrase.toLowerCase()} `) const containsAny = (hay: string, items: string[]) => items.some(p => containsPhrase(hay, p)) -// Classifier → 'guides' | 'code' | 'courses' (Ecosystem removed) -// Anything that would've been "ecosystem" now falls under Guides & Docs unless it matches Code or Courses. +// Classifier → 'guides' | 'code' | 'courses' function classify(meta: ResourceMeta): 'guides' | 'code' | 'courses' { const hay = haystack(meta) const tags = arrLower(meta.tags) @@ -86,7 +70,6 @@ function classify(meta: ResourceMeta): 'guides' | 'code' | 'courses' { ] if (isGithub(meta.url) || containsAny(hay, codeSignals)) return 'code' - // Fold prior "ecosystem" hints into Guides & Docs const ecosystemSignals = [ 'node.js','nodejs','npm','pnpm','yarn','nvm', 'hardhat','foundry','forge','anvil','remix', @@ -103,36 +86,9 @@ function classify(meta: ResourceMeta): 'guides' | 'code' | 'courses' { ] if (containsAny(hay, guideSignals) || containsAny(hay, ecosystemSignals)) return 'guides' - - // Default to Guides & Docs return 'guides' } -// Twitter handle extraction -function extractTwitterHandle(meta: ResourceMeta): string | null { - const handleRe = /^@?([A-Za-z0-9_]{1,15})$/ - for (const a of meta.authors || []) { - const m = String(a).trim().match(handleRe) - if (m) return m[1] - } - if (meta.url) { - try { - const u = new URL(meta.url) - const host = u.hostname.replace(/^www\./,'') - if (host === 'twitter.com' || host === 'x.com') { - const seg = u.pathname.split('/').filter(Boolean)[0] || '' - const m = seg.match(handleRe) - if (m) return m[1] - } - } catch {} - } - if (meta.title?.trim().startsWith('@')) { - const m = meta.title.trim().match(handleRe) - if (m) return m[1] - } - return null -} - // Blog MD -> HTML async function getBlogHtml() { try { @@ -145,44 +101,21 @@ async function getBlogHtml() { } } -// Read .md and split into resources + twitter profiles +// Read .md and collect resources async function getEthereumContent(): Promise<{ resources: (ResourceMeta & { key: string; section: ReturnType })[] - profiles: TwitterProfile[] }> { const dir = path.join(process.cwd(), 'app', 'ethereum') const files = await fs.promises.readdir(dir) const mdFiles = files.filter(f => f.toLowerCase().endsWith('.md') && f.toLowerCase() !== 'blogpost.md') const resources: (ResourceMeta & { key: string; section: ReturnType })[] = [] - const profiles: TwitterProfile[] = [] - const seen = new Set() for (const file of mdFiles) { const raw = await fs.promises.readFile(path.join(dir, file), 'utf-8') const { data } = matter(raw) const meta = data as ResourceMeta - // Twitter profiles - if (isTwitterTag(meta.tags)) { - const h = extractTwitterHandle(meta) - if (h) { - const key = h.toLowerCase() - if (!seen.has(key)) { - seen.add(key) - profiles.push({ - handle: h, - title: meta.title, - displayName: meta.displayName, - avatar: typeof meta.avatar === 'string' ? meta.avatar : undefined, - bio: typeof meta.bio === 'string' ? meta.bio : undefined, - }) - } - } - continue - } - - // Normal resources if (!isEthereumTag(meta.tags)) continue if (!meta.url) continue @@ -202,12 +135,12 @@ async function getEthereumContent(): Promise<{ } resources.sort((a, b) => (new Date(b.dateAdded || 0).getTime()) - (new Date(a.dateAdded || 0).getTime())) - return { resources, profiles } + return { resources } } export default async function EthereumPage() { const [blogContent, content] = await Promise.all([getBlogHtml(), getEthereumContent()]) - const { resources, profiles } = content + const { resources } = content const guidesDocs = resources.filter(r => r.section === 'guides') const codeTemplates = resources.filter(r => r.section === 'code') @@ -254,6 +187,9 @@ export default async function EthereumPage() { ) : null + const ethListId = '1959975314357916004' + const ethListUrl = 'https://x.com/i/lists/1959975314357916004' + return (
{/* Banner */} @@ -291,18 +227,69 @@ export default async function EthereumPage() {
- {/* Auto sections (Ecosystem removed) */} + {/* Auto sections */}
- {/* Ethereum Twitter (unchanged) */} - {profiles.length > 0 && ( -
-

Ethereum Twitter

- -
- )} + {/* New: Interactive Twitter List section (Early-Stage style) */} +
+

+ Ethereum’s core builders shaping the ecosystem +

+ +
+
+ Curated accounts worth following{' '} + + (open on X) + . +
+ + {/* Programmatic timeline container */} +
+ + {/* Hidden fallback hint */} +
+ If you see “Nothing to see here”, enable third-party cookies or disable tracker blockers + for this site, then refresh. You can also open the list directly on X. +
+
+ + {/* Load widgets.js once it’s safe */} + +
{/* CTA */}
diff --git a/app/ethereum/sassal0x.md b/app/ethereum/sassal0x.md deleted file mode 100644 index 706085b..0000000 --- a/app/ethereum/sassal0x.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: "@sassal0x" -description: "X/Twitter profile" -authors: ["@sassal0x"] -tags: ["Ethereum", "Twitter"] -languages: ["General"] -url: "https://x.com/sassal0x" -avatar: "https://x.com/sassal0x/photo" # or any image URL -bio: "Independent Ethereum educator, angel investor and advisor" -dateAdded: 2025-08-12 -category: "Twitter" ---- diff --git a/app/solana/SolanaTimelines.tsx b/app/solana/SolanaTimelines.tsx deleted file mode 100644 index d365a1f..0000000 --- a/app/solana/SolanaTimelines.tsx +++ /dev/null @@ -1,58 +0,0 @@ -'use client' - -type TwitterProfile = { - handle: string - title?: string - avatar?: string - bio?: string -} - -export default function SolanaTimelines({ profiles }: { profiles: TwitterProfile[] }) { - return ( -
- {profiles.map((p) => { - const handle = p.handle.replace(/^@/, '') - const avatar = p.avatar || `https://unavatar.io/twitter/${handle}` - - return ( -
-
-
- {/* eslint-disable-next-line @next/next/no-img-element */} - {`@${handle} { - (e.currentTarget as HTMLImageElement).src = `https://unavatar.io/twitter/${handle}` - }} - /> -
- -
-
@{handle}
- {p.bio ? ( -

{p.bio}

- ) : null} - - -
-
-
- ) - })} -
- ) -} diff --git a/app/solana/TwitterLoader.tsx b/app/solana/TwitterLoader.tsx deleted file mode 100644 index 471fcec..0000000 --- a/app/solana/TwitterLoader.tsx +++ /dev/null @@ -1,24 +0,0 @@ -'use client' - -import Script from 'next/script' -import { useEffect } from 'react' - -export default function TwitterLoader() { - // Re-hydrate embeds after mount / client navigations - useEffect(() => { - // Call once after script is available - const id = setTimeout(() => { - // @ts-ignore - if (window.twttr?.widgets?.load) window.twttr.widgets.load() - }, 0) - return () => clearTimeout(id) - }, []) - - return ( - +
{/* CTA */}
diff --git a/app/solana/solana-twitter-0xmert.md b/app/solana/solana-twitter-0xmert.md deleted file mode 100644 index 80217f3..0000000 --- a/app/solana/solana-twitter-0xmert.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: "@0xMert_" -description: "X/Twitter profile" -authors: ["@0xMert_"] -tags: ["Solana", "Twitter"] -languages: ["General"] -url: "https://x.com/0xMert_" -avatar: "https://x.com/0xMert_/photo" # or any image URL -bio: "ceo @heliuslabs, ex @coinbase — Solana RPCs, APIs, trading infra: http://helius.dev " # optional -dateAdded: 2025-08-12 -category: "Twitter" ---- diff --git a/app/solana/solana-twitter-aeyakovenko.md b/app/solana/solana-twitter-aeyakovenko.md deleted file mode 100644 index c5b7f01..0000000 --- a/app/solana/solana-twitter-aeyakovenko.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: "@aeyakovenko" -description: "X/Twitter profile" -authors: ["@aeyakovenko"] -tags: ["Solana", "Twitter"] -languages: ["General"] -url: "https://x.com/aeyakovenko" -avatar: "https://x.com/aeyakovenko/photo" # or any image URL -bio: "Co-Founder of Solana Labs." # optional -dateAdded: 2025-08-12 -category: "Twitter" ---- diff --git a/app/solana/solana-twitter-bmigliaccio.md b/app/solana/solana-twitter-bmigliaccio.md deleted file mode 100644 index c3235c7..0000000 --- a/app/solana/solana-twitter-bmigliaccio.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: "@b_migliaccio" -description: "X/Twitter profile" -authors: ["@b_migliaccio"] -tags: ["Solana", "Twitter"] -languages: ["General"] -url: "https://x.com/b_migliaccio" -avatar: "https://x.com/b_migliaccio/photo" # or any image URL -bio: "building things 🛠️ @SolanaFndn" # optional -dateAdded: 2025-08-12 -category: "Twitter" ---- diff --git a/app/solana/solana-twitter-foundation.md b/app/solana/solana-twitter-foundation.md deleted file mode 100644 index 2f1d2c9..0000000 --- a/app/solana/solana-twitter-foundation.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: "@SolanaFndn" -description: "X/Twitter profile" -authors: ["@SolanaFndn"] -tags: ["Solana", "Twitter"] -languages: ["General"] -url: "https://x.com/SolanaFndn" -dateAdded: 2025-08-12 -category: "Twitter" ---- diff --git a/app/solana/solana-twitter-guibibeau.md b/app/solana/solana-twitter-guibibeau.md deleted file mode 100644 index 978f70d..0000000 --- a/app/solana/solana-twitter-guibibeau.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: "@GuiBibeau" -description: "X/Twitter profile" -authors: ["@GuiBibeau"] -tags: ["Solana", "Twitter"] -languages: ["General"] -url: "https://x.com/GuiBibeau" -bio: "Education @solanafndn, YouTuber https://guibibeau.com" # optional -dateAdded: 2025-08-12 -category: "Twitter" ---- diff --git a/app/solana/solana-twitter-turbine.md b/app/solana/solana-twitter-turbine.md deleted file mode 100644 index 2ad3173..0000000 --- a/app/solana/solana-twitter-turbine.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: "@solanaturbine" -description: "X/Twitter profile" -authors: ["@solanaturbine"] -tags: ["Solana", "Twitter"] -languages: ["General"] -url: "https://x.com/solanaturbine" -bio: "The premiere developer institute, innovation lab, research hub, talent source, dev shop on @solana. The Solana Talent Engine." # optional -dateAdded: 2025-08-12 -category: "Twitter" ----