Skip to content

Commit 317f6fe

Browse files
vplasenciacedoor
andauthored
feat: add new semaphore explorer to apps (#19)
* feat: add new semaphore explorer to apps re #1 * style(explorer): fix code style with prettier * Update apps/explorer/LICENSE Co-authored-by: Cedoor <me@cedoor.dev> * Update apps/explorer/README.md Co-authored-by: Cedoor <me@cedoor.dev> * feat: improve explorer ux (#20) * fix(explorer): fix filtered groups search * feat(explorer): add loader to pages * chore(explorer): add website metadata --------- Co-authored-by: Cedoor <me@cedoor.dev>
1 parent 95f15e6 commit 317f6fe

File tree

21 files changed

+1446
-11
lines changed

21 files changed

+1446
-11
lines changed

.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,12 @@ node_modules/
3030

3131
# TypeScript cache
3232
*.tsbuildinfo
33+
next-env.d.ts
3334

3435
# Optional npm cache directory
3536
.npm
3637
.DS_Store
38+
*.pem
3739

3840
# Output of 'npm pack'
3941
*.tgz
@@ -94,3 +96,13 @@ typechain-types
9496

9597
# Other
9698
snark-artifacts
99+
100+
# vercel
101+
.vercel
102+
103+
# local env files
104+
.env*.local
105+
106+
# next.js
107+
.next/
108+
out/

apps/explorer/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 Ethereum Foundation
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

apps/explorer/README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<h1 align="center">
2+
Semaphore Explorer
3+
</h1>
4+
5+
<p align="center">
6+
<a href="https://github.com/semaphore-protocol" target="_blank">
7+
<img src="https://img.shields.io/badge/project-Semaphore-blue.svg?style=flat-square">
8+
</a>
9+
<a href="https://github.com/semaphore-protocol/extensions/blob/main/LICENSE">
10+
<img alt="Github license" src="https://img.shields.io/github/license/semaphore-protocol/extensions">
11+
</a>
12+
<a href="https://prettier.io/" target="_blank">
13+
<img alt="Code style prettier" src="https://img.shields.io/badge/code%20style-prettier-f8bc45?style=flat-square&logo=prettier">
14+
</a>
15+
</p>
16+
17+
<div align="center">
18+
<h4>
19+
<a href="../../CONTRIBUTING.md">
20+
👥 Contributing
21+
</a>
22+
<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
23+
<a href="../../CODE_OF_CONDUCT.md">
24+
🤝 Code of conduct
25+
</a>
26+
<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
27+
<a href="https://github.com/semaphore-protocol/extensions/issues/new/choose">
28+
🔎 Issues
29+
</a>
30+
<span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
31+
<a href="https://semaphore.pse.dev/telegram">
32+
🗣️ Chat &amp; Support
33+
</a>
34+
</h4>
35+
</div>
36+
37+
| View and explore on-chain semaphore data from multiple networks in a user-friendly way. Powered by the Semaphore subgraph and the [@semaphore-protocol/data](https://github.com/semaphore-protocol/semaphore/tree/main/packages/data) library, this read-only web application provides a comprehensive and intuitive interface for analyzing blockchain data.
38+
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
39+
40+
## 📜 Usage
41+
42+
### Start
43+
44+
To navigate inside the `explorer` folder, run:
45+
46+
```bash
47+
cd benchmarks
48+
```
49+
50+
To start the web app in a local server, run:
51+
52+
```sh
53+
yarn dev
54+
```
55+
56+
### Build
57+
58+
To build the web app, run:
59+
60+
```
61+
yarn build
62+
```
63+
64+
The `build` command generates static content into the `build` directory that can be served by any static content hosting service.

apps/explorer/next.config.mjs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/** @type {import('next').NextConfig} */
2+
const nextConfig = {
3+
async redirects() {
4+
return [
5+
{
6+
source: "/",
7+
destination: "/sepolia",
8+
permanent: true
9+
}
10+
]
11+
},
12+
reactStrictMode: false
13+
}
14+
15+
export default nextConfig

apps/explorer/package.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "explorer",
3+
"private": true,
4+
"scripts": {
5+
"dev": "next dev",
6+
"build": "next build",
7+
"start": "next start",
8+
"lint": "next lint"
9+
},
10+
"dependencies": {
11+
"@headlessui/react": "^2.1.10",
12+
"@semaphore-protocol/data": "^4.3.1",
13+
"@semaphore-protocol/utils": "^4.3.1",
14+
"next": "14.2.14",
15+
"react": "^18",
16+
"react-dom": "^18",
17+
"react-icons": "^5.3.0"
18+
},
19+
"devDependencies": {
20+
"@types/node": "^20",
21+
"@types/react": "^18",
22+
"@types/react-dom": "^18",
23+
"postcss": "^8",
24+
"tailwindcss": "^3.4.1",
25+
"typescript": "^5"
26+
}
27+
}

apps/explorer/postcss.config.mjs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/** @type {import('postcss-load-config').Config} */
2+
const config = {
3+
plugins: {
4+
tailwindcss: {}
5+
}
6+
}
7+
8+
export default config
4.13 KB
Loading

apps/explorer/public/icon.svg

Lines changed: 4 additions & 0 deletions
Loading
190 KB
Loading
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
"use client"
2+
3+
import { GroupResponse, SemaphoreSubgraph } from "@semaphore-protocol/data"
4+
import { SupportedNetwork } from "@semaphore-protocol/utils"
5+
import { usePathname } from "next/navigation"
6+
import { useCallback, useEffect, useState } from "react"
7+
import SearchBar from "@/components/SearchBar"
8+
9+
export default function Group() {
10+
const pathname = usePathname()
11+
const network = pathname.split("/")[1] as SupportedNetwork
12+
const groupId = pathname.split("/")[2] as SupportedNetwork
13+
14+
const [group, setGroup] = useState<GroupResponse>()
15+
const [filteredCommitments, setFilteredCommitments] = useState<string[]>([])
16+
const [filteredProofs, setFilteredProofs] = useState<any[]>([])
17+
18+
const [loading, setLoading] = useState(false)
19+
20+
useEffect(() => {
21+
const fetchData = async () => {
22+
setLoading(true)
23+
const subgraph = new SemaphoreSubgraph(network)
24+
25+
const groupInfo = await subgraph.getGroup(groupId, {
26+
members: true,
27+
validatedProofs: true
28+
})
29+
30+
setGroup(groupInfo)
31+
32+
setFilteredCommitments(groupInfo.members || [])
33+
setFilteredProofs(groupInfo.validatedProofs || [])
34+
setLoading(false)
35+
}
36+
37+
fetchData()
38+
}, [])
39+
40+
const filterCommitments = useCallback(
41+
(identityCommitment: string) => {
42+
if (group && group.members) {
43+
const identityCommitments = group.members.filter((member) =>
44+
!identityCommitment ? true : member.includes(identityCommitment)
45+
)
46+
47+
setFilteredCommitments(identityCommitments)
48+
}
49+
},
50+
[group]
51+
)
52+
53+
const filterProofs = useCallback(
54+
(proofMessage: string) => {
55+
if (group && group.validatedProofs) {
56+
const proofs = group.validatedProofs.filter((proof) =>
57+
!proofMessage ? true : proof.message.includes(proofMessage)
58+
)
59+
60+
setFilteredProofs(proofs)
61+
}
62+
},
63+
[group]
64+
)
65+
66+
return loading ? (
67+
<div className="flex justify-center items-center h-screen">
68+
<div className="loader" />
69+
</div>
70+
) : (
71+
group && (
72+
<div className="mx-auto max-w-7xl px-4 lg:px-8 pt-20">
73+
<div className="flex justify-center flex-col pb-10 font-[family-name:var(--font-geist-sans)]">
74+
<div className="flex justify-between gap-x-6 py-5">
75+
<div className="min-w-0 flex-auto">
76+
<p className="text-sm font-semibold leading-6 text-gray-800">ID: {group.id}</p>
77+
<p className="mt-1 truncate text-sm leading-6 text-gray-600">Admin: {group.admin}</p>
78+
</div>
79+
<div className="hidden shrink-0 sm:flex sm:flex-col sm:items-end">
80+
<p className="text-sm leading-6 text-gray-600">{group.members?.length} members</p>
81+
<p className="mt-1 text-sm leading-6 text-gray-600">
82+
{group.validatedProofs?.length} proofs
83+
</p>
84+
</div>
85+
</div>
86+
87+
<div className="flex gap-4 flex-col md:flex-row">
88+
<div className="min-w-0 flex-auto">
89+
<SearchBar
90+
className="mb-6"
91+
placeholder="Identity commitment"
92+
onChange={filterCommitments}
93+
/>
94+
95+
<ul className="divide-y divide-gray-300">
96+
{filteredCommitments.map((commitment) => (
97+
<li className="flex justify-between gap-x-6 py-2 px-5" key={commitment}>
98+
<p className="mt-1 truncate text-xs leading-5 text-gray-500">{commitment}</p>
99+
</li>
100+
))}
101+
</ul>
102+
</div>
103+
104+
<div className="min-w-0 flex-auto">
105+
<SearchBar className="mb-6" placeholder="Proof message" onChange={filterProofs} />
106+
107+
<ul className="divide-y divide-gray-300">
108+
{filteredProofs.map((proof) => (
109+
<li className="flex justify-between gap-x-6 py-2 px-5" key={proof.nullifier}>
110+
<p className="mt-1 truncate text-xs leading-5 text-gray-500">{proof.message}</p>
111+
</li>
112+
))}
113+
</ul>
114+
</div>
115+
</div>
116+
</div>
117+
</div>
118+
)
119+
)
120+
}

0 commit comments

Comments
 (0)