Skip to content

Commit 3c3660c

Browse files
committed
feat(support): redesign support dialog with improved UX and cookie-based tracking
- Replace localStorage with cookie-based persistence for better cross-domain support - Implement show count limit (max 2 times) instead of one-time display - Increase initial delay from 2000ms to 4500ms for better UX timing - Remove Discord integration and simplify messaging to focus on GitHub star request - Add custom GitHubIcon component to replace external dependency - Refactor dialog structure using Radix UI primitives for better control - Simplify dialog content with cleaner layout and improved typography - Add cookie utility functions (getCookie, setCookie) for persistent state management - Update imports to use DialogPortal and DialogOverlay for custom styling - Improve accessibility with proper ARIA labels and focus management
1 parent fd6df4e commit 3c3660c

File tree

1 file changed

+96
-72
lines changed

1 file changed

+96
-72
lines changed
Lines changed: 96 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,125 @@
11
"use client";
22

33
import { useState, useEffect } from "react";
4-
import {
5-
Dialog,
6-
DialogContent,
7-
DialogHeader,
8-
DialogTitle,
9-
DialogDescription,
10-
} from "@/components/ui/dialog";
4+
import { Dialog, DialogPortal, DialogOverlay } from "@/components/ui/dialog";
5+
import * as DialogPrimitive from "@radix-ui/react-dialog";
116
import { Button } from "@/components/ui/button";
12-
import { Star, MessageCircle, X } from "lucide-react";
13-
import { DiscordIcon } from "@/components/icons/DiscordIcon";
14-
import { getDiscordLink, GITHUB_REPO_URL } from "@/lib/utils/links";
7+
import { Star, X } from "lucide-react";
8+
import { cn } from "@/lib/utils";
9+
import { GITHUB_REPO_URL } from "@/lib/utils/links";
1510

16-
const STORAGE_KEY = "portfolioly_support_dialog_shown";
11+
const COOKIE_NAME = "portfolioly_support_shown_c";
12+
const MAX_SHOW_COUNT = 2;
13+
const SHOW_DELAY_MS = 4500;
14+
15+
function getCookie(name: string): string | null {
16+
if (typeof document === "undefined") return null;
17+
const match = document.cookie.match(new RegExp(`(^| )${name}=([^;]+)`));
18+
return match ? match[2] : null;
19+
}
20+
21+
function setCookie(name: string, value: string, days: number = 365) {
22+
const expires = new Date(Date.now() + days * 864e5).toUTCString();
23+
document.cookie = `${name}=${value}; expires=${expires}; path=/; SameSite=Lax`;
24+
}
25+
26+
function GitHubIcon({ className }: { className?: string }) {
27+
return (
28+
<svg viewBox="0 0 24 24" fill="currentColor" className={className}>
29+
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
30+
</svg>
31+
);
32+
}
1733

