@nethesis/phone-island 0.18.13 → 1.0.0-dev.10

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.
Files changed (34) hide show
  1. package/dist/App.js +1 -1
  2. package/dist/App.js.map +1 -1
  3. package/dist/_virtual/index6.js +1 -1
  4. package/dist/_virtual/index8.js +1 -1
  5. package/dist/components/AudioPlayerView/index.js +1 -1
  6. package/dist/components/AudioPlayerView/index.js.map +1 -1
  7. package/dist/components/CallView/Number.js +1 -1
  8. package/dist/components/CallView/Number.js.map +1 -1
  9. package/dist/components/CallView/Timer.js +1 -1
  10. package/dist/components/CallView/Timer.js.map +1 -1
  11. package/dist/components/IslandDrag.js +1 -1
  12. package/dist/components/IslandDrag.js.map +1 -1
  13. package/dist/components/RecorderView/index.js +1 -1
  14. package/dist/components/RecorderView/index.js.map +1 -1
  15. package/dist/components/SideView/hooks/useSideViewLogic.js +1 -1
  16. package/dist/components/SideView/hooks/useSideViewLogic.js.map +1 -1
  17. package/dist/components/Socket.js +1 -1
  18. package/dist/components/Socket.js.map +1 -1
  19. package/dist/components/TranscriptionView/TranscriptionView.js +1 -1
  20. package/dist/components/TranscriptionView/TranscriptionView.js.map +1 -1
  21. package/dist/components/WebRTC.js.map +1 -1
  22. package/dist/lib/devices/devices.js +1 -1
  23. package/dist/node_modules/@fortawesome/react-fontawesome/index.es.js +1 -1
  24. package/dist/node_modules/mic-check/lib/index.js +1 -1
  25. package/dist/node_modules/prop-types/index.js +1 -1
  26. package/dist/node_modules/prop-types/node_modules/react-is/index.js +1 -1
  27. package/dist/package.json.js +1 -1
  28. package/dist/services/user.js +1 -1
  29. package/dist/services/user.js.map +1 -1
  30. package/dist/styles/CustomRange.styles.js +1 -1
  31. package/dist/styles/CustomRange.styles.js.map +1 -1
  32. package/dist/styles/Island.styles.js +1 -1
  33. package/dist/styles/Island.styles.js.map +1 -1
  34. package/package.json +6 -5
