feat: activity timeline show vote activity

This commit is contained in:
LinkinStar
2022-11-23 16:12:48 +08:00
parent d399b9756f
commit ff38dc5a0c
6 changed files with 91 additions and 31 deletions
+11 -4
View File
@@ -5,8 +5,15 @@ package constant
type ActivityTypeKey string
const (
ActEdited = "edited"
ActClosed = "closed"
ActEdited = "edited"
ActClosed = "closed"
ActAccepted = "accepted"
ActVotedDown = "voted_down"
ActVotedUp = "voted_up"
ActVoteDown = "vote_down"
ActVoteUp = "vote_up"
ActUpVote = "upvote"
ActDownVote = "downvote"
)
const (
@@ -17,7 +24,7 @@ const (
ActQuestionCommented ActivityTypeKey = "question.commented"
ActQuestionAccept ActivityTypeKey = "question.accept"
ActQuestionUpvote ActivityTypeKey = "question.upvote"
ActQuestionDownvote ActivityTypeKey = "question.downvote"
ActQuestionDownVote ActivityTypeKey = "question.downvote"
ActQuestionEdited ActivityTypeKey = "question.edited"
ActQuestionRollback ActivityTypeKey = "question.rollback"
ActQuestionDeleted ActivityTypeKey = "question.deleted"
@@ -29,7 +36,7 @@ const (
ActAnswerCommented ActivityTypeKey = "answer.commented"
ActAnswerAccept ActivityTypeKey = "answer.accept"
ActAnswerUpvote ActivityTypeKey = "answer.upvote"
ActAnswerDownvote ActivityTypeKey = "answer.downvote"
ActAnswerDownVote ActivityTypeKey = "answer.downvote"
ActAnswerEdited ActivityTypeKey = "answer.edited"
ActAnswerRollback ActivityTypeKey = "answer.rollback"
ActAnswerDeleted ActivityTypeKey = "answer.deleted"
@@ -39,6 +39,9 @@ func (ac *ActivityController) GetObjectTimeline(ctx *gin.Context) {
}
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
if userInfo := middleware.GetUserInfoFromContext(ctx); userInfo != nil {
req.IsAdmin = userInfo.IsAdmin
}
resp, err := ac.activityService.GetObjectTimeline(ctx, req)
handler.HandleResponse(ctx, err, resp)
+19 -1
View File
@@ -2,10 +2,13 @@ package activity
import (
"context"
"fmt"
"github.com/answerdev/answer/internal/base/constant"
"github.com/answerdev/answer/internal/base/data"
"github.com/answerdev/answer/internal/base/reason"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/repo/config"
"github.com/answerdev/answer/internal/service/activity"
"github.com/segmentfault/pacman/errors"
)
@@ -27,7 +30,22 @@ func NewActivityRepo(
func (ar *activityRepo) GetObjectAllActivity(ctx context.Context, objectID string, showVote bool) (
activityList []*entity.Activity, err error) {
activityList = make([]*entity.Activity, 0)
session := ar.data.DB.Desc("created_at") // TODO: if showVote is false do not show vote activity
session := ar.data.DB.Desc("created_at")
if !showVote {
var activityTypeNotShown []int
for _, obj := range []string{constant.AnswerObjectType, constant.QuestionObjectType, constant.CommentObjectType} {
for _, act := range []string{
constant.ActVotedDown,
constant.ActVotedUp,
constant.ActVoteDown,
constant.ActVoteUp,
} {
activityTypeNotShown = append(activityTypeNotShown, config.Key2IDMapping[fmt.Sprintf("%s.%s", obj, act)])
}
}
session.NotIn("activity_type", activityTypeNotShown)
}
err = session.Find(&activityList, &entity.Activity{OriginalObjectID: objectID})
if err != nil {
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
+8 -5
View File
@@ -144,9 +144,11 @@ func (ar *AnswerActivityRepo) AcceptAnswer(ctx context.Context,
if action == acceptAction {
addActivity.UserID = questionUserID
addActivity.TriggerUserID = converter.StringToInt64(answerUserID)
addActivity.OriginalObjectID = questionObjID // if activity is 'accept' means this question is accept the answer.
} else {
addActivity.UserID = answerUserID
addActivity.TriggerUserID = converter.StringToInt64(answerUserID)
addActivity.OriginalObjectID = answerObjID // if activity is 'accepted' means this answer was accepted.
}
if isSelf {
addActivity.Rank = 0
@@ -234,16 +236,17 @@ func (ar *AnswerActivityRepo) CancelAcceptAnswer(ctx context.Context,
return errors.InternalServer(reason.DatabaseError).WithError(e).WithStack()
}
addActivity := &entity.Activity{
ObjectID: answerObjID,
OriginalObjectID: questionObjID,
ActivityType: activityType,
Rank: -deltaRank,
HasRank: hasRank,
ObjectID: answerObjID,
ActivityType: activityType,
Rank: -deltaRank,
HasRank: hasRank,
}
if action == acceptAction {
addActivity.UserID = questionUserID
addActivity.OriginalObjectID = questionObjID
} else {
addActivity.UserID = answerUserID
addActivity.OriginalObjectID = answerObjID
}
addActivityList = append(addActivityList, addActivity)
}
+11 -7
View File
@@ -17,6 +17,7 @@ type GetObjectTimelineReq struct {
ObjectID string `validate:"omitempty,gt=0,lte=100" form:"object_id"`
ShowVote bool `validate:"omitempty" form:"show_vote"`
UserID string `json:"-"`
IsAdmin bool `json:"-"`
}
// GetObjectTimelineResp get object timeline response
@@ -42,10 +43,11 @@ type ActObjectTimeline struct {
// ActObjectInfo act object info
type ActObjectInfo struct {
Title string `json:"title"`
ObjectType string `json:"object_type"`
QuestionID string `json:"question_id"`
AnswerID string `json:"answer_id"`
ObjectType string `json:"object_type"`
Title string `json:"title"`
QuestionID string `json:"question_id"`
AnswerID string `json:"answer_id"`
MainTagSlugName string `json:"main_tag_slug_name"`
}
// GetObjectTimelineDetailReq get object timeline detail request
@@ -63,9 +65,11 @@ type GetObjectTimelineDetailResp struct {
// ObjectTimelineDetail object timeline detail
type ObjectTimelineDetail struct {
Title string `json:"title"`
Tags []*ObjectTimelineTag `json:"tags"`
OriginalText string `json:"original_text"`
Title string `json:"title"`
Tags []*ObjectTimelineTag `json:"tags"`
OriginalText string `json:"original_text"`
SlugName string `json:"slug_name"`
MainTagSlugName string `json:"main_tag_slug_name"`
}
// ObjectTimelineTag object timeline tags
+39 -14
View File
@@ -17,6 +17,7 @@ import (
"github.com/answerdev/answer/internal/service/tag_common"
usercommon "github.com/answerdev/answer/internal/service/user_common"
"github.com/answerdev/answer/pkg/converter"
"github.com/answerdev/answer/pkg/obj"
"github.com/segmentfault/pacman/log"
)
@@ -73,6 +74,14 @@ func (as *ActivityService) GetObjectTimeline(ctx context.Context, req *schema.Ge
return nil, err
}
resp.ObjectInfo.Title = objInfo.Title
if objInfo.ObjectType == constant.TagObjectType {
tag, exist, _ := as.tagCommonService.GetTagByID(ctx, objInfo.TagID)
if exist {
resp.ObjectInfo.Title = tag.SlugName
resp.ObjectInfo.MainTagSlugName = tag.MainTagSlugName
}
}
resp.ObjectInfo.ObjectType = objInfo.ObjectType
resp.ObjectInfo.QuestionID = objInfo.QuestionID
resp.ObjectInfo.AnswerID = objInfo.AnswerID
@@ -89,13 +98,14 @@ func (as *ActivityService) GetObjectTimeline(ctx context.Context, req *schema.Ge
Cancelled: act.Cancelled == entity.ActivityCancelled,
ObjectID: act.ObjectID,
}
item.ObjectType, _ = obj.GetObjectTypeStrByObjectID(act.ObjectID)
if item.Cancelled {
item.CancelledAt = act.CancelledAt.Unix()
}
// database save activity type is number, change to activity type string is like "question.asked".
// so we need to cut the front part of '.'
item.ObjectType, item.ActivityType, _ = strings.Cut(config.ID2KeyMapping[act.ActivityType], ".")
_, item.ActivityType, _ = strings.Cut(config.ID2KeyMapping[act.ActivityType], ".")
isHidden, formattedActivityType := formatActivity(item.ActivityType)
if isHidden {
@@ -103,14 +113,20 @@ func (as *ActivityService) GetObjectTimeline(ctx context.Context, req *schema.Ge
}
item.ActivityType = formattedActivityType
// get user info
userBasicInfo, exist, err := as.userCommon.GetUserBasicInfoByID(ctx, act.UserID)
if err != nil {
return nil, err
}
if exist {
item.Username = userBasicInfo.Username
item.UserDisplayName = userBasicInfo.DisplayName
// if activity is down vote, only admin can see who does it.
if item.ActivityType == constant.ActDownVote && !req.IsAdmin {
item.Username = "N/A"
item.UserDisplayName = "N/A"
} else {
// get user info
userBasicInfo, exist, err := as.userCommon.GetUserBasicInfoByID(ctx, act.UserID)
if err != nil {
return nil, err
}
if exist {
item.Username = userBasicInfo.Username
item.UserDisplayName = userBasicInfo.DisplayName
}
}
if item.ObjectType == constant.CommentObjectType {
@@ -156,8 +172,14 @@ func (as *ActivityService) getOneObjectDetail(ctx context.Context, revisionID st
resp *schema.ObjectTimelineDetail, err error) {
resp = &schema.ObjectTimelineDetail{Tags: make([]*schema.ObjectTimelineTag, 0)}
// if request revision is 0, return null object detail.
if revisionID == "0" {
return nil, nil
}
revision, err := as.revisionService.GetRevision(ctx, revisionID)
if err != nil {
log.Warn(err)
return nil, nil
}
objInfo, err := as.objectInfoService.GetInfo(ctx, revision.ObjectID)
@@ -199,6 +221,8 @@ func (as *ActivityService) getOneObjectDetail(ctx context.Context, revisionID st
}
resp.Title = data.SlugName
resp.OriginalText = data.OriginalText
resp.SlugName = data.SlugName
resp.MainTagSlugName = data.MainTagSlugName
default:
log.Errorf("unknown object type %s", objInfo.ObjectType)
}
@@ -206,14 +230,15 @@ func (as *ActivityService) getOneObjectDetail(ctx context.Context, revisionID st
}
func formatActivity(activityType string) (isHidden bool, formattedActivityType string) {
if activityType == "voted_up" || activityType == "voted_down" || activityType == "accepted" {
if activityType == constant.ActVotedUp ||
activityType == constant.ActVotedDown {
return true, ""
}
if activityType == "vote_up" {
return false, "upvote"
if activityType == constant.ActVoteUp {
return false, constant.ActUpVote
}
if activityType == "vote_down" {
return false, "downvote"
if activityType == constant.ActVoteDown {
return false, constant.ActDownVote
}
return false, activityType
}