Skip to content

Commit

Permalink
feat(web): support binding email (#1418)
Browse files Browse the repository at this point in the history
* feat(web): support binding email
  • Loading branch information
newfish-cmyk committed Jul 25, 2023
1 parent ea07b91 commit ce871fa
Show file tree
Hide file tree
Showing 18 changed files with 441 additions and 191 deletions.
12 changes: 9 additions & 3 deletions web/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,8 @@
"ResetPassword": "Reset Password",
"ResetPasswordSuccess": "Reset password successfully",
"NewPassword": "New Password",
"SignUpSuccess": "Sign up successfully"
"SignUpSuccess": "Sign up successfully",
"EmailTip": "Please input a valid Email"
},
"Time": "Time",
"CreateTime": "Created",
Expand Down Expand Up @@ -550,7 +551,11 @@
"Bonus": "Bonus",
"Channel": "Username",
"Time": "Time",
"InviteTips": "Copy the referral link and send it to others. When they click on this link to register, they will become your invited users, and you will immediately receive bonus."
"InviteTips": "Copy the referral link and send it to others. When they click on this link to register, they will become your invited users, and you will immediately receive bonus.",
"Email": "Email",
"ChangeEmail": "Change Email",
"SmsNumber": "Verification Code",
"ChangeEmailSuccess": "Change Email Success"
},
"Reset": "Reset",
"SettingModal": {
Expand Down Expand Up @@ -578,5 +583,6 @@
"RecycleBinEmpty": "Recycle Bin Empty",
"SelectOne": "At least select one",
"Apply": "apply",
"Developing": "Developing"
"Developing": "Developing",
"Unverified": "Unverified"
}
12 changes: 9 additions & 3 deletions web/public/locales/zh-CN/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,8 @@
"ResetPassword": "重置密码",
"ResetPasswordSuccess": "重置密码成功",
"NewPassword": "新密码",
"SignUpSuccess": "注册成功"
"SignUpSuccess": "注册成功",
"EmailTip": "请输入有效的邮箱"
},
"Time": "时间",
"CreateTime": "创建时间",
Expand Down Expand Up @@ -550,7 +551,11 @@
"Bonus": "奖励",
"Channel": "用户名",
"Time": "时间",
"InviteTips": "将推广链接复制并发送给其他人。当他们通过该链接注册并成为你的邀请对象后,你将立即获得奖励金额。"
"InviteTips": "将推广链接复制并发送给其他人。当他们通过该链接注册并成为你的邀请对象后,你将立即获得奖励金额。",
"Email": "邮箱",
"ChangeEmail": "更改邮箱",
"SmsNumber": "验证码",
"ChangeEmailSuccess": "修改邮箱成功"
},
"Reset": "重置",
"SettingModal": {
Expand Down Expand Up @@ -578,5 +583,6 @@
"RecycleBinEmpty": "回收站已清空",
"SelectOne": "请至少选择一个",
"Apply": "使用",
"Developing": "开发中"
"Developing": "开发中",
"Unverified": "未认证"
}
12 changes: 9 additions & 3 deletions web/public/locales/zh/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,8 @@
"ResetPassword": "重置密码",
"ResetPasswordSuccess": "重置密码成功",
"NewPassword": "新密码",
"SignUpSuccess": "注册成功"
"SignUpSuccess": "注册成功",
"EmailTip": "请输入有效的邮箱"
},
"Time": "时间",
"CreateTime": "创建时间",
Expand Down Expand Up @@ -550,7 +551,11 @@
"Bonus": "奖励",
"Channel": "用户名",
"Time": "时间",
"InviteTips": "将推广链接复制并发送给其他人。当他们通过该链接注册并成为你的邀请对象后,你将立即获得奖励金额。"
"InviteTips": "将推广链接复制并发送给其他人。当他们通过该链接注册并成为你的邀请对象后,你将立即获得奖励金额。",
"Email": "邮箱",
"ChangeEmail": "更改邮箱",
"SmsNumber": "验证码",
"ChangeEmailSuccess": "修改邮箱成功"
},
"Reset": "重置",
"SettingModal": {
Expand Down Expand Up @@ -578,5 +583,6 @@
"RecycleBinEmpty": "回收站已清空",
"SelectOne": "请至少选择一个",
"Apply": "使用",
"Developing": "开发中"
"Developing": "开发中",
"Unverified": "未认证"
}
26 changes: 26 additions & 0 deletions web/src/apis/v1/api-auto.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,11 @@ declare namespace Definitions {
pat?: string /* PAT */;
};

