From f1d97fbaa81baff89a19020202f209a814c63e79 Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Fri, 26 May 2023 15:23:23 +0800 Subject: [PATCH 01/12] fix(user): check email invalid when oauth login --- i18n/en_US.yaml | 3 + internal/base/reason/reason.go | 127 +++++++++--------- .../user_center_login_service.go | 6 + .../user_external_login_service.go | 23 ++++ 4 files changed, 98 insertions(+), 61 deletions(-) diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index f3adcd96..c4b70afb 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -1,4 +1,5 @@ # The following fields are used for back-end + backend: base: success: @@ -221,6 +222,8 @@ backend: no_permission: other: No permission to Revision. user: + external_login_missing_user_id: + other: The third-party platform does not provide a unique UserID, so you cannot login, please contact the website administrator. external_login_unbinding_forbidden: other: Please set a login password for your account before you remove this login. email_or_password_wrong: diff --git a/internal/base/reason/reason.go b/internal/base/reason/reason.go index b4cdbcb9..91ee74c6 100644 --- a/internal/base/reason/reason.go +++ b/internal/base/reason/reason.go @@ -16,65 +16,70 @@ const ( ) const ( - EmailOrPasswordWrong = "error.object.email_or_password_incorrect" - CommentNotFound = "error.comment.not_found" - CommentCannotEditAfterDeadline = "error.comment.cannot_edit_after_deadline" - QuestionNotFound = "error.question.not_found" - QuestionCannotDeleted = "error.question.cannot_deleted" - QuestionCannotClose = "error.question.cannot_close" - QuestionCannotUpdate = "error.question.cannot_update" - QuestionAlreadyDeleted = "error.question.already_deleted" - AnswerNotFound = "error.answer.not_found" - AnswerCannotDeleted = "error.answer.cannot_deleted" - AnswerCannotUpdate = "error.answer.cannot_update" - AnswerCannotAddByClosedQuestion = "error.answer.question_closed_cannot_add" - CommentEditWithoutPermission = "error.comment.edit_without_permission" - DisallowVote = "error.object.disallow_vote" - DisallowFollow = "error.object.disallow_follow" - DisallowVoteYourSelf = "error.object.disallow_vote_your_self" - CaptchaVerificationFailed = "error.object.captcha_verification_failed" - OldPasswordVerificationFailed = "error.object.old_password_verification_failed" - NewPasswordSameAsPreviousSetting = "error.object.new_password_same_as_previous_setting" - UserNotFound = "error.user.not_found" - UsernameInvalid = "error.user.username_invalid" - UsernameDuplicate = "error.user.username_duplicate" - UserSetAvatar = "error.user.set_avatar" - EmailDuplicate = "error.email.duplicate" - EmailVerifyURLExpired = "error.email.verify_url_expired" - EmailNeedToBeVerified = "error.email.need_to_be_verified" - EmailIllegalDomainError = "error.email.illegal_email_domain_error" - UserSuspended = "error.user.suspended" - ObjectNotFound = "error.object.not_found" - TagNotFound = "error.tag.not_found" - TagNotContainSynonym = "error.tag.not_contain_synonym_tags" - TagCannotUpdate = "error.tag.cannot_update" - TagIsUsedCannotDelete = "error.tag.is_used_cannot_delete" - TagAlreadyExist = "error.tag.already_exist" - RankFailToMeetTheCondition = "error.rank.fail_to_meet_the_condition" - VoteRankFailToMeetTheCondition = "error.rank.vote_fail_to_meet_the_condition" - NoEnoughRankToOperate = "error.rank.no_enough_rank_to_operate" - ThemeNotFound = "error.theme.not_found" - LangNotFound = "error.lang.not_found" - ReportHandleFailed = "error.report.handle_failed" - ReportNotFound = "error.report.not_found" - ReadConfigFailed = "error.config.read_config_failed" - DatabaseConnectionFailed = "error.database.connection_failed" - InstallCreateTableFailed = "error.database.create_table_failed" - InstallConfigFailed = "error.install.create_config_failed" - SiteInfoNotFound = "error.site_info.not_found" - UploadFileSourceUnsupported = "error.upload.source_unsupported" - UploadFileUnsupportedFileFormat = "error.upload.unsupported_file_format" - RecommendTagNotExist = "error.tag.recommend_tag_not_found" - RecommendTagEnter = "error.tag.recommend_tag_enter" - RevisionReviewUnderway = "error.revision.review_underway" - RevisionNoPermission = "error.revision.no_permission" - UserCannotUpdateYourRole = "error.user.cannot_update_your_role" - TagCannotSetSynonymAsItself = "error.tag.cannot_set_synonym_as_itself" - NotAllowedRegistration = "error.user.not_allowed_registration" - SMTPConfigFromNameCannotBeEmail = "error.smtp.config_from_name_cannot_be_email" - AdminCannotUpdateTheirPassword = "error.admin.cannot_update_their_password" - AdminCannotModifySelfStatus = "error.admin.cannot_modify_self_status" - UserExternalLoginUnbindingForbidden = "error.user.external_login_unbinding_forbidden" - UserAccessDenied = "error.user.access_denied" - UserPageAccessDenied = "error.user.page_access_denied" + EmailOrPasswordWrong = "error.object.email_or_password_incorrect" + CommentNotFound = "error.comment.not_found" + CommentCannotEditAfterDeadline = "error.comment.cannot_edit_after_deadline" + QuestionNotFound = "error.question.not_found" + QuestionCannotDeleted = "error.question.cannot_deleted" + QuestionCannotClose = "error.question.cannot_close" + QuestionCannotUpdate = "error.question.cannot_update" + QuestionAlreadyDeleted = "error.question.already_deleted" + AnswerNotFound = "error.answer.not_found" + AnswerCannotDeleted = "error.answer.cannot_deleted" + AnswerCannotUpdate = "error.answer.cannot_update" + AnswerCannotAddByClosedQuestion = "error.answer.question_closed_cannot_add" + CommentEditWithoutPermission = "error.comment.edit_without_permission" + DisallowVote = "error.object.disallow_vote" + DisallowFollow = "error.object.disallow_follow" + DisallowVoteYourSelf = "error.object.disallow_vote_your_self" + CaptchaVerificationFailed = "error.object.captcha_verification_failed" + OldPasswordVerificationFailed = "error.object.old_password_verification_failed" + NewPasswordSameAsPreviousSetting = "error.object.new_password_same_as_previous_setting" + UserNotFound = "error.user.not_found" + UsernameInvalid = "error.user.username_invalid" + UsernameDuplicate = "error.user.username_duplicate" + UserSetAvatar = "error.user.set_avatar" + EmailDuplicate = "error.email.duplicate" + EmailVerifyURLExpired = "error.email.verify_url_expired" + EmailNeedToBeVerified = "error.email.need_to_be_verified" + EmailIllegalDomainError = "error.email.illegal_email_domain_error" + UserSuspended = "error.user.suspended" + ObjectNotFound = "error.object.not_found" + TagNotFound = "error.tag.not_found" + TagNotContainSynonym = "error.tag.not_contain_synonym_tags" + TagCannotUpdate = "error.tag.cannot_update" + TagIsUsedCannotDelete = "error.tag.is_used_cannot_delete" + TagAlreadyExist = "error.tag.already_exist" + RankFailToMeetTheCondition = "error.rank.fail_to_meet_the_condition" + VoteRankFailToMeetTheCondition = "error.rank.vote_fail_to_meet_the_condition" + NoEnoughRankToOperate = "error.rank.no_enough_rank_to_operate" + ThemeNotFound = "error.theme.not_found" + LangNotFound = "error.lang.not_found" + ReportHandleFailed = "error.report.handle_failed" + ReportNotFound = "error.report.not_found" + ReadConfigFailed = "error.config.read_config_failed" + DatabaseConnectionFailed = "error.database.connection_failed" + InstallCreateTableFailed = "error.database.create_table_failed" + InstallConfigFailed = "error.install.create_config_failed" + SiteInfoNotFound = "error.site_info.not_found" + UploadFileSourceUnsupported = "error.upload.source_unsupported" + UploadFileUnsupportedFileFormat = "error.upload.unsupported_file_format" + RecommendTagNotExist = "error.tag.recommend_tag_not_found" + RecommendTagEnter = "error.tag.recommend_tag_enter" + RevisionReviewUnderway = "error.revision.review_underway" + RevisionNoPermission = "error.revision.no_permission" + UserCannotUpdateYourRole = "error.user.cannot_update_your_role" + TagCannotSetSynonymAsItself = "error.tag.cannot_set_synonym_as_itself" + NotAllowedRegistration = "error.user.not_allowed_registration" + SMTPConfigFromNameCannotBeEmail = "error.smtp.config_from_name_cannot_be_email" + AdminCannotUpdateTheirPassword = "error.admin.cannot_update_their_password" + AdminCannotModifySelfStatus = "error.admin.cannot_modify_self_status" + UserAccessDenied = "error.user.access_denied" + UserPageAccessDenied = "error.user.page_access_denied" +) + +// user external login reasons +const ( + UserExternalLoginUnbindingForbidden = "error.user.external_login_unbinding_forbidden" + UserExternalLoginMissingUserID = "error.user.external_login_missing_user_id" ) diff --git a/internal/service/user_external_login/user_center_login_service.go b/internal/service/user_external_login/user_center_login_service.go index 14200298..37a0614d 100644 --- a/internal/service/user_external_login/user_center_login_service.go +++ b/internal/service/user_external_login/user_center_login_service.go @@ -49,6 +49,12 @@ func NewUserCenterLoginService( func (us *UserCenterLoginService) ExternalLogin( ctx context.Context, userCenter plugin.UserCenter, basicUserInfo *plugin.UserCenterBasicUserInfo) ( resp *schema.UserExternalLoginResp, err error) { + if len(basicUserInfo.ExternalID) == 0 { + return &schema.UserExternalLoginResp{ + ErrTitle: translator.Tr(handler.GetLangByCtx(ctx), reason.UserAccessDenied), + ErrMsg: translator.Tr(handler.GetLangByCtx(ctx), reason.UserExternalLoginMissingUserID), + }, nil + } if len(basicUserInfo.Email) > 0 { // check whether site allow register or not diff --git a/internal/service/user_external_login/user_external_login_service.go b/internal/service/user_external_login/user_external_login_service.go index 912d9135..baed23fc 100644 --- a/internal/service/user_external_login/user_external_login_service.go +++ b/internal/service/user_external_login/user_external_login_service.go @@ -6,13 +6,16 @@ import ( "fmt" "time" + "github.com/answerdev/answer/internal/base/handler" "github.com/answerdev/answer/internal/base/reason" + "github.com/answerdev/answer/internal/base/translator" "github.com/answerdev/answer/internal/entity" "github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/service/activity" "github.com/answerdev/answer/internal/service/export" "github.com/answerdev/answer/internal/service/siteinfo_common" usercommon "github.com/answerdev/answer/internal/service/user_common" + "github.com/answerdev/answer/pkg/checker" "github.com/answerdev/answer/pkg/random" "github.com/answerdev/answer/pkg/token" "github.com/answerdev/answer/plugin" @@ -64,6 +67,13 @@ func NewUserExternalLoginService( func (us *UserExternalLoginService) ExternalLogin( ctx context.Context, externalUserInfo *schema.ExternalLoginUserInfoCache) ( resp *schema.UserExternalLoginResp, err error) { + if len(externalUserInfo.ExternalID) == 0 { + return &schema.UserExternalLoginResp{ + ErrTitle: translator.Tr(handler.GetLangByCtx(ctx), reason.UserAccessDenied), + ErrMsg: translator.Tr(handler.GetLangByCtx(ctx), reason.UserExternalLoginMissingUserID), + }, nil + } + oldExternalLoginUserInfo, exist, err := us.userExternalLoginRepo.GetByExternalID(ctx, externalUserInfo.Provider, externalUserInfo.ExternalID) if err != nil { @@ -99,6 +109,19 @@ func (us *UserExternalLoginService) ExternalLogin( return &schema.UserExternalLoginResp{BindingKey: bindingKey}, nil } + // check whether site allow register or not + siteInfo, err := us.siteInfoCommonService.GetSiteLogin(ctx) + if err != nil { + return nil, err + } + if !checker.EmailInAllowEmailDomain(externalUserInfo.Email, siteInfo.AllowEmailDomains) { + log.Debugf("email domain not allowed: %s", externalUserInfo.Email) + return &schema.UserExternalLoginResp{ + ErrTitle: translator.Tr(handler.GetLangByCtx(ctx), reason.UserAccessDenied), + ErrMsg: translator.Tr(handler.GetLangByCtx(ctx), reason.EmailIllegalDomainError), + }, nil + } + oldUserInfo, exist, err := us.userRepo.GetByEmail(ctx, externalUserInfo.Email) if err != nil { return nil, err From 6c5d4afbe142180f108300353251fec075cff073 Mon Sep 17 00:00:00 2001 From: shuai Date: Fri, 26 May 2023 17:50:27 +0800 Subject: [PATCH 02/12] fix: i8n use backend --- i18n/en_US.yaml | 8 -------- .../pages/Users/Personal/components/Reputation/index.tsx | 6 +----- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index c7c5e0ac..67d34d6e 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -1086,14 +1086,6 @@ ui: accepted: Accepted answered: answered asked: asked - rank_type: - upvote: upvote - upvoted: upvoted - downvote: downvote - downvoted: downvoted - edit: edit - accept: accept - accepted: accepted downvoted: downvoted mod_short: Mod mod_long: Moderators diff --git a/ui/src/pages/Users/Personal/components/Reputation/index.tsx b/ui/src/pages/Users/Personal/components/Reputation/index.tsx index 936759ca..b9caf624 100644 --- a/ui/src/pages/Users/Personal/components/Reputation/index.tsx +++ b/ui/src/pages/Users/Personal/components/Reputation/index.tsx @@ -1,6 +1,5 @@ import { FC, memo } from 'react'; import { ListGroup, ListGroupItem } from 'react-bootstrap'; -import { useTranslation } from 'react-i18next'; import { FormatTime } from '@/components'; import { pathFactory } from '@/router/pathFactory'; @@ -11,7 +10,6 @@ interface Props { } const Index: FC = ({ visible, data }) => { - const { t } = useTranslation('translation', { keyPrefix: 'personal' }); if (!visible || !data?.length) { return null; } @@ -48,9 +46,7 @@ const Index: FC = ({ visible, data }) => { {item.title}
- - {t(item.rank_type, { keyPrefix: 'personal.rank_type' })} - + {item.rank_type}
From c86057128963e1661ad71b513fa3647041b57e19 Mon Sep 17 00:00:00 2001 From: shuai Date: Fri, 26 May 2023 18:25:04 +0800 Subject: [PATCH 03/12] fix: edit answer page style adjustment --- ui/src/common/sideNavLayout.scss | 23 ++++++----------------- ui/src/components/Editor/utils/index.ts | 16 ++++++++++------ ui/src/components/SideNav/index.scss | 12 ++++++++++++ ui/src/components/SideNav/index.tsx | 2 +- 4 files changed, 29 insertions(+), 24 deletions(-) diff --git a/ui/src/common/sideNavLayout.scss b/ui/src/common/sideNavLayout.scss index 866e4267..e955da35 100644 --- a/ui/src/common/sideNavLayout.scss +++ b/ui/src/common/sideNavLayout.scss @@ -1,28 +1,17 @@ +// .flex-auto { +// max-width: 70%; +// } .page-right-side { flex: none; width: 30%; } -.line { - position: absolute; - top: 0; - right: 12px; - width: 1px; - height: 100%; - background-color: var(--bs-gray-300); - min-height: calc(100vh - 62px - 74px); -} - // lg @media screen and (max-width: 1199.9px) { + // .flex-auto { + // max-width: 100%; + // } .page-right-side { width: 100%; } } - -// md -@media screen and (max-width: 991.9px) { - .line { - display: none; - } -} diff --git a/ui/src/components/Editor/utils/index.ts b/ui/src/components/Editor/utils/index.ts index 7f8f80f6..e56073ef 100644 --- a/ui/src/components/Editor/utils/index.ts +++ b/ui/src/components/Editor/utils/index.ts @@ -94,13 +94,17 @@ export function htmlRender(el: HTMLElement | null) { el.querySelectorAll('.language-mermaid').forEach((pre) => { const flag = Date.now(); - mermaid.render(`theGraph${flag}`, pre.textContent, function (svgCode) { - const p = document.createElement('p'); - p.className = 'text-center'; - p.innerHTML = svgCode; + mermaid.render( + `theGraph${flag}`, + pre.textContent || '', + function (svgCode) { + const p = document.createElement('p'); + p.className = 'text-center'; + p.innerHTML = svgCode; - pre.parentNode?.replaceChild(p, pre); - }); + pre.parentNode?.replaceChild(p, pre); + }, + ); }); }); import('katex/contrib/auto-render/auto-render').then( diff --git a/ui/src/components/SideNav/index.scss b/ui/src/components/SideNav/index.scss index 8e12db8c..d9fb63b9 100644 --- a/ui/src/components/SideNav/index.scss +++ b/ui/src/components/SideNav/index.scss @@ -14,6 +14,15 @@ color: black; background-color: var(--bs-gray-200); } + .side-nav-right-line { + position: absolute; + top: 0; + right: 12px; + width: 1px; + height: 100%; + background-color: var(--bs-gray-300); + min-height: calc(100vh - 62px - 74px); + } } @media screen and (max-width: 991.9px) { @@ -21,5 +30,8 @@ .nav { max-width: 100%; } + .side-nav-right-line { + display: none; + } } } diff --git a/ui/src/components/SideNav/index.tsx b/ui/src/components/SideNav/index.tsx index fcea12c1..31515e20 100644 --- a/ui/src/components/SideNav/index.tsx +++ b/ui/src/components/SideNav/index.tsx @@ -66,7 +66,7 @@ const Index: FC = () => { ) : null} -
+
); }; From e6cc262a5c626dcfed323f774fc2a088157d30fa Mon Sep 17 00:00:00 2001 From: shuai Date: Mon, 29 May 2023 10:09:46 +0800 Subject: [PATCH 04/12] fix: add page-main class --- ui/src/common/sideNavLayout.scss | 12 ++++++------ ui/src/pages/Questions/Ask/index.tsx | 2 +- ui/src/pages/Questions/Detail/index.tsx | 2 +- ui/src/pages/Questions/EditAnswer/index.tsx | 2 +- ui/src/pages/Questions/index.tsx | 2 +- ui/src/pages/Review/index.tsx | 2 +- ui/src/pages/Search/index.tsx | 2 +- ui/src/pages/Tags/Create/index.tsx | 2 +- ui/src/pages/Tags/Detail/index.tsx | 2 +- ui/src/pages/Tags/Edit/index.tsx | 2 +- ui/src/pages/Tags/Info/index.tsx | 2 +- ui/src/pages/Users/Notifications/index.tsx | 2 +- ui/src/pages/Users/Personal/index.tsx | 4 ++-- 13 files changed, 19 insertions(+), 19 deletions(-) diff --git a/ui/src/common/sideNavLayout.scss b/ui/src/common/sideNavLayout.scss index e955da35..a1b726ff 100644 --- a/ui/src/common/sideNavLayout.scss +++ b/ui/src/common/sideNavLayout.scss @@ -1,6 +1,6 @@ -// .flex-auto { -// max-width: 70%; -// } +.page-main { + max-width: 70%; +} .page-right-side { flex: none; width: 30%; @@ -8,9 +8,9 @@ // lg @media screen and (max-width: 1199.9px) { - // .flex-auto { - // max-width: 100%; - // } + .page-main { + max-width: 100%; + } .page-right-side { width: 100%; } diff --git a/ui/src/pages/Questions/Ask/index.tsx b/ui/src/pages/Questions/Ask/index.tsx index 9a920501..cdc087ee 100644 --- a/ui/src/pages/Questions/Ask/index.tsx +++ b/ui/src/pages/Questions/Ask/index.tsx @@ -322,7 +322,7 @@ const Ask = () => {

