Skip to content

Commit 45416a4

Browse files
authored
feat(misc): Add GitHub star toast (#409)
* github star toast * changelog
1 parent 50dc598 commit 45416a4

File tree

4 files changed

+90
-0
lines changed

4 files changed

+90
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
- Add search context to ask sourcebot context selector. [#397](https://github.com/sourcebot-dev/sourcebot/pull/397)
1212
- Add ability to include/exclude connection in search context. [#399](https://github.com/sourcebot-dev/sourcebot/pull/399)
1313
- Search context refactor to search scope and demo card UI changes. [#405](https://github.com/sourcebot-dev/sourcebot/pull/405)
14+
- Add GitHub star toast. [#409](https://github.com/sourcebot-dev/sourcebot/pull/409)
1415

1516
### Fixed
1617
- Fixed multiple writes race condition on config file watcher. [#398](https://github.com/sourcebot-dev/sourcebot/pull/398)
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
'use client';
2+
3+
import { useToast } from "@/components/hooks/use-toast";
4+
import { ToastAction } from "@/components/ui/toast";
5+
import { useEffect } from "react";
6+
import { GitHubLogoIcon } from "@radix-ui/react-icons";
7+
import { captureEvent } from "@/hooks/useCaptureEvent";
8+
9+
const POPUP_SHOWN_COOKIE = "github_popup_shown";
10+
const POPUP_START_TIME_COOKIE = "github_popup_start_time";
11+
const POPUP_DELAY_S = 60;
12+
const SOURCEBOT_GITHUB_URL = "https://github.com/sourcebot-dev/sourcebot";
13+
14+
function getCookie(name: string): string | null {
15+
if (typeof document === "undefined") return null;
16+
17+
const cookies = document.cookie.split(';').map(cookie => cookie.trim());
18+
const targetCookie = cookies.find(cookie => cookie.startsWith(`${name}=`));
19+
20+
if (!targetCookie) return null;
21+
22+
return targetCookie.substring(`${name}=`.length);
23+
}
24+
25+
function setCookie(name: string, value: string, days: number = 365) {
26+
if (typeof document === "undefined") return;
27+
28+
try {
29+
const expires = new Date();
30+
expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000));
31+
document.cookie = `${name}=${value}; expires=${expires.toUTCString()}; path=/; SameSite=Lax`;
32+
} catch (error) {
33+
console.warn('Failed to set GitHub popup cookie:', error);
34+
}
35+
}
36+
37+
export const GitHubStarToast = () => {
38+
const { toast } = useToast();
39+
40+
useEffect(() => {
41+
const hasShownPopup = getCookie(POPUP_SHOWN_COOKIE);
42+
const startTime = getCookie(POPUP_START_TIME_COOKIE);
43+
44+
if (hasShownPopup) {
45+
return;
46+
}
47+
48+
const currentTime = Date.now();
49+
if (!startTime) {
50+
setCookie(POPUP_START_TIME_COOKIE, currentTime.toString());
51+
return;
52+
}
53+
54+
const elapsed = currentTime - parseInt(startTime, 10);
55+
if (elapsed >= (POPUP_DELAY_S * 1000)) {
56+
toast({
57+
title: "Star us on GitHub ❤️",
58+
description: "If you've found Sourcebot useful, please consider starring us on GitHub. Your support means a lot!",
59+
duration: 15 * 1000,
60+
action: (
61+
<div className="flex flex-col gap-1">
62+
<ToastAction
63+
altText="GitHub Button"
64+
onClick={() => {
65+
captureEvent('wa_github_star_toast_clicked', {});
66+
window.open(SOURCEBOT_GITHUB_URL, "_blank");
67+
}}
68+
>
69+
<div className="flex items-center gap-2">
70+
<GitHubLogoIcon className="w-4 h-4" />
71+
Sourcebot
72+
</div>
73+
</ToastAction>
74+
</div>
75+
)
76+
});
77+
78+
captureEvent('wa_github_star_toast_displayed', {});
79+
setCookie(POPUP_SHOWN_COOKIE, "true");
80+
}
81+
}, [toast]);
82+
83+
return null;
84+
}

packages/web/src/app/[domain]/layout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { GcpIapAuth } from "./components/gcpIapAuth";
2121
import { getAnonymousAccessStatus, getMemberApprovalRequired } from "@/actions";
2222
import { JoinOrganizationCard } from "@/app/components/joinOrganizationCard";
2323
import { LogoutEscapeHatch } from "@/app/components/logoutEscapeHatch";
24+
import { GitHubStarToast } from "./components/githubStarToast";
2425

2526
interface LayoutProps {
2627
children: React.ReactNode,
@@ -134,6 +135,7 @@ export default async function Layout({
134135
<SyntaxGuideProvider>
135136
{children}
136137
<SyntaxReferenceGuide />
138+
<GitHubStarToast />
137139
</SyntaxGuideProvider>
138140
)
139141
}

packages/web/src/lib/posthogEvents.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,5 +293,8 @@ export type PosthogEventMap = {
293293
exampleTitle: string,
294294
exampleUrl: string,
295295
},
296+
//////////////////////////////////////////////////////////////////
297+
wa_github_star_toast_displayed: {},
298+
wa_github_star_toast_clicked: {},
296299
}
297300
export type PosthogEvent = keyof PosthogEventMap;

0 commit comments

Comments
 (0)