@defra/flood-webchat 1.0.4 → 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/package.json +1 -1
- 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/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/package.json
CHANGED
|
@@ -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
|
+
}
|
|
@@ -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
|