@seamly/web-ui 24.3.1 → 24.4.1
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/build/dist/lib/index.debug.js +117 -64
- package/build/dist/lib/index.debug.js.map +1 -1
- package/build/dist/lib/index.debug.min.js +1 -1
- package/build/dist/lib/index.debug.min.js.map +1 -1
- package/build/dist/lib/index.js +61 -12
- package/build/dist/lib/index.js.map +1 -1
- package/build/dist/lib/index.min.js +1 -1
- package/build/dist/lib/index.min.js.map +1 -1
- package/build/dist/lib/styles.css +1 -1
- package/package.json +11 -12
- package/src/javascripts/core/domains/store/slice.ts +26 -4
- package/src/javascripts/core/ui/components/conversation/event/choice-prompt.tsx +7 -0
- package/src/javascripts/core/ui/components/conversation/event/conversation-suggestions.tsx +15 -1
- package/src/javascripts/core/ui/components/conversation/event/upload.tsx +2 -1
- package/src/javascripts/core/ui/components/entry/entry-container.tsx +2 -6
- package/src/javascripts/core/ui/components/entry/text-entry/hooks.ts +4 -2
- package/src/javascripts/core/ui/components/entry/upload/index.tsx +19 -0
- package/src/javascripts/core/ui/components/suggestions/index.tsx +8 -0
- package/src/stylesheets/5-components/_message-body.scss +2 -0
package/build/dist/lib/index.js
CHANGED
|
@@ -5427,7 +5427,7 @@ class API {
|
|
|
5427
5427
|
return {
|
|
5428
5428
|
clientName: "@seamly/web-ui",
|
|
5429
5429
|
clientVariant: this.#layoutMode,
|
|
5430
|
-
clientVersion: "24.
|
|
5430
|
+
clientVersion: "24.4.1",
|
|
5431
5431
|
currentUrl: window.location.toString(),
|
|
5432
5432
|
screenResolution: `${window.screen.width}x${window.screen.height}`,
|
|
5433
5433
|
timezone: getTimeZone(),
|
|
@@ -12003,6 +12003,10 @@ const participantReducer = (participantInfo, action) => {
|
|
|
12003
12003
|
};
|
|
12004
12004
|
};
|
|
12005
12005
|
const calculateNewEntryMeta = (entryMeta, channelEvent) => {
|
|
12006
|
+
// Events originating from the client should leave the entry meta as is
|
|
12007
|
+
if (channelEvent?.payload?.fromClient === true) {
|
|
12008
|
+
return entryMeta;
|
|
12009
|
+
}
|
|
12006
12010
|
const entry = channelEvent?.type === 'message' ? channelEvent?.payload.translatedEntry || channelEvent?.payload.entry : {
|
|
12007
12011
|
options: undefined,
|
|
12008
12012
|
type: undefined
|
|
@@ -12224,7 +12228,7 @@ const storeSlice = createSlice({
|
|
|
12224
12228
|
// Use the messages from the payload, as `events` only contains displayable ones.
|
|
12225
12229
|
// The first participant message found is the 'last', as we receive them from newest to oldest.
|
|
12226
12230
|
const lastParticipantEvent = messages.find(m => m.type === 'participant');
|
|
12227
|
-
// @ts-
|
|
12231
|
+
// @ts-expect-error TypeScript incorrectly assumes that the payload can be any of info/message/participant
|
|
12228
12232
|
const lastParticipantId = lastParticipantEvent?.payload?.participant?.id;
|
|
12229
12233
|
const {
|
|
12230
12234
|
entry
|
|
@@ -12238,10 +12242,25 @@ const storeSlice = createSlice({
|
|
|
12238
12242
|
...(entry?.options ? entry.options : {})
|
|
12239
12243
|
}
|
|
12240
12244
|
}, events[events.length - 1]);
|
|
12241
|
-
|
|
12245
|
+
let newFeatures = {
|
|
12242
12246
|
...state.options.features
|
|
12243
12247
|
};
|
|
12248
|
+
// The first service message found is the 'last', as we receive them from newest to oldest.
|
|
12249
|
+
const lastServiceMessage = messages.find(m => !m.payload.fromClient && ['message', 'participant'].includes(m.type));
|
|
12244
12250
|
const newFeaturesHasUpload = newFeatures.hasOwnProperty(featureKeys.uploads);
|
|
12251
|
+
|
|
12252
|
+
// Check for upload enabled by entry type
|
|
12253
|
+
if (newFeaturesHasUpload && lastServiceMessage?.type === 'message') {
|
|
12254
|
+
// @ts-expect-error TypeScript incorrectly assumes that the payload can be any of info/message/participant
|
|
12255
|
+
const entryType = lastServiceMessage.payload.entry?.type || '';
|
|
12256
|
+
newFeatures = {
|
|
12257
|
+
...newFeatures,
|
|
12258
|
+
uploads: {
|
|
12259
|
+
enabled: newFeatures.uploads?.enabled || false,
|
|
12260
|
+
enabledFromEntry: entryType === entryTypes.upload
|
|
12261
|
+
}
|
|
12262
|
+
};
|
|
12263
|
+
}
|
|
12245
12264
|
state.unreadEvents = unreadMessageCount;
|
|
12246
12265
|
state.userHasResponded = userResponded;
|
|
12247
12266
|
state.events = events.filter(e => e.type !== 'participant' || !!e.payload.participant?.introduction);
|
|
@@ -15361,6 +15380,7 @@ const CarouselMessage = ({
|
|
|
15361
15380
|
|
|
15362
15381
|
|
|
15363
15382
|
|
|
15383
|
+
|
|
15364
15384
|
const useChoicePrompt = event => {
|
|
15365
15385
|
const {
|
|
15366
15386
|
payload
|
|
@@ -15371,6 +15391,7 @@ const useChoicePrompt = event => {
|
|
|
15371
15391
|
emitEvent,
|
|
15372
15392
|
sendAction
|
|
15373
15393
|
} = use_seamly_commands();
|
|
15394
|
+
const hasConversation = useSeamlyHasConversation();
|
|
15374
15395
|
const {
|
|
15375
15396
|
activeServiceSessionId
|
|
15376
15397
|
} = useSeamlyServiceInfo();
|
|
@@ -15400,6 +15421,10 @@ const useChoicePrompt = event => {
|
|
|
15400
15421
|
setShowOptions(payload.id === lastEventId);
|
|
15401
15422
|
}, [payload, lastEventId]);
|
|
15402
15423
|
const onChoiceClickHandler = choice => {
|
|
15424
|
+
// Do not allow interaction without a conversation
|
|
15425
|
+
if (!hasConversation()) {
|
|
15426
|
+
return;
|
|
15427
|
+
}
|
|
15403
15428
|
const transactionId = randomId();
|
|
15404
15429
|
const action = {
|
|
15405
15430
|
type: actionTypes.pickChoice,
|
|
@@ -15575,6 +15600,7 @@ const SuggestionsList = ({
|
|
|
15575
15600
|
|
|
15576
15601
|
|
|
15577
15602
|
|
|
15603
|
+
|
|
15578
15604
|
const useSuggestions = event => {
|
|
15579
15605
|
const {
|
|
15580
15606
|
payload
|
|
@@ -15599,6 +15625,7 @@ const ConversationSuggestions = ({
|
|
|
15599
15625
|
emitEvent,
|
|
15600
15626
|
sendAction
|
|
15601
15627
|
} = use_seamly_commands();
|
|
15628
|
+
const hasConversation = useSeamlyHasConversation();
|
|
15602
15629
|
const {
|
|
15603
15630
|
suggestions,
|
|
15604
15631
|
payload
|
|
@@ -15623,6 +15650,10 @@ const ConversationSuggestions = ({
|
|
|
15623
15650
|
id,
|
|
15624
15651
|
question
|
|
15625
15652
|
}) => {
|
|
15653
|
+
// Do not allow interaction without a conversation
|
|
15654
|
+
if (!hasConversation()) {
|
|
15655
|
+
return;
|
|
15656
|
+
}
|
|
15626
15657
|
setIsExpanded(false);
|
|
15627
15658
|
dispatch(setHasResponded(true));
|
|
15628
15659
|
const transactionId = randomId();
|
|
@@ -15643,7 +15674,7 @@ const ConversationSuggestions = ({
|
|
|
15643
15674
|
sendAction(action);
|
|
15644
15675
|
addMessageBubble(question, transactionId);
|
|
15645
15676
|
emitEvent(`action.${action.type}`, action);
|
|
15646
|
-
}, [addMessageBubble, dispatch, emitEvent, payload.id, sendAction]);
|
|
15677
|
+
}, [addMessageBubble, dispatch, emitEvent, hasConversation, payload.id, sendAction]);
|
|
15647
15678
|
if (!isExpanded || userHasResponded || !hasLastTransactionEvent || !showSuggestions) {
|
|
15648
15679
|
return null;
|
|
15649
15680
|
}
|
|
@@ -18011,8 +18042,9 @@ const UploadContent = ({
|
|
|
18011
18042
|
}) => {
|
|
18012
18043
|
if (fileId && !isDeleted) {
|
|
18013
18044
|
return /*#__PURE__*/(0,jsx_runtime_namespaceObject.jsx)("button", {
|
|
18045
|
+
type: "button",
|
|
18014
18046
|
onClick: onClickHandler,
|
|
18015
|
-
className: css_className(['
|
|
18047
|
+
className: css_className(['download', 'download-link']),
|
|
18016
18048
|
children: children
|
|
18017
18049
|
});
|
|
18018
18050
|
}
|
|
@@ -19345,9 +19377,9 @@ function useCharacterLimit(controlName) {
|
|
|
19345
19377
|
const [{
|
|
19346
19378
|
value
|
|
19347
19379
|
}] = useFormControl(controlName);
|
|
19348
|
-
const remainingChars = limit && hasLimit && value ? limit - value.length : limit;
|
|
19380
|
+
const remainingChars = typeof limit === 'number' && hasLimit && value ? limit - value.length : limit;
|
|
19349
19381
|
const reachedCharacterWarning = hasLimit && typeof remainingChars == 'number' ? remainingChars <= maxCharacterWarningLimit : false;
|
|
19350
|
-
const reachedCharacterLimit = hasLimit && remainingChars ? remainingChars < 0 : false;
|
|
19382
|
+
const reachedCharacterLimit = hasLimit && typeof remainingChars === 'number' ? remainingChars < 0 : false;
|
|
19351
19383
|
(0,hooks_.useEffect)(() => {
|
|
19352
19384
|
validateLimit(reachedCharacterWarning, remainingChars);
|
|
19353
19385
|
}, [reachedCharacterWarning, remainingChars, validateLimit]);
|
|
@@ -19809,6 +19841,7 @@ const InOutTransition = ({
|
|
|
19809
19841
|
|
|
19810
19842
|
|
|
19811
19843
|
|
|
19844
|
+
|
|
19812
19845
|
|
|
19813
19846
|
|
|
19814
19847
|
const Suggestions = ({
|
|
@@ -19826,6 +19859,7 @@ const Suggestions = ({
|
|
|
19826
19859
|
emitEvent,
|
|
19827
19860
|
sendAction
|
|
19828
19861
|
} = use_seamly_commands();
|
|
19862
|
+
const hasConversation = useSeamlyHasConversation();
|
|
19829
19863
|
const {
|
|
19830
19864
|
isOpen,
|
|
19831
19865
|
setVisibility
|
|
@@ -19888,6 +19922,10 @@ const Suggestions = ({
|
|
|
19888
19922
|
id,
|
|
19889
19923
|
question
|
|
19890
19924
|
}) => {
|
|
19925
|
+
// Do not allow interaction without a conversation
|
|
19926
|
+
if (!hasConversation()) {
|
|
19927
|
+
return;
|
|
19928
|
+
}
|
|
19891
19929
|
if (hasCountdown) {
|
|
19892
19930
|
endCountdown(true);
|
|
19893
19931
|
}
|
|
@@ -19918,7 +19956,7 @@ const Suggestions = ({
|
|
|
19918
19956
|
});
|
|
19919
19957
|
}
|
|
19920
19958
|
focusSkiplinkTarget();
|
|
19921
|
-
}, [addMessageBubble, continueChat, endCountdown, emitEvent, focusSkiplinkTarget, hasCountdown, hasPrompt, isOpen, payload, sendAction, setVisibility]);
|
|
19959
|
+
}, [addMessageBubble, continueChat, endCountdown, emitEvent, focusSkiplinkTarget, hasConversation, hasCountdown, hasPrompt, isOpen, payload, sendAction, setVisibility]);
|
|
19922
19960
|
(0,hooks_.useEffect)(() => {
|
|
19923
19961
|
if (prevSuggestions.current !== suggestions && !hideSuggestions) {
|
|
19924
19962
|
if (hasSuggestions) {
|
|
@@ -22085,6 +22123,19 @@ const upload_Upload = () => {
|
|
|
22085
22123
|
}
|
|
22086
22124
|
prevIsComplete.current = isComplete;
|
|
22087
22125
|
}, [isUploading, isComplete, clearUploads, cancelEntrySelection, focusSkiplinkTarget, sendPolite, t]);
|
|
22126
|
+
|
|
22127
|
+
// Reset form when service no longer allows uploads
|
|
22128
|
+
(0,hooks_.useEffect)(() => {
|
|
22129
|
+
// If we are currently uploading, don't clear the uploads as that
|
|
22130
|
+
// may be confusing to the user.
|
|
22131
|
+
// When the upload completes and the user clicks the submit button,
|
|
22132
|
+
// there will be an error.
|
|
22133
|
+
if (!serviceAllowsUploads && !isUploading) {
|
|
22134
|
+
clearUploads();
|
|
22135
|
+
cancelEntrySelection();
|
|
22136
|
+
focusSkiplinkTarget();
|
|
22137
|
+
}
|
|
22138
|
+
}, [serviceAllowsUploads, isUploading, clearUploads, cancelEntrySelection, focusSkiplinkTarget]);
|
|
22088
22139
|
const handleSubmit = (0,hooks_.useCallback)(({
|
|
22089
22140
|
fileList
|
|
22090
22141
|
}) => {
|
|
@@ -22277,7 +22328,6 @@ const EntryContainer = () => {
|
|
|
22277
22328
|
upload: entry_upload
|
|
22278
22329
|
});
|
|
22279
22330
|
const [renderEntry, setRenderEntry] = (0,hooks_.useState)(() => activeEntry);
|
|
22280
|
-
const [renderEntryOptions, setRenderEntryOptions] = (0,hooks_.useState)(() => activeEntryOptions);
|
|
22281
22331
|
const config = useConfig();
|
|
22282
22332
|
const {
|
|
22283
22333
|
accountAllowsUploads
|
|
@@ -22308,11 +22358,10 @@ const EntryContainer = () => {
|
|
|
22308
22358
|
}, [hasCountdown, hasResumeConversationPrompt, focusFn]);
|
|
22309
22359
|
(0,hooks_.useEffect)(() => {
|
|
22310
22360
|
setRenderEntry(activeEntry);
|
|
22311
|
-
setRenderEntryOptions(activeEntryOptions);
|
|
22312
22361
|
// This focus action is required for auto entry changes. User driven
|
|
22313
22362
|
// changes should be handled in the originating components.
|
|
22314
22363
|
focusFn();
|
|
22315
|
-
}, [activeEntry,
|
|
22364
|
+
}, [activeEntry, focusFn, entryContainer]);
|
|
22316
22365
|
|
|
22317
22366
|
// Check if the active element is inside this container and save it.
|
|
22318
22367
|
containedFocus.current = !!(entryContainer.current && entryContainer.current.contains(document.activeElement));
|
|
@@ -22321,7 +22370,7 @@ const EntryContainer = () => {
|
|
|
22321
22370
|
// Once we do, this property should be moved to that component instead.
|
|
22322
22371
|
const {
|
|
22323
22372
|
allowManualInput = true
|
|
22324
|
-
} =
|
|
22373
|
+
} = activeEntryOptions;
|
|
22325
22374
|
return /*#__PURE__*/(0,jsx_runtime_namespaceObject.jsxs)("div", {
|
|
22326
22375
|
className: css_className('chat__entry'),
|
|
22327
22376
|
ref: entryContainer,
|