From 53150c8991b9b8e8f493430502e092b4c90439e3 Mon Sep 17 00:00:00 2001 From: neil Date: Mon, 15 May 2023 10:51:15 +0800 Subject: [PATCH] feat: userinfo loading Signed-off-by: neil --- i18n/en/common.json | 3 +- i18n/en/setting.json | 7 +- i18n/en/submit_project.json | 3 +- i18n/zh/common.json | 3 +- i18n/zh/setting.json | 7 +- package.json | 1 + src/common/components/Button.tsx | 36 +++++--- src/common/components/Header/User.tsx | 6 +- src/modules/auth/AuthRequire.tsx | 58 +++++++++++++ src/modules/auth/UserInfoStore.tsx | 30 ------- src/modules/auth/useAuthRedirect.ts | 15 ---- src/modules/auth/useProviderInfo.tsx | 64 ++++++++++++++ .../settings/profile/DeleteAccount.tsx | 2 +- src/modules/settings/profile/OAuthList.tsx | 57 +++++++----- src/modules/settings/profile/ProfileForm.tsx | 86 ++++++++++++++----- .../submitProject/Form/SelectRepoSource.tsx | 6 +- .../submitProject/FormCommunity/index.tsx | 5 +- .../submitProject/FormSingleRepo/index.tsx | 6 +- .../submitProject/Misc/AddSelectPopover.tsx | 5 +- src/modules/submitProject/Misc/Auth.tsx | 39 +++------ src/modules/submitProject/Misc/FillItem.tsx | 5 +- .../submitProject/Misc/InputUrlField.tsx | 5 +- .../submitProject/RepoSelect/index.tsx | 5 +- src/pages/_app.tsx | 12 +++ src/pages/auth/signin.tsx | 3 +- src/pages/settings/profile.tsx | 26 +++++- src/pages/settings/subscribe.tsx | 4 +- src/pages/submit-your-project/community.tsx | 11 +-- src/pages/submit-your-project/index.tsx | 11 +-- yarn.lock | 12 +++ 30 files changed, 358 insertions(+), 175 deletions(-) create mode 100644 src/modules/auth/AuthRequire.tsx delete mode 100644 src/modules/auth/useAuthRedirect.ts create mode 100644 src/modules/auth/useProviderInfo.tsx diff --git a/i18n/en/common.json b/i18n/en/common.json index 905e5208..cafebcc0 100644 --- a/i18n/en/common.json +++ b/i18n/en/common.json @@ -53,6 +53,7 @@ "signout": "SignOut", "btn": { "save": "Save", - "cancel": "Cancel" + "cancel": "Cancel", + "func_disabled": "The feature is temporarily disabled" } } diff --git a/i18n/en/setting.json b/i18n/en/setting.json index 3609efa0..7f1190f7 100644 --- a/i18n/en/setting.json +++ b/i18n/en/setting.json @@ -7,6 +7,7 @@ "name": "name", "name_placeholder": "Please enter your name", "email": "email", + "email_ques_icon": "The email is used to receive project update notifications.", "email_placeholder": "Please enter your email address", "error_require": "{{field}} is a required field", "error_name_max_len": "nickname cannot exceed {{length}} characters", @@ -18,6 +19,10 @@ "connect_multiple_accounts_to_your_user_and_sign_in": "Connect multiple accounts to your user and sign in with any of them", "delete_account": "Delete account", "delete_account_btn": "Delete account", - "delete_account_warning": " Once you delete your account, there is no going back. Please be certain when taking this action." + "delete_account_warning": " Once you delete your account, there is no going back. Please be certain when taking this action.", + "can_be_used_to_submit_project_after_binding": "Can be used to submit project after binding", + "verified": "Verified", + "unverified_yet": "Unverified yet,", + "resend_verification_email": "resend verification email" } } diff --git a/i18n/en/submit_project.json b/i18n/en/submit_project.json index ca2eb68b..b3fb7e81 100644 --- a/i18n/en/submit_project.json +++ b/i18n/en/submit_project.json @@ -6,7 +6,8 @@ "submit_your_project": "Submit your project", "single_repository": "Single repository", "your_project_hosting_on": "Your project hosting on", - "logout": "Logout", + "switch_gitee": "switch to Gitee account", + "switch_github": "switch to GitHub account", "select_your_own_repository_on": "Select your own repository on {{providerName}}", "type_the_address_of_any_repository": "Type the address of any repository", "pick_your_own_repository_on": "Pick your own repository on {{providerName}}", diff --git a/i18n/zh/common.json b/i18n/zh/common.json index d03e90eb..8efc30a4 100644 --- a/i18n/zh/common.json +++ b/i18n/zh/common.json @@ -52,6 +52,7 @@ "signout": "退出", "btn": { "save": "保存", - "cancel": "取消" + "cancel": "取消", + "func_disabled": "该功能临时关闭" } } diff --git a/i18n/zh/setting.json b/i18n/zh/setting.json index 37df55e1..23ac6e01 100644 --- a/i18n/zh/setting.json +++ b/i18n/zh/setting.json @@ -7,6 +7,7 @@ "name": "昵称", "name_placeholder": "请输入昵称", "email": "邮箱", + "email_ques_icon": "该电子邮件用于接收项目更新通知", "email_placeholder": "请输入邮箱地址", "error_require": "{{field}}不能为空", "error_name_max_len": "昵称长度不能超过{{length}}", @@ -18,6 +19,10 @@ "connect_multiple_accounts_to_your_user_and_sign_in": "绑定第三方账号,即可使用任何一个账户进行登录。", "delete_account": "删除账号", "delete_account_btn": "删除我的账号", - "delete_account_warning": "删除账户后,就无法进行撤销。在执行此操作时,请确保您已经仔细考虑过。" + "delete_account_warning": "删除账户后,就无法进行撤销。在执行此操作时,请确保您已经仔细考虑过。", + "can_be_used_to_submit_project_after_binding": "绑定后可用于提交项目", + "verified": "已验证", + "unverified_yet": "未验证,", + "resend_verification_email": "发送验证邮件" } } diff --git a/package.json b/package.json index 84380497..1b009cb1 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "react-dom": "18.2.0", "react-error-boundary": "^3.1.4", "react-hook-form": "^7.36.0", + "react-hot-toast": "^2.4.1", "react-hotkeys-hook": "^3.4.7", "react-i18next": "^12.0.0", "react-icons": "^4.4.0", diff --git a/src/common/components/Button.tsx b/src/common/components/Button.tsx index af61f0d0..9615bd90 100644 --- a/src/common/components/Button.tsx +++ b/src/common/components/Button.tsx @@ -1,4 +1,4 @@ -import React, { PropsWithChildren } from 'react'; +import React, { ReactNode, forwardRef } from 'react'; import classnames from 'classnames'; import { cva, type VariantProps } from 'class-variance-authority'; import { twMerge } from 'tailwind-merge'; @@ -38,17 +38,22 @@ const buttonVariants = cva( interface ButtonVariants extends VariantProps {} -const Button: React.FC> = ({ - children, - disabled = false, - loading = false, - type = 'button', - intent, - size, - className, - onClick, - ...props -}) => { +const Button = forwardRef< + HTMLButtonElement, + ButtonProps & ButtonVariants & { children?: ReactNode | undefined } +>((props, ref) => { + const { + children, + disabled = false, + loading = false, + type = 'button', + intent, + size, + className, + onClick, + ...restProps + } = props; + const cls = classnames( buttonVariants({ intent, size }), { 'opacity-50 cursor-not-allowed hover:opacity-50': disabled }, @@ -57,6 +62,7 @@ const Button: React.FC> = ({ return ( ); -}; +}); + +Button.displayName = 'Button'; export default Button; diff --git a/src/common/components/Header/User.tsx b/src/common/components/Header/User.tsx index 8595b776..8a86a4fe 100644 --- a/src/common/components/Header/User.tsx +++ b/src/common/components/Header/User.tsx @@ -5,16 +5,16 @@ import Image from 'next/image'; import { AiOutlineUser } from 'react-icons/ai'; import { MdOutlineLogout } from 'react-icons/md'; import client from '@graphql/client'; -import { useSnapshot } from 'valtio'; import { useSignOutMutation } from '@graphql/generated'; -import { resetUserInfo, userInfoStore } from '@modules/auth/UserInfoStore'; +import { resetUserInfo } from '@modules/auth/UserInfoStore'; import { useTranslation } from 'react-i18next'; +import useProviderInfo from '@modules/auth/useProviderInfo'; const User = () => { const { t } = useTranslation(); const router = useRouter(); const mutation = useSignOutMutation(client); - const { providerUser: user } = useSnapshot(userInfoStore); + const { providerUser: user } = useProviderInfo(); if (!user) { return ( diff --git a/src/modules/auth/AuthRequire.tsx b/src/modules/auth/AuthRequire.tsx new file mode 100644 index 00000000..7b546737 --- /dev/null +++ b/src/modules/auth/AuthRequire.tsx @@ -0,0 +1,58 @@ +import React, { PropsWithChildren } from 'react'; +import router from 'next/router'; +import { useSnapshot } from 'valtio'; +import { userInfoStore } from './UserInfoStore'; + +interface Props { + className?: string; + loadingUi?: React.ReactNode; + redirectTo?: string; +} + +const AuthRequire: React.FC> = ({ + children, + className, + loadingUi, + redirectTo, +}) => { + const { currentUser, loading } = useSnapshot(userInfoStore); + + if (!loading && !currentUser) { + let redirectUrl = redirectTo ?? window.location.pathname; + router.replace( + `/auth/signin?redirect_to=${encodeURIComponent(redirectUrl)}` + ); + } + + if (loading && loadingUi) { + return <>{loadingUi}; + } + + if (loading) { + return ( +
+
+
+ +
+
+
+
+ +
+ +
+
+
+
+ +
+
+
+ ); + } + + return <>{children}; +}; + +export default AuthRequire; diff --git a/src/modules/auth/UserInfoStore.tsx b/src/modules/auth/UserInfoStore.tsx index 7876fb02..16c09ceb 100644 --- a/src/modules/auth/UserInfoStore.tsx +++ b/src/modules/auth/UserInfoStore.tsx @@ -1,13 +1,8 @@ import React from 'react'; import { proxy, ref } from 'valtio'; import { UserinfoQuery } from '@graphql/generated'; -import { getAuthProvider } from '@common/utils/cookie'; import { EventEmitter } from 'ahooks/lib/useEventEmitter'; -type LoginBind = NonNullable< - NonNullable['loginBinds'] ->[0]; - export const userEvent = { REFRESH: 'refresh' as const, }; @@ -16,39 +11,15 @@ export type UserEventType = typeof userEvent[keyof typeof userEvent]; export const userInfoStore = proxy<{ loading: boolean; - providerUser: LoginBind | null; currentUser: UserinfoQuery['currentUser'] | null; event$: EventEmitter | null; }>({ loading: true, - providerUser: null, currentUser: null, event$: null, }); export const setUserInfo = (res?: UserinfoQuery) => { - let providerUser; - - const provider = getAuthProvider(); - if (provider) { - providerUser = res?.currentUser?.loginBinds?.find( - (bindInfo) => bindInfo.provider === provider - ); - } else { - providerUser = res?.currentUser?.loginBinds?.[0]; - } - - if (providerUser) { - providerUser = { - ...providerUser, - // todo Let the backend modify - // The naming of the returned fields in the interface data is reversed. - account: providerUser?.nickname, - nickname: providerUser?.account, - }; - } - - userInfoStore.providerUser = providerUser || null; userInfoStore.currentUser = res?.currentUser || null; }; @@ -58,6 +29,5 @@ export const serUserLoading = (loading: boolean) => { export const resetUserInfo = () => { userInfoStore.loading = false; - userInfoStore.providerUser = null; userInfoStore.currentUser = null; }; diff --git a/src/modules/auth/useAuthRedirect.ts b/src/modules/auth/useAuthRedirect.ts deleted file mode 100644 index 759fa8c2..00000000 --- a/src/modules/auth/useAuthRedirect.ts +++ /dev/null @@ -1,15 +0,0 @@ -import router from 'next/router'; -import { useSnapshot } from 'valtio'; -import { userInfoStore } from './UserInfoStore'; - -const useAuthRedirect = (options?: { redirectTo?: string }) => { - const { currentUser, loading } = useSnapshot(userInfoStore); - if (!loading && !currentUser) { - let redirectTo = options?.redirectTo ?? window.location.pathname; - router.replace( - `/auth/signin?redirect_to=${encodeURIComponent(redirectTo)}` - ); - } -}; - -export default useAuthRedirect; diff --git a/src/modules/auth/useProviderInfo.tsx b/src/modules/auth/useProviderInfo.tsx new file mode 100644 index 00000000..4b16cc7e --- /dev/null +++ b/src/modules/auth/useProviderInfo.tsx @@ -0,0 +1,64 @@ +import React, { useEffect } from 'react'; +import { useSnapshot } from 'valtio'; +import { useToggle } from 'ahooks'; +import { UserinfoQuery } from '@graphql/generated'; +import { getAuthProvider, setAuthProvider } from '@common/utils/cookie'; +import { userInfoStore } from '@modules/auth/UserInfoStore'; +import { ReadonlyDeep } from 'type-fest'; + +type LoginBinds = ReadonlyDeep< + NonNullable['loginBinds'] +>; + +function findSpecifyProvider({ + loginBinds, + provider, +}: { + loginBinds?: LoginBinds; + provider?: string; +}) { + let providerUser; + + if (provider && loginBinds && loginBinds.length > 1) { + providerUser = loginBinds?.find( + (bindInfo) => bindInfo.provider === provider + ); + } else { + providerUser = loginBinds?.[0]; + } + + if (providerUser) { + providerUser = { + ...providerUser, + // todo Let the backend modify + // The naming of the returned fields in the interface data is reversed. + account: providerUser?.nickname, + nickname: providerUser?.account, + }; + } + + return providerUser || null; +} + +const toggleProviders = ['github', 'gitee']; +const getAnother = (p?: string) => toggleProviders.filter((i) => i !== p)[0]; + +const useProviderInfo = () => { + const { currentUser: user } = useSnapshot(userInfoStore); + + const login = getAuthProvider() || 'github'; + const [provider, { toggle }] = useToggle(login, getAnother(login)); + + useEffect(() => { + setAuthProvider(provider); + }, [provider]); + + const showUser = findSpecifyProvider({ + provider: provider, + loginBinds: user?.loginBinds, + }); + + return { providerUser: showUser, loginBinds: user?.loginBinds, toggle }; +}; + +export default useProviderInfo; diff --git a/src/modules/settings/profile/DeleteAccount.tsx b/src/modules/settings/profile/DeleteAccount.tsx index efbe19b0..7e03bdea 100644 --- a/src/modules/settings/profile/DeleteAccount.tsx +++ b/src/modules/settings/profile/DeleteAccount.tsx @@ -53,7 +53,7 @@ const DeleteAccount = () => { {t('setting:profile.delete_account_warning')} - + + ); +}; + +const OAuthList = () => { + const { t } = useTranslation(); const { currentUser } = useSnapshot(userInfoStore); const providers = [ { name: 'GitHub', id: 'github', - desc: 'Can be used to submit project after binding', + desc: t('setting:profile.can_be_used_to_submit_project_after_binding'), icon: , }, { name: 'Gitee', id: 'gitee', - desc: 'Can be used to submit project after binding', + desc: t('setting:profile.can_be_used_to_submit_project_after_binding'), icon: , }, ]; @@ -71,24 +103,7 @@ const OAuthList = () => {
{bindInfo ? ( - + ) : (
-
- {t('setting:profile.form.email')} +
+
+ {t('setting:profile.form.email')} + {t('setting:profile.form.email_ques_icon')}} + placement="right" + > + + + + +
+ + {currentUser?.emailVerified ? null : ( +
+ {t('setting:profile.unverified_yet')} +
{}} + > + {t('setting:profile.resend_verification_email')} +
+
+ )} +
+
+ + + {currentUser?.emailVerified ? ( +
+ {t('setting:profile.verified')} +
+ ) : null}
- {errors['email']?.message && (

{errors['email'].message}

)}
- + {t('common:btn.func_disabled')}} + placement="right" + > + +
diff --git a/src/modules/submitProject/Form/SelectRepoSource.tsx b/src/modules/submitProject/Form/SelectRepoSource.tsx index fd7cfba8..d22b58dc 100644 --- a/src/modules/submitProject/Form/SelectRepoSource.tsx +++ b/src/modules/submitProject/Form/SelectRepoSource.tsx @@ -4,9 +4,8 @@ import Image from 'next/image'; import { AiFillCaretDown, AiOutlinePlus } from 'react-icons/ai'; import { useClickAway, useSessionStorage } from 'react-use'; import { useQuery } from '@tanstack/react-query'; -import { useSnapshot } from 'valtio'; -import { userInfoStore } from '@modules/auth/UserInfoStore'; import { getOrganizations } from '@modules/submitProject/api'; +import useProviderInfo from '@modules/auth/useProviderInfo'; const SourceItem: React.FC<{ className?: string; @@ -45,8 +44,7 @@ const SelectRepoSource: React.FC< > = ({ className, onChange, value }) => { const [open, setOpen] = useState(false); const ref = useRef(null); - - const { providerUser: user } = useSnapshot(userInfoStore); + const { providerUser: user } = useProviderInfo(); const nickname = user?.nickname!; const account = user?.account!; const provider = user?.provider!; diff --git a/src/modules/submitProject/FormCommunity/index.tsx b/src/modules/submitProject/FormCommunity/index.tsx index e1969af0..16acb271 100644 --- a/src/modules/submitProject/FormCommunity/index.tsx +++ b/src/modules/submitProject/FormCommunity/index.tsx @@ -2,7 +2,6 @@ import React, { useRef, useState } from 'react'; import { useCreateProjectTaskMutation } from '@graphql/generated'; import client from '@graphql/client'; import uniq from 'lodash/uniq'; -import { useSnapshot } from 'valtio'; import { useSessionStorage } from 'react-use'; import Select from '@common/components/Select'; import Button from '@common/components/Button'; @@ -10,13 +9,13 @@ import SwitchToSingleRepo from './SwitchToSingleRepo'; import SoftwareArtifactRepository from './SoftwareArtifactRepository'; import GovernanceRepository from './GovernanceRepository'; import { fillHttps, getRepoName } from '@common/utils'; -import { userInfoStore } from '@modules/auth/UserInfoStore'; +import useProviderInfo from '@modules/auth/useProviderInfo'; import Message from '@modules/submitProject/Misc/Message'; import { useTranslation } from 'react-i18next'; const FormCommunity = () => { const { t } = useTranslation(); - const { providerUser: user } = useSnapshot(userInfoStore); + const { providerUser: user } = useProviderInfo(); const account = user!.account; const provider = user!.provider; diff --git a/src/modules/submitProject/FormSingleRepo/index.tsx b/src/modules/submitProject/FormSingleRepo/index.tsx index 8b66c213..f3375d95 100644 --- a/src/modules/submitProject/FormSingleRepo/index.tsx +++ b/src/modules/submitProject/FormSingleRepo/index.tsx @@ -8,8 +8,7 @@ import SelectLike from '@common/components/SelectLike'; import Input from '@common/components/Input'; import Button from '@common/components/Button'; import Message from '@modules/submitProject/Misc/Message'; -import { useSnapshot } from 'valtio'; -import { userInfoStore } from '@modules/auth/UserInfoStore'; +import useProviderInfo from '@modules/auth/useProviderInfo'; import { fillHttps } from '@common/utils'; import SwitchToCommunity from './SwitchToCommunity'; import RepoSelect from '../RepoSelect'; @@ -17,7 +16,8 @@ import { getUrlReg } from '../Misc'; const FormSingleRepo = () => { const { t } = useTranslation(); - const { providerUser: user } = useSnapshot(userInfoStore); + const { providerUser: user } = useProviderInfo(); + console.log('user', user); const provider = user?.provider || 'github'; const [formType, setFormType] = useState<'select' | 'input'>('input'); diff --git a/src/modules/submitProject/Misc/AddSelectPopover.tsx b/src/modules/submitProject/Misc/AddSelectPopover.tsx index 50d36634..67356c07 100644 --- a/src/modules/submitProject/Misc/AddSelectPopover.tsx +++ b/src/modules/submitProject/Misc/AddSelectPopover.tsx @@ -4,8 +4,7 @@ import { AiFillGithub, AiOutlineLink, AiOutlinePlus } from 'react-icons/ai'; import classnames from 'classnames'; import { SiGitee } from 'react-icons/si'; import { useTranslation } from 'react-i18next'; -import { useSnapshot } from 'valtio'; -import { userInfoStore } from '@modules/auth/UserInfoStore'; +import useProviderInfo from '@modules/auth/useProviderInfo'; export const getIcons = (type: string) => { switch (type) { @@ -26,7 +25,7 @@ const AddSelectPopover: React.FC<{ onClick: (e: React.MouseEvent) => void; }> = ({ className, onSelect, onClick, open, onClose }) => { const { t } = useTranslation(); - const { providerUser: user } = useSnapshot(userInfoStore); + const { providerUser: user } = useProviderInfo(); const provider = user?.provider!; const ref = useRef(null); diff --git a/src/modules/submitProject/Misc/Auth.tsx b/src/modules/submitProject/Misc/Auth.tsx index abd70606..129c9ba6 100644 --- a/src/modules/submitProject/Misc/Auth.tsx +++ b/src/modules/submitProject/Misc/Auth.tsx @@ -1,18 +1,13 @@ import React, { useEffect } from 'react'; import Image from 'next/image'; -import client from '@graphql/client'; -import { useRouter } from 'next/router'; -import { useSnapshot } from 'valtio'; import { useTranslation } from 'react-i18next'; -import { resetUserInfo, userInfoStore } from '@modules/auth/UserInfoStore'; -import { useSignOutMutation } from '@graphql/generated'; +import useProviderInfo from '@modules/auth/useProviderInfo'; const Auth: React.FC = () => { const { t } = useTranslation(); - const router = useRouter(); - const { providerUser: user } = useSnapshot(userInfoStore); + const { providerUser: user, loginBinds, toggle } = useProviderInfo(); const hasLoggedIn = Boolean(user); - const mutation = useSignOutMutation(client); + const bindLen = loginBinds?.length; if (!hasLoggedIn) return null; @@ -46,23 +41,17 @@ const Auth: React.FC = () => {
-
- +
+ {bindLen && bindLen > 1 ? ( + + ) : null}
diff --git a/src/modules/submitProject/Misc/FillItem.tsx b/src/modules/submitProject/Misc/FillItem.tsx index d8518b76..0f1053da 100644 --- a/src/modules/submitProject/Misc/FillItem.tsx +++ b/src/modules/submitProject/Misc/FillItem.tsx @@ -1,8 +1,7 @@ import React from 'react'; import { AiFillGithub, AiOutlineClose } from 'react-icons/ai'; import { SiGitee } from 'react-icons/si'; -import { useSnapshot } from 'valtio'; -import { userInfoStore } from '@modules/auth/UserInfoStore'; +import useProviderInfo from '@modules/auth/useProviderInfo'; export const getIcons = (type: string) => { switch (type) { @@ -19,7 +18,7 @@ const FillItem: React.FC<{ url: string; onDelete: (v: string) => void }> = ({ url, onDelete, }) => { - const { providerUser: user } = useSnapshot(userInfoStore); + const { providerUser: user } = useProviderInfo(); const provider = user?.provider!; return ( diff --git a/src/modules/submitProject/Misc/InputUrlField.tsx b/src/modules/submitProject/Misc/InputUrlField.tsx index cf363193..4e46e812 100644 --- a/src/modules/submitProject/Misc/InputUrlField.tsx +++ b/src/modules/submitProject/Misc/InputUrlField.tsx @@ -9,8 +9,7 @@ import { useHotkeys } from 'react-hotkeys-hook'; import { useTranslation } from 'react-i18next'; import Input from '@common/components/Input'; import { AiOutlineClose } from 'react-icons/ai'; -import { useSnapshot } from 'valtio'; -import { userInfoStore } from '@modules/auth/UserInfoStore'; +import useProviderInfo from '@modules/auth/useProviderInfo'; import { getUrlReg } from '../Misc'; interface Props { @@ -27,7 +26,7 @@ const InputUrlField = forwardRef( ({ onClose, onPressEnter }, ref) => { const { t } = useTranslation(); const inputRef = useRef(null); - const { providerUser: user } = useSnapshot(userInfoStore); + const { providerUser: user } = useProviderInfo(); const provider = user?.provider!; const [value, setValue] = useState(''); diff --git a/src/modules/submitProject/RepoSelect/index.tsx b/src/modules/submitProject/RepoSelect/index.tsx index b466f879..ea1881be 100644 --- a/src/modules/submitProject/RepoSelect/index.tsx +++ b/src/modules/submitProject/RepoSelect/index.tsx @@ -10,8 +10,7 @@ import { useDebounce } from 'ahooks'; import Input from '@common/components/Input'; import { CgSpinner } from 'react-icons/cg'; import SelectRepoSource from '@modules/submitProject/Form/SelectRepoSource'; -import { userInfoStore } from '@modules/auth/UserInfoStore'; -import { useSnapshot } from 'valtio'; +import useProviderInfo from '@modules/auth/useProviderInfo'; import RepoItem from './RepoItem'; import Loading from './Loading'; import { useTranslation } from 'react-i18next'; @@ -21,7 +20,7 @@ const RepoSelect: React.FC<{ onConfirm: (val: string) => void }> = ({ onConfirm, }) => { const { t } = useTranslation(); - const { providerUser: user } = useSnapshot(userInfoStore); + const { providerUser: user } = useProviderInfo(); const nickname = user?.nickname!; const account = user?.account!; const provider = user?.provider!; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 26f08df3..202dc191 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -3,6 +3,7 @@ import Head from 'next/head'; import dynamic from 'next/dynamic'; import App, { AppContext, AppProps } from 'next/app'; import { appWithTranslation } from 'next-i18next'; +import { Toaster } from 'react-hot-toast'; import i18nextConfig from 'next-i18next.config.js'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; @@ -70,6 +71,17 @@ function MyApp({ color="#3A5BEF" options={{ showSpinner: false }} /> + diff --git a/src/pages/auth/signin.tsx b/src/pages/auth/signin.tsx index d97f51c5..a6d620e4 100644 --- a/src/pages/auth/signin.tsx +++ b/src/pages/auth/signin.tsx @@ -25,13 +25,14 @@ const SignIn: React.FC = () => { return (
+ + {error && (

{error}

)} -

{t('submit_project:welcome_to')} diff --git a/src/pages/settings/profile.tsx b/src/pages/settings/profile.tsx index 91e7cafb..14ed74aa 100644 --- a/src/pages/settings/profile.tsx +++ b/src/pages/settings/profile.tsx @@ -1,9 +1,11 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { GetServerSideProps } from 'next'; import { useTranslation } from 'react-i18next'; +import { useRouter } from 'next/router'; +import { toast } from 'react-hot-toast'; import Header from '@common/components/Header'; import Banner from '@modules/submitProject/Misc/Banner'; -import useAuthRedirect from '@modules/auth/useAuthRedirect'; +import AuthRequire from '@modules/auth/AuthRequire'; import ProfileSetting from '@modules/settings/profile'; import getLocalesFile from '@common/utils/getLocalesFile'; @@ -16,14 +18,30 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { }; const Settings = () => { + const { query } = useRouter(); const { t } = useTranslation(); - useAuthRedirect(); + + useEffect(() => { + let t: number; + if (query.error) { + t = window.setTimeout(() => { + toast.error((t) => <>{query.error}, { + position: 'top-center', + }); + }, 400); + } + return () => { + t && clearTimeout(t); + }; + }, [query.error]); return ( <>
- + + + ); }; diff --git a/src/pages/settings/subscribe.tsx b/src/pages/settings/subscribe.tsx index 022260ae..c4b4b51d 100644 --- a/src/pages/settings/subscribe.tsx +++ b/src/pages/settings/subscribe.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { GetServerSideProps } from 'next'; import { useTranslation } from 'react-i18next'; -import useAuthRedirect from '@modules/auth/useAuthRedirect'; +import AuthRequire from '@modules/auth/AuthRequire'; import Header from '@common/components/Header'; import Banner from '@modules/submitProject/Misc/Banner'; import getLocalesFile from '@common/utils/getLocalesFile'; @@ -16,11 +16,11 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { const Subscribe = () => { const { t } = useTranslation(); - useAuthRedirect(); return ( <>
+ ); }; diff --git a/src/pages/submit-your-project/community.tsx b/src/pages/submit-your-project/community.tsx index 063869db..0fb2804f 100644 --- a/src/pages/submit-your-project/community.tsx +++ b/src/pages/submit-your-project/community.tsx @@ -3,7 +3,7 @@ import Header from '@common/components/Header'; import Banner from '@modules/submitProject/Misc/Banner'; import SubmitProject from '@modules/submitProject'; import FormCommunity from '@modules/submitProject/FormCommunity'; -import useAuthRedirect from '@modules/auth/useAuthRedirect'; +import AuthRequire from '@modules/auth/AuthRequire'; import { GetServerSidePropsContext } from 'next'; import getLocalesFile from '@common/utils/getLocalesFile'; import { useTranslation } from 'react-i18next'; @@ -19,14 +19,15 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { const SubmitYourProject: React.FC = () => { const { t } = useTranslation(); - useAuthRedirect(); return ( <>
- - - + + + + + ); }; diff --git a/src/pages/submit-your-project/index.tsx b/src/pages/submit-your-project/index.tsx index 588043c7..dfcc5306 100644 --- a/src/pages/submit-your-project/index.tsx +++ b/src/pages/submit-your-project/index.tsx @@ -1,6 +1,6 @@ import React, { useEffect } from 'react'; import { GetServerSidePropsContext } from 'next'; -import useAuthRedirect from '@modules/auth/useAuthRedirect'; +import AuthRequire from '@modules/auth/AuthRequire'; import Header from '@common/components/Header'; import Banner from '@modules/submitProject/Misc/Banner'; import SubmitProject from '@modules/submitProject'; @@ -19,15 +19,16 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { const SubmitYourProject = () => { const { t } = useTranslation(); - useAuthRedirect(); return ( <>
- - - + + + + + ); }; diff --git a/yarn.lock b/yarn.lock index e78c1060..bc3eb926 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8700,6 +8700,11 @@ globby@^11.0.1, globby@^11.0.2, globby@^11.0.3, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +goober@^2.1.10: + version "2.1.13" + resolved "https://registry.npmmirror.com/goober/-/goober-2.1.13.tgz#e3c06d5578486212a76c9eba860cbc3232ff6d7c" + integrity sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ== + gopd@^1.0.1: version "1.0.1" resolved "https://registry.npmmirror.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -12357,6 +12362,13 @@ react-hook-form@^7.36.0: resolved "https://registry.npmmirror.com/react-hook-form/-/react-hook-form-7.38.0.tgz" integrity sha512-gxWW1kMeru9xR1GoR+Iw4hA+JBOM3SHfr4DWCUKY0xc7Vv1MLsF109oHtBeWl9shcyPFx67KHru44DheN0XY5A== +react-hot-toast@^2.4.1: + version "2.4.1" + resolved "https://registry.npmmirror.com/react-hot-toast/-/react-hot-toast-2.4.1.tgz#df04295eda8a7b12c4f968e54a61c8d36f4c0994" + integrity sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ== + dependencies: + goober "^2.1.10" + react-hotkeys-hook@^3.4.7: version "3.4.7" resolved "https://registry.npmmirror.com/react-hotkeys-hook/-/react-hotkeys-hook-3.4.7.tgz"