Skip to content

Commit

Permalink
Rename writeArticleAction and add deleteArticle function
Browse files Browse the repository at this point in the history
Renamed writeArticleAction to findOrCreateArticleByTopic and added userId parameter. Implemented deleteArticle function to remove articles by topic and userId.
  • Loading branch information
mikepsinn committed Sep 14, 2024
1 parent b4080c7 commit 0ec0a21
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 60 deletions.
12 changes: 6 additions & 6 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import NextTopLoader from "nextjs-toploader"
import { siteConfig } from "@/config/site"
import { cn } from "@/lib/utils"
import { Toaster } from "@/components/ui/toaster"
import { ThemeProvider } from "@/components/theme-provider"
import { Providers } from "@/app/providers" // Import Providers

const inter = Inter({ subsets: ["latin"] })

Expand Down Expand Up @@ -70,12 +70,12 @@ interface RootLayoutProps {
children: React.ReactNode
}

export default async function RootLayout({ children }: RootLayoutProps) {
export default function RootLayout({ children }: RootLayoutProps) {
return (
<html lang="en" suppressHydrationWarning>
<body className={cn("antialiased", inter.className)}>
<CopilotKit url="/api/copilot/openai/">
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem>
<Providers> {/* Wrap the application with Providers */}
<CopilotKit url="/api/copilot/openai/">
<NextTopLoader color="#DC2645" height={2.5} showSpinner={false} />
<div
vaul-drawer-wrapper=""
Expand All @@ -84,9 +84,9 @@ export default async function RootLayout({ children }: RootLayoutProps) {
{children}
</div>
<Toaster />
</ThemeProvider>
</CopilotKit>
<Analytics />
</CopilotKit>
</Providers>
</body>
</html>
)
Expand Down
4 changes: 2 additions & 2 deletions app/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { SessionProvider } from "next-auth/react"
import { ThemeProvider } from "next-themes"
import { ThemeProvider } from "@/components/theme-provider"
import { ThemeProviderProps } from "next-themes/dist/types"

import { SidebarProvider } from "@/lib/hooks/use-sidebar"
Expand All @@ -12,7 +12,7 @@ const queryClient = new QueryClient()

export function Providers({ children, ...props }: ThemeProviderProps) {
return (
<ThemeProvider {...props}>
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem>
<SessionProvider>
<QueryClientProvider client={queryClient}>
<SidebarProvider>
Expand Down
6 changes: 4 additions & 2 deletions app/researcher/article/[articleSlug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import { useParams } from 'next/navigation'
import { getArticleBySlugAction } from '@/app/researcher/researcherActions'
import ArticleRenderer from '@/components/ArticleRenderer'
import { ArticleWithRelations } from '@/lib/agents/researcher/researcher'
import { useSession } from 'next-auth/react'

export default function ArticlePage() {
const { data: session, status } = useSession()
const [article, setArticle] = useState<ArticleWithRelations | null>(null)
const [error, setError] = useState('')
const [isLoading, setIsLoading] = useState(true)
const [isLoading, setIsLoading] = useState(status === "loading")
const params = useParams()
if(!params) {
return <div>Invalid article slug</div>
Expand Down Expand Up @@ -40,7 +42,7 @@ export default function ArticlePage() {
<main className="container mx-auto p-4">
{isLoading && <p>Loading article...</p>}
{error && <p className="text-red-500">{error}</p>}
{!isLoading && article && <ArticleRenderer {...article} />}
{!isLoading && article && <ArticleRenderer article={article} currentUserId={session?.user?.id} />}
</main>
)
}
29 changes: 24 additions & 5 deletions app/researcher/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,27 @@ export const maxDuration = 60;

import { useState, useEffect } from 'react'
import { useSearchParams, useRouter } from 'next/navigation'
import { useSession } from 'next-auth/react' // Import useSession from next-auth
import ArticleRenderer from '@/components/ArticleRenderer'
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
import { ArticleWithRelations } from '@/lib/agents/researcher/researcher'
import GlobalBrainNetwork from "@/components/landingPage/global-brain-network"
import {writeArticleAction} from "@/app/researcher/researcherActions";
import { findOrCreateArticleByTopic } from "@/app/researcher/researcherActions";
import { UserAuthForm } from '@/components/user/user-auth-form'

export default function ResearcherPage() {
const { data: session, status } = useSession()
const loading = status === "loading"
const router = useRouter()
const searchParams = useSearchParams()

const [article, setArticle] = useState<ArticleWithRelations | null>(null)
const [error, setError] = useState('')
const [isGenerating, setIsGenerating] = useState(false)
const [topic, setTopic] = useState('')
const searchParams = useSearchParams()
const router = useRouter()


useEffect(() => {
const queryTopic = searchParams?.get('q')
Expand All @@ -28,6 +34,11 @@ export default function ResearcherPage() {
}, [searchParams])

async function handleSubmit(submittedTopic: string) {

if (!session?.user?.id) {
return <UserAuthForm />
}

if (!submittedTopic) {
setError('Please enter a topic')
return
Expand All @@ -37,7 +48,7 @@ export default function ResearcherPage() {
setError('')

try {
const generatedArticle = await writeArticleAction(submittedTopic)
const generatedArticle = await findOrCreateArticleByTopic(submittedTopic, session?.user?.id)
setArticle(generatedArticle)
router.push(`?q=${encodeURIComponent(submittedTopic)}`, { scroll: false })
} catch (err) {
Expand All @@ -47,6 +58,14 @@ export default function ResearcherPage() {
}
}

if (loading) {
return <div>Loading...</div>
}

if (!session?.user?.id) {
return <UserAuthForm />
}

return (
<main className="container mx-auto p-4">
<Card className="mb-8">
Expand Down Expand Up @@ -84,7 +103,7 @@ export default function ResearcherPage() {
<GlobalBrainNetwork />
</div>
)}
{!isGenerating && article && <ArticleRenderer {...article} />}
{!isGenerating && article && <ArticleRenderer article={article} currentUserId={session?.user?.id} />}
</main>
)
}
12 changes: 9 additions & 3 deletions app/researcher/researcherActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { revalidatePath } from "next/cache";
import { PrismaClient } from "@prisma/client";
import {
ArticleWithRelations,
deleteArticleByPromptedTopic,
findArticleByTopic,
getArticleBySlug,
writeArticle
Expand Down Expand Up @@ -63,14 +64,15 @@ export async function getArticleBySlugAction(slug: string): Promise<ArticleWithR
return getArticleBySlug(slug)
}

export async function writeArticleAction(
topic: string
export async function findOrCreateArticleByTopic(
topic: string,
userId: string
): Promise<ArticleWithRelations> {
let article: ArticleWithRelations | null

article = await findArticleByTopic(topic)
if (!article) {
article = await writeArticle(topic)
article = await writeArticle(topic, userId)
}

revalidatePath("/")
Expand Down Expand Up @@ -106,4 +108,8 @@ export async function generateImage(topic: string, articleId: string) {
console.error("Error generating image:", error)
throw new Error("Failed to generate image")
}
}

export async function deleteArticle(topic: string, userId: string) {
return await deleteArticleByPromptedTopic(topic, userId);
}
29 changes: 15 additions & 14 deletions components/user/user-auth-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,24 @@ export function UserAuthForm({
const [isGithubLoading, setIsGithubLoading] = React.useState<boolean>(false)
const [isEmailLoading, setIsEmailLoading] = React.useState<boolean>(false)
const [email, setEmail] = React.useState<string>("")
if (!callbackUrl) {
callbackUrl = "/dashboard"
}

// Check for callbackUrl in the URL
React.useEffect(() => {
const urlParams = new URLSearchParams(window.location.search)
const urlCallbackUrl = urlParams.get('callbackUrl')
if (urlCallbackUrl) {
callbackUrl = urlCallbackUrl
}
}, [])
const [finalCallbackUrl, setFinalCallbackUrl] = React.useState<string | undefined>(callbackUrl)

// Check for callbackUrl in the URL and set default if not found
React.useEffect(() => {
const urlParams = new URLSearchParams(window.location.search)
const urlCallbackUrl = urlParams.get('callbackUrl')
if (urlCallbackUrl) {
setFinalCallbackUrl(urlCallbackUrl)
} else if (!finalCallbackUrl) {
setFinalCallbackUrl(window.location.href)
}
}, [finalCallbackUrl])

const handleEmailSignIn = async () => {
setIsEmailLoading(true)
setIsLoading(true)
await signIn("email", { email, redirect: false, callbackUrl })
await signIn("email", { email, redirect: false, callbackUrl: finalCallbackUrl })
setIsEmailLoading(false)
setIsLoading(false)
}
Expand All @@ -50,7 +51,7 @@ export function UserAuthForm({
onClick={() => {
setIsGoogleLoading(true)
setIsLoading(true)
signIn("google", { redirect: false, callbackUrl })
signIn("google", { redirect: false, callbackUrl: finalCallbackUrl })
}}
disabled={isGoogleLoading || isLoading}
>
Expand All @@ -67,7 +68,7 @@ export function UserAuthForm({
onClick={() => {
setIsGithubLoading(true)
setIsLoading(true)
signIn("github", { redirect: false, callbackUrl })
signIn("github", { redirect: false, callbackUrl: finalCallbackUrl })
}}
disabled={isGithubLoading || isLoading}
>
Expand Down
37 changes: 9 additions & 28 deletions components/user/user-nav-display.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,26 @@
import React, { useState } from "react"
import React from "react"
import { User } from "next-auth"
import { NavItem } from "@/types"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
import { UserAccountNav } from "./user-account-nav"
import { Dialog, DialogContent, DialogTrigger, DialogTitle } from "@/components/ui/dialog"
import { UserAuthForm } from "@/components/user/user-auth-form"
import {VisuallyHidden} from "@radix-ui/themes";
import { LoginPromptButton } from "../LoginPromptButton"

interface UserNavDisplayProps extends React.HTMLAttributes<HTMLDivElement> {
user: Pick<User, "name" | "image" | "email">
avatarNavItems?: NavItem[]
}

export function UserNavDisplay({ user, avatarNavItems }: UserNavDisplayProps) {
const [open, setOpen] = useState(false)

if (user.email === null || user.email === undefined) {
let callbackUrl = undefined;
if (typeof window !== "undefined") {
callbackUrl = window.location.href;
} else {
console.error("window is not defined in UserNavDisplay");
}
if (!user.email) {
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<button className={cn(buttonVariants({ variant: "outline", size: "sm" }))}>
Sign in
</button>
</DialogTrigger>
<DialogContent>
<VisuallyHidden>
<DialogTitle>Sign In</DialogTitle>
</VisuallyHidden>
<UserAuthForm callbackUrl={callbackUrl} />
</DialogContent>
</Dialog>
<LoginPromptButton
buttonText="Sign in"
buttonVariant="outline"
buttonSize="sm"
/>
)
}


return (
<UserAccountNav
user={{
Expand Down

0 comments on commit 0ec0a21

Please sign in to comment.