export type SendEmailCodeDto = {
email?: string;
type?: string /* verify code type */;
};

export type CreatePATDto = {
name?: string;
expiresIn?: number;
Expand All @@ -308,6 +313,11 @@ declare namespace Definitions {
newSmsCode?: string /* sms verify code for new phone number */;
};

export type BindEmailDto = {
email?: string;
code?: string /* verify code */;
};

export type BindUsernameDto = {
username?: string /* username */;
};
Expand Down Expand Up @@ -991,6 +1001,14 @@ declare namespace Paths {
export type Responses = any;
}

namespace EmailControllerSendCode {
export type QueryParameters = any;

export type BodyParameters = Definitions.SendEmailCodeDto;

export type Responses = any;
}

namespace PatControllerCreate {
export type QueryParameters = any;

Expand Down Expand Up @@ -1039,6 +1057,14 @@ declare namespace Paths {
export type Responses = any;
}

namespace UserControllerBindEmail {
export type QueryParameters = any;

export type BodyParameters = Definitions.BindEmailDto;

export type Responses = any;
}

namespace UserControllerBindUsername {
export type QueryParameters = any;

Expand Down
18 changes: 18 additions & 0 deletions web/src/apis/v1/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,21 @@ export async function AuthenticationControllerPat2token(params: Definitions.Pat2
data: params,
});
}

/**
* Send email verify code
*/
export async function EmailControllerSendCode(params: Definitions.SendEmailCodeDto): Promise<{
error: string;
data: Paths.EmailControllerSendCode.Responses;
}> {
// /v1/auth/email/code
let _params: { [key: string]: any } = {
appid: useGlobalStore.getState().currentApp?.appid || "",
...params,
};
return request(`/v1/auth/email/code`, {
method: "POST",
data: params,
});
}
18 changes: 18 additions & 0 deletions web/src/apis/v1/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,24 @@ export async function UserControllerBindPhone(params: Definitions.BindPhoneDto):
});
}

/**
* Bind email
*/
export async function UserControllerBindEmail(params: Definitions.BindEmailDto): Promise<{
error: string;
data: Definitions.UserWithProfile;
}> {
// /v1/user/bind/email
let _params: { [key: string]: any } = {
appid: useGlobalStore.getState().currentApp?.appid || "",
...params,
};
return request(`/v1/user/bind/email`, {
method: "POST",
data: params,
});
}

/**
* Bind username
*/
Expand Down
9 changes: 9 additions & 0 deletions web/src/chakraTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,15 @@ const Input: ComponentStyleConfig = {
paddingX: 0,
},
},

userInfo: {
field: {
background: "#F8FAFB",
border: "1px",
height: "32px",
borderColor: "#D5D6E1",
},
},
},
defaultProps: {
size: "md",
Expand Down
9 changes: 9 additions & 0 deletions web/src/chakraThemeDark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,15 @@ const Input: ComponentStyleConfig = {
paddingX: 0,
},
},

