From e1d18701e8114d9f01fb9d8561adee3a1a2269c3 Mon Sep 17 00:00:00 2001 From: Felix Wotschofsky Date: Wed, 18 Sep 2024 19:49:54 +0200 Subject: [PATCH] =?UTF-8?q?Add=20feedback=20and=20support=20prompt=20?= =?UTF-8?q?=F0=9F=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/_components/feedback-prompt.tsx | 146 ++++++++++++++++++ app/layout.tsx | 9 ++ .../[domain]/_components/star-reminder.tsx | 103 ------------ app/lookup/[domain]/layout.tsx | 6 - 4 files changed, 155 insertions(+), 109 deletions(-) create mode 100644 app/_components/feedback-prompt.tsx delete mode 100644 app/lookup/[domain]/_components/star-reminder.tsx diff --git a/app/_components/feedback-prompt.tsx b/app/_components/feedback-prompt.tsx new file mode 100644 index 0000000..ff94ec4 --- /dev/null +++ b/app/_components/feedback-prompt.tsx @@ -0,0 +1,146 @@ +'use client'; + +import { useLocalStorage } from '@uidotdev/usehooks'; +import { XIcon } from 'lucide-react'; +import ms from 'ms'; +import { usePlausible } from 'next-plausible'; +import { type FC, useCallback, useState } from 'react'; + +import { Alert } from '@/components/ui/alert'; +import { Button } from '@/components/ui/button'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; + +export const FeedbackPrompt: FC = () => { + const plausible = usePlausible(); + + const [lastDismissed, setLastDismissed] = useLocalStorage( + 'feedback-prompt.last-dismissed', + null + ); + const visible = !lastDismissed || Date.now() - lastDismissed > ms('7d'); + const [dialogOpen, setDialogOpen] = useState(false); + + const handleDismiss = useCallback(() => { + setLastDismissed(Date.now()); + }, [setLastDismissed]); + + const handleNegative = useCallback(() => { + window.open('https://wotschofsky.com/#contact', '_blank'); + handleDismiss(); + plausible('Feedback: Negative'); + }, [handleDismiss]); + + const handleNeutral = useCallback(() => { + handleDismiss(); + plausible('Feedback: Neutral'); + }, [handleDismiss]); + + const handlePositive = useCallback(() => { + setDialogOpen(true); + handleDismiss(); + plausible('Feedback: Positive'); + }, [setDialogOpen, handleDismiss]); + + return ( + <> + {visible && ( +
+ + + +

+ How do you feel about Domain Digger? +

+
+ + 😢 + + + 😐 + + + 😍 + +
+
+
+ )} + + + + + Thanks for your feedback! + +

+ Domain Digger is on a mission to offer the best multi-tool for + anything domain-related. All 100% free, open-source and without + ads. +

+

+ As you seem to enjoy using Domain Digger, please consider + supporting us! +

+ + +
+
+
+
+ + ); +}; diff --git a/app/layout.tsx b/app/layout.tsx index e78f3c5..d62073d 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,5 +1,6 @@ import { SpeedInsights } from '@vercel/speed-insights/next'; import type { Viewport } from 'next'; +import dynamic from 'next/dynamic'; import type { FC, ReactNode } from 'react'; import { Toaster } from '@/components/ui/sonner'; @@ -11,6 +12,13 @@ import { Header } from './_components/header'; import './globals.css'; import { Providers } from './providers'; +const FeedbackPrompt = dynamic( + () => import('./_components/feedback-prompt').then((m) => m.FeedbackPrompt), + { + ssr: false, + } +); + export const viewport: Viewport = { width: 'device-width', initialScale: 1, @@ -48,6 +56,7 @@ const RootLayout: FC = ({ children }) => {
{children}
+