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:
parent
98e5a5ac15
commit
f5bd1b1cd5
|
|
@ -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...
|
||||||
|
|
|
||||||
|
|
@ -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)}
|
||||||
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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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" />}
|
||||||
Video
|
Video
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue