From 3b5a1a62de069a480a62ce0a7fa153c0bbebd7ee Mon Sep 17 00:00:00 2001 From: Umputun Date: Mon, 25 Mar 2019 16:14:10 -0500 Subject: [PATCH 01/15] add vote for the current user, hide list of other votes #297 --- backend/app/rest/api/rest.go | 35 +++++++++++++++++++++++ backend/app/rest/api/rest_private_test.go | 21 ++++++++++---- backend/app/rest/api/rest_public.go | 8 +++--- backend/app/rest/api/rss.go | 6 ++-- backend/app/store/comment.go | 3 +- 5 files changed, 60 insertions(+), 13 deletions(-) diff --git a/backend/app/rest/api/rest.go b/backend/app/rest/api/rest.go index ccad9e37..ad788e5e 100644 --- a/backend/app/rest/api/rest.go +++ b/backend/app/rest/api/rest.go @@ -281,6 +281,41 @@ func (s *Rest) routes() chi.Router { return router } +func (s *Rest) alterComments(comments []store.Comment, r *http.Request) (res []store.Comment) { + + res = s.adminService.alterComments(comments, r) // apply admin's alteration + + // prepare vote info for client view + vote := func(c store.Comment, r *http.Request) store.Comment { + + c.Vote = 0 //default is "none" (not voted) + + user, err := rest.GetUserInfo(r) + if err != nil { + c.Votes = nil // hide voters list }() + return c + } + + if v, ok := c.Votes[user.ID]; ok { + if v { + c.Vote = 1 + } else { + c.Vote = -1 + } + } + + c.Votes = nil // hide voters list }() + return c + } + + for i, c := range res { + c = vote(c, r) + res[i] = c + } + + return res +} + // serves static files from /web or embedded by statik func addFileServer(r chi.Router, path string, root http.FileSystem) { diff --git a/backend/app/rest/api/rest_private_test.go b/backend/app/rest/api/rest_private_test.go index 6131bfb0..5f7cb1d2 100644 --- a/backend/app/rest/api/rest_private_test.go +++ b/backend/app/rest/api/rest_private_test.go @@ -343,7 +343,8 @@ func TestRest_Vote(t *testing.T) { req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s/api/v1/vote/%s?site=radio-t&url=https://radio-t.com/blah&vote=%d", ts.URL, id1, val), nil) assert.Nil(t, err) - req.SetBasicAuth("admin", "password") + req.Header.Add("X-JWT", devToken) + //req.SetBasicAuth("admin", "password") resp, err := client.Do(req) assert.Nil(t, err) return resp.StatusCode @@ -351,22 +352,32 @@ func TestRest_Vote(t *testing.T) { assert.Equal(t, 200, vote(1), "first vote allowed") assert.Equal(t, 400, vote(1), "second vote rejected") - body, code := get(t, fmt.Sprintf("%s/api/v1/id/%s?site=radio-t&url=https://radio-t.com/blah", ts.URL, id1)) + body, code := getWithDevAuth(t, fmt.Sprintf("%s/api/v1/id/%s?site=radio-t&url=https://radio-t.com/blah", ts.URL, id1)) assert.Equal(t, 200, code) cr := store.Comment{} err := json.Unmarshal([]byte(body), &cr) assert.Nil(t, err) assert.Equal(t, 1, cr.Score) - assert.Equal(t, map[string]bool{"admin": true}, cr.Votes) + assert.Equal(t, 1, cr.Vote) + assert.Equal(t, map[string]bool(nil), cr.Votes) - assert.Equal(t, 200, vote(-1), "opposite vote allowed") body, code = get(t, fmt.Sprintf("%s/api/v1/id/%s?site=radio-t&url=https://radio-t.com/blah", ts.URL, id1)) assert.Equal(t, 200, code) cr = store.Comment{} err = json.Unmarshal([]byte(body), &cr) assert.Nil(t, err) + assert.Equal(t, 1, cr.Score) + assert.Equal(t, 0, cr.Vote, "no vote info for not authed user") + assert.Equal(t, map[string]bool(nil), cr.Votes) + + assert.Equal(t, 200, vote(-1), "opposite vote allowed") + body, code = getWithDevAuth(t, fmt.Sprintf("%s/api/v1/id/%s?site=radio-t&url=https://radio-t.com/blah", ts.URL, id1)) + assert.Equal(t, 200, code) + cr = store.Comment{} + err = json.Unmarshal([]byte(body), &cr) + assert.Nil(t, err) assert.Equal(t, 0, cr.Score) - assert.Equal(t, map[string]bool{}, cr.Votes) + assert.Equal(t, map[string]bool(nil), cr.Votes) } func TestRest_UserAllData(t *testing.T) { diff --git a/backend/app/rest/api/rest_public.go b/backend/app/rest/api/rest_public.go index 40c8e99e..4d2c87d4 100644 --- a/backend/app/rest/api/rest_public.go +++ b/backend/app/rest/api/rest_public.go @@ -33,7 +33,7 @@ func (s *Rest) findCommentsCtrl(w http.ResponseWriter, r *http.Request) { if e != nil { comments = []store.Comment{} // error should clear comments and continue for post info } - maskedComments := s.adminService.alterComments(comments, r) + maskedComments := s.alterComments(comments, r) var b []byte switch r.URL.Query().Get("format") { case "tree": @@ -129,7 +129,7 @@ func (s *Rest) lastCommentsCtrl(w http.ResponseWriter, r *http.Request) { if e != nil { return nil, e } - comments = s.adminService.alterComments(comments, r) + comments = s.alterComments(comments, r) // filter deleted from last comments view. Blocked marked as deleted and will sneak in without filterDeleted := filterComments(comments, func(c store.Comment) bool { return !c.Deleted }) return encodeJSONWithHTML(filterDeleted) @@ -159,7 +159,7 @@ func (s *Rest) commentByIDCtrl(w http.ResponseWriter, r *http.Request) { rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get comment by id", rest.ErrCommentNotFound) return } - comment = s.adminService.alterComments([]store.Comment{comment}, r)[0] + comment = s.alterComments([]store.Comment{comment}, r)[0] render.Status(r, http.StatusOK) if err = R.RenderJSONWithHTML(w, r, comment); err != nil { @@ -191,7 +191,7 @@ func (s *Rest) findUserCommentsCtrl(w http.ResponseWriter, r *http.Request) { if e != nil { return nil, e } - comments = s.adminService.alterComments(comments, r) + comments = s.alterComments(comments, r) comments = filterComments(comments, func(c store.Comment) bool { return !c.Deleted }) count, e := s.DataService.UserCount(siteID, userID) if e != nil { diff --git a/backend/app/rest/api/rss.go b/backend/app/rest/api/rss.go index 1573befc..fc1c473d 100644 --- a/backend/app/rest/api/rss.go +++ b/backend/app/rest/api/rss.go @@ -41,7 +41,7 @@ func (s *Rest) rssPostCommentsCtrl(w http.ResponseWriter, r *http.Request) { if e != nil { return nil, e } - comments = s.adminService.alterComments(comments, r) + comments = s.alterComments(comments, r) rss, e := s.toRssFeed(locator.URL, comments, "post comments for "+r.URL.Query().Get("url")) if e != nil { return nil, e @@ -73,7 +73,7 @@ func (s *Rest) rssSiteCommentsCtrl(w http.ResponseWriter, r *http.Request) { if e != nil { return nil, e } - comments = s.adminService.alterComments(comments, r) + comments = s.alterComments(comments, r) rss, e := s.toRssFeed(r.URL.Query().Get("site"), comments, "site comment for "+siteID) if e != nil { @@ -107,7 +107,7 @@ func (s *Rest) rssRepliesCtrl(w http.ResponseWriter, r *http.Request) { if e != nil { return nil, errors.Wrap(e, "can't get last comments") } - comments = s.adminService.alterComments(comments, r) + comments = s.alterComments(comments, r) replies := []store.Comment{} for _, c := range comments { if len(replies) > maxRssItems || c.Timestamp.Add(maxReplyDuration).Before(time.Now()) { diff --git a/backend/app/store/comment.go b/backend/app/store/comment.go index 895c3962..6bc43490 100644 --- a/backend/app/store/comment.go +++ b/backend/app/store/comment.go @@ -17,7 +17,8 @@ type Comment struct { User User `json:"user"` Locator Locator `json:"locator"` Score int `json:"score"` - Votes map[string]bool `json:"votes"` + Votes map[string]bool `json:"votes,omitempty"` + Vote int `json:"vote"` // vote for the current user Controversy float64 `json:"controversy,omitempty"` Timestamp time.Time `json:"time" bson:"time"` Edit *Edit `json:"edit,omitempty" bson:"edit,omitempty"` // pointer to have empty default in json response From 1e06c372d914c795fc07816dc382202af4d26876 Mon Sep 17 00:00:00 2001 From: Umputun Date: Mon, 25 Mar 2019 16:28:22 -0500 Subject: [PATCH 02/15] fix tests for votes default value --- backend/app/store/engine/bolt_admin_test.go | 2 ++ backend/app/store/service/service_test.go | 13 +++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/backend/app/store/engine/bolt_admin_test.go b/backend/app/store/engine/bolt_admin_test.go index 69778ee4..b2196f43 100644 --- a/backend/app/store/engine/bolt_admin_test.go +++ b/backend/app/store/engine/bolt_admin_test.go @@ -107,6 +107,7 @@ func TestBoltAdmin_DeleteUser(t *testing.T) { b, teardown := prep(t) defer teardown() + err := b.DeleteUser("radio-t", "user1") require.NoError(t, err) @@ -157,6 +158,7 @@ func TestBoltAdmin_BlockUserWithTTL(t *testing.T) { b, teardown := prep(t) defer teardown() + assert.False(t, b.IsBlocked("radio-t", "user1"), "nothing blocked") assert.NoError(t, b.SetBlock("radio-t", "user1", true, 50*time.Millisecond)) assert.True(t, b.IsBlocked("radio-t", "user1"), "user1 blocked") diff --git a/backend/app/store/service/service_test.go b/backend/app/store/service/service_test.go index 2612e945..bd3e2541 100644 --- a/backend/app/store/service/service_test.go +++ b/backend/app/store/service/service_test.go @@ -45,7 +45,7 @@ func TestService_CreateFromEmpty(t *testing.T) { assert.Equal(t, "user", res.User.ID) assert.Equal(t, "name", res.User.Name) assert.Equal(t, "23f97cf4d5c29ef788ca2bdd1c9e75656c0e4149", res.User.IP) - assert.Equal(t, map[string]bool{}, res.Votes) + assert.Equal(t, map[string]bool(nil), res.Votes) } func TestService_CreateFromPartial(t *testing.T) { @@ -174,7 +174,7 @@ func TestService_Vote(t *testing.T) { assert.Nil(t, err) assert.Equal(t, 3, len(res)) assert.Equal(t, 0, res[0].Score) - assert.Equal(t, map[string]bool{}, res[0].Votes, "no votes initially") + assert.Equal(t, map[string]bool(nil), res[0].Votes, "no votes initially") c, err := b.Vote(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, "user1", true) assert.Nil(t, err) @@ -200,7 +200,7 @@ func TestService_Vote(t *testing.T) { assert.Nil(t, err) assert.Equal(t, 3, len(res)) assert.Equal(t, 0, res[0].Score) - assert.Equal(t, map[string]bool{}, res[0].Votes, "vote reset ok") + assert.Equal(t, map[string]bool(nil), res[0].Votes, "vote reset ok") } func TestService_VoteLimit(t *testing.T) { @@ -246,7 +246,7 @@ func TestService_VoteAggressive(t *testing.T) { t.Logf("%+v", res[0]) assert.Equal(t, 3, len(res)) assert.Equal(t, 0, res[0].Score) - assert.Equal(t, map[string]bool{}, res[0].Votes, "no votes initially") + assert.Equal(t, map[string]bool(nil), res[0].Votes, "no votes initially") // add a vote as user2 _, err = b.Vote(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, "user2", true) @@ -258,7 +258,8 @@ func TestService_VoteAggressive(t *testing.T) { wg.Add(1) go func() { defer wg.Done() - b.Vote(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, "user1", true) + _, _ = b.Vote(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, "user1", true) + }() } wg.Wait() @@ -277,7 +278,7 @@ func TestService_VoteAggressive(t *testing.T) { go func() { defer wg.Done() val := rand.Intn(2) > 0 - b.Vote(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, "user1", val) + _, _ = b.Vote(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, "user1", val) }() } wg.Wait() From 446cb486a0bc4c5232ad9c8378c056c47b644c33 Mon Sep 17 00:00:00 2001 From: Umputun Date: Sun, 7 Apr 2019 13:47:22 -0500 Subject: [PATCH 03/15] extend test fo votes --- backend/app/rest/api/rest_private_test.go | 39 +++++++++++++++++------ backend/app/store/comment.go | 2 +- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/backend/app/rest/api/rest_private_test.go b/backend/app/rest/api/rest_private_test.go index f075b4ec..ccb26f2a 100644 --- a/backend/app/rest/api/rest_private_test.go +++ b/backend/app/rest/api/rest_private_test.go @@ -353,7 +353,6 @@ func TestRest_Vote(t *testing.T) { fmt.Sprintf("%s/api/v1/vote/%s?site=radio-t&url=https://radio-t.com/blah&vote=%d", ts.URL, id1, val), nil) assert.Nil(t, err) req.Header.Add("X-JWT", devToken) - //req.SetBasicAuth("admin", "password") resp, err := client.Do(req) assert.Nil(t, err) return resp.StatusCode @@ -370,15 +369,6 @@ func TestRest_Vote(t *testing.T) { assert.Equal(t, 1, cr.Vote) assert.Equal(t, map[string]bool(nil), cr.Votes) - body, code = get(t, fmt.Sprintf("%s/api/v1/id/%s?site=radio-t&url=https://radio-t.com/blah", ts.URL, id1)) - assert.Equal(t, 200, code) - cr = store.Comment{} - err = json.Unmarshal([]byte(body), &cr) - assert.Nil(t, err) - assert.Equal(t, 1, cr.Score) - assert.Equal(t, 0, cr.Vote, "no vote info for not authed user") - assert.Equal(t, map[string]bool(nil), cr.Votes) - assert.Equal(t, 200, vote(-1), "opposite vote allowed") body, code = getWithDevAuth(t, fmt.Sprintf("%s/api/v1/id/%s?site=radio-t&url=https://radio-t.com/blah", ts.URL, id1)) assert.Equal(t, 200, code) @@ -386,6 +376,35 @@ func TestRest_Vote(t *testing.T) { err = json.Unmarshal([]byte(body), &cr) assert.Nil(t, err) assert.Equal(t, 0, cr.Score) + assert.Equal(t, 0, cr.Vote) + + assert.Equal(t, 200, vote(-1), "opposite vote allowed one more time") + body, code = getWithDevAuth(t, fmt.Sprintf("%s/api/v1/id/%s?site=radio-t&url=https://radio-t.com/blah", ts.URL, id1)) + assert.Equal(t, 200, code) + cr = store.Comment{} + err = json.Unmarshal([]byte(body), &cr) + assert.Nil(t, err) + assert.Equal(t, -1, cr.Score) + assert.Equal(t, -1, cr.Vote) + + assert.Equal(t, 400, vote(-1), "dbl vote not allowed") + body, code = getWithDevAuth(t, fmt.Sprintf("%s/api/v1/id/%s?site=radio-t&url=https://radio-t.com/blah", ts.URL, id1)) + assert.Equal(t, 200, code) + cr = store.Comment{} + err = json.Unmarshal([]byte(body), &cr) + assert.Nil(t, err) + assert.Equal(t, -1, cr.Score) + assert.Equal(t, -1, cr.Vote) + + body, code = get(t, fmt.Sprintf("%s/api/v1/id/%s?site=radio-t&url=https://radio-t.com/blah", ts.URL, id1)) + assert.Equal(t, 200, code) + cr = store.Comment{} + err = json.Unmarshal([]byte(body), &cr) + assert.Nil(t, err) + assert.Equal(t, -1, cr.Score) + assert.Equal(t, 0, cr.Vote, "no vote info for not authed user") + assert.Equal(t, map[string]bool(nil), cr.Votes) + assert.Equal(t, map[string]bool(nil), cr.Votes) } diff --git a/backend/app/store/comment.go b/backend/app/store/comment.go index 6bc43490..4412e7ae 100644 --- a/backend/app/store/comment.go +++ b/backend/app/store/comment.go @@ -18,7 +18,7 @@ type Comment struct { Locator Locator `json:"locator"` Score int `json:"score"` Votes map[string]bool `json:"votes,omitempty"` - Vote int `json:"vote"` // vote for the current user + Vote int `json:"vote"` // vote for the current user, -1/1/0. set by rest from Votes Controversy float64 `json:"controversy,omitempty"` Timestamp time.Time `json:"time" bson:"time"` Edit *Edit `json:"edit,omitempty" bson:"edit,omitempty"` // pointer to have empty default in json response From 2e4dfe3891b63a759b49d7c79267f2f27e459e5f Mon Sep 17 00:00:00 2001 From: Umputun Date: Sun, 7 Apr 2019 14:05:19 -0500 Subject: [PATCH 04/15] move vote setter to service level --- backend/app/rest/api/rest.go | 19 +++++-------------- backend/app/store/comment.go | 2 +- backend/app/store/service/service.go | 9 +++++++++ backend/app/store/service/service_test.go | 4 ++++ 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/backend/app/rest/api/rest.go b/backend/app/rest/api/rest.go index 373329ed..6774e810 100644 --- a/backend/app/rest/api/rest.go +++ b/backend/app/rest/api/rest.go @@ -312,28 +312,19 @@ func (s *Rest) alterComments(comments []store.Comment, r *http.Request) (res []s // prepare vote info for client view vote := func(c store.Comment, r *http.Request) store.Comment { - c.Vote = 0 //default is "none" (not voted) - - user, err := rest.GetUserInfo(r) + _, err := rest.GetUserInfo(r) if err != nil { - c.Votes = nil // hide voters list }() + c.Vote = 0 //default is "none" (not voted) for non-authed user + c.Votes = nil // hide voters list return c } - if v, ok := c.Votes[user.ID]; ok { - if v { - c.Vote = 1 - } else { - c.Vote = -1 - } - } - - c.Votes = nil // hide voters list }() + c.Votes = nil // hide voters list return c } for i, c := range res { - c = vote(c, r) + c = vote(c, r) // hide voters list res[i] = c } diff --git a/backend/app/store/comment.go b/backend/app/store/comment.go index 4412e7ae..a6241184 100644 --- a/backend/app/store/comment.go +++ b/backend/app/store/comment.go @@ -18,7 +18,7 @@ type Comment struct { Locator Locator `json:"locator"` Score int `json:"score"` Votes map[string]bool `json:"votes,omitempty"` - Vote int `json:"vote"` // vote for the current user, -1/1/0. set by rest from Votes + Vote int `json:"vote"` // vote for the current user, -1/1/0. Controversy float64 `json:"controversy,omitempty"` Timestamp time.Time `json:"time" bson:"time"` Edit *Edit `json:"edit,omitempty" bson:"edit,omitempty"` // pointer to have empty default in json response diff --git a/backend/app/store/service/service.go b/backend/app/store/service/service.go index fe30a28c..43849ee0 100644 --- a/backend/app/store/service/service.go +++ b/backend/app/store/service/service.go @@ -206,6 +206,15 @@ func (s *DataStore) Vote(locator store.Locator, commentID string, userID string, comment.Score-- } + comment.Vote = 0 + if v, ok := comment.Votes[userID]; ok { + if v { + comment.Vote = 1 + } else { + comment.Vote = -1 + } + } + comment.Controversy = s.controversy(s.upsAndDowns(comment)) return comment, s.Put(locator, comment) diff --git a/backend/app/store/service/service_test.go b/backend/app/store/service/service_test.go index 42dcedd1..6fbc9401 100644 --- a/backend/app/store/service/service_test.go +++ b/backend/app/store/service/service_test.go @@ -179,11 +179,13 @@ func TestService_Vote(t *testing.T) { assert.Nil(t, err) assert.Equal(t, 3, len(res)) assert.Equal(t, 0, res[0].Score) + assert.Equal(t, 0, res[0].Vote) assert.Equal(t, map[string]bool(nil), res[0].Votes, "no votes initially") c, err := b.Vote(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, "user1", true) assert.Nil(t, err) assert.Equal(t, 1, c.Score) + assert.Equal(t, 1, c.Vote) assert.Equal(t, map[string]bool{"user1": true}, c.Votes, "user voted +") c, err = b.Vote(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, "user", true) @@ -197,6 +199,7 @@ func TestService_Vote(t *testing.T) { assert.Nil(t, err) assert.Equal(t, 3, len(res)) assert.Equal(t, 1, res[0].Score) + assert.Equal(t, 1, res[0].Vote) assert.Equal(t, 0.0, res[0].Controversy) _, err = b.Vote(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, "user1", false) @@ -205,6 +208,7 @@ func TestService_Vote(t *testing.T) { assert.Nil(t, err) assert.Equal(t, 3, len(res)) assert.Equal(t, 0, res[0].Score) + assert.Equal(t, 0, res[0].Vote) assert.Equal(t, map[string]bool(nil), res[0].Votes, "vote reset ok") } From 4845bf357aeb2cf9c6bbdae19e7ff0176539d9dd Mon Sep 17 00:00:00 2001 From: Umputun Date: Sun, 7 Apr 2019 14:12:40 -0500 Subject: [PATCH 05/15] restore rest setter for vote info of the current user --- backend/app/rest/api/rest.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/backend/app/rest/api/rest.go b/backend/app/rest/api/rest.go index 6774e810..59101517 100644 --- a/backend/app/rest/api/rest.go +++ b/backend/app/rest/api/rest.go @@ -312,19 +312,28 @@ func (s *Rest) alterComments(comments []store.Comment, r *http.Request) (res []s // prepare vote info for client view vote := func(c store.Comment, r *http.Request) store.Comment { - _, err := rest.GetUserInfo(r) + c.Vote = 0 //default is "none" (not voted) + + user, err := rest.GetUserInfo(r) if err != nil { - c.Vote = 0 //default is "none" (not voted) for non-authed user - c.Votes = nil // hide voters list + c.Votes = nil // hide voters list and don't set Vote for non-authed user return c } + if v, ok := c.Votes[user.ID]; ok { + if v { + c.Vote = 1 + } else { + c.Vote = -1 + } + } + c.Votes = nil // hide voters list return c } for i, c := range res { - c = vote(c, r) // hide voters list + c = vote(c, r) res[i] = c } From 128ca53725afa395b8d340c571ed6e874b2a8662 Mon Sep 17 00:00:00 2001 From: Umputun Date: Sun, 7 Apr 2019 15:58:25 -0500 Subject: [PATCH 06/15] more voting tests --- backend/app/rest/api/rest.go | 2 +- backend/app/rest/api/rest_private_test.go | 13 +++++++++++++ backend/app/rest/api/rest_test.go | 10 ++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/backend/app/rest/api/rest.go b/backend/app/rest/api/rest.go index 59101517..25fbfa0d 100644 --- a/backend/app/rest/api/rest.go +++ b/backend/app/rest/api/rest.go @@ -312,7 +312,7 @@ func (s *Rest) alterComments(comments []store.Comment, r *http.Request) (res []s // prepare vote info for client view vote := func(c store.Comment, r *http.Request) store.Comment { - c.Vote = 0 //default is "none" (not voted) + c.Vote = 0 // default is "none" (not voted) user, err := rest.GetUserInfo(r) if err != nil { diff --git a/backend/app/rest/api/rest_private_test.go b/backend/app/rest/api/rest_private_test.go index ccb26f2a..be42276d 100644 --- a/backend/app/rest/api/rest_private_test.go +++ b/backend/app/rest/api/rest_private_test.go @@ -405,6 +405,19 @@ func TestRest_Vote(t *testing.T) { assert.Equal(t, 0, cr.Vote, "no vote info for not authed user") assert.Equal(t, map[string]bool(nil), cr.Votes) + req, err := http.NewRequest("GET", + fmt.Sprintf("%s/api/v1/id/%s?site=radio-t&url=https://radio-t.com/blah", ts.URL, id1), nil) + assert.NoError(t, err) + resp, err := sendReq(t, req, adminUmputunToken) + assert.NoError(t, err) + assert.Equal(t, 200, resp.StatusCode) + cr = store.Comment{} + err = json.NewDecoder(resp.Body).Decode(&cr) + assert.Nil(t, err) + assert.Equal(t, -1, cr.Score) + assert.Equal(t, 0, cr.Vote, "no vote info for different user") + assert.Equal(t, map[string]bool(nil), cr.Votes) + assert.Equal(t, map[string]bool(nil), cr.Votes) } diff --git a/backend/app/rest/api/rest_test.go b/backend/app/rest/api/rest_test.go index ef5108d7..d2ac686f 100644 --- a/backend/app/rest/api/rest_test.go +++ b/backend/app/rest/api/rest_test.go @@ -38,6 +38,8 @@ var getStartedHTML = "/tmp/getstarted.html" var devToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJyZW1hcms0MiIsImV4cCI6Mzc4OTE5MTgyMiwianRpIjoicmFuZG9tIGlkIiwiaXNzIjoicmVtYXJrNDIiLCJuYmYiOjE1MjE4ODQyMjIsInVzZXIiOnsibmFtZSI6ImRldmVsb3BlciBvbmUiLCJpZCI6ImRldiIsInBpY3R1cmUiOiJodHRwOi8vZXhhbXBsZS5jb20vcGljLnBuZyIsImlwIjoiMTI3LjAuMC4xIiwiZW1haWwiOiJtZUBleGFtcGxlLmNvbSJ9fQ.aKUAXiZxXypgV7m1wEOgUcyPOvUDXHDi3A06YWKbcLg" +var adminUmputunToken = "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJyYWRpb3QiLCJleHAiOjE5NTQ1OTc5ODAsImp0aSI6Ijk3YTJlMGFjNGRjN2Q1ZjY5MjZkNWU4NjIwYWNlZjlhNDBjMCIsImlhdCI6MTQ1NDU5NzY4MCwiaXNzIjoicmVtYXJrNDIiLCJ1c2VyIjp7Im5hbWUiOiJVbXB1dHVuIiwiaWQiOiJnaXRodWJfZWYwZjcwNmE3IiwicGljdHVyZSI6Imh0dHBzOi8vcmVtYXJrNDIucmFkaW8tdC5jb20vYXBpL3YxL2F2YXRhci9jYjQyZmY0OTNhZGU2OTZkODhhM2E1OTBmMTM2YWU5ZTM0ZGU3YzFiLmltYWdlIiwiYXR0cnMiOnsiYWRtaW4iOnRydWUsImJsb2NrZWQiOmZhbHNlfX19.I5a8EHbUJy8mApuYCPDRThbC-1jP0sbPh1qwNyY1V4E" + func TestRest_FileServer(t *testing.T) { ts, _, teardown := startupT(t) defer teardown() @@ -296,6 +298,14 @@ func get(t *testing.T, url string) (string, int) { return string(body), r.StatusCode } +func sendReq(t *testing.T, r *http.Request, token string) (*http.Response, error) { + client := http.Client{Timeout: 5 * time.Second} + if token != "" { + r.Header.Set("X-JWT", token) + } + return client.Do(r) +} + func getWithDevAuth(t *testing.T, url string) (body string, code int) { client := &http.Client{Timeout: 5 * time.Second} req, err := http.NewRequest("GET", url, nil) From dc5ceeee12c25f81095b496468794d01640c3ffe Mon Sep 17 00:00:00 2001 From: Umputun Date: Sun, 7 Apr 2019 18:20:05 -0500 Subject: [PATCH 07/15] cache find with userID to prevent leaking (and incorrect) vote status --- backend/app/rest/api/rest.go | 10 +++++++--- backend/remark.rest | 7 ++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/backend/app/rest/api/rest.go b/backend/app/rest/api/rest.go index 25fbfa0d..bcc85bdd 100644 --- a/backend/app/rest/api/rest.go +++ b/backend/app/rest/api/rest.go @@ -399,9 +399,13 @@ func filterComments(comments []store.Comment, fn func(c store.Comment) bool) []s // admins will have different keys in order to prevent leak of admin-only data to regular users func URLKey(r *http.Request) string { adminPrefix := "admin!!" - key := strings.TrimPrefix(r.URL.String(), adminPrefix) // prevents attach with fake url to get admin view - if user, err := rest.GetUserInfo(r); err == nil && user.Admin { // make separate cache key for admins - key = adminPrefix + key + key := strings.TrimPrefix(r.URL.String(), adminPrefix) // prevents attach with fake url to get admin view + if user, err := rest.GetUserInfo(r); err == nil { + if user.Admin { + key = adminPrefix + key // make separate cache key for admins + } else { + key = user.ID + "!!" + key // make separate cache key for authed users + } } return key } diff --git a/backend/remark.rest b/backend/remark.rest index 7319bf87..ec6dd3c7 100644 --- a/backend/remark.rest +++ b/backend/remark.rest @@ -5,6 +5,10 @@ GET {{host}}/api/v1/find?site={{site}}&sort=-controversy&format=tree&url={{url}} ### find request with plain GET {{host}}/api/v1/find?site={{site}}&sort=-controversy&format=plain&url={{url}} +### find request with plain +GET http://127.0.0.1:8080/api/v1/find?site={{site}}&sort=-controversy&format=plain&url={{url}} +X-JWT: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJyZW1hcms0MiIsImV4cCI6Mzc4OTE5MTgyMiwianRpIjoicmFuZG9tIGlkIiwiaXNzIjoicmVtYXJrNDIiLCJuYmYiOjE1MjE4ODQyMjIsInVzZXIiOnsibmFtZSI6ImRldmVsb3BlciBvbmUiLCJpZCI6ImRldiIsInBpY3R1cmUiOiJodHRwOi8vZXhhbXBsZS5jb20vcGljLnBuZyIsImlwIjoiMTI3LjAuMC4xIiwiZW1haWwiOiJtZUBleGFtcGxlLmNvbSJ9fQ.aKUAXiZxXypgV7m1wEOgUcyPOvUDXHDi3A06YWKbcLg + ### last 50 comments GET {{host}}/api/v1/last/50?site={{site}} @@ -53,7 +57,8 @@ Content-Type: application/json PUT {{host}}/api/v1/admin/pin/3665976683?site={{site}}&url={{url}}&pin=1 ### vote for comment -PUT {{host}}/api/v1/vote/73e346f4-d57d-41a8-8803-6671aa187d8e?site={{site}}&url={{url}}&vote=1 +PUT http://127.0.0.1:8080/api/v1/vote/8a8c0b80-0d0a-41c3-84ad-f4034704e827?site={{site}}&url={{url}}&vote=-1 +X-JWT: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJyZW1hcms0MiIsImV4cCI6Mzc4OTE5MTgyMiwianRpIjoicmFuZG9tIGlkIiwiaXNzIjoicmVtYXJrNDIiLCJuYmYiOjE1MjE4ODQyMjIsInVzZXIiOnsibmFtZSI6ImRldmVsb3BlciBvbmUiLCJpZCI6ImRldiIsInBpY3R1cmUiOiJodHRwOi8vZXhhbXBsZS5jb20vcGljLnBuZyIsImlwIjoiMTI3LjAuMC4xIiwiZW1haWwiOiJtZUBleGFtcGxlLmNvbSJ9fQ.aKUAXiZxXypgV7m1wEOgUcyPOvUDXHDi3A06YWKbcLg ### get user info GET {{host}}/api/v1/user From 078bedd8e86caf2c09926e19027932ca063d5996 Mon Sep 17 00:00:00 2001 From: Umputun Date: Sun, 7 Apr 2019 18:34:20 -0500 Subject: [PATCH 08/15] separate caching key URLKeyWithUser for find only --- backend/app/rest/api/rest.go | 11 +++++++++++ backend/app/rest/api/rest_public.go | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/backend/app/rest/api/rest.go b/backend/app/rest/api/rest.go index bcc85bdd..1a3e66d3 100644 --- a/backend/app/rest/api/rest.go +++ b/backend/app/rest/api/rest.go @@ -398,6 +398,17 @@ func filterComments(comments []store.Comment, fn func(c store.Comment) bool) []s // URLKey gets url from request to use it as cache key // admins will have different keys in order to prevent leak of admin-only data to regular users func URLKey(r *http.Request) string { + adminPrefix := "admin!!" + key := strings.TrimPrefix(r.URL.String(), adminPrefix) // prevents attach with fake url to get admin view + if user, err := rest.GetUserInfo(r); err == nil && user.Admin { + key = adminPrefix + key // make separate cache key for admins + } + return key +} + +// URLKeyWithUser gets url from request to use it as cache key and attaching user ID +// admins will have different keys in order to prevent leak of admin-only data to regular users +func URLKeyWithUser(r *http.Request) string { adminPrefix := "admin!!" key := strings.TrimPrefix(r.URL.String(), adminPrefix) // prevents attach with fake url to get admin view if user, err := rest.GetUserInfo(r); err == nil { diff --git a/backend/app/rest/api/rest_public.go b/backend/app/rest/api/rest_public.go index 1b0c0338..815c400c 100644 --- a/backend/app/rest/api/rest_public.go +++ b/backend/app/rest/api/rest_public.go @@ -28,7 +28,7 @@ func (s *Rest) findCommentsCtrl(w http.ResponseWriter, r *http.Request) { } log.Printf("[DEBUG] get comments for %+v, sort %s, format %s", locator, sort, r.URL.Query().Get("format")) - key := cache.NewKey(locator.SiteID).ID(URLKey(r)).Scopes(locator.SiteID, locator.URL) + key := cache.NewKey(locator.SiteID).ID(URLKeyWithUser(r)).Scopes(locator.SiteID, locator.URL) data, err := s.Cache.Get(key, func() ([]byte, error) { comments, e := s.DataService.Find(locator, sort) if e != nil { From 22d787de5829463ac2abdfb244a3a06b08013980 Mon Sep 17 00:00:00 2001 From: Umputun Date: Sun, 7 Apr 2019 18:38:06 -0500 Subject: [PATCH 09/15] user comment cached with user_id as well --- backend/app/rest/api/rest_public.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app/rest/api/rest_public.go b/backend/app/rest/api/rest_public.go index 815c400c..92f26ad5 100644 --- a/backend/app/rest/api/rest_public.go +++ b/backend/app/rest/api/rest_public.go @@ -186,7 +186,7 @@ func (s *Rest) findUserCommentsCtrl(w http.ResponseWriter, r *http.Request) { log.Printf("[DEBUG] get comments for userID %s, %s", userID, siteID) - key := cache.NewKey(siteID).ID(URLKey(r)).Scopes(userID, siteID) + key := cache.NewKey(siteID).ID(URLKeyWithUser(r)).Scopes(userID, siteID) data, err := s.Cache.Get(key, func() ([]byte, error) { comments, e := s.DataService.User(siteID, userID, limit, 0) if e != nil { From cecf147fd0291951ca599b6f4160cef45520e5fc Mon Sep 17 00:00:00 2001 From: Umputun Date: Tue, 9 Apr 2019 00:10:21 -0500 Subject: [PATCH 10/15] add tests for URLKeys --- backend/app/rest/api/rest.go | 9 +++--- backend/app/rest/api/rest_test.go | 51 ++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/backend/app/rest/api/rest.go b/backend/app/rest/api/rest.go index 1a3e66d3..c53f7963 100644 --- a/backend/app/rest/api/rest.go +++ b/backend/app/rest/api/rest.go @@ -346,14 +346,13 @@ func addFileServer(r chi.Router, path string, root http.FileSystem) { var webFS http.Handler statikFS, err := fs.New() - if err == nil { - log.Printf("[INFO] run file server for %s, embedded", root) - webFS = http.FileServer(statikFS) - } if err != nil { log.Printf("[DEBUG] no embedded assets loaded, %s", err) log.Printf("[INFO] run file server for %s, path %s", root, path) webFS = http.FileServer(root) + } else { + log.Printf("[INFO] run file server for %s, embedded", root) + webFS = http.FileServer(statikFS) } origPath := path @@ -413,7 +412,7 @@ func URLKeyWithUser(r *http.Request) string { key := strings.TrimPrefix(r.URL.String(), adminPrefix) // prevents attach with fake url to get admin view if user, err := rest.GetUserInfo(r); err == nil { if user.Admin { - key = adminPrefix + key // make separate cache key for admins + key = adminPrefix + user.ID + "!!" + key // make separate cache key for admins } else { key = user.ID + "!!" + key // make separate cache key for authed users } diff --git a/backend/app/rest/api/rest_test.go b/backend/app/rest/api/rest_test.go index d2ac686f..276d69ac 100644 --- a/backend/app/rest/api/rest_test.go +++ b/backend/app/rest/api/rest_test.go @@ -10,6 +10,7 @@ import ( "net/http" "net/http/httptest" "os" + "strconv" "strings" "testing" "time" @@ -180,7 +181,7 @@ func TestRest_RunAutocertModeHTTPOnly(t *testing.T) { srv.Shutdown() } -func Test_rejectAnonUser(t *testing.T) { +func TestRest_rejectAnonUser(t *testing.T) { ts := httptest.NewServer(fakeAuth(rejectAnonUser(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello") @@ -200,6 +201,54 @@ func Test_rejectAnonUser(t *testing.T) { assert.Equal(t, http.StatusOK, resp.StatusCode, "real user") } +func Test_URLKey(t *testing.T) { + tbl := []struct { + url string + user store.User + key string + }{ + {"http://example.com/1", store.User{}, "http://example.com/1"}, + {"http://example.com/1", store.User{ID: "user"}, "http://example.com/1"}, + {"http://example.com/1", store.User{ID: "user", Admin: true}, "admin!!http://example.com/1"}, + } + + for i, tt := range tbl { + t.Run(strconv.Itoa(i), func(t *testing.T) { + r, err := http.NewRequest("GET", tt.url, nil) + require.NoError(t, err) + if tt.user.ID != "" { + r = rest.SetUserInfo(r, tt.user) + } + assert.Equal(t, tt.key, URLKey(r)) + }) + } + +} + +func Test_URLKeyWithUser(t *testing.T) { + tbl := []struct { + url string + user store.User + key string + }{ + {"http://example.com/1", store.User{}, "http://example.com/1"}, + {"http://example.com/1", store.User{ID: "user"}, "user!!http://example.com/1"}, + {"http://example.com/2", store.User{ID: "user2"}, "user2!!http://example.com/2"}, + {"http://example.com/1", store.User{ID: "user", Admin: true}, "admin!!user!!http://example.com/1"}, + } + + for i, tt := range tbl { + t.Run(strconv.Itoa(i), func(t *testing.T) { + r, err := http.NewRequest("GET", tt.url, nil) + require.NoError(t, err) + if tt.user.ID != "" { + r = rest.SetUserInfo(r, tt.user) + } + assert.Equal(t, tt.key, URLKeyWithUser(r)) + }) + } + +} func startupT(t *testing.T) (ts *httptest.Server, srv *Rest, teardown func()) { testDb := fmt.Sprintf("/tmp/test-remark-%d.db", rand.Int31()) From 46244b855a2bd7d354df8a9d54da3cce778cb7d4 Mon Sep 17 00:00:00 2001 From: Umputun Date: Tue, 9 Apr 2019 14:17:08 -0500 Subject: [PATCH 11/15] make main test slower for unusually slow travis storage init time(?) --- backend/app/main_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/app/main_test.go b/backend/app/main_test.go index 219f9b73..7c8055c6 100644 --- a/backend/app/main_test.go +++ b/backend/app/main_test.go @@ -28,7 +28,7 @@ func Test_Main(t *testing.T) { "--avatar.fs.path=" + dir, "--port=18222", "--url=https://demo.remark42.com", "--dbg", "--notify.type=none"} go func() { - time.Sleep(2000 * time.Millisecond) + time.Sleep(5000 * time.Millisecond) e := syscall.Kill(syscall.Getpid(), syscall.SIGTERM) require.Nil(t, e) }() @@ -38,12 +38,12 @@ func Test_Main(t *testing.T) { go func() { st := time.Now() main() - assert.True(t, time.Since(st).Seconds() > 2, "should take 2s") + assert.True(t, time.Since(st).Seconds() >= 5, "should take about 5s") wg.Done() }() var passed bool - err = repeater.NewDefault(10, time.Millisecond*200).Do(context.Background(), func() error { + err = repeater.NewDefault(10, time.Millisecond*500).Do(context.Background(), func() error { resp, e := http.Get("http://localhost:18222/api/v1/ping") if e != nil { t.Logf("%+v", e) @@ -60,7 +60,7 @@ func Test_Main(t *testing.T) { }) assert.NoError(t, err) - assert.Equal(t, true, passed) + assert.Equal(t, true, passed, "at least on ping passed") wg.Wait() } From bed336cfb15ccc9b45bb4ca771e7d7cbebe19667 Mon Sep 17 00:00:00 2001 From: Vyrtsev Mikhail Date: Mon, 8 Apr 2019 01:10:42 +0300 Subject: [PATCH 12/15] update readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4055096b..9d9c83c9 100644 --- a/README.md +++ b/README.md @@ -507,7 +507,7 @@ type Comment struct { User User `json:"user"` // user info, read only Locator Locator `json:"locator"` // post locator Score int `json:"score"` // comment score, read only - Votes map[string]bool `json:"votes"` // comment votes, read only + Vote int `json:"vote"` // vote for the current user, -1/1/0. Controversy float64 `json:"controversy,omitempty"` // comment controversy, read only Timestamp time.Time `json:"time"` // time stamp, read only Edit *Edit `json:"edit,omitempty" bson:"edit,omitempty"` // pointer to have empty default in json response @@ -609,11 +609,11 @@ Sort can be `time`, `active` or `score`. Supported sort order with prefix -/+, i ### Images management -* `GET /api/v1/picture/{user}/{id}` - load stored image +* `GET /api/v1/picture/{user}/{id}` - load stored image * `POST /api/v1/picture` - upload and store image, uses post form with `FormFile("file")`. returns `{"id": user/imgid}` _auth required_ _returned id should be appended to load image url on caller side_ - + ### Admin * `DELETE /api/v1/admin/comment/{id}?site=site-id&url=post-url` - delete comment by `id`. From eca230b87f90fef7ce5bb0b6586f398d23025573 Mon Sep 17 00:00:00 2001 From: Vyrtsev Mikhail Date: Wed, 10 Apr 2019 02:39:35 +0300 Subject: [PATCH 13/15] fix tabs --- web/.babelrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/.babelrc b/web/.babelrc index 7b541de0..d99459f5 100644 --- a/web/.babelrc +++ b/web/.babelrc @@ -7,7 +7,7 @@ "browsers": ["> 1%", "android >= 4.4.4", "ios >= 9", "IE >= 11"] }, "useBuiltIns": "usage", - "corejs": 3 + "corejs": 3 } ], [ From 6f67cbc30d2362d3c1768f61cc11995e4b2ac5c6 Mon Sep 17 00:00:00 2001 From: Vyrtsev Mikhail Date: Wed, 10 Apr 2019 02:49:08 +0300 Subject: [PATCH 14/15] remove webpack log spam --- web/webpack.config.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/web/webpack.config.js b/web/webpack.config.js index 92ba409e..47aef48c 100644 --- a/web/webpack.config.js +++ b/web/webpack.config.js @@ -139,6 +139,7 @@ module.exports = () => ({ }, stats: { children: false, + entrypoints: false, }, devServer: { host: 'localhost', @@ -157,5 +158,9 @@ module.exports = () => ({ changeOrigin: true, }, }, + stats: { + children: false, + entrypoints: false, + }, }, }); From 3246b662140fca74bb1fcf4befd25b1648220861 Mon Sep 17 00:00:00 2001 From: Vyrtsev Mikhail Date: Wed, 10 Apr 2019 02:24:19 +0300 Subject: [PATCH 15/15] support new voting api in ui --- web/app/common/api.ts | 14 +++++++++++--- web/app/common/types.ts | 8 ++++++-- web/app/components/comment/comment.test.tsx | 6 +++--- web/app/components/comment/comment.tsx | 12 +----------- web/app/components/root/root.tsx | 15 +++++++++++++-- 5 files changed, 34 insertions(+), 21 deletions(-) diff --git a/web/app/common/api.ts b/web/app/common/api.ts index ed6b9c6f..b5948148 100644 --- a/web/app/common/api.ts +++ b/web/app/common/api.ts @@ -52,7 +52,10 @@ export const logOut = (): Promise => export const getConfig = (): Promise => fetcher.get(`/config`); export const getPostComments = (sort: Sorting): Promise => - fetcher.get(`/find?site=${siteId}&url=${url}&sort=${sort}&format=tree`); + fetcher.get({ + url: `/find?site=${siteId}&url=${url}&sort=${sort}&format=tree`, + withCredentials: true, + }); export const getLastComments = (siteId: string, max: number): Promise => fetcher.get(`/last/${max}?site=${siteId}`); @@ -63,7 +66,8 @@ export const getCommentsCount = (siteId: string, urls: string[]): Promise<{ url: body: urls, }); -export const getComment = (id: Comment['id']): Promise => fetcher.get(`/id/${id}?url=${url}`); +export const getComment = (id: Comment['id']): Promise => + fetcher.get({ url: `/id/${id}?url=${url}`, withCredentials: true }); export const getUserComments = ( userId: User['id'], @@ -71,7 +75,11 @@ export const getUserComments = ( ): Promise<{ comments: Comment[]; count: number; -}> => fetcher.get(`/comments?user=${userId}&limit=${limit}`); +}> => + fetcher.get({ + url: `/comments?user=${userId}&limit=${limit}`, + withCredentials: true, + }); export const putCommentVote = ({ id, value }: { id: Comment['id']; value: number }): Promise => fetcher.put({ diff --git a/web/app/common/types.ts b/web/app/common/types.ts index c14d8146..d1843789 100644 --- a/web/app/common/types.ts +++ b/web/app/common/types.ts @@ -44,8 +44,12 @@ export interface Comment { locator: Locator; /** comment score, read only */ score: number; - /** comment votes, read only */ - votes: { [key: string]: boolean }; + /** + * vote delta, + * if user hasn't voted delta will be 0, + * -1/+1 for downvote/upvote + */ + vote: number; /** comment controversy, read only */ controversy?: number; /** pointer to have empty default in json response */ diff --git a/web/app/components/comment/comment.test.tsx b/web/app/components/comment/comment.test.tsx index 254bb4ef..cc8a947e 100644 --- a/web/app/components/comment/comment.test.tsx +++ b/web/app/components/comment/comment.test.tsx @@ -12,7 +12,7 @@ const DefaultProps: Partial = { view: 'main', data: { text: 'test comment', - votes: {}, + vote: 0, user: { id: 'someone', picture: 'somepicture-url', @@ -121,7 +121,7 @@ describe('', () => { const element = ( ); @@ -146,7 +146,7 @@ describe('', () => { const element = ( ); diff --git a/web/app/components/comment/comment.tsx b/web/app/components/comment/comment.tsx index 5fd98b97..3062f922 100644 --- a/web/app/components/comment/comment.tsx +++ b/web/app/components/comment/comment.tsx @@ -104,18 +104,8 @@ export class Comment extends Component { } updateState(props: Props) { - let scoreDelta = 0; - if (props.user) { - if (props.data.votes[props.user.id] === true) { - ++scoreDelta; - } - if (props.data.votes[props.user.id] === false) { - --scoreDelta; - } - } - this.setState({ - scoreDelta, + scoreDelta: props.data.vote, cachedScore: props.data.score, }); diff --git a/web/app/components/root/root.tsx b/web/app/components/root/root.tsx index 345f708a..c638be64 100644 --- a/web/app/components/root/root.tsx +++ b/web/app/components/root/root.tsx @@ -113,6 +113,17 @@ export class Root extends Component { window.addEventListener('message', this.onMessage.bind(this)); } + logIn = async (p: AuthProvider): Promise => { + const user = await this.props.logIn(p); + await this.props.fetchComments(this.props.sort); + return user; + }; + + logOut = async (): Promise => { + await this.props.logOut(); + await this.props.fetchComments(this.props.sort); + }; + checkUrlHash( e: Event & { newURL?: string; @@ -202,8 +213,8 @@ export class Root extends Component { providers={StaticStore.config.auth_providers} isCommentsDisabled={isCommentsDisabled} postInfo={this.props.info} - onSignIn={this.props.logIn} - onSignOut={this.props.logOut} + onSignIn={this.logIn} + onSignOut={this.logOut} onBlockedUsersShow={this.onBlockedUsersShow} onBlockedUsersHide={this.onBlockedUsersHide} onCommentsEnable={this.props.enableComments}