Skip to content

Commit 6f3d4b8

Browse files
authored
[Feat] Added API key configuration checks and a reminder dialog,Fix the styling of the auth menu. (#24)
1.Added API key configuration checks and a reminder dialog. 2.Fix the styling of the auth menu
1 parent 457f819 commit 6f3d4b8

File tree

6 files changed

+221
-44
lines changed

6 files changed

+221
-44
lines changed

app/components/common/Dialog.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,23 +234,25 @@ export function ConfirmDialog({
234234
isOpen,
235235
onClose,
236236
onConfirm,
237-
title = '确认',
237+
title,
238238
message,
239239
confirmText = '确认',
240240
cancelText = '取消',
241241
type = 'default', // default, danger, warning
242242
showCancelButton = true,
243+
showCloseButton = true,
243244
onCancel,
244245
}: {
245246
isOpen: boolean
246247
onClose: () => void
247248
onConfirm: () => void
248249
title?: string
249-
message: string
250+
message: string | React.ReactNode
250251
confirmText?: string | React.ReactNode
251252
cancelText?: string | React.ReactNode
252253
type?: 'default' | 'danger' | 'warning'
253254
showCancelButton?: boolean
255+
showCloseButton?: boolean
254256
onCancel?: () => void
255257
}) {
256258
const getButtonStyle = (isConfirm: boolean) => {
@@ -308,6 +310,7 @@ export function ConfirmDialog({
308310
title={title}
309311
maxWidth="400px"
310312
className="confirm-dialog"
313+
showCloseButton={showCloseButton}
311314
>
312315
<div style={{ marginBottom: '24px' }}>
313316
<p style={{ margin: 0, color: '#ccc', lineHeight: '1.5' }}>{message}</p>

app/components/layout/Navigation.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
} from '@/hooks/useGlobalNotification'
2828
import { useTranslation } from 'react-i18next'
2929
import LanguageSwitcher from './LanguageSwitch'
30+
import './Navigation.scss'
3031
/*
3132
Top navigation component for the application.
3233
@@ -240,10 +241,22 @@ export default function Navigation() {
240241
</button>
241242

242243
{isAuthenticated && showUserMenu && (
243-
<div className="user-menu">
244+
<div
245+
className="user-menu"
246+
style={{ position: 'absolute', top: '100%', right: '10px' }}
247+
>
244248
<div className="user-menu-item">
245249
<span>{t('nav.signedInAs')}</span>
246-
<strong>{userInfo.username}</strong>
250+
<strong
251+
style={{
252+
maxWidth: '200px',
253+
overflow: 'hidden',
254+
textOverflow: 'ellipsis',
255+
whiteSpace: 'nowrap',
256+
}}
257+
>
258+
{userInfo.username}
259+
</strong>
247260
</div>
248261
<button
249262
className="user-menu-item user-menu-action"

app/i18n/locales/en/fronted.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@
121121
"message": "We've detected that you're connecting from a location far from our servers, which may result in higher network latency and a suboptimal user experience. For the best performance and experience, we recommend accessing our open-source project and deploying it locally.",
122122
"confirmText": "Continue"
123123
},
124+
"missingSecret": {
125+
"message_llm": "Missing required API keys for large language model features. Please configure the following credentials:",
126+
"message_tts": "Missing required API keys for text to speech features. Please configure the following credentials:",
127+
"message_asr": "Missing required API keys for auto speech recognition features. Please configure the following credentials:",
128+
"confirmText": "Go to Settings"
129+
},
124130
"notification": {
125131
"editDefaultCharacterNotAllowed": "Editing the default character is not allowed. You must create a copy to make any changes.",
126132
"updateDefaultCharacterNameNotAllowed": "Cannot update character name for the default character",

app/i18n/locales/zh/fronted.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@
121121
"message": "检测到你的地理位置距离在线服务很远,这可能导致更高的网络延迟并影响用户体验。为获得最佳性能和体验,我们建议访问我们的开源项目并部署在本地。",
122122
"confirmText": "仍要访问"
123123
},
124+
"missingSecret": {
125+
"message_llm": "缺少大语言模型功能所必须的API密钥,请配置以下凭证:",
126+
"message_tts": "缺少文本转语音功能所必须的API密钥,请配置以下凭证:",
127+
"message_asr": "缺少自动语音识别功能所必须的API密钥,请配置以下凭证:",
128+
"confirmText": "前往配置"
129+
},
124130
"notification": {
125131
"editDefaultCharacterNotAllowed": "不允许编辑默认角色。请在对话列表创建一个角色副本以修改配置。",
126132
"updateDefaultCharacterNameNotAllowed": "不允许更新默认角色的名称",

app/page.tsx

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,13 @@ import {
3535
getIsLogin,
3636
loadAuthStateFromStorage,
3737
setAuthState,
38+
getUserInfo,
3839
} from '@/features/auth/authStore'
3940
import { usePromptingSettings } from '@/hooks/usePromptingSettings'
4041
import { checkLocation, isSensetimeOrchestrator } from '@/utils/location'
4142
import { ConfirmDialog } from './components/common/Dialog'
4243
import { useTranslation } from 'react-i18next'
44+
import { fetchGetMissingSecret } from '@/request/api'
4345

4446
const link = {
4547
href: 'https://github.com/dlp3d-ai/dlp3d.ai',
@@ -62,6 +64,7 @@ export default function Home() {
6264

6365
const isChatStarting = useSelector(getIsChatStarting)
6466
const isLogin = useSelector(getIsLogin)
67+
const user = useSelector(getUserInfo)
6568
const selectedModelIndex = useSelector(getSelectedModelIndex)
6669
const selectedCharacterId = useSelector(getSelectedCharacterId)
6770
const isCharacterLoading = useSelector(getIsCharacterLoading)
@@ -86,6 +89,16 @@ export default function Home() {
8689
const { loadUserCharacters } = usePromptingSettings()
8790
const [selectedScene, setSelectedScene] = useState(3) // Parameter for scene navigation
8891
const [locationDialogOpen, setLocationDialogOpen] = useState(false)
92+
const [missingSecretDialogOpen, setMissingSecretDialogOpen] = useState(false)
93+
const [missingSecret, setMissingSecret] = useState<{
94+
llm_requirements: string[]
95+
tts_requirements: string[]
96+
asr_requirements: string[]
97+
}>({
98+
llm_requirements: [],
99+
tts_requirements: [],
100+
asr_requirements: [],
101+
})
89102

90103
useEffect(() => {
91104
setIsGlobalLoading(isLoading || isSceneLoading || isCharacterLoading)
@@ -143,6 +156,8 @@ export default function Home() {
143156
*
144157
* Dispatches actions to update loading state and triggers a custom event
145158
* after a delay to ensure all components are initialized.
159+
*
160+
* @returns void
146161
*/
147162
const handleCharacterLoaded = useCallback(() => {
148163
dispatch(setIsCharacterLoading(false))
@@ -159,6 +174,8 @@ export default function Home() {
159174
* Handle scene change.
160175
*
161176
* @param scene The name of the scene to switch to.
177+
*
178+
* @returns void
162179
*/
163180
const handleSceneChange = (scene: string) => {
164181
dispatch(setIsSceneLoading(true))
@@ -173,6 +190,8 @@ export default function Home() {
173190
* Handle scene loaded event.
174191
*
175192
* Dispatches actions to update loading state when the scene has finished loading.
193+
*
194+
* @returns void
176195
*/
177196
const handleSceneLoaded = useCallback(() => {
178197
dispatch(setIsSceneLoading(false))
@@ -185,6 +204,8 @@ export default function Home() {
185204
* Validates prerequisites, saves camera and scene state, captures a screenshot,
186205
* and opens a new window with the chat interface. Handles TTS compatibility checks
187206
* and location-based warnings for specific server hosts.
207+
*
208+
* @returns Promise<void> Resolves after the new window is opened or a notice is shown.
188209
*/
189210
const handleStartConversation = useCallback(async () => {
190211
if (isCharacterLoading || isSceneLoading) {
@@ -231,6 +252,7 @@ export default function Home() {
231252
setLocationDialogOpen(true)
232253
return
233254
}
255+
234256
try {
235257
dispatch(setIsChatStarting(true))
236258
if (typeof window !== 'undefined') {
@@ -242,6 +264,24 @@ export default function Home() {
242264
await loadUserCharacters()
243265
}
244266
await new Promise(r => setTimeout(r, 800))
267+
const missingSecret = await fetchGetMissingSecret(
268+
user!.id,
269+
selectedCharacterId!,
270+
)
271+
if (
272+
missingSecret.llm_requirements.length > 0 ||
273+
missingSecret.tts_requirements.length > 0 ||
274+
missingSecret.asr_requirements.length > 0
275+
) {
276+
const data = {
277+
llm_requirements: missingSecret.llm_requirements.sort(),
278+
tts_requirements: missingSecret.tts_requirements.sort(),
279+
asr_requirements: missingSecret.asr_requirements.sort(),
280+
}
281+
setMissingSecret(data)
282+
setMissingSecretDialogOpen(true)
283+
return
284+
}
245285

246286
if (babylonViewerRef.current?.takeScreenshot) {
247287
const screenshotData = await captureScreenshot()
@@ -312,6 +352,14 @@ export default function Home() {
312352
window.removeEventListener('popstate', handleRouteChange)
313353
}
314354
}, [dispatch])
355+
/**
356+
* Chat action button.
357+
*
358+
* Renders the primary CTA to start a conversation, disabled when prerequisites
359+
* are not met.
360+
*
361+
* @returns JSX.Element | null The button or null when hidden on mobile during startup.
362+
*/
315363
const ChatButton = useCallback(() => {
316364
if (isChatStarting || (isMobile && isLogin)) return null
317365
return (
@@ -347,6 +395,77 @@ export default function Home() {
347395
isCharacterLoading,
348396
isSceneLoading,
349397
])
398+
/**
399+
* Missing secret requirements renderer.
400+
*
401+
* Displays lists of required secrets for LLM/TTS/ASR providers if any are missing.
402+
*
403+
* @returns JSX.Element The message content for the dialog.
404+
*/
405+
const MissingSecretMessage = () => {
406+
return (
407+
<>
408+
{missingSecret.llm_requirements.length > 0 && (
409+
<div>
410+
{t('missingSecret.message_llm')}
411+
<div
412+
style={{
413+
padding: '20px 10px 20px 30px',
414+
display: 'flex',
415+
flexDirection: 'column',
416+
gap: '5px',
417+
}}
418+
>
419+
{missingSecret.llm_requirements.map(requirement => (
420+
<div key={requirement}>
421+
<span style={{ fontWeight: 'bold' }}></span> {requirement}
422+
</div>
423+
))}
424+
</div>
425+
</div>
426+
)}
427+
{missingSecret.tts_requirements.length > 0 && (
428+
<div>
429+
{t('missingSecret.message_tts')}
430+
<div
431+
style={{
432+
padding: '20px 10px 20px 30px',
433+
display: 'flex',
434+
flexDirection: 'column',
435+
gap: '5px',
436+
}}
437+
>
438+
{missingSecret.tts_requirements.map(requirement => (
439+
<div key={requirement}>
440+
<span style={{ fontWeight: 'bold' }}></span> {requirement}
441+
</div>
442+
))}
443+
</div>
444+
</div>
445+
)}
446+
447+
{missingSecret.asr_requirements.length > 0 && (
448+
<div>
449+
{t('missingSecret.message_asr')}
450+
<div
451+
style={{
452+
padding: '20px 10px 20px 30px',
453+
display: 'flex',
454+
flexDirection: 'column',
455+
gap: '5px',
456+
}}
457+
>
458+
{missingSecret.asr_requirements.map(requirement => (
459+
<div key={requirement}>
460+
<span style={{ fontWeight: 'bold' }}></span> {requirement}
461+
</div>
462+
))}
463+
</div>
464+
</div>
465+
)}
466+
</>
467+
)
468+
}
350469

351470
return (
352471
<>
@@ -458,6 +577,20 @@ export default function Home() {
458577
message={t('networkLatencyWarning.message')}
459578
confirmText={t('networkLatencyWarning.confirmText')}
460579
/>
580+
{/* Missing Secret Dialog */}
581+
<ConfirmDialog
582+
isOpen={missingSecretDialogOpen}
583+
onClose={() => {
584+
setMissingSecretDialogOpen(false)
585+
}}
586+
showCloseButton={false}
587+
showCancelButton={false}
588+
message={<MissingSecretMessage />}
589+
confirmText={t('missingSecret.confirmText')}
590+
onConfirm={() => {
591+
setMissingSecretDialogOpen(false)
592+
}}
593+
/>
461594
{/* Footer */}
462595
{!isChatStarting && !isLogin && (
463596
<footer className={uiFadeOut ? 'fade-out' : ''}>

0 commit comments

Comments
 (0)