diff --git a/package.json b/package.json index 59b8e18f..a4223e49 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "@uidotdev/usehooks": "^2.4.1", "@uploadthing/react": "5.2.0", "@upstash/redis": "^1.24.3", + "@vercel/og": "^0.6.2", "ably": "^1.2.48", "ai": "2.2.20", "algoliasearch": "^4.20.0", diff --git a/src/app/api/og/route.tsx b/src/app/api/og/route.tsx new file mode 100644 index 00000000..6357fb57 --- /dev/null +++ b/src/app/api/og/route.tsx @@ -0,0 +1,103 @@ +import { ImageResponse } from "next/og"; +import logo from "@/assets/logo.png"; +// App router includes @vercel/og. +// No need to install it. + +export const runtime = "edge"; + +// Gradient Square Component +function GradientSquare() { + return ( +
+ ); +} + +function Circle() { + return ( +
+ IK +
+ ); +} + +export async function GET(request: Request) { + try { + let title: string | null = ""; + try { + const urlParams = new URLSearchParams(request.url.split("?")[1]); + title = urlParams.get("title"); + console.log("title", title); + } catch (error) { + console.error("Error parsing URL:", error); + } + // const urlParams = new URLSearchParams(request.url.split("?")[1]); // Splitting URL to get query params + // const title = urlParams.get("title"); + // console.log("title", title); + + // const { searchParams } = new URL(request.url); + // console.log("searchParams", searchParams); + // const hasTitle = searchParams.has("title"); + // const title01 = hasTitle + // ? searchParams.get("title")?.slice(0, 100) + // : "My default title"; + + return new ImageResponse( + ( +
+
+

+ {title + ? title + : " The Dual Role of Tween 80 in Biofilm Formation"} +

+

+ Inhibition and Enhancement +

+
+
+ + + + + + {/* */} + +
+
+ ), + { + width: 1200, + height: 630, + }, + ); + } catch (e: any) { + console.log(`${e.message}`); + return new Response(`Failed to generate the image`, { + status: 500, + }); + } +} diff --git a/src/app/dashboard/[slug]/chat/[chatid]/page.tsx b/src/app/dashboard/[slug]/chat/[chatid]/page.tsx index dd9b2309..0377dffc 100644 --- a/src/app/dashboard/[slug]/chat/[chatid]/page.tsx +++ b/src/app/dashboard/[slug]/chat/[chatid]/page.tsx @@ -6,14 +6,61 @@ import { eq, and } from "drizzle-orm"; import { auth, currentUser } from "@clerk/nextjs"; import RoomWrapper from "@/components/room"; import { AblyChannelProvider } from "@/components/ablyprovider"; +import { ResolvingMetadata, Metadata } from "next"; + export const dynamic = "force-dynamic", revalidate = 0; -export default async function Page({ - params, -}: { - params: { uid: string; chatid: string }; -}) { +type Props = { + params: { id: string; uid: string; chatid: string; chattitle: string }; + searchParams: { [key: string]: string | string[] | undefined }; +}; + +export async function generateMetadata( + { params, searchParams }: Props, + parent: ResolvingMetadata, +): Promise { + const { sessionClaims } = auth(); + let fetchedChat: ChatSchema[] = []; + // console.log("parent",parent) + const previousImages = (await parent).openGraph?.images || []; + + // console.log("params", params); + // console.log("searchParams", searchParams) + if (sessionClaims?.org_id) { + fetchedChat = await db + .select() + .from(chats) + .where(and(eq(chats.id, Number(params.chatid)))) + .limit(1) + .all(); + } + console.log("chattitle in chat id page", fetchedChat[0]?.title as string); + + return { + title: "Echoes chat", + description: "echoes Chat", + alternates: { + canonical: `https://www.echoes.team/dashboard/${sessionClaims?.org_slug}/chat/${params.chatid}`, + }, + openGraph: { + title: fetchedChat[0]?.title as string, + description: "Echoes", + type: "website", + + images: [ + { + url: `api/og?title=${fetchedChat[0]?.title as string}`, + width: 1200, + height: 680, + }, + ...previousImages, + ], + }, + }; +} + +export default async function Page({ params }: Props) { const { userId, sessionClaims } = auth(); const user = await currentUser(); @@ -27,7 +74,6 @@ export default async function Page({ } let chatlog: ChatLog = { log: [], tldraw_snapshot: [] }; - // let tldrawSnapshot: SnapShot = { tldraw_snapshot: [] } let fetchedChat: ChatSchema[] = []; if (sessionClaims.org_id) { @@ -44,12 +90,10 @@ export default async function Page({ .all(); } const msg = fetchedChat[0]?.messages; - console.log("msg", msg); if (fetchedChat.length === 1 && msg) { chatlog = JSON.parse(msg as string) as ChatLog; - console.log("chatlog", chatlog); - console.log("chatlogData", chatlog.log); - // console.log("chatlogSnapshot", chatlog.tldraw_snapshot); + // console.log("chatlog", chatlog); + // console.log("chatlogData", chatlog.log); } return ( diff --git a/src/app/dashboard/[slug]/page.tsx b/src/app/dashboard/[slug]/page.tsx index 12a30ab4..714b3be9 100644 --- a/src/app/dashboard/[slug]/page.tsx +++ b/src/app/dashboard/[slug]/page.tsx @@ -5,11 +5,40 @@ import { chats, Chat as ChatSchema } from "@/lib/db/schema"; import { eq, desc, ne, and } from "drizzle-orm"; import { auth } from "@clerk/nextjs"; import ChatCardWrapper from "@/components/chatcardwrapper"; +// import { Metadata } from "next"; + // import Uploadzone from "@/components/uploadzone"; export const dynamic = "force-dynamic", revalidate = 0; +// type Props = { +// params: { id: string }; +// searchParams: { [key: string]: string | string[] | undefined }; +// }; + +// export async function generateMetadata({ +// params, +// searchParams, +// }: Props): Promise { + +// return { +// title: "Echoes slug", +// description: "echoes", +// openGraph: { +// title: "Echoes", +// description: "Echoes", +// type: "website", +// images: [ +// { +// url: "api/og", +// width: 1200, +// height: 680, +// }, +// ], +// }, +// }; +// } export default async function Page({ params, searchParams, diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 093a56a9..e7c711fb 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -7,6 +7,7 @@ import { Inter } from "next/font/google"; import QueryProviders from "@/app/queryProvider"; import { Toaster } from "@/components/ui/toaster"; import { Providers } from "@/app/providers"; + const inter = Inter({ subsets: ["latin"] }); export const metadata = { @@ -15,6 +16,23 @@ export const metadata = { manifest: "/manifest.json", viewport: "minimum-scale=1.0, initial-scale=1.0, width=device-width, shrink-to-fit=no, user-scalable=no, viewport-fit=cover", + openGraph: { + title: "Echoes", + description: + "Collaborative Platform for Researchers. Designed for Humans and AIs.", + url: "https://echoes.team", + siteName: "Echoes", + images: [ + { + url: `/api/og?title=Collaborative Platform for Researchers. Designed for Humans and AIs.`, + width: 1800, + height: 1600, + alt: "Echoes", + }, + ], + locale: "en_US", + type: "website", + }, }; export default function RootLayout({ @@ -236,20 +254,17 @@ export default function RootLayout({ content="https://echoes.team/android-chrome-192x192.png" /> - + {/* - - + + */} - Echoes + {/* Echoes */} diff --git a/src/app/page.tsx b/src/app/page.tsx index 7842b9a6..e70b6b7f 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,5 +1,4 @@ "use client"; - import { useEffect } from "react"; import { Header } from "@/components/header"; import { Button, buttonVariants } from "@/components/button"; @@ -10,6 +9,7 @@ import { motion, AnimatePresence, useAnimation } from "framer-motion"; import { useInView } from "react-intersection-observer"; import { Key, LayoutDashboard } from "lucide-react"; import { useAuth } from "@clerk/nextjs"; + const handleSmoothScroll = (): void => { if (typeof window !== "undefined") { const hashId = window.location.hash; diff --git a/src/app/robots.ts b/src/app/robots.ts new file mode 100644 index 00000000..aa7afb89 --- /dev/null +++ b/src/app/robots.ts @@ -0,0 +1,12 @@ +import { MetadataRoute } from "next"; + +export default function robots(): MetadataRoute.Robots { + return { + rules: { + userAgent: "*", + allow: "/", + disallow: "", // No path is disallowed + }, + sitemap: "https://acme.com/sitemap.xml", + }; +} diff --git a/src/components/chatcard.tsx b/src/components/chatcard.tsx index 3448c3c9..bf4b577f 100644 --- a/src/components/chatcard.tsx +++ b/src/components/chatcard.tsx @@ -101,6 +101,7 @@ const Chatcard = ({ chat, uid, org_id, org_slug, priority, type }: Props) => { const firstMessage = chatlog.log[0].content; const chatTitle = chat.title || firstMessage; + console.log("chatCard chat", chatlog); // extracts chatentry from chatlog const chats = JSON.parse(chat.messages as string) as ChatLog; const userIds = getUserIdList(chats.log); diff --git a/src/components/search.tsx b/src/components/search.tsx index fc628106..c3c8d9d5 100644 --- a/src/components/search.tsx +++ b/src/components/search.tsx @@ -56,13 +56,14 @@ const Search = (props: Props) => { }, []); // open command bar with / key useEffect(() => { + // console.log("throttledValue", throttledValue); index .search(throttledValue, { hitsPerPage: 8, filters: `orgSlug: "${props.orgSlug}"`, }) .then((response) => { - console.log(response); + console.log(" search responce", response); return setResults(response.hits); }); }, [throttledValue]); @@ -71,7 +72,7 @@ const Search = (props: Props) => { { - console.log("got the input value"); + console.log("got the input value", val); setValue(val); }} value={value} diff --git a/src/middleware.ts b/src/middleware.ts index 5cddfb3e..5df36615 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -6,7 +6,7 @@ import { env } from "@/app/env.mjs"; // This is your original middleware const clerkMiddleware = authMiddleware({ // "/" will be accessible to all users - publicRoutes: ["/"], + publicRoutes: ["/api/og", "/"], }); export async function middleware(req: NextRequest, event: NextFetchEvent) { diff --git a/src/utils/apiHelper.ts b/src/utils/apiHelper.ts index 4ea4d238..8410e9f8 100644 --- a/src/utils/apiHelper.ts +++ b/src/utils/apiHelper.ts @@ -685,75 +685,3 @@ export const saveToDB = async ({ console.log("error in saving to db", error); } }; - -// export const saveToDBForImage = async ({ -// _chat, -// chatId, -// orgSlug, -// latestResponse, -// userId, -// urlArray, -// orgId, -// }: { -// latestResponse: ChatEntry; -// _chat: ChatEntry[]; -// chatId: number; -// orgSlug: string; -// orgId: string; -// userId: string; -// urlArray: string[]; -// }) => { -// try { -// if (_chat.length === 1) { -// console.log("got in 1 length case"); -// _chat.push(latestResponse); -// // step for generating title and adding to search index -// axios.post(`https://zeplo.to/step?_token=${env.ZEPLO_TOKEN}`, [ -// { -// url: `https://${urlArray[2]}/api/generateTitle/${chatId}/${orgId}?_step=A`, -// body: JSON.stringify({ chat: _chat }), -// headers: { -// "x-zeplo-secret": env.ZEPLO_SECRET, -// }, -// }, -// { -// url: `https://${urlArray[2]}/api/addToSearch?_step=B&_requires=A`, -// body: JSON.stringify({ -// chats: _chat, -// chatId: chatId, -// orgSlug: orgSlug as string, -// }), -// headers: { -// "x-zeplo-secret": env.ZEPLO_SECRET, -// }, -// }, -// ]); -// await db -// .update(chats) -// .set({ -// messages: JSON.stringify({ log: _chat } as ChatLog), -// creator: userId, -// }) -// .where(eq(chats.id, chatId)) -// .run(); -// } else { -// _chat.push(latestResponse); -// postToAlgolia({ -// chats: [_chat[_chat.length - 2], latestResponse], -// chatId: chatId, -// orgSlug: orgSlug as string, -// urlArray: urlArray, -// }); // add to search index -// await db -// .update(chats) -// .set({ -// messages: JSON.stringify({ log: _chat }), -// updatedAt: new Date(), -// }) -// .where(eq(chats.id, chatId)) -// .run(); -// } -// } catch (error) { -// console.log("error in saving to db", error); -// } -// };