Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/web/src/components/basic/SelectedFilters.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { getClassName, handleA11yAction } from '@appbaseio/reactivecore/lib/util
import Button, { Filter } from '../../styles/Button';
import Container from '../../styles/Container';
import Title from '../../styles/Title';
import { connect, decodeHtml } from '../../utils';
import { connect, decodeHtml, sanitizeImageUrl } from '../../utils';

class SelectedFilters extends Component {
constructor(props) {
Expand Down Expand Up @@ -141,7 +141,7 @@ class SelectedFilters extends Component {
<Filter.Value title={decodeHtml(valueToRender)}>{
selectedValues[component].label}: {decodeHtml(valueToRender)}
</Filter.Value>
{imageValue ? <Filter.ImageValue><img width="30px" alt="thumbnail" src={imageValue} /> </Filter.ImageValue> : null}
{imageValue ? <Filter.ImageValue><img width="30px" alt="thumbnail" src={sanitizeImageUrl(imageValue)} /> </Filter.ImageValue> : null}
<Filter.CloseIcon>&#x2715;</Filter.CloseIcon>
</Button>
);
Expand All @@ -155,7 +155,7 @@ class SelectedFilters extends Component {
tabIndex="0"
>
<Filter.Value>{selectedValues[component].label}:</Filter.Value>
<Filter.ImageValue><img width="30px" alt="thumbnail" src={imageValue} /></Filter.ImageValue>
<Filter.ImageValue><img width="30px" alt="thumbnail" src={sanitizeImageUrl(imageValue)} /></Filter.ImageValue>
<Filter.CloseIcon>&#x2715;</Filter.CloseIcon>
</Button>
);
Expand Down
3 changes: 2 additions & 1 deletion packages/web/src/components/search/AIAnswer/Chat.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import Mic from '../addons/Mic';
import Button from '../../../styles/Button';
import AIFeedback from '../../shared/AIFeedback';
import { Footer, SourceTags } from '../../../styles/SearchBoxAI';
import { sanitizeImageUrl } from '../../../utils';

const md = new Remarkable();

Expand Down Expand Up @@ -76,7 +77,7 @@ const Chat = (props) => {
}
if (props.iconURL) {
return (
<img style={{ maxHeight: '25px' }} src={xss(props.iconURL)} alt="search-icon" />
<img style={{ maxHeight: '25px' }} src={sanitizeImageUrl(props.iconURL)} alt="search-icon" />
);
}
return <SearchSvg />;
Expand Down
182 changes: 182 additions & 0 deletions packages/web/src/components/search/AISection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { Remarkable } from 'remarkable';
import { getClassName } from '@appbaseio/reactivecore/lib/utils/helper';
import { SearchBoxAISection, Answer } from '../../styles/SearchBoxAI';
import TypingEffect from '../shared/TypingEffect';
import AIFeedback from '../shared/AIFeedback';

const md = new Remarkable();
md.set({
html: true,
breaks: true,
xhtmlOut: true,
linkify: true,
linkTarget: '_blank',
});

const AISection = ({
showAIScreen,
themePreset,
renderAIAnswer,
mergedAIQuestion,
mergedAIAnswer,
AIResponse,
isAIResponseLoading,
isLoading,
getAISourceObjects,
AIResponseError,
renderAIScreenLoader,
currentValue,
prevPropsRefIsAITyping,
setShowAIScreenFooter,
AIUIConfig,
showTypingEffect,
setShowFeedbackComponent,
setShowTypingEffect,
_dropdownULRef,
isUserScrolling,
setLastScrollTop,
renderAIScreenFooter,
showFeedbackComponent,
innerClass,
feedbackState,
sessionIdFromStore,
setFeedbackState,
trackUsefullness,
renderError,
isTypingAIAnswer,
}) => {
if (!showAIScreen) return null;

return (
<SearchBoxAISection themePreset={themePreset}>
{typeof renderAIAnswer === 'function' ? (
renderAIAnswer({
question: mergedAIQuestion,
answer: mergedAIAnswer,
documentIds:
(AIResponse
&& AIResponse.response
&& AIResponse.response.answer
&& AIResponse.response.answer.documentIds)
|| [],
loading: isAIResponseLoading || isLoading,
sources: getAISourceObjects(),
error: AIResponseError,
})
) : (
<Fragment>
{isAIResponseLoading || isLoading ? (
renderAIScreenLoader()
) : (
<Fragment>
<Answer>
<TypingEffect
key={currentValue}
message={md.render(mergedAIAnswer || '')}
speed={5}
onTypingComplete={() => {
if (prevPropsRefIsAITyping.current === undefined) {
setShowAIScreenFooter(true);
}
if (
(AIUIConfig
&& typeof AIUIConfig.showFeedback === 'boolean'
? AIUIConfig.showFeedback
: true) &&
showTypingEffect
) {
setShowFeedbackComponent(true);
}

if (mergedAIAnswer) {
setShowTypingEffect(false);
}

setTimeout(() => {
if (_dropdownULRef.current) {
_dropdownULRef.current.scrollTo({
top: _dropdownULRef.current.scrollHeight,
behavior: 'smooth',
});
}
}, 100);
}}
onWhileTyping={() => {
if (!isUserScrolling && _dropdownULRef.current) {
_dropdownULRef.current.scrollTo({
top: _dropdownULRef.current.scrollHeight,
behavior: 'smooth',
});
setLastScrollTop(_dropdownULRef.current.scrollHeight);
}
}}
showTypingEffect={isTypingAIAnswer}
/>
</Answer>
{renderAIScreenFooter()}

{showFeedbackComponent && (
<div className={`${getClassName(innerClass, 'ai-feedback') || ''}`}>
{' '}
<AIFeedback
overrideState={feedbackState}
hideUI={isAIResponseLoading || isLoading || !sessionIdFromStore}
key={sessionIdFromStore}
onFeedbackSubmit={(useful, reason) => {
setFeedbackState({
isRecorded: true,
feedbackType: useful ? 'positive' : 'negative',
});
trackUsefullness(sessionIdFromStore, {
useful,
reason,
});
}}
/>
</div>
)}
</Fragment>
)}
</Fragment>
)}
{renderError(true)}
</SearchBoxAISection>
);
};

AISection.propTypes = {
showAIScreen: PropTypes.bool.isRequired,
themePreset: PropTypes.string,
renderAIAnswer: PropTypes.func,
mergedAIQuestion: PropTypes.string,
mergedAIAnswer: PropTypes.string,
AIResponse: PropTypes.object,
isAIResponseLoading: PropTypes.bool,
isLoading: PropTypes.bool,
getAISourceObjects: PropTypes.func.isRequired,
AIResponseError: PropTypes.object,
renderAIScreenLoader: PropTypes.func.isRequired,
currentValue: PropTypes.string,
prevPropsRefIsAITyping: PropTypes.object.isRequired,
setShowAIScreenFooter: PropTypes.func.isRequired,
AIUIConfig: PropTypes.object,
showTypingEffect: PropTypes.bool.isRequired,
setShowFeedbackComponent: PropTypes.func.isRequired,
setShowTypingEffect: PropTypes.func.isRequired,
_dropdownULRef: PropTypes.object.isRequired,
isUserScrolling: PropTypes.bool.isRequired,
setLastScrollTop: PropTypes.func.isRequired,
renderAIScreenFooter: PropTypes.func.isRequired,
showFeedbackComponent: PropTypes.bool.isRequired,
innerClass: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
feedbackState: PropTypes.object,
sessionIdFromStore: PropTypes.string,
setFeedbackState: PropTypes.func.isRequired,
trackUsefullness: PropTypes.func.isRequired,
renderError: PropTypes.func.isRequired,
isTypingAIAnswer: PropTypes.bool.isRequired,
};

export default AISection;
Loading