432 lines
12 KiB
TypeScript
432 lines
12 KiB
TypeScript
import { ForumCategory, ForumPost, ForumTopic } from '@/proxy/forum/forum'
|
|
import { CreateCategoryRequest, CreatePostRequest, CreateTopicRequest, forumService } from '@/services/forum.service'
|
|
import { useState, useEffect } from 'react'
|
|
|
|
export function useForumData() {
|
|
const [categories, setCategories] = useState<ForumCategory[]>([])
|
|
const [topics, setTopics] = useState<ForumTopic[]>([])
|
|
const [posts, setPosts] = useState<ForumPost[]>([])
|
|
const [loading, setLoading] = useState(false)
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
// Load initial data
|
|
useEffect(() => {
|
|
loadCategories()
|
|
loadTopics()
|
|
loadPosts()
|
|
}, [])
|
|
|
|
const loadCategories = async () => {
|
|
try {
|
|
setLoading(true)
|
|
const response = await forumService.getCategories()
|
|
setCategories(response.items)
|
|
} catch (err) {
|
|
setError('Failed to load categories')
|
|
console.error('Error loading categories:', err)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const loadTopics = async (categoryId?: string) => {
|
|
try {
|
|
setLoading(true)
|
|
const response = await forumService.getTopics({ categoryId })
|
|
setTopics(response.items)
|
|
} catch (err) {
|
|
setError('Failed to load topics')
|
|
console.error('Error loading topics:', err)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const loadPosts = async (topicId?: string) => {
|
|
try {
|
|
setLoading(true)
|
|
const response = await forumService.getPosts({ topicId })
|
|
setPosts(response.items)
|
|
} catch (err) {
|
|
setError('Failed to load posts')
|
|
console.error('Error loading posts:', err)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
// Category operations
|
|
const createCategory = async (categoryData: CreateCategoryRequest) => {
|
|
try {
|
|
setLoading(true)
|
|
const newCategory = await forumService.createCategory(categoryData)
|
|
setCategories((prev) => [...prev, newCategory])
|
|
return newCategory
|
|
} catch (err) {
|
|
setError('Failed to create category')
|
|
console.error('Error creating category:', err)
|
|
throw err
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const updateCategory = async (id: string, updates: Partial<ForumCategory>) => {
|
|
try {
|
|
setLoading(true)
|
|
const updatedCategory = await forumService.updateCategory(id, updates)
|
|
setCategories((prev) => prev.map((cat) => (cat.id === id ? updatedCategory : cat)))
|
|
return updatedCategory
|
|
} catch (err) {
|
|
setError('Failed to update category')
|
|
console.error('Error updating category:', err)
|
|
throw err
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const updateCategoryLockState = async (id: string) => {
|
|
await forumService.updateCategoryLockState(id)
|
|
await loadCategories() // refresh after update
|
|
}
|
|
|
|
const updateCategoryActiveState = async (id: string) => {
|
|
await forumService.updateCategoryActiveState(id)
|
|
await loadCategories() // refresh after update
|
|
}
|
|
|
|
const deleteCategory = async (id: string) => {
|
|
try {
|
|
setLoading(true)
|
|
await forumService.deleteCategory(id)
|
|
setCategories((prev) => prev.filter((cat) => cat.id !== id))
|
|
// Also remove related topics and posts
|
|
const topicsToDelete = topics.filter((topic) => topic.categoryId === id)
|
|
const topicIds = topicsToDelete.map((t) => t.id)
|
|
setTopics((prev) => prev.filter((topic) => topic.categoryId !== id))
|
|
setPosts((prev) => prev.filter((post) => !topicIds.includes(post.topicId)))
|
|
} catch (err) {
|
|
setError('Failed to delete category')
|
|
console.error('Error deleting category:', err)
|
|
throw err
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
// Topic operations
|
|
const createTopic = async (topicData: CreateTopicRequest) => {
|
|
try {
|
|
setLoading(true)
|
|
const newTopic = await forumService.createTopic(topicData)
|
|
setTopics((prev) => [...prev, newTopic])
|
|
// Update category topic count
|
|
setCategories((prev) =>
|
|
prev.map((cat) =>
|
|
cat.id === topicData.categoryId ? { ...cat, topicCount: cat.topicCount + 1 } : cat,
|
|
),
|
|
)
|
|
return newTopic
|
|
} catch (err) {
|
|
setError('Failed to create topic')
|
|
console.error('Error creating topic:', err)
|
|
throw err
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const updateTopic = async (id: string, updates: Partial<ForumTopic>) => {
|
|
try {
|
|
setLoading(true)
|
|
const updatedTopic = await forumService.updateTopic(id, updates)
|
|
setTopics((prev) => prev.map((topic) => (topic.id === id ? updatedTopic : topic)))
|
|
return updatedTopic
|
|
} catch (err) {
|
|
setError('Failed to update topic')
|
|
console.error('Error updating topic:', err)
|
|
throw err
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const deleteTopic = async (id: string) => {
|
|
try {
|
|
setLoading(true)
|
|
const topic = topics.find((t) => t.id === id)
|
|
await forumService.deleteTopic(id)
|
|
setTopics((prev) => prev.filter((t) => t.id !== id))
|
|
setPosts((prev) => prev.filter((post) => post.topicId !== id))
|
|
|
|
// Update category counts
|
|
if (topic) {
|
|
setCategories((prev) =>
|
|
prev.map((cat) =>
|
|
cat.id === topic.categoryId
|
|
? {
|
|
...cat,
|
|
topicCount: Math.max(0, cat.topicCount - 1),
|
|
postCount: Math.max(
|
|
0,
|
|
cat.postCount - posts.filter((p) => p.topicId === id).length,
|
|
),
|
|
}
|
|
: cat,
|
|
),
|
|
)
|
|
}
|
|
} catch (err) {
|
|
setError('Failed to delete topic')
|
|
console.error('Error deleting topic:', err)
|
|
throw err
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const pinTopic = async (id: string) => {
|
|
try {
|
|
const updatedTopic = await forumService.pinTopic(id)
|
|
setTopics((prev) => prev.map((topic) => (topic.id === id ? updatedTopic : topic)))
|
|
return updatedTopic
|
|
} catch (err) {
|
|
setError('Failed to pin topic')
|
|
console.error('Error pinning topic:', err)
|
|
throw err
|
|
}
|
|
}
|
|
|
|
const unpinTopic = async (id: string) => {
|
|
try {
|
|
const updatedTopic = await forumService.unpinTopic(id)
|
|
setTopics((prev) => prev.map((topic) => (topic.id === id ? updatedTopic : topic)))
|
|
return updatedTopic
|
|
} catch (err) {
|
|
setError('Failed to unpin topic')
|
|
console.error('Error unpinning topic:', err)
|
|
throw err
|
|
}
|
|
}
|
|
|
|
const lockTopic = async (id: string) => {
|
|
try {
|
|
const updatedTopic = await forumService.lockTopic(id)
|
|
setTopics((prev) => prev.map((topic) => (topic.id === id ? updatedTopic : topic)))
|
|
return updatedTopic
|
|
} catch (err) {
|
|
setError('Failed to lock topic')
|
|
console.error('Error locking topic:', err)
|
|
throw err
|
|
}
|
|
}
|
|
|
|
const unlockTopic = async (id: string) => {
|
|
try {
|
|
const updatedTopic = await forumService.unlockTopic(id)
|
|
setTopics((prev) => prev.map((topic) => (topic.id === id ? updatedTopic : topic)))
|
|
return updatedTopic
|
|
} catch (err) {
|
|
setError('Failed to unlock topic')
|
|
console.error('Error unlocking topic:', err)
|
|
throw err
|
|
}
|
|
}
|
|
|
|
const solvedTopic = async (id: string) => {
|
|
try {
|
|
const updatedTopic = await forumService.solvedTopic(id)
|
|
setTopics((prev) => prev.map((topic) => (topic.id === id ? updatedTopic : topic)))
|
|
return updatedTopic
|
|
} catch (err) {
|
|
setError('Failed to mark topic as solved')
|
|
console.error('Error marking topic as solved:', err)
|
|
throw err
|
|
}
|
|
}
|
|
|
|
const unsolvedTopic = async (id: string) => {
|
|
try {
|
|
const updatedTopic = await forumService.unsolvedTopic(id)
|
|
setTopics((prev) => prev.map((topic) => (topic.id === id ? updatedTopic : topic)))
|
|
return updatedTopic
|
|
} catch (err) {
|
|
setError('Failed to mark topic as unsolved')
|
|
console.error('Error marking topic as unsolved:', err)
|
|
throw err
|
|
}
|
|
}
|
|
|
|
// Post operations
|
|
const createPost = async (postData: CreatePostRequest) => {
|
|
try {
|
|
setLoading(true)
|
|
const newPost = await forumService.createPost(postData)
|
|
setPosts((prev) => [...prev, newPost])
|
|
|
|
// Update topic and category post counts
|
|
const topic = topics.find((t) => t.id === postData.topicId)
|
|
if (topic) {
|
|
setTopics((prev) =>
|
|
prev.map((t) => (t.id === postData.topicId ? { ...t, replyCount: t.replyCount + 1 } : t)),
|
|
)
|
|
|
|
setCategories((prev) =>
|
|
prev.map((cat) =>
|
|
cat.id === topic.categoryId ? { ...cat, postCount: cat.postCount + 1 } : cat,
|
|
),
|
|
)
|
|
}
|
|
return newPost
|
|
} catch (err) {
|
|
setError('Failed to create post')
|
|
console.error('Error creating post:', err)
|
|
throw err
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const updatePost = async (id: string, updates: Partial<ForumPost>) => {
|
|
try {
|
|
setLoading(true)
|
|
const updatedPost = await forumService.updatePost(id, updates)
|
|
setPosts((prev) => prev.map((post) => (post.id === id ? updatedPost : post)))
|
|
return updatedPost
|
|
} catch (err) {
|
|
setError('Failed to update post')
|
|
console.error('Error updating post:', err)
|
|
throw err
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const deletePost = async (id: string) => {
|
|
try {
|
|
setLoading(true)
|
|
const post = posts.find((p) => p.id === id)
|
|
await forumService.deletePost(id)
|
|
setPosts((prev) => prev.filter((p) => p.id !== id))
|
|
|
|
// Update topic and category counts
|
|
if (post) {
|
|
const topic = topics.find((t) => t.id === post.topicId)
|
|
if (topic) {
|
|
setTopics((prev) =>
|
|
prev.map((t) =>
|
|
t.id === post.topicId ? { ...t, replyCount: Math.max(0, t.replyCount - 1) } : t,
|
|
),
|
|
)
|
|
|
|
setCategories((prev) =>
|
|
prev.map((cat) =>
|
|
cat.id === topic.categoryId
|
|
? { ...cat, postCount: Math.max(0, cat.postCount - 1) }
|
|
: cat,
|
|
),
|
|
)
|
|
}
|
|
}
|
|
} catch (err) {
|
|
setError('Failed to delete post')
|
|
console.error('Error deleting post:', err)
|
|
throw err
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const likePost = async (id: string) => {
|
|
try {
|
|
const updatedPost = await forumService.likePost(id)
|
|
setPosts((prev) => prev.map((post) => (post.id === id ? updatedPost : post)))
|
|
return updatedPost
|
|
} catch (err) {
|
|
setError('Failed to like post')
|
|
console.error('Error liking post:', err)
|
|
throw err
|
|
}
|
|
}
|
|
|
|
const unlikePost = async (id: string) => {
|
|
try {
|
|
const updatedPost = await forumService.unlikePost(id)
|
|
setPosts((prev) => prev.map((post) => (post.id === id ? updatedPost : post)))
|
|
return updatedPost
|
|
} catch (err) {
|
|
setError('Failed to unlike post')
|
|
console.error('Error unliking post:', err)
|
|
throw err
|
|
}
|
|
}
|
|
|
|
const markPostAsAcceptedAnswer = async (id: string) => {
|
|
try {
|
|
const updatedPost = await forumService.markPostAsAcceptedAnswer(id)
|
|
setPosts((prev) => prev.map((post) => (post.id === id ? updatedPost : post)))
|
|
return updatedPost
|
|
} catch (err) {
|
|
setError('Failed to mark post as accepted answer')
|
|
console.error('Error marking post as accepted answer:', err)
|
|
throw err
|
|
}
|
|
}
|
|
|
|
const unmarkPostAsAcceptedAnswer = async (id: string) => {
|
|
try {
|
|
const updatedPost = await forumService.unmarkPostAsAcceptedAnswer(id)
|
|
setPosts((prev) => prev.map((post) => (post.id === id ? updatedPost : post)))
|
|
return updatedPost
|
|
} catch (err) {
|
|
setError('Failed to unmark post as accepted answer')
|
|
console.error('Error unmarking post as accepted answer:', err)
|
|
throw err
|
|
}
|
|
}
|
|
|
|
return {
|
|
// Data
|
|
categories,
|
|
topics,
|
|
posts,
|
|
loading,
|
|
error,
|
|
|
|
// Load functions
|
|
loadCategories,
|
|
loadTopics,
|
|
loadPosts,
|
|
|
|
// Category operations
|
|
createCategory,
|
|
updateCategory,
|
|
updateCategoryLockState,
|
|
updateCategoryActiveState,
|
|
deleteCategory,
|
|
|
|
// Topic operations
|
|
createTopic,
|
|
updateTopic,
|
|
deleteTopic,
|
|
pinTopic,
|
|
unpinTopic,
|
|
lockTopic,
|
|
unlockTopic,
|
|
solvedTopic,
|
|
unsolvedTopic,
|
|
|
|
// Post operations
|
|
createPost,
|
|
updatePost,
|
|
deletePost,
|
|
likePost,
|
|
unlikePost,
|
|
markPostAsAcceptedAnswer,
|
|
unmarkPostAsAcceptedAnswer,
|
|
|
|
// Utility
|
|
clearError: () => setError(null),
|
|
}
|
|
}
|