forked from stackblitz/bolt.new
-
Notifications
You must be signed in to change notification settings - Fork 4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #483 from PuneetP16/feat/enhance-chat-description-…
…management [feat]: Implement chat description editing in sidebar and header, add…
- Loading branch information
Showing
7 changed files
with
364 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
import { useStore } from '@nanostores/react'; | ||
import { useCallback, useEffect, useState } from 'react'; | ||
import { toast } from 'react-toastify'; | ||
import { | ||
chatId as chatIdStore, | ||
description as descriptionStore, | ||
db, | ||
updateChatDescription, | ||
getMessages, | ||
} from '~/lib/persistence'; | ||
|
||
interface EditChatDescriptionOptions { | ||
initialDescription?: string; | ||
customChatId?: string; | ||
syncWithGlobalStore?: boolean; | ||
} | ||
|
||
type EditChatDescriptionHook = { | ||
editing: boolean; | ||
handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void; | ||
handleBlur: () => Promise<void>; | ||
handleSubmit: (event: React.FormEvent) => Promise<void>; | ||
handleKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => Promise<void>; | ||
currentDescription: string; | ||
toggleEditMode: () => void; | ||
}; | ||
|
||
/** | ||
* Hook to manage the state and behavior for editing chat descriptions. | ||
* | ||
* Offers functions to: | ||
* - Switch between edit and view modes. | ||
* - Manage input changes, blur, and form submission events. | ||
* - Save updates to IndexedDB and optionally to the global application state. | ||
* | ||
* @param {Object} options | ||
* @param {string} options.initialDescription - The current chat description. | ||
* @param {string} options.customChatId - Optional ID for updating the description via the sidebar. | ||
* @param {boolean} options.syncWithGlobalStore - Flag to indicate global description store synchronization. | ||
* @returns {EditChatDescriptionHook} Methods and state for managing description edits. | ||
*/ | ||
export function useEditChatDescription({ | ||
initialDescription = descriptionStore.get()!, | ||
customChatId, | ||
syncWithGlobalStore, | ||
}: EditChatDescriptionOptions): EditChatDescriptionHook { | ||
const chatIdFromStore = useStore(chatIdStore); | ||
const [editing, setEditing] = useState(false); | ||
const [currentDescription, setCurrentDescription] = useState(initialDescription); | ||
|
||
const [chatId, setChatId] = useState<string>(); | ||
|
||
useEffect(() => { | ||
setChatId(customChatId || chatIdFromStore); | ||
}, [customChatId, chatIdFromStore]); | ||
useEffect(() => { | ||
setCurrentDescription(initialDescription); | ||
}, [initialDescription]); | ||
|
||
const toggleEditMode = useCallback(() => setEditing((prev) => !prev), []); | ||
|
||
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => { | ||
setCurrentDescription(e.target.value); | ||
}, []); | ||
|
||
const fetchLatestDescription = useCallback(async () => { | ||
if (!db || !chatId) { | ||
return initialDescription; | ||
} | ||
|
||
try { | ||
const chat = await getMessages(db, chatId); | ||
return chat?.description || initialDescription; | ||
} catch (error) { | ||
console.error('Failed to fetch latest description:', error); | ||
return initialDescription; | ||
} | ||
}, [db, chatId, initialDescription]); | ||
|
||
const handleBlur = useCallback(async () => { | ||
const latestDescription = await fetchLatestDescription(); | ||
setCurrentDescription(latestDescription); | ||
toggleEditMode(); | ||
}, [fetchLatestDescription, toggleEditMode]); | ||
|
||
const isValidDescription = useCallback((desc: string): boolean => { | ||
const trimmedDesc = desc.trim(); | ||
|
||
if (trimmedDesc === initialDescription) { | ||
toggleEditMode(); | ||
return false; // No change, skip validation | ||
} | ||
|
||
const lengthValid = trimmedDesc.length > 0 && trimmedDesc.length <= 100; | ||
const characterValid = /^[a-zA-Z0-9\s]+$/.test(trimmedDesc); | ||
|
||
if (!lengthValid) { | ||
toast.error('Description must be between 1 and 100 characters.'); | ||
return false; | ||
} | ||
|
||
if (!characterValid) { | ||
toast.error('Description can only contain alphanumeric characters and spaces.'); | ||
return false; | ||
} | ||
|
||
return true; | ||
}, []); | ||
|
||
const handleSubmit = useCallback( | ||
async (event: React.FormEvent) => { | ||
event.preventDefault(); | ||
|
||
if (!isValidDescription(currentDescription)) { | ||
return; | ||
} | ||
|
||
try { | ||
if (!db) { | ||
toast.error('Chat persistence is not available'); | ||
return; | ||
} | ||
|
||
if (!chatId) { | ||
toast.error('Chat Id is not available'); | ||
return; | ||
} | ||
|
||
await updateChatDescription(db, chatId, currentDescription); | ||
|
||
if (syncWithGlobalStore) { | ||
descriptionStore.set(currentDescription); | ||
} | ||
|
||
toast.success('Chat description updated successfully'); | ||
} catch (error) { | ||
toast.error('Failed to update chat description: ' + (error as Error).message); | ||
} | ||
|
||
toggleEditMode(); | ||
}, | ||
[currentDescription, db, chatId, initialDescription, customChatId], | ||
); | ||
|
||
const handleKeyDown = useCallback( | ||
async (e: React.KeyboardEvent<HTMLInputElement>) => { | ||
if (e.key === 'Escape') { | ||
await handleBlur(); | ||
} | ||
}, | ||
[handleBlur], | ||
); | ||
|
||
return { | ||
editing, | ||
handleChange, | ||
handleBlur, | ||
handleSubmit, | ||
handleKeyDown, | ||
currentDescription, | ||
toggleEditMode, | ||
}; | ||
} |
Oops, something went wrong.