Skip to content

Commit eac9d7a

Browse files
πŸ“ˆ feat: add LogSnag integration for user tracking
Integrate LogSnag to track user sign-ups and downloads: - Track user downloads with OS and user metadata - Identify users and track sign-up events in LogSnag - Add LogSnagProvider for app-wide tracking
1 parent 5a00674 commit eac9d7a

File tree

7 files changed

+99
-26
lines changed

7 files changed

+99
-26
lines changed

β€Žapps/web/app/(default)/download/DownloadButton.tsxβ€Ž

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { getOS } from "@/app/utils";
44
import { GithubStars } from "@/components/GithubStars";
55
import { useEffect, useState } from "react";
66
import { useQuery } from "@tanstack/react-query";
7+
import { useSession } from "next-auth/react";
8+
import { useLogSnag } from "@logsnag/next";
79

810
// Define the interface for a GitHub release asset
911
interface ReleaseAsset {
@@ -47,12 +49,32 @@ const fetchReleases = async (): Promise<GitHubRelease[]> => {
4749

4850
export const DownloadButton = () => {
4951
const [os, setOs] = useState<string | null>(null);
52+
const { data: session } = useSession();
53+
const { track } = useLogSnag();
5054

5155
useEffect(() => {
5256
const updatedOs = getOS();
5357
setOs(updatedOs);
5458
}, []);
5559

60+
const handleDownloadClick = (os: string) => {
61+
// Check if the user is logged in and has a valid session
62+
if (session && session.user && session.user.email) {
63+
// Track the download event with LogSnag
64+
track({
65+
channel: "downloads",
66+
event: "User Download",
67+
// @ts-ignore
68+
user_id: session.user.id,
69+
tags: {
70+
os: os ?? "Unknown",
71+
},
72+
notify: true,
73+
icon: "πŸš€",
74+
});
75+
}
76+
};
77+
5678
const {
5779
data: releases,
5880
isLoading,
@@ -92,20 +114,33 @@ export const DownloadButton = () => {
92114
<span className="animate-pulse inline-flex items-center rounded-md bg-blue-400/10 px-3 py-3 text-2xl font-medium text-blue-400 ring-1 ring-inset ring-blue-400/30 hover:bg-blue-400/20 dark:bg-blue-400/20 dark:text-blue-400 dark:ring-blue-400/20 w-44 h-16 cursor-pointer"></span>
93115
);
94116

95-
if (os === "macOS") return <MacDownloadButton releases={releases ?? []} />;
117+
if (os === "macOS")
118+
return (
119+
<MacDownloadButton
120+
releases={releases ?? []}
121+
handleDownloadClick={handleDownloadClick}
122+
/>
123+
);
96124

97125
return (
98126
<a
99127
href={downloadUrl ?? "#"}
100128
className="inline-flex items-center rounded-md bg-blue-400/10 px-3 py-3 text-2xl font-medium text-blue-400 ring-1 ring-inset ring-blue-400/30 hover:bg-blue-400/20 dark:bg-blue-400/20 dark:text-blue-400 dark:ring-blue-400/20"
101129
download
130+
onClick={() => handleDownloadClick(os)}
102131
>
103132
Download for {os}
104133
</a>
105134
);
106135
};
107136

108-
const MacDownloadButton = ({ releases }: { releases: GitHubRelease[] }) => {
137+
const MacDownloadButton = ({
138+
releases,
139+
handleDownloadClick,
140+
}: {
141+
releases: GitHubRelease[];
142+
handleDownloadClick: (os: string) => void;
143+
}) => {
109144
const intelMacPattern = /ScreenLink-\d+\.\d+\.\d+\.dmg/;
110145
const armMacPattern = /ScreenLink-\d+\.\d+\.\d+-arm64\.dmg/;
111146

@@ -128,6 +163,7 @@ const MacDownloadButton = ({ releases }: { releases: GitHubRelease[] }) => {
128163
}
129164
className="inline-flex items-center rounded-md bg-blue-400/10 px-3 py-3 text-2xl font-medium text-blue-400 ring-1 ring-inset ring-blue-400/30 hover:bg-blue-400/20 dark:bg-blue-400/20 dark:text-blue-400 dark:ring-blue-400/20"
130165
download
166+
onClick={() => handleDownloadClick("mac-intel")}
131167
>
132168
Download for Intel
133169
</a>
@@ -141,6 +177,7 @@ const MacDownloadButton = ({ releases }: { releases: GitHubRelease[] }) => {
141177
}
142178
className="inline-flex items-center rounded-md bg-blue-400/10 px-3 py-3 text-2xl font-medium text-blue-400 ring-1 ring-inset ring-blue-400/30 hover:bg-blue-400/20 dark:bg-blue-400/20 dark:text-blue-400 dark:ring-blue-400/20"
143179
download
180+
onClick={() => handleDownloadClick("mac-arm")}
144181
>
145182
Download for Apple Silicon
146183
</a>

β€Žapps/web/app/(default)/download/page.tsxβ€Ž

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { constructMetadata } from "@/app/utils";
22
import { DownloadButton } from "./DownloadButton";
3-
import Link from "next/link";
43
export const metadata = constructMetadata({
54
description:
65
"Download for MacOS, Windows, or Linux to record your screen and share demos with ScreenLink",

β€Žapps/web/app/api/auth/[...nextauth]/AuthOptions.tsβ€Ž

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { PrismaAdapter } from "@next-auth/prisma-adapter";
66
import { PrismaClient } from "@prisma/client";
77
import { loops, posthog_serverside } from "@/app/utils";
88
import { captureException } from "@sentry/nextjs";
9+
import { logsnag } from "@/utils/logsnag";
910

1011
const prisma = new PrismaClient();
1112

@@ -133,6 +134,24 @@ export const authOptions: NextAuthOptions = {
133134
try {
134135
console.log("User sign in completed");
135136
if (message.isNewUser) {
137+
138+
logsnag.identify({
139+
user_id: message.user.id,
140+
properties: {
141+
name: message.user.name ?? "",
142+
email: message.user.email ?? "",
143+
image: message.user.image ?? "",
144+
},
145+
})
146+
147+
logsnag.track({
148+
channel: "User",
149+
event: "Sign Up",
150+
notify: true,
151+
user_id: message.user.id,
152+
icon: "πŸŽ‰"
153+
})
154+
136155
console.log("New user sign up")
137156
const [firstName, lastName] = message?.user?.name?.split(" ") ?? ["", ""];
138157
const userEmail = message?.user?.email;

β€Žapps/web/app/layout.tsxβ€Ž

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import Script from "next/script";
1212
import { Metadata } from "next";
1313
import { TanQueryProvider } from "@/components/TanQueryProvider";
1414
import { SpeedInsights } from "@vercel/speed-insights/next";
15+
import { LogSnagProvider } from "@logsnag/next";
1516

1617
const inter = Inter({
1718
subsets: ["latin"],
@@ -81,21 +82,23 @@ export default function RootLayout({
8182
<SpeedInsights />
8283
<AuthUser />
8384
<PostHogPageview />
84-
</Suspense>
85-
<Toaster position="top-right" />
86-
<Analytics />
87-
<script
88-
defer={true}
89-
data-domain="screenlink.io"
90-
src="https://analytics.dermot.email/js/script.js"
91-
></script>
92-
<script
93-
async
94-
src="https://js.stripe.com/v3/pricing-table.js"
95-
defer={true}
96-
></script>
97-
<Script strategy="lazyOnload">
98-
{`
85+
<LogSnagProvider
86+
token={process.env.LOGSNAG_API_TOKEN!}
87+
project={process.env.LOGSNAG_PROJECT_NAME!}
88+
/>
89+
<Analytics />
90+
<script
91+
defer={true}
92+
data-domain="screenlink.io"
93+
src="https://analytics.dermot.email/js/script.js"
94+
></script>
95+
<script
96+
async
97+
src="https://js.stripe.com/v3/pricing-table.js"
98+
defer={true}
99+
></script>
100+
<Script strategy="lazyOnload">
101+
{`
99102
var Tawk_API=Tawk_API||{}, Tawk_LoadStart=new Date();
100103
(function(){
101104
var s1=document.createElement("script"),s0=document.getElementsByTagName("script")[0];
@@ -106,20 +109,22 @@ export default function RootLayout({
106109
s0.parentNode.insertBefore(s1,s0);
107110
})();
108111
`}
109-
</Script>
110-
<Script
111-
async
112-
src="https://www.googletagmanager.com/gtag/js?id=AW-16448683587"
113-
/>
114-
<Script>
115-
{`
112+
</Script>
113+
<Script
114+
async
115+
src="https://www.googletagmanager.com/gtag/js?id=AW-16448683587"
116+
/>
117+
<Script>
118+
{`
116119
window.dataLayer = window.dataLayer || [];
117120
function gtag(){dataLayer.push(arguments);}
118121
gtag('js', new Date());
119122
120123
gtag('config', 'AW-16448683587');
121124
`}
122-
</Script>
125+
</Script>
126+
</Suspense>
127+
<Toaster position="top-right" />
123128
</body>
124129
</html>
125130
</AuthProvider>

β€Žapps/web/package.jsonβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"dependencies": {
1212
"@headlessui/react": "^1.7.17",
1313
"@hookform/resolvers": "^3.3.4",
14+
"@logsnag/next": "^1.0.3",
1415
"@mux/mux-node": "^7.3.2",
1516
"@next-auth/prisma-adapter": "^1.0.7",
1617
"@prisma/client": "^5.6.0",

β€Žapps/web/utils/logsnag.tsβ€Ž

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { LogSnag } from '@logsnag/next/server';
2+
3+
export const logsnag = new LogSnag({
4+
token: process.env.LOGSNAG_API_TOKEN!,
5+
project: process.env.LOGSNAG_PROJECT!,
6+
});
7+

β€Žapps/web/yarn.lockβ€Ž

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,11 @@
269269
"@jridgewell/resolve-uri" "^3.1.0"
270270
"@jridgewell/sourcemap-codec" "^1.4.14"
271271

272+
"@logsnag/next@^1.0.3":
273+
version "1.0.3"
274+
resolved "https://registry.yarnpkg.com/@logsnag/next/-/next-1.0.3.tgz#ba59b601a59386fdeb02aa96ba8dc4c9fcd87527"
275+
integrity sha512-HcbZx2NlWK8qVVnsnTAxrWWts/eVZFI1hvHk5FgP/qXIqY5gRyYqwe/r3x6CnHZXw27s4BXW8jFT6zXusk0eYA==
276+
272277
"@mux/mux-node@^7.3.2":
273278
version "7.3.3"
274279
resolved "https://registry.npmjs.org/@mux/mux-node/-/mux-node-7.3.3.tgz"

0 commit comments

Comments
Β (0)