correct way fetching data
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 39s
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 39s
This commit is contained in:
parent
4c8dd1cc52
commit
4dc8fc2350
@ -37,9 +37,10 @@ export default function App() {
|
|||||||
if (session?.user) fetchProfile()
|
if (session?.user) fetchProfile()
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => {
|
const { data: { subscription } } = supabase.auth.onAuthStateChange((event, session) => {
|
||||||
setUser(session?.user ?? null)
|
setUser(session?.user ?? null)
|
||||||
if (session?.user) fetchProfile()
|
// Only fetch profile on actual sign-in, not token refresh
|
||||||
|
if (event === 'SIGNED_IN' && session?.user) fetchProfile()
|
||||||
})
|
})
|
||||||
|
|
||||||
return () => subscription.unsubscribe()
|
return () => subscription.unsubscribe()
|
||||||
|
|||||||
@ -6,29 +6,33 @@ type ProgressMap = Map<string, { seconds: number; percent: number }>
|
|||||||
|
|
||||||
let cachedProgress: ProgressMap = new Map()
|
let cachedProgress: ProgressMap = new Map()
|
||||||
let lastFetch = 0
|
let lastFetch = 0
|
||||||
|
let progressInFlight: Promise<void> | null = null
|
||||||
|
|
||||||
export function useListeningProgress() {
|
export function useListeningProgress() {
|
||||||
const user = useAuthStore((s) => s.user)
|
const userId = useAuthStore((s) => s.user?.id)
|
||||||
const [progress, setProgress] = useState<ProgressMap>(cachedProgress)
|
const [progress, setProgress] = useState<ProgressMap>(cachedProgress)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!user) {
|
if (!userId) {
|
||||||
cachedProgress = new Map()
|
cachedProgress = new Map()
|
||||||
setProgress(cachedProgress)
|
setProgress(cachedProgress)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refetch at most every 10s
|
// Refetch at most every 30s (was 10s — too aggressive)
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
if (now - lastFetch < 10_000 && cachedProgress.size > 0) {
|
if (now - lastFetch < 30_000 && cachedProgress.size > 0) {
|
||||||
setProgress(cachedProgress)
|
setProgress(cachedProgress)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
supabase
|
// Deduplicate concurrent calls
|
||||||
|
if (progressInFlight) return
|
||||||
|
|
||||||
|
progressInFlight = supabase
|
||||||
.from('listen_history')
|
.from('listen_history')
|
||||||
.select('podcast_id, progress_seconds, podcast:podcasts(duration_seconds)')
|
.select('podcast_id, progress_seconds, podcast:podcasts(duration_seconds)')
|
||||||
.eq('user_id', user.id)
|
.eq('user_id', userId)
|
||||||
.eq('completed', false)
|
.eq('completed', false)
|
||||||
.gt('progress_seconds', 5)
|
.gt('progress_seconds', 5)
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
@ -46,7 +50,8 @@ export function useListeningProgress() {
|
|||||||
lastFetch = Date.now()
|
lastFetch = Date.now()
|
||||||
setProgress(map)
|
setProgress(map)
|
||||||
})
|
})
|
||||||
}, [user])
|
.finally(() => { progressInFlight = null })
|
||||||
|
}, [userId])
|
||||||
|
|
||||||
return progress
|
return progress
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ export function useNotificationPolling() {
|
|||||||
|
|
||||||
fetch(user.id)
|
fetch(user.id)
|
||||||
|
|
||||||
intervalRef.current = setInterval(() => fetch(user.id), 30000)
|
intervalRef.current = setInterval(() => fetch(user.id), 60000)
|
||||||
|
|
||||||
function handleVisibility() {
|
function handleVisibility() {
|
||||||
if (document.visibilityState === 'visible' && user) fetch(user.id)
|
if (document.visibilityState === 'visible' && user) fetch(user.id)
|
||||||
|
|||||||
@ -73,15 +73,14 @@ export function Home() {
|
|||||||
setInProgress(items)
|
setInProgress(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch real stats for hero
|
// Compute stats for hero (non-logged-in) — 2 queries instead of 3
|
||||||
if (!user) {
|
if (!user) {
|
||||||
const [{ count: podcastCount }, { data: playsData }, { count: creatorCount }] = await Promise.all([
|
const [{ data: allPlays }, { count: creatorCount }] = await Promise.all([
|
||||||
supabase.from('podcasts').select('*', { count: 'exact', head: true }),
|
|
||||||
supabase.from('podcasts').select('plays_count'),
|
supabase.from('podcasts').select('plays_count'),
|
||||||
supabase.from('profiles').select('*', { count: 'exact', head: true }),
|
supabase.from('profiles').select('*', { count: 'exact', head: true }),
|
||||||
])
|
])
|
||||||
const totalPlays = playsData?.reduce((sum: number, p: any) => sum + (p.plays_count || 0), 0) || 0
|
const totalPlays = allPlays?.reduce((sum: number, p: any) => sum + (p.plays_count || 0), 0) || 0
|
||||||
setStats({ plays: totalPlays, creators: creatorCount || 0, podcasts: podcastCount || 0 })
|
setStats({ plays: totalPlays, creators: creatorCount || 0, podcasts: allPlays?.length || 0 })
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
|||||||
@ -13,6 +13,9 @@ interface AuthState {
|
|||||||
signOut: () => Promise<void>
|
signOut: () => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deduplicate fetchProfile — prevents double-fetch on init (getSession + onAuthStateChange)
|
||||||
|
let profileInFlight: Promise<void> | null = null
|
||||||
|
|
||||||
export const useAuthStore = create<AuthState>((set, get) => ({
|
export const useAuthStore = create<AuthState>((set, get) => ({
|
||||||
user: null,
|
user: null,
|
||||||
profile: null,
|
profile: null,
|
||||||
@ -24,8 +27,12 @@ export const useAuthStore = create<AuthState>((set, get) => ({
|
|||||||
|
|
||||||
fetchProfile: async () => {
|
fetchProfile: async () => {
|
||||||
const { user } = get()
|
const { user } = get()
|
||||||
if (!user) return set({ profile: null })
|
if (!user) { set({ profile: null }); return }
|
||||||
|
|
||||||
|
// If already fetching, reuse the same promise
|
||||||
|
if (profileInFlight) return profileInFlight
|
||||||
|
|
||||||
|
profileInFlight = (async () => {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('profiles')
|
.from('profiles')
|
||||||
.select('*')
|
.select('*')
|
||||||
@ -34,7 +41,8 @@ export const useAuthStore = create<AuthState>((set, get) => ({
|
|||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('Error fetching profile:', error)
|
console.error('Error fetching profile:', error)
|
||||||
return set({ profile: null })
|
set({ profile: null })
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-create profile if it doesn't exist (trigger may have failed)
|
// Auto-create profile if it doesn't exist (trigger may have failed)
|
||||||
@ -45,10 +53,14 @@ export const useAuthStore = create<AuthState>((set, get) => ({
|
|||||||
.insert({ id: user.id, username })
|
.insert({ id: user.id, username })
|
||||||
.select()
|
.select()
|
||||||
.single()
|
.single()
|
||||||
return set({ profile: newProfile })
|
set({ profile: newProfile })
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
set({ profile: data })
|
set({ profile: data })
|
||||||
|
})().finally(() => { profileInFlight = null })
|
||||||
|
|
||||||
|
return profileInFlight
|
||||||
},
|
},
|
||||||
|
|
||||||
signOut: async () => {
|
signOut: async () => {
|
||||||
|
|||||||
@ -10,11 +10,20 @@ interface NotificationsStore {
|
|||||||
markAllRead: (userId: string) => Promise<void>
|
markAllRead: (userId: string) => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deduplicate and throttle notification fetches (min 15s between fetches)
|
||||||
|
let notifInFlight: Promise<void> | null = null
|
||||||
|
let notifLastFetch = 0
|
||||||
|
|
||||||
export const useNotificationsStore = create<NotificationsStore>((set) => ({
|
export const useNotificationsStore = create<NotificationsStore>((set) => ({
|
||||||
notifications: [],
|
notifications: [],
|
||||||
unreadCount: 0,
|
unreadCount: 0,
|
||||||
|
|
||||||
fetch: async (userId) => {
|
fetch: async (userId) => {
|
||||||
|
const now = Date.now()
|
||||||
|
if (now - notifLastFetch < 15_000) return
|
||||||
|
if (notifInFlight) return notifInFlight
|
||||||
|
|
||||||
|
notifInFlight = (async () => {
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('notifications')
|
.from('notifications')
|
||||||
.select('*')
|
.select('*')
|
||||||
@ -27,6 +36,10 @@ export const useNotificationsStore = create<NotificationsStore>((set) => ({
|
|||||||
unreadCount: data.filter((n) => !n.read).length,
|
unreadCount: data.filter((n) => !n.read).length,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
notifLastFetch = Date.now()
|
||||||
|
})().finally(() => { notifInFlight = null })
|
||||||
|
|
||||||
|
return notifInFlight
|
||||||
},
|
},
|
||||||
|
|
||||||
markRead: async (id) => {
|
markRead: async (id) => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user