userInfo: {
field: {
background: "none",
border: "1px",
height: "32px",
borderColor: "#D5D6E1",
},
},
},
defaultProps: {
size: "md",
Expand Down
67 changes: 67 additions & 0 deletions web/src/components/SendEmailCodeButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { useState } from "react";
import { Button } from "@chakra-ui/react";
import clsx from "clsx";
import { t } from "i18next";

import { useSendEmailCodeMutation } from "@/pages/auth/service";
import useGlobalStore from "@/pages/globalStore";

export function SendEmailCodeButton(props: {
getEmail: any;
className?: string;
emailAccount?: string;
type: string;
}) {
const { getEmail, className, emailAccount, type } = props;
const [isSendSmsCode, setIsSendSmsCode] = useState(false);
const [countdown, setCountdown] = useState(60);
const sendSmsCodeMutation = useSendEmailCodeMutation();

const { showSuccess, showError } = useGlobalStore();

const handleSendSmsCode = async () => {
const email = getEmail(emailAccount);

if (isSendSmsCode) {
return;
}

const isValidate = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email);
if (!isValidate) {
showError(t("AuthPanel.EmailTip"));
return;
}

setIsSendSmsCode(true);
setCountdown(60);
const timer = setInterval(() => {
setCountdown((countdown) => {
if (countdown === 0) {
clearInterval(timer);
setIsSendSmsCode(false);
return 0;
}
return countdown - 1;
});
}, 1000);

const res = await sendSmsCodeMutation.mutateAsync({
email,
type,
});

if (res?.data) {
showSuccess(t("AuthPanel.SmsCodeSendSuccess"));
}
};

return (
<Button
className={clsx("w-20", className)}
variant={isSendSmsCode ? "thirdly_disabled" : "thirdly"}
onClick={handleSendSmsCode}
>
{isSendSmsCode ? `${countdown}s` : t("AuthPanel.getValidationCode")}
</Button>
);
}
38 changes: 33 additions & 5 deletions web/src/components/SmsCodeInput/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useRef } from "react";
import { Input } from "@chakra-ui/react";
import { Input, useColorMode } from "@chakra-ui/react";
const CODE_NUMBER = 6;

export default function SmsCodeInput(props: {
Expand All @@ -8,13 +8,22 @@ export default function SmsCodeInput(props: {
}) {
const { value, onChange } = props;
const inputsRefs = useRef<Array<HTMLInputElement | null>>([]);
const darkMode = useColorMode().colorMode === "dark";

if (value) {
const updatedValues = value.split("").slice(0, CODE_NUMBER);
inputsRefs.current.forEach((input, index) => {
if (input) {
input.value = updatedValues[index] || "";
}
});
}

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>, index: number) => {
const inputValue = e.target.value;
if (index < CODE_NUMBER - 1 && inputValue.length === 1) {
inputsRefs.current[index + 1]?.focus();
}

const updatedValue = updateValueAtIndex(value, index, inputValue);
if (onChange) {
onChange(updatedValue);
Expand All @@ -25,7 +34,6 @@ export default function SmsCodeInput(props: {
if (value === undefined) {
return newValue;
}

const newValueArray = value.split("");
newValueArray[index] = newValue;
return newValueArray.join("");
Expand All @@ -40,6 +48,25 @@ export default function SmsCodeInput(props: {
}
};

const handlePaste = (e: React.ClipboardEvent<HTMLInputElement>) => {
e.preventDefault();
const clipboardData = e.clipboardData || window.Clipboard;
const pastedText = clipboardData.getData("text").slice(0, CODE_NUMBER);
const updatedValues = Array.from({ length: CODE_NUMBER }, (_, i) => pastedText[i] || "");

inputsRefs.current.forEach((input, index) => {
if (input) {
input.value = updatedValues[index];
handleInputChange({ target: input } as React.ChangeEvent<HTMLInputElement>, index);
}
});

const updatedValue = updatedValues.join("");
if (onChange) {
onChange(updatedValue);
}
};

const renderInputs = () => {
const inputs = [];
for (let i = 0; i < CODE_NUMBER; i++) {
Expand All @@ -52,17 +79,18 @@ export default function SmsCodeInput(props: {
maxLength={1}
onChange={(e) => handleInputChange(e, i)}
onKeyDown={(e) => handleKeyDown(e, i)}
onPaste={handlePaste}
width={"30px"}
height={"32px"}
bg={"#F8FAFB"}
bg={!darkMode ? "#F8FAFB" : "none"}
border={"1px solid #D5D6E1"}
padding={0}
className="text-center"
ref={(ref) => (inputsRefs.current[i] = ref)}
/>,
);
if (i === CODE_NUMBER / 2 - 1) {
inputs.push(<div className="h-[1px] w-5 bg-[#D5D6E1]" />);
inputs.push(<div key="divider" className="h-[1px] w-5 bg-[#D5D6E1]" />);
}
}
return inputs;
Expand Down
Loading

0 comments on commit ce871fa

Please sign in to comment.