forked from ZyntariHQ/Invoisio
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathloader-provider.tsx
More file actions
68 lines (57 loc) · 1.93 KB
/
Copy pathloader-provider.tsx
File metadata and controls
68 lines (57 loc) · 1.93 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
66
67
68
"use client"
import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"
import Loader from "./loader"
interface LoaderContextValue {
show: () => void
hide: () => void
withLoader<T>(fn: () => Promise<T>): Promise<T>
isLoading: boolean
}
const LoaderContext = createContext<LoaderContextValue | null>(null)
export function useAppLoader(): LoaderContextValue {
const ctx = useContext(LoaderContext)
if (!ctx) {
throw new Error("useAppLoader must be used within LoaderProvider")
}
return ctx
}
export default function LoaderProvider({ children }: { children: React.ReactNode }) {
const [isLoading, setIsLoading] = useState(false)
const show = useCallback(() => setIsLoading(true), [])
const hide = useCallback(() => setIsLoading(false), [])
const withLoader = useCallback(async <T,>(fn: () => Promise<T>): Promise<T> => {
show()
try {
return await fn()
} finally {
// small delay to prevent flicker
setTimeout(hide, 180)
}
}, [show, hide])
// Show loader briefly when opening a new tab/window (redirection UX)
useEffect(() => {
const originalOpen = window.open
window.open = (...args) => {
setIsLoading(true)
const ret = originalOpen(...args)
setTimeout(() => setIsLoading(false), 700)
return ret
}
return () => { window.open = originalOpen }
}, [])
// Prevent background scrolling when loader is shown
useEffect(() => {
if (isLoading) {
const orig = document.documentElement.style.overflow
document.documentElement.style.overflow = "hidden"
return () => { document.documentElement.style.overflow = orig }
}
}, [isLoading])
const value = useMemo(() => ({ show, hide, withLoader, isLoading }), [show, hide, withLoader, isLoading])
return (
<LoaderContext.Provider value={value}>
{children}
{isLoading && <Loader fullscreen />}
</LoaderContext.Provider>
)
}