refactor(32-03): use JKResyncButton and JKVideoButton in JKSessionScreen

- Replaced inline video button with JKVideoButton component
- Replaced inline resync button with JKResyncButton component
- Removed videoLoading and resyncLoading state from parent
- Removed handleVideoClick and handleResync functions
- Removed unused videoIcon and resyncIcon imports
- Loading state changes no longer trigger parent re-renders
This commit is contained in:
Nuwan 2026-03-05 19:39:56 +05:30
parent 98e5a5ac15
commit f5bd1b1cd5
3 changed files with 32 additions and 123 deletions

View File

@ -12,33 +12,30 @@ import PropTypes from 'prop-types';
const JKResyncButton = memo(({ resyncAudio, className }) => { const JKResyncButton = memo(({ resyncAudio, className }) => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const handleClick = useCallback(async (e) => { const handleClick = useCallback(
e.preventDefault(); async e => {
if (loading) return; e.preventDefault();
if (loading) return;
setLoading(true); setLoading(true);
try { try {
await resyncAudio(); await resyncAudio();
// Silent success (matches legacy behavior) // Silent success (matches legacy behavior)
} catch (error) { } catch (error) {
if (error.message === 'timeout') { if (error.message === 'timeout') {
toast.error('Audio resync timed out. Please try again.'); toast.error('Audio resync timed out. Please try again.');
} else { } else {
toast.error('Audio resync failed: ' + (error.message || 'Unknown error')); toast.error('Audio resync failed: ' + (error.message || 'Unknown error'));
}
} finally {
setLoading(false);
} }
} finally { },
setLoading(false); [resyncAudio, loading]
} );
}, [resyncAudio, loading]);
return ( return (
<Button <Button className={className || 'btn-custom-outline'} outline size="md" onClick={handleClick} disabled={loading}>
className={className || 'btn-custom-outline'}
outline
size="md"
onClick={handleClick}
disabled={loading}
>
{loading ? ( {loading ? (
<> <>
<Spinner size="sm" /> Resyncing... <Spinner size="sm" /> Resyncing...

View File

@ -131,6 +131,8 @@ import JKSessionMetronomePlayer from './JKSessionMetronomePlayer.js';
import JKSessionChatWindow from './JKSessionChatWindow.js'; import JKSessionChatWindow from './JKSessionChatWindow.js';
import JKSessionChatButton from './JKSessionChatButton.js'; import JKSessionChatButton from './JKSessionChatButton.js';
import JKPopupMediaControls from '../popups/JKPopupMediaControls.js'; import JKPopupMediaControls from '../popups/JKPopupMediaControls.js';
import JKResyncButton from './JKResyncButton.js';
import JKVideoButton from './JKVideoButton.js';
import { SESSION_PRIVACY_MAP } from '../../helpers/globals.js'; import { SESSION_PRIVACY_MAP } from '../../helpers/globals.js';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@ -139,13 +141,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import gearIcon from '../../assets/img/client/gear.svg'; import gearIcon from '../../assets/img/client/gear.svg';
import inviteIcon from '../../assets/img/client/invite.svg'; import inviteIcon from '../../assets/img/client/invite.svg';
import volumeIcon from '../../assets/img/client/volume.svg'; import volumeIcon from '../../assets/img/client/volume.svg';
import videoIcon from '../../assets/img/client/video.svg';
import recordIcon from '../../assets/img/client/record.svg'; import recordIcon from '../../assets/img/client/record.svg';
import broadcastIcon from '../../assets/img/client/broadcast.svg'; import broadcastIcon from '../../assets/img/client/broadcast.svg';
import openIcon from '../../assets/img/client/open.svg'; import openIcon from '../../assets/img/client/open.svg';
import chatIcon from '../../assets/img/client/chat.svg'; import chatIcon from '../../assets/img/client/chat.svg';
import attachIcon from '../../assets/img/client/attach.svg'; import attachIcon from '../../assets/img/client/attach.svg';
import resyncIcon from '../../assets/img/client/resync.svg';
import helpIcon from '../../assets/img/client/help.svg'; import helpIcon from '../../assets/img/client/help.svg';
const JKSessionScreen = () => { const JKSessionScreen = () => {
@ -247,12 +247,6 @@ const JKSessionScreen = () => {
const [leaveComments, setLeaveComments] = useState(''); const [leaveComments, setLeaveComments] = useState('');
const [leaveLoading, setLeaveLoading] = useState(false); const [leaveLoading, setLeaveLoading] = useState(false);
//state for video button
const [videoLoading, setVideoLoading] = useState(false);
// State for resync button
const [resyncLoading, setResyncLoading] = useState(false);
// Redux backing track state (modal visibility and data) // Redux backing track state (modal visibility and data)
const backingTrackData = useSelector(selectBackingTrackData); const backingTrackData = useSelector(selectBackingTrackData);
const showBackingTrackPlayer = Boolean(backingTrackData); const showBackingTrackPlayer = Boolean(backingTrackData);
@ -1017,39 +1011,6 @@ const JKSessionScreen = () => {
return currentSession?.can_use_video || false; return currentSession?.can_use_video || false;
}; };
// Open external link in new window/tab
const openExternalLink = url => {
window.open(url, '_blank', 'noopener,noreferrer');
};
// Handle video button click - opens new video conferencing server
const handleVideoClick = async () => {
if (!canVideo()) {
// Show upgrade modal/banner
showVideoUpgradePrompt();
return;
}
try {
setVideoLoading(true);
// Get video conferencing room URL from server
const response = await getVideoConferencingRoomUrl(currentSession.id);
const videoUrl = `${response.url}&audiooff=true`;
// Open video URL in new browser window/tab
// console.debug("JKSessionScreen: Opening video conferencing URL", videoUrl);
openExternalLink(videoUrl);
} catch (error) {
// console.error('Failed to get video room URL:', error);
// Handle error - could show error message to user
toast.error('Failed to start video session');
} finally {
// Keep loading state for 10 seconds to prevent multiple clicks
setTimeout(() => setVideoLoading(false), 10000);
}
};
// Show upgrade prompt for users without video permissions // Show upgrade prompt for users without video permissions
const showVideoUpgradePrompt = () => { const showVideoUpgradePrompt = () => {
// Implementation for showing upgrade modal/banner // Implementation for showing upgrade modal/banner
@ -1104,30 +1065,6 @@ const JKSessionScreen = () => {
} }
}; };
// Handle Resync button click - performs audio resync via native client
const handleResync = useCallback(
async e => {
e.preventDefault();
if (resyncLoading) return;
setResyncLoading(true);
try {
await resyncAudio();
// Silent success (matches legacy behavior)
} catch (error) {
// console.error('Audio resync failed:', error);
if (error.message === 'timeout') {
toast.error('Audio resync timed out. Please try again.');
} else {
toast.error('Audio resync failed: ' + (error.message || 'Unknown error'));
}
} finally {
setResyncLoading(false);
}
},
[resyncAudio, resyncLoading]
);
// Attach button handlers // Attach button handlers
const handleAttachClick = useCallback(() => { const handleAttachClick = useCallback(() => {
if (attachFileInputRef.current) { if (attachFileInputRef.current) {
@ -1407,11 +1344,11 @@ const JKSessionScreen = () => {
<img src={volumeIcon} alt="Volume" style={{ width: '20px', height: '20px', marginRight: '0.3rem' }} /> <img src={volumeIcon} alt="Volume" style={{ width: '20px', height: '20px', marginRight: '0.3rem' }} />
Volume Volume
</Button> </Button>
<Button className="btn-custom-outline" outline size="md" onClick={handleVideoClick} disabled={videoLoading}> <JKVideoButton
<img src={videoIcon} alt="Video" style={{ width: '20px', height: '20px', marginRight: '0.3rem' }} /> canVideo={canVideo}
{videoLoading && <Spinner size="sm" />} getVideoUrl={() => getVideoConferencingRoomUrl(currentSession.id)}
&nbsp;Video onUpgradePrompt={showVideoUpgradePrompt}
</Button> />
<Button <Button
className={currentlyRecording ? 'btn-recording-active' : 'btn-custom-outline'} className={currentlyRecording ? 'btn-recording-active' : 'btn-custom-outline'}
color={currentlyRecording ? 'danger' : undefined} color={currentlyRecording ? 'danger' : undefined}
@ -1447,16 +1384,7 @@ const JKSessionScreen = () => {
<img src={attachIcon} alt="Attach" style={{ width: '20px', height: '20px', marginRight: '0.3rem' }} /> <img src={attachIcon} alt="Attach" style={{ width: '20px', height: '20px', marginRight: '0.3rem' }} />
{isUploading ? 'Uploading...' : 'Attach'} {isUploading ? 'Uploading...' : 'Attach'}
</Button> </Button>
<Button className="btn-custom-outline" outline size="md" onClick={handleResync} disabled={resyncLoading}> <JKResyncButton resyncAudio={resyncAudio} />
<img src={resyncIcon} alt="Resync" style={{ width: '20px', height: '20px', marginRight: '0.3rem' }} />
{resyncLoading ? (
<>
<Spinner size="sm" /> Resyncing...
</>
) : (
'Resync'
)}
</Button>
</div> </div>
</CardHeader> </CardHeader>

View File

@ -10,16 +10,11 @@ import videoIcon from '../../assets/images/icons8-video-call-50.png';
* *
* State colocation: https://kentcdodds.com/blog/state-colocation-will-make-your-react-app-faster * State colocation: https://kentcdodds.com/blog/state-colocation-will-make-your-react-app-faster
*/ */
const JKVideoButton = memo(({ const JKVideoButton = memo(({ canVideo, getVideoUrl, onUpgradePrompt, className }) => {
canVideo,
getVideoUrl,
onUpgradePrompt,
className
}) => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
// Open external link in new window/tab // Open external link in new window/tab
const openExternalLink = useCallback((url) => { const openExternalLink = useCallback(url => {
window.open(url, '_blank', 'noopener,noreferrer'); window.open(url, '_blank', 'noopener,noreferrer');
}, []); }, []);
@ -38,7 +33,6 @@ const JKVideoButton = memo(({
// Open video URL in new browser window/tab // Open video URL in new browser window/tab
openExternalLink(videoUrl); openExternalLink(videoUrl);
} catch (error) { } catch (error) {
toast.error('Failed to start video session'); toast.error('Failed to start video session');
} finally { } finally {
@ -48,18 +42,8 @@ const JKVideoButton = memo(({
}, [canVideo, getVideoUrl, onUpgradePrompt, openExternalLink]); }, [canVideo, getVideoUrl, onUpgradePrompt, openExternalLink]);
return ( return (
<Button <Button className={className || 'btn-custom-outline'} outline size="md" onClick={handleClick} disabled={loading}>
className={className || 'btn-custom-outline'} <img src={videoIcon} alt="Video" style={{ width: '20px', height: '20px', marginRight: '0.3rem' }} />
outline
size="md"
onClick={handleClick}
disabled={loading}
>
<img
src={videoIcon}
alt="Video"
style={{ width: '20px', height: '20px', marginRight: '0.3rem' }}
/>
{loading && <Spinner size="sm" />} {loading && <Spinner size="sm" />}
&nbsp;Video &nbsp;Video
</Button> </Button>