mirror of
https://github.com/tornadocash/trusted-setup-server.git
synced 2024-11-22 09:56:53 +01:00
refactor
-add name field validation -disable fields after successful contribute
This commit is contained in:
parent
aa8d72923c
commit
684a7e0a2b
@ -149,6 +149,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
|
||||||
&:hover, &.is-hovered {
|
&:hover, &.is-hovered {
|
||||||
color: $primary;
|
color: $primary;
|
||||||
@ -166,19 +167,49 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
.cloak {
|
.cloak {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.buttons {
|
.form {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button.is-fullwidth + .button.is-fullwidth {
|
.button.is-fullwidth + .button.is-fullwidth {
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-anonymous {
|
||||||
|
width: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset[disabled] {
|
||||||
|
.box {
|
||||||
|
opacity: .5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
background-color: $primary-invert;
|
||||||
|
border-color: #393939;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: $white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cloak {
|
||||||
|
path {
|
||||||
|
stroke: #393939;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.field {
|
.field {
|
||||||
@ -186,6 +217,19 @@
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.help {
|
||||||
|
text-align: left;
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
&.counter {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
&.is-horizontal {
|
&.is-horizontal {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -199,6 +243,16 @@
|
|||||||
margin-right: 1.5rem;
|
margin-right: 1.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.control.has-icons-right {
|
||||||
|
.input {
|
||||||
|
padding-right: calc(#{$control-padding-horizontal} - 1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon.is-right {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.currently {
|
.currently {
|
||||||
@ -543,3 +597,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-bottom: $block-spacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
58
components/Form.vue
Normal file
58
components/Form.vue
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<div class="form">
|
||||||
|
<div v-if="isLoggedIn" class="fields">
|
||||||
|
<b-field
|
||||||
|
:type="{ 'is-danger': hasErrorName.invalid }"
|
||||||
|
:message="{ [hasErrorName.msg]: hasErrorName.invalid }"
|
||||||
|
label="Name"
|
||||||
|
>
|
||||||
|
<b-input v-model="userName" maxlength="35"></b-input>
|
||||||
|
</b-field>
|
||||||
|
<b-field label="Company">
|
||||||
|
<b-input v-model="userCompany"></b-input>
|
||||||
|
</b-field>
|
||||||
|
</div>
|
||||||
|
<div v-else class="buttons">
|
||||||
|
<b-button @click="twitterLogIn" type="is-primary" outlined expanded>
|
||||||
|
Sign in with Twitter
|
||||||
|
</b-button>
|
||||||
|
<b-button :disabled="true" type="is-primary" outlined expanded>
|
||||||
|
Sign in with Github
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters, mapActions } from 'vuex'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
nameErrorMessage: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters('user', ['isLoggedIn', 'hasErrorName']),
|
||||||
|
userName: {
|
||||||
|
get() {
|
||||||
|
return this.$store.state.user.name
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$store.commit('user/SET_NAME', value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
userCompany: {
|
||||||
|
get() {
|
||||||
|
return this.$store.state.user.company
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$store.commit('user/SET_COMPANY', value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions('user', ['makeTweet', 'twitterLogIn'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -1,44 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="ceremony">
|
<div class="ceremony">
|
||||||
<h1 class="title is-size-1 is-spaced">
|
<h1 class="title is-size-1 is-spaced">
|
||||||
Hello, <span>@{{ user.handle }}</span>
|
Hello, <span>@{{ userHandle }}</span>
|
||||||
</h1>
|
</h1>
|
||||||
<h2 class="subtitle">
|
<h2 class="subtitle">
|
||||||
What way do you want to contribute to the Tornado.cash Trusted Setup Ceremony?
|
What way do you want to contribute to the Tornado.cash Trusted Setup Ceremony?
|
||||||
</h2>
|
</h2>
|
||||||
<div class="columns is-centered">
|
<fieldset :disabled="status.type === 'is-success'">
|
||||||
<div class="column is-one-third">
|
<div class="columns is-centered">
|
||||||
<div
|
<div class="column is-one-third">
|
||||||
:class="{ 'is-hovered': contributionType === 'anonymous' }"
|
<button
|
||||||
@click="onAnonymousHandler"
|
:class="{ 'is-hovered': contributionType === 'anonymous' }"
|
||||||
class="box"
|
@click="onAnonymousHandler"
|
||||||
>
|
class="box box-anonymous"
|
||||||
<div class="title is-5">Anonymously</div>
|
>
|
||||||
<Cloak />
|
<div class="title is-5">Anonymously</div>
|
||||||
|
<Cloak />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="column is-one-third">
|
||||||
<div class="column is-one-third">
|
<div :class="{ 'is-hovered': isLoggedIn }" class="box">
|
||||||
<div :class="{ 'is-hovered': isLoggedIn }" class="box">
|
<div class="title is-5">Using a social account</div>
|
||||||
<div class="title is-5">Using a social account</div>
|
<Form />
|
||||||
<div v-if="isLoggedIn" class="fields">
|
|
||||||
<b-field label="Name">
|
|
||||||
<b-input v-model="user.name"></b-input>
|
|
||||||
</b-field>
|
|
||||||
<b-field label="Company">
|
|
||||||
<b-input v-model="user.company"></b-input>
|
|
||||||
</b-field>
|
|
||||||
</div>
|
|
||||||
<div v-else class="buttons">
|
|
||||||
<b-button @click="logIn" type="is-primary" outlined expanded>
|
|
||||||
SignIn via Twitter
|
|
||||||
</b-button>
|
|
||||||
<b-button @click="logIn" :disabled="true" type="is-primary" outlined expanded>
|
|
||||||
SignIn via Github
|
|
||||||
</b-button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</fieldset>
|
||||||
|
|
||||||
<div v-show="status.type === 'is-danger' || status.type === 'is-success'" class="status">
|
<div v-show="status.type === 'is-danger' || status.type === 'is-success'" class="status">
|
||||||
<div :class="status.type" class="status-message">{{ status.msg }}</div>
|
<div :class="status.type" class="status-message">{{ status.msg }}</div>
|
||||||
@ -81,32 +68,66 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
|
import { mapGetters, mapActions } from 'vuex'
|
||||||
import Cloak from '@/components/Cloak'
|
import Cloak from '@/components/Cloak'
|
||||||
|
import Form from '@/components/Form'
|
||||||
const timeout = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
const timeout = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Cloak
|
Cloak,
|
||||||
|
Form
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
contributionType: null,
|
|
||||||
contributionIndex: null,
|
|
||||||
isContributeBtnSnown: false,
|
isContributeBtnSnown: false,
|
||||||
status: {
|
status: {
|
||||||
type: '',
|
type: '',
|
||||||
msg: ''
|
msg: ''
|
||||||
},
|
},
|
||||||
user: { name: '', handle: 'Anonymous', company: '' },
|
|
||||||
loading: false
|
loading: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isLoggedIn() {
|
...mapGetters('user', ['isLoggedIn', 'hasErrorName']),
|
||||||
return !!this.user.name && this.user.name !== 'Anonymous'
|
userName: {
|
||||||
|
get() {
|
||||||
|
return this.$store.state.user.name
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$store.commit('user/SET_NAME', value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
userHandle: {
|
||||||
|
get() {
|
||||||
|
return this.$store.state.user.handle
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$store.commit('user/SET_HANDLE', value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
userCompany: {
|
||||||
|
get() {
|
||||||
|
return this.$store.state.user.company
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$store.commit('user/SET_COMPANY', value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
contributionType: {
|
||||||
|
get() {
|
||||||
|
return this.$store.state.user.contributionType
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$store.commit('user/SET_CONTRIBUTION_TYPE', value)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
isContributeBtnDisabled() {
|
isContributeBtnDisabled() {
|
||||||
return !this.contributionType || (!this.isLoggedIn && this.contributionType !== 'anonymous')
|
return (
|
||||||
|
!this.contributionType ||
|
||||||
|
(!this.isLoggedIn && this.contributionType !== 'anonymous') ||
|
||||||
|
this.hasErrorName.invalid
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
@ -115,8 +136,8 @@ export default {
|
|||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
console.log('data', data)
|
console.log('data', data)
|
||||||
if (data.name !== 'Anonymous') {
|
if (data.name !== 'Anonymous') {
|
||||||
this.user.handle = data.handle
|
this.userHandle = data.handle
|
||||||
this.user.name = data.name
|
this.userName = data.name
|
||||||
// TODO check whether it's github or twitter
|
// TODO check whether it's github or twitter
|
||||||
this.contributionType = 'twitter'
|
this.contributionType = 'twitter'
|
||||||
}
|
}
|
||||||
@ -125,33 +146,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
makeTweet() {
|
...mapActions('user', ['makeTweet', 'logOut']),
|
||||||
const tweetText = `Just made the contribution %23${this.contributionIndex} to Tornado.cash Trusted Setup Ceremony! 🚀`
|
|
||||||
const popUpWindowWidth = 600
|
|
||||||
const popUpWindowHeight = 250
|
|
||||||
const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : window.screenX
|
|
||||||
const dualScreenTop = window.screenTop !== undefined ? window.screenTop : window.screenY
|
|
||||||
|
|
||||||
const width = window.innerWidth
|
|
||||||
? window.innerWidth
|
|
||||||
: document.documentElement.clientWidth
|
|
||||||
? document.documentElement.clientWidth
|
|
||||||
: screen.width
|
|
||||||
const height = window.innerHeight
|
|
||||||
? window.innerHeight
|
|
||||||
: document.documentElement.clientHeight
|
|
||||||
? document.documentElement.clientHeight
|
|
||||||
: screen.height
|
|
||||||
|
|
||||||
const systemZoom = width / window.screen.availWidth
|
|
||||||
const left = (width - popUpWindowWidth) / 2 / systemZoom + dualScreenLeft
|
|
||||||
const top = (height - popUpWindowHeight) / 2 / systemZoom + dualScreenTop
|
|
||||||
window.open(
|
|
||||||
`https://twitter.com/intent/tweet?text=${tweetText}`,
|
|
||||||
'',
|
|
||||||
`menubar=no,toolbar=no,resizable=yes,scrollbars=no,height=${popUpWindowHeight},width=${popUpWindowWidth},top=${top},left=${left}`
|
|
||||||
)
|
|
||||||
},
|
|
||||||
async makeContribution({ retry = 0 } = {}) {
|
async makeContribution({ retry = 0 } = {}) {
|
||||||
try {
|
try {
|
||||||
this.isContributeBtnSnown = true
|
this.isContributeBtnSnown = true
|
||||||
@ -169,12 +164,12 @@ export default {
|
|||||||
console.log('Updated params', result)
|
console.log('Updated params', result)
|
||||||
|
|
||||||
this.status.msg = 'Uploading and verifying your contribution'
|
this.status.msg = 'Uploading and verifying your contribution'
|
||||||
console.log('this.user.name', this.user)
|
console.log('this.user.name', this.userName, this.userHandle, this.userCompany)
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('response', new Blob([result], { type: 'application/octet-stream' }))
|
formData.append('response', new Blob([result], { type: 'application/octet-stream' }))
|
||||||
if (this.contributionType !== 'anonymous') {
|
if (this.contributionType !== 'anonymous') {
|
||||||
formData.append('name', this.user.name)
|
formData.append('name', this.userName)
|
||||||
formData.append('company', this.user.company)
|
formData.append('company', this.userCompany)
|
||||||
}
|
}
|
||||||
const resp = await fetch('api/response', {
|
const resp = await fetch('api/response', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -184,7 +179,7 @@ export default {
|
|||||||
this.status.msg = 'Your contribution is verified and recorded. Thank you.'
|
this.status.msg = 'Your contribution is verified and recorded. Thank you.'
|
||||||
this.status.type = 'is-success'
|
this.status.type = 'is-success'
|
||||||
const responseData = await resp.json()
|
const responseData = await resp.json()
|
||||||
this.contributionIndex = responseData.contributionIndex
|
this.$store.commit('user/SET_CONTRIBUTION_INDEX', responseData.contributionIndex)
|
||||||
} else if (resp.status === 422) {
|
} else if (resp.status === 422) {
|
||||||
if (retry < 3) {
|
if (retry < 3) {
|
||||||
console.log(`Looks like someone else uploaded contribution ahead of us, retrying`)
|
console.log(`Looks like someone else uploaded contribution ahead of us, retrying`)
|
||||||
@ -208,13 +203,12 @@ export default {
|
|||||||
this.loading = false
|
this.loading = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
logIn() {
|
|
||||||
this.contributionType = 'twitter'
|
|
||||||
window.location.replace('/api/connect')
|
|
||||||
},
|
|
||||||
onAnonymousHandler() {
|
onAnonymousHandler() {
|
||||||
|
this.logOut()
|
||||||
this.contributionType = 'anonymous'
|
this.contributionType = 'anonymous'
|
||||||
this.user = { name: '', handle: 'Anonymous', company: '' }
|
this.userName = null
|
||||||
|
this.userHandle = 'Anonymous'
|
||||||
|
this.userCompany = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
93
store/user.js
Normal file
93
store/user.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
const state = () => {
|
||||||
|
return {
|
||||||
|
name: null,
|
||||||
|
handle: 'Anonymous',
|
||||||
|
company: '',
|
||||||
|
contributionType: null,
|
||||||
|
contributionIndex: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
SET_NAME(state, name) {
|
||||||
|
state.name = name
|
||||||
|
},
|
||||||
|
SET_HANDLE(state, handle) {
|
||||||
|
state.handle = handle
|
||||||
|
},
|
||||||
|
SET_COMPANY(state, company) {
|
||||||
|
state.company = company
|
||||||
|
},
|
||||||
|
SET_CONTRIBUTION_TYPE(state, contributionType) {
|
||||||
|
state.contributionType = contributionType
|
||||||
|
},
|
||||||
|
SET_CONTRIBUTION_INDEX(state, contributionIndex) {
|
||||||
|
state.contributionIndex = contributionIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getters = {
|
||||||
|
isLoggedIn: (state) => {
|
||||||
|
return state.name !== null && state.name !== 'Anonymous'
|
||||||
|
},
|
||||||
|
hasErrorName: (state) => {
|
||||||
|
const name = state.name
|
||||||
|
if (name === null) {
|
||||||
|
return { invalid: false, msg: '' }
|
||||||
|
}
|
||||||
|
if (name === '') {
|
||||||
|
return { invalid: true, msg: 'Name is empty' }
|
||||||
|
}
|
||||||
|
if (name.length < 4) {
|
||||||
|
return { invalid: true, msg: 'Name is too short' }
|
||||||
|
}
|
||||||
|
if (name.length > 35) {
|
||||||
|
return { invalid: true, msg: 'Name is too long' }
|
||||||
|
}
|
||||||
|
return { invalid: false, msg: '' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
twitterLogIn() {
|
||||||
|
window.location.replace('/api/connect')
|
||||||
|
},
|
||||||
|
makeTweet({ state }) {
|
||||||
|
const tweetText = `Just made the contribution %23${state.contributionIndex} to Tornado.cash Trusted Setup Ceremony! 🚀`
|
||||||
|
const popUpWindowWidth = 600
|
||||||
|
const popUpWindowHeight = 250
|
||||||
|
const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : window.screenX
|
||||||
|
const dualScreenTop = window.screenTop !== undefined ? window.screenTop : window.screenY
|
||||||
|
|
||||||
|
const width = window.innerWidth
|
||||||
|
? window.innerWidth
|
||||||
|
: document.documentElement.clientWidth
|
||||||
|
? document.documentElement.clientWidth
|
||||||
|
: screen.width
|
||||||
|
const height = window.innerHeight
|
||||||
|
? window.innerHeight
|
||||||
|
: document.documentElement.clientHeight
|
||||||
|
? document.documentElement.clientHeight
|
||||||
|
: screen.height
|
||||||
|
|
||||||
|
const systemZoom = width / window.screen.availWidth
|
||||||
|
const left = (width - popUpWindowWidth) / 2 / systemZoom + dualScreenLeft
|
||||||
|
const top = (height - popUpWindowHeight) / 2 / systemZoom + dualScreenTop
|
||||||
|
window.open(
|
||||||
|
`https://twitter.com/intent/tweet?text=${tweetText}`,
|
||||||
|
'',
|
||||||
|
`menubar=no,toolbar=no,resizable=yes,scrollbars=no,height=${popUpWindowHeight},width=${popUpWindowWidth},top=${top},left=${left}`
|
||||||
|
)
|
||||||
|
},
|
||||||
|
async logOut() {
|
||||||
|
await fetch('/api/logout')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state,
|
||||||
|
getters,
|
||||||
|
mutations,
|
||||||
|
actions
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user