mirror of
https://github.com/social-tw/social-tw-website.git
synced 2026-01-08 23:18:05 -05:00
feat: style Post component
This commit is contained in:
@@ -1,193 +1 @@
|
||||
[
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "contract Unirep",
|
||||
"name": "_unirep",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "contract IVerifier",
|
||||
"name": "_dataVerifier",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint48",
|
||||
"name": "_epochLength",
|
||||
"type": "uint48"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "uint256",
|
||||
"name": "epochKey",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "uint256",
|
||||
"name": "postId",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "string",
|
||||
"name": "content",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"name": "Post",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{ "internalType": "uint256", "name": "", "type": "uint256" }
|
||||
],
|
||||
"name": "epochKeyPostIndex",
|
||||
"outputs": [
|
||||
{ "internalType": "uint256", "name": "", "type": "uint256" }
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{ "internalType": "uint256", "name": "", "type": "uint256" },
|
||||
{ "internalType": "uint256", "name": "", "type": "uint256" }
|
||||
],
|
||||
"name": "epochKeyPostVoteMap",
|
||||
"outputs": [
|
||||
{ "internalType": "uint256", "name": "upVote", "type": "uint256" },
|
||||
{ "internalType": "uint256", "name": "downVote", "type": "uint256" }
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256[]",
|
||||
"name": "publicSignals",
|
||||
"type": "uint256[]"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256[8]",
|
||||
"name": "proof",
|
||||
"type": "uint256[8]"
|
||||
},
|
||||
{ "internalType": "string", "name": "content", "type": "string" }
|
||||
],
|
||||
"name": "post",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{ "internalType": "bytes32", "name": "", "type": "bytes32" }
|
||||
],
|
||||
"name": "proofNullifier",
|
||||
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "epochKey",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint48",
|
||||
"name": "targetEpoch",
|
||||
"type": "uint48"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "fieldIndex",
|
||||
"type": "uint256"
|
||||
},
|
||||
{ "internalType": "uint256", "name": "val", "type": "uint256" }
|
||||
],
|
||||
"name": "submitAttestation",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "epochKey",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint48",
|
||||
"name": "targetEpoch",
|
||||
"type": "uint48"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256[]",
|
||||
"name": "fieldIndices",
|
||||
"type": "uint256[]"
|
||||
},
|
||||
{ "internalType": "uint256[]", "name": "vals", "type": "uint256[]" }
|
||||
],
|
||||
"name": "submitManyAttestations",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "unirep",
|
||||
"outputs": [
|
||||
{ "internalType": "contract Unirep", "name": "", "type": "address" }
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256[]",
|
||||
"name": "publicSignals",
|
||||
"type": "uint256[]"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256[8]",
|
||||
"name": "proof",
|
||||
"type": "uint256[8]"
|
||||
}
|
||||
],
|
||||
"name": "userSignUp",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256[5]",
|
||||
"name": "publicSignals",
|
||||
"type": "uint256[5]"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256[8]",
|
||||
"name": "proof",
|
||||
"type": "uint256[8]"
|
||||
}
|
||||
],
|
||||
"name": "verifyDataProof",
|
||||
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
[{"inputs":[{"internalType":"contract Unirep","name":"_unirep","type":"address"},{"internalType":"contract IVerifier","name":"_dataVerifier","type":"address"},{"internalType":"uint48","name":"_epochLength","type":"uint48"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epochKey","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"postId","type":"uint256"},{"indexed":false,"internalType":"string","name":"content","type":"string"}],"name":"Post","type":"event"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"epochKeyPostIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"epochKeyPostVoteMap","outputs":[{"internalType":"uint256","name":"upVote","type":"uint256"},{"internalType":"uint256","name":"downVote","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"publicSignals","type":"uint256[]"},{"internalType":"uint256[8]","name":"proof","type":"uint256[8]"},{"internalType":"string","name":"content","type":"string"}],"name":"post","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"proofNullifier","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epochKey","type":"uint256"},{"internalType":"uint48","name":"targetEpoch","type":"uint48"},{"internalType":"uint256","name":"fieldIndex","type":"uint256"},{"internalType":"uint256","name":"val","type":"uint256"}],"name":"submitAttestation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"epochKey","type":"uint256"},{"internalType":"uint48","name":"targetEpoch","type":"uint48"},{"internalType":"uint256[]","name":"fieldIndices","type":"uint256[]"},{"internalType":"uint256[]","name":"vals","type":"uint256[]"}],"name":"submitManyAttestations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unirep","outputs":[{"internalType":"contract Unirep","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"publicSignals","type":"uint256[]"},{"internalType":"uint256[8]","name":"proof","type":"uint256[8]"}],"name":"userSignUp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[5]","name":"publicSignals","type":"uint256[5]"},{"internalType":"uint256[8]","name":"proof","type":"uint256[8]"}],"name":"verifyDataProof","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]
|
||||
@@ -18,11 +18,14 @@
|
||||
"@lexical/react": "^0.11.1",
|
||||
"@unirep-app/circuits": "1.0.0",
|
||||
"@unirep/core": "2.0.0-beta-3",
|
||||
"boring-avatars": "^1.10.1",
|
||||
"dayjs": "^1.11.9",
|
||||
"ethers": "^5.7.2",
|
||||
"file-loader": "^6.2.0",
|
||||
"iconoir-react": "^6.9.0",
|
||||
"lexical": "^0.11.1",
|
||||
"mobx-react-lite": "^3.4.0"
|
||||
"mobx-react-lite": "^3.4.0",
|
||||
"react-twitter-login": "^1.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.19.6",
|
||||
|
||||
69
packages/frontend/src/components/Post.tsx
Normal file
69
packages/frontend/src/components/Post.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import Avatar from 'boring-avatars'
|
||||
import dayjs from 'dayjs'
|
||||
import { ArrowDown, ArrowUp } from 'iconoir-react'
|
||||
|
||||
export default function ({
|
||||
epochKey,
|
||||
content = '',
|
||||
publishedAt = new Date(),
|
||||
commentCount = 0,
|
||||
upCount = 0,
|
||||
downCount = 0,
|
||||
}: {
|
||||
epochKey: string
|
||||
content: string
|
||||
publishedAt: Date
|
||||
commentCount: number
|
||||
upCount: number
|
||||
downCount: number
|
||||
}) {
|
||||
const publishedTime = dayjs(publishedAt)
|
||||
const publishedLabel = publishedTime.isBefore(dayjs())
|
||||
? publishedTime.format('YYYY/MM/DD')
|
||||
: publishedTime.fromNow()
|
||||
|
||||
return (
|
||||
<article className="flex items-start gap-6">
|
||||
<section className="w-24 h-24">
|
||||
<div className="border-2 border-white rounded-full avatar">
|
||||
<Avatar
|
||||
size={80}
|
||||
name={epochKey}
|
||||
variant="beam"
|
||||
colors={[
|
||||
'#92A1C6',
|
||||
'#146A7C',
|
||||
'#F0AB3D',
|
||||
'#C271B4',
|
||||
'#C20D90',
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<div className="flex items-start gap-4 px-8 py-8 bg-white rounded-3xl">
|
||||
<section className="flex flex-col items-center">
|
||||
<button className="btn btn-circle btn-ghost">
|
||||
<ArrowUp strokeWidth={3} />
|
||||
</button>
|
||||
<span className="inline-flex items-center justify-center w-12 h-12 text-lg font-medium">
|
||||
{upCount - downCount}
|
||||
</span>
|
||||
<button className="btn btn-circle btn-ghost">
|
||||
<ArrowDown strokeWidth={3} />
|
||||
</button>
|
||||
</section>
|
||||
<section>
|
||||
<p className="text-2xl font-medium text-black">{content}</p>
|
||||
<div className="flex justify-between mt-8">
|
||||
<span className="text-2xl text-blue-300">
|
||||
{publishedLabel}
|
||||
</span>
|
||||
<span className="text-2xl text-blue-300">
|
||||
{commentCount} 則留言
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</article>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
// import './index.css'
|
||||
import './styles/main.css'
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import { BrowserRouter, Route, Routes } from 'react-router-dom'
|
||||
import Dashboard from './pages/Dashboard'
|
||||
@@ -8,6 +10,8 @@ import PostCreate from './pages/PostCreate'
|
||||
import PostList from './pages/PostList'
|
||||
import Start from './pages/Start'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<BrowserRouter>
|
||||
|
||||
@@ -1,68 +1,74 @@
|
||||
import { Link } from 'react-router-dom'
|
||||
import Post from '../components/Post'
|
||||
|
||||
const posts = [
|
||||
{
|
||||
id: '1',
|
||||
epochKey: 'epochKey-1',
|
||||
publishedAt: '3 小時前',
|
||||
publishedAt: new Date(),
|
||||
content:
|
||||
'今天真是一個美好的日子!我終於完成了我夢寐以求的目標:跑完全馬拉松!這個挑戰對我來說真的非常艱巨,但我堅持下來了。在這個過程中,我學到了很多關於毅力和奮鬥的價值。我要特別感謝我的家人和朋友對我一直以來的支持和鼓勵。無論你們在生活中面對什麼困難,只要你們相信自己,付出努力,你們一定可以實現自己的目標!今天,我真心覺得自己是最幸運的人。',
|
||||
commentCount: 0,
|
||||
upCount: 0,
|
||||
downCount: 0,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
epochKey: 'epochKey-2',
|
||||
publishedAt: '4 小時前',
|
||||
publishedAt: new Date(),
|
||||
content:
|
||||
'最近我剛看完一本非常棒的書,推薦給大家!這本書叫做《思考快與慢》,作者是丹尼爾·卡尼曼。這本書深入探討了人類思考的方式和偏見。它教會了我們如何辨識和避免那些常常影響我們判斷力的錯誤和陷阱。我學到了很多關於認知心理學的知識,這些知識不僅適用於個人生活,還能幫助我們在工作和人際關係中做出更明智的決策。如果你對心理學或者是提升自己的思考能力感興趣,這本書絕對是值得一讀的!',
|
||||
commentCount: 0,
|
||||
upCount: 0,
|
||||
downCount: 0,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
epochKey: 'epochKey-3',
|
||||
publishedAt: '4 小時前',
|
||||
publishedAt: new Date(),
|
||||
content:
|
||||
'剛剛和一群好友一起參加了一場令人驚喜的音樂會!我們聽到了一位非常出色的音樂家演奏,他的技巧和激情真是讓人難以置信。音樂會的現場氣氛也非常棒,大家都在跟著節奏搖擺,沉浸在美妙的音樂中。音樂總是有種神奇的力量,它能夠觸動人心,帶給我們情緒的共鳴。這次音樂會真的讓我重新燃起對音樂的熱愛,我想以後會更積極地參加各種音樂活動。如果你也喜歡音樂,不妨多花時間去欣賞和體驗。',
|
||||
commentCount: 0,
|
||||
upCount: 0,
|
||||
downCount: 0,
|
||||
},
|
||||
]
|
||||
|
||||
export default function PostList() {
|
||||
return (
|
||||
<main className="max-w-3xl py-6 mx-auto space-y-6">
|
||||
<section className="text-center">
|
||||
<Link className="btn btn-primary" to="/posts/create">
|
||||
<main>
|
||||
<section className="px-16 py-24">
|
||||
<div className="text-4xl font-semibold leading-relaxed text-white">
|
||||
嗨 🙌🏻 歡迎來到 Unirep Social TW
|
||||
</div>
|
||||
<div className="text-4xl font-semibold leading-relaxed text-white">
|
||||
提供你 100% 匿名身份、安全發言的社群!
|
||||
</div>
|
||||
</section>
|
||||
<section className="py-6 text-center">
|
||||
<Link
|
||||
className="text-2xl font-medium text-white hover:underline underline-offset-4"
|
||||
to="/posts/create"
|
||||
>
|
||||
✏️ 撰寫貼文
|
||||
</Link>
|
||||
</section>
|
||||
<ul className="space-y-6">
|
||||
{posts.map((post) => (
|
||||
<li key={post.id}>
|
||||
<article
|
||||
className="card bg-base-200 shadow-xl prose prose-lg"
|
||||
data-theme="light"
|
||||
>
|
||||
<div className="card-body">
|
||||
<header className="flex items-center">
|
||||
<span className="text-base font-medium">
|
||||
{post.epochKey}
|
||||
</span>
|
||||
<div className="divider divider-horizontal" />
|
||||
<span className="text-sm text-gray-500">
|
||||
{post.publishedAt}
|
||||
</span>
|
||||
</header>
|
||||
<p>{post.content}</p>
|
||||
<div className="card-actions justify-end">
|
||||
<button className="btn btn-sm btn-primary">
|
||||
vote up
|
||||
</button>
|
||||
<button className="btn btn-sm btn-primary">
|
||||
vote down
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<section className="max-w-5xl p-6 mx-auto">
|
||||
<ul className="space-y-6">
|
||||
{posts.map((post) => (
|
||||
<li key={post.id}>
|
||||
<Post
|
||||
epochKey={post.epochKey}
|
||||
content={post.content}
|
||||
publishedAt={post.publishedAt}
|
||||
commentCount={post.commentCount}
|
||||
upCount={post.upCount}
|
||||
downCount={post.downCount}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
html,
|
||||
body {
|
||||
font-family: Inter, sans-serif;
|
||||
}
|
||||
|
||||
22
yarn.lock
22
yarn.lock
@@ -2969,6 +2969,11 @@ boolbase@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||
integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
|
||||
|
||||
boring-avatars@^1.10.1:
|
||||
version "1.10.1"
|
||||
resolved "https://registry.yarnpkg.com/boring-avatars/-/boring-avatars-1.10.1.tgz#0e40811043cb3412bee38a0b6ca14f85fcdb0d2b"
|
||||
integrity sha512-WcgHDeLrazCR03CDPEvCchLsUecZAZvs4F6FnMiGlTEjyQQf15Q5TRl4EUaAQ1dacvhPq7lC9EOTWkCojQ6few==
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
@@ -3885,6 +3890,11 @@ crypto-browserify@^3.12.0:
|
||||
randombytes "^2.0.0"
|
||||
randomfill "^1.0.3"
|
||||
|
||||
crypto-js@^3.1.9-1:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b"
|
||||
integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==
|
||||
|
||||
crypto-random-string@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
|
||||
@@ -3963,6 +3973,11 @@ dateformat@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
|
||||
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
|
||||
|
||||
dayjs@^1.11.9:
|
||||
version "1.11.9"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.9.tgz#9ca491933fadd0a60a2c19f6c237c03517d71d1a"
|
||||
integrity sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA==
|
||||
|
||||
debug@2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
@@ -8284,6 +8299,13 @@ react-router@6.11.2:
|
||||
dependencies:
|
||||
"@remix-run/router" "1.6.2"
|
||||
|
||||
react-twitter-login@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/react-twitter-login/-/react-twitter-login-1.5.0.tgz#09903d4b216268f26ca0cfb696fa011fb91281a1"
|
||||
integrity sha512-onS9dwZ+07+4mMi+2xh6w0KPgY6jkWEfVVom5RCzAolGkDKWTvEMIutkAm1s6O7QtFmam+mpKoMBF8Lwam5iKA==
|
||||
dependencies:
|
||||
crypto-js "^3.1.9-1"
|
||||
|
||||
react@^18.2.0:
|
||||
version "18.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
|
||||
|
||||
Reference in New Issue
Block a user