forked from koshikraj/ottopus
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprovider.tsx
More file actions
65 lines (58 loc) · 3.13 KB
/
Copy pathprovider.tsx
File metadata and controls
65 lines (58 loc) · 3.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
'use client'
import { useIdentityToken, usePrivy } from '@privy-io/react-auth'
import { createContext, useCallback, useContext, useEffect, useRef, useState, type ReactNode } from 'react'
import { usePrivyAvailable } from '@/components/auth'
import { linkToPlan, listPendingPlans, type Credentials } from '@/lib/api'
import { pollRequests, type RequestsState } from './poll'
interface Requests extends RequestsState {
refresh: () => void
open: (id: string) => Promise<string>
}
const initial: RequestsState = { plans: [], loaded: false, failed: false }
const Context = createContext<Requests>({ ...initial, refresh: () => {}, open: async () => { throw new Error('Sign in to open a request') } })
export const useRequests = () => useContext(Context)
export function RequestsProvider({ children }: { children: ReactNode }) {
return usePrivyAvailable() ? <Connected>{children}</Connected> : <Context.Provider value={{ ...initial, failed: true, refresh: () => {}, open: async () => { throw new Error('Sign in is unavailable') } }}>{children}</Context.Provider>
}
function Connected({ children }: { children: ReactNode }) {
const { ready, authenticated, user, getAccessToken } = usePrivy()
const { identityToken } = useIdentityToken()
const key = ready && authenticated ? user?.id : undefined
const [result, setResult] = useState<{ key: string; state: RequestsState } | null>(null)
const refreshRef = useRef<() => void>(() => {})
const credentials = useCallback(async (): Promise<Credentials> => {
const accessToken = await getAccessToken()
if (!accessToken) throw new Error('Sign in to open a request')
return { accessToken, identityToken }
}, [getAccessToken, identityToken])
useEffect(() => {
if (!key) return
const poll = pollRequests(async () => listPendingPlans(await credentials()), (state) => setResult({ key, state }))
refreshRef.current = () => { void poll.refresh() }
const focus = () => { if (document.visibilityState === 'visible') void poll.refresh() }
window.addEventListener('focus', focus)
document.addEventListener('visibilitychange', focus)
return () => {
poll.stop()
refreshRef.current = () => {}
window.removeEventListener('focus', focus)
document.removeEventListener('visibilitychange', focus)
}
}, [key, credentials])
const open = useCallback(async (id: string) => {
try {
const link = await linkToPlan(await credentials(), id)
// Keep navigation on this app; the service owns the opaque review token.
return `/review/${encodeURIComponent(link.token)}`
} finally {
refreshRef.current()
}
}, [credentials])
const state = key && result?.key === key ? result.state : initial
return <Context.Provider value={{ ...state, refresh: () => refreshRef.current(), open }}>{children}</Context.Provider>
}
export function RequestsBadge() {
const { plans } = useRequests()
if (!plans.length) return null
return <span aria-label={`${plans.length} pending requests`} className="ml-1 inline-flex min-w-5 items-center justify-center rounded-full bg-[var(--ot-plan-bg)] px-1.5 py-0.5 text-[10px] font-semibold text-[var(--ot-plan-text)]">{plans.length}</span>
}