diff --git a/components/nav.js b/components/nav.js
index 34a712a..f37a24f 100644
--- a/components/nav.js
+++ b/components/nav.js
@@ -46,6 +46,9 @@ class Nav extends React.Component {
diff --git a/components/upvotevalidator.js b/components/upvotevalidator.js
new file mode 100644
index 0000000..9f47b1f
--- /dev/null
+++ b/components/upvotevalidator.js
@@ -0,0 +1,47 @@
+import React from 'react'
+import makeRPC from '../utils/rpcUtils'
+import encoding from '../utils/encoding'
+
+class UpvoteValidator extends React.Component {
+ constructor(props, context) {
+ super(props, context);
+ this.state = {};
+ }
+
+ upvote = () => {
+ console.log("validator: " + this.props.validator)
+ if (!this.props.user) {
+ window.location.href = '/signup';
+ return;
+ }
+
+ const secret = encoding.hex2ab(localStorage.getItem('mintPK'));
+ const publicKey = nacl.util.encodeBase64(nacl.sign.keyPair.fromSecretKey(secret).publicKey);
+
+ let txBody = {
+ type: "upvoteValidator",
+ entity: {
+ stamp: new Date().getTime(),
+ validator: this.props.validator._id,
+ // TODO actually user not needed. We can fetch user from DB by quering pubKey. To be Removed!
+ user: this.props.user._id,
+ }
+ }
+
+ console.log("txBody "+ txBody)
+ console.log("txBody "+ JSON.stringify(txBody))
+ console.log("publicKey "+ publicKey)
+ console.log("secret "+ secret)
+ makeRPC(txBody, publicKey, secret);
+
+ this.props.upvoteCallback(this.props.validator._id);
+ }
+
+ render() {
+ return (
+
+ )
+ }
+}
+
+export default UpvoteValidator
diff --git a/pages/validators.js b/pages/validators.js
new file mode 100644
index 0000000..d5a0773
--- /dev/null
+++ b/pages/validators.js
@@ -0,0 +1,143 @@
+import Link from 'next/link'
+import React from 'react'
+import Nav from '../components/nav'
+import encoding from '../utils/encoding'
+import makeRPC from '../utils/rpcUtils'
+import ajaxUtils from '../utils/ajaxUtils'
+import UpvoteValidator from '../components/upvotevalidator'
+import bson from 'bson'
+import Helmet from 'react-helmet'
+
+class Validators extends React.Component {
+
+ static async getInitialProps({ req }) {
+ const id = req.query.id;
+ const data = await ajaxUtils.getValidators();
+ return {
+ active: data.active,
+ standby: data.standby
+ };
+ }
+
+ constructor(props, context) {
+ super(props, context);
+ this.state = {
+ active: props.active,
+ standby: props.standby
+ };
+ }
+
+ async componentDidMount() {
+ const key = localStorage.getItem('mintPK');
+
+ if (!key) {
+ return;
+ }
+
+ const keyPair = nacl.sign.keyPair.fromSecretKey(encoding.hex2ab(key));
+ const publicKey = encoding.toHexString(keyPair.publicKey).toUpperCase();
+
+ const user = await ajaxUtils.loadUser(publicKey);
+ this.setState({ user });
+ }
+
+ upvoteCallback = (validatorId) => {
+ // TODO should be refactored to update the state.
+ // ATM page needs to be refreshed to see updated results
+ }
+
+ showLoginPrompt = e => {
+ e.preventDefault();
+ const pk = prompt("Please enter your private key");
+ if (!pk) {
+ return;
+ }
+ window.localStorage.setItem("mintPK", pk);
+ window.location.href = "/";
+ }
+
+ render() {
+ const active = this.state.active.map((validator, index) => {
+ return (
+
+ |
+
+
+
+ |
+ {validator.rank} |
+ {validator._id} |
+ {validator.name} |
+ {validator.upvotes} |
+
+ )
+ });
+ const standby = this.state.standby.map((validator, index) => {
+ return (
+
+ |
+
+
+
+ |
+ {validator.rank} |
+ {validator._id} |
+ {validator.name} |
+ {validator.upvotes} |
+
+ )
+ });
+ return (
+
+
+
+
+
+
Active validators:
+
+
+
+
+ |
+ Rank |
+ Address |
+ Witness |
+ Votes |
+
+
+
+ {active}
+
+
+
+
+
+
+
+
+
Standby validators:
+
+
+
+
+ |
+ Rank |
+ Address |
+ Witness |
+ Votes |
+
+
+
+ {standby}
+
+
+
+
+
+
+
+ )
+ }
+}
+
+export default Validators;
diff --git a/routes.js b/routes.js
index fef8d97..a5e330d 100644
--- a/routes.js
+++ b/routes.js
@@ -7,7 +7,8 @@ module.exports = () => {
'/ask': { page: '/index' },
'/signup': { page: '/signup' },
'/submit': { page: '/submit' },
+ '/validators': { page: '/validators' },
'/post': { page: '/post' },
'/comment-reply': { page: '/commentreply' }
}
-}
\ No newline at end of file
+}
diff --git a/routes/rpc.routes.js b/routes/rpc.routes.js
index 06aede3..b734155 100644
--- a/routes/rpc.routes.js
+++ b/routes/rpc.routes.js
@@ -24,4 +24,4 @@ router.route('/rpc').post((req, res) => {
});
});
-module.exports = router;
\ No newline at end of file
+module.exports = router;
diff --git a/routes/site.routes.js b/routes/site.routes.js
index cafa729..9e6cf85 100644
--- a/routes/site.routes.js
+++ b/routes/site.routes.js
@@ -1,6 +1,6 @@
const express = require('express');
const router = express.Router();
-const dbUtil = require('../db');
+const dbUtil = require('../db');
const async = require('async');
const ObjectId = require('mongodb').ObjectId;
const _ = require('lodash');
@@ -19,11 +19,11 @@ function unflatten( array, parent, tree ){
if( !_.isEmpty( children ) ){
if(!parent._id){
- tree = children;
+ tree = children;
}else{
parent['comments'] = children;
}
- _.each( children, function( child ){ unflatten( array, child ) } );
+ _.each( children, function( child ){ unflatten( array, child ) } );
}
return tree;
@@ -33,7 +33,42 @@ router.route('/ajax/fetch-user').get((req, res) => {
const db = dbUtil.getDB();
db.collection('users').findOne({publicKey: req.query.pk}, function(err, user){
- res.json({ user: user});
+ res.json({ user: user });
+ });
+});
+
+router.route('/ajax/validators').get((req, res) => {
+ const numActiveValidators = 3
+ const db = dbUtil.getDB();
+ db.collection("validators").aggregate(
+ [
+ {
+ $project: {
+ name: 1,
+ upvotes: 1
+ }
+ },
+ {
+ $sort:
+ {upvotes : -1}
+ }
+ ]
+ ).toArray(function(err, result) {
+ if (err) throw err;
+
+ result.forEach((element, index) => {
+ element.rank = index + 1
+ })
+ console.log("here: ")
+ console.log(result)
+ // TODO looking at the fact that I'm starting to add knowledge about 'numActiveValidators' here
+ // and that knowledge is duplicted in ABCI backend app, I'm thinking we could consider
+ // moving this as a proper backend service exposing API to the fronted. Effectively,
+ // building single point of access for frontend (but I may be biased since I'm mainly a backend dev :P)
+ res.json({
+ active : result.slice(0, numActiveValidators),
+ standby: result.slice(numActiveValidators)
+ })
});
});
@@ -56,7 +91,7 @@ router.route('/ajax/get-posts').get((req, res) => {
db.collection('users').findOne({ _id: post.author }, function(err, user){
post.author = user;
cb(null, post);
- });
+ });
}, (err, result) => {
res.json({ posts: result });
});
@@ -170,4 +205,4 @@ router.route('/ajax/get-comment-upvote-status').get((req, res) => {
});
});
-module.exports = router;
\ No newline at end of file
+module.exports = router;
diff --git a/utils/ajaxUtils.js b/utils/ajaxUtils.js
index 3f8f974..0392b26 100644
--- a/utils/ajaxUtils.js
+++ b/utils/ajaxUtils.js
@@ -19,7 +19,7 @@ export default {
else if (path === '/ask') {
url += '?type=askUH';
}
-
+
const response = await fetch(url, {
method: 'GET',
headers: {
@@ -27,7 +27,7 @@ export default {
},
credentials: 'same-origin'
});
-
+
const data = await response.json();
return data.posts;
},
@@ -39,7 +39,7 @@ export default {
},
credentials: 'same-origin'
});
-
+
const data = await response.json();
return data.user;
},
@@ -51,7 +51,7 @@ export default {
},
credentials: 'same-origin'
});
-
+
const data = await response.json();
return data.status;
},
@@ -63,7 +63,7 @@ export default {
},
credentials: 'same-origin'
});
-
+
const data = await response.json();
return data.status;
},
@@ -75,10 +75,22 @@ export default {
},
credentials: 'same-origin'
});
-
+
const data = await response.json();
return data.post;
},
+ getValidators: async () => {
+ const response = await fetch(publicRuntimeConfig.baseUrl + '/ajax/validators', {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ credentials: 'same-origin'
+ });
+
+ const data = await response.json();
+ return data;
+ },
getComment: async (id) => {
const response = await fetch(publicRuntimeConfig.baseUrl + '/ajax/comment?id=' + id, {
method: 'GET',
@@ -87,8 +99,8 @@ export default {
},
credentials: 'same-origin'
});
-
+
const data = await response.json();
return data.comment;
}
-}
\ No newline at end of file
+}
diff --git a/utils/rpcUtils.js b/utils/rpcUtils.js
index bd2e196..bf2c954 100644
--- a/utils/rpcUtils.js
+++ b/utils/rpcUtils.js
@@ -1,6 +1,7 @@
import encoding from './encoding';
export default async function makeRPC(txBody, publicKey, secret, cb) {
+ console.log("makeRPC txBody "+ txBody)
let signature = nacl.sign.detached(nacl.util.decodeUTF8(JSON.stringify(txBody)), secret);
let tx = {
@@ -21,4 +22,4 @@ export default async function makeRPC(txBody, publicKey, secret, cb) {
const json = await response.json();
cb && cb(json);
-}
\ No newline at end of file
+}