157 lines
4.7 KiB
TypeScript
157 lines
4.7 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useRef, useState } from 'react';
|
|
import { useSession } from 'next-auth/react';
|
|
|
|
interface ResponsiveIframeProps {
|
|
src: string;
|
|
className?: string;
|
|
allow?: string;
|
|
style?: React.CSSProperties;
|
|
token?: string;
|
|
}
|
|
|
|
export function ResponsiveIframe({ src, className = '', allow, style, token }: ResponsiveIframeProps) {
|
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
const { data: session } = useSession();
|
|
const [authError, setAuthError] = useState<string | null>(null);
|
|
|
|
// Add token parameter only if token is provided
|
|
const fullSrc = token ?
|
|
`${src}${src.includes('?') ? '&' : '?'}token=${encodeURIComponent(token)}` :
|
|
src;
|
|
|
|
// Handle silent authentication refresh
|
|
useEffect(() => {
|
|
let silentRefreshTimer: NodeJS.Timeout;
|
|
|
|
// Set up periodic silent refresh (every 15 minutes)
|
|
const startSilentRefresh = () => {
|
|
silentRefreshTimer = setInterval(() => {
|
|
console.log('Performing silent authentication check for iframes');
|
|
|
|
// Create a hidden iframe for silent authentication
|
|
const refreshFrame = document.createElement('iframe');
|
|
refreshFrame.style.display = 'none';
|
|
refreshFrame.src = '/silent-refresh';
|
|
document.body.appendChild(refreshFrame);
|
|
|
|
// Remove iframe after it has loaded (5 seconds timeout)
|
|
setTimeout(() => {
|
|
if (refreshFrame && refreshFrame.parentNode) {
|
|
refreshFrame.parentNode.removeChild(refreshFrame);
|
|
}
|
|
}, 5000);
|
|
}, 15 * 60 * 1000); // 15 minutes
|
|
};
|
|
|
|
if (session) {
|
|
startSilentRefresh();
|
|
}
|
|
|
|
return () => {
|
|
if (silentRefreshTimer) {
|
|
clearInterval(silentRefreshTimer);
|
|
}
|
|
};
|
|
}, [session]);
|
|
|
|
useEffect(() => {
|
|
const iframe = iframeRef.current;
|
|
if (!iframe) return;
|
|
|
|
const calculateHeight = () => {
|
|
const pageY = (elem: HTMLElement): number => {
|
|
return elem.offsetParent ?
|
|
(elem.offsetTop + pageY(elem.offsetParent as HTMLElement)) :
|
|
elem.offsetTop;
|
|
};
|
|
|
|
const height = document.documentElement.clientHeight;
|
|
const iframeY = pageY(iframe);
|
|
const newHeight = Math.max(0, height - iframeY);
|
|
iframe.style.height = `${newHeight}px`;
|
|
};
|
|
|
|
const handleHashChange = () => {
|
|
if (window.location.hash && window.location.hash.length) {
|
|
const iframeURL = new URL(iframe.src);
|
|
iframeURL.hash = window.location.hash;
|
|
iframe.src = iframeURL.toString();
|
|
}
|
|
};
|
|
|
|
// Handle authentication messages from iframe
|
|
const handleMessage = (event: MessageEvent) => {
|
|
// Accept messages from our iframe or from silent auth iframe
|
|
if (event.source !== iframe.contentWindow &&
|
|
!event.data?.type?.startsWith('SILENT_AUTH_')) return;
|
|
|
|
const { type, data, error } = event.data || {};
|
|
|
|
// Handle auth-related messages
|
|
if (type === 'AUTH_ERROR' || type === 'SESSION_EXPIRED') {
|
|
console.log('Auth error in iframe:', data || error);
|
|
setAuthError(error || 'Authentication error');
|
|
} else if (type === 'SILENT_AUTH_SUCCESS') {
|
|
console.log('Silent authentication successful');
|
|
setAuthError(null);
|
|
} else if (type === 'SILENT_AUTH_FAILURE') {
|
|
console.log('Silent authentication failed:', error);
|
|
// Only set error if it's persistent
|
|
if (error !== 'loading') {
|
|
setAuthError('Session expired');
|
|
}
|
|
}
|
|
};
|
|
|
|
// Initial setup
|
|
calculateHeight();
|
|
handleHashChange();
|
|
|
|
// Event listeners
|
|
window.addEventListener('resize', calculateHeight);
|
|
window.addEventListener('hashchange', handleHashChange);
|
|
window.addEventListener('message', handleMessage);
|
|
iframe.addEventListener('load', calculateHeight);
|
|
|
|
// Cleanup
|
|
return () => {
|
|
window.removeEventListener('resize', calculateHeight);
|
|
window.removeEventListener('hashchange', handleHashChange);
|
|
window.removeEventListener('message', handleMessage);
|
|
iframe.removeEventListener('load', calculateHeight);
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<>
|
|
{authError && (
|
|
<div
|
|
style={{
|
|
backgroundColor: 'rgba(255, 0, 0, 0.1)',
|
|
padding: '10px',
|
|
borderRadius: '4px',
|
|
marginBottom: '10px'
|
|
}}
|
|
>
|
|
Authentication error: {authError}. The service might not work correctly.
|
|
</div>
|
|
)}
|
|
<iframe
|
|
ref={iframeRef}
|
|
id="myFrame"
|
|
src={fullSrc}
|
|
className={`w-full border-none ${className}`}
|
|
style={{
|
|
display: 'block',
|
|
width: '100%',
|
|
height: '100%',
|
|
...style
|
|
}}
|
|
allow={allow}
|
|
allowFullScreen
|
|
/>
|
|
</>
|
|
);
|
|
}
|