Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
mastodon
GitHub Repository: mastodon/joinmastodon
Path: blob/main/donate/DonatePopup.tsx
1006 views
1
import {
2
PropsWithChildren,
3
useCallback,
4
useEffect,
5
useRef,
6
useState,
7
} from "react"
8
import { FormattedMessage } from "react-intl"
9
import { Dialog, DialogBackdrop, DialogPanel } from "@headlessui/react"
10
import classNames from "classnames"
11
12
import { Button, ButtonProps } from "../components/Button"
13
import CloseIcon from "../public/icons/close.svg?inline"
14
import LoadingIcon from "../public/icons/loading.svg?inline"
15
16
import { isPopupMessage } from "./utils"
17
18
import type { Step } from "./types"
19
20
export function DonatePopup({
21
children,
22
...buttonProps
23
}: PropsWithChildren<ButtonProps>) {
24
const iframeRef = useRef<HTMLIFrameElement>(null)
25
26
const [open, setOpen] = useState(false)
27
const [currentStep, setCurrentStep] = useState<Step>("loading")
28
29
const handleOpen = useCallback(() => {
30
setOpen(true)
31
setCurrentStep("loading")
32
}, [])
33
const handleClose = useCallback(() => {
34
setOpen(false)
35
setTimeout(() => setCurrentStep("loading"), 300)
36
}, [])
37
38
useEffect(() => {
39
const handleMessage = (event: MessageEvent) => {
40
const message = event.data
41
if (!isPopupMessage(message)) {
42
return
43
}
44
45
switch (message.action) {
46
case "widget-loaded":
47
setCurrentStep("choose")
48
break
49
case "checkout-loaded":
50
setCurrentStep("checkout")
51
break
52
case "complete-loaded":
53
setCurrentStep("complete")
54
break
55
case "close":
56
handleClose()
57
break
58
}
59
}
60
window.addEventListener("message", handleMessage)
61
return () => {
62
window.removeEventListener("message", handleMessage)
63
}
64
}, [handleClose])
65
66
return (
67
<>
68
<Button {...buttonProps} onClick={handleOpen}>
69
{children}
70
</Button>
71
<Dialog
72
open={open}
73
onClose={handleClose}
74
transition
75
className="sm:transition-opacity sm:opacity-0 sm:data-[open]:opacity-100"
76
>
77
<DialogBackdrop className="fixed inset-0 bg-black/30" />
78
<div
79
className={classNames(
80
"fixed inset-0 flex w-screen items-end sm:items-center justify-center z-20",
81
"transition-transform"
82
)}
83
>
84
<DialogPanel
85
className={
86
"w-full max-w-md bg-white rounded-md max-sm:rounded-b-none " +
87
"overflow-hidden flex items-center justify-center relative"
88
}
89
>
90
<iframe
91
src="/donate"
92
className={classNames(
93
"w-full transition-transform",
94
currentStep === "loading" && "hidden",
95
currentStep === "choose" && "h-[30rem]",
96
currentStep === "checkout" && "h-[40rem]",
97
currentStep === "complete" && "h-[30rem]"
98
)}
99
ref={iframeRef}
100
></iframe>
101
{currentStep === "loading" && (
102
<p className="flex gap-2 items-center justify-center text-gray-2 p-4 h-[30rem]">
103
<LoadingIcon className="motion-safe:animate-spin size-5" />
104
<FormattedMessage
105
id="donate_widget.loading"
106
defaultMessage="Loading&hellip;"
107
/>
108
</p>
109
)}
110
<button
111
className={classNames("absolute top-2 right-2 text-gray-2")}
112
onClick={handleClose}
113
>
114
<CloseIcon className="size-5" />
115
</button>
116
</DialogPanel>
117
</div>
118
</Dialog>
119
</>
120
)
121
}
122
123