import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html lang="bg">
<Head>
{/* Removed Google Fonts preconnects and links since we're now using next/font/google */}
{/* Preload critical custom font */}
<link
rel="preload"
href="/custom-font.woff2"
as="font"
type="font/woff2"
crossOrigin="anonymous"
/>
{/* Optimize for back/forward cache */}
<meta name="bfcache-opt-in" content="true" />
</Head>
<body>
<Main />
<NextScript />
{/* Comprehensive bfcache fixes */}
<script
dangerouslySetInnerHTML={{
__html: `
// Fix for back/forward cache issues
(function() {
// 1. Handle BroadcastChannel
if (typeof BroadcastChannel !== 'undefined') {
// Store the original BroadcastChannel constructor
const OriginalBroadcastChannel = window.BroadcastChannel;
// Track active channels for cleanup
let activeChannels = [];
// Wrap the constructor to ensure channels are closed on page hide
window.BroadcastChannel = function(name) {
// Create a deferred instance that only initializes when needed
let instance = null;
let listeners = [];
// Create a proxy that defers initialization until the channel is actually used
const proxy = new Proxy({}, {
get: function(target, prop) {
if (prop === 'postMessage') {
// Initialize on first use
if (!instance) {
instance = new OriginalBroadcastChannel(name);
activeChannels.push(instance);
}
return instance.postMessage.bind(instance);
}
if (prop === 'addEventListener') {
return function(type, listener) {
if (!instance) {
instance = new OriginalBroadcastChannel(name);
activeChannels.push(instance);
}
listeners.push({ type, listener });
instance.addEventListener(type, listener);
};
}
if (prop === 'removeEventListener') {
return function(type, listener) {
if (instance) {
instance.removeEventListener(type, listener);
listeners = listeners.filter(l => l.type !== type || l.listener !== listener);
// Close if no more listeners
if (listeners.length === 0) {
instance.close();
activeChannels = activeChannels.filter(c => c !== instance);
instance = null;
}
}
};
}
if (prop === 'close') {
return function() {
if (instance) {
instance.close();
activeChannels = activeChannels.filter(c => c !== instance);
instance = null;
listeners = [];
}
};
}
// For any other property, initialize if needed
if (!instance) {
instance = new OriginalBroadcastChannel(name);
activeChannels.push(instance);
}
return instance[prop];
}
});
return proxy;
};
// Clean up all channels on page hide
window.addEventListener('pagehide', function() {
activeChannels.forEach(channel => {
if (channel && typeof channel.close === 'function') {
channel.close();
}
});
activeChannels = [];
});
}
// 2. Handle WebLocks
if (window.navigator && navigator.locks) {
try {
// Create a non-functional locks API that doesn't interfere with bfcache
const dummyLocks = {
request: function(name, options, callback) {
// Handle different parameter patterns
if (typeof options === 'function') {
callback = options;
options = {};
}
// Call callback immediately with dummy lock
if (typeof callback === 'function') {
Promise.resolve().then(() => {
callback({ name: 'dummy-lock' });
});
}
return Promise.resolve();
},
query: function() {
return Promise.resolve({ held: [] });
}
};
// Replace the locks API
Object.defineProperty(navigator, 'locks', {
configurable: true,
enumerable: true,
get: function() {
return dummyLocks;
}
});
} catch (e) {
console.warn('Failed to patch WebLocks API:', e);
}
}
// 3. Cleanup any other problematic resources on page hide
const cleanupOnPageHide = function() {
// Close any open IndexedDB connections
if (window.indexedDB && indexedDB.databases) {
try {
indexedDB.databases().then(databases => {
databases.forEach(database => {
try {
indexedDB.deleteDatabase(database.name);
} catch (e) {
// Ignore errors
}
});
}).catch(() => {
// Ignore errors
});
} catch (e) {
// Ignore errors
}
}
// Cancel any pending fetch requests
if (window.AbortController && typeof AbortController.prototype.abort === 'function') {
if (window._pendingFetches) {
window._pendingFetches.forEach(controller => {
try {
controller.abort();
} catch (e) {
// Ignore errors
}
});
window._pendingFetches = [];
}
}
// Attempt to release any MediaStream tracks
if (navigator.mediaDevices) {
if (window._activeMediaStreams) {
window._activeMediaStreams.forEach(stream => {
try {
stream.getTracks().forEach(track => track.stop());
} catch (e) {
// Ignore errors
}
});
window._activeMediaStreams = [];
}
}
};
// Register cleanup for back/forward cache
window.addEventListener('pagehide', cleanupOnPageHide);
// Track fetch requests to ensure they can be aborted
if (window.fetch && window.AbortController) {
const originalFetch = window.fetch;
window._pendingFetches = window._pendingFetches || [];
window.fetch = function(...args) {
const controller = new AbortController();
const signal = controller.signal;
window._pendingFetches.push(controller);
// Add signal to request if not already present
if (args[1] && typeof args[1] === 'object') {
args[1] = { ...args[1], signal };
} else {
args[1] = { signal };
}
return originalFetch(...args).finally(() => {
// Remove from pending fetches on completion
window._pendingFetches = window._pendingFetches.filter(c => c !== controller);
});
};
}
// Track MediaStreams
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
const originalGetUserMedia = navigator.mediaDevices.getUserMedia;
window._activeMediaStreams = window._activeMediaStreams || [];
navigator.mediaDevices.getUserMedia = function(...args) {
return originalGetUserMedia.apply(this, args).then(stream => {
window._activeMediaStreams.push(stream);
return stream;
});
};
}
// Signal to browsers that we've optimized for bfcache
if (document.createEvent) {
try {
// Create and dispatch a custom event to indicate bfcache support
const event = document.createEvent('CustomEvent');
event.initCustomEvent('BFCacheOptimized', true, true, {
optimized: true,
timestamp: Date.now()
});
document.dispatchEvent(event);
} catch (e) {
// Ignore errors
}
}
})();
`
}}
/>
</body>
</Html>
)
}