my-kaiqiuwang/src/page/UserCenter.tsx
kyuuseiryuu 23888b31bc feat: integrate Logto for authentication
- Add @logto/react dependencies to package.json and lockfile.
- Replace custom App layout with LogtoProvider for authentication handling.
- Configure Logto settings (endpoint, appId) in frontend.tsx.
- Refactor FindUserPage search logic to trigger request on value change instead of manual search key refresh.
2026-03-07 02:24:01 +09:00

81 lines
3.1 KiB
TypeScript

import { EditOutlined, HomeOutlined, KeyOutlined, LockOutlined, LoginOutlined, LogoutOutlined, MailOutlined, MobileOutlined } from "@ant-design/icons";
import { useLogto, type IdTokenClaims } from "@logto/react";
import { Avatar, Button, Divider, Flex, Modal, Typography } from "antd";
import { useCallback, useEffect, useState } from "react";
import { useNavigate } from "react-router";
enum modifyRoutes {
username = '/account/username',
email = '/account/email',
passwd = '/account/password',
passkey = '/account/passkey/manage',
auth_app = '/account/authenticator-app',
}
const redirect = encodeURIComponent(`${window.location.origin}/user-center`);
const logto = 'https://logto.ksr.la';
export const UserCenter = () => {
const { signIn, isAuthenticated, signOut, getIdTokenClaims } = useLogto();
const [user, setUser] = useState<IdTokenClaims>();
const navigate = useNavigate();
useEffect(() => {
if (!isAuthenticated) return;
getIdTokenClaims().then(claims => setUser(claims));
}, [isAuthenticated]);
const handleModifyInfo = useCallback((url: string) => {
window.location.href = `${logto}${url}?redirect=${redirect}`;
}, []);
if (!isAuthenticated) {
return (
<div className="app">
<Button
block
type="primary"
icon={<LogoutOutlined />}
size="large"
onClick={() => signIn(`${window.location.origin}/auth/callback`)}
>
</Button>
</div>
);
}
console.debug(user);
return (
<Flex className="app" gap={12} vertical align="center" style={{ maxWidth: 600 }}>
<Avatar size={128} src={user?.picture ?? user?.name} />
<Flex>
<Typography.Text>{user?.username ?? user?.name ?? '未设置'}</Typography.Text>
</Flex>
<Divider></Divider>
<Button block icon={<EditOutlined />} onClick={() => handleModifyInfo(modifyRoutes.username)}>({user?.username ?? '未设置'})</Button>
<Button block icon={<MailOutlined />} onClick={() => handleModifyInfo(modifyRoutes.email)}> E-Mail</Button>
<Button block icon={<LockOutlined />} onClick={() => handleModifyInfo(modifyRoutes.auth_app)}></Button>
<Button block icon={<MobileOutlined />} onClick={() => handleModifyInfo(modifyRoutes.auth_app)}></Button>
<Button block icon={<KeyOutlined />} onClick={() => handleModifyInfo(modifyRoutes.passkey)}> Passkey</Button>
<Divider />
<Button block type="primary" onClick={() => navigate('/')} icon={<HomeOutlined />}></Button>
<Divider />
<Button
block
danger
icon={<LoginOutlined />}
onClick={() => Modal.confirm({
title: '确认登出?',
cancelText: '回到首页',
cancelButtonProps: { icon: <HomeOutlined /> },
onCancel: () => navigate('/'),
okText: '确认登出',
okButtonProps: {
icon: <LogoutOutlined />,
danger: true,
},
onOk: () => signOut(`${window.location.origin}/user-center`),
})}
>
</Button>
</Flex>
);
}