Skip to content

Commit f67ea69

Browse files
authored
Merge pull request #94 from AugustoL/about-page
Redesign About page with vision, principles, and GitHub stats
2 parents aa9c970 + 8e0bece commit f67ea69

3 files changed

Lines changed: 360 additions & 65 deletions

File tree

‎src/components/pages/about/index.tsx‎

Lines changed: 220 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,207 @@
11
import type React from "react";
2+
import { useEffect, useState } from "react";
3+
import { getEnabledNetworks } from "../../../config/networks";
4+
import NetworkIcon from "../../common/NetworkIcon";
5+
6+
interface GitHubStats {
7+
commits: number | null;
8+
contributors: number | null;
9+
}
210

311
const About: React.FC = () => {
412
const appVersion = process.env.REACT_APP_VERSION || "0.1.0";
513
const commitHash = process.env.REACT_APP_COMMIT_HASH || "development";
614
const formattedCommitHash = commitHash.length > 7 ? commitHash.substring(0, 7) : commitHash;
715

16+
const [githubStats, setGithubStats] = useState<GitHubStats>({
17+
commits: null,
18+
contributors: null,
19+
});
20+
21+
useEffect(() => {
22+
const fetchGitHubStats = async () => {
23+
try {
24+
// Fetch contributors count
25+
const contributorsRes = await fetch(
26+
"https://api.github.com/repos/openscan-explorer/explorer/contributors?per_page=1",
27+
);
28+
if (contributorsRes.ok) {
29+
const linkHeader = contributorsRes.headers.get("Link");
30+
if (linkHeader) {
31+
const match = linkHeader.match(/page=(\d+)>; rel="last"/);
32+
const lastPage = match?.[1];
33+
if (lastPage) {
34+
setGithubStats((prev) => ({ ...prev, contributors: parseInt(lastPage, 10) }));
35+
}
36+
} else {
37+
const contributors = await contributorsRes.json();
38+
setGithubStats((prev) => ({ ...prev, contributors: contributors.length }));
39+
}
40+
}
41+
42+
// Fetch commit count from default branch
43+
const repoRes = await fetch(
44+
"https://api.github.com/repos/openscan-explorer/explorer/commits?per_page=1",
45+
);
46+
if (repoRes.ok) {
47+
const linkHeader = repoRes.headers.get("Link");
48+
if (linkHeader) {
49+
const match = linkHeader.match(/page=(\d+)>; rel="last"/);
50+
const lastPage = match?.[1];
51+
if (lastPage) {
52+
setGithubStats((prev) => ({ ...prev, commits: parseInt(lastPage, 10) }));
53+
}
54+
}
55+
}
56+
} catch (error) {
57+
console.error("Failed to fetch GitHub stats:", error);
58+
}
59+
};
60+
61+
fetchGitHubStats();
62+
}, []);
63+
64+
const visionCommitments = [
65+
{
66+
title: "Trusted Open-Source Alternative",
67+
description:
68+
"To be recognised as the most trusted and best alternative to existing, centralised explorer services.",
69+
},
70+
{
71+
title: "Open Standard Adoption",
72+
description:
73+
"To serve as a ubiquitous open standard, readily adopted by major wallets, dApps, and blockchain networks.",
74+
},
75+
{
76+
title: "Lean and Transparent Team",
77+
description:
78+
"To maintain a small, focused, high-quality core team operating with complete financial and operational transparency.",
79+
},
80+
{
81+
title: "Growth over Extraction",
82+
description:
83+
"To ensure all generated revenue is reinvested solely for the platform's continuous development and growth.",
84+
},
85+
];
86+
87+
const corePrinciples = [
88+
{
89+
icon: "👤",
90+
title: "User-First Mandate",
91+
description:
92+
"The platform is permanently free, fully open, and completely transparent for all end-users.",
93+
},
94+
{
95+
icon: "🔒",
96+
title: "Privacy & Experience",
97+
description: "No invasive advertising, trackers, or hidden user-data harvesting.",
98+
},
99+
{
100+
icon: "📖",
101+
title: "Open-Source & Auditable",
102+
description:
103+
"The entire codebase is publicly available, auditable, and encourages community contribution.",
104+
},
105+
{
106+
icon: "🌱",
107+
title: "Organic Growth",
108+
description:
109+
"Success is driven by technical excellence, accessibility, trustworthiness, and organic adoption.",
110+
},
111+
{
112+
icon: "🏛️",
113+
title: "Infrastructure as a Public Good",
114+
description:
115+
"The decentralised ecosystem thrives when core infrastructure is free, accessible, and neutral.",
116+
},
117+
];
118+
119+
const features = [
120+
{
121+
title: "Multi-Chain Support",
122+
description:
123+
"Explore Ethereum Mainnet, Sepolia, Arbitrum One, Optimism, Base, and local development networks.",
124+
},
125+
{
126+
title: "Block Explorer",
127+
description:
128+
"View detailed information about blocks, transactions, and addresses across all supported chains.",
129+
},
130+
{
131+
title: "Contract Verification",
132+
description:
133+
"Integration with Sourcify API to display verified contract source code, ABI, and metadata.",
134+
},
135+
{
136+
title: "Real-Time Data",
137+
description: "Live network statistics, gas prices, and blockchain data for supported chains.",
138+
},
139+
{
140+
title: "Modern UI",
141+
description:
142+
"Clean, responsive interface with dark theme optimized for blockchain data visualization.",
143+
},
144+
{
145+
title: "Fast Performance",
146+
description:
147+
"Efficient caching and lazy loading for quick data access and smooth user experience.",
148+
},
149+
];
150+
151+
const networks = getEnabledNetworks();
152+
8153
return (
9154
<div className="container-medium page-container-padded">
10155
<div className="page-card">
11156
{/* Header */}
12157
<div className="text-center mb-large">
13-
<h1 className="page-heading">About OpenScan</h1>
14-
<p className="page-subtitle">
15-
An open-source, lightweight, multi-chain blockchain explorer
158+
<h1 className="page-heading">OpenScan</h1>
159+
<p className="page-subtitle">A Decentralised Explorer</p>
160+
</div>
161+
162+
{/* Vision Section */}
163+
<div className="about-section">
164+
<p className="about-section-intro">
165+
Our long-term vision is to establish the most accessible, neutral, and transparent
166+
blockchain exploration infrastructure globally—one that is{" "}
167+
<strong>not governed by private companies</strong> and sustained by ethical,
168+
non-extractive revenue models.
16169
</p>
170+
<div className="data-grid-2 mb-large">
171+
{visionCommitments.map((item) => (
172+
<div key={item.title} className="feature-card text-center">
173+
<h3 className="feature-card-title">{item.title}</h3>
174+
<p className="feature-card-description">{item.description}</p>
175+
</div>
176+
))}
177+
</div>
17178
</div>
18179

19-
{/* Version Info Card */}
20-
<div className="version-card">
21-
<div className="version-card-grid">
22-
<div className="version-info-item">
23-
<div className="version-info-label">Version</div>
24-
<div className="version-info-value">{appVersion}</div>
25-
</div>
26-
<div className="version-info-item">
27-
<div className="version-info-label">Commit</div>
28-
<div className="version-info-value-mono">{formattedCommitHash}</div>
29-
</div>
180+
{/* Core Principles Section */}
181+
<div className="about-section">
182+
<h2 className="page-heading-large text-center">Core Principles</h2>
183+
<p className="about-section-intro">
184+
OpenScan is not merely a product; it is a declaration of principles for public
185+
blockchain infrastructure.
186+
</p>
187+
<div className="principles-list">
188+
{corePrinciples.map((principle) => (
189+
<div key={principle.title} className="principle-item">
190+
<span className="principle-icon">{principle.icon}</span>
191+
<div className="principle-content">
192+
<h4 className="principle-title">{principle.title}</h4>
193+
<p className="principle-description">{principle.description}</p>
194+
</div>
195+
</div>
196+
))}
30197
</div>
31198
</div>
32199

33200
{/* Features Grid */}
34201
<h2 className="page-heading-large text-center">Features</h2>
35-
36-
<div className="data-grid-3 mb-large">
37-
{[
38-
{
39-
title: "🌐 Multi-Chain Support",
40-
description:
41-
"Explore Ethereum Mainnet, Sepolia, Arbitrum One, Optimism, and local development networks.",
42-
},
43-
{
44-
title: "🔍 Block Explorer",
45-
description:
46-
"View detailed information about blocks, transactions, and addresses across all supported chains.",
47-
},
48-
{
49-
title: "✅ Contract Verification",
50-
description:
51-
"Integration with Sourcify API to display verified contract source code, ABI, and metadata.",
52-
},
53-
{
54-
title: "📊 Real-Time Data",
55-
description:
56-
"Live network statistics, gas prices, and mempool monitoring for supported chains.",
57-
},
58-
{
59-
title: "🎨 Modern UI",
60-
description:
61-
"Clean, responsive interface with dark theme optimized for blockchain data visualization.",
62-
},
63-
{
64-
title: "⚡ Fast Performance",
65-
description:
66-
"Efficient caching and lazy loading for quick data access and smooth user experience.",
67-
},
68-
].map((feature) => (
69-
<div key={feature.title} className="feature-card">
202+
<div className="data-grid-3-centered mb-large">
203+
{features.map((feature) => (
204+
<div key={feature.title} className="feature-card text-center">
70205
<h3 className="feature-card-title">{feature.title}</h3>
71206
<p className="feature-card-description">{feature.description}</p>
72207
</div>
@@ -75,20 +210,12 @@ const About: React.FC = () => {
75210

76211
{/* Supported Networks */}
77212
<h2 className="page-heading-large text-center">Supported Networks</h2>
78-
79-
<div className="data-grid-3 mb-large">
80-
{[
81-
{ name: "Ethereum Mainnet", chainId: "1", icon: "⟠" },
82-
{ name: "Sepolia Testnet", chainId: "11155111", icon: "🧪" },
83-
{ name: "Arbitrum One", chainId: "42161", icon: "🔷" },
84-
{ name: "Optimism", chainId: "10", icon: "🔴" },
85-
{ name: "Localhost", chainId: "31337", icon: "💻" },
86-
].map((network) => (
87-
<div key={network.chainId} className="network-item">
88-
<div className="network-icon-box">{network.icon}</div>
213+
<div className="data-grid-3-centered mb-large">
214+
{networks.map((network) => (
215+
<div key={network.networkId} className="network-item text-center">
89216
<div className="network-info">
217+
<NetworkIcon network={network} size={32} />
90218
<div className="network-name">{network.name}</div>
91-
<div className="network-chain-id">Chain ID: {network.chainId}</div>
92219
</div>
93220
</div>
94221
))}
@@ -98,23 +225,51 @@ const About: React.FC = () => {
98225
<div className="text-center mb-large opensource-card">
99226
<h2 className="opensource-title">Open Source</h2>
100227
<p className="opensource-description">
101-
OpenScan is free and open source software. Contributions, issues, and feature requests
102-
are welcome!
228+
OpenScan is free and open source software. The entire codebase is publicly available,
229+
auditable, and welcomes community contribution.
103230
</p>
104231
<a
105232
href="https://github.com/openscan-explorer/explorer"
106233
target="_blank"
107234
rel="noopener noreferrer"
108235
className="button-primary-inline"
109236
>
110-
View on GitHub ↗
237+
View on GitHub
111238
</a>
112239
</div>
113240

241+
{/* Version Info Card */}
242+
<div className="version-card">
243+
<div className="version-card-grid-4 text-center">
244+
<div className="version-info-item">
245+
<div className="version-info-label">Version</div>
246+
<div className="version-info-value">{appVersion}</div>
247+
</div>
248+
<div className="version-info-item">
249+
<div className="version-info-label">Commit</div>
250+
<div className="version-info-value-mono">{formattedCommitHash}</div>
251+
</div>
252+
<div className="version-info-item">
253+
<div className="version-info-label">Total Commits</div>
254+
<div className="version-info-value">
255+
{githubStats.commits !== null ? githubStats.commits.toLocaleString() : "—"}
256+
</div>
257+
</div>
258+
<div className="version-info-item">
259+
<div className="version-info-label">Contributors</div>
260+
<div className="version-info-value">
261+
{githubStats.contributors !== null ? githubStats.contributors : "—"}
262+
</div>
263+
</div>
264+
</div>
265+
</div>
266+
114267
{/* License */}
115268
<div className="text-center license-footer">
116269
<p>OpenScan is licensed under the MIT License</p>
117-
<p className="license-footer-spacing">Built with ❤️ for the Ethereum community</p>
270+
<p className="license-footer-spacing">
271+
Built with purpose for the decentralised ecosystem
272+
</p>
118273
</div>
119274
</div>
120275
</div>

0 commit comments

Comments
 (0)