@memori.ai/memori-react 2.11.0 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/README.md +51 -0
- package/dist/components/Chat/Chat.css +3 -3
- package/dist/components/Chat/Chat.d.ts +4 -3
- package/dist/components/Chat/Chat.js +9 -4
- package/dist/components/Chat/Chat.js.map +1 -1
- package/dist/components/ChatBubble/ChatBubble.css +10 -5
- package/dist/components/MediaWidget/MediaItemWidget.d.ts +2 -2
- package/dist/components/MediaWidget/MediaItemWidget.js +1 -13
- package/dist/components/MediaWidget/MediaItemWidget.js.map +1 -1
- package/dist/components/MediaWidget/MediaWidget.d.ts +2 -2
- package/dist/components/MediaWidget/MediaWidget.js +1 -1
- package/dist/components/MediaWidget/MediaWidget.js.map +1 -1
- package/dist/components/MemoriWidget/MemoriWidget.d.ts +5 -3
- package/dist/components/MemoriWidget/MemoriWidget.js +56 -17
- package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
- package/dist/components/Typing/Typing.d.ts +7 -0
- package/dist/components/Typing/Typing.js +46 -0
- package/dist/components/Typing/Typing.js.map +1 -0
- package/dist/components/Typing/Typing.test.d.ts +1 -0
- package/dist/components/Typing/Typing.test.js +23 -0
- package/dist/components/Typing/Typing.test.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/esm/components/Chat/Chat.css +3 -3
- package/esm/components/Chat/Chat.d.ts +4 -3
- package/esm/components/Chat/Chat.js +10 -5
- package/esm/components/Chat/Chat.js.map +1 -1
- package/esm/components/ChatBubble/ChatBubble.css +10 -5
- package/esm/components/MediaWidget/MediaItemWidget.d.ts +2 -2
- package/esm/components/MediaWidget/MediaItemWidget.js +2 -14
- package/esm/components/MediaWidget/MediaItemWidget.js.map +1 -1
- package/esm/components/MediaWidget/MediaWidget.d.ts +2 -2
- package/esm/components/MediaWidget/MediaWidget.js +2 -2
- package/esm/components/MediaWidget/MediaWidget.js.map +1 -1
- package/esm/components/MemoriWidget/MemoriWidget.d.ts +5 -3
- package/esm/components/MemoriWidget/MemoriWidget.js +56 -17
- package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
- package/esm/components/Typing/Typing.d.ts +7 -0
- package/esm/components/Typing/Typing.js +44 -0
- package/esm/components/Typing/Typing.js.map +1 -0
- package/esm/components/Typing/Typing.test.d.ts +1 -0
- package/esm/components/Typing/Typing.test.js +20 -0
- package/esm/components/Typing/Typing.test.js.map +1 -0
- package/esm/index.d.ts +1 -0
- package/esm/index.js +3 -3
- package/esm/index.js.map +1 -1
- package/package.json +10 -10
- package/src/components/Chat/Chat.css +3 -3
- package/src/components/Chat/Chat.tsx +22 -13
- package/src/components/Chat/__snapshots__/Chat.test.tsx.snap +14 -10
- package/src/components/ChatBubble/ChatBubble.css +10 -5
- package/src/components/MediaWidget/MediaItemWidget.tsx +2 -17
- package/src/components/MediaWidget/MediaWidget.tsx +2 -2
- package/src/components/MemoriWidget/MemoriWidget.tsx +76 -16
- package/src/components/Typing/Typing.stories.tsx +55 -0
- package/src/components/Typing/Typing.test.tsx +24 -0
- package/src/components/Typing/Typing.tsx +70 -0
- package/src/components/Typing/__snapshots__/Typing.test.tsx.snap +106 -0
- package/src/components/layouts/layouts.stories.tsx +1 -1
- package/src/index.tsx +4 -1
|
@@ -3506,17 +3506,21 @@ exports[`renders Chat with memori typing unchanged 1`] = `
|
|
|
3506
3506
|
class="memori-chat--bubble"
|
|
3507
3507
|
>
|
|
3508
3508
|
<div
|
|
3509
|
-
|
|
3509
|
+
class="memori-chat--bubble-typing"
|
|
3510
3510
|
>
|
|
3511
|
-
<
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3511
|
+
<div
|
|
3512
|
+
id="wave"
|
|
3513
|
+
>
|
|
3514
|
+
<span
|
|
3515
|
+
class="dot"
|
|
3516
|
+
/>
|
|
3517
|
+
<span
|
|
3518
|
+
class="dot"
|
|
3519
|
+
/>
|
|
3520
|
+
<span
|
|
3521
|
+
class="dot"
|
|
3522
|
+
/>
|
|
3523
|
+
</div>
|
|
3520
3524
|
</div>
|
|
3521
3525
|
</div>
|
|
3522
3526
|
<div
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
|
|
36
36
|
.memori-chat--bubble #wave {
|
|
37
37
|
position: relative;
|
|
38
|
-
width:
|
|
38
|
+
width: 50px;
|
|
39
39
|
margin-right: auto;
|
|
40
40
|
margin-left: auto;
|
|
41
41
|
text-align: center;
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
|
|
44
44
|
.memori-chat--bubble #wave .dot {
|
|
45
45
|
display: inline-block;
|
|
46
|
-
width:
|
|
47
|
-
height:
|
|
46
|
+
width: 6px;
|
|
47
|
+
height: 6px;
|
|
48
48
|
border-radius: 50%;
|
|
49
49
|
margin-right: 3px;
|
|
50
50
|
animation: wave 1.5s linear infinite;
|
|
@@ -59,12 +59,16 @@
|
|
|
59
59
|
animation-delay: -1.1s;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
.memori-chat--bubble-typing {
|
|
63
|
+
display: flex;
|
|
64
|
+
}
|
|
65
|
+
|
|
62
66
|
.memori-chat--bubble p {
|
|
63
67
|
margin-top: 0;
|
|
64
68
|
margin-bottom: 0;
|
|
65
69
|
}
|
|
66
70
|
|
|
67
|
-
.memori-chat--bubble p
|
|
71
|
+
.memori-chat--bubble p+p {
|
|
68
72
|
margin-top: 0.5em;
|
|
69
73
|
}
|
|
70
74
|
|
|
@@ -215,6 +219,7 @@
|
|
|
215
219
|
}
|
|
216
220
|
|
|
217
221
|
@keyframes wave {
|
|
222
|
+
|
|
218
223
|
0%,
|
|
219
224
|
60%,
|
|
220
225
|
100% {
|
|
@@ -224,4 +229,4 @@
|
|
|
224
229
|
30% {
|
|
225
230
|
transform: translateY(-10px);
|
|
226
231
|
}
|
|
227
|
-
}
|
|
232
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Medium } from '@memori.ai/memori-api-client/dist/types';
|
|
2
|
-
import React, { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import React, { useCallback, useEffect, useState, memo } from 'react';
|
|
3
3
|
import { getResourceUrl } from '../../helpers/media';
|
|
4
4
|
import { getTranslation } from '../../helpers/translations';
|
|
5
5
|
import { prismSyntaxLangs } from '../../helpers/constants';
|
|
@@ -285,21 +285,6 @@ const MediaItemWidget: React.FC<Props> = ({
|
|
|
285
285
|
if (translateTo) translateMediaCaptions();
|
|
286
286
|
}, [translateTo, translateMediaCaptions]);
|
|
287
287
|
|
|
288
|
-
useEffect(() => {
|
|
289
|
-
let executableScripts = items.filter(
|
|
290
|
-
m => m.mimeType === 'text/javascript' && !!m.properties?.executable
|
|
291
|
-
);
|
|
292
|
-
executableScripts.forEach(s => {
|
|
293
|
-
try {
|
|
294
|
-
// eslint-disable-next-line no-new-func
|
|
295
|
-
new Function(s.content ?? '')();
|
|
296
|
-
} catch (e) {
|
|
297
|
-
console.error(e);
|
|
298
|
-
}
|
|
299
|
-
});
|
|
300
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
301
|
-
}, []);
|
|
302
|
-
|
|
303
288
|
return (
|
|
304
289
|
<Transition appear show as="div" className="memori-media-items">
|
|
305
290
|
<div className="memori-media-items--grid">
|
|
@@ -407,4 +392,4 @@ const MediaItemWidget: React.FC<Props> = ({
|
|
|
407
392
|
);
|
|
408
393
|
};
|
|
409
394
|
|
|
410
|
-
export default MediaItemWidget;
|
|
395
|
+
export default memo(MediaItemWidget);
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
Medium,
|
|
3
3
|
TranslatedHint,
|
|
4
4
|
} from '@memori.ai/memori-api-client/dist/types';
|
|
5
|
-
import React, { useEffect, useState } from 'react';
|
|
5
|
+
import React, { useEffect, useState, memo } from 'react';
|
|
6
6
|
import Button from '../ui/Button';
|
|
7
7
|
import LinkItemWidget from './LinkItemWidget';
|
|
8
8
|
import MediaItemWidget, { Props as MediaItemProps } from './MediaItemWidget';
|
|
@@ -103,4 +103,4 @@ const MediaWidget: React.FC<Props> = ({
|
|
|
103
103
|
);
|
|
104
104
|
};
|
|
105
105
|
|
|
106
|
-
export default MediaWidget;
|
|
106
|
+
export default memo(MediaWidget);
|
|
@@ -98,25 +98,31 @@ type MemoriTextEnteredEvent = CustomEvent<{
|
|
|
98
98
|
text: string;
|
|
99
99
|
waitForPrevious?: boolean;
|
|
100
100
|
hidden?: boolean;
|
|
101
|
+
typingText?: string;
|
|
101
102
|
}>;
|
|
102
103
|
|
|
103
104
|
const typeMessage = (
|
|
104
105
|
message: string,
|
|
105
106
|
waitForPrevious = true,
|
|
106
|
-
hidden = false
|
|
107
|
+
hidden = false,
|
|
108
|
+
typingText?: string
|
|
107
109
|
) => {
|
|
108
110
|
const e: MemoriTextEnteredEvent = new CustomEvent('MemoriTextEntered', {
|
|
109
111
|
detail: {
|
|
110
112
|
text: message,
|
|
111
113
|
waitForPrevious,
|
|
112
114
|
hidden,
|
|
115
|
+
typingText,
|
|
113
116
|
},
|
|
114
117
|
});
|
|
115
118
|
|
|
116
119
|
document.dispatchEvent(e);
|
|
117
120
|
};
|
|
118
|
-
const typeMessageHidden = (
|
|
119
|
-
|
|
121
|
+
const typeMessageHidden = (
|
|
122
|
+
message: string,
|
|
123
|
+
waitForPrevious = true,
|
|
124
|
+
typingText?: string
|
|
125
|
+
) => typeMessage(message, waitForPrevious, true, typingText);
|
|
120
126
|
|
|
121
127
|
interface CustomEventMap {
|
|
122
128
|
MemoriTextEntered: MemoriTextEnteredEvent;
|
|
@@ -189,6 +195,7 @@ export interface Props {
|
|
|
189
195
|
showDates?: boolean;
|
|
190
196
|
showContextPerLine?: boolean;
|
|
191
197
|
showSettings?: boolean;
|
|
198
|
+
showTypingText?: boolean;
|
|
192
199
|
preview?: boolean;
|
|
193
200
|
embed?: boolean;
|
|
194
201
|
height?: number | string;
|
|
@@ -229,6 +236,7 @@ const MemoriWidget = ({
|
|
|
229
236
|
showDates = false,
|
|
230
237
|
showContextPerLine = false,
|
|
231
238
|
showSettings = true,
|
|
239
|
+
showTypingText = false,
|
|
232
240
|
height = '100vh',
|
|
233
241
|
secret,
|
|
234
242
|
baseUrl = 'https://app.twincreator.com',
|
|
@@ -293,7 +301,7 @@ const MemoriWidget = ({
|
|
|
293
301
|
);
|
|
294
302
|
|
|
295
303
|
const [loading, setLoading] = useState(false);
|
|
296
|
-
const [memoriTyping, setMemoriTyping] = useState(false);
|
|
304
|
+
const [memoriTyping, setMemoriTyping] = useState<boolean | string>(false);
|
|
297
305
|
|
|
298
306
|
const selectedLayout = layout || integrationConfig?.layout || 'DEFAULT';
|
|
299
307
|
|
|
@@ -400,7 +408,17 @@ const MemoriWidget = ({
|
|
|
400
408
|
const [listening, setListening] = useState(false);
|
|
401
409
|
const [history, setHistory] = useState<Message[]>([]);
|
|
402
410
|
const pushMessage = (message: Message) => {
|
|
403
|
-
setHistory(history => [
|
|
411
|
+
setHistory(history => [
|
|
412
|
+
...history,
|
|
413
|
+
{
|
|
414
|
+
...message,
|
|
415
|
+
media:
|
|
416
|
+
message.media?.filter(
|
|
417
|
+
m =>
|
|
418
|
+
!(m.mimeType === 'text/javascript' && !!m.properties?.executable)
|
|
419
|
+
) ?? [],
|
|
420
|
+
},
|
|
421
|
+
]);
|
|
404
422
|
};
|
|
405
423
|
const sendMessage = async (
|
|
406
424
|
text: string,
|
|
@@ -408,7 +426,8 @@ const MemoriWidget = ({
|
|
|
408
426
|
newSessionId?: string,
|
|
409
427
|
translate: boolean = true,
|
|
410
428
|
translatedText?: string,
|
|
411
|
-
hidden: boolean = false
|
|
429
|
+
hidden: boolean = false,
|
|
430
|
+
typingText?: string
|
|
412
431
|
) => {
|
|
413
432
|
const sessionID =
|
|
414
433
|
newSessionId ||
|
|
@@ -427,7 +446,7 @@ const MemoriWidget = ({
|
|
|
427
446
|
: !!newSessionId,
|
|
428
447
|
});
|
|
429
448
|
|
|
430
|
-
setMemoriTyping(true);
|
|
449
|
+
setMemoriTyping(typingText ?? true);
|
|
431
450
|
|
|
432
451
|
let msg = text;
|
|
433
452
|
|
|
@@ -654,6 +673,38 @@ const MemoriWidget = ({
|
|
|
654
673
|
if (onStateChange) {
|
|
655
674
|
onStateChange(state);
|
|
656
675
|
}
|
|
676
|
+
|
|
677
|
+
const executableSnippets = state?.media?.filter(
|
|
678
|
+
m => m.mimeType === 'text/javascript' && !!m.properties?.executable
|
|
679
|
+
);
|
|
680
|
+
executableSnippets?.forEach(s => {
|
|
681
|
+
try {
|
|
682
|
+
setTimeout(() => {
|
|
683
|
+
console.log('snippet', s);
|
|
684
|
+
// eslint-disable-next-line no-new-func
|
|
685
|
+
new Function(s.content ?? '')();
|
|
686
|
+
|
|
687
|
+
setTimeout(() => {
|
|
688
|
+
if (document.body.classList.contains('chat-focused')) {
|
|
689
|
+
document.querySelector('.ant-tabs-nav')?.scrollIntoView();
|
|
690
|
+
document
|
|
691
|
+
.querySelector('.memori-chat--content')
|
|
692
|
+
?.scrollTo(
|
|
693
|
+
0,
|
|
694
|
+
document.querySelector('.memori-chat--content')
|
|
695
|
+
?.scrollHeight ?? 0
|
|
696
|
+
);
|
|
697
|
+
} else {
|
|
698
|
+
document.querySelector('#end-messages-ref')?.scrollIntoView({
|
|
699
|
+
behavior: 'smooth',
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
}, 400);
|
|
703
|
+
}, 3000);
|
|
704
|
+
} catch (e) {
|
|
705
|
+
console.error(e);
|
|
706
|
+
}
|
|
707
|
+
});
|
|
657
708
|
};
|
|
658
709
|
const fetchSession = async (
|
|
659
710
|
params: OpenSession
|
|
@@ -705,6 +756,7 @@ const MemoriWidget = ({
|
|
|
705
756
|
...params,
|
|
706
757
|
additionalInfo: {
|
|
707
758
|
...(additionalInfo || {}),
|
|
759
|
+
loginToken: additionalInfo?.loginToken ?? authToken,
|
|
708
760
|
language: getCultureCodeByLanguage(userLang),
|
|
709
761
|
referral: referral,
|
|
710
762
|
},
|
|
@@ -792,6 +844,7 @@ const MemoriWidget = ({
|
|
|
792
844
|
birthDate: birthDate || storageBirthDate || undefined,
|
|
793
845
|
additionalInfo: {
|
|
794
846
|
...(additionalInfo || {}),
|
|
847
|
+
loginToken: additionalInfo?.loginToken ?? authToken,
|
|
795
848
|
language: getCultureCodeByLanguage(userLang),
|
|
796
849
|
referral: referral,
|
|
797
850
|
},
|
|
@@ -912,6 +965,7 @@ const MemoriWidget = ({
|
|
|
912
965
|
birthDate: birthDate || storageBirthDate || undefined,
|
|
913
966
|
additionalInfo: {
|
|
914
967
|
...(additionalInfo || {}),
|
|
968
|
+
loginToken: additionalInfo?.loginToken ?? authToken,
|
|
915
969
|
language: getCultureCodeByLanguage(userLang),
|
|
916
970
|
referral: referral,
|
|
917
971
|
},
|
|
@@ -987,7 +1041,7 @@ const MemoriWidget = ({
|
|
|
987
1041
|
!!speechSynthesizer ||
|
|
988
1042
|
isPlayingAudio ||
|
|
989
1043
|
!!userMessage.length ||
|
|
990
|
-
memoriTyping ||
|
|
1044
|
+
!!memoriTyping ||
|
|
991
1045
|
listening
|
|
992
1046
|
) {
|
|
993
1047
|
resetInteractionTimeout();
|
|
@@ -1053,7 +1107,7 @@ const MemoriWidget = ({
|
|
|
1053
1107
|
timeoutRef.current = uiTimeout;
|
|
1054
1108
|
};
|
|
1055
1109
|
useEffect(() => {
|
|
1056
|
-
if (!!userMessage.length || isPlayingAudio || memoriTyping)
|
|
1110
|
+
if (!!userMessage.length || isPlayingAudio || !!memoriTyping)
|
|
1057
1111
|
clearInteractionTimeout();
|
|
1058
1112
|
if (sessionId && !!!userMessage.length) resetInteractionTimeout();
|
|
1059
1113
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -1954,17 +2008,14 @@ const MemoriWidget = ({
|
|
|
1954
2008
|
// to use in integrations or snippets
|
|
1955
2009
|
const memoriTextEnteredHandler = useCallback(
|
|
1956
2010
|
(e: MemoriTextEnteredEvent) => {
|
|
1957
|
-
const { text, waitForPrevious, hidden } = e.detail;
|
|
1958
|
-
|
|
1959
|
-
const sessionID =
|
|
1960
|
-
sessionId || (window.getMemoriState() as MemoriSession)?.sessionID;
|
|
2011
|
+
const { text, waitForPrevious, hidden, typingText } = e.detail;
|
|
1961
2012
|
|
|
1962
2013
|
if (text) {
|
|
1963
2014
|
// wait to finish reading previous emission
|
|
1964
2015
|
if (
|
|
1965
2016
|
waitForPrevious &&
|
|
1966
2017
|
!speakerMuted &&
|
|
1967
|
-
(memoriSpeaking || memoriTyping)
|
|
2018
|
+
(memoriSpeaking || !!memoriTyping)
|
|
1968
2019
|
) {
|
|
1969
2020
|
setTimeout(() => {
|
|
1970
2021
|
memoriTextEnteredHandler(e);
|
|
@@ -1972,7 +2023,15 @@ const MemoriWidget = ({
|
|
|
1972
2023
|
} else {
|
|
1973
2024
|
stopListening();
|
|
1974
2025
|
stopAudio();
|
|
1975
|
-
sendMessage(
|
|
2026
|
+
sendMessage(
|
|
2027
|
+
text,
|
|
2028
|
+
undefined,
|
|
2029
|
+
undefined,
|
|
2030
|
+
undefined,
|
|
2031
|
+
undefined,
|
|
2032
|
+
hidden,
|
|
2033
|
+
typingText
|
|
2034
|
+
);
|
|
1976
2035
|
}
|
|
1977
2036
|
}
|
|
1978
2037
|
},
|
|
@@ -2328,7 +2387,7 @@ const MemoriWidget = ({
|
|
|
2328
2387
|
setAvatar3dVisible,
|
|
2329
2388
|
hasUserActivatedSpeak,
|
|
2330
2389
|
isPlayingAudio,
|
|
2331
|
-
loading: memoriTyping,
|
|
2390
|
+
loading: !!memoriTyping,
|
|
2332
2391
|
baseUrl,
|
|
2333
2392
|
apiUrl,
|
|
2334
2393
|
};
|
|
@@ -2369,6 +2428,7 @@ const MemoriWidget = ({
|
|
|
2369
2428
|
baseUrl,
|
|
2370
2429
|
apiUrl,
|
|
2371
2430
|
memoriTyping,
|
|
2431
|
+
showTypingText,
|
|
2372
2432
|
history: layout === 'TOTEM' ? history.slice(-2) : history,
|
|
2373
2433
|
authToken: loginToken,
|
|
2374
2434
|
dialogState: currentDialogState,
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Meta, Story } from '@storybook/react';
|
|
3
|
+
import Typing, { Props } from './Typing';
|
|
4
|
+
|
|
5
|
+
const meta: Meta = {
|
|
6
|
+
title: 'Widget/Typing',
|
|
7
|
+
component: Typing,
|
|
8
|
+
argTypes: {
|
|
9
|
+
useDefaultSentences: {
|
|
10
|
+
control: {
|
|
11
|
+
type: 'boolean',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
lang: {
|
|
15
|
+
control: {
|
|
16
|
+
type: 'select',
|
|
17
|
+
options: ['en', 'it'],
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
sentence: {
|
|
21
|
+
control: {
|
|
22
|
+
type: 'text',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
parameters: {
|
|
27
|
+
controls: { expanded: false },
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default meta;
|
|
32
|
+
|
|
33
|
+
const Template: Story<Props> = args => <Typing {...args} />;
|
|
34
|
+
|
|
35
|
+
// By passing using the Args format for exported stories, you can control the props for a component for reuse in a test
|
|
36
|
+
// https://storybook.js.org/docs/react/workflows/unit-testing
|
|
37
|
+
export const Default = Template.bind({});
|
|
38
|
+
Default.args = {};
|
|
39
|
+
|
|
40
|
+
export const WithLoadingTextIT = Template.bind({});
|
|
41
|
+
WithLoadingTextIT.args = {
|
|
42
|
+
useDefaultSentences: true,
|
|
43
|
+
lang: 'it',
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const WithLoadingTextEN = Template.bind({});
|
|
47
|
+
WithLoadingTextEN.args = {
|
|
48
|
+
useDefaultSentences: true,
|
|
49
|
+
lang: 'en',
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const WithCustomLoadingText = Template.bind({});
|
|
53
|
+
WithCustomLoadingText.args = {
|
|
54
|
+
sentence: 'Chiedo agli unicorni cosa ne pensano...',
|
|
55
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { render } from '@testing-library/react';
|
|
2
|
+
import Typing from './Typing';
|
|
3
|
+
|
|
4
|
+
it('renders Typing unchanged', () => {
|
|
5
|
+
const { container } = render(<Typing />);
|
|
6
|
+
expect(container).toMatchSnapshot();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('renders Typing with default loading text unchanged', () => {
|
|
10
|
+
const { container } = render(<Typing useDefaultSentences lang="en" />);
|
|
11
|
+
expect(container).toMatchSnapshot();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('renders Typing with default italian loading text unchanged', () => {
|
|
15
|
+
const { container } = render(<Typing useDefaultSentences lang="it" />);
|
|
16
|
+
expect(container).toMatchSnapshot();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('renders Typing with custom loading text unchanged', () => {
|
|
20
|
+
const { container } = render(
|
|
21
|
+
<Typing sentence="Chiedo agli unicorni cosa ne pensano..." />
|
|
22
|
+
);
|
|
23
|
+
expect(container).toMatchSnapshot();
|
|
24
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
const separator = ' ';
|
|
4
|
+
const sentences = {
|
|
5
|
+
en: [
|
|
6
|
+
'Generating an accurate and fancy response...',
|
|
7
|
+
'Generating a response that will blow your mind...',
|
|
8
|
+
'Generating a response that will make you smile...',
|
|
9
|
+
'Thinking of a response...',
|
|
10
|
+
'Thinking of a response that will make you smile...',
|
|
11
|
+
'Gathering my thoughts...',
|
|
12
|
+
'Gathering my thoughts to give you a response...',
|
|
13
|
+
],
|
|
14
|
+
it: [
|
|
15
|
+
'Sto generando una risposta accurata e fantasiosa...',
|
|
16
|
+
'Sto generando una risposta che ti farà impazzire...',
|
|
17
|
+
'Sto generando una risposta che ti farà sorridere...',
|
|
18
|
+
'Sto pensando ad una risposta...',
|
|
19
|
+
'Sto pensando ad una risposta che ti farà sorridere...',
|
|
20
|
+
'Sto raccogliendo i miei pensieri...',
|
|
21
|
+
'Sto raccogliendo i miei pensieri per darti una risposta...',
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export interface Props {
|
|
26
|
+
useDefaultSentences?: boolean;
|
|
27
|
+
lang?: 'en' | 'it';
|
|
28
|
+
sentence?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const Typing = ({
|
|
32
|
+
useDefaultSentences = false,
|
|
33
|
+
lang = 'en',
|
|
34
|
+
sentence,
|
|
35
|
+
}: Props) => {
|
|
36
|
+
const [text, setText] = useState(sentence ? `${sentence}${separator}` : '');
|
|
37
|
+
const [shownText, setShownText] = useState('');
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
const interval = setInterval(() => {
|
|
41
|
+
const letter = text[shownText.length];
|
|
42
|
+
if (letter !== undefined && text.length > 0) {
|
|
43
|
+
setShownText(prev => prev + letter);
|
|
44
|
+
} else if (!sentence && useDefaultSentences) {
|
|
45
|
+
setShownText('');
|
|
46
|
+
setText(
|
|
47
|
+
sentences[lang][Math.floor(Math.random() * sentences[lang].length)] +
|
|
48
|
+
separator
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}, 50);
|
|
52
|
+
|
|
53
|
+
return () => clearInterval(interval);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<div className="memori-chat--bubble">
|
|
58
|
+
<div className="memori-chat--bubble-typing">
|
|
59
|
+
<div id="wave">
|
|
60
|
+
<span className="dot"></span>
|
|
61
|
+
<span className="dot"></span>
|
|
62
|
+
<span className="dot"></span>
|
|
63
|
+
</div>
|
|
64
|
+
{text.length > 0 && <p>{shownText}</p>}
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export default Typing;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`renders Typing unchanged 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="memori-chat--bubble"
|
|
7
|
+
>
|
|
8
|
+
<div
|
|
9
|
+
class="memori-chat--bubble-typing"
|
|
10
|
+
>
|
|
11
|
+
<div
|
|
12
|
+
id="wave"
|
|
13
|
+
>
|
|
14
|
+
<span
|
|
15
|
+
class="dot"
|
|
16
|
+
/>
|
|
17
|
+
<span
|
|
18
|
+
class="dot"
|
|
19
|
+
/>
|
|
20
|
+
<span
|
|
21
|
+
class="dot"
|
|
22
|
+
/>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
exports[`renders Typing with custom loading text unchanged 1`] = `
|
|
30
|
+
<div>
|
|
31
|
+
<div
|
|
32
|
+
class="memori-chat--bubble"
|
|
33
|
+
>
|
|
34
|
+
<div
|
|
35
|
+
class="memori-chat--bubble-typing"
|
|
36
|
+
>
|
|
37
|
+
<div
|
|
38
|
+
id="wave"
|
|
39
|
+
>
|
|
40
|
+
<span
|
|
41
|
+
class="dot"
|
|
42
|
+
/>
|
|
43
|
+
<span
|
|
44
|
+
class="dot"
|
|
45
|
+
/>
|
|
46
|
+
<span
|
|
47
|
+
class="dot"
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
<p />
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
exports[`renders Typing with default italian loading text unchanged 1`] = `
|
|
57
|
+
<div>
|
|
58
|
+
<div
|
|
59
|
+
class="memori-chat--bubble"
|
|
60
|
+
>
|
|
61
|
+
<div
|
|
62
|
+
class="memori-chat--bubble-typing"
|
|
63
|
+
>
|
|
64
|
+
<div
|
|
65
|
+
id="wave"
|
|
66
|
+
>
|
|
67
|
+
<span
|
|
68
|
+
class="dot"
|
|
69
|
+
/>
|
|
70
|
+
<span
|
|
71
|
+
class="dot"
|
|
72
|
+
/>
|
|
73
|
+
<span
|
|
74
|
+
class="dot"
|
|
75
|
+
/>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
`;
|
|
81
|
+
|
|
82
|
+
exports[`renders Typing with default loading text unchanged 1`] = `
|
|
83
|
+
<div>
|
|
84
|
+
<div
|
|
85
|
+
class="memori-chat--bubble"
|
|
86
|
+
>
|
|
87
|
+
<div
|
|
88
|
+
class="memori-chat--bubble-typing"
|
|
89
|
+
>
|
|
90
|
+
<div
|
|
91
|
+
id="wave"
|
|
92
|
+
>
|
|
93
|
+
<span
|
|
94
|
+
class="dot"
|
|
95
|
+
/>
|
|
96
|
+
<span
|
|
97
|
+
class="dot"
|
|
98
|
+
/>
|
|
99
|
+
<span
|
|
100
|
+
class="dot"
|
|
101
|
+
/>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
`;
|
package/src/index.tsx
CHANGED
|
@@ -26,6 +26,7 @@ export interface Props {
|
|
|
26
26
|
showShare?: boolean;
|
|
27
27
|
showSettings?: boolean;
|
|
28
28
|
showInstruct?: boolean;
|
|
29
|
+
showTypingText?: boolean;
|
|
29
30
|
height?: number | string;
|
|
30
31
|
baseURL?: string;
|
|
31
32
|
apiURL?: string;
|
|
@@ -75,6 +76,7 @@ const Memori: React.FC<Props> = ({
|
|
|
75
76
|
showShare = true,
|
|
76
77
|
showSettings = true,
|
|
77
78
|
showInstruct = false,
|
|
79
|
+
showTypingText = false,
|
|
78
80
|
height = '100%',
|
|
79
81
|
baseURL,
|
|
80
82
|
apiURL = 'https://backend.memori.ai',
|
|
@@ -204,6 +206,7 @@ const Memori: React.FC<Props> = ({
|
|
|
204
206
|
showShare={showShare}
|
|
205
207
|
showSettings={showSettings}
|
|
206
208
|
showInstruct={showInstruct}
|
|
209
|
+
showTypingText={showTypingText}
|
|
207
210
|
integration={memori?.integrations?.find(i =>
|
|
208
211
|
integrationID
|
|
209
212
|
? i.integrationID === integrationID
|
|
@@ -248,7 +251,7 @@ Memori.propTypes = {
|
|
|
248
251
|
showShare: PropTypes.bool,
|
|
249
252
|
showSettings: PropTypes.bool,
|
|
250
253
|
showInstruct: PropTypes.bool,
|
|
251
|
-
layout: PropTypes.oneOf(['DEFAULT', 'FULLPAGE', 'TOTEM']),
|
|
254
|
+
layout: PropTypes.oneOf(['DEFAULT', 'FULLPAGE', 'TOTEM', 'CHAT']),
|
|
252
255
|
height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
253
256
|
baseURL: PropTypes.string,
|
|
254
257
|
apiURL: PropTypes.string,
|