Skip to content

Commit c4d28c9

Browse files
authored
Merge pull request #224 from oasisprotocol/ml/survey-popup
Add survey popup
2 parents 82d9b40 + f0d84a4 commit c4d28c9

File tree

5 files changed

+129
-3
lines changed

5 files changed

+129
-3
lines changed

src/components/RootLayout/index.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { ErrorBoundary } from '../ErrorBoundary'
99
import { ErrorDisplay } from '../ErrorDisplay'
1010
import { RoflAppBackendAuthProvider } from '../../contexts/RoflAppBackendAuth/Provider'
1111
import { RainbowKitProviderWithAuth } from '../../components/RainbowKitProviderWithAuth'
12+
import { SurveyPopupProvider } from '../../contexts/SurveyPopupProvider'
1213

1314
// TODO: use import { Toaster } from '@oasisprotocol/ui-library' when it doesn't break
1415
const Toaster = ({ ...props }: ToasterProps) => {
@@ -42,9 +43,11 @@ export const RootLayout: FC = () => (
4243
<RoflAppBackendAuthProvider>
4344
<RainbowKitProviderWithAuth>
4445
<ErrorBoundary fallbackRender={fallbackRender}>
45-
<FathomAnalytics />
46-
<Toaster />
47-
<Outlet />
46+
<SurveyPopupProvider>
47+
<FathomAnalytics />
48+
<Toaster />
49+
<Outlet />
50+
</SurveyPopupProvider>
4851
</ErrorBoundary>
4952
</RainbowKitProviderWithAuth>
5053
</RoflAppBackendAuthProvider>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { type FC } from 'react'
2+
import { ExternalLink, X } from 'lucide-react'
3+
import { Card, CardContent } from '@oasisprotocol/ui-library/src/components/ui/card'
4+
import { Button } from '@oasisprotocol/ui-library/src/components/ui/button'
5+
import OasisLogoBlue from '../icons/OasisLogoBlue.svg'
6+
import { toast } from 'sonner'
7+
8+
const SURVEY_LINK = 'https://forms.gle/hiaMTdEZ43PM72QW7'
9+
10+
interface SurveyPopupProps {
11+
onClose: () => void
12+
isVisible: boolean
13+
}
14+
15+
export const SurveyPopup: FC<SurveyPopupProps> = ({ onClose, isVisible }) => {
16+
// This is not the most reliable check, but simple enough opposed to hijacking the API, to count active toasts
17+
const hasActiveToasts = toast.getHistory().length > 0
18+
19+
if (!isVisible || hasActiveToasts) return null
20+
21+
return (
22+
<div className="fixed bottom-5 right-5 md:right-12.5 z-50 max-w-[calc(100vw-2.5rem)] md:max-w-none">
23+
<Card className="w-full md:w-[420px] rounded-xl bg-background border shadow-lg animate-in slide-in-from-bottom-4 fade-in-0 duration-300">
24+
<CardContent className="flex flex-col gap-4 md:gap-6">
25+
<div className="flex justify-between items-start gap-4 md:gap-6 w-full">
26+
<div className="text-primary">
27+
<img src={OasisLogoBlue} alt="Oasis" className="h-6 md:h-auto" />
28+
</div>
29+
<button className="opacity-70 flex-shrink-0" onClick={onClose}>
30+
<X className="h-4 w-4 text-foreground" />
31+
</button>
32+
</div>
33+
34+
<div className="flex flex-col gap-1.5">
35+
<h2 className="text-2xl md:text-[30px] font-bold leading-tight md:leading-9 text-card-foreground">
36+
Help us improve ROFL 🚀
37+
</h2>
38+
<p className="text-sm font-normal leading-5 text-muted-foreground">
39+
Tell us what's working (and what's not). Your feedback makes a difference. Share your thoughts
40+
and help shape what's next.
41+
</p>
42+
</div>
43+
44+
<Button className="w-full gap-2" asChild>
45+
<a href={SURVEY_LINK} rel="noopener noreferrer" target="_blank">
46+
Take the survey
47+
<ExternalLink className="h-4 w-4" />
48+
</a>
49+
</Button>
50+
</CardContent>
51+
</Card>
52+
</div>
53+
)
54+
}
Lines changed: 20 additions & 0 deletions
Loading
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { useSurveyPopup } from '../../hooks/useSurveyPopup.ts'
2+
import type { FC, PropsWithChildren } from 'react'
3+
import { SurveyPopup } from '../../components/SurveyPopup'
4+
5+
export const SurveyPopupProvider: FC<PropsWithChildren> = ({ children }) => {
6+
const { isVisible, closePopup } = useSurveyPopup()
7+
8+
return (
9+
<>
10+
{children}
11+
<SurveyPopup isVisible={isVisible} onClose={closePopup} />
12+
</>
13+
)
14+
}

src/hooks/useSurveyPopup.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { useState, useEffect } from 'react'
2+
3+
const SURVEY_POPUP_STORAGE_KEY = 'survey-popup-dismissed'
4+
5+
export const useSurveyPopup = () => {
6+
const [isVisible, setIsVisible] = useState(false)
7+
8+
useEffect(() => {
9+
const isDismissed = localStorage.getItem(SURVEY_POPUP_STORAGE_KEY)
10+
11+
if (!isDismissed) {
12+
const timer = setTimeout(() => {
13+
setIsVisible(true)
14+
}, 3000)
15+
16+
return () => clearTimeout(timer)
17+
}
18+
}, [])
19+
20+
const closePopup = () => {
21+
setIsVisible(false)
22+
localStorage.setItem(SURVEY_POPUP_STORAGE_KEY, 'true')
23+
}
24+
25+
const resetPopup = () => {
26+
localStorage.removeItem(SURVEY_POPUP_STORAGE_KEY)
27+
setIsVisible(false)
28+
}
29+
30+
return {
31+
isVisible,
32+
closePopup,
33+
resetPopup,
34+
}
35+
}

0 commit comments

Comments
 (0)