Files
frontend-app-authoring/src/generic/clipboard/hooks/useCopyToClipboard.js

82 lines
3.2 KiB
JavaScript

// @ts-check
import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getClipboard } from '../../data/api';
import { updateClipboardData } from '../../data/slice';
import { CLIPBOARD_STATUS, STRUCTURAL_XBLOCK_TYPES, STUDIO_CLIPBOARD_CHANNEL } from '../../../constants';
import { getClipboardData } from '../../data/selectors';
/**
* Custom React hook for managing clipboard functionality.
*
* @param {boolean} canEdit - Flag indicating whether the clipboard is editable.
* @returns {Object} - An object containing state variables and functions related to clipboard functionality.
* @property {boolean} showPasteUnit - Flag indicating whether the "Paste Unit" button should be visible.
* @property {boolean} showPasteXBlock - Flag indicating whether the "Paste XBlock" button should be visible.
* @property {Object} sharedClipboardData - The shared clipboard data object.
*/
const useCopyToClipboard = (canEdit = true) => {
const dispatch = useDispatch();
const [clipboardBroadcastChannel] = useState(() => new BroadcastChannel(STUDIO_CLIPBOARD_CHANNEL));
const [showPasteUnit, setShowPasteUnit] = useState(false);
const [showPasteXBlock, setShowPasteXBlock] = useState(false);
const [sharedClipboardData, setSharedClipboardData] = useState({});
const clipboardData = useSelector(getClipboardData);
// Function to refresh the paste button's visibility
const refreshPasteButton = (data) => {
const isPasteable = canEdit && data?.content && data.content.status !== CLIPBOARD_STATUS.expired;
const isPasteableXBlock = isPasteable && !STRUCTURAL_XBLOCK_TYPES.includes(data.content.blockType);
const isPasteableUnit = isPasteable && data.content.blockType === 'vertical';
setShowPasteXBlock(!!isPasteableXBlock);
setShowPasteUnit(!!isPasteableUnit);
};
// Called on initial render to fetch and populate the initial clipboard data in redux state.
// Without this, the initial clipboard data redux state is always null.
useEffect(() => {
const fetchInitialClipboardData = async () => {
try {
const userClipboard = await getClipboard();
dispatch(updateClipboardData(userClipboard));
} catch (error) {
// eslint-disable-next-line no-console
console.error(`Failed to fetch initial clipboard data: ${error}`);
}
};
fetchInitialClipboardData();
}, [dispatch]);
useEffect(() => {
// Handle updates to clipboard data
if (canEdit) {
refreshPasteButton(clipboardData);
setSharedClipboardData(clipboardData);
clipboardBroadcastChannel.postMessage(clipboardData);
} else {
setShowPasteXBlock(false);
setShowPasteUnit(false);
}
}, [clipboardData, canEdit, clipboardBroadcastChannel]);
useEffect(() => {
// Handle messages from the broadcast channel
clipboardBroadcastChannel.onmessage = (event) => {
setSharedClipboardData(event.data);
refreshPasteButton(event.data);
};
// Cleanup function for the BroadcastChannel when the hook is unmounted
return () => {
clipboardBroadcastChannel.close();
};
}, [clipboardBroadcastChannel]);
return { showPasteUnit, showPasteXBlock, sharedClipboardData };
};
export default useCopyToClipboard;