Merge branch 'develop' into currency-filter-or-fix-eth

This commit is contained in:
P 2018-02-16 16:20:38 +01:00 committed by GitHub
commit 7ea65bdadf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 206 additions and 71 deletions

View File

@ -42,7 +42,6 @@
[cheshire "5.8.0"] [cheshire "5.8.0"]
[mpg "1.3.0"] [mpg "1.3.0"]
[pandect "0.6.1"] [pandect "0.6.1"]
[cljsjs/moment "2.17.1-1"]
[org.clojure/tools.nrepl "0.2.13"] [org.clojure/tools.nrepl "0.2.13"]
[com.cemerick/piggieback "0.2.2"] [com.cemerick/piggieback "0.2.2"]
[jarohen/chime "0.2.2"] [jarohen/chime "0.2.2"]

View File

@ -0,0 +1,35 @@
BEGIN;
-- restore the previous version of the view
CREATE OR REPLACE VIEW "public"."claims_view" AS
SELECT
i.title AS issue_title,
i.issue_number,
r.repo AS repo_name,
r.owner AS repo_owner,
COALESCE(u.name, u.login) AS user_name,
u.avatar_url AS user_avatar_url,
i.payout_receipt,
p.updated,
i.updated AS issue_updated,
i.balance_eth,
i.tokens,
i.value_usd,
p.state AS pr_state,
i.is_open AS issue_open,
(case when u.address IS NULL THEN false ELSE true END) AS user_has_address
FROM issues i,
users u,
repositories r,
pull_requests p
WHERE r.repo_id = i.repo_id
AND p.issue_id = i.issue_id
AND p.user_id = u.id
AND i.contract_address IS NOT NULL
AND i.comment_id IS NOT NULL
ORDER BY p.updated;
ALTER TABLE users
DROP COLUMN is_hidden_in_hunters;
COMMIT;

View File

@ -0,0 +1,35 @@
BEGIN;
ALTER TABLE users
ADD COLUMN is_hidden_in_hunters BOOLEAN NOT NULL DEFAULT FALSE;
CREATE OR REPLACE VIEW "public"."claims_view" AS
SELECT
i.title AS issue_title,
i.issue_number,
r.repo AS repo_name,
r.owner AS repo_owner,
COALESCE(u.name, u.login) AS user_name,
u.avatar_url AS user_avatar_url,
i.payout_receipt,
p.updated,
i.updated AS issue_updated,
i.balance_eth,
i.tokens,
i.value_usd,
p.state AS pr_state,
i.is_open AS issue_open,
(case when u.address IS NULL THEN false ELSE true END) AS user_has_address
FROM issues i,
users u,
repositories r,
pull_requests p
WHERE r.repo_id = i.repo_id
AND p.issue_id = i.issue_id
AND p.user_id = u.id
AND i.contract_address IS NOT NULL
AND i.comment_id IS NOT NULL
AND NOT u.is_hidden_in_hunters -- added
ORDER BY p.updated;
COMMIT;

View File

@ -17,6 +17,12 @@ WHERE NOT exists(SELECT 1
WHERE id = :id) WHERE id = :id)
RETURNING id, login, name, email, avatar_url, address, created; RETURNING id, login, name, email, avatar_url, address, created;
-- :name user-exists? :? :1
-- :doc Checks where a user exists in the database.
select 1
from users u
where u.id = :id;
-- :name update-user! :! :n -- :name update-user! :! :n
-- :doc updates an existing user record -- :doc updates an existing user record
UPDATE users UPDATE users
@ -571,6 +577,7 @@ WHERE
pr.commit_sha = i.commit_sha pr.commit_sha = i.commit_sha
AND u.id = pr.user_id AND u.id = pr.user_id
AND i.payout_receipt IS NOT NULL AND i.payout_receipt IS NOT NULL
AND NOT u.is_hidden_in_hunters
GROUP BY u.id GROUP BY u.id
ORDER BY total_usd DESC ORDER BY total_usd DESC
LIMIT 5; LIMIT 5;

View File

