Skip to content

Commit 20d16e9

Browse files
committed
Add page screenshot
1 parent 25780ed commit 20d16e9

File tree

4 files changed

+130
-58
lines changed

4 files changed

+130
-58
lines changed

apps/web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"@types/validator": "^13.15.3",
3131
"@vercel/og": "^0.0.20",
3232
"canvas-confetti": "^1.9.3",
33+
"capture-node": "^2.2.0",
3334
"chrono-node": "^2.7.6",
3435
"classnames": "^2.3.1",
3536
"easymde": "^2.18.0",

apps/web/pages/pages/index.tsx

Lines changed: 80 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { PageType, PageTypeToLabel } from "@changes-page/supabase/types/page";
22
import { PlusIcon, UserGroupIcon } from "@heroicons/react/solid";
33
import classNames from "classnames";
4-
import { InferGetServerSidePropsType } from "next";
4+
import type { InferGetServerSidePropsType } from "next";
55
import Link from "next/link";
66
import { useRouter } from "next/router";
7-
import { useEffect, type JSX } from "react";
7+
import { type JSX, useEffect } from "react";
88
import { useHotkeys } from "react-hotkeys-hook";
99
import { PrimaryRouterButton } from "../../components/core/buttons.component";
1010
import { notifyError } from "../../components/core/toast.component";
@@ -13,6 +13,7 @@ import AuthLayout from "../../components/layout/auth-layout.component";
1313
import Page from "../../components/layout/page.component";
1414
import Changelog from "../../components/marketing/changelog";
1515
import { ROUTES } from "../../data/routes.data";
16+
import { getPageScreenshotUrl } from "../../utils/capture";
1617
import { getAppBaseURL } from "../../utils/helpers";
1718
import { withSupabase } from "../../utils/supabase/withSupabase";
1819
import { useUserData } from "../../utils/useUser";
@@ -33,16 +34,26 @@ export const getServerSideProps = withSupabase(async (_, { supabase }) => {
3334
)
3435
.order("updated_at", { ascending: false });
3536

37+
const screenshots = pages.map((page) =>
38+
getPageScreenshotUrl(
39+
page.page_settings?.custom_domain
40+
? `https://${page.page_settings.custom_domain}`
41+
: `https://${page.url_slug}.changes.page`
42+
)
43+
);
44+
3645
return {
3746
props: {
3847
pages: pages ?? [],
48+
screenshots,
3949
error,
4050
},
4151
};
4252
});
4353

