Skip to content

Commit f645f73

Browse files
committed
refactor: optimize iOS login flow and refactor profile fetching
1 parent b0bc8b9 commit f645f73

5 files changed

Lines changed: 59 additions & 15 deletions

File tree

web/src/locales/en.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@
8484
"nicknameRequired": "Nickname is required",
8585
"nicknameMaxLength": "Nickname cannot exceed 20 characters"
8686
},
87+
"authorize": {
88+
"title": "Authorize Login",
89+
"message": "You are about to authorize Rote Web App to sign in to Rote iOS App as {{username}}",
90+
"button": "Authorize Login"
91+
},
8792
"messages": {
8893
"loggingIn": "Logging in...",
8994
"loginSuccess": "Login successful",

web/src/locales/zh.json

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,17 @@
8181
"usernameFormat": "用户名只能包含字母、数字、下划线和连字符",
8282
"usernameConflict": "此用户名与系统路由冲突",
8383
"passwordRequired": "请输入密码",
84-
"passwordMaxLength": "密码不能超过30个字符",
84+
"passwordMaxLength": "密码最长30位",
8585
"emailRequired": "请输入邮箱",
86-
"emailMaxLength": "邮箱不能超过30个字符",
87-
"emailFormat": "邮箱格式不正确",
86+
"emailMaxLength": "邮箱最长30位",
87+
"emailFormat": "请输入正确的邮箱格式",
8888
"nicknameRequired": "请输入昵称",
89-
"nicknameMaxLength": "昵称不能超过20个字符"
89+
"nicknameMaxLength": "昵称最长20位"
90+
},
91+
"authorize": {
92+
"title": "授权登录",
93+
"message": "你将授权 Rote Web App 以 {{username}} 的身份登录 Rote iOS App",
94+
"button": "授权登录"
9095
},
9196
"messages": {
9297
"loggingIn": "登录中...",

web/src/pages/login/index.tsx

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,9 @@ import { Button } from '@/components/ui/button';
1212
import { Input } from '@/components/ui/input';
1313
import { Label } from '@/components/ui/label';
1414
import mainJson from '@/json/main.json';
15-
import { loadProfileAtom, profileAtom } from '@/state/profile';
1615
import { get, post } from '@/utils/api';
1716
import { authService } from '@/utils/auth';
1817
import { useAPIGet } from '@/utils/fetcher';
19-
import { useAtomValue, useSetAtom } from 'jotai';
2018
import { useEffect, useState } from 'react';
2119
import { useTranslation } from 'react-i18next';
2220
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
@@ -33,8 +31,10 @@ function Login() {
3331
get('/site/status').then((res) => res.data)
3432
);
3533

36-
const profile = useAtomValue(profileAtom);
37-
const loadProfile = useSetAtom(loadProfileAtom);
34+
const { data: profile, mutate: mutateProfile } = useAPIGet(
35+
authService.hasValidAccessToken() ? '/users/me/profile' : null,
36+
() => get('/users/me/profile').then((res) => res.data)
37+
);
3838

3939
const navigate = useNavigate();
4040

@@ -77,6 +77,17 @@ function Login() {
7777
nickname: z.string().min(1, t('nicknameRequired')).max(20, t('nicknameMaxLength')),
7878
});
7979

80+
function authorizeIosLogin() {
81+
const accessToken = authService.getAccessToken();
82+
if (accessToken) {
83+
const callbackUrl = `rote://callback?token=${accessToken}`;
84+
window.location.href = callbackUrl;
85+
} else {
86+
// 如果 token 丢失,提示用户重新登录
87+
toast.error(t('messages.tokenNotFound'));
88+
}
89+
}
90+
8091
function login() {
8192
try {
8293
LoginDataZod.parse(loginData);
@@ -96,7 +107,7 @@ function Login() {
96107
toast.success(t('messages.loginSuccess'));
97108
setDisbled(false);
98109
// 登录成功后刷新全局 profile
99-
loadProfile();
110+
mutateProfile();
100111

101112
// 检查是否为 iOS web 登录流程
102113
if (searchParams.get('type') === 'ioslogin') {
@@ -177,17 +188,38 @@ function Login() {
177188
}
178189

179190
useEffect(() => {
180-
if (profile) {
181-
navigate('/home');
191+
// 如果用户已有 token,则主动加载 profile
192+
// 这可以确保在用户已登录的情况下,直接访问登录页也能正确显示授权 UI
193+
if (authService.hasValidAccessToken()) {
194+
mutateProfile();
182195
}
183-
}, [profile, navigate]);
196+
}, [mutateProfile]);
184197

185198
return (
186-
<div className="bg-pattern relative flex h-dvh w-full items-center justify-center">
199+
<div className="relative flex h-dvh w-full items-center justify-center">
187200
<div className="animate-show text-primary z-10 flex w-96 flex-col gap-2 rounded-lg px-2 py-6 pb-10 opacity-0">
188201
{isCheckingStatus ? (
189202
<LoadingPlaceholder className="py-8" size={6} />
203+
) : profile && searchParams.get('type') === 'ioslogin' ? (
204+
// 已登录且是 iOS 登录流程,显示授权UI
205+
<>
206+
<div className="mb-4">
207+
<Logo className="w-32" color="#07C160" />
208+
</div>
209+
<div className="bg-muted/50 w-full rounded-lg p-6">
210+
<h2 className="mb-4 text-lg">{t('authorize.title')}</h2>
211+
<p className="mb-6 text-sm font-light">
212+
{t('authorize.message', {
213+
username: profile.nickname || profile.username,
214+
})}
215+
</p>
216+
<Button onClick={authorizeIosLogin} className="w-full">
217+
{t('authorize.button')}
218+
</Button>
219+
</div>
220+
</>
190221
) : (
222+
// 未登录或普通 web 访问,显示标准登录/注册UI
191223
<>
192224
<div className="mb-4">
193225
<Logo className="w-32" color="#07C160" />

web/src/route/main.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import SetupPage from '@/pages/setup';
1717
import UserPage from '@/pages/user/:username';
1818

1919
export default function GlobalRouterProvider() {
20+
const isIosLogin = new URLSearchParams(window.location.search).get('type') === 'ioslogin';
21+
2022
const router = createBrowserRouter([
2123
{
2224
path: 'landing',
@@ -25,7 +27,7 @@ export default function GlobalRouterProvider() {
2527
},
2628
{
2729
path: 'login',
28-
element: isTokenValid() ? <Navigate to="/home" /> : <Login />,
30+
element: isTokenValid() && !isIosLogin ? <Navigate to="/home" /> : <Login />,
2931
errorElement: <ErrorPage />,
3032
},
3133
{

web/src/utils/fetcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ interface APIGetProps {
4747
}
4848

4949
export function useAPIGet<TData>(
50-
props: APIGetProps | string,
50+
props: APIGetProps | string | null,
5151
fetcher: () => Promise<TData>,
5252
options?: SWRConfiguration<TData>
5353
) {

0 commit comments

Comments
 (0)