diff --git a/src/components/ContactUsModal/index.tsx b/src/components/ContactUsModal/index.tsx index 68bfcc929..f90ce8e9c 100644 --- a/src/components/ContactUsModal/index.tsx +++ b/src/components/ContactUsModal/index.tsx @@ -1,7 +1,7 @@ import { Flex, Text, Input, Textarea } from '@chakra-ui/react'; import BaseModal from '../BaseModal'; import s from './styles2.module.scss'; -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { isEmpty } from 'lodash'; import { submitContact } from '@/services/api/l2services'; @@ -12,6 +12,7 @@ import { Select } from '@chakra-ui/react'; import { useL2ServiceTracking } from '@/hooks/useL2ServiceTracking'; import { DATA_BRAND } from '@/modules/landingV3/data-sections'; import Image from 'next/image'; +import { throttle } from 'lodash'; const SUBJECT_LIST = [ `I'd like to build a Rollup on Bitcoin`, @@ -106,8 +107,17 @@ const ContactUsModal = ({ } }; - const submitHandler = async () => { + const submitHandler = async ( + methodInputStr: any, + methodContact: METHODS_CONTACT_ENUM, + ) => { + // console.log('submitHandler Params ', { + // methodInputStr, + // methodContact, + // }); + tracking('SUBMIT_CONTACT_US'); + try { let valid = true; // if (!valideYourXAcc(yourXAcc)) { @@ -120,12 +130,12 @@ const ContactUsModal = ({ // valid = false; // } - if (!validateMethodContact(methodInput)) { + if (!validateMethodContact(methodInputStr)) { valid = false; } - // console.log('valid ', valid); - // console.log('methodContact ', methodContact); + // console.log('LOG valid ', valid); + // console.log('LOG methodContact ', methodContact); if (valid) { let submitParams: SubmitFormParams = { @@ -144,21 +154,21 @@ const ContactUsModal = ({ if (methodContact === METHODS_CONTACT_ENUM.Email) { submitParams = { ...submitParams, - email: methodInput, + email: methodInputStr, }; } if (methodContact === METHODS_CONTACT_ENUM.Telegram) { submitParams = { ...submitParams, - telegram: methodInput, + telegram: methodInputStr, }; } if (methodContact === METHODS_CONTACT_ENUM.Twitter) { submitParams = { ...submitParams, - twName: methodInput, + twName: methodInputStr, }; } @@ -176,6 +186,14 @@ const ContactUsModal = ({ } }; + const submitHanlderDebouce = useCallback( + throttle(submitHandler, 500, { + trailing: true, + leading: false, + }), + [], + ); + const renderXfield = () => { return ( */} {/**/} -
+
submitHanlderDebouce(methodInput, methodContact)} + >

Submit

diff --git a/src/components/MagicIcon/index.tsx b/src/components/MagicIcon/index.tsx new file mode 100644 index 000000000..57a8adf5a --- /dev/null +++ b/src/components/MagicIcon/index.tsx @@ -0,0 +1,43 @@ +import { ReactElement } from 'react'; + +export default function MagicIcon({ + color, +}: { + color: 'white' | 'black'; +}): ReactElement { + return ( +
+ + + + + + + + + + + + + +
+ ); +} diff --git a/src/constants/constants.ts b/src/constants/constants.ts index 76e737c89..38564e6f4 100644 --- a/src/constants/constants.ts +++ b/src/constants/constants.ts @@ -8,13 +8,9 @@ const MULTIPLE_POINT_SYMBOL = 'SHARD'; const BVM_TOKEN_SYMBOL = '$BVM'; export { - MIN_DECIMAL, - MAX_DECIMAL, - NATIVE_ETH_ADDRESS, - METAMASK_DOWNLOAD_PAGE, - MULTIPLE_POINT_SYMBOL, - BVM_TOKEN_SYMBOL, + BVM_TOKEN_SYMBOL, MAX_DECIMAL, METAMASK_DOWNLOAD_PAGE, MIN_DECIMAL, MULTIPLE_POINT_SYMBOL, NATIVE_ETH_ADDRESS }; + export const ALLOWED_ATTRIBUTES = { '*': ['style'], diff --git a/src/modules/blockchains/Buy/component4/Lego/index.tsx b/src/modules/blockchains/Buy/component4/Lego/index.tsx index 88545fb23..18ed4e923 100644 --- a/src/modules/blockchains/Buy/component4/Lego/index.tsx +++ b/src/modules/blockchains/Buy/component4/Lego/index.tsx @@ -1,16 +1,15 @@ -import React from 'react'; import cn from 'classnames'; +import React from 'react'; import SvgInset from '@/components/SvgInset'; import { adjustBrightness } from '../../utils'; -import styles from './styles.module.scss'; -import { Box, Flex, Image, Tooltip } from '@chakra-ui/react'; -import { FieldModel } from '@/types/customize-model'; -import { legoDragging } from '@/modules/blockchains/dapp/ui-helper/LegoDragging'; -import { useCaptureStore } from '@/modules/blockchains/Buy/stores/index_v3'; import { iconToolNames } from '@/modules/blockchains/Buy/Buy.data'; +import { useCaptureStore } from '@/modules/blockchains/Buy/stores/index_v3'; +import { FieldModel } from '@/types/customize-model'; +import { Flex, Image, Tooltip } from '@chakra-ui/react'; +import styles from './styles.module.scss'; type Position = | { @@ -50,6 +49,7 @@ type Props = { children?: React.ReactNode; preview?: boolean; checked?: boolean; + legoAI?: boolean; fields?: FieldModel[]; infoLego?: { title: string; @@ -64,6 +64,7 @@ const Lego = (props: Props) => { background = '#c4513a', icon, title, + legoAI, tooltip, titleInLeft = false, titleInRight = false, @@ -117,7 +118,13 @@ const Lego = (props: Props) => { }} ref={legoRef} > -
+
diff --git a/src/modules/blockchains/Buy/component4/Lego/styles.module.scss b/src/modules/blockchains/Buy/component4/Lego/styles.module.scss index 7f18d35d0..0c4e28e97 100644 --- a/src/modules/blockchains/Buy/component4/Lego/styles.module.scss +++ b/src/modules/blockchains/Buy/component4/Lego/styles.module.scss @@ -28,7 +28,7 @@ left: 11px; &__top { - top: -1px; + top: -1.5px; svg path { fill: #ffffff !important; } @@ -106,7 +106,7 @@ .titleSingle { width: max-content; - padding-top: .15em; + padding-top: 0.15em; display: block; // font-size: 14px; // font-weight: 500; @@ -122,3 +122,13 @@ height: 100%; } } + +.lego__piece__top__ai { + top: -1px; + svg path { + fill: #ffeee8 !important; + } + &::after { + background-color: #ffeee8; + } +} diff --git a/src/modules/blockchains/Buy/components3/ComputerNameInput/index_v2.tsx b/src/modules/blockchains/Buy/components3/ComputerNameInput/index_v2.tsx index ad1866388..880c5b19b 100644 --- a/src/modules/blockchains/Buy/components3/ComputerNameInput/index_v2.tsx +++ b/src/modules/blockchains/Buy/components3/ComputerNameInput/index_v2.tsx @@ -19,6 +19,7 @@ const ComputerNameInput = () => { useChainProvider(); const { computerName, + isComputerNameFocused, setComputerName, setComputerNameFocused, setComputerNameErrMsg, @@ -56,15 +57,19 @@ const ComputerNameInput = () => { }; useEffect(() => { - let computerName; + let computerNameStr; if (isCreateChainFlow) { - computerName = `${PREFIX} ${chainID}`; + if (isComputerNameFocused) { + computerNameStr = `${computerName}`; + } else { + computerNameStr = `${PREFIX} ${chainID}`; + } } else { - computerName = order?.chainName || ''; + computerNameStr = order?.chainName || ''; } - setChainName(computerName); - setComputerName(computerName); - }, [isCreateChainFlow, order, chainID]); + setChainName(computerNameStr); + setComputerName(computerNameStr); + }, [isCreateChainFlow, order, chainID, isComputerNameFocused, computerName]); return (
@@ -77,6 +82,7 @@ const ComputerNameInput = () => { onChange={(e: any) => { const text = e.target.value; onChangeHandler(text); + setComputerNameFocused(true); }} onBlur={(e: any) => { const text = e.target.value; diff --git a/src/modules/blockchains/Buy/components3/NetworkDropdown/index.tsx b/src/modules/blockchains/Buy/components3/NetworkDropdown/index.tsx index e61d730b7..e37e42c88 100644 --- a/src/modules/blockchains/Buy/components3/NetworkDropdown/index.tsx +++ b/src/modules/blockchains/Buy/components3/NetworkDropdown/index.tsx @@ -78,6 +78,12 @@ const NetworkDropdown = ({}: Props) => { [currentValue?.icon], ); + React.useEffect(() => { + if (field['network']?.value === null) { + setField('network', options[0].key, true); + } + }, [field['network']?.value]); + return (
{ const { field } = useOrderFormStoreV3(); const { dappCount } = useFormDappToFormChain(); + console.log('[useFormChain] field', field); + const getDynamicForm = () => { - if (!categories) + if (!categories) { return { dynamicForm: [], allOptionKeyDragged: [], allRequiredForKey: [], optionMapping: {}, }; + } const ignoreKeys = ['bridge_apps', 'wallet', 'gaming_apps']; const dynamicForm: IModelCategory[] = []; @@ -97,6 +100,8 @@ const useFormChain = () => { field.options = uniqBy(field.options, 'key'); }); + console.log('[useFormChain] getDynamicForm', dynamicForm); + return { dynamicForm, allOptionKeyDragged, diff --git a/src/modules/blockchains/Buy/hooks/useNodeFlowControl.ts b/src/modules/blockchains/Buy/hooks/useNodeFlowControl.ts index fa3b4914d..c4485bbf5 100644 --- a/src/modules/blockchains/Buy/hooks/useNodeFlowControl.ts +++ b/src/modules/blockchains/Buy/hooks/useNodeFlowControl.ts @@ -31,6 +31,7 @@ import useModelCategoriesStore from '../stores/useModelCategoriesStore'; import handleStatusEdges from '@utils/helpers'; import { useAAModule } from '@/modules/blockchains/detail_v4/hook/useAAModule'; import { useBridgesModule } from '@/modules/blockchains/detail_v4/hook/useBridgesModule'; +import { IModelOption } from '@/types/customize-model'; export default function useNodeFlowControl() { const { dapps } = useDapps(); @@ -96,7 +97,7 @@ export default function useNodeFlowControl() { }; useSignalEffect(() => { - console.log('[useNodeFlowControl]', {nodes}); + console.log('[useNodeFlowControl]', { nodes }); needReactFlowRenderSignal.value = true; @@ -128,7 +129,7 @@ export default function useNodeFlowControl() { title: thisDapp.title, dapp: thisDapp, baseIndex: draggedIds2D.length - 1, - categoryOption, + categoryOption: categoryOption as IModelOption, ids: draggedIds2D[draggedIds2D.length - 1], targetHandles: [`account_abstraction-t-${rootNode}`], sourceHandles: [], @@ -189,7 +190,7 @@ export default function useNodeFlowControl() { title: thisDapp.title, dapp: thisDapp, baseIndex: 0, - categoryOption: {}, + categoryOption: {} as IModelOption, ids: [], targetHandles: [`bridge_apps-t-${rootNode}`], sourceHandles: [], @@ -248,7 +249,7 @@ export default function useNodeFlowControl() { title: thisDapp.title, dapp: thisDapp, baseIndex: 0, - categoryOption: {}, + categoryOption: {} as IModelOption, ids: [], targetHandles: [`gaming_apps-t-${rootNode}`], sourceHandles: [], @@ -417,7 +418,7 @@ export default function useNodeFlowControl() { title: thisDapp.title, dapp: thisDapp, baseIndex: draggedIds2D.length - 1, - categoryOption, + categoryOption: categoryOption as IModelOption, ids: draggedIds2D[draggedIds2D.length - 1], targetHandles: [`${newNodeId}-t-${rootNode}`], sourceHandles: [], diff --git a/src/modules/blockchains/Buy/hooks/useSetDefaultDapp.ts b/src/modules/blockchains/Buy/hooks/useSetDefaultDapp.ts index f37fe0629..9f88af24c 100644 --- a/src/modules/blockchains/Buy/hooks/useSetDefaultDapp.ts +++ b/src/modules/blockchains/Buy/hooks/useSetDefaultDapp.ts @@ -16,6 +16,7 @@ import useModelCategoriesStore from '../stores/useModelCategoriesStore'; import { needReactFlowRenderSignal } from '../studio/ReactFlowRender'; import { dappKeyToChainKey } from '../utils'; import useDapps from './useDapps'; +import { IModelOption } from '@/types/customize-model'; const useSetDefaultDapp = () => { const searchParams = useSearchParams(); @@ -26,7 +27,7 @@ const useSetDefaultDapp = () => { const { draggedIds2D, setDraggedIds2D } = useDraggedId2DStore(); - const [loaded, setLoaded] = useState(false) + const [loaded, setLoaded] = useState(false); const updateBaseDapp = (dappIndex: number) => { const thisDapp = dapps[dappIndex]; @@ -96,7 +97,7 @@ const useSetDefaultDapp = () => { title: thisDapp.title, dapp: thisDapp, baseIndex: 0, - categoryOption, + categoryOption: categoryOption as IModelOption, ids, targetHandles: [`${newNodeId}-t-${rootNode}`], sourceHandles: [], @@ -168,8 +169,8 @@ const useSetDefaultDapp = () => { }; React.useEffect(() => { - if(nodes.length === 0 ) return; - if(loaded) return; + if (nodes.length === 0) return; + if (loaded) return; if (!categories || categories.length === 0 || dapps.length <= 2) return; diff --git a/src/modules/blockchains/Buy/hooks/useTemplate.ts b/src/modules/blockchains/Buy/hooks/useTemplate.ts index 5b3566b29..c93ef35e6 100644 --- a/src/modules/blockchains/Buy/hooks/useTemplate.ts +++ b/src/modules/blockchains/Buy/hooks/useTemplate.ts @@ -4,20 +4,22 @@ import useModelCategoriesStore from '@/modules/blockchains/Buy/stores/useModelCa import { IModelCategory } from '@/types/customize-model'; import { useSearchParams } from 'next/navigation'; import { cloneDeep } from '../utils'; -import { - useOptionInputStore -} from '@/modules/blockchains/Buy/component4/DappRenderer/OptionInputValue/useOptionInputStore'; +import { useOptionInputStore } from '@/modules/blockchains/Buy/component4/DappRenderer/OptionInputValue/useOptionInputStore'; export default function useTemplate() { const searchParams = useSearchParams(); const { setDraggedFields } = useDragStore(); const { parsedCategories, categoriesTemplates } = useModelCategoriesStore(); const { field, setField, setFields } = useOrderFormStoreV3(); - const {setValue} = useOptionInputStore(); + const { setValue } = useOptionInputStore(); // console.log('useTemplate -> field', field); const setTemplate = (template: IModelCategory[]) => { + if (template.length === 0) { + return; + } + const newFields = cloneDeep(field); template.forEach((_field) => { @@ -44,11 +46,11 @@ export default function useTemplate() { newFields[_field.key].value = _field.options[0].key; newFields[_field.key].dragged = true; } - _field.options.forEach(option=>{ - if(option.addOnInputs){ + _field.options.forEach((option) => { + if (option.addOnInputs) { setValue(option.key, option.addOnInputs.attrs?.value); } - }) + }); _draggedFields.push(_field.key); }); diff --git a/src/modules/blockchains/Buy/studio/ButtonStartChat/index.tsx b/src/modules/blockchains/Buy/studio/ButtonStartChat/index.tsx new file mode 100644 index 000000000..2ba937fa4 --- /dev/null +++ b/src/modules/blockchains/Buy/studio/ButtonStartChat/index.tsx @@ -0,0 +1,49 @@ +import MagicIcon from '@/components/MagicIcon'; +import { gsap } from 'gsap'; +import { ReactElement, useEffect, useRef } from 'react'; +import Chatbox from '../Chatbox'; +import useChatBoxState from '../Chatbox/chatbox-store'; +import styles from './styles.module.scss'; + +export default function ButtonStartChat(): ReactElement { + const { isChatboxOpen, setIsChatboxOpen } = useChatBoxState((state) => state); + const chatboxRef = useRef(null); + + useEffect(() => { + if (chatboxRef.current) { + if (isChatboxOpen) { + gsap.fromTo( + chatboxRef.current, + { x: '100%' }, + { x: '0%', duration: 0.5, ease: 'power2.out' }, + ); + } else { + gsap.to(chatboxRef.current, { + x: '100%', + duration: 0.5, + ease: 'power2.in', + }); + } + } + }, [isChatboxOpen]); + + return ( + <> + +
+ +
+ + ); +} diff --git a/src/modules/blockchains/Buy/studio/ButtonStartChat/styles.module.scss b/src/modules/blockchains/Buy/studio/ButtonStartChat/styles.module.scss new file mode 100644 index 000000000..5e6d48469 --- /dev/null +++ b/src/modules/blockchains/Buy/studio/ButtonStartChat/styles.module.scss @@ -0,0 +1,27 @@ +.button { + display: flex; + align-items: center; + justify-content: center; + padding: 10px 15px; + background: linear-gradient(360deg, #00789F 0%, #32009B 14.56%, #7500D2 44.15%, #DA00DE 84.23%, #FEDEC0 100%); + color: white; + border: none; + border-radius: 100px; + font-size: 14px; + font-weight: 600; + font-family: 'SF Pro Display', sans-serif; + text-transform: uppercase; + cursor: pointer; + transition: background 0.3s ease; + position: absolute; + top: 10px; + right: 10px; + + &:hover { + background: linear-gradient(360deg, #005F7F 0%, #26007B 14.56%, #5E00A8 44.15%, #AE00B2 84.23%, #CBB299 100%); + } + + svg { + margin-left: 8px; + } +} diff --git a/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonApply/index.tsx b/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonApply/index.tsx new file mode 100644 index 000000000..27b88deba --- /dev/null +++ b/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonApply/index.tsx @@ -0,0 +1,38 @@ +import useTemplate from '@/modules/blockchains/Buy/hooks/useTemplate'; +import useChatBoxState, { ChatBoxStatus } from '../../chatbox-store'; +import styles from './styles.module.scss'; + +const ButtonApply = () => { + const { setTemplate } = useTemplate(); + const { prepareCategoryTemplate, setChatBoxStatus } = useChatBoxState(); + + const handleApply = () => { + setChatBoxStatus({ + status: ChatBoxStatus.Close, + isGenerating: false, + isComplete: false, + isListening: false, + }); + setTemplate(prepareCategoryTemplate); + }; + + return ( + + ); +}; + +export default ButtonApply; diff --git a/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonApply/styles.module.scss b/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonApply/styles.module.scss new file mode 100644 index 000000000..826b1e789 --- /dev/null +++ b/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonApply/styles.module.scss @@ -0,0 +1,19 @@ +.applyButton { + background-color: #FA4E0E; + color: #ffffff; + border-radius: 4px; + border: none; + cursor: pointer; + font-family: SF Pro Display; + font-size: 16px; + font-weight: 500; + line-height: 24px; + text-align: right; + height: 28px; + display: flex; + align-items: center; + gap: 8px; + padding-left: 8px; + padding-right: 8px; + justify-content: center; +} diff --git a/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonCancle/index.tsx b/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonCancle/index.tsx new file mode 100644 index 000000000..61d2ddf97 --- /dev/null +++ b/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonCancle/index.tsx @@ -0,0 +1,31 @@ +import useChatBoxState from '../../chatbox-store'; +import styles from './styles.module.scss'; + +export default function ButtonCancel({ onClick }: { onClick: () => void }) { + const { setIsChatboxOpen } = useChatBoxState(); + + return ( + + ); +} diff --git a/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonCancle/styles.module.scss b/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonCancle/styles.module.scss new file mode 100644 index 000000000..32b391244 --- /dev/null +++ b/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonCancle/styles.module.scss @@ -0,0 +1,14 @@ +.voiceButton { + border: none; + cursor: pointer; + font-family: SF Pro Display; + font-size: 16px; + font-weight: 500; + line-height: 24px; + text-align: right; + display: flex; + align-items: center; + gap: 8px; + height: 32px; + color: #555555; +} diff --git a/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonClsoe/index.tsx b/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonClsoe/index.tsx new file mode 100644 index 000000000..24ebc0da0 --- /dev/null +++ b/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonClsoe/index.tsx @@ -0,0 +1,34 @@ +import useChatBoxState from '../../chatbox-store'; +import styles from './styles.module.scss'; + +export default function ButtonClose() { + const { setIsChatboxOpen } = useChatBoxState(); + + return ( + + ); +} diff --git a/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonClsoe/styles.module.scss b/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonClsoe/styles.module.scss new file mode 100644 index 000000000..a2c02c19a --- /dev/null +++ b/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonClsoe/styles.module.scss @@ -0,0 +1,14 @@ +.buttonClose { + border: none; + cursor: pointer; + font-family: SF Pro Display; + font-size: 16px; + font-weight: 500; + line-height: 24px; + text-align: right; + display: flex; + align-items: center; + gap: 8px; + height: 32px; + color: #555555; +} diff --git a/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonStop/index.tsx b/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonStop/index.tsx new file mode 100644 index 000000000..ce3e117c8 --- /dev/null +++ b/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonStop/index.tsx @@ -0,0 +1,46 @@ +import useChatBoxState, { ChatBoxStatus } from '../../chatbox-store'; +import styles from './styles.module.scss'; + +const ButtonStop = () => { + const { setChatBoxStatus } = useChatBoxState((state) => state); + return ( + + ); +}; + +export default ButtonStop; diff --git a/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonStop/styles.module.scss b/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonStop/styles.module.scss new file mode 100644 index 000000000..ce057f670 --- /dev/null +++ b/src/modules/blockchains/Buy/studio/Chatbox/Actions/ButtonStop/styles.module.scss @@ -0,0 +1,14 @@ +.buttonStop { + border: none; + cursor: pointer; + font-family: SF Pro Display; + font-size: 16px; + font-weight: 500; + line-height: 24px; + text-align: right; + display: flex; + align-items: center; + gap: 8px; + height: 32px; + color: #555555; +} diff --git a/src/modules/blockchains/Buy/studio/Chatbox/LabelListening/index.tsx b/src/modules/blockchains/Buy/studio/Chatbox/LabelListening/index.tsx new file mode 100644 index 000000000..ebcceff69 --- /dev/null +++ b/src/modules/blockchains/Buy/studio/Chatbox/LabelListening/index.tsx @@ -0,0 +1,25 @@ +import styles from './styles.module.scss'; + +export default function LabelListening() { + return ( +
+ + + + + + + + Listening... +
+ ); +} diff --git a/src/modules/blockchains/Buy/studio/Chatbox/LabelListening/styles.module.scss b/src/modules/blockchains/Buy/studio/Chatbox/LabelListening/styles.module.scss new file mode 100644 index 000000000..dfe5dd90c --- /dev/null +++ b/src/modules/blockchains/Buy/studio/Chatbox/LabelListening/styles.module.scss @@ -0,0 +1,14 @@ +.listeningOverlay { + position: absolute; + top: 16px; + left: 16px; + display: flex; + align-items: center; + font-family: SF Pro Display; + font-size: 18px; + font-weight: 500; + line-height: 24px; + text-align: left; + color: #333333; + gap: 8px; +} diff --git a/src/modules/blockchains/Buy/studio/Chatbox/Message/index.tsx b/src/modules/blockchains/Buy/studio/Chatbox/Message/index.tsx new file mode 100644 index 000000000..e9c42570b --- /dev/null +++ b/src/modules/blockchains/Buy/studio/Chatbox/Message/index.tsx @@ -0,0 +1,120 @@ +import { IModelCategory } from '@/types/customize-model'; +import { useCallback, useEffect, useRef, useState } from 'react'; +import Lego from '../../../component4/Lego'; +import useChatBoxState, { ChatBoxStatus } from '../chatbox-store'; +import styles from './styles.module.scss'; + +export default function Message({ + message, + template, + onUpdateScroll, +}: { + message: string; + template: IModelCategory[]; + onUpdateScroll: () => void; +}) { + const { setChatBoxStatus, isGenerating, prepareCategoryTemplate } = + useChatBoxState((state) => state); + const [isRendered, setIsRendered] = useState(false); + + const refRender = useRef(); + const [displayedMessage, setDisplayedMessage] = useState(''); + const [displayedTemplate, setDisplayedTemplate] = useState( + [], + ); + + const refTextRender = useRef(''); + + const animateMessage = useCallback(() => { + let messageIndex = 0; + let templateIndex = 0; + let optionIndex = 0; + + refRender.current = setInterval(() => { + if (messageIndex < message.length) { + refTextRender.current += message[messageIndex]; + setDisplayedMessage(refTextRender.current); + messageIndex++; + } else if (templateIndex < template.length) { + const currentTemplate = template[templateIndex]; + + if (optionIndex < currentTemplate.options.length) { + setDisplayedTemplate((prev) => { + const updatedTemplate = [...prev]; + + if (!updatedTemplate[templateIndex]) { + updatedTemplate[templateIndex] = { + ...currentTemplate, + options: [currentTemplate.options[optionIndex]], + }; + } else { + updatedTemplate[templateIndex].options.push( + currentTemplate.options[optionIndex], + ); + } + + optionIndex++; + + return updatedTemplate; + }); + } else { + templateIndex++; + optionIndex = 0; + } + } else { + refRender.current && clearInterval(refRender.current); + setIsRendered(true); + setChatBoxStatus({ + status: + prepareCategoryTemplate.length > 0 + ? ChatBoxStatus.Complete + : ChatBoxStatus.Cancel, + isGenerating: false, + isComplete: prepareCategoryTemplate.length > 0, + isListening: false, + }); + } + onUpdateScroll(); + }, 30); + }, [message, template]); + + useEffect(() => { + if (isRendered) return; + + if (isGenerating) { + animateMessage(); + } else { + setIsRendered(true); + refRender.current && clearInterval(refRender.current); + } + }, [isGenerating]); + + return ( +
+
{displayedMessage}
+ +
+ {displayedTemplate.map((item) => ( +
+
Generated {item.title}
+ +
+ {item.options.map((option) => ( + + ))} +
+
+ ))} +
+
+ ); +} diff --git a/src/modules/blockchains/Buy/studio/Chatbox/Message/styles.module.scss b/src/modules/blockchains/Buy/studio/Chatbox/Message/styles.module.scss new file mode 100644 index 000000000..e56b9d93c --- /dev/null +++ b/src/modules/blockchains/Buy/studio/Chatbox/Message/styles.module.scss @@ -0,0 +1,23 @@ +.categories { + margin-top: 16px; + display: flex; + flex-direction: column; + gap: 16px; + + .category { + display: flex; + flex-direction: column; + gap: 4px; + + .categoryTitle { + font-size: 16px; + font-weight: 500; + line-height: 24px; + } + + .categoryOptions { + display: flex; + gap: 8px; + } + } +} diff --git a/src/modules/blockchains/Buy/studio/Chatbox/TextInput/index.tsx b/src/modules/blockchains/Buy/studio/Chatbox/TextInput/index.tsx new file mode 100644 index 000000000..b88c52535 --- /dev/null +++ b/src/modules/blockchains/Buy/studio/Chatbox/TextInput/index.tsx @@ -0,0 +1,44 @@ +import { ReactElement } from 'react'; +import useChatBoxState from '../chatbox-store'; +import LabelListening from '../LabelListening'; +import styles from './styles.module.scss'; + +export default function TextInput({ + handleSendMessage, +}: { + handleSendMessage: any; +}): ReactElement { + const { inputMessage, isListening, isGenerating, setInputMessage } = + useChatBoxState((state) => state); + return ( +
+

Prompt

+
+