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