Initial commit

This commit is contained in:
Andrey Shovkoplyas 2018-03-20 11:38:33 +03:00
commit 86509b1712
No known key found for this signature in database
GPG Key ID: EAAB7C8622D860A4
18 changed files with 2870 additions and 0 deletions

1
README.md Normal file
View File

@ -0,0 +1 @@
# status-dapp

2398
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

16
package.json Normal file
View File

@ -0,0 +1,16 @@
{
"name": "status-dapp",
"version": "0.0.1",
"scripts": {
"watch": "webpack -d --watch",
"build": "webpack -p"
},
"dependencies": {
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-native-web": "^0.3.0-rc.4"
},
"devDependencies": {
"webpack": "^3.10.0"
}
}

37
project.clj Normal file
View File

@ -0,0 +1,37 @@
(defproject status-dapp "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.9.0"]
[org.clojure/clojurescript "1.10.191"]
[reagent "0.7.0" :exclusions [cljsjs/react cljsjs/react-dom]]
[re-frame "0.10.2"]
[cljs-web3 "0.19.0-0-9"]
[day8.re-frame/http-fx "0.1.5"]]
:plugins [[lein-cljsbuild "1.1.7"]]
:min-lein-version "2.5.3"
:clean-targets ^{:protect false} ["resources/public/js/compiled" "target"]
:source-paths ["src/cljs"]
:profiles
{:dev
{:dependencies [[re-frisk "0.5.3"]]
:plugins [[lein-figwheel "0.5.15"]]
:cljsbuild {:builds {:app {:figwheel {:on-jsload "status-dapp.core/mount-root"}
:compiler {:main status-dapp.core
:output-dir "resources/public/js/compiled/out"
:asset-path "js/compiled/out"}}}}}
:prod
{:cljsbuild {:builds {:app {:compiler {:optimizations :whitespace
:pretty-print false}}}}}}
:cljsbuild
{:builds
{:app {:id "app"
:source-paths ["src/cljs"]
:compiler {:main status-dapp.core
:output-to "resources/public/js/compiled/app.js"
:foreign-libs [{:file "resources/public/js/bundle.js"
:provides ["cljsjs.react" "cljsjs.react.dom" "webpack.bundle"]}]}}}}
:aliases {"figwheel-repl" ["with-profile" "dev" "figwheel"]
"build-prod" ["do"
["clean"]
["with-profile" "prod" "cljsbuild" "once"]]})

View File

@ -0,0 +1,27 @@
<!doctype html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta content="ie=edge" http-equiv="x-ua-compatible" />
<title>DAPP</title>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
<style>
.app, html, body {
height:100%;
margin:0;
}
.app {
display: flex;
flex-direction: column;
flex: 1;
}
</style>
</head>
<body>
<div id="app" class="app"></div>
<script src="js/compiled/app.js"></script>
<script>
status_dapp.core.init();
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,14 @@
(ns status-dapp.components
(:require [status-dapp.react-native-web :as react]))
(defn button [label on-press]
[react/touchable-highlight {:on-press on-press}
[react/view {:style {:flex-direction :row :align-items :center :margin-top 10}}
[react/view {:style {:padding 4 :background-color "#4360df" :border-radius 4}}
[react/text {:style {:color :white}} label]]]])
(defn label [label value]
[react/view {:style {:flex-direction :row :align-items :center :margin-top 10}}
[react/view {:style {:padding 4 :background-color "#4360df99" :border-radius 4}}
[react/text {:style {:color :white}} label]]
[react/text {:style {:margin-left 10}} value]])

View File

@ -0,0 +1,4 @@
(ns status-dapp.config)
(def debug?
^boolean goog.DEBUG)

View File

@ -0,0 +1,8 @@
(ns status-dapp.constants)
(def chains
{"1" "Mainnet"
"3" "Testnet Ropsten"
"4" "Testnet Rinkeby"})
(def stt-ropsten-contract "0x34358C45FbA99ef9b78cB501584E8cBFa6f85Cef")

View File

@ -0,0 +1,23 @@
(ns status-dapp.core
(:require [reagent.core :as reagent]
[re-frame.core :as re-frame]
status-dapp.events
status-dapp.subs
[status-dapp.views :as views]
[status-dapp.config :as config]))
(defn dev-setup []
(when config/debug?
(enable-console-print!)
(println "dev mode")))
(defn mount-root []
(re-frame/clear-subscription-cache!)
(reagent/render [views/main]
(.getElementById js/document "app")))
(defn ^:export init []
(re-frame/dispatch-sync [:initialize-db])
(re-frame/dispatch [:request-web3-async-data])
(dev-setup)
(mount-root))

View File

@ -0,0 +1,6 @@
(ns status-dapp.db)
(def default-db
{:web3 (when (exists? js/web3) js/web3)
:web3-async-data {}
:view-id (if (exists? js/web3) :web3 :no-web3)})

View File

