@@ -428,17 +428,27 @@ export default class Plugin {
428428 } ) ;
429429
430430 const connectToCall = async ( channelId : string , teamId ?: string , title ?: string , rootId ?: string ) => {
431- if ( ! channelIDForCurrentCall ( store . getState ( ) ) ) {
431+ const currentCallChannelId = channelIDForCurrentCall ( store . getState ( ) ) ;
432+
433+ // Also check window.callsClient for active call (handles race condition during page load)
434+ const hasActiveClient = Boolean ( window . callsClient ) ;
435+ const activeClientChannel = window . callsClient ?. channelID ;
436+
437+ if ( ! currentCallChannelId && ! hasActiveClient ) {
438+ // Not in any call - join the new one
432439 connectCall ( channelId , title , rootId ) ;
433440
434441 // following the thread only on join. On call start
435442 // this is done in the call_start ws event handler.
436443 if ( channelHasCall ( store . getState ( ) , channelId ) ) {
437444 followThread ( store , channelId , teamId ) ;
438445 }
439- } else if ( channelIDForCurrentCall ( store . getState ( ) ) !== channelId ) {
446+ } else if ( ( currentCallChannelId && currentCallChannelId !== channelId ) || ( activeClientChannel && activeClientChannel !== channelId ) ) {
447+ // In a different call - show switch modal
440448 store . dispatch ( showSwitchCallModal ( channelId ) ) ;
441449 }
450+
451+ // If already in this call, do nothing
442452 } ;
443453
444454 const joinCall = async ( channelId : string , teamId ?: string , title ?: string , rootId ?: string ) => {
@@ -1115,9 +1125,84 @@ export default class Plugin {
11151125 this . registerWebSocketEvents ( registry , store ) ;
11161126
11171127 let currChannelId = getCurrentChannelId ( store . getState ( ) ) ;
1118- let joinCallParam = new URLSearchParams ( window . location . search ) . get ( 'join_call' ) ;
1128+ let processedJoinCallUrl = '' ;
1129+ let pendingJoinChannelId = '' ;
1130+ let lastCheckedUrl = '' ;
1131+
1132+ // Capture join_call parameter immediately at initialization, before React Router can strip it
1133+ // This is essential for pasted URLs where the channel ID isn't loaded in Redux yet
1134+ let initialJoinCallParam = new URLSearchParams ( window . location . search ) . get ( 'join_call' ) ;
1135+
1136+ // Function to check and handle join_call parameter
1137+ const handleJoinCallParam = ( ) => {
1138+ const currentChannelId = getCurrentChannelId ( store . getState ( ) ) ;
1139+ const currentUrl = window . location . href ;
1140+ const joinCallParam = new URLSearchParams ( window . location . search ) . get ( 'join_call' ) ;
1141+
1142+ // Check join_call parameter - only process each unique URL once
1143+ if ( joinCallParam && currentChannelId && currentUrl !== processedJoinCallUrl ) {
1144+ connectToCall ( currentChannelId ) ;
1145+ processedJoinCallUrl = currentUrl ;
1146+ initialJoinCallParam = null ; // Clear captured param since we processed it
1147+ }
1148+ } ;
1149+
1150+ // Intercept clicks on links with join_call parameter BEFORE React Router handles them
1151+ const handleLinkClick = ( e : MouseEvent ) => {
1152+ const target = e . target as HTMLElement ;
1153+ const link = target . closest ( 'a' ) ;
1154+ if ( ! link ) {
1155+ return ;
1156+ }
1157+
1158+ const href = link . getAttribute ( 'href' ) ;
1159+ if ( ! href ) {
1160+ return ;
1161+ }
1162+
1163+ // Check if link contains join_call parameter
1164+ try {
1165+ const url = new URL ( href , window . location . origin ) ;
1166+ if ( url . searchParams . get ( 'join_call' ) === 'true' ) {
1167+ // Extract channel ID from URL
1168+ // URL format: /team-name/channels/channel-id?join_call=true
1169+ const channelMatch = url . pathname . match ( / \/ c h a n n e l s \/ ( [ a - z 0 - 9 ] + ) / i) ;
1170+ if ( channelMatch ) {
1171+ const targetChannelId = channelMatch [ 1 ] ;
1172+ const currentChannelId = getCurrentChannelId ( store . getState ( ) ) ;
1173+
1174+ // If clicking link in same channel, prevent navigation and join directly
1175+ if ( targetChannelId === currentChannelId ) {
1176+ e . preventDefault ( ) ;
1177+ e . stopPropagation ( ) ;
1178+ e . stopImmediatePropagation ( ) ;
1179+
1180+ // Defer connectToCall to next tick to avoid the modal's closeOnBlur handler
1181+ // catching the same click event that triggered showing the modal, which would
1182+ // immediately hide the modal that was just shown
1183+ setTimeout ( ( ) => {
1184+ connectToCall ( targetChannelId ) ;
1185+ } , 0 ) ;
1186+ return ;
1187+ }
1188+
1189+ // Different channel - set pending join and let navigation happen
1190+ // React Router will strip the query param, so we track it here
1191+ pendingJoinChannelId = targetChannelId ;
1192+ }
1193+ }
1194+ } catch {
1195+ // Invalid URL, ignore
1196+ }
1197+ } ;
1198+ document . addEventListener ( 'click' , handleLinkClick , true ) ;
1199+ this . unsubscribers . push ( ( ) => document . removeEventListener ( 'click' , handleLinkClick , true ) ) ;
1200+
1201+ // Also check on Redux store updates (for navigation to different channels)
11191202 this . unsubscribers . push ( store . subscribe ( ( ) => {
11201203 const currentChannelId = getCurrentChannelId ( store . getState ( ) ) ;
1204+
1205+ // Handle channel changes
11211206 if ( currChannelId !== currentChannelId ) {
11221207 const firstLoad = ! currChannelId ;
11231208 currChannelId = currentChannelId ;
@@ -1126,12 +1211,29 @@ export default class Plugin {
11261211 // on every channel switch.
11271212 if ( firstLoad ) {
11281213 registerHeaderMenuComponentIfNeeded ( currentChannelId ) ;
1214+
1215+ // On first load, if we captured a join_call parameter, process it now
1216+ // This handles pasted URLs where the parameter is captured before channel loads
1217+ if ( initialJoinCallParam && currentChannelId ) {
1218+ initialJoinCallParam = null ; // Clear it so we don't process again
1219+ connectToCall ( currentChannelId ) ;
1220+ }
11291221 }
11301222
1131- if ( currChannelId && Boolean ( joinCallParam ) && ! channelIDForCurrentCall ( store . getState ( ) ) ) {
1132- connectCall ( currChannelId ) ;
1223+ // Check if we navigated to a pending join channel
1224+ if ( pendingJoinChannelId && pendingJoinChannelId === currentChannelId ) {
1225+ // Clear the flag immediately - connectToCall() will handle the rest
1226+ // (including showing switch modal if already in a call)
1227+ pendingJoinChannelId = '' ;
1228+ connectToCall ( currentChannelId ) ;
11331229 }
1134- joinCallParam = '' ;
1230+ }
1231+
1232+ // Check for join_call parameter only when URL changes (optimization)
1233+ const currentUrl = window . location . href ;
1234+ if ( currentUrl !== lastCheckedUrl ) {
1235+ lastCheckedUrl = currentUrl ;
1236+ handleJoinCallParam ( ) ;
11351237 }
11361238 } ) ) ;
11371239
0 commit comments