Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/adding ogimage to app #56

Open
wants to merge 41 commits into
base: favicon-cover-image
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
2e1de34
changes : change for deployement
faisal004 Dec 19, 2024
dcbf976
changes
faisal004 Dec 19, 2024
b155421
added : og image
faisal004 Dec 19, 2024
40a8838
added : dyanmic og image generation added
faisal004 Dec 23, 2024
ac63fce
fix : og image fixed
faisal004 Dec 23, 2024
2cc2ac5
fix : og image from db
faisal004 Dec 23, 2024
81f1c0b
fix : console removed
faisal004 Dec 23, 2024
b066cf0
Merge branch 'main' of https://github.com/tyaga001/devtoolsacademy in…
faisal004 Jan 2, 2025
0941247
Merge branch 'tyaga001-main'
faisal004 Jan 2, 2025
d59ee5c
fix : conflix fixed
faisal004 Jan 2, 2025
c1049ac
added : og iamge preview
faisal004 Jan 2, 2025
4e311b9
fix : eslint rule
faisal004 Jan 2, 2025
0ef326d
changes
faisal004 Jan 3, 2025
9dde11e
added : url fallback
faisal004 Jan 3, 2025
ed45aae
changes : for local deployment
faisal004 Feb 8, 2025
e25a063
changes : for local deployment
faisal004 Feb 8, 2025
9ff88ef
adding : og image
faisal004 Feb 8, 2025
9c588e3
fix : url path fix
faisal004 Feb 8, 2025
21d16ec
just for testing will revert it
faisal004 Feb 8, 2025
3d2699d
adding : dynamic og iamge to /blog
faisal004 Feb 8, 2025
acac98d
adding : dynamic og iamge to /blog
faisal004 Feb 8, 2025
85c9560
added : dynamic og image to routes
faisal004 Feb 8, 2025
6622adc
reverting back to original url
faisal004 Feb 8, 2025
26cfd98
Merge branch 'main' of https://github.com/faisal004/devtoolsacademy i…
faisal004 Feb 8, 2025
246964e
remove env as config file handled that
faisal004 Feb 8, 2025
38096ed
for check
faisal004 Feb 8, 2025
bd711cc
removed cover and added dynamic image
faisal004 Feb 10, 2025
f122db4
removed cover and added dynamic image - 2
faisal004 Feb 10, 2025
384033e
url changed back
faisal004 Feb 10, 2025
edd2bd6
url for test
faisal004 Feb 10, 2025
2b96735
Delete public/cover.png
faisal004 Feb 10, 2025
cd9a08a
Favicon and website og image (#96)
nirnejak Feb 26, 2025
6d5a3c3
Merge branch 'favicon-cover-image' into feature/adding-ogimage-to-app
faisal004 Feb 26, 2025
b5775d9
Merge branch 'main' of https://github.com/faisal004/devtoolsacademy i…
faisal004 Feb 26, 2025
71e364f
fix
faisal004 Feb 26, 2025
91e857a
Merge branch 'main' of https://github.com/faisal004/devtoolsacademy
faisal004 Feb 26, 2025
42b1e08
fix
faisal004 Feb 26, 2025
40ac873
Merge branch 'main' of https://github.com/faisal004/devtoolsacademy i…
faisal004 Feb 26, 2025
4af98c2
fix : for ig image
faisal004 Feb 26, 2025
6204bc3
fix : for og image
faisal004 Feb 26, 2025
61ea83c
Merge branch 'main' of https://github.com/faisal004/devtoolsacademy i…
faisal004 Feb 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
NEXT_PUBLIC_BASE_URL=https://www.devtoolsacademy.com
DATABASE_URL=
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=
CLERK_SECRET_KEY=
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"lint": "next lint",
"migrate": "npx prisma migrate dev --schema=./prisma/schema.prisma",
"db-create": "npx prisma db push --schema=./prisma/schema.prisma",
"format": "prettier --write --ignore-path .gitignore ."
"format": "prettier --write --ignore-path .gitignore .",
"postinstall": "prisma generate"
},
"dependencies": {
"@clerk/nextjs": "^5.2.4",
Expand Down
Binary file added public/og-bg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/og-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
100 changes: 100 additions & 0 deletions src/app/api/og/route.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { ImageResponse } from 'next/og';
import { NextRequest } from 'next/server';

export async function GET(req: NextRequest) {
const { searchParams } = req.nextUrl;
const postTitle = searchParams.get('title');

return new (ImageResponse as any)(
(
<div
style={{
height: '100%',
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
justifyContent: 'center',
backgroundImage: `url("${process.env.NEXT_PUBLIC_BASE_URL}/og-bg.png")`,
}}
>
<h1 tw="absolute inset-0 flex justify-center items-center mb-96 flex-row">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
tw="h-10 w-10"
alt="Alt"
// @ts-ignore
src={`${process.env.NEXT_PUBLIC_BASE_URL}/favicon.png`}
/>
<span
tw="ml-3"
style={{
background:
'linear-gradient(180deg, rgba(255,255,255,1) 0%, rgba(156, 163, 175, 1) 100%)',
backgroundClip: 'text',
WebkitBackgroundClip: 'text',
color: 'transparent',
fontSize: 35,
letterSpacing: '-1.5px',
}}
>
DevToolsAcademy
</span>
</h1>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Based on the gathered information, I can now provide the final response:

The styling approach needs to be standardized across the codebase

The codebase shows mixed styling patterns:

  • The OG route uses Twin.Macro (tw attributes) along with inline styles
  • Other components primarily use standard Tailwind CSS with the project's configured theme
  • There's no explicit Twin.Macro setup in the build configuration (package.json, next.config.js)
  • Consider removing Twin.Macro from src/app/api/og/route.tsx and use standard Tailwind CSS classes to maintain consistency with the rest of the codebase
  • If Twin.Macro is needed specifically for OG image generation, ensure it's properly configured in the build pipeline
🔗 Analysis chain

Review styling references.

The inline styling plus Twin.Macro syntax (the “tw” attributes) is an interesting approach. Confirm that your build pipeline supports it properly and ensure it’s consistent throughout the codebase to maintain uniform styling.


Let me verify the usage of Twin.Macro and styling approaches in the codebase.


Let me check the actual implementation of styling in these files to verify consistency.


Let me check for any Twin.Macro configuration files and verify the build setup.


Let me check the tailwind configuration and any other styling-related setup files.

🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Search for Twin.Macro imports and usage
rg -l "twin\.macro|tw=\"" 

# Search for files with similar styling patterns
rg -l "style=\{\{|tw=\""

# Look for any Twin.Macro configuration files
fd -g "*twin*" -g "*tailwind*"

Length of output: 405


Script:

#!/bin/bash
# Check the styling patterns in the identified files
echo "=== TableOfContents.tsx ==="
rg "tw=\"|style=\{\{" src/components/TableOfContents.tsx -A 2

echo "=== Hero.tsx ==="
rg "tw=\"|style=\{\{" src/components/Hero.tsx -A 2

echo "=== Footer.tsx ==="
rg "tw=\"|style=\{\{" src/components/Footer.tsx -A 2

echo "=== ContributePage.tsx ==="
rg "tw=\"|style=\{\{" src/components/ContributePage.tsx -A 2

echo "=== ToolDetailsPage.tsx ==="
rg "tw=\"|style=\{\{" src/components/tools/toolDetails/ToolDetailsPage.tsx -A 2

Length of output: 1603


Script:

#!/bin/bash
# Check for Twin.Macro configuration in package.json and other config files
echo "=== Package.json Twin.Macro dependencies ==="
cat package.json | grep -A 5 -B 5 "twin"

echo "=== Tailwind Config ==="
fd -e js -e ts -e json -g "*tailwind*"

echo "=== Babel Config ==="
fd -e js -e json -g "*babel*"

echo "=== Next.js Config ==="
cat next.config.js 2>/dev/null || echo "next.config.js not found"

Length of output: 2113


Script:

#!/bin/bash
# Check Tailwind configuration
echo "=== Tailwind Config Content ==="
cat tailwind.config.ts

# Check for any global styles
echo "=== Global Styles ==="
fd -e css -e scss

# Check for styled-components or emotion setup
echo "=== Styling Setup in _app or layout files ==="
rg -l "styled-components|@emotion" src/

Length of output: 4590

{postTitle && (
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '100vh',
width: '100vw',
}}
>
<p
style={{
whiteSpace: 'pre-wrap',
alignContent: 'center',
justifyContent: 'center',
background:
'linear-gradient(180deg, rgba(105, 105, 105, 1) 0%, rgba(211, 211, 211, 1) 100%)',
backgroundClip: 'text',
WebkitBackgroundClip: 'text',
textAlign: 'center',
fontSize: 100,
letterSpacing: '-5px',
color: 'transparent',
WebkitTextFillColor: 'transparent',
}}
>
{postTitle}
</p>
</div>
)}

<h1 tw="absolute inset-0 flex justify-center items-center mt-57 flex-col">
<span
style={{
background:
'linear-gradient(180deg, rgba(255,255,255,1) 0%, rgba(156, 163, 175, 1) 100%)',
// "linear-gradient(180deg,rgba(240,238,249,.8) 0%,#E2E8FF 100%)",
backgroundClip: 'text',
WebkitBackgroundClip: 'text',
color: 'transparent',
fontSize: 50,
letterSpacing: '-1.5px',
marginTop: 150,
}}
>
A special blog made for Developers.
</span>
</h1>
</div>
),
{
width: 1920,
height: 1080,
},
);
}
239 changes: 163 additions & 76 deletions src/app/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,88 +1,175 @@
import Image from 'next/image';
import Link from 'next/link';
import { MDXRemote, MDXRemoteProps } from 'next-mdx-remote/rsc';
import { getPostBySlug, getViewCount, getAllPosts } from '@/lib/posts';
import TableOfContents from '@/components/TableOfContents';
import Breadcrumb from '@/components/Breadcrumb';
import CommentSection from '@/components/CommentSection';
import BlogPostClient from '@/components/BlogPostClient';
import ServerlessDiagram from '@/components/ServerlessDiagram';
import CodeBlock from '@/components/CodeBlock';
import { Callout } from '@/components/Callout';
import { Alert, AlertDescription } from '@/components/Alert';
import { MDXComponents } from 'mdx/types';
import BackToTop from '@/components/BackToTop';

