diff --git a/src/lib/components/Article.svelte b/src/lib/components/Article.svelte index 261dd661f9..b381305e20 100644 --- a/src/lib/components/Article.svelte +++ b/src/lib/components/Article.svelte @@ -2,50 +2,64 @@ import Media from '$lib/UI/Media.svelte'; import { formatDate } from '$lib/utils/date'; + import type { AuthorInfo } from '$lib/utils/blog-authors'; + interface ArticleProps { title: string; cover: string; href: string; date: Date; timeToRead: number; - author: string; - avatar: string; + authors: AuthorInfo[]; + avatars: string[]; } - const { title, cover, href, date, timeToRead, author, avatar }: ArticleProps = $props(); + const { title, cover, href, date, timeToRead, authors, avatars }: ArticleProps = $props(); + + const authorAvatarPairs = $derived( + avatars.map((avatar, i) => ({ avatar, author: authors[i] })).filter(({ avatar }) => avatar) + );
  • - -
    - + + + +
    +
    + {formatDate(date)} - {timeToRead} min
    -
    -

    + +

    {title}

    -
    -
    -
    - {author} -

    {author}

    -
    -
    - - {formatDate(date)} - - {timeToRead} min -
    -
    +
    +
    +
    + {#each authorAvatarPairs as { avatar, author }, i} + {author?.name + {/each}
    + + {#each authors as author, i} + {author.name}{#if i < authors.length - 1},{' '}{/if} + {/each} +
    - +

  • diff --git a/src/lib/components/blog/article.svelte b/src/lib/components/blog/article.svelte index 4188e1b062..260861dd4b 100644 --- a/src/lib/components/blog/article.svelte +++ b/src/lib/components/blog/article.svelte @@ -1,6 +1,7 @@ - -
    +
    + -
    +
    -

    - {title} -

    +
    + {formatDate(date)} - {timeToRead} min +
    + +

    + {title} +

    +
    - {author} +
    + {#each authorAvatarPairs as { avatar, author }, i} + {author?.name + {/each} +
    -

    {author}

    -
      -
    • - {formatDate(date)} -
    • -
    • {timeToRead} min
    • -
    +

    + {#each authors as author, i} + {author.name}{#if i < authors.length - 1},{' '}{/if} + {/each} +

    - +
    diff --git a/src/lib/utils/blog-authors.ts b/src/lib/utils/blog-authors.ts new file mode 100644 index 0000000000..41d9d74844 --- /dev/null +++ b/src/lib/utils/blog-authors.ts @@ -0,0 +1,53 @@ +import type { AuthorData } from '$routes/blog/content'; + +export interface AuthorInfo { + name: string; + href: string; +} + +/** + * Extracts author information from a post for use in Article components + */ +export function getPostAuthors( + postAuthor: string | string[], + allAuthors: AuthorData[] +): { + postAuthors: AuthorInfo[]; + authorAvatars: string[]; + primaryAuthor: AuthorData | undefined; +} { + const postAuthorSlugs = Array.isArray(postAuthor) ? postAuthor : [postAuthor]; + if (postAuthorSlugs.length === 0) { + return { + postAuthors: [], + authorAvatars: [], + primaryAuthor: undefined + }; + } + + const authorsBySlug = new Map(); + for (const author of allAuthors) { + authorsBySlug.set(author.slug, author); + } + + const postAuthors: AuthorInfo[] = []; + const authorAvatars: string[] = []; + let primaryAuthor: AuthorData | undefined; + + for (const slug of postAuthorSlugs) { + const author = authorsBySlug.get(slug); + if (author) { + postAuthors.push({ name: author.name, href: author.href }); + authorAvatars.push(author.avatar || ''); + if (!primaryAuthor) { + primaryAuthor = author; + } + } + } + + return { + postAuthors, + authorAvatars, + primaryAuthor + }; +} diff --git a/src/markdoc/layouts/Author.svelte b/src/markdoc/layouts/Author.svelte index be15d095be..47cd544700 100644 --- a/src/markdoc/layouts/Author.svelte +++ b/src/markdoc/layouts/Author.svelte @@ -6,6 +6,7 @@ import { TITLE_SUFFIX } from '$routes/titles'; import type { PostsData, AuthorData } from '$routes/blog/content'; import { DEFAULT_HOST } from '$lib/utils/metadata'; + import { getPostAuthors } from '$lib/utils/blog-authors'; import FloatingHead from '$lib/components/FloatingHead.svelte'; export let name: string; @@ -170,23 +171,25 @@
    diff --git a/src/markdoc/layouts/Category.svelte b/src/markdoc/layouts/Category.svelte index 6711c02e10..d7f3a73daf 100644 --- a/src/markdoc/layouts/Category.svelte +++ b/src/markdoc/layouts/Category.svelte @@ -3,6 +3,7 @@ import { Article, FooterNav, MainFooter } from '$lib/components'; import { Main } from '$lib/layouts'; import { DEFAULT_HOST } from '$lib/utils/metadata'; + import { getPostAuthors } from '$lib/utils/blog-authors'; import type { AuthorData, PostsData } from '$routes/blog/content'; import { TITLE_SUFFIX } from '$routes/titles'; import { getContext } from 'svelte'; @@ -58,29 +59,19 @@
      {#each posts as post} - {@const postAuthorSlugs = Array.isArray(post.author) - ? post.author - : [post.author]} - {@const primarySlug = postAuthorSlugs[0]} - {@const author = - authors.find((a) => a.slug === primarySlug) || - authors.find((a) => postAuthorSlugs.includes(a.slug))} - {#if author} - {@const authorNames = postAuthorSlugs - .map((slug) => authors.find((a) => a.slug === slug)?.name) - .filter(Boolean)} - {@const authorLabel = - authorNames.length > 1 - ? `${authorNames[0]} +${authorNames.length - 1}` - : authorNames[0] || author.name} + {@const { postAuthors, authorAvatars, primaryAuthor } = getPostAuthors( + post.author, + authors + )} + {#if primaryAuthor}
      {/if} {/each} diff --git a/src/markdoc/layouts/Post.svelte b/src/markdoc/layouts/Post.svelte index da9a4b5820..7812ce6966 100644 --- a/src/markdoc/layouts/Post.svelte +++ b/src/markdoc/layouts/Post.svelte @@ -20,6 +20,7 @@ } from '$lib/utils/metadata'; import { isAnnouncement, parseCategories } from '$lib/utils/blog-cta'; import { prepareBlogCtaState, type BlogCallToActionInput } from '$lib/utils/blog-mid-cta'; + import { getPostAuthors } from '$lib/utils/blog-authors'; import type { AuthorData, PostsData } from '$routes/blog/content'; import { TITLE_SUFFIX } from '$routes/titles'; import { getContext, setContext } from 'svelte'; @@ -191,29 +192,19 @@
      {#each posts.filter((p) => p.title !== title).slice(0, 3) as post} - {@const postAuthorSlugs = Array.isArray(post.author) - ? post.author - : [post.author]} - {@const primarySlug = postAuthorSlugs[0]} - {@const postAuthor = - authors.find((a) => a.slug === primarySlug) || - authors.find((a) => postAuthorSlugs.includes(a.slug))} - {#if postAuthor} - {@const authorNames = postAuthorSlugs - .map((slug) => authors.find((a) => a.slug === slug)?.name) - .filter(Boolean)} - {@const authorLabel = - authorNames.length > 1 - ? `${authorNames[0]} +${authorNames.length - 1}` - : authorNames[0] || postAuthor.name} + {@const { postAuthors, authorAvatars, primaryAuthor } = getPostAuthors( + post.author, + authors + )} + {#if primaryAuthor}
      {/if} {/each} diff --git a/src/routes/blog/[[page]]/+page.svelte b/src/routes/blog/[[page]]/+page.svelte index 93da240913..eef97009dd 100644 --- a/src/routes/blog/[[page]]/+page.svelte +++ b/src/routes/blog/[[page]]/+page.svelte @@ -6,6 +6,7 @@ import { Main } from '$lib/layouts'; import { createDebounce } from '$lib/utils/debounce'; import { DEFAULT_HOST } from '$lib/utils/metadata'; + import { getPostAuthors } from '$lib/utils/blog-authors'; import { TITLE_SUFFIX } from '$routes/titles'; import { tick } from 'svelte'; @@ -212,25 +213,11 @@

      Blog

      {#if featured} - {@const featuredAuthorSlugs = Array.isArray(featured.author) - ? featured.author - : [featured.author]} - {@const featuredPrimarySlug = featuredAuthorSlugs[0]} - {@const author = - data.authors.find((author) => author.slug === featuredPrimarySlug) || - data.authors.find((author) => - featuredAuthorSlugs.includes(author.slug) - )} - {@const featuredAuthors = featuredAuthorSlugs - .map((slug) => data.authors.find((a) => a.slug === slug)) - .filter((a) => a !== undefined)} - {@const featuredAuthorNames = featuredAuthors.map((a) => a.name)} - {@const featuredAuthorLabel = - featuredAuthorNames.length > 2 - ? `${featuredAuthorNames[0]} +${featuredAuthorNames.length - 1}` - : featuredAuthorNames.length === 2 - ? featuredAuthorNames.join(', ') - : featuredAuthorNames[0] || author?.name} + {@const { + postAuthors: featuredAuthors, + authorAvatars: featuredAvatars, + primaryAuthor: author + } = getPostAuthors(featured.author, data.authors)}
      - {featuredAuthorLabel} - + + {#each featuredAuthors as featuredAuthor, i} + {featuredAuthor.name}{#if i < featuredAuthors.length - 1},{' '}{/if} + {/each} + @@ -399,33 +389,19 @@
        0}> {#each data.posts as post (post.slug)} - {@const postAuthorSlugs = Array.isArray(post.author) - ? post.author - : [post.author]} - {@const primarySlug = postAuthorSlugs[0]} - {@const author = - data.authors.find((author) => author.slug === primarySlug) || - data.authors.find((author) => - postAuthorSlugs.includes(author.slug) - )} - {#if author && !post.draft} - {@const authorNames = postAuthorSlugs - .map((slug) => data.authors.find((a) => a.slug === slug)?.name) - .filter(Boolean)} - {@const authorLabel = - authorNames.length > 2 - ? `${authorNames[0]} +${authorNames.length - 1}` - : authorNames.length === 2 - ? authorNames.join(', ') - : authorNames[0] || author.name} + {@const { postAuthors, authorAvatars, primaryAuthor } = getPostAuthors( + post.author, + data.authors + )} + {#if primaryAuthor && !post.draft}
        {/if} {:else}