"use client" import type React from "react" import { createContext, useContext, useState, useCallback, useEffect } from "react" import type { FamilyMember, FamilyTreeData } from "@/types/family" import { useSession } from "next-auth/react" import { useSearchParams } from "next/navigation" interface FamilyTreeInfo { id: string name: string description: string | null ownerId: string rootMemberId: string | null } interface FamilyContextType { treeData: FamilyTreeData currentTree: FamilyTreeInfo | null getMember: (id: string) => FamilyMember | undefined addMember: (member: FamilyMember) => Promise updateMember: (id: string, updates: Partial) => Promise deleteMember: (id: string) => Promise searchResults: FamilyMember[] searchMembers: (query: string) => void loadData: (data: FamilyTreeData) => Promise resetData: () => Promise isLoading: boolean highlightedMemberId: string | null setHighlightedMemberId: (id: string | null) => void } const FamilyContext = createContext(undefined) export function FamilyProvider({ children }: { children: React.ReactNode }) { const { data: session } = useSession() const searchParams = useSearchParams() const [searchResults, setSearchResults] = useState([]) const [isLoading, setIsLoading] = useState(true) const [highlightedMemberId, setHighlightedMemberId] = useState(null) const [treeData, setTreeData] = useState({ rootId: "", members: {}, }) const [currentTree, setCurrentTree] = useState(null) // 监听 URL 和 Session 变化,加载家族树 useEffect(() => { const treeId = searchParams.get('treeId') console.log('FamilyContext - Effect triggered, treeId:', treeId, 'currentTree:', currentTree?.id) // 如果 session 不存在,清除所有数据 if (!session?.user?.id) { console.log('FamilyContext - No session, clearing data') setCurrentTree(null) setTreeData({ rootId: "", members: {} }) setIsLoading(false) return } // 如果 treeId 与当前树相同,跳过 if (treeId && treeId === currentTree?.id) { console.log('FamilyContext - Same tree, skipping') return } // 如果没有 treeId 且已有树,跳过 if (!treeId && currentTree) { console.log('FamilyContext - No treeId but has currentTree, skipping') return } // 需要加载新树 const targetTreeId = treeId || 'first' console.log('FamilyContext - Loading tree:', targetTreeId) setIsLoading(true) const fetchUrl = targetTreeId === 'first' ? '/api/trees' : `/api/trees/${targetTreeId}` console.log('FamilyContext - Fetching URL:', fetchUrl) fetch(fetchUrl) .then(res => res.json()) .then(data => { let tree = null if (targetTreeId === 'first') { tree = data.trees?.[0] } else { tree = data.tree } console.log('FamilyContext - Fetched tree:', tree?.name, tree?.id) if (tree) { setCurrentTree(tree) // 加载该树的成员数据 return fetch(`/api/trees/${tree.id}/members`) .then(res => res.json()) .then(memberData => { console.log('FamilyContext - Loaded members:', memberData.members?.length) if (memberData.members) { const membersMap = memberData.members.reduce( (acc: Record, member: FamilyMember) => { acc[member.id] = member return acc }, {} ) setTreeData({ rootId: memberData.rootId || "", members: membersMap, }) } }) } else { // 没找到树(可能是被删除了,或者没有权限) console.log('FamilyContext - Tree not found') setCurrentTree(null) setTreeData({ rootId: "", members: {} }) } }) .catch(err => { console.error('FamilyContext - Load failed:', err) setCurrentTree(null) setTreeData({ rootId: "", members: {} }) }) .finally(() => setIsLoading(false)) }, [searchParams, session?.user?.id]) const getMember = useCallback( (id: string) => { return treeData.members[id] }, [treeData], ) const addMember = useCallback(async (member: FamilyMember) => { if (!currentTree?.id) throw new Error('没有选择家族树') const response = await fetch(`/api/trees/${currentTree.id}/members`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(member), }) if (!response.ok) { const error = await response.json() throw new Error(error.error || '添加成员失败') } const data = await response.json() // 更新本地状态 setTreeData(prev => ({ ...prev, members: { ...prev.members, [data.member.id]: data.member, }, })) }, [currentTree?.id]) const updateMember = useCallback(async (id: string, updates: Partial) => { if (!currentTree?.id) throw new Error('没有选择家族树') const response = await fetch(`/api/trees/${currentTree.id}/members/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(updates), }) if (!response.ok) { const error = await response.json() throw new Error(error.error || '更新成员失败') } const data = await response.json() // 更新本地状态 setTreeData(prev => ({ ...prev, members: { ...prev.members, [id]: data.member, }, })) }, [currentTree?.id]) const deleteMember = useCallback(async (id: string) => { if (!currentTree?.id) throw new Error('没有选择家族树') const response = await fetch(`/api/trees/${currentTree.id}/members/${id}`, { method: 'DELETE', }) if (!response.ok) { const error = await response.json() throw new Error(error.error || '删除成员失败') } // 更新本地状态 setTreeData(prev => { const newMembers = { ...prev.members } delete newMembers[id] return { ...prev, members: newMembers, } }) }, [currentTree?.id]) const loadData = useCallback(async (data: FamilyTreeData) => { if (!currentTree?.id) throw new Error('没有选择家族树') // 导入数据到服务器 const response = await fetch(`/api/trees/${currentTree.id}/import`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }) if (!response.ok) { const error = await response.json() throw new Error(error.error || '导入数据失败') } // 重新加载数据 const membersResponse = await fetch(`/api/trees/${currentTree.id}/members`) const membersData = await membersResponse.json() if (membersData.members) { const membersMap = membersData.members.reduce( (acc: Record, member: FamilyMember) => { acc[member.id] = member return acc }, {} ) setTreeData({ rootId: membersData.rootId || "", members: membersMap, }) } }, [currentTree?.id]) const resetData = useCallback(async () => { // 重置为空数据 setTreeData({ rootId: "", members: {} }) }, []) const searchMembers = useCallback( (query: string) => { if (!query.trim()) { setSearchResults([]) return } const lowerQuery = query.toLowerCase() // Perform search on the in-memory treeData for simplicity and speed // (Dexie also supports querying, but we already have the data loaded) const results = Object.values(treeData.members).filter( (member) => member.fullName.includes(lowerQuery) || member.givenName.includes(lowerQuery) || member.courtesyName?.includes(lowerQuery) || member.artName?.includes(lowerQuery), ) setSearchResults(results) }, [treeData], ) // Listen for import events useEffect(() => { const handleImport = (e: CustomEvent) => { loadData(e.detail) } window.addEventListener("family-data-import" as any, handleImport as any) return () => window.removeEventListener("family-data-import" as any, handleImport as any) }, [loadData]) return ( {children} ) } export function useFamily() { const context = useContext(FamilyContext) if (context === undefined) { throw new Error("useFamily must be used within a FamilyProvider") } return context }