Home page server list to load from google published file
parent
dcd5f5771f
commit
ffe8bf1db2
|
|
@ -48,6 +48,7 @@
|
|||
"fs-extra": "^10.0.0",
|
||||
"klaw": "^3.0.0",
|
||||
"lottie-react": "^2.4.0",
|
||||
"papaparse": "^5.4.1",
|
||||
"sirv-cli": "^1.0.14",
|
||||
"vite": "^3.0.5"
|
||||
},
|
||||
|
|
@ -92,6 +93,7 @@
|
|||
"@types/lodash.defaultsdeep": "^4.6.6",
|
||||
"@types/lodash.isequal": "^4.5.5",
|
||||
"@types/node": "^15.14.9",
|
||||
"@types/papaparse": "^5.3.15",
|
||||
"@types/preact-i18n": "^2.3.0",
|
||||
"@types/prismjs": "^1.26.0",
|
||||
"@types/react-beautiful-dnd": "^13",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import {
|
|||
Lock,
|
||||
} from "@styled-icons/boxicons-solid";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import Papa from "papaparse";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import styled from "styled-components/macro";
|
||||
|
||||
|
|
@ -39,240 +41,116 @@ const DisabledButtonWrapper = styled.div`
|
|||
pointer-events: none;
|
||||
`;
|
||||
|
||||
export default observer(() => {
|
||||
interface Server {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
inviteCode: string;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
interface CachedData {
|
||||
timestamp: number;
|
||||
data: Server[];
|
||||
}
|
||||
|
||||
const CACHE_KEY = "server_list_cache";
|
||||
const CACHE_DURATION = 15 * 60 * 1000; // 15 minutes in milliseconds
|
||||
|
||||
// Safe localStorage wrapper
|
||||
const safeStorage = {
|
||||
getItem: (key: string): string | null => {
|
||||
try {
|
||||
return localStorage.getItem(key);
|
||||
} catch (e) {
|
||||
console.warn("Failed to read from localStorage:", e);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
setItem: (key: string, value: string): void => {
|
||||
try {
|
||||
localStorage.setItem(key, value);
|
||||
} catch (e) {
|
||||
console.warn("Failed to write to localStorage:", e);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const Home: React.FC = () => {
|
||||
const client = useClient();
|
||||
const [servers, setServers] = useState<Server[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const servers = [
|
||||
{
|
||||
id: "01J544PT4T3WQBVBSDK3TBFZW7",
|
||||
name: "PepChat Official",
|
||||
description:
|
||||
"Get your questions answered and stay up-to-date with the state of the project.",
|
||||
inviteCode: "pepchatdiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J5ZQMJSQ5AFZJJ3S204JK5Q4",
|
||||
name: "Elite Group Buy (EGB)",
|
||||
description: "Group buy peptides, amino blends & more.",
|
||||
inviteCode: "elitegroupbuydiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J545CBXQRWZZAASZQ6THKE96",
|
||||
name: "Qingdao Sigma Chemical (QSC)",
|
||||
description:
|
||||
"China wholesale bioactive compounds. (International, US, EU, Canada and Australia domestic)",
|
||||
inviteCode: "qscdiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J63A8HQ8S10MM4B3K85VMYBW",
|
||||
name: "Wonderland",
|
||||
description: "Peptide life social group.",
|
||||
inviteCode: "wonderlanddiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J5VPXSS0EK69QD69RX6SKZHW",
|
||||
name: "Kimmes Korner",
|
||||
description: "Peptide group buys.",
|
||||
inviteCode: "kimmeskornerdiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J5Z5QBQWREPZZPMVKJNCBDP2",
|
||||
name: "Joyous",
|
||||
description: "Peptide group buys.",
|
||||
inviteCode: "joyousdiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J6FNC5667A6RWV1SK4FMP19S",
|
||||
name: "Rabbit Hole Research",
|
||||
description:
|
||||
"A peptide research collective focused on community, education, and facilitating group buys.",
|
||||
inviteCode: "rabbitholediscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J6DDFWNT3SFKVQHK8J29RPXE",
|
||||
name: "Johnny 5",
|
||||
description:
|
||||
"Amazing community of helpful people. Focus on weight loss group buys.",
|
||||
inviteCode: "johnny5discover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J64CC6710N7CCWBBT625VXQ3",
|
||||
name: "The Raven Nest",
|
||||
description:
|
||||
"Group buys, protocols, social, and all things peptides.",
|
||||
inviteCode: "ravennestdiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J72VR94J6722AHF1MD33DB4F",
|
||||
name: "New Beginnings Research",
|
||||
description:
|
||||
"Peptide community focused on education, research, and organized group buys.",
|
||||
inviteCode: "newbeginningsdiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J6ZRS52BA42BJFVT0M4WY0Q6",
|
||||
name: "Deb's PepTalk",
|
||||
description: "Peptide GB's, education & ramblings.",
|
||||
inviteCode: "debspeptalkdiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J7E2NW9WXSHWJR7B75CDB2AC",
|
||||
name: "AOB",
|
||||
description: "Handmade organic beauty products",
|
||||
inviteCode: "aobdiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J6DHAK4RH0H6QK35CZ4G3ZSW",
|
||||
name: "Cousin Eddie's Corner",
|
||||
description: "Peptides with a dose of humour!",
|
||||
inviteCode: "cousineddiescornerdiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J6RS5RR3YKPMW09M7D71BTD2",
|
||||
name: "HYB",
|
||||
description: "China wholesale direct.",
|
||||
inviteCode: "hybdiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J740MT75NC05F6VB9EJ4Y115",
|
||||
name: "Royal Peptides",
|
||||
description:
|
||||
"USA domestic wholesale vendor with 3rd party tested kits.",
|
||||
inviteCode: "royalpeptidesdiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J78Z1C1XW209S5YSQZMPS0E4",
|
||||
name: "The Pep Planner",
|
||||
description:
|
||||
"Planner to keep track of daily pins, peptide information, orders & more.",
|
||||
inviteCode: "thepepplannerdiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J74BC8PFE9XBDX05J3Y9R9PV",
|
||||
name: "Monkey Peps",
|
||||
description:
|
||||
"A Peptide Community for support, sourcing and group testing.",
|
||||
inviteCode: "monkeypepsdiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J7EGW77XE2GSJGPR87MQXZW4",
|
||||
name: "SRY-LAB",
|
||||
description:
|
||||
"Peptide factory in China. Wholesale, retail and customization.",
|
||||
inviteCode: "srylabdiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J7NZR6KTG9BTRMNPCQQJ1VES",
|
||||
name: "Shanghai Nexa Pharma",
|
||||
description:
|
||||
"Ship from domestic USA 3-5 business days. Custom batch manufacture MOQ 300 vials.",
|
||||
inviteCode: "snpdiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J72F71TZWQFEBNSSFBMSDZK1",
|
||||
name: "Angel Shanghai Chem (ASC)",
|
||||
description: "Manufacturer of Peptides",
|
||||
inviteCode: "ascdiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J72C64KX97MP5K6ABDRP62P4",
|
||||
name: "The Hood",
|
||||
description:
|
||||
"Welcome to the neighbour-hood. A magical place full of potions and peps.",
|
||||
inviteCode: "thehooddiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J7RF37VXVMTS55K1C18PQ2HY",
|
||||
name: "Peppy Princess",
|
||||
description: "Beauty and skin-care experts.",
|
||||
inviteCode: "peppyprincessdiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J71Z3FVMJVCVCD8X4WGVR1SF",
|
||||
name: "JoLynn's World",
|
||||
description: "Pep talk and group buys.",
|
||||
inviteCode: "jolynnsworlddiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J84NMVTR2NQVHV9FQ1VR6YBN",
|
||||
name: "Henan Tirzepa Peptides",
|
||||
description:
|
||||
"Factory direct wholesale of peptide products, door-to-door delivery.",
|
||||
inviteCode: "henantirzepadiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J8CQBJRR8EYVQFM7ARD1P11P",
|
||||
name: "Peptopia",
|
||||
description:
|
||||
"Discussions about safe use, sourcing, testing, & more. GBs for Tirz, Reta, Sema, Cagri & more.",
|
||||
inviteCode: "peptopiadiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J8GZYC66E5T7PZNYVHD4DC6V",
|
||||
name: "Nantong Guangyuan Chemical (GYC)",
|
||||
description:
|
||||
"High quality peptides with 99% purity from manufacturers.",
|
||||
inviteCode: "gycdiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J9QDPBRHTCBV4DJ15G28393H",
|
||||
name: "Uther Pharmaceutical Peptide",
|
||||
description: "Chemistry changes the world.",
|
||||
inviteCode: "uppdiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J9R4AP31FG4VX4FTZTSMWHFF",
|
||||
name: "Tianjin Cangtu",
|
||||
description:
|
||||
"Direct factory supply with 99%+ purity and safe delivery.",
|
||||
inviteCode: "tianjincangtudiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01JAJBYY4N7ZATDG446M4XGTMA",
|
||||
name: "Shanghai Sigma Audley",
|
||||
description:
|
||||
"China peptides, steroid tablets, oil, and APIs supplier.",
|
||||
inviteCode: "shanghaisigmaaudleydiscover",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "01J5TQYA639STTEX7SH5KXC96M",
|
||||
name: "Joe Lu's Hideout",
|
||||
description: "Peptide group buys.",
|
||||
inviteCode: "placeholder",
|
||||
disabled: true,
|
||||
},
|
||||
];
|
||||
const fetchAndCacheData = async () => {
|
||||
try {
|
||||
const csvUrl =
|
||||
"https://docs.google.com/spreadsheets/d/e/2PACX-1vRY41D-NgTE6bC3kTN3dRpisI-DoeHG8Eg7n31xb1CdydWjOLaphqYckkTiaG9oIQSWP92h3NE-7cpF/pub?gid=0&single=true&output=csv";
|
||||
|
||||
const renderServerButton = (server) => {
|
||||
Papa.parse<Server>(csvUrl, {
|
||||
download: true,
|
||||
header: true,
|
||||
dynamicTyping: true,
|
||||
complete: (result) => {
|
||||
if (result.errors.length > 0) {
|
||||
console.error("CSV parsing errors:", result.errors);
|
||||
setError("Error parsing server data");
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const cacheData: CachedData = {
|
||||
timestamp: Date.now(),
|
||||
data: result.data,
|
||||
};
|
||||
|
||||
safeStorage.setItem(CACHE_KEY, JSON.stringify(cacheData));
|
||||
setServers(result.data);
|
||||
setLoading(false);
|
||||
},
|
||||
error: (err) => {
|
||||
console.error("Error fetching CSV:", err);
|
||||
setError(
|
||||
"Failed to load server data. Please try again later.",
|
||||
);
|
||||
setLoading(false);
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Unexpected error:", err);
|
||||
setError("An unexpected error occurred. Please try again later.");
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const getCachedOrFetchData = async () => {
|
||||
try {
|
||||
const cachedData = safeStorage.getItem(CACHE_KEY);
|
||||
|
||||
if (cachedData) {
|
||||
const parsed: CachedData = JSON.parse(cachedData);
|
||||
const isExpired =
|
||||
Date.now() - parsed.timestamp > CACHE_DURATION;
|
||||
|
||||
if (!isExpired && Array.isArray(parsed.data)) {
|
||||
setServers(parsed.data);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn("Error reading cache:", err);
|
||||
// Continue to fetch fresh data if cache read fails
|
||||
}
|
||||
|
||||
await fetchAndCacheData();
|
||||
};
|
||||
|
||||
getCachedOrFetchData();
|
||||
}, []);
|
||||
|
||||
const renderServerButton = (server: Server) => {
|
||||
const isServerJoined = client.servers.get(server.id);
|
||||
const linkTo = isServerJoined
|
||||
? `/server/${server.id}`
|
||||
|
|
@ -298,7 +176,9 @@ export default observer(() => {
|
|||
|
||||
if (server.disabled) {
|
||||
return (
|
||||
<DisabledButtonWrapper>{buttonContent}</DisabledButtonWrapper>
|
||||
<DisabledButtonWrapper key={server.id}>
|
||||
{buttonContent}
|
||||
</DisabledButtonWrapper>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
|
|
@ -309,6 +189,14 @@ export default observer(() => {
|
|||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <div>{error}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.home}>
|
||||
<Overlay>
|
||||
|
|
@ -323,7 +211,9 @@ export default observer(() => {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Overlay>{" "}
|
||||
</Overlay>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export default observer(Home);
|
||||
|
|
|
|||
18
yarn.lock
18
yarn.lock
|
|
@ -2731,6 +2731,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/papaparse@npm:^5.3.15":
|
||||
version: 5.3.15
|
||||
resolution: "@types/papaparse@npm:5.3.15"
|
||||
dependencies:
|
||||
"@types/node": "*"
|
||||
checksum: 265cc2fd7e36514568398491a9b1d47ad4408f3946fd19f69202ef7af313c8d094ef1562172a36bec6e34ae8e9daf80fc1c5a36327858c236c50c6ebaac3e221
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/parse-json@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "@types/parse-json@npm:4.0.0"
|
||||
|
|
@ -3724,6 +3733,7 @@ __metadata:
|
|||
"@types/lodash.defaultsdeep": ^4.6.6
|
||||
"@types/lodash.isequal": ^4.5.5
|
||||
"@types/node": ^15.14.9
|
||||
"@types/papaparse": ^5.3.15
|
||||
"@types/preact-i18n": ^2.3.0
|
||||
"@types/prismjs": ^1.26.0
|
||||
"@types/react-beautiful-dnd": ^13
|
||||
|
|
@ -3759,6 +3769,7 @@ __metadata:
|
|||
mediasoup-client: "npm:@insertish/mediasoup-client@3.6.36-esnext"
|
||||
mobx: ^6.6.0
|
||||
mobx-react-lite: 3.4.0
|
||||
papaparse: ^5.4.1
|
||||
preact: ^10.5.14
|
||||
preact-context-menu: 0.4.1
|
||||
preact-i18n: ^2.4.0-preactx
|
||||
|
|
@ -7324,6 +7335,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"papaparse@npm:^5.4.1":
|
||||
version: 5.4.1
|
||||
resolution: "papaparse@npm:5.4.1"
|
||||
checksum: fc9e52f7158dca3517c229e3309065b1ab5da6c7194572fba4f31ff138bc43e3c91182cc40365cc828f97fe10d0aca416068fd731661058bea0f69ddb84a411a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"parent-module@npm:^1.0.0":
|
||||
version: 1.0.1
|
||||
resolution: "parent-module@npm:1.0.1"
|
||||
|
|
|
|||
Loading…
Reference in New Issue