feat: integrate PostHog for analytics tracking and add PostHogProvider component
This commit is contained in:
@@ -2,7 +2,8 @@
|
|||||||
NEXT_PUBLIC_NAVIDROME_URL=http://localhost:4533
|
NEXT_PUBLIC_NAVIDROME_URL=http://localhost:4533
|
||||||
NEXT_PUBLIC_NAVIDROME_USERNAME=your_username
|
NEXT_PUBLIC_NAVIDROME_USERNAME=your_username
|
||||||
NEXT_PUBLIC_NAVIDROME_PASSWORD=your_password
|
NEXT_PUBLIC_NAVIDROME_PASSWORD=your_password
|
||||||
|
NEXT_PUBLIC_POSTHOG_KEY=KEY
|
||||||
|
NEXT_PUBLIC_POSTHOG_HOST=HOSTURL
|
||||||
# Example for external server:
|
# Example for external server:
|
||||||
# NEXT_PUBLIC_NAVIDROME_URL=https://your-navidrome-server.com
|
# NEXT_PUBLIC_NAVIDROME_URL=https://your-navidrome-server.com
|
||||||
# NEXT_PUBLIC_NAVIDROME_USERNAME=your_username
|
# NEXT_PUBLIC_NAVIDROME_USERNAME=your_username
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ import { Progress } from '@/components/ui/progress';
|
|||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
|
|
||||||
export const AudioPlayer: React.FC = () => {
|
export const AudioPlayer: React.FC = () => {
|
||||||
const { currentTrack, playPreviousTrack, addToQueue, playNextTrack, clearQueue } = useAudioPlayer();
|
const { currentTrack, playPreviousTrack, addToQueue, playNextTrack, clearQueue, queue } = useAudioPlayer();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const audioRef = useRef<HTMLAudioElement>(null);
|
const audioRef = useRef<HTMLAudioElement>(null);
|
||||||
|
const preloadAudioRef = useRef<HTMLAudioElement>(null);
|
||||||
const [progress, setProgress] = useState(0);
|
const [progress, setProgress] = useState(0);
|
||||||
const [isPlaying, setIsPlaying] = useState(false);
|
const [isPlaying, setIsPlaying] = useState(false);
|
||||||
const [showVolumeSlider, setShowVolumeSlider] = useState(false);
|
const [showVolumeSlider, setShowVolumeSlider] = useState(false);
|
||||||
@@ -324,6 +325,7 @@ export const AudioPlayer: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<audio ref={audioRef} hidden />
|
<audio ref={audioRef} hidden />
|
||||||
|
<audio ref={preloadAudioRef} hidden preload="metadata" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -377,6 +379,7 @@ export const AudioPlayer: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<audio ref={audioRef} hidden />
|
<audio ref={audioRef} hidden />
|
||||||
|
<audio ref={preloadAudioRef} hidden preload="metadata" />
|
||||||
|
|
||||||
{/* Full Screen Player */}
|
{/* Full Screen Player */}
|
||||||
<FullScreenPlayer
|
<FullScreenPlayer
|
||||||
|
|||||||
50
app/components/PostHogProvider.tsx
Normal file
50
app/components/PostHogProvider.tsx
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import posthog from "posthog-js"
|
||||||
|
import { PostHogProvider as PHProvider, usePostHog } from "posthog-js/react"
|
||||||
|
import { Suspense, useEffect } from "react"
|
||||||
|
import { usePathname, useSearchParams } from "next/navigation"
|
||||||
|
|
||||||
|
function PathnameTracker() {
|
||||||
|
const posthogClient = usePostHog()
|
||||||
|
const pathname = usePathname()
|
||||||
|
const searchParams = useSearchParams()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (posthogClient) {
|
||||||
|
posthogClient.capture('$pageview', {
|
||||||
|
path: pathname + (searchParams.toString() ? `?${searchParams.toString()}` : ''),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [posthogClient, pathname, searchParams])
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function SuspendedPostHogPageView() {
|
||||||
|
return (
|
||||||
|
<Suspense fallback={null}>
|
||||||
|
<PathnameTracker />
|
||||||
|
</Suspense>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PostHogProvider({ children }: { children: React.ReactNode }) {
|
||||||
|
useEffect(() => {
|
||||||
|
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
|
||||||
|
api_host: "/ingest",
|
||||||
|
ui_host: "https://us.posthog.com",
|
||||||
|
capture_pageview: 'history_change',
|
||||||
|
capture_pageleave: true,
|
||||||
|
capture_exceptions: true,
|
||||||
|
debug: process.env.NODE_ENV === "development",
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PHProvider client={posthog}>
|
||||||
|
<SuspendedPostHogPageView />
|
||||||
|
{children}
|
||||||
|
</PHProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import { AudioPlayerProvider } from "./components/AudioPlayerContext";
|
|||||||
import { NavidromeProvider } from "./components/NavidromeContext";
|
import { NavidromeProvider } from "./components/NavidromeContext";
|
||||||
import { NavidromeConfigProvider } from "./components/NavidromeConfigContext";
|
import { NavidromeConfigProvider } from "./components/NavidromeConfigContext";
|
||||||
import { ThemeProvider } from "./components/ThemeProvider";
|
import { ThemeProvider } from "./components/ThemeProvider";
|
||||||
|
import { PostHogProvider } from "./components/PostHogProvider";
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
import type { Viewport } from 'next';
|
import type { Viewport } from 'next';
|
||||||
import Ihateserverside from './components/ihateserverside';
|
import Ihateserverside from './components/ihateserverside';
|
||||||
@@ -75,6 +76,7 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
/>
|
/>
|
||||||
</head>
|
</head>
|
||||||
<body className={`${geistSans.variable} ${geistMono.variable} antialiased bg-background`}>
|
<body className={`${geistSans.variable} ${geistMono.variable} antialiased bg-background`}>
|
||||||
|
<PostHogProvider>
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<NavidromeConfigProvider>
|
<NavidromeConfigProvider>
|
||||||
<NavidromeProvider>
|
<NavidromeProvider>
|
||||||
@@ -86,6 +88,7 @@ export default function Layout({ children }: LayoutProps) {
|
|||||||
</NavidromeProvider>
|
</NavidromeProvider>
|
||||||
</NavidromeConfigProvider>
|
</NavidromeConfigProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
</PostHogProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
11
lib/posthog.ts
Normal file
11
lib/posthog.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { PostHog } from "posthog-node"
|
||||||
|
|
||||||
|
export default function PostHogClient() {
|
||||||
|
const posthogClient = new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
|
||||||
|
host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
|
||||||
|
capture_pageview: 'history_change',
|
||||||
|
flushAt: 1,
|
||||||
|
flushInterval: 0,
|
||||||
|
})
|
||||||
|
return posthogClient
|
||||||
|
}
|
||||||
@@ -46,6 +46,24 @@ const nextConfig = {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
async rewrites() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
source: '/ingest/static/:path*',
|
||||||
|
destination: 'https://us-assets.i.posthog.com/static/:path*',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: '/ingest/:path*',
|
||||||
|
destination: 'https://us.i.posthog.com/:path*',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: '/ingest/decide',
|
||||||
|
destination: 'https://us.i.posthog.com/decide',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
// This is required to support PostHog trailing slash API requests
|
||||||
|
skipTrailingSlashRedirect: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
@@ -31,6 +31,8 @@
|
|||||||
"colorthief": "^2.6.0",
|
"colorthief": "^2.6.0",
|
||||||
"lucide-react": "^0.469.0",
|
"lucide-react": "^0.469.0",
|
||||||
"next": "^15.0.3",
|
"next": "^15.0.3",
|
||||||
|
"posthog-js": "^1.255.0",
|
||||||
|
"posthog-node": "^5.1.1",
|
||||||
"react": "^19",
|
"react": "^19",
|
||||||
"react-dom": "^19",
|
"react-dom": "^19",
|
||||||
"react-hook-form": "^7.53.2",
|
"react-hook-form": "^7.53.2",
|
||||||
|
|||||||
50
pnpm-lock.yaml
generated
50
pnpm-lock.yaml
generated
@@ -74,6 +74,12 @@ importers:
|
|||||||
next:
|
next:
|
||||||
specifier: ^15.0.3
|
specifier: ^15.0.3
|
||||||
version: 15.1.4(@babel/core@7.26.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
version: 15.1.4(@babel/core@7.26.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
posthog-js:
|
||||||
|
specifier: ^1.255.0
|
||||||
|
version: 1.255.0
|
||||||
|
posthog-node:
|
||||||
|
specifier: ^5.1.1
|
||||||
|
version: 5.1.1
|
||||||
react:
|
react:
|
||||||
specifier: ^19
|
specifier: ^19
|
||||||
version: 19.0.0
|
version: 19.0.0
|
||||||
@@ -1487,6 +1493,9 @@ packages:
|
|||||||
convert-source-map@2.0.0:
|
convert-source-map@2.0.0:
|
||||||
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
||||||
|
|
||||||
|
core-js@3.43.0:
|
||||||
|
resolution: {integrity: sha512-N6wEbTTZSYOY2rYAn85CuvWWkCK6QweMn7/4Nr3w+gDBeBhk/x4EJeY6FPo4QzDoJZxVTv8U7CMvgWk6pOHHqA==}
|
||||||
|
|
||||||
create-jest@29.7.0:
|
create-jest@29.7.0:
|
||||||
resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
|
resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
|
||||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
@@ -1822,6 +1831,9 @@ packages:
|
|||||||
fb-watchman@2.0.2:
|
fb-watchman@2.0.2:
|
||||||
resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==}
|
resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==}
|
||||||
|
|
||||||
|
fflate@0.4.8:
|
||||||
|
resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
|
||||||
|
|
||||||
file-entry-cache@8.0.0:
|
file-entry-cache@8.0.0:
|
||||||
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
||||||
engines: {node: '>=16.0.0'}
|
engines: {node: '>=16.0.0'}
|
||||||
@@ -2715,6 +2727,24 @@ packages:
|
|||||||
resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==}
|
resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==}
|
||||||
engines: {node: ^10 || ^12 || >=14}
|
engines: {node: ^10 || ^12 || >=14}
|
||||||
|
|
||||||
|
posthog-js@1.255.0:
|
||||||
|
resolution: {integrity: sha512-2ZZKrGB1Ih425IoPvmiDYN+BcDJvNJvVGRrey2ARR4UJ85oB+sNCJAx6DuwIlvsIQTe8QjuUhxrHlxAT5/7IMA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@rrweb/types': 2.0.0-alpha.17
|
||||||
|
rrweb-snapshot: 2.0.0-alpha.17
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@rrweb/types':
|
||||||
|
optional: true
|
||||||
|
rrweb-snapshot:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
posthog-node@5.1.1:
|
||||||
|
resolution: {integrity: sha512-6VISkNdxO24ehXiDA4dugyCSIV7lpGVaEu5kn/dlAj+SJ1lgcDru9PQ8p/+GSXsXVxohd1t7kHL2JKc9NoGb0w==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
preact@10.26.9:
|
||||||
|
resolution: {integrity: sha512-SSjF9vcnF27mJK1XyFMNJzFd5u3pQiATFqoaDy03XuN00u4ziveVVEGt5RKJrDR8MHE/wJo9Nnad56RLzS2RMA==}
|
||||||
|
|
||||||
prelude-ls@1.2.1:
|
prelude-ls@1.2.1:
|
||||||
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
|
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
@@ -3226,6 +3256,9 @@ packages:
|
|||||||
walker@1.0.8:
|
walker@1.0.8:
|
||||||
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
|
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
|
||||||
|
|
||||||
|
web-vitals@4.2.4:
|
||||||
|
resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==}
|
||||||
|
|
||||||
which-boxed-primitive@1.1.1:
|
which-boxed-primitive@1.1.1:
|
||||||
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
|
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -4806,6 +4839,8 @@ snapshots:
|
|||||||
|
|
||||||
convert-source-map@2.0.0: {}
|
convert-source-map@2.0.0: {}
|
||||||
|
|
||||||
|
core-js@3.43.0: {}
|
||||||
|
|
||||||
create-jest@29.7.0(@types/node@22.10.5):
|
create-jest@29.7.0(@types/node@22.10.5):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
@@ -5283,6 +5318,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
bser: 2.1.1
|
bser: 2.1.1
|
||||||
|
|
||||||
|
fflate@0.4.8: {}
|
||||||
|
|
||||||
file-entry-cache@8.0.0:
|
file-entry-cache@8.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
flat-cache: 4.0.1
|
flat-cache: 4.0.1
|
||||||
@@ -6350,6 +6387,17 @@ snapshots:
|
|||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
source-map-js: 1.2.1
|
source-map-js: 1.2.1
|
||||||
|
|
||||||
|
posthog-js@1.255.0:
|
||||||
|
dependencies:
|
||||||
|
core-js: 3.43.0
|
||||||
|
fflate: 0.4.8
|
||||||
|
preact: 10.26.9
|
||||||
|
web-vitals: 4.2.4
|
||||||
|
|
||||||
|
posthog-node@5.1.1: {}
|
||||||
|
|
||||||
|
preact@10.26.9: {}
|
||||||
|
|
||||||
prelude-ls@1.2.1: {}
|
prelude-ls@1.2.1: {}
|
||||||
|
|
||||||
pretty-format@29.7.0:
|
pretty-format@29.7.0:
|
||||||
@@ -6937,6 +6985,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
makeerror: 1.0.12
|
makeerror: 1.0.12
|
||||||
|
|
||||||
|
web-vitals@4.2.4: {}
|
||||||
|
|
||||||
which-boxed-primitive@1.1.1:
|
which-boxed-primitive@1.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-bigint: 1.1.0
|
is-bigint: 1.1.0
|
||||||
|
|||||||
Reference in New Issue
Block a user