Skip to content

Commit

Permalink
Add preview button in cms, add preview mode in client, update story d…
Browse files Browse the repository at this point in the history
…ata model
  • Loading branch information
barbara-chaves committed Dec 14, 2023
1 parent be6bace commit abb52a6
Show file tree
Hide file tree
Showing 15 changed files with 1,231 additions and 1,821 deletions.
37 changes: 37 additions & 0 deletions client/src/app/api/preview/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// route handler with secret and slug
import { draftMode } from 'next/headers';
import { redirect } from 'next/navigation';

import env from '@/env.mjs';

import { getStoriesId } from '@/types/generated/story';

export async function GET(request: Request) {
// Parse query string parameters
const { searchParams } = new URL(request.url);
const secret = searchParams.get('secret');
const slug = searchParams.get('slug');

// Check the secret and next parameters
// This secret should only be known to this route handler and the CMS
if (secret !== env.NEXT_PUBLIC_PREVIEW_SECRET || !slug) {
return new Response('Invalid token', { status: 401 });
}

// Fetch the headless CMS to check if the provided `slug` exists
// getPostBySlug would implement the required fetching logic to the headless CMS

const story = await getStoriesId(+slug);

// If the slug doesn't exist prevent draft mode from being enabled
if (!story) {
return new Response('Invalid slug', { status: 401 });
}

// Enable Draft Mode by setting the cookie
draftMode().enable();

// Redirect to the path from the fetched story
// We don't redirect to searchParams.slug as that might lead to open redirect vulnerabilities
redirect(`${env.NEXT_PUBLIC_URL}/stories/${slug}`);
}
38 changes: 14 additions & 24 deletions client/src/containers/story/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,13 @@ import { layersAtom, tmpBboxAtom } from '@/store';
import { stepAtom } from '@/store/stories';

import { useGetStoriesId } from '@/types/generated/story';
import { Bbox } from '@/types/map';

import { Button } from '@/components/ui/button';

import Step from './steps';
import { ScrollItemController } from './steps/controller/controller-item';
import { ScrollItem } from './steps/controller/scroll-item';

type StepLocation = {
bbox: Bbox;
zoom: number;
pitch: number;
bearing: number;
padding: {
top: number;
left: number;
right: number;
bottom: number;
};
latitude: number;
longitude: number;
};
import { isMapNotEmpty } from './utils';

const headerButtonClassName =
'rounded-4xl h-auto border-gray-800 bg-[hsl(198,100%,14%)]/75 px-5 py-2.5 hover:bg-gray-800';
Expand All @@ -54,7 +39,7 @@ const Story = () => {
});

const story = useMemo(() => storyData?.data?.attributes, [storyData]);
const steps = useMemo(() => story?.steps?.data || [], [story]);
const steps = useMemo(() => story?.steps || [], [story]);

const handleGoHome = () => {
resetLayers();
Expand All @@ -63,23 +48,28 @@ const Story = () => {

useEffect(() => {
if (!steps) return;
const stepLayout = steps[step]?.attributes?.layout?.[0];
const currStep = steps[step];

// Location
const stepLocation = stepLayout?.map?.location;
if (!currStep || !isMapNotEmpty(currStep.map)) {
return;
}

// Bbox
const stepLocation = currStep?.map.location;
if (stepLocation) {
const { bbox, ...options } = stepLocation as StepLocation;
const { bbox, ...options } = stepLocation;
setTmpBbox({
bbox,
options,
});
}

// Layers
const stepLayers = stepLayout?.layers;
const stepLayers = currStep.layers;
if (stepLayers) {
const _layers: number[] =
stepLayers.data?.reduce(
(acc: number[], layer: { id: number }) => (layer.id ? [...acc, layer.id] : acc),
(acc: number[], layer) => (layer.id ? [...acc, layer.id] : acc),
[]
) || [];
setLayers(_layers);
Expand Down Expand Up @@ -116,7 +106,7 @@ const Story = () => {
)}
key={index}
newStep={index}
title={s.attributes?.layout[0]?.card && s.attributes?.layout[0]?.card[0]?.title}
title=""
/>
))}
</div>
Expand Down
13 changes: 6 additions & 7 deletions client/src/containers/story/steps/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
StepLayoutMediaStepComponentMedia,
StepLayoutOutroStepComponentMedia,
StoryCategory,
StoryStepsDataItem,
StoryStepsItem,
} from '@/types/generated/strapi.schemas';