4454
export default function Pages({
4555
pages,
56+
screenshots,
4657
error,
4758
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
4859
const { billingDetails, user } = useUserData();
@@ -80,7 +91,7 @@ export default function Pages({
8091
message="Get started by creating your first page."
8192
buttonLink={
8293
billingDetails?.has_active_subscription
83-
? `/pages/new`
94+
? "/pages/new"
8495
: `/api/billing/redirect-to-checkout?return_url=${getAppBaseURL()}/pages`
8596
}
8697
buttonLabel={
@@ -107,69 +118,79 @@ export default function Pages({
107118
{pages.length ? (
108119
<div className="overflow-hidden shadow rounded-md bg-white dark:bg-gray-900 border dark:border-gray-800">
109120
<ul className="divide-y divide-gray-200 dark:divide-gray-800">
110-
{pages.map((page) => (
121+
{pages.map((page, idx) => (
111122
<li
112123
key={page.id}
113124
className="relative group hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors duration-200"
114125
>
115126
<div className="flex items-center justify-between p-6">
116-
<div className="flex-1 min-w-0">
117-
<div className="flex items-center gap-3 mb-2">
118-
<span
119-
className={classNames(
120-
page.type === PageType.announcements &&
121-
"bg-blue-100 dark:bg-blue-700",
122-
page.type === PageType.changelogs &&
123-
"bg-teal-100 dark:bg-teal-700",
124-
page.type === PageType.releases &&
125-
"bg-rose-100 dark:bg-rose-700",
126-
page.type === PageType.updates &&
127-
"bg-amber-100 dark:bg-amber-700",
128-
page.type === PageType.announcements &&
129-
"text-blue-500 dark:text-blue-100",
130-
page.type === PageType.changelogs &&
131-
"text-teal-500 dark:text-teal-100",
132-
page.type === PageType.releases &&
133-
"text-rose-500 dark:text-rose-100",
134-
page.type === PageType.updates &&
135-
"text-amber-500 dark:text-amber-100",
136-
"inline-flex px-2 py-1 text-xs font-bold rounded-md"
137-
)}
138-
>
139-
{PageTypeToLabel[page.type]}
140-
</span>
141-
{page.teams && page.user_id !== user?.id && (
142-
<div className="flex items-center gap-1 text-gray-500 dark:text-gray-400">
143-
<UserGroupIcon className="h-4 w-4" />
144-
<span className="text-xs font-medium">
145-
Editor ({page.teams.name})
146-
</span>
147-
</div>
148-
)}
127+
<div className="flex items-center gap-4">
128+
<div className="flex-shrink-0 hidden sm:block">
129+
<img
130+
className="w-48 h-18 object-cover rounded-md border border-gray-200 dark:border-gray-700"
131+
src={screenshots[idx]}
132+
alt={`Screenshot of ${page.title}`}
133+
loading="lazy"
134+
/>
149135
</div>
150-
<div>
151-
<h3 className="text-lg font-bold tracking-tight text-gray-900 dark:text-white">
152-
<Link
153-
href={`${ROUTES.PAGES}/${page.id}`}
154-
className="focus:outline-none hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors duration-200"
136+
<div className="flex-1 min-w-0">
137+
<div className="flex items-center gap-3 mb-2">
138+
<span
139+
className={classNames(
140+
page.type === PageType.announcements &&
141+
"bg-blue-100 dark:bg-blue-700",
142+
page.type === PageType.changelogs &&
143+
"bg-teal-100 dark:bg-teal-700",
144+
page.type === PageType.releases &&
145+
"bg-rose-100 dark:bg-rose-700",
146+
page.type === PageType.updates &&
147+
"bg-amber-100 dark:bg-amber-700",
148+
page.type === PageType.announcements &&
149+
"text-blue-500 dark:text-blue-100",
150+
page.type === PageType.changelogs &&
151+
"text-teal-500 dark:text-teal-100",
152+
page.type === PageType.releases &&
153+
"text-rose-500 dark:text-rose-100",
154+
page.type === PageType.updates &&
155+
"text-amber-500 dark:text-amber-100",
156+
"inline-flex px-2 py-1 text-xs font-bold rounded-md"
157+
)}
155158
>
156-
<span
157-
className="absolute inset-0"
158-
aria-hidden="true"
159-
/>
160-
{page.title}
161-
</Link>
162-
</h3>
163-
{page.description && (
164-
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400 line-clamp-2">
165-
{page.description}
159+
{PageTypeToLabel[page.type]}
160+
</span>
161+
{page.teams && page.user_id !== user?.id && (
162+
<div className="flex items-center gap-1 text-gray-500 dark:text-gray-400">
163+
<UserGroupIcon className="h-4 w-4" />
164+
<span className="text-xs font-medium">
165+
Editor ({page.teams.name})
166+
</span>
167+
</div>
168+
)}
169+
</div>
170+
<div>
171+
<h3 className="text-lg font-bold tracking-tight text-gray-900 dark:text-white">
172+
<Link
173+
href={`${ROUTES.PAGES}/${page.id}`}
174+
className="focus:outline-none hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors duration-200"
175+
>
176+
<span
177+
className="absolute inset-0"
178+
aria-hidden="true"
179+
/>
180+
{page.title}
181+
</Link>
182+
</h3>
183+
{page.description && (
184+
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400 line-clamp-2">
185+
{page.description}
186+
</p>
187+
)}
188+
<p className="mt-2 text-xs text-gray-400 dark:text-gray-500">
189+
{page.page_settings?.custom_domain
190+
? page.page_settings.custom_domain
191+
: `${page.url_slug}.changes.page`}
166192
</p>
167-
)}
168-
<p className="mt-2 text-xs text-gray-400 dark:text-gray-500">
169-
{page.page_settings?.custom_domain
170-
? page.page_settings.custom_domain
171-
: `${page.url_slug}.changes.page`}
172-
</p>
193+
</div>
173194
</div>
174195
</div>
175196
<div className="ml-4 flex-shrink-0">
@@ -178,6 +199,7 @@ export default function Pages({
178199
fill="currentColor"
179200
viewBox="0 0 20 20"
180201
>
202+
<title>Go to page</title>
181203
<path
182204
fillRule="evenodd"
183205
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"

apps/web/utils/capture.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { Capture } from "capture-node";
2+
3+
export function getPageScreenshotUrl(url: string) {
4+
const capture = new Capture(
5+
process.env.CAPTURE_API_KEY,
6+
process.env.CAPTURE_API_SECRET
7+
);
8+
9+
return capture.buildImageUrl(url, {
10+
vw: 1280,
11+
vh: 720,
12+
scaleFactor: 1.5,
13+
});
14+
}

pnpm-lock.yaml

Lines changed: 35 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)