feat: activity timeline show vote activity
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user