mirror of
https://github.com/social-tw/social-tw-website.git
synced 2026-01-10 16:08:21 -05:00
feat: run lint: fix and yarn-deduplicate
This commit is contained in:
6
.vscode/launch.json
vendored
6
.vscode/launch.json
vendored
@@ -4,7 +4,6 @@
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"name": "Launch Chrome",
|
||||
"request": "launch",
|
||||
@@ -23,7 +22,10 @@
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Debug Frontend & Backend",
|
||||
"configurations": ["Launch Chrome", "Attach to backend process TS-Node"]
|
||||
"configurations": [
|
||||
"Launch Chrome",
|
||||
"Attach to backend process TS-Node"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
125146
package-lock.json
generated
125146
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
64
package.json
64
package.json
@@ -1,34 +1,34 @@
|
||||
{
|
||||
"name": "social-tw-website",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"scripts": {
|
||||
"install": "lerna bootstrap",
|
||||
"build": "lerna run build",
|
||||
"circuits": "yarn workspace @unirep-app/circuits run",
|
||||
"contracts": "yarn workspace @unirep-app/contracts run",
|
||||
"frontend": "yarn workspace @unirep-app/frontend run",
|
||||
"relay": "yarn workspace @unirep-app/relay run",
|
||||
"start": "node scripts/start.mjs",
|
||||
"linkUnirep": "sh ./scripts/linkUnirep.sh",
|
||||
"copyUnirep": "sh ./scripts/copyUnirep.sh",
|
||||
"lint": "prettier .",
|
||||
"lint:fix": "yarn lint --write",
|
||||
"lint:check": "yarn lint --check"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.4",
|
||||
"@types/testing-library__jest-dom": "^6.0.0",
|
||||
"hardhat": "^2.17.1",
|
||||
"lerna": "^6.0.1",
|
||||
"node-fetch": "^3.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"prettier": "^2.8.4"
|
||||
}
|
||||
"name": "social-tw-website",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"scripts": {
|
||||
"install": "lerna bootstrap",
|
||||
"build": "lerna run build",
|
||||
"circuits": "yarn workspace @unirep-app/circuits run",
|
||||
"contracts": "yarn workspace @unirep-app/contracts run",
|
||||
"frontend": "yarn workspace @unirep-app/frontend run",
|
||||
"relay": "yarn workspace @unirep-app/relay run",
|
||||
"start": "node scripts/start.mjs",
|
||||
"linkUnirep": "sh ./scripts/linkUnirep.sh",
|
||||
"copyUnirep": "sh ./scripts/copyUnirep.sh",
|
||||
"lint": "prettier .",
|
||||
"lint:fix": "yarn lint --write",
|
||||
"lint:check": "yarn lint --check"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.4",
|
||||
"@types/testing-library__jest-dom": "^6.0.0",
|
||||
"hardhat": "^2.17.1",
|
||||
"lerna": "^6.0.1",
|
||||
"node-fetch": "^3.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"prettier": "^2.8.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,7 @@
|
||||
"outDir": "./dist",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"types": [
|
||||
"jest",
|
||||
"node"
|
||||
]
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"exclude": ["node_modules/**"],
|
||||
"include": ["./src", "./config", "./provers"]
|
||||
|
||||
@@ -1 +1,317 @@
|
||||
[{"inputs":[{"internalType":"contract Unirep","name":"_unirep","type":"address"},{"internalType":"contract EpochKeyVerifierHelper","name":"_epkHelper","type":"address"},{"internalType":"contract IVerifier","name":"_dataVerifier","type":"address"},{"internalType":"uint48","name":"_epochLength","type":"uint48"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint160","name":"attesterId","type":"uint160"}],"name":"AttesterIdNotMatch","type":"error"},{"inputs":[{"internalType":"uint256","name":"hashUserId","type":"uint256"},{"internalType":"enum UnirepApp.RegisterStatus","name":"status","type":"uint8"}],"name":"UserAlreadySignedUp","type":"error"},{"inputs":[{"internalType":"uint256","name":"hashUserId","type":"uint256"}],"name":"UserInitExpiry","type":"error"},{"inputs":[{"internalType":"uint256","name":"hashUserId","type":"uint256"}],"name":"UserInitStatusInvalid","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epochKey","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"postId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"string","name":"content","type":"string"}],"name":"Post","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"hashUserId","type":"uint256"}],"name":"UserInitSuccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"hashUserId","type":"uint256"}],"name":"UserSignUpSuccess","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":"hashUserId","type":"uint256"}],"name":"initUserStatus","outputs":[],"stateMutability":"nonpayable","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":"hashUserId","type":"uint256"}],"name":"queryUserStatus","outputs":[{"internalType":"enum UnirepApp.RegisterStatus","name":"","type":"uint8"}],"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]"},{"internalType":"uint256","name":"hashUserId","type":"uint256"},{"internalType":"bool","name":"fromServer","type":"bool"}],"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 EpochKeyVerifierHelper",
|
||||
"name": "_epkHelper",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "contract IVerifier",
|
||||
"name": "_dataVerifier",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint48",
|
||||
"name": "_epochLength",
|
||||
"type": "uint48"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint160",
|
||||
"name": "attesterId",
|
||||
"type": "uint160"
|
||||
}
|
||||
],
|
||||
"name": "AttesterIdNotMatch",
|
||||
"type": "error"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "hashUserId",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "enum UnirepApp.RegisterStatus",
|
||||
"name": "status",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"name": "UserAlreadySignedUp",
|
||||
"type": "error"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "hashUserId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "UserInitExpiry",
|
||||
"type": "error"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "hashUserId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "UserInitStatusInvalid",
|
||||
"type": "error"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "uint256",
|
||||
"name": "epochKey",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "uint256",
|
||||
"name": "postId",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "uint256",
|
||||
"name": "epoch",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "string",
|
||||
"name": "content",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"name": "Post",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "hashUserId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "UserInitSuccess",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "hashUserId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "UserSignUpSuccess",
|
||||
"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": "hashUserId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "initUserStatus",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"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": "hashUserId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "queryUserStatus",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "enum UnirepApp.RegisterStatus",
|
||||
"name": "",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"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]"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "hashUserId",
|
||||
"type": "uint256"
|
||||
},
|
||||
{ "internalType": "bool", "name": "fromServer", "type": "bool" }
|
||||
],
|
||||
"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"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
name: Playwright Tests
|
||||
on:
|
||||
push:
|
||||
branches: [ main, master ]
|
||||
pull_request:
|
||||
branches: [ main, master ]
|
||||
push:
|
||||
branches: [main, master]
|
||||
pull_request:
|
||||
branches: [main, master]
|
||||
jobs:
|
||||
test:
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
- name: Install Playwright Browsers
|
||||
run: yarn playwright install --with-deps
|
||||
- name: Run Playwright tests
|
||||
run: yarn playwright test
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
||||
test:
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
- name: Install Playwright Browsers
|
||||
run: yarn playwright install --with-deps
|
||||
- name: Run Playwright tests
|
||||
run: yarn playwright test
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
['@babel/preset-env', {targets: {node: 'current'}}],
|
||||
['@babel/preset-env', { targets: { node: 'current' } }],
|
||||
'@babel/preset-typescript',
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'jsdom',
|
||||
moduleNameMapper: {
|
||||
"@testing-library/react": "<rootDir>/node_modules/@testing-library/react",
|
||||
"@types/jest": "jest"
|
||||
},
|
||||
};
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'jsdom',
|
||||
moduleNameMapper: {
|
||||
'@testing-library/react':
|
||||
'<rootDir>/node_modules/@testing-library/react',
|
||||
'@types/jest': 'jest',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Login and Sign up Flow
|
||||
|
||||
1. Follow the process in README (at root) to initialize the environment
|
||||
2. Add .env in the packages/relay below is a public sample for test
|
||||
3. Go to localhost:3000/login to start
|
||||
@@ -6,5 +7,5 @@
|
||||
5. Login with twitter then redirect
|
||||
6. Choose sign up with server or wallet
|
||||
7. Navigate to home
|
||||
8. Process sing up
|
||||
9. If success then update the states
|
||||
8. Process sing up
|
||||
9. If success then update the states
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import React from 'react';
|
||||
import BaseLayout from '../../layouts/BaseLayout';
|
||||
import {render, screen} from '@testing-library/react'
|
||||
import React from 'react'
|
||||
import BaseLayout from '../../layouts/BaseLayout'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import '@testing-library/jest-dom'
|
||||
import {expect} from "@jest/globals";
|
||||
import { expect } from '@jest/globals'
|
||||
|
||||
test("BaseLayout should render", () => {
|
||||
render(
|
||||
<BaseLayout/>
|
||||
);
|
||||
});
|
||||
test('BaseLayout should render', () => {
|
||||
render(<BaseLayout />)
|
||||
})
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import React from 'react';
|
||||
import {render, screen} from '@testing-library/react';
|
||||
import OnboardingLayout from '../../layouts/OnboardingLayout';
|
||||
import {UserProvider} from '../../contexts/User';
|
||||
import React from 'react'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import OnboardingLayout from '../../layouts/OnboardingLayout'
|
||||
import { UserProvider } from '../../contexts/User'
|
||||
import '@testing-library/jest-dom'
|
||||
import {MemoryRouter} from "react-router-dom";
|
||||
import {expect} from "@jest/globals";
|
||||
import { MemoryRouter } from 'react-router-dom'
|
||||
import { expect } from '@jest/globals'
|
||||
|
||||
test("OnboardingLayout should render", () => {
|
||||
test('OnboardingLayout should render', () => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<UserProvider>
|
||||
<OnboardingLayout/>
|
||||
<OnboardingLayout />
|
||||
</UserProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
});
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import ErrorPage from '../../pages/ErrorPage';
|
||||
import { useRouteError } from 'react-router-dom';
|
||||
import React from 'react'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import '@testing-library/jest-dom'
|
||||
import ErrorPage from '../../pages/ErrorPage'
|
||||
import { useRouteError } from 'react-router-dom'
|
||||
|
||||
// Mock the useRouteError hook from react-router-dom
|
||||
jest.mock('react-router-dom', () => {
|
||||
return ({
|
||||
return {
|
||||
// @ts-ignore
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useRouteError: jest.fn(),
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
describe('<ErrorPage />', () => {
|
||||
it('displays the statusText of the error', () => {
|
||||
// Mock the hook to return a specific error
|
||||
// @ts-ignore
|
||||
(useRouteError as jest.Mock).mockReturnValue({
|
||||
;(useRouteError as jest.Mock).mockReturnValue({
|
||||
message: 'A test error',
|
||||
statusText: '404 Not Found',
|
||||
});
|
||||
})
|
||||
|
||||
render(<ErrorPage />);
|
||||
render(<ErrorPage />)
|
||||
|
||||
// @ts-ignore
|
||||
expect(screen.getByText('404 Not Found')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('404 Not Found')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Add more tests as needed for other scenarios or other parts of the component
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import React from 'react';
|
||||
import {render, screen} from '@testing-library/react';
|
||||
import Login from '../../pages/Login';
|
||||
import React from 'react'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import Login from '../../pages/Login'
|
||||
import '@testing-library/jest-dom'
|
||||
import {expect} from "@jest/globals";
|
||||
import {MemoryRouter} from "react-router-dom";
|
||||
import {UserProvider} from "../../contexts/User";
|
||||
import { expect } from '@jest/globals'
|
||||
import { MemoryRouter } from 'react-router-dom'
|
||||
import { UserProvider } from '../../contexts/User'
|
||||
|
||||
test("Login should render", () => {
|
||||
test('Login should render', () => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<UserProvider>
|
||||
<Login/>
|
||||
<Login />
|
||||
</UserProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
)
|
||||
// @ts-ignore
|
||||
expect(screen.getByAltText('UniRep Logo')).toBeInTheDocument();
|
||||
expect(screen.getByAltText('UniRep Logo')).toBeInTheDocument()
|
||||
// @ts-ignore
|
||||
expect(screen.getByText('Unirep Social TW')).toBeInTheDocument();
|
||||
expect(screen.getByText('Unirep Social TW')).toBeInTheDocument()
|
||||
|
||||
// ... Add more tests as needed ...
|
||||
});
|
||||
})
|
||||
|
||||
@@ -23,14 +23,29 @@ const AuthForm: React.FC<AuthFormProps> = ({
|
||||
method,
|
||||
onSignup,
|
||||
onLogin,
|
||||
}
|
||||
) => {
|
||||
}) => {
|
||||
const navigate = useNavigate()
|
||||
const { setIsSignupLoading, isSignupLoading, handleServerSignMessage, handleWalletSignMessage, signup } = useUser()
|
||||
const {
|
||||
setIsSignupLoading,
|
||||
isSignupLoading,
|
||||
handleServerSignMessage,
|
||||
handleWalletSignMessage,
|
||||
signup,
|
||||
} = useUser()
|
||||
const [noteStatus, setNoteStatus] = useState('close')
|
||||
const twitterVerify = useTwitterVerify(SERVER)
|
||||
const signupWithWallet = useSignUpWithWallet(navigate, setIsSignupLoading, handleWalletSignMessage, signup)
|
||||
const signupWithServer = useSignupWithServer(navigate, setIsSignupLoading, handleServerSignMessage, signup)
|
||||
const signupWithWallet = useSignUpWithWallet(
|
||||
navigate,
|
||||
setIsSignupLoading,
|
||||
handleWalletSignMessage,
|
||||
signup
|
||||
)
|
||||
const signupWithServer = useSignupWithServer(
|
||||
navigate,
|
||||
setIsSignupLoading,
|
||||
handleServerSignMessage,
|
||||
signup
|
||||
)
|
||||
|
||||
const authVarients = {
|
||||
hidden: { opacity: 0 },
|
||||
@@ -39,62 +54,61 @@ const AuthForm: React.FC<AuthFormProps> = ({
|
||||
transition: {
|
||||
delay: 1,
|
||||
duration: 1,
|
||||
ease: "easeInOut",
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const sendSignupStepContent =
|
||||
const sendSignupStepContent = (
|
||||
<>
|
||||
<LoginButton
|
||||
isLoading={isSignupLoading}
|
||||
onClick={signupWithWallet}
|
||||
title='錢包註冊'
|
||||
subTitle='使用 MetaMask 錢包進行登入'
|
||||
color='#2F9CAF'
|
||||
text='MetaMask 錢包'
|
||||
title="錢包註冊"
|
||||
subTitle="使用 MetaMask 錢包進行登入"
|
||||
color="#2F9CAF"
|
||||
text="MetaMask 錢包"
|
||||
setNoteStatus={() => setNoteStatus('metamask')}
|
||||
/>
|
||||
<LoginButton
|
||||
isLoading={isSignupLoading}
|
||||
onClick={signupWithServer}
|
||||
title='直接註冊'
|
||||
subTitle='沒有錢包嗎? 沒關係! 可以直接使用 Server 註冊'
|
||||
color='#DB7622'
|
||||
text='Server 註冊'
|
||||
title="直接註冊"
|
||||
subTitle="沒有錢包嗎? 沒關係! 可以直接使用 Server 註冊"
|
||||
color="#DB7622"
|
||||
text="Server 註冊"
|
||||
setNoteStatus={() => setNoteStatus('server')}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
const firtSignupStepContent = method === '' ?
|
||||
(
|
||||
const firtSignupStepContent =
|
||||
method === '' ? (
|
||||
<>
|
||||
<LoginButton
|
||||
isLoading={isSignupLoading}
|
||||
onClick={() => { }}
|
||||
title='立即登入'
|
||||
subTitle='歡迎提供你的獨到見解!'
|
||||
color='#2F9CAF'
|
||||
onClick={() => {}}
|
||||
title="立即登入"
|
||||
subTitle="歡迎提供你的獨到見解!"
|
||||
color="#2F9CAF"
|
||||
/>
|
||||
<LoginButton
|
||||
isLoading={isSignupLoading}
|
||||
onClick={onSignup}
|
||||
title='立即註冊'
|
||||
subTitle='只要兩步驟,即可安全匿名分享你的想法!'
|
||||
color='#FF892A'
|
||||
title="立即註冊"
|
||||
subTitle="只要兩步驟,即可安全匿名分享你的想法!"
|
||||
color="#FF892A"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
:
|
||||
(
|
||||
) : (
|
||||
<>
|
||||
<LoginButton
|
||||
<LoginButton
|
||||
isLoading={isSignupLoading}
|
||||
icon={BsTwitter}
|
||||
onClick={twitterVerify}
|
||||
title='使用 Twitter 帳號登入'
|
||||
subTitle='歡迎提供你的獨到見解!'
|
||||
color='#2F9CAF'
|
||||
title="使用 Twitter 帳號登入"
|
||||
subTitle="歡迎提供你的獨到見解!"
|
||||
color="#2F9CAF"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
@@ -119,4 +133,3 @@ const AuthForm: React.FC<AuthFormProps> = ({
|
||||
}
|
||||
|
||||
export default AuthForm
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { IoIosMore } from 'react-icons/io';
|
||||
import { BiSolidLike, BiSolidDislike, BiCommentDetail } from 'react-icons/bi';
|
||||
import { motion } from 'framer-motion';
|
||||
import React from 'react'
|
||||
import { IoIosMore } from 'react-icons/io'
|
||||
import { BiSolidLike, BiSolidDislike, BiCommentDetail } from 'react-icons/bi'
|
||||
import { motion } from 'framer-motion'
|
||||
|
||||
interface DemoPostProps {
|
||||
text: string
|
||||
@@ -16,22 +16,22 @@ const DemoPost: React.FC<DemoPostProps> = ({
|
||||
likes,
|
||||
dislikes,
|
||||
comments,
|
||||
index
|
||||
index,
|
||||
}) => {
|
||||
const getOpacity = (index: number): number => {
|
||||
switch (index) {
|
||||
case 1:
|
||||
return 0.9;
|
||||
return 0.9
|
||||
case 2:
|
||||
return 0.2;
|
||||
return 0.2
|
||||
case 4:
|
||||
return 0.15;
|
||||
return 0.15
|
||||
default:
|
||||
return 0.1;
|
||||
return 0.1
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const opacity = getOpacity(index);
|
||||
const opacity = getOpacity(index)
|
||||
|
||||
const postVarient = {
|
||||
visible: { opacity: 1 },
|
||||
@@ -40,58 +40,48 @@ const DemoPost: React.FC<DemoPostProps> = ({
|
||||
transition: {
|
||||
delay: 1,
|
||||
duration: 1,
|
||||
ease: "easeInOut",
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className='max-w-[600px] flex flex-col justify-between px-5 py-4 bg-[#FFFFFFE5] w-11/12 h-[164px] rounded-xl'
|
||||
<motion.div
|
||||
className="max-w-[600px] flex flex-col justify-between px-5 py-4 bg-[#FFFFFFE5] w-11/12 h-[164px] rounded-xl"
|
||||
variants={postVarient}
|
||||
initial="visible"
|
||||
animate="hidden"
|
||||
>
|
||||
<div className='flex justify-between'>
|
||||
<div className='flex justify-center items-center'>
|
||||
<p className='mr-5 border-2 rounded-full border-white text-center'>
|
||||
<div className="flex justify-between">
|
||||
<div className="flex justify-center items-center">
|
||||
<p className="mr-5 border-2 rounded-full border-white text-center">
|
||||
🎃
|
||||
</p>
|
||||
<span className='text-[10px] text-black font-medium'>
|
||||
<span className="text-[10px] text-black font-medium">
|
||||
就在剛剛
|
||||
</span>
|
||||
</div>
|
||||
<IoIosMore
|
||||
className='text-[#8F8F8F]'
|
||||
/>
|
||||
<IoIosMore className="text-[#8F8F8F]" />
|
||||
</div>
|
||||
<p className='text-xs font-semibold'>
|
||||
{text}...
|
||||
</p>
|
||||
<div className='flex gap-2'>
|
||||
<div className='flex items-center gap-1'>
|
||||
<div className='text-white bg-[#DB7622] rounded-full p-[5px]'>
|
||||
<BiSolidLike className='text-sm' />
|
||||
<p className="text-xs font-semibold">{text}...</p>
|
||||
<div className="flex gap-2">
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="text-white bg-[#DB7622] rounded-full p-[5px]">
|
||||
<BiSolidLike className="text-sm" />
|
||||
</div>
|
||||
<p className='text-[10px]'>
|
||||
{likes}
|
||||
</p>
|
||||
<p className="text-[10px]">{likes}</p>
|
||||
</div>
|
||||
<div className='flex items-center gap-1'>
|
||||
<div className='text-white bg-[black] rounded-full p-[5px]'>
|
||||
<BiSolidDislike className='text-sm' />
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="text-white bg-[black] rounded-full p-[5px]">
|
||||
<BiSolidDislike className="text-sm" />
|
||||
</div>
|
||||
<p className='text-[10px]'>
|
||||
{dislikes}
|
||||
</p>
|
||||
<p className="text-[10px]">{dislikes}</p>
|
||||
</div>
|
||||
<div className='flex items-center gap-1'>
|
||||
<div className='text-white bg-[#2F9CAF] rounded-full p-[5px]'>
|
||||
<BiCommentDetail className='text-sm' />
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="text-white bg-[#2F9CAF] rounded-full p-[5px]">
|
||||
<BiCommentDetail className="text-sm" />
|
||||
</div>
|
||||
<p className='text-[10px] font-medium'>
|
||||
{comments}
|
||||
</p>
|
||||
<p className="text-[10px] font-medium">{comments}</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
@@ -99,4 +89,3 @@ const DemoPost: React.FC<DemoPostProps> = ({
|
||||
}
|
||||
|
||||
export default DemoPost
|
||||
|
||||
|
||||
@@ -1,56 +1,57 @@
|
||||
import React from 'react'
|
||||
import DemoPost from './DemoPost'
|
||||
|
||||
|
||||
// TODO: opacity animation
|
||||
const DemoPostList = () => {
|
||||
const data = [
|
||||
{
|
||||
text: '今天真是一個美好的日子!我終於完成了我夢寐以求的目標:跑完全馬拉松!這個挑戰對我來說真的非常艱巨,但我堅持下來了。我要特別感謝我的家人和朋友對我一直以來的支持和鼓勵。無論你們在生活中面對什麼',
|
||||
likes: 214,
|
||||
dislikes: 26,
|
||||
comments: 16,
|
||||
},
|
||||
{
|
||||
text: '今天真是一個美好的日子!我終於完成了我夢寐以求的目標:跑完全馬拉松!這個挑戰對我來說真的非常艱巨,但我堅持下來了。我要特別感謝我的家人和朋友對我一直以來的支持和鼓勵。無論你們在生活中面對什麼',
|
||||
likes: 214,
|
||||
dislikes: 26,
|
||||
comments: 16,
|
||||
},
|
||||
{
|
||||
text: '今天真是一個美好的日子!我終於完成了我夢寐以求的目標:跑完全馬拉松!這個挑戰對我來說真的非常艱巨,但我堅持下來了。我要特別感謝我的家人和朋友對我一直以來的支持和鼓勵。無論你們在生活中面對什麼',
|
||||
likes: 214,
|
||||
dislikes: 26,
|
||||
comments: 16,
|
||||
},
|
||||
{
|
||||
text: '今天真是一個美好的日子!我終於完成了我夢寐以求的目標:跑完全馬拉松!這個挑戰對我來說真的非常艱巨,但我堅持下來了。我要特別感謝我的家人和朋友對我一直以來的支持和鼓勵。無論你們在生活中面對什麼',
|
||||
likes: 214,
|
||||
dislikes: 26,
|
||||
comments: 16,
|
||||
}
|
||||
];
|
||||
const data = [
|
||||
{
|
||||
text: '今天真是一個美好的日子!我終於完成了我夢寐以求的目標:跑完全馬拉松!這個挑戰對我來說真的非常艱巨,但我堅持下來了。我要特別感謝我的家人和朋友對我一直以來的支持和鼓勵。無論你們在生活中面對什麼',
|
||||
likes: 214,
|
||||
dislikes: 26,
|
||||
comments: 16,
|
||||
},
|
||||
{
|
||||
text: '今天真是一個美好的日子!我終於完成了我夢寐以求的目標:跑完全馬拉松!這個挑戰對我來說真的非常艱巨,但我堅持下來了。我要特別感謝我的家人和朋友對我一直以來的支持和鼓勵。無論你們在生活中面對什麼',
|
||||
likes: 214,
|
||||
dislikes: 26,
|
||||
comments: 16,
|
||||
},
|
||||
{
|
||||
text: '今天真是一個美好的日子!我終於完成了我夢寐以求的目標:跑完全馬拉松!這個挑戰對我來說真的非常艱巨,但我堅持下來了。我要特別感謝我的家人和朋友對我一直以來的支持和鼓勵。無論你們在生活中面對什麼',
|
||||
likes: 214,
|
||||
dislikes: 26,
|
||||
comments: 16,
|
||||
},
|
||||
{
|
||||
text: '今天真是一個美好的日子!我終於完成了我夢寐以求的目標:跑完全馬拉松!這個挑戰對我來說真的非常艱巨,但我堅持下來了。我要特別感謝我的家人和朋友對我一直以來的支持和鼓勵。無論你們在生活中面對什麼',
|
||||
likes: 214,
|
||||
dislikes: 26,
|
||||
comments: 16,
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className='
|
||||
return (
|
||||
<div
|
||||
className="
|
||||
flex
|
||||
flex-col
|
||||
items-center
|
||||
justify-center
|
||||
gap-6
|
||||
'>
|
||||
{data.map((item, i) => (
|
||||
<DemoPost
|
||||
key={i}
|
||||
index={i}
|
||||
text={item.text}
|
||||
likes={item.likes}
|
||||
dislikes={item.dislikes}
|
||||
comments={item.comments}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
"
|
||||
>
|
||||
{data.map((item, i) => (
|
||||
<DemoPost
|
||||
key={i}
|
||||
index={i}
|
||||
text={item.text}
|
||||
likes={item.likes}
|
||||
dislikes={item.dislikes}
|
||||
comments={item.comments}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DemoPostList
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { clsx } from 'clsx';
|
||||
import { clsx } from 'clsx'
|
||||
import React from 'react'
|
||||
import { IconType, icons } from 'react-icons';
|
||||
import { IconType, icons } from 'react-icons'
|
||||
|
||||
interface LoginButtonProps {
|
||||
icon?: IconType
|
||||
@@ -22,11 +22,11 @@ const LoginButton: React.FC<LoginButtonProps> = ({
|
||||
title,
|
||||
subTitle,
|
||||
color,
|
||||
setNoteStatus
|
||||
setNoteStatus,
|
||||
}) => {
|
||||
return (
|
||||
<button
|
||||
type='button'
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
disabled={isLoading}
|
||||
className={`
|
||||
@@ -44,8 +44,10 @@ const LoginButton: React.FC<LoginButtonProps> = ({
|
||||
bg-opacity-70
|
||||
`}
|
||||
>
|
||||
<span className='text-white font-semibold text-2xl tracking-wider'>{title}</span>
|
||||
<span className='text-xs tracking-wider'>{subTitle}</span>
|
||||
<span className="text-white font-semibold text-2xl tracking-wider">
|
||||
{title}
|
||||
</span>
|
||||
<span className="text-xs tracking-wider">{subTitle}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
import { clsx } from "clsx"
|
||||
import React from "react"
|
||||
import { clsx } from 'clsx'
|
||||
import React from 'react'
|
||||
|
||||
interface StepInfoProps {
|
||||
hashUserId: string | null
|
||||
}
|
||||
|
||||
const StepInfo: React.FC<StepInfoProps> = ({
|
||||
hashUserId,
|
||||
}) => {
|
||||
const StepInfo: React.FC<StepInfoProps> = ({ hashUserId }) => {
|
||||
return (
|
||||
<>
|
||||
<div className='
|
||||
<div
|
||||
className="
|
||||
flex
|
||||
w-[80px]
|
||||
flex-col
|
||||
justify-center
|
||||
items-center
|
||||
gap-2'
|
||||
gap-2"
|
||||
>
|
||||
<div
|
||||
className={clsx(`
|
||||
className={clsx(
|
||||
`
|
||||
text-white
|
||||
w-[60px]
|
||||
h-[60px]
|
||||
@@ -33,40 +33,45 @@ const StepInfo: React.FC<StepInfoProps> = ({
|
||||
`,
|
||||
hashUserId && 'border-[5px] border-white'
|
||||
)}
|
||||
>1</div>
|
||||
<div className='
|
||||
>
|
||||
1
|
||||
</div>
|
||||
<div
|
||||
className="
|
||||
text-white
|
||||
text-sm
|
||||
flex
|
||||
flex-col
|
||||
justify-center
|
||||
items-center
|
||||
tracking-wide'
|
||||
tracking-wide"
|
||||
>
|
||||
<span>Twitter</span>
|
||||
<span>帳號登入</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className='
|
||||
<div
|
||||
className="
|
||||
w-[50px]
|
||||
mb-10
|
||||
flex
|
||||
justify-between
|
||||
items-center
|
||||
'
|
||||
>
|
||||
<span className='bg-[#FF892A] h-1 w-1'></span>
|
||||
<span className='bg-[#FF892A] h-1 w-2'></span>
|
||||
<span className='bg-[#FF892A] h-1 w-2'></span>
|
||||
<span className='bg-[#FF892A] h-1 w-1'></span>
|
||||
"
|
||||
>
|
||||
<span className="bg-[#FF892A] h-1 w-1"></span>
|
||||
<span className="bg-[#FF892A] h-1 w-2"></span>
|
||||
<span className="bg-[#FF892A] h-1 w-2"></span>
|
||||
<span className="bg-[#FF892A] h-1 w-1"></span>
|
||||
</div>
|
||||
<div
|
||||
className={clsx(
|
||||
`flex w-[80px] flex-col justify-center items-center gap-2`,
|
||||
!hashUserId && 'opacity-30'
|
||||
)}>
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className='
|
||||
className="
|
||||
text-white
|
||||
w-[60px]
|
||||
h-[60px]
|
||||
@@ -77,16 +82,19 @@ const StepInfo: React.FC<StepInfoProps> = ({
|
||||
bg-[#FF892A]
|
||||
rounded-full
|
||||
text-[30px]
|
||||
'
|
||||
>2</div>
|
||||
<div className='
|
||||
"
|
||||
>
|
||||
2
|
||||
</div>
|
||||
<div
|
||||
className="
|
||||
text-white
|
||||
text-sm
|
||||
flex
|
||||
flex-col
|
||||
justify-center
|
||||
items-center
|
||||
tracking-wide'
|
||||
tracking-wide"
|
||||
>
|
||||
<span>錢包註冊</span>
|
||||
<span>Or 直接註冊</span>
|
||||
|
||||
@@ -10,11 +10,7 @@ interface ModalProps {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
const Modal: React.FC<ModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
children
|
||||
}) => {
|
||||
const Modal: React.FC<ModalProps> = ({ isOpen, onClose, children }) => {
|
||||
const modalVariants = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
@@ -43,28 +39,28 @@ const Modal: React.FC<ModalProps> = ({
|
||||
return (
|
||||
<>
|
||||
<motion.div
|
||||
className='
|
||||
className="
|
||||
fixed
|
||||
inset-0
|
||||
z-50
|
||||
bg-black
|
||||
bg-opacity-90
|
||||
h-full
|
||||
'
|
||||
"
|
||||
variants={modalVariants}
|
||||
initial='hidden'
|
||||
animate='visible'
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
/>
|
||||
<motion.div
|
||||
className='
|
||||
className="
|
||||
fixed
|
||||
inset-0
|
||||
z-[60]
|
||||
bg-opacity-80
|
||||
'
|
||||
"
|
||||
variants={chidrenVarients}
|
||||
initial='hidden'
|
||||
animate='visible'
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { IconType } from 'react-icons';
|
||||
import Modal from './Modal';
|
||||
import React from 'react'
|
||||
import { IconType } from 'react-icons'
|
||||
import Modal from './Modal'
|
||||
|
||||
interface NoteModallProps {
|
||||
noteStatus: string
|
||||
@@ -8,43 +8,65 @@ interface NoteModallProps {
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
|
||||
const NoteModal: React.FC<NoteModallProps> = ({
|
||||
noteStatus,
|
||||
icon: Icon,
|
||||
onClose
|
||||
onClose,
|
||||
}) => {
|
||||
let content;
|
||||
let content
|
||||
|
||||
switch (noteStatus) {
|
||||
case 'metamask':
|
||||
content = (
|
||||
<>
|
||||
<p className='underline font-semibold'>什麼是 MetaMask 錢包?</p>
|
||||
<p>MetaMask 是一個以太坊(Ethereum)區塊鏈上的加密貨幣錢包和瀏覽器擴展程式,為用戶提供了方便的加密貨幣管理和去中心化應用(DApps)訪問。使用者能夠透過 MetaMask 在網頁瀏覽器中安全地存儲、管理和交易以太幣(ETH)及其他基於以太坊的代幣。</p>
|
||||
<p>MetaMask 不僅僅是一個安全的錢包,它還是一個將區塊鏈技術融入日常網頁使用的工具。使用者可以透過 MetaMask 輕鬆地訪問各種去中心化應用,例如賭博平台、藝術品市場、去中心化金融服務等。MetaMask 通過助記詞和私鑰的方式,確保使用者對其資產擁有完全控制,同時也提供了在不同以太坊網絡之間切換的便利性。</p>
|
||||
<p>MetaMask 在以太坊生態系中扮演著重要角色,為使用者提供了便捷的加密貨幣管理手段,同時也促進了去中心化應用的普及和使用。無論是初次接觸區塊鏈世界的新手,還是資深的加密貨幣愛好者,MetaMask 都是一個不可或缺的工具。</p>
|
||||
<p className="underline font-semibold">
|
||||
什麼是 MetaMask 錢包?
|
||||
</p>
|
||||
<p>
|
||||
MetaMask
|
||||
是一個以太坊(Ethereum)區塊鏈上的加密貨幣錢包和瀏覽器擴展程式,為用戶提供了方便的加密貨幣管理和去中心化應用(DApps)訪問。使用者能夠透過
|
||||
MetaMask
|
||||
在網頁瀏覽器中安全地存儲、管理和交易以太幣(ETH)及其他基於以太坊的代幣。
|
||||
</p>
|
||||
<p>
|
||||
MetaMask
|
||||
不僅僅是一個安全的錢包,它還是一個將區塊鏈技術融入日常網頁使用的工具。使用者可以透過
|
||||
MetaMask
|
||||
輕鬆地訪問各種去中心化應用,例如賭博平台、藝術品市場、去中心化金融服務等。MetaMask
|
||||
通過助記詞和私鑰的方式,確保使用者對其資產擁有完全控制,同時也提供了在不同以太坊網絡之間切換的便利性。
|
||||
</p>
|
||||
<p>
|
||||
MetaMask
|
||||
在以太坊生態系中扮演著重要角色,為使用者提供了便捷的加密貨幣管理手段,同時也促進了去中心化應用的普及和使用。無論是初次接觸區塊鏈世界的新手,還是資深的加密貨幣愛好者,MetaMask
|
||||
都是一個不可或缺的工具。
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
break;
|
||||
)
|
||||
break
|
||||
case 'server':
|
||||
content = (
|
||||
<>
|
||||
<p className='underline font-semibold'>甚麼是 Server 註冊?</p>
|
||||
<p>如果您並未安裝 Metamask 錢包,我們提供 Server 註冊幫助您解決這項問題。</p>
|
||||
<p>使用 Server 註冊將允許我們使用伺服器進行鍊上簽名,並進行註冊動作,放心一切資訊一樣會是匿名的。</p>
|
||||
<p className="underline font-semibold">
|
||||
甚麼是 Server 註冊?
|
||||
</p>
|
||||
<p>
|
||||
如果您並未安裝 Metamask 錢包,我們提供 Server
|
||||
註冊幫助您解決這項問題。
|
||||
</p>
|
||||
<p>
|
||||
使用 Server
|
||||
註冊將允許我們使用伺服器進行鍊上簽名,並進行註冊動作,放心一切資訊一樣會是匿名的。
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
break;
|
||||
)
|
||||
break
|
||||
}
|
||||
return (
|
||||
<Modal
|
||||
isOpen={noteStatus !== 'close'}
|
||||
>
|
||||
<div className='flex flex-col justify-center items-center h-full p-4'>
|
||||
<div className='relative p-12 flex flex-col gap-4 bg-white max-w-[600px] overflow-auto leading-7 tex-[15px] tracking-wider rounded-lg'>
|
||||
<Icon
|
||||
className='absolute right-12 cursor-pointer'
|
||||
<Modal isOpen={noteStatus !== 'close'}>
|
||||
<div className="flex flex-col justify-center items-center h-full p-4">
|
||||
<div className="relative p-12 flex flex-col gap-4 bg-white max-w-[600px] overflow-auto leading-7 tex-[15px] tracking-wider rounded-lg">
|
||||
<Icon
|
||||
className="absolute right-12 cursor-pointer"
|
||||
size={24}
|
||||
onClick={onClose}
|
||||
/>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import React from 'react'
|
||||
import { motion } from 'framer-motion'
|
||||
|
||||
const Forwording = () => {
|
||||
|
||||
const upvoteVariants = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
@@ -13,7 +12,7 @@ const Forwording = () => {
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const downvoteVariants = {
|
||||
hidden: { opacity: 0 },
|
||||
@@ -26,7 +25,7 @@ const Forwording = () => {
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const commentVariants = {
|
||||
hidden: { opacity: 0 },
|
||||
@@ -39,7 +38,7 @@ const Forwording = () => {
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const logoColoredVariants = {
|
||||
hidden: { opacity: 0 },
|
||||
@@ -52,7 +51,7 @@ const Forwording = () => {
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const logoWhiteVariants = {
|
||||
hidden: { opacity: 0 },
|
||||
@@ -64,57 +63,54 @@ const Forwording = () => {
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full h-full flex justify-center items-center'>
|
||||
<div className='flex flex-col justiy-center items-center'>
|
||||
<div className='relative mb-2 w-[120px] h-[120px]'>
|
||||
<div className="w-full h-full flex justify-center items-center">
|
||||
<div className="flex flex-col justiy-center items-center">
|
||||
<div className="relative mb-2 w-[120px] h-[120px]">
|
||||
<motion.img
|
||||
src={require('../../../public/unirep_logo_colored.png')}
|
||||
className='absolute inset-0'
|
||||
className="absolute inset-0"
|
||||
variants={logoColoredVariants}
|
||||
initial='hidden'
|
||||
animate='visible'
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
/>
|
||||
<motion.img
|
||||
src={require('../../../public/unirep_logo_white.png')}
|
||||
className='absolute inset-0'
|
||||
className="absolute inset-0"
|
||||
variants={logoWhiteVariants}
|
||||
initial='hidden'
|
||||
animate='visible'
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
/>
|
||||
<motion.img
|
||||
src={require('../../../public/comment.png')}
|
||||
className='absolute inset-0'
|
||||
className="absolute inset-0"
|
||||
variants={commentVariants}
|
||||
initial='hidden'
|
||||
animate='visible'
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
/>
|
||||
<motion.img
|
||||
src={require('../../../public/upvote.png')}
|
||||
className='absolute inset-0'
|
||||
className="absolute inset-0"
|
||||
variants={upvoteVariants}
|
||||
initial='hidden'
|
||||
animate='visible'
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
/>
|
||||
<motion.img
|
||||
src={require('../../../public/downvote.png')}
|
||||
className='absolute inset-0'
|
||||
className="absolute inset-0"
|
||||
variants={downvoteVariants}
|
||||
initial='hidden'
|
||||
animate='visible'
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
/>
|
||||
</div>
|
||||
<motion.h1
|
||||
className='text-2xl text-neutral-200 font-semibold'
|
||||
>
|
||||
<motion.h1 className="text-2xl text-neutral-200 font-semibold">
|
||||
Unirep Social TW
|
||||
</motion.h1>
|
||||
<motion.h2
|
||||
className='mb-6 mt-9 text-sm font-light text-white text-center tracking-wider'
|
||||
>
|
||||
嗨 🙌🏻 歡迎來到 Unirep Social TW <br />提供你 100% 匿名身份、安全發言的社群!
|
||||
<motion.h2 className="mb-6 mt-9 text-sm font-light text-white text-center tracking-wider">
|
||||
嗨 🙌🏻 歡迎來到 Unirep Social TW <br />
|
||||
提供你 100% 匿名身份、安全發言的社群!
|
||||
</motion.h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
import { VscAccount } from 'react-icons/vsc'
|
||||
import { useUser} from "../../contexts/User"
|
||||
import { useUser } from '../../contexts/User'
|
||||
|
||||
const UserDropdown: React.FC = () => {
|
||||
const { logout } = useUser()
|
||||
|
||||
@@ -2,31 +2,34 @@ import React, { createContext, useState, useContext } from 'react'
|
||||
|
||||
export type LoadingStatus = 'loading' | 'success' | 'fail' | 'start'
|
||||
type LoadingContextType = {
|
||||
status: LoadingStatus
|
||||
setStatus: React.Dispatch<React.SetStateAction<LoadingStatus>>
|
||||
status: LoadingStatus
|
||||
setStatus: React.Dispatch<React.SetStateAction<LoadingStatus>>
|
||||
}
|
||||
|
||||
const defaultContextValue: LoadingContextType = {
|
||||
status: 'start',
|
||||
setStatus: () => {}
|
||||
status: 'start',
|
||||
setStatus: () => {},
|
||||
}
|
||||
|
||||
export const LoadingContext = createContext<LoadingContextType>(defaultContextValue)
|
||||
export const LoadingContext =
|
||||
createContext<LoadingContextType>(defaultContextValue)
|
||||
|
||||
type LoadingProviderProps = {
|
||||
children: React.ReactNode
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export const LoadingProvider: React.FC<LoadingProviderProps> = ({ children }) => {
|
||||
const [status, setStatus] = useState<LoadingStatus>('loading')
|
||||
export const LoadingProvider: React.FC<LoadingProviderProps> = ({
|
||||
children,
|
||||
}) => {
|
||||
const [status, setStatus] = useState<LoadingStatus>('loading')
|
||||
|
||||
return (
|
||||
<LoadingContext.Provider value={{ status, setStatus }}>
|
||||
{children}
|
||||
</LoadingContext.Provider>
|
||||
)
|
||||
return (
|
||||
<LoadingContext.Provider value={{ status, setStatus }}>
|
||||
{children}
|
||||
</LoadingContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useLoading = () => {
|
||||
return useContext(LoadingContext)
|
||||
return useContext(LoadingContext)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import React, { createContext, useState, useEffect, useContext, ReactNode, useCallback, useMemo } from 'react'
|
||||
import React, {
|
||||
createContext,
|
||||
useState,
|
||||
useEffect,
|
||||
useContext,
|
||||
ReactNode,
|
||||
useCallback,
|
||||
useMemo,
|
||||
} from 'react'
|
||||
import { stringifyBigInts } from '@unirep/utils'
|
||||
import { Identity } from '@semaphore-protocol/identity'
|
||||
import { UserState } from '@unirep/core'
|
||||
@@ -24,7 +32,7 @@ export interface UserContextType {
|
||||
setProvableData: (provableData: bigint[]) => void
|
||||
userState?: UserState
|
||||
setUserState: (userState?: UserState) => void
|
||||
provider: any // TODO: Replace with the appropriate type
|
||||
provider: any // TODO: Replace with the appropriate type
|
||||
setProvider: (provider: any) => void
|
||||
signature: string
|
||||
setSignature: (signature: string) => void
|
||||
@@ -39,7 +47,10 @@ export interface UserContextType {
|
||||
handleWalletSignMessage: () => Promise<void>
|
||||
signup: () => Promise<void>
|
||||
stateTransition: () => Promise<void>
|
||||
requestData: (reqData: { [key: number]: string | number }, epkNonce: number) => Promise<void>
|
||||
requestData: (
|
||||
reqData: { [key: number]: string | number },
|
||||
epkNonce: number
|
||||
) => Promise<void>
|
||||
proveData: (data: { [key: number]: string | number }) => Promise<any>
|
||||
logout: () => void
|
||||
}
|
||||
@@ -56,14 +67,15 @@ interface UserProviderProps {
|
||||
|
||||
export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
|
||||
const [currentEpoch, setCurrentEpoch] = useState<number>(0)
|
||||
const [latestTransitionedEpoch, setLatestTransitionedEpoch] = useState<number>(0)
|
||||
const [latestTransitionedEpoch, setLatestTransitionedEpoch] =
|
||||
useState<number>(0)
|
||||
const [isSignupLoading, setIsSignupLoading] = useState<boolean>(false)
|
||||
const [fromServer, setFromServer] = useState<boolean>(false)
|
||||
const [hasSignedUp, setHasSignedUp] = useState<boolean>(false)
|
||||
const [data, setData] = useState<bigint[]>([])
|
||||
const [provableData, setProvableData] = useState<bigint[]>([])
|
||||
const [userState, setUserState] = useState<UserState | undefined>()
|
||||
const [provider, setProvider] = useState<any>() // TODO: Replace with the appropriate type
|
||||
const [provider, setProvider] = useState<any>() // TODO: Replace with the appropriate type
|
||||
const [signature, setSignature] = useState<string>('')
|
||||
const [hashUserId, setHashUserId] = useState<string>('')
|
||||
|
||||
@@ -90,15 +102,13 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
|
||||
|
||||
setProvider(providerInstance)
|
||||
|
||||
const userStateInstance = new UserState(
|
||||
{
|
||||
provider: providerInstance,
|
||||
prover,
|
||||
unirepAddress: UNIREP_ADDRESS,
|
||||
attesterId: BigInt(APP_ADDRESS),
|
||||
id: identity,
|
||||
},
|
||||
)
|
||||
const userStateInstance = new UserState({
|
||||
provider: providerInstance,
|
||||
prover,
|
||||
unirepAddress: UNIREP_ADDRESS,
|
||||
attesterId: BigInt(APP_ADDRESS),
|
||||
id: identity,
|
||||
})
|
||||
|
||||
await userStateInstance.sync.start()
|
||||
setUserState(userStateInstance)
|
||||
@@ -113,15 +123,18 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
|
||||
setLatestTransitionedEpoch(latestEpoch)
|
||||
}
|
||||
|
||||
const loadData = useCallback(async (userState: UserState) => {
|
||||
if (!userState) throw new Error('user state not initialized')
|
||||
const loadData = useCallback(
|
||||
async (userState: UserState) => {
|
||||
if (!userState) throw new Error('user state not initialized')
|
||||
|
||||
const fetchedData = await userState.getData()
|
||||
const fetchedProvableData = await userState.getProvableData()
|
||||
const fetchedData = await userState.getData()
|
||||
const fetchedProvableData = await userState.getProvableData()
|
||||
|
||||
setData(fetchedData)
|
||||
setProvableData(fetchedProvableData)
|
||||
}, [userState])
|
||||
setData(fetchedData)
|
||||
setProvableData(fetchedProvableData)
|
||||
},
|
||||
[userState]
|
||||
)
|
||||
|
||||
const fieldCount = useMemo(() => {
|
||||
return userState?.sync.settings.fieldCount
|
||||
@@ -131,12 +144,15 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
|
||||
return userState?.sync.settings.sumFieldCount
|
||||
}, [userState])
|
||||
|
||||
const epochKey = useCallback((nonce: number) => {
|
||||
if (!userState) return '0x'
|
||||
const epoch = userState.sync.calcCurrentEpoch()
|
||||
const key = userState.getEpochKeys(epoch, nonce)
|
||||
return `0x${key.toString(16)}`
|
||||
}, [userState])
|
||||
const epochKey = useCallback(
|
||||
(nonce: number) => {
|
||||
if (!userState) return '0x'
|
||||
const epoch = userState.sync.calcCurrentEpoch()
|
||||
const key = userState.getEpochKeys(epoch, nonce)
|
||||
return `0x${key.toString(16)}`
|
||||
},
|
||||
[userState]
|
||||
)
|
||||
|
||||
const handleServerSignMessage = async () => {
|
||||
const response = await fetch(`${SERVER}/api/identity`, {
|
||||
@@ -146,7 +162,7 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
|
||||
},
|
||||
body: JSON.stringify({
|
||||
hashUserId,
|
||||
})
|
||||
}),
|
||||
})
|
||||
if (!response.ok) {
|
||||
throw new Error('False Identity')
|
||||
@@ -158,15 +174,15 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
|
||||
}
|
||||
|
||||
const handleWalletSignMessage = async () => {
|
||||
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' })
|
||||
const accounts = await window.ethereum.request({
|
||||
method: 'eth_requestAccounts',
|
||||
})
|
||||
const account = accounts[0]
|
||||
|
||||
const signature = await window.ethereum.request({
|
||||
method: 'personal_sign',
|
||||
params: [
|
||||
ethers.utils.hexlify(
|
||||
ethers.utils.toUtf8Bytes(hashUserId)
|
||||
),
|
||||
ethers.utils.hexlify(ethers.utils.toUtf8Bytes(hashUserId)),
|
||||
account,
|
||||
],
|
||||
})
|
||||
@@ -177,8 +193,10 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
|
||||
const signup = useCallback(async () => {
|
||||
if (!userState) throw new Error('user state not initialized')
|
||||
const signupProof = await userState.genUserSignUpProof()
|
||||
const publicSignals = signupProof.publicSignals.map(item => item.toString())
|
||||
const proof = signupProof.proof.map(item => item.toString())
|
||||
const publicSignals = signupProof.publicSignals.map((item) =>
|
||||
item.toString()
|
||||
)
|
||||
const proof = signupProof.proof.map((item) => item.toString())
|
||||
|
||||
const response = await fetch(`${SERVER}/api/signup`, {
|
||||
method: 'POST',
|
||||
@@ -230,77 +248,83 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
|
||||
setLatestTransitionedEpoch(latestTransitionEpoch)
|
||||
}
|
||||
|
||||
const requestData = useCallback(
|
||||
async (
|
||||
reqData: { [key: number]: string | number },
|
||||
epkNonce: number
|
||||
) => {
|
||||
if (!userState) throw new Error('user state not initialized')
|
||||
|
||||
const requestData = useCallback(async (
|
||||
reqData: { [key: number]: string | number },
|
||||
epkNonce: number
|
||||
) => {
|
||||
if (!userState) throw new Error('user state not initialized')
|
||||
const filteredReqData = Object.entries(reqData)
|
||||
.filter(([_, value]) => value !== '')
|
||||
.reduce((obj, [key, value]) => ({ ...obj, [key]: value }), {})
|
||||
|
||||
const filteredReqData = Object.entries(reqData)
|
||||
.filter(([_, value]) => value !== '')
|
||||
.reduce((obj, [key, value]) => ({ ...obj, [key]: value }), {})
|
||||
if (Object.keys(filteredReqData).length === 0) {
|
||||
throw new Error('No data in the attestation')
|
||||
}
|
||||
|
||||
if (Object.keys(filteredReqData).length === 0) {
|
||||
throw new Error('No data in the attestation')
|
||||
}
|
||||
const epochKeyProof = await userState.genEpochKeyProof({
|
||||
nonce: epkNonce,
|
||||
})
|
||||
const response = await fetch(`${SERVER}/api/request`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(
|
||||
stringifyBigInts({
|
||||
reqData: filteredReqData,
|
||||
publicSignals: epochKeyProof.publicSignals,
|
||||
proof: epochKeyProof.proof,
|
||||
})
|
||||
),
|
||||
})
|
||||
const data = await response.json()
|
||||
await provider.waitForTransaction(data.hash)
|
||||
await userState.waitForSync()
|
||||
await loadData(userState)
|
||||
},
|
||||
[userState, provider, loadData]
|
||||
)
|
||||
|
||||
const epochKeyProof = await userState.genEpochKeyProof({
|
||||
nonce: epkNonce,
|
||||
})
|
||||
const response = await fetch(`${SERVER}/api/request`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(
|
||||
stringifyBigInts({
|
||||
reqData: filteredReqData,
|
||||
publicSignals: epochKeyProof.publicSignals,
|
||||
proof: epochKeyProof.proof,
|
||||
})
|
||||
),
|
||||
})
|
||||
const data = await response.json()
|
||||
await provider.waitForTransaction(data.hash)
|
||||
await userState.waitForSync()
|
||||
await loadData(userState)
|
||||
}, [userState, provider, loadData])
|
||||
|
||||
const proveData = useCallback(async (data: { [key: number]: string | number }) => {
|
||||
if (!userState) throw new Error('user state not initialized')
|
||||
const epoch = await userState.sync.loadCurrentEpoch()
|
||||
const stateTree = await userState.sync.genStateTree(epoch)
|
||||
const index = await userState.latestStateTreeLeafIndex(epoch)
|
||||
const stateTreeProof = stateTree.createProof(index)
|
||||
const provableData = await userState.getProvableData()
|
||||
const sumFieldCount = userState.sync.settings.sumFieldCount
|
||||
const values = Array(sumFieldCount).fill(0)
|
||||
for (let [key, value] of Object.entries(data)) {
|
||||
values[Number(key)] = value
|
||||
}
|
||||
const attesterId = userState.sync.attesterId
|
||||
const circuitInputs = stringifyBigInts({
|
||||
identity_secret: userState.id.secret,
|
||||
state_tree_indexes: stateTreeProof.pathIndices,
|
||||
state_tree_elements: stateTreeProof.siblings,
|
||||
data: provableData,
|
||||
epoch: epoch,
|
||||
attester_id: attesterId,
|
||||
value: values,
|
||||
})
|
||||
const { publicSignals, proof } = await prover.genProofAndPublicSignals(
|
||||
'dataProof',
|
||||
circuitInputs
|
||||
)
|
||||
const dataProof = new DataProof(publicSignals, proof, prover)
|
||||
const valid = await dataProof.verify()
|
||||
return stringifyBigInts({
|
||||
publicSignals: dataProof.publicSignals,
|
||||
proof: dataProof.proof,
|
||||
valid,
|
||||
})
|
||||
}, [userState])
|
||||
const proveData = useCallback(
|
||||
async (data: { [key: number]: string | number }) => {
|
||||
if (!userState) throw new Error('user state not initialized')
|
||||
const epoch = await userState.sync.loadCurrentEpoch()
|
||||
const stateTree = await userState.sync.genStateTree(epoch)
|
||||
const index = await userState.latestStateTreeLeafIndex(epoch)
|
||||
const stateTreeProof = stateTree.createProof(index)
|
||||
const provableData = await userState.getProvableData()
|
||||
const sumFieldCount = userState.sync.settings.sumFieldCount
|
||||
const values = Array(sumFieldCount).fill(0)
|
||||
for (let [key, value] of Object.entries(data)) {
|
||||
values[Number(key)] = value
|
||||
}
|
||||
const attesterId = userState.sync.attesterId
|
||||
const circuitInputs = stringifyBigInts({
|
||||
identity_secret: userState.id.secret,
|
||||
state_tree_indexes: stateTreeProof.pathIndices,
|
||||
state_tree_elements: stateTreeProof.siblings,
|
||||
data: provableData,
|
||||
epoch: epoch,
|
||||
attester_id: attesterId,
|
||||
value: values,
|
||||
})
|
||||
const { publicSignals, proof } =
|
||||
await prover.genProofAndPublicSignals(
|
||||
'dataProof',
|
||||
circuitInputs
|
||||
)
|
||||
const dataProof = new DataProof(publicSignals, proof, prover)
|
||||
const valid = await dataProof.verify()
|
||||
return stringifyBigInts({
|
||||
publicSignals: dataProof.publicSignals,
|
||||
proof: dataProof.proof,
|
||||
valid,
|
||||
})
|
||||
},
|
||||
[userState]
|
||||
)
|
||||
|
||||
const logout = () => {
|
||||
setHasSignedUp(false)
|
||||
@@ -311,7 +335,6 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
|
||||
localStorage.removeItem('hashUserId')
|
||||
}
|
||||
|
||||
|
||||
const value: UserContextType = {
|
||||
currentEpoch,
|
||||
setCurrentEpoch,
|
||||
@@ -346,7 +369,7 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
|
||||
stateTransition,
|
||||
requestData,
|
||||
proveData,
|
||||
logout
|
||||
logout,
|
||||
}
|
||||
|
||||
return <UserContext.Provider value={value}>{children}</UserContext.Provider>
|
||||
@@ -360,4 +383,4 @@ export const useUser = (): UserContextType => {
|
||||
throw new Error('useUser must be used within a UserProvider')
|
||||
}
|
||||
return context
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useEffect } from "react";
|
||||
import { useEffect } from 'react'
|
||||
|
||||
// TODO: No need now
|
||||
const useAutoNavigation = (
|
||||
hashUserId: string | null,
|
||||
status: string | null,
|
||||
navigate: (path: string) => void,
|
||||
hashUserId: string | null,
|
||||
status: string | null,
|
||||
navigate: (path: string) => void,
|
||||
hasSignedUp: boolean,
|
||||
isSignupLoading: boolean,
|
||||
isLoading: boolean
|
||||
@@ -13,13 +13,18 @@ const useAutoNavigation = (
|
||||
if (!isLoading) {
|
||||
if (!hasSignedUp && hashUserId) {
|
||||
navigate(`/login?code=${hashUserId}&status=${status}`)
|
||||
} else if (!hasSignedUp && !hashUserId && !status && !isSignupLoading) {
|
||||
} else if (
|
||||
!hasSignedUp &&
|
||||
!hashUserId &&
|
||||
!status &&
|
||||
!isSignupLoading
|
||||
) {
|
||||
navigate('/login')
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
}, [hasSignedUp, hashUserId, status, isSignupLoading, isLoading]);
|
||||
};
|
||||
}, [hasSignedUp, hashUserId, status, isSignupLoading, isLoading])
|
||||
}
|
||||
|
||||
export default useAutoNavigation;
|
||||
export default useAutoNavigation
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
const useInitUser = (
|
||||
load: () => Promise<void>,
|
||||
hashUserId: string | null,
|
||||
) => {
|
||||
const useInitUser = (load: () => Promise<void>, hashUserId: string | null) => {
|
||||
useEffect(() => {
|
||||
const initUser = async () => {
|
||||
try {
|
||||
await load()
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hashUserId) {
|
||||
|
||||
@@ -8,8 +8,7 @@ export default function usePosts() {
|
||||
const randomNonce = () => Math.round(Math.random())
|
||||
|
||||
const create = async (content: string) => {
|
||||
if (!userState)
|
||||
throw new Error('user state not initialized')
|
||||
if (!userState) throw new Error('user state not initialized')
|
||||
|
||||
if (
|
||||
userState.sync.calcCurrentEpoch() !==
|
||||
|
||||
@@ -2,7 +2,7 @@ const useSignupWithServer = (
|
||||
navigate: (path: string) => void,
|
||||
setIsSignupLoading: (loading: boolean) => void,
|
||||
getServerSignMessage: () => Promise<void>,
|
||||
signup: () => Promise<void>,
|
||||
signup: () => Promise<void>
|
||||
) => {
|
||||
const signupWithServer = async () => {
|
||||
try {
|
||||
@@ -21,4 +21,4 @@ const useSignupWithServer = (
|
||||
return signupWithServer
|
||||
}
|
||||
|
||||
export default useSignupWithServer
|
||||
export default useSignupWithServer
|
||||
|
||||
@@ -8,7 +8,7 @@ const useSignupWithWallet = (
|
||||
navigate: (path: string) => void,
|
||||
setIsSignupLoading: (loading: boolean) => void,
|
||||
handleWalletSignMessage: () => Promise<void>,
|
||||
signup: () => Promise<void>,
|
||||
signup: () => Promise<void>
|
||||
) => {
|
||||
const signUpWithWallet = async () => {
|
||||
try {
|
||||
@@ -20,9 +20,9 @@ const useSignupWithWallet = (
|
||||
handleWalletSignMessage()
|
||||
await signup()
|
||||
console.log('has signed up')
|
||||
} catch (error) {
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
} finally {
|
||||
setIsSignupLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback } from "react"
|
||||
import { useCallback } from 'react'
|
||||
|
||||
const useTwitterVerify = (SERVER: string) => {
|
||||
const handleTwitterVerify = useCallback(async () => {
|
||||
@@ -13,4 +13,4 @@ const useTwitterVerify = (SERVER: string) => {
|
||||
return handleTwitterVerify
|
||||
}
|
||||
|
||||
export default useTwitterVerify
|
||||
export default useTwitterVerify
|
||||
|
||||
@@ -32,10 +32,10 @@ const BaseLayout = () => {
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className='h-full overflow-y-scroll'
|
||||
className="h-full overflow-y-scroll"
|
||||
variants={gradientVariants}
|
||||
initial='animate'
|
||||
animate='animate'
|
||||
initial="animate"
|
||||
animate="animate"
|
||||
>
|
||||
<Outlet />
|
||||
</motion.div>
|
||||
|
||||
@@ -64,74 +64,75 @@ const Login: React.FC = () => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className='flex flex-col h-full items-center'>
|
||||
<div className={clsx(
|
||||
`z-40 h-full flex flex-col w-11/12`,
|
||||
method === 'signup' ? 'gap-12' : 'justify-between'
|
||||
)}>
|
||||
<div className={clsx(`flex flex-col gap-12`, method === 'signup' && 'sm:flex hidden')}>
|
||||
<div
|
||||
className='pt-24 flex items-center flex-col justify-center'
|
||||
>
|
||||
<div className="flex flex-col h-full items-center">
|
||||
<div
|
||||
className={clsx(
|
||||
`z-40 h-full flex flex-col w-11/12`,
|
||||
method === 'signup' ? 'gap-12' : 'justify-between'
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={clsx(
|
||||
`flex flex-col gap-12`,
|
||||
method === 'signup' && 'sm:flex hidden'
|
||||
)}
|
||||
>
|
||||
<div className="pt-24 flex items-center flex-col justify-center">
|
||||
<motion.img
|
||||
src={require('../../public/unirep_logo_white.png')}
|
||||
alt='UniRep Logo'
|
||||
className='w-[120px] mb-2'
|
||||
alt="UniRep Logo"
|
||||
className="w-[120px] mb-2"
|
||||
variants={basicVarients}
|
||||
initial='hidden'
|
||||
animate='visible'
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
/>
|
||||
<motion.h1
|
||||
className='text-2xl font-semibold text-neutral-200'
|
||||
className="text-2xl font-semibold text-neutral-200"
|
||||
variants={textVariants}
|
||||
initial='hidden'
|
||||
animate='visible'
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
>
|
||||
Unirep Social TW
|
||||
</motion.h1>
|
||||
<motion.h2
|
||||
className='text-sm font-light tracking-wider text-center text-white mt-9'
|
||||
className="text-sm font-light tracking-wider text-center text-white mt-9"
|
||||
variants={textVariants}
|
||||
initial='hidden'
|
||||
animate='visible'
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
>
|
||||
嗨 🙌🏻 歡迎來到 Unirep Social TW <br />
|
||||
提供你 100% 匿名身份、安全發言的社群!
|
||||
</motion.h2>
|
||||
</div>
|
||||
{method === 'signup' &&
|
||||
{method === 'signup' && (
|
||||
<motion.div
|
||||
className='flex justify-center'
|
||||
className="flex justify-center"
|
||||
variants={basicVarients}
|
||||
initial='hidden'
|
||||
animate='visible'
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
>
|
||||
{
|
||||
<StepInfo
|
||||
hashUserId={hashUserId}
|
||||
/>
|
||||
}
|
||||
{<StepInfo hashUserId={hashUserId} />}
|
||||
</motion.div>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
{method === 'signup' &&
|
||||
<>
|
||||
<div
|
||||
className='absolute top-7 bg-[#E8ECF4] p-3 sm:px-4 sm:py-2 rounded-lg cursor-pointer flex justify-center items-center'
|
||||
onClick={handleBack}
|
||||
>
|
||||
<IoChevronBack
|
||||
size={16}
|
||||
/>
|
||||
<span className='sm:block hidden mx-2 text-sm font-bold'>回到註冊頁</span>
|
||||
</div>
|
||||
<div className='sm:hidden flex flex-col text-white font-semibold text-2xl tracking-wider mt-40'>
|
||||
<p>歡迎回到</p>
|
||||
<p>Unirep Social TW!</p>
|
||||
{hashUserId && <p>再一步即可完成登入</p>}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
{method === 'signup' && (
|
||||
<>
|
||||
<div
|
||||
className="absolute top-7 bg-[#E8ECF4] p-3 sm:px-4 sm:py-2 rounded-lg cursor-pointer flex justify-center items-center"
|
||||
onClick={handleBack}
|
||||
>
|
||||
<IoChevronBack size={16} />
|
||||
<span className="sm:block hidden mx-2 text-sm font-bold">
|
||||
回到註冊頁
|
||||
</span>
|
||||
</div>
|
||||
<div className="sm:hidden flex flex-col text-white font-semibold text-2xl tracking-wider mt-40">
|
||||
<p>歡迎回到</p>
|
||||
<p>Unirep Social TW!</p>
|
||||
{hashUserId && <p>再一步即可完成登入</p>}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<AuthForm
|
||||
hashUserId={hashUserId}
|
||||
method={method}
|
||||
@@ -139,18 +140,16 @@ const Login: React.FC = () => {
|
||||
onLogin={() => setMethod('login')}
|
||||
/>
|
||||
</div>
|
||||
{method === ''
|
||||
&&
|
||||
{method === '' && (
|
||||
<motion.div
|
||||
className='fixed inset-0 z-30 overflow-y-none mt-[220px]'
|
||||
className="fixed inset-0 z-30 overflow-y-none mt-[220px]"
|
||||
variants={postListVariants}
|
||||
initial='start'
|
||||
animate='end'
|
||||
initial="start"
|
||||
animate="end"
|
||||
>
|
||||
|
||||
<DemoPostList />
|
||||
</motion.div>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
"useDefineForClassFields": true,
|
||||
"outDir": "build"
|
||||
},
|
||||
"include": [
|
||||
"src",
|
||||
"../../node_modules/ts-jest/globals.d.ts"
|
||||
],
|
||||
"include": ["src", "../../node_modules/ts-jest/globals.d.ts"],
|
||||
"exclude": ["node_modules", "typings", "build/**/*"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user