Skip to content

Commit

Permalink
feat: userinfo loading
Browse files Browse the repository at this point in the history
Signed-off-by: neil <[email protected]>
  • Loading branch information
nanzm committed May 15, 2023
1 parent 1c6713b commit 53150c8
Show file tree
Hide file tree
Showing 30 changed files with 358 additions and 175 deletions.
3 changes: 2 additions & 1 deletion i18n/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"signout": "SignOut",
"btn": {
"save": "Save",
"cancel": "Cancel"
"cancel": "Cancel",
"func_disabled": "The feature is temporarily disabled"
}
}
7 changes: 6 additions & 1 deletion i18n/en/setting.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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"
}
}
3 changes: 2 additions & 1 deletion i18n/en/submit_project.json
Original file line number Diff line number Diff line change
Expand Up @@ -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}}",
Expand Down
3 changes: 2 additions & 1 deletion i18n/zh/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"signout": "退出",
"btn": {
"save": "保存",
"cancel": "取消"
"cancel": "取消",
"func_disabled": "该功能临时关闭"
}
}
7 changes: 6 additions & 1 deletion i18n/zh/setting.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"name": "昵称",
"name_placeholder": "请输入昵称",
"email": "邮箱",
"email_ques_icon": "该电子邮件用于接收项目更新通知",
"email_placeholder": "请输入邮箱地址",
"error_require": "{{field}}不能为空",
"error_name_max_len": "昵称长度不能超过{{length}}",
Expand All @@ -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": "发送验证邮件"
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
36 changes: 22 additions & 14 deletions src/common/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -38,17 +38,22 @@ const buttonVariants = cva(

interface ButtonVariants extends VariantProps<typeof buttonVariants> {}

const Button: React.FC<PropsWithChildren<ButtonProps & ButtonVariants>> = ({
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 },
Expand All @@ -57,6 +62,7 @@ const Button: React.FC<PropsWithChildren<ButtonProps & ButtonVariants>> = ({

return (
<button
ref={ref}
type={type}
className={twMerge(cls)}
onClick={(e) => {
Expand All @@ -66,12 +72,14 @@ const Button: React.FC<PropsWithChildren<ButtonProps & ButtonVariants>> = ({
}
onClick?.(e);
}}
{...props}
{...restProps}
>
{loading && <CgSpinner className="mr-1 animate-spin text-xl" />}
{children}
</button>
);
};
});

Button.displayName = 'Button';

export default Button;
6 changes: 3 additions & 3 deletions src/common/components/Header/User.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
58 changes: 58 additions & 0 deletions src/modules/auth/AuthRequire.tsx
Original file line number Diff line number Diff line change
@@ -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<PropsWithChildren<Props>> = ({
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 (
<div className={className}>
<div className="flex-1 space-y-4">
<div className="h-6 rounded bg-slate-200"></div>

<div className="grid grid-cols-3 gap-4">
<div className="col-span-2 h-6 rounded bg-slate-200"></div>
<div className="col-span-1 h-6 rounded bg-slate-200"></div>
</div>

<div className="h-6 rounded bg-slate-200"></div>

<div className="grid grid-cols-3 gap-4">
<div className="col-span-1 h-6 rounded bg-slate-200"></div>
<div className="col-span-2 h-6 rounded bg-slate-200"></div>
</div>

<div className="h-6 rounded bg-slate-200"></div>
</div>
</div>
);
}

return <>{children}</>;
};

export default AuthRequire;
30 changes: 0 additions & 30 deletions src/modules/auth/UserInfoStore.tsx
Original file line number Diff line number Diff line change
@@ -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<UserinfoQuery['currentUser']>['loginBinds']
>[0];

export const userEvent = {
REFRESH: 'refresh' as const,
};
Expand All @@ -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<UserEventType> | 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;
};

Expand All @@ -58,6 +29,5 @@ export const serUserLoading = (loading: boolean) => {

export const resetUserInfo = () => {
userInfoStore.loading = false;
userInfoStore.providerUser = null;
userInfoStore.currentUser = null;
};
15 changes: 0 additions & 15 deletions src/modules/auth/useAuthRedirect.ts

This file was deleted.

64 changes: 64 additions & 0 deletions src/modules/auth/useProviderInfo.tsx
Original file line number Diff line number Diff line change
@@ -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<UserinfoQuery['currentUser']>['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;
2 changes: 1 addition & 1 deletion src/modules/settings/profile/DeleteAccount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const DeleteAccount = () => {
{t('setting:profile.delete_account_warning')}
</DialogContentText>
</DialogContent>
<DialogActions className="p-6">
<DialogActions className="!p-6">
<Button
intent="text"
size="sm"
Expand Down
Loading

0 comments on commit 53150c8

Please sign in to comment.