Closed CodeKarl closed 1 month ago
I believe at present the developer is responsible for revoking the draft mode cookie, see: https://github.com/payloadcms/payload/blob/ea48cfbfe9acabaf75c7adb008ce15f8103744a1/templates/website/src/app/next/exit-preview/route.ts
I ended up creating a banner that displays on my page but not within the livepreview iframe when a user has the draft mode cookie active:
const onExitPreview = () => {
fetch("/exit-preview", {
method: "GET",
}).then(() => {
window.location.reload();
});
};
export const ExitPreviewBanner = () => {
const [showBanner, setShowBanner] = useState<boolean>(false);
useEffect(() => {
// Prevent hydration error in SSR
if (typeof window === "undefined" || window.frameElement) {
setShowBanner(false);
} else {
setShowBanner(true);
}
}, []);
if (!showBanner) {
return null;
}
return (
// ...
<button onClick={onExitPreview}>
Exit Preview
</button>
// ...
)
onExitPreview
Thank for the response :)
I found a solution based on what you mentioned:
components/AdminBar/index.tsx
'use client'
import type { PayloadAdminBarProps, PayloadMeUser } from 'payload-admin-bar'
import React, { useEffect, useState } from 'react'
const CMS_URL = process.env.NEXT_PUBLIC_SERVER_URL || 'http://localhost:8000';
const API_PATH = '/api';
const ADMIN_PATH = '/admin';
export const AdminBar: React.FC<{
adminBarProps?: PayloadAdminBarProps
}> = (props) => {
const [showDraftPreview, setShowDraftPreview] = useState(false)
const [draftUser, setDraftUser] = useState<PayloadMeUser | undefined>(undefined);
const [isExitingDraftMode, setIsExitingDraftMode] = useState(false)
async function fetchDraftPreviewUser() {
try {
const draftModeResponse = await fetch('/next/check-draft-mode', { method: 'GET' });
if (!draftModeResponse.ok) {
throw new Error('Failed to check draft mode');
}
const { isDraft } = await draftModeResponse.json();
if (!isDraft) {
setShowDraftPreview(false);
setDraftUser(undefined);
// Exit early if not in draft mode as there is no need to fetch the user
return;
}
const userResponse = await fetch(`${CMS_URL}${API_PATH}/users/me`, {
method: 'GET',
credentials: 'include',
});
if (!userResponse.ok) {
throw new Error('Failed to fetch user');
}
const { user } = await userResponse.json();
if (user) {
setDraftUser(user);
setShowDraftPreview(true);
} else {
setDraftUser(undefined);
setShowDraftPreview(false);
}
} catch (error) {
console.error('Error fetching draft preview user:', error);
setShowDraftPreview(false);
}
}
useEffect(() => {
if (!isExitingDraftMode) fetchDraftPreviewUser();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
CMS_URL,
ADMIN_PATH,
API_PATH,
])
async function onPreviewExit() {
try {
setIsExitingDraftMode(true);
const response = await fetch('/next/exit-preview', { method: 'GET' });
if (!response.ok) {
throw new Error('Failed to exit preview');
}
// Hide the admin bar as you have exited draft mode
setShowDraftPreview(false);
// Reload the page to display the non-draft content
window.location.reload();
} catch (error) {
console.error('Failed to exit preview:', error);
} finally {
setIsExitingDraftMode(false);
}
}
if (!showDraftPreview) return null;
return (
<>
<div className='z-50 fixed right-4 bottom-4 rounded-full px-4 py-2 shadow-[rgba(0,_0,_0,_0.25)_0px_16px_32px_0px]'>
<div
className='bg-[#1C1C1E] rounded-full absolute inset-0'
/>
<div className='relative flex gap-2 items-center'>
{isExitingDraftMode ? (
<>
<span className='loading loading-spinner loading-sm' />
<span className='text-white'>
Exiting Preview Mode...
</span>
</>
) : (
<>
<span className='bg-yellow-500 aspect-square h-3 rounded-full animate-pulse' />
<span className='text-white'>
Preview Mode{draftUser ? ` as ${draftUser.email}` : ''}
</span>
<button onClick={onPreviewExit}>
X
</button>
</>
)}
</div>
</div>
</>
)
}
In addition to the exit-preview
-route I also added a check-draft-mode
-route.
This one just return if draft mode is active or not. As this cannot be done in the client-component of AdminBar
. Then this is used to show the preview overlay or not.
next/check-draft-mode/route.ts
:
import { draftMode } from 'next/headers'
export async function GET(): Promise<Response> {
const { isEnabled: isDraft } = draftMode()
return new Response(JSON.stringify({ isDraft }))
}
This code successfully disables the preview mode, no longer shows the preview overlay, returns the live page data instead of showing the draft version, and also keeps the admin user logged into payload cms in any other tab.
You can then re-enable draft mode by pressing the preview button in the payload cms admin panel.
This issue has been automatically locked. Please open a new issue if this issue persists with any additional detail.
Link to reproduction
https://demo.payloadcms.com/
Describe the Bug
Once having pressed the
Preview
-button on aDraft
- orChanged
-statusPage
-Collection item. You go to the frontend to view it inDraft Preview
mode.There is then no way to return to the normal frontend in the unedited state.
Opening a new tab and going to the frontend again will also trigger the Preview mode now, making the published version inaccessible.
Is there any way to clear the
Draft Preview
-mode in the frontend to return to the current state?I did notice I can change the history to the currently published version, but this will not clear the admin bar or other pages in Draft mode.
To Reproduce
Payload Version
Seen in the 2.0 demo + The 3.0 beta Website Example Demo
Adapters and Plugins
No response