Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
"use client"
|
||||
|
||||
import { createContext, ReactNode, useCallback, useContext, useEffect, useState } from "react"
|
||||
import { useAuth } from "@/lib/auth-context"
|
||||
|
||||
type UserRecommendedNovel = {
|
||||
id: string
|
||||
title: string
|
||||
slug: string
|
||||
authorName: string
|
||||
coverUrl: string | null
|
||||
status: string
|
||||
totalChapters: number
|
||||
}
|
||||
|
||||
type UserRecommendationItem = {
|
||||
id: string
|
||||
novelId: string
|
||||
createdAt: string | null
|
||||
novel: UserRecommendedNovel
|
||||
}
|
||||
|
||||
type RecommendationContextType = {
|
||||
recommendations: UserRecommendationItem[]
|
||||
isRecommended: (novelId: string) => boolean
|
||||
toggleRecommendation: (novelId: string) => Promise<{ status: "added" | "removed" | "exists" }>
|
||||
}
|
||||
|
||||
const RecommendationContext = createContext<RecommendationContextType | undefined>(undefined)
|
||||
|
||||
export function RecommendationProvider({ children }: { children: ReactNode }) {
|
||||
const { user } = useAuth()
|
||||
const [recommendations, setRecommendations] = useState<UserRecommendationItem[]>([])
|
||||
|
||||
const fetchRecommendations = useCallback(async () => {
|
||||
if (!user) {
|
||||
setRecommendations([])
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch("/api/user/recommendations")
|
||||
if (!res.ok) {
|
||||
setRecommendations([])
|
||||
return
|
||||
}
|
||||
|
||||
const data = await res.json()
|
||||
setRecommendations(Array.isArray(data) ? data : [])
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch recommendations", error)
|
||||
}
|
||||
}, [user])
|
||||
|
||||
useEffect(() => {
|
||||
fetchRecommendations()
|
||||
}, [fetchRecommendations])
|
||||
|
||||
const isRecommended = useCallback(
|
||||
(novelId: string) => recommendations.some((item) => item.novelId === novelId),
|
||||
[recommendations]
|
||||
)
|
||||
|
||||
const toggleRecommendation = useCallback(
|
||||
async (novelId: string) => {
|
||||
if (!user) throw new Error("Unauthorized")
|
||||
if (!novelId) throw new Error("Missing novel id")
|
||||
|
||||
const existed = recommendations.some((item) => item.novelId === novelId)
|
||||
|
||||
if (existed) {
|
||||
const res = await fetch(`/api/user/recommendations?novelId=${encodeURIComponent(novelId)}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
const data = (await res.json()) as { error?: string }
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(data.error || "Không thể bỏ đề cử")
|
||||
}
|
||||
|
||||
await fetchRecommendations()
|
||||
return { status: "removed" as const }
|
||||
}
|
||||
|
||||
const res = await fetch("/api/user/recommendations", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ novelId }),
|
||||
})
|
||||
|
||||
const data = (await res.json()) as { error?: string }
|
||||
|
||||
if (!res.ok) {
|
||||
if (res.status === 409) {
|
||||
await fetchRecommendations()
|
||||
return { status: "exists" as const }
|
||||
}
|
||||
|
||||
throw new Error(data.error || "Không thể đề cử truyện")
|
||||
}
|
||||
|
||||
await fetchRecommendations()
|
||||
return { status: "added" as const }
|
||||
},
|
||||
[fetchRecommendations, recommendations, user]
|
||||
)
|
||||
|
||||
return (
|
||||
<RecommendationContext.Provider
|
||||
value={{
|
||||
recommendations,
|
||||
isRecommended,
|
||||
toggleRecommendation,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</RecommendationContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useRecommendations() {
|
||||
const context = useContext(RecommendationContext)
|
||||
if (!context) throw new Error("useRecommendations must be used within RecommendationProvider")
|
||||
return context
|
||||
}
|
||||
Reference in New Issue
Block a user