1function getChatStorageKey(chatbotId) { 2 return 'pgnai_chat_' + chatbotId; 3} 4 5function readChatState(chatbotId) { 6 try { 7 const raw = window.localStorage.getItem(getChatStorageKey(chatbotId)); 8 if (!raw) return null; 9 const parsed = JSON.parse(raw); 10 return parsed && typeof parsed === 'object' ? parsed : null; 11 } catch (e) { 12 // ignore private mode / parse errors 13 return null; 14 } 15} 16 17function writeChatState(chatbotId, state) { 18 try { 19 if (!state || !state.conversationId) return; 20 window.localStorage.setItem(getChatStorageKey(chatbotId), JSON.stringify(state)); 21 } catch (e) { 22 // ignore private mode / storage errors 23 } 24} 25 26async function getConversation(chatbotConfig) { 27 const chatbotId = chatbotConfig.chatbotId; 28 const useLocalMemory = !!chatbotConfig.useLocalMemory; 29 30 const storedState = readChatState(chatbotId); 31 32 // Cache-first path: skip the server call when local memory is enabled and this 33 // visitor already owns a browser-scoped conversation. The unified key holds the 34 // conversation id, the message history and the lead-captured flag together. 35 if (useLocalMemory && storedState && storedState.conversationId) { 36 return { 37 conversationId: storedState.conversationId, 38 initialMessages: Array.isArray(storedState.messages) ? storedState.messages : [], 39 leadAlreadyCaptured: !!storedState.leadCaptured, 40 }; 41 } 42 43 const response = await PGNAIPlugin.ApiWrapper.call( 44 'getConversation', 45 { 46 chatbot_id: chatbotId, 47 public_id: storedState ? storedState.conversationId : '', 48 } 49 ); 50 51 if (response.state === 'ok' && response.data && response.data.public_id) { 52 const publicId = response.data.public_id; 53 const initialMessages = Array.isArray(response.data.initialMessages) 54 ? response.data.initialMessages 55 : []; 56 const leadAlreadyCaptured = !!response.data.leadAlreadyCaptured; 57 58 writeChatState(chatbotId, { 59 conversationId: publicId, 60 messages: initialMessages, 61 leadCaptured: leadAlreadyCaptured, 62 }); 63 64 return { 65 conversationId: publicId, 66 initialMessages, 67 leadAlreadyCaptured, 68 }; 69 } 70 71 return { 72 conversationId: storedState ? storedState.conversationId : '', 73 initialMessages: [], 74 leadAlreadyCaptured: storedState ? !!storedState.leadCaptured : false, 75 }; 76} 77 78async function processQueue() { 79 const queue = window.PGNAIChatWidgetQueue || []; 80 81 for (const item of queue) { 82 const root = document.getElementById(item.containerId); 83 84 const chatbotConfig = window.PGNAIChatWidgetConfig 85 ? window.PGNAIChatWidgetConfig[item.scriptId] 86 : null; 87 88 if (!root || !chatbotConfig) continue; 89 90 let Component = null; 91 try { 92 Component = await PGNAIPlugin.Ui.waitForComponent( 93 'PGNAIComp.ChatWidgetContainer.ChatWidgetContainer', 5000, 100 94 ); 95 } catch (e) { 96 console.warn('Component not found for ' + item.scriptId + ':', e?.message || e); 97 continue; 98 } 99 100 let conversationId = chatbotConfig.conversationId; 101 let initialMessages = chatbotConfig.initialMessages; 102 let leadAlreadyCaptured = !!chatbotConfig.leadAlreadyCaptured; 103 104 if (!chatbotConfig.isPreviewMode) { 105 // Skip the server call for a brand-new visitor: the conversation is only 106 // created lazily when they send their first message. Returning visitors 107 // reconnect to their existing conversation via its stored public id. 108 const storedState = readChatState(chatbotConfig.chatbotId); 109 110 if (storedState && storedState.conversationId) { 111 const resolved = await getConversation(chatbotConfig); 112 conversationId = resolved.conversationId; 113 initialMessages = resolved.initialMessages; 114 leadAlreadyCaptured = resolved.leadAlreadyCaptured; 115 } 116 } 117 118 const chatRef = wp.element.createRef(); 119 120 const sharedProps = { 121 ref: chatRef, 122 apiWrapper: PGNAIPlugin.ApiWrapper, 123 formatDate: PGNAIHelper.formatDate, 124 ...chatbotConfig, 125 initialMessages, 126 conversationId, 127 leadAlreadyCaptured, 128 }; 129 130 PGNAIPlugin.Ui.renderReact(Component, sharedProps, root); 131 } 132} 133 134function registerConversationStorageSync() { 135 window.addEventListener('storage', function (event) { 136 if (!event.key) return; 137 if (!/^pgnai_chat_\d+$/.test(event.key)) return; 138 139 // Another tab of the same browser changed the visitor's unified chat state 140 // (conversation id and/or cached messages). Re-resolve and re-render so the 141 // current tab stays in sync without a reload. 142 processQueue(); 143 }); 144} 145 146document.addEventListener('DOMContentLoaded', async function () { 147 registerConversationStorageSync(); 148 processQueue(); 149});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.