Skip to content

Commit

Permalink
✨ preview mode
Browse files Browse the repository at this point in the history
  • Loading branch information
juliuxu committed Jun 22, 2023
1 parent 8e93abc commit 0c06498
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 25 deletions.
3 changes: 0 additions & 3 deletions apps/nye-julianjark.no/app/components/preview-mode.tsx

This file was deleted.

9 changes: 0 additions & 9 deletions apps/nye-julianjark.no/app/is-preview-mode.server.ts

This file was deleted.

27 changes: 25 additions & 2 deletions apps/nye-julianjark.no/app/root.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { cssBundleHref } from "@remix-run/css-bundle";
import { type LinksFunction } from "@remix-run/node";
import type { LoaderArgs } from "@remix-run/node";
import { json, type LinksFunction } from "@remix-run/node";
import {
Link,
Links,
Expand All @@ -19,7 +20,12 @@ import backSvg from "~/assets/back.svg";
import julianFace from "~/assets/julian-face.svg";
import { ClearCacheButton } from "./routes/api.clear-cache";
import { classes } from "~/routes/$notionPage/notion-driven-page";
import { classNames } from "./misc";
import { classNames } from "~/misc";
import {
getPreviewMode,
PreviewMode,
serializePreviewModeToCookie,
} from "~/routes/api.preview-mode";

export const links: LinksFunction = () => [
...(cssBundleHref ? [{ rel: "stylesheet", href: cssBundleHref }] : []),
Expand All @@ -38,6 +44,22 @@ export const links: LinksFunction = () => [
},
];

export const loader = async ({ request }: LoaderArgs) => {
const previewMode = await getPreviewMode(request);
const headers = previewMode
? { "Set-Cookie": await serializePreviewModeToCookie(previewMode) }
: { "Set-Cookie": "" };

return json(
{
previewMode,
},
{
headers,
}
);
};

// TODO: This might belong in the tailwind config
export const sharedClasses /*tw*/ = {
container: "pl-[7.5vw] pr-[7.5vw]",
Expand Down Expand Up @@ -102,6 +124,7 @@ function Footer() {
* Had this been a bigger site, with more people involved I could put the content in CMS/Notion instead
* */}
<footer className="flex flex-row gap-6">
<PreviewMode />
<ClearCacheButton>
<img src={julianFace} alt="Illustrajon av fjeset til Julian" />
</ClearCacheButton>
Expand Down
7 changes: 2 additions & 5 deletions apps/nye-julianjark.no/app/routes/$notionPage/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@ import { NotionPage } from "~/routes/$notionPage/notion-driven-page";
import { getNotionDrivenPageWithBlocks } from "./client";
import { config } from "~/config.server";
import { getCustomBlocksData } from "./custom-blocks.server";
import { useHydrated } from "~/components/use-hydrated";
import { slugify } from "@julianjark/notion-utils";
import { useMemo } from "react";
import { TableOfConents } from "~/components/table-of-contents";
import { isPreviewMode } from "~/is-preview-mode.server";
import { isPreviewMode } from "~/routes/api.preview-mode";

export const meta: V2_MetaFunction<typeof loader> = ({ data }) => {
return [
Expand All @@ -26,7 +23,7 @@ export const loader = async ({

const page = await getNotionDrivenPageWithBlocks(
notionPage,
isPreviewMode(request)
await isPreviewMode(request)
);
if (!page) throw new Response(null, { status: 404 });

Expand Down
4 changes: 2 additions & 2 deletions apps/nye-julianjark.no/app/routes/_index/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getLatestTodayILearnedEntries } from "~/service/notion-today-i-learned/
import { NotionPage } from "~/routes/$notionPage/notion-driven-page";
import { getNotionDrivenLandingPage } from "../$notionPage/client";
import { getFeaturedProject } from "~/service/notion-projects/client";
import { isPreviewMode } from "~/is-preview-mode.server";
import { isPreviewMode } from "~/routes/api.preview-mode";

export const meta: V2_MetaFunction<typeof loader> = ({ data }) => {
return [
Expand All @@ -20,7 +20,7 @@ export const loader = async ({ request }: LoaderArgs) => {
[
getNotionDrivenLandingPage(),
getFeaturedProject(),
getLatestTodayILearnedEntries(isPreviewMode(request)),
getLatestTodayILearnedEntries(await isPreviewMode(request)),
]
);

Expand Down
90 changes: 90 additions & 0 deletions apps/nye-julianjark.no/app/routes/api.preview-mode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { ActionArgs, SerializeFrom, createCookie } from "@remix-run/node";
import { useRouteLoaderData } from "@remix-run/react";
import { z } from "zod";
import { useShortcut } from "~/components/use-shortcut";
import { config } from "~/config.server";
import { loader } from "~/root";

// Schema
const preivewModeSchema = z.object({
enabled: z.boolean(),
secret: z.string(),
});
export type PreviewMode = z.infer<typeof preivewModeSchema>;

// Cookie
const cookie = createCookie("preview-mode");
export async function serializePreviewModeToCookie(previewMode: PreviewMode) {
return await cookie.serialize(previewMode);
}
async function safeParsePreviewModeCookie(cookieHeader: string | null) {
const value = await cookie.parse(cookieHeader);
const parsed = preivewModeSchema.safeParse(value);
if (parsed.success) return parsed.data;
else return undefined;
}

// Getter
export async function isPreviewMode(fromRequest: Request) {
return (await getPreviewMode(fromRequest))?.enabled ?? false;
}
export async function getPreviewMode(
fromRequest: Request
): Promise<PreviewMode | undefined> {
// First. always use the url
const previewSecretFromUrl = new URL(fromRequest.url).searchParams.get(
"preview"
);
const previewModeEnabledFromUrl =
previewSecretFromUrl === config.previewSecret;
if (previewModeEnabledFromUrl) {
return {
enabled: true,
secret: previewSecretFromUrl,
};
}

// Second. use the cookie, if the secret is correct of course
const previewMode = await safeParsePreviewModeCookie(
fromRequest.headers.get("cookie")
);
if (previewMode?.secret === config.previewSecret) {
return previewMode;
}

return undefined;
}

export const action = async ({ request }: ActionArgs) => {
const body = preivewModeSchema.parse(await request.json());
if (body.secret !== config.previewSecret) {
return new Response("Unauthorized", { status: 401 });
}

const headers = { "Set-Cookie": await serializePreviewModeToCookie(body) };
return new Response("OK", { status: 200, headers });
};

export function PreviewMode() {
const { previewMode } = useRouteLoaderData("root") as SerializeFrom<
typeof loader
>;
async function togglePreviewMode() {
const secret =
previewMode?.secret ?? window.prompt("Fyll inn preview secret") ?? "";
const previewModeToSend: PreviewMode = {
enabled: !previewMode?.enabled,
secret,
};
const response = await fetch(`/api/preview-mode`, {
method: "POST",
body: JSON.stringify(previewModeToSend),
headers: { "Content-Type": "application/json" },
});
if (response.ok) {
window.location.reload();
}
}
useShortcut("pp", togglePreviewMode);
return null;
}
4 changes: 2 additions & 2 deletions apps/nye-julianjark.no/app/routes/i-dag-lærte-jeg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import type { TodayILearnedEntry } from "~/service/notion-today-i-learned/schema
import { slugify } from "@julianjark/notion-utils";
import { getTextFromRichText } from "@julianjark/notion-utils";
import { classNames } from "~/misc";
import { isPreviewMode } from "~/is-preview-mode.server";
import { isPreviewMode } from "~/routes/api.preview-mode";

export const meta: V2_MetaFunction<typeof loader> = ({ data }) => {
return [
Expand All @@ -34,7 +34,7 @@ export const meta: V2_MetaFunction<typeof loader> = ({ data }) => {

export const loader = async ({ request }: LoaderArgs) => {
const { metainfo, entries } = await getAllTodayILearnedEntriesAndMetainfo(
isPreviewMode(request)
await isPreviewMode(request)
);
return json(
{ metainfo, entries },
Expand Down
4 changes: 2 additions & 2 deletions apps/nye-julianjark.no/app/routes/prosjekter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Image } from "@unpic/react";
import { classNames } from "~/misc";
import { getTextFromRichText } from "@julianjark/notion-utils";
import githubIcon from "~/assets/github-mark.svg";
import { isPreviewMode } from "~/is-preview-mode.server";
import { isPreviewMode } from "~/routes/api.preview-mode";

export const meta: V2_MetaFunction<typeof loader> = ({ data }) => {
return [
Expand All @@ -28,7 +28,7 @@ export const meta: V2_MetaFunction<typeof loader> = ({ data }) => {

export const loader = async ({ request }: LoaderArgs) => {
const { metainfo, projects } = await getAllProjectsAndMetainfo(
isPreviewMode(request)
await isPreviewMode(request)
);
return json(
{ metainfo, projects },
Expand Down

0 comments on commit 0c06498

Please sign in to comment.