refactor: 抽象并统一Entry界面按钮样式

feat/uniplus
moonrailgun 2 years ago
parent de8712129c
commit c473a70b46

@ -10,6 +10,9 @@ import React, { useState } from 'react';
import { Spinner } from '../../components/Spinner';
import { string } from 'yup';
import { useNavToView } from './utils';
import { EntryInput } from './components/Input';
import { SecondaryBtn } from './components/SecondaryBtn';
import { PrimaryBtn } from './components/PrimaryBtn';
/**
*
@ -55,8 +58,7 @@ export const ForgetPasswordView: React.FC = React.memo(() => {
<div>
<div className="mb-4">
<div className="mb-2">{t('邮箱')}</div>
<input
className="appearance-none rounded-md relative block w-full px-4 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 text-base mobile:text-sm"
<EntryInput
name="forget-email"
placeholder="name@example.com"
type="text"
@ -67,22 +69,16 @@ export const ForgetPasswordView: React.FC = React.memo(() => {
</div>
{!sendedEmail && (
<button
className="w-full py-2 px-4 mb-2 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50"
disabled={sendEmailLoading}
onClick={handleSendEmail}
>
{sendEmailLoading && <Spinner />}
<PrimaryBtn loading={sendEmailLoading} onClick={handleSendEmail}>
{t('向邮箱发送OTP')}
</button>
</PrimaryBtn>
)}
{sendedEmail && (
<>
<div className="mb-4">
<div className="mb-2">{t('OTP')}</div>
<input
className="appearance-none rounded-md relative block w-full px-4 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 text-base mobile:text-sm"
<EntryInput
name="forget-otp"
type="text"
value={otp}
@ -92,8 +88,7 @@ export const ForgetPasswordView: React.FC = React.memo(() => {
<div className="mb-4">
<div className="mb-2">{t('新密码')}</div>
<input
className="appearance-none rounded-md relative block w-full px-4 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 text-base mobile:text-sm"
<EntryInput
name="forget-password"
type="password"
placeholder="******"
@ -102,25 +97,19 @@ export const ForgetPasswordView: React.FC = React.memo(() => {
/>
</div>
<button
className="w-full py-2 px-4 mb-2 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50"
disabled={loading}
onClick={handleResetPassword}
>
{loading && <Spinner />}
<PrimaryBtn loading={loading} onClick={handleResetPassword}>
{t('重设密码')}
</button>
</PrimaryBtn>
</>
)}
<button
className="w-full py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white focus:outline-none disabled:opacity-50"
<SecondaryBtn
disabled={loading}
onClick={() => navToView('/entry/login')}
>
<Icon icon="mdi:arrow-left" className="mr-1 inline" />
{t('返回登录')}
</button>
</SecondaryBtn>
</div>
</div>
);

@ -13,6 +13,9 @@ import {
} from 'tailchat-shared';
import { string } from 'yup';
import { useNavToView } from './utils';
import { EntryInput } from './components/Input';
import { PrimaryBtn } from './components/PrimaryBtn';
import { SecondaryBtn } from './components/SecondaryBtn';
export const GuestView: React.FC = React.memo(() => {
const navigate = useNavigate();
@ -42,8 +45,7 @@ export const GuestView: React.FC = React.memo(() => {
<div>
<div className="mb-4">
<div className="mb-2">{t('昵称')}</div>
<input
className="appearance-none rounded-md relative block w-full px-4 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 text-base mobile:text-sm"
<EntryInput
placeholder={t('想要让大家如何称呼你')}
type="text"
value={nickname}
@ -51,23 +53,18 @@ export const GuestView: React.FC = React.memo(() => {
/>
</div>
<button
className="w-full py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50"
disabled={loading}
onClick={handleCreateTemporaryUser}
>
{loading && <Spinner />}
<PrimaryBtn loading={loading} onClick={handleCreateTemporaryUser}>
{t('立即进入')}
</button>
</PrimaryBtn>
<button
className="w-full py-2 px-4 border border-transparent text-sm text-left font-medium text-white disabled:opacity-50"
<SecondaryBtn
className="text-left"
disabled={loading}
onClick={() => navToView('/entry/login')}
>
<Icon icon="mdi:arrow-left" className="mr-1 inline" />
{t('返回登录')}
</button>
</SecondaryBtn>
</div>
</div>
);

@ -13,6 +13,9 @@ import { IconBtn } from '@/components/IconBtn';
import { openModal } from '@/components/Modal';
import { ServiceUrlSettings } from '@/components/modals/ServiceUrlSettings';
import { LanguageSelect } from '@/components/LanguageSelect';
import { EntryInput } from './components/Input';
import { SecondaryBtn } from './components/SecondaryBtn';
import { PrimaryBtn } from './components/PrimaryBtn';
/**
* TODO:
@ -82,9 +85,8 @@ export const LoginView: React.FC = React.memo(() => {
<div>
<div className="mb-4">
<div className="mb-2">{t('邮箱')}</div>
<input
<EntryInput
name="login-email"
className="appearance-none rounded-md relative block w-full px-4 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 text-base mobile:text-sm"
placeholder="name@example.com"
type="text"
value={email}
@ -93,9 +95,8 @@ export const LoginView: React.FC = React.memo(() => {
</div>
<div className="mb-4">
<div className="mb-2">{t('密码')}</div>
<input
<EntryInput
name="login-password"
className="appearance-none rounded-md relative block w-full px-4 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 text-base mobile:text-sm"
type="password"
placeholder="******"
value={password}
@ -115,32 +116,25 @@ export const LoginView: React.FC = React.memo(() => {
</div>
)}
<button
className="w-full py-2 px-4 mb-2 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50"
disabled={loading}
onClick={handleLogin}
>
{loading && <Spinner />}
<PrimaryBtn loading={loading} onClick={handleLogin}>
{t('登录')}
</button>
</PrimaryBtn>
<button
className="w-full py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white focus:outline-none disabled:opacity-50"
<SecondaryBtn
disabled={loading}
onClick={() => navToView('/entry/register')}
>
{t('注册账号')}
<Icon icon="mdi:arrow-right" className="ml-1 inline" />
</button>
</SecondaryBtn>
<button
className="w-full py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white focus:outline-none disabled:opacity-50"
<SecondaryBtn
disabled={loading}
onClick={() => navToView('/entry/guest')}
>
{t('游客访问')}
<Icon icon="mdi:arrow-right" className="ml-1 inline" />
</button>
</SecondaryBtn>
</div>
<div className="absolute bottom-4 left-0 space-x-2">

@ -8,6 +8,9 @@ import { setUserJWT } from '../../utils/jwt-helper';
import { setGlobalUserLoginInfo } from '../../utils/user-helper';
import { useSearchParam } from '@/hooks/useSearchParam';
import { useNavToView } from './utils';
import { EntryInput } from './components/Input';
import { SecondaryBtn } from './components/SecondaryBtn';
import { PrimaryBtn } from './components/PrimaryBtn';
/**
*
@ -15,6 +18,7 @@ import { useNavToView } from './utils';
export const RegisterView: React.FC = React.memo(() => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [emailOTP, setEmailOTP] = useState('');
const navigate = useNavigate();
const navRedirect = useSearchParam('redirect');
@ -29,7 +33,7 @@ export const RegisterView: React.FC = React.memo(() => {
.required(t('密码不能为空'))
.validate(password);
const data = await registerWithEmail(email, password);
const data = await registerWithEmail(email, password, emailOTP);
setGlobalUserLoginInfo(data);
await setUserJWT(data.token);
@ -39,7 +43,7 @@ export const RegisterView: React.FC = React.memo(() => {
} else {
navigate('/main');
}
}, [email, password, navRedirect]);
}, [email, password, emailOTP, navRedirect]);
const navToView = useNavToView();
@ -50,20 +54,19 @@ export const RegisterView: React.FC = React.memo(() => {
<div>
<div className="mb-4">
<div className="mb-2">{t('邮箱')}</div>
<input
<EntryInput
name="reg-email"
className="appearance-none rounded-md relative block w-full px-4 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 text-base mobile:text-sm"
placeholder="name@example.com"
type="text"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div className="mb-4">
<div className="mb-2">{t('密码')}</div>
<input
<EntryInput
name="reg-password"
className="appearance-none rounded-md relative block w-full px-4 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 text-base mobile:text-sm"
type="password"
placeholder="******"
value={password}
@ -73,23 +76,18 @@ export const RegisterView: React.FC = React.memo(() => {
{error && <p className="text-red-500 text-sm mb-4">{error.message}</p>}
<button
className="w-full py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50"
disabled={loading}
onClick={handleRegister}
>
{loading && <Spinner />}
<PrimaryBtn loading={loading} onClick={handleRegister}>
{t('注册账号')}
</button>
</PrimaryBtn>
<button
className="w-full py-2 px-4 border border-transparent text-sm text-left font-medium text-white disabled:opacity-50"
<SecondaryBtn
className="text-left"
disabled={loading}
onClick={() => navToView('/entry/login')}
>
<Icon icon="mdi:arrow-left" className="mr-1 inline" />
{t('返回登录')}
</button>
</SecondaryBtn>
</div>
</div>
);

@ -0,0 +1,18 @@
import clsx from 'clsx';
import React, { InputHTMLAttributes } from 'react';
export const EntryInput: React.FC<InputHTMLAttributes<HTMLInputElement>> =
React.memo((props) => {
return (
<input
{...props}
className={clsx(
'appearance-none rounded-md relative block w-full px-4 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 text-base mobile:text-sm',
props.className
)}
>
{props.children}
</input>
);
});
EntryInput.displayName = 'EntryInput';

@ -0,0 +1,24 @@
import { Spinner } from '@/components/Spinner';
import clsx from 'clsx';
import React, { ButtonHTMLAttributes } from 'react';
export const PrimaryBtn: React.FC<
ButtonHTMLAttributes<HTMLButtonElement> & {
loading?: boolean;
}
> = React.memo((props) => {
return (
<button
disabled={props.loading}
{...props}
className={clsx(
'w-full py-2 px-4 mb-2 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50',
props.className
)}
>
{props.loading && <Spinner />}
{props.children}
</button>
);
});
PrimaryBtn.displayName = 'PrimaryBtn';

@ -0,0 +1,18 @@
import clsx from 'clsx';
import React, { ButtonHTMLAttributes } from 'react';
export const SecondaryBtn: React.FC<ButtonHTMLAttributes<HTMLButtonElement>> =
React.memo((props) => {
return (
<button
{...props}
className={clsx(
'w-full py-2 px-4 border border-transparent text-sm font-medium text-white focus:outline-none disabled:opacity-50',
props.className
)}
>
{props.children}
</button>
);
});
SecondaryBtn.displayName = 'SecondaryBtn';
Loading…
Cancel
Save