-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Fix: ElevenLabs API Key Not Persisting #6557
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
54fb99a
f259ebe
2f7f8a5
f27d619
7952b5c
6a28a3d
544d452
e877fe2
0659945
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| import { useState, useEffect, useRef } from 'react'; | ||
| import { useState, useEffect, useCallback } from 'react'; | ||
| import { Input } from '../../ui/input'; | ||
| import { Button } from '../../ui/button'; | ||
| import { useConfig } from '../../ConfigContext'; | ||
| import { ELEVENLABS_API_KEY, isSecretKeyConfigured } from '../../../hooks/dictationConstants'; | ||
| import { setElevenLabsKeyCache } from '../../../hooks/useDictationSettings'; | ||
|
|
@@ -8,71 +9,75 @@ export const ElevenLabsKeyInput = () => { | |
| const [elevenLabsApiKey, setElevenLabsApiKey] = useState(''); | ||
| const [isLoadingKey, setIsLoadingKey] = useState(false); | ||
| const [hasElevenLabsKey, setHasElevenLabsKey] = useState(false); | ||
| const elevenLabsApiKeyRef = useRef(''); | ||
| const { upsert, read } = useConfig(); | ||
| const [validationError, setValidationError] = useState(''); | ||
| const [isEditing, setIsEditing] = useState(false); | ||
| const { upsert, read, remove } = useConfig(); | ||
|
|
||
| useEffect(() => { | ||
| const loadKey = async () => { | ||
| setIsLoadingKey(true); | ||
| try { | ||
| const response = await read(ELEVENLABS_API_KEY, true); | ||
| if (isSecretKeyConfigured(response)) { | ||
| setHasElevenLabsKey(true); | ||
| setElevenLabsKeyCache(true); | ||
| } else { | ||
| setElevenLabsKeyCache(false); | ||
| } | ||
| } catch (error) { | ||
| console.error('Error checking ElevenLabs API key:', error); | ||
| setElevenLabsKeyCache(false); | ||
| } finally { | ||
| setIsLoadingKey(false); | ||
| } | ||
| }; | ||
|
|
||
| loadKey(); | ||
| const loadKey = useCallback(async () => { | ||
| setIsLoadingKey(true); | ||
| try { | ||
| const response = await read(ELEVENLABS_API_KEY, true); | ||
| const hasKey = isSecretKeyConfigured(response); | ||
| setHasElevenLabsKey(hasKey); | ||
| setElevenLabsKeyCache(hasKey); | ||
| } catch (error) { | ||
| console.error(error); | ||
| setElevenLabsKeyCache(false); | ||
| } finally { | ||
| setIsLoadingKey(false); | ||
| } | ||
| }, [read]); | ||
|
|
||
| // Save key on unmount to avoid losing unsaved changes | ||
| useEffect(() => { | ||
| return () => { | ||
| if (elevenLabsApiKeyRef.current) { | ||
| const keyToSave = elevenLabsApiKeyRef.current; | ||
| if (keyToSave.trim()) { | ||
| upsert(ELEVENLABS_API_KEY, keyToSave, true) | ||
| .then(() => setElevenLabsKeyCache(true)) | ||
| .catch((error) => { | ||
| console.error('Error saving ElevenLabs API key on unmount:', error); | ||
| }); | ||
| } | ||
| } | ||
| }; | ||
| }, [upsert]); | ||
| loadKey(); | ||
| }, [loadKey]); | ||
|
|
||
| const handleElevenLabsKeyChange = (key: string) => { | ||
| setElevenLabsApiKey(key); | ||
| elevenLabsApiKeyRef.current = key; | ||
| if (key.length > 0) { | ||
| setHasElevenLabsKey(false); | ||
| if (validationError) { | ||
| setValidationError(''); | ||
| } | ||
| }; | ||
|
|
||
| const saveElevenLabsKey = async () => { | ||
| const handleSave = async () => { | ||
| try { | ||
| if (elevenLabsApiKey.trim()) { | ||
| await upsert(ELEVENLABS_API_KEY, elevenLabsApiKey, true); | ||
| setHasElevenLabsKey(true); | ||
| setElevenLabsKeyCache(true); | ||
| } else { | ||
| await upsert(ELEVENLABS_API_KEY, null, true); | ||
| setHasElevenLabsKey(false); | ||
| setElevenLabsKeyCache(false); | ||
| const trimmedKey = elevenLabsApiKey.trim(); | ||
|
|
||
| if (!trimmedKey) { | ||
| setValidationError('API key is required'); | ||
| return; | ||
| } | ||
|
|
||
| await upsert(ELEVENLABS_API_KEY, trimmedKey, true); | ||
| setElevenLabsApiKey(''); | ||
| setValidationError(''); | ||
| setIsEditing(false); | ||
| await loadKey(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @Abhijay007 just curious why we hav to loadKey again in the upsert and remove? It added an extra call to the server side and it seems unnecessary? Shall we just update the cache and hasElevenLabsKey instead of the additional call?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it was mentioned here by copoilt : #6557 (comment) so I thought of implementing it |
||
| } catch (error) { | ||
| console.error('Error saving ElevenLabs API key:', error); | ||
| console.error(error); | ||
| setValidationError('Failed to save API key'); | ||
| } | ||
| }; | ||
Abhijay007 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| const handleRemove = async () => { | ||
| try { | ||
| await remove(ELEVENLABS_API_KEY, true); | ||
| await loadKey(); | ||
| setElevenLabsApiKey(''); | ||
| setValidationError(''); | ||
| setIsEditing(false); | ||
| } catch (error) { | ||
| console.error(error); | ||
| setValidationError('Failed to remove API key'); | ||
| } | ||
| }; | ||
|
|
||
| const handleCancel = () => { | ||
| setElevenLabsApiKey(''); | ||
| setValidationError(''); | ||
| setIsEditing(false); | ||
| }; | ||
|
|
||
| return ( | ||
| <div className="py-2 px-2 bg-background-subtle rounded-lg"> | ||
| <div className="mb-2"> | ||
|
|
@@ -82,17 +87,42 @@ export const ElevenLabsKeyInput = () => { | |
| {hasElevenLabsKey && <span className="text-green-600 ml-2">(Configured)</span>} | ||
| </p> | ||
| </div> | ||
| <Input | ||
| type="password" | ||
| value={elevenLabsApiKey} | ||
| onChange={(e) => handleElevenLabsKeyChange(e.target.value)} | ||
| onBlur={saveElevenLabsKey} | ||
| placeholder={ | ||
| hasElevenLabsKey ? 'Enter new API key to update' : 'Enter your ElevenLabs API key' | ||
| } | ||
| className="max-w-md" | ||
| disabled={isLoadingKey} | ||
| /> | ||
|
|
||
| {!isEditing ? ( | ||
| <Button | ||
| variant="outline" | ||
| size="sm" | ||
| onClick={() => setIsEditing(true)} | ||
| disabled={isLoadingKey} | ||
| > | ||
| {hasElevenLabsKey ? 'Update API Key' : 'Add API Key'} | ||
| </Button> | ||
| ) : ( | ||
| <div className="space-y-2"> | ||
| <Input | ||
| type="password" | ||
| value={elevenLabsApiKey} | ||
| onChange={(e) => handleElevenLabsKeyChange(e.target.value)} | ||
| placeholder="Enter your ElevenLabs API key" | ||
| className="max-w-md" | ||
| autoFocus | ||
| /> | ||
| {validationError && <p className="text-xs text-red-600 mt-1">{validationError}</p>} | ||
| <div className="flex gap-2"> | ||
| <Button size="sm" onClick={handleSave}> | ||
| Save | ||
| </Button> | ||
| <Button variant="outline" size="sm" onClick={handleCancel}> | ||
| Cancel | ||
| </Button> | ||
| {hasElevenLabsKey && ( | ||
| <Button variant="destructive" size="sm" onClick={handleRemove}> | ||
| Remove | ||
| </Button> | ||
| )} | ||
| </div> | ||
| </div> | ||
| )} | ||
| </div> | ||
| ); | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
frankly, this whole module feels rather complicated. aren't we just setting an API key? there's validation code, but all it seems to do is check that the API key is set. or that the server failed but that's not very useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lifeizhou-ap , @DOsinga should I consider this update in this PR or make another one for this refactor ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @Abhijay007,
Let's make another PR for this refactor.