Added OG Image (#5251)
- Added dynamic OG Image to share and download in contributors page <img width="1176" alt="Screenshot 2024-05-02 at 16 24 00" src="https://github.com/twentyhq/twenty/assets/102751374/0579454b-ccc7-46ba-9875-52458f06ee82"> - Added dynamic metadata - Added design to contributor page - Added a NEXT_PUBLIC_HOST_URL in the .env file Co-authored-by: Ady Beraud <a.beraud96@gmail.com>
This commit is contained in:
@ -0,0 +1,131 @@
|
||||
import { format } from 'date-fns';
|
||||
import { ImageResponse } from 'next/og';
|
||||
|
||||
import {
|
||||
bottomBackgroundImage,
|
||||
container,
|
||||
contributorInfo,
|
||||
contributorInfoBox,
|
||||
contributorInfoContainer,
|
||||
contributorInfoStats,
|
||||
contributorInfoTitle,
|
||||
infoSeparator,
|
||||
profileContainer,
|
||||
profileContributionHeader,
|
||||
profileInfoContainer,
|
||||
profileUsernameHeader,
|
||||
styledContributorAvatar,
|
||||
topBackgroundImage,
|
||||
} from '@/app/api/contributors/og-image/[slug]/style';
|
||||
import { getContributorActivity } from '@/app/contributors/utils/get-contributor-activity';
|
||||
|
||||
const GABARITO_FONT_CDN_URL =
|
||||
'https://fonts.cdnfonts.com/s/105143/Gabarito-Medium-BF651cdf1f3f18e.woff';
|
||||
|
||||
const getGabarito = async () => {
|
||||
const fontGabarito = await fetch(GABARITO_FONT_CDN_URL).then((res) =>
|
||||
res.arrayBuffer(),
|
||||
);
|
||||
|
||||
return fontGabarito;
|
||||
};
|
||||
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
const url = request.url;
|
||||
|
||||
const username = url.split('/')?.pop() || '';
|
||||
|
||||
const contributorActivity = await getContributorActivity(username);
|
||||
if (contributorActivity) {
|
||||
const {
|
||||
firstContributionAt,
|
||||
mergedPRsCount,
|
||||
rank,
|
||||
activeDays,
|
||||
contributorAvatar,
|
||||
} = contributorActivity;
|
||||
return await new ImageResponse(
|
||||
(
|
||||
<div style={container}>
|
||||
<div style={topBackgroundImage}></div>
|
||||
<div style={bottomBackgroundImage}></div>
|
||||
<div style={profileContainer}>
|
||||
<img src={contributorAvatar} style={styledContributorAvatar} />
|
||||
<div style={profileInfoContainer}>
|
||||
<h1 style={profileUsernameHeader}>@{username} x Twenty</h1>
|
||||
<h2 style={profileContributionHeader}>
|
||||
Since {format(new Date(firstContributionAt), 'MMMM yyyy')}
|
||||
</h2>
|
||||
</div>
|
||||
<svg
|
||||
width="96"
|
||||
height="96"
|
||||
viewBox="0 0 136 136"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clip-path="url(#clip0_2343_96406)">
|
||||
<path
|
||||
d="M136 2.28882e-05H0L0.000144482 136H136V2.28882e-05ZM27.27 50.6401C27.27 43.2101 33.3 37.1801 40.73 37.1801H66.64C67.02 37.1801 67.37 37.4101 67.53 37.7601C67.69 38.1101 67.62 38.5201 67.36 38.8101L61.68 44.9801C60.69 46.0501 59.3 46.6701 57.84 46.6701H40.8C38.57 46.6701 36.76 48.4801 36.76 50.7101V60.8901C36.76 62.2001 35.7 63.2601 34.39 63.2601H29.65C28.34 63.2601 27.28 62.2001 27.28 60.8901V50.6401H27.27ZM107.88 85.3601C107.88 92.7901 101.85 98.82 94.42 98.82H83.41C75.98 98.82 69.95 92.7901 69.95 85.3601V66.0901C69.95 64.7801 70.44 63.5201 71.33 62.5501L77.75 55.5801C78.02 55.2901 78.44 55.1901 78.82 55.3301C79.19 55.4801 79.44 55.83 79.44 56.23V85.3001C79.44 87.5301 81.25 89.3401 83.48 89.3401H94.36C96.59 89.3401 98.4 87.5301 98.4 85.3001V50.7101C98.4 48.4801 96.59 46.6701 94.36 46.6701H81.71C80.26 46.6701 78.88 47.2801 77.89 48.3401L40.16 89.3401H62.83C64.14 89.3401 65.2 90.4001 65.2 91.7101V96.4501C65.2 97.7601 64.14 98.82 62.83 98.82H32.28C29.51 98.82 27.26 96.5701 27.26 93.8001V91.29C27.26 90.03 27.73 88.8201 28.59 87.8901L70.89 41.9401C73.69 38.9001 77.62 37.1801 81.75 37.1801H94.41C101.84 37.1801 107.87 43.2101 107.87 50.6401V85.3601H107.88Z"
|
||||
fill="black"
|
||||
/>
|
||||
<path
|
||||
d="M27.27 50.6401C27.27 43.2101 33.3 37.1801 40.73 37.1801H66.64C67.02 37.1801 67.37 37.4101 67.53 37.7601C67.69 38.1101 67.62 38.5201 67.36 38.8101L61.68 44.9801C60.69 46.0501 59.3 46.6701 57.84 46.6701H40.8C38.57 46.6701 36.76 48.4801 36.76 50.7101V60.8901C36.76 62.2001 35.7 63.2601 34.39 63.2601H29.65C28.34 63.2601 27.28 62.2001 27.28 60.8901V50.6401H27.27Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M107.88 85.3601C107.88 92.7901 101.85 98.82 94.42 98.82H83.41C75.98 98.82 69.95 92.7901 69.95 85.3601V66.0901C69.95 64.7801 70.44 63.5201 71.33 62.5501L77.75 55.5801C78.02 55.2901 78.44 55.1901 78.82 55.3301C79.19 55.4801 79.44 55.83 79.44 56.23V85.3001C79.44 87.5301 81.25 89.3401 83.48 89.3401H94.36C96.59 89.3401 98.4 87.5301 98.4 85.3001V50.7101C98.4 48.4801 96.59 46.6701 94.36 46.6701H81.71C80.26 46.6701 78.88 47.2801 77.89 48.3401L40.16 89.3401H62.83C64.14 89.3401 65.2 90.4001 65.2 91.7101V96.4501C65.2 97.7601 64.14 98.82 62.83 98.82H32.28C29.51 98.82 27.26 96.5701 27.26 93.8001V91.29C27.26 90.03 27.73 88.8201 28.59 87.8901L70.89 41.9401C73.69 38.9001 77.62 37.1801 81.75 37.1801H94.41C101.84 37.1801 107.87 43.2101 107.87 50.6401V85.3601H107.88Z"
|
||||
fill="white"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_2343_96406">
|
||||
<rect width="136" height="136" rx="16" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
<div style={contributorInfoContainer}>
|
||||
<div style={contributorInfoBox}>
|
||||
<div style={contributorInfo}>
|
||||
<h3 style={contributorInfoTitle}>Merged PR</h3>
|
||||
<p style={contributorInfoStats}>{mergedPRsCount}</p>
|
||||
</div>
|
||||
<div style={infoSeparator} />
|
||||
</div>
|
||||
<div style={contributorInfoBox}>
|
||||
<div style={contributorInfo}>
|
||||
<h3 style={contributorInfoTitle}>Ranking</h3>
|
||||
<p style={contributorInfoStats}>{rank}%</p>
|
||||
</div>
|
||||
<div style={infoSeparator} />
|
||||
</div>
|
||||
<div style={contributorInfoBox}>
|
||||
<div style={contributorInfo}>
|
||||
<h3 style={contributorInfoTitle}>Active Days</h3>
|
||||
<h1 style={contributorInfoStats}>{activeDays}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
width: 1200,
|
||||
height: 630,
|
||||
fonts: [
|
||||
{
|
||||
name: 'Gabarito',
|
||||
data: await getGabarito(),
|
||||
style: 'normal',
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
return new Response(`error: ${error}`, {
|
||||
status: 500,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,130 @@
|
||||
import { CSSProperties } from 'react';
|
||||
|
||||
const BACKGROUND_IMAGE_URL =
|
||||
'https://framerusercontent.com/images/nqEmdwe7yDXNsOZovuxG5zvj2E.png';
|
||||
|
||||
export const container: CSSProperties = {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '1200px',
|
||||
height: '630px',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'white',
|
||||
fontFamily: 'Gabarito',
|
||||
};
|
||||
|
||||
export const topBackgroundImage: CSSProperties = {
|
||||
backgroundImage: `url(${BACKGROUND_IMAGE_URL})`,
|
||||
position: 'absolute',
|
||||
zIndex: '-1',
|
||||
width: '1300px',
|
||||
height: '250px',
|
||||
transform: 'rotate(-11deg)',
|
||||
opacity: '0.2',
|
||||
top: '-100',
|
||||
left: '-25',
|
||||
};
|
||||
|
||||
export const bottomBackgroundImage: CSSProperties = {
|
||||
backgroundImage: `url(${BACKGROUND_IMAGE_URL})`,
|
||||
position: 'absolute',
|
||||
zIndex: '-1',
|
||||
width: '1300px',
|
||||
height: '250px',
|
||||
transform: 'rotate(-11deg)',
|
||||
opacity: '0.2',
|
||||
bottom: '-120',
|
||||
right: '-40',
|
||||
};
|
||||
|
||||
export const profileContainer: CSSProperties = {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
width: '780px',
|
||||
margin: '0px 0px 40px',
|
||||
};
|
||||
|
||||
export const styledContributorAvatar = {
|
||||
display: 'flex',
|
||||
width: '96px',
|
||||
height: '96px',
|
||||
margin: '0px',
|
||||
border: '3px solid #141414',
|
||||
borderRadius: '16px',
|
||||
};
|
||||
|
||||
export const profileInfoContainer: CSSProperties = {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '8px',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
};
|
||||
|
||||
export const profileUsernameHeader: CSSProperties = {
|
||||
margin: '0px',
|
||||
fontSize: '28px',
|
||||
fontWeight: '700',
|
||||
color: '#141414',
|
||||
fontFamily: 'Gabarito',
|
||||
};
|
||||
|
||||
export const profileContributionHeader: CSSProperties = {
|
||||
margin: '0px',
|
||||
color: '#818181',
|
||||
fontSize: '20px',
|
||||
fontWeight: '400',
|
||||
};
|
||||
|
||||
export const contributorInfoContainer: CSSProperties = {
|
||||
border: '3px solid #141414',
|
||||
borderRadius: '12px',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-around',
|
||||
width: '780px',
|
||||
height: '149px',
|
||||
backgroundColor: '#F1F1F1',
|
||||
};
|
||||
|
||||
export const contributorInfoBox: CSSProperties = {
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
position: 'relative',
|
||||
};
|
||||
|
||||
export const contributorInfo: CSSProperties = {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
margin: '32px',
|
||||
gap: '16px',
|
||||
};
|
||||
|
||||
export const contributorInfoTitle = {
|
||||
color: '#B3B3B3',
|
||||
margin: '0px',
|
||||
fontWeight: '500',
|
||||
fontSize: '24px',
|
||||
};
|
||||
|
||||
export const contributorInfoStats = {
|
||||
color: '#474747',
|
||||
margin: '0px',
|
||||
fontWeight: '700',
|
||||
fontSize: '40px',
|
||||
};
|
||||
|
||||
export const infoSeparator: CSSProperties = {
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
display: 'flex',
|
||||
width: '2px',
|
||||
height: '85px',
|
||||
backgroundColor: '#141414',
|
||||
};
|
||||
Reference in New Issue
Block a user