1834
export function SupportDialog() {
1935
const [open, setOpen] = useState(false);
2036

2137
useEffect(() => {
22-
const hasShown = localStorage.getItem(STORAGE_KEY);
23-
if (!hasShown) {
24-
const timer = setTimeout(() => setOpen(true), 2000);
38+
const shownCount = parseInt(getCookie(COOKIE_NAME) || "0", 10);
39+
40+
if (shownCount < MAX_SHOW_COUNT) {
41+
const timer = setTimeout(() => setOpen(true), SHOW_DELAY_MS);
2542
return () => clearTimeout(timer);
2643
}
2744
}, []);
2845

2946
const handleClose = () => {
30-
localStorage.setItem(STORAGE_KEY, "true");
47+
const currentCount = parseInt(getCookie(COOKIE_NAME) || "0", 10);
48+
setCookie(COOKIE_NAME, String(currentCount + 1));
3149
setOpen(false);
3250
};
3351

34-
const discordLink = getDiscordLink();
35-
3652
return (
3753
<Dialog open={open} onOpenChange={(isOpen) => !isOpen && handleClose()}>
38-
<DialogContent className="sm:max-w-md">
39-
<DialogHeader>
40-
<DialogTitle className="flex items-center gap-2">
41-
<MessageCircle className="h-5 w-5 text-primary" />
42-
Welcome to Portfolioly!
43-
</DialogTitle>
44-
<DialogDescription>
45-
We&apos;re glad you&apos;re here. Here&apos;s how you can connect
46-
with us.
47-
</DialogDescription>
48-
</DialogHeader>
54+
<DialogPortal>
55+
<DialogOverlay className="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 duration-300" />
56+
<DialogPrimitive.Content
57+
className={cn(
58+
"fixed left-[50%] top-[50%] z-50 w-full max-w-md translate-x-[-50%] translate-y-[-50%] border bg-background shadow-lg sm:rounded-lg",
59+
"data-[state=open]:animate-in data-[state=closed]:animate-out",
60+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
61+
"duration-300 ease-out"
62+
)}
63+
>
64+
<DialogPrimitive.Close
65+
className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
66+
onClick={handleClose}
67+
>
68+
<X className="h-4 w-4" />
69+
<span className="sr-only">Close</span>
70+
</DialogPrimitive.Close>
4971

50-
<div className="space-y-4 py-4">
51-
<div className="flex items-start gap-3 p-3 rounded-lg bg-muted/50">
52-
<DiscordIcon className="h-5 w-5 mt-0.5 text-[#5865F2]" />
53-
<div className="flex-1">
54-
<p className="font-medium text-sm">
55-
Need help or have questions?
56-
</p>
57-
<p className="text-sm text-muted-foreground">
58-
Join our Discord community for support and updates.
59-
</p>
60-
<Button
61-
variant="outline"
62-
size="sm"
63-
className="mt-2"
64-
onClick={() => window.open(discordLink, "_blank")}
65-
>
66-
<DiscordIcon className="h-4 w-4 mr-2" />
67-
Join Discord
68-
</Button>
72+
<div className="p-6 pb-5 space-y-4">
73+
<div className="flex items-center gap-3">
74+
<div className="flex items-center justify-center w-10 h-10 rounded-full bg-yellow-500/10">
75+
<Star className="h-5 w-5 text-yellow-500" />
76+
</div>
77+
<h3 className="text-lg font-semibold">Enjoying Portfolioly?</h3>
6978
</div>
70-
</div>
7179

72-
<div className="flex items-start gap-3 p-3 rounded-lg bg-muted/50">
73-
<Star className="h-5 w-5 mt-0.5 text-yellow-500" />
74-
<div className="flex-1">
75-
<p className="font-medium text-sm">Enjoying Portfolioly?</p>
76-
<p className="text-sm text-muted-foreground">
77-
A star on GitHub helps us grow and improve!
78-
</p>
79-
<Button
80-
variant="outline"
81-
size="sm"
82-
className="mt-2"
83-
onClick={() => window.open(GITHUB_REPO_URL, "_blank")}
80+
<p className="text-muted-foreground leading-relaxed">
81+
If you like what we&apos;re building, it would really help if you
82+
could star us on GitHub. It means a lot and helps others discover
83+
the project :)
84+
</p>
85+
86+
<p className="text-sm text-muted-foreground">
87+
Questions or issues?{" "}
88+
<a
89+
href={`${GITHUB_REPO_URL}/issues`}
90+
target="_blank"
91+
rel="noopener noreferrer"
92+
className="inline-flex items-center gap-1 text-foreground hover:underline font-medium"
8493
>
85-
<Star className="h-4 w-4 mr-2" />
86-
Star on GitHub
87-
</Button>
88-
</div>
94+
<GitHubIcon className="h-3.5 w-3.5" />
95+
Report here
96+
</a>
97+
</p>
8998
</div>
90-
</div>
9199

92-
<div className="flex justify-end">
93-
<Button variant="ghost" size="sm" onClick={handleClose}>
94-
<X className="h-4 w-4 mr-1" />
95-
Close
96-
</Button>
97-
</div>
98-
</DialogContent>
100+
<div className="flex border-t">
101+
<Button
102+
variant="ghost"
103+
className="flex-1 rounded-none h-12 text-muted-foreground hover:text-foreground"
104+
onClick={handleClose}
105+
>
106+
Continue
107+
</Button>
108+
<div className="w-px bg-border" />
109+
<Button
110+
variant="ghost"
111+
className="flex-1 rounded-none h-12 gap-2 font-medium"
112+
onClick={() => {
113+
window.open(GITHUB_REPO_URL, "_blank");
114+
handleClose();
115+
}}
116+
>
117+
<GitHubIcon className="h-4 w-4" />
118+
Star on GitHub
119+
</Button>
120+
</div>
121+
</DialogPrimitive.Content>
122+
</DialogPortal>
99123
</Dialog>
100124
);
101125
}

0 commit comments

Comments
 (0)