Files
ai-showcase-platform/contexts/auth-context.tsx

631 lines
17 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from "react"
interface User {
id: string
name: string
email: string
avatar?: string
department: string
role: "user" | "developer" | "admin"
join_date: string
favoriteApps: string[]
recentApps: string[]
total_likes: number
total_views: number
is_active: boolean
last_login?: string
phone?: string
location?: string
bio?: string
created_at: string
updated_at: string
}
interface AppLike {
appId: string
userId: string
likedAt: string
}
interface AuthContextType {
user: User | null
login: (email: string, password: string) => Promise<boolean>
register: (userData: RegisterData) => Promise<boolean>
logout: () => void
updateProfile: (userData: Partial<User>) => Promise<boolean>
toggleFavorite: (appId: string) => Promise<boolean>
isFavorite: (appId: string) => boolean
addToRecentApps: (appId: string) => void
getAppLikes: (appId: string) => number
incrementViewCount: (appId: string) => void
getViewCount: (appId: string) => number
updateAppRating: (appId: string, rating: number) => void
getAppRating: (appId: string) => number
canSubmitApp: () => boolean
canAccessAdmin: () => boolean
isLoading: boolean
isInitialized: boolean
// New like functionality
toggleLike: (appId: string) => Promise<boolean>
isLiked: (appId: string) => boolean
hasLikedToday: (appId: string) => boolean
getAppLikesInPeriod: (appId: string, startDate: string, endDate: string) => number
getUserLikeHistory: () => Array<{ appId: string; date: string }>
getLikeCount: (appId: string) => number
likeApp: (appId: string) => Promise<boolean>
}
interface RegisterData {
name: string
email: string
password: string
department: string
role?: string
}
const AuthContext = createContext<AuthContextType | undefined>(undefined)
// Mock users data with new role system
const mockUsers: User[] = []
// Global app likes counter - in a real app this would be in a database
const appLikesCounter: Record<string, number> = {}
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null)
const [isLoading, setIsLoading] = useState(false)
const [isInitialized, setIsInitialized] = useState(false)
// View count state with localStorage persistence
const [appViews, setAppViews] = useState<Record<string, number>>(() => {
if (typeof window !== "undefined") {
const saved = localStorage.getItem("appViews")
return saved ? JSON.parse(saved) : {}
}
return {}
})
// App ratings state with localStorage persistence
const [appRatings, setAppRatings] = useState<Record<string, number>>(() => {
if (typeof window !== "undefined") {
const saved = localStorage.getItem("appRatings")
return saved ? JSON.parse(saved) : {}
}
return {}
})
// App statistics state (from database)
const [appStats, setAppStats] = useState<Record<string, {
likesCount: number
viewsCount: number
rating: number
reviewsCount: number
}>>({})
// User interaction states (from database) - 不使用 localStorage總是從資料庫載入
const [userLikes, setUserLikes] = useState<Record<string, string[]>>({})
const [userFavorites, setUserFavorites] = useState<Record<string, string[]>>({})
const [appLikes, setAppLikes] = useState<Record<string, AppLike[]>>(() => {
if (typeof window !== "undefined") {
const saved = localStorage.getItem("appLikes")
return saved ? JSON.parse(saved) : {}
}
return {}
})
// New like system state with localStorage persistence
const [userLikesOld, setUserLikesOld] = useState<Record<string, Array<{ appId: string; date: string }>>>(() => {
if (typeof window !== "undefined") {
const saved = localStorage.getItem("userLikes")
return saved ? JSON.parse(saved) : {}
}
return {}
})
// App likes with date tracking
const [appLikesOld, setAppLikesOld] = useState<Record<string, Array<{ userId: string; date: string }>>>(() => {
if (typeof window !== "undefined") {
const saved = localStorage.getItem("appLikesOld")
return saved ? JSON.parse(saved) : {}
}
return {}
})
useEffect(() => {
// Check for stored user session only on client side
if (typeof window !== 'undefined') {
const storedUser = localStorage.getItem("user")
if (storedUser) {
const userData = JSON.parse(storedUser)
setUser(userData)
// 立即載入用戶的互動狀態,不使用 localStorage
fetchUserInteractions(userData.id)
}
}
setIsLoading(false)
setIsInitialized(true)
}, [])
// 當用戶登入時載入互動狀態
useEffect(() => {
if (user) {
fetchUserInteractions(user.id)
}
}, [user])
// Save old likes to localStorage when they change (保留舊系統的 localStorage)
useEffect(() => {
if (typeof window !== "undefined") {
localStorage.setItem("appLikesOld", JSON.stringify(appLikesOld))
}
}, [appLikesOld])
const login = async (email: string, password: string): Promise<boolean> => {
setIsLoading(true)
try {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, password }),
})
const data = await response.json()
if (data.success && data.user) {
// 添加前端需要的額外字段
const userWithExtras = {
...data.user,
favoriteApps: [],
recentApps: []
}
setUser(userWithExtras)
localStorage.setItem("user", JSON.stringify(userWithExtras))
// 載入用戶的互動狀態
await fetchUserInteractions(data.user.id)
setIsLoading(false)
return true
} else {
setIsLoading(false)
return false
}
} catch (error) {
console.error('登入錯誤:', error)
setIsLoading(false)
return false
}
}
const register = async (userData: RegisterData): Promise<boolean> => {
setIsLoading(true)
try {
const response = await fetch('/api/auth/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(userData),
})
const data = await response.json()
if (data.success && data.user) {
// 添加前端需要的額外字段
const userWithExtras = {
...data.user,
favoriteApps: [],
recentApps: []
}
setUser(userWithExtras)
localStorage.setItem("user", JSON.stringify(userWithExtras))
setIsLoading(false)
return true
} else {
setIsLoading(false)
return false
}
} catch (error) {
console.error('註冊錯誤:', error)
setIsLoading(false)
return false
}
}
const logout = () => {
setUser(null)
localStorage.removeItem("user")
}
const updateProfile = async (userData: Partial<User>): Promise<boolean> => {
if (!user) return false
setIsLoading(true)
try {
const response = await fetch('/api/auth/profile', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
userId: user.id,
...userData
}),
})
const data = await response.json()
if (data.success && data.user) {
// 保持前端需要的額外字段
const userWithExtras = {
...data.user,
favoriteApps: user.favoriteApps,
recentApps: user.recentApps
}
setUser(userWithExtras)
localStorage.setItem("user", JSON.stringify(userWithExtras))
setIsLoading(false)
return true
} else {
setIsLoading(false)
return false
}
} catch (error) {
console.error('更新資料錯誤:', error)
setIsLoading(false)
return false
}
}
const toggleFavorite = async (appId: string): Promise<boolean> => {
if (!user) return false
try {
const isFavorited = userFavorites[user.id]?.includes(appId) || false
if (isFavorited) {
// 移除收藏
const response = await fetch(`/api/apps/${appId}/favorite?userId=${user.id}`, {
method: 'DELETE'
})
if (response.ok) {
const data = await response.json()
if (data.success) {
// 更新本地狀態
setUserFavorites(prev => ({
...prev,
[user.id]: (prev[user.id] || []).filter(id => id !== appId)
}))
// 更新用戶的 favoriteApps
const updatedFavorites = user.favoriteApps.filter((id) => id !== appId)
await updateProfile({
favoriteApps: updatedFavorites,
})
return false
}
}
} else {
// 添加收藏
const response = await fetch(`/api/apps/${appId}/favorite`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
userId: user.id
})
})
if (response.ok) {
const data = await response.json()
if (data.success) {
// 更新本地狀態
setUserFavorites(prev => ({
...prev,
[user.id]: [...(prev[user.id] || []), appId]
}))
// 更新用戶的 favoriteApps
const updatedFavorites = [...user.favoriteApps, appId]
await updateProfile({
favoriteApps: updatedFavorites,
})
return true
}
} else if (response.status === 409) {
// 已經收藏過,更新本地狀態
setUserFavorites(prev => ({
...prev,
[user.id]: [...(prev[user.id] || []), appId]
}))
return true
}
}
} catch (error) {
console.error('切換收藏狀態錯誤:', error)
}
return false
}
const isFavorite = useCallback((appId: string): boolean => {
if (!user) return false
const userFavs = userFavorites[user.id] || []
return userFavs.includes(appId)
}, [user, userFavorites])
const isLiked = useCallback((appId: string): boolean => {
if (!user) return false
const userLikedApps = userLikes[user.id] || []
return userLikedApps.includes(appId)
}, [user, userLikes])
const addToRecentApps = (appId: string): void => {
if (!user) return
const updatedRecent = [appId, ...user.recentApps.filter((id) => id !== appId)].slice(0, 10)
updateProfile({
recentApps: updatedRecent,
})
}
const getAppLikes = (appId: string): number => {
return appStats[appId]?.likesCount || 0
}
// 從資料庫獲取應用統計數據
const fetchAppStats = async (appId: string) => {
try {
const response = await fetch(`/api/apps/${appId}/interactions`)
if (response.ok) {
const data = await response.json()
if (data.success) {
setAppStats(prev => ({
...prev,
[appId]: data.data
}))
}
}
} catch (error) {
console.error('獲取應用統計數據錯誤:', error)
}
}
// 從資料庫獲取用戶的按讚和收藏狀態
const fetchUserInteractions = async (userId: string) => {
try {
const response = await fetch(`/api/user/interactions?userId=${userId}`)
if (response.ok) {
const data = await response.json()
if (data.success) {
setUserLikes(prev => ({ ...prev, [userId]: data.data.likes }))
setUserFavorites(prev => ({ ...prev, [userId]: data.data.favorites }))
}
}
} catch (error) {
console.error('獲取用戶互動狀態錯誤:', error)
}
}
// New like functionality
const toggleLike = async (appId: string): Promise<boolean> => {
if (!user) return false
try {
const response = await fetch(`/api/apps/${appId}/interactions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action: 'like',
userId: user.id
})
})
if (response.ok) {
const data = await response.json()
if (data.success) {
// 更新應用統計數據
setAppStats(prev => ({
...prev,
[appId]: data.data
}))
// 使用 API 返回的按讚狀態更新本地狀態
const newLikedState = data.data.userLiked
setUserLikes(prev => ({
...prev,
[user.id]: newLikedState
? [...(prev[user.id] || []), appId]
: (prev[user.id] || []).filter(id => id !== appId)
}))
return newLikedState
}
}
} catch (error) {
console.error('切換按讚狀態錯誤:', error)
}
return false
}
const hasLikedTodayOld = (appId: string): boolean => {
if (!user) return false
const today = new Date().toISOString().split("T")[0]
const userLikeHistory = userLikesOld[user.id] || []
return userLikeHistory.some((like) => like.appId === appId && like.date === today)
}
const getAppLikesInPeriod = (appId: string, startDate: string, endDate: string): number => {
const appLikeHistory = appLikesOld[appId] || []
return appLikeHistory.filter((like) => {
return like.date >= startDate && like.date <= endDate
}).length
}
const getUserLikeHistory = (): Array<{ appId: string; date: string }> => {
if (!user) return []
return userLikesOld[user.id] || []
}
// View count functionality
const incrementViewCount = async (appId: string): Promise<void> => {
if (!user) return
try {
const response = await fetch(`/api/apps/${appId}/interactions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action: 'view',
userId: user.id
})
})
if (response.ok) {
const data = await response.json()
if (data.success) {
setAppStats(prev => ({
...prev,
[appId]: data.data
}))
}
}
} catch (error) {
console.error('增加觀看次數錯誤:', error)
}
}
const getViewCount = (appId: string): number => {
return appStats[appId]?.viewsCount || 0
}
// Rating functionality
const updateAppRating = (appId: string, rating: number): void => {
setAppRatings((prev) => {
const newRatings = { ...prev, [appId]: rating }
if (typeof window !== "undefined") {
localStorage.setItem("appRatings", JSON.stringify(newRatings))
}
return newRatings
})
}
const getAppRating = (appId: string): number => {
return appStats[appId]?.rating || 0
}
const getLikeCount = (appId: string): number => {
return appLikes[appId]?.length || 0
}
const hasLikedToday = (appId: string): boolean => {
if (!user) return false
const today = new Date().toISOString().split("T")[0]
return appLikes[appId]?.some((like) => like.userId === user.id && like.likedAt.startsWith(today)) || false
}
const likeApp = async (appId: string): Promise<boolean> => {
if (!user) return false
const today = new Date().toISOString().split("T")[0]
// Check if user already liked today
if (hasLikedToday(appId)) return false
const newLike: AppLike = {
appId,
userId: user.id,
likedAt: new Date().toISOString(),
}
setAppLikes((prev) => {
const updatedLikes = { ...prev }
if (!updatedLikes[appId]) {
updatedLikes[appId] = []
}
updatedLikes[appId] = [...updatedLikes[appId], newLike]
if (typeof window !== "undefined") {
localStorage.setItem("appLikes", JSON.stringify(updatedLikes))
}
return updatedLikes
})
return true
}
// Permission check functions
const canSubmitApp = (): boolean => {
return user?.role === "developer"
}
const canAccessAdmin = (): boolean => {
return user?.role === "admin"
}
return (
<AuthContext.Provider
value={{
user,
login,
register,
logout,
updateProfile,
toggleFavorite,
isFavorite,
addToRecentApps,
getAppLikes,
incrementViewCount,
getViewCount,
updateAppRating,
getAppRating,
canSubmitApp,
canAccessAdmin,
isLoading,
isInitialized,
// New like functionality
toggleLike,
isLiked,
hasLikedTodayOld,
getAppLikesInPeriod,
getUserLikeHistory,
getLikeCount,
hasLikedToday,
likeApp,
}}
>
{children}
</AuthContext.Provider>
)
}
export function useAuth() {
const context = useContext(AuthContext)
if (context === undefined) {
throw new Error("useAuth must be used within an AuthProvider")
}
return context
}