@nethesis/phone-island 1.0.7-dev.4 → 1.0.8-dev.1
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/App.js +1 -1
- package/dist/App.js.map +1 -1
- package/dist/components/SideView/hooks/useSideViewLogic.js +1 -1
- package/dist/components/SideView/hooks/useSideViewLogic.js.map +1 -1
- package/dist/components/Socket.js +1 -1
- package/dist/components/Socket.js.map +1 -1
- package/dist/components/TranscriptionView/TranscriptionView.js.map +1 -1
- package/dist/components/WebRTC.js +1 -1
- package/dist/components/WebRTC.js.map +1 -1
- package/dist/models/webrtc.js +1 -1
- package/dist/models/webrtc.js.map +1 -1
- package/dist/node_modules/@fortawesome/react-fontawesome/index.es.js +1 -1
- package/dist/node_modules/@fortawesome/react-fontawesome/index.es.js.map +1 -1
- package/dist/node_modules/@headlessui/react/dist/components/portal/portal.js +1 -1
- package/dist/node_modules/@headlessui/react/dist/components/portal/portal.js.map +1 -1
- package/dist/node_modules/@headlessui/react/dist/utils/render.js +1 -1
- package/dist/node_modules/@headlessui/react/dist/utils/render.js.map +1 -1
- package/dist/node_modules/immer/dist/immer.esm.mjs.js +1 -1
- package/dist/node_modules/immer/dist/immer.esm.mjs.js.map +1 -1
- package/dist/node_modules/socket.io-parser/build/esm/index.js +1 -1
- package/dist/node_modules/socket.io-parser/build/esm/index.js.map +1 -1
- package/dist/package.json.js +1 -1
- package/dist/services/user.js +1 -1
- package/dist/services/user.js.map +1 -1
- package/dist/utils/genericFunctions/summaryEvents.js +2 -0
- package/dist/utils/genericFunctions/summaryEvents.js.map +1 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TranscriptionView.js","sources":["../../../src/components/TranscriptionView/TranscriptionView.tsx"],"sourcesContent":["// Copyright (C) 2025 Nethesis S.r.l.\n// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport React, { FC, memo, useState, useEffect, useRef } from 'react'\nimport { useSelector } from 'react-redux'\nimport { RootState } from '../../store'\nimport { motion, AnimatePresence } from 'framer-motion'\nimport { useTranslation } from 'react-i18next'\nimport { useEventListener, eventDispatch } from '../../utils'\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome'\nimport { faAngleUp, faArrowDown } from '@fortawesome/free-solid-svg-icons'\n\nconst ANIMATION_CONFIG = {\n initial: { height: 0, opacity: 0 },\n animate: { height: '360px', opacity: 1 },\n exit: { height: 0, opacity: 0 },\n transition: {\n duration: 0.1,\n ease: 'easeOut',\n },\n}\n\nconst STYLE_CONFIG = {\n borderBottomLeftRadius: '20px',\n borderBottomRightRadius: '20px',\n transformOrigin: 'top',\n overflow: 'hidden',\n} as const\n\ninterface TranscriptionViewProps {\n isVisible: boolean\n}\n\ninterface TranscriptionMessage {\n id: string\n timestamp: number\n channelIndex: number\n segmentStart: number\n speaker: string\n speakerNumber: string\n counterpart: string\n counterpartNumber: string\n text: string\n isFinal: boolean\n}\n\nconst TypewriterText: FC<{ text: string; isFinal: boolean; speed?: number }> = ({\n text,\n isFinal,\n speed = 50,\n}) => {\n const [displayText, setDisplayText] = useState('')\n\n useEffect(() => {\n if (isFinal) {\n setDisplayText(text)\n return\n }\n\n setDisplayText('')\n let currentIndex = 0\n\n const typeInterval = setInterval(() => {\n if (currentIndex < text.length) {\n setDisplayText(text.slice(0, currentIndex + 1))\n currentIndex++\n } else {\n clearInterval(typeInterval)\n }\n }, speed)\n\n return () => clearInterval(typeInterval)\n }, [text, isFinal, speed])\n\n return (\n <div className='pi-inline-flex pi-items-center pi-flex-wrap'>\n <span>{displayText}</span>\n </div>\n )\n}\n\nconst TranscriptionView: FC<TranscriptionViewProps> = memo(({ isVisible }) => {\n const { actionsExpanded, view } = useSelector((state: RootState) => state.island)\n const currentUser = useSelector((state: RootState) => state.currentUser)\n const currentCallStartTime = useSelector((state: RootState) => state.currentCall.startTime)\n const { t } = useTranslation()\n\n const [allMessages, setAllMessages] = useState<TranscriptionMessage[]>([])\n const [visibleMessages, setVisibleMessages] = useState<TranscriptionMessage[]>([])\n const [hasNewContent, setHasNewContent] = useState(false)\n const [userScrolled, setUserScrolled] = useState(false)\n const [lastSeenMessageIndex, setLastSeenMessageIndex] = useState(0)\n const messagesEndRef = useRef<HTMLDivElement>(null)\n const scrollContainerRef = useRef<HTMLDivElement>(null)\n const timestampCorrectionRef = useRef<number | null>(null)\n const [autoScroll, setAutoScroll] = useState(true)\n\n const MAX_VISIBLE_MESSAGES = 100\n const BUFFER_MESSAGES = 10\n const SCROLL_DEBOUNCE_MS = 100\n\n const resetTranscriptionState = () => {\n timestampCorrectionRef.current = null\n setAllMessages([])\n setVisibleMessages([])\n setHasNewContent(false)\n setUserScrolled(false)\n setLastSeenMessageIndex(0)\n setAutoScroll(true)\n }\n\n const getLocalCallElapsedSeconds = () => {\n const start = Number(currentCallStartTime)\n if (!Number.isFinite(start) || start <= 0) {\n return null\n }\n return Math.max(0, Math.floor(Date.now() / 1000) - start)\n }\n\n\n // Function to check if a speaker number belongs to current user\n const isMyNumber = (speakerNumber: string): boolean => {\n if (!currentUser || !speakerNumber) return false\n\n // Check main extension from endpoints\n if (currentUser.endpoints?.mainextension?.[0]?.id === speakerNumber) return true\n\n // Check other extensions in endpoints\n if (currentUser.endpoints?.extension) {\n return Object.values(currentUser.endpoints.extension).some(\n (ext: any) => ext.id === speakerNumber || ext.exten === speakerNumber,\n )\n }\n\n return false\n }\n\n // Update visible messages when all messages change.\n // Sort all messages (finals + interims) chronologically so that concurrent\n // speakers always appear in the correct time order.\n useEffect(() => {\n const startIndex = Math.max(0, allMessages.length - MAX_VISIBLE_MESSAGES)\n const recentMessages = allMessages.slice(startIndex)\n // JS Array.sort is stable: messages with equal timestamps keep insertion order.\n const sorted = [...recentMessages].sort((a, b) => a.timestamp - b.timestamp)\n setVisibleMessages(sorted)\n }, [allMessages])\n\n // Handle incoming transcription messages\n const addTranscriptionMessage = (data: any) => {\n const rawTimestamp = Number(data.timestamp) || 0\n const channelIndex = Number.isFinite(Number(data.channel_index)) ? Number(data.channel_index) : -1\n const segmentStart = Number.isFinite(Number(data.segment_start))\n ? Number(data.segment_start)\n : rawTimestamp\n const uniqueId =\n data.uniqueid && channelIndex >= 0\n ? `${data.uniqueid}_${channelIndex}_${segmentStart.toFixed(3)}`\n : `${data.uniqueid}_${rawTimestamp}`\n const localElapsed = getLocalCallElapsedSeconds()\n let correctedTimestamp = rawTimestamp\n\n // Keep transcription timestamps aligned to local call timer, avoiding progressive drift.\n if (localElapsed !== null) {\n if (timestampCorrectionRef.current === null) {\n timestampCorrectionRef.current = localElapsed - rawTimestamp\n } else {\n const predicted = rawTimestamp + timestampCorrectionRef.current\n const error = localElapsed - predicted\n const boundedError = Math.max(-2, Math.min(2, error))\n timestampCorrectionRef.current += boundedError * 0.2\n }\n\n correctedTimestamp = rawTimestamp + (timestampCorrectionRef.current || 0)\n correctedTimestamp = Math.max(0, Math.min(localElapsed, correctedTimestamp))\n }\n\n const message: TranscriptionMessage = {\n id: uniqueId,\n timestamp: correctedTimestamp,\n channelIndex,\n segmentStart,\n speaker: data.speaker_name || 'Unknown',\n speakerNumber: data.speaker_number || '',\n counterpart: data.speaker_counterpart_name || '',\n counterpartNumber: data.speaker_counterpart_number || '',\n text: data.transcription || '',\n isFinal: data.is_final || false,\n }\n\n setAllMessages((prevMessages) => {\n const findLastIndex = (predicate: (message: TranscriptionMessage) => boolean) => {\n for (let index = prevMessages.length - 1; index >= 0; index -= 1) {\n if (predicate(prevMessages[index])) {\n return index\n }\n }\n return -1\n }\n\n const isSameSpeakerStream = (existingMessage: TranscriptionMessage) => {\n if (message.channelIndex >= 0 && existingMessage.channelIndex >= 0) {\n return existingMessage.channelIndex === message.channelIndex\n }\n\n if (message.speakerNumber && existingMessage.speakerNumber) {\n return existingMessage.speakerNumber === message.speakerNumber\n }\n\n return existingMessage.speaker === message.speaker\n }\n\n // 1. Exact match by segment identity – update in place, but never\n // overwrite an already-finalized message with a new interim.\n const existingMessageIndex = prevMessages.findIndex((msg) => msg.id === uniqueId)\n if (existingMessageIndex !== -1) {\n const existing = prevMessages[existingMessageIndex]\n if (existing.isFinal && !message.isFinal) {\n // UniqueId collision: the existing segment was finalized but the\n // backend started a new interim with a colliding id. Treat it as\n // a brand-new message so the final bubble is preserved.\n return [...prevMessages, { ...message, id: `${message.id}_${Date.now()}` }]\n }\n const updatedMessages = [...prevMessages]\n updatedMessages[existingMessageIndex] = message\n return updatedMessages\n }\n\n // 2. Final message path\n if (message.isFinal) {\n // Check for a duplicate final with identical text and close timestamp\n const similarFinalIndex = findLastIndex(\n (existing) =>\n existing.isFinal &&\n isSameSpeakerStream(existing) &&\n existing.text.trim() === message.text.trim() &&\n Math.abs(existing.timestamp - message.timestamp) <= 1,\n )\n if (similarFinalIndex !== -1) {\n const updatedMessages = [...prevMessages]\n updatedMessages[similarFinalIndex] = {\n ...message,\n id: prevMessages[similarFinalIndex].id,\n }\n return updatedMessages\n }\n\n // Replace the active interim from the same speaker (the \"speaking\" bubble)\n const activeInterimIndex = findLastIndex(\n (existing) => !existing.isFinal && isSameSpeakerStream(existing),\n )\n if (activeInterimIndex !== -1) {\n const updatedMessages = [...prevMessages]\n updatedMessages[activeInterimIndex] = {\n ...message,\n id: prevMessages[activeInterimIndex].id,\n }\n return updatedMessages\n }\n\n // No interim to replace – just append.\n return [...prevMessages, message]\n }\n\n // 3. Interim message path – keep at most one active interim per speaker/channel.\n const activeInterimIndex = findLastIndex(\n (existing) => !existing.isFinal && isSameSpeakerStream(existing),\n )\n if (activeInterimIndex !== -1) {\n const updatedMessages = [...prevMessages]\n updatedMessages[activeInterimIndex] = {\n ...message,\n id: prevMessages[activeInterimIndex].id,\n }\n return updatedMessages\n }\n\n return [...prevMessages, message]\n })\n }\n\n // Check if user is at the bottom of the scroll area\n const isAtBottom = () => {\n if (!scrollContainerRef.current) return true\n const { scrollTop, scrollHeight, clientHeight } = scrollContainerRef.current\n return scrollHeight - scrollTop <= clientHeight + 10 // 10px tolerance\n }\n\n // Handle scroll events to detect user scrolling\n const handleScroll = () => {\n if (!scrollContainerRef.current) return\n\n const atBottom = isAtBottom()\n const { scrollTop, scrollHeight, clientHeight } = scrollContainerRef.current\n\n if (atBottom) {\n // User is at bottom, clear indicators and enable auto-scroll\n setHasNewContent(false)\n setUserScrolled(false)\n setAutoScroll(true)\n setLastSeenMessageIndex(allMessages.length)\n } else {\n setUserScrolled(true)\n setAutoScroll(false)\n }\n }\n\n // Scroll to bottom function\n const scrollToBottom = () => {\n if (scrollContainerRef.current) {\n scrollContainerRef.current.scrollTop = scrollContainerRef.current.scrollHeight\n\n // Update state\n setHasNewContent(false)\n setUserScrolled(false)\n setAutoScroll(true)\n setLastSeenMessageIndex(allMessages.length)\n }\n }\n\n // Calculate unseen messages count\n const unseenMessagesCount = Math.max(0, allMessages.length - lastSeenMessageIndex)\n\n // Auto-scroll to bottom when new messages arrive\n useEffect(() => {\n if (allMessages.length === 0) return\n\n if (autoScroll && scrollContainerRef.current) {\n // Auto-scroll to bottom immediately for new messages\n setTimeout(() => {\n if (scrollContainerRef.current) {\n scrollContainerRef.current.scrollTop = scrollContainerRef.current.scrollHeight\n }\n }, 100)\n } else if (userScrolled && !autoScroll) {\n // If user has scrolled up and there's a new message, show the indicator\n setHasNewContent(true)\n }\n }, [allMessages])\n\n useEffect(() => {\n if (isVisible && allMessages.length > 0) {\n setAutoScroll(true)\n setUserScrolled(false)\n setHasNewContent(false)\n }\n }, [isVisible])\n\n // Listen for transcription events\n useEventListener('phone-island-conversation-transcription', (transcriptionData: any) => {\n addTranscriptionMessage(transcriptionData)\n })\n\n useEventListener('phone-island-transcription-opened', () => {\n resetTranscriptionState()\n })\n\n useEventListener('phone-island-transcription-closed', () => {\n resetTranscriptionState()\n })\n\n\n // Format timestamp - converts seconds from call start to MM:SS format\n const formatTimestamp = (timestamp: number) => {\n const minutes = Math.floor(timestamp / 60)\n const seconds = Math.floor(timestamp % 60)\n return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`\n }\n\n // Skeleton component for loading state\n const TranscriptionSkeleton: FC = () => (\n <div className='pi-space-y-2 pi-animate-pulse'>\n {/* First shorter bar */}\n <div className='pi-h-4 pi-bg-gray-200 dark:pi-bg-gray-700 pi-rounded pi-w-2/5'></div>\n {/* Second longer bar */}\n <div className='pi-h-4 pi-bg-gray-200 dark:pi-bg-gray-700 pi-rounded pi-w-4/5'></div>\n {/* First shorter bar */}\n <div className='pi-h-4 pi-bg-gray-200 dark:pi-bg-gray-700 pi-rounded pi-w-2/5'></div>\n {/* Third medium bar */}\n <div className='pi-h-4 pi-bg-gray-200 dark:pi-bg-gray-700 pi-rounded pi-w-4/5'></div>\n </div>\n )\n\n const containerClassName = `pi-absolute pi-w-full pi-bg-elevationL2 pi-flex pi-flex-col pi-text-iconWhite dark:pi-text-iconWhiteDark pi-left-0 -pi-z-10 pi-pointer-events-auto ${\n view === 'settings' || actionsExpanded ? 'pi-top-[17rem]' : 'pi-top-[13rem]'\n }`\n\n return (\n <>\n <AnimatePresence>\n {isVisible && (\n <motion.div className={containerClassName} style={STYLE_CONFIG} {...ANIMATION_CONFIG}>\n <div className='pi-h-full pi-rounded-lg pi-overflow-hidden pi-bg-elevationL2 dark:pi-bg-elevationL2Dark pi-relative pi-flex pi-flex-col pi-border-2 pi-border-gray-100 dark:pi-border-gray-600 pi-shadow-lg'>\n {/* Main Content Card */}\n <div className='pi-flex-1 pi-pt-4 pi-px-4 pi-mt-8'>\n <div className='pi-h-60 pi-bg-gray-100 dark:pi-bg-gray-800 pi-rounded-lg pi-border pi-border-gray-200 dark:pi-border-gray-700 pi-overflow-hidden pi-flex pi-flex-col'>\n <AnimatePresence>\n {hasNewContent && userScrolled && (\n <motion.div\n initial={{ opacity: 0, y: -10, scale: 0.9 }}\n animate={{ opacity: 1, y: 0, scale: 1 }}\n exit={{ opacity: 0, y: -10, scale: 0.9 }}\n className='pi-absolute pi-top-16 pi-left-0 pi-right-0 pi-flex pi-justify-center pi-z-20'\n >\n <button\n onClick={scrollToBottom}\n className='pi-bg-phoneIslandActive dark:pi-bg-phoneIslandActiveDark hover:pi-bg-gray-500 dark:hover:pi-bg-gray-50 focus:pi-ring-emerald-500 dark:focus:pi-ring-emerald-300 pi-text-primaryInvert dark:pi-text-primaryInvertDark pi-px-4 pi-py-2 pi-rounded-full pi-text-sm pi-shadow-lg pi-flex pi-items-center pi-gap-2 pi-transition-all pi-duration-200 pi-border pi-backdrop-blur-sm'\n >\n <FontAwesomeIcon icon={faArrowDown} className='pi-w-4 pi-h-4' />\n {unseenMessagesCount > 1\n ? t('TranscriptionView.New messages')\n : t('TranscriptionView.New message')}\n </button>\n </motion.div>\n )}\n </AnimatePresence>\n\n <div\n ref={scrollContainerRef}\n onScroll={handleScroll}\n className={`pi-flex-1 pi-p-4 ${\n visibleMessages.length > 0\n ? 'pi-overflow-y-auto pi-scrollbar-thin pi-scrollbar-thumb-gray-400 pi-scrollbar-thumb-rounded-full pi-scrollbar-thumb-opacity-50 pi-scrollbar-track-gray-200 dark:pi-scrollbar-track-gray-900 pi-scrollbar-track-rounded-full pi-scrollbar-track-opacity-25'\n : 'pi-overflow-hidden'\n }`}\n >\n {visibleMessages.length === 0 ? (\n <TranscriptionSkeleton />\n ) : (\n <div className='pi-space-y-4'>\n {/* Show indicator if there are more messages than displayed */}\n {allMessages.length > MAX_VISIBLE_MESSAGES && (\n <div className='pi-text-center pi-py-2 pi-text-xs pi-text-gray-500 dark:pi-text-gray-400 pi-border-b pi-border-gray-200 dark:pi-border-gray-700'>\n {t('TranscriptionView.Showing messages', {\n visible: visibleMessages.length,\n total: allMessages.length,\n })}\n </div>\n )}\n\n {visibleMessages.map((message, index) => (\n <motion.div\n key={message.id}\n layout\n initial={{ opacity: 0, y: 20 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.3, layout: { duration: 0.25 } }}\n className='pi-mb-4'\n >\n {/* Speaker Name */}\n <div className='pi-mb-2'>\n <span className='pi-font-medium pi-text-xs pi-text-secondaryNeutral dark:pi-text-secondaryNeutralDark'>\n {isMyNumber(message.speakerNumber)\n ? t('Common.Me', 'Me')\n : message.speaker}\n </span>\n </div>\n\n {/* Message Bubble with Background */}\n <div\n className={`pi-relative pi-p-3 pi-rounded-lg pi-text-xs pi-font-regular ${\n isMyNumber(message.speakerNumber)\n ? 'pi-text-gray-800 dark:pi-text-gray-100 pi-bg-gray-200 dark:pi-bg-gray-600'\n : 'pi-text-indigo-800 dark:pi-text-indigo-100 pi-bg-indigo-100 dark:pi-bg-indigo-700'\n }`}\n >\n <div className='pi-flex pi-items-start pi-justify-between pi-gap-3'>\n <div className='pi-flex-1'>\n <TypewriterText\n text={message.text}\n isFinal={message.isFinal}\n speed={30}\n />\n </div>\n {/* Timestamp on the right */}\n <div\n className={`pi-flex-shrink-0 pi-mt-1 pi-text-xs pi-font-regular ${\n isMyNumber(message.speakerNumber)\n ? 'pi-text-gray-800 dark:pi-text-gray-100 pi-bg-gray-200 dark:pi-bg-gray-600'\n : 'pi-text-indigo-800 dark:pi-text-indigo-100 pi-bg-indigo-100 dark:pi-bg-indigo-700'\n }`}\n >\n {formatTimestamp(message.timestamp)}\n </div>\n </div>\n </div>\n\n {!message.isFinal && message.text.trim() !== '' && (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n className='pi-mt-1 pi-ml-3 pi-flex pi-items-center pi-gap-1'\n >\n <span className='pi-text-xs pi-text-gray-400 dark:pi-text-gray-500 pi-italic'>\n {t('TranscriptionView.Is speaking', '')}\n </span>\n <div className='pi-inline-flex pi-items-center pi-gap-1'>\n <motion.div\n className='pi-w-1 pi-h-1 pi-bg-gray-400 dark:pi-bg-gray-500 pi-rounded-full'\n animate={{ opacity: [0.3, 1, 0.3] }}\n transition={{ duration: 1.5, repeat: Infinity, delay: 0 }}\n />\n <motion.div\n className='pi-w-1 pi-h-1 pi-bg-gray-400 dark:pi-bg-gray-500 pi-rounded-full'\n animate={{ opacity: [0.3, 1, 0.3] }}\n transition={{ duration: 1.5, repeat: Infinity, delay: 0.2 }}\n />\n <motion.div\n className='pi-w-1 pi-h-1 pi-bg-gray-400 dark:pi-bg-gray-500 pi-rounded-full'\n animate={{ opacity: [0.3, 1, 0.3] }}\n transition={{ duration: 1.5, repeat: Infinity, delay: 0.4 }}\n />\n </div>\n </motion.div>\n )}\n </motion.div>\n ))}\n <div ref={messagesEndRef} className='pi-pb-4' />\n </div>\n )}\n </div>\n </div>\n </div>\n\n {/* Footer with Close Button */}\n <div className='pi-flex pi-items-center pi-justify-center pi-py-2'>\n <button\n onClick={() => eventDispatch('phone-island-transcription-close', {})}\n className='pi-bg-transparent dark:enabled:hover:pi-bg-gray-700/30 enabled:hover:pi-bg-gray-300/70 focus:pi-ring-offset-gray-200 dark:focus:pi-ring-gray-500 focus:pi-ring-gray-400 pi-text-secondaryNeutral pi-outline-none pi-border-transparent dark:pi-text-secondaryNeutralDark pi-h-12 pi-w-24 pi-rounded-fullpi-px-4 pi-py-2 pi-rounded-full pi-text-lg pi-flex pi-items-center pi-gap-2 pi-transition-all pi-duration-200 pi-border pi-backdrop-blur-sm'\n >\n <FontAwesomeIcon icon={faAngleUp} className='pi-w-4 pi-h-4 pi-ml-1' />\n {t('Common.Close')}\n </button>\n </div>\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n </>\n )\n})\n\nTranscriptionView.displayName = 'TranscriptionView'\n\nexport default TranscriptionView\n"],"names":["ANIMATION_CONFIG","initial","height","opacity","animate","exit","transition","duration","ease","STYLE_CONFIG","borderBottomLeftRadius","borderBottomRightRadius","transformOrigin","overflow","TypewriterText","_a","text","isFinal","_b","speed","_c","useState","displayText","setDisplayText","useEffect","currentIndex","typeInterval","setInterval","length","slice","clearInterval","React","createElement","className","TranscriptionView","memo","isVisible","useSelector","state","island","actionsExpanded","view","currentUser","currentCallStartTime","currentCall","startTime","t","useTranslation","allMessages","setAllMessages","_d","visibleMessages","setVisibleMessages","_e","hasNewContent","setHasNewContent","_f","userScrolled","setUserScrolled","_g","lastSeenMessageIndex","setLastSeenMessageIndex","messagesEndRef","useRef","scrollContainerRef","timestampCorrectionRef","_h","autoScroll","setAutoScroll","resetTranscriptionState","current","isMyNumber","speakerNumber","endpoints","mainextension","id","extension","Object","values","some","ext","exten","startIndex","Math","max","recentMessages","sorted","__spreadArray","sort","a","b","timestamp","addTranscriptionMessage","data","start","rawTimestamp","Number","channelIndex","isFinite","channel_index","segmentStart","segment_start","uniqueId","uniqueid","concat","toFixed","localElapsed","floor","Date","now","correctedTimestamp","error","boundedError","min","message","speaker","speaker_name","speaker_number","counterpart","speaker_counterpart_name","counterpartNumber","speaker_counterpart_number","transcription","is_final","prevMessages","findLastIndex","predicate","index","isSameSpeakerStream","existingMessage","existingMessageIndex","findIndex","msg","__assign","updatedMessages","similarFinalIndex","existing","trim","abs","activeInterimIndex_1","activeInterimIndex","unseenMessagesCount","setTimeout","scrollTop","scrollHeight","useEventListener","transcriptionData","containerClassName","Fragment","AnimatePresence","motion","div","style","y","scale","onClick","FontAwesomeIcon","icon","faArrowDown","ref","onScroll","atBottom","isAtBottom","visible","total","map","key","layout","minutes","seconds","toString","padStart","repeat","Infinity","delay","eventDispatch","faAngleUp","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TranscriptionView.js","sources":["../../../src/components/TranscriptionView/TranscriptionView.tsx"],"sourcesContent":["// Copyright (C) 2025 Nethesis S.r.l.\n// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport React, { FC, memo, useState, useEffect, useRef } from 'react'\nimport { useSelector } from 'react-redux'\nimport { RootState } from '../../store'\nimport { motion, AnimatePresence } from 'framer-motion'\nimport { useTranslation } from 'react-i18next'\nimport { useEventListener, eventDispatch } from '../../utils'\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome'\nimport { faAngleUp, faArrowDown } from '@fortawesome/free-solid-svg-icons'\n\nconst ANIMATION_CONFIG = {\n initial: { height: 0, opacity: 0 },\n animate: { height: '360px', opacity: 1 },\n exit: { height: 0, opacity: 0 },\n transition: {\n duration: 0.1,\n ease: 'easeOut',\n },\n}\n\nconst STYLE_CONFIG = {\n borderBottomLeftRadius: '20px',\n borderBottomRightRadius: '20px',\n transformOrigin: 'top',\n overflow: 'hidden',\n} as const\n\ninterface TranscriptionViewProps {\n isVisible: boolean\n}\n\ninterface TranscriptionMessage {\n id: string\n timestamp: number\n channelIndex: number\n segmentStart: number\n speaker: string\n speakerNumber: string\n counterpart: string\n counterpartNumber: string\n text: string\n isFinal: boolean\n}\n\nconst TypewriterText: FC<{ text: string; isFinal: boolean; speed?: number }> = ({\n text,\n isFinal,\n speed = 50,\n}) => {\n const [displayText, setDisplayText] = useState('')\n\n useEffect(() => {\n if (isFinal) {\n setDisplayText(text)\n return\n }\n\n setDisplayText('')\n let currentIndex = 0\n\n const typeInterval = setInterval(() => {\n if (currentIndex < text.length) {\n setDisplayText(text.slice(0, currentIndex + 1))\n currentIndex++\n } else {\n clearInterval(typeInterval)\n }\n }, speed)\n\n return () => clearInterval(typeInterval)\n }, [text, isFinal, speed])\n\n return (\n <div className='pi-inline-flex pi-items-center pi-flex-wrap'>\n <span>{displayText}</span>\n </div>\n )\n}\n\nconst TranscriptionView: FC<TranscriptionViewProps> = memo(({ isVisible }) => {\n const { actionsExpanded, view } = useSelector((state: RootState) => state.island)\n const currentUser = useSelector((state: RootState) => state.currentUser)\n const currentCallStartTime = useSelector((state: RootState) => state.currentCall.startTime)\n const { t } = useTranslation()\n\n const [allMessages, setAllMessages] = useState<TranscriptionMessage[]>([])\n const [visibleMessages, setVisibleMessages] = useState<TranscriptionMessage[]>([])\n const [hasNewContent, setHasNewContent] = useState(false)\n const [userScrolled, setUserScrolled] = useState(false)\n const [lastSeenMessageIndex, setLastSeenMessageIndex] = useState(0)\n const messagesEndRef = useRef<HTMLDivElement>(null)\n const scrollContainerRef = useRef<HTMLDivElement>(null)\n const timestampCorrectionRef = useRef<number | null>(null)\n const [autoScroll, setAutoScroll] = useState(true)\n\n const MAX_VISIBLE_MESSAGES = 100\n const BUFFER_MESSAGES = 10\n const SCROLL_DEBOUNCE_MS = 100\n\n const resetTranscriptionState = () => {\n timestampCorrectionRef.current = null\n setAllMessages([])\n setVisibleMessages([])\n setHasNewContent(false)\n setUserScrolled(false)\n setLastSeenMessageIndex(0)\n setAutoScroll(true)\n }\n\n const getLocalCallElapsedSeconds = () => {\n const start = Number(currentCallStartTime)\n if (!Number.isFinite(start) || start <= 0) {\n return null\n }\n return Math.max(0, Math.floor(Date.now() / 1000) - start)\n }\n\n\n // Function to check if a speaker number belongs to current user\n const isMyNumber = (speakerNumber: string): boolean => {\n if (!currentUser || !speakerNumber) return false\n\n // Check main extension from endpoints\n if (currentUser.endpoints?.mainextension?.[0]?.id === speakerNumber) return true\n\n // Check other extensions in endpoints\n if (currentUser.endpoints?.extension) {\n return Object.values(currentUser.endpoints.extension).some(\n (ext: any) => ext.id === speakerNumber || ext.exten === speakerNumber,\n )\n }\n\n return false\n }\n\n // Update visible messages when all messages change.\n // Sort all messages (finals + interims) chronologically so that concurrent\n // speakers always appear in the correct time order.\n useEffect(() => {\n const startIndex = Math.max(0, allMessages.length - MAX_VISIBLE_MESSAGES)\n const recentMessages = allMessages.slice(startIndex)\n // JS Array.sort is stable: messages with equal timestamps keep insertion order.\n const sorted = [...recentMessages].sort((a, b) => a.timestamp - b.timestamp)\n setVisibleMessages(sorted)\n }, [allMessages])\n\n // Handle incoming transcription messages\n const addTranscriptionMessage = (data: any) => {\n const rawTimestamp = Number(data.timestamp) || 0\n const channelIndex = Number.isFinite(Number(data.channel_index)) ? Number(data.channel_index) : -1\n const segmentStart = Number.isFinite(Number(data.segment_start))\n ? Number(data.segment_start)\n : rawTimestamp\n const uniqueId =\n data.uniqueid && channelIndex >= 0\n ? `${data.uniqueid}_${channelIndex}_${segmentStart.toFixed(3)}`\n : `${data.uniqueid}_${rawTimestamp}`\n const localElapsed = getLocalCallElapsedSeconds()\n let correctedTimestamp = rawTimestamp\n\n // Keep transcription timestamps aligned to local call timer, avoiding progressive drift.\n if (localElapsed !== null) {\n if (timestampCorrectionRef.current === null) {\n timestampCorrectionRef.current = localElapsed - rawTimestamp\n } else {\n const predicted = rawTimestamp + timestampCorrectionRef.current\n const error = localElapsed - predicted\n const boundedError = Math.max(-2, Math.min(2, error))\n timestampCorrectionRef.current += boundedError * 0.2\n }\n\n correctedTimestamp = rawTimestamp + (timestampCorrectionRef.current || 0)\n correctedTimestamp = Math.max(0, Math.min(localElapsed, correctedTimestamp))\n }\n\n const message: TranscriptionMessage = {\n id: uniqueId,\n timestamp: correctedTimestamp,\n channelIndex,\n segmentStart,\n speaker: data.speaker_name || 'Unknown',\n speakerNumber: data.speaker_number || '',\n counterpart: data.speaker_counterpart_name || '',\n counterpartNumber: data.speaker_counterpart_number || '',\n text: data.transcription || '',\n isFinal: data.is_final || false,\n }\n\n setAllMessages((prevMessages) => {\n const findLastIndex = (predicate: (message: TranscriptionMessage) => boolean) => {\n for (let index = prevMessages.length - 1; index >= 0; index -= 1) {\n if (predicate(prevMessages[index])) {\n return index\n }\n }\n return -1\n }\n\n const isSameSpeakerStream = (existingMessage: TranscriptionMessage) => {\n if (message.channelIndex >= 0 && existingMessage.channelIndex >= 0) {\n return existingMessage.channelIndex === message.channelIndex\n }\n\n if (message.speakerNumber && existingMessage.speakerNumber) {\n return existingMessage.speakerNumber === message.speakerNumber\n }\n\n return existingMessage.speaker === message.speaker\n }\n\n // 1. Exact match by segment identity – update in place, but never\n // overwrite an already-finalized message with a new interim.\n const existingMessageIndex = prevMessages.findIndex((msg) => msg.id === uniqueId)\n if (existingMessageIndex !== -1) {\n const existing = prevMessages[existingMessageIndex]\n if (existing.isFinal && !message.isFinal) {\n // UniqueId collision: the existing segment was finalized but the\n // backend started a new interim with a colliding id. Treat it as\n // a brand-new message so the final bubble is preserved.\n return [...prevMessages, { ...message, id: `${message.id}_${Date.now()}` }]\n }\n const updatedMessages = [...prevMessages]\n updatedMessages[existingMessageIndex] = message\n return updatedMessages\n }\n\n // 2. Final message path\n if (message.isFinal) {\n // Check for a duplicate final with identical text and close timestamp\n const similarFinalIndex = findLastIndex(\n (existing) =>\n existing.isFinal &&\n isSameSpeakerStream(existing) &&\n existing.text.trim() === message.text.trim() &&\n Math.abs(existing.timestamp - message.timestamp) <= 1,\n )\n if (similarFinalIndex !== -1) {\n const updatedMessages = [...prevMessages]\n updatedMessages[similarFinalIndex] = {\n ...message,\n id: prevMessages[similarFinalIndex].id,\n }\n return updatedMessages\n }\n\n // Replace the active interim from the same speaker (the \"speaking\" bubble)\n const activeInterimIndex = findLastIndex(\n (existing) => !existing.isFinal && isSameSpeakerStream(existing),\n )\n if (activeInterimIndex !== -1) {\n const updatedMessages = [...prevMessages]\n updatedMessages[activeInterimIndex] = {\n ...message,\n id: prevMessages[activeInterimIndex].id,\n }\n return updatedMessages\n }\n\n // No interim to replace – just append.\n return [...prevMessages, message]\n }\n\n // 3. Interim message path – keep at most one active interim per speaker/channel.\n const activeInterimIndex = findLastIndex(\n (existing) => !existing.isFinal && isSameSpeakerStream(existing),\n )\n if (activeInterimIndex !== -1) {\n const updatedMessages = [...prevMessages]\n updatedMessages[activeInterimIndex] = {\n ...message,\n id: prevMessages[activeInterimIndex].id,\n }\n return updatedMessages\n }\n\n return [...prevMessages, message]\n })\n }\n\n // Check if user is at the bottom of the scroll area\n const isAtBottom = () => {\n if (!scrollContainerRef.current) return true\n const { scrollTop, scrollHeight, clientHeight } = scrollContainerRef.current\n return scrollHeight - scrollTop <= clientHeight + 10 // 10px tolerance\n }\n\n // Handle scroll events to detect user scrolling\n const handleScroll = () => {\n if (!scrollContainerRef.current) return\n\n const atBottom = isAtBottom()\n const { scrollTop, scrollHeight, clientHeight } = scrollContainerRef.current\n\n if (atBottom) {\n // User is at bottom, clear indicators and enable auto-scroll\n setHasNewContent(false)\n setUserScrolled(false)\n setAutoScroll(true)\n setLastSeenMessageIndex(allMessages.length)\n } else {\n setUserScrolled(true)\n setAutoScroll(false)\n }\n }\n\n // Scroll to bottom function\n const scrollToBottom = () => {\n if (scrollContainerRef.current) {\n scrollContainerRef.current.scrollTop = scrollContainerRef.current.scrollHeight\n\n // Update state\n setHasNewContent(false)\n setUserScrolled(false)\n setAutoScroll(true)\n setLastSeenMessageIndex(allMessages.length)\n }\n }\n\n // Calculate unseen messages count\n const unseenMessagesCount = Math.max(0, allMessages.length - lastSeenMessageIndex)\n\n // Auto-scroll to bottom when new messages arrive\n useEffect(() => {\n if (allMessages.length === 0) return\n\n if (autoScroll && scrollContainerRef.current) {\n // Auto-scroll to bottom immediately for new messages\n setTimeout(() => {\n if (scrollContainerRef.current) {\n scrollContainerRef.current.scrollTop = scrollContainerRef.current.scrollHeight\n }\n }, 100)\n } else if (userScrolled && !autoScroll) {\n // If user has scrolled up and there's a new message, show the indicator\n setHasNewContent(true)\n }\n }, [allMessages])\n\n useEffect(() => {\n if (isVisible && allMessages.length > 0) {\n setAutoScroll(true)\n setUserScrolled(false)\n setHasNewContent(false)\n }\n }, [isVisible])\n\n // Listen for transcription events\n useEventListener('phone-island-conversation-transcription', (data: any) => {\n addTranscriptionMessage(data)\n })\n\n useEventListener('phone-island-transcription-opened', () => {\n resetTranscriptionState()\n })\n\n useEventListener('phone-island-transcription-closed', () => {\n resetTranscriptionState()\n })\n\n\n // Format timestamp - converts seconds from call start to MM:SS format\n const formatTimestamp = (timestamp: number) => {\n const minutes = Math.floor(timestamp / 60)\n const seconds = Math.floor(timestamp % 60)\n return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`\n }\n\n // Skeleton component for loading state\n const TranscriptionSkeleton: FC = () => (\n <div className='pi-space-y-2 pi-animate-pulse'>\n {/* First shorter bar */}\n <div className='pi-h-4 pi-bg-gray-200 dark:pi-bg-gray-700 pi-rounded pi-w-2/5'></div>\n {/* Second longer bar */}\n <div className='pi-h-4 pi-bg-gray-200 dark:pi-bg-gray-700 pi-rounded pi-w-4/5'></div>\n {/* First shorter bar */}\n <div className='pi-h-4 pi-bg-gray-200 dark:pi-bg-gray-700 pi-rounded pi-w-2/5'></div>\n {/* Third medium bar */}\n <div className='pi-h-4 pi-bg-gray-200 dark:pi-bg-gray-700 pi-rounded pi-w-4/5'></div>\n </div>\n )\n\n const containerClassName = `pi-absolute pi-w-full pi-bg-elevationL2 pi-flex pi-flex-col pi-text-iconWhite dark:pi-text-iconWhiteDark pi-left-0 -pi-z-10 pi-pointer-events-auto ${\n view === 'settings' || actionsExpanded ? 'pi-top-[17rem]' : 'pi-top-[13rem]'\n }`\n\n return (\n <>\n <AnimatePresence>\n {isVisible && (\n <motion.div className={containerClassName} style={STYLE_CONFIG} {...ANIMATION_CONFIG}>\n <div className='pi-h-full pi-rounded-lg pi-overflow-hidden pi-bg-elevationL2 dark:pi-bg-elevationL2Dark pi-relative pi-flex pi-flex-col pi-border-2 pi-border-gray-100 dark:pi-border-gray-600 pi-shadow-lg'>\n {/* Main Content Card */}\n <div className='pi-flex-1 pi-pt-4 pi-px-4 pi-mt-8'>\n <div className='pi-h-60 pi-bg-gray-100 dark:pi-bg-gray-800 pi-rounded-lg pi-border pi-border-gray-200 dark:pi-border-gray-700 pi-overflow-hidden pi-flex pi-flex-col'>\n <AnimatePresence>\n {hasNewContent && userScrolled && (\n <motion.div\n initial={{ opacity: 0, y: -10, scale: 0.9 }}\n animate={{ opacity: 1, y: 0, scale: 1 }}\n exit={{ opacity: 0, y: -10, scale: 0.9 }}\n className='pi-absolute pi-top-16 pi-left-0 pi-right-0 pi-flex pi-justify-center pi-z-20'\n >\n <button\n onClick={scrollToBottom}\n className='pi-bg-phoneIslandActive dark:pi-bg-phoneIslandActiveDark hover:pi-bg-gray-500 dark:hover:pi-bg-gray-50 focus:pi-ring-emerald-500 dark:focus:pi-ring-emerald-300 pi-text-primaryInvert dark:pi-text-primaryInvertDark pi-px-4 pi-py-2 pi-rounded-full pi-text-sm pi-shadow-lg pi-flex pi-items-center pi-gap-2 pi-transition-all pi-duration-200 pi-border pi-backdrop-blur-sm'\n >\n <FontAwesomeIcon icon={faArrowDown} className='pi-w-4 pi-h-4' />\n {unseenMessagesCount > 1\n ? t('TranscriptionView.New messages')\n : t('TranscriptionView.New message')}\n </button>\n </motion.div>\n )}\n </AnimatePresence>\n\n <div\n ref={scrollContainerRef}\n onScroll={handleScroll}\n className={`pi-flex-1 pi-p-4 ${\n visibleMessages.length > 0\n ? 'pi-overflow-y-auto pi-scrollbar-thin pi-scrollbar-thumb-gray-400 pi-scrollbar-thumb-rounded-full pi-scrollbar-thumb-opacity-50 pi-scrollbar-track-gray-200 dark:pi-scrollbar-track-gray-900 pi-scrollbar-track-rounded-full pi-scrollbar-track-opacity-25'\n : 'pi-overflow-hidden'\n }`}\n >\n {visibleMessages.length === 0 ? (\n <TranscriptionSkeleton />\n ) : (\n <div className='pi-space-y-4'>\n {/* Show indicator if there are more messages than displayed */}\n {allMessages.length > MAX_VISIBLE_MESSAGES && (\n <div className='pi-text-center pi-py-2 pi-text-xs pi-text-gray-500 dark:pi-text-gray-400 pi-border-b pi-border-gray-200 dark:pi-border-gray-700'>\n {t('TranscriptionView.Showing messages', {\n visible: visibleMessages.length,\n total: allMessages.length,\n })}\n </div>\n )}\n\n {visibleMessages.map((message, index) => (\n <motion.div\n key={message.id}\n layout\n initial={{ opacity: 0, y: 20 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.3, layout: { duration: 0.25 } }}\n className='pi-mb-4'\n >\n {/* Speaker Name */}\n <div className='pi-mb-2'>\n <span className='pi-font-medium pi-text-xs pi-text-secondaryNeutral dark:pi-text-secondaryNeutralDark'>\n {isMyNumber(message.speakerNumber)\n ? t('Common.Me', 'Me')\n : message.speaker}\n </span>\n </div>\n\n {/* Message Bubble with Background */}\n <div\n className={`pi-relative pi-p-3 pi-rounded-lg pi-text-xs pi-font-regular ${\n isMyNumber(message.speakerNumber)\n ? 'pi-text-gray-800 dark:pi-text-gray-100 pi-bg-gray-200 dark:pi-bg-gray-600'\n : 'pi-text-indigo-800 dark:pi-text-indigo-100 pi-bg-indigo-100 dark:pi-bg-indigo-700'\n }`}\n >\n <div className='pi-flex pi-items-start pi-justify-between pi-gap-3'>\n <div className='pi-flex-1'>\n <TypewriterText\n text={message.text}\n isFinal={message.isFinal}\n speed={30}\n />\n </div>\n {/* Timestamp on the right */}\n <div\n className={`pi-flex-shrink-0 pi-mt-1 pi-text-xs pi-font-regular ${\n isMyNumber(message.speakerNumber)\n ? 'pi-text-gray-800 dark:pi-text-gray-100 pi-bg-gray-200 dark:pi-bg-gray-600'\n : 'pi-text-indigo-800 dark:pi-text-indigo-100 pi-bg-indigo-100 dark:pi-bg-indigo-700'\n }`}\n >\n {formatTimestamp(message.timestamp)}\n </div>\n </div>\n </div>\n\n {!message.isFinal && message.text.trim() !== '' && (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n className='pi-mt-1 pi-ml-3 pi-flex pi-items-center pi-gap-1'\n >\n <span className='pi-text-xs pi-text-gray-400 dark:pi-text-gray-500 pi-italic'>\n {t('TranscriptionView.Is speaking', '')}\n </span>\n <div className='pi-inline-flex pi-items-center pi-gap-1'>\n <motion.div\n className='pi-w-1 pi-h-1 pi-bg-gray-400 dark:pi-bg-gray-500 pi-rounded-full'\n animate={{ opacity: [0.3, 1, 0.3] }}\n transition={{ duration: 1.5, repeat: Infinity, delay: 0 }}\n />\n <motion.div\n className='pi-w-1 pi-h-1 pi-bg-gray-400 dark:pi-bg-gray-500 pi-rounded-full'\n animate={{ opacity: [0.3, 1, 0.3] }}\n transition={{ duration: 1.5, repeat: Infinity, delay: 0.2 }}\n />\n <motion.div\n className='pi-w-1 pi-h-1 pi-bg-gray-400 dark:pi-bg-gray-500 pi-rounded-full'\n animate={{ opacity: [0.3, 1, 0.3] }}\n transition={{ duration: 1.5, repeat: Infinity, delay: 0.4 }}\n />\n </div>\n </motion.div>\n )}\n </motion.div>\n ))}\n <div ref={messagesEndRef} className='pi-pb-4' />\n </div>\n )}\n </div>\n </div>\n </div>\n\n {/* Footer with Close Button */}\n <div className='pi-flex pi-items-center pi-justify-center pi-py-2'>\n <button\n onClick={() => eventDispatch('phone-island-transcription-close', {})}\n className='pi-bg-transparent dark:enabled:hover:pi-bg-gray-700/30 enabled:hover:pi-bg-gray-300/70 focus:pi-ring-offset-gray-200 dark:focus:pi-ring-gray-500 focus:pi-ring-gray-400 pi-text-secondaryNeutral pi-outline-none pi-border-transparent dark:pi-text-secondaryNeutralDark pi-h-12 pi-w-24 pi-rounded-fullpi-px-4 pi-py-2 pi-rounded-full pi-text-lg pi-flex pi-items-center pi-gap-2 pi-transition-all pi-duration-200 pi-border pi-backdrop-blur-sm'\n >\n <FontAwesomeIcon icon={faAngleUp} className='pi-w-4 pi-h-4 pi-ml-1' />\n {t('Common.Close')}\n </button>\n </div>\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n </>\n )\n})\n\nTranscriptionView.displayName = 'TranscriptionView'\n\nexport default TranscriptionView\n"],"names":["ANIMATION_CONFIG","initial","height","opacity","animate","exit","transition","duration","ease","STYLE_CONFIG","borderBottomLeftRadius","borderBottomRightRadius","transformOrigin","overflow","TypewriterText","_a","text","isFinal","_b","speed","_c","useState","displayText","setDisplayText","useEffect","currentIndex","typeInterval","setInterval","length","slice","clearInterval","React","createElement","className","TranscriptionView","memo","isVisible","useSelector","state","island","actionsExpanded","view","currentUser","currentCallStartTime","currentCall","startTime","t","useTranslation","allMessages","setAllMessages","_d","visibleMessages","setVisibleMessages","_e","hasNewContent","setHasNewContent","_f","userScrolled","setUserScrolled","_g","lastSeenMessageIndex","setLastSeenMessageIndex","messagesEndRef","useRef","scrollContainerRef","timestampCorrectionRef","_h","autoScroll","setAutoScroll","resetTranscriptionState","current","isMyNumber","speakerNumber","endpoints","mainextension","id","extension","Object","values","some","ext","exten","startIndex","Math","max","recentMessages","sorted","__spreadArray","sort","a","b","timestamp","addTranscriptionMessage","data","start","rawTimestamp","Number","channelIndex","isFinite","channel_index","segmentStart","segment_start","uniqueId","uniqueid","concat","toFixed","localElapsed","floor","Date","now","correctedTimestamp","error","boundedError","min","message","speaker","speaker_name","speaker_number","counterpart","speaker_counterpart_name","counterpartNumber","speaker_counterpart_number","transcription","is_final","prevMessages","findLastIndex","predicate","index","isSameSpeakerStream","existingMessage","existingMessageIndex","findIndex","msg","__assign","updatedMessages","similarFinalIndex","existing","trim","abs","activeInterimIndex_1","activeInterimIndex","unseenMessagesCount","setTimeout","scrollTop","scrollHeight","useEventListener","containerClassName","Fragment","AnimatePresence","motion","div","style","y","scale","onClick","FontAwesomeIcon","icon","faArrowDown","ref","onScroll","atBottom","isAtBottom","visible","total","map","key","layout","minutes","seconds","toString","padStart","repeat","Infinity","delay","eventDispatch","faAngleUp","displayName"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("../node_modules/tslib/tslib.es6.js"),t=require("react");require("../node_modules/react-redux/es/index.js");var n=require("../node_modules/webrtc-adapter/src/js/adapter_core.js"),r=require("../lib/webrtc/janus.js"),o=require("../lib/webrtc/messages.js"),a=require("../store/index.js"),i=require("../lib/devices/devices.js"),c=require("../lib/phone/call.js"),l=require("../lib/webrtc/connection.js"),s=require("../static/outgoing_ringtone.js"),u=require("../utils/customHooks/useEventListener.js"),d=require("../utils/genericFunctions/eventDispatch.js"),g=require("../utils/genericFunctions/localStorage.js"),v=require("../lib/user/default_device.js"),p=require("../node_modules/react-redux/es/hooks/useDispatch.js");function m(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var f=m(t);exports.WebRTC=function(m){var S,w,b=m.hostName,h=m.sipExten,D=m.sipSecret,C=m.children,I=m.sipHost,A=m.sipPort,R=m.reload,T=m.uaType,y=m.reloadedCallback,k=p.useDispatch(),U=t.useRef(null),E=t.useRef(!1),j=t.useRef(Date.now()),G=t.useRef(!1),O=t.useRef(0),N=t.useRef(!1),J=t.useRef(null),_=t.useRef(!1),L=t.useRef(!1),P=t.useRef(null),W=t.useRef(null),M=t.useRef(r.default),H={},q=t.useCallback((function(){var t,r,i,l,u,p;if(L.current)console.log("[JANUS-GUARD] initWebRTC already in progress, skipping",{timestamp:(new Date).toISOString()});else{L.current=!0,P.current&&clearTimeout(P.current),P.current=setTimeout((function(){L.current&&(console.warn("[JANUS-GUARD] Initialization timeout (30s) - resetting isInitializing flag",{timestamp:(new Date).toISOString()}),L.current=!1,N.current=!0,k.alerts.setAlert("webrtc_down"),d.eventDispatch("phone-island-alert-set",{type:"webrtc_down"})),P.current=null}),3e4);var m=a.store.getState().webrtc,f=m.janusInstance,S=m.registered,w=!0;if(f){var C=null===(t=f.getSessionId)||void 0===t?void 0:t.call(f),R=null===(r=f.isConnected)||void 0===r?void 0:r.call(f),y=O.current,U=Math.round(y/6e4),j=y>18e5,G=a.store.getState().webrtc,_=G.sipcall,q=!!G.jsepGlobal,V="connected"===(null===(l=null===(i=null==_?void 0:_.webrtcStuff)||void 0===i?void 0:i.pc)||void 0===l?void 0:l.iceConnectionState)||"completed"===(null===(p=null===(u=null==_?void 0:_.webrtcStuff)||void 0===u?void 0:u.pc)||void 0===p?void 0:p.iceConnectionState);if(R&&S&&(!j||(q||V)))console.log("[JANUS-GUARD] Valid session already exists, skipping init",{sessionId:C,isConnected:R,registered:S,inactivityMinutes:U,hasIncomingCall:q,hasActiveCall:V,timestamp:(new Date).toISOString()}),w=!1;else{var x=R?S?j?"long inactivity (".concat(U," min) without active call"):"unknown":"not registered":"not connected";console.warn("[JANUS-GUARD] Session exists but is invalid, cleaning up and reinitializing",{reason:x,sessionId:C,isConnected:R,registered:S,inactivityMinutes:U,hasIncomingCall:q,hasActiveCall:V,timestamp:(new Date).toISOString()});try{f.destroy({unload:!0,notifyDestroyed:!1,cleanupHandles:!0})}catch(e){console.error("[JANUS-GUARD] Error destroying session",e)}a.store.dispatch.webrtc.updateWebRTC({janusInstance:null,sipcall:null,registered:!1,jsepGlobal:null})}}if(!w)return L.current=!1,void(P.current&&(clearTimeout(P.current),P.current=null));console.log("[JANUS-GUARD] Creating new session",{timestamp:(new Date).toISOString()}),M.current.init({debug:"all",dependencies:M.current.useDefaultDependencies({adapter:n.default}),callback:function(){var t,n=new M.current({server:"https://".concat(b,"/janus"),success:function(){n.attach&&n.attach({plugin:"janus.plugin.sip",opaqueId:"sebastian_"+(new Date).getTime(),success:function(e){e&&(k.webrtc.updateWebRTC({sipcall:e}),o.register({sipExten:h,sipSecret:D,sipHost:I,sipPort:A}),e&&M.current.log&&M.current.log("SIP plugin attached! ("+e.getPlugin()+", id = )"))},error:function(e){M.current.error&&(M.current.error(" -- Error attaching plugin..."),M.current.error(e)),L.current=!1,P.current&&(clearTimeout(P.current),P.current=null)},consentDialog:function(e){M.current.log&&M.current.log("janus consentDialog (on: ".concat(e,")"))},webrtcState:function(e){if(M.current.log&&M.current.log("Janus says our WebRTC PeerConnection is "+(e?"up":"down")+" now"),e){var t=a.store.getState().island.view,n=a.store.getState().currentCall,r=n.accepted,o=n.outgoing,i=n.incoming;(r||o||i)&&!t&&(console.warn('[WEBRTC] WebRTC up but view is null with active call - forcing view to "call"'),a.store.dispatch.island.setIslandView("call"))}},iceState:function(e){if(a.store.getState().webrtc.sipcall&&M.current.log&&M.current.log('ICE state of PeerConnection of handle has changed to "'.concat(e,'"')),"connected"===e||"completed"===e){var t=a.store.getState().island.view,n=a.store.getState().currentCall,r=n.accepted,o=n.outgoing,i=n.incoming;(r||o||i)&&!t&&(console.warn('[WEBRTC] ICE connected but view is null with active call - forcing view to "call"'),a.store.dispatch.island.setIslandView("call"))}},mediaState:function(e,t){M.current.log&&M.current.log("Janus "+(t?"started":"stopped")+" receiving our "+e)},slowLink:function(e,t){e?M.current.warn&&M.current.warn("SLOW link: several missing packets from janus (".concat(t,")")):M.current.warn&&M.current.warn("SLOW link: janus is not receiving all your packets (".concat(t,")"))},onmessage:function(e,t){var n=a.store.getState().webrtc.sipcall;M.current.debug&&(M.current.debug(" ::: Got a message :::"),M.current.debug(JSON.stringify(e)));var r=e.error;if(null==r||null==r){var i=e.result;if(null!=i&&void 0!==i.event&&null!==i.event){var l=i.event,u=a.store.getState().recorder.recording,g=a.store.getState().island.view;switch(l){case"registration_failed":M.current.error&&M.current.error("Registration failed: "+i.code+" "+i.reason);break;case"unregistered":M.current.log&&M.current.log("Successfully un-registered as "+i.username+"!"),a.store.dispatch.webrtc.updateWebRTC({registered:!1}),d.eventDispatch("phone-island-webrtc-unregistered",{});break;case"registered":M.current.log&&M.current.log("Successfully registered as "+i.username+"!"),console.log("[REGISTER] Registration successful",{username:i.username,wasAlreadyRegistered:a.store.getState().webrtc.registered,timestamp:(new Date).toISOString()}),d.eventDispatch("phone-island-webrtc-registered",{}),a.store.getState().webrtc.registered||a.store.dispatch.webrtc.updateWebRTC({registered:!0}),k.alerts.removeAlert("webrtc_down"),d.eventDispatch("phone-island-alert-removed",{type:"webrtc_down"}),N.current=!1,L.current=!1,P.current&&(clearTimeout(P.current),P.current=null),W.current&&(clearTimeout(W.current),W.current=null,console.info("[JANUS-GUARD] Cleared network error grace timeout - connection restored")),k.webrtc.updateLastActivity((new Date).getTime());break;case"registering":M.current.log&&M.current.log("janus registering");break;case"calling":k.currentCall.checkOutgoingUpdate({outgoingWebRTC:!0}),k.webrtc.updateLastActivity((new Date).getTime());break;case"ringing":a.store.getState().player.audioPlayerPlaying||k.player.updateStartAudioPlayer({src:s.default,loop:!0}),k.webrtc.updateLastActivity((new Date).getTime()),"call"!==g&&k.island.setIslandView("call");break;case"progress":M.current.log&&M.current.log("There's early media from "+i.username+", wairing for the call!"),t&&o.handleRemote(t),k.webrtc.updateLastActivity((new Date).getTime());break;case"incomingcall":var p=a.store.getState().currentUser.default_device,m=a.store.getState().currentUser,f=m.endpoints,S=m.username,w=a.store.getState().users.extensions,b=function(){if(!w||!S)return!1;var e=Object.values(w).filter((function(e){return(null==e?void 0:e.username)===S}));return null==e?void 0:e.some((function(e){var t=null==f?void 0:f.extension.find((function(t){return t.id===(null==e?void 0:e.exten)}));return"nethlink"===(null==t?void 0:t.type)&&"offline"!==(null==e?void 0:e.status)}))};t&&(console.log("[JSEP] Saving jsepGlobal for incoming call",{from:i.username,timestamp:(new Date).toISOString()}),k.webrtc.updateWebRTC({jsepGlobal:t}),J.current=Date.now()),("mobile"===T&&b()||"desktop"===T&&("webrtc"===(null==p?void 0:p.type)||void 0===(null==p?void 0:p.type)&&!b()||!b()&&"physical"===(null==p?void 0:p.type)))&&(u?k.recorder.setIncoming(!0):(k.currentCall.checkIncomingUpdatePlay({incoming:!0,incomingWebRTC:!0}),M.current.log&&(k.currentCall.updateIncoming(!0),M.current.log("Incoming call from "+i.username+"!"))),k.webrtc.updateLastActivity((new Date).getTime()));break;case"accepted":var h=Math.floor(Date.now()/1e3);if(M.current.log){var D=i.username||i.displayname||a.store.getState().currentCall.number||"Remote party";M.current.log(D+" accepted the call!")}t&&o.handleRemote(t),console.log("[JSEP] Clearing jsepGlobal after call accepted",{timestamp:(new Date).toISOString()}),k.webrtc.updateWebRTC({jsepGlobal:null}),J.current=null,k.currentCall.checkAcceptedUpdate({acceptedWebRTC:!0}),k.currentCall.updateCurrentCall({incoming:!1,incomingWebRTC:!1,startTime:null==h?void 0:h.toString()}),a.store.dispatch.player.stopAudioPlayer(),k.webrtc.updateLastActivity((new Date).getTime());break;case"hangup":console.log("[JSEP] Clearing jsepGlobal on hangup",{timestamp:(new Date).toISOString()}),k.webrtc.updateWebRTC({jsepGlobal:null}),J.current=null,u&&k.recorder.setRecording(!1),v.isPhysical()||"mobile"===T||(c.hangupCurrentCall(),n.hangup(),a.store.dispatch.player.stopAudioPlayer(),a.store.dispatch.currentCall.reset(),M.current.log&&M.current.log("Call hung up ("+i.code+" "+i.reason+")!"),k.webrtc.updateLastActivity((new Date).getTime())),a.store.dispatch.player.stopAudioPlayer();var C=a.store.getState().screenShare,I=C.active,A=C.plugin,R=C.localScreenStream,y=C.remoteScreenStream;I&&(M.current.stopAllTracks(R),M.current.stopAllTracks(y),k.screenShare.update({active:!1}),A.detach()),N.current&&(console.info("[JANUS-GUARD] Call ended with stale connection - forcing reload to reset Janus session",{timestamp:(new Date).toISOString()}),setTimeout((function(){k.island.setForceReload(!0)}),500));break;case"gateway_down":console.warn("THE GATEWAY IS DOWN");break;case"info":"application/media_control+xml"===i.type&&i.content.includes("<picture_fast_update")&&n.send({message:{request:"keyframe",user:!0,peer:!0}});break;default:M.current.debug&&M.current.debug("Event not handled:",l)}}}else a.store.getState().webrtc.registered?(n&&n.hangup(),a.store.dispatch.player.stopAudioPlayer()):M.current.log&&M.current.log("User is not registered")},onlocaltrack:function(e,t){M.current.debug&&M.current.debug("Local track "+(t?"added":"removed")+":",e);var n=e.id.replace(/[{}]/g,"");if(!t){var r=H[n];if(r)try{var o=r.getTracks();for(var i in o){var c=o[i];c&&c.stop()}}catch(e){M.current.error&&M.current.error("Error removing track:",e)}return e.kind,void delete H[n]}var l=H[n];if(!l)if("audio"===e.kind)l=new MediaStream([e]),a.store.dispatch.webrtc.updateLocalAudioStream(l);else{l=new MediaStream([e]),a.store.dispatch.webrtc.updateLocalVideoStream(l),H[n]=l,M.current.debug&&M.current.debug("Created local stream:",l);var s=a.store.getState().player.localVideo;M.current.attachMediaStream&&s&&s.current&&M.current.attachMediaStream(s.current,l)}},onremotetrack:function(t,n,r){var o=this;if(M.current.debug&&M.current.debug("Remote track (mid="+n+") "+(r?"added":"removed")+":",t),a.store.dispatch.player.stopAudioPlayer(),!r)return t.kind,void k.currentCall.updateCurrentCall({showRemoteVideoPlaceHolder:!0});if("audio"===t.kind){var i=new MediaStream([t]);M.current.debug&&M.current.debug("Created remote audio stream: "+i);var c=a.store.getState().player.remoteAudio;if(c&&c.current&&M.current.attachMediaStream){M.current.attachMediaStream(c.current,i);var l=g.getJSONItem("phone-island-audio-output-device");if(null==l?void 0:l.deviceId){e.__awaiter(o,void 0,void 0,(function(){var t,n,r,o,a;return e.__generator(this,(function(e){switch(e.label){case 0:if(!(t=l.deviceId)||"default"===t)return[3,4];e.label=1;case 1:return e.trys.push([1,3,,4]),[4,navigator.mediaDevices.enumerateDevices()];case 2:return n=e.sent(),n.filter((function(e){return"audiooutput"===e.kind})).some((function(e){return e.deviceId===t}))||(console.warn("Saved audio device ".concat(t," no longer available, using default device")),t="default",g.setJSONItem("phone-island-audio-output-device",{deviceId:"default"})),[3,4];case 3:return r=e.sent(),console.warn("Error checking device availability, using default:",r),t="default",[3,4];case 4:if(!c.current)return console.warn("Remote audio element no longer available"),[2];e.label=5;case 5:return e.trys.push([5,7,,12]),[4,c.current.setSinkId(t)];case 6:return e.sent(),console.info("Audio output device applied successfully to new stream:",t),[3,12];case 7:if(o=e.sent(),console.warn("Failed to apply audio output device to new stream:",o),"default"===t||!c.current)return[3,11];e.label=8;case 8:return e.trys.push([8,10,,11]),[4,c.current.setSinkId("default")];case 9:return e.sent(),g.setJSONItem("phone-island-audio-output-device",{deviceId:"default"}),console.info("Fallback to default device successful"),[3,11];case 10:return a=e.sent(),console.error("Even default device failed:",a),[3,11];case 11:return[3,12];case 12:return[2]}}))}))}}a.store.dispatch.webrtc.updateRemoteAudioStream(i)}else{i=new MediaStream([t]);a.store.dispatch.webrtc.updateRemoteVideoStream(i),M.current.debug&&M.current.debug("Created remote video stream:"+i);var s=a.store.getState().player.largeRemoteVideo,u=a.store.getState().player.smallRemoteVideo;M.current.attachMediaStream&&s&&s.current&&M.current.attachMediaStream(s.current,i),M.current.attachMediaStream&&u&&u.current&&(M.current.attachMediaStream(u.current,i),k.currentCall.updateCurrentCall({showRemoteVideoPlaceHolder:!1}))}},oncleanup:function(){M.current.log&&M.current.log(" ::: janus Got a cleanup notification :::")}})},error:function(e){var t,n;M.current.log&&M.current.log("error",e),console.warn("[JANUS-GUARD] Network error detected, marking connection as stale",{error:e,timestamp:(new Date).toISOString()}),N.current=!0,L.current=!1,P.current&&(clearTimeout(P.current),P.current=null);var r=a.store.getState().webrtc.sipcall,o=a.store.getState().currentCall,i=o.accepted,c=o.outgoing,l=null===(n=null===(t=null==r?void 0:r.webrtcStuff)||void 0===t?void 0:t.pc)||void 0===n?void 0:n.iceConnectionState;i||c||"connected"===l||"completed"===l?(console.info("[JANUS-GUARD] Network error during active call - giving ICE grace period to recover",{iceState:l,accepted:i,outgoing:c,timestamp:(new Date).toISOString()}),W.current&&clearTimeout(W.current),W.current=setTimeout((function(){var e,t,n=a.store.getState().webrtc.sipcall,r=null===(t=null===(e=null==n?void 0:n.webrtcStuff)||void 0===e?void 0:e.pc)||void 0===t?void 0:t.iceConnectionState,o=a.store.getState().currentCall,i=o.accepted,c=o.outgoing,l=i||c;l&&("disconnected"===r||"failed"===r||!r)?(console.warn("[JANUS-GUARD] ICE failed to recover after grace period - resetting call state and activating alert",{checkIceState:r||"undefined/null",timestamp:(new Date).toISOString()}),a.store.dispatch.currentCall.reset(),k.alerts.setAlert("webrtc_down")):l?console.info("[JANUS-GUARD] ICE recovered during grace period - call preserved, Janus HTTP still stale",{checkIceState:r,connectionStale:N.current,timestamp:(new Date).toISOString()}):(console.info("[JANUS-GUARD] Call ended during grace period - activating alert for reconnection",{timestamp:(new Date).toISOString()}),k.alerts.setAlert("webrtc_down")),W.current=null}),15e3)):k.alerts.setAlert("webrtc_down")},destroyed:function(){console.log("[JANUS-GUARD] Session destroyed, clearing janusInstance",{timestamp:(new Date).toISOString()}),L.current=!1,P.current&&(clearTimeout(P.current),P.current=null),k.webrtc.updateWebRTC({destroyed:!0,janusInstance:null}),E.current?console.log("[JANUS-GUARD] Skipping alert activation (voluntary reload in progress)"):(console.log("[JANUS-GUARD] Activating webrtc_down alert (not a voluntary reload)"),k.alerts.setAlert("webrtc_down"))}});console.log("[JANUS-GUARD] Saving janusInstance to Redux",{sessionId:null===(t=n.getSessionId)||void 0===t?void 0:t.call(n),timestamp:(new Date).toISOString()}),k.webrtc.updateWebRTC({janusInstance:n})}})}}),[M.current]);t.useEffect((function(){void 0!==a.store.getState().currentUser.default_device&&i.checkMediaPermissions()}),[null===(w=null===(S=null===a.store||void 0===a.store?void 0:a.store.getState())||void 0===S?void 0:S.currentUser)||void 0===w?void 0:w.default_device]);var V=t.useState(navigator.onLine),x=V[0],z=V[1],F=t.useState(!1),B=F[0],Y=F[1],K=t.useRef(!1);return t.useEffect((function(){var e=function(){return z(!0)},t=function(){return z(!1)};return window.addEventListener("online",e),window.addEventListener("offline",t),function(){window.removeEventListener("online",e),window.removeEventListener("offline",t)}}),[]),t.useEffect((function(){x?K.current&&(console.log("Internet connection restored."),Y(!0),K.current=!1):(console.log("Internet connection lost."),K.current=!0,Y(!1))}),[x]),t.useEffect((function(){var e;return q(),e=a.store.getState().webrtc.CHECK_INTERVAL_TIME,U.current||(U.current=setInterval((function(){return l.webrtcCheck((function(){o.register({sipExten:h,sipSecret:D,sipHost:I,sipPort:A})}))}),e)),function(){o.unregister(),clearInterval(U.current),P.current&&(clearTimeout(P.current),P.current=null),W.current&&(clearTimeout(W.current),W.current=null)}}),[]),t.useEffect((function(){var e,t,n,r,i;if(R||B){var c=a.store.getState().alerts.data,l=a.store.getState().island.forceReload,s=a.store.getState().webrtc,u=s.sipcall,d=s.janusInstance,g=a.store.getState().currentCall,v=g.accepted,p=g.outgoing,m=null===(t=null===(e=null==u?void 0:u.webrtcStuff)||void 0===e?void 0:e.pc)||void 0===t?void 0:t.iceConnectionState,f=v||p||"connected"===m||"completed"===m,S=(null===(n=c.webrtc_down)||void 0===n?void 0:n.active)||!1;if(B&&f&&!l){var w=null===(r=null==d?void 0:d.isConnected)||void 0===r?void 0:r.call(d);return!w&&(null==d?void 0:d.reconnect)?(console.info("[JANUS-GUARD] Connection returned with active call but Janus HTTP is dead - attempting Janus reconnect",{iceState:m,sessionId:null===(i=null==d?void 0:d.getSessionId)||void 0===i?void 0:i.call(d),timestamp:(new Date).toISOString()}),d.reconnect({success:function(){var e;console.info("[JANUS-GUARD] Janus HTTP reconnected successfully during active call (connectionReturned)",{sessionId:null===(e=null==d?void 0:d.getSessionId)||void 0===e?void 0:e.call(d),timestamp:(new Date).toISOString()}),N.current=!1,W.current&&(clearTimeout(W.current),W.current=null)},error:function(e){console.error("[JANUS-GUARD] Janus reconnect failed during active call (connectionReturned)",{error:e,timestamp:(new Date).toISOString()}),N.current=!0}})):console.info("[JANUS-GUARD] Connection returned but active call in progress - Janus still connected, no action needed",{iceState:m,janusConnected:w,timestamp:(new Date).toISOString()}),void Y(!1)}if(S||l||B){if(E.current||L.current)return void console.log("[JANUS-GUARD] Reload or init already in progress, skipping",{isReloading:E.current,isInitializing:L.current});E.current=!0,P.current&&(clearTimeout(P.current),P.current=null),console.info(l?"Force reload requested, performing full WebRTC reconnection":B?"Internet connection restored, performing full WebRTC reconnection":"WebRTC down detected (alert active), performing full reload"),l&&a.store.dispatch.island.setForceReload(!1),console.log("[JANUS-GUARD] Manual reload, clearing janusInstance and registered state",{timestamp:(new Date).toISOString()}),k.webrtc.updateWebRTC({janusInstance:null,sipcall:null,registered:!1,jsepGlobal:null}),J.current=null,o.unregister(),u&&u.detach(),d&&d.destroy&&d.destroy({unload:!0,notifyDestroyed:!1,cleanupHandles:!0}),setTimeout((function(){q(),B&&Y(!1),y&&y(),setTimeout((function(){E.current=!1,N.current=!1,_.current=!1,O.current=0}),1e3)}),100)}else console.info("WebRTC already connected (no alert active), skipping heavy reload"),y&&y()}}),[R,B]),t.useEffect((function(){var e,t=function(){var e;navigator&&(null===navigator||void 0===navigator?void 0:navigator.mediaDevices)&&(null===(e=null===navigator||void 0===navigator?void 0:navigator.mediaDevices)||void 0===e?void 0:e.enumerateDevices)?null===navigator||void 0===navigator||navigator.mediaDevices.enumerateDevices().then((function(e){k.mediaDevices.updateMediaDevices(e)})).catch((function(e){console.error("Error fetching devices:",e)})):(console.warn("MediaDevices API not supported in this browser or context"),k.mediaDevices.updateMediaDevices([]))};if(t(),navigator&&(null===navigator||void 0===navigator?void 0:navigator.mediaDevices))return null===(e=null===navigator||void 0===navigator?void 0:navigator.mediaDevices)||void 0===e||e.addEventListener("devicechange",t),function(){var e;null===(e=null===navigator||void 0===navigator?void 0:navigator.mediaDevices)||void 0===e||e.removeEventListener("devicechange",t)}}),[]),t.useEffect((function(){var e=function(){console.warn("[STANDBY-GUARD] Page frozen (standby or browser froze tab)",{timestamp:(new Date).toISOString()}),_.current=!0},t=function(){console.log("[STANDBY-GUARD] Page resumed from freeze",{timestamp:(new Date).toISOString()})};return document.addEventListener("freeze",e),document.addEventListener("resume",t),function(){document.removeEventListener("freeze",e),document.removeEventListener("resume",t)}}),[]),t.useEffect((function(){var e=function(){var e,t,n,r,i=Date.now();if(document.hidden)G.current=!0,j.current=i,console.log("[STANDBY-GUARD] Tab going to background",{timestamp:(new Date).toISOString()});else if(G.current){var c=i-j.current;O.current=c,console.log("[STANDBY-GUARD] Tab returning to foreground",{timeHiddenMs:c,timeHiddenMinutes:Math.round(c/6e4),wasFrozen:_.current,timestamp:(new Date).toISOString()});var l=a.store.getState().webrtc,s=l.registered,u=l.jsepGlobal,d=l.sipcall,g=a.store.getState().currentCall.outgoing,v=!!u,p="connected"===(null===(t=null===(e=null==d?void 0:d.webrtcStuff)||void 0===e?void 0:e.pc)||void 0===t?void 0:t.iceConnectionState)||"completed"===(null===(r=null===(n=null==d?void 0:d.webrtcStuff)||void 0===n?void 0:n.pc)||void 0===r?void 0:r.iceConnectionState),m=v||p||g,f=c>18e4&&!m,S=c>18e5&&!m,w=_.current||N.current||f||S;if(w){var b=_.current?"frozen":N.current?"stale connection":S?"throttled >30min (session too old)":"throttled >3min";if(m){var h=p?"active call":g?"outgoing call":"incoming call";console.warn("[STANDBY-GUARD] Reload needed (".concat(b,") but ").concat(h," in progress. ")+"Skipping reload to preserve call.",{wasFrozen:_.current,connectionStale:N.current,wasThrottledShort:f,wasThrottledVeryLong:S,hasIncomingCall:v,hasOutgoingCall:g,hasActiveCall:p,timestamp:(new Date).toISOString()})}else console.warn("[STANDBY-GUARD] Reload needed (".concat(b,"), forcing reload"),{wasFrozen:_.current,connectionStale:N.current,wasThrottledShort:f,wasThrottledVeryLong:S,timestamp:(new Date).toISOString()})}else if(m){h=p?"active call":g?"outgoing call":"incoming call";console.log("[STANDBY-GUARD] Tab change without issues, ".concat(h," preserved"),{timeHiddenMinutes:Math.round(c/6e4),hasIncomingCall:v,hasOutgoingCall:g,hasActiveCall:p,timestamp:(new Date).toISOString()})}var D=s&&!E.current&&!L.current&&w&&!v&&!g;if(D){console.warn("[STANDBY-GUARD] ⚠️ Reloading WebRTC",{timestamp:(new Date).toISOString()}),E.current=!0,P.current&&(clearTimeout(P.current),P.current=null);var C=a.store.getState().webrtc,I=C.janusInstance,A=C.sipcall;o.unregister(),A&&A.detach(),I&&I.destroy&&I.destroy({unload:!0,notifyDestroyed:!1,cleanupHandles:!0}),k.webrtc.updateWebRTC({janusInstance:null,sipcall:null,registered:!1,jsepGlobal:null}),J.current=null,setTimeout((function(){q(),setTimeout((function(){E.current=!1,N.current=!1,_.current=!1,O.current=0}),1e3)}),100)}G.current=!1,D||(_.current=!1,O.current=0)}};return document.addEventListener("visibilitychange",e),function(){document.removeEventListener("visibilitychange",e)}}),[q,k]),u.useEventListener("phone-island-attach",(function(e){console.log("[EVENT] phone-island-attach received, calling initWebRTC",{timestamp:(new Date).toISOString()}),q(),d.eventDispatch("phone-island-attached",{})})),u.useEventListener("phone-island-socket-reconnected",(function(){var e,t,n,r,o=a.store.getState().webrtc,i=o.sipcall,c=o.janusInstance,l=a.store.getState().currentCall,s=l.accepted,u=l.outgoing,d=null===(t=null===(e=null==i?void 0:i.webrtcStuff)||void 0===e?void 0:e.pc)||void 0===t?void 0:t.iceConnectionState,g=s||u||"connected"===d||"completed"===d,v=null===(n=null==c?void 0:c.isConnected)||void 0===n?void 0:n.call(c);return console.log("[EVENT] phone-island-socket-reconnected received",{hasActiveCall:g,iceState:d,accepted:s,outgoing:u,janusConnected:v,timestamp:(new Date).toISOString()}),g&&!v&&(null==c?void 0:c.reconnect)?(console.info("[EVENT] Socket reconnected with active call but Janus HTTP is dead - attempting Janus reconnect",{iceState:d,sessionId:null===(r=null==c?void 0:c.getSessionId)||void 0===r?void 0:r.call(c),timestamp:(new Date).toISOString()}),void c.reconnect({success:function(){var e;console.info("[JANUS-GUARD] Janus HTTP reconnected successfully during active call",{sessionId:null===(e=null==c?void 0:c.getSessionId)||void 0===e?void 0:e.call(c),timestamp:(new Date).toISOString()}),N.current=!1,W.current&&(clearTimeout(W.current),W.current=null)},error:function(e){console.error("[JANUS-GUARD] Janus reconnect failed during active call",{error:e,timestamp:(new Date).toISOString()}),N.current=!0}})):g&&v?(console.info("[EVENT] Socket reconnected with active call, Janus HTTP still connected - no action needed",{iceState:d,timestamp:(new Date).toISOString()}),void(W.current&&(clearTimeout(W.current),W.current=null))):(a.store.getState().webrtc.jsepGlobal&&(console.log("[EVENT] Clearing stale jsepGlobal after socket reconnect",{timestamp:(new Date).toISOString()}),k.webrtc.updateWebRTC({jsepGlobal:null}),J.current=null),void Y(!0))})),u.useEventListener("phone-island-call-transfer",(function(t){var n=null==t?void 0:t.to;k.island.toggleIsOpen(!0),function(t){e.__awaiter(this,void 0,void 0,(function(){return e.__generator(this,(function(e){switch(e.label){case 0:return[4,c.attendedTransfer(t)];case 1:return e.sent()&&(k.currentCall.updateCurrentCall({transferring:!0,paused:!1}),k.player.playRemoteAudio()),[2]}}))}))}(n),d.eventDispatch("phone-island-call-transfer-opened",{})})),f.default.createElement(f.default.Fragment,null,C)};
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("../node_modules/tslib/tslib.es6.js"),t=require("react");require("../node_modules/react-redux/es/index.js");var n=require("../node_modules/webrtc-adapter/src/js/adapter_core.js"),r=require("../lib/webrtc/janus.js"),o=require("../lib/webrtc/messages.js"),a=require("../store/index.js"),i=require("../lib/devices/devices.js"),c=require("../lib/phone/call.js"),l=require("../lib/webrtc/connection.js"),s=require("../static/outgoing_ringtone.js"),u=require("../utils/customHooks/useEventListener.js"),d=require("../utils/genericFunctions/eventDispatch.js"),g=require("../utils/genericFunctions/localStorage.js"),v=require("../lib/user/default_device.js"),p=require("../node_modules/react-redux/es/hooks/useDispatch.js");function m(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var f=m(t);exports.WebRTC=function(m){var S,w,b=m.hostName,h=m.sipExten,D=m.sipSecret,C=m.children,I=m.sipHost,A=m.sipPort,R=m.reload,T=m.uaType,y=m.reloadedCallback,k=p.useDispatch(),U=t.useRef(null),E=t.useRef(!1),j=t.useRef(Date.now()),G=t.useRef(!1),O=t.useRef(0),N=t.useRef(!1),J=t.useRef(null),_=t.useRef(!1),L=t.useRef(!1),P=t.useRef(null),W=t.useRef(null),M=t.useRef(r.default),H={},q=t.useCallback((function(){var t,r,i,l,u,p;if(L.current)console.log("[JANUS-GUARD] initWebRTC already in progress, skipping",{timestamp:(new Date).toISOString()});else{L.current=!0,P.current&&clearTimeout(P.current),P.current=setTimeout((function(){L.current&&(console.warn("[JANUS-GUARD] Initialization timeout (30s) - resetting isInitializing flag",{timestamp:(new Date).toISOString()}),L.current=!1,N.current=!0,k.alerts.setAlert("webrtc_down"),d.eventDispatch("phone-island-alert-set",{type:"webrtc_down"})),P.current=null}),3e4);var m=a.store.getState().webrtc,f=m.janusInstance,S=m.registered,w=m.isDetached,C=!0;if(f){var R=null===(t=f.getSessionId)||void 0===t?void 0:t.call(f),y=null===(r=f.isConnected)||void 0===r?void 0:r.call(f),U=O.current,j=Math.round(U/6e4),G=U>18e5,_=a.store.getState().webrtc,q=_.sipcall,V=!!_.jsepGlobal,x="connected"===(null===(l=null===(i=null==q?void 0:q.webrtcStuff)||void 0===i?void 0:i.pc)||void 0===l?void 0:l.iceConnectionState)||"completed"===(null===(p=null===(u=null==q?void 0:q.webrtcStuff)||void 0===u?void 0:u.pc)||void 0===p?void 0:p.iceConnectionState);if(!y||!S||w||G&&!(V||x)){var z=y?w?"phone island detached":S?G?"long inactivity (".concat(j," min) without active call"):"unknown":"not registered":"not connected";console.warn("[JANUS-GUARD] Session exists but is invalid, cleaning up and reinitializing",{reason:z,sessionId:R,isConnected:y,registered:S,isDetached:w,inactivityMinutes:j,hasIncomingCall:V,hasActiveCall:x,timestamp:(new Date).toISOString()});try{f.destroy({unload:!0,notifyDestroyed:!1,cleanupHandles:!0})}catch(e){console.error("[JANUS-GUARD] Error destroying session",e)}a.store.dispatch.webrtc.updateWebRTC({janusInstance:null,sipcall:null,registered:!1,isDetached:!1,jsepGlobal:null})}else console.log("[JANUS-GUARD] Valid session already exists, skipping init",{sessionId:R,isConnected:y,registered:S,isDetached:w,inactivityMinutes:j,hasIncomingCall:V,hasActiveCall:x,timestamp:(new Date).toISOString()}),C=!1}if(!C)return L.current=!1,void(P.current&&(clearTimeout(P.current),P.current=null));console.log("[JANUS-GUARD] Creating new session",{timestamp:(new Date).toISOString()}),M.current.init({debug:"all",dependencies:M.current.useDefaultDependencies({adapter:n.default}),callback:function(){var t,n=new M.current({server:"https://".concat(b,"/janus"),success:function(){n.attach&&n.attach({plugin:"janus.plugin.sip",opaqueId:"sebastian_"+(new Date).getTime(),success:function(e){e&&(k.webrtc.updateWebRTC({sipcall:e}),o.register({sipExten:h,sipSecret:D,sipHost:I,sipPort:A}),e&&M.current.log&&M.current.log("SIP plugin attached! ("+e.getPlugin()+", id = )"))},error:function(e){M.current.error&&(M.current.error(" -- Error attaching plugin..."),M.current.error(e)),L.current=!1,P.current&&(clearTimeout(P.current),P.current=null)},consentDialog:function(e){M.current.log&&M.current.log("janus consentDialog (on: ".concat(e,")"))},webrtcState:function(e){if(M.current.log&&M.current.log("Janus says our WebRTC PeerConnection is "+(e?"up":"down")+" now"),e){var t=a.store.getState().island.view,n=a.store.getState().currentCall,r=n.accepted,o=n.outgoing,i=n.incoming;(r||o||i)&&!t&&(console.warn('[WEBRTC] WebRTC up but view is null with active call - forcing view to "call"'),a.store.dispatch.island.setIslandView("call"))}},iceState:function(e){if(a.store.getState().webrtc.sipcall&&M.current.log&&M.current.log('ICE state of PeerConnection of handle has changed to "'.concat(e,'"')),"connected"===e||"completed"===e){var t=a.store.getState().island.view,n=a.store.getState().currentCall,r=n.accepted,o=n.outgoing,i=n.incoming;(r||o||i)&&!t&&(console.warn('[WEBRTC] ICE connected but view is null with active call - forcing view to "call"'),a.store.dispatch.island.setIslandView("call"))}},mediaState:function(e,t){M.current.log&&M.current.log("Janus "+(t?"started":"stopped")+" receiving our "+e)},slowLink:function(e,t){e?M.current.warn&&M.current.warn("SLOW link: several missing packets from janus (".concat(t,")")):M.current.warn&&M.current.warn("SLOW link: janus is not receiving all your packets (".concat(t,")"))},onmessage:function(e,t){var n=a.store.getState().webrtc.sipcall;M.current.debug&&(M.current.debug(" ::: Got a message :::"),M.current.debug(JSON.stringify(e)));var r=e.error;if(null==r||null==r){var i=e.result;if(null!=i&&void 0!==i.event&&null!==i.event){var l=i.event,u=a.store.getState().recorder.recording,g=a.store.getState().island.view;switch(l){case"registration_failed":M.current.error&&M.current.error("Registration failed: "+i.code+" "+i.reason);break;case"unregistered":M.current.log&&M.current.log("Successfully un-registered as "+i.username+"!"),a.store.dispatch.webrtc.updateWebRTC({registered:!1}),d.eventDispatch("phone-island-webrtc-unregistered",{});break;case"registered":M.current.log&&M.current.log("Successfully registered as "+i.username+"!"),console.log("[REGISTER] Registration successful",{username:i.username,wasAlreadyRegistered:a.store.getState().webrtc.registered,timestamp:(new Date).toISOString()}),d.eventDispatch("phone-island-webrtc-registered",{}),a.store.getState().webrtc.registered||a.store.dispatch.webrtc.updateWebRTC({registered:!0,isDetached:!1}),k.alerts.removeAlert("webrtc_down"),d.eventDispatch("phone-island-alert-removed",{type:"webrtc_down"}),N.current=!1,L.current=!1,P.current&&(clearTimeout(P.current),P.current=null),W.current&&(clearTimeout(W.current),W.current=null,console.info("[JANUS-GUARD] Cleared network error grace timeout - connection restored")),k.webrtc.updateLastActivity((new Date).getTime());break;case"registering":M.current.log&&M.current.log("janus registering");break;case"calling":k.currentCall.checkOutgoingUpdate({outgoingWebRTC:!0}),k.webrtc.updateLastActivity((new Date).getTime());break;case"ringing":a.store.getState().player.audioPlayerPlaying||k.player.updateStartAudioPlayer({src:s.default,loop:!0}),k.webrtc.updateLastActivity((new Date).getTime()),"call"!==g&&k.island.setIslandView("call");break;case"progress":M.current.log&&M.current.log("There's early media from "+i.username+", wairing for the call!"),t&&o.handleRemote(t),k.webrtc.updateLastActivity((new Date).getTime());break;case"incomingcall":var p=a.store.getState().currentUser.default_device,m=a.store.getState().currentUser,f=m.endpoints,S=m.username,w=a.store.getState().users.extensions,b=function(){if(!w||!S)return!1;var e=Object.values(w).filter((function(e){return(null==e?void 0:e.username)===S}));return null==e?void 0:e.some((function(e){var t=null==f?void 0:f.extension.find((function(t){return t.id===(null==e?void 0:e.exten)}));return"nethlink"===(null==t?void 0:t.type)&&"offline"!==(null==e?void 0:e.status)}))};t&&(console.log("[JSEP] Saving jsepGlobal for incoming call",{from:i.username,timestamp:(new Date).toISOString()}),k.webrtc.updateWebRTC({jsepGlobal:t}),J.current=Date.now()),("mobile"===T&&b()||"desktop"===T&&("webrtc"===(null==p?void 0:p.type)||void 0===(null==p?void 0:p.type)&&!b()||!b()&&"physical"===(null==p?void 0:p.type)))&&(u?k.recorder.setIncoming(!0):(k.currentCall.checkIncomingUpdatePlay({incoming:!0,incomingWebRTC:!0}),M.current.log&&(k.currentCall.updateIncoming(!0),M.current.log("Incoming call from "+i.username+"!"))),k.webrtc.updateLastActivity((new Date).getTime()));break;case"accepted":var h=Math.floor(Date.now()/1e3);if(M.current.log){var D=i.username||i.displayname||a.store.getState().currentCall.number||"Remote party";M.current.log(D+" accepted the call!")}t&&o.handleRemote(t),console.log("[JSEP] Clearing jsepGlobal after call accepted",{timestamp:(new Date).toISOString()}),k.webrtc.updateWebRTC({jsepGlobal:null}),J.current=null,k.currentCall.checkAcceptedUpdate({acceptedWebRTC:!0}),k.currentCall.updateCurrentCall({incoming:!1,incomingWebRTC:!1,startTime:null==h?void 0:h.toString()}),a.store.dispatch.player.stopAudioPlayer(),k.webrtc.updateLastActivity((new Date).getTime());break;case"hangup":console.log("[JSEP] Clearing jsepGlobal on hangup",{timestamp:(new Date).toISOString()}),k.webrtc.updateWebRTC({jsepGlobal:null}),J.current=null,u&&k.recorder.setRecording(!1),v.isPhysical()||"mobile"===T||(c.hangupCurrentCall(),n.hangup(),a.store.dispatch.player.stopAudioPlayer(),a.store.dispatch.currentCall.reset(),M.current.log&&M.current.log("Call hung up ("+i.code+" "+i.reason+")!"),k.webrtc.updateLastActivity((new Date).getTime())),a.store.dispatch.player.stopAudioPlayer();var C=a.store.getState().screenShare,I=C.active,A=C.plugin,R=C.localScreenStream,y=C.remoteScreenStream;I&&(M.current.stopAllTracks(R),M.current.stopAllTracks(y),k.screenShare.update({active:!1}),A.detach()),N.current&&(console.info("[JANUS-GUARD] Call ended with stale connection - forcing reload to reset Janus session",{timestamp:(new Date).toISOString()}),setTimeout((function(){k.island.setForceReload(!0)}),500));break;case"gateway_down":console.warn("THE GATEWAY IS DOWN");break;case"info":"application/media_control+xml"===i.type&&i.content.includes("<picture_fast_update")&&n.send({message:{request:"keyframe",user:!0,peer:!0}});break;default:M.current.debug&&M.current.debug("Event not handled:",l)}}}else a.store.getState().webrtc.registered?(n&&n.hangup(),a.store.dispatch.player.stopAudioPlayer()):M.current.log&&M.current.log("User is not registered")},onlocaltrack:function(e,t){M.current.debug&&M.current.debug("Local track "+(t?"added":"removed")+":",e);var n=e.id.replace(/[{}]/g,"");if(!t){var r=H[n];if(r)try{var o=r.getTracks();for(var i in o){var c=o[i];c&&c.stop()}}catch(e){M.current.error&&M.current.error("Error removing track:",e)}return e.kind,void delete H[n]}var l=H[n];if(!l)if("audio"===e.kind)l=new MediaStream([e]),a.store.dispatch.webrtc.updateLocalAudioStream(l);else{l=new MediaStream([e]),a.store.dispatch.webrtc.updateLocalVideoStream(l),H[n]=l,M.current.debug&&M.current.debug("Created local stream:",l);var s=a.store.getState().player.localVideo;M.current.attachMediaStream&&s&&s.current&&M.current.attachMediaStream(s.current,l)}},onremotetrack:function(t,n,r){var o=this;if(M.current.debug&&M.current.debug("Remote track (mid="+n+") "+(r?"added":"removed")+":",t),a.store.dispatch.player.stopAudioPlayer(),!r)return t.kind,void k.currentCall.updateCurrentCall({showRemoteVideoPlaceHolder:!0});if("audio"===t.kind){var i=new MediaStream([t]);M.current.debug&&M.current.debug("Created remote audio stream: "+i);var c=a.store.getState().player.remoteAudio;if(c&&c.current&&M.current.attachMediaStream){M.current.attachMediaStream(c.current,i);var l=g.getJSONItem("phone-island-audio-output-device");if(null==l?void 0:l.deviceId){e.__awaiter(o,void 0,void 0,(function(){var t,n,r,o,a;return e.__generator(this,(function(e){switch(e.label){case 0:if(!(t=l.deviceId)||"default"===t)return[3,4];e.label=1;case 1:return e.trys.push([1,3,,4]),[4,navigator.mediaDevices.enumerateDevices()];case 2:return n=e.sent(),n.filter((function(e){return"audiooutput"===e.kind})).some((function(e){return e.deviceId===t}))||(console.warn("Saved audio device ".concat(t," no longer available, using default device")),t="default",g.setJSONItem("phone-island-audio-output-device",{deviceId:"default"})),[3,4];case 3:return r=e.sent(),console.warn("Error checking device availability, using default:",r),t="default",[3,4];case 4:if(!c.current)return console.warn("Remote audio element no longer available"),[2];e.label=5;case 5:return e.trys.push([5,7,,12]),[4,c.current.setSinkId(t)];case 6:return e.sent(),console.info("Audio output device applied successfully to new stream:",t),[3,12];case 7:if(o=e.sent(),console.warn("Failed to apply audio output device to new stream:",o),"default"===t||!c.current)return[3,11];e.label=8;case 8:return e.trys.push([8,10,,11]),[4,c.current.setSinkId("default")];case 9:return e.sent(),g.setJSONItem("phone-island-audio-output-device",{deviceId:"default"}),console.info("Fallback to default device successful"),[3,11];case 10:return a=e.sent(),console.error("Even default device failed:",a),[3,11];case 11:return[3,12];case 12:return[2]}}))}))}}a.store.dispatch.webrtc.updateRemoteAudioStream(i)}else{i=new MediaStream([t]);a.store.dispatch.webrtc.updateRemoteVideoStream(i),M.current.debug&&M.current.debug("Created remote video stream:"+i);var s=a.store.getState().player.largeRemoteVideo,u=a.store.getState().player.smallRemoteVideo;M.current.attachMediaStream&&s&&s.current&&M.current.attachMediaStream(s.current,i),M.current.attachMediaStream&&u&&u.current&&(M.current.attachMediaStream(u.current,i),k.currentCall.updateCurrentCall({showRemoteVideoPlaceHolder:!1}))}},oncleanup:function(){M.current.log&&M.current.log(" ::: janus Got a cleanup notification :::")}})},error:function(e){var t,n;M.current.log&&M.current.log("error",e),console.warn("[JANUS-GUARD] Network error detected, marking connection as stale",{error:e,timestamp:(new Date).toISOString()}),N.current=!0,L.current=!1,P.current&&(clearTimeout(P.current),P.current=null);var r=a.store.getState().webrtc.sipcall,o=a.store.getState().currentCall,i=o.accepted,c=o.outgoing,l=null===(n=null===(t=null==r?void 0:r.webrtcStuff)||void 0===t?void 0:t.pc)||void 0===n?void 0:n.iceConnectionState;i||c||"connected"===l||"completed"===l?(console.info("[JANUS-GUARD] Network error during active call - giving ICE grace period to recover",{iceState:l,accepted:i,outgoing:c,timestamp:(new Date).toISOString()}),W.current&&clearTimeout(W.current),W.current=setTimeout((function(){var e,t,n=a.store.getState().webrtc.sipcall,r=null===(t=null===(e=null==n?void 0:n.webrtcStuff)||void 0===e?void 0:e.pc)||void 0===t?void 0:t.iceConnectionState,o=a.store.getState().currentCall,i=o.accepted,c=o.outgoing,l=i||c;l&&("disconnected"===r||"failed"===r||!r)?(console.warn("[JANUS-GUARD] ICE failed to recover after grace period - resetting call state and activating alert",{checkIceState:r||"undefined/null",timestamp:(new Date).toISOString()}),a.store.dispatch.currentCall.reset(),k.alerts.setAlert("webrtc_down")):l?console.info("[JANUS-GUARD] ICE recovered during grace period - call preserved, Janus HTTP still stale",{checkIceState:r,connectionStale:N.current,timestamp:(new Date).toISOString()}):(console.info("[JANUS-GUARD] Call ended during grace period - activating alert for reconnection",{timestamp:(new Date).toISOString()}),k.alerts.setAlert("webrtc_down")),W.current=null}),15e3)):k.alerts.setAlert("webrtc_down")},destroyed:function(){console.log("[JANUS-GUARD] Session destroyed, clearing janusInstance",{timestamp:(new Date).toISOString()}),L.current=!1,P.current&&(clearTimeout(P.current),P.current=null),k.webrtc.updateWebRTC({destroyed:!0,janusInstance:null}),E.current?console.log("[JANUS-GUARD] Skipping alert activation (voluntary reload in progress)"):(console.log("[JANUS-GUARD] Activating webrtc_down alert (not a voluntary reload)"),k.alerts.setAlert("webrtc_down"))}});console.log("[JANUS-GUARD] Saving janusInstance to Redux",{sessionId:null===(t=n.getSessionId)||void 0===t?void 0:t.call(n),timestamp:(new Date).toISOString()}),k.webrtc.updateWebRTC({janusInstance:n})}})}}),[M.current]);t.useEffect((function(){void 0!==a.store.getState().currentUser.default_device&&i.checkMediaPermissions()}),[null===(w=null===(S=null===a.store||void 0===a.store?void 0:a.store.getState())||void 0===S?void 0:S.currentUser)||void 0===w?void 0:w.default_device]);var V=t.useState(navigator.onLine),x=V[0],z=V[1],F=t.useState(!1),B=F[0],Y=F[1],K=t.useRef(!1);return t.useEffect((function(){var e=function(){return z(!0)},t=function(){return z(!1)};return window.addEventListener("online",e),window.addEventListener("offline",t),function(){window.removeEventListener("online",e),window.removeEventListener("offline",t)}}),[]),t.useEffect((function(){x?K.current&&(console.log("Internet connection restored."),Y(!0),K.current=!1):(console.log("Internet connection lost."),K.current=!0,Y(!1))}),[x]),t.useEffect((function(){var e;return q(),e=a.store.getState().webrtc.CHECK_INTERVAL_TIME,U.current||(U.current=setInterval((function(){return l.webrtcCheck((function(){o.register({sipExten:h,sipSecret:D,sipHost:I,sipPort:A})}))}),e)),function(){o.unregister(),clearInterval(U.current),P.current&&(clearTimeout(P.current),P.current=null),W.current&&(clearTimeout(W.current),W.current=null)}}),[]),t.useEffect((function(){var e,t,n,r,i;if(R||B){var c=a.store.getState().alerts.data,l=a.store.getState().island.forceReload,s=a.store.getState().webrtc,u=s.sipcall,d=s.janusInstance,g=a.store.getState().currentCall,v=g.accepted,p=g.outgoing,m=null===(t=null===(e=null==u?void 0:u.webrtcStuff)||void 0===e?void 0:e.pc)||void 0===t?void 0:t.iceConnectionState,f=v||p||"connected"===m||"completed"===m,S=(null===(n=c.webrtc_down)||void 0===n?void 0:n.active)||!1;if(B&&f&&!l){var w=null===(r=null==d?void 0:d.isConnected)||void 0===r?void 0:r.call(d);return!w&&(null==d?void 0:d.reconnect)?(console.info("[JANUS-GUARD] Connection returned with active call but Janus HTTP is dead - attempting Janus reconnect",{iceState:m,sessionId:null===(i=null==d?void 0:d.getSessionId)||void 0===i?void 0:i.call(d),timestamp:(new Date).toISOString()}),d.reconnect({success:function(){var e;console.info("[JANUS-GUARD] Janus HTTP reconnected successfully during active call (connectionReturned)",{sessionId:null===(e=null==d?void 0:d.getSessionId)||void 0===e?void 0:e.call(d),timestamp:(new Date).toISOString()}),N.current=!1,W.current&&(clearTimeout(W.current),W.current=null)},error:function(e){console.error("[JANUS-GUARD] Janus reconnect failed during active call (connectionReturned)",{error:e,timestamp:(new Date).toISOString()}),N.current=!0}})):console.info("[JANUS-GUARD] Connection returned but active call in progress - Janus still connected, no action needed",{iceState:m,janusConnected:w,timestamp:(new Date).toISOString()}),void Y(!1)}if(S||l||B){if(E.current||L.current)return void console.log("[JANUS-GUARD] Reload or init already in progress, skipping",{isReloading:E.current,isInitializing:L.current});E.current=!0,P.current&&(clearTimeout(P.current),P.current=null),console.info(l?"Force reload requested, performing full WebRTC reconnection":B?"Internet connection restored, performing full WebRTC reconnection":"WebRTC down detected (alert active), performing full reload"),l&&a.store.dispatch.island.setForceReload(!1),console.log("[JANUS-GUARD] Manual reload, clearing janusInstance and registered state",{timestamp:(new Date).toISOString()}),k.webrtc.updateWebRTC({janusInstance:null,sipcall:null,registered:!1,isDetached:!1,jsepGlobal:null}),J.current=null,o.unregister(),u&&u.detach(),d&&d.destroy&&d.destroy({unload:!0,notifyDestroyed:!1,cleanupHandles:!0}),setTimeout((function(){q(),B&&Y(!1),y&&y(),setTimeout((function(){E.current=!1,N.current=!1,_.current=!1,O.current=0}),1e3)}),100)}else console.info("WebRTC already connected (no alert active), skipping heavy reload"),y&&y()}}),[R,B]),t.useEffect((function(){var e,t=function(){var e;navigator&&(null===navigator||void 0===navigator?void 0:navigator.mediaDevices)&&(null===(e=null===navigator||void 0===navigator?void 0:navigator.mediaDevices)||void 0===e?void 0:e.enumerateDevices)?null===navigator||void 0===navigator||navigator.mediaDevices.enumerateDevices().then((function(e){k.mediaDevices.updateMediaDevices(e)})).catch((function(e){console.error("Error fetching devices:",e)})):(console.warn("MediaDevices API not supported in this browser or context"),k.mediaDevices.updateMediaDevices([]))};if(t(),navigator&&(null===navigator||void 0===navigator?void 0:navigator.mediaDevices))return null===(e=null===navigator||void 0===navigator?void 0:navigator.mediaDevices)||void 0===e||e.addEventListener("devicechange",t),function(){var e;null===(e=null===navigator||void 0===navigator?void 0:navigator.mediaDevices)||void 0===e||e.removeEventListener("devicechange",t)}}),[]),t.useEffect((function(){var e=function(){console.warn("[STANDBY-GUARD] Page frozen (standby or browser froze tab)",{timestamp:(new Date).toISOString()}),_.current=!0},t=function(){console.log("[STANDBY-GUARD] Page resumed from freeze",{timestamp:(new Date).toISOString()})};return document.addEventListener("freeze",e),document.addEventListener("resume",t),function(){document.removeEventListener("freeze",e),document.removeEventListener("resume",t)}}),[]),t.useEffect((function(){var e=function(){var e,t,n,r,i=Date.now();if(document.hidden)G.current=!0,j.current=i,console.log("[STANDBY-GUARD] Tab going to background",{timestamp:(new Date).toISOString()});else if(G.current){var c=i-j.current;O.current=c,console.log("[STANDBY-GUARD] Tab returning to foreground",{timeHiddenMs:c,timeHiddenMinutes:Math.round(c/6e4),wasFrozen:_.current,timestamp:(new Date).toISOString()});var l=a.store.getState().webrtc,s=l.registered,u=l.jsepGlobal,d=l.sipcall,g=a.store.getState().currentCall.outgoing,v=!!u,p="connected"===(null===(t=null===(e=null==d?void 0:d.webrtcStuff)||void 0===e?void 0:e.pc)||void 0===t?void 0:t.iceConnectionState)||"completed"===(null===(r=null===(n=null==d?void 0:d.webrtcStuff)||void 0===n?void 0:n.pc)||void 0===r?void 0:r.iceConnectionState),m=v||p||g,f=c>18e4&&!m,S=c>18e5&&!m,w=_.current||N.current||f||S;if(w){var b=_.current?"frozen":N.current?"stale connection":S?"throttled >30min (session too old)":"throttled >3min";if(m){var h=p?"active call":g?"outgoing call":"incoming call";console.warn("[STANDBY-GUARD] Reload needed (".concat(b,") but ").concat(h," in progress. ")+"Skipping reload to preserve call.",{wasFrozen:_.current,connectionStale:N.current,wasThrottledShort:f,wasThrottledVeryLong:S,hasIncomingCall:v,hasOutgoingCall:g,hasActiveCall:p,timestamp:(new Date).toISOString()})}else console.warn("[STANDBY-GUARD] Reload needed (".concat(b,"), forcing reload"),{wasFrozen:_.current,connectionStale:N.current,wasThrottledShort:f,wasThrottledVeryLong:S,timestamp:(new Date).toISOString()})}else if(m){h=p?"active call":g?"outgoing call":"incoming call";console.log("[STANDBY-GUARD] Tab change without issues, ".concat(h," preserved"),{timeHiddenMinutes:Math.round(c/6e4),hasIncomingCall:v,hasOutgoingCall:g,hasActiveCall:p,timestamp:(new Date).toISOString()})}var D=s&&!E.current&&!L.current&&w&&!v&&!g;if(D){console.warn("[STANDBY-GUARD] ⚠️ Reloading WebRTC",{timestamp:(new Date).toISOString()}),E.current=!0,P.current&&(clearTimeout(P.current),P.current=null);var C=a.store.getState().webrtc,I=C.janusInstance,A=C.sipcall;o.unregister(),A&&A.detach(),I&&I.destroy&&I.destroy({unload:!0,notifyDestroyed:!1,cleanupHandles:!0}),k.webrtc.updateWebRTC({janusInstance:null,sipcall:null,registered:!1,isDetached:!1,jsepGlobal:null}),J.current=null,setTimeout((function(){q(),setTimeout((function(){E.current=!1,N.current=!1,_.current=!1,O.current=0}),1e3)}),100)}G.current=!1,D||(_.current=!1,O.current=0)}};return document.addEventListener("visibilitychange",e),function(){document.removeEventListener("visibilitychange",e)}}),[q,k]),u.useEventListener("phone-island-attach",(function(e){console.log("[EVENT] phone-island-attach received, calling initWebRTC",{timestamp:(new Date).toISOString()}),q(),d.eventDispatch("phone-island-attached",{})})),u.useEventListener("phone-island-socket-reconnected",(function(){var e,t,n,r,o=a.store.getState().webrtc,i=o.sipcall,c=o.janusInstance,l=a.store.getState().currentCall,s=l.accepted,u=l.outgoing,d=null===(t=null===(e=null==i?void 0:i.webrtcStuff)||void 0===e?void 0:e.pc)||void 0===t?void 0:t.iceConnectionState,g=s||u||"connected"===d||"completed"===d,v=null===(n=null==c?void 0:c.isConnected)||void 0===n?void 0:n.call(c);return console.log("[EVENT] phone-island-socket-reconnected received",{hasActiveCall:g,iceState:d,accepted:s,outgoing:u,janusConnected:v,timestamp:(new Date).toISOString()}),g&&!v&&(null==c?void 0:c.reconnect)?(console.info("[EVENT] Socket reconnected with active call but Janus HTTP is dead - attempting Janus reconnect",{iceState:d,sessionId:null===(r=null==c?void 0:c.getSessionId)||void 0===r?void 0:r.call(c),timestamp:(new Date).toISOString()}),void c.reconnect({success:function(){var e;console.info("[JANUS-GUARD] Janus HTTP reconnected successfully during active call",{sessionId:null===(e=null==c?void 0:c.getSessionId)||void 0===e?void 0:e.call(c),timestamp:(new Date).toISOString()}),N.current=!1,W.current&&(clearTimeout(W.current),W.current=null)},error:function(e){console.error("[JANUS-GUARD] Janus reconnect failed during active call",{error:e,timestamp:(new Date).toISOString()}),N.current=!0}})):g&&v?(console.info("[EVENT] Socket reconnected with active call, Janus HTTP still connected - no action needed",{iceState:d,timestamp:(new Date).toISOString()}),void(W.current&&(clearTimeout(W.current),W.current=null))):(a.store.getState().webrtc.jsepGlobal&&(console.log("[EVENT] Clearing stale jsepGlobal after socket reconnect",{timestamp:(new Date).toISOString()}),k.webrtc.updateWebRTC({jsepGlobal:null}),J.current=null),void Y(!0))})),u.useEventListener("phone-island-call-transfer",(function(t){var n=null==t?void 0:t.to;k.island.toggleIsOpen(!0),function(t){e.__awaiter(this,void 0,void 0,(function(){return e.__generator(this,(function(e){switch(e.label){case 0:return[4,c.attendedTransfer(t)];case 1:return e.sent()&&(k.currentCall.updateCurrentCall({transferring:!0,paused:!1}),k.player.playRemoteAudio()),[2]}}))}))}(n),d.eventDispatch("phone-island-call-transfer-opened",{})})),f.default.createElement(f.default.Fragment,null,C)};
|
|
2
2
|
//# sourceMappingURL=WebRTC.js.map
|