emoji

Topに戻る

blog に embed を追加した

emoji
2022/05/29 03:23
emoji
2022/05/29 03:52
Tech
どうも、 uzimaru です。
ブログに Embed を追加しました。
とりあえずは Twitter, CodePen, GitHub の gist を追加しました。

サンプル

実装

Notion の API からは↓のようなJSONが降ってきます
{
  "object": "block",
  "id": "ba08ae81-7321-48be-958a-89a526d7853e",
  "created_time": "2022-05-08T09:58:00.000Z",
  "last_edited_time": "2022-05-08T09:58:00.000Z",
  "created_by": {
    "object": "user",
    "id": "a7d811a0-5862-44b9-b0f8-8b5151542736"
  },
  "last_edited_by": {
    "object": "user",
    "id": "a7d811a0-5862-44b9-b0f8-8b5151542736"
  },
  "has_children": false,
  "archived": false,
  "type": "embed",
  "embed": {
    "caption": [],
    "url": "https://twitter.com/uzimaru0000/status/1327932457224597504?ref_src=twsrc%5Etfw"
  }
}
見てわかるように embed という識別しかされておらず何の embed なのかは embed.urlhost を見るしかありません

URL から識別する

以下のようなコードで URL から何の embed なのかを識別しました。
type KnownURL = 'twitter' | 'gist' | 'codepen';

type TypedURL = {
  type: KnownURL;
  url: URL;
};

const isTwitter = (x: URL) => {
  return x.host === 'twitter.com';
};

const isGist = (x: URL) => {
  return x.host === 'gist.github.com';
};

const isCodePen = (x: URL) => {
  return x.host === 'codepen.io';
};

const typedUrl = (rawUrl: string): TypedURL => {
  try {
    const url = new URL(rawUrl);

    if (isTwitter(url)) {
      return {
        type: 'twitter' as const,
        url,
      };
    } else if (isGist(url)) {
      return {
        type: 'gist' as const,
        url,
      };
    } else if (isCodePen(url)) {
      return {
        type: 'codepen' as const,
        url,
      };
    } else {
      return null;
    }
  } catch {
    return null;
  }
};
はい、愚直にやってます。
TypedURLtype で何の Embed なのかを識別できるようになったのでそれを使ってコンポーネントを出し分けます。

Twitter コンポーネント

雑実装ですが
const Twitter = ({ url }: TypedURL) => {
  useEffect(() => {
    const script = document.createElement('script');
    script.async = true;
    script.src = 'https://platform.twitter.com/widgets.js';
    document.head.appendChild(script);

    return () => {
      document.head.removeChild(script);
    };
  }, []);

  return (
    <Box display="flex" justifyContent="center" w="full">
      <blockquote className="twitter-tweet">
        <a href={url.href} />
      </blockquote>
    </Box>
  );
};
Twitter の Embed に必要な Script を useEffect で入れてます

CodePen コンポーネント

CodePen は、 iframe が提供されているのでそれを使いました
const CodePen = ({ url }: TypedURL) => {
  const { userId, codeId } = useMemo(() => {
    const [, userId, , codeId] = url.pathname.split('/');
    return {
      userId,
      codeId,
    };
  }, [url]);

  return (
    <iframe
      height="800"
      style={{ width: '100%' }}
      scrolling="no"
      title="Recursion"
      src={`https://codepen.io/${userId}/embed/${codeId}`}
      frameBorder="no"
      loading="lazy"
    >
      See the Pen <a href={url.href}>Recursion</a> by
      <a href={`https://codepen.io/${userId}`}>{`@${userId}`}</a> on{' '}
      <a href="https://codepen.io">CodePen</a>.
    </iframe>
  );
};

Gist コンポーネント

bvanderhoof/gist-embed を使って embed してます
const Gist = ({ url }: TypedURL) => {
  const { gistId } = useMemo(() => {
    const [, userId, gistId] = url.pathname.split('/');
    return {
      userId,
      gistId,
    };
  }, [url]);
  useEffect(() => {
    const script = document.createElement('script');
    script.src =
      'https://cdn.jsdelivr.net/npm/gist-embed@1.0.4/dist/gist-embed.min.js';
    script.type = 'text/javascript';
    document.head.appendChild(script);
    return () => {
      document.head.removeChild(script);
    };
  }, [url]);

  return (
    <code
      data-gist-id={gistId}
      style={{
        width: '100%',
      }}
    />
  );
};

まとめ

これで最低限のブログとしての機能はそろったかな?という感じです。
更新頑張ります
B!
emoji

Topに戻る

このサイトではアクセス解析のためにcookieを使用したGoogle Analyticsを使用しています。

© 2022

uzimaru