| import { useStore } from '@nanostores/react'; |
| import { useCallback, useEffect, useState } from 'react'; |
| import { toast } from 'react-toastify'; |
| import { |
| chatId as chatIdStore, |
| db, |
| description as descriptionStore, |
| getMessages, |
| updateChatDescription, |
| } 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; |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| } |
|
|
| 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 letters, numbers, spaces, and basic punctuation.'); |
| 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, |
| }; |
| } |
|
|