@defra/flood-webchat 1.0.3 → 1.0.5
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/dist/client.js +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/src/client/components/availability/availability.jsx +7 -11
- package/src/client/components/chat/chat.jsx +14 -9
- package/src/client/components/live-region.jsx +4 -2
- package/src/client/components/panel/panel-header.jsx +1 -1
- package/src/client/components/panel/panel.jsx +35 -40
- package/src/client/components/screens/end-chat.jsx +6 -3
- package/src/client/components/screens/request-chat.jsx +13 -9
- package/src/client/components/screens/settings.jsx +1 -1
- package/src/client/components/skip-link.jsx +1 -1
- package/src/client/hooks/useFocusedElements.js +8 -6
- package/src/client/index.jsx +22 -9
- package/src/client/store/{AppProvider.jsx → app/AppProvider.jsx} +34 -86
- package/src/client/store/{actions-map.js → app/actions-map.js} +14 -48
- package/src/client/store/{reducer.js → app/reducer.js} +1 -4
- package/src/client/store/sdk/SdkProvider.jsx +141 -0
- package/src/client/store/sdk/actions-map.js +45 -0
- package/src/client/store/sdk/reducer.js +21 -0
- package/src/client/store/sdk/useSdk.js +5 -0
- package/src/server/lib/client.js +2 -2
- package/src/client/store/useChatSdk.js +0 -37
- /package/src/client/store/{useApp.js → app/useApp.js} +0 -0
package/dist/client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{"use strict";var e={n:t=>{var a=t&&t.__esModule?()=>t.default:()=>t;return e.d(a,{a}),a},d:(t,a)=>{for(var n in a)e.o(a,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:a[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{init:()=>ee});const a=require("react");var n=e.n(a);const l=require("react-dom/client"),o=require("@nice-devone/nice-cxone-chat-web-sdk"),r=(...e)=>e.filter((e=>e&&"string"==typeof e)).join(" "),c=require("react-dom"),s=require("luxon"),i=require("html-react-parser");var u=e.n(i);const d=/(?:www|https?)[^\s]+/gi,m=e=>(e.match(d)&&(e=e.replace(d,(e=>`<a class="govuk-link" href="${e}" target="_blank" rel="noreferrer">${e.replace(/https?:\/\//gi,"")}</a>`))),u()(e)),g=e=>({id:e.id,text:e.messageContent?.text,createdAt:new Date(e.createdAt),user:e.authorEndUserIdentity?.fullName?.trim()||null,assignee:e.authorUser?.firstName||null,direction:e.direction}),h=e=>{const t=s.DateTime.local();t.setZone("Europe/London");let a=`Floodline webchat transcript, ${t.toFormat("HH:mm:ss a, dd LLLL yyyy")}\n\n`;for(const t of e){const{text:e,direction:n,user:l,assignee:o,createdAt:r}=t,c="inbound"===n?l:`${o} (Floodline adviser)`;a+=`[${s.DateTime.fromJSDate(new Date(r)).setZone("Europe/London").toFormat("HH:mm:ss a, dd LLLL yyyy")}] ${c}: \n${e}\n\n`}return a=a.replace(/<a\b[^>]*>/i,"").replace(/<\/a>/i,""),encodeURIComponent(a)},E="webchat_settings",w="webchat_customer_id",v="webchat_thread_id",y=4e3,b={SET_CHAT_VISIBILITY:(e,t)=>({...e,isChatOpen:t}),SET_AVAILABILITY:(e,t)=>({...e,availability:t}),SET_SETTINGS:(e,t)=>(t?window.localStorage.setItem(E,JSON.stringify(t)):window.localStorage.removeItem(E),{...e,settings:t}),SET_CUSTOMER_ID:(e,t)=>(t?window.localStorage.setItem(w,t):window.localStorage.removeItem(w),{...e,customerId:t}),SET_THREAD_ID:(e,t)=>(t?window.localStorage.setItem(v,t):window.localStorage.removeItem(v),{...e,threadId:t}),SET_THREAD:(e,t)=>({...e,thread:t}),SET_MESSAGE:(e,t)=>({...e,message:g(t),messages:[...e.messages,g(t)]}),SET_MESSAGES:(e,t)=>{return{...e,messages:(a=t,a.map((e=>g(e)))).reverse()};var a},SET_AGENT:(e,t)=>({...e,agent:t}),SET_AGENT_TYPING:(e,t)=>({...e,isAgentTyping:t}),SET_AGENT_STATUS:(e,t)=>({...e,agentStatus:t}),SET_UNSEEN_COUNT:(e,t)=>({...e,unseenCount:t}),SET_INSTIGATOR_ID:(e,t)=>({...e,instigatorId:t}),SET_LIVE_REGION_TEXT:(e,t)=>({...e,liveRegionText:t}),TOGGLE_IS_MOBILE:(e,t)=>({...e,isMobile:t}),TOGGLE_IS_KEYBOARD:(e,t)=>({...e,isKeyboard:t})},f={availability:null,customerId:null,threadId:null,thread:null,messages:[],unseenCount:0,instigatorId:null,agent:null,agentStatus:null,isAgentTyping:!1,isChatOpen:!1,isMobile:window.matchMedia("(max-width: 640px)").matches,isKeyboard:!1,settings:{audio:!0,scroll:!0}},p=(e,t)=>{const{type:a,payload:n}=t,l=b[a];return l?l.bind(void 0,e,n)():e},k=(0,a.createContext)(f),_=({sdk:e,availability:t,playSound:l,children:r})=>{const[c,s]=(0,a.useReducer)(p,f),i=e=>{s({type:"SET_AGENT",payload:e.detail.data.inboxAssignee}),s({type:"SET_AGENT_STATUS",payload:e.detail.data.contact.status}),s({type:"SET_UNSEEN_COUNT",payload:e.detail.data.contact.customerStatistics.unseenMessagesCount})},u=e=>{s({type:"SET_AGENT",payload:e.detail.data.inboxAssignee}),s({type:"SET_AGENT_STATUS",payload:e.detail.data.case.status})},d=()=>{s({type:"SET_AGENT_TYPING",payload:!0})},m=()=>{s({type:"SET_AGENT_TYPING",payload:!1}),s({type:"SET_LIVE_REGION_TEXT"})},g=e=>{s({type:"SET_MESSAGE",payload:e.detail.data.message}),s({type:"SET_AGENT_STATUS",payload:e.detail.data.case.status}),s({type:"SET_UNSEEN_COUNT",payload:e.detail.data.case.customerStatistics.unseenMessagesCount}),JSON.parse(window.localStorage.getItem(E)).audio&&"outbound"===e.detail.data.message.direction&&l&&l()},h=e=>{s({type:"SET_AGENT_STATUS",payload:e.detail.data.case.status})},y=e=>{s({type:"TOGGLE_IS_MOBILE",payload:e.matches})},b=()=>{s({type:"TOGGLE_IS_KEYBOARD",payload:!0})},_=()=>{s({type:"TOGGLE_IS_KEYBOARD",payload:!1})};(0,a.useEffect)((()=>(e.onChatEvent(o.ChatEvent.LIVECHAT_RECOVERED,i),e.onChatEvent(o.ChatEvent.MESSAGE_CREATED,g),e.onChatEvent(o.ChatEvent.AGENT_TYPING_STARTED,d),e.onChatEvent(o.ChatEvent.AGENT_TYPING_ENDED,m),e.onChatEvent(o.ChatEvent.ASSIGNED_AGENT_CHANGED,u),e.onChatEvent(o.ChatEvent.CONTACT_STATUS_CHANGED,h),window.matchMedia("(max-width: 640px)").addEventListener("change",y),window.addEventListener("keydown",b),window.addEventListener("pointerdown",_),()=>{window.removeEventListener("change",y),window.removeEventListener("keydown",b),window.removeEventListener("pointerdown",_)})),[e]),(0,a.useEffect)((()=>{s({type:"SET_AVAILABILITY",payload:t}),N(window.localStorage.getItem(w)),T(window.localStorage.getItem(v)),A(JSON.parse(window.localStorage.getItem(E))||c.settings)}),[]),(0,a.useEffect)((()=>{"#webchat"===window.location.hash&&S(!0);const e=()=>{"#webchat"===window.location.hash?S(!0):S(!1)};return window.addEventListener("popstate",e),()=>{window.removeEventListener("popstate",e)}}),[]);const S=e=>{s({type:"SET_CHAT_VISIBILITY",payload:e})},N=e=>{s({type:"SET_CUSTOMER_ID",payload:e})},T=e=>{s({type:"SET_THREAD_ID",payload:e})},C=e=>{s({type:"SET_THREAD",payload:e})},I=e=>{s({type:"SET_MESSAGES",payload:e})},A=e=>{s({type:"SET_SETTINGS",payload:e})},L=e=>{s({type:"SET_UNSEEN_COUNT",payload:e})},x=e=>{s({type:"SET_INSTIGATOR_ID",payload:e})},D=e=>{s({type:"SET_LIVE_REGION_TEXT",payload:e})},G=(0,a.useMemo)((()=>({...c,sdk:e,setSettings:A,setCustomerId:N,setThreadId:T,setThread:C,setMessages:I,setUnseenCount:L,setChatVisibility:S,setInstigatorId:x,setLiveRegionText:D,onLiveChatRecovered:i,onAssignedAgentChanged:u,onAgentTypingStarted:d,onAgentTypingEnded:m,onMessageCreated:g,onContactStatusChanged:h})));return n().createElement(k.Provider,{value:G},r)},S=()=>(0,a.useContext)(k),N=()=>{const e=window.location.href.split("#")[0];window.history.pushState({history:!0},null,`${e}#webchat`)},T=()=>{if(window.history.state?.history)return window.history.back();const e=window.location.href.split("#")[0];return window.history.replaceState(null,null,e)};function C(){const{thread:e,threadId:t,setChatVisibility:a,setUnseenCount:l,isMobile:o}=S(),r=n=>{n.preventDefault(),a(!1),T(),t&&(e?.lastMessageSeen(),l(0))},c=n().createElement("button",{className:"wc-header__back","aria-label":"Close the webchat",onClick:r},n().createElement("svg",{"aria-hidden":"true",focusable:"false",width:"20",height:"20",viewBox:"0 0 20 20"},n().createElement("path",{d:"M4.828,11L12.314,18.485L10.899,19.899L1,10L10.899,0.101L12.314,1.515L4.828,9L19,9L19,11L4.828,11Z",fill:"currentColor"}))),s=n().createElement("button",{className:"wc-header__close","aria-label":"Close the webchat",onClick:r},n().createElement("svg",{"aria-hidden":"true",focusable:"false",width:"20",height:"20",viewBox:"0 0 20 20"},n().createElement("path",{d:"M10,8.6L15.6,3L17,4.4L11.4,10L17,15.6L15.6,17L10,11.4L4.4,17L3,15.6L8.6,10L3,4.4L4.4,3L10,8.6Z",fill:"currentColor"}))),i=n().createElement("button",{className:"wc-header__hide","aria-label":"Minimise the webchat",onClick:r},n().createElement("svg",{"aria-hidden":"true",focusable:"false",width:"20",height:"20",viewBox:"0 0 20 20"},n().createElement("path",{d:"M10 14.4l-7-7L4.4 6l5.6 5.6L15.6 6 17 7.4l-7 7z",fill:"currentColor"}))),u=o&&window.history.state,d=o&&!window.history.state;let m=s;return t&&(m=i,d&&(m=i)),!t&&d&&(m=s),u&&(m=null),n().createElement("div",{className:"wc-header"},u?c:null,n().createElement("h2",{id:"wc-header",className:"wc-header__title"},"Floodline webchat"),m)}function I({onContinue:e}){return n().createElement(n().Fragment,null,n().createElement(C,null),n().createElement("div",{className:"wc-body"},n().createElement("div",{className:"wc-content"},n().createElement("h3",{id:"wc-subtitle",className:"wc-heading","aria-live":"polite"},"What you can use webchat for"),n().createElement("p",null,"Webchat lets you talk directly to a Floodline adviser."),n().createElement("p",null,"You can use webchat to get:"),n().createElement("ul",{className:"wc-list"},n().createElement("li",null,"current flood warnings and alerts in your area"),n().createElement("li",null,"information on the flood warning service"),n().createElement("li",null,"advice on what to do before, during and after a flood")),n().createElement("p",null,"There are other ways to ",n().createElement("a",{className:"govuk-link",href:"https://www.gov.uk/sign-up-for-flood-warnings",target:"_blank",rel:"noreferrer"},"sign up for flood warnings")," and ",n().createElement("a",{className:"govuk-link",href:"https://www.fws.environment-agency.gov.uk/app/olr/login",target:"_blank",rel:"noreferrer"},"manage your flood warnings account"),"."),n().createElement("p",null,"Do not use webchat to ",n().createElement("a",{className:"govuk-link",href:"https://www.gov.uk/report-flood-cause",target:"_blank",rel:"noreferrer"},"report a flood"),"."),n().createElement("button",{className:"govuk-button wc-button govuk-!-margin-top-1","data-module":"govuk-button",onClick:e},"Continue"),n().createElement("h3",{className:"wc-heading"},"Other flood information"),n().createElement("p",null,"Call Floodline for all other flood and flood warning information."),n().createElement("p",null,n().createElement("strong",null,"Floodline helpline"),n().createElement("br",null),"Telephone: 0345 988 1188",n().createElement("br",null),"Textphone: 0345 602 6340",n().createElement("br",null),"Open 24 hours a day, 7 days a week",n().createElement("br",null),n().createElement("a",{className:"govuk-link",href:"https://gov.uk/call-charges",target:"_blank",rel:"noreferrer"},"Find out more about call charges")))))}const A=require("uuid"),L=e=>{const t=async()=>e.authorize();return{connect:t,fetchCustomerId:async()=>{const e=await t();return e?.consumerIdentity.idOnExternalPlatform},fetchThread:async a=>(await t(),e.getThread(a)),fetchMessages:async(e,a)=>{await t();const n=await e.recover(a),l=[];let o=n.messages;for(;o.length;){o.map((e=>l.push(e)));try{o=(await e.loadMoreMessages()).data.messages}catch(e){o=[]}}return l}}},x=({errors:e})=>{const t=Object.keys(e),a=e=>{e.preventDefault();const t=e.target.getAttribute("data-key");document.querySelector(`#wc-${t}`).focus()};return 0===t.length?null:n().createElement("div",{className:"govuk-error-summary govuk-!-static-margin-bottom-7","data-module":"govuk-error-summary",tabIndex:"-1"},n().createElement("div",{role:"alert"},n().createElement("h2",{id:"wc-error",className:"wc-error-summary__title govuk-error-summary__title"},"There is a problem"),n().createElement("div",{className:"govuk-error-summary__body"},n().createElement("ul",{className:"govuk-list govuk-error-summary__list wc-error-summary__list"},t.map((t=>n().createElement("li",{key:t},n().createElement("a",{href:"#","data-key":t,onClick:a},e[t]))))))))},D=500;function G({onPreChatScreen:e}){const{sdk:t,setCustomerId:l,setThreadId:o,setThread:c,setLiveRegionText:s}=S(),{fetchCustomerId:i,fetchThread:u}=L(t),[d,m]=(0,a.useState)({}),[g,h]=(0,a.useState)(0),[E,w]=(0,a.useState)(!1),v=(0,a.useRef)(),y=(0,a.useRef)(),b=D>=g;let f=`You have ${D-g} characters remaining`;b||(f=`You have ${g-D} characters too many`),(0,a.useEffect)((()=>{Object.keys(d).length&&document.querySelector(".govuk-error-summary")?.focus()}),[d]);const p=E?"Requesting...":"Request chat";return(0,a.useEffect)((()=>{E&&s(p)}),[E]),n().createElement(n().Fragment,null,n().createElement(C,null),n().createElement("div",{className:"wc-body"},n().createElement("div",{className:"wc-content"},n().createElement("a",{href:"#",className:"wc-back-link govuk-back-link",onClick:e},"What you can use webchat for"),n().createElement(x,{errors:d}),n().createElement("h3",{id:"wc-subtitle",className:"wc-heading","aria-live":"polite"},"Your name and question"),n().createElement("form",null,n().createElement("div",{className:r("wc-form-group","govuk-form-group",d.name&&"govuk-form-group--error")},n().createElement("label",{className:"wc-label govuk-label",htmlFor:"wc-name"},"Your name"),d.name&&n().createElement("p",{className:"govuk-error-message"},n().createElement("span",{className:"govuk-visually-hidden"},"Error:")," ",d.name),n().createElement("input",{ref:v,className:r("wc-input","govuk-input",d.name&&"govuk-input--error"),id:"wc-name",name:"name",type:"text","data-testid":"request-chat-user-name"})),n().createElement("div",{className:"wc-form-group govuk-character-count","data-module":"govuk-character-count","data-maxlength":"500"},n().createElement("div",{className:r("govuk-form-group",d.question&&"govuk-form-group--error")},n().createElement("label",{className:"wc-label govuk-label",htmlFor:"wc-question"},"Your question"),d.question&&n().createElement("p",{className:"govuk-error-message"},n().createElement("span",{className:"govuk-visually-hidden"},"Error:"),d.question),n().createElement("textarea",{ref:y,id:"wc-question",name:"question",rows:"5","aria-describedby":"wc-question-info",onChange:e=>{h(e.target.value.length)},className:r("wc-textarea","govuk-textarea","govuk-js-character-count",!b||d.question?"govuk-textarea--error":""),"data-testid":"request-chat-user-question"}),n().createElement("div",{id:"wc-question-info",className:"wc-hint govuk-hint govuk-char-count__msg",style:{color:b?"":"#d4351c"},"aria-hidden":"true"},f),n().createElement("div",{className:"govuk-character-count__sr-status govuk-visually-hidden","aria-live":"polite"},f))),n().createElement("div",{className:"wc-inset-text govuk-inset-text"},"By selecting 'Request chat' you agree to the terms of our ",n().createElement("a",{href:"https://check-for-flooding.service.gov.uk/privacy-notice",target:"_blank",rel:"noreferrer",className:"govuk-link"},"privacy notice"),"."),n().createElement("button",{className:"wc-button govuk-button govuk-!-margin-top-1","data-module":"govuk-button",onClick:async e=>{if(e.preventDefault(),E)return;const a={};if(0===v.current.value.length&&(a.name="Enter your name"),0===y.current.value.length&&(a.question="Enter your question"),y.current.value.length>D&&(a.question="Your question must be 500 characters or less"),0===Object.keys(a).length)try{w(!0);const e=A.v4(),a=await i(),n=await u(e);t.getCustomer().setName(v.current.value),await n.startChat(y.current.value||"Begin conversation"),l(a),o(e),c(n),n.setCustomField("threadid",e)}catch(e){console.log("[Request chat Error]",e)}m(a)},"aria-disabled":E},p)))))}function q({children:e}){return n().createElement("div",{className:"wc-footer"},e)}function O({message:e,previousMessage:t}){const a="outbound"===e.direction,l=e?.direction===t?.direction;return n().createElement("li",{className:r("wc-chat__message",a?"outbound":"inbound")},n().createElement("div",{className:"wc-chat__from"},l?null:n().createElement("span",{className:r(a?"outbound":"inbound")},a?e.assignee:"You"),n().createElement("span",{className:"govuk-visually-hidden"},":")),n().createElement("div",{className:r("wc-chat__text",a?"outbound":"inbound")},m(e.text)))}const M=(e,t)=>{(0,a.useEffect)((()=>{if(e){e.style.height="0px";const t=e.scrollHeight;e.style.height=`${t}px`}}),[e,t])},R=(e,t,a)=>{if("UNAVAILABLE"===e)return"Webchat is not currently available";if(!t)return"Connecting to Floodline";switch(t){case"closed":case"resolved":return a?`${a} ended the session`:"Session ended by advisor";default:return a?`You are speaking with ${a}`:"Waiting for an adviser"}};function F({onEndChatScreen:e,onSettingsScreen:t}){const{availability:l,thread:o,messages:r,agent:c,agentStatus:s,isAgentTyping:i,isChatOpen:u,settings:d,isKeyboard:m,setLiveRegionText:g}=S(),[E,w]=(0,a.useState)(""),[v,b]=(0,a.useState)(!1),f=(0,a.useRef)();M(f.current,E),(0,a.useEffect)((()=>{if(d.scroll&&0!==r.length){const e=document.querySelector(".wc-body");e.scrollTop=e.scrollHeight}}),[r,i]),(0,a.useEffect)((()=>{const e=document.querySelector(".wc-form__label");0===E.length?e.classList.remove("govuk-visually-hidden"):e.classList.add("govuk-visually-hidden")}),[E]);const p=c?.nickname||c?.firstName,k=R(l,s,p);(0,a.useEffect)((()=>(k&&g(`Floodline webchat - ${k}`),()=>{g()})),[k]),(0,a.useEffect)((()=>(i&&u&&g(`${p} is typing...`),()=>{g()})),[i,u]),(0,a.useEffect)((()=>{const e=r[r.length-1];return"closed"!==s&&"outbound"===e?.direction&&g(`${p} said: ${e.text}`),()=>{g()}}),[r]);const _=()=>{if(0!==f.current.value.length&&"closed"!==s){try{const e=f.current.value.trim();o.sendTextMessage(e),g(`You said: ${e}`)}catch(e){console.log("[Chat Error] sendMessage",e)}w("")}},N=()=>{const e=h(r);document.querySelector("#transcript-download").setAttribute("href",`data:text/plain;charset=utf-8,${e}`)},T=a=>{if("Enter"!==a.key||a.shiftKey)" "===a.key?("end-chat"===a.target.id&&e(a),"wc-settings"===a.target.id&&t(a)):(o.keystroke(),setTimeout((()=>{o.stopTyping()}),y));else switch(a.target.id){case"text-area":a.preventDefault(),_();break;case"end-chat":e(a);break;case"wc-settings":t(a);break;case"transcript-download":N()}};return n().createElement(n().Fragment,null,n().createElement(C,null),n().createElement("div",{className:"wc-status"},n().createElement("p",{className:"wc-status__availability"},k),n().createElement("a",{id:"end-chat",className:"wc-status__link",href:"#","data-module":"govuk-button",role:"button",onClick:e,onKeyDown:T},"End chat")),n().createElement("div",{className:"wc-body",tabIndex:"0"},n().createElement("ul",{className:"wc-chat"},r.length?r.map(((e,t)=>n().createElement(O,{key:e.id,message:e,previousMessage:r[t-1]}))):null,i?n().createElement("li",{className:"wc-chat__message outbound"},n().createElement("div",{className:"wc-chat__from"},p," is typing"),n().createElement("div",{className:"wc-chat__text outbound"},n().createElement("svg",{width:"28",height:"16",x:"0px",y:"0px",viewBox:"0 0 28 16"},n().createElement("circle",{stroke:"none",cx:"3",cy:"8",r:"3",fill:"currentColor"}),n().createElement("circle",{stroke:"none",cx:"14",cy:"8",r:"3",fill:"currentColor"}),n().createElement("circle",{stroke:"none",cx:"25",cy:"8",r:"3",fill:"currentColor"})))):null)),n().createElement(q,null,n().createElement("form",{className:"wc-form"+(v?" wc-focus-within":""),noValidate:!0,onSubmit:e=>{e.preventDefault(),_()}},n().createElement("label",{className:"govuk-label wc-form__label",htmlFor:"wc-form-textarea"},"Your message",n().createElement("span",{className:"govuk-visually-hidden"}," (enter key submits)")),n().createElement("textarea",{ref:f,rows:"1","aria-required":"true",className:"wc-form__textarea",id:"text-area",name:"message",onChange:e=>{w(e.target?.value)},onKeyDown:T,onFocus:()=>{b(m)},onBlur:()=>{b(!1)},value:E}),n().createElement("input",{type:"submit",className:"wc-form__button govuk-button",value:"Send","data-prevent-double-click":"true"})),n().createElement("div",{className:"wc-footer__settings"},n().createElement("a",{href:"#",id:"wc-settings",className:"wc-footer__settings-link","data-module":"govuk-button",onKeyDown:T,onClick:t},"Settings"),n().createElement("a",{href:"#",id:"transcript-download",className:"wc-footer__settings-link","data-module":"govuk-button",download:"floodline-webchat-transcript.txt",onKeyDown:T,onClick:N},"Save chat"))))}function U(){return n().createElement(n().Fragment,null,n().createElement(C,null),n().createElement("div",{className:"wc-body"},n().createElement("h3",{id:"wc-subtitle",className:"govuk-heading-m"},"Webchat is currently not available"),n().createElement("p",{className:"govuk-body-s"},"Try again later, or call Floodline:"),n().createElement("p",{className:"govuk-body-s"},n().createElement("strong",null,"Floodline helpline"),n().createElement("br",null),"Telephone: 0345 988 1188",n().createElement("br",null),"Textphone: 0345 602 6340",n().createElement("br",null),"Open 24 hours a day, 7 days a week",n().createElement("br",null),n().createElement("a",{className:"govuk-link",href:"https://gov.uk/call-charges"},"Find out more about call charges")),n().createElement("p",{className:"govuk-body-s"},"We're running webchat as a trial, it will not always be available.")))}function B({onChatScreen:e,onEndChatConfirm:t}){const{setCustomerId:l,setThreadId:o,setMessages:r,agentStatus:c,thread:s,threadId:i,setUnseenCount:u,isKeyboard:d}=S(),m=async e=>{e.preventDefault(),window.localStorage.setItem("tmpThreadId",i),o(),r([]),l(),s.lastMessageSeen(),u(0),"closed"!==c&&s.endChat(),t(e)};(0,a.useEffect)((()=>{d&&setTimeout((()=>{document.querySelector("#confirm-endchat").focus()}),10)}),[]);const g=t=>{"Enter"!==t.key&&" "!==t.key||"confirm-endchat"!==t.target.id||m(t),"Enter"!==t.key&&" "!==t.key||"resume-chat"!==t.target.id||e(t)};return n().createElement(n().Fragment,null,n().createElement(C,null),n().createElement("div",{className:"wc-body"},n().createElement("div",{className:"wc-content"},n().createElement("h3",{id:"wc-subtitle",className:"wc-heading","aria-live":"polite"},"Are you sure you want to end the chat?"),n().createElement("div",{className:"govuk-button-group"},n().createElement("a",{id:"confirm-endchat",href:"#endChat",role:"button",className:"wc-button govuk-button","data-module":"govuk-button",onClick:m,onKeyDown:g},"Yes, end chat"),n().createElement("a",{id:"resume-chat",href:"#resumeChat",role:"button",className:"wc-link govuk-link","data-module":"govuk-button",onClick:e,onKeyDown:g},"No, resume chat")))))}function Y({onCancel:e}){const{settings:t,setSettings:l}=S(),[o,r]=(0,a.useState)(t.audio),[c,s]=(0,a.useState)(t.scroll),i=t=>{t.preventDefault(),l({audio:o,scroll:c}),e(t)},u=t=>{"Enter"!==t.key&&" "!==t.key||"settings-save"!==t.target.id?"Enter"!==t.key&&" "!==t.key||"settings-cancel"!==t.target.id||e(t):i(t)};return n().createElement(n().Fragment,null,n().createElement(C,null),n().createElement("div",{className:"wc-body"},n().createElement("div",{className:"wc-content"},n().createElement("fieldset",{className:"govuk-fieldset govuk-!-margin-bottom-4"},n().createElement("legend",{className:"govuk-fieldset__legend govuk-fieldset__legend"},n().createElement("h3",{id:"wc-subtitle",className:"wc-heading"},"Change settings")),n().createElement("div",{className:"govuk-checkboxes govuk-checkboxes--small","data-module":"govuk-checkboxes"},n().createElement("div",{className:"govuk-checkboxes__item"},n().createElement("input",{id:"audio",name:"audio",type:"checkbox",value:"audio",className:"govuk-checkboxes__input",defaultChecked:o,onChange:()=>r(!o)}),n().createElement("label",{className:"wc-label govuk-label govuk-checkboxes__label",htmlFor:"audio"},"Play a sound when receiving a new message")),n().createElement("div",{className:"govuk-checkboxes__item"},n().createElement("input",{id:"scroll",name:"scroll",type:"checkbox",value:"scroll",className:"govuk-checkboxes__input",defaultChecked:c,onChange:()=>s(!c)}),n().createElement("label",{className:"wc-label govuk-label govuk-checkboxes__label",htmlFor:"scroll"},"Scroll automatically to a new message")))),n().createElement("div",{className:"govuk-button-group"},n().createElement("a",{id:"settings-save",href:"#",className:"wc-button govuk-button","data-module":"govuk-button",role:"button",onClick:i,onKeyDown:u},"Save"),n().createElement("a",{id:"settings-cancel",href:"#",className:"wc-link govuk-link","data-module":"govuk-button",role:"button",onClick:e,onKeyDown:u},"Cancel")))))}function $({onCancel:e}){const t=()=>{const e=window.localStorage.getItem("tmpThreadId");window.location.href=`https://defragroup.eu.qualtrics.com/jfe/form/SV_8dgFSJcxxIfqx5Y?Id=${e}&Source=${window.location.href}`,window.localStorage.removeItem("tmpThreadId")},a=async t=>{window.localStorage.removeItem("tmpThreadId"),e(t)},l=e=>{"Enter"!==e.key&&" "!==e.key||("feedback-close"===e.target.id&&a(e),"feedback-send"===e.target.id&&t())};return n().createElement(n().Fragment,null,n().createElement(C,null),n().createElement("div",{className:"wc-body"},n().createElement("div",{className:"wc-content"},n().createElement("h3",{id:"wc-subtitle",className:"wc-heading"},"Give feedback on Floodline webchat"),n().createElement("p",null,"We're running webchat as a trial. you can ",n().createElement("a",{id:"feedback-send",className:"govuk-link",href:"#",target:"_blank",rel:"noreferrer",onKeyDown:l,onClick:t},"give feedback")," to help us improve it."),n().createElement("p",null,n().createElement("a",{id:"feedback-close",className:"wc-link govuk-link",href:"#","data-module":"govuk-button",role:"button",onKeyDown:l,onClick:a},"Close")))))}const K=require("lodash.debounce");var V=e.n(K);const H=()=>{const{agentStatus:e,liveRegionText:t,setLiveRegionText:l}=S(),[o,r]=(0,a.useState)(),[c,s]=(0,a.useState)(),i="closed"===e||null===e,u=()=>{r(),s()},d=(0,a.useCallback)(V()((e=>{o?s(e):r(e)}),2e3),[o,c]);return(0,a.useEffect)((()=>(t&&(u(),d(t)),i&&(u(),l()),()=>{u(),l()})),[t]),n().createElement(n().Fragment,null,n().createElement("div",{className:"wc-live",role:"status","aria-atomic":"true"},o),n().createElement("div",{className:"wc-live",role:"status","aria-atomic":"true"},c))},P="aria-hidden",j="data-wc-inert",W=e=>{for(const t of document.body.children)"wc-panel"!==t.id&&(e&&!t.getAttribute(P)?(t.setAttribute(P,"true"),t.setAttribute(j,"")):t.hasAttribute(j)&&(t.removeAttribute(P),t.removeAttribute(j)))},J=e=>{const[t,n]=(0,a.useState)([]),l=(0,a.useCallback)((e=>{const a=document.querySelector("#wc-panel");"Tab"===e.key&&(a&&!document.activeElement.closest("#wc-panel")&&a.focus(),e.shiftKey?(document.activeElement===t[0]||document.activeElement===document.querySelector("#wc-panel"))&&(t[t.length-1].focus(),e.preventDefault()):document.activeElement===t[t.length-1]&&(t[0].focus(),e.preventDefault()))}),[t]);(0,a.useEffect)((()=>{n((()=>{const e=document.body.querySelectorAll(["#wc-panel a:not([disabled])","#wc-panel button:not([disabled])","#wc-panel select:not([disabled])","#wc-panel input:not([disabled])","#wc-panel textarea:not([disabled])",'#wc-panel *[tabindex="0"]:not([disabled])'].join(","));return Array.from(e).filter((e=>!e.closest("[hidden]")&&!e.closest('[aria-hidden="true"]')))})())}),[e]),(0,a.useEffect)((()=>(W(!0),document.querySelector("#wc-panel").focus(),document.addEventListener("keydown",l),()=>{W(),document.removeEventListener("keydown",l)})),[t])};function X(){const{sdk:e,availability:t,instigatorId:l,thread:o,threadId:s,setThread:i,setThreadId:u,setChatVisibility:d,setMessages:m,setUnseenCount:g,isMobile:h,isKeyboard:E}=S(),{fetchThread:w,fetchMessages:v}=L(e),[y,b]=(0,a.useState)(s?2:0);J(y);const f=(0,a.useCallback)((e=>{"Escape"!==e.key&&"Esc"!==e.key||(s&&(o?.lastMessageSeen(),g(0)),d(!1),T())}),[o,s,g,d]);(0,a.useEffect)((()=>()=>{document.getElementById(l)?.focus()}),[]),(0,a.useEffect)((()=>(document.body.classList.remove("wc-u-hidden"),document.getElementsByTagName("html")[0].classList.add("wc-u-html"),document.body.classList.add("wc-u-body"),()=>{document.getElementsByTagName("html")[0].classList.remove("wc-u-html"),document.body.classList.remove("wc-u-body")})),[h]),(0,a.useEffect)((()=>(document.addEventListener("keydown",f),()=>{document.removeEventListener("keydown",f)})),[f,d]),(0,a.useEffect)((()=>{s&&(o?(o.lastMessageSeen(),g(0),b(2)):(async()=>{try{const e=await w(s);i(e);const t=await v(e,s);m(t)}catch(e){console.log("[Chat Error] fetchThread",e),u(),i(),b(0)}})())}),[o,s]);const p=e=>t=>{t.preventDefault(),b(e)},k=p(0),_=p(1),N=p(2),C=p(3),A=p(4),x=p(5);let D;switch(y){case 0:default:D=n().createElement(I,{onContinue:_});break;case 1:D=n().createElement(G,{onPreChatScreen:k});break;case 2:D=n().createElement(F,{onSettingsScreen:x,onEndChatScreen:C});break;case 3:D=n().createElement(B,{onChatScreen:N,onEndChatConfirm:A});break;case 4:D=n().createElement($,{onCancel:()=>{d(!1),T()}});break;case 5:D=n().createElement(Y,{onCancel:N})}"UNAVAILABLE"===t&&(D=n().createElement(U,null));const q=n().createElement("div",{id:"wc-panel",role:"dialog",className:r("wc-panel",E&&"wc-focus-visible"),tabIndex:"-1","aria-modal":"true","aria-labelledby":"wc-header wc-subtitle"},n().createElement("div",{className:"wc-panel__inner"},D),n().createElement(H,null));return n().createElement(n().Fragment,null,(0,c.createPortal)(q,document.body))}const Z=()=>{const{threadId:e,setInstigatorId:t}=S();if(!e)return null;const a=document.getElementById("webchat-skip-link-container");return a?n().createElement(n().Fragment,null,(0,c.createPortal)(n().createElement("a",{id:"webchat-skip-link",href:"#webchat",className:"govuk-skip-link","data-module":"govuk-skip-link",onClick:e=>{e.preventDefault(),t(e.target.id),window.location.hash="#webchat"},"data-wc-skiplink":!0,"data-wc-open-btn":!0},"Skip to webchat"),a)):null};function z(){const{availability:e,isChatOpen:t,setChatVisibility:l,unseenCount:o,threadId:c,setUnseenCount:s,setInstigatorId:i,setLiveRegionText:u}=S(),d=(0,a.useRef)(),m=o>0&&!t,g=o>1?"new messages":"new message",h=e=>{e.preventDefault(),s(0),l(!t),i(e.target.id),t?T():N()},E=e=>{" "===e.key&&e.preventDefault()},w=e=>{" "===e.key&&(s(0),l(!t))};switch((0,a.useEffect)((()=>(m&&u(`Floodline webchat - ${o} ${g}`),()=>{u()})),[o,t]),(0,a.useEffect)((()=>{const a=()=>{if(!c)return;if("AVAILABLE"!==e)return;const a=d.current.parentElement.getBoundingClientRect().top+35>(window.innerHeight||document.documentElement.clientHeight),n=!t&&a;document.documentElement.classList.toggle("wc-u-scroll-padding",n),document.body.classList.toggle("wc-u-scroll-padding",n),d.current.classList.toggle("wc-availability--fixed",n)};return document.addEventListener("scroll",a),a(),()=>{document.removeEventListener("scroll",a)}}),[c,t,e]),e){case"AVAILABLE":return n().createElement(n().Fragment,null,n().createElement("div",{className:r("wc-availability",t&&"wc-open"),ref:d},n().createElement("div",{className:"wc-availability__inner"},n().createElement("a",{id:"webchat-start-chat-link",className:"wc-availability__link",href:"#webchat",draggable:"false",onClick:h,onKeyUp:w,onKeyDown:E,role:"button","data-module":"govuk-button"},c?n().createElement(n().Fragment,null,"Show chat"):n().createElement(n().Fragment,null,"Start chat"),m?n().createElement("span",{className:"wc-availability__unseen"},o,n().createElement("span",{className:"govuk-visually-hidden"}," ",g)):null))),t?n().createElement(X,null):n().createElement(H,null),n().createElement(Z,null));case"EXISTING":case"UNAVAILABLE":return n().createElement("p",{className:"govuk-body"},"When it is available, a 'start chat' link will appear.");default:return n().createElement("p",{className:"govuk-body"},"Checking availability")}}const Q=async e=>{const t=new(window.AudioContext||window.webkitAudioContext);if("suspended"===t.state){const e=["touchstart","touchend","mousedown","wheel","keydown","click"],a=n=>{e.forEach((e=>{document.body.removeEventListener(e,a)})),t.resume()};e.forEach((e=>{document.body.addEventListener(e,a,!1)}))}const a=await fetch(e),n=await a.arrayBuffer(),l=await t.decodeAudioData(n);return()=>{const e=t.createBufferSource();e.buffer=l,e.connect(t.destination),e.start()}};async function ee(e,t){const a=new o.ChatSdk({brandId:t.brandId,channelId:t.channelId,customerId:window.localStorage.getItem(w)||"",environment:t.environment}),r=(0,l.createRoot)(e);let c,s;try{const e=await async function(e){const t=await fetch(e);return{availability:(await t.json()).availability||"UNAVAILABLE"}}(t.availabilityEndpoint);s=await Q(t.audioUrl),c=e.availability}catch(e){c="UNAVAILABLE"}r.render(n().createElement(_,{sdk:a,availability:c,playSound:s},n().createElement(z,null)))}module.exports=t})();
|
|
1
|
+
(()=>{"use strict";var e={n:t=>{var a=t&&t.__esModule?()=>t.default:()=>t;return e.d(a,{a}),a},d:(t,a)=>{for(var n in a)e.o(a,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:a[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{init:()=>le});const a=require("react");var n=e.n(a);const o=require("react-dom/client"),l=require("@nice-devone/nice-cxone-chat-web-sdk"),r="webchat_settings",c="webchat_customer_id",s="webchat_thread_id",i=4e3,u={SET_SDK:(e,t)=>({...e,sdk:t}),SET_THREAD:(e,t)=>({...e,thread:t}),SET_CHAT_VISIBILITY:(e,t)=>({...e,isChatOpen:t}),SET_AVAILABILITY:(e,t)=>({...e,availability:t}),SET_SETTINGS:(e,t)=>(t?window.localStorage.setItem(r,JSON.stringify(t)):window.localStorage.removeItem(r),{...e,settings:t}),SET_CUSTOMER_ID:(e,t)=>(t?window.localStorage.setItem(c,t):window.localStorage.removeItem(c),{...e,customerId:t}),SET_THREAD_ID:(e,t)=>(t?window.localStorage.setItem(s,t):window.localStorage.removeItem(s),{...e,threadId:t}),SET_UNSEEN_COUNT:(e,t)=>({...e,unseenCount:t}),SET_INSTIGATOR_ID:(e,t)=>({...e,instigatorId:t}),SET_LIVE_REGION_TEXT:(e,t)=>({...e,liveRegionText:t}),TOGGLE_IS_MOBILE:(e,t)=>({...e,isMobile:t}),TOGGLE_IS_KEYBOARD:(e,t)=>({...e,isKeyboard:t})},d={sdk:null,availability:null,customerId:null,threadId:null,thread:null,unseenCount:0,instigatorId:null,isChatOpen:!1,isMobile:window.matchMedia("(max-width: 640px)").matches,isKeyboard:!1,settings:{audio:!0,scroll:!0}},m=(e,t)=>{const{type:a,payload:n}=t,o=u[a];return o?o.bind(void 0,e,n)():e},g=(0,a.createContext)(d),E=({availability:e,children:t})=>{const[o,l]=(0,a.useReducer)(m,d);(0,a.useEffect)((()=>{l({type:"SET_AVAILABILITY",payload:e}),v(window.localStorage.getItem(c)),y(window.localStorage.getItem(s)),f(JSON.parse(window.localStorage.getItem(r))||o.settings)}),[]),(0,a.useEffect)((()=>{"#webchat"===window.location.hash&&w(!0);const e=()=>{"#webchat"===window.location.hash?w(!0):w(!1)};return window.addEventListener("popstate",e),()=>{window.removeEventListener("popstate",e)}}),[]);const i=e=>{l({type:"TOGGLE_IS_MOBILE",payload:e.matches})},u=()=>{l({type:"TOGGLE_IS_KEYBOARD",payload:!0})},E=()=>{l({type:"TOGGLE_IS_KEYBOARD",payload:!1})};(0,a.useEffect)((()=>(window.matchMedia("(max-width: 640px)").addEventListener("change",i),window.addEventListener("keydown",u),window.addEventListener("pointerdown",E),()=>{window.removeEventListener("change",i),window.removeEventListener("keydown",u),window.removeEventListener("pointerdown",E)})),[]);const h=e=>{l({type:"SET_SDK",payload:e})},w=e=>{l({type:"SET_CHAT_VISIBILITY",payload:e})},v=e=>{l({type:"SET_CUSTOMER_ID",payload:e})},y=e=>{l({type:"SET_THREAD_ID",payload:e})},b=e=>{l({type:"SET_THREAD",payload:e})},f=e=>{l({type:"SET_SETTINGS",payload:e})},p=e=>{l({type:"SET_UNSEEN_COUNT",payload:e})},k=e=>{l({type:"SET_INSTIGATOR_ID",payload:e})},S=e=>{l({type:"SET_LIVE_REGION_TEXT",payload:e})},_=(0,a.useMemo)((()=>({...o,setSdk:h,setSettings:f,setCustomerId:v,setThreadId:y,setThread:b,setUnseenCount:p,setChatVisibility:w,setInstigatorId:k,setLiveRegionText:S})));return n().createElement(g.Provider,{value:_},t)},h=()=>(0,a.useContext)(g),w=(...e)=>e.filter((e=>e&&"string"==typeof e)).join(" "),v=()=>{const e=window.location.href.split("#")[0];window.history.pushState({history:!0},null,`${e}#webchat`)},y=()=>{if(window.history.state?.history)return window.history.back();const e=window.location.href.split("#")[0];return window.history.replaceState(null,null,e)},b=require("lodash.debounce");var f=e.n(b);const p=require("luxon"),k=require("html-react-parser");var S=e.n(k);const _=/(?:www|https?)[^\s]+/gi,N=e=>(e.match(_)&&(e=e.replace(_,(e=>`<a class="govuk-link" href="${e}" target="_blank" rel="noreferrer">${e.replace(/https?:\/\//gi,"")}</a>`))),S()(e)),T=e=>({id:e.id,text:e.messageContent?.text,createdAt:new Date(e.createdAt),user:e.authorEndUserIdentity?.fullName?.trim()||null,assignee:e.authorUser?.firstName||null,direction:e.direction}),C=e=>{const t=p.DateTime.local();t.setZone("Europe/London");let a=`Floodline webchat transcript, ${t.toFormat("HH:mm:ss a, dd LLLL yyyy")}\n\n`;for(const t of e){const{text:e,direction:n,user:o,assignee:l,createdAt:r}=t,c="inbound"===n?o:`${l} (Floodline adviser)`;a+=`[${p.DateTime.fromJSDate(new Date(r)).setZone("Europe/London").toFormat("HH:mm:ss a, dd LLLL yyyy")}] ${c}: \n${e}\n\n`}return a=a.replace(/<a\b[^>]*>/i,"").replace(/<\/a>/i,""),encodeURIComponent(a)},I={SET_MESSAGE:(e,t)=>({...e,message:T(t),messages:[...e.messages,T(t)]}),SET_MESSAGES:(e,t)=>{return{...e,messages:(a=t,a.map((e=>T(e)))).reverse()};var a},SET_AGENT:(e,t)=>({...e,agent:t}),SET_AGENT_TYPING:(e,t)=>({...e,isAgentTyping:t}),SET_AGENT_STATUS:(e,t)=>({...e,agentStatus:t})},A={messages:[],agent:null,agentStatus:null,isAgentTyping:!1},L=(e,t)=>{const{type:a,payload:n}=t,o=I[a];return o?o.bind(void 0,e,n)():e},x=(0,a.createContext)(A),D=({sdk:e,playSound:t,onRecoverError:o,children:c})=>{const[s,i]=(0,a.useReducer)(L,A),{thread:u,threadId:d,setThread:m,setThreadId:g,setUnseenCount:E}=h(),w=e=>{i({type:"SET_AGENT",payload:e.detail.data.inboxAssignee}),i({type:"SET_AGENT_STATUS",payload:e.detail.data.contact.status}),E(e.detail.data.contact.customerStatistics.unseenMessagesCount)},v=e=>{i({type:"SET_AGENT",payload:e.detail.data.inboxAssignee}),i({type:"SET_AGENT_STATUS",payload:e.detail.data.case.status})},y=()=>{i({type:"SET_AGENT_TYPING",payload:!0})},b=()=>{i({type:"SET_AGENT_TYPING",payload:!1}),i({type:"SET_LIVE_REGION_TEXT"})},f=e=>{i({type:"SET_MESSAGE",payload:e.detail.data.message}),i({type:"SET_AGENT_STATUS",payload:e.detail.data.case.status}),E(e.detail.data.case.customerStatistics.unseenMessagesCount),JSON.parse(window.localStorage.getItem(r)).audio&&"outbound"===e.detail.data.message.direction&&t&&t()},p=e=>{i({type:"SET_AGENT_STATUS",payload:e.detail.data.case.status})};(0,a.useEffect)((()=>{e&&(e.onChatEvent(l.ChatEvent.LIVECHAT_RECOVERED,w),e.onChatEvent(l.ChatEvent.MESSAGE_CREATED,f),e.onChatEvent(l.ChatEvent.AGENT_TYPING_STARTED,y),e.onChatEvent(l.ChatEvent.AGENT_TYPING_ENDED,b),e.onChatEvent(l.ChatEvent.ASSIGNED_AGENT_CHANGED,v),e.onChatEvent(l.ChatEvent.CONTACT_STATUS_CHANGED,p))}),[e]);const k=async e=>{const t=await e.recover(),a=[];let n=t.messages;for(;n.length;){n.map((e=>a.push(e)));try{n=(await e.loadMoreMessages()).data.messages}catch(e){n=[]}}return a};(0,a.useEffect)((()=>{e&&d&&!u&&(console.log("recovering chat..."),(async()=>{try{await e.authorize();const t=await e.getThread(d),a=await k(t);m(t),S(a)}catch(e){console.log("[Chat Error] fetchThread",e),g(),m(),o()}})())}),[e,u,d]);const S=e=>{i({type:"SET_MESSAGES",payload:e})},_=(0,a.useMemo)((()=>({...s,sdk:e,fetchMessages:k,setMessages:S,onLiveChatRecovered:w,onAssignedAgentChanged:v,onAgentTypingStarted:y,onAgentTypingEnded:b,onMessageCreated:f,onContactStatusChanged:p})));return n().createElement(x.Provider,{value:_},c)},G=()=>(0,a.useContext)(x),q=()=>{const{liveRegionText:e,setLiveRegionText:t}=h(),{agentStatus:o}=G(),[l,r]=(0,a.useState)(),[c,s]=(0,a.useState)(),i="closed"===o||null===o,u=()=>{r(),s()},d=(0,a.useCallback)(f()((e=>{l?s(e):r(e)}),2e3),[l,c]);return(0,a.useEffect)((()=>(e&&(u(),d(e)),i&&(u(),t()),()=>{u(),t()})),[e]),n().createElement(n().Fragment,null,n().createElement("div",{className:"wc-live",role:"status","aria-atomic":"true"},l),n().createElement("div",{className:"wc-live",role:"status","aria-atomic":"true"},c))};function O(){const{availability:e,isChatOpen:t,setChatVisibility:o,unseenCount:l,threadId:r,setUnseenCount:c,setInstigatorId:s,setLiveRegionText:i}=h(),u=(0,a.useRef)(),d=l>0&&!t,m=l>1?"new messages":"new message",g=e=>{e.preventDefault(),c(0),o(!t),s(e.target.id),t?y():v()},E=e=>{" "===e.key&&e.preventDefault()},b=e=>{" "===e.key&&(c(0),o(!t))};switch((0,a.useEffect)((()=>(d&&i(`Floodline webchat - ${l} ${m}`),()=>{i()})),[l,t]),(0,a.useEffect)((()=>{const a=()=>{if(!r)return;if("AVAILABLE"!==e)return;const a=u.current.parentElement.getBoundingClientRect().top+35>(window.innerHeight||document.documentElement.clientHeight),n=!t&&a;document.documentElement.classList.toggle("wc-u-scroll-padding",n),document.body.classList.toggle("wc-u-scroll-padding",n),u.current.classList.toggle("wc-availability--fixed",n)};return document.addEventListener("scroll",a),a(),()=>{document.removeEventListener("scroll",a)}}),[r,t,e]),e){case"EXISTING":case"UNAVAILABLE":return n().createElement("p",{className:"govuk-body"},"When it is available, a 'start chat' link will appear.");case"AVAILABLE":return n().createElement(n().Fragment,null,n().createElement("div",{className:w("wc-availability",t&&"wc-open"),ref:u},n().createElement("div",{className:"wc-availability__inner"},n().createElement("a",{id:"webchat-start-chat-link",className:"wc-availability__link",href:"#webchat",draggable:"false",onClick:g,onKeyUp:b,onKeyDown:E,role:"button","data-module":"govuk-button"},r?n().createElement(n().Fragment,null,"Show chat"):n().createElement(n().Fragment,null,"Start chat"),d?n().createElement("span",{className:"wc-availability__unseen"},l,n().createElement("span",{className:"govuk-visually-hidden"}," ",m)):null))),!t&&n().createElement(q,null));default:return n().createElement("p",{className:"govuk-body"},"Checking availability")}}const R=async e=>{const t=new(window.AudioContext||window.webkitAudioContext);if("suspended"===t.state){const e=["touchstart","touchend","mousedown","wheel","keydown","click"],a=n=>{e.forEach((e=>{document.body.removeEventListener(e,a)})),t.resume()};e.forEach((e=>{document.body.addEventListener(e,a,!1)}))}const a=await fetch(e),n=await a.arrayBuffer(),o=await t.decodeAudioData(n);return()=>{const e=t.createBufferSource();e.buffer=o,e.connect(t.destination),e.start()}},M=require("react-dom"),F=()=>{const{threadId:e,setInstigatorId:t}=h();if(!e)return null;const a=document.getElementById("webchat-skip-link-container");return a?n().createElement(n().Fragment,null,(0,M.createPortal)(n().createElement("a",{id:"webchat-skip-link",href:"#webchat",className:"govuk-skip-link","data-module":"govuk-skip-link",onClick:e=>{e.preventDefault(),t(e.target.id),window.location.hash="#webchat"},"data-wc-skiplink":!0,"data-wc-open-btn":!0},"Skip to webchat"),a)):null};function B(){const{thread:e,threadId:t,setChatVisibility:a,setUnseenCount:o,isMobile:l}=h(),r=n=>{n.preventDefault(),a(!1),y(),t&&(e?.lastMessageSeen(),o(0))},c=n().createElement("button",{className:"wc-header__back","aria-label":"Close the webchat",onClick:r},n().createElement("svg",{"aria-hidden":"true",focusable:"false",width:"20",height:"20",viewBox:"0 0 20 20"},n().createElement("path",{d:"M4.828,11L12.314,18.485L10.899,19.899L1,10L10.899,0.101L12.314,1.515L4.828,9L19,9L19,11L4.828,11Z",fill:"currentColor"}))),s=n().createElement("button",{className:"wc-header__close","aria-label":"Close the webchat",onClick:r},n().createElement("svg",{"aria-hidden":"true",focusable:"false",width:"20",height:"20",viewBox:"0 0 20 20"},n().createElement("path",{d:"M10,8.6L15.6,3L17,4.4L11.4,10L17,15.6L15.6,17L10,11.4L4.4,17L3,15.6L8.6,10L3,4.4L4.4,3L10,8.6Z",fill:"currentColor"}))),i=n().createElement("button",{className:"wc-header__hide","aria-label":"Minimise the webchat",onClick:r},n().createElement("svg",{"aria-hidden":"true",focusable:"false",width:"20",height:"20",viewBox:"0 0 20 20"},n().createElement("path",{d:"M10 14.4l-7-7L4.4 6l5.6 5.6L15.6 6 17 7.4l-7 7z",fill:"currentColor"}))),u=l&&window.history.state,d=l&&!window.history.state;let m=s;return t&&(m=i,d&&(m=i)),!t&&d&&(m=s),u&&(m=null),n().createElement("div",{className:"wc-header"},u?c:null,n().createElement("h2",{id:"wc-header",className:"wc-header__title"},"Floodline webchat"),m)}function U({onContinue:e}){return n().createElement(n().Fragment,null,n().createElement(B,null),n().createElement("div",{className:"wc-body"},n().createElement("div",{className:"wc-content"},n().createElement("h3",{id:"wc-subtitle",className:"wc-heading","aria-live":"polite"},"What you can use webchat for"),n().createElement("p",null,"Webchat lets you talk directly to a Floodline adviser."),n().createElement("p",null,"You can use webchat to get:"),n().createElement("ul",{className:"wc-list"},n().createElement("li",null,"current flood warnings and alerts in your area"),n().createElement("li",null,"information on the flood warning service"),n().createElement("li",null,"advice on what to do before, during and after a flood")),n().createElement("p",null,"There are other ways to ",n().createElement("a",{className:"govuk-link",href:"https://www.gov.uk/sign-up-for-flood-warnings",target:"_blank",rel:"noreferrer"},"sign up for flood warnings")," and ",n().createElement("a",{className:"govuk-link",href:"https://www.fws.environment-agency.gov.uk/app/olr/login",target:"_blank",rel:"noreferrer"},"manage your flood warnings account"),"."),n().createElement("p",null,"Do not use webchat to ",n().createElement("a",{className:"govuk-link",href:"https://www.gov.uk/report-flood-cause",target:"_blank",rel:"noreferrer"},"report a flood"),"."),n().createElement("button",{className:"govuk-button wc-button govuk-!-margin-top-1","data-module":"govuk-button",onClick:e},"Continue"),n().createElement("h3",{className:"wc-heading"},"Other flood information"),n().createElement("p",null,"Call Floodline for all other flood and flood warning information."),n().createElement("p",null,n().createElement("strong",null,"Floodline helpline"),n().createElement("br",null),"Telephone: 0345 988 1188",n().createElement("br",null),"Textphone: 0345 602 6340",n().createElement("br",null),"Open 24 hours a day, 7 days a week",n().createElement("br",null),n().createElement("a",{className:"govuk-link",href:"https://gov.uk/call-charges",target:"_blank",rel:"noreferrer"},"Find out more about call charges")))))}const Y=require("uuid"),K=({errors:e})=>{const t=Object.keys(e),a=e=>{e.preventDefault();const t=e.target.getAttribute("data-key");document.querySelector(`#wc-${t}`).focus()};return 0===t.length?null:n().createElement("div",{className:"govuk-error-summary govuk-!-static-margin-bottom-7","data-module":"govuk-error-summary",tabIndex:"-1"},n().createElement("div",{role:"alert"},n().createElement("h2",{id:"wc-error",className:"wc-error-summary__title govuk-error-summary__title"},"There is a problem"),n().createElement("div",{className:"govuk-error-summary__body"},n().createElement("ul",{className:"govuk-list govuk-error-summary__list wc-error-summary__list"},t.map((t=>n().createElement("li",{key:t},n().createElement("a",{href:"#","data-key":t,onClick:a},e[t]))))))))},$=500;function V({initSdk:e,onPreChatScreen:t}){const{setSdk:o,setCustomerId:l,setThreadId:r,setThread:c,setLiveRegionText:s}=h(),[i,u]=(0,a.useState)({}),[d,m]=(0,a.useState)(0),[g,E]=(0,a.useState)(!1),v=(0,a.useRef)(),y=(0,a.useRef)(),b=$>=d;let f=`You have ${$-d} characters remaining`;b||(f=`You have ${d-$} characters too many`),(0,a.useEffect)((()=>{Object.keys(i).length&&document.querySelector(".govuk-error-summary")?.focus()}),[i]);const p=g?"Requesting...":"Request chat";return(0,a.useEffect)((()=>{g&&s(p)}),[g]),n().createElement(n().Fragment,null,n().createElement(B,null),n().createElement("div",{className:"wc-body"},n().createElement("div",{className:"wc-content"},n().createElement("a",{href:"#",className:"wc-back-link govuk-back-link",onClick:t},"What you can use webchat for"),n().createElement(K,{errors:i}),n().createElement("h3",{id:"wc-subtitle",className:"wc-heading","aria-live":"polite"},"Your name and question"),n().createElement("form",null,n().createElement("div",{className:w("wc-form-group","govuk-form-group",i.name&&"govuk-form-group--error")},n().createElement("label",{className:"wc-label govuk-label",htmlFor:"wc-name"},"Your name"),i.name&&n().createElement("p",{className:"govuk-error-message"},n().createElement("span",{className:"govuk-visually-hidden"},"Error:")," ",i.name),n().createElement("input",{ref:v,className:w("wc-input","govuk-input",i.name&&"govuk-input--error"),id:"wc-name",name:"name",type:"text","data-testid":"request-chat-user-name"})),n().createElement("div",{className:"wc-form-group govuk-character-count","data-module":"govuk-character-count","data-maxlength":"500"},n().createElement("div",{className:w("govuk-form-group",i.question&&"govuk-form-group--error")},n().createElement("label",{className:"wc-label govuk-label",htmlFor:"wc-question"},"Your question"),i.question&&n().createElement("p",{className:"govuk-error-message"},n().createElement("span",{className:"govuk-visually-hidden"},"Error:"),i.question),n().createElement("textarea",{ref:y,id:"wc-question",name:"question",rows:"5","aria-describedby":"wc-question-info",onChange:e=>{m(e.target.value.length)},className:w("wc-textarea","govuk-textarea","govuk-js-character-count",!b||i.question?"govuk-textarea--error":""),"data-testid":"request-chat-user-question"}),n().createElement("div",{id:"wc-question-info",className:"wc-hint govuk-hint govuk-char-count__msg",style:{color:b?"":"#d4351c"},"aria-hidden":"true"},f),n().createElement("div",{className:"govuk-character-count__sr-status govuk-visually-hidden","aria-live":"polite"},f))),n().createElement("div",{className:"wc-inset-text govuk-inset-text"},"By selecting 'Request chat' you agree to the terms of our ",n().createElement("a",{href:"https://check-for-flooding.service.gov.uk/privacy-notice",target:"_blank",rel:"noreferrer",className:"govuk-link"},"privacy notice"),"."),n().createElement("button",{className:"wc-button govuk-button govuk-!-margin-top-1","data-module":"govuk-button",onClick:async t=>{if(t.preventDefault(),g)return;const a={};if(0===v.current.value.length&&(a.name="Enter your name"),0===y.current.value.length&&(a.question="Enter your question"),y.current.value.length>$&&(a.question="Your question must be 500 characters or less"),0===Object.keys(a).length)try{E(!0);const t=Y.v4(),a=e(),n=await a.authorize(),s=n?.consumerIdentity.idOnExternalPlatform,i=await a.getThread(t);i.setCustomField("threadid",t),await a.getCustomer().setName(v.current.value),await i.startChat(y.current.value||"Begin conversation"),o(a),l(s),r(t),c(i)}catch(e){console.log("[Request chat Error]",e)}u(a)},"aria-disabled":g},p)))))}function H({children:e}){return n().createElement("div",{className:"wc-footer"},e)}function P({message:e,previousMessage:t}){const a="outbound"===e.direction,o=e?.direction===t?.direction;return n().createElement("li",{className:w("wc-chat__message",a?"outbound":"inbound")},n().createElement("div",{className:"wc-chat__from"},o?null:n().createElement("span",{className:w(a?"outbound":"inbound")},a?e.assignee:"You"),n().createElement("span",{className:"govuk-visually-hidden"},":")),n().createElement("div",{className:w("wc-chat__text",a?"outbound":"inbound")},N(e.text)))}const j=(e,t)=>{(0,a.useEffect)((()=>{if(e){e.style.height="0px";const t=e.scrollHeight;e.style.height=`${t}px`}}),[e,t])},W=(e,t,a)=>{if("UNAVAILABLE"===e)return"Webchat is not currently available";if(!t)return"Connecting to Floodline";switch(t){case"closed":case"resolved":return a?`${a} ended the session`:"Session ended by advisor";default:return a?`You are speaking with ${a}`:"Waiting for an adviser"}};function J({onEndChatScreen:e,onSettingsScreen:t}){const{availability:o,thread:l,isChatOpen:r,settings:c,isKeyboard:s,setLiveRegionText:u}=h(),{messages:d,agent:m,agentStatus:g,isAgentTyping:E}=G(),[w,v]=(0,a.useState)(""),[y,b]=(0,a.useState)(),[f,p]=(0,a.useState)(),[k,S]=(0,a.useState)(!1),_=(0,a.useRef)();j(_.current,w),(0,a.useEffect)((()=>{if(c.scroll&&0!==d.length){const e=document.querySelector(".wc-body");e.scrollTop=e.scrollHeight}}),[d,E]),(0,a.useEffect)((()=>{const e=document.querySelector(".wc-form__label");0===w.length?e.classList.remove("govuk-visually-hidden"):e.classList.add("govuk-visually-hidden")}),[w]),(0,a.useEffect)((()=>{b(m?.nickname||m?.firstName),p(W(o,g,y))}),[m,y,g]),(0,a.useEffect)((()=>(f&&r&&u(`Floodline webchat - ${f}`),()=>{u()})),[f,r]),(0,a.useEffect)((()=>(E&&r&&u(`${y} is typing...`),()=>{u()})),[E,r]),(0,a.useEffect)((()=>{const e=d[d.length-1];return"closed"!==g&&"outbound"===e?.direction&&r&&u(`${y} said: ${e.text}`),()=>{u()}}),[d,r]);const N=()=>{if(0!==_.current.value.length&&"closed"!==g){try{const e=_.current.value.trim();l.sendTextMessage(e),u(`You said: ${e}`)}catch(e){console.log("[Chat Error] sendMessage",e)}v("")}},T=()=>{const e=C(d);document.querySelector("#transcript-download").setAttribute("href",`data:text/plain;charset=utf-8,${e}`)},I=a=>{if("Enter"!==a.key||a.shiftKey)" "===a.key?("end-chat"===a.target.id&&e(a),"wc-settings"===a.target.id&&t(a)):(l.keystroke(),setTimeout((()=>{l.stopTyping()}),i));else switch(a.target.id){case"text-area":a.preventDefault(),N();break;case"end-chat":e(a);break;case"wc-settings":t(a);break;case"transcript-download":T()}};return n().createElement(n().Fragment,null,n().createElement(B,null),n().createElement("div",{className:"wc-status"},n().createElement("p",{className:"wc-status__availability"},f),n().createElement("a",{id:"end-chat",className:"wc-status__link",href:"#","data-module":"govuk-button",role:"button",onClick:e,onKeyDown:I},"End chat")),n().createElement("div",{className:"wc-body",tabIndex:"0"},n().createElement("ul",{className:"wc-chat"},d.length?d.map(((e,t)=>n().createElement(P,{key:e.id,message:e,previousMessage:d[t-1]}))):null,E?n().createElement("li",{className:"wc-chat__message outbound"},n().createElement("div",{className:"wc-chat__from"},y," is typing"),n().createElement("div",{className:"wc-chat__text outbound"},n().createElement("svg",{width:"28",height:"16",x:"0px",y:"0px",viewBox:"0 0 28 16"},n().createElement("circle",{stroke:"none",cx:"3",cy:"8",r:"3",fill:"currentColor"}),n().createElement("circle",{stroke:"none",cx:"14",cy:"8",r:"3",fill:"currentColor"}),n().createElement("circle",{stroke:"none",cx:"25",cy:"8",r:"3",fill:"currentColor"})))):null)),n().createElement(H,null,n().createElement("form",{className:"wc-form"+(k?" wc-focus-within":""),noValidate:!0,onSubmit:e=>{e.preventDefault(),N()}},n().createElement("label",{className:"govuk-label wc-form__label",htmlFor:"wc-form-textarea"},"Your message",n().createElement("span",{className:"govuk-visually-hidden"}," (enter key submits)")),n().createElement("textarea",{ref:_,rows:"1","aria-required":"true",className:"wc-form__textarea",id:"text-area",name:"message",onChange:e=>{v(e.target?.value)},onKeyDown:I,onFocus:()=>{S(s)},onBlur:()=>{S(!1)},value:w}),n().createElement("input",{type:"submit",className:"wc-form__button govuk-button",value:"Send","data-prevent-double-click":"true"})),n().createElement("div",{className:"wc-footer__settings"},n().createElement("a",{href:"#",id:"wc-settings",className:"wc-footer__settings-link","data-module":"govuk-button",onKeyDown:I,onClick:t},"Settings"),n().createElement("a",{href:"#",id:"transcript-download",className:"wc-footer__settings-link","data-module":"govuk-button",download:"floodline-webchat-transcript.txt",onKeyDown:I,onClick:T},"Save chat"))))}function X(){return n().createElement(n().Fragment,null,n().createElement(B,null),n().createElement("div",{className:"wc-body"},n().createElement("h3",{id:"wc-subtitle",className:"govuk-heading-m"},"Webchat is currently not available"),n().createElement("p",{className:"govuk-body-s"},"Try again later, or call Floodline:"),n().createElement("p",{className:"govuk-body-s"},n().createElement("strong",null,"Floodline helpline"),n().createElement("br",null),"Telephone: 0345 988 1188",n().createElement("br",null),"Textphone: 0345 602 6340",n().createElement("br",null),"Open 24 hours a day, 7 days a week",n().createElement("br",null),n().createElement("a",{className:"govuk-link",href:"https://gov.uk/call-charges"},"Find out more about call charges")),n().createElement("p",{className:"govuk-body-s"},"We're running webchat as a trial, it will not always be available.")))}function Z({onChatScreen:e,onEndChatConfirm:t}){const{thread:o,setSdk:l,setCustomerId:r,setThreadId:c,threadId:s,setUnseenCount:i,isKeyboard:u}=h(),{agentStatus:d,setMessages:m}=G(),g=async e=>{e.preventDefault(),window.localStorage.setItem("tmpThreadId",s),o?.lastMessageSeen(),l(),c(),m([]),r(),i(0),"closed"!==d&&o.endChat(),t(e)};(0,a.useEffect)((()=>{u&&setTimeout((()=>{document.querySelector("#confirm-endchat").focus()}),10)}),[]);const E=t=>{"Enter"!==t.key&&" "!==t.key||"confirm-endchat"!==t.target.id||g(t),"Enter"!==t.key&&" "!==t.key||"resume-chat"!==t.target.id||e(t)};return n().createElement(n().Fragment,null,n().createElement(B,null),n().createElement("div",{className:"wc-body"},n().createElement("div",{className:"wc-content"},n().createElement("h3",{id:"wc-subtitle",className:"wc-heading","aria-live":"polite"},"Are you sure you want to end the chat?"),n().createElement("div",{className:"govuk-button-group"},n().createElement("a",{id:"confirm-endchat",href:"#endChat",role:"button",className:"wc-button govuk-button","data-module":"govuk-button",onClick:g,onKeyDown:E},"Yes, end chat"),n().createElement("a",{id:"resume-chat",href:"#resumeChat",role:"button",className:"wc-link govuk-link","data-module":"govuk-button",onClick:e,onKeyDown:E},"No, resume chat")))))}function z({onCancel:e}){const{settings:t,setSettings:o}=h(),[l,r]=(0,a.useState)(t.audio),[c,s]=(0,a.useState)(t.scroll),i=t=>{t.preventDefault(),o({audio:l,scroll:c}),e(t)},u=t=>{"Enter"!==t.key&&" "!==t.key||"settings-save"!==t.target.id?"Enter"!==t.key&&" "!==t.key||"settings-cancel"!==t.target.id||e(t):i(t)};return n().createElement(n().Fragment,null,n().createElement(B,null),n().createElement("div",{className:"wc-body"},n().createElement("div",{className:"wc-content"},n().createElement("fieldset",{className:"govuk-fieldset govuk-!-margin-bottom-4"},n().createElement("legend",{className:"govuk-fieldset__legend govuk-fieldset__legend"},n().createElement("h3",{id:"wc-subtitle",className:"wc-heading"},"Change settings")),n().createElement("div",{className:"govuk-checkboxes govuk-checkboxes--small","data-module":"govuk-checkboxes"},n().createElement("div",{className:"govuk-checkboxes__item"},n().createElement("input",{id:"audio",name:"audio",type:"checkbox",value:"audio",className:"govuk-checkboxes__input",defaultChecked:l,onChange:()=>r(!l)}),n().createElement("label",{className:"wc-label govuk-label govuk-checkboxes__label",htmlFor:"audio"},"Play a sound when receiving a new message")),n().createElement("div",{className:"govuk-checkboxes__item"},n().createElement("input",{id:"scroll",name:"scroll",type:"checkbox",value:"scroll",className:"govuk-checkboxes__input",defaultChecked:c,onChange:()=>s(!c)}),n().createElement("label",{className:"wc-label govuk-label govuk-checkboxes__label",htmlFor:"scroll"},"Scroll automatically to a new message")))),n().createElement("div",{className:"govuk-button-group"},n().createElement("a",{id:"settings-save",href:"#",className:"wc-button govuk-button","data-module":"govuk-button",role:"button",onClick:i,onKeyDown:u},"Save"),n().createElement("a",{id:"settings-cancel",href:"#",className:"wc-link govuk-link","data-module":"govuk-button",role:"button",onClick:e,onKeyDown:u},"Cancel")))))}function Q({onCancel:e}){const t=()=>{const e=window.localStorage.getItem("tmpThreadId");window.location.href=`https://defragroup.eu.qualtrics.com/jfe/form/SV_8dgFSJcxxIfqx5Y?Id=${e}&Source=${window.location.href}`,window.localStorage.removeItem("tmpThreadId")},a=async t=>{window.localStorage.removeItem("tmpThreadId"),e(t)},o=e=>{"Enter"!==e.key&&" "!==e.key||("feedback-close"===e.target.id&&a(e),"feedback-send"===e.target.id&&t())};return n().createElement(n().Fragment,null,n().createElement(B,null),n().createElement("div",{className:"wc-body"},n().createElement("div",{className:"wc-content"},n().createElement("h3",{id:"wc-subtitle",className:"wc-heading"},"Give feedback on Floodline webchat"),n().createElement("p",null,"We're running webchat as a trial. you can ",n().createElement("a",{id:"feedback-send",className:"govuk-link",href:"#",target:"_blank",rel:"noreferrer",onKeyDown:o,onClick:t},"give feedback")," to help us improve it."),n().createElement("p",null,n().createElement("a",{id:"feedback-close",className:"wc-link govuk-link",href:"#","data-module":"govuk-button",role:"button",onKeyDown:o,onClick:a},"Close")))))}const ee="aria-hidden",te="data-wc-inert",ae=e=>{for(const t of document.body.children)"wc-panel"!==t.id&&(e&&!t.getAttribute(ee)?(t.setAttribute(ee,"true"),t.setAttribute(te,"")):t.hasAttribute(te)&&(t.removeAttribute(ee),t.removeAttribute(te)))},ne=(e,t)=>{const[n,o]=(0,a.useState)([]),l=(0,a.useCallback)((e=>{const t=document.querySelector("#wc-panel");"Tab"===e.key&&(t&&!document.activeElement.closest("#wc-panel")&&t.focus(),e.shiftKey?(document.activeElement===n[0]||document.activeElement===document.querySelector("#wc-panel"))&&(n[n.length-1].focus(),e.preventDefault()):document.activeElement===n[n.length-1]&&(n[0].focus(),e.preventDefault()))}),[n]);(0,a.useEffect)((()=>{o((()=>{const e=document.body.querySelectorAll(["#wc-panel a:not([disabled])","#wc-panel button:not([disabled])","#wc-panel select:not([disabled])","#wc-panel input:not([disabled])","#wc-panel textarea:not([disabled])",'#wc-panel *[tabindex="0"]:not([disabled])'].join(","));return Array.from(e).filter((e=>!e.closest("[hidden]")&&!e.closest('[aria-hidden="true"]')))})())}),[e]),(0,a.useEffect)((()=>(t&&(ae(!0),document.querySelector("#wc-panel").focus(),document.addEventListener("keydown",l)),()=>{ae(),document.removeEventListener("keydown",l)})),[n,t])};function oe({initSdk:e,playSound:t}){const{sdk:o,thread:l,availability:r,instigatorId:c,threadId:s,setSdk:i,setChatVisibility:u,setUnseenCount:d,isMobile:m,isKeyboard:g,isChatOpen:E}=h(),[v,b]=(0,a.useState)(s?2:0);ne(v,E);const f=(0,a.useCallback)((e=>{"Escape"!==e.key&&"Esc"!==e.key||(s&&(l?.lastMessageSeen(),d(0)),u(!1),y())}),[l,s,d,u]);(0,a.useEffect)((()=>{E||document.getElementById(c)?.focus()}),[E]),(0,a.useEffect)((()=>(document.body.classList.remove("wc-u-hidden"),document.getElementsByTagName("html")[0].classList.add("wc-u-html"),document.body.classList.add("wc-u-body"),()=>{document.getElementsByTagName("html")[0].classList.remove("wc-u-html"),document.body.classList.remove("wc-u-body")})),[m]),(0,a.useEffect)((()=>(document.addEventListener("keydown",f),()=>{document.removeEventListener("keydown",f)})),[f,u]),(0,a.useEffect)((()=>{E&&s&&(l||i(e()),b(2))}),[E,l,s]);const p=e=>t=>{t.preventDefault(),b(e)},k=p(0),S=p(1),_=p(2),N=p(3),T=p(4),C=p(5);let I;switch(v){case 0:default:I=n().createElement(U,{onContinue:S});break;case 1:I=n().createElement(V,{initSdk:e,onPreChatScreen:k});break;case 2:I=n().createElement(D,{sdk:o,playSound:t,onRecoverError:()=>b(0)},n().createElement(J,{onSettingsScreen:C,onEndChatScreen:N}));break;case 3:I=n().createElement(D,{sdk:o,playSound:t},n().createElement(Z,{onChatScreen:_,onEndChatConfirm:T}));break;case 4:I=n().createElement(Q,{onCancel:()=>{u(!1),y(),b(0)}});break;case 5:I=n().createElement(D,{sdk:o,playSound:t},n().createElement(z,{onCancel:_}))}"UNAVAILABLE"===r&&(I=n().createElement(X,null));const A=n().createElement("div",{id:"wc-panel",role:"dialog",style:{display:E?"flex":"none"},className:w("wc-panel",g&&"wc-focus-visible"),tabIndex:"-1","aria-modal":"true","aria-labelledby":"wc-header wc-subtitle",open:E},n().createElement("div",{className:"wc-panel__inner"},I),n().createElement(q,null));return n().createElement(n().Fragment,null,(0,M.createPortal)(A,document.body))}async function le(e,t){const a=(0,o.createRoot)(e);let r,s;try{const e=await async function(e){const t=await fetch(e);return{availability:(await t.json()).availability||"UNAVAILABLE"}}(t.availabilityEndpoint);s=await R(t.audioUrl),r=e.availability}catch(e){r="UNAVAILABLE"}a.render(n().createElement(E,{availability:r},n().createElement(O,null),n().createElement(F,null),n().createElement(oe,{initSdk:()=>new l.ChatSdk({brandId:t.brandId,channelId:t.channelId,customerId:window.localStorage.getItem(c)||"",environment:t.environment,onError:e=>{console.log("ChatSdk onError",e)}}),playSound:s})))}module.exports=t})();
|
package/dist/server.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{var e={525:(e,t,n)=>{const{authenticate:o,getApiBaseUrl:a,getIsOpen:i,getActivity:s}=n(398);e.exports=async function({clientId:e,clientSecret:t,accessKey:n,accessSecret:r,maxQueueCount:c,skillEndpoint:u,hoursEndpoint:l,wellKnownUri:p="https://cxone.niceincontact.com/.well-known/cxone-configuration",authenticationUri:d="https://cxone.niceincontact.com/auth/token"}){const h="Basic "+Buffer.from(`${encodeURIComponent(e)}:${encodeURIComponent(t)}`).toString("base64"),{tenantId:m,token:
|
|
1
|
+
(()=>{var e={525:(e,t,n)=>{const{authenticate:o,getApiBaseUrl:a,getIsOpen:i,getActivity:s}=n(398);e.exports=async function({clientId:e,clientSecret:t,accessKey:n,accessSecret:r,maxQueueCount:c,skillEndpoint:u,hoursEndpoint:l,wellKnownUri:p="https://cxone.niceincontact.com/.well-known/cxone-configuration",authenticationUri:d="https://cxone.niceincontact.com/auth/token"}){const h="Basic "+Buffer.from(`${encodeURIComponent(e)}:${encodeURIComponent(t)}`).toString("base64"),{tenantId:m,token:y,tokenType:w}=await o({authenticationUri:d,authorisation:h,accessKey:n,accessSecret:r}),g=await a({wellKnownUri:p,tenantId:m}),[{hasCapacity:A,hasAgentsAvailable:k},f]=await Promise.all([s({baseUrl:g,tokenType:w,token:y,skillEndpoint:u,maxQueueCount:c}),i({baseUrl:g,token:y,tokenType:w,hoursEndpoint:l})]);let x="UNAVAILABLE";return f&&k&&A?x="AVAILABLE":f&&k&&!A&&(x="EXISTING"),{date:new Date,availability:x}}},398:(e,t,n)=>{const o=n(45),a=n(938),i=n(158),{isWithinHours:s}=n(792),r="application/x-www-form-urlencoded";e.exports={authenticate:async({authenticationUri:e,authorisation:t,accessKey:n,accessSecret:s})=>{const c=new URL(e),u=o.stringify({grant_type:"password",username:n,password:s}),l=await a.post(c.href,u,{signal:AbortSignal.timeout(3e3),headers:{Host:c.host,"Content-Type":r,Authorization:t}});return{token:l.data.access_token,tokenType:l.data.token_type,tenantId:i(l.data.id_token)?.tenantId}},getApiBaseUrl:async({wellKnownUri:e,tenantId:t})=>{const n=new URL(e);n.searchParams.set("tenantId",t);const{data:o}=await a.get(n.href,{headers:{Host:n.host},signal:AbortSignal.timeout(3e3)});return o.api_endpoint},getIsOpen:async({baseUrl:e,token:t,tokenType:n,hoursEndpoint:o})=>{const i=new URL(o,e),c=(await a.get(i.href,{signal:AbortSignal.timeout(3e3),headers:{Host:i.host,Authorization:`${n} ${t}`,"Content-Type":r}})).data.resultSet.hoursOfOperationProfiles[0].days;return s(c)},getActivity:async({tokenType:e,token:t,baseUrl:n,skillEndpoint:o,maxQueueCount:i})=>{const s=new URL(o,n),c=(await a.get(s.href,{signal:AbortSignal.timeout(3e3),headers:{Host:s.host,Authorization:`${e} ${t}`,"Content-Type":r}})).data.skillActivity[0];return{hasCapacity:c.queueCount<i,hasAgentsAvailable:c.agentsAvailable>=1}}}},792:(e,t,n)=>{const{DateTime:o}=n(168),a=e=>Number(e.split(":")[0]);e.exports={isWithinHours:(e,t)=>{const n=t?o.fromISO(t):o.local();n.setZone("Europe/London");const i=t?new Date(t):new Date,s=i.toLocaleDateString("en-GB",{weekday:"long"}),r=i.toLocaleDateString("en-GB").split("/"),c=e.find((e=>e.day===s)),u=o.local(Number(r[2]),Number(r[1]),Number(r[0]),a(c.openTime)),l=o.local(Number(r[2]),Number(r[1]),Number(r[0]),a(c.closeTime));return n.diff(u).milliseconds>=0&&n.diff(l).milliseconds<=0}}},938:e=>{"use strict";e.exports=require("axios")},158:e=>{"use strict";e.exports=require("jwt-decode")},168:e=>{"use strict";e.exports=require("luxon")},45:e=>{"use strict";e.exports=require("querystring")}},t={},n=function n(o){var a=t[o];if(void 0!==a)return a.exports;var i=t[o]={exports:{}};return e[o](i,i.exports,n),i.exports}(525);module.exports=n})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defra/flood-webchat",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"browser": "dist/client.js",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"@nice-devone/nice-cxone-chat-web-sdk": "^1.18.0",
|
|
64
64
|
"axios": "^1.7.2",
|
|
65
65
|
"html-react-parser": "^5.1.10",
|
|
66
|
-
"jwt-decode": "^
|
|
66
|
+
"jwt-decode": "^3.1.2",
|
|
67
67
|
"lodash.debounce": "^4.0.8",
|
|
68
68
|
"luxon": "^3.4.3",
|
|
69
69
|
"querystring": "^0.2.1",
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import React, { useEffect, useRef } from 'react'
|
|
2
2
|
|
|
3
|
+
import { useApp } from '../../store/app/useApp'
|
|
3
4
|
import { classnames } from '../../lib/classnames'
|
|
4
|
-
import { Panel } from '../panel/panel.jsx'
|
|
5
|
-
import { SkipLink } from '../skip-link.jsx'
|
|
6
|
-
|
|
7
|
-
import { useApp } from '../../store/useApp'
|
|
8
5
|
import { historyPushState, historyReplaceState } from '../../lib/history.js'
|
|
9
6
|
import { LiveRegion } from '../live-region.jsx'
|
|
10
7
|
|
|
@@ -82,6 +79,11 @@ export function Availability () {
|
|
|
82
79
|
}, [threadId, isChatOpen, availability])
|
|
83
80
|
|
|
84
81
|
switch (availability) {
|
|
82
|
+
case 'EXISTING':
|
|
83
|
+
case 'UNAVAILABLE':
|
|
84
|
+
return (
|
|
85
|
+
<p className='govuk-body'>When it is available, a 'start chat' link will appear.</p>
|
|
86
|
+
)
|
|
85
87
|
case 'AVAILABLE':
|
|
86
88
|
return (
|
|
87
89
|
<>
|
|
@@ -113,15 +115,9 @@ export function Availability () {
|
|
|
113
115
|
</a>
|
|
114
116
|
</div>
|
|
115
117
|
</div>
|
|
116
|
-
{!isChatOpen
|
|
117
|
-
<SkipLink />
|
|
118
|
+
{!isChatOpen && <LiveRegion />}
|
|
118
119
|
</>
|
|
119
120
|
)
|
|
120
|
-
case 'EXISTING':
|
|
121
|
-
case 'UNAVAILABLE':
|
|
122
|
-
return (
|
|
123
|
-
<p className='govuk-body'>When it is available, a 'start chat' link will appear.</p>
|
|
124
|
-
)
|
|
125
121
|
default:
|
|
126
122
|
return (
|
|
127
123
|
<p className='govuk-body'>Checking availability</p>
|
|
@@ -6,15 +6,19 @@ import { PanelHeader } from '../panel/panel-header.jsx'
|
|
|
6
6
|
import { PanelFooter } from '../panel/panel-footer.jsx'
|
|
7
7
|
import { Message } from '../message/message.jsx'
|
|
8
8
|
|
|
9
|
-
import { useApp } from '../../store/useApp.js'
|
|
9
|
+
import { useApp } from '../../store/app/useApp.js'
|
|
10
|
+
import { useSdk } from '../../store/sdk/useSdk.js'
|
|
10
11
|
import { useTextareaAutosize } from '../../hooks/useTextareaAutosize.js'
|
|
11
12
|
import { formatTranscript } from '../../lib/transform-messages.js'
|
|
12
13
|
import { agentStatusHeadline } from '../../lib/agent-status-headline.js'
|
|
13
14
|
|
|
14
15
|
export function Chat ({ onEndChatScreen, onSettingsScreen }) {
|
|
15
|
-
const { availability, thread,
|
|
16
|
+
const { availability, thread, isChatOpen, settings, isKeyboard, setLiveRegionText } = useApp()
|
|
17
|
+
const { messages, agent, agentStatus, isAgentTyping } = useSdk()
|
|
16
18
|
|
|
17
19
|
const [userMessage, setUserMessage] = useState('')
|
|
20
|
+
const [agentName, setAgentName] = useState()
|
|
21
|
+
const [connectionHeadlineText, setConnectionHeadlineText] = useState()
|
|
18
22
|
const [focusVisibleWithin, setFocusVisibleWithin] = useState(false)
|
|
19
23
|
|
|
20
24
|
const messageRef = useRef()
|
|
@@ -42,19 +46,20 @@ export function Chat ({ onEndChatScreen, onSettingsScreen }) {
|
|
|
42
46
|
setUserMessage(e.target?.value)
|
|
43
47
|
}
|
|
44
48
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
setAgentName(agent?.nickname || agent?.firstName)
|
|
51
|
+
setConnectionHeadlineText(agentStatusHeadline(availability, agentStatus, agentName))
|
|
52
|
+
}, [agent, agentName, agentStatus])
|
|
48
53
|
|
|
49
54
|
useEffect(() => {
|
|
50
|
-
if (connectionHeadlineText) {
|
|
55
|
+
if (connectionHeadlineText && isChatOpen) {
|
|
51
56
|
setLiveRegionText(`Floodline webchat - ${connectionHeadlineText}`)
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
return () => {
|
|
55
60
|
setLiveRegionText()
|
|
56
61
|
}
|
|
57
|
-
}, [connectionHeadlineText])
|
|
62
|
+
}, [connectionHeadlineText, isChatOpen])
|
|
58
63
|
|
|
59
64
|
useEffect(() => {
|
|
60
65
|
if (isAgentTyping && isChatOpen) {
|
|
@@ -69,14 +74,14 @@ export function Chat ({ onEndChatScreen, onSettingsScreen }) {
|
|
|
69
74
|
useEffect(() => {
|
|
70
75
|
const lastAgentMessage = messages[messages.length - 1]
|
|
71
76
|
|
|
72
|
-
if (agentStatus !== 'closed' && lastAgentMessage?.direction === 'outbound') {
|
|
77
|
+
if (agentStatus !== 'closed' && lastAgentMessage?.direction === 'outbound' && isChatOpen) {
|
|
73
78
|
setLiveRegionText(`${agentName} said: ${lastAgentMessage.text}`)
|
|
74
79
|
}
|
|
75
80
|
|
|
76
81
|
return () => {
|
|
77
82
|
setLiveRegionText()
|
|
78
83
|
}
|
|
79
|
-
}, [messages])
|
|
84
|
+
}, [messages, isChatOpen])
|
|
80
85
|
|
|
81
86
|
const sendMessage = () => {
|
|
82
87
|
if (messageRef.current.value.length === 0 || agentStatus === 'closed') {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import React, { useEffect, useState, useCallback } from 'react'
|
|
2
2
|
import debounce from 'lodash.debounce'
|
|
3
|
-
import { useApp } from '../store/useApp'
|
|
3
|
+
import { useApp } from '../store/app/useApp'
|
|
4
|
+
import { useSdk } from '../store/sdk/useSdk'
|
|
4
5
|
|
|
5
6
|
const DEBOUNCE_MILLISECONDS = 2000
|
|
6
7
|
|
|
7
8
|
export const LiveRegion = () => {
|
|
8
|
-
const {
|
|
9
|
+
const { liveRegionText, setLiveRegionText } = useApp()
|
|
10
|
+
const { agentStatus } = useSdk()
|
|
9
11
|
|
|
10
12
|
const [textA, setTextA] = useState()
|
|
11
13
|
const [textB, setTextB] = useState()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useEffect, useState, useCallback } from 'react'
|
|
2
2
|
import { createPortal } from 'react-dom'
|
|
3
|
-
import { classnames } from '../../lib/classnames.js'
|
|
4
3
|
|
|
4
|
+
import { classnames } from '../../lib/classnames.js'
|
|
5
5
|
import { PreChat } from '../screens/pre-chat.jsx'
|
|
6
6
|
import { RequestChat } from '../screens/request-chat.jsx'
|
|
7
7
|
import { Chat } from '../chat/chat.jsx'
|
|
@@ -11,18 +11,17 @@ import { Settings } from '../screens/settings.jsx'
|
|
|
11
11
|
import { Feedback } from '../screens/feedback.jsx'
|
|
12
12
|
import { LiveRegion } from '../live-region.jsx'
|
|
13
13
|
|
|
14
|
-
import { useApp } from '../../store/useApp.js'
|
|
15
|
-
import { useChatSdk } from '../../store/useChatSdk.js'
|
|
14
|
+
import { useApp } from '../../store/app/useApp.js'
|
|
16
15
|
import { useFocusedElements } from '../../hooks/useFocusedElements.js'
|
|
17
16
|
import { historyReplaceState } from '../../lib/history.js'
|
|
17
|
+
import { SdkProvider } from '../../store/sdk/SdkProvider.jsx'
|
|
18
18
|
|
|
19
|
-
export function Panel () {
|
|
20
|
-
const { sdk, availability, instigatorId,
|
|
21
|
-
const { fetchThread, fetchMessages } = useChatSdk(sdk)
|
|
19
|
+
export function Panel ({ initSdk, playSound }) {
|
|
20
|
+
const { sdk, thread, availability, instigatorId, threadId, setSdk, setChatVisibility, setUnseenCount, isMobile, isKeyboard, isChatOpen } = useApp()
|
|
22
21
|
|
|
23
22
|
const [screen, setScreen] = useState(threadId ? 2 : 0)
|
|
24
23
|
|
|
25
|
-
useFocusedElements(screen)
|
|
24
|
+
useFocusedElements(screen, isChatOpen)
|
|
26
25
|
|
|
27
26
|
const onEscapeKey = useCallback(e => {
|
|
28
27
|
if (e.key === 'Escape' || e.key === 'Esc') {
|
|
@@ -39,18 +38,19 @@ export function Panel () {
|
|
|
39
38
|
* Focus previously focused element when closing the webchat
|
|
40
39
|
*/
|
|
41
40
|
useEffect(() => {
|
|
42
|
-
|
|
41
|
+
if (!isChatOpen) {
|
|
43
42
|
document.getElementById(instigatorId)?.focus()
|
|
44
43
|
}
|
|
45
|
-
}, [])
|
|
44
|
+
}, [isChatOpen])
|
|
46
45
|
|
|
47
46
|
/**
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
* We need ammend classes on the body element to handle mobile behaviour
|
|
48
|
+
*/
|
|
50
49
|
useEffect(() => {
|
|
51
50
|
document.body.classList.remove('wc-u-hidden')
|
|
52
51
|
document.getElementsByTagName('html')[0].classList.add('wc-u-html')
|
|
53
52
|
document.body.classList.add('wc-u-body')
|
|
53
|
+
|
|
54
54
|
return () => {
|
|
55
55
|
document.getElementsByTagName('html')[0].classList.remove('wc-u-html')
|
|
56
56
|
document.body.classList.remove('wc-u-body')
|
|
@@ -69,35 +69,17 @@ export function Panel () {
|
|
|
69
69
|
}, [onEscapeKey, setChatVisibility])
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
|
-
*
|
|
72
|
+
* Initialize SDK if theres a thread ID
|
|
73
73
|
*/
|
|
74
74
|
useEffect(() => {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
setThread(fetchedThread)
|
|
79
|
-
|
|
80
|
-
const fetchedMessages = await fetchMessages(fetchedThread, threadId)
|
|
81
|
-
setMessages(fetchedMessages)
|
|
82
|
-
} catch (err) {
|
|
83
|
-
console.log('[Chat Error] fetchThread', err)
|
|
84
|
-
|
|
85
|
-
setThreadId()
|
|
86
|
-
setThread()
|
|
87
|
-
setScreen(0)
|
|
75
|
+
if (isChatOpen && threadId) {
|
|
76
|
+
if (!thread) {
|
|
77
|
+
setSdk(initSdk())
|
|
88
78
|
}
|
|
89
|
-
}
|
|
90
79
|
|
|
91
|
-
|
|
92
|
-
if (thread) {
|
|
93
|
-
thread.lastMessageSeen()
|
|
94
|
-
setUnseenCount(0)
|
|
95
|
-
setScreen(2)
|
|
96
|
-
} else {
|
|
97
|
-
recover()
|
|
98
|
-
}
|
|
80
|
+
setScreen(2)
|
|
99
81
|
}
|
|
100
|
-
}, [thread, threadId])
|
|
82
|
+
}, [isChatOpen, thread, threadId])
|
|
101
83
|
|
|
102
84
|
const handleScreenChange = newScreen => e => {
|
|
103
85
|
e.preventDefault()
|
|
@@ -118,25 +100,38 @@ export function Panel () {
|
|
|
118
100
|
ScreenComponent = <PreChat onContinue={goToRequestChatScreen} />
|
|
119
101
|
break
|
|
120
102
|
case 1:
|
|
121
|
-
ScreenComponent = <RequestChat onPreChatScreen={goToPreChatScreen} />
|
|
103
|
+
ScreenComponent = <RequestChat initSdk={initSdk} onPreChatScreen={goToPreChatScreen} />
|
|
122
104
|
break
|
|
123
105
|
case 2:
|
|
124
|
-
ScreenComponent =
|
|
106
|
+
ScreenComponent = (
|
|
107
|
+
<SdkProvider sdk={sdk} playSound={playSound} onRecoverError={() => setScreen(0)}>
|
|
108
|
+
<Chat onSettingsScreen={goToSettingsScreen} onEndChatScreen={goToEndChatScreen} />
|
|
109
|
+
</SdkProvider>
|
|
110
|
+
)
|
|
125
111
|
break
|
|
126
112
|
case 3:
|
|
127
|
-
ScreenComponent =
|
|
113
|
+
ScreenComponent = (
|
|
114
|
+
<SdkProvider sdk={sdk} playSound={playSound}>
|
|
115
|
+
<EndChat onChatScreen={goToChatScreen} onEndChatConfirm={goToFeedbackScreen} />
|
|
116
|
+
</SdkProvider>
|
|
117
|
+
)
|
|
128
118
|
break
|
|
129
119
|
case 4:
|
|
130
120
|
ScreenComponent = (
|
|
131
121
|
<Feedback onCancel={() => {
|
|
132
122
|
setChatVisibility(false)
|
|
133
123
|
historyReplaceState()
|
|
124
|
+
setScreen(0)
|
|
134
125
|
}}
|
|
135
126
|
/>
|
|
136
127
|
)
|
|
137
128
|
break
|
|
138
129
|
case 5:
|
|
139
|
-
ScreenComponent =
|
|
130
|
+
ScreenComponent = (
|
|
131
|
+
<SdkProvider sdk={sdk} playSound={playSound}>
|
|
132
|
+
<Settings onCancel={goToChatScreen} />
|
|
133
|
+
</SdkProvider>
|
|
134
|
+
)
|
|
140
135
|
break
|
|
141
136
|
default:
|
|
142
137
|
ScreenComponent = <PreChat onContinue={goToRequestChatScreen} />
|
|
@@ -147,7 +142,7 @@ export function Panel () {
|
|
|
147
142
|
}
|
|
148
143
|
|
|
149
144
|
const Component = (
|
|
150
|
-
<div id='wc-panel' role='dialog' className={classnames('wc-panel', isKeyboard && 'wc-focus-visible')} tabIndex='-1' aria-modal='true' aria-labelledby='wc-header wc-subtitle'>
|
|
145
|
+
<div id='wc-panel' role='dialog' style={{ display: isChatOpen ? 'flex' : 'none' }} className={classnames('wc-panel', isKeyboard && 'wc-focus-visible')} tabIndex='-1' aria-modal='true' aria-labelledby='wc-header wc-subtitle' open={isChatOpen}>
|
|
151
146
|
<div className='wc-panel__inner'>
|
|
152
147
|
{ScreenComponent}
|
|
153
148
|
</div>
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import React, { useEffect } from 'react'
|
|
2
2
|
import { PanelHeader } from '../panel/panel-header.jsx'
|
|
3
|
-
import { useApp } from '../../store/useApp.js'
|
|
3
|
+
import { useApp } from '../../store/app/useApp.js'
|
|
4
|
+
import { useSdk } from '../../store/sdk/useSdk.js'
|
|
4
5
|
|
|
5
6
|
export function EndChat ({ onChatScreen, onEndChatConfirm }) {
|
|
6
|
-
const {
|
|
7
|
+
const { thread, setSdk, setCustomerId, setThreadId, threadId, setUnseenCount, isKeyboard } = useApp()
|
|
8
|
+
const { agentStatus, setMessages } = useSdk()
|
|
7
9
|
|
|
8
10
|
const confirmEndChat = async e => {
|
|
9
11
|
e.preventDefault()
|
|
10
12
|
|
|
11
13
|
window.localStorage.setItem('tmpThreadId', threadId)
|
|
14
|
+
thread?.lastMessageSeen()
|
|
15
|
+
setSdk()
|
|
12
16
|
setThreadId()
|
|
13
17
|
setMessages([])
|
|
14
18
|
setCustomerId()
|
|
15
|
-
thread.lastMessageSeen()
|
|
16
19
|
setUnseenCount(0)
|
|
17
20
|
|
|
18
21
|
// End chat if still open
|
|
@@ -3,15 +3,14 @@ import * as uuid from 'uuid'
|
|
|
3
3
|
|
|
4
4
|
import { classnames } from '../../lib/classnames.js'
|
|
5
5
|
import { PanelHeader } from '../panel/panel-header.jsx'
|
|
6
|
-
import { useApp } from '../../store/useApp.js'
|
|
7
|
-
import { useChatSdk } from '../../store/useChatSdk.js'
|
|
6
|
+
import { useApp } from '../../store/app/useApp.js'
|
|
8
7
|
import { ErrorSummary } from '../errorSummary/error-summary.jsx'
|
|
9
8
|
|
|
10
9
|
const QUESTION_MAX_LENGTH = 500
|
|
11
10
|
|
|
12
|
-
export function RequestChat ({ onPreChatScreen }) {
|
|
13
|
-
const {
|
|
14
|
-
|
|
11
|
+
export function RequestChat ({ initSdk, onPreChatScreen }) {
|
|
12
|
+
const { setSdk, setCustomerId, setThreadId, setThread, setLiveRegionText } = useApp()
|
|
13
|
+
|
|
15
14
|
const [errors, setErrors] = useState({})
|
|
16
15
|
const [questionLength, setQuestionLength] = useState(0)
|
|
17
16
|
const [isButtonDisabled, setButtonDisabled] = useState(false)
|
|
@@ -72,17 +71,22 @@ export function RequestChat ({ onPreChatScreen }) {
|
|
|
72
71
|
try {
|
|
73
72
|
setButtonDisabled(true)
|
|
74
73
|
const threadId = uuid.v4()
|
|
75
|
-
const customerId = await fetchCustomerId()
|
|
76
|
-
const thread = await fetchThread(threadId)
|
|
77
74
|
|
|
78
|
-
sdk
|
|
75
|
+
const sdk = initSdk()
|
|
76
|
+
const conn = await sdk.authorize()
|
|
77
|
+
const customerId = conn?.consumerIdentity.idOnExternalPlatform
|
|
78
|
+
|
|
79
|
+
const thread = await sdk.getThread(threadId)
|
|
80
|
+
thread.setCustomField('threadid', threadId)
|
|
81
|
+
|
|
82
|
+
await sdk.getCustomer().setName(nameRef.current.value)
|
|
79
83
|
|
|
80
84
|
await thread.startChat(questionRef.current.value || 'Begin conversation')
|
|
81
85
|
|
|
86
|
+
setSdk(sdk)
|
|
82
87
|
setCustomerId(customerId)
|
|
83
88
|
setThreadId(threadId)
|
|
84
89
|
setThread(thread)
|
|
85
|
-
thread.setCustomField('threadid', threadId)
|
|
86
90
|
} catch (err) {
|
|
87
91
|
console.log('[Request chat Error]', err)
|
|
88
92
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
import { PanelHeader } from '../panel/panel-header.jsx'
|
|
3
3
|
|
|
4
|
-
import { useApp } from '../../store/useApp.js'
|
|
4
|
+
import { useApp } from '../../store/app/useApp.js'
|
|
5
5
|
|
|
6
6
|
export function Settings ({ onCancel }) {
|
|
7
7
|
const { settings, setSettings } = useApp()
|
|
@@ -32,7 +32,7 @@ export const getFocusableElements = () => {
|
|
|
32
32
|
return Array.from(elements).filter(e => !e.closest('[hidden]') && !e.closest('[aria-hidden="true"]'))
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
const useFocusedElements = screen => {
|
|
35
|
+
const useFocusedElements = (screen, isChatOpen) => {
|
|
36
36
|
const [panelElements, setPanelElements] = useState([])
|
|
37
37
|
|
|
38
38
|
const onKeyDown = useCallback(e => {
|
|
@@ -63,18 +63,20 @@ const useFocusedElements = screen => {
|
|
|
63
63
|
}, [screen])
|
|
64
64
|
|
|
65
65
|
useEffect(() => {
|
|
66
|
-
|
|
66
|
+
if (isChatOpen) {
|
|
67
|
+
setAriaHidden(true)
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
const panelElement = document.querySelector('#wc-panel')
|
|
70
|
+
panelElement.focus()
|
|
70
71
|
|
|
71
|
-
|
|
72
|
+
document.addEventListener('keydown', onKeyDown)
|
|
73
|
+
}
|
|
72
74
|
|
|
73
75
|
return () => {
|
|
74
76
|
setAriaHidden()
|
|
75
77
|
document.removeEventListener('keydown', onKeyDown)
|
|
76
78
|
}
|
|
77
|
-
}, [panelElements])
|
|
79
|
+
}, [panelElements, isChatOpen])
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
export { useFocusedElements }
|
package/src/client/index.jsx
CHANGED
|
@@ -3,31 +3,44 @@ import { createRoot } from 'react-dom/client'
|
|
|
3
3
|
import { ChatSdk } from '@nice-devone/nice-cxone-chat-web-sdk'
|
|
4
4
|
import { Availability } from './components/availability/availability.jsx'
|
|
5
5
|
import { checkAvailability } from './lib/check-availability'
|
|
6
|
-
import { AppProvider } from './store/AppProvider.jsx'
|
|
6
|
+
import { AppProvider } from './store/app/AppProvider.jsx'
|
|
7
7
|
import { CUSTOMER_ID_STORAGE_KEY } from './store/constants.js'
|
|
8
8
|
import { messageNotification } from './lib/message-notification.js'
|
|
9
|
+
import { SkipLink } from './components/skip-link.jsx'
|
|
10
|
+
import { Panel } from './components/panel/panel.jsx'
|
|
9
11
|
|
|
10
12
|
export async function init (container, options) {
|
|
11
|
-
const sdk = new ChatSdk({
|
|
12
|
-
brandId: options.brandId,
|
|
13
|
-
channelId: options.channelId,
|
|
14
|
-
customerId: window.localStorage.getItem(CUSTOMER_ID_STORAGE_KEY) || '',
|
|
15
|
-
environment: options.environment
|
|
16
|
-
})
|
|
17
|
-
|
|
18
13
|
const root = createRoot(container)
|
|
14
|
+
|
|
15
|
+
const initSdk = () => {
|
|
16
|
+
return new ChatSdk({
|
|
17
|
+
brandId: options.brandId,
|
|
18
|
+
channelId: options.channelId,
|
|
19
|
+
customerId: window.localStorage.getItem(CUSTOMER_ID_STORAGE_KEY) || '',
|
|
20
|
+
environment: options.environment,
|
|
21
|
+
onError: error => {
|
|
22
|
+
console.log('ChatSdk onError', error)
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
19
27
|
let availability
|
|
20
28
|
let playSound
|
|
29
|
+
|
|
21
30
|
try {
|
|
22
31
|
const result = await checkAvailability(options.availabilityEndpoint)
|
|
32
|
+
|
|
23
33
|
playSound = await messageNotification(options.audioUrl)
|
|
24
34
|
availability = result.availability
|
|
25
35
|
} catch (e) {
|
|
26
36
|
availability = 'UNAVAILABLE'
|
|
27
37
|
}
|
|
38
|
+
|
|
28
39
|
root.render(
|
|
29
|
-
<AppProvider
|
|
40
|
+
<AppProvider availability={availability}>
|
|
30
41
|
<Availability />
|
|
42
|
+
<SkipLink />
|
|
43
|
+
<Panel initSdk={initSdk} playSound={playSound} />
|
|
31
44
|
</AppProvider>
|
|
32
45
|
)
|
|
33
46
|
}
|
|
@@ -1,84 +1,14 @@
|
|
|
1
1
|
import React, { createContext, useEffect, useReducer, useMemo } from 'react'
|
|
2
|
-
import { ChatEvent } from '@nice-devone/nice-cxone-chat-web-sdk'
|
|
2
|
+
// import { ChatEvent } from '@nice-devone/nice-cxone-chat-web-sdk'
|
|
3
3
|
|
|
4
4
|
import { initialState, reducer } from './reducer.js'
|
|
5
|
-
import { CUSTOMER_ID_STORAGE_KEY, THREAD_ID_STORAGE_KEY, SETTINGS_STORAGE_KEY } from '
|
|
5
|
+
import { CUSTOMER_ID_STORAGE_KEY, THREAD_ID_STORAGE_KEY, SETTINGS_STORAGE_KEY } from '../constants.js'
|
|
6
6
|
|
|
7
7
|
export const AppContext = createContext(initialState)
|
|
8
8
|
|
|
9
|
-
export const AppProvider = ({
|
|
9
|
+
export const AppProvider = ({ availability, children }) => {
|
|
10
10
|
const [state, dispatch] = useReducer(reducer, initialState)
|
|
11
11
|
|
|
12
|
-
/**
|
|
13
|
-
* SDK event handlers
|
|
14
|
-
*/
|
|
15
|
-
const onLiveChatRecovered = e => {
|
|
16
|
-
dispatch({ type: 'SET_AGENT', payload: e.detail.data.inboxAssignee })
|
|
17
|
-
dispatch({ type: 'SET_AGENT_STATUS', payload: e.detail.data.contact.status })
|
|
18
|
-
dispatch({ type: 'SET_UNSEEN_COUNT', payload: e.detail.data.contact.customerStatistics.unseenMessagesCount })
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const onAssignedAgentChanged = e => {
|
|
22
|
-
dispatch({ type: 'SET_AGENT', payload: e.detail.data.inboxAssignee })
|
|
23
|
-
dispatch({ type: 'SET_AGENT_STATUS', payload: e.detail.data.case.status })
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const onAgentTypingStarted = () => {
|
|
27
|
-
dispatch({ type: 'SET_AGENT_TYPING', payload: true })
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const onAgentTypingEnded = () => {
|
|
31
|
-
dispatch({ type: 'SET_AGENT_TYPING', payload: false })
|
|
32
|
-
dispatch({ type: 'SET_LIVE_REGION_TEXT' })
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const onMessageCreated = e => {
|
|
36
|
-
dispatch({ type: 'SET_MESSAGE', payload: e.detail.data.message })
|
|
37
|
-
dispatch({ type: 'SET_AGENT_STATUS', payload: e.detail.data.case.status })
|
|
38
|
-
dispatch({ type: 'SET_UNSEEN_COUNT', payload: e.detail.data.case.customerStatistics.unseenMessagesCount })
|
|
39
|
-
|
|
40
|
-
const isAudioOn = JSON.parse(window.localStorage.getItem(SETTINGS_STORAGE_KEY)).audio
|
|
41
|
-
|
|
42
|
-
if (isAudioOn && e.detail.data.message.direction === 'outbound' && playSound) {
|
|
43
|
-
playSound()
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const onContactStatusChanged = e => {
|
|
48
|
-
dispatch({ type: 'SET_AGENT_STATUS', payload: e.detail.data.case.status })
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const onMatchMedia = e => {
|
|
52
|
-
dispatch({ type: 'TOGGLE_IS_MOBILE', payload: e.matches })
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const onKeydown = () => {
|
|
56
|
-
dispatch({ type: 'TOGGLE_IS_KEYBOARD', payload: true })
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const onPointerdown = () => {
|
|
60
|
-
dispatch({ type: 'TOGGLE_IS_KEYBOARD', payload: false })
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
useEffect(() => {
|
|
64
|
-
sdk.onChatEvent(ChatEvent.LIVECHAT_RECOVERED, onLiveChatRecovered)
|
|
65
|
-
sdk.onChatEvent(ChatEvent.MESSAGE_CREATED, onMessageCreated)
|
|
66
|
-
sdk.onChatEvent(ChatEvent.AGENT_TYPING_STARTED, onAgentTypingStarted)
|
|
67
|
-
sdk.onChatEvent(ChatEvent.AGENT_TYPING_ENDED, onAgentTypingEnded)
|
|
68
|
-
sdk.onChatEvent(ChatEvent.ASSIGNED_AGENT_CHANGED, onAssignedAgentChanged)
|
|
69
|
-
sdk.onChatEvent(ChatEvent.CONTACT_STATUS_CHANGED, onContactStatusChanged)
|
|
70
|
-
// We need to know if it is a mobile and if it is a keyboard interaction
|
|
71
|
-
window.matchMedia('(max-width: 640px)').addEventListener('change', onMatchMedia)
|
|
72
|
-
window.addEventListener('keydown', onKeydown)
|
|
73
|
-
window.addEventListener('pointerdown', onPointerdown)
|
|
74
|
-
// Tidying up
|
|
75
|
-
return () => {
|
|
76
|
-
window.removeEventListener('change', onMatchMedia)
|
|
77
|
-
window.removeEventListener('keydown', onKeydown)
|
|
78
|
-
window.removeEventListener('pointerdown', onPointerdown)
|
|
79
|
-
}
|
|
80
|
-
}, [sdk])
|
|
81
|
-
|
|
82
12
|
/**
|
|
83
13
|
* Initialize customerId, threadId and whether the webchat should be open
|
|
84
14
|
*/
|
|
@@ -113,9 +43,38 @@ export const AppProvider = ({ sdk, availability, playSound, children }) => {
|
|
|
113
43
|
}
|
|
114
44
|
}, [])
|
|
115
45
|
|
|
46
|
+
const onMatchMedia = e => {
|
|
47
|
+
dispatch({ type: 'TOGGLE_IS_MOBILE', payload: e.matches })
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const onKeydown = () => {
|
|
51
|
+
dispatch({ type: 'TOGGLE_IS_KEYBOARD', payload: true })
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const onPointerdown = () => {
|
|
55
|
+
dispatch({ type: 'TOGGLE_IS_KEYBOARD', payload: false })
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
// We need to know if it is a mobile and if it is a keyboard interaction
|
|
60
|
+
window.matchMedia('(max-width: 640px)').addEventListener('change', onMatchMedia)
|
|
61
|
+
window.addEventListener('keydown', onKeydown)
|
|
62
|
+
window.addEventListener('pointerdown', onPointerdown)
|
|
63
|
+
|
|
64
|
+
return () => {
|
|
65
|
+
window.removeEventListener('change', onMatchMedia)
|
|
66
|
+
window.removeEventListener('keydown', onKeydown)
|
|
67
|
+
window.removeEventListener('pointerdown', onPointerdown)
|
|
68
|
+
}
|
|
69
|
+
}, [])
|
|
70
|
+
|
|
116
71
|
/**
|
|
117
72
|
* State update functions
|
|
118
73
|
*/
|
|
74
|
+
const setSdk = payload => {
|
|
75
|
+
dispatch({ type: 'SET_SDK', payload })
|
|
76
|
+
}
|
|
77
|
+
|
|
119
78
|
const setChatVisibility = payload => {
|
|
120
79
|
dispatch({ type: 'SET_CHAT_VISIBILITY', payload })
|
|
121
80
|
}
|
|
@@ -132,10 +91,6 @@ export const AppProvider = ({ sdk, availability, playSound, children }) => {
|
|
|
132
91
|
dispatch({ type: 'SET_THREAD', payload: thread })
|
|
133
92
|
}
|
|
134
93
|
|
|
135
|
-
const setMessages = messages => {
|
|
136
|
-
dispatch({ type: 'SET_MESSAGES', payload: messages })
|
|
137
|
-
}
|
|
138
|
-
|
|
139
94
|
const setSettings = data => {
|
|
140
95
|
dispatch({ type: 'SET_SETTINGS', payload: data })
|
|
141
96
|
}
|
|
@@ -157,22 +112,15 @@ export const AppProvider = ({ sdk, availability, playSound, children }) => {
|
|
|
157
112
|
*/
|
|
158
113
|
const store = useMemo(() => ({
|
|
159
114
|
...state,
|
|
160
|
-
|
|
115
|
+
setSdk,
|
|
161
116
|
setSettings,
|
|
162
117
|
setCustomerId,
|
|
163
118
|
setThreadId,
|
|
164
119
|
setThread,
|
|
165
|
-
setMessages,
|
|
166
120
|
setUnseenCount,
|
|
167
121
|
setChatVisibility,
|
|
168
122
|
setInstigatorId,
|
|
169
|
-
setLiveRegionText
|
|
170
|
-
onLiveChatRecovered,
|
|
171
|
-
onAssignedAgentChanged,
|
|
172
|
-
onAgentTypingStarted,
|
|
173
|
-
onAgentTypingEnded,
|
|
174
|
-
onMessageCreated,
|
|
175
|
-
onContactStatusChanged
|
|
123
|
+
setLiveRegionText
|
|
176
124
|
}))
|
|
177
125
|
|
|
178
126
|
return (
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { CUSTOMER_ID_STORAGE_KEY, THREAD_ID_STORAGE_KEY, SETTINGS_STORAGE_KEY } from '../constants'
|
|
2
|
+
|
|
3
|
+
const setSdk = (state, payload) => {
|
|
4
|
+
return {
|
|
5
|
+
...state,
|
|
6
|
+
sdk: payload
|
|
7
|
+
}
|
|
8
|
+
}
|
|
3
9
|
|
|
4
10
|
const setSettings = (state, payload) => {
|
|
5
11
|
if (payload) {
|
|
@@ -40,20 +46,6 @@ const setThreadId = (state, payload) => {
|
|
|
40
46
|
}
|
|
41
47
|
}
|
|
42
48
|
|
|
43
|
-
const setChatVisibility = (state, payload) => {
|
|
44
|
-
return {
|
|
45
|
-
...state,
|
|
46
|
-
isChatOpen: payload
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const setAvailability = (state, payload) => {
|
|
51
|
-
return {
|
|
52
|
-
...state,
|
|
53
|
-
availability: payload
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
49
|
const setThread = (state, payload) => {
|
|
58
50
|
return {
|
|
59
51
|
...state,
|
|
@@ -61,39 +53,17 @@ const setThread = (state, payload) => {
|
|
|
61
53
|
}
|
|
62
54
|
}
|
|
63
55
|
|
|
64
|
-
const
|
|
65
|
-
return {
|
|
66
|
-
...state,
|
|
67
|
-
messages: transformMessages(payload).reverse()
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const setMessage = (state, payload) => {
|
|
72
|
-
return {
|
|
73
|
-
...state,
|
|
74
|
-
message: transformMessage(payload),
|
|
75
|
-
messages: [...state.messages, transformMessage(payload)]
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const setAgent = (state, payload) => {
|
|
80
|
-
return {
|
|
81
|
-
...state,
|
|
82
|
-
agent: payload
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const setAgentIsTyping = (state, payload) => {
|
|
56
|
+
const setChatVisibility = (state, payload) => {
|
|
87
57
|
return {
|
|
88
58
|
...state,
|
|
89
|
-
|
|
59
|
+
isChatOpen: payload
|
|
90
60
|
}
|
|
91
61
|
}
|
|
92
62
|
|
|
93
|
-
const
|
|
63
|
+
const setAvailability = (state, payload) => {
|
|
94
64
|
return {
|
|
95
65
|
...state,
|
|
96
|
-
|
|
66
|
+
availability: payload
|
|
97
67
|
}
|
|
98
68
|
}
|
|
99
69
|
|
|
@@ -133,17 +103,13 @@ const toggleIsKeyboard = (state, payload) => {
|
|
|
133
103
|
}
|
|
134
104
|
|
|
135
105
|
export const actionsMap = {
|
|
106
|
+
SET_SDK: setSdk,
|
|
107
|
+
SET_THREAD: setThread,
|
|
136
108
|
SET_CHAT_VISIBILITY: setChatVisibility,
|
|
137
109
|
SET_AVAILABILITY: setAvailability,
|
|
138
110
|
SET_SETTINGS: setSettings,
|
|
139
111
|
SET_CUSTOMER_ID: setCustomerId,
|
|
140
112
|
SET_THREAD_ID: setThreadId,
|
|
141
|
-
SET_THREAD: setThread,
|
|
142
|
-
SET_MESSAGE: setMessage,
|
|
143
|
-
SET_MESSAGES: setMessages,
|
|
144
|
-
SET_AGENT: setAgent,
|
|
145
|
-
SET_AGENT_TYPING: setAgentIsTyping,
|
|
146
|
-
SET_AGENT_STATUS: setAgentStatus,
|
|
147
113
|
SET_UNSEEN_COUNT: setUnseenCount,
|
|
148
114
|
SET_INSTIGATOR_ID: setInstigatorId,
|
|
149
115
|
SET_LIVE_REGION_TEXT: setLiveRegionText,
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import { actionsMap } from './actions-map'
|
|
2
2
|
|
|
3
3
|
export const initialState = {
|
|
4
|
+
sdk: null,
|
|
4
5
|
availability: null,
|
|
5
6
|
customerId: null,
|
|
6
7
|
threadId: null,
|
|
7
8
|
thread: null,
|
|
8
|
-
messages: [],
|
|
9
9
|
unseenCount: 0,
|
|
10
10
|
instigatorId: null,
|
|
11
|
-
agent: null,
|
|
12
|
-
agentStatus: null,
|
|
13
|
-
isAgentTyping: false,
|
|
14
11
|
isChatOpen: false,
|
|
15
12
|
isMobile: window.matchMedia('(max-width: 640px)').matches,
|
|
16
13
|
isKeyboard: false,
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import React, { createContext, useEffect, useReducer, useMemo } from 'react'
|
|
2
|
+
import { ChatEvent } from '@nice-devone/nice-cxone-chat-web-sdk'
|
|
3
|
+
|
|
4
|
+
import { initialState, reducer } from './reducer.js'
|
|
5
|
+
import { SETTINGS_STORAGE_KEY } from '../constants.js'
|
|
6
|
+
import { useApp } from '../app/useApp.js'
|
|
7
|
+
|
|
8
|
+
export const SdkContext = createContext(initialState)
|
|
9
|
+
|
|
10
|
+
export const SdkProvider = ({ sdk, playSound, onRecoverError, children }) => {
|
|
11
|
+
const [state, dispatch] = useReducer(reducer, initialState)
|
|
12
|
+
const { thread, threadId, setThread, setThreadId, setUnseenCount } = useApp()
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* SDK event handlers
|
|
16
|
+
*/
|
|
17
|
+
const onLiveChatRecovered = e => {
|
|
18
|
+
dispatch({ type: 'SET_AGENT', payload: e.detail.data.inboxAssignee })
|
|
19
|
+
dispatch({ type: 'SET_AGENT_STATUS', payload: e.detail.data.contact.status })
|
|
20
|
+
setUnseenCount(e.detail.data.contact.customerStatistics.unseenMessagesCount)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const onAssignedAgentChanged = e => {
|
|
24
|
+
dispatch({ type: 'SET_AGENT', payload: e.detail.data.inboxAssignee })
|
|
25
|
+
dispatch({ type: 'SET_AGENT_STATUS', payload: e.detail.data.case.status })
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const onAgentTypingStarted = () => {
|
|
29
|
+
dispatch({ type: 'SET_AGENT_TYPING', payload: true })
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const onAgentTypingEnded = () => {
|
|
33
|
+
dispatch({ type: 'SET_AGENT_TYPING', payload: false })
|
|
34
|
+
dispatch({ type: 'SET_LIVE_REGION_TEXT' })
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const onMessageCreated = e => {
|
|
38
|
+
dispatch({ type: 'SET_MESSAGE', payload: e.detail.data.message })
|
|
39
|
+
dispatch({ type: 'SET_AGENT_STATUS', payload: e.detail.data.case.status })
|
|
40
|
+
|
|
41
|
+
setUnseenCount(e.detail.data.case.customerStatistics.unseenMessagesCount)
|
|
42
|
+
|
|
43
|
+
const isAudioOn = JSON.parse(window.localStorage.getItem(SETTINGS_STORAGE_KEY)).audio
|
|
44
|
+
|
|
45
|
+
if (isAudioOn && e.detail.data.message.direction === 'outbound' && playSound) {
|
|
46
|
+
playSound()
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const onContactStatusChanged = e => {
|
|
51
|
+
dispatch({ type: 'SET_AGENT_STATUS', payload: e.detail.data.case.status })
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (sdk) {
|
|
56
|
+
sdk.onChatEvent(ChatEvent.LIVECHAT_RECOVERED, onLiveChatRecovered)
|
|
57
|
+
sdk.onChatEvent(ChatEvent.MESSAGE_CREATED, onMessageCreated)
|
|
58
|
+
sdk.onChatEvent(ChatEvent.AGENT_TYPING_STARTED, onAgentTypingStarted)
|
|
59
|
+
sdk.onChatEvent(ChatEvent.AGENT_TYPING_ENDED, onAgentTypingEnded)
|
|
60
|
+
sdk.onChatEvent(ChatEvent.ASSIGNED_AGENT_CHANGED, onAssignedAgentChanged)
|
|
61
|
+
sdk.onChatEvent(ChatEvent.CONTACT_STATUS_CHANGED, onContactStatusChanged)
|
|
62
|
+
}
|
|
63
|
+
}, [sdk])
|
|
64
|
+
|
|
65
|
+
const fetchMessages = async threadItem => {
|
|
66
|
+
const recovered = await threadItem.recover()
|
|
67
|
+
|
|
68
|
+
const allMessages = []
|
|
69
|
+
|
|
70
|
+
let fetchedMessages = recovered.messages
|
|
71
|
+
|
|
72
|
+
while (fetchedMessages.length) {
|
|
73
|
+
fetchedMessages.map(msg => allMessages.push(msg))
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const response = await threadItem.loadMoreMessages()
|
|
77
|
+
fetchedMessages = response.data.messages
|
|
78
|
+
} catch (err) {
|
|
79
|
+
fetchedMessages = []
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return allMessages
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Recovers the thread if there is a threadId but no thread loaded in to state
|
|
88
|
+
*/
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
const recover = async () => {
|
|
91
|
+
try {
|
|
92
|
+
await sdk.authorize()
|
|
93
|
+
|
|
94
|
+
const fetchedThread = await sdk.getThread(threadId)
|
|
95
|
+
const fetchedMessages = await fetchMessages(fetchedThread)
|
|
96
|
+
|
|
97
|
+
setThread(fetchedThread)
|
|
98
|
+
setMessages(fetchedMessages)
|
|
99
|
+
} catch (err) {
|
|
100
|
+
console.log('[Chat Error] fetchThread', err)
|
|
101
|
+
setThreadId()
|
|
102
|
+
setThread()
|
|
103
|
+
onRecoverError()
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (sdk && threadId && !thread) {
|
|
108
|
+
console.log('recovering chat...')
|
|
109
|
+
recover()
|
|
110
|
+
}
|
|
111
|
+
}, [sdk, thread, threadId])
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* State update functions
|
|
115
|
+
*/
|
|
116
|
+
const setMessages = messages => {
|
|
117
|
+
dispatch({ type: 'SET_MESSAGES', payload: messages })
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Application-wide state and state functions
|
|
122
|
+
*/
|
|
123
|
+
const store = useMemo(() => ({
|
|
124
|
+
...state,
|
|
125
|
+
sdk,
|
|
126
|
+
fetchMessages,
|
|
127
|
+
setMessages,
|
|
128
|
+
onLiveChatRecovered,
|
|
129
|
+
onAssignedAgentChanged,
|
|
130
|
+
onAgentTypingStarted,
|
|
131
|
+
onAgentTypingEnded,
|
|
132
|
+
onMessageCreated,
|
|
133
|
+
onContactStatusChanged
|
|
134
|
+
}))
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<SdkContext.Provider value={store}>
|
|
138
|
+
{children}
|
|
139
|
+
</SdkContext.Provider>
|
|
140
|
+
)
|
|
141
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { transformMessages, transformMessage } from '../../lib/transform-messages'
|
|
2
|
+
|
|
3
|
+
const setMessages = (state, payload) => {
|
|
4
|
+
return {
|
|
5
|
+
...state,
|
|
6
|
+
messages: transformMessages(payload).reverse()
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const setMessage = (state, payload) => {
|
|
11
|
+
return {
|
|
12
|
+
...state,
|
|
13
|
+
message: transformMessage(payload),
|
|
14
|
+
messages: [...state.messages, transformMessage(payload)]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const setAgent = (state, payload) => {
|
|
19
|
+
return {
|
|
20
|
+
...state,
|
|
21
|
+
agent: payload
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const setAgentIsTyping = (state, payload) => {
|
|
26
|
+
return {
|
|
27
|
+
...state,
|
|
28
|
+
isAgentTyping: payload
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const setAgentStatus = (state, payload) => {
|
|
33
|
+
return {
|
|
34
|
+
...state,
|
|
35
|
+
agentStatus: payload
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const actionsMap = {
|
|
40
|
+
SET_MESSAGE: setMessage,
|
|
41
|
+
SET_MESSAGES: setMessages,
|
|
42
|
+
SET_AGENT: setAgent,
|
|
43
|
+
SET_AGENT_TYPING: setAgentIsTyping,
|
|
44
|
+
SET_AGENT_STATUS: setAgentStatus
|
|
45
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { actionsMap } from './actions-map'
|
|
2
|
+
|
|
3
|
+
export const initialState = {
|
|
4
|
+
messages: [],
|
|
5
|
+
agent: null,
|
|
6
|
+
agentStatus: null,
|
|
7
|
+
isAgentTyping: false
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const reducer = (state, action) => {
|
|
11
|
+
const { type, payload } = action
|
|
12
|
+
|
|
13
|
+
const fn = actionsMap[type]
|
|
14
|
+
|
|
15
|
+
if (fn) {
|
|
16
|
+
const actionFunction = fn.bind(this, state, payload)
|
|
17
|
+
return actionFunction()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return state
|
|
21
|
+
}
|
package/src/server/lib/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const querystring = require('querystring')
|
|
2
2
|
const axios = require('axios')
|
|
3
|
-
const
|
|
3
|
+
const jwtdecode = require('jwt-decode')
|
|
4
4
|
const { isWithinHours } = require('./utils.js')
|
|
5
5
|
|
|
6
6
|
const contentType = 'application/x-www-form-urlencoded'
|
|
@@ -25,7 +25,7 @@ const authenticate = async ({ authenticationUri, authorisation, accessKey, acces
|
|
|
25
25
|
return {
|
|
26
26
|
token: auth.data.access_token,
|
|
27
27
|
tokenType: auth.data.token_type,
|
|
28
|
-
tenantId:
|
|
28
|
+
tenantId: jwtdecode(auth.data.id_token)?.tenantId
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export const useChatSdk = sdk => {
|
|
2
|
-
const connect = async () => sdk.authorize()
|
|
3
|
-
|
|
4
|
-
const fetchCustomerId = async () => {
|
|
5
|
-
const response = await connect()
|
|
6
|
-
return response?.consumerIdentity.idOnExternalPlatform
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const fetchThread = async threadId => {
|
|
10
|
-
await connect()
|
|
11
|
-
return sdk.getThread(threadId)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const fetchMessages = async (thread, threadId) => {
|
|
15
|
-
await connect()
|
|
16
|
-
|
|
17
|
-
const recovered = await thread.recover(threadId)
|
|
18
|
-
|
|
19
|
-
const allMessages = []
|
|
20
|
-
let fetchedMessages = recovered.messages
|
|
21
|
-
|
|
22
|
-
while (fetchedMessages.length) {
|
|
23
|
-
fetchedMessages.map(msg => allMessages.push(msg))
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
const response = await thread.loadMoreMessages()
|
|
27
|
-
fetchedMessages = response.data.messages
|
|
28
|
-
} catch (err) {
|
|
29
|
-
fetchedMessages = []
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return allMessages
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return { connect, fetchCustomerId, fetchThread, fetchMessages }
|
|
37
|
-
}
|
|
File without changes
|