Skip to content

Commit

Permalink
fix/#91 各NotionBlockへの細かい対応 #90 埋め込み続き 読み込み時 caption fix
Browse files Browse the repository at this point in the history
- 縦横比から画像サイズを動的に変更
- captionに対応
- 大きさを調整
- TikTok Skeltonに対応
  • Loading branch information
u-ecmaker committed Oct 22, 2024
1 parent 104d9b5 commit f1c72a0
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 50 deletions.
9 changes: 8 additions & 1 deletion src/components/notion/blocks/Audio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ type Props = {
};

export const Audio = ({ block: { audio } }: Props) => {
const caption = audio.caption.length > 0 ? audio.caption[0].plain_text : '';

return (
<div className="my-4">
<div className="my-4 flex flex-col items-center">
<audio controls className="w-full">
<source
src={audio.type === 'file' ? audio.file.url : audio.external.url}
Expand All @@ -19,6 +21,11 @@ export const Audio = ({ block: { audio } }: Props) => {
/>
Your browser does not support the audio element.
</audio>
{caption && (
<figcaption className="text-xs text-gray-400 text-center mt-1">
{caption}
</figcaption>
)}
</div>
);
};
17 changes: 12 additions & 5 deletions src/components/notion/blocks/Embed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,26 @@ type Props = {

export const Embed = ({ block }: Props) => {
const { url } = block.embed;
const caption = block.embed.caption.length > 0 ? block.embed.caption[0].plain_text : '';

if (url.includes('instagram.com')) return <Instagram url={url} />;
if (url.includes('facebook.com')) return <Facebook url={url} />;
if (url.includes('twitter.com')) return <Twitter url={url} />;
if (url.includes('tiktok.com')) return <TikTok url={url} />;
if (url.includes('instagram.com')) return <Instagram url={url} caption={caption} />;
if (url.includes('facebook.com')) return <Facebook url={url} caption={caption} />;
if (url.includes('twitter.com')) return <Twitter url={url} caption={caption} />;
if (url.includes('x.com')) return <Twitter url={url} caption={caption} />;
if (url.includes('tiktok.com')) return <TikTok url={url} caption={caption} />;

return (
<div className="my-5">
<div className="my-5 text-center">
<iframe
src={url}
height={500}
className="w-full overflow-hidden"
></iframe>
{caption && (
<figcaption className="text-xs text-gray-400 text-center">
{caption}
</figcaption>
)}
</div>
);
};
10 changes: 5 additions & 5 deletions src/components/notion/blocks/File.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ export const File = ({ block }: Props) => {
<span className="group-hover:underline">
{lastElementInArray.split('?')[0]}
</span>
{caption_file && (
<figcaption className="text-xs text-gray-400">
{caption_file}
</figcaption>
)}
</span>
</Link>
{caption_file && (
<figcaption className="text-xs text-gray-400 mt-1 ml-7">
{caption_file}
</figcaption>
)}
</figure>
);
};
11 changes: 9 additions & 2 deletions src/components/notion/blocks/Pdf.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ type Props = {
};

export const Pdf = ({ block: { pdf } }: Props) => {
const caption = pdf.caption.length > 0 ? pdf.caption[0].plain_text : '';

return (
<div className="my-4 leading-8">
<div className="my-4 leading-8 flex flex-col items-center">
<object
data={pdf.type === 'file' ? pdf.file.url : pdf.external.url}
type="application/pdf"
className="h-auto w-full"
className="w-full max-w-[700px] sm:max-w-[450px] h-auto min-h-[500px]"
aria-labelledby="PDF document"
>
<div>Your browser does not support PDFs.</div>
Expand All @@ -24,6 +26,11 @@ export const Pdf = ({ block: { pdf } }: Props) => {
Download the PDF
</Link>
</object>
{caption && (
<figcaption className="text-xs text-gray-400 text-center mt-1">
{caption}
</figcaption>
)}
</div>
);
};
37 changes: 28 additions & 9 deletions src/components/notion/blocks/Video.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,49 @@
import type { VideoBlockObjectResponse } from '@notionhq/client/build/src/api-endpoints';
import type { BlockWithChildren } from '~/types/notion';

import { Facebook } from './embed/Facebook';
import { YouTube } from './embed/YouTube';

type Props = {
block: BlockWithChildren<VideoBlockObjectResponse>;
};

export const Video = ({ block }: Props) => {
const caption = block.video.caption.length > 0 ? block.video.caption[0].plain_text : '';

if (block.video.type === 'file') {
return (
<video controls className="h-40 w-72 md:h-52 md:w-96">
<source src={block.video.file.url} type="video/mp4" />
<source src={block.video.file.url} type="video/ogg" />
Your browser does not support the video tag.
</video>
<div className="my-5 flex flex-col text-center justify-center items-center">
<video controls className="w-full max-h-[400px] max-w-[550px]">
<source src={block.video.file.url} type="video/mp4" />
<source src={block.video.file.url} type="video/ogg" />
Your browser does not support the video tag.
</video>
{caption && (
<figcaption className="text-xs text-gray-400 text-center mt-1">
{caption}
</figcaption>
)}
</div>
);
} else if (block.video.type === 'external') {
const { url } = block.video.external;
if (url.includes('youtu.be') || url.includes('youtube.com'))
return <YouTube url={url} />;
return <YouTube url={url} caption={caption} />;
if (url.includes('facebook') || url.includes('fb.watch'))
return <Facebook url={url} caption={caption} />;

return (
<video controls className="h-40 w-72 md:h-52 md:w-96">
<source src={block.video.external.url} />
</video>
<div className="my-5 flex flex-col text-center justify-center items-center">
<video controls className="w-full max-h-[400px] max-w-[550px]">
<source src={block.video.external.url} />
</video>
{caption && (
<figcaption className="text-xs text-gray-400 text-center mt-1">
{caption}
</figcaption>
)}
</div>
);
}

Expand Down
26 changes: 22 additions & 4 deletions src/components/notion/blocks/embed/Facebook.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import Link from 'next/link';
import Script from 'next/script';
import { useReducer, useRef } from 'react';

import { useMutationObserver } from '~/hooks/useMutationObserver';

type Props = {
url: string;
caption?: string;
};

export const Facebook = ({ url }: Props) => {
export const Facebook = ({ url, caption }: Props) => {
const ref = useRef<HTMLDivElement>(null);
const [isLoading, onLoaded] = useReducer(() => false, true);
useMutationObserver({
Expand All @@ -20,19 +22,24 @@ export const Facebook = ({ url }: Props) => {
});

return (
<div className="relative my-5">
{isLoading && <Skeleton />}
<div className="relative my-5 text-center mx-auto">
{isLoading && <Skeleton url={url} />}
<div className="fb-post min-h-[328px]" data-href={url} ref={ref}></div>
<Script
async
defer
src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v3.2"
/>
{caption && (
<figcaption className="text-xs text-gray-400 text-center mt-1">
{caption}
</figcaption>
)}
</div>
);
};

const Skeleton = () => {
const Skeleton = ({ url }: { url: string }) => {
return (
<div className="absolute top-0 w-full min-w-[350px] max-w-[750px] bg-white drop-shadow">
<div className="animate-pulse">
Expand All @@ -45,6 +52,17 @@ const Skeleton = () => {
</div>
</div>
</div>
<div className="absolute top-32 left-1/2 flex -translate-x-1/2 flex-col items-center justify-center gap-4">
<div className="mx-auto w-[200px]">
<Link
href={url}
className="text-center text-sm font-semibold text-blue-600"
target="_blank"
>
View on Facebook
</Link>
</div>
</div>
</div>
);
};
10 changes: 8 additions & 2 deletions src/components/notion/blocks/embed/Instagram.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import Script from 'next/script';

type Props = {
url: string;
caption?: string;
};

export const Instagram = ({ url }: Props) => {
export const Instagram = ({ url, caption }: Props) => {
return (
<div className="my-5">
<div className="my-5 text-center mx-auto max-w-[360px]">
<blockquote
className="instagram-media w-full rounded border-2 border-gray-100 bg-white drop-shadow-md"
data-instgrm-captioned
Expand All @@ -18,6 +19,11 @@ export const Instagram = ({ url }: Props) => {
<Skeleton url={url} />
</blockquote>
<Script async defer src="//www.instagram.com/embed.js" />
{caption && (
<figcaption className="text-xs text-gray-400 text-center mt-1">
{caption}
</figcaption>
)}
</div>
);
};
Expand Down
68 changes: 58 additions & 10 deletions src/components/notion/blocks/embed/TikTok.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,70 @@
import Link from 'next/link';
import Script from 'next/script';
import { useState, useEffect } from 'react';

type Props = {
url: string;
caption?: string;
};

export const TikTok = ({ url }: Props) => {
export const TikTok = ({ url, caption }: Props) => {
const [isLoaded, setIsLoaded] = useState(false);
const TikTokVideoId = url.match(/\/video\/(\d+)/)?.[1];

useEffect(() => {
const handleLoad = () => setIsLoaded(true);
window.addEventListener('tiktok-load', handleLoad);

return () => {
window.removeEventListener('tiktok-load', handleLoad);
};
}, []);

return (
<div className="my-5 text-center mx-auto">
{!isLoaded && <Skeleton url={url} />}
{isLoaded && (
<blockquote
className="tiktok-embed min-w-[325px] max-w-[605px]"
data-video-id={TikTokVideoId}
cite={url}
>
<section></section>
</blockquote>
)}
<Script async defer src="https://www.tiktok.com/embed.js" onLoad={() => setIsLoaded(true)} />
{caption && (
<figcaption className="-mt-4 text-xs text-gray-400 text-center">
{caption}
</figcaption>
)}
</div>
);
};

const Skeleton = ({ url }: { url: string }) => {
return (
<div className="my-5">
<blockquote
cite={url}
data-video-id={TikTokVideoId}
className="tiktok-embed min-w[325px] max-w-[605px]"
>
<section></section>
</blockquote>
<Script async defer src="https://www.tiktok.com/embed.js" />
<div className="mb-4 relative w-80 left-1/2 transform -translate-x-1/2 flex flex-col items-center bg-white drop-shadow ">
<div className="animate-pulse">
<div className="flex items-center gap-2 p-[10px]"></div>
<div className="h-80 w-60 bg-gray-200"></div>
<div className="h-14 p-3">
<div className="h-3 w-36 rounded bg-gray-200"></div>
<div className="mt-1 h-3 w-28 rounded bg-gray-200"></div>
</div>
</div>
<div className="absolute top-40 left-1/2 transform -translate-x-1/2 flex flex-col items-center justify-center gap-4 z-10">
<div className="mx-auto w-[200px]">
<Link
href={url}
className="text-center text-sm font-semibold text-blue-600"
target="_blank"
style={{ color: '#2563EB' }}
>
View on TikTok
</Link>
</div>
</div>
</div>
);
};
Loading

0 comments on commit f1c72a0

Please sign in to comment.