astro-notion-blogの記事一覧でFeaturedImageにも記事へのリンクを設定する

Featured image of the post

目次

まえがき

astro-notion-blogの記事一覧では、記事タイトルかRead moreをクリックすることによって記事が開きます。

FeaturedImageのクリックでも記事が開くようにしたかったので、FeaturedImageに記事へのリンクを埋め込みます。

過程

記事一覧のページはsrc\pages\index.astroが司っています。該当部分を探すと、37行目付近にありました。

<div class={styles.post} key={post.Slug}>
  <PostDate post={post} />
  <PostTags post={post} />
  <PostTitle post={post} />
  <PostFeaturedImage post={post} />
  <PostExcerpt post={post} />
  <ReadMoreLink post={post} />
</div>
src/pages/index.astro

これは上から順に、投稿日、タグ、タイトル、画像、説明文、Read moreが配置されるというそのままの意味です。

Image in a image block
実際の見た目

<PostFeaturedImage post={post} />がFeaturedImageに関する処理部分ですが、これはsrc\components\PostFeaturedImage.astroで行った処理を引っ張ってきているだけなので、PostFeaturedImage.astroの方を弄ります。

22行目からのコードがこれです。
HTMLはまだ全然読めませんが、非常にシンプルであることは分かります。

{
  image && (
    <div class="post-featured-image">
      <img src={image} alt="post-featured-image" />
    </div>
  )
}

ここにaタグで記事へのリンクを埋め込めばいいのですが、肝心の記事へのリンクを”記事ごとに”取得する方法がわかりません。そこで、似たような処理をして記事へのリンクが埋め込まれている記事タイトルを参考にします。見るのはsrc\components\PostTitle.astroです。

18行目からリンク埋め込みのaタグが書かれていました。

<a href={getPostLink(post.Slug)}>
  {post.Icon && post.Icon.Type === 'emoji' ? (
    <>
      <span>{post.Icon.Emoji}</span>
      {title}
    </>
  ) : post.Icon && post.Icon.Type === 'external' ? (
    <>
      <img src={post.Icon.Url} />
      {title}
    </>
  ) : (
    title
  )}
</a>

getPostLink(post.Slug)が記事ごとのリンクだと分かりました。これがどこから来ているのか探すと、3行目にimport { getPostLink } from '../lib/blog-helpers.ts'というのがありました。

つまり、これをPostFeaturedImage.astroに追加し、リンクを埋め込むように書き換えれば意図した通りに動作するはずです。

追記(2023/07/18)

Icon in a callout block
この次にFeaturedImageを弄る記事ではFeaturedImageを記事のトップに表示する、という内容の変更を行いました。しかし、この記事でリンクを埋め込む変更を行ったことによって、記事のFeaturedImageをクリックするとその記事に飛ぶという無限ループみたいな状態になることに気づきました。特に問題は無いのですが、気持ち悪いのでリンクの埋め込みをオンオフする処理を追加するためにこれ以降の手順を変更しました。

📄Arrow icon of a page linkastro-notion-blogの記事の最初にFeaturedImageを配置する

コードの書き換え 共通部

PostFeaturedImage.astroの4行目にimport { getPostLink } from '../lib/blog-helpers.ts'を追加。

---
import { Post } from '../lib/interfaces.ts'
import { filePath } from '../lib/blog-helpers'
import { getPostLink } from '../lib/blog-helpers.ts'
こうなる

コードの書き換え 旧

22行目からの表示部分のHTMLにaタグを追加する。

{
  image && (
    <div class="post-featured-image">
      <img src={image} alt="post-featured-image" />
    </div>
  )
}
これが
{
  image && (
    <a href={getPostLink(post.Slug)}>
      <div class="post-featured-image">
        <img src={image} alt="post-featured-image" />
      </div>
    </a>
  )
}
こうなる

コードの書き換え 新

リンク埋め込みをオンオフするのをどうするか考えて、コードを眺めていたらすぐ上に<PostTitle post={post} enableLink={false} />がありました。これはまさにリンク埋め込みをオンオフしています。この処理部が記されているsrc\components\PostTitle.astroを確認すると、

5行目のところにオンオフの判定用の入れ物が

export interface Props {
  post: Post
  enableLink: boolean
}

15行目からは条件分岐の記載がありました。

<h3 class="post-title">
  {
    enableLink ? (
      <a href={getPostLink(post.Slug)}>
        {post.Icon && post.Icon.Type === 'emoji' ? (
          <>
            <span>{post.Icon.Emoji}</span>
            {title}
          </>
        ) : post.Icon && post.Icon.Type === 'external' ? (
          <>
            <img src={post.Icon.Url} />
            {title}
          </>
        ) : (
          title
        )}
      </a>
    ) : (
      <>
        {post.Icon && post.Icon.Type === 'emoji' ? (
          <>
            <span>{post.Icon.Emoji}</span>
            {title}
          </>
        ) : post.Icon && post.Icon.Type === 'external' ? (
          <>
            <img src={post.Icon.Url} />
            {title}
          </>
        ) : (
          title
        )}
      </>
    )
  }
</h3>

これを真似てPostFeaturedImage.astroをリンク埋め込みオンオフ可能に書き換えます。

具体的にはenableLink: booleanを追記して、リンク埋め込みのオンオフを外部から指定できるようにします。

PostFeaturedImage.astroの6行目のところ

また、const { post, enableLink = true } = Astro.propsとしてPostTitle.astroと同じようにデフォルトではtrueとしておきます。(今後FeaturedImageにはデフォルトでリンクが埋め込まれる。オフにしたければオプションとして指示する必要がある。)(FeaturedImageは記事冒頭に置くもの以外は全て記事本体への誘導の役割になるはずなので、デフォルトオンで問題ないはず。)

export interface Props {
  post: Post
}

const { post } = Astro.props
これが
export interface Props {
  post: Post
  enableLink: boolean
}

const { post, enableLink = true } = Astro.props
こうなる

次が分岐です。

{
  image && (
    <div class="post-featured-image">
      <img src={image} alt="post-featured-image" />
    </div>
  )
}
これが
{
  enableLink ? (
    {
      image && (
        <a href={getPostLink(post.Slug)}>
          <div class="post-featured-image">
            <img src={image} alt="post-featured-image" />
          </div>
        </a>
      )
    }
    </a>
  ) : (
    {
      image && (
        <div class="post-featured-image">
          <img src={image} alt="post-featured-image" />
        </div>
      )
    }
  )
}
こうなる…はず…

リンク埋め込みの構文はそのまま旧バージョンのを流用しました。変に弄って壊れても困るので愚直に分岐させています。Chat GPTに聞いてみても問題なさそうだったのでこれで終わりにしていますが、正しい書き方があったら教えてください。(今はコメント欄がないのでTwitterなんかで…)

動作確認

Image in a image block

画像右の余白(メインカラムの横幅の範囲)にも判定が発生していますが、これはdiv要素に対してaタグを付けてるからだと推測します。これは別に気にならないのでそのままにしておきます。

Image in a image block

また、FeaturedImageが設定されていない記事で何も表示されないのは変わりありませんでした。下手に手を加えたことで変なものが表示されるのが若干怖かったので安心です。