"use client" import type React from "react" import { createContext, useContext, useState, useCallback, useEffect } from "react" import type { FamilyMember, FamilyTreeData } from "@/types/family" import { mockFamilyData } from "@/lib/mock-data" import { db } from "@/lib/db" import { useLiveQuery } from "dexie-react-hooks" import { setupDailyNotificationCheck } from "@/lib/notifications" import { logActivity } from "@/lib/activity-logger" interface FamilyContextType { treeData: FamilyTreeData 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 [searchResults, setSearchResults] = useState([]) const [isInitialized, setIsInitialized] = useState(false) const [highlightedMemberId, setHighlightedMemberId] = useState(null) // Live query to get all members const members = useLiveQuery(() => db.members.toArray()) // Live query to get rootId const rootIdSetting = useLiveQuery(() => db.settings.get("rootId")) // Initialize DB with mock data if empty useEffect(() => { const initDb = async () => { const count = await db.members.count() if (count === 0) { await db.transaction("rw", db.members, db.settings, async () => { await db.members.bulkAdd(Object.values(mockFamilyData.members)) await db.settings.put({ key: "rootId", value: mockFamilyData.rootId }) }) } setIsInitialized(true) } initDb() }, []) // 设置通知检查 useEffect(() => { if (isInitialized && members && members.length > 0) { // 检查通知权限并设置每日检查 if ("Notification" in window && Notification.permission === "granted") { setupDailyNotificationCheck(members) } } }, [isInitialized, members]) // Construct treeData from DB results const treeData: FamilyTreeData = { rootId: rootIdSetting?.value || "", members: (members || []).reduce( (acc, member) => { acc[member.id] = member return acc }, {} as Record, ), } const getMember = useCallback( (id: string) => { return treeData.members[id] }, [treeData], ) const addMember = useCallback(async (member: FamilyMember) => { await db.transaction("rw", db.members, db.activityLogs, async () => { // Add the member await db.members.add(member) // Update relationships (Father) if (member.fatherId) { const father = await db.members.get(member.fatherId) if (father && !father.childrenIds.includes(member.id)) { await db.members.update(member.fatherId, { childrenIds: [...father.childrenIds, member.id], }) } } // Update relationships (Mother) if (member.motherId) { const mother = await db.members.get(member.motherId) if (mother && !mother.childrenIds.includes(member.id)) { await db.members.update(member.motherId, { childrenIds: [...mother.childrenIds, member.id], }) } } // Update relationships (Spouses) if (member.spouseIds && member.spouseIds.length > 0) { for (const spouseId of member.spouseIds) { const spouse = await db.members.get(spouseId) if (spouse && !spouse.spouseIds.includes(member.id)) { await db.members.update(spouseId, { spouseIds: [...spouse.spouseIds, member.id], }) } } } // 记录日志(在事务内) await logActivity("create", "member", member.id, member.fullName) }) }, []) const updateMember = useCallback(async (id: string, updates: Partial) => { await db.transaction("rw", db.members, db.activityLogs, async () => { const member = await db.members.get(id) await db.members.update(id, { ...updates, updatedAt: new Date().toISOString(), }) // 记录日志(在事务内) if (member) { await logActivity("update", "member", id, member.fullName, updates) } }) }, []) const deleteMember = useCallback(async (id: string) => { await db.transaction("rw", db.members, db.settings, db.activityLogs, async () => { const member = await db.members.get(id) // Remove from DB await db.members.delete(id) // Note: In a real app, we should also remove references to this ID from other members' // childrenIds, spouseIds, etc. For now, we'll keep it simple as per the original implementation. // If we deleted the root, we might want to handle that, but for now let's leave it. // 记录日志(在事务内) if (member) { await logActivity("delete", "member", id, member.fullName) } }) }, []) const loadData = useCallback(async (data: FamilyTreeData) => { await db.transaction("rw", db.members, db.settings, db.activityLogs, async () => { await db.members.clear() await db.members.bulkAdd(Object.values(data.members)) await db.settings.put({ key: "rootId", value: data.rootId }) // 记录日志(在事务内) await logActivity("import", "settings", undefined, "导入家族数据", { memberCount: Object.keys(data.members).length }) }) }, []) const resetData = useCallback(async () => { await loadData(mockFamilyData) }, [loadData]) 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 (legacy support for the file input in settings if needed, // but we should prefer using the context method) 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 }