feat(32-03): create JKVideoButton with colocated loading state
- Extracted video button from JKSessionScreen - Local loading state prevents parent re-renders - memo() wrapper for render optimization - Preserves 10-second loading timeout behavior - Handles permission checks via canVideo prop
This commit is contained in:
parent
e35bed21e3
commit
2075989409
|
|
@ -0,0 +1,78 @@
|
|||
import React, { useState, useCallback, memo } from 'react';
|
||||
import { Button, Spinner } from 'reactstrap';
|
||||
import { toast } from 'react-toastify';
|
||||
import PropTypes from 'prop-types';
|
||||
import videoIcon from '../../assets/images/icons8-video-call-50.png';
|
||||
|
||||
/**
|
||||
* Self-contained video button with colocated loading state.
|
||||
* Loading state changes only re-render this component, not the parent.
|
||||
*
|
||||
* State colocation: https://kentcdodds.com/blog/state-colocation-will-make-your-react-app-faster
|
||||
*/
|
||||
const JKVideoButton = memo(({
|
||||
canVideo,
|
||||
getVideoUrl,
|
||||
onUpgradePrompt,
|
||||
className
|
||||
}) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// Open external link in new window/tab
|
||||
const openExternalLink = useCallback((url) => {
|
||||
window.open(url, '_blank', 'noopener,noreferrer');
|
||||
}, []);
|
||||
|
||||
const handleClick = useCallback(async () => {
|
||||
if (!canVideo()) {
|
||||
onUpgradePrompt();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
// Get video conferencing room URL from server
|
||||
const response = await getVideoUrl();
|
||||
const videoUrl = `${response.url}&audiooff=true`;
|
||||
|
||||
// Open video URL in new browser window/tab
|
||||
openExternalLink(videoUrl);
|
||||
|
||||
} catch (error) {
|
||||
toast.error('Failed to start video session');
|
||||
} finally {
|
||||
// Keep loading state for 10 seconds to prevent multiple clicks
|
||||
setTimeout(() => setLoading(false), 10000);
|
||||
}
|
||||
}, [canVideo, getVideoUrl, onUpgradePrompt, openExternalLink]);
|
||||
|
||||
return (
|
||||
<Button
|
||||
className={className || 'btn-custom-outline'}
|
||||
outline
|
||||
size="md"
|
||||
onClick={handleClick}
|
||||
disabled={loading}
|
||||
>
|
||||
<img
|
||||
src={videoIcon}
|
||||
alt="Video"
|
||||
style={{ width: '20px', height: '20px', marginRight: '0.3rem' }}
|
||||
/>
|
||||
{loading && <Spinner size="sm" />}
|
||||
Video
|
||||
</Button>
|
||||
);
|
||||
});
|
||||
|
||||
JKVideoButton.displayName = 'JKVideoButton';
|
||||
|
||||
JKVideoButton.propTypes = {
|
||||
canVideo: PropTypes.func.isRequired,
|
||||
getVideoUrl: PropTypes.func.isRequired,
|
||||
onUpgradePrompt: PropTypes.func.isRequired,
|
||||
className: PropTypes.string
|
||||
};
|
||||
|
||||
export default JKVideoButton;
|
||||
Loading…
Reference in New Issue