{isEdit ? t('edit_title') : t('title')}

- +
{isEdit && ( diff --git a/ui/src/pages/Questions/Detail/index.tsx b/ui/src/pages/Questions/Detail/index.tsx index 7a17dff6..0ccaca1d 100644 --- a/ui/src/pages/Questions/Detail/index.tsx +++ b/ui/src/pages/Questions/Detail/index.tsx @@ -212,7 +212,7 @@ const Index = () => { return ( - + {question?.operation?.level && } {isLoading ? ( diff --git a/ui/src/pages/Questions/EditAnswer/index.tsx b/ui/src/pages/Questions/EditAnswer/index.tsx index f0eb3f78..00e6fad1 100644 --- a/ui/src/pages/Questions/EditAnswer/index.tsx +++ b/ui/src/pages/Questions/EditAnswer/index.tsx @@ -187,7 +187,7 @@ const Index = () => {

{t('title')}

- + { usePageTags({ title: pageTitle, subtitle: slogan }); return ( - + { return (

{t('review')}

- + {!noTasks && ro && ( <> diff --git a/ui/src/pages/Search/index.tsx b/ui/src/pages/Search/index.tsx index 935a5f2e..4ea10ee3 100644 --- a/ui/src/pages/Search/index.tsx +++ b/ui/src/pages/Search/index.tsx @@ -39,7 +39,7 @@ const Index = () => { }); return ( - + diff --git a/ui/src/pages/Tags/Create/index.tsx b/ui/src/pages/Tags/Create/index.tsx index a3bef8fe..4cf3716f 100644 --- a/ui/src/pages/Tags/Create/index.tsx +++ b/ui/src/pages/Tags/Create/index.tsx @@ -142,7 +142,7 @@ const Index = () => {

{t('title')}

- + {t('form.fields.display_name.label')} diff --git a/ui/src/pages/Tags/Detail/index.tsx b/ui/src/pages/Tags/Detail/index.tsx index 5fbf9973..3144bc7b 100644 --- a/ui/src/pages/Tags/Detail/index.tsx +++ b/ui/src/pages/Tags/Detail/index.tsx @@ -101,7 +101,7 @@ const Questions: FC = () => { }); return ( - + {isLoading || listLoading ? (
diff --git a/ui/src/pages/Tags/Edit/index.tsx b/ui/src/pages/Tags/Edit/index.tsx index 8423e5d6..0ede8fc3 100644 --- a/ui/src/pages/Tags/Edit/index.tsx +++ b/ui/src/pages/Tags/Edit/index.tsx @@ -191,7 +191,7 @@ const Index = () => {

{t('title')}

- + {t('form.fields.revision.label')} diff --git a/ui/src/pages/Tags/Info/index.tsx b/ui/src/pages/Tags/Info/index.tsx index b4125172..c5867617 100644 --- a/ui/src/pages/Tags/Info/index.tsx +++ b/ui/src/pages/Tags/Info/index.tsx @@ -133,7 +133,7 @@ const TagIntroduction = () => { return ( - +

{ }); return ( - +

{t('title')}

diff --git a/ui/src/pages/Users/Personal/index.tsx b/ui/src/pages/Users/Personal/index.tsx index 999aa2ed..29bbee36 100644 --- a/ui/src/pages/Users/Personal/index.tsx +++ b/ui/src/pages/Users/Personal/index.tsx @@ -63,7 +63,7 @@ const Personal: FC = () => { {userInfo?.status !== 'normal' && userInfo?.status_msg && ( )} - + { - + Date: Tue, 30 May 2023 10:24:08 +0800 Subject: [PATCH 05/12] fix: sidenav need sticky --- ui/src/components/SideNav/index.scss | 9 +++ ui/src/components/SideNav/index.tsx | 82 ++++++++++++++-------------- 2 files changed, 51 insertions(+), 40 deletions(-) diff --git a/ui/src/components/SideNav/index.scss b/ui/src/components/SideNav/index.scss index d9fb63b9..d07a65af 100644 --- a/ui/src/components/SideNav/index.scss +++ b/ui/src/components/SideNav/index.scss @@ -1,5 +1,14 @@ #sideNav { + .nav-wrap { + position: sticky; + width: auto; + top: 62px; + box-sizing: border-box; + max-height: calc(100vh - 74px - 62px - 24px); + overflow-y: auto; + margin-bottom: 8px; + } .nav { max-width: 172px; } diff --git a/ui/src/components/SideNav/index.tsx b/ui/src/components/SideNav/index.tsx index 31515e20..3efe7653 100644 --- a/ui/src/components/SideNav/index.tsx +++ b/ui/src/components/SideNav/index.tsx @@ -20,52 +20,54 @@ const Index: FC = () => { lg={3} md={12} className={classnames( - 'pt-4 position-relative', - visible ? 'fade-in' : 'd-none d-lg-block', + 'position-relative', + visible ? '' : 'd-none d-lg-block', )} id="sideNav"> - +
); From 58ff1215f1040eba40964165f19e9373c5c61adf Mon Sep 17 00:00:00 2001 From: shuai Date: Tue, 30 May 2023 18:01:22 +0800 Subject: [PATCH 06/12] fix: change sidenav max width --- ui/src/components/SideNav/index.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/components/SideNav/index.scss b/ui/src/components/SideNav/index.scss index d07a65af..a147e390 100644 --- a/ui/src/components/SideNav/index.scss +++ b/ui/src/components/SideNav/index.scss @@ -10,7 +10,7 @@ margin-bottom: 8px; } .nav { - max-width: 172px; + max-width: calc(100% - 24px); } .nav-link { color: rgba(0, 0, 0, 0.65); From 378b6e6b2d86cee2c6d91a6e251cce86416b1819 Mon Sep 17 00:00:00 2001 From: haitaoo Date: Wed, 31 May 2023 15:27:13 +0800 Subject: [PATCH 07/12] fix(guard.isIgnoredPath): Avoid incorrect matches when using `indexOf` --- .../Auth/components/WeCom/index.tsx | 5 ++- ui/src/pages/UserCenter/Auth/index.tsx | 2 +- .../AuthFailed/components/WeCom.tsx | 2 +- ui/src/pages/UserCenter/AuthFailed/index.tsx | 2 +- ui/src/utils/floppyNavigation.ts | 42 +++++++++++++++++++ ui/src/utils/guard.ts | 10 +++-- 6 files changed, 54 insertions(+), 9 deletions(-) diff --git a/ui/src/pages/UserCenter/Auth/components/WeCom/index.tsx b/ui/src/pages/UserCenter/Auth/components/WeCom/index.tsx index 21970f55..70e4a018 100644 --- a/ui/src/pages/UserCenter/Auth/components/WeCom/index.tsx +++ b/ui/src/pages/UserCenter/Auth/components/WeCom/index.tsx @@ -65,12 +65,13 @@ const Index: FC = () => { clearTimeout(checkTimer); }; }, []); + if (getUaType() !== USER_AGENT_NAMES.WeCom) { return ( - {ucAgent?.agent_info.display_name} {t('login')} + {ucAgent?.agent_info?.display_name} {t('login')} {qrcodeDataUrl ? ( <> @@ -82,7 +83,7 @@ const Index: FC = () => { />
{t('qrcode_login_tip', { - agentName: ucAgent?.agent_info.display_name, + agentName: ucAgent?.agent_info?.display_name, })}
diff --git a/ui/src/pages/UserCenter/Auth/index.tsx b/ui/src/pages/UserCenter/Auth/index.tsx index 9387daea..f7a4f86c 100644 --- a/ui/src/pages/UserCenter/Auth/index.tsx +++ b/ui/src/pages/UserCenter/Auth/index.tsx @@ -13,7 +13,7 @@ const Index = () => { const { t } = useTranslation('translation'); const [searchParam] = useSearchParams(); const { agent: ucAgent } = userCenterStore(); - let agentName = ucAgent?.agent_info.name || ''; + let agentName = ucAgent?.agent_info?.name || ''; if (searchParam.get('agent_name')) { agentName = searchParam.get('agent_name') || ''; } diff --git a/ui/src/pages/UserCenter/AuthFailed/components/WeCom.tsx b/ui/src/pages/UserCenter/AuthFailed/components/WeCom.tsx index c277a558..b69561b4 100644 --- a/ui/src/pages/UserCenter/AuthFailed/components/WeCom.tsx +++ b/ui/src/pages/UserCenter/AuthFailed/components/WeCom.tsx @@ -35,7 +35,7 @@ const Index: FC = () => {

- {ucAgent?.agent_info.display_name} {t('login')} + {ucAgent?.agent_info?.display_name} {t('login')}

{t('login_failed_email_tip')} diff --git a/ui/src/pages/UserCenter/AuthFailed/index.tsx b/ui/src/pages/UserCenter/AuthFailed/index.tsx index 05c9c78d..9ab9cc72 100644 --- a/ui/src/pages/UserCenter/AuthFailed/index.tsx +++ b/ui/src/pages/UserCenter/AuthFailed/index.tsx @@ -13,7 +13,7 @@ const Index = () => { const { t } = useTranslation('translation'); const [searchParam] = useSearchParams(); const { agent: ucAgent } = userCenterStore(); - let agentName = ucAgent?.agent_info.name || ''; + let agentName = ucAgent?.agent_info?.name || ''; if (searchParam.get('agent_name')) { agentName = searchParam.get('agent_name') || ''; } diff --git a/ui/src/utils/floppyNavigation.ts b/ui/src/utils/floppyNavigation.ts index 6cdb1544..7ed3a77b 100644 --- a/ui/src/utils/floppyNavigation.ts +++ b/ui/src/utils/floppyNavigation.ts @@ -10,6 +10,47 @@ const equalToCurrentHref = (target: string, base?: string) => { const targetUrl = new URL(target, base); return targetUrl.toString() === window.location.href; }; +const matchToCurrentHref = (target: string) => { + target = (target || '').trim(); + // Empty string or `/` can match any path + if (!target || target === '/') { + return true; + } + const { pathname, search, hash } = window.location; + const tPart = target.split('?'); + + /** + * With the current requirements, `hash` and `search` can simply be matched + * Later extended to field-by-field matching if necessary + */ + if (tPart[1]) { + const tChip = tPart[1].split('#'); + const tSearch = tChip[0] || ''; + const tHash = tChip[1] || ''; + if (tHash && hash.indexOf(tHash) === -1) { + return false; + } + if (tSearch && search.indexOf(tSearch) === -1) { + return false; + } + } + + /** + * As determination above, `tPart[0]` must be a valid string + */ + let pathMatch = true; + const tPath = tPart[0].split('/').filter((_) => !!_); + const lPath = pathname.split('/').filter((_) => !!_); + + tPath.forEach((p, i) => { + const lp = lPath[i]; + if (p !== lp) { + pathMatch = false; + } + }); + + return pathMatch; +}; const storageLoginRedirect = () => { const { pathname } = window.location; @@ -136,5 +177,6 @@ export const floppyNavigation = { isRoutableLink, handleRouteLinkClick, equalToCurrentHref, + matchToCurrentHref, storageLoginRedirect, }; diff --git a/ui/src/utils/guard.ts b/ui/src/utils/guard.ts index c9c34a2d..0b92f081 100644 --- a/ui/src/utils/guard.ts +++ b/ui/src/utils/guard.ts @@ -94,13 +94,15 @@ export const IGNORE_PATH_LIST = [ '/user-center/', ]; -export const isIgnoredPath = (ignoredPath: string | string[]) => { +export const isIgnoredPath = (ignoredPath?: string | string[]) => { + if (!ignoredPath) { + ignoredPath = IGNORE_PATH_LIST; + } if (!Array.isArray(ignoredPath)) { ignoredPath = [ignoredPath]; } - const { pathname } = window.location; - const matchingPath = ignoredPath.find((_) => { - return pathname.indexOf(_) !== -1; + const matchingPath = ignoredPath.find((p) => { + return floppyNavigation.matchToCurrentHref(p); }); return !!matchingPath; }; From 7662e9a5523e6283e93163a0985cfd0ee938d96e Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Wed, 31 May 2023 16:25:47 +0800 Subject: [PATCH 08/12] refactor(upload): extract uploader service interface --- internal/base/middleware/avatar.go | 4 ++-- internal/controller/upload_controller.go | 4 ++-- internal/controller/user_controller.go | 4 ++-- internal/service/uploader/upload.go | 27 +++++++++++++++--------- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/internal/base/middleware/avatar.go b/internal/base/middleware/avatar.go index d38e76c9..82e6ceab 100644 --- a/internal/base/middleware/avatar.go +++ b/internal/base/middleware/avatar.go @@ -17,12 +17,12 @@ import ( type AvatarMiddleware struct { serviceConfig *service_config.ServiceConfig - uploaderService *uploader.UploaderService + uploaderService uploader.UploaderService } // NewAvatarMiddleware new auth user middleware func NewAvatarMiddleware(serviceConfig *service_config.ServiceConfig, - uploaderService *uploader.UploaderService, + uploaderService uploader.UploaderService, ) *AvatarMiddleware { return &AvatarMiddleware{ serviceConfig: serviceConfig, diff --git a/internal/controller/upload_controller.go b/internal/controller/upload_controller.go index 9429eba5..3154cc85 100644 --- a/internal/controller/upload_controller.go +++ b/internal/controller/upload_controller.go @@ -21,11 +21,11 @@ const ( // UploadController upload controller type UploadController struct { - uploaderService *uploader.UploaderService + uploaderService uploader.UploaderService } // NewUploadController new controller -func NewUploadController(uploaderService *uploader.UploaderService) *UploadController { +func NewUploadController(uploaderService uploader.UploaderService) *UploadController { return &UploadController{ uploaderService: uploaderService, } diff --git a/internal/controller/user_controller.go b/internal/controller/user_controller.go index 88e57ca3..51fbc4ea 100644 --- a/internal/controller/user_controller.go +++ b/internal/controller/user_controller.go @@ -24,7 +24,7 @@ type UserController struct { userService *service.UserService authService *auth.AuthService actionService *action.CaptchaService - uploaderService *uploader.UploaderService + uploaderService uploader.UploaderService emailService *export.EmailService siteInfoCommonService *siteinfo_common.SiteInfoCommonService } @@ -35,7 +35,7 @@ func NewUserController( userService *service.UserService, actionService *action.CaptchaService, emailService *export.EmailService, - uploaderService *uploader.UploaderService, + uploaderService uploader.UploaderService, siteInfoCommonService *siteinfo_common.SiteInfoCommonService, ) *UserController { return &UserController{ diff --git a/internal/service/uploader/upload.go b/internal/service/uploader/upload.go index 3de12ff2..3b978521 100644 --- a/internal/service/uploader/upload.go +++ b/internal/service/uploader/upload.go @@ -51,29 +51,36 @@ var ( } ) -// UploaderService user service -type UploaderService struct { +type UploaderService interface { + UploadAvatarFile(ctx *gin.Context) (url string, err error) + AvatarThumbFile(ctx *gin.Context, uploadPath, fileName string, size int) (avatarFile []byte, err error) + UploadPostFile(ctx *gin.Context) (url string, err error) + UploadBrandingFile(ctx *gin.Context) (url string, err error) +} + +// uploaderService uploader service +type uploaderService struct { serviceConfig *service_config.ServiceConfig siteInfoService *siteinfo_common.SiteInfoCommonService } // NewUploaderService new upload service func NewUploaderService(serviceConfig *service_config.ServiceConfig, - siteInfoService *siteinfo_common.SiteInfoCommonService) *UploaderService { + siteInfoService *siteinfo_common.SiteInfoCommonService) UploaderService { for _, subPath := range subPathList { err := dir.CreateDirIfNotExist(filepath.Join(serviceConfig.UploadPath, subPath)) if err != nil { panic(err) } } - return &UploaderService{ + return &uploaderService{ serviceConfig: serviceConfig, siteInfoService: siteInfoService, } } // UploadAvatarFile upload avatar file -func (us *UploaderService) UploadAvatarFile(ctx *gin.Context) (url string, err error) { +func (us *uploaderService) UploadAvatarFile(ctx *gin.Context) (url string, err error) { url, err = us.tryToUploadByPlugin(ctx, plugin.UserAvatar) if err != nil { return "", err @@ -98,7 +105,7 @@ func (us *UploaderService) UploadAvatarFile(ctx *gin.Context) (url string, err e return us.uploadFile(ctx, file, avatarFilePath) } -func (us *UploaderService) AvatarThumbFile(ctx *gin.Context, uploadPath, fileName string, size int) ( +func (us *uploaderService) AvatarThumbFile(ctx *gin.Context, uploadPath, fileName string, size int) ( avatarfile []byte, err error) { if size > 1024 { size = 1024 @@ -151,7 +158,7 @@ func (us *UploaderService) AvatarThumbFile(ctx *gin.Context, uploadPath, fileNam return buf.Bytes(), nil } -func (us *UploaderService) UploadPostFile(ctx *gin.Context) ( +func (us *uploaderService) UploadPostFile(ctx *gin.Context) ( url string, err error) { url, err = us.tryToUploadByPlugin(ctx, plugin.UserAvatar) if err != nil { @@ -177,7 +184,7 @@ func (us *UploaderService) UploadPostFile(ctx *gin.Context) ( return us.uploadFile(ctx, file, avatarFilePath) } -func (us *UploaderService) UploadBrandingFile(ctx *gin.Context) ( +func (us *uploaderService) UploadBrandingFile(ctx *gin.Context) ( url string, err error) { url, err = us.tryToUploadByPlugin(ctx, plugin.UserAvatar) if err != nil { @@ -204,7 +211,7 @@ func (us *UploaderService) UploadBrandingFile(ctx *gin.Context) ( return us.uploadFile(ctx, file, avatarFilePath) } -func (us *UploaderService) uploadFile(ctx *gin.Context, file *multipart.FileHeader, fileSubPath string) ( +func (us *uploaderService) uploadFile(ctx *gin.Context, file *multipart.FileHeader, fileSubPath string) ( url string, err error) { siteGeneral, err := us.siteInfoService.GetSiteGeneral(ctx) if err != nil { @@ -230,7 +237,7 @@ func (us *UploaderService) uploadFile(ctx *gin.Context, file *multipart.FileHead return url, nil } -func (us *UploaderService) tryToUploadByPlugin(ctx *gin.Context, source plugin.UploadSource) ( +func (us *uploaderService) tryToUploadByPlugin(ctx *gin.Context, source plugin.UploadSource) ( url string, err error) { _ = plugin.CallStorage(func(fn plugin.Storage) error { resp := fn.UploadFile(ctx, source) From 9104b31231dc0a373f12a78712ac5729d6d51fd9 Mon Sep 17 00:00:00 2001 From: haitaoo Date: Wed, 31 May 2023 19:01:58 +0800 Subject: [PATCH 09/12] fix(InviteToAnswer): Adjusting style and interaction details --- ui/src/components/Avatar/index.tsx | 5 +- .../InviteToAnswer/PeopleDropdown.scss | 10 ++ .../InviteToAnswer/PeopleDropdown.tsx | 103 ++++++++++++++---- .../components/InviteToAnswer/index.tsx | 19 ++-- 4 files changed, 106 insertions(+), 31 deletions(-) create mode 100644 ui/src/pages/Questions/Detail/components/InviteToAnswer/PeopleDropdown.scss diff --git a/ui/src/components/Avatar/index.tsx b/ui/src/components/Avatar/index.tsx index f9d5bd6f..79d73970 100644 --- a/ui/src/components/Avatar/index.tsx +++ b/ui/src/components/Avatar/index.tsx @@ -27,12 +27,15 @@ const Index: FC = ({ avatar, size, className, searchStr = '' }) => { url = `${avatar.custom}?${searchStr}`; } + const roundedCls = + className && className.indexOf('rounded') !== -1 ? '' : 'rounded'; + return ( ); diff --git a/ui/src/pages/Questions/Detail/components/InviteToAnswer/PeopleDropdown.scss b/ui/src/pages/Questions/Detail/components/InviteToAnswer/PeopleDropdown.scss new file mode 100644 index 00000000..6ec2eaa1 --- /dev/null +++ b/ui/src/pages/Questions/Detail/components/InviteToAnswer/PeopleDropdown.scss @@ -0,0 +1,10 @@ +.people-dropdown { + .dropdown-menu { + min-width: 15rem; + } + + .dropdown-item.active { + color: #212529; + background-color: #e9ecef; + } +} diff --git a/ui/src/pages/Questions/Detail/components/InviteToAnswer/PeopleDropdown.tsx b/ui/src/pages/Questions/Detail/components/InviteToAnswer/PeopleDropdown.tsx index 2eb06ccf..b4ed12a7 100644 --- a/ui/src/pages/Questions/Detail/components/InviteToAnswer/PeopleDropdown.tsx +++ b/ui/src/pages/Questions/Detail/components/InviteToAnswer/PeopleDropdown.tsx @@ -1,4 +1,4 @@ -import { FC, memo, useEffect, useState } from 'react'; +import { FC, useEffect, useState } from 'react'; import { Dropdown, Form } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; @@ -6,21 +6,31 @@ import { loggedUserInfoStore } from '@/stores'; import { userSearchByName } from '@/services'; import { Avatar } from '@/components'; import * as Type from '@/common/interface'; +import './PeopleDropdown.scss'; interface Props { selectedPeople: Type.UserInfoBase[] | undefined; onSelect: (people: Type.UserInfoBase) => void; + visible?: boolean; } -const Index: FC = ({ selectedPeople = [], onSelect }) => { +const Index: FC = ({ + selectedPeople = [], + visible = false, + onSelect, +}) => { const { user: currentUser } = loggedUserInfoStore(); const { t } = useTranslation('translation', { keyPrefix: 'invite_to_answer', }); const [toggleState, setToggleState] = useState(false); const [peopleList, setPeopleList] = useState([]); - + const [currentIndex, setCurrentIndex] = useState(0); + const [searchValue, setSearchValue] = useState(''); const filterAndSetPeople = (source) => { + if (!toggleState) { + return; + } const filteredPeople: Type.UserInfoBase[] = []; source.forEach((p) => { if (currentUser && currentUser.username === p.username) { @@ -34,56 +44,109 @@ const Index: FC = ({ selectedPeople = [], onSelect }) => { setPeopleList(filteredPeople); }; - const searchPeople = (evt) => { - const name = evt.target.value; - if (!name) { + const searchPeople = (s) => { + if (!s) { + setPeopleList([]); return; } - userSearchByName(name).then((resp) => { + userSearchByName(s).then((resp) => { filterAndSetPeople(resp); }); }; + const handleSearch = (evt) => { + const s = evt.target.value; + setSearchValue(s); + searchPeople(s); + }; + + const updateCurrentIndex = (pl: number) => { + let curIndex = currentIndex; + if (currentIndex >= pl) { + curIndex = Math.max(pl - 1, 0); + } + setCurrentIndex(curIndex); + }; const handleSelect = (idx) => { + if (idx < 0 || idx >= peopleList.length) { + return; + } const people = peopleList[idx]; if (people) { + updateCurrentIndex(peopleList.length - 1); onSelect(people); } }; + const handleKeyDown = (evt) => { + evt.stopPropagation(); + + if (!peopleList?.length) { + return; + } + const { keyCode } = evt; + if (keyCode === 38 && currentIndex > 0) { + setCurrentIndex(currentIndex - 1); + } + if (keyCode === 40 && currentIndex < peopleList.length - 1) { + setCurrentIndex(currentIndex + 1); + } + + if (keyCode === 13 && currentIndex > -1) { + evt.preventDefault(); + handleSelect(currentIndex); + } + }; + useEffect(() => { filterAndSetPeople(peopleList); }, [selectedPeople]); - return ( + useEffect(() => { + searchPeople(searchValue); + }, [toggleState]); + + useEffect(() => { + if (!visible && toggleState) { + setToggleState(false); + } + }, [visible]); + + return visible ? ( - {t('add')} + + + + {t('add')} - + - + {toggleState ? ( + + ) : null} {peopleList.map((p, idx) => { return (

- + {p.display_name} @{p.username}
@@ -92,7 +155,7 @@ const Index: FC = ({ selectedPeople = [], onSelect }) => { })} - ); + ) : null; }; -export default memo(Index); +export default Index; diff --git a/ui/src/pages/Questions/Detail/components/InviteToAnswer/index.tsx b/ui/src/pages/Questions/Detail/components/InviteToAnswer/index.tsx index 4632ccbc..b139e271 100644 --- a/ui/src/pages/Questions/Detail/components/InviteToAnswer/index.tsx +++ b/ui/src/pages/Questions/Detail/components/InviteToAnswer/index.tsx @@ -113,9 +113,9 @@ const Index: FC = ({ questionId, readOnly = false }) => { {user.display_name} {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */} removeInviteUser(user)}> - x + × ); @@ -125,17 +125,16 @@ const Index: FC = ({ questionId, readOnly = false }) => { key={user.username} to={`/users/${user.username}`} className="mx-2 my-1 d-inline-flex flex-nowrap"> - - {user.display_name} + + {user.display_name} ); })} - {showAddButton ? ( - - ) : null} +
{showInviteDesc ? ( <> From edba84c47ebf969bfa7f9cf89f8c84f66687a402 Mon Sep 17 00:00:00 2001 From: haitaoo Date: Wed, 31 May 2023 19:25:27 +0800 Subject: [PATCH 10/12] fix(InviteToAnswer): Hide components when not necessary --- .../Detail/components/InviteToAnswer/index.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/ui/src/pages/Questions/Detail/components/InviteToAnswer/index.tsx b/ui/src/pages/Questions/Detail/components/InviteToAnswer/index.tsx index b139e271..9604b1c4 100644 --- a/ui/src/pages/Questions/Detail/components/InviteToAnswer/index.tsx +++ b/ui/src/pages/Questions/Detail/components/InviteToAnswer/index.tsx @@ -73,9 +73,15 @@ const Index: FC = ({ questionId, readOnly = false }) => { }, [questionId]); const showAddButton = editing && (!users || users.length < MAX_ASK_NUMBER); - const showInviteDesc = !editing && users?.length === 0; + const showInviteFeat = !editing && users?.length === 0; + const showInviteButton = showInviteFeat && !readOnly; const showEditButton = !readOnly && !editing && users?.length; const showSaveButton = !readOnly && editing; + const showEmpty = readOnly && users?.length === 0; + + if (showEmpty) { + return null; + } return ( @@ -136,10 +142,10 @@ const Index: FC = ({ questionId, readOnly = false }) => { onSelect={updateInviteUsers} />

- {showInviteDesc ? ( + {showInviteFeat ? ( <>
{t('desc')}
- {readOnly ? null : ( + {showInviteButton ? ( - )} + ) : null} ) : null} From ae90d4b8ad6328d467523748d7a59d7ad5d7d761 Mon Sep 17 00:00:00 2001 From: haitaoo Date: Wed, 31 May 2023 19:48:50 +0800 Subject: [PATCH 11/12] fix(QuestionList): Make the Tag details page remember how to sort the list of questions too --- ui/src/components/QuestionList/index.tsx | 13 ++++++++++++- ui/src/pages/Tags/Detail/index.tsx | 4 ++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ui/src/components/QuestionList/index.tsx b/ui/src/components/QuestionList/index.tsx index 0911a945..bbf07612 100644 --- a/ui/src/components/QuestionList/index.tsx +++ b/ui/src/components/QuestionList/index.tsx @@ -17,6 +17,8 @@ import { Icon, } from '@/components'; import * as Type from '@/common/interface'; +import { Storage } from '@/utils'; +import { QUESTIONS_ORDER_STORAGE_KEY } from '@/common/constants'; export const QUESTION_ORDER_KEYS: Type.QuestionOrderBy[] = [ 'active', @@ -40,8 +42,17 @@ const QuestionList: FC = ({ }) => { const { t } = useTranslation('translation', { keyPrefix: 'question' }); const [urlSearchParams] = useSearchParams(); + + const storageOrder = Storage.get(QUESTIONS_ORDER_STORAGE_KEY); const curOrder = - order || urlSearchParams.get('order') || QUESTION_ORDER_KEYS[0]; + order || + urlSearchParams.get('order') || + storageOrder || + QUESTION_ORDER_KEYS[0]; + if (curOrder !== storageOrder) { + Storage.set(QUESTIONS_ORDER_STORAGE_KEY, curOrder); + } + const curPage = Number(urlSearchParams.get('page')) || 1; const pageSize = 20; const count = data?.count || 0; diff --git a/ui/src/pages/Tags/Detail/index.tsx b/ui/src/pages/Tags/Detail/index.tsx index 3144bc7b..ffd5341e 100644 --- a/ui/src/pages/Tags/Detail/index.tsx +++ b/ui/src/pages/Tags/Detail/index.tsx @@ -22,7 +22,7 @@ import HotQuestions from '@/components/HotQuestions'; import { escapeRemove, guard } from '@/utils'; import { pathFactory } from '@/router/pathFactory'; -const Questions: FC = () => { +const Index: FC = () => { const { t } = useTranslation('translation', { keyPrefix: 'tags' }); const navigate = useNavigate(); const routeParams = useParams(); @@ -159,4 +159,4 @@ const Questions: FC = () => { ); }; -export default Questions; +export default Index; From b191b9e471f87755859b0718d0ff153a67268bec Mon Sep 17 00:00:00 2001 From: haitaoo Date: Wed, 31 May 2023 20:46:21 +0800 Subject: [PATCH 12/12] fix(QuestionList): Make the Tag details page remember how to sort the list of questions too --- ui/src/components/QuestionList/index.tsx | 13 +------------ ui/src/pages/Tags/Detail/index.tsx | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ui/src/components/QuestionList/index.tsx b/ui/src/components/QuestionList/index.tsx index bbf07612..0911a945 100644 --- a/ui/src/components/QuestionList/index.tsx +++ b/ui/src/components/QuestionList/index.tsx @@ -17,8 +17,6 @@ import { Icon, } from '@/components'; import * as Type from '@/common/interface'; -import { Storage } from '@/utils'; -import { QUESTIONS_ORDER_STORAGE_KEY } from '@/common/constants'; export const QUESTION_ORDER_KEYS: Type.QuestionOrderBy[] = [ 'active', @@ -42,17 +40,8 @@ const QuestionList: FC = ({ }) => { const { t } = useTranslation('translation', { keyPrefix: 'question' }); const [urlSearchParams] = useSearchParams(); - - const storageOrder = Storage.get(QUESTIONS_ORDER_STORAGE_KEY); const curOrder = - order || - urlSearchParams.get('order') || - storageOrder || - QUESTION_ORDER_KEYS[0]; - if (curOrder !== storageOrder) { - Storage.set(QUESTIONS_ORDER_STORAGE_KEY, curOrder); - } - + order || urlSearchParams.get('order') || QUESTION_ORDER_KEYS[0]; const curPage = Number(urlSearchParams.get('page')) || 1; const pageSize = 20; const count = data?.count || 0; diff --git a/ui/src/pages/Tags/Detail/index.tsx b/ui/src/pages/Tags/Detail/index.tsx index ffd5341e..5932f5c5 100644 --- a/ui/src/pages/Tags/Detail/index.tsx +++ b/ui/src/pages/Tags/Detail/index.tsx @@ -17,10 +17,11 @@ import { useQuerySynonymsTags, useQuestionList, } from '@/services'; -import QuestionList from '@/components/QuestionList'; +import QuestionList, { QUESTION_ORDER_KEYS } from '@/components/QuestionList'; import HotQuestions from '@/components/HotQuestions'; -import { escapeRemove, guard } from '@/utils'; +import { escapeRemove, guard, Storage } from '@/utils'; import { pathFactory } from '@/router/pathFactory'; +import { QUESTIONS_ORDER_STORAGE_KEY } from '@/common/constants'; const Index: FC = () => { const { t } = useTranslation('translation', { keyPrefix: 'tags' }); @@ -28,7 +29,12 @@ const Index: FC = () => { const routeParams = useParams(); const curTagName = routeParams.tagName || ''; const [urlSearchParams] = useSearchParams(); - const curOrder = urlSearchParams.get('order') || 'active'; + const storageOrder = Storage.get(QUESTIONS_ORDER_STORAGE_KEY); + const curOrder = + urlSearchParams.get('order') || storageOrder || QUESTION_ORDER_KEYS[0]; + if (curOrder !== storageOrder) { + Storage.set(QUESTIONS_ORDER_STORAGE_KEY, curOrder); + } const curPage = Number(urlSearchParams.get('page')) || 1; const reqParams: Type.QueryQuestionsReq = { page_size: 20, @@ -148,7 +154,12 @@ const Index: FC = () => {
)} - +