@lobehub/lobehub 2.0.0-next.163 → 2.0.0-next.165
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/desktop-feature-implementation.mdc +31 -34
- package/.cursor/rules/desktop-local-tools-implement.mdc +3 -3
- package/.cursor/rules/desktop-window-management.mdc +56 -66
- package/CHANGELOG.md +52 -0
- package/README.md +6 -6
- package/README.zh-CN.md +6 -6
- package/apps/desktop/Development.md +42 -46
- package/apps/desktop/README.md +37 -1
- package/apps/desktop/README.zh-CN.md +26 -1
- package/apps/desktop/electron.vite.config.ts +1 -0
- package/apps/desktop/src/main/controllers/AuthCtr.ts +4 -3
- package/apps/desktop/src/main/controllers/BrowserWindowsCtr.ts +33 -20
- package/apps/desktop/src/main/controllers/DevtoolsCtr.ts +4 -2
- package/apps/desktop/src/main/controllers/LocalFileCtr.ts +14 -13
- package/apps/desktop/src/main/controllers/MenuCtr.ts +5 -4
- package/apps/desktop/src/main/controllers/NetworkProxyCtr.ts +18 -19
- package/apps/desktop/src/main/controllers/NotificationCtr.ts +4 -3
- package/apps/desktop/src/main/controllers/RemoteServerConfigCtr.ts +5 -4
- package/apps/desktop/src/main/controllers/RemoteServerSyncCtr.ts +3 -2
- package/apps/desktop/src/main/controllers/ShellCommandCtr.ts +5 -4
- package/apps/desktop/src/main/controllers/ShortcutCtr.ts +4 -3
- package/apps/desktop/src/main/controllers/SystemCtr.ts +7 -37
- package/apps/desktop/src/main/controllers/SystemServerCtr.ts +38 -0
- package/apps/desktop/src/main/controllers/TrayMenuCtr.ts +5 -4
- package/apps/desktop/src/main/controllers/UpdaterCtr.ts +6 -5
- package/apps/desktop/src/main/controllers/UploadFileCtr.ts +3 -25
- package/apps/desktop/src/main/controllers/UploadFileServerCtr.ts +33 -0
- package/apps/desktop/src/main/controllers/__tests__/AuthCtr.test.ts +9 -1
- package/apps/desktop/src/main/controllers/__tests__/BrowserWindowsCtr.test.ts +29 -9
- package/apps/desktop/src/main/controllers/__tests__/DevtoolsCtr.test.ts +12 -3
- package/apps/desktop/src/main/controllers/__tests__/LocalFileCtr.test.ts +7 -0
- package/apps/desktop/src/main/controllers/__tests__/MenuCtr.test.ts +10 -0
- package/apps/desktop/src/main/controllers/__tests__/NetworkProxyCtr.test.ts +10 -0
- package/apps/desktop/src/main/controllers/__tests__/NotificationCtr.test.ts +8 -0
- package/apps/desktop/src/main/controllers/__tests__/RemoteServerConfigCtr.test.ts +8 -0
- package/apps/desktop/src/main/controllers/__tests__/RemoteServerSyncCtr.test.ts +1 -0
- package/apps/desktop/src/main/controllers/__tests__/ShellCommandCtr.test.ts +10 -0
- package/apps/desktop/src/main/controllers/__tests__/ShortcutCtr.test.ts +11 -0
- package/apps/desktop/src/main/controllers/__tests__/SystemCtr.test.ts +43 -73
- package/apps/desktop/src/main/controllers/__tests__/SystemServerCtr.test.ts +75 -0
- package/apps/desktop/src/main/controllers/__tests__/TrayMenuCtr.test.ts +24 -13
- package/apps/desktop/src/main/controllers/__tests__/UpdaterCtr.test.ts +13 -2
- package/apps/desktop/src/main/controllers/__tests__/UploadFileCtr.test.ts +29 -108
- package/apps/desktop/src/main/controllers/__tests__/UploadFileServerCtr.test.ts +55 -0
- package/apps/desktop/src/main/controllers/_template.ts +2 -2
- package/apps/desktop/src/main/controllers/index.ts +5 -29
- package/apps/desktop/src/main/controllers/registry.ts +52 -0
- package/apps/desktop/src/main/core/App.ts +15 -47
- package/apps/desktop/src/main/core/__tests__/App.test.ts +5 -4
- package/apps/desktop/src/main/core/infrastructure/IoCContainer.ts +0 -5
- package/apps/desktop/src/main/core/infrastructure/__tests__/IoCContainer.test.ts +0 -50
- package/apps/desktop/src/main/exports.d.ts +8 -0
- package/apps/desktop/src/main/exports.ts +2 -0
- package/apps/desktop/src/main/global.d.ts +3 -0
- package/apps/desktop/src/main/modules/fileSearch/__tests__/macOS.integration.test.ts +17 -8
- package/apps/desktop/src/main/package.json +10 -0
- package/apps/desktop/src/main/services/fileSrv.ts +1 -1
- package/apps/desktop/src/main/utils/ipc/__tests__/base.test.ts +91 -0
- package/apps/desktop/src/main/utils/ipc/base.ts +170 -0
- package/apps/desktop/src/main/utils/ipc/index.ts +11 -0
- package/apps/desktop/src/main/utils/ipc/utility.ts +20 -0
- package/apps/desktop/src/preload/electronApi.ts +4 -1
- package/apps/desktop/src/preload/invoke.test.ts +13 -16
- package/apps/desktop/src/preload/invoke.ts +2 -5
- package/apps/desktop/src/preload/routeInterceptor.test.ts +13 -13
- package/apps/desktop/src/preload/routeInterceptor.ts +4 -4
- package/apps/desktop/tsconfig.json +15 -5
- package/changelog/v1.json +14 -0
- package/locales/ar/auth.json +3 -0
- package/locales/bg-BG/auth.json +3 -0
- package/locales/de-DE/auth.json +3 -0
- package/locales/en-US/auth.json +4 -1
- package/locales/es-ES/auth.json +3 -0
- package/locales/fa-IR/auth.json +3 -0
- package/locales/fr-FR/auth.json +3 -0
- package/locales/it-IT/auth.json +3 -0
- package/locales/ja-JP/auth.json +3 -0
- package/locales/ko-KR/auth.json +3 -0
- package/locales/nl-NL/auth.json +3 -0
- package/locales/pl-PL/auth.json +3 -0
- package/locales/pt-BR/auth.json +3 -0
- package/locales/ru-RU/auth.json +3 -0
- package/locales/tr-TR/auth.json +3 -0
- package/locales/vi-VN/auth.json +3 -0
- package/locales/zh-CN/auth.json +3 -0
- package/locales/zh-TW/auth.json +3 -0
- package/package.json +4 -3
- package/packages/electron-client-ipc/src/index.ts +1 -1
- package/packages/electron-client-ipc/src/ipc.test.ts +62 -0
- package/packages/electron-client-ipc/src/ipc.ts +63 -0
- package/packages/electron-client-ipc/src/streamInvoke.ts +7 -1
- package/packages/electron-client-ipc/src/types/dispatch.ts +1 -10
- package/packages/electron-client-ipc/vitest.config.mts +10 -0
- package/packages/electron-server-ipc/src/ipcClient.ts +1 -2
- package/packages/electron-server-ipc/src/ipcServer.ts +1 -2
- package/packages/electron-server-ipc/src/types/index.ts +1 -5
- package/pnpm-workspace.yaml +1 -1
- package/scripts/i18nWorkflow/const.ts +2 -2
- package/scripts/i18nWorkflow/i18nConfig.ts +7 -0
- package/scripts/i18nWorkflow/utils.ts +1 -1
- package/src/app/[variants]/(auth)/signup/[[...signup]]/BetterAuthSignUpForm.tsx +23 -0
- package/src/app/[variants]/(main)/(mobile)/me/(home)/features/UserBanner.tsx +3 -9
- package/src/app/[variants]/(main)/discover/(detail)/provider/features/Sidebar/ActionButton/ProviderConfig.tsx +2 -2
- package/src/app/[variants]/(main)/profile/(home)/Client.tsx +206 -138
- package/src/features/User/PlanTag.tsx +4 -4
- package/src/locales/default/auth.ts +3 -0
- package/src/locales/default/setting.ts +1 -0
- package/src/server/modules/ElectronIPCClient/index.ts +59 -13
- package/src/services/electron/__tests__/devtools.test.ts +10 -6
- package/src/services/electron/autoUpdate.ts +5 -5
- package/src/services/electron/desktopNotification.ts +4 -7
- package/src/services/electron/devtools.ts +2 -2
- package/src/services/electron/file.ts +3 -2
- package/src/services/electron/localFileService.ts +17 -16
- package/src/services/electron/remoteServer.ts +7 -6
- package/src/services/electron/settings.ts +9 -11
- package/src/services/electron/system.ts +8 -6
- package/src/store/chat/slices/plugin/actions/pluginTypes.ts +1 -1
- package/src/store/global/actions/general.ts +8 -10
- package/src/utils/electron/desktopRemoteRPCFetch.ts +3 -2
- package/src/utils/electron/ipc.ts +12 -0
- package/tsconfig.json +5 -0
- package/apps/desktop/src/main/types/ipcClientEvent.ts +0 -3
- package/packages/electron-client-ipc/src/dispatch.ts +0 -41
|
@@ -52,6 +52,7 @@ const useStyles = createStyles(({ css, token }) => ({
|
|
|
52
52
|
}));
|
|
53
53
|
|
|
54
54
|
interface SignUpFormValues {
|
|
55
|
+
confirmPassword: string;
|
|
55
56
|
email: string;
|
|
56
57
|
password: string;
|
|
57
58
|
}
|
|
@@ -179,6 +180,28 @@ export default function BetterAuthSignUpForm() {
|
|
|
179
180
|
/>
|
|
180
181
|
</Form.Item>
|
|
181
182
|
|
|
183
|
+
<Form.Item
|
|
184
|
+
dependencies={['password']}
|
|
185
|
+
name="confirmPassword"
|
|
186
|
+
rules={[
|
|
187
|
+
{ message: t('betterAuth.errors.confirmPasswordRequired'), required: true },
|
|
188
|
+
({ getFieldValue }) => ({
|
|
189
|
+
validator(_, value) {
|
|
190
|
+
if (!value || getFieldValue('password') === value) {
|
|
191
|
+
return Promise.resolve();
|
|
192
|
+
}
|
|
193
|
+
return Promise.reject(new Error(t('betterAuth.errors.passwordMismatch')));
|
|
194
|
+
},
|
|
195
|
+
}),
|
|
196
|
+
]}
|
|
197
|
+
>
|
|
198
|
+
<Input.Password
|
|
199
|
+
placeholder={t('betterAuth.signup.confirmPasswordPlaceholder')}
|
|
200
|
+
prefix={<Lock size={16} />}
|
|
201
|
+
size="large"
|
|
202
|
+
/>
|
|
203
|
+
</Form.Item>
|
|
204
|
+
|
|
182
205
|
<Form.Item>
|
|
183
206
|
<Button
|
|
184
207
|
block
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { memo } from 'react';
|
|
4
|
-
import { Link, useNavigate } from 'react-router-dom';
|
|
5
4
|
import { Flexbox } from 'react-layout-kit';
|
|
5
|
+
import { Link } from 'react-router-dom';
|
|
6
6
|
|
|
7
|
-
import { enableAuth
|
|
7
|
+
import { enableAuth } from '@/const/auth';
|
|
8
8
|
import DataStatistics from '@/features/User/DataStatistics';
|
|
9
9
|
import UserInfo from '@/features/User/UserInfo';
|
|
10
10
|
import UserLoginOrSignup from '@/features/User/UserLoginOrSignup/Community';
|
|
@@ -12,7 +12,6 @@ import { useUserStore } from '@/store/user';
|
|
|
12
12
|
import { authSelectors } from '@/store/user/selectors';
|
|
13
13
|
|
|
14
14
|
const UserBanner = memo(() => {
|
|
15
|
-
const navigate = useNavigate();
|
|
16
15
|
const isLoginWithAuth = useUserStore(authSelectors.isLoginWithAuth);
|
|
17
16
|
const [signIn] = useUserStore((s) => [s.openLogin]);
|
|
18
17
|
|
|
@@ -30,12 +29,7 @@ const UserBanner = memo(() => {
|
|
|
30
29
|
) : (
|
|
31
30
|
<UserLoginOrSignup
|
|
32
31
|
onClick={() => {
|
|
33
|
-
|
|
34
|
-
if (enableNextAuth) {
|
|
35
|
-
signIn();
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
navigate('/login');
|
|
32
|
+
signIn();
|
|
39
33
|
}}
|
|
40
34
|
/>
|
|
41
35
|
)}
|
|
@@ -30,8 +30,8 @@ const ProviderConfig = memo(() => {
|
|
|
30
30
|
const tab = 'provider';
|
|
31
31
|
|
|
32
32
|
if (isDesktop) {
|
|
33
|
-
const {
|
|
34
|
-
await
|
|
33
|
+
const { ensureElectronIpc } = await import('@/utils/electron/ipc');
|
|
34
|
+
await ensureElectronIpc().windows.openSettingsWindow({
|
|
35
35
|
searchParams,
|
|
36
36
|
tab,
|
|
37
37
|
});
|
|
@@ -31,6 +31,7 @@ interface ProfileRowProps {
|
|
|
31
31
|
action?: ReactNode;
|
|
32
32
|
children: ReactNode;
|
|
33
33
|
label: string;
|
|
34
|
+
mobile?: boolean;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
const rowStyle: CSSProperties = {
|
|
@@ -43,17 +44,31 @@ const labelStyle: CSSProperties = {
|
|
|
43
44
|
width: 160,
|
|
44
45
|
};
|
|
45
46
|
|
|
46
|
-
const ProfileRow = memo<ProfileRowProps>(({ label, children, action }) =>
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
<
|
|
50
|
-
|
|
47
|
+
const ProfileRow = memo<ProfileRowProps>(({ label, children, action, mobile }) => {
|
|
48
|
+
if (mobile) {
|
|
49
|
+
return (
|
|
50
|
+
<Flexbox gap={12} style={rowStyle}>
|
|
51
|
+
<Flexbox align="center" horizontal justify="space-between">
|
|
52
|
+
<Typography.Text strong>{label}</Typography.Text>
|
|
53
|
+
{action}
|
|
54
|
+
</Flexbox>
|
|
55
|
+
<Flexbox>{children}</Flexbox>
|
|
56
|
+
</Flexbox>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<Flexbox align="center" gap={24} horizontal justify="space-between" style={rowStyle}>
|
|
62
|
+
<Flexbox align="center" gap={24} horizontal style={{ flex: 1 }}>
|
|
63
|
+
<Typography.Text style={labelStyle}>{label}</Typography.Text>
|
|
64
|
+
<Flexbox style={{ flex: 1 }}>{children}</Flexbox>
|
|
65
|
+
</Flexbox>
|
|
66
|
+
{action && <Flexbox>{action}</Flexbox>}
|
|
51
67
|
</Flexbox>
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
));
|
|
68
|
+
);
|
|
69
|
+
});
|
|
55
70
|
|
|
56
|
-
const AvatarRow = memo(() => {
|
|
71
|
+
const AvatarRow = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
57
72
|
const { t } = useTranslation('auth');
|
|
58
73
|
const isLogin = useUserStore(authSelectors.isLogin);
|
|
59
74
|
const updateAvatar = useUserStore((s) => s.updateAvatar);
|
|
@@ -89,34 +104,48 @@ const AvatarRow = memo(() => {
|
|
|
89
104
|
|
|
90
105
|
const canUpload = !enableAuth || isLogin;
|
|
91
106
|
|
|
107
|
+
const avatarContent = canUpload ? (
|
|
108
|
+
<Spin indicator={<LoadingOutlined spin />} spinning={uploading}>
|
|
109
|
+
<Upload beforeUpload={handleUploadAvatar} itemRender={() => void 0} maxCount={1}>
|
|
110
|
+
<UserAvatar clickable size={40} />
|
|
111
|
+
</Upload>
|
|
112
|
+
</Spin>
|
|
113
|
+
) : (
|
|
114
|
+
<UserAvatar size={40} />
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const updateAction = canUpload ? (
|
|
118
|
+
<Upload beforeUpload={handleUploadAvatar} itemRender={() => void 0} maxCount={1}>
|
|
119
|
+
<Typography.Text style={{ cursor: 'pointer', fontSize: 13 }}>
|
|
120
|
+
{t('profile.updateAvatar')}
|
|
121
|
+
</Typography.Text>
|
|
122
|
+
</Upload>
|
|
123
|
+
) : null;
|
|
124
|
+
|
|
125
|
+
if (mobile) {
|
|
126
|
+
return (
|
|
127
|
+
<Flexbox gap={12} style={rowStyle}>
|
|
128
|
+
<Flexbox align="center" horizontal justify="space-between">
|
|
129
|
+
<Typography.Text strong>{t('profile.avatar')}</Typography.Text>
|
|
130
|
+
{updateAction}
|
|
131
|
+
</Flexbox>
|
|
132
|
+
<Flexbox>{avatarContent}</Flexbox>
|
|
133
|
+
</Flexbox>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
92
137
|
return (
|
|
93
138
|
<Flexbox align="center" gap={24} horizontal justify="space-between" style={rowStyle}>
|
|
94
139
|
<Flexbox align="center" gap={24} horizontal style={{ flex: 1 }}>
|
|
95
140
|
<Typography.Text style={labelStyle}>{t('profile.avatar')}</Typography.Text>
|
|
96
|
-
<Flexbox style={{ flex: 1 }}>
|
|
97
|
-
{canUpload ? (
|
|
98
|
-
<Spin indicator={<LoadingOutlined spin />} spinning={uploading}>
|
|
99
|
-
<Upload beforeUpload={handleUploadAvatar} itemRender={() => void 0} maxCount={1}>
|
|
100
|
-
<UserAvatar clickable size={40} />
|
|
101
|
-
</Upload>
|
|
102
|
-
</Spin>
|
|
103
|
-
) : (
|
|
104
|
-
<UserAvatar size={40} />
|
|
105
|
-
)}
|
|
106
|
-
</Flexbox>
|
|
141
|
+
<Flexbox style={{ flex: 1 }}>{avatarContent}</Flexbox>
|
|
107
142
|
</Flexbox>
|
|
108
|
-
{
|
|
109
|
-
<Upload beforeUpload={handleUploadAvatar} itemRender={() => void 0} maxCount={1}>
|
|
110
|
-
<Typography.Text style={{ cursor: 'pointer', fontSize: 13 }}>
|
|
111
|
-
{t('profile.updateAvatar')}
|
|
112
|
-
</Typography.Text>
|
|
113
|
-
</Upload>
|
|
114
|
-
)}
|
|
143
|
+
{updateAction}
|
|
115
144
|
</Flexbox>
|
|
116
145
|
);
|
|
117
146
|
});
|
|
118
147
|
|
|
119
|
-
const FullNameRow = memo(() => {
|
|
148
|
+
const FullNameRow = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
120
149
|
const { t } = useTranslation('auth');
|
|
121
150
|
const fullName = useUserStore(userProfileSelectors.fullName);
|
|
122
151
|
const updateFullName = useUserStore((s) => s.updateFullName);
|
|
@@ -152,64 +181,83 @@ const FullNameRow = memo(() => {
|
|
|
152
181
|
}
|
|
153
182
|
}, [editValue, updateFullName]);
|
|
154
183
|
|
|
184
|
+
const editingContent = (
|
|
185
|
+
<motion.div
|
|
186
|
+
animate={{ opacity: 1, y: 0 }}
|
|
187
|
+
exit={{ opacity: 0, y: -10 }}
|
|
188
|
+
initial={{ opacity: 0, y: -10 }}
|
|
189
|
+
key="editing"
|
|
190
|
+
transition={{ duration: 0.2 }}
|
|
191
|
+
>
|
|
192
|
+
<Flexbox gap={12}>
|
|
193
|
+
{!mobile && <Typography.Text strong>{t('profile.fullNameInputHint')}</Typography.Text>}
|
|
194
|
+
<Input
|
|
195
|
+
autoFocus
|
|
196
|
+
onChange={(e) => setEditValue(e.target.value)}
|
|
197
|
+
onPressEnter={handleSave}
|
|
198
|
+
placeholder={t('profile.fullName')}
|
|
199
|
+
value={editValue}
|
|
200
|
+
/>
|
|
201
|
+
<Flexbox gap={8} horizontal justify="flex-end">
|
|
202
|
+
<Button disabled={saving} onClick={handleCancel} size="small">
|
|
203
|
+
{t('profile.cancel')}
|
|
204
|
+
</Button>
|
|
205
|
+
<Button loading={saving} onClick={handleSave} size="small" type="primary">
|
|
206
|
+
{t('profile.save')}
|
|
207
|
+
</Button>
|
|
208
|
+
</Flexbox>
|
|
209
|
+
</Flexbox>
|
|
210
|
+
</motion.div>
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const displayContent = (
|
|
214
|
+
<motion.div
|
|
215
|
+
animate={{ opacity: 1 }}
|
|
216
|
+
exit={{ opacity: 0 }}
|
|
217
|
+
initial={{ opacity: 0 }}
|
|
218
|
+
key="display"
|
|
219
|
+
transition={{ duration: 0.2 }}
|
|
220
|
+
>
|
|
221
|
+
{mobile ? (
|
|
222
|
+
<Typography.Text>{fullName || '--'}</Typography.Text>
|
|
223
|
+
) : (
|
|
224
|
+
<Flexbox align="center" horizontal justify="space-between">
|
|
225
|
+
<Typography.Text>{fullName || '--'}</Typography.Text>
|
|
226
|
+
<Typography.Text onClick={handleStartEdit} style={{ cursor: 'pointer', fontSize: 13 }}>
|
|
227
|
+
{t('profile.updateFullName')}
|
|
228
|
+
</Typography.Text>
|
|
229
|
+
</Flexbox>
|
|
230
|
+
)}
|
|
231
|
+
</motion.div>
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
if (mobile) {
|
|
235
|
+
return (
|
|
236
|
+
<Flexbox gap={12} style={rowStyle}>
|
|
237
|
+
<Flexbox align="center" horizontal justify="space-between">
|
|
238
|
+
<Typography.Text strong>{t('profile.fullName')}</Typography.Text>
|
|
239
|
+
{!isEditing && (
|
|
240
|
+
<Typography.Text onClick={handleStartEdit} style={{ cursor: 'pointer', fontSize: 13 }}>
|
|
241
|
+
{t('profile.updateFullName')}
|
|
242
|
+
</Typography.Text>
|
|
243
|
+
)}
|
|
244
|
+
</Flexbox>
|
|
245
|
+
<AnimatePresence mode="wait">{isEditing ? editingContent : displayContent}</AnimatePresence>
|
|
246
|
+
</Flexbox>
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
155
250
|
return (
|
|
156
251
|
<Flexbox gap={24} horizontal style={rowStyle}>
|
|
157
252
|
<Typography.Text style={labelStyle}>{t('profile.fullName')}</Typography.Text>
|
|
158
253
|
<Flexbox style={{ flex: 1 }}>
|
|
159
|
-
<AnimatePresence mode="wait">
|
|
160
|
-
{isEditing ? (
|
|
161
|
-
<motion.div
|
|
162
|
-
animate={{ opacity: 1, y: 0 }}
|
|
163
|
-
exit={{ opacity: 0, y: -10 }}
|
|
164
|
-
initial={{ opacity: 0, y: -10 }}
|
|
165
|
-
key="editing"
|
|
166
|
-
transition={{ duration: 0.2 }}
|
|
167
|
-
>
|
|
168
|
-
<Flexbox gap={12}>
|
|
169
|
-
<Typography.Text strong>{t('profile.fullNameInputHint')}</Typography.Text>
|
|
170
|
-
<Input
|
|
171
|
-
autoFocus
|
|
172
|
-
onChange={(e) => setEditValue(e.target.value)}
|
|
173
|
-
onPressEnter={handleSave}
|
|
174
|
-
placeholder={t('profile.fullName')}
|
|
175
|
-
value={editValue}
|
|
176
|
-
/>
|
|
177
|
-
<Flexbox gap={8} horizontal justify="flex-end">
|
|
178
|
-
<Button disabled={saving} onClick={handleCancel} size="small">
|
|
179
|
-
{t('profile.cancel')}
|
|
180
|
-
</Button>
|
|
181
|
-
<Button loading={saving} onClick={handleSave} size="small" type="primary">
|
|
182
|
-
{t('profile.save')}
|
|
183
|
-
</Button>
|
|
184
|
-
</Flexbox>
|
|
185
|
-
</Flexbox>
|
|
186
|
-
</motion.div>
|
|
187
|
-
) : (
|
|
188
|
-
<motion.div
|
|
189
|
-
animate={{ opacity: 1 }}
|
|
190
|
-
exit={{ opacity: 0 }}
|
|
191
|
-
initial={{ opacity: 0 }}
|
|
192
|
-
key="display"
|
|
193
|
-
transition={{ duration: 0.2 }}
|
|
194
|
-
>
|
|
195
|
-
<Flexbox align="center" horizontal justify="space-between">
|
|
196
|
-
<Typography.Text>{fullName || '--'}</Typography.Text>
|
|
197
|
-
<Typography.Text
|
|
198
|
-
onClick={handleStartEdit}
|
|
199
|
-
style={{ cursor: 'pointer', fontSize: 13 }}
|
|
200
|
-
>
|
|
201
|
-
{t('profile.updateFullName')}
|
|
202
|
-
</Typography.Text>
|
|
203
|
-
</Flexbox>
|
|
204
|
-
</motion.div>
|
|
205
|
-
)}
|
|
206
|
-
</AnimatePresence>
|
|
254
|
+
<AnimatePresence mode="wait">{isEditing ? editingContent : displayContent}</AnimatePresence>
|
|
207
255
|
</Flexbox>
|
|
208
256
|
</Flexbox>
|
|
209
257
|
);
|
|
210
258
|
});
|
|
211
259
|
|
|
212
|
-
const UsernameRow = memo(() => {
|
|
260
|
+
const UsernameRow = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
213
261
|
const { t } = useTranslation('auth');
|
|
214
262
|
const username = useUserStore(userProfileSelectors.username);
|
|
215
263
|
const updateUsername = useUserStore((s) => s.updateUsername);
|
|
@@ -281,70 +329,89 @@ const UsernameRow = memo(() => {
|
|
|
281
329
|
setError('');
|
|
282
330
|
};
|
|
283
331
|
|
|
332
|
+
const editingContent = (
|
|
333
|
+
<motion.div
|
|
334
|
+
animate={{ opacity: 1, y: 0 }}
|
|
335
|
+
exit={{ opacity: 0, y: -10 }}
|
|
336
|
+
initial={{ opacity: 0, y: -10 }}
|
|
337
|
+
key="editing"
|
|
338
|
+
transition={{ duration: 0.2 }}
|
|
339
|
+
>
|
|
340
|
+
<Flexbox gap={12}>
|
|
341
|
+
{!mobile && <Typography.Text strong>{t('profile.usernameInputHint')}</Typography.Text>}
|
|
342
|
+
<Input
|
|
343
|
+
autoFocus
|
|
344
|
+
onChange={handleInputChange}
|
|
345
|
+
onPressEnter={handleSave}
|
|
346
|
+
placeholder={t('profile.usernamePlaceholder')}
|
|
347
|
+
status={error ? 'error' : undefined}
|
|
348
|
+
value={editValue}
|
|
349
|
+
/>
|
|
350
|
+
{error && (
|
|
351
|
+
<Typography.Text style={{ fontSize: 12 }} type="danger">
|
|
352
|
+
{error}
|
|
353
|
+
</Typography.Text>
|
|
354
|
+
)}
|
|
355
|
+
<Flexbox gap={8} horizontal justify="flex-end">
|
|
356
|
+
<Button disabled={saving} onClick={handleCancel} size="small">
|
|
357
|
+
{t('profile.cancel')}
|
|
358
|
+
</Button>
|
|
359
|
+
<Button loading={saving} onClick={handleSave} size="small" type="primary">
|
|
360
|
+
{t('profile.save')}
|
|
361
|
+
</Button>
|
|
362
|
+
</Flexbox>
|
|
363
|
+
</Flexbox>
|
|
364
|
+
</motion.div>
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
const displayContent = (
|
|
368
|
+
<motion.div
|
|
369
|
+
animate={{ opacity: 1 }}
|
|
370
|
+
exit={{ opacity: 0 }}
|
|
371
|
+
initial={{ opacity: 0 }}
|
|
372
|
+
key="display"
|
|
373
|
+
transition={{ duration: 0.2 }}
|
|
374
|
+
>
|
|
375
|
+
{mobile ? (
|
|
376
|
+
<Typography.Text>{username || '--'}</Typography.Text>
|
|
377
|
+
) : (
|
|
378
|
+
<Flexbox align="center" horizontal justify="space-between">
|
|
379
|
+
<Typography.Text>{username || '--'}</Typography.Text>
|
|
380
|
+
<Typography.Text onClick={handleStartEdit} style={{ cursor: 'pointer', fontSize: 13 }}>
|
|
381
|
+
{t('profile.updateUsername')}
|
|
382
|
+
</Typography.Text>
|
|
383
|
+
</Flexbox>
|
|
384
|
+
)}
|
|
385
|
+
</motion.div>
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
if (mobile) {
|
|
389
|
+
return (
|
|
390
|
+
<Flexbox gap={12} style={rowStyle}>
|
|
391
|
+
<Flexbox align="center" horizontal justify="space-between">
|
|
392
|
+
<Typography.Text strong>{t('profile.username')}</Typography.Text>
|
|
393
|
+
{!isEditing && (
|
|
394
|
+
<Typography.Text onClick={handleStartEdit} style={{ cursor: 'pointer', fontSize: 13 }}>
|
|
395
|
+
{t('profile.updateUsername')}
|
|
396
|
+
</Typography.Text>
|
|
397
|
+
)}
|
|
398
|
+
</Flexbox>
|
|
399
|
+
<AnimatePresence mode="wait">{isEditing ? editingContent : displayContent}</AnimatePresence>
|
|
400
|
+
</Flexbox>
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
|
|
284
404
|
return (
|
|
285
405
|
<Flexbox gap={24} horizontal style={rowStyle}>
|
|
286
406
|
<Typography.Text style={labelStyle}>{t('profile.username')}</Typography.Text>
|
|
287
407
|
<Flexbox style={{ flex: 1 }}>
|
|
288
|
-
<AnimatePresence mode="wait">
|
|
289
|
-
{isEditing ? (
|
|
290
|
-
<motion.div
|
|
291
|
-
animate={{ opacity: 1, y: 0 }}
|
|
292
|
-
exit={{ opacity: 0, y: -10 }}
|
|
293
|
-
initial={{ opacity: 0, y: -10 }}
|
|
294
|
-
key="editing"
|
|
295
|
-
transition={{ duration: 0.2 }}
|
|
296
|
-
>
|
|
297
|
-
<Flexbox gap={12}>
|
|
298
|
-
<Typography.Text strong>{t('profile.usernameInputHint')}</Typography.Text>
|
|
299
|
-
<Input
|
|
300
|
-
autoFocus
|
|
301
|
-
onChange={handleInputChange}
|
|
302
|
-
onPressEnter={handleSave}
|
|
303
|
-
placeholder={t('profile.usernamePlaceholder')}
|
|
304
|
-
status={error ? 'error' : undefined}
|
|
305
|
-
value={editValue}
|
|
306
|
-
/>
|
|
307
|
-
{error && (
|
|
308
|
-
<Typography.Text style={{ fontSize: 12 }} type="danger">
|
|
309
|
-
{error}
|
|
310
|
-
</Typography.Text>
|
|
311
|
-
)}
|
|
312
|
-
<Flexbox gap={8} horizontal justify="flex-end">
|
|
313
|
-
<Button disabled={saving} onClick={handleCancel} size="small">
|
|
314
|
-
{t('profile.cancel')}
|
|
315
|
-
</Button>
|
|
316
|
-
<Button loading={saving} onClick={handleSave} size="small" type="primary">
|
|
317
|
-
{t('profile.save')}
|
|
318
|
-
</Button>
|
|
319
|
-
</Flexbox>
|
|
320
|
-
</Flexbox>
|
|
321
|
-
</motion.div>
|
|
322
|
-
) : (
|
|
323
|
-
<motion.div
|
|
324
|
-
animate={{ opacity: 1 }}
|
|
325
|
-
exit={{ opacity: 0 }}
|
|
326
|
-
initial={{ opacity: 0 }}
|
|
327
|
-
key="display"
|
|
328
|
-
transition={{ duration: 0.2 }}
|
|
329
|
-
>
|
|
330
|
-
<Flexbox align="center" horizontal justify="space-between">
|
|
331
|
-
<Typography.Text>{username || '--'}</Typography.Text>
|
|
332
|
-
<Typography.Text
|
|
333
|
-
onClick={handleStartEdit}
|
|
334
|
-
style={{ cursor: 'pointer', fontSize: 13 }}
|
|
335
|
-
>
|
|
336
|
-
{t('profile.updateUsername')}
|
|
337
|
-
</Typography.Text>
|
|
338
|
-
</Flexbox>
|
|
339
|
-
</motion.div>
|
|
340
|
-
)}
|
|
341
|
-
</AnimatePresence>
|
|
408
|
+
<AnimatePresence mode="wait">{isEditing ? editingContent : displayContent}</AnimatePresence>
|
|
342
409
|
</Flexbox>
|
|
343
410
|
</Flexbox>
|
|
344
411
|
);
|
|
345
412
|
});
|
|
346
413
|
|
|
347
|
-
const PasswordRow = memo(() => {
|
|
414
|
+
const PasswordRow = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
348
415
|
const { t } = useTranslation('auth');
|
|
349
416
|
const userProfile = useUserStore(userProfileSelectors.userProfile);
|
|
350
417
|
const hasPasswordAccount = useUserStore(authSelectors.hasPasswordAccount);
|
|
@@ -387,6 +454,7 @@ const PasswordRow = memo(() => {
|
|
|
387
454
|
</Typography.Text>
|
|
388
455
|
}
|
|
389
456
|
label={t('profile.password')}
|
|
457
|
+
mobile={mobile}
|
|
390
458
|
>
|
|
391
459
|
<Typography.Text>{hasPasswordAccount ? '••••••' : '--'}</Typography.Text>
|
|
392
460
|
</ProfileRow>
|
|
@@ -435,24 +503,24 @@ const Client = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
|
435
503
|
<Divider style={{ marginBlock: 0 }} />
|
|
436
504
|
|
|
437
505
|
{/* Avatar Row - Editable */}
|
|
438
|
-
<AvatarRow />
|
|
506
|
+
<AvatarRow mobile={mobile} />
|
|
439
507
|
|
|
440
508
|
<Divider style={{ margin: 0 }} />
|
|
441
509
|
|
|
442
510
|
{/* Full Name Row - Editable */}
|
|
443
|
-
<FullNameRow />
|
|
511
|
+
<FullNameRow mobile={mobile} />
|
|
444
512
|
|
|
445
513
|
<Divider style={{ margin: 0 }} />
|
|
446
514
|
|
|
447
515
|
{/* Username Row - Editable */}
|
|
448
|
-
<UsernameRow />
|
|
516
|
+
<UsernameRow mobile={mobile} />
|
|
449
517
|
|
|
450
518
|
<Divider style={{ margin: 0 }} />
|
|
451
519
|
|
|
452
520
|
{/* Password Row - For Better Auth users to change or set password */}
|
|
453
521
|
{isLoginWithBetterAuth && (
|
|
454
522
|
<>
|
|
455
|
-
<PasswordRow />
|
|
523
|
+
<PasswordRow mobile={mobile} />
|
|
456
524
|
<Divider style={{ margin: 0 }} />
|
|
457
525
|
</>
|
|
458
526
|
)}
|
|
@@ -460,7 +528,7 @@ const Client = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
|
460
528
|
{/* Email Row - Read Only */}
|
|
461
529
|
{isLoginWithAuth && userProfile?.email && (
|
|
462
530
|
<>
|
|
463
|
-
<ProfileRow label={t('profile.email')}>
|
|
531
|
+
<ProfileRow label={t('profile.email')} mobile={mobile}>
|
|
464
532
|
<Typography.Text>{userProfile.email}</Typography.Text>
|
|
465
533
|
</ProfileRow>
|
|
466
534
|
<Divider style={{ margin: 0 }} />
|
|
@@ -469,7 +537,7 @@ const Client = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
|
469
537
|
|
|
470
538
|
{/* SSO Providers Row */}
|
|
471
539
|
{isLoginWithAuth && (
|
|
472
|
-
<ProfileRow label={t('profile.sso.providers')}>
|
|
540
|
+
<ProfileRow label={t('profile.sso.providers')} mobile={mobile}>
|
|
473
541
|
<SSOProvidersList />
|
|
474
542
|
</ProfileRow>
|
|
475
543
|
)}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Tag } from '@lobehub/ui';
|
|
2
2
|
import { useTheme } from 'antd-style';
|
|
3
|
-
import Link from 'next/link';
|
|
4
3
|
import { memo } from 'react';
|
|
5
4
|
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { Link } from 'react-router-dom';
|
|
6
6
|
import urlJoin from 'url-join';
|
|
7
7
|
|
|
8
8
|
import { OFFICIAL_URL } from '@/const/url';
|
|
@@ -37,12 +37,12 @@ const PlanTag = memo<PlanTagProps>(({ type = PlanType.Preview }) => {
|
|
|
37
37
|
|
|
38
38
|
return (
|
|
39
39
|
<Link
|
|
40
|
-
|
|
40
|
+
style={{ cursor: 'pointer' }}
|
|
41
|
+
target={isDesktop ? '_blank' : undefined}
|
|
42
|
+
to={urlJoin(
|
|
41
43
|
isDesktop ? OFFICIAL_URL : '/',
|
|
42
44
|
isFree ? '/subscription/plans' : '/subscription/usage',
|
|
43
45
|
)}
|
|
44
|
-
style={{ cursor: 'pointer' }}
|
|
45
|
-
target={isDesktop ? '_blank' : undefined}
|
|
46
46
|
>
|
|
47
47
|
<PlanIcon plan={type} size={22} type={'tag'} />
|
|
48
48
|
</Link>
|
|
@@ -54,6 +54,7 @@ export default {
|
|
|
54
54
|
},
|
|
55
55
|
betterAuth: {
|
|
56
56
|
errors: {
|
|
57
|
+
confirmPasswordRequired: '请确认密码',
|
|
57
58
|
emailExists: '该邮箱已注册,请直接登录',
|
|
58
59
|
emailInvalid: '请输入有效的邮箱地址或用户名',
|
|
59
60
|
emailNotRegistered: '该邮箱或用户名尚未注册',
|
|
@@ -65,6 +66,7 @@ export default {
|
|
|
65
66
|
passwordFormat: '密码必须同时包含字母和数字',
|
|
66
67
|
passwordMaxLength: '密码最多不超过 64 个字符',
|
|
67
68
|
passwordMinLength: '密码至少需要 8 个字符',
|
|
69
|
+
passwordMismatch: '两次输入的密码不一致',
|
|
68
70
|
passwordRequired: '请输入密码',
|
|
69
71
|
usernameNotRegistered: '该用户名尚未注册',
|
|
70
72
|
usernameRequired: '请输入用户名',
|
|
@@ -125,6 +127,7 @@ export default {
|
|
|
125
127
|
submit: '登录',
|
|
126
128
|
},
|
|
127
129
|
signup: {
|
|
130
|
+
confirmPasswordPlaceholder: '请确认密码',
|
|
128
131
|
emailPlaceholder: '请输入邮箱地址',
|
|
129
132
|
error: '注册失败,请重试',
|
|
130
133
|
firstNamePlaceholder: '名字',
|