mirror of
https://github.com/status-im/open-bounty.git
synced 2025-02-17 03:47:58 +00:00
WIP: Activity feed feature
* backend support for activity feed * partial frontend support (still needs work) * save issue modification timestamp to db * rename commit-id -> commit-sha everywhere for consistency * "No data" texts for UI collections when no data exists
This commit is contained in:
parent
c33fb858f9
commit
2599b85d41
@ -0,0 +1,5 @@
|
||||
ALTER TABLE "public"."issues" ADD COLUMN "updated" timestamp without time zone DEFAULT timezone('utc'::text, now());
|
||||
|
||||
ALTER TABLE "public"."issues" RENAME COLUMN "commit_id" TO "commit_sha";
|
||||
|
||||
ALTER TABLE "public"."pull_requests" RENAME COLUMN "commit_id" TO "commit_sha";
|
87
resources/migrations/20170226230237-activity-feed.up.sql
Normal file
87
resources/migrations/20170226230237-activity-feed.up.sql
Normal file
@ -0,0 +1,87 @@
|
||||
create view bounties_view as
|
||||
select
|
||||
i.title as issue_title,
|
||||
i.issue_number,
|
||||
r.repo as repo_name,
|
||||
u.name as user_name,
|
||||
u.avatar_url as user_avatar_url,
|
||||
i.payout_receipt,
|
||||
i.balance,
|
||||
i.updated as updated
|
||||
FROM issues i, users u, repositories r
|
||||
WHERE r.repo_id = i.repo_id
|
||||
AND r.user_id = u.id
|
||||
and contract_address is not null
|
||||
and comment_id is not null
|
||||
order by updated;
|
||||
|
||||
create view claims_view as
|
||||
select
|
||||
i.title as issue_title,
|
||||
i.issue_number,
|
||||
r.repo as repo_name,
|
||||
u.name as user_name,
|
||||
u.avatar_url as user_avatar_url,
|
||||
i.payout_receipt,
|
||||
p.updated as updated,
|
||||
i.balance,
|
||||
p.state as pr_state
|
||||
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;
|
||||
|
||||
create view activity_feed_view as
|
||||
select 'open-claim' as type,
|
||||
issue_title,
|
||||
repo_name,
|
||||
issue_number,
|
||||
user_name,
|
||||
user_avatar_url,
|
||||
balance,
|
||||
updated
|
||||
from claims_view
|
||||
where
|
||||
pr_state=0
|
||||
and payout_receipt is null
|
||||
union
|
||||
select 'claim-payout' as type,
|
||||
issue_title,
|
||||
repo_name,
|
||||
issue_number,
|
||||
user_name,
|
||||
user_avatar_url,
|
||||
balance,
|
||||
updated
|
||||
from claims_view
|
||||
where
|
||||
pr_state=1
|
||||
and payout_receipt is not null
|
||||
union
|
||||
select 'new-bounty' as type,
|
||||
issue_title,
|
||||
repo_name,
|
||||
issue_number,
|
||||
user_name,
|
||||
user_avatar_url,
|
||||
balance,
|
||||
updated
|
||||
from bounties_view
|
||||
where balance=0
|
||||
and payout_receipt is null
|
||||
union
|
||||
select 'balance-update' as type,
|
||||
issue_title,
|
||||
repo_name,
|
||||
issue_number,
|
||||
user_name,
|
||||
user_avatar_url,
|
||||
balance,
|
||||
updated
|
||||
from bounties_view
|
||||
where balance>0
|
||||
and payout_receipt is null
|
||||
order by updated desc;
|
@ -121,17 +121,19 @@ INSERT INTO issues (repo_id, issue_id, issue_number, title)
|
||||
FROM issues
|
||||
WHERE repo_id = :repo_id AND issue_id = :issue_id);
|
||||
|
||||
-- :name update-commit-id :<! :1
|
||||
-- :doc updates issue with commit_id
|
||||
-- :name update-commit-sha :<! :1
|
||||
-- :doc updates issue with commit_sha
|
||||
UPDATE issues
|
||||
SET commit_id = :commit_id
|
||||
SET commit_sha = :commit_sha,
|
||||
updated = timezone('utc'::text, now())
|
||||
WHERE issue_id = :issue_id
|
||||
RETURNING repo_id, issue_id, issue_number, title, commit_id, contract_address;
|
||||
RETURNING repo_id, issue_id, issue_number, title, commit_sha, contract_address;
|
||||
|
||||
-- :name update-transaction-hash :! :n
|
||||
-- :doc updates transaction-hash for a given issue
|
||||
UPDATE issues
|
||||
SET transaction_hash = :transaction_hash
|
||||
SET transaction_hash = :transaction_hash,
|
||||
updated = timezone('utc'::text, now())
|
||||
WHERE issue_id = :issue_id;
|
||||
|
||||
|
||||
@ -153,7 +155,8 @@ WITH t AS (
|
||||
AND i.issue_id = :issue_id
|
||||
)
|
||||
UPDATE issues i
|
||||
SET contract_address = :contract_address
|
||||
SET contract_address = :contract_address,
|
||||
updated = timezone('utc'::text, now())
|
||||
FROM t
|
||||
WHERE i.issue_id = :issue_id
|
||||
RETURNING t.issue_id, t.issue_number, t.title, t.transaction_hash, i.contract_address, t.login, t.repo, t.repo_id;
|
||||
@ -161,7 +164,8 @@ RETURNING t.issue_id, t.issue_number, t.title, t.transaction_hash, i.contract_ad
|
||||
-- :name update-comment-id :! :n
|
||||
-- :doc updates comment-id for a given issue
|
||||
UPDATE issues
|
||||
SET comment_id = :comment_id
|
||||
SET comment_id = :comment_id,
|
||||
updated = timezone('utc'::text, now())
|
||||
WHERE issue_id = :issue_id;
|
||||
|
||||
-- :name list-pending-deployments :? :*
|
||||
@ -182,7 +186,7 @@ INSERT INTO pull_requests (pr_id,
|
||||
pr_number,
|
||||
issue_number,
|
||||
issue_id,
|
||||
commit_id,
|
||||
commit_sha,
|
||||
user_id,
|
||||
state)
|
||||
VALUES(:pr_id,
|
||||
@ -190,14 +194,14 @@ VALUES(:pr_id,
|
||||
:pr_number,
|
||||
:issue_number,
|
||||
:issue_id,
|
||||
:commit_id,
|
||||
:commit_sha,
|
||||
:user_id,
|
||||
:state)
|
||||
ON CONFLICT (pr_id) DO UPDATE
|
||||
SET
|
||||
state = :state,
|
||||
updated = timezone('utc'::text, now()),
|
||||
commit_id = :commit_id;
|
||||
commit_sha = :commit_sha;
|
||||
|
||||
-- Bounties ------------------------------------------------------------------------
|
||||
|
||||
@ -247,42 +251,31 @@ AND i.payout_hash IS NOT NULL;
|
||||
-- :name update-confirm-hash :! :n
|
||||
-- :doc updates issue with confirmation hash
|
||||
UPDATE issues
|
||||
SET confirm_hash = :confirm_hash
|
||||
SET confirm_hash = :confirm_hash,
|
||||
updated = timezone('utc'::text, now())
|
||||
WHERE issue_id = :issue_id;
|
||||
|
||||
-- :name update-execute-hash :! :n
|
||||
-- :doc updates issue with execute transaction hash
|
||||
UPDATE issues
|
||||
SET execute_hash = :execute_hash
|
||||
SET execute_hash = :execute_hash,
|
||||
updated = timezone('utc'::text, now())
|
||||
WHERE issue_id = :issue_id;
|
||||
|
||||
-- :name update-payout-hash :! :n
|
||||
-- :doc updates issue with payout transaction hash
|
||||
UPDATE issues
|
||||
SET payout_hash = :payout_hash
|
||||
SET payout_hash = :payout_hash,
|
||||
updated = timezone('utc'::text, now())
|
||||
WHERE issue_id = :issue_id;
|
||||
|
||||
-- :name update-payout-receipt :! :n
|
||||
-- :doc updates issue with payout transaction receipt
|
||||
UPDATE issues
|
||||
SET payout_receipt = :payout_receipt
|
||||
SET payout_receipt = :payout_receipt,
|
||||
updated = timezone('utc'::text, now())
|
||||
WHERE issue_id = :issue_id;
|
||||
|
||||
-- :name all-bounties-list :? :*
|
||||
-- :doc open (not merged) bounty issues
|
||||
SELECT
|
||||
i.contract_address AS contract_address,
|
||||
i.issue_id AS issue_id,
|
||||
i.issue_number AS issue_number,
|
||||
i.title AS issue_title,
|
||||
i.repo_id AS repo_id,
|
||||
i.balance AS issue_balance,
|
||||
r.login AS owner_name,
|
||||
r.repo AS repo_name
|
||||
FROM issues i, repositories r
|
||||
WHERE
|
||||
r.repo_id = i.repo_id
|
||||
AND i.commit_id IS NULL;
|
||||
|
||||
-- :name owner-bounties-list :? :*
|
||||
-- :doc all bounty issues for given owner
|
||||
@ -352,7 +345,7 @@ SELECT
|
||||
FROM issues i, repositories r
|
||||
WHERE r.repo_id = i.repo_id
|
||||
AND r.user_id = :owner_id
|
||||
AND i.commit_id IS NULL
|
||||
AND i.commit_sha IS NULL
|
||||
AND NOT exists(SELECT 1
|
||||
FROM pull_requests
|
||||
WHERE issue_number = i.issue_number
|
||||
@ -398,7 +391,8 @@ WHERE contract_address = :contract_address;
|
||||
-- :name update-balance :! :n
|
||||
-- :doc updates balance of a wallet attached to a given issue
|
||||
UPDATE issues
|
||||
SET balance = :balance
|
||||
SET balance = :balance,
|
||||
updated = timezone('utc'::text, now())
|
||||
WHERE contract_address = :contract_address;
|
||||
|
||||
|
||||
@ -419,6 +413,8 @@ WHERE issue_id = :issue_id;
|
||||
|
||||
|
||||
-- :name top-hunters :? :*
|
||||
-- :doc list of user that have reveived bounty payouts with sum of
|
||||
-- earnings
|
||||
SELECT
|
||||
u.id AS user_id,
|
||||
u.login AS login,
|
||||
@ -427,8 +423,24 @@ u.avatar_url AS avatar_url,
|
||||
SUM(i.balance) AS total_eth
|
||||
FROM issues i, users u, pull_requests pr
|
||||
WHERE
|
||||
pr.commit_id = i.commit_id
|
||||
pr.commit_sha = i.commit_sha
|
||||
AND u.id = pr.user_id
|
||||
AND i.payout_receipt IS NOT NULL
|
||||
GROUP BY u.id
|
||||
ORDER BY total_eth DESC;
|
||||
|
||||
|
||||
-- :name bounties-activity :? :*
|
||||
-- :doc data for bounty activity feed
|
||||
SELECT
|
||||
type,
|
||||
issue_title,
|
||||
repo_name,
|
||||
issue_number,
|
||||
user_name,
|
||||
user_avatar_url,
|
||||
balance,
|
||||
updated
|
||||
FROM activity_feed_view
|
||||
ORDER BY updated DESC
|
||||
LIMIT 100;
|
||||
|
@ -4,10 +4,6 @@
|
||||
[clojure.set :refer [rename-keys]]))
|
||||
|
||||
|
||||
(defn list-all-bounties
|
||||
[]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/all-bounties-list con-db)))
|
||||
|
||||
(defn list-owner-bounties
|
||||
[owner]
|
||||
@ -75,3 +71,8 @@
|
||||
[]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/top-hunters con-db)))
|
||||
|
||||
(defn bounty-activity
|
||||
[]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/bounties-activity con-db)))
|
||||
|
@ -12,12 +12,12 @@
|
||||
:issue_number issue-number
|
||||
:title issue-title})))
|
||||
|
||||
(defn update-commit-id
|
||||
"Updates issue with commit_id"
|
||||
[commit-id issue-id]
|
||||
(defn update-commit-sha
|
||||
"Updates issue with commit-sha"
|
||||
[issue-id commit-sha]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/update-commit-id con-db {:issue_id issue-id
|
||||
:commit_id commit-id})))
|
||||
(db/update-commit-sha con-db {:issue_id issue-id
|
||||
:commit_sha commit-sha})))
|
||||
|
||||
(defn update-transaction-hash
|
||||
"Updates issue with transaction-hash"
|
||||
|
@ -116,6 +116,11 @@
|
||||
(bounties-db/top-hunters))))
|
||||
|
||||
|
||||
(defn activity-feed []
|
||||
(let [activity-items (bounties-db/bounty-activity)]
|
||||
(map #(update % :balance decimal->str) activity-items)))
|
||||
|
||||
|
||||
(defapi service-routes
|
||||
{:swagger {:ui "/swagger-ui"
|
||||
:spec "/swagger.json"
|
||||
@ -127,10 +132,9 @@
|
||||
(GET "/top-hunters" []
|
||||
(log/debug "/top-hunters")
|
||||
(ok (top-hunters)))
|
||||
(context "/bounties" []
|
||||
(GET "/all" []
|
||||
(log/debug "/bounties/all")
|
||||
(ok (bounties-db/list-all-bounties))))
|
||||
(GET "/activity-feed" []
|
||||
(log/debug "/activity-feed")
|
||||
(ok (activity-feed)))
|
||||
(context "/user" []
|
||||
(GET "/" []
|
||||
:auth-rules authenticated?
|
||||
|
@ -30,9 +30,9 @@
|
||||
(= "labeled" action)
|
||||
(= bounties/label-name (get-in issue [:label :name]))))
|
||||
|
||||
(defn find-commit-id
|
||||
(defn find-commit-sha
|
||||
[user repo issue-number event-types]
|
||||
(log/debug "find-commit-id" user repo issue-number event-types)
|
||||
(log/debug "find-commit-sha" user repo issue-number event-types)
|
||||
(some identity (map #(->
|
||||
(github/get-issue-events user repo issue-number)
|
||||
(find-issue-event % user)
|
||||
@ -55,8 +55,8 @@
|
||||
[{{{owner :login} :owner repo :name} :repository
|
||||
{issue-id :id issue-number :number} :issue}]
|
||||
(log/debug "handle-issue-closed" owner repo issue-number issue-id)
|
||||
(when-let [commit-id (find-commit-id owner repo issue-number ["referenced" "closed"])]
|
||||
(log/debug (format "Issue %s/%s/%s closed with commit %s" owner repo issue-number commit-id))
|
||||
(when-let [commit-sha (find-commit-sha owner repo issue-number ["referenced" "closed"])]
|
||||
(log/debug (format "Issue %s/%s/%s closed with commit %s" owner repo issue-number commit-sha))
|
||||
(log/info "NOT considering event as bounty winner")
|
||||
;; TODO: disabled for now since the system is meant to be used
|
||||
;; exclusively via pull requests. issue closed event without a PR
|
||||
@ -65,7 +65,7 @@
|
||||
;; maintainer (could be that the bounty hunter had write access
|
||||
;; to master, but that scenario should be very rare and better
|
||||
;; not to support it)
|
||||
#_(issues/close commit-id issue-id)))
|
||||
#_(issues/close commit-sha issue-id)))
|
||||
|
||||
(def ^:const keywords
|
||||
[#"(?i)close:?\s+#(\d+)"
|
||||
@ -148,19 +148,19 @@
|
||||
(log/info "PR with reference to bounty issue"
|
||||
bounty-issue-number "opened")
|
||||
(pull-requests/save (merge pr-data {:state :opened
|
||||
:commit_id head-sha})))
|
||||
:commit-sha head-sha})))
|
||||
:closed (if merged?
|
||||
(do (log/info "PR with reference to bounty issue"
|
||||
bounty-issue-number "merged")
|
||||
(pull-requests/save
|
||||
(merge pr-data {:state :merged
|
||||
:commit_id head-sha}))
|
||||
(issues/update-commit-id head-sha (:id issue)))
|
||||
:commit-sha head-sha}))
|
||||
(issues/update-commit-sha (:id issue) head-sha))
|
||||
(do (log/info "PR with reference to bounty issue"
|
||||
bounty-issue-number "closed with no merge")
|
||||
(pull-requests/save
|
||||
(merge pr-data {:state :closed
|
||||
:commit_id head-sha}))))))))
|
||||
:commit-sha head-sha}))))))))
|
||||
|
||||
|
||||
(defn handle-issue
|
||||
|
@ -1,12 +1,15 @@
|
||||
(ns commiteth.activity
|
||||
(:require [re-frame.core :as rf]))
|
||||
(:require [re-frame.core :as rf]
|
||||
[reagent.core :as r]))
|
||||
|
||||
|
||||
|
||||
(defn activity-item [{{image-url :avatar-url
|
||||
display-name :display-name} :user
|
||||
timestamp :timestamp
|
||||
description :description} item]
|
||||
(defn activity-item [{image-url :user_avatar_url
|
||||
display-name :user_name
|
||||
timestamp :updated
|
||||
balance :balance
|
||||
issue-title :issue_title
|
||||
item-type :type} item]
|
||||
|
||||
[:div.item.activity-item
|
||||
[:div.ui.mini.circular.image
|
||||
@ -14,13 +17,16 @@
|
||||
[:div.content
|
||||
[:div.header display-name]
|
||||
[:div.description
|
||||
[:p description]]
|
||||
[:div.time timestamp]]])
|
||||
[:p item-type]
|
||||
[:p issue-title]]
|
||||
#_[:div.time timestamp]]])
|
||||
|
||||
(defn activity-page []
|
||||
(let [activity-items (rf/subscribe [:activity-feed])]
|
||||
(fn []
|
||||
[:div.ui.container
|
||||
(into [:div.ui.items]
|
||||
(for [item @activity-items]
|
||||
[activity-item item]))])))
|
||||
(if (empty? @activity-items)
|
||||
[:div.ui.text "No data"]
|
||||
(into [:div.ui.items]
|
||||
(for [item @activity-items]
|
||||
[activity-item item])))])))
|
||||
|
@ -112,16 +112,18 @@
|
||||
(defn top-hunters []
|
||||
(let [top-hunters (rf/subscribe [:top-hunters])]
|
||||
(fn []
|
||||
(into [:div.ui.items.top-hunters]
|
||||
(map-indexed (fn [idx hunter]
|
||||
[:div.item
|
||||
[:div.leader-ordinal (str (+ 1 idx))]
|
||||
[:div.ui..mini.circular.image
|
||||
[:img {:src (:avatar-url hunter)}]]
|
||||
[:div.content
|
||||
[:div.header (:display-name hunter)]
|
||||
[:div.description (str "ETH " (:total-eth hunter))]]])
|
||||
@top-hunters)))))
|
||||
(if (empty? @top-hunters)
|
||||
[:div.ui.text "No data"]
|
||||
(into [:div.ui.items.top-hunters]
|
||||
(map-indexed (fn [idx hunter]
|
||||
[:div.item
|
||||
[:div.leader-ordinal (str (+ 1 idx))]
|
||||
[:div.ui..mini.circular.image
|
||||
[:img {:src (:avatar-url hunter)}]]
|
||||
[:div.content
|
||||
[:div.header (:display-name hunter)]
|
||||
[:div.description (str "ETH " (:total-eth hunter))]]])
|
||||
@top-hunters))))))
|
||||
|
||||
(defn page []
|
||||
(fn []
|
||||
@ -174,6 +176,7 @@
|
||||
(reset! active-user nil)))
|
||||
|
||||
(defn load-data []
|
||||
(rf/dispatch [:load-activity-feed])
|
||||
(rf/dispatch [:load-top-hunters])
|
||||
(load-user))
|
||||
|
||||
|
@ -6,19 +6,23 @@
|
||||
:repos-loading? false
|
||||
:repos {}
|
||||
:owner-bounties {}
|
||||
:top-hunters [{:avatar-url "https://randomuser.me/api/portraits/men/4.jpg"
|
||||
:display-name "Place Holder"
|
||||
:total-eth "11 000.00"}
|
||||
{:avatar-url "https://randomuser.me/api/portraits/men/6.jpg"
|
||||
:display-name "Dummy User"
|
||||
:total-eth "8 400.00"}]
|
||||
:activity-feed [{:type :submit-claim
|
||||
:user {:display-name "Dummy User"
|
||||
:avatar-url "https://randomuser.me/api/portraits/men/6.jpg"}
|
||||
:description "Submitted a claim for X"
|
||||
:timestamp "1 day ago"}
|
||||
{:type :submit-claim
|
||||
:user {:display-name "Place Holder"
|
||||
:avatar-url "https://randomuser.me/api/portraits/men/4.jpg"}
|
||||
:description "Posted ETH 15 bounty to Y"
|
||||
:timestamp "2 days ago"}]})
|
||||
:top-hunters []
|
||||
:activity-feed [] #_[{:type :create-bounty
|
||||
:user {:display-name "Dummy User"
|
||||
:avatar-url "https://randomuser.me/api/portraits/men/6.jpg"}
|
||||
:issue-title "Feature X"
|
||||
:issue-url "https://github.com/foo/bar/issues/2"
|
||||
:timestamp "1 day ago"}
|
||||
{:type :submit-claim
|
||||
:user {:display-name "Place Holder"
|
||||
:avatar-url "https://randomuser.me/api/portraits/men/4.jpg"}
|
||||
:balance-eth "15"
|
||||
:timestamp "2 days ago"}
|
||||
{:type :balance-update
|
||||
:user {:display-name "Place Holder"
|
||||
:avatar-url "https://randomuser.me/api/portraits/men/4.jpg"}
|
||||
|
||||
:issue-title "Feature Y"
|
||||
:issue-url "https://github.com/foo/bar/issues/1"
|
||||
:balance-eth "15"
|
||||
:timestamp "2 days ago"}]})
|
||||
|
@ -94,6 +94,20 @@
|
||||
(fn [db [_ top-hunters]]
|
||||
(assoc db :top-hunters top-hunters)))
|
||||
|
||||
(reg-event-fx
|
||||
:load-activity-feed
|
||||
(fn [{:keys [db]} [_]]
|
||||
{:db db
|
||||
:http {:method GET
|
||||
:url "/api/activity-feed"
|
||||
:on-success #(dispatch [:set-activity-feed %])}}))
|
||||
|
||||
|
||||
(reg-event-db
|
||||
:set-activity-feed
|
||||
(fn [db [_ activity-feed]]
|
||||
(assoc db :activity-feed activity-feed)))
|
||||
|
||||
|
||||
(reg-event-fx
|
||||
:load-owner-bounties
|
||||
|
Loading…
x
Reference in New Issue
Block a user