/ src / modules / Vote / Vote.jsx
Vote.jsx
  1  import React, { Component } from 'react'
  2  import PropTypes from 'prop-types'
  3  import ReactImageFallback from 'react-image-fallback'
  4  import styles from './Vote.module.scss'
  5  import sntIcon from '../../common/assets/images/SNT.svg'
  6  import CategoriesUtils from '../Categories/Categories.utils'
  7  import Categories from '../../common/utils/categories'
  8  import icon from '../../common/assets/images/icon.svg'
  9  import Modal from '../../common/components/Modal'
 10  import { DappModel } from '../../common/utils/models'
 11  
 12  const getCategoryName = category =>
 13    Categories.find(x => x.key === category).value
 14  
 15  class Vote extends Component {
 16    constructor(props) {
 17      super(props)
 18      this.onClickUpvote = this.onClickUpvote.bind(this)
 19      this.onClickDownvote = this.onClickDownvote.bind(this)
 20      this.handleChange = this.handleChange.bind(this)
 21      this.onClickVote = this.onClickVote.bind(this)
 22    }
 23  
 24    onClickUpvote() {
 25      const { dapp, onClickUpvote, fetchVoteRating } = this.props
 26      let { sntValue } = this.props
 27      if (sntValue === '') sntValue = 0
 28      onClickUpvote()
 29      fetchVoteRating(dapp, true, parseInt(sntValue, 10))
 30    }
 31  
 32    onClickDownvote() {
 33      const { dapp, onClickDownvote, fetchVoteRating } = this.props
 34      onClickDownvote()
 35      fetchVoteRating(dapp, false)
 36    }
 37  
 38    handleChange(e) {
 39      const { value } = e.target
 40      if (value !== '' && /^[1-9][0-9]*$/.test(value) === false) return
 41  
 42      const intValue = value === '' ? 0 : parseInt(value, 10)
 43      if (intValue > 1571296) return
 44  
 45      const { dapp, onInputSntValue, fetchVoteRating } = this.props
 46      onInputSntValue(value)
 47      fetchVoteRating(dapp, true, intValue)
 48    }
 49  
 50    onClickVote() {
 51      const { dapp, sntValue, isUpvote, upVote, downVote } = this.props
 52      if (isUpvote) upVote(dapp, parseInt(sntValue, 10))
 53      else downVote(dapp, sntValue)
 54    }
 55  
 56    render() {
 57      const {
 58        visible,
 59        onClickClose,
 60        isUpvote,
 61        dapp,
 62        dapps,
 63        sntValue,
 64        afterVoteRating,
 65      } = this.props
 66  
 67      if (dapp === null) {
 68        return <Modal visible={false} onClickClose={onClickClose} />
 69      }
 70  
 71      //const catPosition = dapp.categoryPosition
 72      // const upvoteSNTcost = currentSNTamount + parseInt(sntValue, 10)
 73      const currentSNTamount = dapp.sntValue
 74      const dappsByCategory = dapps.filter(
 75        dapp_ => dapp_.category === dapp.category,
 76      )
 77  
 78      let catPosition = dappsByCategory.length
 79      for (let i = 0; i < dappsByCategory.length; ++i) {
 80        if (dapp.id === dappsByCategory[i].id) {
 81          catPosition = i + 1
 82          break
 83        }
 84      }
 85  
 86      let afterVoteCategoryPosition = null
 87      if (afterVoteRating !== null) {
 88        afterVoteCategoryPosition = 1
 89        for (let i = 0; i < dappsByCategory.length; ++i) {
 90          if (dappsByCategory[i].id === dapp.id) continue
 91          if (dappsByCategory[i].sntValue < dapp.sntValue + afterVoteRating) break
 92          afterVoteCategoryPosition++
 93        }
 94      }
 95  
 96      return (
 97        <Modal
 98          visible={visible && window.location.hash === '#vote'}
 99          onClickClose={onClickClose}
100          windowClassName={styles.modalWindow}
101          contentClassName={styles.modalContent}
102        >
103          <div className={styles.tabs}>
104            <button
105              className={isUpvote ? styles.active : ''}
106              type="button"
107              onClick={this.onClickUpvote}
108            >
109              ↑ UPVOTE
110            </button>
111            <button
112              className={!isUpvote ? styles.active : ''}
113              type="button"
114              onClick={this.onClickDownvote}
115            >
116              ↓ DOWNVOTE
117            </button>
118          </div>
119          <div className={styles.dapp}>
120            <ReactImageFallback
121              className={styles.image}
122              src={dapp.image}
123              fallbackImage={icon}
124              alt="App icon"
125              width={24}
126              height={24}
127            />
128            {dapp.name}
129          </div>
130          <div className={styles.items}>
131            <div className={styles.itemRow}>
132              <span className={styles.item}>
133                <img src={sntIcon} alt="SNT" width="24" height="24" />
134                {currentSNTamount.toLocaleString()}
135              </span>
136              {isUpvote && afterVoteRating !== null && (
137                <span className={styles.greenBadge}>
138                  {`${(dapp.sntValue + afterVoteRating).toLocaleString()} ↑`}
139                </span>
140              )}
141              {!isUpvote && afterVoteRating !== null && (
142                <span className={styles.redBadge}>
143                  {`${(dapp.sntValue - afterVoteRating).toLocaleString()} ↓`}
144                </span>
145              )}
146            </div>
147            <div className={styles.itemRow}>
148              <span className={styles.item}>
149                <img
150                  src={CategoriesUtils(dapp.category)}
151                  alt={getCategoryName(dapp.category)}
152                  width="24"
153                  height="24"
154                />
155                {`${getCategoryName(dapp.category)} №${catPosition}`}
156              </span>
157              {isUpvote &&
158                afterVoteCategoryPosition !== null &&
159                afterVoteCategoryPosition !== catPosition && (
160                  <span className={styles.greenBadge}>
161                    {`№${afterVoteCategoryPosition} ↑`}
162                  </span>
163                )}
164              {!isUpvote &&
165                afterVoteCategoryPosition !== null &&
166                afterVoteCategoryPosition !== catPosition && (
167                  <span className={styles.redBadge}>
168                    {`№${afterVoteCategoryPosition} ↓`}
169                  </span>
170                )}
171            </div>
172          </div>
173          {!isUpvote && (
174            <div
175              className={styles.inputArea}
176              style={{ opacity: sntValue !== '0' ? 1 : 0 }}
177            >
178              <span>{sntValue}</span>
179            </div>
180          )}
181          {isUpvote && (
182            <div className={`${styles.inputArea} ${styles.inputAreaBorder}`}>
183              <input
184                type="text"
185                value={sntValue}
186                onChange={this.handleChange}
187                style={{ width: `${21 * Math.max(1, sntValue.length)}px` }}
188              />
189            </div>
190          )}
191  
192          <div className={styles.footer}>
193            {isUpvote && (
194              <p className={styles.disclaimer}>
195                SNT you spend to upvote is locked in the contract and contributes
196                directly to {dapp.name}'s ranking.{' '}
197                <a href="#" target="_blank">
198                  Learn more↗
199                </a>
200              </p>
201            )}
202            {!isUpvote && (
203              <p className={styles.disclaimer}>
204                SNT you spend to downvote goes directly back to {dapp.name}.
205                Downvoting moves their DApp down by 1% of the current ranking. The
206                cost is fixed by our unique bonded curve.{' '}
207                <a href="#" target="_blank">
208                  Learn more↗
209                </a>
210              </p>
211            )}
212            <button
213              type="submit"
214              disabled={(!sntValue || sntValue === '0') && isUpvote}
215              onClick={this.onClickVote}
216            >
217              {isUpvote ? 'Upvote' : 'Downvote'}
218            </button>
219          </div>
220        </Modal>
221      )
222    }
223  }
224  
225  Vote.defaultProps = {
226    dapp: null,
227    afterVoteRating: null,
228  }
229  
230  Vote.propTypes = {
231    dapp: PropTypes.shape(DappModel),
232    isUpvote: PropTypes.bool.isRequired,
233    visible: PropTypes.bool.isRequired,
234    sntValue: PropTypes.string.isRequired,
235    afterVoteRating: PropTypes.number,
236    onClickClose: PropTypes.func.isRequired,
237    onClickUpvote: PropTypes.func.isRequired,
238    onClickDownvote: PropTypes.func.isRequired,
239    onInputSntValue: PropTypes.func.isRequired,
240    fetchVoteRating: PropTypes.func.isRequired,
241    upVote: PropTypes.func.isRequired,
242    downVote: PropTypes.func.isRequired,
243  }
244  
245  export default Vote