import React, { useEffect, useCallback, useState, useRef } from "react"; import { useForm } from "react-hook-form"; import { yupResolver } from "@hookform/resolvers/yup"; import * as yup from "yup"; import { VStack, Box, Text, ButtonGroup, Button, FormControl, FormLabel, Input, FormHelperText, FormErrorMessage, InputGroup, InputRightElement, Icon, Flex, HStack, Image, Progress, } from "@chakra-ui/react"; import { RiCheckLine, RiErrorWarningFill, RiCheckboxCircleFill, } from "react-icons/ri"; import { PublicKey } from "@solana/web3.js"; import { useWallet } from "@solana/wallet-adapter-react"; import { randomizeFileName, uploadFiles } from "@strata-foundation/chat"; import { useErrorHandler } from "@strata-foundation/react"; import { ICreateChatModalState } from "./CreateChatModal"; import { useChatSdk } from "../../contexts/chatSdk"; import { useChatStorageAccountKey } from "../../hooks/useChatStorageAccountKey"; import { useLoadDelegate } from "../../hooks/useLoadDelegate"; import { useWalletFromChatIdentifier } from "../../hooks/useWalletFromChatIdentifier"; import { FormControlWithError } from "../form/FormControlWithError"; import { STRATA_KEY } from "../../constants/globals"; interface IBasicInfoProps { state: ICreateChatModalState; setState: React.Dispatch>; onBack: () => void; onNext: () => void; } const validationSchema = yup .object({ name: yup.string().required().max(28), identifier: yup .string() .required() .max(28) .matches( /^[a-zA-Z0-9\_]+$/g, "must be alphanumeric and not have any spaces" ), description: yup.string(), image: yup.mixed(), imageUrl: yup.string(), }) .required(); export const BasicInfo: React.FC = ({ state, setState, onBack, onNext, }) => { const { publicKey: connectedWallet } = useWallet(); const { chatSdk } = useChatSdk(); const { handleErrors } = useErrorHandler(); const { result: chatStorage } = useChatStorageAccountKey(); const hiddenFileInput = useRef(null); const { imageUploaded } = state.wizardData; const [isUploading, setIsUploading] = useState(false); const [imgUrl, setImgUrl] = useState(); const [isValidIdentifier, setIsValidIdentifier] = useState( null ); const { delegateWallet, error: delegateError } = useLoadDelegate(); handleErrors(delegateError); const { register, handleSubmit, watch, formState: { errors }, setValue, setError, clearErrors, } = useForm({ mode: "onChange", //@ts-ignore resolver: yupResolver(validationSchema), defaultValues: { ...state.wizardData, }, }); const { name, identifier, image, imageUrl } = watch(); const { publicKey } = useWallet(); const { wallet: identifierOwner } = useWalletFromChatIdentifier(identifier); const inputBg = { bg: "gray.200", _dark: { bg: "gray.800" } }; const helpTextColor = { color: "black", _dark: { color: "gray.400" } }; const handleImageChange = (e: React.ChangeEvent) => { const file = e.target.files![0]; setValue("image", file || null); clearErrors("image"); }; const verifyIdentifier = useCallback( ( identifier: string, identifierOwner: PublicKey | undefined, connectedWallet: PublicKey | null ) => { if (identifier === "") { setIsValidIdentifier(null); clearErrors("identifier"); } else { if (identifier.length >= 6) { const ownsIdentifier = identifierOwner?.equals(connectedWallet!); const isValid = !identifierOwner || ownsIdentifier; if (isValid) { setIsValidIdentifier(true); } else { setIsValidIdentifier(false); setError("identifier", { message: ".chat domain is already taken!", }); } } } }, [] ); useEffect(() => { verifyIdentifier(identifier, identifierOwner, connectedWallet); }, [identifier, identifierOwner]); const onSubmit = (data: any) => { if (!publicKey?.equals(STRATA_KEY) && data.identifier.length < 6 && !identifierOwner?.equals(connectedWallet!)) { setError("identifier", { message: "Domain must be at least 6 characters.", }); return; } setState({ ...state, wizardData: { ...state.wizardData, ...data, imageUploaded: true, }, }); onNext(); }; useEffect(() => { (async () => { if (image) { const reader = new FileReader(); reader.onload = (event) => { setImgUrl((event.target?.result as string) || ""); }; reader.readAsDataURL(image); if (!imageUrl) { setIsUploading(true); randomizeFileName(image); let innerImageUploaded = false; try { const uri = await uploadFiles( chatSdk!.provider, [image], delegateWallet ); if (uri && uri.length > 0) { setValue("imageUrl", uri[0]); innerImageUploaded = true; setState({ ...state, wizardData: { ...state.wizardData, imageUploaded: true, }, }); } } catch (e) { handleErrors(e as Error); } finally { setIsUploading(false); if (!innerImageUploaded) { setValue("imageUrl", undefined); setValue("image", undefined); setImgUrl(undefined); setError("image", { message: "Image failed to upload, please try again", }); if (hiddenFileInput.current) { hiddenFileInput.current.value = ""; } } } } } else { setImgUrl(undefined); } })(); }, [image]); return (
Let's Start with the Basic info What do you want your chat to be called? .chat Domain {isValidIdentifier && ( )} {!errors.identifier?.message ? ( {isValidIdentifier ? ( .chat domain is available! ) : ( 'The shortlink for the chat, i.e "solana" for solana.chat. You will receive an NFT representing ownership of the chat domain.' )} ) : ( //@ts-ignore {errors.identifier.message} )} Upload Picture {image && ( {image?.name} {image?.name} {imageUploaded && ( )} )} {!errors.image?.message ? ( The image that will appear in the sidebar. ) : ( //@ts-ignore {errors.image.message} )} {isUploading && ( )}
); };