import Image from 'next/image'
import Link from 'next/link'
import { MDXRemote, MDXRemoteProps } from 'next-mdx-remote/rsc'
import { getPostBySlug, getViewCount, getAllPosts } from '@/lib/posts'
import TableOfContents from '@/components/TableOfContents'
import Breadcrumb from '@/components/Breadcrumb'
import CommentSection from '@/components/CommentSection'
import BlogPostClient from '@/components/BlogPostClient'
import ServerlessDiagram from '@/components/ServerlessDiagram'
import CodeBlock from '@/components/CodeBlock'
import { Callout } from '@/components/Callout'
import { Alert, AlertDescription } from '@/components/Alert'
import { MDXComponents } from 'mdx/types'
import BackToTop from '@/components/BackToTop'
import { Metadata } from 'next'

const generateId = (children: any) => {
if (Array.isArray(children)) {
children = children.join('');
}
return typeof children === 'string' ? children.toLowerCase().replace(/\s+/g, '-') : '';
};
if (Array.isArray(children)) {
children = children.join('')
}
return typeof children === 'string'
? children.toLowerCase().replace(/\s+/g, '-')
: ''
}

const components = {
h1: (props: any) => <h1 className="text-4xl font-bold mt-8 mb-4" {...props} />,
h2: (props: any) => <h2 id={generateId(props.children)} className="text-3xl font-semibold mt-6 mb-3" {...props} />,
h3: (props: any) => <h3 id={generateId(props.children)} className="text-2xl font-semibold mt-4 mb-2" {...props} />,
h4: (props: any) => <h4 id={generateId(props.children)} className="text-xl font-semibold mt-3 mb-2" {...props} />,
p: (props: any) => <p className="my-2" {...props} />,
a: (props: any) => <Link className="text-blue-500 hover:underline" {...props} />,
Image: (props: any) => <Image className="my-4" {...props} />,
blockquote: (props: any) => <blockquote className="border-l-4 border-gray-300 pl-4 my-4 italic" {...props} />,
ServerlessDiagram: ServerlessDiagram,
code: CodeBlock,
Callout: Callout,
Alert: Alert,
AlertDescription: AlertDescription,
table: (props: any) => <table className="min-w-full border border-gray-300 my-4" {...props} />,
th: (props: any) => <th className="border border-gray-300 px-4 py-2 bg-gray-100" {...props} />,
td: (props: any) => <td className="border border-gray-300 px-4 py-2" {...props} />,
};
h1: (props: any) => (
<h1 className="text-4xl font-bold mt-8 mb-4" {...props} />
),
h2: (props: any) => (
<h2
id={generateId(props.children)}
className="text-3xl font-semibold mt-6 mb-3"
{...props}
/>
),
h3: (props: any) => (
<h3
id={generateId(props.children)}
className="text-2xl font-semibold mt-4 mb-2"
{...props}
/>
),
h4: (props: any) => (
<h4
id={generateId(props.children)}
className="text-xl font-semibold mt-3 mb-2"
{...props}
/>
),
p: (props: any) => <p className="my-2" {...props} />,
a: (props: any) => (
<Link className="text-blue-500 hover:underline" {...props} />
),
Image: (props: any) => <Image className="my-4" {...props} alt="Image" />,
blockquote: (props: any) => (
<blockquote
className="border-l-4 border-gray-300 pl-4 my-4 italic"
{...props}
/>
),
ServerlessDiagram: ServerlessDiagram,
code: CodeBlock,
Callout: Callout,
Alert: Alert,
AlertDescription: AlertDescription,
table: (props: any) => (
<table className="min-w-full border border-gray-300 my-4" {...props} />
),
th: (props: any) => (
<th className="border border-gray-300 px-4 py-2 bg-gray-100" {...props} />
),
td: (props: any) => (
<td className="border border-gray-300 px-4 py-2" {...props} />
),
}

