Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions apps/site/src/components/Avatar.astro
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ const {
height = 48,
} = Astro.props;

const src = Astro.locals.sanity
.urlForImage(coverImage ?? undefined)
?.width(width)
.height(height)
const builder = Astro.locals.sanity.urlForImage(coverImage ?? undefined);
const src = builder
?.width(width * 2)
.height(height * 2)
.quality(82)
.url();

// stegaClean: href is an attribute; zero-width characters break the URL.
Expand Down
6 changes: 4 additions & 2 deletions apps/site/src/components/ContentCard.astro
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,17 @@ const people = [
<CoverImage
image={item.coverImage}
fallbackAlt={item.title || "CodingCat.dev author photo"}
width={256}
height={256}
width={640}
height={640}
sizes="(max-width: 768px) 100vw, 560px"
class="w-full h-auto aspect-square rounded-md object-cover"
priority={priority}
/>
) : (
<CoverImage
image={item.coverImage}
fallbackAlt={item.title || "CodingCat.dev tutorial cover image"}
sizes="(max-width: 768px) 100vw, 560px"
priority={priority}
/>
)}
Expand Down
29 changes: 25 additions & 4 deletions apps/site/src/components/CoverImage.astro
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface Props {
width?: number;
height?: number;
quality?: number;
sizes?: string;
/** Above-the-fold images opt out of lazy loading. */
priority?: boolean;
class?: string;
Expand All @@ -18,18 +19,37 @@ const {
fallbackAlt = "CodingCat.dev tutorial cover image",
width = 1920,
height = 1080,
quality = 80,
quality = 82,
sizes = "(max-width: 768px) 100vw, (max-width: 1280px) 50vw, 960px",
priority = false,
class: className = "w-full h-auto aspect-video rounded-md",
} = Astro.props;

const src = Astro.locals.sanity
.urlForImage(image ?? undefined)
const builder = Astro.locals.sanity.urlForImage(image ?? undefined);
const aspect = height / width;

const src = builder
?.width(width)
.height(height)
.quality(quality)
.url();

const responsiveWidths = [320, 480, 640, 960, 1280, 1600, 1920].filter(
(w) => w <= width,
);
if (!responsiveWidths.includes(width)) {
responsiveWidths.push(width);
}
const srcset = builder
? responsiveWidths
.map((w) => {
const h = Math.round(w * aspect);
const u = builder.width(w).height(h).quality(quality).url();
return `${u} ${w}w`;
})
.join(", ")
: undefined;

// stegaClean: alt lands in an attribute, where stega's zero-width characters
// would be read out verbatim by screen readers.
const rawAlt = stegaClean((image as { alt?: string } | null)?.alt);
Expand All @@ -42,13 +62,14 @@ const alt = rawAlt && rawAlt.trim().length > 0 ? rawAlt : fallbackAlt;
<img
class={className}
src={src}
srcset={srcset}
width={width}
height={height}
alt={alt}
loading={priority ? "eager" : "lazy"}
fetchpriority={priority ? "high" : "auto"}
decoding={priority ? "sync" : "async"}
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
sizes={sizes}
/>
) : (
<div class="bg-muted rounded-md" style="padding-top: 50%" />
Expand Down
10 changes: 8 additions & 2 deletions apps/site/src/components/CoverMedia.astro
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,14 @@ const youtubeUrl = stegaClean(youtube);
</video>
</div>
) : youtubeUrl ? (
<PtYouTube node={{ _type: "youtube", youtube: youtubeUrl }} index={0} isInline={false} />
<PtYouTube
node={{ _type: "youtube", youtube: youtubeUrl }}
coverImage={image}
priority
index={0}
isInline={false}
/>
) : (
<CoverImage image={image} priority class={className} />
<CoverImage image={image} priority sizes="(max-width: 1280px) 100vw, 1280px" class={className} />
)
}
5 changes: 3 additions & 2 deletions apps/site/src/components/HomeSection.astro
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ const { title, description, items, muted = false } = Astro.props;
<CoverImage
image={item.coverImage}
fallbackAlt={item.title || "CodingCat.dev tutorial cover image"}
width={320}
height={180}
width={960}
height={540}
sizes="(max-width: 1024px) 100vw, 512px"
class="aspect-video w-full object-cover object-center"
/>
)}
Expand Down
71 changes: 53 additions & 18 deletions apps/site/src/components/portable-text/PtYouTube.astro
Original file line number Diff line number Diff line change
@@ -1,39 +1,74 @@
---
import { stegaClean } from "@sanity/client/stega";
import type { SanityImageSource } from "@sanity/image-url";
import type { Props as PtProps } from "astro-portabletext/types";
import { youtubeId } from "@/lib/youtube";

type YouTubeNode = { _type: "youtube"; youtube?: string };

type Props = PtProps<YouTubeNode>;
type Props = PtProps<YouTubeNode> & {
coverImage?: (SanityImageSource & { alt?: string }) | null;
priority?: boolean;
};

const { node } = Astro.props;
const { node, coverImage, priority = false } = Astro.props;
const id = youtubeId(stegaClean(node.youtube));

const builder = Astro.locals.sanity.urlForImage(coverImage ?? undefined);
const sanitySrc = builder?.width(1920).height(1080).quality(82).url();
const sanitySrcset = builder
? [480, 640, 960, 1280, 1600, 1920]
.map((w) => {
const h = Math.round((w * 9) / 16);
return `${builder.width(w).height(h).quality(82).url()} ${w}w`;
})
.join(", ")
: undefined;
const rawAlt = stegaClean((coverImage as { alt?: string } | null)?.alt);
const alt = rawAlt && rawAlt.trim().length > 0 ? rawAlt : "";
---