@ -27,7 +27,6 @@
:migration-table-name "schema_migrations" :migration-table-name "schema_migrations"
:db db}] :db db}]
(migratus/migrate migratus-config) (migratus/migrate migratus-config)
(conman/bind-connection db "sql/queries.sql")
(conman/connect! {:jdbc-url db}) (conman/connect! {:jdbc-url db})
db)) db))
@ -85,3 +84,11 @@
(sql-value [value] (to-pg-json value)) (sql-value [value] (to-pg-json value))
IPersistentVector IPersistentVector
(sql-value [value] (to-pg-json value))) (sql-value [value] (to-pg-json value)))
(defmacro with-tx [& body]
"Performs a set of queries in transaction."
`(conman/with-transaction [*db*]
~@body))
(defn update! [& args]
(apply jdbc/update! *db* args))

View File

@ -5,6 +5,7 @@
[compojure.api.meta :refer [restructure-param]] [compojure.api.meta :refer [restructure-param]]
[buddy.auth.accessrules :refer [restrict]] [buddy.auth.accessrules :refer [restrict]]
[buddy.auth :refer [authenticated?]] [buddy.auth :refer [authenticated?]]
[commiteth.db.core :as db]
[commiteth.db.users :as users] [commiteth.db.users :as users]
[commiteth.db.usage-metrics :as usage-metrics] [commiteth.db.usage-metrics :as usage-metrics]
[commiteth.db.repositories :as repositories] [commiteth.db.repositories :as repositories]
@ -214,26 +215,35 @@
(do (do
(log/debug "/usage-metrics" user) (log/debug "/usage-metrics" user)
(ok (usage-metrics/usage-metrics-by-day)))) (ok (usage-metrics/usage-metrics-by-day))))
(context "/user" [] (context "/user" []
(GET "/" {:keys [params]} (GET "/" {:keys [params]}
:auth-rules authenticated? :auth-rules authenticated?
:current-user user :current-user user
(ok (handle-get-user user (:token params)))) (ok (handle-get-user user (:token params))))
(POST "/address" []
(POST "/" []
:auth-rules authenticated? :auth-rules authenticated?
:body-params [user-id :- Long, address :- String] :current-user user
:summary "Update user address" :body [body {:address s/Str
(if-not (eth/valid-address? address) :is_hidden_in_hunters s/Bool}]
(do :summary "Updates user's fields."
(log/debug "POST /address: invalid input" address)
{:status 400 (let [user-id (:id user)
:body (str "Invalid Ethereum address '" address "'")}) {:keys [address]} body]
(let [result (users/update-user-address
user-id (when-not (eth/valid-address? address)
address)] (log/debugf "POST /user: Wrong address %s" address)
(if (= 1 result) (bad-request! (format "Invalid Ethereum address: %s" address)))
(ok)
(internal-server-error))))) (db/with-tx
(when-not (db/user-exists? {:id user-id})
(not-found! "No such a user."))
(db/update! :users body ["id = ?" user-id]))
(ok)))
(GET "/repositories" {:keys [params]} (GET "/repositories" {:keys [params]}
:auth-rules authenticated? :auth-rules authenticated?
:current-user user :current-user user

View File

@ -1,7 +1,7 @@
(ns commiteth.activity (ns commiteth.activity
(:require [re-frame.core :as rf] (:require [re-frame.core :as rf]
[reagent.core :as r] [reagent.core :as r]
[commiteth.common :refer [moment-timestamp [commiteth.common :refer [relative-time
items-per-page items-per-page
display-data-page display-data-page
issue-url]])) issue-url]]))
@ -54,7 +54,7 @@
^{:key (random-uuid)} ^{:key (random-uuid)}
[:div.balance-badge.token [:div.balance-badge.token
(str (subs (str tla) 1) " " balance)])]) (str (subs (str tla) 1) " " balance)])])
[:div.time (moment-timestamp updated)]]]]) [:div.time (relative-time updated)]]]])
(defn activity-list [{:keys [items item-count page-number total-count] (defn activity-list [{:keys [items item-count page-number total-count]
:as activity-page-data} :as activity-page-data}

View File

@ -1,7 +1,7 @@
(ns commiteth.bounties (ns commiteth.bounties
(:require [reagent.core :as r] (:require [reagent.core :as r]
[re-frame.core :as rf] [re-frame.core :as rf]
[commiteth.common :refer [moment-timestamp [commiteth.common :refer [relative-time
display-data-page display-data-page
items-per-page items-per-page
issue-url]] issue-url]]
@ -33,7 +33,7 @@
[:div.open-bounty-item-content [:div.open-bounty-item-content
[:div.header issue-link] [:div.header issue-link]
[:div.bounty-item-row [:div.bounty-item-row
[:div.time (moment-timestamp updated)] [:div.time (relative-time updated)]
[:span.bounty-repo-label repo-link]] [:span.bounty-repo-label repo-link]]
[:div.footer-row [:div.footer-row

View File

@ -2,7 +2,7 @@
(:require [reagent.core :as r] (:require [reagent.core :as r]
[re-frame.core :as rf] [re-frame.core :as rf]
[clojure.string :as str] [clojure.string :as str]
[cljsjs.moment])) [goog.date.relative]))
(defn input [val-ratom props] (defn input [val-ratom props]
(fn [] (fn []
@ -12,21 +12,22 @@
:on-change #(reset! val-ratom (-> % .-target .-value))})])) :on-change #(reset! val-ratom (-> % .-target .-value))})]))
(defn dropdown [props title val-ratom items] (defn dropdown [props title val-ratom items]
"If val-ratom is set, preselect it in the dropdown.
Otherwise, prepend title as a disabled option."
(fn [] (fn []
(if (= 1 (count items))
(reset! val-ratom (first items)))
[:select.ui.basic.selection.dropdown [:select.ui.basic.selection.dropdown
(merge props {:on-change (merge props {:on-change
#(reset! val-ratom (-> % .-target .-value))}) #(reset! val-ratom (-> % .-target .-value))
(doall (for [item items] :default-value (or @val-ratom title)})
^{:key item} [:option (for [item items]
{:value item} ^{:key item} [:option {:value item
item]))])) :disabled (= item title)}
item])]))
(defn moment-timestamp [time] (defn relative-time [time]
(let [now (.now js/Date.) "converts time in milliseconds to a relative form of '1 hour ago'"
js-time (clj->js time)] (let [js-time (clj->js time)]
(.to (js/moment.utc) js-time))) (goog.date.relative/format js-time)))
(defn issue-url [owner repo number] (defn issue-url [owner repo number]
(str "https://github.com/" owner "/" repo "/issues/" number)) (str "https://github.com/" owner "/" repo "/issues/" number))

View File

@ -217,8 +217,8 @@
[:div.ui.container.top-hunters [:div.ui.container.top-hunters
[:h3.top-hunters-header "Top 5 hunters"] [:h3.top-hunters-header "Top 5 hunters"]
[:div.top-hunters-subheader "All time"] [:div.top-hunters-subheader "All time"]
[top-hunters]]])]] [top-hunters]]])]]]
[footer]]]))) [footer]])))
(secretary/set-config! :prefix "#") (secretary/set-config! :prefix "#")

View File

@ -62,7 +62,6 @@
(fn [db [_ path value]] (fn [db [_ path value]]
(assoc-in db path value))) (assoc-in db path value)))
(reg-event-db (reg-event-db
:set-active-page :set-active-page
(fn [db [_ page]] (fn [db [_ page]]
@ -319,32 +318,38 @@
{:db db {:db db
:dispatch [:set-active-page :update-address]})) :dispatch [:set-active-page :update-address]}))
(reg-event-db
:update-user
(fn [db [_ fields]]
(update db :user merge fields)))
(reg-event-fx (reg-event-fx
:save-user-address :save-user-fields
(fn [{:keys [db]} [_ user-id address]] (fn [{:keys [db]} [_ fields]]
(prn "save-user-address" user-id address) {:dispatch [:set-updating-user]
{:db (assoc db :updating-address true)
:http {:method POST :http {:method POST
:url "/api/user/address" :url "/api/user"
:on-success #(do :on-success #(do
(dispatch [:assoc-in [:user [:address] address]]) (dispatch [:update-user fields])
(dispatch [:set-flash-message (dispatch [:set-flash-message
:success :success
"Address saved"])) "Settings saved"]))
:on-error #(do :on-error #(dispatch [:set-flash-message
(println %) :error
(dispatch [:set-flash-message (:response %)])
:error :finally #(dispatch [:clear-updating-user])
(:response %)])) :params fields}}))
:finally #(dispatch [:clear-updating-address])
:params {:user-id user-id :address address}}}))
(reg-event-db (reg-event-db
:clear-updating-address :set-updating-user
(fn [db _] (fn [db _]
(dissoc db :updating-address))) (assoc db :updating-user true)))
(reg-event-db
:clear-updating-user
(fn [db _]
(dissoc db :updating-user)))
(reg-event-fx (reg-event-fx
:save-payout-hash :save-payout-hash

View File

@ -1,6 +1,6 @@
(ns commiteth.manage-payouts (ns commiteth.manage-payouts
(:require [re-frame.core :as rf] (:require [re-frame.core :as rf]
[commiteth.common :refer [moment-timestamp]])) [commiteth.common :refer [relative-time]]))
@ -33,7 +33,7 @@
[:div.description (if paid? [:div.description (if paid?
(str "(paid to " winner-login ")") (str "(paid to " winner-login ")")
(str "(" (if merged? "merged" "open") ")"))] (str "(" (if merged? "merged" "open") ")"))]
[:div.time (moment-timestamp updated)] [:div.time (relative-time updated)]
[:button.ui.button [:button.ui.button
(merge (if (and merged? (not paid?)) (merge (if (and merged? (not paid?))
{} {}

View File

@ -3,42 +3,70 @@
[commiteth.common :refer [input dropdown]] [commiteth.common :refer [input dropdown]]
[reagent.core :as r] [reagent.core :as r]
[reagent.crypt :as crypt] [reagent.crypt :as crypt]
[clojure.string :as str]
[cljs-web3.eth :as web3-eth])) [cljs-web3.eth :as web3-eth]))
(defn update-address-page-contents [] (defn update-address-page-contents []
(let [db (rf/subscribe [:db]) (let [db (rf/subscribe [:db])
user (rf/subscribe [:user]) updating-user (rf/subscribe [:get-in [:updating-user]])
updating-address (rf/subscribe [:get-in [:updating-address]]) address (r/atom @(rf/subscribe [:get-in [:user :address]]))
address (r/atom @(rf/subscribe [:get-in [:user :address]]))] hidden (r/atom @(rf/subscribe [:get-in [:user :is_hidden_in_hunters]]))]
(fn [] (fn []
(let [web3 (:web3 @db) (let [web3 (:web3 @db)
web3-accounts (when web3 web3-accounts (when web3
(web3-eth/accounts web3))] (web3-eth/accounts web3))]
(println "web3-accounts" web3-accounts)
[:div.ui.container.grid [:div.ui.container.grid
[:div.ui.form.sixteen.wide.column [:div.ui.form.sixteen.wide.column
[:h3 "Update address"] [:h3 "Update address"]
[:p "Insert your Ethereum address in hex format."] [:p "Insert your Ethereum address in hex format."]
[:div.field [:div.field
(if-not (empty? web3-accounts) (if-not (empty? web3-accounts)
[dropdown {:class "address-input"} "Select address" ; Add value of address if it's missing from items list.
address ; If address is empty, add title
(vec (let [accounts (map str/lower-case web3-accounts)
(for [acc web3-accounts] addr @address
acc))] title "Select address"
addr-not-in-web3? (and addr (as-> web3-accounts acc
(map str/lower-case acc)
(set acc)
(contains? acc addr)
(not acc)))
items (cond->> web3-accounts
addr-not-in-web3? (into [addr])
(not addr) (into [title]))]
[dropdown {:class "address-input"}
title
address
items])
[:div.ui.input.address-input [:div.ui.input.address-input
[input address {:placeholder "0x0000000000000000000000000000000000000000" [input address {:placeholder "0x0000000000000000000000000000000000000000"
:auto-complete "off" :auto-complete "off"
:auto-correct "off" :auto-correct "off"
:spell-check "false" :spell-check "false"
:max-length 42}]])] :max-length 42}]])]
[:h3 "Settings"]
[:div
[:input
{:type :checkbox
:disabled @updating-user
:id :input-hidden
:checked @hidden
:on-change
(fn [e]
(let [value (-> e .-target .-checked)]
(reset! hidden value)))}]
[:label {:for :input-hidden} "Disguise myself from the top hunters and activity lists."]]
[:button [:button
(merge {:on-click (merge {:on-click
#(rf/dispatch [:save-user-address #(rf/dispatch [:save-user-fields {:address @address
(:id @user) :is_hidden_in_hunters @hidden}])
@address])
:class (str "ui button small update-address-button" :class (str "ui button small update-address-button"
(when @updating-address (when @updating-user
" busy loading"))}) " busy loading"))})
"UPDATE"]]])))) "UPDATE"]]]))))

View File

@ -41,17 +41,26 @@
.update-address-button { .update-address-button {
background-color: #57a7ed!important; background-color: #57a7ed!important;
margin-top: 10px !important;
&:hover { &:hover {
background-color: #57a7ed!important; background-color: #57a7ed!important;
} }
} }
label[for="input-hidden"] {
padding-left: 8px;
}
#input-hidden {
vertical-align: bottom;
position: relative;
top: -4px;
}
.login-button { .login-button {
background-color: rgba(255,255,255,0.2)!important; background-color: rgba(255,255,255,0.2)!important;
} }
.commiteth-header { .commiteth-header {
background-color: #57a7ed!important; background-color: #57a7ed!important;
border-radius: 0em; border-radius: 0em;
@ -864,6 +873,8 @@
box-shadow: none; box-shadow: none;
border-radius: 0.3em; border-radius: 0.3em;
min-height: calc(~"100vh - 400px");
h3 { h3 {
color: #474951; color: #474951;
} }
@ -986,7 +997,6 @@
z-index: -1; z-index: -1;
display: flex; display: flex;
justify-content: center; justify-content: center;
margin-top: 20px;
} }
@ -1285,5 +1295,3 @@ body {
color: #8d99a4; color: #8d99a4;
padding-top: 20px; padding-top: 20px;
} }