diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/community-directory.json | 282 | ||||
| -rw-r--r-- | src/features/messages.js | 67 | ||||
| -rw-r--r-- | src/i18n.js | 2 |
3 files changed, 323 insertions, 28 deletions
diff --git a/src/community-directory.json b/src/community-directory.json new file mode 100644 index 0000000..3d90f02 --- /dev/null +++ b/src/community-directory.json @@ -0,0 +1,282 @@ +[ + { + "name": "Vector Community", + "description": "The Official Vector Privacy Community", + "category": "General", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygrp0t08a8c422vhs7tg5mms8wjtawqrmurtxpvhxt6p6dm3a4hwtspsgqqqsg2s38pkw7#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbRw_Cn34fgsEYkvqGqu_LiY", + "member_count": 268 + }, + { + "name": "FOSS Apps", + "description": "Talk about your favorite FOSS Apps", + "category": "Privacy & Security", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygq6s5n5w8hvu4nncyly8jus4cv2rjd9c8mjcjxjcp3dh0zptjtcz5psgqqqsg2s6jmpkn#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbTaSCPrS_jGZlZozTNd5iIM", + "member_count": 83 + }, + { + "name": "Nostr Talk", + "description": "Nostr Community on Vector", + "category": "General", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqyg9g7qululdtdsut52mv9ksgwj6zrv0k0smqchkzwtl09rc2s4wl0cpsgqqqsg2sahulk5#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbdxrjLZ21pC33OLWqd1FVl0", + "member_count": 73 + }, + { + "name": "Monero Community", + "description": "Monero Community on Vector", + "category": "Bitcoin & Crypto", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygr75m3mrzs0xfmsjkhkwa00svs272unprp2mz3ysjtnnnvxvygvwypsgqqqsg2sv666ef#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbSeQqxFnUQWdc6fw1Y6nib4", + "member_count": 66 + }, + { + "name": "0xPrivacy Community", + "description": "Breaking the digital cage, one guide at a time. \n#Monero • #TOR • #Nostr • #IPFS #mirrors\n\nhttps://linkfork.io/0xprivacy", + "category": "Privacy & Security", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygxel95damgkg9220up8p7xvj7uadh9pjp4c4vmx2nwrqgdf922ll5psgqqqsg2sueyzt7#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbTvwaMUcU_AGMnDxCXiYOnQ", + "member_count": 61 + }, + { + "name": "Bitcoin Community", + "description": "We are all Satoshi, except Craig Wright.", + "category": "Bitcoin & Crypto", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqyg843mgn60k47mgfk2lr3wtf06l5nt48ptszmu652zxnzjcede4epcpsgqqqsg2sxl92nz#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbSpQgbd1r25mUXY3o4dPaRY", + "member_count": 40 + }, + { + "name": "PIVX Community", + "description": "The Official PIVX Community on Vector", + "category": "Bitcoin & Crypto", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygrul6nvrcf56l5ufqq9vqkrcpajgp6xcseyqmwpltjgh8hfkh498gpsgqqqsg2sys4p52#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbUDwW71f6gB_MpbDP_vSYA8", + "member_count": 30 + }, + { + "name": "Movies & Shows", + "description": "A chat about movies & shows you love or hate", + "category": "General", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygp95jafl95d2a3p9vdg0lwxlrkf0yqfnfxrpz9hcdq8ff6vea38mupsgqqqsg2shlm80j#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbRVod9h7AsPSPyREnZdkxW4", + "member_count": 28 + }, + { + "name": "Massive Music Group", + "description": "Your Home For Music", + "category": "Music", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqyg9v30lq49skxq7phwrs5zz0pfdw8kz8zqyqq33yxldl9sh5w4spd5psgqqqsg2s0pfdym#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvba4IDbyQoIlEO8OsEE41D0w", + "member_count": 26 + }, + { + "name": "ᘔᗩᗰᖇOᑕK ᖇᗩᗪIO", + "description": "100+ countries tuned in. Zero corporate bullshit. We broadcast psychedelic, afro-beat, surf & funk. Open-source, self-hosted, and unowned. ZamRock.net 📡", + "category": "Music", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqyg8pp9hm5tumdwz2u6dzplarspr5szqyp2a84jw79ck20m5rug67uqpsgqqqsg2smmn3mf#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbWu-qRb2VLnrnDxNO2anxtE", + "member_count": 23 + }, + { + "name": "Nostr Music Share", + "description": "Nostr music, nostr links or nevents only", + "category": "Music", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygrhvjedv5xznnmktkgyeaajull8fehvd6kytj533ez8jy56dcka5upsgqqqsg2suwp4yn#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbXm9exm8vfI0K88v5v68fAM", + "member_count": 20 + }, + { + "name": "Web-XDC Game Dev", + "description": "Community for development and testing of XDC games like this one: https://github.com/th2tran/gomoku-webxdc", + "category": "Gaming", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygqsj22nwsj7ey2uq966ye3knhw5058vtduvk847z35jfkl3ue9jtspsgqqqsg2s3dfnhc#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbUYe9Li8nvfk0cYNsBo7oWE", + "member_count": 20 + }, + { + "name": "Meme Addicts", + "description": "Spam the group with all your favorite memes", + "category": "Art & Creative", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygzlrevgglkj93u3rxys9duhet9ep4h4pp6qgfczvy2554vzc4wyrupsgqqqsg2szeprjm#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbZ8Lv5xiN62CYSQRoVb2VzI", + "member_count": 19 + }, + { + "name": "The Nostr Pirates", + "description": "Talk about Piracy , Tech or just retarded stuff as long as it's not porn and specially CP everything is allowed here", + "category": "Technology", + "invite_url": "https://vectorapp.io/invite/naddr1qvzqqqyzz5pzqck4pqttjtv3rjynfg333g68h3y8mtuuy5vwel29h5nt72dfwwk3qqqqjgu2kx#BAHD06ir6Cf2UoRvzBE2G16K", + "member_count": 19 + }, + { + "name": "Vector Gaming Group", + "description": "A Community To Discuss Vector Built In Games Or Any Other Games", + "category": "Gaming", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygxglnmwm09vgtvpyy9tmm8vt038wpcphq4tk0qr9kr5jjg05g2sf5psgqqqsg2sy03x73#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbcEzoWO0CYloQCAeOaEMTcA", + "member_count": 17 + }, + { + "name": "Philosophy", + "description": "Metaphysics, Epistemology, Ethics, Human Nature, Good and Evil, etc. The deep conversations about the ultimate fundamental abstract principles that shape reality and our understanding of it.", + "category": "General", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqyg9erxx7zm7fhteclu0wkqygr09txg8vds03fre9k40gw56w0s4skgpsgqqqsg2syawhrk#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbQWq9Uyr_YUGd9XGHaGSF-o", + "member_count": 17 + }, + { + "name": "Conspiracy Theories/Truths", + "description": "Discussions of Any Topics That Are Categorized As Conspiracy Theories", + "category": "General", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqyg8ycuht7vqvlzqg8e0z0zhsraq3yljthl5swahfsx2mdxtmsflatgpsgqqqsg2sv6g97v#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbUbWGoMYbbrG5HjLamPAb0k", + "member_count": 16 + }, + { + "name": "Lounge Chat", + "description": "Keep it civil and child friendly if possible. But otherwise anything goes.", + "category": "General", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygzl4q3l3t049mqy4e6rthpes0f9e794juz54dhhv5gtatsskpaaygpsgqqqsg2sgvap7a#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbYyfHi5njaqBjK2JMKyjZVk", + "member_count": 15 + }, + { + "name": "Mega Utilities™", + "description": "Mega Utilities we create bots, websites, games, and more!", + "category": "General", + "invite_url": "https://vectorapp.io/invite/naddr1qvzqqqyzz5pzpmcqczfxkr8ly7kqqp5lmqta32uyut4gz8qjq3z7x8l89ssy5xd7qqqqy23y9x#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbdBWhWXDbWyRZlSHLe2-oZc", + "member_count": 15 + }, + { + "name": "History", + "description": "Nostalgia, Ancient History & Everything in Between", + "category": "General", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygyhmfjjmuy7eeegyxpq8u7et0xv0s4wscr29fqu2axk9xglpqsfacpsgqqqsg2s39wlra#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbdSbOfD0XUw1acQSoC_7Q2Y", + "member_count": 13 + }, + { + "name": "Barbie Fan Club", + "description": "A community of Barbie lovers celebrating everything pink, fabulous, and iconic. From vintage collectors to movie fans, we unite over the magic of this timeless doll who inspires generations. Because B", + "category": "General", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygq2ngw90fmyaw9tqyqrcmjjan23gf7h70uv6lnw8q0rtplg2hwcrypsgqqqsg2slhc9ka#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbRVUG4y6PGcfVCWoXmvGdp8", + "member_count": 11 + }, + { + "name": "Effective Accelerationism", + "description": "The future, today: for those with interest in AI, Space, Tech, Energy, and the climbing of the Kardashev Scale.", + "category": "Technology", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygxqux0cm6s74lgveqg629f25sdlr654w07keuy882c2mjtv3grd9spsgqqqsg2sjedwa7#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbYiq8HmWCJdtqjVopXwpgaU", + "member_count": 11 + }, + { + "name": "Roger's Hangout", + "description": "Hanging out, coding, gaming together, and being a community.", + "category": "General", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygymc3fcux23xmvz893ldn3sc2gg3khsgvawsyle8en97fe93hq7gvpsgqqqsg2sslmxry#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbcGBuDAZfIAWiPs7QnVpe-g", + "member_count": 11 + }, + { + "name": "Vectoriel Francophone", + "description": "Pour les utilisateurs francophones de Vector, ce chan est là pour se retrouver, échanger, discuter et proposer. For FR speaking people (but not exclusively) you're welcome to share and discuss", + "category": "General", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygqclr7ymkke7w84h75cnwr7mvhwhxs00qgth2hqc6q94yz70sd6tcpsgqqqsg2sf40a0z#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbVhCn0du_XmF7Ol8madGfUQ", + "member_count": 10 + }, + { + "name": "Sapchat Group", + "description": "A group where everyone is encouraged to use 24h expiring messages", + "category": "General", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygyufv2f236glg89f4mf02lplyv79zajyfnyz7s3scmslkruwp2avcpsgqqqsg2s3tau6j#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbeaTeU2Pkhp8qL079AY-_Go", + "member_count": 10 + }, + { + "name": "🔒 Retro Gaiden", + "description": "Retro Gaiden is a community by retro gamers for retro gamers. It’s an extension of the Retro Gaiden Mastodon server (retro-gaiden.com)\n\nThis space is primarily focused on retro gaming. Casual discussi", + "category": "Gaming", + "invite_url": "https://vectorapp.io/invite/naddr1qvzqqqyzz5pzq97fuz3tt55ck6laatahkyh85v0mung4pmt60u9lkr8r95hmjgu3qqqqsm9tn6#BAFiJBHI-TnHmwfMTAh3SLNm", + "member_count": 7 + }, + { + "name": "Pixel Art", + "description": "Pixel Art Community on Vector. Pixel Art Creators, Pixel Art Enthusiasts, and all around Pixel Art Lovers. Nostalgia Unlocked.", + "category": "Art & Creative", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygym72cl4da8dxpj9yld3hcerfxug69vg9t4u94v29xzuaj6g9qx3gpsgqqqsg2skdxg4g#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbTRoqt5b7KXbL44R3DtfR3M", + "member_count": 7 + }, + { + "name": "Vector Bot And Webxdc dev", + "description": "Developing vector bots and webxdc application.", + "category": "General", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygqay0qa29v4q3lmwx5gq9ysnz7ethc7n7shaqdgs6fts75xxvtg2ypsgqqqsg2s3xqkhl#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbdD8WoQHbyKmhUGA7bAcIQ8", + "member_count": 7 + }, + { + "name": "TTRPGs", + "description": "A community for those interested in games such as Dungeons and Dragons, DC20, Pathfinder, and more.", + "category": "General", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygp3qz6z0s7j7ke53rx9nh0dpnpmlr54w32t3lyea6pecg04p8fkkvpsgqqqsg2s3astcn#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbUk-MH4eBH6jl69KgN2R6xg", + "member_count": 6 + }, + { + "name": "Design 'Til I Die", + "description": "Dedicated Designer Group.\n\nFeedback, Critique, Ideas, Suggestions, Software, Tools, Tricks, Clients, & Collaborations\n\nYou name it, we've done it.\n(probably the wrong way)", + "category": "Art & Creative", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqyg8qmmyqt3s3gnxl585xzu3vuyq2drx60wy5nrtqc8arx8djm9jftupsgqqqsg2sedyyjj#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbcyfwmTYHiQcFNJJp_utfyc", + "member_count": 6 + }, + { + "name": "Ethereum Community", + "description": "Ethereum Community on Vector\n\nDiscussing development, updates, price, and projects.\n\nDo not shill or directly advertise your token, contract address, or NFTs. You will be banned for spam and advertisi", + "category": "Bitcoin & Crypto", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygxnufhkreg6e045r2h2zxly5luvlxsufxpm0wj3avdqs3n82ylhevpsgqqqsg2sha4f8a#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbT9_GZYaNeNGah1x4Qm87ww", + "member_count": 5 + }, + { + "name": "Anime", + "description": "For anime fans!", + "category": "Art & Creative", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygyr5uyw6nelxuh3gxtpyw2yaprgttu29tpvkkg6lk8228fy06m2hspsgqqqsg2s7hzrm5#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbagcdETBjHpNBXfjftUjRLw", + "member_count": 5 + }, + { + "name": "MAD LADS", + "description": "Mad Lads is a popular 10,000-piece NFT collection on the Solana blockchain, created by Backpack. Launched in April 2023, it serves as the cultural arm of the Backpack exchange, featuring tokenized ap", + "category": "Bitcoin & Crypto", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygxe6m97f50cz2s0xpksgqnr5x222st92y4eq5awyw5pxgfp8vq9gspsgqqqsg2s2es8av#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbYudTNTya0pSsGz6PuBTQ-o", + "member_count": 3 + }, + { + "name": "Magick, Occult, Witchcraft", + "description": "Esoteric / Metaphysical discussion", + "category": "Other", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygzzk3x8c980vcn28pg4ldmlcmf5uujk5z2fh4ud62tfqrjq3mn7gspsgqqqsg2sxfjkha#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbdQCcecX3D27flKvJBKXS5M", + "member_count": 3 + }, + { + "name": "Zcash Community", + "description": "Zcash is a privacy-focused cryptocurrency launched in October 2016 by the Electric Coin Company, led by Zooko Wilcox. It uses zk-SNARKs zero-knowledge proofs to enable optional shielded transactions,", + "category": "Bitcoin & Crypto", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygzz8m37fsv7z5aqh7pd068h35xxv4qp9c73snmjd0rhggaw2mch4cpsgqqqsg2spaxzj7#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbccGKgU2EdmdcUuDSQ_LQlY", + "member_count": 2 + }, + { + "name": "Solana Community", + "description": "A general Solana discussion community on Vector.", + "category": "Bitcoin & Crypto", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygq0lxvwm4dwchp8f5dc2dyftrdgxn4jlv3k9ycgpcugfrw23kpylcpsgqqqsg2sggvjpf#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvba_b_Moz_ivhKvvOIe9mIlc", + "member_count": 2 + }, + { + "name": "Zano", + "description": "Community for Zano, a private cryptocurrency.\n\nFor more information, go to https://zano.org", + "category": "Bitcoin & Crypto", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygxy8c2kgkd9tu0wrukdz7z5sk3s8p8sm3nesfhr7n9w9sgulqw7lspsgqqqsg2srwqtx7#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbWQ_U3V5H4oQ01mnS4lrhGY", + "member_count": 2 + }, + { + "name": "Faith in Christ (✝)", + "description": "Faith in Christ Community on Vector\n↪ United by Christ through Faith and God's Grace\n\n\"The fear of the LORD is the beginning of wisdom, and knowledge of the Holy One is understanding.\" -Proverbs 9:10", + "category": "Other", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygyjfwmqna8xnjkex876c5z8209dn0fy5u9ear0tlv04czqupjpxt5psgqqqsg2su0n6q8#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbYr6O1rMeW2IRfpVh51GNPM", + "member_count": 2 + }, + { + "name": "Italiani", + "description": "Comunità italiana su Vector", + "category": "Local & Regional", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygy34e9vkrcfj37rvk4adxy75zw6m7uvsl9kt4l33zhwut2ct4mk2vpsgqqqsg2sewgx9d#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbRSSejOSBb8IrqbgqGlSEQ0", + "member_count": 2 + }, + { + "name": "Jade: AI Waifu", + "description": "Experimental AI personality", + "category": "Art & Creative", + "invite_url": "https://vectorapp.io/invite/naddr1qqqqygree0y66yu3cehka6zdd5yfrca4rct6hy2ka8ul3y52h9t5r22dzgpsgqqqsg2sglseng#BAADAQIAGG5vc3RyLmNvbXB1dGluZ2NhY2hlLmNvbeRV60HAcgvMBKMN18MgP-w", + "member_count": 1 + } +] diff --git a/src/features/messages.js b/src/features/messages.js index a9b21b0..8001d39 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -50,6 +50,7 @@ import { qrSvg } from '../qr.js'; // The coinos community's join material lives in ../community.js — shared // with the public read-only chat page so the two can never drift. import { COMMUNITY, EPOCH } from '../community.js'; +import COMMUNITY_DIRECTORY from '../community-directory.json'; const DM_RELAYS = ['wss://relay.coinos.io', 'wss://nos.lol']; const NOTIFIER = 'https://nwcpush.coinos.io'; const APP_BASE = 'https://v3.coinos.io'; @@ -1857,30 +1858,50 @@ export function messagesFeature(ctx) { scheduleRepaint(); } - // ---- community discovery: the Vector Hub's public listing as suggestions - // on Messages and under Browse. Fetched when Messages opens and kept - // for the session; every listing is a CORD-05 link, which is exactly what - // the paste box takes, so a tap previews it like a pasted invite. + // Public suggestions render immediately, including on a first/offline visit. + // The bundled snapshot is a fallback; the device cache refreshes once a day. + // Invite previews still check revocation and expiration on the relays. const HUB_URL = 'https://vectorapp.io/api/hub'; - const hub = { state: 'idle', list: [] }; + const HUB_CACHE_KEY = 'coinos-community-directory-v1'; + const HUB_REFRESH_MS = 86400_000; + function normalizeHub(list) { + return list.filter((c) => c && c.name && parseInviteLink(c.invite_url || '')) + .map((c) => ({ + name: String(c.name).slice(0, 64), description: String(c.description || '').slice(0, 200), + category: String(c.category || '').slice(0, 64), invite: c.invite_url, + members: Math.max(0, Number(c.member_count) || 0), + icon: /^data:image\//.test(c.icon_url || '') && c.icon_url.length <= 32_768 ? c.icon_url : null, + })) + .sort((a, b) => b.members - a.members); + } + const hub = { list: normalizeHub(COMMUNITY_DIRECTORY), at: 0, attempted: false, loading: false }; + try { + const cached = JSON.parse(localStorage.getItem(HUB_CACHE_KEY) || 'null'); + if (cached && Array.isArray(cached.list) && Number.isFinite(cached.at)) { + const list = normalizeHub(cached.list); + if (list.length || !cached.list.length) { hub.list = list; hub.at = cached.at; } + } + } catch {} async function loadHub() { - if (hub.state !== 'idle') return; - hub.state = 'loading'; scheduleRepaint(); + if (hub.loading || hub.attempted || (hub.at > 0 && Date.now() - hub.at < HUB_REFRESH_MS)) return; + hub.attempted = true; hub.loading = true; try { const res = await fetch(HUB_URL, { signal: AbortSignal.timeout(20_000) }); if (!res.ok) throw new Error('http ' + res.status); - const list = await res.json(); - hub.list = (Array.isArray(list) ? list : []) - .filter((c) => c && c.name && parseInviteLink(c.invite_url || '')) - .map((c) => ({ - name: String(c.name).slice(0, 64), description: String(c.description || '').slice(0, 200), - category: String(c.category || ''), invite: c.invite_url, members: Number(c.member_count) || 0, - icon: /^data:image\//.test(c.icon_url || '') ? c.icon_url : null, official: !!c.official, - })) - .sort((a, b) => b.members - a.members); - hub.state = 'ready'; - } catch { hub.state = 'error'; } - scheduleRepaint(); + const raw = await res.json(); + if (!Array.isArray(raw)) throw new Error('invalid directory'); + const list = normalizeHub(raw); + if (raw.length && !list.length) throw new Error('invalid invites'); + hub.list = list; hub.at = Date.now(); + try { + // Bound icon sizes so public suggestions do not crowd out wallet data. + const cached = list.map((c) => ({ name: c.name, description: c.description, category: c.category, + invite_url: c.invite, member_count: c.members, icon_url: c.icon })); + localStorage.setItem(HUB_CACHE_KEY, JSON.stringify({ at: hub.at, list: cached })); + } catch {} + scheduleRepaint(); + } catch { /* Keep the usable directory if refresh fails or we're offline. */ } + finally { hub.loading = false; } } function hubRows({ preview = false } = {}) { const q = preview ? '' : (ui.msgJoinText || '').trim().toLowerCase(); @@ -1908,13 +1929,7 @@ export function messagesFeature(ctx) { h('div', { class: 'row between', style: 'align-items:baseline' }, h('h3', { style: 'margin:0' }, t(preview ? 'msgPopularCommunities' : 'msgDiscoverTitle')), preview ? h('button', { class: 'btn-sm', onClick: (e) => { e.stopPropagation(); ui.msgJoinText = ''; ui.msgHomePanel = 'join'; render(); } }, t('msgBrowseCommunities')) : null), - h('a', { class: 'small muted', href: 'https://vectorapp.io/hub/', target: '_blank', rel: 'noopener noreferrer' }, t('msgHubCredit')), - hub.state === 'error' ? h('div', { class: 'row gap6', style: 'align-items:center' }, - h('span', { class: 'small muted grow' }, t('msgHubFailed')), - h('button', { class: 'btn-sm', onClick: (e) => { e.stopPropagation(); hub.state = 'idle'; loadHub().catch(() => {}); } }, t('retry'))) - : hub.state !== 'ready' ? h('div', { class: 'row gap6', style: 'align-items:center;padding:6px 0' }, h('span', { class: 'spinner sm' }), h('span', { class: 'small muted' }, t('msgHubLoading'))) - : h('div', { class: 'list' + (preview ? '' : ' hub-list') }, hubRows({ preview })), - h('div', { class: 'small muted' }, t('msgWhiteNoiseDiscovery'))); + h('div', { class: 'list' + (preview ? '' : ' hub-list') }, hubRows({ preview }))); } function joinFromText(text) { diff --git a/src/i18n.js b/src/i18n.js index 8881cfe..20818c7 100644 --- a/src/i18n.js +++ b/src/i18n.js @@ -676,9 +676,7 @@ const STR = { msgDiscoverTitle: 'Suggested communities', msgPopularCommunities: 'Popular communities', msgBrowseCommunities: 'Browse all', - msgHubCredit: 'Concord · From the Vector Hub', msgHubNoSuggestions: 'No new communities to suggest right now.', - msgWhiteNoiseDiscovery: 'White Noise groups require an invitation. Ask a member to invite your npub.', msgHubLoading: 'Finding communities…', msgHubFailed: 'Could not load suggestions right now.', msgJoined: 'joined', |