@ -0,0 +1,106 @@
(ns status-dapp.events
(:require [re-frame.core :as re-frame]
[status-dapp.db :as db]
[day8.re-frame.http-fx]))
(defn set-web3-value [key]
(fn [error result]
(re-frame/dispatch [:set-in [:web3-async-data key] result])))
(re-frame/reg-fx
:web3-node-fx
(fn [web3]
(.getNode (.-version web3) (set-web3-value :node))))
(re-frame/reg-fx
:web3-network-fx
(fn [web3]
(.getNetwork (.-version web3) (set-web3-value :network))))
(re-frame/reg-fx
:web3-ethereum-fx
(fn [web3]
(.getEthereum (.-version web3) (set-web3-value :ethereum))))
(re-frame/reg-fx
:web3-whisper-fx
(fn [web3]
(.getWhisper (.-version web3) (set-web3-value :whisper))))
(re-frame/reg-fx
:web3-accounts-fx
(fn [web3]
(.getAccounts (.-eth web3) (set-web3-value :accounts))))
(re-frame/reg-fx
:web3-syncyng-fx
(fn [web3]
(.getSyncing (.-eth web3) (set-web3-value :syncing))))
(re-frame/reg-fx
:web3-gas-price-fx
(fn [web3]
(.getGasPrice (.-eth web3) (set-web3-value :gas-price))))
(re-frame/reg-fx
:send-transaction-fx
(fn [[web3 data]]
(.sendTransaction (.-eth web3) data #())))
(re-frame/reg-event-db
:set
(fn [db [_ k v]]
(assoc db k v)))
(re-frame/reg-event-db
:set-in
(fn [db [_ path v]]
(assoc-in db path v)))
(re-frame/reg-event-db
:initialize-db
(fn [_ _]
db/default-db))
;; Status web3 doesn't support sync calls
(re-frame/reg-event-fx
:request-web3-async-data
(fn [{{:keys [web3] :as db} :db} _]
(when web3
{:db (update db :web3-async-data
assoc
:api (.-api (.-version web3))
:default-account (.-defaultAccount (.-eth web3))
:default-block (.-defaultBlock (.-eth web3)))
:web3-node-fx web3
:web3-network-fx web3
:web3-ethereum-fx web3
:web3-whisper-fx web3
:web3-accounts-fx web3
:web3-syncyng-fx web3
:web3-gas-price-fx web3})))
(re-frame/reg-event-fx
:send-transaction
(fn [{{:keys [web3]} :db} [_ data]]
(when web3
{:send-transaction-fx [web3 (clj->js data)]})))
(re-frame/reg-event-fx
:good-request-ropsten-eth
(fn [_ _]
(js/alert "Faucet request recieved")))
(re-frame/reg-event-fx
:bad-request-ropsten-eth
(fn [_ _]
(js/alert "Faucet request error")))
(re-frame/reg-event-fx
:request-ropsten-eth
(fn [_ [_ address]]
(js/alert "Requested")
{:http-xhrio {:method :get
:uri (str "http://51.15.45.169:3001/donate/" address)
:on-success [:good-request-ropsten-eth]
:on-failure [:bad-request-ropsten-eth]}}))

View File

@ -0,0 +1,31 @@
(ns status-dapp.react-native-web
(:require [reagent.core :as reagent]
[clojure.string :as string]))
(defn get-react-property [name]
(goog.object/get js/ReactNativeWeb name))
(defn adapt-class [class]
(when class
(reagent/adapt-react-class class)))
(defn get-class [name]
(adapt-class (get-react-property name)))
(def view (get-class "View"))
(def text-class (get-class "Text"))
(def image (get-class "Image"))
(def touchable-highlight (get-class "TouchableOpacity"))
(def scroll-view (get-class "ScrollView"))
(def text-input (get-class "TextInput"))
(defn text
([t]
(reagent/as-element [text-class t]))
([{:keys [uppercase?] :as opts} t & ts]
(reagent/as-element
(let [ts (cond->> (conj ts t)
uppercase? (map #(when % (string/upper-case %))))]
(vec (concat
[text-class opts]
ts))))))

View File

@ -0,0 +1,12 @@
(ns status-dapp.subs
(:require [re-frame.core :as re-frame]))
(re-frame/reg-sub
:get
(fn [db [_ k]]
(get db k)))
(re-frame/reg-sub
:get-in
(fn [db [_ path]]
(get-in db path)))

View File

@ -0,0 +1,68 @@
(ns status-dapp.utils
(:require [clojure.walk :as w]))
(defn atom? [sub]
(or (vector? sub)
(and (seq sub)
(#{`reagent.core/atom} (first sub)))))
(defn walk-sub [sub form->sym]
(if (coll? sub)
(w/postwalk (fn [f]
(or (form->sym f) f)) sub)
(or (form->sym sub) sub)))
(defn prepare-subs [subs]
(let [pairs (map (fn [[form sub]]
{:form form
:sub sub
:sym (if (atom? sub)
(gensym (str (if (map? form) "keys" form)))
form)})
(partition 2 subs))
form->sym (->> pairs
(map (fn [{:keys [form sym]}]
[form sym]))
(into {}))]
[(mapcat (fn [{:keys [form sym sub]}]
(if (vector? sub)
[sym `(re-frame.core/subscribe ~(walk-sub sub form->sym))]
[form (walk-sub sub form->sym)]))
pairs)
(apply concat (keep (fn [{:keys [sym form sub]}]
(when (atom? sub)
[form `(deref ~sym)]))
pairs))]))
(defmacro letsubs [args & body])
(defmacro defview
[n params & rest-body]
(let [first-symbol (ffirst rest-body)
rest-body' (if (and (symbol? first-symbol)
(= (name first-symbol) "letsubs"))
(rest (first rest-body))
rest-body)
[subs component-map body] (case (count rest-body')
1 [nil {} (first rest-body')]
2 [(first rest-body') {} (second rest-body')]
3 rest-body')
[subs-bindings vars-bindings] (prepare-subs subs)]
`(do
(when-not (find-ns 're-frame.core)
(require 're-frame.core))
(defn ~n ~params
(let [~@subs-bindings]
(reagent.core/create-class
(merge ~(->> component-map
(map (fn [[k f]]
(let [args (gensym "args")]
[k `(fn [& ~args]
(let [~@vars-bindings]
(apply ~f ~args)))])))
(into {}))
{:display-name (name '~n)
:reagent-render
(fn ~params
(let [~@vars-bindings]
~body))})))))))

View File

@ -0,0 +1,57 @@
(ns status-dapp.views
(:require-macros [status-dapp.utils :refer [defview letsubs]])
(:require [status-dapp.react-native-web :as react]
[re-frame.core :as re-frame]
[status-dapp.components :as ui]
[status-dapp.constants :as constants]))
(defn no-web3 []
[react/view {:style {:flex 1 :padding 10 :align-items :center :justify-content :center}}
[react/text {:style {:font-weight :bold}}
"Can't find web3 library"]])
(defn send-transaction [from]
(re-frame/dispatch [:send-transaction {:from from
:to constants/stt-ropsten-contract
:value 0
:gasPrise 150000}]))
(defview web3-view []
(letsubs [{:keys [api node network ethereum whisper accounts syncing gas-price
default-account default-block]}
[:get :web3-async-data]]
[react/scroll-view {:style {:flex 1}}
[react/view {:style {:flex 1 :padding 10}}
[react/view {:style {:flex-direction :row}}
;[ui/button "Request Ropsten ETH" #(re-frame/dispatch [:request-ropsten-eth (str (first accounts))])]
;[react/view {:style {:width 5}}]
(when (= "3" network)
[ui/button "Request 1000 STT" #(send-transaction (str (first accounts)))])]
[react/text {:style {:font-weight :bold :margin-top 20}} "Version"]
[ui/label "api" api]
[ui/label "node" node]
[ui/label "network" (str network " (" (or (constants/chains network) "Unknown") ")")]
[ui/label "ethereum" ethereum]
[ui/label "whisper" whisper]
[react/text {:style {:font-weight :bold :margin-top 20}} "Accounts"]
[ui/label "defaultAccount" default-account]
[ui/label "accounts" ""]
(for [account accounts]
[react/text account])
[react/text {:style {:font-weight :bold :margin-top 20}} "Eth"]
[ui/label "defaultBlock" default-block]
(if syncing
[react/view
[ui/label "isSyncing" "true"]
[ui/label "startingBlock" (.-startingBlock syncing)]
[ui/label "currentBlock" (.-currentBlock syncing)]
[ui/label "highestBlock" (.-highestBlock syncing)]]
[ui/label "isSyncing" "false"])
(when gas-price
[ui/label "gasPrice" (str (.toString gas-price 10) " wei")])]]))
(defview main []
(letsubs [view-id [:get :view-id]]
(case view-id
:web3 [web3-view]
[no-web3])))

9
src/js/main.js Normal file
View File

@ -0,0 +1,9 @@
window.deps = {
'react' : require('react'),
'react-dom' : require('react-dom'),
'react-native-web' : require('react-native-web'),
};
window.React = window.deps['react'];
window.ReactDOM = window.deps['react-dom'];
window.ReactNativeWeb = window.deps['react-native-web'];

15
webpack.config.js Normal file
View File

@ -0,0 +1,15 @@
const webpack = require('webpack');
const path = require('path');
const BUILD_DIR = path.resolve(__dirname, 'resources', 'public', 'js');
const APP_DIR = path.resolve(__dirname, 'src', 'js');
const config = {
entry: `${APP_DIR}/main.js`,
output: {
path: BUILD_DIR,
filename: 'bundle.js'
},
};
module.exports = config;