@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 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,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra/flood-webchat",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "",
5
5
  "main": "dist/server.js",
6
6
  "browser": "dist/client.js",
@@ -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 ? <LiveRegion /> : <Panel />}
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, messages, agent, agentStatus, isAgentTyping, isChatOpen, settings, isKeyboard, setLiveRegionText } = useApp()
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
- const agentName = agent?.nickname || agent?.firstName
46
-
47
- const connectionHeadlineText = agentStatusHeadline(availability, agentStatus, agentName)
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 { agentStatus, liveRegionText, setLiveRegionText } = useApp()
9
+ const { liveRegionText, setLiveRegionText } = useApp()
10
+ const { agentStatus } = useSdk()
9
11
 
10
12
  const [textA, setTextA] = useState()
11
13
  const [textB, setTextB] = useState()
@@ -1,5 +1,5 @@
1
1
  import React from 'react'
2
- import { useApp } from '../../store/useApp.js'
2
+ import { useApp } from '../../store/app/useApp.js'
3
3
  import { historyReplaceState } from '../../lib/history.js'
4
4
 
5
5
  export function PanelHeader () {
@@ -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, thread, threadId, setThread, setThreadId, setChatVisibility, setMessages, setUnseenCount, isMobile, isKeyboard } = useApp()
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
- return () => {
41
+ if (!isChatOpen) {
43
42
  document.getElementById(instigatorId)?.focus()
44
43
  }
45
- }, [])
44
+ }, [isChatOpen])
46
45
 
47
46
  /**
48
- * We need ammend classes on the body element to handle mobile behaviour
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
- * Recovers the thread if there is a threadId but no thread loaded in to state
72
+ * Initialize SDK if theres a thread ID
73
73
  */
74
74
  useEffect(() => {
75
- const recover = async () => {
76
- try {
77
- const fetchedThread = await fetchThread(threadId)
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
- if (threadId) {
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 = <Chat onSettingsScreen={goToSettingsScreen} onEndChatScreen={goToEndChatScreen} />
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 = <EndChat onChatScreen={goToChatScreen} onEndChatConfirm={goToFeedbackScreen} />
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 = <Settings onCancel={goToChatScreen} />
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 { setCustomerId, setThreadId, setMessages, agentStatus, thread, threadId, setUnseenCount, isKeyboard } = useApp()
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 { sdk, setCustomerId, setThreadId, setThread, setLiveRegionText } = useApp()
14
- const { fetchCustomerId, fetchThread } = useChatSdk(sdk)
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.getCustomer().setName(nameRef.current.value)
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()
@@ -1,6 +1,6 @@
1
1
  import React from 'react'
2
2
  import { createPortal } from 'react-dom'
3
- import { useApp } from '../store/useApp'
3
+ import { useApp } from '../store/app/useApp'
4
4
 
5
5
  export const SkipLink = () => {
6
6
  const { threadId, setInstigatorId } = 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
- setAriaHidden(true)
66
+ if (isChatOpen) {
67
+ setAriaHidden(true)
67
68
 
68
- const panelElement = document.querySelector('#wc-panel')
69
- panelElement.focus()
69
+ const panelElement = document.querySelector('#wc-panel')
70
+ panelElement.focus()
70
71
 
71
- document.addEventListener('keydown', onKeyDown)
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 }
@@ -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 sdk={sdk} availability={availability} playSound={playSound}>
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 './constants.js'
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 = ({ sdk, availability, playSound, children }) => {
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
- sdk,
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 { transformMessages, transformMessage } from '../lib/transform-messages'
2
- import { CUSTOMER_ID_STORAGE_KEY, THREAD_ID_STORAGE_KEY, SETTINGS_STORAGE_KEY } from './constants'
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 setMessages = (state, payload) => {
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
- isAgentTyping: payload
59
+ isChatOpen: payload
90
60
  }
91
61
  }
92
62
 
93
- const setAgentStatus = (state, payload) => {
63
+ const setAvailability = (state, payload) => {
94
64
  return {
95
65
  ...state,
96
- agentStatus: payload
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
+ }
@@ -0,0 +1,5 @@
1
+ import { useContext } from 'react'
2
+
3
+ import { SdkContext } from './SdkProvider.jsx'
4
+
5
+ export const useSdk = () => useContext(SdkContext)
@@ -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