Skip to content
This repository was archived by the owner on Jun 3, 2025. It is now read-only.

Commit e0618ab

Browse files
authored
Merge pull request #76 from SAPConversationalAI/mergeToOpenSource
Merge to open source
2 parents 9480199 + 0d6f221 commit e0618ab

File tree

15 files changed

+24271
-14317
lines changed

15 files changed

+24271
-14317
lines changed

.babelrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[
44
"@babel/preset-env", {
55
"targets": {
6-
"browsers": [">1%", "not op_mini all"],
6+
"browsers": [">1%", "not op_mini all", "ie 11"],
77
"node": "current"
88
},
99
"useBuiltIns": "usage",

package-lock.json

Lines changed: 24165 additions & 14292 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "webchat",
3-
"version": "1.4.35",
3+
"version": "1.4.37",
44
"description": "",
55
"main": "lib/index.js",
66
"scripts": {
@@ -17,16 +17,16 @@
1717
"test": "MOCHA_TEST=true nyc --reporter=text mocha 'src/**/*.test.js'",
1818
"testHtml": "MOCHA_TEST=true nyc --reporter=html --report-dir coverage/html mocha --reporter mocha-junit-reporter --reporter-options mochaFile=./reports/mocha.xml 'src/**/*.test.js'",
1919
"coverage": "nyc report --cache false --reporter=html --report-dir coverage/html",
20-
"coverage:clover": "nyc report --cache false --reporter=clover --report-dir coverage/clover && chown -R 1000:999 coverage/clover",
21-
"coverage:cobertura": "nyc report --cache false --reporter=cobertura --report-dir coverage/cobertura && chown -R 1000:999 coverage/cobertura",
22-
"coverage:html": "nyc report --cache false --reporter=html --report-dir coverage/html && chown -R 1000:999 coverage/html",
23-
"coverage:lcov": "nyc report --cache false --reporter=lcov --report-dir coverage/lcov && chown -R 1000:999 coverage/lcov"
20+
"coverage:clover": "nyc report --cache false --reporter=clover --report-dir coverage/clover",
21+
"coverage:cobertura": "nyc report --cache false --reporter=cobertura --report-dir coverage/cobertura",
22+
"coverage:html": "nyc report --cache false --reporter=html --report-dir coverage/html",
23+
"coverage:lcov": "nyc report --cache false --reporter=lcov --report-dir coverage/lcov"
2424
},
2525
"keywords": [],
2626
"author": "",
2727
"license": "MIT",
2828
"dependencies": {
29-
"@braintree/sanitize-url": "^2.1.0",
29+
"@braintree/sanitize-url": "^5.0.2",
3030
"@types/react": "16.8.22",
3131
"axios": "^0.21.1",
3232
"classnames": "^2.2.5",
@@ -59,6 +59,7 @@
5959
"@babel/preset-react": "^7.0.0",
6060
"@babel/register": "^7.6.0",
6161
"@babel/runtime": "7.12.1",
62+
"@ui5/cli": "^2.0.2",
6263
"autoprefixer": "^9.6.1",
6364
"axios-mock-adapter": "^1.18.2",
6465
"babel-eslint": "^10.0.3",
@@ -78,7 +79,6 @@
7879
"eslint-config-zavatta-react": "^2.3.1",
7980
"eslint-plugin-react": "^7.14.3",
8081
"esm": "^3.2.25",
81-
"extract-text-webpack-plugin": "^3.0.2",
8282
"file-loader": "^1.1.5",
8383
"ignore-styles": "^5.0.1",
8484
"jsdom": "^16.4.0",
@@ -99,7 +99,7 @@
9999
"sass-loader": "^7.3.1",
100100
"sinon": "^9.2.4",
101101
"style-loader": "^1.0.0",
102-
"uglifyjs-webpack-plugin": "^1.1.2",
102+
"uglifyjs-webpack-plugin": "2.2.0",
103103
"webpack": "^4.41.2",
104104
"webpack-dev-server": "^3.9.0"
105105
},

src/components/Button/style.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66

77
font-weight: bold;
88
color: cornflowerblue;
9+
10+
overflow: hidden;
11+
word-wrap: break-word;
12+
word-break: break-word;
13+
overflow-wrap: break-word;
914

1015
&--ReadOnly {
1116
opacity: 0.5;

src/components/Live/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ class Live extends Component {
101101
lastMessage
102102
&& (sendMessagePromiseCondition || pollMessageCondition)
103103
&& !lastMessage.retry
104-
&& !lastMessage.isSending
105104
&& showTyping
106105
)
107106

src/components/Message/Card.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,20 @@ import { sanitizeUrl } from '@braintree/sanitize-url'
55
import { truncate, safeArrayOfItem } from 'helpers'
66

77
import Button from 'components/Button'
8+
import { propOr } from 'ramda'
89

910
const Card = ({ content, sendMessage, onImageLoaded, readOnlyMode, isLastMessage }) => {
10-
const { title, subtitle, imageUrl, buttons } = content
11+
const title = propOr('', 'title', content)
12+
const subtitle = propOr(null, 'subtitle', content)
13+
const buttons = propOr(null, 'buttons', content)
14+
let imageUrl = propOr(null, 'imageUrl', content)
1115

1216
if (imageUrl && sanitizeUrl(imageUrl) === 'about:blank') {
13-
return null
17+
console.warn('Warning the image url is not supported')
18+
// If the image is invalid still show the card without the image
19+
imageUrl = null
1420
}
21+
1522
// https://sapjira.wdf.sap.corp/browse/SAPMLCONV-6296
1623
// Need to check if buttons is null before rendering the button html.
1724
return (

src/components/Message/Text.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ const allowedMarkdownTypes = [
3232
'tableCell',
3333
]
3434

35+
// Export for unit test
36+
export const getValidMarkDownLinkString = (isMarkdown, compiledResponse) => {
37+
if (isMarkdown && compiledResponse) {
38+
// Search the text starting with [ with :// and ends with ]
39+
return compiledResponse
40+
}
41+
42+
return null
43+
}
44+
3545
const Text = ({ content, style, isMarkdown, readOnlyMode }) => {
3646
const respond = safeStringValue(content)
3747

@@ -66,15 +76,15 @@ const Text = ({ content, style, isMarkdown, readOnlyMode }) => {
6676
rel='noopener noreferrer'>{props.children}
6777
</a>)
6878
}
79+
const markDownResponse = getValidMarkDownLinkString(isMarkdown, compiledResponse)
6980

7081
return (
7182
<div style={style} className={'RecastAppText CaiAppText'}>
7283
{isMarkdown ? (
7384
<ReactMarkdown
7485
plugins={[gfm]}
7586
renderers={{ link: LinkRenderer }}
76-
allowedTypes={allowedMarkdownTypes}
77-
>{compiledResponse}
87+
allowedTypes={allowedMarkdownTypes}>{markDownResponse}
7888
</ReactMarkdown>
7989
) : (
8090
compiledResponse

src/components/Message/style.scss

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -270,8 +270,8 @@
270270

271271
.RecastAppPicture, .CaiAppPicture {
272272
width: 270px;
273-
max-height: 270px;
274-
273+
max-height: 540px;
274+
object-fit: cover;
275275
border-radius: 3px;
276276
border: 1px solid lightgrey;
277277
}
@@ -352,7 +352,11 @@
352352

353353
&--text {
354354
padding: 0.4rem;
355-
355+
overflow: hidden;
356+
word-wrap: break-word;
357+
word-break: break-word;
358+
overflow-wrap: break-word;
359+
356360
&-title {
357361
font-weight: bold;
358362
}

src/containers/Chat/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,9 @@ class Chat extends Component {
424424
conversationId,
425425
lastMessageId,
426426
)
427-
shouldPoll = waitTime === 0
427+
// Handle the case where waitTime is null
428+
const waitingTime = typeof waitTime === 'number' ? waitTime : 0
429+
shouldPoll = waitingTime === 0
428430
shouldWaitXseconds = waitTime > 0
429431
numberCallsWithoutAnyMessages = this._deteremNumberCallsWithoutAnyMessages(numberCallsWithoutAnyMessages, shouldWaitXseconds, messages)
430432
timeToSleep = waitTime * 1000

src/middlewares/api.js

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,36 @@
11
import config from 'config'
22
import qs from 'query-string'
33
import axios from 'axios'
4+
import { pathOr, propOr } from 'ramda'
5+
6+
let lastProcessed = null
7+
let createdConversation = false
8+
9+
function waitforTime (milisec) {
10+
return new Promise(resolve => { setTimeout(resolve, milisec) })
11+
}
12+
13+
const delayBetweenMessages = async (messages, dispatch) => {
14+
const lastMsg = messages.slice(-1)[0]
15+
const lastMessageId = propOr(null, 'id', lastMsg)
16+
if (lastMessageId) {
17+
if (lastProcessed === lastMessageId) {
18+
// already processed skip for now. (Bug in server return the same list over and over)
19+
return
20+
}
21+
lastProcessed = lastMessageId
22+
// For polling set the lastMessageId to avoid getting the same messages again while dispatching
23+
dispatch({ type: 'SET_CREDENTIALS', payload: { lastMessageId } })
24+
}
25+
for (const msg of messages) {
26+
dispatch({ type: 'ADD_MESSAGES', payload: { messages: [msg] } })
27+
if (lastMessageId !== msg.id) {
28+
// If there is a delay in this message wait before showing the next message.
29+
const messageDelay = pathOr(0, ['attachment', 'delay'], msg) * 1000
30+
await waitforTime(messageDelay)
31+
}
32+
}
33+
}
434

535
export default store => next => action => {
636
if (!action.type.startsWith('API:')) {
@@ -24,7 +54,14 @@ export default store => next => action => {
2454

2555
return axios(options)
2656
.then(res => {
27-
dispatch({ type: `${prefix}_SUCCESS`, payload: { ...res.data.results } })
57+
createdConversation = createdConversation || prefix === 'CREATE_CONVERSATION'
58+
const isFirstCall = propOr(null, 'last_message_id', query) === null && !createdConversation
59+
if (prefix === 'POLL_MESSAGES' && !isFirstCall) {
60+
const messages = pathOr([], ['data', 'results', 'messages'], res)
61+
delayBetweenMessages(messages, dispatch)
62+
} else {
63+
dispatch({ type: `${prefix}_SUCCESS`, payload: { ...res.data.results } })
64+
}
2865
return res.data.results
2966
})
3067
.catch(err => {

0 commit comments

Comments
 (0)