export default async function BlogPost({params}: { params: { slug: string } }) {
const post = await getPostBySlug(params.slug);
const initialViews = await getViewCount(params.slug);
export async function generateMetadata({
params,
}: {
params: { slug: string }
}): Promise<Metadata> {
const post = await getPostBySlug(params.slug)
if (!post) {
return {}
}
const { title, featuredImage } = post
const ogImage = featuredImage
? featuredImage
: `${process.env.NEXT_PUBLIC_BASE_URL}/api/og?title=${post.title}`
return {
title: `${post.title} | Dev Tools Academy`,
description: post.description,
openGraph: {
title: post.title,
description: post.description,
type: 'article',
url: `${process.env.NEXT_PUBLIC_BASE_URL}/${post.slug}`,
images: [
{
url: ogImage,
width: 1200,
height: 630,
alt: 'Dev Tools Academy',
},
],
},
twitter: {
card: 'summary_large_image',
title: post.title,
description: post.description,
images: [
{
url: ogImage,
width: 1200,
height: 630,
alt: title,
},
],
},
}
}
export default async function BlogPost({
params,
}: {
params: { slug: string }
}) {
const post = await getPostBySlug(params.slug)
const initialViews = await getViewCount(params.slug)

const breadcrumbItems = [
{label: 'Home', href: '/'},
{label: 'Blog', href: '/blog'},
{label: post.title, href: `#`},
];
const breadcrumbItems = [
{ label: 'Home', href: '/' },
{ label: 'Blog', href: '/blog' },
{ label: post.title, href: `#` },
]

return (
<div className="max-w-5xl mx-auto px-4 py-12 relative">
<Breadcrumb items={breadcrumbItems}/>
<BlogPostClient
slug={params.slug}
title={post.title}
publishedAt={post.publishedAt}
initialViews={initialViews}
content={post.content}
description={post.description}
featuredImage={post.featuredImage || '/T.png'}
return (
<div className="max-w-5xl mx-auto px-4 py-12 relative">
<Breadcrumb items={breadcrumbItems} />
<BlogPostClient
slug={params.slug}
title={post.title}
publishedAt={post.publishedAt}
initialViews={initialViews}
content={post.content}
description={post.description}
featuredImage={post.featuredImage || '/T.png'}
/>
<div className="flex flex-col lg:flex-row">
<div className="w-full lg:w-3/4">
<article className="prose prose-lg dark:prose-invert max-w-none">
<MDXRemote
source={post.content}
components={components as MDXComponents}
/>
<div className="flex flex-col lg:flex-row">
<div className="w-full lg:w-3/4">
<article className="prose prose-lg dark:prose-invert max-w-none">
<MDXRemote source={post.content} components={components as MDXComponents} />
</article>
<CommentSection postSlug={params.slug}/>
</div>
<aside className="lg:w-1/4 lg:pl-8">
<div className="sticky top-24">
<TableOfContents />
</div>
</aside>
</div>
<BackToTop />
</article>
<CommentSection postSlug={params.slug} />
</div>
)
<aside className="lg:w-1/4 lg:pl-8">
<div className="sticky top-24">
<TableOfContents />
</div>
</aside>
</div>
<BackToTop />
</div>
)
}

export async function generateStaticParams() {
const posts = await getAllPosts()
return posts.map((post) => ({
slug: post.slug,
}))
}
const posts = await getAllPosts()
return posts.map((post) => ({
slug: post.slug,
}))
}
Loading