Skip to content

Commit 4cd79c0

Browse files
committed
Add OpenGraph preview
Create OpenGraphPreview component to fetch and parse metadata. Integrate into LinkPreview to show if no other preview available. Implement polished styling for clean, readable design.
1 parent 09ea590 commit 4cd79c0

File tree

3 files changed

+83
-4
lines changed

3 files changed

+83
-4
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { useEffect, useState } from 'react';
2+
3+
export default function OpenGraphPreview({ url }) {
4+
const [data, setData] = useState(null);
5+
6+
useEffect(() => {
7+
async function fetchData() {
8+
const response = await fetch(url);
9+
const html = await response.text();
10+
const doc = new DOMParser().parseFromString(html, 'text/html');
11+
12+
const metaContent = (property) =>
13+
doc.querySelector(`meta[property="${property}"]`)?.getAttribute('content');
14+
15+
const [title, description, image, source] = [
16+
'og:title',
17+
'og:description',
18+
'og:image',
19+
'og:site_name',
20+
].map(metaContent);
21+
setData({ title, description, image, source });
22+
}
23+
fetchData();
24+
}, [url]);
25+
if (!data || data.title === undefined) {
26+
return null;
27+
}
28+
29+
return (
30+
<div className="opengraph-preview">
31+
{data.image && <img className="opengraph-image" src={data.image} alt={data.title} />}
32+
<div>
33+
<div className="opengraph-title">{data.title}</div>
34+
<div className="opengraph-description">{data.description}</div>
35+
<div className="opengraph-source">{data.source}</div>
36+
</div>
37+
</div>
38+
);
39+
}

src/components/link-preview/preview.jsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import TikTokPreview, { canShowURL as tikTokCanShowURL } from './tiktok';
1111
import SoundCloudPreview, { canShowURL as soundCloudCanShowURL } from './soundcloud';
1212
import SpotifyPreview, { canShowURL as spotifyCanShowURL } from './spotify';
1313
import AppleMusicPreview, { canShowUrl as appleMusicCanShowURL } from './apple-music';
14-
14+
import OpenGraphPreview from './open-graph';
1515
import EmbedlyPreview from './embedly';
1616

1717
export default function LinkPreview({ allowEmbedly, url }) {
@@ -41,12 +41,10 @@ export default function LinkPreview({ allowEmbedly, url }) {
4141
} else if (appleMusicCanShowURL(url)) {
4242
return <AppleMusicPreview url={url} />;
4343
}
44-
4544
if (allowEmbedly) {
4645
return <EmbedlyPreview url={url} />;
4746
}
48-
49-
return false;
47+
return <OpenGraphPreview url={url} />;
5048
}
5149

5250
LinkPreview.propTypes = {

styles/shared/post.scss

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,48 @@ $post-line-height: rem(20px);
772772
.tiktok-video-iframe {
773773
width: 100%;
774774
}
775+
776+
.opengraph-preview {
777+
max-width: 500px;
778+
border-radius: 10px;
779+
padding: 10px;
780+
margin-top: 10px;
781+
margin-bottom: 10px;
782+
background-color: #f8f8f8;
783+
border: 1px solid #555;
784+
.dark-theme & {
785+
background-color: #222;
786+
}
787+
}
788+
789+
.opengraph-image {
790+
max-width: 100%;
791+
max-height: 300px;
792+
}
793+
794+
.opengraph-title {
795+
font-size: 1.2em;
796+
margin-top: 0.5em;
797+
color: #111;
798+
.dark-theme & {
799+
color: #d9d9d9;
800+
}
801+
}
802+
803+
.opengraph-description {
804+
font-size: 1em;
805+
margin-top: 0.5em;
806+
color: #333;
807+
.dark-theme & {
808+
color: #a9a9a9;
809+
}
810+
}
811+
812+
.opengraph-source {
813+
font-size: 1em;
814+
margin-top: 0.5em;
815+
color: #777;
816+
}
775817
}
776818

777819
.submit-mode-hint {

0 commit comments

Comments
 (0)