import MapStepLayout from './layouts/map-step';
Expand All @@ -18,7 +18,7 @@ import { getStepType } from './utils';

type StepProps = PropsWithChildren<{
media?: StepLayoutMediaStepComponentMedia | StepLayoutOutroStepComponentMedia;
step: StoryStepsDataItem;
step: StoryStepsItem;
category?: StoryCategory;
index: number;
}>;
Expand All @@ -28,26 +28,25 @@ const Step = ({ step, category, index }: StepProps) => {

const STEP_COMPONENT = useMemo(() => {
const type = getStepType(step);
const stepLayout = step?.attributes?.layout?.[0];
if (!type || !stepLayout) return null;
if (!type || !step) return null;

switch (type) {
case 'map-step': {
return (
<MapStepLayout
stepIndex={index}
category={category?.data?.attributes}
step={stepLayout}
step={step}
showContent={currentStep === index}
/>
);
}
case 'media-step':
return <MediaStepLayout step={stepLayout} />;
return <MediaStepLayout step={step} />;
case 'outro-step':
return (
<OutroStepLayout
step={stepLayout}
step={step}
showContent={currentStep === index}
categoryId={category?.data?.id}
/>
Expand Down
11 changes: 4 additions & 7 deletions client/src/containers/story/steps/utils.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import {
StepLayoutMediaStepComponentMedia,
StepLayoutOutroStepComponentMedia,
StepListResponseDataItem,
StoryStepsItem,
} from '@/types/generated/strapi.schemas';

export const getStepType = (step: StepListResponseDataItem) => {
return step?.attributes?.layout?.[0]?.__component?.split('.')?.[1];
export const getStepType = (step: StoryStepsItem) => {
return step?.__component?.split('.')?.[1];
};

export const getMedia = (
media?: StepLayoutMediaStepComponentMedia | StepLayoutOutroStepComponentMedia
) => {
export const getMedia = (media?: StepLayoutOutroStepComponentMedia) => {
const url = `${process.env.NEXT_PUBLIC_API_URL?.replace('/api', '')}${
media?.data?.attributes?.url
}`;
Expand Down
10 changes: 10 additions & 0 deletions client/src/containers/story/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { StepLayoutMapStepComponent, StoryStepsItem } from '@/types/generated/strapi.schemas';
import { StoryStepMap } from '@/types/story';

export const isMapStep = (step: StoryStepsItem): step is StepLayoutMapStepComponent => {
return !!step?.__component?.includes('map-step');
};

export const isMapNotEmpty = (map: StoryStepsItem['map']): map is StoryStepMap => {
return Object.values((map as StoryStepMap)?.location).length > 0;
};
2 changes: 2 additions & 0 deletions client/src/env.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const env = createEnv({
NEXT_PUBLIC_MAPBOX_API_TOKEN: z.string(),
NEXT_PUBLIC_GA_TRACKING_ID: z.string().optional(),
NEXT_PUBLIC_BASE_PATH: z.string().optional(),
NEXT_PUBLIC_PREVIEW_SECRET: z.string().optional(),
},
/*
* Due to how Next.js bundles environment variables on Edge and Client,
Expand All @@ -46,6 +47,7 @@ export const env = createEnv({
RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED:
process.env.RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED,
NEXT_PUBLIC_BASE_PATH: process.env.NEXT_PUBLIC_BASE_PATH,
NEXT_PUBLIC_PREVIEW_SECRET: process.env.NEXT_PUBLIC_PREVIEW_SECRET,
},
});

Expand Down
Loading

0 comments on commit abb52a6

Please sign in to comment.