UNPKG

node-red-contrib-chatbot

Version:

REDBot a Chat bot for a full featured chat bot for Telegram, Facebook Messenger and Slack. Almost no coding skills required

211 lines (200 loc) 7.18 kB
import React, { useState } from 'react'; import { AutoComplete, InputGroup, Button, FlexboxGrid, ButtonGroup, IconButton, Icon, Tooltip, Whisper } from 'rsuite'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import { useQuery } from 'react-apollo'; import Language from '../../components/language'; import useMCContext from '../../hooks/mc-context'; import ContentPreview from './views/content-preview'; import ContentIcon from './views/content-icon'; import useCreateContent from './hooks/create-content'; import { SEARCH } from './queries'; import './style.scss'; const withTooltip = Component => { return ({ children, tooltip, ...rest }) => ( <Whisper placement="top" trigger="hover" speaker={<Tooltip>{tooltip}</Tooltip>} > <Component {...rest}>{children}</Component> </Whisper> ) }; const IconButtonTooltip = withTooltip(IconButton); const ContentAutocomplete = ({ value, onChange = () => {}, style, useSlug = false, canCreate = true, fluid = false, namespace = 'content', customFieldsSchema, disabled: componentDisabled = false }) => { const { state } = useMCContext(); const [search, setSearch] = useState(null); const [items, setItems] = useState(null); const [content, setContent] = useState(null); const { createContent, modal } = useCreateContent({ onComplete: async content => { onChange(useSlug ? content.slug : content.id); const result = await refetch(); setItems(result.data.contents); } }); const variables = { search: search != null ? search : undefined, id: search == null && !useSlug ? value || 0 : undefined, slug: search == null && useSlug ? value || 'invalid-slug' : undefined, namespace, chatbotId: state.chatbotId }; const { client, refetch } = useQuery(SEARCH, { fetchPolicy: 'network-only', variables, onCompleted: data => setItems(data.contents) }); const disabled = componentDisabled; let current; if (search != null) { current = search; } else { if (!_.isEmpty(items)) { current = useSlug ? `slug: ${items[0].slug}` : items[0].title; } else { current = ''; } } return ( <div className={classNames('ui-content-autocomplete', { fluid })}> <div className="autocomplete"> <InputGroup inside style={style}> <AutoComplete value={current} className="autocomplete-box with-buttons" disabled={disabled} renderItem={({ label, slug, title, language }) => { return useSlug ? <div><em>({slug})</em>: {title} <Language>{language}</Language></div> : <div>{title} <em>({slug})</em> <Language>{language}</Language></div>; }} onChange={(current, event) => { const isBackspace = event.nativeEvent != null && event.nativeEvent.inputType === 'deleteContentBackward'; if (event.keyCode === 13) { const found = items.find(item => item.id === current); if (found != null) { setSearch(null); onChange(useSlug ? found.slug : found.id); } } else if (isBackspace) { if (search != null) { setSearch(current); } setItems(null); onChange(null); } else if (useSlug && event.nativeEvent != null && event.nativeEvent.inputType === 'insertText' && current.startsWith('slug: ')) { setSearch(event.nativeEvent.data); } else if (event.nativeEvent != null && event.nativeEvent.inputType === 'insertText') { setSearch(String(current)); } }} onSelect={item => { if (item != null) { setSearch(null); onChange(useSlug ? item.slug : item.id); } }} data={(items || []).map(item => ({ key: item.id, value: item.id, label: item.slug + ' ' + item.title, ...item }))} /> {search == null && items != null && ( <InputGroup.Addon style={{ marginTop: '-2px', marginRight: '-2px' }}> {items.map(item => ( <ContentIcon key={item.id} disabled={item.id === content || disabled} {...item} onClick={() => setContent(item.id)} /> ))} </InputGroup.Addon> )} </InputGroup> </div> <ButtonGroup className="content-buttons"> {canCreate && ( <IconButtonTooltip tooltip={useSlug && !_.isEmpty(value) ? `Create content for slug "${value}"`: 'Create content'} appearance="default" icon={<Icon icon="plus-square"/>} size="md" disabled={disabled} onClick={() => createContent({ namespace, slug: useSlug && value != null ? value : undefined, chatbotId: state.chatbotId }, { disabledLanguages: (items || []).map(item => item.language), customFieldsSchema })} /> )} <IconButtonTooltip tooltip="Remove all" appearance="default" size="md" icon={<Icon icon="close"/>} onClick={() => onChange(null)} disabled={disabled || value == null} /> </ButtonGroup> {modal} {content != null && ( <ContentPreview contentId={content} onCancel={() => setContent(null)} customFieldsSchema={customFieldsSchema} onDelete={async () => { setContent(null); const { data } = await client.query({ query: SEARCH, fetchPolicy: 'network-only', variables }); setItems(data.contents); }} onSubmit={async () => { setContent(null); const { data } = await client.query({ query: SEARCH, fetchPolicy: 'network-only', variables }); setItems(data.contents); }} /> )} </div> ); }; ContentAutocomplete.propTypes = { value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // select content using the slug and not the id (slug could include a group of posts) useSlug: PropTypes.bool, // add can create button canCreate: PropTypes.bool, // component takes all the width fluid: PropTypes.bool, onChange: PropTypes.func, style: PropTypes.object, // restrict autocomplete and creation to this namespace namespace: PropTypes.string, // the schema for custom fields (suggest a limited set of custom field name) customFieldsSchema: PropTypes.arrayOf(PropTypes.shape({ key: PropTypes.string, type: PropTypes.string, description: PropTypes.string, defaultValue: PropTypes.string, color: PropTypes.oneOf(['red','orange', 'yellow', 'green', 'cyan', 'blue', 'violet']) })) }; export default ContentAutocomplete;