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