This is an automated email from the ASF dual-hosted git repository. linkinstar pushed a commit to branch dev in repository https://gitbox.apache.org/repos/asf/incubator-answer.git
commit 31a6fc9c367e755a282f8369d4986c640e87150f Author: hgaol <[email protected]> AuthorDate: Thu May 9 23:07:17 2024 +0800 convert tooltip in backend and refactored the ReactionResp structure --- i18n/en_US.yaml | 22 +++++--- i18n/zh_CN.yaml | 11 ++-- .../meta_schema.go => base/constant/meta.go} | 23 ++------- internal/controller/meta_controller.go | 3 +- internal/schema/meta_schema.go | 13 +++-- internal/service/meta/meta_service.go | 55 ++++++++++++++------ .../service/meta_common/meta_common_service.go | 2 +- ui/src/common/interface.ts | 8 ++- .../Detail/components/Reactions/index.tsx | 59 +++++++++++----------- 9 files changed, 114 insertions(+), 82 deletions(-) diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index 6442818d..25f3ad04 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -18,7 +18,6 @@ # The following fields are used for back-end backend: - base: success: other: Success. @@ -202,6 +201,9 @@ backend: other: The old password verification failed new_password_same_as_previous_setting: other: The new password is the same as the previous one. + meta: + object_not_found: + other: Meta object not found question: already_deleted: other: This post has been deleted. @@ -314,7 +316,8 @@ backend: name: other: rude or abusive desc: - other: A reasonable person would find this content inappropriate for respectful + other: + A reasonable person would find this content inappropriate for respectful discourse. a_duplicate: name: @@ -327,7 +330,8 @@ backend: name: other: not an answer desc: - other: This was posted as an answer, but it does not attempt to answer the + other: + This was posted as an answer, but it does not attempt to answer the question. It should possibly be an edit, a comment, another question, or deleted altogether. no_longer_needed: @@ -388,7 +392,8 @@ backend: name: other: needs details or clarity desc: - other: This question currently includes multiple questions in one. It should + other: + This question currently includes multiple questions in one. It should focus on one problem only. other: name: @@ -505,6 +510,9 @@ backend: other: Flagged post suggested_post_edit: other: Suggested edits + reaction: + tooltip: + other: "{{ .Names }} and {{ .Count }} more..." # The following fields are used for interface presentation(Front-end) ui: @@ -769,7 +777,6 @@ ui: heart: heart smile: smile frown: frown - tooltip: " and {{count}} more..." comment: btn_add_comment: Add comment reply_to: Reply to @@ -1446,7 +1453,6 @@ ui: qrcode_login_tip: Please use {{ agentName }} to scan the QR code and log in. login_failed_email_tip: Login failed, please allow this app to access your email information before try again. - admin: admin_header: title: Admin @@ -1827,7 +1833,7 @@ ui: text: Choose the reputation required for the privileges msg: should_be_number: the input should be number - number_larger_1 : number should be equal or larger than 1 + number_larger_1: number should be equal or larger than 1 form: optional: (optional) @@ -1911,7 +1917,7 @@ ui: post_deleted: This post has been deleted. post_pin: This post has been pinned. post_unpin: This post has been unpinned. - post_hide_list: This post has been hidden from list. + post_hide_list: This post has been hidden from list. post_show_list: This post has been shown to list. post_reopen: This post has been reopened. post_list: This post has been listed. diff --git a/i18n/zh_CN.yaml b/i18n/zh_CN.yaml index 56db05c2..9abddc22 100644 --- a/i18n/zh_CN.yaml +++ b/i18n/zh_CN.yaml @@ -200,6 +200,9 @@ backend: other: 旧密码验证失败。 new_password_same_as_previous_setting: other: 新密码和旧密码相同。 + meta: + object_not_found: + other: Meta 对象未找到 question: already_deleted: other: 该帖子已被删除。 @@ -499,6 +502,9 @@ backend: other: 举报的帖子 suggested_post_edit: other: 建议的编辑 + reaction: + tooltip: + other: "{{ .Names }} 以及另外 {{ .Count }} 个..." # The following fields are used for interface presentation(Front-end) ui: how_to_format: @@ -751,7 +757,6 @@ ui: heart: 爱心 smile: 笑脸 frown: 难过 - tooltip: " 以及另外 {{count}} 个..." comment: btn_add_comment: 添加评论 reply_to: 回复 @@ -1797,7 +1802,7 @@ ui: proposed: 提案 question_edit: 问题编辑 answer_edit: 回答编辑 - tag_edit: '标签管理: 编辑标签' + tag_edit: "标签管理: 编辑标签" edit_summary: 编辑备注 edit_question: 编辑问题 edit_answer: 编辑回答 @@ -1826,7 +1831,7 @@ ui: upvote: 点赞 accept: 采纳 cancelled: 已取消 - commented: '评论:' + commented: "评论:" rollback: 回滚 edited: 最后编辑于 answered: 回答于 diff --git a/internal/schema/meta_schema.go b/internal/base/constant/meta.go similarity index 55% copy from internal/schema/meta_schema.go copy to internal/base/constant/meta.go index 0774aca2..820572d7 100644 --- a/internal/schema/meta_schema.go +++ b/internal/base/constant/meta.go @@ -17,23 +17,8 @@ * under the License. */ -package schema +package constant -type UpdateReactionReq struct { - ObjectID string `validate:"required" json:"object_id"` - Emoji string `validate:"required,oneof=heart smile frown" json:"emoji"` - Reaction string `validate:"required,oneof=activate deactivate" json:"reaction"` - UserID string `json:"-"` -} - -type GetReactionReq struct { - ObjectID string `validate:"required" form:"object_id"` -} - -type ReactSummaryMeta map[string][]string - -type ReactionResp struct { - // reaction summary is a map, key is emoji, value is username list - // such as {"heart": ["jack", "tom"], "smile": ["andy"], "frown": ["bob"]} - ReactionSummary ReactSummaryMeta `json:"reaction_summary"` -} +const ( + ReactionTooltipLabel = "reaction.tooltip" +) diff --git a/internal/controller/meta_controller.go b/internal/controller/meta_controller.go index 2a9ab866..b8cef085 100644 --- a/internal/controller/meta_controller.go +++ b/internal/controller/meta_controller.go @@ -75,7 +75,8 @@ func (mc *MetaController) GetReaction(ctx *gin.Context) { if handler.BindAndCheck(ctx, req) { return } + req.UserID = middleware.GetLoginUserIDFromContext(ctx) - resp, err := mc.metaService.GetReactionByObjectId(ctx, req.ObjectID) + resp, err := mc.metaService.GetReactionByObjectId(ctx, req) handler.HandleResponse(ctx, err, resp) } diff --git a/internal/schema/meta_schema.go b/internal/schema/meta_schema.go index 0774aca2..d5fa46ff 100644 --- a/internal/schema/meta_schema.go +++ b/internal/schema/meta_schema.go @@ -28,12 +28,17 @@ type UpdateReactionReq struct { type GetReactionReq struct { ObjectID string `validate:"required" form:"object_id"` + UserID string `json:"-"` } -type ReactSummaryMeta map[string][]string +type ReactionSummaryMeta map[string][]string + +type ReactionItem struct { + Count int `json:"count"` + Tooltip string `json:"tooltip"` + IsActive bool `json:"is_active"` +} type ReactionResp struct { - // reaction summary is a map, key is emoji, value is username list - // such as {"heart": ["jack", "tom"], "smile": ["andy"], "frown": ["bob"]} - ReactionSummary ReactSummaryMeta `json:"reaction_summary"` + ReactionSummary map[string]*ReactionItem `json:"reaction_summary"` } diff --git a/internal/service/meta/meta_service.go b/internal/service/meta/meta_service.go index a22e0237..33129015 100644 --- a/internal/service/meta/meta_service.go +++ b/internal/service/meta/meta_service.go @@ -23,9 +23,13 @@ import ( "context" "encoding/json" "errors" + "strconv" + "strings" "github.com/apache/incubator-answer/internal/base/constant" + "github.com/apache/incubator-answer/internal/base/handler" "github.com/apache/incubator-answer/internal/base/reason" + "github.com/apache/incubator-answer/internal/base/translator" "github.com/apache/incubator-answer/internal/entity" "github.com/apache/incubator-answer/internal/schema" answercommon "github.com/apache/incubator-answer/internal/service/answer_common" @@ -54,26 +58,25 @@ func NewMetaService(metaCommonService *metacommon.MetaCommonService, userCommon } // GetReactionByObjectId get reaction -func (ms *MetaService) GetReactionByObjectId(ctx context.Context, objectID string) (resp *schema.ReactionResp, err error) { - resp = &schema.ReactionResp{} - reactionMeta, err := ms.metaCommonService.GetMetaByObjectIdAndKey(ctx, objectID, entity.ObjectReactSummaryKey) +func (ms *MetaService) GetReactionByObjectId(ctx context.Context, req *schema.GetReactionReq) (resp *schema.ReactionResp, err error) { + reactionMeta, err := ms.metaCommonService.GetMetaByObjectIdAndKey(ctx, req.ObjectID, entity.ObjectReactSummaryKey) // if not exist, return nil if err != nil { var pacmanErr *myErrors.Error if errors.As(err, &pacmanErr) && pacmanErr.Reason == reason.MetaObjectNotFound { - return resp, nil + return nil, nil } else { - return resp, err + return nil, err } } - var reaction schema.ReactSummaryMeta + var reaction schema.ReactionSummaryMeta err = json.Unmarshal([]byte(reactionMeta.Value), &reaction) if err != nil { - return resp, err + return nil, err } - return ms.convertToReactionResp(ctx, reaction) + return ms.convertToReactionResp(ctx, req.UserID, reaction) } // AddOrUpdateReaction add or update reaction @@ -104,11 +107,11 @@ func (ms *MetaService) AddOrUpdateReaction(ctx context.Context, req *schema.Upda } // add or update - var reaction schema.ReactSummaryMeta + var reaction schema.ReactionSummaryMeta err = ms.metaCommonService.AddOrUpdateMetaByObjectIdAndKey(ctx, req.ObjectID, entity.ObjectReactSummaryKey, func(meta *entity.Meta, exist bool) (*entity.Meta, error) { // if not exist, create new one if !exist { - reaction = schema.ReactSummaryMeta{} + reaction = schema.ReactionSummaryMeta{} } else { err = json.Unmarshal([]byte(meta.Value), &reaction) if err != nil { @@ -136,7 +139,7 @@ func (ms *MetaService) AddOrUpdateReaction(ctx context.Context, req *schema.Upda return nil, myErrors.InternalServer(reason.DatabaseError).WithError(err) } - resp, err = ms.convertToReactionResp(ctx, reaction) + resp, err = ms.convertToReactionResp(ctx, req.UserID, reaction) if err != nil { return nil, err } @@ -145,7 +148,7 @@ func (ms *MetaService) AddOrUpdateReaction(ctx context.Context, req *schema.Upda } // updateReaction update reaction -func (ms *MetaService) updateReaction(req *schema.UpdateReactionReq, reaction schema.ReactSummaryMeta) { +func (ms *MetaService) updateReaction(req *schema.UpdateReactionReq, reaction schema.ReactionSummaryMeta) { emojiUserIds, ok := reaction[req.Emoji] if !ok { @@ -181,12 +184,25 @@ func (ms *MetaService) updateReaction(req *schema.UpdateReactionReq, reaction sc reaction[req.Emoji] = emojiUserIds } -func (ms *MetaService) convertToReactionResp(ctx context.Context, reaction schema.ReactSummaryMeta) (*schema.ReactionResp, error) { +func (ms *MetaService) convertToReactionResp(ctx context.Context, userId string, reaction schema.ReactionSummaryMeta) (*schema.ReactionResp, error) { + lang := handler.GetLangByCtx(ctx) resp := &schema.ReactionResp{ - ReactionSummary: make(schema.ReactSummaryMeta), + ReactionSummary: make(map[string]*schema.ReactionItem), + } + isInArray := func(arr []string, target string) bool { + for _, str := range arr { + if str == target { + return true + } + } + return false } // traverse map and convert to username for emoji, userIds := range reaction { + resp.ReactionSummary[emoji] = &schema.ReactionItem{ + Count: len(userIds), + IsActive: isInArray(userIds, userId), + } userNames := make([]string, 0) userBasicInfos, err := ms.userCommon.BatchUserBasicInfoByID(ctx, userIds) if err != nil { @@ -194,9 +210,18 @@ func (ms *MetaService) convertToReactionResp(ctx context.Context, reaction schem } // get username for _, userBasicInfo := range userBasicInfos { + if len(userNames) == 5 { + resp.ReactionSummary[emoji].Tooltip = translator.TrWithData(lang, constant.ReactionTooltipLabel, map[string]string{ + "Count": strconv.Itoa(len(userIds) - 5), + "Names": strings.Join(userNames, ", "), + }) + break + } userNames = append(userNames, userBasicInfo.Username) } - resp.ReactionSummary[emoji] = userNames + if len(userIds) <= 5 { + resp.ReactionSummary[emoji].Tooltip = strings.Join(userNames, ", ") + } } return resp, nil diff --git a/internal/service/meta_common/meta_common_service.go b/internal/service/meta_common/meta_common_service.go index d34f7d43..08065d54 100644 --- a/internal/service/meta_common/meta_common_service.go +++ b/internal/service/meta_common/meta_common_service.go @@ -81,7 +81,7 @@ func (ms *MetaCommonService) AddOrUpdateMetaByObjectIdAndKey(ctx context.Context func (ms *MetaCommonService) GetMetaByObjectIdAndKey(ctx context.Context, objectID, key string) (meta *entity.Meta, err error) { meta, exist, err := ms.metaRepo.GetMetaByObjectIdAndKey(ctx, objectID, key) if err != nil { - return + return nil, err } if !exist { return nil, myErrors.BadRequest(reason.MetaObjectNotFound) diff --git a/ui/src/common/interface.ts b/ui/src/common/interface.ts index d5f64f40..700746b8 100644 --- a/ui/src/common/interface.ts +++ b/ui/src/common/interface.ts @@ -724,5 +724,11 @@ export interface PutFlagReviewParams { * @description response for reaction */ export interface ReactionItems { - reaction_summary: Record<string, string[]>; + reaction_summary: Record<string, ReactionItem>; +} + +export interface ReactionItem { + count: number; + tooltip: string; + is_active: boolean; } diff --git a/ui/src/pages/Questions/Detail/components/Reactions/index.tsx b/ui/src/pages/Questions/Detail/components/Reactions/index.tsx index 0d380204..dc08cf1e 100644 --- a/ui/src/pages/Questions/Detail/components/Reactions/index.tsx +++ b/ui/src/pages/Questions/Detail/components/Reactions/index.tsx @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'; import { Icon } from '@/components'; import { queryReactions, updateReaction } from '@/services'; import { tryNormalLogged } from '@/utils/guard'; -import { loggedUserInfoStore } from '@/stores'; +import { ReactionItem } from '@/common/interface'; interface Props { objectId: string; @@ -36,9 +36,8 @@ const Index: FC<Props> = ({ showAddCommentBtn, handleClickComment, }) => { - const [reactions, setReactions] = useState<Record<string, string[]>>(); + const [reactions, setReactions] = useState<Record<string, ReactionItem>>(); const { t } = useTranslation('translation'); - const { username = '' } = loggedUserInfoStore((state) => state.user); useEffect(() => { queryReactions(objectId).then((res) => { @@ -50,34 +49,34 @@ const Index: FC<Props> = ({ if (!tryNormalLogged(true)) { return; } - let reaction = 'activate'; - if ( - reactions && - reactions[params.emoji] && - reactions[params.emoji].includes(username) - ) { - reaction = 'deactivate'; - } - updateReaction({ ...params, reaction }).then((res) => { + updateReaction({ + ...params, + reaction: + reactions && + reactions[params.emoji] && + reactions[params.emoji].is_active + ? 'deactivate' + : 'activate', + }).then((res) => { setReactions(res.reaction_summary); }); }; - const convertToTooltip = (names: string[]) => { - const n: number = Math.min(5, names.length); - let ret = ''; - for (let i = 0; i < n; i += 1) { - if (i === n - 1) { - ret += names[i]; - } else { - ret += `${names[i]}, `; - } - } - if (names.length > 5) { - ret += t('reaction.tooltip', { count: names.length - 5 }); - } - return ret; - }; + // const convertToTooltip = (names: string[]) => { + // const n: number = Math.min(5, names.length); + // let ret = ''; + // for (let i = 0; i < n; i += 1) { + // if (i === n - 1) { + // ret += names[i]; + // } else { + // ret += `${names[i]}, `; + // } + // } + // if (names.length > 5) { + // ret += t('reaction.tooltip', { count: names.length - 5 }); + // } + // return ret; + // }; const renderTooltip = (props) => ( <Tooltip id="reaction-button-tooltip" {...props} bsPrefix="tooltip"> @@ -120,7 +119,7 @@ const Index: FC<Props> = ({ {reactions && emojiMap.map((emoji) => { - if (!reactions[emoji.name] || reactions[emoji.name].length === 0) { + if (!reactions[emoji.name] || reactions[emoji.name].count === 0) { return null; } return ( @@ -131,7 +130,7 @@ const Index: FC<Props> = ({ <Tooltip> <div className="text-start"> <b>{t(`reaction.${emoji.name}`)}</b> <br />{' '} - {convertToTooltip(reactions[emoji.name])} + {reactions[emoji.name].tooltip} </div> </Tooltip> }> @@ -143,7 +142,7 @@ const Index: FC<Props> = ({ handleSubmit({ object_id: objectId, emoji: emoji.name }) }> <Icon name={emoji.icon} className={emoji.className} /> - <span className="ms-1">{reactions[emoji.name].length}</span> + <span className="ms-1">{reactions[emoji.name].count}</span> </Button> </OverlayTrigger> );
