From dc757d561aa96ca5cf0bca944637fe1ec97c9bda Mon Sep 17 00:00:00 2001
From: sy-records <52o@qq52o.cn>
Date: Tue, 26 Mar 2024 18:06:16 +0800
Subject: [PATCH 1/7] fix: Account setting label issue
---
i18n/af_ZA.yaml | 4 +++-
i18n/ar_SA.yaml | 4 +++-
i18n/az_AZ.yaml | 4 +++-
i18n/bal_BA.yaml | 4 +++-
i18n/ban_ID.yaml | 4 +++-
i18n/bn_BD.yaml | 4 +++-
i18n/bs_BA.yaml | 4 +++-
i18n/ca_ES.yaml | 4 +++-
i18n/cs_CZ.yaml | 2 ++
i18n/cy_GB.yaml | 2 ++
i18n/da_DK.yaml | 2 ++
i18n/de_DE.yaml | 2 ++
i18n/el_GR.yaml | 4 +++-
i18n/en_US.yaml | 2 ++
i18n/es_ES.yaml | 2 ++
i18n/fi_FI.yaml | 4 +++-
i18n/he_IL.yaml | 4 +++-
i18n/hi_IN.yaml | 2 ++
i18n/hu_HU.yaml | 4 +++-
i18n/hy_AM.yaml | 4 +++-
i18n/id_ID.yaml | 2 ++
i18n/ja_JP.yaml | 2 ++
i18n/ko_KR.yaml | 2 ++
i18n/nl_NL.yaml | 4 +++-
i18n/no_NO.yaml | 4 +++-
i18n/sk_SK.yaml | 2 ++
i18n/sq_AL.yaml | 4 +++-
i18n/sr_SP.yaml | 4 +++-
i18n/te_IN.yaml | 2 ++
i18n/tr_TR.yaml | 2 ++
i18n/vi_VN.yaml | 2 ++
i18n/zh_CN.yaml | 2 ++
i18n/zh_TW.yaml | 2 ++
.../Users/Settings/Account/components/ModifyEmail/index.tsx | 4 ++--
34 files changed, 85 insertions(+), 19 deletions(-)
diff --git a/i18n/af_ZA.yaml b/i18n/af_ZA.yaml
index 89c34f733..71162ef52 100644
--- a/i18n/af_ZA.yaml
+++ b/i18n/af_ZA.yaml
@@ -715,7 +715,9 @@ ui:
We've sent an email to that address. Please follow the confirmation instructions.
email:
label: Email
- msg: Email cannot be empty.
+ new_email:
+ label: New email
+ msg: New email cannot be empty.
password_title: Password
current_pass:
label: Current Password
diff --git a/i18n/ar_SA.yaml b/i18n/ar_SA.yaml
index 28941e9e9..2f686840a 100644
--- a/i18n/ar_SA.yaml
+++ b/i18n/ar_SA.yaml
@@ -715,7 +715,9 @@ ui:
We've sent an email to that address. Please follow the confirmation instructions.
email:
label: Email
- msg: Email cannot be empty.
+ new_email:
+ label: New email
+ msg: New email cannot be empty.
password_title: Password
current_pass:
label: Current Password
diff --git a/i18n/az_AZ.yaml b/i18n/az_AZ.yaml
index 483744f70..4270506ad 100644
--- a/i18n/az_AZ.yaml
+++ b/i18n/az_AZ.yaml
@@ -708,7 +708,9 @@ ui:
We've sent an email to that address. Please follow the confirmation instructions.
email:
label: Email
- msg: Email cannot be empty.
+ new_email:
+ label: New email
+ msg: New email cannot be empty.
password_title: Password
current_pass:
label: Current Password
diff --git a/i18n/bal_BA.yaml b/i18n/bal_BA.yaml
index 483744f70..4270506ad 100644
--- a/i18n/bal_BA.yaml
+++ b/i18n/bal_BA.yaml
@@ -708,7 +708,9 @@ ui:
We've sent an email to that address. Please follow the confirmation instructions.
email:
label: Email
- msg: Email cannot be empty.
+ new_email:
+ label: New email
+ msg: New email cannot be empty.
password_title: Password
current_pass:
label: Current Password
diff --git a/i18n/ban_ID.yaml b/i18n/ban_ID.yaml
index 483744f70..4270506ad 100644
--- a/i18n/ban_ID.yaml
+++ b/i18n/ban_ID.yaml
@@ -708,7 +708,9 @@ ui:
We've sent an email to that address. Please follow the confirmation instructions.
email:
label: Email
- msg: Email cannot be empty.
+ new_email:
+ label: New email
+ msg: New email cannot be empty.
password_title: Password
current_pass:
label: Current Password
diff --git a/i18n/bn_BD.yaml b/i18n/bn_BD.yaml
index 483744f70..4270506ad 100644
--- a/i18n/bn_BD.yaml
+++ b/i18n/bn_BD.yaml
@@ -708,7 +708,9 @@ ui:
We've sent an email to that address. Please follow the confirmation instructions.
email:
label: Email
- msg: Email cannot be empty.
+ new_email:
+ label: New email
+ msg: New email cannot be empty.
password_title: Password
current_pass:
label: Current Password
diff --git a/i18n/bs_BA.yaml b/i18n/bs_BA.yaml
index 483744f70..4270506ad 100644
--- a/i18n/bs_BA.yaml
+++ b/i18n/bs_BA.yaml
@@ -708,7 +708,9 @@ ui:
We've sent an email to that address. Please follow the confirmation instructions.
email:
label: Email
- msg: Email cannot be empty.
+ new_email:
+ label: New email
+ msg: New email cannot be empty.
password_title: Password
current_pass:
label: Current Password
diff --git a/i18n/ca_ES.yaml b/i18n/ca_ES.yaml
index 28941e9e9..2f686840a 100644
--- a/i18n/ca_ES.yaml
+++ b/i18n/ca_ES.yaml
@@ -715,7 +715,9 @@ ui:
We've sent an email to that address. Please follow the confirmation instructions.
email:
label: Email
- msg: Email cannot be empty.
+ new_email:
+ label: New email
+ msg: New email cannot be empty.
password_title: Password
current_pass:
label: Current Password
diff --git a/i18n/cs_CZ.yaml b/i18n/cs_CZ.yaml
index e71eeda48..3e5bc495f 100644
--- a/i18n/cs_CZ.yaml
+++ b/i18n/cs_CZ.yaml
@@ -999,6 +999,8 @@ ui:
change_email_info: >-
We've sent an email to that address. Please follow the confirmation instructions.
email:
+ label: Email
+ new_email:
label: New email
msg: New email cannot be empty.
pass:
diff --git a/i18n/cy_GB.yaml b/i18n/cy_GB.yaml
index a1c14f5f9..8825ee2b7 100644
--- a/i18n/cy_GB.yaml
+++ b/i18n/cy_GB.yaml
@@ -999,6 +999,8 @@ ui:
change_email_info: >-
We've sent an email to that address. Please follow the confirmation instructions.
email:
+ label: Email
+ new_email:
label: New email
msg: New email cannot be empty.
pass:
diff --git a/i18n/da_DK.yaml b/i18n/da_DK.yaml
index 0d48719dc..88110a3e7 100644
--- a/i18n/da_DK.yaml
+++ b/i18n/da_DK.yaml
@@ -999,6 +999,8 @@ ui:
change_email_info: >-
We've sent an email to that address. Please follow the confirmation instructions.
email:
+ label: Email
+ new_email:
label: New email
msg: New email cannot be empty.
pass:
diff --git a/i18n/de_DE.yaml b/i18n/de_DE.yaml
index 7ac93bfd7..ed6b2ef2c 100644
--- a/i18n/de_DE.yaml
+++ b/i18n/de_DE.yaml
@@ -999,6 +999,8 @@ ui:
change_email_info: >-
Wir haben eine E-Mail an diese Adresse geschickt. Bitte befolge die Anweisungen zur Bestätigung.
email:
+ label: E-Mail
+ new_email:
label: Neue E-Mail
msg: Neue E-Mail darf nicht leer sein.
pass:
diff --git a/i18n/el_GR.yaml b/i18n/el_GR.yaml
index 28941e9e9..2f686840a 100644
--- a/i18n/el_GR.yaml
+++ b/i18n/el_GR.yaml
@@ -715,7 +715,9 @@ ui:
We've sent an email to that address. Please follow the confirmation instructions.
email:
label: Email
- msg: Email cannot be empty.
+ new_email:
+ label: New email
+ msg: New email cannot be empty.
password_title: Password
current_pass:
label: Current Password
diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml
index 943608dd6..0221cfa02 100644
--- a/i18n/en_US.yaml
+++ b/i18n/en_US.yaml
@@ -1029,6 +1029,8 @@ ui:
We've sent an email to that address. Please follow the confirmation
instructions.
email:
+ label: Email
+ new_email:
label: New email
msg: New email cannot be empty.
pass:
diff --git a/i18n/es_ES.yaml b/i18n/es_ES.yaml
index 2ffbe74c5..272b22082 100644
--- a/i18n/es_ES.yaml
+++ b/i18n/es_ES.yaml
@@ -999,6 +999,8 @@ ui:
change_email_info: >-
Te hemos enviado un email a esa dirección. Por favor sigue las instrucciones de confirmación.
email:
+ label: Correo
+ new_email:
label: Nuevo correo
msg: El nuevo correo no puede estar vacío.
pass:
diff --git a/i18n/fi_FI.yaml b/i18n/fi_FI.yaml
index 28941e9e9..2f686840a 100644
--- a/i18n/fi_FI.yaml
+++ b/i18n/fi_FI.yaml
@@ -715,7 +715,9 @@ ui:
We've sent an email to that address. Please follow the confirmation instructions.
email:
label: Email
- msg: Email cannot be empty.
+ new_email:
+ label: New email
+ msg: New email cannot be empty.
password_title: Password
current_pass:
label: Current Password
diff --git a/i18n/he_IL.yaml b/i18n/he_IL.yaml
index 28941e9e9..2f686840a 100644
--- a/i18n/he_IL.yaml
+++ b/i18n/he_IL.yaml
@@ -715,7 +715,9 @@ ui:
We've sent an email to that address. Please follow the confirmation instructions.
email:
label: Email
- msg: Email cannot be empty.
+ new_email:
+ label: New email
+ msg: New email cannot be empty.
password_title: Password
current_pass:
label: Current Password
diff --git a/i18n/hi_IN.yaml b/i18n/hi_IN.yaml
index 0d48719dc..88110a3e7 100644
--- a/i18n/hi_IN.yaml
+++ b/i18n/hi_IN.yaml
@@ -999,6 +999,8 @@ ui:
change_email_info: >-
We've sent an email to that address. Please follow the confirmation instructions.
email:
+ label: Email
+ new_email:
label: New email
msg: New email cannot be empty.
pass:
diff --git a/i18n/hu_HU.yaml b/i18n/hu_HU.yaml
index 28941e9e9..2f686840a 100644
--- a/i18n/hu_HU.yaml
+++ b/i18n/hu_HU.yaml
@@ -715,7 +715,9 @@ ui:
We've sent an email to that address. Please follow the confirmation instructions.
email:
label: Email
- msg: Email cannot be empty.
+ new_email:
+ label: New email
+ msg: New email cannot be empty.
password_title: Password
current_pass:
label: Current Password
diff --git a/i18n/hy_AM.yaml b/i18n/hy_AM.yaml
index 483744f70..4270506ad 100644
--- a/i18n/hy_AM.yaml
+++ b/i18n/hy_AM.yaml
@@ -708,7 +708,9 @@ ui:
We've sent an email to that address. Please follow the confirmation instructions.
email:
label: Email
- msg: Email cannot be empty.
+ new_email:
+ label: New email
+ msg: New email cannot be empty.
password_title: Password
current_pass:
label: Current Password
diff --git a/i18n/id_ID.yaml b/i18n/id_ID.yaml
index de2f95c43..e9277a5e4 100644
--- a/i18n/id_ID.yaml
+++ b/i18n/id_ID.yaml
@@ -999,6 +999,8 @@ ui:
change_email_info: >-
We've sent an email to that address. Please follow the confirmation instructions.
email:
+ label: Email
+ new_email:
label: New email
msg: New email cannot be empty.
pass:
diff --git a/i18n/ja_JP.yaml b/i18n/ja_JP.yaml
index d1452052f..bfb9b0324 100644
--- a/i18n/ja_JP.yaml
+++ b/i18n/ja_JP.yaml
@@ -999,6 +999,8 @@ ui:
change_email_info: >-
We've sent an email to that address. Please follow the confirmation instructions.
email:
+ label: Email
+ new_email:
label: New email
msg: New email cannot be empty.
pass:
diff --git a/i18n/ko_KR.yaml b/i18n/ko_KR.yaml
index 1e584537d..f0bc42c81 100644
--- a/i18n/ko_KR.yaml
+++ b/i18n/ko_KR.yaml
@@ -999,6 +999,8 @@ ui:
change_email_info: >-
We've sent an email to that address. Please follow the confirmation instructions.
email:
+ label: Email
+ new_email:
label: New email
msg: New email cannot be empty.
pass:
diff --git a/i18n/nl_NL.yaml b/i18n/nl_NL.yaml
index 28941e9e9..2f686840a 100644
--- a/i18n/nl_NL.yaml
+++ b/i18n/nl_NL.yaml
@@ -715,7 +715,9 @@ ui:
We've sent an email to that address. Please follow the confirmation instructions.
email:
label: Email
- msg: Email cannot be empty.
+ new_email:
+ label: New email
+ msg: New email cannot be empty.
password_title: Password
current_pass:
label: Current Password
diff --git a/i18n/no_NO.yaml b/i18n/no_NO.yaml
index 28941e9e9..2f686840a 100644
--- a/i18n/no_NO.yaml
+++ b/i18n/no_NO.yaml
@@ -715,7 +715,9 @@ ui:
We've sent an email to that address. Please follow the confirmation instructions.
email:
label: Email
- msg: Email cannot be empty.
+ new_email:
+ label: New email
+ msg: New email cannot be empty.
password_title: Password
current_pass:
label: Current Password
diff --git a/i18n/sk_SK.yaml b/i18n/sk_SK.yaml
index 7daf62b06..f3fa8dd41 100644
--- a/i18n/sk_SK.yaml
+++ b/i18n/sk_SK.yaml
@@ -999,6 +999,8 @@ ui:
change_email_info: >-
Na túto adresu sme poslali e-mail. Postupujte podľa pokynov na potvrdenie.
email:
+ label: Email
+ new_email:
label: New email
msg: New email cannot be empty.
pass:
diff --git a/i18n/sq_AL.yaml b/i18n/sq_AL.yaml
index 483744f70..4270506ad 100644
--- a/i18n/sq_AL.yaml
+++ b/i18n/sq_AL.yaml
@@ -708,7 +708,9 @@ ui:
We've sent an email to that address. Please follow the confirmation instructions.
email:
label: Email
- msg: Email cannot be empty.
+ new_email:
+ label: New email
+ msg: New email cannot be empty.
password_title: Password
current_pass:
label: Current Password
diff --git a/i18n/sr_SP.yaml b/i18n/sr_SP.yaml
index 28941e9e9..2f686840a 100644
--- a/i18n/sr_SP.yaml
+++ b/i18n/sr_SP.yaml
@@ -715,7 +715,9 @@ ui:
We've sent an email to that address. Please follow the confirmation instructions.
email:
label: Email
- msg: Email cannot be empty.
+ new_email:
+ label: New email
+ msg: New email cannot be empty.
password_title: Password
current_pass:
label: Current Password
diff --git a/i18n/te_IN.yaml b/i18n/te_IN.yaml
index dc22340cd..8be7a385b 100644
--- a/i18n/te_IN.yaml
+++ b/i18n/te_IN.yaml
@@ -999,6 +999,8 @@ ui:
change_email_info: >-
We've sent an email to that address. Please follow the confirmation instructions.
email:
+ label: Email
+ new_email:
label: New email
msg: New email cannot be empty.
pass:
diff --git a/i18n/tr_TR.yaml b/i18n/tr_TR.yaml
index 0460fda5c..c9cfe1c9c 100644
--- a/i18n/tr_TR.yaml
+++ b/i18n/tr_TR.yaml
@@ -999,6 +999,8 @@ ui:
change_email_info: >-
We've sent an email to that address. Please follow the confirmation instructions.
email:
+ label: Email
+ new_email:
label: New email
msg: New email cannot be empty.
pass:
diff --git a/i18n/vi_VN.yaml b/i18n/vi_VN.yaml
index d28041ce4..acf8084cd 100644
--- a/i18n/vi_VN.yaml
+++ b/i18n/vi_VN.yaml
@@ -999,6 +999,8 @@ ui:
change_email_info: >-
We've sent an email to that address. Please follow the confirmation instructions.
email:
+ label: Email
+ new_email:
label: New email
msg: New email cannot be empty.
pass:
diff --git a/i18n/zh_CN.yaml b/i18n/zh_CN.yaml
index f4d142ca4..0a6258d6c 100644
--- a/i18n/zh_CN.yaml
+++ b/i18n/zh_CN.yaml
@@ -999,6 +999,8 @@ ui:
change_email_info: >-
邮件已发送。请根据指引完成验证。
email:
+ label: 电子邮件地址
+ new_email:
label: 新的电子邮件地址
msg: 新邮箱不能为空。
pass:
diff --git a/i18n/zh_TW.yaml b/i18n/zh_TW.yaml
index c0377af7c..4b5cce48e 100644
--- a/i18n/zh_TW.yaml
+++ b/i18n/zh_TW.yaml
@@ -999,6 +999,8 @@ ui:
change_email_info: >-
我們已經寄出一封郵件至此電子郵件地址,請遵照說明進行確認。
email:
+ label: Email
+ new_email:
label: New email
msg: New email cannot be empty.
pass:
diff --git a/ui/src/pages/Users/Settings/Account/components/ModifyEmail/index.tsx b/ui/src/pages/Users/Settings/Account/components/ModifyEmail/index.tsx
index 5672782b4..ac42f078a 100644
--- a/ui/src/pages/Users/Settings/Account/components/ModifyEmail/index.tsx
+++ b/ui/src/pages/Users/Settings/Account/components/ModifyEmail/index.tsx
@@ -66,7 +66,7 @@ const Index: FC = () => {
formData.e_mail = {
value: '',
isInvalid: true,
- errorMsg: t('email.msg'),
+ errorMsg: t('new_email.msg'),
};
}
@@ -194,7 +194,7 @@ const Index: FC = () => {
- {t('email.label')}
+ {t('new_email.label')}
Date: Fri, 5 Apr 2024 23:13:56 +0530
Subject: [PATCH 2/7] fix bug where no error message is showing when comment
length exceeds max length when updating comment
---
ui/src/components/Comment/index.tsx | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/ui/src/components/Comment/index.tsx b/ui/src/components/Comment/index.tsx
index 81345e7e7..06ecfc26f 100644
--- a/ui/src/components/Comment/index.tsx
+++ b/ui/src/components/Comment/index.tsx
@@ -178,8 +178,13 @@ const Comment = ({ objectId, mode, commentId }) => {
})
.catch((err) => {
if (err.isError) {
- editCaptcha.handleCaptchaError(err.list);
+ const captchaErr = editCaptcha.handleCaptchaError(err.list);
+ // If it is not a CAPTCHA error, leave it to the subsequent error handling logic to continue processing.
+ if (!(captchaErr && err.list.length === 1)) {
+ return Promise.reject(err);
+ }
}
+ return Promise.resolve();
});
});
}
From 9853a60c6aa7221497798c67fc7d95bf354861f3 Mon Sep 17 00:00:00 2001
From: robin
Date: Sun, 7 Apr 2024 18:01:13 +0800
Subject: [PATCH 3/7] refactor(ui): Refactor flex layout
---
ui/src/components/SideNav/index.scss | 1 -
ui/src/pages/Layout/index.tsx | 2 +-
ui/src/pages/SideNavLayout/index.tsx | 4 ++--
3 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/ui/src/components/SideNav/index.scss b/ui/src/components/SideNav/index.scss
index d8bcc8f11..0cf6201bc 100644
--- a/ui/src/components/SideNav/index.scss
+++ b/ui/src/components/SideNav/index.scss
@@ -48,7 +48,6 @@
width: 1px;
height: 100%;
background-color: var(--bs-border-color);
- min-height: calc(100vh - 62px - 74px);
}
}
diff --git a/ui/src/pages/Layout/index.tsx b/ui/src/pages/Layout/index.tsx
index f0a061bd0..cf7a2f1ba 100644
--- a/ui/src/pages/Layout/index.tsx
+++ b/ui/src/pages/Layout/index.tsx
@@ -75,7 +75,7 @@ const Layout: FC = () => {
}}>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}
-
+
{httpStatusCode ? (
) : (
diff --git a/ui/src/pages/SideNavLayout/index.tsx b/ui/src/pages/SideNavLayout/index.tsx
index 268401580..773d572c3 100644
--- a/ui/src/pages/SideNavLayout/index.tsx
+++ b/ui/src/pages/SideNavLayout/index.tsx
@@ -27,8 +27,8 @@ import '@/common/sideNavLayout.scss';
const Index: FC = () => {
return (
-
-
+
+
From d91cb2e60f6cdc5520e530ad5f21f363c089a748 Mon Sep 17 00:00:00 2001
From: shuai
Date: Mon, 8 Apr 2024 11:32:30 +0800
Subject: [PATCH 4/7] fix:fix answer draft dispaly error
---
.../Detail/components/WriteAnswer/index.tsx | 22 ++++++++++++-------
1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/ui/src/pages/Questions/Detail/components/WriteAnswer/index.tsx b/ui/src/pages/Questions/Detail/components/WriteAnswer/index.tsx
index 9bc8be448..24a301f8c 100644
--- a/ui/src/pages/Questions/Detail/components/WriteAnswer/index.tsx
+++ b/ui/src/pages/Questions/Detail/components/WriteAnswer/index.tsx
@@ -65,6 +65,7 @@ const Index: FC = ({ visible = false, data, callback }) => {
const [showTips, setShowTips] = useState(data.loggedUserRank < 100);
const aCaptcha = useCaptchaModal('answer');
const writeInfo = writeSettingStore((state) => state.write);
+ const [editorCanSave, setEditorCanSave] = useState(false);
usePromptWithUnload({
when: Boolean(formData.content.value),
@@ -80,6 +81,7 @@ const Index: FC = ({ visible = false, data, callback }) => {
useEffect(() => {
const draft = storageExpires.get(DRAFT_ANSWER_STORAGE_KEY);
if (draft?.questionId === data.qid && draft?.content) {
+ setShowEditor(true);
setFormData({
content: {
value: draft.content,
@@ -87,9 +89,11 @@ const Index: FC = ({ visible = false, data, callback }) => {
errorMsg: '',
},
});
- setShowEditor(true);
setHasDraft(true);
}
+ setTimeout(() => {
+ setEditorCanSave(true);
+ }, 100);
}, []);
useEffect(() => {
@@ -260,13 +264,15 @@ const Index: FC = ({ visible = false, data, callback }) => {
value={formData.content.value}
autoFocus={editorFocusState}
onChange={(val) => {
- setFormData({
- content: {
- value: val,
- isInvalid: false,
- errorMsg: '',
- },
- });
+ if (editorCanSave) {
+ setFormData({
+ content: {
+ value: val,
+ isInvalid: false,
+ errorMsg: '',
+ },
+ });
+ }
}}
onFocus={() => {
setFocusType('answer');
From 877cfe81277aa69d1bc472321445e54b3977b1fd Mon Sep 17 00:00:00 2001
From: shuai
Date: Tue, 9 Apr 2024 10:15:22 +0800
Subject: [PATCH 5/7] feat: use captcha plugin replace captcha modal component
---
.vscode/settings.json | 5 +-
i18n/af_ZA.yaml | 2 +-
i18n/ar_SA.yaml | 2 +-
i18n/az_AZ.yaml | 2 +-
i18n/bal_BA.yaml | 2 +-
i18n/ban_ID.yaml | 2 +-
i18n/bn_BD.yaml | 2 +-
i18n/bs_BA.yaml | 2 +-
i18n/ca_ES.yaml | 2 +-
i18n/cs_CZ.yaml | 2 +-
i18n/cy_GB.yaml | 2 +-
i18n/da_DK.yaml | 2 +-
i18n/el_GR.yaml | 2 +-
i18n/en_US.yaml | 2 +-
i18n/fi_FI.yaml | 2 +-
i18n/he_IL.yaml | 2 +-
i18n/hi_IN.yaml | 2 +-
i18n/hu_HU.yaml | 2 +-
i18n/hy_AM.yaml | 2 +-
i18n/id_ID.yaml | 2 +-
i18n/ja_JP.yaml | 8 +-
i18n/ko_KR.yaml | 2 +-
i18n/nl_NL.yaml | 2 +-
i18n/no_NO.yaml | 2 +-
i18n/sq_AL.yaml | 2 +-
i18n/sr_SP.yaml | 2 +-
i18n/sv_SE.yaml | 2 +-
i18n/te_IN.yaml | 2 +-
i18n/tr_TR.yaml | 2 +-
i18n/uk_UA.yaml | 2 +-
i18n/vi_VN.yaml | 2 +-
ui/src/components/Actions/index.tsx | 80 ++---
ui/src/components/Comment/index.tsx | 274 ++++++++++--------
ui/src/components/Operate/index.tsx | 107 ++++---
ui/src/components/PluginRender/index.tsx | 3 +-
ui/src/components/Unactivate/index.tsx | 16 +-
ui/src/hooks/useReportModal/index.tsx | 63 ++--
ui/src/pages/Questions/Ask/index.tsx | 159 +++++-----
.../components/InviteToAnswer/index.tsx | 47 +--
.../Detail/components/WriteAnswer/index.tsx | 78 ++---
ui/src/pages/Questions/EditAnswer/index.tsx | 76 ++---
.../components/ApproveDropdown/index.tsx | 89 +++---
.../Review/components/EditPostModal/index.tsx | 69 +++--
ui/src/pages/Search/index.tsx | 15 +-
.../AccountForgot/components/sendEmail.tsx | 17 +-
.../ChangeEmail/components/sendEmail.tsx | 17 +-
ui/src/pages/Users/Login/index.tsx | 29 +-
.../Register/components/SignUpForm/index.tsx | 14 +-
.../Account/components/ModifyEmail/index.tsx | 18 +-
.../Account/components/ModifyPass/index.tsx | 17 +-
.../builtin/HostingConnector/index.tsx | 2 +-
ui/src/plugins/builtin/SearchInfo/index.tsx | 2 +-
.../builtin/ThirdPartyConnector/index.tsx | 2 +-
ui/src/router/pathFactory.ts | 2 +-
ui/src/utils/pluginKit/index.ts | 44 +--
ui/src/utils/pluginKit/interface.ts | 27 ++
ui/src/utils/pluginKit/utils.ts | 10 +-
57 files changed, 769 insertions(+), 579 deletions(-)
create mode 100644 ui/src/utils/pluginKit/interface.ts
diff --git a/.vscode/settings.json b/.vscode/settings.json
index db563c27c..b7d39e6e5 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -2,5 +2,8 @@
"eslint.workingDirectories": [
"ui"
],
- "explorer.autoReveal": "focusNoScroll"
+ "explorer.autoReveal": "focusNoScroll",
+ "cSpell.words": [
+ "grecaptcha"
+ ]
}
diff --git a/i18n/af_ZA.yaml b/i18n/af_ZA.yaml
index 89c34f733..41fbb4f85 100644
--- a/i18n/af_ZA.yaml
+++ b/i18n/af_ZA.yaml
@@ -811,7 +811,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/ar_SA.yaml b/i18n/ar_SA.yaml
index 28941e9e9..2a5f88bd8 100644
--- a/i18n/ar_SA.yaml
+++ b/i18n/ar_SA.yaml
@@ -811,7 +811,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/az_AZ.yaml b/i18n/az_AZ.yaml
index 483744f70..e8c1c619a 100644
--- a/i18n/az_AZ.yaml
+++ b/i18n/az_AZ.yaml
@@ -803,7 +803,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/bal_BA.yaml b/i18n/bal_BA.yaml
index 483744f70..e8c1c619a 100644
--- a/i18n/bal_BA.yaml
+++ b/i18n/bal_BA.yaml
@@ -803,7 +803,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/ban_ID.yaml b/i18n/ban_ID.yaml
index 483744f70..e8c1c619a 100644
--- a/i18n/ban_ID.yaml
+++ b/i18n/ban_ID.yaml
@@ -803,7 +803,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/bn_BD.yaml b/i18n/bn_BD.yaml
index 483744f70..e8c1c619a 100644
--- a/i18n/bn_BD.yaml
+++ b/i18n/bn_BD.yaml
@@ -803,7 +803,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/bs_BA.yaml b/i18n/bs_BA.yaml
index 483744f70..e8c1c619a 100644
--- a/i18n/bs_BA.yaml
+++ b/i18n/bs_BA.yaml
@@ -803,7 +803,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/ca_ES.yaml b/i18n/ca_ES.yaml
index 28941e9e9..2a5f88bd8 100644
--- a/i18n/ca_ES.yaml
+++ b/i18n/ca_ES.yaml
@@ -811,7 +811,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/cs_CZ.yaml b/i18n/cs_CZ.yaml
index e71eeda48..dade37f62 100644
--- a/i18n/cs_CZ.yaml
+++ b/i18n/cs_CZ.yaml
@@ -1176,7 +1176,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/cy_GB.yaml b/i18n/cy_GB.yaml
index a1c14f5f9..9a3a556db 100644
--- a/i18n/cy_GB.yaml
+++ b/i18n/cy_GB.yaml
@@ -1176,7 +1176,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/da_DK.yaml b/i18n/da_DK.yaml
index 0d48719dc..0bad6afac 100644
--- a/i18n/da_DK.yaml
+++ b/i18n/da_DK.yaml
@@ -1176,7 +1176,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/el_GR.yaml b/i18n/el_GR.yaml
index 28941e9e9..2a5f88bd8 100644
--- a/i18n/el_GR.yaml
+++ b/i18n/el_GR.yaml
@@ -811,7 +811,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml
index 943608dd6..e7f4fe0ba 100644
--- a/i18n/en_US.yaml
+++ b/i18n/en_US.yaml
@@ -1213,7 +1213,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/fi_FI.yaml b/i18n/fi_FI.yaml
index 28941e9e9..2a5f88bd8 100644
--- a/i18n/fi_FI.yaml
+++ b/i18n/fi_FI.yaml
@@ -811,7 +811,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/he_IL.yaml b/i18n/he_IL.yaml
index 28941e9e9..2a5f88bd8 100644
--- a/i18n/he_IL.yaml
+++ b/i18n/he_IL.yaml
@@ -811,7 +811,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/hi_IN.yaml b/i18n/hi_IN.yaml
index 0d48719dc..0bad6afac 100644
--- a/i18n/hi_IN.yaml
+++ b/i18n/hi_IN.yaml
@@ -1176,7 +1176,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/hu_HU.yaml b/i18n/hu_HU.yaml
index 28941e9e9..2a5f88bd8 100644
--- a/i18n/hu_HU.yaml
+++ b/i18n/hu_HU.yaml
@@ -811,7 +811,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/hy_AM.yaml b/i18n/hy_AM.yaml
index 483744f70..e8c1c619a 100644
--- a/i18n/hy_AM.yaml
+++ b/i18n/hy_AM.yaml
@@ -803,7 +803,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/id_ID.yaml b/i18n/id_ID.yaml
index de2f95c43..07705dcfd 100644
--- a/i18n/id_ID.yaml
+++ b/i18n/id_ID.yaml
@@ -1176,7 +1176,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/ja_JP.yaml b/i18n/ja_JP.yaml
index d1452052f..840824dc3 100644
--- a/i18n/ja_JP.yaml
+++ b/i18n/ja_JP.yaml
@@ -518,7 +518,7 @@ ui:
edit_tag: タグを編集
ask_a_question: 質問を追加
edit_question: 質問を編集
- edit_answer: 回答を編集
+ edit_answer: 回答を編集
search: 検索
posts_containing: Posts containing
settings: 設定
@@ -763,8 +763,8 @@ ui:
Use comments to reply to other users or notify them of changes. If you are adding new information, edit your post instead of commenting.
tip_vote: It adds something useful to the post
edit_answer:
- title: 回答を編集
- default_reason: 回答を編集
+ title: 回答を編集
+ default_reason: 回答を編集
default_first_reason: 回答を追加
form:
fields:
@@ -1176,7 +1176,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/ko_KR.yaml b/i18n/ko_KR.yaml
index 1e584537d..ae74dc491 100644
--- a/i18n/ko_KR.yaml
+++ b/i18n/ko_KR.yaml
@@ -1176,7 +1176,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/nl_NL.yaml b/i18n/nl_NL.yaml
index 28941e9e9..2a5f88bd8 100644
--- a/i18n/nl_NL.yaml
+++ b/i18n/nl_NL.yaml
@@ -811,7 +811,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/no_NO.yaml b/i18n/no_NO.yaml
index 28941e9e9..2a5f88bd8 100644
--- a/i18n/no_NO.yaml
+++ b/i18n/no_NO.yaml
@@ -811,7 +811,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/sq_AL.yaml b/i18n/sq_AL.yaml
index 483744f70..e8c1c619a 100644
--- a/i18n/sq_AL.yaml
+++ b/i18n/sq_AL.yaml
@@ -803,7 +803,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/sr_SP.yaml b/i18n/sr_SP.yaml
index 28941e9e9..2a5f88bd8 100644
--- a/i18n/sr_SP.yaml
+++ b/i18n/sr_SP.yaml
@@ -811,7 +811,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/sv_SE.yaml b/i18n/sv_SE.yaml
index 7cef69322..2030c9515 100644
--- a/i18n/sv_SE.yaml
+++ b/i18n/sv_SE.yaml
@@ -1176,7 +1176,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/te_IN.yaml b/i18n/te_IN.yaml
index dc22340cd..43fd17019 100644
--- a/i18n/te_IN.yaml
+++ b/i18n/te_IN.yaml
@@ -1176,7 +1176,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/tr_TR.yaml b/i18n/tr_TR.yaml
index 0460fda5c..82d15621b 100644
--- a/i18n/tr_TR.yaml
+++ b/i18n/tr_TR.yaml
@@ -1176,7 +1176,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/uk_UA.yaml b/i18n/uk_UA.yaml
index 21421f664..acd8b2f84 100644
--- a/i18n/uk_UA.yaml
+++ b/i18n/uk_UA.yaml
@@ -1176,7 +1176,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/vi_VN.yaml b/i18n/vi_VN.yaml
index d28041ce4..f193e7426 100644
--- a/i18n/vi_VN.yaml
+++ b/i18n/vi_VN.yaml
@@ -1176,7 +1176,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/ui/src/components/Actions/index.tsx b/ui/src/components/Actions/index.tsx
index b83571054..b58d0c8cd 100644
--- a/ui/src/components/Actions/index.tsx
+++ b/ui/src/components/Actions/index.tsx
@@ -25,7 +25,8 @@ import classNames from 'classnames';
import { Icon } from '@/components';
import { loggedUserInfoStore } from '@/stores';
-import { useToast, useCaptchaModal } from '@/hooks';
+import { useToast } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import { tryNormalLogged } from '@/utils/guard';
import { bookmark, postVote } from '@/services';
import * as Types from '@/common/interface';
@@ -56,7 +57,7 @@ const Index: FC = ({ className, data, source }) => {
const { username = '' } = loggedUserInfoStore((state) => state.user);
const toast = useToast();
const { t } = useTranslation();
- const vCaptcha = useCaptchaModal('vote');
+ const vCaptcha = useCaptchaPlugin('vote');
useEffect(() => {
if (data) {
@@ -70,6 +71,42 @@ const Index: FC = ({ className, data, source }) => {
}
}, []);
+ const submitVote = (type) => {
+ const isCancel = (type === 'up' && like) || (type === 'down' && hate);
+ const imgCode: Types.ImgCodeReq = {
+ captcha_id: undefined,
+ captcha_code: undefined,
+ };
+ vCaptcha?.resolveCaptchaReq?.(imgCode);
+
+ postVote(
+ {
+ object_id: data?.id,
+ is_cancel: isCancel,
+ ...imgCode,
+ },
+ type,
+ )
+ .then(async (res) => {
+ await vCaptcha?.close();
+ setVotes(res.votes);
+ setLike(res.vote_status === 'vote_up');
+ setHated(res.vote_status === 'vote_down');
+ })
+ .catch((err) => {
+ if (err?.isError) {
+ vCaptcha?.handleCaptchaError(err.list);
+ }
+ const errMsg = err?.value;
+ if (errMsg) {
+ toast.onShow({
+ msg: errMsg,
+ variant: 'danger',
+ });
+ }
+ });
+ };
+
const handleVote = (type: 'up' | 'down') => {
if (!tryNormalLogged(true)) {
return;
@@ -82,39 +119,14 @@ const Index: FC = ({ className, data, source }) => {
});
return;
}
- const isCancel = (type === 'up' && like) || (type === 'down' && hate);
+
+ if (!vCaptcha) {
+ submitVote(type);
+ return;
+ }
+
vCaptcha.check(() => {
- const imgCode: Types.ImgCodeReq = {
- captcha_id: undefined,
- captcha_code: undefined,
- };
- vCaptcha.resolveCaptchaReq(imgCode);
- postVote(
- {
- object_id: data?.id,
- is_cancel: isCancel,
- ...imgCode,
- },
- type,
- )
- .then(async (res) => {
- await vCaptcha.close();
- setVotes(res.votes);
- setLike(res.vote_status === 'vote_up');
- setHated(res.vote_status === 'vote_down');
- })
- .catch((err) => {
- if (err?.isError) {
- vCaptcha.handleCaptchaError(err.list);
- }
- const errMsg = err?.value;
- if (errMsg) {
- toast.onShow({
- msg: errMsg,
- variant: 'danger',
- });
- }
- });
+ submitVote(type);
});
};
diff --git a/ui/src/components/Comment/index.tsx b/ui/src/components/Comment/index.tsx
index 81345e7e7..822a444e3 100644
--- a/ui/src/components/Comment/index.tsx
+++ b/ui/src/components/Comment/index.tsx
@@ -35,6 +35,7 @@ import {
bgFadeOut,
} from '@/utils';
import { tryNormalLogged } from '@/utils/guard';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import {
useQueryComments,
addComment,
@@ -66,9 +67,9 @@ const Comment = ({ objectId, mode, commentId }) => {
const reportModal = useReportModal();
const addCaptcha = useCaptchaModal('comment');
- const editCaptcha = useCaptchaModal('edit');
- const dCaptcha = useCaptchaModal('delete');
- const vCaptcha = useCaptchaModal('vote');
+ const editCaptcha = useCaptchaPlugin('edit');
+ const dCaptcha = useCaptchaPlugin('delete');
+ const vCaptcha = useCaptchaPlugin('vote');
const { t } = useTranslation('translation', { keyPrefix: 'comment' });
@@ -136,6 +137,80 @@ const Comment = ({ objectId, mode, commentId }) => {
);
};
+ const submitUpdateComment = (params, item) => {
+ const up = {
+ ...params,
+ comment_id: item.comment_id,
+ captcha_code: undefined,
+ captcha_id: undefined,
+ };
+ editCaptcha?.resolveCaptchaReq(up);
+
+ return updateComment(up)
+ .then(async (res) => {
+ await editCaptcha?.close();
+ setComments(
+ comments.map((comment) => {
+ if (comment.comment_id === item.comment_id) {
+ comment.showEdit = false;
+ comment.parsed_text = res.parsed_text;
+ comment.original_text = res.original_text;
+ }
+ return comment;
+ }),
+ );
+ })
+ .catch((err) => {
+ if (err.isError) {
+ editCaptcha?.handleCaptchaError(err.list);
+ }
+ });
+ };
+
+ const submitAddComment = (params, item) => {
+ const req = {
+ ...params,
+ captcha_code: undefined,
+ captcha_id: undefined,
+ };
+ addCaptcha?.resolveCaptchaReq(req);
+
+ return addComment(req)
+ .then(async (res) => {
+ await addCaptcha?.close();
+ if (item.type === 'reply') {
+ const index = comments.findIndex(
+ (comment) => comment.comment_id === item.comment_id,
+ );
+ updateCurrentReplyId('');
+ comments.splice(index + 1, 0, res);
+ setComments([...comments]);
+ } else {
+ setComments([
+ ...comments.map((comment) => {
+ if (comment.comment_id === item.comment_id) {
+ updateCurrentReplyId('');
+ }
+ return comment;
+ }),
+ res,
+ ]);
+ }
+
+ setVisibleComment(false);
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ const captchaErr = addCaptcha?.handleCaptchaError(ex.list);
+ // If it is not a CAPTCHA error, leave it to the subsequent error handling logic to continue processing.
+ if (!(captchaErr && ex.list.length === 1)) {
+ return Promise.reject(ex);
+ }
+ }
+ return Promise.resolve();
+ });
+ };
+
const handleSendReply = (item) => {
const users = matchedUsers(item.value);
const userNames = unionBy(users.map((user) => user.userName));
@@ -153,80 +228,36 @@ const Comment = ({ objectId, mode, commentId }) => {
};
if (item.type === 'edit') {
- return editCaptcha.check(() => {
- const up = {
- ...params,
- comment_id: item.comment_id,
- captcha_code: undefined,
- captcha_id: undefined,
- };
- editCaptcha.resolveCaptchaReq(up);
-
- return updateComment(up)
- .then(async (res) => {
- await editCaptcha.close();
- setComments(
- comments.map((comment) => {
- if (comment.comment_id === item.comment_id) {
- comment.showEdit = false;
- comment.parsed_text = res.parsed_text;
- comment.original_text = res.original_text;
- }
- return comment;
- }),
- );
- })
- .catch((err) => {
- if (err.isError) {
- editCaptcha.handleCaptchaError(err.list);
- }
- });
- });
+ if (!editCaptcha) {
+ return submitUpdateComment(params, item);
+ }
+ return editCaptcha.check(() => submitUpdateComment(params, item));
}
- return addCaptcha.check(() => {
- const req = {
- ...params,
- captcha_code: undefined,
- captcha_id: undefined,
- };
- addCaptcha.resolveCaptchaReq(req);
-
- return addComment(req)
- .then(async (res) => {
- await addCaptcha.close();
- if (item.type === 'reply') {
- const index = comments.findIndex(
- (comment) => comment.comment_id === item.comment_id,
- );
- updateCurrentReplyId('');
- comments.splice(index + 1, 0, res);
- setComments([...comments]);
- } else {
- setComments([
- ...comments.map((comment) => {
- if (comment.comment_id === item.comment_id) {
- updateCurrentReplyId('');
- }
- return comment;
- }),
- res,
- ]);
- }
+ if (!addCaptcha) {
+ return submitAddComment(params, item);
+ }
- setVisibleComment(false);
- })
- .catch((ex) => {
- if (ex.isError) {
- const captchaErr = addCaptcha.handleCaptchaError(ex.list);
- // If it is not a CAPTCHA error, leave it to the subsequent error handling logic to continue processing.
- if (!(captchaErr && ex.list.length === 1)) {
- return Promise.reject(ex);
- }
- }
- return Promise.resolve();
- });
- });
+ return addCaptcha.check(() => submitAddComment(params, item));
+ };
+
+ const submitDeleteComment = (id) => {
+ const imgCode = { captcha_id: undefined, captcha_code: undefined };
+ dCaptcha?.resolveCaptchaReq(imgCode);
+
+ deleteComment(id, imgCode)
+ .then(async () => {
+ await dCaptcha?.close();
+ if (pageIndex === 0) {
+ mutate();
+ }
+ setComments(comments.filter((item) => item.comment_id !== id));
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ dCaptcha?.handleCaptchaError(ex.list);
+ }
+ });
};
const handleDelete = (id) => {
@@ -236,67 +267,64 @@ const Comment = ({ objectId, mode, commentId }) => {
confirmBtnVariant: 'danger',
confirmText: t('delete', { keyPrefix: 'btns' }),
onConfirm: () => {
+ if (!dCaptcha) {
+ submitDeleteComment(id);
+ return;
+ }
dCaptcha.check(() => {
- const imgCode = { captcha_id: undefined, captcha_code: undefined };
- dCaptcha.resolveCaptchaReq(imgCode);
-
- deleteComment(id, imgCode)
- .then(async () => {
- await dCaptcha.close();
- if (pageIndex === 0) {
- mutate();
- }
- setComments(comments.filter((item) => item.comment_id !== id));
- })
- .catch((ex) => {
- if (ex.isError) {
- dCaptcha.handleCaptchaError(ex.list);
- }
- });
+ submitDeleteComment(id);
});
},
});
};
+ const submitVoteComment = (id, is_cancel) => {
+ const imgCode: Types.ImgCodeReq = {
+ captcha_id: undefined,
+ captcha_code: undefined,
+ };
+ vCaptcha?.resolveCaptchaReq(imgCode);
+
+ postVote(
+ {
+ object_id: id,
+ is_cancel,
+ ...imgCode,
+ },
+ 'up',
+ )
+ .then(async () => {
+ await vCaptcha?.close();
+ setComments(
+ comments.map((item) => {
+ if (item.comment_id === id) {
+ item.vote_count = is_cancel
+ ? item.vote_count - 1
+ : item.vote_count + 1;
+ item.is_vote = !is_cancel;
+ }
+ return item;
+ }),
+ );
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ vCaptcha?.handleCaptchaError(ex.list);
+ }
+ });
+ };
const handleVote = (id, is_cancel) => {
if (!tryNormalLogged(true)) {
return;
}
+ if (!vCaptcha) {
+ submitVoteComment(id, is_cancel);
+ return;
+ }
+
vCaptcha.check(() => {
- const imgCode: Types.ImgCodeReq = {
- captcha_id: undefined,
- captcha_code: undefined,
- };
- vCaptcha.resolveCaptchaReq(imgCode);
-
- postVote(
- {
- object_id: id,
- is_cancel,
- ...imgCode,
- },
- 'up',
- )
- .then(async () => {
- await vCaptcha.close();
- setComments(
- comments.map((item) => {
- if (item.comment_id === id) {
- item.vote_count = is_cancel
- ? item.vote_count - 1
- : item.vote_count + 1;
- item.is_vote = !is_cancel;
- }
- return item;
- }),
- );
- })
- .catch((ex) => {
- if (ex.isError) {
- vCaptcha.handleCaptchaError(ex.list);
- }
- });
+ submitVoteComment(id, is_cancel);
});
};
diff --git a/ui/src/components/Operate/index.tsx b/ui/src/components/Operate/index.tsx
index e04416f2f..47b4463f0 100644
--- a/ui/src/components/Operate/index.tsx
+++ b/ui/src/components/Operate/index.tsx
@@ -23,7 +23,8 @@ import { Link, useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Modal } from '@/components';
-import { useReportModal, useToast, useCaptchaModal } from '@/hooks';
+import { useReportModal, useToast } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import { QuestionOperationReq } from '@/common/interface';
import Share from '../Share';
import {
@@ -63,7 +64,7 @@ const Index: FC = ({
const toast = useToast();
const navigate = useNavigate();
const reportModal = useReportModal();
- const dCaptcha = useCaptchaModal('delete');
+ const dCaptcha = useCaptchaPlugin('delete');
const refreshQuestion = () => {
callback?.('default');
@@ -88,6 +89,55 @@ const Index: FC = ({
});
};
+ const submitDeleteQuestion = () => {
+ const req = {
+ id: qid,
+ captcha_code: undefined,
+ captcha_id: undefined,
+ };
+ dCaptcha?.resolveCaptchaReq(req);
+
+ deleteQuestion(req)
+ .then(async () => {
+ await dCaptcha?.close();
+ toast.onShow({
+ msg: t('post_deleted', { keyPrefix: 'messages' }),
+ variant: 'success',
+ });
+ callback?.('delete_question');
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ dCaptcha?.handleCaptchaError(ex.list);
+ }
+ });
+ };
+
+ const submitDeleteAnswer = () => {
+ const req = {
+ id: aid,
+ captcha_code: undefined,
+ captcha_id: undefined,
+ };
+ dCaptcha?.resolveCaptchaReq(req);
+
+ deleteAnswer(req)
+ .then(async () => {
+ await dCaptcha?.close();
+ // refresh page
+ toast.onShow({
+ msg: t('tip_answer_deleted'),
+ variant: 'success',
+ });
+ callback?.('delete_answer');
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ dCaptcha?.handleCaptchaError(ex.list);
+ }
+ });
+ };
+
const handleDelete = () => {
if (type === 'question') {
Modal.confirm({
@@ -97,28 +147,12 @@ const Index: FC = ({
confirmBtnVariant: 'danger',
confirmText: t('delete', { keyPrefix: 'btns' }),
onConfirm: () => {
+ if (!dCaptcha) {
+ submitDeleteQuestion();
+ return;
+ }
dCaptcha.check(() => {
- const req = {
- id: qid,
- captcha_code: undefined,
- captcha_id: undefined,
- };
- dCaptcha.resolveCaptchaReq(req);
-
- deleteQuestion(req)
- .then(async () => {
- await dCaptcha.close();
- toast.onShow({
- msg: t('post_deleted', { keyPrefix: 'messages' }),
- variant: 'success',
- });
- callback?.('delete_question');
- })
- .catch((ex) => {
- if (ex.isError) {
- dCaptcha.handleCaptchaError(ex.list);
- }
- });
+ submitDeleteQuestion();
});
},
});
@@ -132,29 +166,12 @@ const Index: FC = ({
confirmBtnVariant: 'danger',
confirmText: t('delete', { keyPrefix: 'btns' }),
onConfirm: () => {
+ if (!dCaptcha) {
+ submitDeleteAnswer();
+ return;
+ }
dCaptcha.check(() => {
- const req = {
- id: aid,
- captcha_code: undefined,
- captcha_id: undefined,
- };
- dCaptcha.resolveCaptchaReq(req);
-
- deleteAnswer(req)
- .then(async () => {
- await dCaptcha.close();
- // refresh page
- toast.onShow({
- msg: t('tip_answer_deleted'),
- variant: 'success',
- });
- callback?.('delete_answer');
- })
- .catch((ex) => {
- if (ex.isError) {
- dCaptcha.handleCaptchaError(ex.list);
- }
- });
+ submitDeleteAnswer();
});
},
});
diff --git a/ui/src/components/PluginRender/index.tsx b/ui/src/components/PluginRender/index.tsx
index 3da9fd0b3..2426eb9cd 100644
--- a/ui/src/components/PluginRender/index.tsx
+++ b/ui/src/components/PluginRender/index.tsx
@@ -19,7 +19,8 @@
import React, { FC, ReactNode } from 'react';
-import PluginKit, { Plugin, PluginType } from '@/utils/pluginKit';
+import PluginKit, { Plugin } from '@/utils/pluginKit';
+import type { PluginType } from '@/utils/pluginKit/interface';
/**
* Note:Please set at least either of the `slug_name` and `type` attributes, otherwise no plugins will be rendered.
*
diff --git a/ui/src/components/Unactivate/index.tsx b/ui/src/components/Unactivate/index.tsx
index e96e28754..163da2bd3 100644
--- a/ui/src/components/Unactivate/index.tsx
+++ b/ui/src/components/Unactivate/index.tsx
@@ -26,7 +26,7 @@ import type { ImgCodeReq, FormDataType } from '@/common/interface';
import { loggedUserInfoStore } from '@/stores';
import { resendEmail } from '@/services';
import { handleFormError } from '@/utils';
-import { useCaptchaModal } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
interface IProps {
visible?: boolean;
@@ -44,12 +44,12 @@ const Index: React.FC = () => {
},
});
- const emailCaptcha = useCaptchaModal('email');
+ const emailCaptcha = useCaptchaPlugin('email');
const submit = () => {
let req: ImgCodeReq = {};
- const imgCode = emailCaptcha.getCaptcha();
- if (imgCode.verify) {
+ const imgCode = emailCaptcha?.getCaptcha();
+ if (imgCode?.verify) {
req = {
captcha_code: imgCode.captcha_code,
captcha_id: imgCode.captcha_id,
@@ -57,12 +57,12 @@ const Index: React.FC = () => {
}
resendEmail(req)
.then(async () => {
- await emailCaptcha.close();
+ await emailCaptcha?.close();
setSuccess(true);
})
.catch((err) => {
if (err.isError) {
- emailCaptcha.handleCaptchaError(err.list);
+ emailCaptcha?.handleCaptchaError(err.list);
const data = handleFormError(err, formData);
setFormData({ ...data });
}
@@ -71,6 +71,10 @@ const Index: React.FC = () => {
const onSentEmail = (evt) => {
evt.preventDefault();
+ if (!emailCaptcha) {
+ submit();
+ return;
+ }
emailCaptcha.check(() => {
submit();
});
diff --git a/ui/src/hooks/useReportModal/index.tsx b/ui/src/hooks/useReportModal/index.tsx
index e20b8eb61..843472cd7 100644
--- a/ui/src/hooks/useReportModal/index.tsx
+++ b/ui/src/hooks/useReportModal/index.tsx
@@ -23,7 +23,8 @@ import { useTranslation } from 'react-i18next';
import ReactDOM from 'react-dom/client';
-import { useToast, useCaptchaModal } from '@/hooks';
+import { useToast } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import type * as Type from '@/common/interface';
import {
reportList,
@@ -65,7 +66,7 @@ const useReportModal = (callback?: () => void) => {
const [show, setShow] = useState(false);
const [list, setList] = useState([]);
- const rCaptcha = useCaptchaModal('report');
+ const rCaptcha = useCaptchaPlugin('report');
useEffect(() => {
const div = document.createElement('div');
@@ -168,6 +169,34 @@ const useReportModal = (callback?: () => void) => {
return true;
};
+ const submitReport = (data) => {
+ const flagReq = {
+ source: data.type,
+ report_type: reportType.type,
+ object_id: data.id,
+ content: content.value,
+ captcha_code: undefined,
+ captcha_id: undefined,
+ };
+ rCaptcha?.resolveCaptchaReq(flagReq);
+
+ postReport(flagReq)
+ .then(async () => {
+ await rCaptcha?.close();
+ toast.onShow({
+ msg: t('flag_success', { keyPrefix: 'toast' }),
+ variant: 'warning',
+ });
+ onClose();
+ asyncCallback();
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ rCaptcha?.handleCaptchaError(ex.list);
+ }
+ });
+ };
+
const handleSubmit = () => {
if (!params) {
return;
@@ -205,32 +234,12 @@ const useReportModal = (callback?: () => void) => {
return;
}
if (!params.isBackend && params.action === 'flag') {
+ if (!rCaptcha) {
+ submitReport(params);
+ return;
+ }
rCaptcha.check(() => {
- const flagReq = {
- source: params.type,
- report_type: reportType.type,
- object_id: params.id,
- content: content.value,
- captcha_code: undefined,
- captcha_id: undefined,
- };
- rCaptcha.resolveCaptchaReq(flagReq);
-
- postReport(flagReq)
- .then(async () => {
- await rCaptcha.close();
- toast.onShow({
- msg: t('flag_success', { keyPrefix: 'toast' }),
- variant: 'warning',
- });
- onClose();
- asyncCallback();
- })
- .catch((ex) => {
- if (ex.isError) {
- rCaptcha.handleCaptchaError(ex.list);
- }
- });
+ submitReport(params);
});
}
diff --git a/ui/src/pages/Questions/Ask/index.tsx b/ui/src/pages/Questions/Ask/index.tsx
index 9a1ff14a9..367ccfca8 100644
--- a/ui/src/pages/Questions/Ask/index.tsx
+++ b/ui/src/pages/Questions/Ask/index.tsx
@@ -27,7 +27,7 @@ import classNames from 'classnames';
import isEqual from 'lodash/isEqual';
import debounce from 'lodash/debounce';
-import { usePageTags, usePromptWithUnload, useCaptchaModal } from '@/hooks';
+import { usePageTags, usePromptWithUnload } from '@/hooks';
import { Editor, EditorRef, TagSelector } from '@/components';
import type * as Type from '@/common/interface';
import { DRAFT_QUESTION_STORAGE_KEY } from '@/common/constants';
@@ -42,6 +42,7 @@ import {
} from '@/services';
import { handleFormError, SaveDraft, storageExpires } from '@/utils';
import { pathFactory } from '@/router/pathFactory';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import SearchQuestion from './components/SearchQuestion';
@@ -120,8 +121,8 @@ const Ask = () => {
const isEdit = qid !== undefined;
- const saveCaptcha = useCaptchaModal('question');
- const editCaptcha = useCaptchaModal('edit');
+ const saveCaptcha = useCaptchaPlugin('question');
+ const editCaptcha = useCaptchaPlugin('edit');
const removeDraft = () => {
saveDraft.save.cancel();
@@ -276,6 +277,79 @@ const Ask = () => {
}
};
+ const submitModifyQuestion = (params) => {
+ contentChangedRef.current = false;
+ const ep = {
+ ...params,
+ id: qid,
+ edit_summary: formData.edit_summary.value,
+ };
+ const imgCode = editCaptcha?.getCaptcha();
+ if (imgCode?.verify) {
+ ep.captcha_code = imgCode.captcha_code;
+ ep.captcha_id = imgCode.captcha_id;
+ }
+ modifyQuestion(ep)
+ .then(async (res) => {
+ await editCaptcha?.close();
+ navigate(pathFactory.questionLanding(qid, res?.url_title), {
+ state: { isReview: res?.wait_for_review },
+ });
+ })
+ .catch((err) => {
+ if (err.isError) {
+ editCaptcha?.handleCaptchaError(err.list);
+ const data = handleFormError(err, formData);
+ setFormData({ ...data });
+ }
+ });
+ };
+
+ const submitQuestion = async (params) => {
+ contentChangedRef.current = false;
+ const imgCode = saveCaptcha?.getCaptcha();
+ if (imgCode?.verify) {
+ params.captcha_code = imgCode.captcha_code;
+ params.captcha_id = imgCode.captcha_id;
+ }
+ let res;
+ if (checked) {
+ res = await saveQuestionWithAnswer({
+ ...params,
+ answer_content: formData.answer_content.value,
+ }).catch((err) => {
+ if (err.isError) {
+ const captchaErr = saveCaptcha?.handleCaptchaError(err.list);
+ if (!(captchaErr && err.list.length === 1)) {
+ const data = handleFormError(err, formData);
+ setFormData({ ...data });
+ }
+ }
+ });
+ } else {
+ res = await saveQuestion(params).catch((err) => {
+ if (err.isError) {
+ const captchaErr = saveCaptcha?.handleCaptchaError(err.list);
+ if (!(captchaErr && err.list.length === 1)) {
+ const data = handleFormError(err, formData);
+ setFormData({ ...data });
+ }
+ }
+ });
+ }
+
+ const id = res?.id || res?.question?.id;
+ if (id) {
+ await saveCaptcha?.close();
+ if (checked) {
+ navigate(pathFactory.questionLanding(id, res?.question?.url_title));
+ } else {
+ navigate(pathFactory.questionLanding(id, res?.url_title));
+ }
+ }
+ removeDraft();
+ };
+
const handleSubmit = async (event: React.FormEvent) => {
event.preventDefault();
event.stopPropagation();
@@ -287,77 +361,20 @@ const Ask = () => {
};
if (isEdit) {
+ if (!editCaptcha) {
+ submitModifyQuestion(params);
+ return;
+ }
editCaptcha.check(() => {
- contentChangedRef.current = false;
- const ep = {
- ...params,
- id: qid,
- edit_summary: formData.edit_summary.value,
- };
- const imgCode = editCaptcha.getCaptcha();
- if (imgCode.verify) {
- ep.captcha_code = imgCode.captcha_code;
- ep.captcha_id = imgCode.captcha_id;
- }
- modifyQuestion(ep)
- .then(async (res) => {
- await editCaptcha.close();
- navigate(pathFactory.questionLanding(qid, res?.url_title), {
- state: { isReview: res?.wait_for_review },
- });
- })
- .catch((err) => {
- if (err.isError) {
- editCaptcha.handleCaptchaError(err.list);
- const data = handleFormError(err, formData);
- setFormData({ ...data });
- }
- });
+ submitModifyQuestion(params);
});
} else {
- saveCaptcha.check(async () => {
- contentChangedRef.current = false;
- const imgCode = saveCaptcha.getCaptcha();
- if (imgCode.verify) {
- params.captcha_code = imgCode.captcha_code;
- params.captcha_id = imgCode.captcha_id;
- }
- let res;
- if (checked) {
- res = await saveQuestionWithAnswer({
- ...params,
- answer_content: formData.answer_content.value,
- }).catch((err) => {
- if (err.isError) {
- const captchaErr = saveCaptcha.handleCaptchaError(err.list);
- if (!(captchaErr && err.list.length === 1)) {
- const data = handleFormError(err, formData);
- setFormData({ ...data });
- }
- }
- });
- } else {
- res = await saveQuestion(params).catch((err) => {
- if (err.isError) {
- const captchaErr = saveCaptcha.handleCaptchaError(err.list);
- if (!(captchaErr && err.list.length === 1)) {
- const data = handleFormError(err, formData);
- setFormData({ ...data });
- }
- }
- });
- }
-
- const id = res?.id || res?.question?.id;
- if (id) {
- await saveCaptcha.close();
- if (checked) {
- navigate(pathFactory.questionLanding(id, res?.question?.url_title));
- } else {
- navigate(pathFactory.questionLanding(id, res?.url_title));
- }
- }
- removeDraft();
+ if (!saveCaptcha) {
+ submitQuestion(params);
+ return;
+ }
+ saveCaptcha?.check(async () => {
+ submitQuestion(params);
});
}
};
diff --git a/ui/src/pages/Questions/Detail/components/InviteToAnswer/index.tsx b/ui/src/pages/Questions/Detail/components/InviteToAnswer/index.tsx
index 82f9fa275..b8e29942c 100644
--- a/ui/src/pages/Questions/Detail/components/InviteToAnswer/index.tsx
+++ b/ui/src/pages/Questions/Detail/components/InviteToAnswer/index.tsx
@@ -27,7 +27,7 @@ import classNames from 'classnames';
import { Avatar } from '@/components';
import { getInviteUser, putInviteUser } from '@/services';
import type * as Type from '@/common/interface';
-import { useCaptchaModal } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import PeopleDropdown from './PeopleDropdown';
@@ -44,7 +44,7 @@ const Index: FC = ({ questionId, readOnly = false }) => {
const [editing, setEditing] = useState(false);
const [users, setUsers] = useState([]);
- const iaCaptcha = useCaptchaModal('invitation_answer');
+ const iaCaptcha = useCaptchaPlugin('invitation_answer');
const initInviteUsers = () => {
if (!questionId) {
@@ -74,28 +74,35 @@ const Index: FC = ({ questionId, readOnly = false }) => {
setUsers(userList);
};
+ const submitInviteUser = () => {
+ const names = users.map((_) => {
+ return _.username;
+ });
+ const imgCode: Type.ImgCodeReq = {};
+ iaCaptcha?.resolveCaptchaReq(imgCode);
+ putInviteUser(questionId, names, imgCode)
+ .then(async () => {
+ await iaCaptcha?.close();
+ setEditing(false);
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ iaCaptcha?.handleCaptchaError(ex.list);
+ }
+ });
+ };
+
const saveInviteUsers = () => {
if (!users) {
return;
}
- const names = users.map((_) => {
- return _.username;
- });
- iaCaptcha.check(() => {
- const imgCode: Type.ImgCodeReq = {};
- iaCaptcha.resolveCaptchaReq(imgCode);
- putInviteUser(questionId, names, imgCode)
- .then(async () => {
- await iaCaptcha.close();
- setEditing(false);
- })
- .catch((ex) => {
- if (ex.isError) {
- iaCaptcha.handleCaptchaError(ex.list);
- }
- console.error('putInviteUser error: ', ex);
- });
- });
+
+ if (!iaCaptcha) {
+ submitInviteUser();
+ return;
+ }
+
+ iaCaptcha.check(() => submitInviteUser());
};
useEffect(() => {
diff --git a/ui/src/pages/Questions/Detail/components/WriteAnswer/index.tsx b/ui/src/pages/Questions/Detail/components/WriteAnswer/index.tsx
index 9bc8be448..f3ebd9c5b 100644
--- a/ui/src/pages/Questions/Detail/components/WriteAnswer/index.tsx
+++ b/ui/src/pages/Questions/Detail/components/WriteAnswer/index.tsx
@@ -25,7 +25,8 @@ import { Link } from 'react-router-dom';
import { marked } from 'marked';
import classNames from 'classnames';
-import { usePromptWithUnload, useCaptchaModal } from '@/hooks';
+import { usePromptWithUnload } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import { Editor, Modal, TextArea } from '@/components';
import { FormDataType, PostAnswerReq } from '@/common/interface';
import { postAnswer } from '@/services';
@@ -63,7 +64,7 @@ const Index: FC = ({ visible = false, data, callback }) => {
const [editorFocusState, setEditorFocusState] = useState(false);
const [hasDraft, setHasDraft] = useState(false);
const [showTips, setShowTips] = useState(data.loggedUserRank < 100);
- const aCaptcha = useCaptchaModal('answer');
+ const aCaptcha = useCaptchaPlugin('answer');
const writeInfo = writeSettingStore((state) => state.write);
usePromptWithUnload({
@@ -152,6 +153,40 @@ const Index: FC = ({ visible = false, data, callback }) => {
}
};
+ const submitAnswer = () => {
+ const params: PostAnswerReq = {
+ question_id: data?.qid,
+ content: formData.content.value,
+ html: marked.parse(formData.content.value),
+ };
+ const imgCode = aCaptcha?.getCaptcha();
+ if (imgCode?.verify) {
+ params.captcha_code = imgCode.captcha_code;
+ params.captcha_id = imgCode.captcha_id;
+ }
+ postAnswer(params)
+ .then(async (res) => {
+ await aCaptcha?.close();
+ setShowEditor(false);
+ setFormData({
+ content: {
+ value: '',
+ isInvalid: false,
+ errorMsg: '',
+ },
+ });
+ removeDraft();
+ callback?.(res.info);
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ aCaptcha?.handleCaptchaError(ex.list);
+ const stateData = handleFormError(ex, formData);
+ setFormData({ ...stateData });
+ }
+ });
+ };
+
const handleSubmit = () => {
if (!guard.tryNormalLogged(true)) {
return;
@@ -159,40 +194,11 @@ const Index: FC = ({ visible = false, data, callback }) => {
if (!checkValidated()) {
return;
}
-
- aCaptcha.check(() => {
- const params: PostAnswerReq = {
- question_id: data?.qid,
- content: formData.content.value,
- html: marked.parse(formData.content.value),
- };
- const imgCode = aCaptcha.getCaptcha();
- if (imgCode.verify) {
- params.captcha_code = imgCode.captcha_code;
- params.captcha_id = imgCode.captcha_id;
- }
- postAnswer(params)
- .then(async (res) => {
- await aCaptcha.close();
- setShowEditor(false);
- setFormData({
- content: {
- value: '',
- isInvalid: false,
- errorMsg: '',
- },
- });
- removeDraft();
- callback?.(res.info);
- })
- .catch((ex) => {
- if (ex.isError) {
- aCaptcha.handleCaptchaError(ex.list);
- const stateData = handleFormError(ex, formData);
- setFormData({ ...stateData });
- }
- });
- });
+ if (!aCaptcha) {
+ submitAnswer();
+ return;
+ }
+ aCaptcha.check(() => submitAnswer());
};
const clickBtn = () => {
diff --git a/ui/src/pages/Questions/EditAnswer/index.tsx b/ui/src/pages/Questions/EditAnswer/index.tsx
index fbb3e3279..49e7b33a1 100644
--- a/ui/src/pages/Questions/EditAnswer/index.tsx
+++ b/ui/src/pages/Questions/EditAnswer/index.tsx
@@ -26,7 +26,8 @@ import dayjs from 'dayjs';
import classNames from 'classnames';
import { handleFormError, scrollToDocTop } from '@/utils';
-import { usePageTags, usePromptWithUnload, useCaptchaModal } from '@/hooks';
+import { usePageTags, usePromptWithUnload } from '@/hooks';
+import { useCaptchaPlugin, useRenderHtmlPlugin } from '@/utils/pluginKit';
import { pathFactory } from '@/router/pathFactory';
import { Editor, EditorRef, Icon, htmlRender } from '@/components';
import type * as Type from '@/common/interface';
@@ -35,7 +36,6 @@ import {
modifyAnswer,
useQueryRevisions,
} from '@/services';
-import { useRenderHtmlPlugin } from '@/utils/pluginKit';
import './index.scss';
@@ -71,7 +71,7 @@ const Index = () => {
const [formData, setFormData] = useState(initFormData);
const [immData, setImmData] = useState(initFormData);
const [contentChanged, setContentChanged] = useState(false);
- const editCaptcha = useCaptchaModal('edit');
+ const editCaptcha = useCaptchaPlugin('edit');
useEffect(() => {
if (data?.info?.content) {
@@ -153,6 +153,39 @@ const Index = () => {
return bol;
};
+ const submitEditAnswer = () => {
+ const params: Type.AnswerParams = {
+ content: formData.content.value,
+ html: editorRef.current.getHtml(),
+ question_id: qid,
+ id: aid,
+ edit_summary: formData.description.value,
+ };
+ editCaptcha?.resolveCaptchaReq(params);
+
+ modifyAnswer(params)
+ .then(async (res) => {
+ await editCaptcha?.close();
+ navigate(
+ pathFactory.answerLanding({
+ questionId: qid,
+ slugTitle: data?.question?.url_title,
+ answerId: aid,
+ }),
+ {
+ state: { isReview: res?.wait_for_review },
+ },
+ );
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ editCaptcha?.handleCaptchaError(ex.list);
+ const stateData = handleFormError(ex, formData);
+ setFormData({ ...stateData });
+ }
+ });
+ };
+
const handleSubmit = async (event: React.FormEvent) => {
setContentChanged(false);
@@ -163,38 +196,11 @@ const Index = () => {
return;
}
- editCaptcha.check(() => {
- const params: Type.AnswerParams = {
- content: formData.content.value,
- html: editorRef.current.getHtml(),
- question_id: qid,
- id: aid,
- edit_summary: formData.description.value,
- };
- editCaptcha.resolveCaptchaReq(params);
-
- modifyAnswer(params)
- .then(async (res) => {
- await editCaptcha.close();
- navigate(
- pathFactory.answerLanding({
- questionId: qid,
- slugTitle: data?.question?.url_title,
- answerId: aid,
- }),
- {
- state: { isReview: res?.wait_for_review },
- },
- );
- })
- .catch((ex) => {
- if (ex.isError) {
- editCaptcha.handleCaptchaError(ex.list);
- const stateData = handleFormError(ex, formData);
- setFormData({ ...stateData });
- }
- });
- });
+ if (!editCaptcha) {
+ submitEditAnswer();
+ return;
+ }
+ editCaptcha.check(() => submitEditAnswer());
};
const handleSelectedRevision = (e) => {
const index = e.target.value;
diff --git a/ui/src/pages/Review/components/ApproveDropdown/index.tsx b/ui/src/pages/Review/components/ApproveDropdown/index.tsx
index f6a624716..b2c05c96a 100644
--- a/ui/src/pages/Review/components/ApproveDropdown/index.tsx
+++ b/ui/src/pages/Review/components/ApproveDropdown/index.tsx
@@ -23,7 +23,8 @@ import { useTranslation } from 'react-i18next';
import { Modal } from '@/components';
import { putFlagReviewAction } from '@/services';
-import { useCaptchaModal, useReportModal, useToast } from '@/hooks';
+import { useReportModal, useToast } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import type * as Type from '@/common/interface';
import EditPostModal from '../EditPostModal';
@@ -46,12 +47,52 @@ const Index: FC = ({
const [showEditPostModal, setShowEditPostModal] = useState(false);
const closeModal = useReportModal(approveCallback);
const toast = useToast();
- const dCaptcha = useCaptchaModal('delete');
+ const dCaptcha = useCaptchaPlugin('delete');
const handleEditPostModalState = () => {
setShowEditPostModal(!showEditPostModal);
};
+ const submitReviewAction = () => {
+ const req: Type.PutFlagReviewParams = {
+ operation_type: 'delete_post',
+ flag_id: String(itemData?.flag_id),
+ captcha_code: undefined,
+ captcha_id: undefined,
+ };
+ dCaptcha?.resolveCaptchaReq(req);
+
+ delete req.captcha_code;
+ delete req.captcha_id;
+
+ putFlagReviewAction(req)
+ .then(async () => {
+ await dCaptcha?.close();
+ let msg = '';
+ if (objectType === 'question') {
+ msg = t('post_deleted', { keyPrefix: 'messages' });
+ }
+ if (objectType === 'answer') {
+ msg = t('tip_answer_deleted');
+ }
+ if (objectType === 'answer' || objectType === 'question') {
+ toast.onShow({
+ msg,
+ variant: 'success',
+ });
+ }
+ approveCallback();
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ dCaptcha?.handleCaptchaError(ex.list);
+ }
+ })
+ .finally(() => {
+ setIsLoading(false);
+ });
+ };
+
const handleDelete = () => {
let content = '';
@@ -78,45 +119,11 @@ const Index: FC = ({
confirmBtnVariant: 'danger',
confirmText: t('delete', { keyPrefix: 'btns' }),
onConfirm: () => {
- dCaptcha.check(() => {
- const req: Type.PutFlagReviewParams = {
- operation_type: 'delete_post',
- flag_id: String(itemData?.flag_id),
- captcha_code: undefined,
- captcha_id: undefined,
- };
- dCaptcha.resolveCaptchaReq(req);
-
- delete req.captcha_code;
- delete req.captcha_id;
-
- putFlagReviewAction(req)
- .then(async () => {
- await dCaptcha.close();
- let msg = '';
- if (objectType === 'question') {
- msg = t('post_deleted', { keyPrefix: 'messages' });
- }
- if (objectType === 'answer') {
- msg = t('tip_answer_deleted');
- }
- if (objectType === 'answer' || objectType === 'question') {
- toast.onShow({
- msg,
- variant: 'success',
- });
- }
- approveCallback();
- })
- .catch((ex) => {
- if (ex.isError) {
- dCaptcha.handleCaptchaError(ex.list);
- }
- })
- .finally(() => {
- setIsLoading(false);
- });
- });
+ if (!dCaptcha) {
+ submitReviewAction();
+ return;
+ }
+ dCaptcha.check(() => submitReviewAction());
},
onCancel: () => {
setIsLoading(false);
diff --git a/ui/src/pages/Review/components/EditPostModal/index.tsx b/ui/src/pages/Review/components/EditPostModal/index.tsx
index 0eefd311d..4ee80b0aa 100644
--- a/ui/src/pages/Review/components/EditPostModal/index.tsx
+++ b/ui/src/pages/Review/components/EditPostModal/index.tsx
@@ -24,7 +24,8 @@ import { useTranslation } from 'react-i18next';
import classNames from 'classnames';
import { putFlagReviewAction } from '@/services';
-import { useCaptchaModal, usePageUsers } from '@/hooks';
+import { usePageUsers } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import { Editor, TagSelector, Mentions, TextArea } from '@/components';
import {
// matchedUsers,
@@ -89,7 +90,7 @@ const Index: FC = ({
const [loaded, setLoaded] = useState(false);
const pageUsers = usePageUsers();
- const editCaptcha = useCaptchaModal('edit');
+ const editCaptcha = useCaptchaPlugin('edit');
const onClose = (bol) => {
if (bol) {
@@ -160,14 +161,7 @@ const Index: FC = ({
return bol;
};
- const handleSubmit = (event: React.FormEvent) => {
- event.preventDefault();
- event.stopPropagation();
-
- if (!checkValidated()) {
- return;
- }
-
+ const submitFlagReviewAction = () => {
const params: Type.PutFlagReviewParams = {
title: formData.title.value,
content: formData.content.value,
@@ -191,28 +185,41 @@ const Index: FC = ({
delete params.title;
delete params.tags;
}
-
- editCaptcha.check(() => {
- if (objectType === 'question') {
- const imgCode = editCaptcha.getCaptcha();
- if (imgCode.verify) {
- params.captcha_code = imgCode.captcha_code;
- params.captcha_id = imgCode.captcha_id;
- }
+ if (objectType === 'question') {
+ const imgCode = editCaptcha?.getCaptcha();
+ if (imgCode?.verify) {
+ params.captcha_code = imgCode.captcha_code;
+ params.captcha_id = imgCode.captcha_id;
}
- putFlagReviewAction(params)
- .then(async () => {
- await editCaptcha.close();
- onClose(true);
- })
- .catch((err) => {
- if (err.isError) {
- editCaptcha.handleCaptchaError(err.list);
- const data = handleFormError(err, formData);
- setFormData({ ...data });
- }
- });
- });
+ }
+ putFlagReviewAction(params)
+ .then(async () => {
+ await editCaptcha?.close();
+ onClose(true);
+ })
+ .catch((err) => {
+ if (err.isError) {
+ editCaptcha?.handleCaptchaError(err.list);
+ const data = handleFormError(err, formData);
+ setFormData({ ...data });
+ }
+ });
+ };
+
+ const handleSubmit = (event: React.FormEvent) => {
+ event.preventDefault();
+ event.stopPropagation();
+
+ if (!checkValidated()) {
+ return;
+ }
+
+ if (!editCaptcha) {
+ submitFlagReviewAction();
+ return;
+ }
+
+ editCaptcha.check(() => submitFlagReviewAction());
};
const handleSelected = (val) => {
diff --git a/ui/src/pages/Search/index.tsx b/ui/src/pages/Search/index.tsx
index 8517172de..a016af707 100644
--- a/ui/src/pages/Search/index.tsx
+++ b/ui/src/pages/Search/index.tsx
@@ -22,7 +22,8 @@ import { useTranslation } from 'react-i18next';
import { useSearchParams } from 'react-router-dom';
import { useEffect, useState } from 'react';
-import { usePageTags, useCaptchaModal, useSkeletonControl } from '@/hooks';
+import { usePageTags, useSkeletonControl } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import { Pagination } from '@/components';
import { getSearchResult } from '@/services';
import type { SearchParams, SearchRes } from '@/common/interface';
@@ -51,7 +52,7 @@ const Index = () => {
});
const { count = 0, list = [], extra = null } = data || {};
- const searchCaptcha = useCaptchaModal('search');
+ const searchCaptcha = useCaptchaPlugin('search');
const doSearch = () => {
setIsLoading(true);
@@ -62,7 +63,7 @@ const Index = () => {
size: 20,
};
- const captcha = searchCaptcha.getCaptcha();
+ const captcha = searchCaptcha?.getCaptcha();
if (captcha?.verify) {
params.captcha_id = captcha.captcha_id;
params.captcha_code = captcha.captcha_code;
@@ -70,12 +71,12 @@ const Index = () => {
getSearchResult(params)
.then(async (resp) => {
- await searchCaptcha.close();
+ await searchCaptcha?.close();
setData(resp);
})
.catch((err) => {
if (err.isError) {
- searchCaptcha.handleCaptchaError(err.list);
+ searchCaptcha?.handleCaptchaError(err.list);
}
})
.finally(() => {
@@ -84,6 +85,10 @@ const Index = () => {
};
useEffect(() => {
+ if (!searchCaptcha) {
+ doSearch();
+ return;
+ }
searchCaptcha.check(() => {
doSearch();
});
diff --git a/ui/src/pages/Users/AccountForgot/components/sendEmail.tsx b/ui/src/pages/Users/AccountForgot/components/sendEmail.tsx
index acca34aa9..5d7d2c9bb 100644
--- a/ui/src/pages/Users/AccountForgot/components/sendEmail.tsx
+++ b/ui/src/pages/Users/AccountForgot/components/sendEmail.tsx
@@ -24,7 +24,7 @@ import { useTranslation } from 'react-i18next';
import type { PasswordResetReq, FormDataType } from '@/common/interface';
import { resetPassword } from '@/services';
import { handleFormError } from '@/utils';
-import { useCaptchaModal } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
interface IProps {
// eslint-disable-next-line react/no-unused-prop-types
@@ -42,7 +42,7 @@ const Index: FC = ({ callback }) => {
},
});
- const emailCaptcha = useCaptchaModal('email');
+ const emailCaptcha = useCaptchaPlugin('email');
const handleChange = (params: FormDataType) => {
setFormData({ ...formData, ...params });
@@ -73,20 +73,20 @@ const Index: FC = ({ callback }) => {
e_mail: formData.e_mail.value,
};
- const captcha = emailCaptcha.getCaptcha();
- if (captcha.verify) {
+ const captcha = emailCaptcha?.getCaptcha();
+ if (captcha?.verify) {
params.captcha_code = captcha.captcha_code;
params.captcha_id = captcha.captcha_id;
}
resetPassword(params)
.then(async () => {
- await emailCaptcha.close();
+ await emailCaptcha?.close();
callback?.(2, formData.e_mail.value);
})
.catch((err) => {
if (err.isError) {
- emailCaptcha.handleCaptchaError(err.list);
+ emailCaptcha?.handleCaptchaError(err.list);
const data = handleFormError(err, formData);
setFormData({ ...data });
}
@@ -101,6 +101,11 @@ const Index: FC = ({ callback }) => {
return;
}
+ if (!emailCaptcha) {
+ sendEmail();
+ return;
+ }
+
emailCaptcha.check(() => {
sendEmail();
});
diff --git a/ui/src/pages/Users/ChangeEmail/components/sendEmail.tsx b/ui/src/pages/Users/ChangeEmail/components/sendEmail.tsx
index cf321a1ff..419e226e1 100644
--- a/ui/src/pages/Users/ChangeEmail/components/sendEmail.tsx
+++ b/ui/src/pages/Users/ChangeEmail/components/sendEmail.tsx
@@ -26,7 +26,7 @@ import type { PasswordResetReq, FormDataType } from '@/common/interface';
import { loggedUserInfoStore } from '@/stores';
import { changeEmail } from '@/services';
import { handleFormError } from '@/utils';
-import { useCaptchaModal } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
const Index: FC = () => {
const { t } = useTranslation('translation', { keyPrefix: 'change_email' });
@@ -41,7 +41,7 @@ const Index: FC = () => {
const navigate = useNavigate();
const { user: userInfo, update: updateUser } = loggedUserInfoStore();
- const emailCaptcha = useCaptchaModal('email');
+ const emailCaptcha = useCaptchaPlugin('email');
const handleChange = (params: FormDataType) => {
setFormData({ ...formData, ...params });
@@ -71,22 +71,22 @@ const Index: FC = () => {
const params: PasswordResetReq = {
e_mail: formData.e_mail.value,
};
- const imgCode = emailCaptcha.getCaptcha();
- if (imgCode.verify) {
+ const imgCode = emailCaptcha?.getCaptcha();
+ if (imgCode?.verify) {
params.captcha_code = imgCode.captcha_code;
params.captcha_id = imgCode.captcha_id;
}
changeEmail(params)
.then(async () => {
- await emailCaptcha.close();
+ await emailCaptcha?.close();
userInfo.e_mail = formData.e_mail.value;
updateUser(userInfo);
navigate('/users/login', { replace: true });
})
.catch((err) => {
if (err.isError) {
- emailCaptcha.handleCaptchaError(err.list);
+ emailCaptcha?.handleCaptchaError(err.list);
const data = handleFormError(err, formData);
setFormData({ ...data });
}
@@ -99,7 +99,10 @@ const Index: FC = () => {
if (!checkValidated()) {
return;
}
-
+ if (!emailCaptcha) {
+ sendEmail();
+ return;
+ }
emailCaptcha.check(() => {
sendEmail();
});
diff --git a/ui/src/pages/Users/Login/index.tsx b/ui/src/pages/Users/Login/index.tsx
index bbb1f67fa..52bbd00e4 100644
--- a/ui/src/pages/Users/Login/index.tsx
+++ b/ui/src/pages/Users/Login/index.tsx
@@ -22,7 +22,7 @@ import { Container, Form, Button, Col } from 'react-bootstrap';
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
import { Trans, useTranslation } from 'react-i18next';
-import { usePageTags, useCaptchaModal } from '@/hooks';
+import { usePageTags } from '@/hooks';
import type { LoginReqParams, FormDataType } from '@/common/interface';
import { Unactivate, WelcomeTitle, PluginRender } from '@/components';
import {
@@ -31,6 +31,7 @@ import {
userCenterStore,
} from '@/stores';
import { floppyNavigation, guard, handleFormError, userCenter } from '@/utils';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import { login, UcAgent } from '@/services';
import { setupAppTheme } from '@/utils/localize';
@@ -68,7 +69,7 @@ const Index: React.FC = () => {
setFormData({ ...formData, ...params });
};
- const passwordCaptcha = useCaptchaModal('password');
+ const passwordCaptcha = useCaptchaPlugin('password');
const checkValidated = (): boolean => {
let bol = true;
@@ -107,7 +108,7 @@ const Index: React.FC = () => {
pass: formData.pass.value,
};
- const captcha = passwordCaptcha.getCaptcha();
+ const captcha = passwordCaptcha?.getCaptcha();
if (captcha?.verify) {
params.captcha_code = captcha.captcha_code;
params.captcha_id = captcha.captcha_id;
@@ -115,7 +116,7 @@ const Index: React.FC = () => {
login(params)
.then(async (res) => {
- await passwordCaptcha.close();
+ await passwordCaptcha?.close?.();
updateUser(res);
setupAppTheme();
const userStat = guard.deriveLoginState();
@@ -130,7 +131,7 @@ const Index: React.FC = () => {
if (err.isError) {
const data = handleFormError(err, formData);
setFormData({ ...data });
- passwordCaptcha.handleCaptchaError(err.list);
+ passwordCaptcha?.handleCaptchaError?.(err.list);
}
});
};
@@ -143,7 +144,12 @@ const Index: React.FC = () => {
return;
}
- passwordCaptcha.check(() => {
+ if (!passwordCaptcha) {
+ handleLogin();
+ return;
+ }
+
+ passwordCaptcha?.check?.(() => {
handleLogin();
});
};
@@ -165,6 +171,17 @@ const Index: React.FC = () => {
{step === 1 ? (
+
+
+
{ucAgentInfo ? (
= ({ callback }) => {
});
const updateUser = userStore((state) => state.update);
- const emailCaptcha = useCaptchaModal('email');
+ const emailCaptcha = useCaptchaPlugin('email');
const handleChange = (params: FormDataType) => {
setFormData({ ...formData, ...params });
@@ -99,7 +99,7 @@ const Index: React.FC = ({ callback }) => {
pass: formData.pass.value,
};
- const captcha = emailCaptcha.getCaptcha();
+ const captcha = emailCaptcha?.getCaptcha();
if (captcha?.verify) {
reqParams.captcha_code = captcha.captcha_code;
reqParams.captcha_id = captcha.captcha_id;
@@ -107,13 +107,13 @@ const Index: React.FC = ({ callback }) => {
register(reqParams)
.then(async (res) => {
- await emailCaptcha.close();
+ await emailCaptcha?.close();
updateUser(res);
callback();
})
.catch((err) => {
if (err.isError) {
- emailCaptcha.handleCaptchaError(err.list);
+ emailCaptcha?.handleCaptchaError(err.list);
const data = handleFormError(err, formData);
setFormData({ ...data });
}
@@ -126,6 +126,10 @@ const Index: React.FC = ({ callback }) => {
if (!checkValidated()) {
return;
}
+ if (!emailCaptcha) {
+ handleRegister();
+ return;
+ }
emailCaptcha.check(() => {
handleRegister();
});
diff --git a/ui/src/pages/Users/Settings/Account/components/ModifyEmail/index.tsx b/ui/src/pages/Users/Settings/Account/components/ModifyEmail/index.tsx
index 5672782b4..442d2582c 100644
--- a/ui/src/pages/Users/Settings/Account/components/ModifyEmail/index.tsx
+++ b/ui/src/pages/Users/Settings/Account/components/ModifyEmail/index.tsx
@@ -22,7 +22,8 @@ import { Form, Button } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
import type * as Type from '@/common/interface';
-import { useToast, useCaptchaModal } from '@/hooks';
+import { useToast } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import { getLoggedUserInfo, changeEmail } from '@/services';
import { handleFormError } from '@/utils';
@@ -45,7 +46,7 @@ const Index: FC = () => {
});
const [userInfo, setUserInfo] = useState();
const toast = useToast();
- const emailCaptcha = useCaptchaModal('edit_userinfo');
+ const emailCaptcha = useCaptchaPlugin('edit_userinfo');
useEffect(() => {
getLoggedUserInfo().then((resp) => {
@@ -108,14 +109,14 @@ const Index: FC = () => {
pass: formData.pass.value,
};
- const imgCode = emailCaptcha.getCaptcha();
- if (imgCode.verify) {
+ const imgCode = emailCaptcha?.getCaptcha();
+ if (imgCode?.verify) {
params.captcha_code = imgCode.captcha_code;
params.captcha_id = imgCode.captcha_id;
}
changeEmail(params)
.then(async () => {
- await emailCaptcha.close();
+ await emailCaptcha?.close();
setStep(1);
toast.onShow({
msg: t('change_email_info'),
@@ -125,7 +126,7 @@ const Index: FC = () => {
})
.catch((err) => {
if (err.isError) {
- emailCaptcha.handleCaptchaError(err.list);
+ emailCaptcha?.handleCaptchaError(err.list);
const data = handleFormError(err, formData);
setFormData({ ...data });
}
@@ -138,7 +139,10 @@ const Index: FC = () => {
if (!checkValidated()) {
return;
}
-
+ if (!emailCaptcha) {
+ postEmail();
+ return;
+ }
emailCaptcha.check(() => {
postEmail();
});
diff --git a/ui/src/pages/Users/Settings/Account/components/ModifyPass/index.tsx b/ui/src/pages/Users/Settings/Account/components/ModifyPass/index.tsx
index 6b919c5cb..bdc84bd74 100644
--- a/ui/src/pages/Users/Settings/Account/components/ModifyPass/index.tsx
+++ b/ui/src/pages/Users/Settings/Account/components/ModifyPass/index.tsx
@@ -23,7 +23,8 @@ import { useTranslation } from 'react-i18next';
import classname from 'classnames';
-import { useToast, useCaptchaModal } from '@/hooks';
+import { useToast } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import type { FormDataType } from '@/common/interface';
import { modifyPassword } from '@/services';
import { handleFormError } from '@/utils';
@@ -54,7 +55,7 @@ const Index: FC = () => {
},
});
- const infoCaptcha = useCaptchaModal('edit_userinfo');
+ const infoCaptcha = useCaptchaPlugin('edit_userinfo');
const handleFormState = () => {
setFormState((pre) => !pre);
@@ -134,14 +135,14 @@ const Index: FC = () => {
pass: formData.pass.value,
};
- const imgCode = infoCaptcha.getCaptcha();
- if (imgCode.verify) {
+ const imgCode = infoCaptcha?.getCaptcha();
+ if (imgCode?.verify) {
params.captcha_code = imgCode.captcha_code;
params.captcha_id = imgCode.captcha_id;
}
modifyPassword(params)
.then(async () => {
- await infoCaptcha.close();
+ await infoCaptcha?.close();
toast.onShow({
msg: t('update_password', { keyPrefix: 'toast' }),
variant: 'success',
@@ -150,7 +151,7 @@ const Index: FC = () => {
})
.catch((err) => {
if (err.isError) {
- infoCaptcha.handleCaptchaError(err.list);
+ infoCaptcha?.handleCaptchaError(err.list);
const data = handleFormError(err, formData);
setFormData({ ...data });
}
@@ -163,6 +164,10 @@ const Index: FC = () => {
if (!checkValidated()) {
return;
}
+ if (!infoCaptcha) {
+ postModifyPass();
+ return;
+ }
infoCaptcha.check(() => {
postModifyPass();
diff --git a/ui/src/plugins/builtin/HostingConnector/index.tsx b/ui/src/plugins/builtin/HostingConnector/index.tsx
index bcddf3625..850095a25 100644
--- a/ui/src/plugins/builtin/HostingConnector/index.tsx
+++ b/ui/src/plugins/builtin/HostingConnector/index.tsx
@@ -23,7 +23,7 @@ import { useTranslation } from 'react-i18next';
import classnames from 'classnames';
-import { PluginInfo } from '@/utils/pluginKit';
+import type { PluginInfo } from '@/utils/pluginKit/interface';
import { getTransNs, getTransKeyPrefix } from '@/utils/pluginKit/utils';
import { SvgIcon } from '@/components';
import { userCenterStore } from '@/stores';
diff --git a/ui/src/plugins/builtin/SearchInfo/index.tsx b/ui/src/plugins/builtin/SearchInfo/index.tsx
index e57819755..99e0564e7 100644
--- a/ui/src/plugins/builtin/SearchInfo/index.tsx
+++ b/ui/src/plugins/builtin/SearchInfo/index.tsx
@@ -20,7 +20,7 @@
import { memo, FC } from 'react';
import { useTranslation } from 'react-i18next';
-import { PluginInfo } from '@/utils/pluginKit';
+import type { PluginInfo } from '@/utils/pluginKit/interface';
import { getTransNs, getTransKeyPrefix } from '@/utils/pluginKit/utils';
import { SvgIcon } from '@/components';
diff --git a/ui/src/plugins/builtin/ThirdPartyConnector/index.tsx b/ui/src/plugins/builtin/ThirdPartyConnector/index.tsx
index 99032b208..c47b723bd 100644
--- a/ui/src/plugins/builtin/ThirdPartyConnector/index.tsx
+++ b/ui/src/plugins/builtin/ThirdPartyConnector/index.tsx
@@ -23,7 +23,7 @@ import { useTranslation } from 'react-i18next';
import classnames from 'classnames';
-import { PluginInfo } from '@/utils/pluginKit';
+import type { PluginInfo } from '@/utils/pluginKit/interface';
import { getTransNs, getTransKeyPrefix } from '@/utils/pluginKit/utils';
import { SvgIcon } from '@/components';
diff --git a/ui/src/router/pathFactory.ts b/ui/src/router/pathFactory.ts
index ea3536041..256dcbf03 100644
--- a/ui/src/router/pathFactory.ts
+++ b/ui/src/router/pathFactory.ts
@@ -34,7 +34,7 @@ const tagEdit = (tagId: string) => {
return r;
};
-const questionLanding = (questionId: string, slugTitle: string = '') => {
+const questionLanding = (questionId: string = '', slugTitle: string = '') => {
const { seo } = seoSettingStore.getState();
if (!questionId) {
return slugTitle ? `/questions/null/${slugTitle}` : '/questions/null';
diff --git a/ui/src/utils/pluginKit/index.ts b/ui/src/utils/pluginKit/index.ts
index dfc1f736e..5224105c8 100644
--- a/ui/src/utils/pluginKit/index.ts
+++ b/ui/src/utils/pluginKit/index.ts
@@ -25,26 +25,7 @@ import type * as Type from '@/common/interface';
import { getPluginsStatus } from '@/services';
import { initI18nResource } from './utils';
-
-/**
- * This information is to be defined for all components.
- * It may be used for feature upgrades or version compatibility processing.
- *
- * @field slug_name: Unique identity string for the plugin, usually configured in `info.yaml`
- * @field type: The type of plugin is defined and a single type of plugin can have multiple implementations.
- * For example, a plugin of type `connector` can have a `google` implementation and a `github` implementation.
- * `PluginRender` automatically renders the plug-in types already included in `PluginType`.
- * @field name: Plugin name, optionally configurable. Usually read from the `i18n` file
- * @field description: Plugin description, optionally configurable. Usually read from the `i18n` file
- */
-
-export type PluginType = 'connector' | 'search' | 'editor';
-export interface PluginInfo {
- slug_name: string;
- type: PluginType;
- name?: string;
- description?: string;
-}
+import type { PluginInfo } from './interface';
export interface Plugin {
info: PluginInfo;
@@ -52,6 +33,13 @@ export interface Plugin {
i18nConfig?;
hooks?: {
useRender?: Array<(element: HTMLElement | null) => void>;
+ useCaptcha?: (key: Type.CaptchaKey) => {
+ getCaptcha: () => Record;
+ check: (t: () => void) => void;
+ handleCaptchaError: (error) => any;
+ close: () => Promise;
+ resolveCaptchaReq: (data) => void;
+ };
};
activated?: boolean;
}
@@ -137,6 +125,11 @@ class Plugins {
return this.plugins.find((p) => p.info.slug_name === slug_name);
}
+ getOnePluginHooks(slug_name: string) {
+ const plugin = this.getPlugin(slug_name);
+ return plugin?.hooks;
+ }
+
getPlugins() {
return this.plugins;
}
@@ -155,5 +148,14 @@ const useRenderHtmlPlugin = (element: HTMLElement | null) => {
});
};
-export { useRenderHtmlPlugin };
+// Only one captcha type plug-in can be enabled at the same time
+const useCaptchaPlugin = (key: Type.CaptchaKey) => {
+ const captcha = plugins
+ .getPlugins()
+ .filter((plugin) => plugin.info.type === 'captcha');
+ const pluginHooks = plugins.getOnePluginHooks(captcha[0]?.info.slug_name);
+ return pluginHooks?.useCaptcha?.(key);
+};
+
+export { useRenderHtmlPlugin, useCaptchaPlugin };
export default plugins;
diff --git a/ui/src/utils/pluginKit/interface.ts b/ui/src/utils/pluginKit/interface.ts
new file mode 100644
index 000000000..d43767649
--- /dev/null
+++ b/ui/src/utils/pluginKit/interface.ts
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export type PluginType = 'connector' | 'search' | 'editor' | 'captcha';
+
+export interface PluginInfo {
+ slug_name: string;
+ type: PluginType;
+ name?: string;
+ description?: string;
+}
diff --git a/ui/src/utils/pluginKit/utils.ts b/ui/src/utils/pluginKit/utils.ts
index 76bbf3880..686806c3e 100644
--- a/ui/src/utils/pluginKit/utils.ts
+++ b/ui/src/utils/pluginKit/utils.ts
@@ -21,6 +21,8 @@ import { NamedExoticComponent, FC } from 'react';
import i18next from 'i18next';
+import type { PluginInfo } from './interface';
+
/**
* This information is to be defined for all components.
* It may be used for feature upgrades or version compatibility processing.
@@ -35,14 +37,6 @@ import i18next from 'i18next';
const I18N_NS = 'plugin';
-export type PluginType = 'connector' | 'search' | 'editor';
-export interface PluginInfo {
- slug_name: string;
- type: PluginType;
- name?: string;
- description?: string;
-}
-
export interface Plugin {
info: PluginInfo;
component: NamedExoticComponent | FC;
From 1ce7863376ef6dcdcafee64151e17bb4f05442f6 Mon Sep 17 00:00:00 2001
From: robin
Date: Mon, 8 Apr 2024 17:44:08 +0800
Subject: [PATCH 6/7] feat(ui): Support route types in plugin system
---
ui/src/router/index.tsx | 4 +-
ui/src/utils/pluginKit/index.ts | 67 ++++++++++++++++++++++-----------
ui/src/utils/pluginKit/utils.ts | 8 +++-
3 files changed, 56 insertions(+), 23 deletions(-)
diff --git a/ui/src/router/index.tsx b/ui/src/router/index.tsx
index dca1e1bee..4cb222a97 100644
--- a/ui/src/router/index.tsx
+++ b/ui/src/router/index.tsx
@@ -21,6 +21,7 @@ import { Suspense, lazy } from 'react';
import { RouteObject } from 'react-router-dom';
import Layout from '@/pages/Layout';
+import { mergeRoutePlugins } from '@/utils/pluginKit';
import baseRoutes, { RouteNode } from './routes';
import RouteGuard from './RouteGuard';
@@ -69,5 +70,6 @@ const routeWrapper = (routeNodes: RouteNode[], root: RouteNode[]) => {
};
routeWrapper(baseRoutes, routes);
+const mergedRoutes = mergeRoutePlugins(routes);
-export default routes as RouteObject[];
+export default mergedRoutes as RouteObject[];
diff --git a/ui/src/utils/pluginKit/index.ts b/ui/src/utils/pluginKit/index.ts
index dfc1f736e..481fadd39 100644
--- a/ui/src/utils/pluginKit/index.ts
+++ b/ui/src/utils/pluginKit/index.ts
@@ -17,14 +17,16 @@
* under the License.
*/
-import { NamedExoticComponent, FC } from 'react';
+import React from 'react';
import builtin from '@/plugins/builtin';
import * as allPlugins from '@/plugins';
import type * as Type from '@/common/interface';
+import { LOGGED_TOKEN_STORAGE_KEY } from '@/common/constants';
import { getPluginsStatus } from '@/services';
+import Storage from '@/utils/storage';
-import { initI18nResource } from './utils';
+import { initI18nResource, Plugin, PluginInfo, PluginType } from './utils';
/**
* This information is to be defined for all components.
@@ -38,24 +40,6 @@ import { initI18nResource } from './utils';
* @field description: Plugin description, optionally configurable. Usually read from the `i18n` file
*/
-export type PluginType = 'connector' | 'search' | 'editor';
-export interface PluginInfo {
- slug_name: string;
- type: PluginType;
- name?: string;
- description?: string;
-}
-
-export interface Plugin {
- info: PluginInfo;
- component: NamedExoticComponent | FC;
- i18nConfig?;
- hooks?: {
- useRender?: Array<(element: HTMLElement | null) => void>;
- };
- activated?: boolean;
-}
-
class Plugins {
plugins: Plugin[] = [];
@@ -155,5 +139,46 @@ const useRenderHtmlPlugin = (element: HTMLElement | null) => {
});
};
-export { useRenderHtmlPlugin };
+const getRoutePlugins = () => {
+ return plugins.getPlugins().filter((plugin) => plugin.info.type === 'route');
+};
+
+const defaultProps = () => {
+ const token = Storage.get(LOGGED_TOKEN_STORAGE_KEY) || '';
+ return {
+ key: token,
+ headers: {
+ Authorization: token,
+ },
+ };
+};
+
+const mergeRoutePlugins = (routes) => {
+ const routePlugins = getRoutePlugins();
+ if (routePlugins.length === 0) {
+ return routes;
+ }
+ routes.forEach((route) => {
+ if (route.page === 'pages/Layout') {
+ route.children?.forEach((child) => {
+ if (child.page === 'pages/SideNavLayout') {
+ routePlugins.forEach((plugin) => {
+ const { slug_name, route: path } = plugin.info;
+ const Component = plugin.component;
+
+ child.children.push({
+ page: `plugin/${slug_name}`,
+ path,
+ element: React.createElement(Component, defaultProps(), null),
+ });
+ });
+ }
+ });
+ }
+ });
+ return routes;
+};
+
+export { useRenderHtmlPlugin, mergeRoutePlugins };
+export type { Plugin, PluginInfo, PluginType };
export default plugins;
diff --git a/ui/src/utils/pluginKit/utils.ts b/ui/src/utils/pluginKit/utils.ts
index 76bbf3880..a179360de 100644
--- a/ui/src/utils/pluginKit/utils.ts
+++ b/ui/src/utils/pluginKit/utils.ts
@@ -35,17 +35,23 @@ import i18next from 'i18next';
const I18N_NS = 'plugin';
-export type PluginType = 'connector' | 'search' | 'editor';
+export type PluginType = 'connector' | 'search' | 'editor' | 'route';
export interface PluginInfo {
slug_name: string;
type: PluginType;
name?: string;
description?: string;
+ route?: string;
}
export interface Plugin {
info: PluginInfo;
component: NamedExoticComponent | FC;
+ i18nConfig?;
+ hooks?: {
+ useRender?: Array<(element: HTMLElement | null) => void>;
+ };
+ activated?: boolean;
}
interface I18nResource {
From aade1d9baffb1a6dfb40fd92aaf86f4f318474cf Mon Sep 17 00:00:00 2001
From: shuai
Date: Tue, 9 Apr 2024 10:15:22 +0800
Subject: [PATCH 7/7] feat: use captcha plugin replace captcha modal component
---
.vscode/settings.json | 5 +-
i18n/af_ZA.yaml | 2 +-
i18n/ar_SA.yaml | 2 +-
i18n/az_AZ.yaml | 2 +-
i18n/bal_BA.yaml | 2 +-
i18n/ban_ID.yaml | 2 +-
i18n/bn_BD.yaml | 2 +-
i18n/bs_BA.yaml | 2 +-
i18n/ca_ES.yaml | 2 +-
i18n/cs_CZ.yaml | 2 +-
i18n/cy_GB.yaml | 2 +-
i18n/da_DK.yaml | 2 +-
i18n/el_GR.yaml | 2 +-
i18n/en_US.yaml | 2 +-
i18n/fi_FI.yaml | 2 +-
i18n/he_IL.yaml | 2 +-
i18n/hi_IN.yaml | 2 +-
i18n/hu_HU.yaml | 2 +-
i18n/hy_AM.yaml | 2 +-
i18n/id_ID.yaml | 2 +-
i18n/ja_JP.yaml | 8 +-
i18n/ko_KR.yaml | 2 +-
i18n/nl_NL.yaml | 2 +-
i18n/no_NO.yaml | 2 +-
i18n/sq_AL.yaml | 2 +-
i18n/sr_SP.yaml | 2 +-
i18n/sv_SE.yaml | 2 +-
i18n/te_IN.yaml | 2 +-
i18n/tr_TR.yaml | 2 +-
i18n/uk_UA.yaml | 2 +-
i18n/vi_VN.yaml | 2 +-
ui/src/components/Actions/index.tsx | 80 ++---
ui/src/components/Comment/index.tsx | 284 ++++++++++--------
ui/src/components/Operate/index.tsx | 107 ++++---
ui/src/components/Unactivate/index.tsx | 16 +-
ui/src/hooks/useReportModal/index.tsx | 63 ++--
ui/src/pages/Questions/Ask/index.tsx | 159 +++++-----
.../components/InviteToAnswer/index.tsx | 47 +--
.../Detail/components/WriteAnswer/index.tsx | 78 ++---
ui/src/pages/Questions/EditAnswer/index.tsx | 76 ++---
.../components/ApproveDropdown/index.tsx | 89 +++---
.../Review/components/EditPostModal/index.tsx | 69 +++--
ui/src/pages/Search/index.tsx | 15 +-
.../AccountForgot/components/sendEmail.tsx | 17 +-
.../ChangeEmail/components/sendEmail.tsx | 17 +-
ui/src/pages/Users/Login/index.tsx | 29 +-
.../Register/components/SignUpForm/index.tsx | 14 +-
.../Account/components/ModifyEmail/index.tsx | 18 +-
.../Account/components/ModifyPass/index.tsx | 17 +-
.../builtin/HostingConnector/index.tsx | 7 +-
ui/src/plugins/builtin/SearchInfo/index.tsx | 7 +-
.../builtin/ThirdPartyConnector/index.tsx | 7 +-
ui/src/router/pathFactory.ts | 2 +-
ui/src/utils/pluginKit/index.ts | 17 +-
ui/src/utils/pluginKit/utils.ts | 16 +-
55 files changed, 763 insertions(+), 559 deletions(-)
diff --git a/.vscode/settings.json b/.vscode/settings.json
index db563c27c..b7d39e6e5 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -2,5 +2,8 @@
"eslint.workingDirectories": [
"ui"
],
- "explorer.autoReveal": "focusNoScroll"
+ "explorer.autoReveal": "focusNoScroll",
+ "cSpell.words": [
+ "grecaptcha"
+ ]
}
diff --git a/i18n/af_ZA.yaml b/i18n/af_ZA.yaml
index 71162ef52..8c6d0d7db 100644
--- a/i18n/af_ZA.yaml
+++ b/i18n/af_ZA.yaml
@@ -813,7 +813,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/ar_SA.yaml b/i18n/ar_SA.yaml
index 2f686840a..39b63e8ce 100644
--- a/i18n/ar_SA.yaml
+++ b/i18n/ar_SA.yaml
@@ -813,7 +813,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/az_AZ.yaml b/i18n/az_AZ.yaml
index 4270506ad..07e505418 100644
--- a/i18n/az_AZ.yaml
+++ b/i18n/az_AZ.yaml
@@ -805,7 +805,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/bal_BA.yaml b/i18n/bal_BA.yaml
index 4270506ad..07e505418 100644
--- a/i18n/bal_BA.yaml
+++ b/i18n/bal_BA.yaml
@@ -805,7 +805,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/ban_ID.yaml b/i18n/ban_ID.yaml
index 4270506ad..07e505418 100644
--- a/i18n/ban_ID.yaml
+++ b/i18n/ban_ID.yaml
@@ -805,7 +805,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/bn_BD.yaml b/i18n/bn_BD.yaml
index 4270506ad..07e505418 100644
--- a/i18n/bn_BD.yaml
+++ b/i18n/bn_BD.yaml
@@ -805,7 +805,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/bs_BA.yaml b/i18n/bs_BA.yaml
index 4270506ad..07e505418 100644
--- a/i18n/bs_BA.yaml
+++ b/i18n/bs_BA.yaml
@@ -805,7 +805,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/ca_ES.yaml b/i18n/ca_ES.yaml
index 2f686840a..39b63e8ce 100644
--- a/i18n/ca_ES.yaml
+++ b/i18n/ca_ES.yaml
@@ -813,7 +813,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/cs_CZ.yaml b/i18n/cs_CZ.yaml
index 3e5bc495f..0a260a046 100644
--- a/i18n/cs_CZ.yaml
+++ b/i18n/cs_CZ.yaml
@@ -1178,7 +1178,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/cy_GB.yaml b/i18n/cy_GB.yaml
index 8825ee2b7..a148a9fa7 100644
--- a/i18n/cy_GB.yaml
+++ b/i18n/cy_GB.yaml
@@ -1178,7 +1178,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/da_DK.yaml b/i18n/da_DK.yaml
index 88110a3e7..c210b1887 100644
--- a/i18n/da_DK.yaml
+++ b/i18n/da_DK.yaml
@@ -1178,7 +1178,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/el_GR.yaml b/i18n/el_GR.yaml
index 2f686840a..39b63e8ce 100644
--- a/i18n/el_GR.yaml
+++ b/i18n/el_GR.yaml
@@ -813,7 +813,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml
index 0221cfa02..ee47b3e1d 100644
--- a/i18n/en_US.yaml
+++ b/i18n/en_US.yaml
@@ -1215,7 +1215,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/fi_FI.yaml b/i18n/fi_FI.yaml
index 2f686840a..39b63e8ce 100644
--- a/i18n/fi_FI.yaml
+++ b/i18n/fi_FI.yaml
@@ -813,7 +813,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/he_IL.yaml b/i18n/he_IL.yaml
index 2f686840a..39b63e8ce 100644
--- a/i18n/he_IL.yaml
+++ b/i18n/he_IL.yaml
@@ -813,7 +813,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/hi_IN.yaml b/i18n/hi_IN.yaml
index 88110a3e7..c210b1887 100644
--- a/i18n/hi_IN.yaml
+++ b/i18n/hi_IN.yaml
@@ -1178,7 +1178,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/hu_HU.yaml b/i18n/hu_HU.yaml
index 2f686840a..39b63e8ce 100644
--- a/i18n/hu_HU.yaml
+++ b/i18n/hu_HU.yaml
@@ -813,7 +813,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/hy_AM.yaml b/i18n/hy_AM.yaml
index 4270506ad..07e505418 100644
--- a/i18n/hy_AM.yaml
+++ b/i18n/hy_AM.yaml
@@ -805,7 +805,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/id_ID.yaml b/i18n/id_ID.yaml
index e9277a5e4..e0278c462 100644
--- a/i18n/id_ID.yaml
+++ b/i18n/id_ID.yaml
@@ -1178,7 +1178,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/ja_JP.yaml b/i18n/ja_JP.yaml
index bfb9b0324..f4a290ace 100644
--- a/i18n/ja_JP.yaml
+++ b/i18n/ja_JP.yaml
@@ -518,7 +518,7 @@ ui:
edit_tag: タグを編集
ask_a_question: 質問を追加
edit_question: 質問を編集
- edit_answer: 回答を編集
+ edit_answer: 回答を編集
search: 検索
posts_containing: Posts containing
settings: 設定
@@ -763,8 +763,8 @@ ui:
Use comments to reply to other users or notify them of changes. If you are adding new information, edit your post instead of commenting.
tip_vote: It adds something useful to the post
edit_answer:
- title: 回答を編集
- default_reason: 回答を編集
+ title: 回答を編集
+ default_reason: 回答を編集
default_first_reason: 回答を追加
form:
fields:
@@ -1178,7 +1178,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/ko_KR.yaml b/i18n/ko_KR.yaml
index f0bc42c81..d04af1b59 100644
--- a/i18n/ko_KR.yaml
+++ b/i18n/ko_KR.yaml
@@ -1178,7 +1178,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/nl_NL.yaml b/i18n/nl_NL.yaml
index 2f686840a..39b63e8ce 100644
--- a/i18n/nl_NL.yaml
+++ b/i18n/nl_NL.yaml
@@ -813,7 +813,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/no_NO.yaml b/i18n/no_NO.yaml
index 2f686840a..39b63e8ce 100644
--- a/i18n/no_NO.yaml
+++ b/i18n/no_NO.yaml
@@ -813,7 +813,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/sq_AL.yaml b/i18n/sq_AL.yaml
index 4270506ad..07e505418 100644
--- a/i18n/sq_AL.yaml
+++ b/i18n/sq_AL.yaml
@@ -805,7 +805,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/sr_SP.yaml b/i18n/sr_SP.yaml
index 2f686840a..39b63e8ce 100644
--- a/i18n/sr_SP.yaml
+++ b/i18n/sr_SP.yaml
@@ -813,7 +813,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/sv_SE.yaml b/i18n/sv_SE.yaml
index 7cef69322..2030c9515 100644
--- a/i18n/sv_SE.yaml
+++ b/i18n/sv_SE.yaml
@@ -1176,7 +1176,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/te_IN.yaml b/i18n/te_IN.yaml
index 8be7a385b..bc5070233 100644
--- a/i18n/te_IN.yaml
+++ b/i18n/te_IN.yaml
@@ -1178,7 +1178,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/tr_TR.yaml b/i18n/tr_TR.yaml
index c9cfe1c9c..7a9cc5918 100644
--- a/i18n/tr_TR.yaml
+++ b/i18n/tr_TR.yaml
@@ -1178,7 +1178,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/uk_UA.yaml b/i18n/uk_UA.yaml
index 21421f664..acd8b2f84 100644
--- a/i18n/uk_UA.yaml
+++ b/i18n/uk_UA.yaml
@@ -1176,7 +1176,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/i18n/vi_VN.yaml b/i18n/vi_VN.yaml
index acf8084cd..925a1434b 100644
--- a/i18n/vi_VN.yaml
+++ b/i18n/vi_VN.yaml
@@ -1178,7 +1178,7 @@ ui:
more: More
tips:
title: Advanced Search Tips
- tag: "<1>[tag]1> search withing a tag"
+ tag: "<1>[tag]1> search with a tag"
user: "<1>user:username1> search by author"
answer: "<1>answers:01> unanswered questions"
score: "<1>score:31> posts with a 3+ score"
diff --git a/ui/src/components/Actions/index.tsx b/ui/src/components/Actions/index.tsx
index b83571054..b58d0c8cd 100644
--- a/ui/src/components/Actions/index.tsx
+++ b/ui/src/components/Actions/index.tsx
@@ -25,7 +25,8 @@ import classNames from 'classnames';
import { Icon } from '@/components';
import { loggedUserInfoStore } from '@/stores';
-import { useToast, useCaptchaModal } from '@/hooks';
+import { useToast } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import { tryNormalLogged } from '@/utils/guard';
import { bookmark, postVote } from '@/services';
import * as Types from '@/common/interface';
@@ -56,7 +57,7 @@ const Index: FC = ({ className, data, source }) => {
const { username = '' } = loggedUserInfoStore((state) => state.user);
const toast = useToast();
const { t } = useTranslation();
- const vCaptcha = useCaptchaModal('vote');
+ const vCaptcha = useCaptchaPlugin('vote');
useEffect(() => {
if (data) {
@@ -70,6 +71,42 @@ const Index: FC = ({ className, data, source }) => {
}
}, []);
+ const submitVote = (type) => {
+ const isCancel = (type === 'up' && like) || (type === 'down' && hate);
+ const imgCode: Types.ImgCodeReq = {
+ captcha_id: undefined,
+ captcha_code: undefined,
+ };
+ vCaptcha?.resolveCaptchaReq?.(imgCode);
+
+ postVote(
+ {
+ object_id: data?.id,
+ is_cancel: isCancel,
+ ...imgCode,
+ },
+ type,
+ )
+ .then(async (res) => {
+ await vCaptcha?.close();
+ setVotes(res.votes);
+ setLike(res.vote_status === 'vote_up');
+ setHated(res.vote_status === 'vote_down');
+ })
+ .catch((err) => {
+ if (err?.isError) {
+ vCaptcha?.handleCaptchaError(err.list);
+ }
+ const errMsg = err?.value;
+ if (errMsg) {
+ toast.onShow({
+ msg: errMsg,
+ variant: 'danger',
+ });
+ }
+ });
+ };
+
const handleVote = (type: 'up' | 'down') => {
if (!tryNormalLogged(true)) {
return;
@@ -82,39 +119,14 @@ const Index: FC = ({ className, data, source }) => {
});
return;
}
- const isCancel = (type === 'up' && like) || (type === 'down' && hate);
+
+ if (!vCaptcha) {
+ submitVote(type);
+ return;
+ }
+
vCaptcha.check(() => {
- const imgCode: Types.ImgCodeReq = {
- captcha_id: undefined,
- captcha_code: undefined,
- };
- vCaptcha.resolveCaptchaReq(imgCode);
- postVote(
- {
- object_id: data?.id,
- is_cancel: isCancel,
- ...imgCode,
- },
- type,
- )
- .then(async (res) => {
- await vCaptcha.close();
- setVotes(res.votes);
- setLike(res.vote_status === 'vote_up');
- setHated(res.vote_status === 'vote_down');
- })
- .catch((err) => {
- if (err?.isError) {
- vCaptcha.handleCaptchaError(err.list);
- }
- const errMsg = err?.value;
- if (errMsg) {
- toast.onShow({
- msg: errMsg,
- variant: 'danger',
- });
- }
- });
+ submitVote(type);
});
};
diff --git a/ui/src/components/Comment/index.tsx b/ui/src/components/Comment/index.tsx
index 06ecfc26f..ca69b2dee 100644
--- a/ui/src/components/Comment/index.tsx
+++ b/ui/src/components/Comment/index.tsx
@@ -35,6 +35,7 @@ import {
bgFadeOut,
} from '@/utils';
import { tryNormalLogged } from '@/utils/guard';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import {
useQueryComments,
addComment,
@@ -66,9 +67,9 @@ const Comment = ({ objectId, mode, commentId }) => {
const reportModal = useReportModal();
const addCaptcha = useCaptchaModal('comment');
- const editCaptcha = useCaptchaModal('edit');
- const dCaptcha = useCaptchaModal('delete');
- const vCaptcha = useCaptchaModal('vote');
+ const editCaptcha = useCaptchaPlugin('edit');
+ const dCaptcha = useCaptchaPlugin('delete');
+ const vCaptcha = useCaptchaPlugin('vote');
const { t } = useTranslation('translation', { keyPrefix: 'comment' });
@@ -136,6 +137,85 @@ const Comment = ({ objectId, mode, commentId }) => {
);
};
+ const submitUpdateComment = (params, item) => {
+ const up = {
+ ...params,
+ comment_id: item.comment_id,
+ captcha_code: undefined,
+ captcha_id: undefined,
+ };
+ editCaptcha?.resolveCaptchaReq(up);
+
+ return updateComment(up)
+ .then(async (res) => {
+ await editCaptcha?.close();
+ setComments(
+ comments.map((comment) => {
+ if (comment.comment_id === item.comment_id) {
+ comment.showEdit = false;
+ comment.parsed_text = res.parsed_text;
+ comment.original_text = res.original_text;
+ }
+ return comment;
+ }),
+ );
+ })
+ .catch((err) => {
+ if (err.isError) {
+ const captchaErr = editCaptcha?.handleCaptchaError(err.list);
+ // If it is not a CAPTCHA error, leave it to the subsequent error handling logic to continue processing.
+ if (!(captchaErr && err.list.length === 1)) {
+ return Promise.reject(err);
+ }
+ }
+ return Promise.resolve();
+ });
+ };
+
+ const submitAddComment = (params, item) => {
+ const req = {
+ ...params,
+ captcha_code: undefined,
+ captcha_id: undefined,
+ };
+ addCaptcha?.resolveCaptchaReq(req);
+
+ return addComment(req)
+ .then(async (res) => {
+ await addCaptcha?.close();
+ if (item.type === 'reply') {
+ const index = comments.findIndex(
+ (comment) => comment.comment_id === item.comment_id,
+ );
+ updateCurrentReplyId('');
+ comments.splice(index + 1, 0, res);
+ setComments([...comments]);
+ } else {
+ setComments([
+ ...comments.map((comment) => {
+ if (comment.comment_id === item.comment_id) {
+ updateCurrentReplyId('');
+ }
+ return comment;
+ }),
+ res,
+ ]);
+ }
+
+ setVisibleComment(false);
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ const captchaErr = addCaptcha?.handleCaptchaError(ex.list);
+ // If it is not a CAPTCHA error, leave it to the subsequent error handling logic to continue processing.
+ if (!(captchaErr && ex.list.length === 1)) {
+ return Promise.reject(ex);
+ }
+ }
+ return Promise.resolve();
+ });
+ };
+
const handleSendReply = (item) => {
const users = matchedUsers(item.value);
const userNames = unionBy(users.map((user) => user.userName));
@@ -153,85 +233,36 @@ const Comment = ({ objectId, mode, commentId }) => {
};
if (item.type === 'edit') {
- return editCaptcha.check(() => {
- const up = {
- ...params,
- comment_id: item.comment_id,
- captcha_code: undefined,
- captcha_id: undefined,
- };
- editCaptcha.resolveCaptchaReq(up);
-
- return updateComment(up)
- .then(async (res) => {
- await editCaptcha.close();
- setComments(
- comments.map((comment) => {
- if (comment.comment_id === item.comment_id) {
- comment.showEdit = false;
- comment.parsed_text = res.parsed_text;
- comment.original_text = res.original_text;
- }
- return comment;
- }),
- );
- })
- .catch((err) => {
- if (err.isError) {
- const captchaErr = editCaptcha.handleCaptchaError(err.list);
- // If it is not a CAPTCHA error, leave it to the subsequent error handling logic to continue processing.
- if (!(captchaErr && err.list.length === 1)) {
- return Promise.reject(err);
- }
- }
- return Promise.resolve();
- });
- });
+ if (!editCaptcha) {
+ return submitUpdateComment(params, item);
+ }
+ return editCaptcha.check(() => submitUpdateComment(params, item));
}
- return addCaptcha.check(() => {
- const req = {
- ...params,
- captcha_code: undefined,
- captcha_id: undefined,
- };
- addCaptcha.resolveCaptchaReq(req);
-
- return addComment(req)
- .then(async (res) => {
- await addCaptcha.close();
- if (item.type === 'reply') {
- const index = comments.findIndex(
- (comment) => comment.comment_id === item.comment_id,
- );
- updateCurrentReplyId('');
- comments.splice(index + 1, 0, res);
- setComments([...comments]);
- } else {
- setComments([
- ...comments.map((comment) => {
- if (comment.comment_id === item.comment_id) {
- updateCurrentReplyId('');
- }
- return comment;
- }),
- res,
- ]);
- }
+ if (!addCaptcha) {
+ return submitAddComment(params, item);
+ }
- setVisibleComment(false);
- })
- .catch((ex) => {
- if (ex.isError) {
- const captchaErr = addCaptcha.handleCaptchaError(ex.list);
- // If it is not a CAPTCHA error, leave it to the subsequent error handling logic to continue processing.
- if (!(captchaErr && ex.list.length === 1)) {
- return Promise.reject(ex);
- }
- }
- return Promise.resolve();
- });
- });
+ return addCaptcha.check(() => submitAddComment(params, item));
+ };
+
+ const submitDeleteComment = (id) => {
+ const imgCode = { captcha_id: undefined, captcha_code: undefined };
+ dCaptcha?.resolveCaptchaReq(imgCode);
+
+ deleteComment(id, imgCode)
+ .then(async () => {
+ await dCaptcha?.close();
+ if (pageIndex === 0) {
+ mutate();
+ }
+ setComments(comments.filter((item) => item.comment_id !== id));
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ dCaptcha?.handleCaptchaError(ex.list);
+ }
+ });
};
const handleDelete = (id) => {
@@ -241,67 +272,64 @@ const Comment = ({ objectId, mode, commentId }) => {
confirmBtnVariant: 'danger',
confirmText: t('delete', { keyPrefix: 'btns' }),
onConfirm: () => {
+ if (!dCaptcha) {
+ submitDeleteComment(id);
+ return;
+ }
dCaptcha.check(() => {
- const imgCode = { captcha_id: undefined, captcha_code: undefined };
- dCaptcha.resolveCaptchaReq(imgCode);
-
- deleteComment(id, imgCode)
- .then(async () => {
- await dCaptcha.close();
- if (pageIndex === 0) {
- mutate();
- }
- setComments(comments.filter((item) => item.comment_id !== id));
- })
- .catch((ex) => {
- if (ex.isError) {
- dCaptcha.handleCaptchaError(ex.list);
- }
- });
+ submitDeleteComment(id);
});
},
});
};
+ const submitVoteComment = (id, is_cancel) => {
+ const imgCode: Types.ImgCodeReq = {
+ captcha_id: undefined,
+ captcha_code: undefined,
+ };
+ vCaptcha?.resolveCaptchaReq(imgCode);
+
+ postVote(
+ {
+ object_id: id,
+ is_cancel,
+ ...imgCode,
+ },
+ 'up',
+ )
+ .then(async () => {
+ await vCaptcha?.close();
+ setComments(
+ comments.map((item) => {
+ if (item.comment_id === id) {
+ item.vote_count = is_cancel
+ ? item.vote_count - 1
+ : item.vote_count + 1;
+ item.is_vote = !is_cancel;
+ }
+ return item;
+ }),
+ );
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ vCaptcha?.handleCaptchaError(ex.list);
+ }
+ });
+ };
const handleVote = (id, is_cancel) => {
if (!tryNormalLogged(true)) {
return;
}
+ if (!vCaptcha) {
+ submitVoteComment(id, is_cancel);
+ return;
+ }
+
vCaptcha.check(() => {
- const imgCode: Types.ImgCodeReq = {
- captcha_id: undefined,
- captcha_code: undefined,
- };
- vCaptcha.resolveCaptchaReq(imgCode);
-
- postVote(
- {
- object_id: id,
- is_cancel,
- ...imgCode,
- },
- 'up',
- )
- .then(async () => {
- await vCaptcha.close();
- setComments(
- comments.map((item) => {
- if (item.comment_id === id) {
- item.vote_count = is_cancel
- ? item.vote_count - 1
- : item.vote_count + 1;
- item.is_vote = !is_cancel;
- }
- return item;
- }),
- );
- })
- .catch((ex) => {
- if (ex.isError) {
- vCaptcha.handleCaptchaError(ex.list);
- }
- });
+ submitVoteComment(id, is_cancel);
});
};
diff --git a/ui/src/components/Operate/index.tsx b/ui/src/components/Operate/index.tsx
index e04416f2f..47b4463f0 100644
--- a/ui/src/components/Operate/index.tsx
+++ b/ui/src/components/Operate/index.tsx
@@ -23,7 +23,8 @@ import { Link, useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Modal } from '@/components';
-import { useReportModal, useToast, useCaptchaModal } from '@/hooks';
+import { useReportModal, useToast } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import { QuestionOperationReq } from '@/common/interface';
import Share from '../Share';
import {
@@ -63,7 +64,7 @@ const Index: FC = ({
const toast = useToast();
const navigate = useNavigate();
const reportModal = useReportModal();
- const dCaptcha = useCaptchaModal('delete');
+ const dCaptcha = useCaptchaPlugin('delete');
const refreshQuestion = () => {
callback?.('default');
@@ -88,6 +89,55 @@ const Index: FC = ({
});
};
+ const submitDeleteQuestion = () => {
+ const req = {
+ id: qid,
+ captcha_code: undefined,
+ captcha_id: undefined,
+ };
+ dCaptcha?.resolveCaptchaReq(req);
+
+ deleteQuestion(req)
+ .then(async () => {
+ await dCaptcha?.close();
+ toast.onShow({
+ msg: t('post_deleted', { keyPrefix: 'messages' }),
+ variant: 'success',
+ });
+ callback?.('delete_question');
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ dCaptcha?.handleCaptchaError(ex.list);
+ }
+ });
+ };
+
+ const submitDeleteAnswer = () => {
+ const req = {
+ id: aid,
+ captcha_code: undefined,
+ captcha_id: undefined,
+ };
+ dCaptcha?.resolveCaptchaReq(req);
+
+ deleteAnswer(req)
+ .then(async () => {
+ await dCaptcha?.close();
+ // refresh page
+ toast.onShow({
+ msg: t('tip_answer_deleted'),
+ variant: 'success',
+ });
+ callback?.('delete_answer');
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ dCaptcha?.handleCaptchaError(ex.list);
+ }
+ });
+ };
+
const handleDelete = () => {
if (type === 'question') {
Modal.confirm({
@@ -97,28 +147,12 @@ const Index: FC = ({
confirmBtnVariant: 'danger',
confirmText: t('delete', { keyPrefix: 'btns' }),
onConfirm: () => {
+ if (!dCaptcha) {
+ submitDeleteQuestion();
+ return;
+ }
dCaptcha.check(() => {
- const req = {
- id: qid,
- captcha_code: undefined,
- captcha_id: undefined,
- };
- dCaptcha.resolveCaptchaReq(req);
-
- deleteQuestion(req)
- .then(async () => {
- await dCaptcha.close();
- toast.onShow({
- msg: t('post_deleted', { keyPrefix: 'messages' }),
- variant: 'success',
- });
- callback?.('delete_question');
- })
- .catch((ex) => {
- if (ex.isError) {
- dCaptcha.handleCaptchaError(ex.list);
- }
- });
+ submitDeleteQuestion();
});
},
});
@@ -132,29 +166,12 @@ const Index: FC = ({
confirmBtnVariant: 'danger',
confirmText: t('delete', { keyPrefix: 'btns' }),
onConfirm: () => {
+ if (!dCaptcha) {
+ submitDeleteAnswer();
+ return;
+ }
dCaptcha.check(() => {
- const req = {
- id: aid,
- captcha_code: undefined,
- captcha_id: undefined,
- };
- dCaptcha.resolveCaptchaReq(req);
-
- deleteAnswer(req)
- .then(async () => {
- await dCaptcha.close();
- // refresh page
- toast.onShow({
- msg: t('tip_answer_deleted'),
- variant: 'success',
- });
- callback?.('delete_answer');
- })
- .catch((ex) => {
- if (ex.isError) {
- dCaptcha.handleCaptchaError(ex.list);
- }
- });
+ submitDeleteAnswer();
});
},
});
diff --git a/ui/src/components/Unactivate/index.tsx b/ui/src/components/Unactivate/index.tsx
index e96e28754..163da2bd3 100644
--- a/ui/src/components/Unactivate/index.tsx
+++ b/ui/src/components/Unactivate/index.tsx
@@ -26,7 +26,7 @@ import type { ImgCodeReq, FormDataType } from '@/common/interface';
import { loggedUserInfoStore } from '@/stores';
import { resendEmail } from '@/services';
import { handleFormError } from '@/utils';
-import { useCaptchaModal } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
interface IProps {
visible?: boolean;
@@ -44,12 +44,12 @@ const Index: React.FC = () => {
},
});
- const emailCaptcha = useCaptchaModal('email');
+ const emailCaptcha = useCaptchaPlugin('email');
const submit = () => {
let req: ImgCodeReq = {};
- const imgCode = emailCaptcha.getCaptcha();
- if (imgCode.verify) {
+ const imgCode = emailCaptcha?.getCaptcha();
+ if (imgCode?.verify) {
req = {
captcha_code: imgCode.captcha_code,
captcha_id: imgCode.captcha_id,
@@ -57,12 +57,12 @@ const Index: React.FC = () => {
}
resendEmail(req)
.then(async () => {
- await emailCaptcha.close();
+ await emailCaptcha?.close();
setSuccess(true);
})
.catch((err) => {
if (err.isError) {
- emailCaptcha.handleCaptchaError(err.list);
+ emailCaptcha?.handleCaptchaError(err.list);
const data = handleFormError(err, formData);
setFormData({ ...data });
}
@@ -71,6 +71,10 @@ const Index: React.FC = () => {
const onSentEmail = (evt) => {
evt.preventDefault();
+ if (!emailCaptcha) {
+ submit();
+ return;
+ }
emailCaptcha.check(() => {
submit();
});
diff --git a/ui/src/hooks/useReportModal/index.tsx b/ui/src/hooks/useReportModal/index.tsx
index e20b8eb61..843472cd7 100644
--- a/ui/src/hooks/useReportModal/index.tsx
+++ b/ui/src/hooks/useReportModal/index.tsx
@@ -23,7 +23,8 @@ import { useTranslation } from 'react-i18next';
import ReactDOM from 'react-dom/client';
-import { useToast, useCaptchaModal } from '@/hooks';
+import { useToast } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import type * as Type from '@/common/interface';
import {
reportList,
@@ -65,7 +66,7 @@ const useReportModal = (callback?: () => void) => {
const [show, setShow] = useState(false);
const [list, setList] = useState([]);
- const rCaptcha = useCaptchaModal('report');
+ const rCaptcha = useCaptchaPlugin('report');
useEffect(() => {
const div = document.createElement('div');
@@ -168,6 +169,34 @@ const useReportModal = (callback?: () => void) => {
return true;
};
+ const submitReport = (data) => {
+ const flagReq = {
+ source: data.type,
+ report_type: reportType.type,
+ object_id: data.id,
+ content: content.value,
+ captcha_code: undefined,
+ captcha_id: undefined,
+ };
+ rCaptcha?.resolveCaptchaReq(flagReq);
+
+ postReport(flagReq)
+ .then(async () => {
+ await rCaptcha?.close();
+ toast.onShow({
+ msg: t('flag_success', { keyPrefix: 'toast' }),
+ variant: 'warning',
+ });
+ onClose();
+ asyncCallback();
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ rCaptcha?.handleCaptchaError(ex.list);
+ }
+ });
+ };
+
const handleSubmit = () => {
if (!params) {
return;
@@ -205,32 +234,12 @@ const useReportModal = (callback?: () => void) => {
return;
}
if (!params.isBackend && params.action === 'flag') {
+ if (!rCaptcha) {
+ submitReport(params);
+ return;
+ }
rCaptcha.check(() => {
- const flagReq = {
- source: params.type,
- report_type: reportType.type,
- object_id: params.id,
- content: content.value,
- captcha_code: undefined,
- captcha_id: undefined,
- };
- rCaptcha.resolveCaptchaReq(flagReq);
-
- postReport(flagReq)
- .then(async () => {
- await rCaptcha.close();
- toast.onShow({
- msg: t('flag_success', { keyPrefix: 'toast' }),
- variant: 'warning',
- });
- onClose();
- asyncCallback();
- })
- .catch((ex) => {
- if (ex.isError) {
- rCaptcha.handleCaptchaError(ex.list);
- }
- });
+ submitReport(params);
});
}
diff --git a/ui/src/pages/Questions/Ask/index.tsx b/ui/src/pages/Questions/Ask/index.tsx
index 9a1ff14a9..367ccfca8 100644
--- a/ui/src/pages/Questions/Ask/index.tsx
+++ b/ui/src/pages/Questions/Ask/index.tsx
@@ -27,7 +27,7 @@ import classNames from 'classnames';
import isEqual from 'lodash/isEqual';
import debounce from 'lodash/debounce';
-import { usePageTags, usePromptWithUnload, useCaptchaModal } from '@/hooks';
+import { usePageTags, usePromptWithUnload } from '@/hooks';
import { Editor, EditorRef, TagSelector } from '@/components';
import type * as Type from '@/common/interface';
import { DRAFT_QUESTION_STORAGE_KEY } from '@/common/constants';
@@ -42,6 +42,7 @@ import {
} from '@/services';
import { handleFormError, SaveDraft, storageExpires } from '@/utils';
import { pathFactory } from '@/router/pathFactory';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import SearchQuestion from './components/SearchQuestion';
@@ -120,8 +121,8 @@ const Ask = () => {
const isEdit = qid !== undefined;
- const saveCaptcha = useCaptchaModal('question');
- const editCaptcha = useCaptchaModal('edit');
+ const saveCaptcha = useCaptchaPlugin('question');
+ const editCaptcha = useCaptchaPlugin('edit');
const removeDraft = () => {
saveDraft.save.cancel();
@@ -276,6 +277,79 @@ const Ask = () => {
}
};
+ const submitModifyQuestion = (params) => {
+ contentChangedRef.current = false;
+ const ep = {
+ ...params,
+ id: qid,
+ edit_summary: formData.edit_summary.value,
+ };
+ const imgCode = editCaptcha?.getCaptcha();
+ if (imgCode?.verify) {
+ ep.captcha_code = imgCode.captcha_code;
+ ep.captcha_id = imgCode.captcha_id;
+ }
+ modifyQuestion(ep)
+ .then(async (res) => {
+ await editCaptcha?.close();
+ navigate(pathFactory.questionLanding(qid, res?.url_title), {
+ state: { isReview: res?.wait_for_review },
+ });
+ })
+ .catch((err) => {
+ if (err.isError) {
+ editCaptcha?.handleCaptchaError(err.list);
+ const data = handleFormError(err, formData);
+ setFormData({ ...data });
+ }
+ });
+ };
+
+ const submitQuestion = async (params) => {
+ contentChangedRef.current = false;
+ const imgCode = saveCaptcha?.getCaptcha();
+ if (imgCode?.verify) {
+ params.captcha_code = imgCode.captcha_code;
+ params.captcha_id = imgCode.captcha_id;
+ }
+ let res;
+ if (checked) {
+ res = await saveQuestionWithAnswer({
+ ...params,
+ answer_content: formData.answer_content.value,
+ }).catch((err) => {
+ if (err.isError) {
+ const captchaErr = saveCaptcha?.handleCaptchaError(err.list);
+ if (!(captchaErr && err.list.length === 1)) {
+ const data = handleFormError(err, formData);
+ setFormData({ ...data });
+ }
+ }
+ });
+ } else {
+ res = await saveQuestion(params).catch((err) => {
+ if (err.isError) {
+ const captchaErr = saveCaptcha?.handleCaptchaError(err.list);
+ if (!(captchaErr && err.list.length === 1)) {
+ const data = handleFormError(err, formData);
+ setFormData({ ...data });
+ }
+ }
+ });
+ }
+
+ const id = res?.id || res?.question?.id;
+ if (id) {
+ await saveCaptcha?.close();
+ if (checked) {
+ navigate(pathFactory.questionLanding(id, res?.question?.url_title));
+ } else {
+ navigate(pathFactory.questionLanding(id, res?.url_title));
+ }
+ }
+ removeDraft();
+ };
+
const handleSubmit = async (event: React.FormEvent) => {
event.preventDefault();
event.stopPropagation();
@@ -287,77 +361,20 @@ const Ask = () => {
};
if (isEdit) {
+ if (!editCaptcha) {
+ submitModifyQuestion(params);
+ return;
+ }
editCaptcha.check(() => {
- contentChangedRef.current = false;
- const ep = {
- ...params,
- id: qid,
- edit_summary: formData.edit_summary.value,
- };
- const imgCode = editCaptcha.getCaptcha();
- if (imgCode.verify) {
- ep.captcha_code = imgCode.captcha_code;
- ep.captcha_id = imgCode.captcha_id;
- }
- modifyQuestion(ep)
- .then(async (res) => {
- await editCaptcha.close();
- navigate(pathFactory.questionLanding(qid, res?.url_title), {
- state: { isReview: res?.wait_for_review },
- });
- })
- .catch((err) => {
- if (err.isError) {
- editCaptcha.handleCaptchaError(err.list);
- const data = handleFormError(err, formData);
- setFormData({ ...data });
- }
- });
+ submitModifyQuestion(params);
});
} else {
- saveCaptcha.check(async () => {
- contentChangedRef.current = false;
- const imgCode = saveCaptcha.getCaptcha();
- if (imgCode.verify) {
- params.captcha_code = imgCode.captcha_code;
- params.captcha_id = imgCode.captcha_id;
- }
- let res;
- if (checked) {
- res = await saveQuestionWithAnswer({
- ...params,
- answer_content: formData.answer_content.value,
- }).catch((err) => {
- if (err.isError) {
- const captchaErr = saveCaptcha.handleCaptchaError(err.list);
- if (!(captchaErr && err.list.length === 1)) {
- const data = handleFormError(err, formData);
- setFormData({ ...data });
- }
- }
- });
- } else {
- res = await saveQuestion(params).catch((err) => {
- if (err.isError) {
- const captchaErr = saveCaptcha.handleCaptchaError(err.list);
- if (!(captchaErr && err.list.length === 1)) {
- const data = handleFormError(err, formData);
- setFormData({ ...data });
- }
- }
- });
- }
-
- const id = res?.id || res?.question?.id;
- if (id) {
- await saveCaptcha.close();
- if (checked) {
- navigate(pathFactory.questionLanding(id, res?.question?.url_title));
- } else {
- navigate(pathFactory.questionLanding(id, res?.url_title));
- }
- }
- removeDraft();
+ if (!saveCaptcha) {
+ submitQuestion(params);
+ return;
+ }
+ saveCaptcha?.check(async () => {
+ submitQuestion(params);
});
}
};
diff --git a/ui/src/pages/Questions/Detail/components/InviteToAnswer/index.tsx b/ui/src/pages/Questions/Detail/components/InviteToAnswer/index.tsx
index 82f9fa275..b8e29942c 100644
--- a/ui/src/pages/Questions/Detail/components/InviteToAnswer/index.tsx
+++ b/ui/src/pages/Questions/Detail/components/InviteToAnswer/index.tsx
@@ -27,7 +27,7 @@ import classNames from 'classnames';
import { Avatar } from '@/components';
import { getInviteUser, putInviteUser } from '@/services';
import type * as Type from '@/common/interface';
-import { useCaptchaModal } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import PeopleDropdown from './PeopleDropdown';
@@ -44,7 +44,7 @@ const Index: FC = ({ questionId, readOnly = false }) => {
const [editing, setEditing] = useState(false);
const [users, setUsers] = useState([]);
- const iaCaptcha = useCaptchaModal('invitation_answer');
+ const iaCaptcha = useCaptchaPlugin('invitation_answer');
const initInviteUsers = () => {
if (!questionId) {
@@ -74,28 +74,35 @@ const Index: FC = ({ questionId, readOnly = false }) => {
setUsers(userList);
};
+ const submitInviteUser = () => {
+ const names = users.map((_) => {
+ return _.username;
+ });
+ const imgCode: Type.ImgCodeReq = {};
+ iaCaptcha?.resolveCaptchaReq(imgCode);
+ putInviteUser(questionId, names, imgCode)
+ .then(async () => {
+ await iaCaptcha?.close();
+ setEditing(false);
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ iaCaptcha?.handleCaptchaError(ex.list);
+ }
+ });
+ };
+
const saveInviteUsers = () => {
if (!users) {
return;
}
- const names = users.map((_) => {
- return _.username;
- });
- iaCaptcha.check(() => {
- const imgCode: Type.ImgCodeReq = {};
- iaCaptcha.resolveCaptchaReq(imgCode);
- putInviteUser(questionId, names, imgCode)
- .then(async () => {
- await iaCaptcha.close();
- setEditing(false);
- })
- .catch((ex) => {
- if (ex.isError) {
- iaCaptcha.handleCaptchaError(ex.list);
- }
- console.error('putInviteUser error: ', ex);
- });
- });
+
+ if (!iaCaptcha) {
+ submitInviteUser();
+ return;
+ }
+
+ iaCaptcha.check(() => submitInviteUser());
};
useEffect(() => {
diff --git a/ui/src/pages/Questions/Detail/components/WriteAnswer/index.tsx b/ui/src/pages/Questions/Detail/components/WriteAnswer/index.tsx
index 24a301f8c..456fbe370 100644
--- a/ui/src/pages/Questions/Detail/components/WriteAnswer/index.tsx
+++ b/ui/src/pages/Questions/Detail/components/WriteAnswer/index.tsx
@@ -25,7 +25,8 @@ import { Link } from 'react-router-dom';
import { marked } from 'marked';
import classNames from 'classnames';
-import { usePromptWithUnload, useCaptchaModal } from '@/hooks';
+import { usePromptWithUnload } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import { Editor, Modal, TextArea } from '@/components';
import { FormDataType, PostAnswerReq } from '@/common/interface';
import { postAnswer } from '@/services';
@@ -63,7 +64,7 @@ const Index: FC = ({ visible = false, data, callback }) => {
const [editorFocusState, setEditorFocusState] = useState(false);
const [hasDraft, setHasDraft] = useState(false);
const [showTips, setShowTips] = useState(data.loggedUserRank < 100);
- const aCaptcha = useCaptchaModal('answer');
+ const aCaptcha = useCaptchaPlugin('answer');
const writeInfo = writeSettingStore((state) => state.write);
const [editorCanSave, setEditorCanSave] = useState(false);
@@ -156,6 +157,40 @@ const Index: FC = ({ visible = false, data, callback }) => {
}
};
+ const submitAnswer = () => {
+ const params: PostAnswerReq = {
+ question_id: data?.qid,
+ content: formData.content.value,
+ html: marked.parse(formData.content.value),
+ };
+ const imgCode = aCaptcha?.getCaptcha();
+ if (imgCode?.verify) {
+ params.captcha_code = imgCode.captcha_code;
+ params.captcha_id = imgCode.captcha_id;
+ }
+ postAnswer(params)
+ .then(async (res) => {
+ await aCaptcha?.close();
+ setShowEditor(false);
+ setFormData({
+ content: {
+ value: '',
+ isInvalid: false,
+ errorMsg: '',
+ },
+ });
+ removeDraft();
+ callback?.(res.info);
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ aCaptcha?.handleCaptchaError(ex.list);
+ const stateData = handleFormError(ex, formData);
+ setFormData({ ...stateData });
+ }
+ });
+ };
+
const handleSubmit = () => {
if (!guard.tryNormalLogged(true)) {
return;
@@ -163,40 +198,11 @@ const Index: FC = ({ visible = false, data, callback }) => {
if (!checkValidated()) {
return;
}
-
- aCaptcha.check(() => {
- const params: PostAnswerReq = {
- question_id: data?.qid,
- content: formData.content.value,
- html: marked.parse(formData.content.value),
- };
- const imgCode = aCaptcha.getCaptcha();
- if (imgCode.verify) {
- params.captcha_code = imgCode.captcha_code;
- params.captcha_id = imgCode.captcha_id;
- }
- postAnswer(params)
- .then(async (res) => {
- await aCaptcha.close();
- setShowEditor(false);
- setFormData({
- content: {
- value: '',
- isInvalid: false,
- errorMsg: '',
- },
- });
- removeDraft();
- callback?.(res.info);
- })
- .catch((ex) => {
- if (ex.isError) {
- aCaptcha.handleCaptchaError(ex.list);
- const stateData = handleFormError(ex, formData);
- setFormData({ ...stateData });
- }
- });
- });
+ if (!aCaptcha) {
+ submitAnswer();
+ return;
+ }
+ aCaptcha.check(() => submitAnswer());
};
const clickBtn = () => {
diff --git a/ui/src/pages/Questions/EditAnswer/index.tsx b/ui/src/pages/Questions/EditAnswer/index.tsx
index fbb3e3279..49e7b33a1 100644
--- a/ui/src/pages/Questions/EditAnswer/index.tsx
+++ b/ui/src/pages/Questions/EditAnswer/index.tsx
@@ -26,7 +26,8 @@ import dayjs from 'dayjs';
import classNames from 'classnames';
import { handleFormError, scrollToDocTop } from '@/utils';
-import { usePageTags, usePromptWithUnload, useCaptchaModal } from '@/hooks';
+import { usePageTags, usePromptWithUnload } from '@/hooks';
+import { useCaptchaPlugin, useRenderHtmlPlugin } from '@/utils/pluginKit';
import { pathFactory } from '@/router/pathFactory';
import { Editor, EditorRef, Icon, htmlRender } from '@/components';
import type * as Type from '@/common/interface';
@@ -35,7 +36,6 @@ import {
modifyAnswer,
useQueryRevisions,
} from '@/services';
-import { useRenderHtmlPlugin } from '@/utils/pluginKit';
import './index.scss';
@@ -71,7 +71,7 @@ const Index = () => {
const [formData, setFormData] = useState(initFormData);
const [immData, setImmData] = useState(initFormData);
const [contentChanged, setContentChanged] = useState(false);
- const editCaptcha = useCaptchaModal('edit');
+ const editCaptcha = useCaptchaPlugin('edit');
useEffect(() => {
if (data?.info?.content) {
@@ -153,6 +153,39 @@ const Index = () => {
return bol;
};
+ const submitEditAnswer = () => {
+ const params: Type.AnswerParams = {
+ content: formData.content.value,
+ html: editorRef.current.getHtml(),
+ question_id: qid,
+ id: aid,
+ edit_summary: formData.description.value,
+ };
+ editCaptcha?.resolveCaptchaReq(params);
+
+ modifyAnswer(params)
+ .then(async (res) => {
+ await editCaptcha?.close();
+ navigate(
+ pathFactory.answerLanding({
+ questionId: qid,
+ slugTitle: data?.question?.url_title,
+ answerId: aid,
+ }),
+ {
+ state: { isReview: res?.wait_for_review },
+ },
+ );
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ editCaptcha?.handleCaptchaError(ex.list);
+ const stateData = handleFormError(ex, formData);
+ setFormData({ ...stateData });
+ }
+ });
+ };
+
const handleSubmit = async (event: React.FormEvent) => {
setContentChanged(false);
@@ -163,38 +196,11 @@ const Index = () => {
return;
}
- editCaptcha.check(() => {
- const params: Type.AnswerParams = {
- content: formData.content.value,
- html: editorRef.current.getHtml(),
- question_id: qid,
- id: aid,
- edit_summary: formData.description.value,
- };
- editCaptcha.resolveCaptchaReq(params);
-
- modifyAnswer(params)
- .then(async (res) => {
- await editCaptcha.close();
- navigate(
- pathFactory.answerLanding({
- questionId: qid,
- slugTitle: data?.question?.url_title,
- answerId: aid,
- }),
- {
- state: { isReview: res?.wait_for_review },
- },
- );
- })
- .catch((ex) => {
- if (ex.isError) {
- editCaptcha.handleCaptchaError(ex.list);
- const stateData = handleFormError(ex, formData);
- setFormData({ ...stateData });
- }
- });
- });
+ if (!editCaptcha) {
+ submitEditAnswer();
+ return;
+ }
+ editCaptcha.check(() => submitEditAnswer());
};
const handleSelectedRevision = (e) => {
const index = e.target.value;
diff --git a/ui/src/pages/Review/components/ApproveDropdown/index.tsx b/ui/src/pages/Review/components/ApproveDropdown/index.tsx
index f6a624716..b2c05c96a 100644
--- a/ui/src/pages/Review/components/ApproveDropdown/index.tsx
+++ b/ui/src/pages/Review/components/ApproveDropdown/index.tsx
@@ -23,7 +23,8 @@ import { useTranslation } from 'react-i18next';
import { Modal } from '@/components';
import { putFlagReviewAction } from '@/services';
-import { useCaptchaModal, useReportModal, useToast } from '@/hooks';
+import { useReportModal, useToast } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import type * as Type from '@/common/interface';
import EditPostModal from '../EditPostModal';
@@ -46,12 +47,52 @@ const Index: FC = ({
const [showEditPostModal, setShowEditPostModal] = useState(false);
const closeModal = useReportModal(approveCallback);
const toast = useToast();
- const dCaptcha = useCaptchaModal('delete');
+ const dCaptcha = useCaptchaPlugin('delete');
const handleEditPostModalState = () => {
setShowEditPostModal(!showEditPostModal);
};
+ const submitReviewAction = () => {
+ const req: Type.PutFlagReviewParams = {
+ operation_type: 'delete_post',
+ flag_id: String(itemData?.flag_id),
+ captcha_code: undefined,
+ captcha_id: undefined,
+ };
+ dCaptcha?.resolveCaptchaReq(req);
+
+ delete req.captcha_code;
+ delete req.captcha_id;
+
+ putFlagReviewAction(req)
+ .then(async () => {
+ await dCaptcha?.close();
+ let msg = '';
+ if (objectType === 'question') {
+ msg = t('post_deleted', { keyPrefix: 'messages' });
+ }
+ if (objectType === 'answer') {
+ msg = t('tip_answer_deleted');
+ }
+ if (objectType === 'answer' || objectType === 'question') {
+ toast.onShow({
+ msg,
+ variant: 'success',
+ });
+ }
+ approveCallback();
+ })
+ .catch((ex) => {
+ if (ex.isError) {
+ dCaptcha?.handleCaptchaError(ex.list);
+ }
+ })
+ .finally(() => {
+ setIsLoading(false);
+ });
+ };
+
const handleDelete = () => {
let content = '';
@@ -78,45 +119,11 @@ const Index: FC = ({
confirmBtnVariant: 'danger',
confirmText: t('delete', { keyPrefix: 'btns' }),
onConfirm: () => {
- dCaptcha.check(() => {
- const req: Type.PutFlagReviewParams = {
- operation_type: 'delete_post',
- flag_id: String(itemData?.flag_id),
- captcha_code: undefined,
- captcha_id: undefined,
- };
- dCaptcha.resolveCaptchaReq(req);
-
- delete req.captcha_code;
- delete req.captcha_id;
-
- putFlagReviewAction(req)
- .then(async () => {
- await dCaptcha.close();
- let msg = '';
- if (objectType === 'question') {
- msg = t('post_deleted', { keyPrefix: 'messages' });
- }
- if (objectType === 'answer') {
- msg = t('tip_answer_deleted');
- }
- if (objectType === 'answer' || objectType === 'question') {
- toast.onShow({
- msg,
- variant: 'success',
- });
- }
- approveCallback();
- })
- .catch((ex) => {
- if (ex.isError) {
- dCaptcha.handleCaptchaError(ex.list);
- }
- })
- .finally(() => {
- setIsLoading(false);
- });
- });
+ if (!dCaptcha) {
+ submitReviewAction();
+ return;
+ }
+ dCaptcha.check(() => submitReviewAction());
},
onCancel: () => {
setIsLoading(false);
diff --git a/ui/src/pages/Review/components/EditPostModal/index.tsx b/ui/src/pages/Review/components/EditPostModal/index.tsx
index 0eefd311d..4ee80b0aa 100644
--- a/ui/src/pages/Review/components/EditPostModal/index.tsx
+++ b/ui/src/pages/Review/components/EditPostModal/index.tsx
@@ -24,7 +24,8 @@ import { useTranslation } from 'react-i18next';
import classNames from 'classnames';
import { putFlagReviewAction } from '@/services';
-import { useCaptchaModal, usePageUsers } from '@/hooks';
+import { usePageUsers } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import { Editor, TagSelector, Mentions, TextArea } from '@/components';
import {
// matchedUsers,
@@ -89,7 +90,7 @@ const Index: FC = ({
const [loaded, setLoaded] = useState(false);
const pageUsers = usePageUsers();
- const editCaptcha = useCaptchaModal('edit');
+ const editCaptcha = useCaptchaPlugin('edit');
const onClose = (bol) => {
if (bol) {
@@ -160,14 +161,7 @@ const Index: FC = ({
return bol;
};
- const handleSubmit = (event: React.FormEvent) => {
- event.preventDefault();
- event.stopPropagation();
-
- if (!checkValidated()) {
- return;
- }
-
+ const submitFlagReviewAction = () => {
const params: Type.PutFlagReviewParams = {
title: formData.title.value,
content: formData.content.value,
@@ -191,28 +185,41 @@ const Index: FC = ({
delete params.title;
delete params.tags;
}
-
- editCaptcha.check(() => {
- if (objectType === 'question') {
- const imgCode = editCaptcha.getCaptcha();
- if (imgCode.verify) {
- params.captcha_code = imgCode.captcha_code;
- params.captcha_id = imgCode.captcha_id;
- }
+ if (objectType === 'question') {
+ const imgCode = editCaptcha?.getCaptcha();
+ if (imgCode?.verify) {
+ params.captcha_code = imgCode.captcha_code;
+ params.captcha_id = imgCode.captcha_id;
}
- putFlagReviewAction(params)
- .then(async () => {
- await editCaptcha.close();
- onClose(true);
- })
- .catch((err) => {
- if (err.isError) {
- editCaptcha.handleCaptchaError(err.list);
- const data = handleFormError(err, formData);
- setFormData({ ...data });
- }
- });
- });
+ }
+ putFlagReviewAction(params)
+ .then(async () => {
+ await editCaptcha?.close();
+ onClose(true);
+ })
+ .catch((err) => {
+ if (err.isError) {
+ editCaptcha?.handleCaptchaError(err.list);
+ const data = handleFormError(err, formData);
+ setFormData({ ...data });
+ }
+ });
+ };
+
+ const handleSubmit = (event: React.FormEvent) => {
+ event.preventDefault();
+ event.stopPropagation();
+
+ if (!checkValidated()) {
+ return;
+ }
+
+ if (!editCaptcha) {
+ submitFlagReviewAction();
+ return;
+ }
+
+ editCaptcha.check(() => submitFlagReviewAction());
};
const handleSelected = (val) => {
diff --git a/ui/src/pages/Search/index.tsx b/ui/src/pages/Search/index.tsx
index 8517172de..a016af707 100644
--- a/ui/src/pages/Search/index.tsx
+++ b/ui/src/pages/Search/index.tsx
@@ -22,7 +22,8 @@ import { useTranslation } from 'react-i18next';
import { useSearchParams } from 'react-router-dom';
import { useEffect, useState } from 'react';
-import { usePageTags, useCaptchaModal, useSkeletonControl } from '@/hooks';
+import { usePageTags, useSkeletonControl } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import { Pagination } from '@/components';
import { getSearchResult } from '@/services';
import type { SearchParams, SearchRes } from '@/common/interface';
@@ -51,7 +52,7 @@ const Index = () => {
});
const { count = 0, list = [], extra = null } = data || {};
- const searchCaptcha = useCaptchaModal('search');
+ const searchCaptcha = useCaptchaPlugin('search');
const doSearch = () => {
setIsLoading(true);
@@ -62,7 +63,7 @@ const Index = () => {
size: 20,
};
- const captcha = searchCaptcha.getCaptcha();
+ const captcha = searchCaptcha?.getCaptcha();
if (captcha?.verify) {
params.captcha_id = captcha.captcha_id;
params.captcha_code = captcha.captcha_code;
@@ -70,12 +71,12 @@ const Index = () => {
getSearchResult(params)
.then(async (resp) => {
- await searchCaptcha.close();
+ await searchCaptcha?.close();
setData(resp);
})
.catch((err) => {
if (err.isError) {
- searchCaptcha.handleCaptchaError(err.list);
+ searchCaptcha?.handleCaptchaError(err.list);
}
})
.finally(() => {
@@ -84,6 +85,10 @@ const Index = () => {
};
useEffect(() => {
+ if (!searchCaptcha) {
+ doSearch();
+ return;
+ }
searchCaptcha.check(() => {
doSearch();
});
diff --git a/ui/src/pages/Users/AccountForgot/components/sendEmail.tsx b/ui/src/pages/Users/AccountForgot/components/sendEmail.tsx
index acca34aa9..5d7d2c9bb 100644
--- a/ui/src/pages/Users/AccountForgot/components/sendEmail.tsx
+++ b/ui/src/pages/Users/AccountForgot/components/sendEmail.tsx
@@ -24,7 +24,7 @@ import { useTranslation } from 'react-i18next';
import type { PasswordResetReq, FormDataType } from '@/common/interface';
import { resetPassword } from '@/services';
import { handleFormError } from '@/utils';
-import { useCaptchaModal } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
interface IProps {
// eslint-disable-next-line react/no-unused-prop-types
@@ -42,7 +42,7 @@ const Index: FC = ({ callback }) => {
},
});
- const emailCaptcha = useCaptchaModal('email');
+ const emailCaptcha = useCaptchaPlugin('email');
const handleChange = (params: FormDataType) => {
setFormData({ ...formData, ...params });
@@ -73,20 +73,20 @@ const Index: FC = ({ callback }) => {
e_mail: formData.e_mail.value,
};
- const captcha = emailCaptcha.getCaptcha();
- if (captcha.verify) {
+ const captcha = emailCaptcha?.getCaptcha();
+ if (captcha?.verify) {
params.captcha_code = captcha.captcha_code;
params.captcha_id = captcha.captcha_id;
}
resetPassword(params)
.then(async () => {
- await emailCaptcha.close();
+ await emailCaptcha?.close();
callback?.(2, formData.e_mail.value);
})
.catch((err) => {
if (err.isError) {
- emailCaptcha.handleCaptchaError(err.list);
+ emailCaptcha?.handleCaptchaError(err.list);
const data = handleFormError(err, formData);
setFormData({ ...data });
}
@@ -101,6 +101,11 @@ const Index: FC = ({ callback }) => {
return;
}
+ if (!emailCaptcha) {
+ sendEmail();
+ return;
+ }
+
emailCaptcha.check(() => {
sendEmail();
});
diff --git a/ui/src/pages/Users/ChangeEmail/components/sendEmail.tsx b/ui/src/pages/Users/ChangeEmail/components/sendEmail.tsx
index cf321a1ff..419e226e1 100644
--- a/ui/src/pages/Users/ChangeEmail/components/sendEmail.tsx
+++ b/ui/src/pages/Users/ChangeEmail/components/sendEmail.tsx
@@ -26,7 +26,7 @@ import type { PasswordResetReq, FormDataType } from '@/common/interface';
import { loggedUserInfoStore } from '@/stores';
import { changeEmail } from '@/services';
import { handleFormError } from '@/utils';
-import { useCaptchaModal } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
const Index: FC = () => {
const { t } = useTranslation('translation', { keyPrefix: 'change_email' });
@@ -41,7 +41,7 @@ const Index: FC = () => {
const navigate = useNavigate();
const { user: userInfo, update: updateUser } = loggedUserInfoStore();
- const emailCaptcha = useCaptchaModal('email');
+ const emailCaptcha = useCaptchaPlugin('email');
const handleChange = (params: FormDataType) => {
setFormData({ ...formData, ...params });
@@ -71,22 +71,22 @@ const Index: FC = () => {
const params: PasswordResetReq = {
e_mail: formData.e_mail.value,
};
- const imgCode = emailCaptcha.getCaptcha();
- if (imgCode.verify) {
+ const imgCode = emailCaptcha?.getCaptcha();
+ if (imgCode?.verify) {
params.captcha_code = imgCode.captcha_code;
params.captcha_id = imgCode.captcha_id;
}
changeEmail(params)
.then(async () => {
- await emailCaptcha.close();
+ await emailCaptcha?.close();
userInfo.e_mail = formData.e_mail.value;
updateUser(userInfo);
navigate('/users/login', { replace: true });
})
.catch((err) => {
if (err.isError) {
- emailCaptcha.handleCaptchaError(err.list);
+ emailCaptcha?.handleCaptchaError(err.list);
const data = handleFormError(err, formData);
setFormData({ ...data });
}
@@ -99,7 +99,10 @@ const Index: FC = () => {
if (!checkValidated()) {
return;
}
-
+ if (!emailCaptcha) {
+ sendEmail();
+ return;
+ }
emailCaptcha.check(() => {
sendEmail();
});
diff --git a/ui/src/pages/Users/Login/index.tsx b/ui/src/pages/Users/Login/index.tsx
index bbb1f67fa..52bbd00e4 100644
--- a/ui/src/pages/Users/Login/index.tsx
+++ b/ui/src/pages/Users/Login/index.tsx
@@ -22,7 +22,7 @@ import { Container, Form, Button, Col } from 'react-bootstrap';
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
import { Trans, useTranslation } from 'react-i18next';
-import { usePageTags, useCaptchaModal } from '@/hooks';
+import { usePageTags } from '@/hooks';
import type { LoginReqParams, FormDataType } from '@/common/interface';
import { Unactivate, WelcomeTitle, PluginRender } from '@/components';
import {
@@ -31,6 +31,7 @@ import {
userCenterStore,
} from '@/stores';
import { floppyNavigation, guard, handleFormError, userCenter } from '@/utils';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import { login, UcAgent } from '@/services';
import { setupAppTheme } from '@/utils/localize';
@@ -68,7 +69,7 @@ const Index: React.FC = () => {
setFormData({ ...formData, ...params });
};
- const passwordCaptcha = useCaptchaModal('password');
+ const passwordCaptcha = useCaptchaPlugin('password');
const checkValidated = (): boolean => {
let bol = true;
@@ -107,7 +108,7 @@ const Index: React.FC = () => {
pass: formData.pass.value,
};
- const captcha = passwordCaptcha.getCaptcha();
+ const captcha = passwordCaptcha?.getCaptcha();
if (captcha?.verify) {
params.captcha_code = captcha.captcha_code;
params.captcha_id = captcha.captcha_id;
@@ -115,7 +116,7 @@ const Index: React.FC = () => {
login(params)
.then(async (res) => {
- await passwordCaptcha.close();
+ await passwordCaptcha?.close?.();
updateUser(res);
setupAppTheme();
const userStat = guard.deriveLoginState();
@@ -130,7 +131,7 @@ const Index: React.FC = () => {
if (err.isError) {
const data = handleFormError(err, formData);
setFormData({ ...data });
- passwordCaptcha.handleCaptchaError(err.list);
+ passwordCaptcha?.handleCaptchaError?.(err.list);
}
});
};
@@ -143,7 +144,12 @@ const Index: React.FC = () => {
return;
}
- passwordCaptcha.check(() => {
+ if (!passwordCaptcha) {
+ handleLogin();
+ return;
+ }
+
+ passwordCaptcha?.check?.(() => {
handleLogin();
});
};
@@ -165,6 +171,17 @@ const Index: React.FC = () => {
{step === 1 ? (
+
+
+
{ucAgentInfo ? (
= ({ callback }) => {
});
const updateUser = userStore((state) => state.update);
- const emailCaptcha = useCaptchaModal('email');
+ const emailCaptcha = useCaptchaPlugin('email');
const handleChange = (params: FormDataType) => {
setFormData({ ...formData, ...params });
@@ -99,7 +99,7 @@ const Index: React.FC = ({ callback }) => {
pass: formData.pass.value,
};
- const captcha = emailCaptcha.getCaptcha();
+ const captcha = emailCaptcha?.getCaptcha();
if (captcha?.verify) {
reqParams.captcha_code = captcha.captcha_code;
reqParams.captcha_id = captcha.captcha_id;
@@ -107,13 +107,13 @@ const Index: React.FC = ({ callback }) => {
register(reqParams)
.then(async (res) => {
- await emailCaptcha.close();
+ await emailCaptcha?.close();
updateUser(res);
callback();
})
.catch((err) => {
if (err.isError) {
- emailCaptcha.handleCaptchaError(err.list);
+ emailCaptcha?.handleCaptchaError(err.list);
const data = handleFormError(err, formData);
setFormData({ ...data });
}
@@ -126,6 +126,10 @@ const Index: React.FC = ({ callback }) => {
if (!checkValidated()) {
return;
}
+ if (!emailCaptcha) {
+ handleRegister();
+ return;
+ }
emailCaptcha.check(() => {
handleRegister();
});
diff --git a/ui/src/pages/Users/Settings/Account/components/ModifyEmail/index.tsx b/ui/src/pages/Users/Settings/Account/components/ModifyEmail/index.tsx
index ac42f078a..27eda1812 100644
--- a/ui/src/pages/Users/Settings/Account/components/ModifyEmail/index.tsx
+++ b/ui/src/pages/Users/Settings/Account/components/ModifyEmail/index.tsx
@@ -22,7 +22,8 @@ import { Form, Button } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
import type * as Type from '@/common/interface';
-import { useToast, useCaptchaModal } from '@/hooks';
+import { useToast } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import { getLoggedUserInfo, changeEmail } from '@/services';
import { handleFormError } from '@/utils';
@@ -45,7 +46,7 @@ const Index: FC = () => {
});
const [userInfo, setUserInfo] = useState();
const toast = useToast();
- const emailCaptcha = useCaptchaModal('edit_userinfo');
+ const emailCaptcha = useCaptchaPlugin('edit_userinfo');
useEffect(() => {
getLoggedUserInfo().then((resp) => {
@@ -108,14 +109,14 @@ const Index: FC = () => {
pass: formData.pass.value,
};
- const imgCode = emailCaptcha.getCaptcha();
- if (imgCode.verify) {
+ const imgCode = emailCaptcha?.getCaptcha();
+ if (imgCode?.verify) {
params.captcha_code = imgCode.captcha_code;
params.captcha_id = imgCode.captcha_id;
}
changeEmail(params)
.then(async () => {
- await emailCaptcha.close();
+ await emailCaptcha?.close();
setStep(1);
toast.onShow({
msg: t('change_email_info'),
@@ -125,7 +126,7 @@ const Index: FC = () => {
})
.catch((err) => {
if (err.isError) {
- emailCaptcha.handleCaptchaError(err.list);
+ emailCaptcha?.handleCaptchaError(err.list);
const data = handleFormError(err, formData);
setFormData({ ...data });
}
@@ -138,7 +139,10 @@ const Index: FC = () => {
if (!checkValidated()) {
return;
}
-
+ if (!emailCaptcha) {
+ postEmail();
+ return;
+ }
emailCaptcha.check(() => {
postEmail();
});
diff --git a/ui/src/pages/Users/Settings/Account/components/ModifyPass/index.tsx b/ui/src/pages/Users/Settings/Account/components/ModifyPass/index.tsx
index 6b919c5cb..bdc84bd74 100644
--- a/ui/src/pages/Users/Settings/Account/components/ModifyPass/index.tsx
+++ b/ui/src/pages/Users/Settings/Account/components/ModifyPass/index.tsx
@@ -23,7 +23,8 @@ import { useTranslation } from 'react-i18next';
import classname from 'classnames';
-import { useToast, useCaptchaModal } from '@/hooks';
+import { useToast } from '@/hooks';
+import { useCaptchaPlugin } from '@/utils/pluginKit';
import type { FormDataType } from '@/common/interface';
import { modifyPassword } from '@/services';
import { handleFormError } from '@/utils';
@@ -54,7 +55,7 @@ const Index: FC = () => {
},
});
- const infoCaptcha = useCaptchaModal('edit_userinfo');
+ const infoCaptcha = useCaptchaPlugin('edit_userinfo');
const handleFormState = () => {
setFormState((pre) => !pre);
@@ -134,14 +135,14 @@ const Index: FC = () => {
pass: formData.pass.value,
};
- const imgCode = infoCaptcha.getCaptcha();
- if (imgCode.verify) {
+ const imgCode = infoCaptcha?.getCaptcha();
+ if (imgCode?.verify) {
params.captcha_code = imgCode.captcha_code;
params.captcha_id = imgCode.captcha_id;
}
modifyPassword(params)
.then(async () => {
- await infoCaptcha.close();
+ await infoCaptcha?.close();
toast.onShow({
msg: t('update_password', { keyPrefix: 'toast' }),
variant: 'success',
@@ -150,7 +151,7 @@ const Index: FC = () => {
})
.catch((err) => {
if (err.isError) {
- infoCaptcha.handleCaptchaError(err.list);
+ infoCaptcha?.handleCaptchaError(err.list);
const data = handleFormError(err, formData);
setFormData({ ...data });
}
@@ -163,6 +164,10 @@ const Index: FC = () => {
if (!checkValidated()) {
return;
}
+ if (!infoCaptcha) {
+ postModifyPass();
+ return;
+ }
infoCaptcha.check(() => {
postModifyPass();
diff --git a/ui/src/plugins/builtin/HostingConnector/index.tsx b/ui/src/plugins/builtin/HostingConnector/index.tsx
index bcddf3625..ae0986372 100644
--- a/ui/src/plugins/builtin/HostingConnector/index.tsx
+++ b/ui/src/plugins/builtin/HostingConnector/index.tsx
@@ -23,8 +23,11 @@ import { useTranslation } from 'react-i18next';
import classnames from 'classnames';
-import { PluginInfo } from '@/utils/pluginKit';
-import { getTransNs, getTransKeyPrefix } from '@/utils/pluginKit/utils';
+import {
+ getTransNs,
+ getTransKeyPrefix,
+ PluginInfo,
+} from '@/utils/pluginKit/utils';
import { SvgIcon } from '@/components';
import { userCenterStore } from '@/stores';
import './i18n';
diff --git a/ui/src/plugins/builtin/SearchInfo/index.tsx b/ui/src/plugins/builtin/SearchInfo/index.tsx
index e57819755..24db01676 100644
--- a/ui/src/plugins/builtin/SearchInfo/index.tsx
+++ b/ui/src/plugins/builtin/SearchInfo/index.tsx
@@ -20,8 +20,11 @@
import { memo, FC } from 'react';
import { useTranslation } from 'react-i18next';
-import { PluginInfo } from '@/utils/pluginKit';
-import { getTransNs, getTransKeyPrefix } from '@/utils/pluginKit/utils';
+import {
+ getTransNs,
+ getTransKeyPrefix,
+ PluginInfo,
+} from '@/utils/pluginKit/utils';
import { SvgIcon } from '@/components';
import info from './info.yaml';
diff --git a/ui/src/plugins/builtin/ThirdPartyConnector/index.tsx b/ui/src/plugins/builtin/ThirdPartyConnector/index.tsx
index 99032b208..14ba7c0c1 100644
--- a/ui/src/plugins/builtin/ThirdPartyConnector/index.tsx
+++ b/ui/src/plugins/builtin/ThirdPartyConnector/index.tsx
@@ -23,8 +23,11 @@ import { useTranslation } from 'react-i18next';
import classnames from 'classnames';
-import { PluginInfo } from '@/utils/pluginKit';
-import { getTransNs, getTransKeyPrefix } from '@/utils/pluginKit/utils';
+import {
+ getTransNs,
+ getTransKeyPrefix,
+ PluginInfo,
+} from '@/utils/pluginKit/utils';
import { SvgIcon } from '@/components';
import info from './info.yaml';
diff --git a/ui/src/router/pathFactory.ts b/ui/src/router/pathFactory.ts
index ea3536041..256dcbf03 100644
--- a/ui/src/router/pathFactory.ts
+++ b/ui/src/router/pathFactory.ts
@@ -34,7 +34,7 @@ const tagEdit = (tagId: string) => {
return r;
};
-const questionLanding = (questionId: string, slugTitle: string = '') => {
+const questionLanding = (questionId: string = '', slugTitle: string = '') => {
const { seo } = seoSettingStore.getState();
if (!questionId) {
return slugTitle ? `/questions/null/${slugTitle}` : '/questions/null';
diff --git a/ui/src/utils/pluginKit/index.ts b/ui/src/utils/pluginKit/index.ts
index 481fadd39..eca058bdc 100644
--- a/ui/src/utils/pluginKit/index.ts
+++ b/ui/src/utils/pluginKit/index.ts
@@ -121,6 +121,11 @@ class Plugins {
return this.plugins.find((p) => p.info.slug_name === slug_name);
}
+ getOnePluginHooks(slug_name: string) {
+ const plugin = this.getPlugin(slug_name);
+ return plugin?.hooks;
+ }
+
getPlugins() {
return this.plugins;
}
@@ -179,6 +184,16 @@ const mergeRoutePlugins = (routes) => {
return routes;
};
-export { useRenderHtmlPlugin, mergeRoutePlugins };
+// Only one captcha type plug-in can be enabled at the same time
+const useCaptchaPlugin = (key: Type.CaptchaKey) => {
+ const captcha = plugins
+ .getPlugins()
+ .filter((plugin) => plugin.info.type === 'captcha');
+ const pluginHooks = plugins.getOnePluginHooks(captcha[0]?.info.slug_name);
+ return pluginHooks?.useCaptcha?.(key);
+};
+
export type { Plugin, PluginInfo, PluginType };
+
+export { useRenderHtmlPlugin, mergeRoutePlugins, useCaptchaPlugin };
export default plugins;
diff --git a/ui/src/utils/pluginKit/utils.ts b/ui/src/utils/pluginKit/utils.ts
index a179360de..a6ca44cea 100644
--- a/ui/src/utils/pluginKit/utils.ts
+++ b/ui/src/utils/pluginKit/utils.ts
@@ -21,6 +21,8 @@ import { NamedExoticComponent, FC } from 'react';
import i18next from 'i18next';
+import type * as Type from '@/common/interface';
+
/**
* This information is to be defined for all components.
* It may be used for feature upgrades or version compatibility processing.
@@ -35,7 +37,12 @@ import i18next from 'i18next';
const I18N_NS = 'plugin';
-export type PluginType = 'connector' | 'search' | 'editor' | 'route';
+export type PluginType =
+ | 'connector'
+ | 'search'
+ | 'editor'
+ | 'route'
+ | 'captcha';
export interface PluginInfo {
slug_name: string;
type: PluginType;
@@ -50,6 +57,13 @@ export interface Plugin {
i18nConfig?;
hooks?: {
useRender?: Array<(element: HTMLElement | null) => void>;
+ useCaptcha?: (key: Type.CaptchaKey) => {
+ getCaptcha: () => Record;
+ check: (t: () => void) => void;
+ handleCaptchaError: (error) => any;
+ close: () => Promise;
+ resolveCaptchaReq: (data) => void;
+ };
};
activated?: boolean;
}