@@ -1 +1 @@
1
- {"version":3,"file":"WebRTC.js","sources":["../../src/components/WebRTC.tsx"],"sourcesContent":["// Copyright (C) 2024 Nethesis S.r.l.\n// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport React, { type ReactNode, FC, useEffect, useRef, useCallback, useState } from 'react'\nimport { useDispatch } from 'react-redux'\nimport { Dispatch } from '../store'\nimport adapter from 'webrtc-adapter'\nimport JanusLib from '../lib/webrtc/janus.js'\nimport type { JanusTypes } from '../types'\nimport { register, unregister, handleRemote } from '../lib/webrtc/messages'\nimport { store } from '../store'\nimport { checkMediaPermissions } from '../lib/devices/devices'\nimport { attendedTransfer, hangupCurrentCall } from '../lib/phone/call'\nimport { webrtcCheck } from '../lib/webrtc/connection'\nimport outgoingRingtone from '../static/outgoing_ringtone'\nimport { eventDispatch, useEventListener, getJSONItem, setJSONItem } from '../utils'\nimport { isPhysical } from '../lib/user/default_device'\n\ninterface WebRTCProps {\n children: ReactNode\n sipExten: string\n sipSecret: string\n hostName: string\n sipHost: string\n sipPort: string\n reload: boolean\n uaType: string\n reloadedCallback?: () => void\n}\n\nexport const WebRTC: FC<WebRTCProps> = ({\n hostName,\n sipExten,\n sipSecret,\n children,\n sipHost,\n sipPort,\n reload,\n uaType,\n reloadedCallback,\n}) => {\n // Initialize store dispatch\n const dispatch = useDispatch<Dispatch>()\n\n // Initialize janus check interval id\n const janusCheckInterval = useRef<any>(null)\n\n // Flag to prevent concurrent reloads\n const isReloading = useRef<boolean>(false)\n\n // Track when tab goes to background to detect long standby periods\n const lastVisibilityChange = useRef<number>(Date.now())\n const wasHidden = useRef<boolean>(false)\n const lastInactivityDuration = useRef<number>(0) // Duration of last inactivity period in ms\n\n // Track if connection became stale due to network errors (e.g., during standby)\n const connectionStale = useRef<boolean>(false)\n\n // Track when jsepGlobal was saved (for incoming calls)\n const jsepGlobalTimestamp = useRef<number | null>(null)\n\n // Track if page was frozen (standby, browser froze tab)\n const wasFrozen = useRef<boolean>(false)\n\n // Flag to prevent concurrent initWebRTC calls\n const isInitializing = useRef<boolean>(false)\n\n // Timeout ID for initialization safety timeout\n const initTimeoutRef = useRef<NodeJS.Timeout | null>(null)\n\n // Timeout ID for network error grace period (during active calls)\n const networkErrorGraceRef = useRef<NodeJS.Timeout | null>(null)\n\n // Initialize Janus from Janus library\n const janus = useRef<any>(JanusLib)\n\n let localTracks = {}\n let localVideos = 0\n let remoteTracks = {}\n let remoteVideos = 0\n\n // Initializes the webrtc connection and handlers\n const initWebRTC = useCallback(() => {\n // Prevent concurrent initWebRTC calls\n if (isInitializing.current) {\n console.log('[JANUS-GUARD] initWebRTC already in progress, skipping', {\n timestamp: new Date().toISOString()\n })\n return\n }\n isInitializing.current = true\n\n // Safety timeout: reset isInitializing if initialization doesn't complete in 30 seconds\n // This prevents getting stuck in an unrecoverable state after network timeouts\n if (initTimeoutRef.current) {\n clearTimeout(initTimeoutRef.current)\n }\n initTimeoutRef.current = setTimeout(() => {\n if (isInitializing.current) {\n console.warn('[JANUS-GUARD] Initialization timeout (30s) - resetting isInitializing flag', {\n timestamp: new Date().toISOString()\n })\n isInitializing.current = false\n connectionStale.current = true\n dispatch.alerts.setAlert('webrtc_down')\n eventDispatch('phone-island-alert-set', { type: 'webrtc_down' })\n }\n initTimeoutRef.current = null\n }, 30000)\n\n // Prevent multiple Janus session creation\n const { janusInstance: existingInstance, registered } = store.getState().webrtc\n\n // Check if existing session is valid and connected\n let shouldInit = true\n\n if (existingInstance) {\n // Verify the session is still connected\n const sessionId = existingInstance.getSessionId?.()\n const isConnected = existingInstance.isConnected?.()\n\n // Check how long the tab was in background (from last visibility change)\n const inactivityMs = lastInactivityDuration.current\n const inactivityMinutes = Math.round(inactivityMs / 60000)\n const longInactivity = inactivityMs > 30 * 60 * 1000 // 30 minutes\n\n // Check if there's an active call that should be preserved\n const { sipcall: currentSipcall, jsepGlobal: currentJsep }: { sipcall: any; jsepGlobal: any } = store.getState().webrtc\n const hasIncomingCall = !!currentJsep\n const hasActiveCall = currentSipcall?.webrtcStuff?.pc?.iceConnectionState === 'connected' ||\n currentSipcall?.webrtcStuff?.pc?.iceConnectionState === 'completed'\n const hasAnyCall = hasIncomingCall || hasActiveCall\n\n // If session exists AND is connected AND registered, skip init\n // UNLESS there has been long inactivity (>30 min) WITHOUT an active call\n // (we preserve sessions with active calls even after long inactivity)\n if (isConnected && registered && (!longInactivity || hasAnyCall)) {\n console.log('[JANUS-GUARD] Valid session already exists, skipping init', {\n sessionId,\n isConnected,\n registered,\n inactivityMinutes,\n hasIncomingCall,\n hasActiveCall,\n timestamp: new Date().toISOString()\n })\n shouldInit = false\n // NOTE: Do NOT reset lastInactivityDuration here! It will be reset on next visibility change\n // or after a successful reload. Resetting here causes race conditions with phone-island-attach.\n } else {\n // Session exists but is dead/disconnected/stale, clean it up and recreate\n const reason = !isConnected\n ? 'not connected'\n : !registered\n ? 'not registered'\n : longInactivity\n ? `long inactivity (${inactivityMinutes} min) without active call`\n : 'unknown'\n\n console.warn('[JANUS-GUARD] Session exists but is invalid, cleaning up and reinitializing', {\n reason,\n sessionId,\n isConnected,\n registered,\n inactivityMinutes,\n hasIncomingCall,\n hasActiveCall,\n timestamp: new Date().toISOString()\n })\n\n // Cleanup dead/stale session\n try {\n existingInstance.destroy({ unload: true, notifyDestroyed: false, cleanupHandles: true })\n } catch (e) {\n console.error('[JANUS-GUARD] Error destroying session', e)\n }\n\n // Clear state to allow new session\n store.dispatch.webrtc.updateWebRTC({\n janusInstance: null,\n sipcall: null,\n registered: false,\n jsepGlobal: null,\n })\n // Will reset inactivity after successful reload\n }\n }\n\n if (!shouldInit) {\n isInitializing.current = false\n if (initTimeoutRef.current) {\n clearTimeout(initTimeoutRef.current)\n initTimeoutRef.current = null\n }\n return\n }\n\n console.log('[JANUS-GUARD] Creating new session', {\n timestamp: new Date().toISOString()\n })\n\n janus.current.init({\n debug: 'all',\n dependencies: janus.current.useDefaultDependencies({\n adapter,\n }),\n callback: function () {\n const janusInstance = new janus.current({\n server: `https://${hostName}/janus`,\n success: () => {\n if (janusInstance.attach) {\n // Use Janus Sip Plugin\n janusInstance.attach({\n plugin: 'janus.plugin.sip',\n opaqueId: 'sebastian' + '_' + new Date().getTime(),\n success: function (pluginHandle) {\n // Set sipcall to the store\n if (pluginHandle) {\n dispatch.webrtc.updateWebRTC({\n sipcall: pluginHandle,\n })\n // Register the extension to the server\n register({ sipExten, sipSecret, sipHost, sipPort })\n if (pluginHandle) {\n if (janus.current.log)\n janus.current.log(\n 'SIP plugin attached! (' + pluginHandle.getPlugin() + ', id = ' + ')',\n )\n }\n }\n },\n error: function (error) {\n if (janus.current.error) {\n janus.current.error(' -- Error attaching plugin...')\n janus.current.error(error)\n }\n // Reset init flag on plugin attach error\n isInitializing.current = false\n if (initTimeoutRef.current) {\n clearTimeout(initTimeoutRef.current)\n initTimeoutRef.current = null\n }\n // reject()\n },\n consentDialog: function (on) {\n if (janus.current.log) janus.current.log(`janus consentDialog (on: ${on})`)\n },\n webrtcState: function (on) {\n if (janus.current.log)\n janus.current.log(\n 'Janus says our WebRTC PeerConnection is ' + (on ? 'up' : 'down') + ' now',\n )\n\n // Fix: Ensure view is set to 'call' when WebRTC is up but view is null\n // This can happen after standby when socket events are missed\n if (on) {\n const { view } = store.getState().island\n const { accepted, outgoing, incoming } = store.getState().currentCall\n const hasActiveCall = accepted || outgoing || incoming\n\n if (hasActiveCall && !view) {\n console.warn('[WEBRTC] WebRTC up but view is null with active call - forcing view to \"call\"')\n store.dispatch.island.setIslandView('call')\n }\n }\n },\n iceState: function (newState) {\n const { sipcall }: { sipcall: any } = store.getState().webrtc\n\n if (sipcall) {\n if (janus.current.log)\n janus.current.log(\n `ICE state of PeerConnection of handle has changed to \"${newState}\"`,\n )\n }\n\n // Fix: Ensure view is set to 'call' when ICE is connected but view is null\n // This can happen after standby when socket events are missed\n if (newState === 'connected' || newState === 'completed') {\n const { view } = store.getState().island\n const { accepted, outgoing, incoming } = store.getState().currentCall\n const hasActiveCall = accepted || outgoing || incoming\n\n if (hasActiveCall && !view) {\n console.warn('[WEBRTC] ICE connected but view is null with active call - forcing view to \"call\"')\n store.dispatch.island.setIslandView('call')\n }\n }\n },\n mediaState: function (medium, on) {\n if (janus.current.log)\n janus.current.log(\n 'Janus ' + (on ? 'started' : 'stopped') + ' receiving our ' + medium,\n )\n },\n slowLink: function (uplink, count) {\n if (uplink) {\n if (janus.current.warn)\n janus.current.warn(`SLOW link: several missing packets from janus (${count})`)\n } else {\n if (janus.current.warn)\n janus.current.warn(\n `SLOW link: janus is not receiving all your packets (${count})`,\n )\n }\n },\n onmessage: function (msg, jsep) {\n // Get webrtc state\n const { sipcall }: { sipcall: any } = store.getState().webrtc\n\n if (janus.current.debug) {\n janus.current.debug(' ::: Got a message :::')\n janus.current.debug(JSON.stringify(msg))\n }\n\n // Handle errors in message\n var error = msg['error']\n if (error != null && error != undefined) {\n if (!store.getState().webrtc.registered) {\n if (janus.current.log) janus.current.log('User is not registered')\n } else {\n // Reset status\n sipcall && sipcall.hangup()\n\n // Stop the local audio element ringing\n store.dispatch.player.stopAudioPlayer()\n }\n return\n }\n // Manage events\n var result = msg['result']\n if (\n result !== null &&\n result !== undefined &&\n result['event'] !== undefined &&\n result['event'] !== null\n ) {\n // Get event data\n var event = result['event']\n\n // Get the recording state\n const { recording } = store.getState().recorder\n const { view } = store.getState().island\n\n // Manage different types of events\n switch (event) {\n case 'registration_failed':\n if (janus.current.error)\n janus.current.error(\n 'Registration failed: ' + result['code'] + ' ' + result['reason'],\n )\n break\n\n case 'unregistered':\n if (janus.current.log)\n janus.current.log(\n 'Successfully un-registered as ' + result['username'] + '!',\n )\n // Update registered status to false\n store.dispatch.webrtc.updateWebRTC({\n registered: false,\n })\n eventDispatch('phone-island-webrtc-unregistered', {})\n break\n\n case 'registered':\n if (janus.current.log)\n janus.current.log(\n 'Successfully registered as ' + result['username'] + '!',\n )\n console.log('[REGISTER] Registration successful', {\n username: result['username'],\n wasAlreadyRegistered: store.getState().webrtc.registered,\n timestamp: new Date().toISOString()\n })\n eventDispatch('phone-island-webrtc-registered', {})\n if (!store.getState().webrtc.registered) {\n store.dispatch.webrtc.updateWebRTC({\n registered: true,\n })\n }\n // Remove WebRTC connections alert if any\n dispatch.alerts.removeAlert('webrtc_down')\n eventDispatch('phone-island-alert-removed', {\n type: 'webrtc_down',\n })\n // Connection is healthy again, reset stale flag\n connectionStale.current = false\n // Init completed successfully\n isInitializing.current = false\n if (initTimeoutRef.current) {\n clearTimeout(initTimeoutRef.current)\n initTimeoutRef.current = null\n }\n // Clear any network error grace timeout - connection is back\n if (networkErrorGraceRef.current) {\n clearTimeout(networkErrorGraceRef.current)\n networkErrorGraceRef.current = null\n console.info('[JANUS-GUARD] Cleared network error grace timeout - connection restored')\n }\n // Update webrtc lastActivity time\n dispatch.webrtc.updateLastActivity(new Date().getTime())\n break\n\n case 'registering':\n if (janus.current.log) {\n janus.current.log('janus registering')\n }\n break\n\n // This event arrive on outgoing call start\n case 'calling':\n // Number and display name are updated inside socket\n dispatch.currentCall.checkOutgoingUpdate({\n outgoingWebRTC: true,\n })\n\n // Update webrtc last activity time\n dispatch.webrtc.updateLastActivity(new Date().getTime())\n break\n\n // After an outgoing call start on 180 code, it means\n // ...that the local outgoing ringtone must be player\n case 'ringing':\n const { audioPlayerPlaying } = store.getState().player\n\n // Check if the local audio is already playing and start playing\n if (!audioPlayerPlaying) {\n // Update audio player and start playing\n dispatch.player.updateStartAudioPlayer({\n src: outgoingRingtone,\n loop: true,\n })\n }\n // Update webrtc lastActivity time\n dispatch.webrtc.updateLastActivity(new Date().getTime())\n if (view !== 'call') {\n dispatch.island.setIslandView('call')\n }\n break\n\n // After an outgoing call start on 183 code, it means\n // ...that the outgoing ringtone arrives from the stream\n // ...playing the local outgoing ringtone isn't needed\n case 'progress':\n if (janus.current.log) {\n janus.current.log(\n \"There's early media from \" +\n result['username'] +\n ', wairing for the call!',\n )\n }\n // Set the remote description to janus lib\n if (jsep) {\n handleRemote(jsep)\n }\n // Update webrtc lastActivity time\n dispatch.webrtc.updateLastActivity(new Date().getTime())\n break\n\n case 'incomingcall':\n const { default_device } = store.getState().currentUser\n const { endpoints, username } = store.getState().currentUser\n const { extensions } = store.getState().users\n\n const hasOnlineNethlink = () => {\n if (!extensions || !username) return false\n\n // Get all extensions for current user\n const userExtensions: any = Object.values(extensions).filter(\n (ext) => ext?.username === username,\n )\n\n // Check if any extension is nethlink type and online\n return userExtensions?.some((ext) => {\n const endpointExtension = endpoints?.extension.find(\n (endpoint) => endpoint.id === ext?.exten,\n )\n return (\n endpointExtension?.type === 'nethlink' && ext?.status !== 'offline'\n )\n })\n }\n\n // ALWAYS save jsepGlobal when we receive an incoming call JSEP\n // This allows answering from any device, even if it's not the default\n if (jsep) {\n console.log('[JSEP] Saving jsepGlobal for incoming call', {\n from: result['username'],\n timestamp: new Date().toISOString()\n })\n dispatch.webrtc.updateWebRTC({ jsepGlobal: jsep })\n // Track when this call arrived\n jsepGlobalTimestamp.current = Date.now()\n }\n\n if (\n (uaType === 'mobile' && hasOnlineNethlink()) ||\n (uaType === 'desktop' &&\n (default_device?.type === 'webrtc' ||\n (default_device?.type === undefined && !hasOnlineNethlink()) ||\n (!hasOnlineNethlink() && default_device?.type === 'physical')))\n ) {\n // Check if is recording an audio through call\n // ...recording an audio is a request made by the user\n // ...it must be managed differently than an incoming call\n if (recording) {\n // Update the recorder state\n dispatch.recorder.setIncoming(true)\n } else {\n // Manage the incoming message as a webrtc call\n // Update incoming webrtc state, number and display name\n // ...are updated inside socket\n dispatch.currentCall.checkIncomingUpdatePlay({\n incoming: true,\n incomingWebRTC: true,\n })\n\n if (janus.current.log) {\n dispatch.currentCall.updateIncoming(true)\n janus.current.log('Incoming call from ' + result['username'] + '!')\n }\n }\n\n // Update the webrtc last activity time\n dispatch.webrtc.updateLastActivity(new Date().getTime())\n }\n\n break\n\n case 'accepted':\n const acceptedTimestamp = Math.floor(Date.now() / 1000)\n if (janus.current.log) {\n const caller = result['username'] || result['displayname'] || store.getState().currentCall.number || 'Remote party'\n janus.current.log(caller + ' accepted the call!')\n }\n // Set the remote description to janus lib\n if (jsep) {\n handleRemote(jsep)\n }\n // Clear jsepGlobal after call is accepted\n console.log('[JSEP] Clearing jsepGlobal after call accepted', {\n timestamp: new Date().toISOString()\n })\n dispatch.webrtc.updateWebRTC({ jsepGlobal: null })\n jsepGlobalTimestamp.current = null\n\n // Set current call accepted\n dispatch.currentCall.checkAcceptedUpdate({\n acceptedWebRTC: true,\n })\n\n // Set incoming value to false and set start time\n dispatch.currentCall.updateCurrentCall({\n incoming: false,\n incomingWebRTC: false,\n startTime: acceptedTimestamp?.toString(),\n })\n\n // Stop the local audio element ringing\n store.dispatch.player.stopAudioPlayer()\n\n // Update webrtc lastActivity time\n dispatch.webrtc.updateLastActivity(new Date().getTime())\n break\n\n case 'hangup':\n // Clear jsepGlobal when call ends\n console.log('[JSEP] Clearing jsepGlobal on hangup', {\n timestamp: new Date().toISOString()\n })\n dispatch.webrtc.updateWebRTC({ jsepGlobal: null })\n jsepGlobalTimestamp.current = null\n\n // Manage hangup message during recording\n if (recording) {\n dispatch.recorder.setRecording(false)\n }\n if (!isPhysical() && uaType !== 'mobile') {\n hangupCurrentCall()\n sipcall.hangup()\n\n // Stop the local audio element ringing\n store.dispatch.player.stopAudioPlayer()\n\n // Check the janus doc before enable the following\n // if (\n // result['code'] === 486 &&\n // result['event'] === 'hangup' &&\n // result['reason'] === 'Busy Here'\n // ) {\n // dispatch.player.updateAudioSource({\n // src: busyRingtone,\n // })\n // dispatch.player.playAudio()\n // }\n // Reset current call info\n store.dispatch.currentCall.reset()\n if (janus.current.log)\n janus.current.log(\n 'Call hung up (' + result['code'] + ' ' + result['reason'] + ')!',\n )\n // Update webrtc lastActivity time\n dispatch.webrtc.updateLastActivity(new Date().getTime())\n // stopScreenSharingI()\n }\n\n // Stop the local audio element ringing\n store.dispatch.player.stopAudioPlayer()\n\n // Stop screen sharing if active\n const {\n active: screenShareActive,\n plugin,\n localScreenStream,\n remoteScreenStream,\n } = store.getState().screenShare\n\n if (screenShareActive) {\n janus.current.stopAllTracks(localScreenStream)\n janus.current.stopAllTracks(remoteScreenStream)\n dispatch.screenShare.update({ active: false })\n plugin.detach()\n }\n\n // After call cleanup, if connection was stale, force reload to reset Janus session\n if (connectionStale.current) {\n console.info('[JANUS-GUARD] Call ended with stale connection - forcing reload to reset Janus session', {\n timestamp: new Date().toISOString()\n })\n // Small delay to let cleanup complete before reload\n setTimeout(() => {\n dispatch.island.setForceReload(true)\n }, 500)\n }\n break\n\n case 'gateway_down':\n console.warn('THE GATEWAY IS DOWN')\n\n break\n\n case 'info':\n // Check if it's a keyframe request (see: https://github.com/meetecho/janus-gateway/pull/3517)\n if (\n result['type'] === 'application/media_control+xml' &&\n result['content'].includes('<picture_fast_update')\n ) {\n sipcall.send({\n message: { request: 'keyframe', user: true, peer: true },\n })\n }\n break\n\n default:\n if (janus.current.debug) {\n janus.current.debug('Event not handled:', event)\n }\n break\n }\n }\n },\n onlocaltrack: function (track, on) {\n if (janus.current.debug) {\n janus.current.debug('Local track ' + (on ? 'added' : 'removed') + ':', track)\n }\n\n // We use the track ID as name of the element, but it may contain invalid characters\n let trackId = track.id.replace(/[{}]/g, '')\n if (!on) {\n // Track removed, get rid of the stream and the rendering\n let stream = localTracks[trackId]\n if (stream) {\n try {\n let tracks = stream.getTracks()\n for (let i in tracks) {\n let mst = tracks[i]\n if (mst) mst.stop()\n }\n } catch (e: any) {\n if (janus.current.error) {\n janus.current.error('Error removing track:', e)\n }\n }\n }\n if (track.kind === 'video') {\n localVideos--\n }\n delete localTracks[trackId]\n return\n }\n // If we're here, a new track was added\n let stream = localTracks[trackId]\n if (stream) {\n // We've been here already\n return\n }\n if (track.kind === 'audio') {\n // We ignore local audio tracks, they'd generate echo anyway\n\n stream = new MediaStream([track])\n\n // Save the new audio stream to the store\n store.dispatch.webrtc.updateLocalAudioStream(stream)\n } else {\n // New video track: create a stream out of it\n localVideos++\n stream = new MediaStream([track])\n\n // Save the new video stream to the store\n store.dispatch.webrtc.updateLocalVideoStream(stream)\n\n localTracks[trackId] = stream\n if (janus.current.debug) {\n janus.current.debug('Created local stream:', stream)\n }\n const localVideoElement = store.getState().player.localVideo\n\n if (\n janus.current.attachMediaStream &&\n localVideoElement &&\n localVideoElement.current\n ) {\n janus.current.attachMediaStream(localVideoElement.current, stream)\n }\n }\n },\n onremotetrack: function (track, mid, on) {\n if (janus.current.debug) {\n janus.current.debug(\n 'Remote track (mid=' + mid + ') ' + (on ? 'added' : 'removed') + ':',\n track,\n )\n }\n\n // Stop the local audio element ringing\n store.dispatch.player.stopAudioPlayer()\n\n if (!on) {\n // Track removed, get rid of the stream and the rendering\n if (track.kind === 'video') {\n remoteVideos--\n }\n delete remoteTracks[mid]\n\n // Show remote video placeholder\n dispatch.currentCall.updateCurrentCall({\n showRemoteVideoPlaceHolder: true,\n })\n\n return\n }\n\n if (track.kind === 'audio') {\n // New audio track: create a stream out of it, and use a hidden <audio> element\n let stream = new MediaStream([track])\n remoteTracks[mid] = stream\n if (janus.current.debug) {\n janus.current.debug('Created remote audio stream: ' + stream)\n }\n const remoteAudioElement = store.getState().player.remoteAudio\n\n if (\n remoteAudioElement &&\n remoteAudioElement.current &&\n janus.current.attachMediaStream\n ) {\n janus.current.attachMediaStream(remoteAudioElement.current, stream)\n\n // Apply saved audio output device if available\n const defaultAudioOutputDevice: any = getJSONItem('phone-island-audio-output-device')\n\n if (defaultAudioOutputDevice?.deviceId) {\n const applySavedDevice = async () => {\n let targetDeviceId = defaultAudioOutputDevice.deviceId\n\n // Check if the saved device is still available\n if (targetDeviceId && targetDeviceId !== 'default') {\n try {\n const devices = await navigator.mediaDevices.enumerateDevices()\n const audioOutputDevices = devices.filter(device => device.kind === 'audiooutput')\n const deviceExists = audioOutputDevices.some(device => device.deviceId === targetDeviceId)\n\n if (!deviceExists) {\n console.warn(`Saved audio device ${targetDeviceId} no longer available, using default device`)\n targetDeviceId = 'default'\n // Update localStorage with the fallback\n setJSONItem('phone-island-audio-output-device', { deviceId: 'default' })\n }\n } catch (err) {\n console.warn('Error checking device availability, using default:', err)\n targetDeviceId = 'default'\n }\n }\n\n // Apply the device\n if (!remoteAudioElement.current) {\n console.warn('Remote audio element no longer available')\n return\n }\n\n try {\n await remoteAudioElement.current.setSinkId(targetDeviceId)\n console.info('Audio output device applied successfully to new stream:', targetDeviceId)\n } catch (err) {\n console.warn('Failed to apply audio output device to new stream:', err)\n // Final fallback to default if not already using it\n if (targetDeviceId !== 'default' && remoteAudioElement.current) {\n try {\n await remoteAudioElement.current.setSinkId('default')\n setJSONItem('phone-island-audio-output-device', { deviceId: 'default' })\n console.info('Fallback to default device successful')\n } catch (defaultErr) {\n console.error('Even default device failed:', defaultErr)\n }\n }\n }\n }\n\n applySavedDevice()\n }\n }\n // Save the new audio stream to the store\n store.dispatch.webrtc.updateRemoteAudioStream(stream)\n } else {\n // New video track: create a stream out of it\n remoteVideos++\n let stream = new MediaStream([track])\n\n // Save the new video stream to the store\n store.dispatch.webrtc.updateRemoteVideoStream(stream)\n\n remoteTracks[mid] = stream\n if (janus.current.debug) {\n janus.current.debug('Created remote video stream:' + stream)\n }\n const largeRemoteVideoElement = store.getState().player.largeRemoteVideo\n const smallRemoteVideoElement = store.getState().player.smallRemoteVideo\n\n if (\n janus.current.attachMediaStream &&\n largeRemoteVideoElement &&\n largeRemoteVideoElement.current\n ) {\n janus.current.attachMediaStream(largeRemoteVideoElement.current, stream)\n }\n\n if (\n janus.current.attachMediaStream &&\n smallRemoteVideoElement &&\n smallRemoteVideoElement.current\n ) {\n janus.current.attachMediaStream(smallRemoteVideoElement.current, stream)\n\n // Hide remote video placeholder\n dispatch.currentCall.updateCurrentCall({\n showRemoteVideoPlaceHolder: false,\n })\n }\n }\n },\n oncleanup: function () {\n if (janus.current.log) {\n janus.current.log(' ::: janus Got a cleanup notification :::')\n }\n },\n })\n }\n },\n error: (err: any) => {\n if (janus.current.log) janus.current.log('error', err)\n // Mark connection as stale due to network error\n console.warn('[JANUS-GUARD] Network error detected, marking connection as stale', {\n error: err,\n timestamp: new Date().toISOString()\n })\n connectionStale.current = true\n // Reset init flag on error so retry is possible\n isInitializing.current = false\n if (initTimeoutRef.current) {\n clearTimeout(initTimeoutRef.current)\n initTimeoutRef.current = null\n }\n\n // Check if there's an active call - if so, give ICE time to recover\n // WebRTC/ICE is resilient to brief network interruptions (WiFi roaming, etc.)\n const { sipcall: currentSipcall }: { sipcall: any } = store.getState().webrtc\n const { accepted, outgoing } = store.getState().currentCall\n const iceState = currentSipcall?.webrtcStuff?.pc?.iceConnectionState\n const hasActiveCall = accepted || outgoing || iceState === 'connected' || iceState === 'completed'\n\n if (hasActiveCall) {\n console.info('[JANUS-GUARD] Network error during active call - giving ICE grace period to recover', {\n iceState,\n accepted,\n outgoing,\n timestamp: new Date().toISOString()\n })\n // Don't set alert immediately - give 15 seconds for ICE to recover\n // Clear any existing grace timeout\n if (networkErrorGraceRef.current) {\n clearTimeout(networkErrorGraceRef.current)\n }\n networkErrorGraceRef.current = setTimeout(() => {\n // Check again if call is still active and ICE is disconnected\n const { sipcall: checkSipcall }: { sipcall: any } = store.getState().webrtc\n const checkIceState = checkSipcall?.webrtcStuff?.pc?.iceConnectionState\n const { accepted: stillAccepted, outgoing: stillOutgoing } = store.getState().currentCall\n const stillHasCall = stillAccepted || stillOutgoing\n\n const iceIsDown = checkIceState === 'disconnected' || checkIceState === 'failed' || !checkIceState\n if (stillHasCall && iceIsDown) {\n console.warn('[JANUS-GUARD] ICE failed to recover after grace period - resetting call state and activating alert', {\n checkIceState: checkIceState || 'undefined/null',\n timestamp: new Date().toISOString()\n })\n // Reset call state since call is effectively dead (ICE disconnected/failed/destroyed)\n // This allows App.tsx to proceed with reload instead of skipping it\n store.dispatch.currentCall.reset()\n dispatch.alerts.setAlert('webrtc_down')\n } else if (!stillHasCall) {\n console.info('[JANUS-GUARD] Call ended during grace period - activating alert for reconnection', {\n timestamp: new Date().toISOString()\n })\n dispatch.alerts.setAlert('webrtc_down')\n } else {\n console.info('[JANUS-GUARD] ICE recovered during grace period - call preserved, Janus HTTP still stale', {\n checkIceState,\n connectionStale: connectionStale.current,\n timestamp: new Date().toISOString()\n })\n // Keep connectionStale.current = true so hangup handler will force reload\n // ICE recovered but Janus HTTP session is still dead\n }\n networkErrorGraceRef.current = null\n }, 15000) // 15 second grace period\n } else {\n // No active call - activate alert immediately\n dispatch.alerts.setAlert('webrtc_down')\n }\n },\n destroyed: () => {\n console.log('[JANUS-GUARD] Session destroyed, clearing janusInstance', {\n timestamp: new Date().toISOString()\n })\n // Reset init flag when session is destroyed\n isInitializing.current = false\n if (initTimeoutRef.current) {\n clearTimeout(initTimeoutRef.current)\n initTimeoutRef.current = null\n }\n // Set webrtc destroyed status and clear janusInstance\n dispatch.webrtc.updateWebRTC({\n destroyed: true,\n janusInstance: null,\n })\n // Only activate alert if we're NOT already doing a voluntary reload\n // Otherwise we create a reload loop\n if (!isReloading.current) {\n console.log('[JANUS-GUARD] Activating webrtc_down alert (not a voluntary reload)')\n dispatch.alerts.setAlert('webrtc_down')\n } else {\n console.log('[JANUS-GUARD] Skipping alert activation (voluntary reload in progress)')\n }\n },\n })\n // Set janus instance to the store\n console.log('[JANUS-GUARD] Saving janusInstance to Redux', {\n sessionId: janusInstance.getSessionId?.(),\n timestamp: new Date().toISOString()\n })\n dispatch.webrtc.updateWebRTC({\n janusInstance,\n })\n },\n })\n }, [janus.current])\n\n // Check audio and video permissions when default_device is loaded or changed\n useEffect(() => {\n const { default_device } = store.getState().currentUser\n if (default_device !== undefined) {\n checkMediaPermissions()\n }\n }, [store?.getState()?.currentUser?.default_device])\n\n const [isOnline, setIsOnline] = useState(navigator.onLine)\n const [connectionReturned, setConnectionReturned] = useState(false)\n const wasOfflineRef = useRef(false)\n\n useEffect(() => {\n // Event listeners for online/offline status\n const handleOnline = () => setIsOnline(true)\n const handleOffline = () => setIsOnline(false)\n\n window.addEventListener('online', handleOnline)\n window.addEventListener('offline', handleOffline)\n\n return () => {\n window.removeEventListener('online', handleOnline)\n window.removeEventListener('offline', handleOffline)\n }\n }, [])\n\n // Reconnection management\n useEffect(() => {\n if (!isOnline) {\n console.log('Internet connection lost.')\n wasOfflineRef.current = true\n setConnectionReturned(false)\n } else if (wasOfflineRef.current) {\n console.log('Internet connection restored.')\n setConnectionReturned(true)\n wasOfflineRef.current = false\n }\n }, [isOnline])\n\n // Manage webrtc connections and events\n useEffect(() => {\n // Initializes the webrtc registration check interval\n function startWebrtcCheck() {\n const { CHECK_INTERVAL_TIME } = store.getState().webrtc\n if (!janusCheckInterval.current) {\n // Initialize the interval that check the webrtc\n janusCheckInterval.current = setInterval(\n () =>\n webrtcCheck(() => {\n // Do the register as callback of webrtc check\n register({ sipExten, sipSecret, sipHost, sipPort })\n }),\n CHECK_INTERVAL_TIME,\n )\n }\n }\n\n // Start webrtc initialization and handlers\n initWebRTC()\n // Start the check of webrtc activity\n startWebrtcCheck()\n\n return () => {\n // Unregister from janus\n unregister()\n // Stop Janus check interval\n clearInterval(janusCheckInterval.current)\n // Clear initialization timeout\n if (initTimeoutRef.current) {\n clearTimeout(initTimeoutRef.current)\n initTimeoutRef.current = null\n }\n // Clear network error grace timeout\n if (networkErrorGraceRef.current) {\n clearTimeout(networkErrorGraceRef.current)\n networkErrorGraceRef.current = null\n }\n }\n }, [])\n\n // Manage reload events\n useEffect(() => {\n if (reload || connectionReturned) {\n // Check if WebRTC is actually disconnected using alerts (more reliable than registered/sipcall)\n const { data } = store.getState().alerts\n const { forceReload } = store.getState().island\n const { sipcall, janusInstance }: { sipcall: any; janusInstance: any } = store.getState().webrtc\n const { accepted, outgoing } = store.getState().currentCall\n\n // Check if there's an active call - if so, don't reload automatically\n // WebRTC/ICE is designed to recover from brief network interruptions\n const iceState = sipcall?.webrtcStuff?.pc?.iceConnectionState\n const hasActiveCall = accepted || outgoing || iceState === 'connected' || iceState === 'completed'\n\n // Only do full reload if webrtc_down alert is active OR force reload is requested OR connection just returned\n const isWebRTCDown = data.webrtc_down?.active || false\n\n // If connection returned but there's an active call, try to reconnect Janus HTTP without full reload\n // This preserves ICE (audio) while restoring Janus signaling\n if (connectionReturned && hasActiveCall && !forceReload) {\n const janusConnected = janusInstance?.isConnected?.()\n\n if (!janusConnected && janusInstance?.reconnect) {\n console.info('[JANUS-GUARD] Connection returned with active call but Janus HTTP is dead - attempting Janus reconnect', {\n iceState,\n sessionId: janusInstance?.getSessionId?.(),\n timestamp: new Date().toISOString()\n })\n\n janusInstance.reconnect({\n success: () => {\n console.info('[JANUS-GUARD] Janus HTTP reconnected successfully during active call (connectionReturned)', {\n sessionId: janusInstance?.getSessionId?.(),\n timestamp: new Date().toISOString()\n })\n connectionStale.current = false\n // Clear grace period timer since we successfully reconnected\n if (networkErrorGraceRef.current) {\n clearTimeout(networkErrorGraceRef.current)\n networkErrorGraceRef.current = null\n }\n },\n error: (error) => {\n console.error('[JANUS-GUARD] Janus reconnect failed during active call (connectionReturned)', {\n error,\n timestamp: new Date().toISOString()\n })\n connectionStale.current = true\n }\n })\n } else {\n console.info('[JANUS-GUARD] Connection returned but active call in progress - Janus still connected, no action needed', {\n iceState,\n janusConnected,\n timestamp: new Date().toISOString()\n })\n }\n\n setConnectionReturned(false)\n return\n }\n\n if (isWebRTCDown || forceReload || connectionReturned) {\n // Prevent concurrent reloads or interrupting an in-progress init\n if (isReloading.current || isInitializing.current) {\n console.log('[JANUS-GUARD] Reload or init already in progress, skipping', {\n isReloading: isReloading.current,\n isInitializing: isInitializing.current\n })\n return\n }\n\n isReloading.current = true\n\n // Clear any pending init timeout before starting reload\n if (initTimeoutRef.current) {\n clearTimeout(initTimeoutRef.current)\n initTimeoutRef.current = null\n }\n\n console.info(\n forceReload\n ? 'Force reload requested, performing full WebRTC reconnection'\n : connectionReturned\n ? 'Internet connection restored, performing full WebRTC reconnection'\n : 'WebRTC down detected (alert active), performing full reload'\n )\n // Reset force reload flag\n if (forceReload) {\n store.dispatch.island.setForceReload(false)\n }\n\n // Clear janusInstance and registered state from Redux to allow new session creation\n console.log('[JANUS-GUARD] Manual reload, clearing janusInstance and registered state', {\n timestamp: new Date().toISOString()\n })\n dispatch.webrtc.updateWebRTC({\n janusInstance: null,\n sipcall: null,\n registered: false,\n jsepGlobal: null, // Also clear stale jsepGlobal\n })\n jsepGlobalTimestamp.current = null // Clear timestamp to match jsepGlobal\n // Unregister the WebRTC extension\n unregister()\n // Detach sipcall\n if (sipcall) sipcall.detach()\n // Destroy Janus session (use janusInstance from store, NOT janus.current which is the library!)\n if (janusInstance && janusInstance.destroy) {\n janusInstance.destroy({\n unload: true,\n notifyDestroyed: false,\n cleanupHandles: true,\n })\n }\n // Initialize a new Janus session immediately\n setTimeout(() => {\n initWebRTC()\n // Reset connection returned flag\n if (connectionReturned) {\n setConnectionReturned(false)\n }\n // Execute the reloaded callback\n if (reloadedCallback) reloadedCallback()\n // Reset reload flag and other state flags after completion\n setTimeout(() => {\n isReloading.current = false\n connectionStale.current = false\n wasFrozen.current = false\n lastInactivityDuration.current = 0\n }, 1000)\n }, 100)\n } else {\n console.info('WebRTC already connected (no alert active), skipping heavy reload')\n // Execute callback without reload\n if (reloadedCallback) reloadedCallback()\n }\n }\n }, [reload, connectionReturned])\n\n // Manage media devices (audio/video)\n useEffect(() => {\n const getMediaDevices = () => {\n if (navigator && navigator?.mediaDevices && navigator?.mediaDevices?.enumerateDevices) {\n navigator?.mediaDevices\n .enumerateDevices()\n .then((deviceInfos) => {\n dispatch.mediaDevices.updateMediaDevices(deviceInfos)\n })\n .catch((error) => {\n console.error('Error fetching devices:', error)\n })\n } else {\n console.warn('MediaDevices API not supported in this browser or context')\n dispatch.mediaDevices.updateMediaDevices([])\n }\n }\n getMediaDevices()\n\n if (navigator && navigator?.mediaDevices) {\n navigator?.mediaDevices?.addEventListener('devicechange', getMediaDevices)\n\n return () => {\n navigator?.mediaDevices?.removeEventListener('devicechange', getMediaDevices)\n }\n }\n }, [])\n\n // Detect page freeze/resume events (standby, browser freezing tab)\n useEffect(() => {\n const handleFreeze = () => {\n console.warn('[STANDBY-GUARD] Page frozen (standby or browser froze tab)', {\n timestamp: new Date().toISOString()\n })\n wasFrozen.current = true\n }\n\n const handleResume = () => {\n console.log('[STANDBY-GUARD] Page resumed from freeze', {\n timestamp: new Date().toISOString()\n })\n // wasFrozen flag will be checked on visibilitychange\n }\n\n document.addEventListener('freeze', handleFreeze)\n document.addEventListener('resume', handleResume)\n\n return () => {\n document.removeEventListener('freeze', handleFreeze)\n document.removeEventListener('resume', handleResume)\n }\n }, [])\n\n // Detect long standby periods and preemptively refresh WebRTC connection\n useEffect(() => {\n const handleVisibilityChange = () => {\n const now = Date.now()\n\n if (document.hidden) {\n // Tab going to background - track this\n wasHidden.current = true\n lastVisibilityChange.current = now\n console.log('[STANDBY-GUARD] Tab going to background', {\n timestamp: new Date().toISOString()\n })\n } else if (wasHidden.current) {\n // Tab returning to foreground after being hidden\n const timeHidden = now - lastVisibilityChange.current\n const threeMinutes = 3 * 60 * 1000 // 3 minutes - fallback threshold for throttling\n\n // Save this duration so initWebRTC can check it later\n lastInactivityDuration.current = timeHidden\n\n console.log('[STANDBY-GUARD] Tab returning to foreground', {\n timeHiddenMs: timeHidden,\n timeHiddenMinutes: Math.round(timeHidden / 60000),\n wasFrozen: wasFrozen.current,\n timestamp: new Date().toISOString()\n })\n\n const { registered, jsepGlobal, sipcall }: { registered: boolean; jsepGlobal: any; sipcall: any } = store.getState().webrtc\n const { outgoing: hasOutgoingCall } = store.getState().currentCall\n\n // Check if there's an active call (either incoming, outgoing, or in progress)\n const hasIncomingCall = !!jsepGlobal\n const hasActiveCall = sipcall?.webrtcStuff?.pc?.iceConnectionState === 'connected' ||\n sipcall?.webrtcStuff?.pc?.iceConnectionState === 'completed'\n const hasAnyCall = hasIncomingCall || hasActiveCall || hasOutgoingCall\n\n // Check if we need to reload:\n // 1. Page was frozen (freeze event) - standby or browser froze tab, OR\n // 2. Connection is stale (network errors detected), OR\n // 3. Tab was throttled for >3 minutes without any call (preventive reload), OR\n // 4. Tab was throttled for >30 minutes ONLY if there's no call (incoming or active)\n const thirtyMinutes = 30 * 60 * 1000\n const wasThrottledShort = timeHidden > threeMinutes && !hasAnyCall // 3+ min without call\n // Only reload after 30+ min if no call at all (preserve both incoming and active calls!)\n const wasThrottledVeryLong = timeHidden > thirtyMinutes && !hasAnyCall\n const needsReload = wasFrozen.current || connectionStale.current || wasThrottledShort || wasThrottledVeryLong\n\n if (needsReload) {\n const reloadReason = wasFrozen.current\n ? 'frozen'\n : connectionStale.current\n ? 'stale connection'\n : wasThrottledVeryLong\n ? 'throttled >30min (session too old)'\n : 'throttled >3min'\n\n if (hasAnyCall) {\n const callType = hasActiveCall ? 'active call' : hasOutgoingCall ? 'outgoing call' : 'incoming call'\n console.warn(\n `[STANDBY-GUARD] Reload needed (${reloadReason}) but ${callType} in progress. ` +\n 'Skipping reload to preserve call.',\n {\n wasFrozen: wasFrozen.current,\n connectionStale: connectionStale.current,\n wasThrottledShort,\n wasThrottledVeryLong,\n hasIncomingCall,\n hasOutgoingCall,\n hasActiveCall,\n timestamp: new Date().toISOString()\n }\n )\n } else {\n console.warn(\n `[STANDBY-GUARD] Reload needed (${reloadReason}), forcing reload`,\n {\n wasFrozen: wasFrozen.current,\n connectionStale: connectionStale.current,\n wasThrottledShort,\n wasThrottledVeryLong,\n timestamp: new Date().toISOString()\n }\n )\n }\n } else if (hasAnyCall) {\n // No freeze/throttling detected and there's a call - preserve it\n const callType = hasActiveCall ? 'active call' : hasOutgoingCall ? 'outgoing call' : 'incoming call'\n console.log(\n `[STANDBY-GUARD] Tab change without issues, ${callType} preserved`,\n {\n timeHiddenMinutes: Math.round(timeHidden / 60000),\n hasIncomingCall,\n hasOutgoingCall,\n hasActiveCall,\n timestamp: new Date().toISOString()\n }\n )\n }\n\n // Reload if page was frozen, connection is stale, or throttled too long\n // BUT NOT if there's an incoming or outgoing call (preserve call state)\n // Also don't reload if another init is already in progress\n const shouldReload = registered && !isReloading.current && !isInitializing.current && needsReload && !hasIncomingCall && !hasOutgoingCall\n\n if (shouldReload) {\n console.warn(\n '[STANDBY-GUARD] ⚠️ Reloading WebRTC',\n {\n timestamp: new Date().toISOString()\n }\n )\n\n // Trigger preventive reload\n isReloading.current = true\n\n // Clear any pending init timeout before starting reload\n if (initTimeoutRef.current) {\n clearTimeout(initTimeoutRef.current)\n initTimeoutRef.current = null\n }\n\n // Clear existing session\n const { janusInstance, sipcall: sipcallToDestroy }: { janusInstance: any; sipcall: any } = store.getState().webrtc\n // Unregister the WebRTC extension\n unregister()\n // Detach sipcall handle\n if (sipcallToDestroy) sipcallToDestroy.detach()\n // Destroy Janus session\n if (janusInstance && janusInstance.destroy) {\n janusInstance.destroy({\n unload: true,\n notifyDestroyed: false,\n cleanupHandles: true,\n })\n }\n\n // Clear state\n dispatch.webrtc.updateWebRTC({\n janusInstance: null,\n sipcall: null,\n registered: false,\n jsepGlobal: null,\n })\n jsepGlobalTimestamp.current = null\n\n // Reinitialize after a short delay\n setTimeout(() => {\n initWebRTC()\n setTimeout(() => {\n isReloading.current = false\n // Reset flags after successful reload\n connectionStale.current = false\n wasFrozen.current = false\n lastInactivityDuration.current = 0 // Reset inactivity duration after reload\n }, 1000)\n }, 100)\n }\n\n // Reset flags\n wasHidden.current = false\n // Reset frozen flag even if we didn't reload (for next cycle)\n if (!shouldReload) {\n wasFrozen.current = false\n // Reset inactivity duration since we've handled the visibility change\n lastInactivityDuration.current = 0\n }\n }\n }\n\n document.addEventListener('visibilitychange', handleVisibilityChange)\n\n return () => {\n document.removeEventListener('visibilitychange', handleVisibilityChange)\n }\n }, [initWebRTC, dispatch])\n\n useEventListener('phone-island-attach', (data) => {\n console.log('[EVENT] phone-island-attach received, calling initWebRTC', {\n timestamp: new Date().toISOString()\n })\n initWebRTC()\n eventDispatch('phone-island-attached', {})\n })\n\n // Force WebRTC reload when socket reconnects after network change\n // This prevents stale Janus sessions that cause 469 \"Unexpected ANSWER\" errors\n // BUT if there's an active call, try to reconnect Janus HTTP without killing ICE\n useEventListener('phone-island-socket-reconnected', () => {\n const { sipcall, janusInstance }: { sipcall: any; janusInstance: any } = store.getState().webrtc\n const { accepted, outgoing } = store.getState().currentCall\n const iceState = sipcall?.webrtcStuff?.pc?.iceConnectionState\n const hasActiveCall = accepted || outgoing || iceState === 'connected' || iceState === 'completed'\n const janusConnected = janusInstance?.isConnected?.()\n\n console.log('[EVENT] phone-island-socket-reconnected received', {\n hasActiveCall,\n iceState,\n accepted,\n outgoing,\n janusConnected,\n timestamp: new Date().toISOString()\n })\n\n // If there's an active call and Janus HTTP is disconnected, try to reconnect without full reload\n if (hasActiveCall && !janusConnected && janusInstance?.reconnect) {\n console.info('[EVENT] Socket reconnected with active call but Janus HTTP is dead - attempting Janus reconnect', {\n iceState,\n sessionId: janusInstance?.getSessionId?.(),\n timestamp: new Date().toISOString()\n })\n\n janusInstance.reconnect({\n success: () => {\n console.info('[JANUS-GUARD] Janus HTTP reconnected successfully during active call', {\n sessionId: janusInstance?.getSessionId?.(),\n timestamp: new Date().toISOString()\n })\n // Reset stale flag since we reconnected\n connectionStale.current = false\n // Clear grace period timer since we successfully reconnected\n if (networkErrorGraceRef.current) {\n clearTimeout(networkErrorGraceRef.current)\n networkErrorGraceRef.current = null\n }\n },\n error: (error) => {\n console.error('[JANUS-GUARD] Janus reconnect failed during active call', {\n error,\n timestamp: new Date().toISOString()\n })\n // Mark connection as stale so we reload after call ends\n connectionStale.current = true\n }\n })\n return\n }\n\n // If there's an active call and Janus is connected, clear grace period and do nothing\n if (hasActiveCall && janusConnected) {\n console.info('[EVENT] Socket reconnected with active call, Janus HTTP still connected - no action needed', {\n iceState,\n timestamp: new Date().toISOString()\n })\n // Clear grace period timer since Janus is already connected\n if (networkErrorGraceRef.current) {\n clearTimeout(networkErrorGraceRef.current)\n networkErrorGraceRef.current = null\n }\n return\n }\n\n // No active call - proceed with reload to ensure clean state\n // Clear any stale jsepGlobal - it's invalid after network reconnect\n const { jsepGlobal } = store.getState().webrtc\n if (jsepGlobal) {\n console.log('[EVENT] Clearing stale jsepGlobal after socket reconnect', {\n timestamp: new Date().toISOString()\n })\n dispatch.webrtc.updateWebRTC({ jsepGlobal: null })\n jsepGlobalTimestamp.current = null // Clear timestamp to match jsepGlobal\n }\n // Trigger reload via connectionReturned (forceReload not needed - connectionReturned already triggers reload)\n setConnectionReturned(true)\n })\n\n /**\n * Event listner for phone-island-call-transfer event\n */\n useEventListener('phone-island-call-transfer', (data) => {\n const transferNumber = data?.to\n dispatch.island.toggleIsOpen(true)\n handleAttendedTransfer(transferNumber)\n eventDispatch('phone-island-call-transfer-opened', {})\n })\n\n async function handleAttendedTransfer(number: string) {\n // Send attended transfer message\n const transferringMessageSent = await attendedTransfer(number)\n if (transferringMessageSent) {\n // Set transferring and disable pause\n dispatch.currentCall.updateCurrentCall({\n transferring: true,\n paused: false,\n })\n // Play the remote audio element\n dispatch.player.playRemoteAudio()\n }\n }\n\n return <>{children}</>\n}\n"],"names":["_a","hostName","sipExten","sipSecret","children","sipHost","sipPort","reload","uaType","reloadedCallback","dispatch","useDispatch","janusCheckInterval","useRef","isReloading","lastVisibilityChange","Date","now","wasHidden","lastInactivityDuration","connectionStale","jsepGlobalTimestamp","wasFrozen","isInitializing","initTimeoutRef","networkErrorGraceRef","janus","JanusLib","localTracks","initWebRTC","useCallback","current","console","log","timestamp","toISOString","clearTimeout","setTimeout","warn","alerts","setAlert","eventDispatch","type","_g","store","getState","webrtc","existingInstance","janusInstance","registered","shouldInit","sessionId","getSessionId","isConnected","_b","inactivityMs","inactivityMinutes","Math","round","longInactivity","_h","currentSipcall","sipcall","hasIncomingCall","hasActiveCall","_d","_c","webrtcStuff","pc","iceConnectionState","_f","_e","reason","concat","destroy","unload","notifyDestroyed","cleanupHandles","e","error","updateWebRTC","jsepGlobal","init","debug","dependencies","useDefaultDependencies","adapter","callback","server","success","attach","plugin","opaqueId","getTime","pluginHandle","register","getPlugin","consentDialog","on","webrtcState","view","island","currentCall","accepted","outgoing","incoming","setIslandView","iceState","newState","mediaState","medium","slowLink","uplink","count","onmessage","msg","jsep","JSON","stringify","undefined","result","event","recording","recorder","username","wasAlreadyRegistered","removeAlert","info","updateLastActivity","checkOutgoingUpdate","outgoingWebRTC","player","audioPlayerPlaying","updateStartAudioPlayer","src","outgoingRingtone","loop","handleRemote","default_device","currentUser","endpoints_1","endpoints","username_1","extensions_1","users","extensions","hasOnlineNethlink","userExtensions","Object","values","filter","ext","some","endpointExtension","extension","find","endpoint","id","exten","status","from","setIncoming","checkIncomingUpdatePlay","incomingWebRTC","updateIncoming","acceptedTimestamp","floor","caller","number","checkAcceptedUpdate","acceptedWebRTC","updateCurrentCall","startTime","toString","stopAudioPlayer","setRecording","isPhysical","hangupCurrentCall","hangup","reset","screenShare","screenShareActive","localScreenStream","remoteScreenStream","stopAllTracks","update","active","detach","setForceReload","includes","send","message","request","user","peer","onlocaltrack","track","trackId","replace","stream_1","tracks","getTracks","i","mst","stop","kind","stream","MediaStream","updateLocalAudioStream","updateLocalVideoStream","localVideoElement","localVideo","attachMediaStream","onremotetrack","mid","_this","this","showRemoteVideoPlaceHolder","remoteAudioElement_1","remoteAudio","defaultAudioOutputDevice_1","getJSONItem","deviceId","__awaiter","targetDeviceId","navigator","mediaDevices","enumerateDevices","devices","sent","device","setJSONItem","err_1","setSinkId","err_2","defaultErr_1","updateRemoteAudioStream","updateRemoteVideoStream","largeRemoteVideoElement","largeRemoteVideo","smallRemoteVideoElement","smallRemoteVideo","oncleanup","err","checkSipcall","checkIceState","stillAccepted","stillOutgoing","stillHasCall","destroyed","call","useEffect","checkMediaPermissions","useState","onLine","isOnline","setIsOnline","connectionReturned","setConnectionReturned","wasOfflineRef","handleOnline","handleOffline","window","addEventListener","removeEventListener","CHECK_INTERVAL_TIME","setInterval","webrtcCheck","unregister","clearInterval","data","forceReload","janusInstance_1","isWebRTCDown","webrtc_down","janusConnected","reconnect","getMediaDevices","then","deviceInfos","updateMediaDevices","catch","handleFreeze","handleResume","document","handleVisibilityChange","hidden","timeHidden","timeHiddenMs","timeHiddenMinutes","hasOutgoingCall","hasAnyCall","wasThrottledShort","wasThrottledVeryLong","needsReload","reloadReason","callType","shouldReload","sipcallToDestroy","useEventListener","transferNumber","to","toggleIsOpen","attendedTransfer","transferring","paused","playRemoteAudio","handleAttendedTransfer","React","createElement","Fragment"],"mappings":"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"}
1
+ {"version":3,"file":"WebRTC.js","sources":["../../src/components/WebRTC.tsx"],"sourcesContent":["// Copyright (C) 2024 Nethesis S.r.l.\n// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport React, { type ReactNode, FC, useEffect, useRef, useCallback, useState } from 'react'\nimport { useDispatch } from 'react-redux'\nimport { Dispatch } from '../store'\nimport adapter from 'webrtc-adapter'\nimport JanusLib from '../lib/webrtc/janus.js'\nimport type { JanusTypes } from '../types'\nimport { register, unregister, handleRemote } from '../lib/webrtc/messages'\nimport { store } from '../store'\nimport { checkMediaPermissions } from '../lib/devices/devices'\nimport { attendedTransfer, hangupCurrentCall } from '../lib/phone/call'\nimport { webrtcCheck } from '../lib/webrtc/connection'\nimport outgoingRingtone from '../static/outgoing_ringtone'\nimport { eventDispatch, useEventListener, getJSONItem, setJSONItem } from '../utils'\nimport { isPhysical } from '../lib/user/default_device'\n\ninterface WebRTCProps {\n children: ReactNode\n sipExten: string\n sipSecret: string\n hostName: string\n sipHost: string\n sipPort: string\n reload: boolean\n uaType: string\n reloadedCallback?: () => void\n}\n\nexport const WebRTC: FC<WebRTCProps> = ({\n hostName,\n sipExten,\n sipSecret,\n children,\n sipHost,\n sipPort,\n reload,\n uaType,\n reloadedCallback,\n}) => {\n // Initialize store dispatch\n const dispatch = useDispatch<Dispatch>()\n\n // Initialize janus check interval id\n const janusCheckInterval = useRef<any>(null)\n\n // Flag to prevent concurrent reloads\n const isReloading = useRef<boolean>(false)\n\n // Track when tab goes to background to detect long standby periods\n const lastVisibilityChange = useRef<number>(Date.now())\n const wasHidden = useRef<boolean>(false)\n const lastInactivityDuration = useRef<number>(0) // Duration of last inactivity period in ms\n\n // Track if connection became stale due to network errors (e.g., during standby)\n const connectionStale = useRef<boolean>(false)\n\n // Track when jsepGlobal was saved (for incoming calls)\n const jsepGlobalTimestamp = useRef<number | null>(null)\n\n // Track if page was frozen (standby, browser froze tab)\n const wasFrozen = useRef<boolean>(false)\n\n // Flag to prevent concurrent initWebRTC calls\n const isInitializing = useRef<boolean>(false)\n\n // Timeout ID for initialization safety timeout\n const initTimeoutRef = useRef<NodeJS.Timeout | null>(null)\n\n // Timeout ID for network error grace period (during active calls)\n const networkErrorGraceRef = useRef<NodeJS.Timeout | null>(null)\n\n // Initialize Janus from Janus library\n const janus = useRef<any>(JanusLib)\n\n let localTracks = {}\n let localVideos = 0\n let remoteTracks = {}\n let remoteVideos = 0\n\n // Initializes the webrtc connection and handlers\n const initWebRTC = useCallback(() => {\n // Prevent concurrent initWebRTC calls\n if (isInitializing.current) {\n console.log('[JANUS-GUARD] initWebRTC already in progress, skipping', {\n timestamp: new Date().toISOString()\n })\n return\n }\n isInitializing.current = true\n\n // Safety timeout: reset isInitializing if initialization doesn't complete in 30 seconds\n // This prevents getting stuck in an unrecoverable state after network timeouts\n if (initTimeoutRef.current) {\n clearTimeout(initTimeoutRef.current)\n }\n initTimeoutRef.current = setTimeout(() => {\n if (isInitializing.current) {\n console.warn('[JANUS-GUARD] Initialization timeout (30s) - resetting isInitializing flag', {\n timestamp: new Date().toISOString()\n })\n isInitializing.current = false\n connectionStale.current = true\n dispatch.alerts.setAlert('webrtc_down')\n eventDispatch('phone-island-alert-set', { type: 'webrtc_down' })\n }\n initTimeoutRef.current = null\n }, 30000)\n\n // Prevent multiple Janus session creation\n const { janusInstance: existingInstance, registered } = store.getState().webrtc\n\n // Check if existing session is valid and connected\n let shouldInit = true\n\n if (existingInstance) {\n // Verify the session is still connected\n const sessionId = existingInstance.getSessionId?.()\n const isConnected = existingInstance.isConnected?.()\n\n // Check how long the tab was in background (from last visibility change)\n const inactivityMs = lastInactivityDuration.current\n const inactivityMinutes = Math.round(inactivityMs / 60000)\n const longInactivity = inactivityMs > 30 * 60 * 1000 // 30 minutes\n\n // Check if there's an active call that should be preserved\n const { sipcall: currentSipcall, jsepGlobal: currentJsep }: { sipcall: any; jsepGlobal: any } = store.getState().webrtc\n const hasIncomingCall = !!currentJsep\n const hasActiveCall = currentSipcall?.webrtcStuff?.pc?.iceConnectionState === 'connected' ||\n currentSipcall?.webrtcStuff?.pc?.iceConnectionState === 'completed'\n const hasAnyCall = hasIncomingCall || hasActiveCall\n\n // If session exists AND is connected AND registered, skip init\n // UNLESS there has been long inactivity (>30 min) WITHOUT an active call\n // (we preserve sessions with active calls even after long inactivity)\n if (isConnected && registered && (!longInactivity || hasAnyCall)) {\n console.log('[JANUS-GUARD] Valid session already exists, skipping init', {\n sessionId,\n isConnected,\n registered,\n inactivityMinutes,\n hasIncomingCall,\n hasActiveCall,\n timestamp: new Date().toISOString()\n })\n shouldInit = false\n // NOTE: Do NOT reset lastInactivityDuration here! It will be reset on next visibility change\n // or after a successful reload. Resetting here causes race conditions with phone-island-attach.\n } else {\n // Session exists but is dead/disconnected/stale, clean it up and recreate\n const reason = !isConnected\n ? 'not connected'\n : !registered\n ? 'not registered'\n : longInactivity\n ? `long inactivity (${inactivityMinutes} min) without active call`\n : 'unknown'\n\n console.warn('[JANUS-GUARD] Session exists but is invalid, cleaning up and reinitializing', {\n reason,\n sessionId,\n isConnected,\n registered,\n inactivityMinutes,\n hasIncomingCall,\n hasActiveCall,\n timestamp: new Date().toISOString()\n })\n\n // Cleanup dead/stale session\n try {\n existingInstance.destroy({ unload: true, notifyDestroyed: false, cleanupHandles: true })\n } catch (e) {\n console.error('[JANUS-GUARD] Error destroying session', e)\n }\n\n // Clear state to allow new session\n store.dispatch.webrtc.updateWebRTC({\n janusInstance: null,\n sipcall: null,\n registered: false,\n jsepGlobal: null,\n })\n // Will reset inactivity after successful reload\n }\n }\n\n if (!shouldInit) {\n isInitializing.current = false\n if (initTimeoutRef.current) {\n clearTimeout(initTimeoutRef.current)\n initTimeoutRef.current = null\n }\n return\n }\n\n console.log('[JANUS-GUARD] Creating new session', {\n timestamp: new Date().toISOString()\n })\n\n janus.current.init({\n debug: 'all',\n dependencies: janus.current.useDefaultDependencies({\n adapter,\n }),\n callback: function () {\n const janusInstance = new janus.current({\n server: `https://${hostName}/janus`,\n success: () => {\n if (janusInstance.attach) {\n // Use Janus Sip Plugin\n janusInstance.attach({\n plugin: 'janus.plugin.sip',\n opaqueId: 'sebastian' + '_' + new Date().getTime(),\n success: function (pluginHandle) {\n // Set sipcall to the store\n if (pluginHandle) {\n dispatch.webrtc.updateWebRTC({\n sipcall: pluginHandle,\n })\n // Register the extension to the server\n register({ sipExten, sipSecret, sipHost, sipPort })\n if (pluginHandle) {\n if (janus.current.log)\n janus.current.log(\n 'SIP plugin attached! (' + pluginHandle.getPlugin() + ', id = ' + ')',\n )\n }\n }\n },\n error: function (error) {\n if (janus.current.error) {\n janus.current.error(' -- Error attaching plugin...')\n janus.current.error(error)\n }\n // Reset init flag on plugin attach error\n isInitializing.current = false\n if (initTimeoutRef.current) {\n clearTimeout(initTimeoutRef.current)\n initTimeoutRef.current = null\n }\n // reject()\n },\n consentDialog: function (on) {\n if (janus.current.log) janus.current.log(`janus consentDialog (on: ${on})`)\n },\n webrtcState: function (on) {\n if (janus.current.log)\n janus.current.log(\n 'Janus says our WebRTC PeerConnection is ' + (on ? 'up' : 'down') + ' now',\n )\n\n // Fix: Ensure view is set to 'call' when WebRTC is up but view is null\n // This can happen after standby when socket events are missed\n if (on) {\n const { view } = store.getState().island\n const { accepted, outgoing, incoming } = store.getState().currentCall\n const hasActiveCall = accepted || outgoing || incoming\n\n if (hasActiveCall && !view) {\n console.warn('[WEBRTC] WebRTC up but view is null with active call - forcing view to \"call\"')\n store.dispatch.island.setIslandView('call')\n }\n }\n },\n iceState: function (newState) {\n const { sipcall }: { sipcall: any } = store.getState().webrtc\n\n if (sipcall) {\n if (janus.current.log)\n janus.current.log(\n `ICE state of PeerConnection of handle has changed to \"${newState}\"`,\n )\n }\n\n // Fix: Ensure view is set to 'call' when ICE is connected but view is null\n // This can happen after standby when socket events are missed\n if (newState === 'connected' || newState === 'completed') {\n const { view } = store.getState().island\n const { accepted, outgoing, incoming } = store.getState().currentCall\n const hasActiveCall = accepted || outgoing || incoming\n\n if (hasActiveCall && !view) {\n console.warn('[WEBRTC] ICE connected but view is null with active call - forcing view to \"call\"')\n store.dispatch.island.setIslandView('call')\n }\n }\n },\n mediaState: function (medium, on) {\n if (janus.current.log)\n janus.current.log(\n 'Janus ' + (on ? 'started' : 'stopped') + ' receiving our ' + medium,\n )\n },\n slowLink: function (uplink, count) {\n if (uplink) {\n if (janus.current.warn)\n janus.current.warn(`SLOW link: several missing packets from janus (${count})`)\n } else {\n if (janus.current.warn)\n janus.current.warn(\n `SLOW link: janus is not receiving all your packets (${count})`,\n )\n }\n },\n onmessage: function (msg, jsep) {\n // Get webrtc state\n const { sipcall }: { sipcall: any } = store.getState().webrtc\n\n if (janus.current.debug) {\n janus.current.debug(' ::: Got a message :::')\n janus.current.debug(JSON.stringify(msg))\n }\n\n // Handle errors in message\n var error = msg['error']\n if (error != null && error != undefined) {\n if (!store.getState().webrtc.registered) {\n if (janus.current.log) janus.current.log('User is not registered')\n } else {\n // Reset status\n sipcall && sipcall.hangup()\n\n // Stop the local audio element ringing\n store.dispatch.player.stopAudioPlayer()\n }\n return\n }\n // Manage events\n var result = msg['result']\n if (\n result !== null &&\n result !== undefined &&\n result['event'] !== undefined &&\n result['event'] !== null\n ) {\n // Get event data\n var event = result['event']\n\n // Get the recording state\n const { recording } = store.getState().recorder\n const { view } = store.getState().island\n\n // Manage different types of events\n switch (event) {\n case 'registration_failed':\n if (janus.current.error)\n janus.current.error(\n 'Registration failed: ' + result['code'] + ' ' + result['reason'],\n )\n break\n\n case 'unregistered':\n if (janus.current.log)\n janus.current.log(\n 'Successfully un-registered as ' + result['username'] + '!',\n )\n // Update registered status to false\n store.dispatch.webrtc.updateWebRTC({\n registered: false,\n })\n eventDispatch('phone-island-webrtc-unregistered', {})\n break\n\n case 'registered':\n if (janus.current.log)\n janus.current.log(\n 'Successfully registered as ' + result['username'] + '!',\n )\n console.log('[REGISTER] Registration successful', {\n username: result['username'],\n wasAlreadyRegistered: store.getState().webrtc.registered,\n timestamp: new Date().toISOString()\n })\n eventDispatch('phone-island-webrtc-registered', {})\n if (!store.getState().webrtc.registered) {\n store.dispatch.webrtc.updateWebRTC({\n registered: true,\n })\n }\n // Remove WebRTC connections alert if any\n dispatch.alerts.removeAlert('webrtc_down')\n eventDispatch('phone-island-alert-removed', {\n type: 'webrtc_down',\n })\n // Connection is healthy again, reset stale flag\n connectionStale.current = false\n // Init completed successfully\n isInitializing.current = false\n if (initTimeoutRef.current) {\n clearTimeout(initTimeoutRef.current)\n initTimeoutRef.current = null\n }\n // Clear any network error grace timeout - connection is back\n if (networkErrorGraceRef.current) {\n clearTimeout(networkErrorGraceRef.current)\n networkErrorGraceRef.current = null\n console.info('[JANUS-GUARD] Cleared network error grace timeout - connection restored')\n }\n // Update webrtc lastActivity time\n dispatch.webrtc.updateLastActivity(new Date().getTime())\n break\n\n case 'registering':\n if (janus.current.log) {\n janus.current.log('janus registering')\n }\n break\n\n // This event arrive on outgoing call start\n case 'calling':\n // Number and display name are updated inside socket\n dispatch.currentCall.checkOutgoingUpdate({\n outgoingWebRTC: true,\n })\n\n // Update webrtc last activity time\n dispatch.webrtc.updateLastActivity(new Date().getTime())\n break\n\n // After an outgoing call start on 180 code, it means\n // ...that the local outgoing ringtone must be player\n case 'ringing':\n const { audioPlayerPlaying } = store.getState().player\n\n // Check if the local audio is already playing and start playing\n if (!audioPlayerPlaying) {\n // Update audio player and start playing\n dispatch.player.updateStartAudioPlayer({\n src: outgoingRingtone,\n loop: true,\n })\n }\n // Update webrtc lastActivity time\n dispatch.webrtc.updateLastActivity(new Date().getTime())\n if (view !== 'call') {\n dispatch.island.setIslandView('call')\n }\n break\n\n // After an outgoing call start on 183 code, it means\n // ...that the outgoing ringtone arrives from the stream\n // ...playing the local outgoing ringtone isn't needed\n case 'progress':\n if (janus.current.log) {\n janus.current.log(\n \"There's early media from \" +\n result['username'] +\n ', wairing for the call!',\n )\n }\n // Set the remote description to janus lib\n if (jsep) {\n handleRemote(jsep)\n }\n // Update webrtc lastActivity time\n dispatch.webrtc.updateLastActivity(new Date().getTime())\n break\n\n case 'incomingcall':\n const { default_device } = store.getState().currentUser\n const { endpoints, username } = store.getState().currentUser\n const { extensions } = store.getState().users\n\n const hasOnlineNethlink = () => {\n if (!extensions || !username) return false\n\n // Get all extensions for current user\n const userExtensions: any = Object.values(extensions).filter(\n (ext) => ext?.username === username,\n )\n\n // Check if any extension is nethlink type and online\n return userExtensions?.some((ext) => {\n const endpointExtension = endpoints?.extension.find(\n (endpoint) => endpoint.id === ext?.exten,\n )\n return (\n endpointExtension?.type === 'nethlink' && ext?.status !== 'offline'\n )\n })\n }\n\n // ALWAYS save jsepGlobal when we receive an incoming call JSEP\n // This allows answering from any device, even if it's not the default\n if (jsep) {\n console.log('[JSEP] Saving jsepGlobal for incoming call', {\n from: result['username'],\n timestamp: new Date().toISOString()\n })\n dispatch.webrtc.updateWebRTC({ jsepGlobal: jsep })\n // Track when this call arrived\n jsepGlobalTimestamp.current = Date.now()\n }\n\n if (\n (uaType === 'mobile' && hasOnlineNethlink()) ||\n (uaType === 'desktop' &&\n (default_device?.type === 'webrtc' ||\n (default_device?.type === undefined && !hasOnlineNethlink()) ||\n (!hasOnlineNethlink() && default_device?.type === 'physical')))\n ) {\n // Check if is recording an audio through call\n // ...recording an audio is a request made by the user\n // ...it must be managed differently than an incoming call\n if (recording) {\n // Update the recorder state\n dispatch.recorder.setIncoming(true)\n } else {\n // Manage the incoming message as a webrtc call\n // Update incoming webrtc state, number and display name\n // ...are updated inside socket\n dispatch.currentCall.checkIncomingUpdatePlay({\n incoming: true,\n incomingWebRTC: true,\n })\n\n if (janus.current.log) {\n dispatch.currentCall.updateIncoming(true)\n janus.current.log('Incoming call from ' + result['username'] + '!')\n }\n }\n\n // Update the webrtc last activity time\n dispatch.webrtc.updateLastActivity(new Date().getTime())\n }\n\n break\n\n case 'accepted':\n const acceptedTimestamp = Math.floor(Date.now() / 1000)\n if (janus.current.log) {\n const caller = result['username'] || result['displayname'] || store.getState().currentCall.number || 'Remote party'\n janus.current.log(caller + ' accepted the call!')\n }\n // Set the remote description to janus lib\n if (jsep) {\n handleRemote(jsep)\n }\n // Clear jsepGlobal after call is accepted\n console.log('[JSEP] Clearing jsepGlobal after call accepted', {\n timestamp: new Date().toISOString()\n })\n dispatch.webrtc.updateWebRTC({ jsepGlobal: null })\n jsepGlobalTimestamp.current = null\n\n // Set current call accepted\n dispatch.currentCall.checkAcceptedUpdate({\n acceptedWebRTC: true,\n })\n\n // Set incoming value to false and set start time\n dispatch.currentCall.updateCurrentCall({\n incoming: false,\n incomingWebRTC: false,\n startTime: acceptedTimestamp?.toString(),\n })\n\n // Stop the local audio element ringing\n store.dispatch.player.stopAudioPlayer()\n\n // Update webrtc lastActivity time\n dispatch.webrtc.updateLastActivity(new Date().getTime())\n break\n\n case 'hangup':\n // Clear jsepGlobal when call ends\n console.log('[JSEP] Clearing jsepGlobal on hangup', {\n timestamp: new Date().toISOString()\n })\n dispatch.webrtc.updateWebRTC({ jsepGlobal: null })\n jsepGlobalTimestamp.current = null\n\n // Manage hangup message during recording\n if (recording) {\n dispatch.recorder.setRecording(false)\n }\n if (!isPhysical() && uaType !== 'mobile') {\n hangupCurrentCall()\n sipcall.hangup()\n\n // Stop the local audio element ringing\n store.dispatch.player.stopAudioPlayer()\n\n // Check the janus doc before enable the following\n // if (\n // result['code'] === 486 &&\n // result['event'] === 'hangup' &&\n // result['reason'] === 'Busy Here'\n // ) {\n // dispatch.player.updateAudioSource({\n // src: busyRingtone,\n // })\n // dispatch.player.playAudio()\n // }\n // Reset current call info\n store.dispatch.currentCall.reset()\n if (janus.current.log)\n janus.current.log(\n 'Call hung up (' + result['code'] + ' ' + result['reason'] + ')!',\n )\n // Update webrtc lastActivity time\n dispatch.webrtc.updateLastActivity(new Date().getTime())\n // stopScreenSharingI()\n }\n\n // Stop the local audio element ringing\n store.dispatch.player.stopAudioPlayer()\n\n // Stop screen sharing if active\n const {\n active: screenShareActive,\n plugin,\n localScreenStream,\n remoteScreenStream,\n } = store.getState().screenShare\n\n if (screenShareActive) {\n janus.current.stopAllTracks(localScreenStream)\n janus.current.stopAllTracks(remoteScreenStream)\n dispatch.screenShare.update({ active: false })\n plugin.detach()\n }\n\n // After call cleanup, if connection was stale, force reload to reset Janus session\n if (connectionStale.current) {\n console.info('[JANUS-GUARD] Call ended with stale connection - forcing reload to reset Janus session', {\n timestamp: new Date().toISOString()\n })\n // Small delay to let cleanup complete before reload\n setTimeout(() => {\n dispatch.island.setForceReload(true)\n }, 500)\n }\n break\n\n case 'gateway_down':\n console.warn('THE GATEWAY IS DOWN')\n\n break\n\n case 'info':\n // Check if it's a keyframe request (see: https://github.com/meetecho/janus-gateway/pull/3517)\n if (\n result['type'] === 'application/media_control+xml' &&\n result['content'].includes('<picture_fast_update')\n ) {\n sipcall.send({\n message: { request: 'keyframe', user: true, peer: true },\n })\n }\n break\n\n default:\n if (janus.current.debug) {\n janus.current.debug('Event not handled:', event)\n }\n break\n }\n }\n },\n onlocaltrack: function (track, on) {\n if (janus.current.debug) {\n janus.current.debug('Local track ' + (on ? 'added' : 'removed') + ':', track)\n }\n\n // We use the track ID as name of the element, but it may contain invalid characters\n let trackId = track.id.replace(/[{}]/g, '')\n if (!on) {\n // Track removed, get rid of the stream and the rendering\n let stream = localTracks[trackId]\n if (stream) {\n try {\n let tracks = stream.getTracks()\n for (let i in tracks) {\n let mst = tracks[i]\n if (mst) mst.stop()\n }\n } catch (e: any) {\n if (janus.current.error) {\n janus.current.error('Error removing track:', e)\n }\n }\n }\n if (track.kind === 'video') {\n localVideos--\n }\n delete localTracks[trackId]\n return\n }\n // If we're here, a new track was added\n let stream = localTracks[trackId]\n if (stream) {\n // We've been here already\n return\n }\n if (track.kind === 'audio') {\n // We ignore local audio tracks, they'd generate echo anyway\n\n stream = new MediaStream([track])\n\n // Save the new audio stream to the store\n store.dispatch.webrtc.updateLocalAudioStream(stream)\n } else {\n // New video track: create a stream out of it\n localVideos++\n stream = new MediaStream([track])\n\n // Save the new video stream to the store\n store.dispatch.webrtc.updateLocalVideoStream(stream)\n\n localTracks[trackId] = stream\n if (janus.current.debug) {\n janus.current.debug('Created local stream:', stream)\n }\n const localVideoElement = store.getState().player.localVideo\n\n if (\n janus.current.attachMediaStream &&\n localVideoElement &&\n localVideoElement.current\n ) {\n janus.current.attachMediaStream(localVideoElement.current, stream)\n }\n }\n },\n onremotetrack: function (track, mid, on) {\n if (janus.current.debug) {\n janus.current.debug(\n 'Remote track (mid=' + mid + ') ' + (on ? 'added' : 'removed') + ':',\n track,\n )\n }\n\n // Stop the local audio element ringing\n store.dispatch.player.stopAudioPlayer()\n\n if (!on) {\n // Track removed, get rid of the stream and the rendering\n if (track.kind === 'video') {\n remoteVideos--\n }\n delete remoteTracks[mid]\n\n // Show remote video placeholder\n dispatch.currentCall.updateCurrentCall({\n showRemoteVideoPlaceHolder: true,\n })\n\n return\n }\n\n if (track.kind === 'audio') {\n // New audio track: create a stream out of it, and use a hidden <audio> element\n let stream = new MediaStream([track])\n remoteTracks[mid] = stream\n if (janus.current.debug) {\n janus.current.debug('Created remote audio stream: ' + stream)\n }\n const remoteAudioElement = store.getState().player.remoteAudio\n\n if (\n remoteAudioElement &&\n remoteAudioElement.current &&\n janus.current.attachMediaStream\n ) {\n janus.current.attachMediaStream(remoteAudioElement.current, stream)\n\n // Apply saved audio output device if available\n const defaultAudioOutputDevice: any = getJSONItem('phone-island-audio-output-device')\n\n if (defaultAudioOutputDevice?.deviceId) {\n const applySavedDevice = async () => {\n let targetDeviceId = defaultAudioOutputDevice.deviceId\n\n // Check if the saved device is still available\n if (targetDeviceId && targetDeviceId !== 'default') {\n try {\n const devices = await navigator.mediaDevices.enumerateDevices()\n const audioOutputDevices = devices.filter(device => device.kind === 'audiooutput')\n const deviceExists = audioOutputDevices.some(device => device.deviceId === targetDeviceId)\n\n if (!deviceExists) {\n console.warn(`Saved audio device ${targetDeviceId} no longer available, using default device`)\n targetDeviceId = 'default'\n // Update localStorage with the fallback\n setJSONItem('phone-island-audio-output-device', { deviceId: 'default' })\n }\n } catch (err) {\n console.warn('Error checking device availability, using default:', err)\n targetDeviceId = 'default'\n }\n }\n\n // Apply the device\n if (!remoteAudioElement.current) {\n console.warn('Remote audio element no longer available')\n return\n }\n\n try {\n await (remoteAudioElement.current as any).setSinkId(targetDeviceId)\n console.info('Audio output device applied successfully to new stream:', targetDeviceId)\n } catch (err) {\n console.warn('Failed to apply audio output device to new stream:', err)\n // Final fallback to default if not already using it\n if (targetDeviceId !== 'default' && remoteAudioElement.current) {\n try {\n await (remoteAudioElement.current as any).setSinkId('default')\n setJSONItem('phone-island-audio-output-device', { deviceId: 'default' })\n console.info('Fallback to default device successful')\n } catch (defaultErr) {\n console.error('Even default device failed:', defaultErr)\n }\n }\n }\n }\n\n applySavedDevice()\n }\n }\n // Save the new audio stream to the store\n store.dispatch.webrtc.updateRemoteAudioStream(stream)\n } else {\n // New video track: create a stream out of it\n remoteVideos++\n let stream = new MediaStream([track])\n\n // Save the new video stream to the store\n store.dispatch.webrtc.updateRemoteVideoStream(stream)\n\n remoteTracks[mid] = stream\n if (janus.current.debug) {\n janus.current.debug('Created remote video stream:' + stream)\n }\n const largeRemoteVideoElement = store.getState().player.largeRemoteVideo\n const smallRemoteVideoElement = store.getState().player.smallRemoteVideo\n\n if (\n janus.current.attachMediaStream &&\n largeRemoteVideoElement &&\n largeRemoteVideoElement.current\n ) {\n janus.current.attachMediaStream(largeRemoteVideoElement.current, stream)\n }\n\n if (\n janus.current.attachMediaStream &&\n smallRemoteVideoElement &&\n smallRemoteVideoElement.current\n ) {\n janus.current.attachMediaStream(smallRemoteVideoElement.current, stream)\n\n // Hide remote video placeholder\n dispatch.currentCall.updateCurrentCall({\n showRemoteVideoPlaceHolder: false,\n })\n }\n }\n },\n oncleanup: function () {\n if (janus.current.log) {\n janus.current.log(' ::: janus Got a cleanup notification :::')\n }\n },\n })\n }\n },\n error: (err: any) => {\n if (janus.current.log) janus.current.log('error', err)\n // Mark connection as stale due to network error\n console.warn('[JANUS-GUARD] Network error detected, marking connection as stale', {\n error: err,\n timestamp: new Date().toISOString()\n })\n connectionStale.current = true\n // Reset init flag on error so retry is possible\n isInitializing.current = false\n if (initTimeoutRef.current) {\n clearTimeout(initTimeoutRef.current)\n initTimeoutRef.current = null\n }\n\n // Check if there's an active call - if so, give ICE time to recover\n // WebRTC/ICE is resilient to brief network interruptions (WiFi roaming, etc.)\n const { sipcall: currentSipcall }: { sipcall: any } = store.getState().webrtc\n const { accepted, outgoing } = store.getState().currentCall\n const iceState = currentSipcall?.webrtcStuff?.pc?.iceConnectionState\n const hasActiveCall = accepted || outgoing || iceState === 'connected' || iceState === 'completed'\n\n if (hasActiveCall) {\n console.info('[JANUS-GUARD] Network error during active call - giving ICE grace period to recover', {\n iceState,\n accepted,\n outgoing,\n timestamp: new Date().toISOString()\n })\n // Don't set alert immediately - give 15 seconds for ICE to recover\n // Clear any existing grace timeout\n if (networkErrorGraceRef.current) {\n clearTimeout(networkErrorGraceRef.current)\n }\n networkErrorGraceRef.current = setTimeout(() => {\n // Check again if call is still active and ICE is disconnected\n const { sipcall: checkSipcall }: { sipcall: any } = store.getState().webrtc\n const checkIceState = checkSipcall?.webrtcStuff?.pc?.iceConnectionState\n const { accepted: stillAccepted, outgoing: stillOutgoing } = store.getState().currentCall\n const stillHasCall = stillAccepted || stillOutgoing\n\n const iceIsDown = checkIceState === 'disconnected' || checkIceState === 'failed' || !checkIceState\n if (stillHasCall && iceIsDown) {\n console.warn('[JANUS-GUARD] ICE failed to recover after grace period - resetting call state and activating alert', {\n checkIceState: checkIceState || 'undefined/null',\n timestamp: new Date().toISOString()\n })\n // Reset call state since call is effectively dead (ICE disconnected/failed/destroyed)\n // This allows App.tsx to proceed with reload instead of skipping it\n store.dispatch.currentCall.reset()\n dispatch.alerts.setAlert('webrtc_down')\n } else if (!stillHasCall) {\n console.info('[JANUS-GUARD] Call ended during grace period - activating alert for reconnection', {\n timestamp: new Date().toISOString()\n })\n dispatch.alerts.setAlert('webrtc_down')\n } else {\n console.info('[JANUS-GUARD] ICE recovered during grace period - call preserved, Janus HTTP still stale', {\n checkIceState,\n connectionStale: connectionStale.current,\n timestamp: new Date().toISOString()\n })\n // Keep connectionStale.current = true so hangup handler will force reload\n // ICE recovered but Janus HTTP session is still dead\n }\n networkErrorGraceRef.current = null\n }, 15000) // 15 second grace period\n } else {\n // No active call - activate alert immediately\n dispatch.alerts.setAlert('webrtc_down')\n }\n },\n destroyed: () => {\n console.log('[JANUS-GUARD] Session destroyed, clearing janusInstance', {\n timestamp: new Date().toISOString()\n })\n // Reset init flag when session is destroyed\n isInitializing.current = false\n if (initTimeoutRef.current) {\n clearTimeout(initTimeoutRef.current)\n initTimeoutRef.current = null\n }\n // Set webrtc destroyed status and clear janusInstance\n dispatch.webrtc.updateWebRTC({\n destroyed: true,\n janusInstance: null,\n })\n // Only activate alert if we're NOT already doing a voluntary reload\n // Otherwise we create a reload loop\n if (!isReloading.current) {\n console.log('[JANUS-GUARD] Activating webrtc_down alert (not a voluntary reload)')\n dispatch.alerts.setAlert('webrtc_down')\n } else {\n console.log('[JANUS-GUARD] Skipping alert activation (voluntary reload in progress)')\n }\n },\n })\n // Set janus instance to the store\n console.log('[JANUS-GUARD] Saving janusInstance to Redux', {\n sessionId: janusInstance.getSessionId?.(),\n timestamp: new Date().toISOString()\n })\n dispatch.webrtc.updateWebRTC({\n janusInstance,\n })\n },\n })\n }, [janus.current])\n\n // Check audio and video permissions when default_device is loaded or changed\n useEffect(() => {\n const { default_device } = store.getState().currentUser\n if (default_device !== undefined) {\n checkMediaPermissions()\n }\n }, [store?.getState()?.currentUser?.default_device])\n\n const [isOnline, setIsOnline] = useState(navigator.onLine)\n const [connectionReturned, setConnectionReturned] = useState(false)\n const wasOfflineRef = useRef(false)\n\n useEffect(() => {\n // Event listeners for online/offline status\n const handleOnline = () => setIsOnline(true)\n const handleOffline = () => setIsOnline(false)\n\n window.addEventListener('online', handleOnline)\n window.addEventListener('offline', handleOffline)\n\n return () => {\n window.removeEventListener('online', handleOnline)\n window.removeEventListener('offline', handleOffline)\n }\n }, [])\n\n // Reconnection management\n useEffect(() => {\n if (!isOnline) {\n console.log('Internet connection lost.')\n wasOfflineRef.current = true\n setConnectionReturned(false)\n } else if (wasOfflineRef.current) {\n console.log('Internet connection restored.')\n setConnectionReturned(true)\n wasOfflineRef.current = false\n }\n }, [isOnline])\n\n // Manage webrtc connections and events\n useEffect(() => {\n // Initializes the webrtc registration check interval\n function startWebrtcCheck() {\n const { CHECK_INTERVAL_TIME } = store.getState().webrtc\n if (!janusCheckInterval.current) {\n // Initialize the interval that check the webrtc\n janusCheckInterval.current = setInterval(\n () =>\n webrtcCheck(() => {\n // Do the register as callback of webrtc check\n register({ sipExten, sipSecret, sipHost, sipPort })\n }),\n CHECK_INTERVAL_TIME,\n )\n }\n }\n\n // Start webrtc initialization and handlers\n initWebRTC()\n // Start the check of webrtc activity\n startWebrtcCheck()\n\n return () => {\n // Unregister from janus\n unregister()\n // Stop Janus check interval\n clearInterval(janusCheckInterval.current)\n // Clear initialization timeout\n if (initTimeoutRef.current) {\n clearTimeout(initTimeoutRef.current)\n initTimeoutRef.current = null\n }\n // Clear network error grace timeout\n if (networkErrorGraceRef.current) {\n clearTimeout(networkErrorGraceRef.current)\n networkErrorGraceRef.current = null\n }\n }\n }, [])\n\n // Manage reload events\n useEffect(() => {\n if (reload || connectionReturned) {\n // Check if WebRTC is actually disconnected using alerts (more reliable than registered/sipcall)\n const { data } = store.getState().alerts\n const { forceReload } = store.getState().island\n const { sipcall, janusInstance }: { sipcall: any; janusInstance: any } = store.getState().webrtc\n const { accepted, outgoing } = store.getState().currentCall\n\n // Check if there's an active call - if so, don't reload automatically\n // WebRTC/ICE is designed to recover from brief network interruptions\n const iceState = sipcall?.webrtcStuff?.pc?.iceConnectionState\n const hasActiveCall = accepted || outgoing || iceState === 'connected' || iceState === 'completed'\n\n // Only do full reload if webrtc_down alert is active OR force reload is requested OR connection just returned\n const isWebRTCDown = data.webrtc_down?.active || false\n\n // If connection returned but there's an active call, try to reconnect Janus HTTP without full reload\n // This preserves ICE (audio) while restoring Janus signaling\n if (connectionReturned && hasActiveCall && !forceReload) {\n const janusConnected = janusInstance?.isConnected?.()\n\n if (!janusConnected && janusInstance?.reconnect) {\n console.info('[JANUS-GUARD] Connection returned with active call but Janus HTTP is dead - attempting Janus reconnect', {\n iceState,\n sessionId: janusInstance?.getSessionId?.(),\n timestamp: new Date().toISOString()\n })\n\n janusInstance.reconnect({\n success: () => {\n console.info('[JANUS-GUARD] Janus HTTP reconnected successfully during active call (connectionReturned)', {\n sessionId: janusInstance?.getSessionId?.(),\n timestamp: new Date().toISOString()\n })\n connectionStale.current = false\n // Clear grace period timer since we successfully reconnected\n if (networkErrorGraceRef.current) {\n clearTimeout(networkErrorGraceRef.current)\n networkErrorGraceRef.current = null\n }\n },\n error: (error) => {\n console.error('[JANUS-GUARD] Janus reconnect failed during active call (connectionReturned)', {\n error,\n timestamp: new Date().toISOString()\n })\n connectionStale.current = true\n }\n })\n } else {\n console.info('[JANUS-GUARD] Connection returned but active call in progress - Janus still connected, no action needed', {\n iceState,\n janusConnected,\n timestamp: new Date().toISOString()\n })\n }\n\n setConnectionReturned(false)\n return\n }\n\n if (isWebRTCDown || forceReload || connectionReturned) {\n // Prevent concurrent reloads or interrupting an in-progress init\n if (isReloading.current || isInitializing.current) {\n console.log('[JANUS-GUARD] Reload or init already in progress, skipping', {\n isReloading: isReloading.current,\n isInitializing: isInitializing.current\n })\n return\n }\n\n isReloading.current = true\n\n // Clear any pending init timeout before starting reload\n if (initTimeoutRef.current) {\n clearTimeout(initTimeoutRef.current)\n initTimeoutRef.current = null\n }\n\n console.info(\n forceReload\n ? 'Force reload requested, performing full WebRTC reconnection'\n : connectionReturned\n ? 'Internet connection restored, performing full WebRTC reconnection'\n : 'WebRTC down detected (alert active), performing full reload'\n )\n // Reset force reload flag\n if (forceReload) {\n store.dispatch.island.setForceReload(false)\n }\n\n // Clear janusInstance and registered state from Redux to allow new session creation\n console.log('[JANUS-GUARD] Manual reload, clearing janusInstance and registered state', {\n timestamp: new Date().toISOString()\n })\n dispatch.webrtc.updateWebRTC({\n janusInstance: null,\n sipcall: null,\n registered: false,\n jsepGlobal: null, // Also clear stale jsepGlobal\n })\n jsepGlobalTimestamp.current = null // Clear timestamp to match jsepGlobal\n // Unregister the WebRTC extension\n unregister()\n // Detach sipcall\n if (sipcall) sipcall.detach()\n // Destroy Janus session (use janusInstance from store, NOT janus.current which is the library!)\n if (janusInstance && janusInstance.destroy) {\n janusInstance.destroy({\n unload: true,\n notifyDestroyed: false,\n cleanupHandles: true,\n })\n }\n // Initialize a new Janus session immediately\n setTimeout(() => {\n initWebRTC()\n // Reset connection returned flag\n if (connectionReturned) {\n setConnectionReturned(false)\n }\n // Execute the reloaded callback\n if (reloadedCallback) reloadedCallback()\n // Reset reload flag and other state flags after completion\n setTimeout(() => {\n isReloading.current = false\n connectionStale.current = false\n wasFrozen.current = false\n lastInactivityDuration.current = 0\n }, 1000)\n }, 100)\n } else {\n console.info('WebRTC already connected (no alert active), skipping heavy reload')\n // Execute callback without reload\n if (reloadedCallback) reloadedCallback()\n }\n }\n }, [reload, connectionReturned])\n\n // Manage media devices (audio/video)\n useEffect(() => {\n const getMediaDevices = () => {\n if (navigator && navigator?.mediaDevices && navigator?.mediaDevices?.enumerateDevices) {\n navigator?.mediaDevices\n .enumerateDevices()\n .then((deviceInfos) => {\n dispatch.mediaDevices.updateMediaDevices(deviceInfos)\n })\n .catch((error) => {\n console.error('Error fetching devices:', error)\n })\n } else {\n console.warn('MediaDevices API not supported in this browser or context')\n dispatch.mediaDevices.updateMediaDevices([])\n }\n }\n getMediaDevices()\n\n if (navigator && navigator?.mediaDevices) {\n navigator?.mediaDevices?.addEventListener('devicechange', getMediaDevices)\n\n return () => {\n navigator?.mediaDevices?.removeEventListener('devicechange', getMediaDevices)\n }\n }\n }, [])\n\n // Detect page freeze/resume events (standby, browser freezing tab)\n useEffect(() => {\n const handleFreeze = () => {\n console.warn('[STANDBY-GUARD] Page frozen (standby or browser froze tab)', {\n timestamp: new Date().toISOString()\n })\n wasFrozen.current = true\n }\n\n const handleResume = () => {\n console.log('[STANDBY-GUARD] Page resumed from freeze', {\n timestamp: new Date().toISOString()\n })\n // wasFrozen flag will be checked on visibilitychange\n }\n\n document.addEventListener('freeze', handleFreeze)\n document.addEventListener('resume', handleResume)\n\n return () => {\n document.removeEventListener('freeze', handleFreeze)\n document.removeEventListener('resume', handleResume)\n }\n }, [])\n\n // Detect long standby periods and preemptively refresh WebRTC connection\n useEffect(() => {\n const handleVisibilityChange = () => {\n const now = Date.now()\n\n if (document.hidden) {\n // Tab going to background - track this\n wasHidden.current = true\n lastVisibilityChange.current = now\n console.log('[STANDBY-GUARD] Tab going to background', {\n timestamp: new Date().toISOString()\n })\n } else if (wasHidden.current) {\n // Tab returning to foreground after being hidden\n const timeHidden = now - lastVisibilityChange.current\n const threeMinutes = 3 * 60 * 1000 // 3 minutes - fallback threshold for throttling\n\n // Save this duration so initWebRTC can check it later\n lastInactivityDuration.current = timeHidden\n\n console.log('[STANDBY-GUARD] Tab returning to foreground', {\n timeHiddenMs: timeHidden,\n timeHiddenMinutes: Math.round(timeHidden / 60000),\n wasFrozen: wasFrozen.current,\n timestamp: new Date().toISOString()\n })\n\n const { registered, jsepGlobal, sipcall }: { registered: boolean; jsepGlobal: any; sipcall: any } = store.getState().webrtc\n const { outgoing: hasOutgoingCall } = store.getState().currentCall\n\n // Check if there's an active call (either incoming, outgoing, or in progress)\n const hasIncomingCall = !!jsepGlobal\n const hasActiveCall = sipcall?.webrtcStuff?.pc?.iceConnectionState === 'connected' ||\n sipcall?.webrtcStuff?.pc?.iceConnectionState === 'completed'\n const hasAnyCall = hasIncomingCall || hasActiveCall || hasOutgoingCall\n\n // Check if we need to reload:\n // 1. Page was frozen (freeze event) - standby or browser froze tab, OR\n // 2. Connection is stale (network errors detected), OR\n // 3. Tab was throttled for >3 minutes without any call (preventive reload), OR\n // 4. Tab was throttled for >30 minutes ONLY if there's no call (incoming or active)\n const thirtyMinutes = 30 * 60 * 1000\n const wasThrottledShort = timeHidden > threeMinutes && !hasAnyCall // 3+ min without call\n // Only reload after 30+ min if no call at all (preserve both incoming and active calls!)\n const wasThrottledVeryLong = timeHidden > thirtyMinutes && !hasAnyCall\n const needsReload = wasFrozen.current || connectionStale.current || wasThrottledShort || wasThrottledVeryLong\n\n if (needsReload) {\n const reloadReason = wasFrozen.current\n ? 'frozen'\n : connectionStale.current\n ? 'stale connection'\n : wasThrottledVeryLong\n ? 'throttled >30min (session too old)'\n : 'throttled >3min'\n\n if (hasAnyCall) {\n const callType = hasActiveCall ? 'active call' : hasOutgoingCall ? 'outgoing call' : 'incoming call'\n console.warn(\n `[STANDBY-GUARD] Reload needed (${reloadReason}) but ${callType} in progress. ` +\n 'Skipping reload to preserve call.',\n {\n wasFrozen: wasFrozen.current,\n connectionStale: connectionStale.current,\n wasThrottledShort,\n wasThrottledVeryLong,\n hasIncomingCall,\n hasOutgoingCall,\n hasActiveCall,\n timestamp: new Date().toISOString()\n }\n )\n } else {\n console.warn(\n `[STANDBY-GUARD] Reload needed (${reloadReason}), forcing reload`,\n {\n wasFrozen: wasFrozen.current,\n connectionStale: connectionStale.current,\n wasThrottledShort,\n wasThrottledVeryLong,\n timestamp: new Date().toISOString()\n }\n )\n }\n } else if (hasAnyCall) {\n // No freeze/throttling detected and there's a call - preserve it\n const callType = hasActiveCall ? 'active call' : hasOutgoingCall ? 'outgoing call' : 'incoming call'\n console.log(\n `[STANDBY-GUARD] Tab change without issues, ${callType} preserved`,\n {\n timeHiddenMinutes: Math.round(timeHidden / 60000),\n hasIncomingCall,\n hasOutgoingCall,\n hasActiveCall,\n timestamp: new Date().toISOString()\n }\n )\n }\n\n // Reload if page was frozen, connection is stale, or throttled too long\n // BUT NOT if there's an incoming or outgoing call (preserve call state)\n // Also don't reload if another init is already in progress\n const shouldReload = registered && !isReloading.current && !isInitializing.current && needsReload && !hasIncomingCall && !hasOutgoingCall\n\n if (shouldReload) {\n console.warn(\n '[STANDBY-GUARD] ⚠️ Reloading WebRTC',\n {\n timestamp: new Date().toISOString()\n }\n )\n\n // Trigger preventive reload\n isReloading.current = true\n\n // Clear any pending init timeout before starting reload\n if (initTimeoutRef.current) {\n clearTimeout(initTimeoutRef.current)\n initTimeoutRef.current = null\n }\n\n // Clear existing session\n const { janusInstance, sipcall: sipcallToDestroy }: { janusInstance: any; sipcall: any } = store.getState().webrtc\n // Unregister the WebRTC extension\n unregister()\n // Detach sipcall handle\n if (sipcallToDestroy) sipcallToDestroy.detach()\n // Destroy Janus session\n if (janusInstance && janusInstance.destroy) {\n janusInstance.destroy({\n unload: true,\n notifyDestroyed: false,\n cleanupHandles: true,\n })\n }\n\n // Clear state\n dispatch.webrtc.updateWebRTC({\n janusInstance: null,\n sipcall: null,\n registered: false,\n jsepGlobal: null,\n })\n jsepGlobalTimestamp.current = null\n\n // Reinitialize after a short delay\n setTimeout(() => {\n initWebRTC()\n setTimeout(() => {\n isReloading.current = false\n // Reset flags after successful reload\n connectionStale.current = false\n wasFrozen.current = false\n lastInactivityDuration.current = 0 // Reset inactivity duration after reload\n }, 1000)\n }, 100)\n }\n\n // Reset flags\n wasHidden.current = false\n // Reset frozen flag even if we didn't reload (for next cycle)\n if (!shouldReload) {\n wasFrozen.current = false\n // Reset inactivity duration since we've handled the visibility change\n lastInactivityDuration.current = 0\n }\n }\n }\n\n document.addEventListener('visibilitychange', handleVisibilityChange)\n\n return () => {\n document.removeEventListener('visibilitychange', handleVisibilityChange)\n }\n }, [initWebRTC, dispatch])\n\n useEventListener('phone-island-attach', (data) => {\n console.log('[EVENT] phone-island-attach received, calling initWebRTC', {\n timestamp: new Date().toISOString()\n })\n initWebRTC()\n eventDispatch('phone-island-attached', {})\n })\n\n // Force WebRTC reload when socket reconnects after network change\n // This prevents stale Janus sessions that cause 469 \"Unexpected ANSWER\" errors\n // BUT if there's an active call, try to reconnect Janus HTTP without killing ICE\n useEventListener('phone-island-socket-reconnected', () => {\n const { sipcall, janusInstance }: { sipcall: any; janusInstance: any } = store.getState().webrtc\n const { accepted, outgoing } = store.getState().currentCall\n const iceState = sipcall?.webrtcStuff?.pc?.iceConnectionState\n const hasActiveCall = accepted || outgoing || iceState === 'connected' || iceState === 'completed'\n const janusConnected = janusInstance?.isConnected?.()\n\n console.log('[EVENT] phone-island-socket-reconnected received', {\n hasActiveCall,\n iceState,\n accepted,\n outgoing,\n janusConnected,\n timestamp: new Date().toISOString()\n })\n\n // If there's an active call and Janus HTTP is disconnected, try to reconnect without full reload\n if (hasActiveCall && !janusConnected && janusInstance?.reconnect) {\n console.info('[EVENT] Socket reconnected with active call but Janus HTTP is dead - attempting Janus reconnect', {\n iceState,\n sessionId: janusInstance?.getSessionId?.(),\n timestamp: new Date().toISOString()\n })\n\n janusInstance.reconnect({\n success: () => {\n console.info('[JANUS-GUARD] Janus HTTP reconnected successfully during active call', {\n sessionId: janusInstance?.getSessionId?.(),\n timestamp: new Date().toISOString()\n })\n // Reset stale flag since we reconnected\n connectionStale.current = false\n // Clear grace period timer since we successfully reconnected\n if (networkErrorGraceRef.current) {\n clearTimeout(networkErrorGraceRef.current)\n networkErrorGraceRef.current = null\n }\n },\n error: (error) => {\n console.error('[JANUS-GUARD] Janus reconnect failed during active call', {\n error,\n timestamp: new Date().toISOString()\n })\n // Mark connection as stale so we reload after call ends\n connectionStale.current = true\n }\n })\n return\n }\n\n // If there's an active call and Janus is connected, clear grace period and do nothing\n if (hasActiveCall && janusConnected) {\n console.info('[EVENT] Socket reconnected with active call, Janus HTTP still connected - no action needed', {\n iceState,\n timestamp: new Date().toISOString()\n })\n // Clear grace period timer since Janus is already connected\n if (networkErrorGraceRef.current) {\n clearTimeout(networkErrorGraceRef.current)\n networkErrorGraceRef.current = null\n }\n return\n }\n\n // No active call - proceed with reload to ensure clean state\n // Clear any stale jsepGlobal - it's invalid after network reconnect\n const { jsepGlobal } = store.getState().webrtc\n if (jsepGlobal) {\n console.log('[EVENT] Clearing stale jsepGlobal after socket reconnect', {\n timestamp: new Date().toISOString()\n })\n dispatch.webrtc.updateWebRTC({ jsepGlobal: null })\n jsepGlobalTimestamp.current = null // Clear timestamp to match jsepGlobal\n }\n // Trigger reload via connectionReturned (forceReload not needed - connectionReturned already triggers reload)\n setConnectionReturned(true)\n })\n\n /**\n * Event listner for phone-island-call-transfer event\n */\n useEventListener('phone-island-call-transfer', (data) => {\n const transferNumber = data?.to\n dispatch.island.toggleIsOpen(true)\n handleAttendedTransfer(transferNumber)\n eventDispatch('phone-island-call-transfer-opened', {})\n })\n\n async function handleAttendedTransfer(number: string) {\n // Send attended transfer message\n const transferringMessageSent = await attendedTransfer(number)\n if (transferringMessageSent) {\n // Set transferring and disable pause\n dispatch.currentCall.updateCurrentCall({\n transferring: true,\n paused: false,\n })\n // Play the remote audio element\n dispatch.player.playRemoteAudio()\n }\n }\n\n return <>{children}</>\n}\n"],"names":["_a","hostName","sipExten","sipSecret","children","sipHost","sipPort","reload","uaType","reloadedCallback","dispatch","useDispatch","janusCheckInterval","useRef","isReloading","lastVisibilityChange","Date","now","wasHidden","lastInactivityDuration","connectionStale","jsepGlobalTimestamp","wasFrozen","isInitializing","initTimeoutRef","networkErrorGraceRef","janus","JanusLib","localTracks","initWebRTC","useCallback","current","console","log","timestamp","toISOString","clearTimeout","setTimeout","warn","alerts","setAlert","eventDispatch","type","_g","store","getState","webrtc","existingInstance","janusInstance","registered","shouldInit","sessionId","getSessionId","isConnected","_b","inactivityMs","inactivityMinutes","Math","round","longInactivity","_h","currentSipcall","sipcall","hasIncomingCall","hasActiveCall","_d","_c","webrtcStuff","pc","iceConnectionState","_f","_e","reason","concat","destroy","unload","notifyDestroyed","cleanupHandles","e","error","updateWebRTC","jsepGlobal","init","debug","dependencies","useDefaultDependencies","adapter","callback","server","success","attach","plugin","opaqueId","getTime","pluginHandle","register","getPlugin","consentDialog","on","webrtcState","view","island","currentCall","accepted","outgoing","incoming","setIslandView","iceState","newState","mediaState","medium","slowLink","uplink","count","onmessage","msg","jsep","JSON","stringify","undefined","result","event","recording","recorder","username","wasAlreadyRegistered","removeAlert","info","updateLastActivity","checkOutgoingUpdate","outgoingWebRTC","player","audioPlayerPlaying","updateStartAudioPlayer","src","outgoingRingtone","loop","handleRemote","default_device","currentUser","endpoints_1","endpoints","username_1","extensions_1","users","extensions","hasOnlineNethlink","userExtensions","Object","values","filter","ext","some","endpointExtension","extension","find","endpoint","id","exten","status","from","setIncoming","checkIncomingUpdatePlay","incomingWebRTC","updateIncoming","acceptedTimestamp","floor","caller","number","checkAcceptedUpdate","acceptedWebRTC","updateCurrentCall","startTime","toString","stopAudioPlayer","setRecording","isPhysical","hangupCurrentCall","hangup","reset","screenShare","screenShareActive","localScreenStream","remoteScreenStream","stopAllTracks","update","active","detach","setForceReload","includes","send","message","request","user","peer","onlocaltrack","track","trackId","replace","stream_1","tracks","getTracks","i","mst","stop","kind","stream","MediaStream","updateLocalAudioStream","updateLocalVideoStream","localVideoElement","localVideo","attachMediaStream","onremotetrack","mid","_this","this","showRemoteVideoPlaceHolder","remoteAudioElement_1","remoteAudio","defaultAudioOutputDevice_1","getJSONItem","deviceId","__awaiter","targetDeviceId","navigator","mediaDevices","enumerateDevices","devices","sent","device","setJSONItem","err_1","setSinkId","err_2","defaultErr_1","updateRemoteAudioStream","updateRemoteVideoStream","largeRemoteVideoElement","largeRemoteVideo","smallRemoteVideoElement","smallRemoteVideo","oncleanup","err","checkSipcall","checkIceState","stillAccepted","stillOutgoing","stillHasCall","destroyed","call","useEffect","checkMediaPermissions","useState","onLine","isOnline","setIsOnline","connectionReturned","setConnectionReturned","wasOfflineRef","handleOnline","handleOffline","window","addEventListener","removeEventListener","CHECK_INTERVAL_TIME","setInterval","webrtcCheck","unregister","clearInterval","data","forceReload","janusInstance_1","isWebRTCDown","webrtc_down","janusConnected","reconnect","getMediaDevices","then","deviceInfos","updateMediaDevices","catch","handleFreeze","handleResume","document","handleVisibilityChange","hidden","timeHidden","timeHiddenMs","timeHiddenMinutes","hasOutgoingCall","hasAnyCall","wasThrottledShort","wasThrottledVeryLong","needsReload","reloadReason","callType","shouldReload","sipcallToDestroy","useEventListener","transferNumber","to","toggleIsOpen","attendedTransfer","transferring","paused","playRemoteAudio","handleAttendedTransfer","React","createElement","Fragment"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),require("../../node_modules/mic-check/lib/index.js");var e=require("../webrtc/janus.js"),i=require("../../store/index.js"),r=require("../user/default_device.js");require("react");var t=require("../../utils/genericFunctions/localStorage.js");require("../../node_modules/tslib/tslib.es6.js");var n=require("../../_virtual/index8.js"),o=e.default;exports.checkMediaPermissions=function(){if(r.isPhysical())return i.store.dispatch.alerts.removeAlert("browser_permissions"),i.store.dispatch.alerts.removeAlert("user_permissions"),i.store.dispatch.alerts.removeAlert("busy_camera"),void i.store.dispatch.alerts.removeAlert("unknown_media_permissions");n.__exports.requestMediaPermissions({audio:!0}).then((function(){})).catch((function(e){var r,t,s=e.type,d=null!==(t=((r={})[n.__exports.MediaPermissionsErrorType.SystemPermissionDenied]={alert:"browser_permissions",message:"WebRTC: browser does not have permission to access camera or microphone"},r[n.__exports.MediaPermissionsErrorType.UserPermissionDenied]={alert:"user_permissions",message:"WebRTC: user didn't allow app to access camera or microphone"},r)[s])&&void 0!==t?t:{alert:"unknown_media_permissions",message:"WebRTC: can't access audio or camera on this device. unknown error"};i.store.dispatch.alerts.setAlert(d.alert),o.error&&o.error(d.message)}))},exports.checkWebCamPermission=function(){return new Promise((function(e){n.__exports.requestMediaPermissions({video:!0}).then((function(){e(!0)})).catch((function(i){console.error("Error requesting webcam permission:",i),e(!1)}))}))},exports.getCurrentAudioInputDeviceId=function(){var e=t.getJSONItem("phone-island-audio-input-device").deviceId||null,r=i.store.select.mediaDevices.audioInputDevices(i.store.getState());if(r.find((function(i){return i.deviceId===e})))return e;var n=r.find((function(e){return"default"===e.deviceId||""===e.deviceId}))||r[0],o=n?n.deviceId:"default";return null!==e&&o!==e&&(console.warn("Audio input device ".concat(e," no longer available, falling back to default device")),t.setJSONItem("phone-island-audio-input-device",{deviceId:o})),o},exports.getCurrentAudioOutputDeviceId=function(){var e=t.getJSONItem("phone-island-audio-output-device").deviceId||null,r=i.store.select.mediaDevices.audioOutputDevices(i.store.getState());if(r.find((function(i){return i.deviceId===e})))return e;var n=r.find((function(e){return"default"===e.deviceId||""===e.deviceId}))||r[0],o=n?n.deviceId:"default";return null!==e&&o!==e&&(console.warn("Audio output device ".concat(e," no longer available, falling back to default device")),t.setJSONItem("phone-island-audio-output-device",{deviceId:o})),o},exports.getCurrentVideoInputDeviceId=function(){var e=t.getJSONItem("phone-island-video-input-device").deviceId||null,r=i.store.select.mediaDevices.videoInputDevices(i.store.getState());if(r.find((function(i){return i.deviceId===e})))return e;var n=r.find((function(e){return"default"===e.deviceId||""===e.deviceId}))||r[0],o=n?n.deviceId:"default";return null!==e&&o!==e&&(console.warn("Video input device ".concat(e," no longer available, falling back to default device")),t.setJSONItem("phone-island-video-input-device",{deviceId:o})),o},exports.getSupportedDevices=function(e){var i,r,t=!1,n=!1,s=!1,d=!1;i=function(){var i={audio:t,audioCap:s,video:n,videoCap:d};o.log&&o.log("supportedDevices=",i),e()},r=[],navigator.mediaDevices.enumerateDevices().then((function(e){e.forEach((function(e){var i=!1;r.forEach((function(r){r.deviceId===e.deviceId&&r.kind===e.kind&&(i=!0)})),i||("videoinput"!==e.kind||d||(d=!0),"audioinput"!==e.kind||s||(s=!0),"audioinput"===e.kind&&(t=!0),e.kind,"videoinput"===e.kind&&(n=!0),r.push(e))})),i&&i()}))};
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),require("../../node_modules/mic-check/lib/index.js");var e=require("../webrtc/janus.js"),i=require("../../store/index.js"),r=require("../user/default_device.js");require("react");var t=require("../../utils/genericFunctions/localStorage.js");require("../../node_modules/tslib/tslib.es6.js");var n=require("../../_virtual/index6.js"),o=e.default;exports.checkMediaPermissions=function(){if(r.isPhysical())return i.store.dispatch.alerts.removeAlert("browser_permissions"),i.store.dispatch.alerts.removeAlert("user_permissions"),i.store.dispatch.alerts.removeAlert("busy_camera"),void i.store.dispatch.alerts.removeAlert("unknown_media_permissions");n.__exports.requestMediaPermissions({audio:!0}).then((function(){})).catch((function(e){var r,t,s=e.type,d=null!==(t=((r={})[n.__exports.MediaPermissionsErrorType.SystemPermissionDenied]={alert:"browser_permissions",message:"WebRTC: browser does not have permission to access camera or microphone"},r[n.__exports.MediaPermissionsErrorType.UserPermissionDenied]={alert:"user_permissions",message:"WebRTC: user didn't allow app to access camera or microphone"},r)[s])&&void 0!==t?t:{alert:"unknown_media_permissions",message:"WebRTC: can't access audio or camera on this device. unknown error"};i.store.dispatch.alerts.setAlert(d.alert),o.error&&o.error(d.message)}))},exports.checkWebCamPermission=function(){return new Promise((function(e){n.__exports.requestMediaPermissions({video:!0}).then((function(){e(!0)})).catch((function(i){console.error("Error requesting webcam permission:",i),e(!1)}))}))},exports.getCurrentAudioInputDeviceId=function(){var e=t.getJSONItem("phone-island-audio-input-device").deviceId||null,r=i.store.select.mediaDevices.audioInputDevices(i.store.getState());if(r.find((function(i){return i.deviceId===e})))return e;var n=r.find((function(e){return"default"===e.deviceId||""===e.deviceId}))||r[0],o=n?n.deviceId:"default";return null!==e&&o!==e&&(console.warn("Audio input device ".concat(e," no longer available, falling back to default device")),t.setJSONItem("phone-island-audio-input-device",{deviceId:o})),o},exports.getCurrentAudioOutputDeviceId=function(){var e=t.getJSONItem("phone-island-audio-output-device").deviceId||null,r=i.store.select.mediaDevices.audioOutputDevices(i.store.getState());if(r.find((function(i){return i.deviceId===e})))return e;var n=r.find((function(e){return"default"===e.deviceId||""===e.deviceId}))||r[0],o=n?n.deviceId:"default";return null!==e&&o!==e&&(console.warn("Audio output device ".concat(e," no longer available, falling back to default device")),t.setJSONItem("phone-island-audio-output-device",{deviceId:o})),o},exports.getCurrentVideoInputDeviceId=function(){var e=t.getJSONItem("phone-island-video-input-device").deviceId||null,r=i.store.select.mediaDevices.videoInputDevices(i.store.getState());if(r.find((function(i){return i.deviceId===e})))return e;var n=r.find((function(e){return"default"===e.deviceId||""===e.deviceId}))||r[0],o=n?n.deviceId:"default";return null!==e&&o!==e&&(console.warn("Video input device ".concat(e," no longer available, falling back to default device")),t.setJSONItem("phone-island-video-input-device",{deviceId:o})),o},exports.getSupportedDevices=function(e){var i,r,t=!1,n=!1,s=!1,d=!1;i=function(){var i={audio:t,audioCap:s,video:n,videoCap:d};o.log&&o.log("supportedDevices=",i),e()},r=[],navigator.mediaDevices.enumerateDevices().then((function(e){e.forEach((function(e){var i=!1;r.forEach((function(r){r.deviceId===e.deviceId&&r.kind===e.kind&&(i=!0)})),i||("videoinput"!==e.kind||d||(d=!0),"audioinput"!==e.kind||s||(s=!0),"audioinput"===e.kind&&(t=!0),e.kind,"videoinput"===e.kind&&(n=!0),r.push(e))})),i&&i()}))};
2
2
  //# sourceMappingURL=devices.js.map
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var t=require("../fontawesome-svg-core/index.mjs.js");require("../../prop-types/index.js");var e=require("react"),r=require("../../../_virtual/index6.js");function o(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var n=o(e);function a(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,o=Array(e);r<e;r++)o[r]=t[r];return o}function i(t,e,r){return(e=function(t){var e=function(t,e){if("object"!=typeof t||!t)return t;var r=t[Symbol.toPrimitive];if(void 0!==r){var o=r.call(t,e||"default");if("object"!=typeof o)return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===e?String:Number)(t)}(t,"string");return"symbol"==typeof e?e:e+""}(e))in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}function s(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);e&&(o=o.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,o)}return r}function l(t){for(var e=1;e<arguments.length;e++){var r=null!=arguments[e]?arguments[e]:{};e%2?s(Object(r),!0).forEach((function(e){i(t,e,r[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):s(Object(r)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))}))}return t}function f(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){var r=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null!=r){var o,n,a,i,s=[],l=!0,f=!1;try{if(a=(r=r.call(t)).next,0===e){if(Object(r)!==r)return;l=!1}else for(;!(l=(o=a.call(r)).done)&&(s.push(o.value),s.length!==e);l=!0);}catch(t){f=!0,n=t}finally{try{if(!l&&null!=r.return&&(i=r.return(),Object(i)!==i))return}finally{if(f)throw n}}return s}}(t,e)||p(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function u(t){return function(t){if(Array.isArray(t))return a(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||p(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function c(t){return c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},c(t)}function p(t,e){if(t){if("string"==typeof t)return a(t,e);var r={}.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?a(t,e):void 0}}var b;try{var y=require("@fortawesome/fontawesome-svg-core/package.json");b=y.version}catch(t){b="undefined"!=typeof process&&process.env.FA_VERSION||"7.0.0"}function m(t){var e=t.beat,r=t.fade,o=t.beatFade,n=t.bounce,a=t.shake,s=t.flash,l=t.spin,u=t.spinPulse,c=t.spinReverse,p=t.pulse,y=t.fixedWidth,m=t.inverse,d=t.border,x=t.listItem,v=t.flip,h=t.size,g=t.rotation,O=t.pull,w=t.swapOpacity,j=t.rotateBy,A=t.widthAuto,I=function(t,e){for(var r=f(t.split("-"),2),o=r[0],n=r[1],a=f(e.split("-"),2),i=a[0],s=a[1],l=o.split("."),u=i.split("."),c=0;c<Math.max(l.length,u.length);c++){var p=l[c]||"0",b=u[c]||"0",y=parseInt(p,10),m=parseInt(b,10);if(y!==m)return y>m}for(var d=0;d<Math.max(l.length,u.length);d++){var x=l[d]||"0",v=u[d]||"0";if(x!==v&&x.length!==v.length)return x.length<v.length}return!(n&&!s)}(b,"7.0.0"),k=i(i(i(i(i(i({"fa-beat":e,"fa-fade":r,"fa-beat-fade":o,"fa-bounce":n,"fa-shake":a,"fa-flash":s,"fa-spin":l,"fa-spin-reverse":c,"fa-spin-pulse":u,"fa-pulse":p,"fa-fw":y,"fa-inverse":m,"fa-border":d,"fa-li":x,"fa-flip":!0===v,"fa-flip-horizontal":"horizontal"===v||"both"===v,"fa-flip-vertical":"vertical"===v||"both"===v},"fa-".concat(h),null!=h),"fa-rotate-".concat(g),null!=g&&0!==g),"fa-pull-".concat(O),null!=O),"fa-swap-opacity",w),"fa-rotate-by",I&&j),"fa-width-auto",I&&A);return Object.keys(k).map((function(t){return k[t]?t:null})).filter((function(t){return t}))}function d(t){return e=t,(e-=0)==e?t:(t=t.replace(/[\-_\s]+(.)?/g,(function(t,e){return e?e.toUpperCase():""}))).substr(0,1).toLowerCase()+t.substr(1);var e}var x=["style"];var v=!1;try{v="production"===process.env.NODE_ENV}catch(t){}function h(e){return e&&"object"===c(e)&&e.prefix&&e.iconName&&e.icon?e:t.parse.icon?t.parse.icon(e):null===e?null:e&&"object"===c(e)&&e.prefix&&e.iconName?e:Array.isArray(e)&&2===e.length?{prefix:e[0],iconName:e[1]}:"string"==typeof e?{prefix:"fas",iconName:e}:void 0}function g(t,e){return Array.isArray(e)&&e.length>0||!Array.isArray(e)&&e?i({},t,e):{}}var O={border:!1,className:"",mask:null,maskId:null,fixedWidth:!1,inverse:!1,flip:!1,icon:null,listItem:!1,pull:null,pulse:!1,rotation:null,rotateBy:!1,size:null,spin:!1,spinPulse:!1,spinReverse:!1,beat:!1,fade:!1,beatFade:!1,bounce:!1,shake:!1,symbol:!1,title:"",titleId:null,transform:null,swapOpacity:!1,widthAuto:!1},w=n.default.forwardRef((function(e,r){var o=l(l({},O),e),n=o.icon,a=o.mask,i=o.symbol,s=o.className,f=o.title,c=o.titleId,p=o.maskId,b=h(n),y=g("classes",[].concat(u(m(o)),u((s||"").split(" ")))),d=g("transform","string"==typeof o.transform?t.parse.transform(o.transform):o.transform),x=g("mask",h(a)),w=t.icon(b,l(l(l(l({},y),d),x),{},{symbol:i,title:f,titleId:c,maskId:p}));if(!w)return function(){var t;!v&&console&&"function"==typeof console.error&&(t=console).error.apply(t,arguments)}("Could not find icon",b),null;var A=w.abstract,I={ref:r};return Object.keys(o).forEach((function(t){O.hasOwnProperty(t)||(I[t]=o[t])})),j(A[0],I)}));w.displayName="FontAwesomeIcon",w.propTypes={beat:r.exports.bool,border:r.exports.bool,beatFade:r.exports.bool,bounce:r.exports.bool,className:r.exports.string,fade:r.exports.bool,flash:r.exports.bool,mask:r.exports.oneOfType([r.exports.object,r.exports.array,r.exports.string]),maskId:r.exports.string,fixedWidth:r.exports.bool,inverse:r.exports.bool,flip:r.exports.oneOf([!0,!1,"horizontal","vertical","both"]),icon:r.exports.oneOfType([r.exports.object,r.exports.array,r.exports.string]),listItem:r.exports.bool,pull:r.exports.oneOf(["right","left"]),pulse:r.exports.bool,rotation:r.exports.oneOf([0,90,180,270]),rotateBy:r.exports.bool,shake:r.exports.bool,size:r.exports.oneOf(["2xs","xs","sm","lg","xl","2xl","1x","2x","3x","4x","5x","6x","7x","8x","9x","10x"]),spin:r.exports.bool,spinPulse:r.exports.bool,spinReverse:r.exports.bool,symbol:r.exports.oneOfType([r.exports.bool,r.exports.string]),title:r.exports.string,titleId:r.exports.string,transform:r.exports.oneOfType([r.exports.string,r.exports.object]),swapOpacity:r.exports.bool,widthAuto:r.exports.bool};var j=function t(e,r){var o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if("string"==typeof r)return r;var n=(r.children||[]).map((function(r){return t(e,r)})),a=Object.keys(r.attributes||{}).reduce((function(t,e){var o=r.attributes[e];switch(e){case"class":t.attrs.className=o,delete r.attributes.class;break;case"style":t.attrs.style=o.split(";").map((function(t){return t.trim()})).filter((function(t){return t})).reduce((function(t,e){var r,o=e.indexOf(":"),n=d(e.slice(0,o)),a=e.slice(o+1).trim();return n.startsWith("webkit")?t[(r=n,r.charAt(0).toUpperCase()+r.slice(1))]=a:t[n]=a,t}),{});break;default:0===e.indexOf("aria-")||0===e.indexOf("data-")?t.attrs[e.toLowerCase()]=o:t.attrs[d(e)]=o}return t}),{attrs:{}}),i=o.style,s=void 0===i?{}:i,f=function(t,e){if(null==t)return{};var r,o,n=function(t,e){if(null==t)return{};var r={};for(var o in t)if({}.hasOwnProperty.call(t,o)){if(-1!==e.indexOf(o))continue;r[o]=t[o]}return r}(t,e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);for(o=0;o<a.length;o++)r=a[o],-1===e.indexOf(r)&&{}.propertyIsEnumerable.call(t,r)&&(n[r]=t[r])}return n}(o,x);return a.attrs.style=l(l({},a.attrs.style),s),e.apply(void 0,[r.tag,l(l({},a.attrs),f)].concat(u(n)))}.bind(null,n.default.createElement);exports.FontAwesomeIcon=w;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var t=require("../fontawesome-svg-core/index.mjs.js");require("../../prop-types/index.js");var e=require("react"),r=require("../../../_virtual/index7.js");function o(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var n=o(e);function a(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,o=Array(e);r<e;r++)o[r]=t[r];return o}function i(t,e,r){return(e=function(t){var e=function(t,e){if("object"!=typeof t||!t)return t;var r=t[Symbol.toPrimitive];if(void 0!==r){var o=r.call(t,e||"default");if("object"!=typeof o)return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===e?String:Number)(t)}(t,"string");return"symbol"==typeof e?e:e+""}(e))in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}function s(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);e&&(o=o.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,o)}return r}function l(t){for(var e=1;e<arguments.length;e++){var r=null!=arguments[e]?arguments[e]:{};e%2?s(Object(r),!0).forEach((function(e){i(t,e,r[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):s(Object(r)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))}))}return t}function f(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){var r=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null!=r){var o,n,a,i,s=[],l=!0,f=!1;try{if(a=(r=r.call(t)).next,0===e){if(Object(r)!==r)return;l=!1}else for(;!(l=(o=a.call(r)).done)&&(s.push(o.value),s.length!==e);l=!0);}catch(t){f=!0,n=t}finally{try{if(!l&&null!=r.return&&(i=r.return(),Object(i)!==i))return}finally{if(f)throw n}}return s}}(t,e)||p(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function u(t){return function(t){if(Array.isArray(t))return a(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||p(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function c(t){return c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},c(t)}function p(t,e){if(t){if("string"==typeof t)return a(t,e);var r={}.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?a(t,e):void 0}}var b;try{var y=require("@fortawesome/fontawesome-svg-core/package.json");b=y.version}catch(t){b="undefined"!=typeof process&&process.env.FA_VERSION||"7.0.0"}function m(t){var e=t.beat,r=t.fade,o=t.beatFade,n=t.bounce,a=t.shake,s=t.flash,l=t.spin,u=t.spinPulse,c=t.spinReverse,p=t.pulse,y=t.fixedWidth,m=t.inverse,d=t.border,x=t.listItem,v=t.flip,h=t.size,g=t.rotation,O=t.pull,w=t.swapOpacity,j=t.rotateBy,A=t.widthAuto,I=function(t,e){for(var r=f(t.split("-"),2),o=r[0],n=r[1],a=f(e.split("-"),2),i=a[0],s=a[1],l=o.split("."),u=i.split("."),c=0;c<Math.max(l.length,u.length);c++){var p=l[c]||"0",b=u[c]||"0",y=parseInt(p,10),m=parseInt(b,10);if(y!==m)return y>m}for(var d=0;d<Math.max(l.length,u.length);d++){var x=l[d]||"0",v=u[d]||"0";if(x!==v&&x.length!==v.length)return x.length<v.length}return!(n&&!s)}(b,"7.0.0"),k=i(i(i(i(i(i({"fa-beat":e,"fa-fade":r,"fa-beat-fade":o,"fa-bounce":n,"fa-shake":a,"fa-flash":s,"fa-spin":l,"fa-spin-reverse":c,"fa-spin-pulse":u,"fa-pulse":p,"fa-fw":y,"fa-inverse":m,"fa-border":d,"fa-li":x,"fa-flip":!0===v,"fa-flip-horizontal":"horizontal"===v||"both"===v,"fa-flip-vertical":"vertical"===v||"both"===v},"fa-".concat(h),null!=h),"fa-rotate-".concat(g),null!=g&&0!==g),"fa-pull-".concat(O),null!=O),"fa-swap-opacity",w),"fa-rotate-by",I&&j),"fa-width-auto",I&&A);return Object.keys(k).map((function(t){return k[t]?t:null})).filter((function(t){return t}))}function d(t){return e=t,(e-=0)==e?t:(t=t.replace(/[\-_\s]+(.)?/g,(function(t,e){return e?e.toUpperCase():""}))).substr(0,1).toLowerCase()+t.substr(1);var e}var x=["style"];var v=!1;try{v="production"===process.env.NODE_ENV}catch(t){}function h(e){return e&&"object"===c(e)&&e.prefix&&e.iconName&&e.icon?e:t.parse.icon?t.parse.icon(e):null===e?null:e&&"object"===c(e)&&e.prefix&&e.iconName?e:Array.isArray(e)&&2===e.length?{prefix:e[0],iconName:e[1]}:"string"==typeof e?{prefix:"fas",iconName:e}:void 0}function g(t,e){return Array.isArray(e)&&e.length>0||!Array.isArray(e)&&e?i({},t,e):{}}var O={border:!1,className:"",mask:null,maskId:null,fixedWidth:!1,inverse:!1,flip:!1,icon:null,listItem:!1,pull:null,pulse:!1,rotation:null,rotateBy:!1,size:null,spin:!1,spinPulse:!1,spinReverse:!1,beat:!1,fade:!1,beatFade:!1,bounce:!1,shake:!1,symbol:!1,title:"",titleId:null,transform:null,swapOpacity:!1,widthAuto:!1},w=n.default.forwardRef((function(e,r){var o=l(l({},O),e),n=o.icon,a=o.mask,i=o.symbol,s=o.className,f=o.title,c=o.titleId,p=o.maskId,b=h(n),y=g("classes",[].concat(u(m(o)),u((s||"").split(" ")))),d=g("transform","string"==typeof o.transform?t.parse.transform(o.transform):o.transform),x=g("mask",h(a)),w=t.icon(b,l(l(l(l({},y),d),x),{},{symbol:i,title:f,titleId:c,maskId:p}));if(!w)return function(){var t;!v&&console&&"function"==typeof console.error&&(t=console).error.apply(t,arguments)}("Could not find icon",b),null;var A=w.abstract,I={ref:r};return Object.keys(o).forEach((function(t){O.hasOwnProperty(t)||(I[t]=o[t])})),j(A[0],I)}));w.displayName="FontAwesomeIcon",w.propTypes={beat:r.exports.bool,border:r.exports.bool,beatFade:r.exports.bool,bounce:r.exports.bool,className:r.exports.string,fade:r.exports.bool,flash:r.exports.bool,mask:r.exports.oneOfType([r.exports.object,r.exports.array,r.exports.string]),maskId:r.exports.string,fixedWidth:r.exports.bool,inverse:r.exports.bool,flip:r.exports.oneOf([!0,!1,"horizontal","vertical","both"]),icon:r.exports.oneOfType([r.exports.object,r.exports.array,r.exports.string]),listItem:r.exports.bool,pull:r.exports.oneOf(["right","left"]),pulse:r.exports.bool,rotation:r.exports.oneOf([0,90,180,270]),rotateBy:r.exports.bool,shake:r.exports.bool,size:r.exports.oneOf(["2xs","xs","sm","lg","xl","2xl","1x","2x","3x","4x","5x","6x","7x","8x","9x","10x"]),spin:r.exports.bool,spinPulse:r.exports.bool,spinReverse:r.exports.bool,symbol:r.exports.oneOfType([r.exports.bool,r.exports.string]),title:r.exports.string,titleId:r.exports.string,transform:r.exports.oneOfType([r.exports.string,r.exports.object]),swapOpacity:r.exports.bool,widthAuto:r.exports.bool};var j=function t(e,r){var o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if("string"==typeof r)return r;var n=(r.children||[]).map((function(r){return t(e,r)})),a=Object.keys(r.attributes||{}).reduce((function(t,e){var o=r.attributes[e];switch(e){case"class":t.attrs.className=o,delete r.attributes.class;break;case"style":t.attrs.style=o.split(";").map((function(t){return t.trim()})).filter((function(t){return t})).reduce((function(t,e){var r,o=e.indexOf(":"),n=d(e.slice(0,o)),a=e.slice(o+1).trim();return n.startsWith("webkit")?t[(r=n,r.charAt(0).toUpperCase()+r.slice(1))]=a:t[n]=a,t}),{});break;default:0===e.indexOf("aria-")||0===e.indexOf("data-")?t.attrs[e.toLowerCase()]=o:t.attrs[d(e)]=o}return t}),{attrs:{}}),i=o.style,s=void 0===i?{}:i,f=function(t,e){if(null==t)return{};var r,o,n=function(t,e){if(null==t)return{};var r={};for(var o in t)if({}.hasOwnProperty.call(t,o)){if(-1!==e.indexOf(o))continue;r[o]=t[o]}return r}(t,e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);for(o=0;o<a.length;o++)r=a[o],-1===e.indexOf(r)&&{}.propertyIsEnumerable.call(t,r)&&(n[r]=t[r])}return n}(o,x);return a.attrs.style=l(l({},a.attrs.style),s),e.apply(void 0,[r.tag,l(l({},a.attrs),f)].concat(u(n)))}.bind(null,n.default.createElement);exports.FontAwesomeIcon=w;
2
2
  //# sourceMappingURL=index.es.js.map
@@ -1,2 +1,2 @@
1
- "use strict";var e=require("../../../_virtual/index8.js");require("./requestMediaPermissions.js");var r=require("../../../_virtual/requestMediaPermissions.js");!function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.requestMediaPermissions=e.MediaPermissionsErrorType=void 0;var i=r.__exports;Object.defineProperty(e,"MediaPermissionsErrorType",{enumerable:!0,get:function(){return i.MediaPermissionsErrorType}}),Object.defineProperty(e,"requestMediaPermissions",{enumerable:!0,get:function(){return i.requestMediaPermissions}})}(e.__exports);
1
+ "use strict";var e=require("../../../_virtual/index6.js");require("./requestMediaPermissions.js");var r=require("../../../_virtual/requestMediaPermissions.js");!function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.requestMediaPermissions=e.MediaPermissionsErrorType=void 0;var i=r.__exports;Object.defineProperty(e,"MediaPermissionsErrorType",{enumerable:!0,get:function(){return i.MediaPermissionsErrorType}}),Object.defineProperty(e,"requestMediaPermissions",{enumerable:!0,get:function(){return i.requestMediaPermissions}})}(e.__exports);
2
2
  //# sourceMappingURL=index.js.map
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("../../_virtual/index6.js"),r=require("./node_modules/react-is/index.js"),t=require("./factoryWithTypeCheckers.js"),i=require("./factoryWithThrowingShims.js");if("production"!==process.env.NODE_ENV){var s=r.__require();e.__module.exports=t.__require()(s.isElement,!0)}else e.__module.exports=i.__require()();Object.defineProperty(exports,"default",{enumerable:!0,get:function(){return e.exports}});
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("../../_virtual/index7.js"),r=require("./node_modules/react-is/index.js"),t=require("./factoryWithTypeCheckers.js"),i=require("./factoryWithThrowingShims.js");if("production"!==process.env.NODE_ENV){var s=r.__require();e.__module.exports=t.__require()(s.isElement,!0)}else e.__module.exports=i.__require()();Object.defineProperty(exports,"default",{enumerable:!0,get:function(){return e.exports}});
2
2
  //# sourceMappingURL=index.js.map
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,r=require("../../../../_virtual/index7.js"),s=require("./cjs/react-is.production.min.js"),t=require("./cjs/react-is.development.js");exports.__require=function(){return e||(e=1,i=r.__module,"production"===process.env.NODE_ENV?i.exports=s.__require():i.exports=t.__require()),r.exports;var i};
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,r=require("../../../../_virtual/index8.js"),s=require("./cjs/react-is.production.min.js"),t=require("./cjs/react-is.development.js");exports.__require=function(){return e||(e=1,i=r.__module,"production"===process.env.NODE_ENV?i.exports=s.__require():i.exports=t.__require()),r.exports;var i};
2
2
  //# sourceMappingURL=index.js.map