{
id && (
<div class="not-prose my-6">
<div class="yt-embed relative aspect-video isolate overflow-hidden rounded-md" data-yt={id}>
<picture>
<source
type="image/webp"
srcset={[
`https://i.ytimg.com/vi_webp/${id}/mqdefault.webp 320w`,
`https://i.ytimg.com/vi_webp/${id}/hqdefault.webp 480w`,
`https://i.ytimg.com/vi_webp/${id}/sddefault.webp 640w`,
].join(", ")}
/>
{sanitySrc ? (
<img
class="size-full object-cover"
src={`https://i.ytimg.com/vi/${id}/sddefault.jpg`}
alt=""
width={640}
height={480}
loading="lazy"
decoding="async"
src={sanitySrc}
srcset={sanitySrcset}
sizes="(max-width: 1280px) 100vw, 1280px"
alt={alt}
width={1920}
height={1080}
loading={priority ? "eager" : "lazy"}
fetchpriority={priority ? "high" : "auto"}
decoding={priority ? "sync" : "async"}
/>
</picture>
) : (
<picture>
<source
type="image/webp"
sizes="(max-width: 1280px) 100vw, 1280px"
srcset={[
`https://i.ytimg.com/vi_webp/${id}/mqdefault.webp 320w`,
`https://i.ytimg.com/vi_webp/${id}/hqdefault.webp 480w`,
`https://i.ytimg.com/vi_webp/${id}/sddefault.webp 640w`,
`https://i.ytimg.com/vi_webp/${id}/maxresdefault.webp 1280w`,
].join(", ")}
/>
<img
class="size-full object-cover"
src={`https://i.ytimg.com/vi/${id}/maxresdefault.jpg`}
alt=""
width={1280}
height={720}
loading={priority ? "eager" : "lazy"}
fetchpriority={priority ? "high" : "auto"}
decoding={priority ? "sync" : "async"}
/>
</picture>
)}
<button
type="button"
class="absolute inset-0 grid size-full place-items-center border-0 bg-transparent"
Expand Down
5 changes: 3 additions & 2 deletions apps/site/src/pages/api/guest-portal/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,13 @@ export const POST: APIRoute = async ({ request }) => {

const existingGuest = await client.fetch<{
_id: string;
_type: string;
title?: string;
socials?: Record<string, string>;
websites?: Array<any>;
} | null>(
`*[_type == "guest" && _id == $id][0]{ _id, title, socials, websites }`,
{ id: guestId },
`*[_type in ["guest", "author"] && (_id == $id || slug.current == $slug)][0]{ _id, _type, title, socials, websites }`,
{ id: guestId, slug: verification.payload?.slug || "" },
);

if (!existingGuest) {
Expand Down
21 changes: 14 additions & 7 deletions apps/site/src/pages/guest-portal/[token].astro
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface EpisodeRef {

interface GuestDoc {
_id: string;
_type: "guest" | "author";
title: string;
slug: string;
avatarUrl?: string;
Expand All @@ -42,16 +43,17 @@ let guestDoc: GuestDoc | null = null;

if (isValid && verification.payload) {
guestDoc = await client.fetch<GuestDoc | null>(
`*[_type == "guest" && (_id == $id || slug.current == $slug)][0]{
`*[_type in ["guest", "author"] && (_id == $id || slug.current == $slug)][0]{
_id,
_type,
title,
"slug": slug.current,
"avatarUrl": coverImage.asset->url,
excerpt,
content,
socials,
websites,
"episodes": *[_type == "podcast" && references(^._id) && !(_id in path("drafts.**"))] | order(season desc, episode desc) {
"episodes": *[_type == "podcast" && references(^._id) && !(_id in path("drafts.**"))] | order(season desc, episode desc) [0...10] {
_id,
title,
"slug": slug.current,
Expand All @@ -64,6 +66,8 @@ if (isValid && verification.payload) {
);
}

const isAuthor = guestDoc?._type === "author";
const profilePathPrefix = isAuthor ? "author" : "guest";
const socials = guestDoc?.socials || {};
const primaryWebsite =
guestDoc?.websites?.[0]?.link?.href || guestDoc?.websites?.[0]?.site || "";
Expand Down Expand Up @@ -168,8 +172,11 @@ const socialFields = [
<div class="mx-auto max-w-4xl px-4 py-10 sm:px-6 lg:px-8">
<Breadcrumbs
links={[
{ title: "Guests", href: "/guests" },
{ title: guestDoc?.title || "Guest Portal" },
{
title: isAuthor ? "Authors" : "Guests",
href: isAuthor ? "/authors/page/1" : "/guests/page/1",
},
{ title: guestDoc?.title || "Profile Portal" },
]}
/>

Expand Down Expand Up @@ -212,14 +219,14 @@ const socialFields = [
{guestDoc.title}
</h1>
<p class="text-sm text-muted-foreground">
Public Speaker Profile:{" "}
Public {isAuthor ? "Author" : "Speaker"} Profile:{" "}
<a
href={`/guest/${guestDoc.slug}`}
href={`/${profilePathPrefix}/${guestDoc.slug}`}
target="_blank"
rel="noreferrer"
class="font-medium text-primary underline"
>
codingcat.dev/guest/{guestDoc.slug}
codingcat.dev/{profilePathPrefix}/{guestDoc.slug}
</a>
</p>
</div>
Expand Down
Loading