feat: integrate umami (#1809)
* feat: integrate umami * fix: linter issue * fix: run eslint * fix window umami null * fix property type error * fix: check configuration before requesting analytics script * fix: test cases --------- Co-authored-by: Louis <louis@jan.ai>
This commit is contained in:
parent
11e2a763cb
commit
36cd5988d4
@ -98,8 +98,8 @@ jobs:
|
||||
make build-and-publish
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ANALYTICS_ID: ${{ secrets.JAN_APP_POSTHOG_PROJECT_API_KEY }}
|
||||
ANALYTICS_HOST: ${{ secrets.JAN_APP_POSTHOG_URL }}
|
||||
ANALYTICS_ID: ${{ secrets.JAN_APP_UMAMI_PROJECT_API_KEY }}
|
||||
ANALYTICS_HOST: ${{ secrets.JAN_APP_UMAMI_URL }}
|
||||
|
||||
- name: Upload Artifact .deb file
|
||||
if: inputs.public_provider != 'github'
|
||||
|
||||
4
.github/workflows/template-build-macos.yml
vendored
4
.github/workflows/template-build-macos.yml
vendored
@ -137,8 +137,8 @@ jobs:
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
APP_PATH: "."
|
||||
DEVELOPER_ID: ${{ secrets.DEVELOPER_ID }}
|
||||
ANALYTICS_ID: ${{ secrets.JAN_APP_POSTHOG_PROJECT_API_KEY }}
|
||||
ANALYTICS_HOST: ${{ secrets.JAN_APP_POSTHOG_URL }}
|
||||
ANALYTICS_ID: ${{ secrets.JAN_APP_UMAMI_PROJECT_API_KEY }}
|
||||
ANALYTICS_HOST: ${{ secrets.JAN_APP_UMAMI_URL }}
|
||||
|
||||
- name: Upload Artifact
|
||||
if: inputs.public_provider != 'github'
|
||||
|
||||
@ -127,8 +127,8 @@ jobs:
|
||||
make build-and-publish
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ANALYTICS_ID: ${{ secrets.JAN_APP_POSTHOG_PROJECT_API_KEY }}
|
||||
ANALYTICS_HOST: ${{ secrets.JAN_APP_POSTHOG_URL }}
|
||||
ANALYTICS_ID: ${{ secrets.JAN_APP_UMAMI_PROJECT_API_KEY }}
|
||||
ANALYTICS_HOST: ${{ secrets.JAN_APP_UMAMI_URL }}
|
||||
AZURE_KEY_VAULT_URI: ${{ secrets.AZURE_KEY_VAULT_URI }}
|
||||
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
GTM_ID=xxxx
|
||||
POSTHOG_PROJECT_API_KEY=xxxx
|
||||
POSTHOG_APP_URL=xxxx
|
||||
UMAMI_PROJECT_API_KEY=xxxx
|
||||
UMAMI_APP_URL=xxxx
|
||||
ALGOLIA_API_KEY=xxxx
|
||||
ALGOLIA_APP_ID=xxxx
|
||||
@ -38,7 +38,6 @@ test.afterAll(async () => {
|
||||
})
|
||||
|
||||
test('explores hub', async () => {
|
||||
// Set the timeout for this test to 60 seconds
|
||||
test.setTimeout(TIMEOUT)
|
||||
await page.getByTestId('Hub').first().click({
|
||||
timeout: TIMEOUT,
|
||||
|
||||
@ -38,6 +38,7 @@ test.afterAll(async () => {
|
||||
})
|
||||
|
||||
test('renders left navigation panel', async () => {
|
||||
test.setTimeout(TIMEOUT)
|
||||
const systemMonitorBtn = await page
|
||||
.getByTestId('System Monitor')
|
||||
.first()
|
||||
@ -50,8 +51,11 @@ test('renders left navigation panel', async () => {
|
||||
.isEnabled({ timeout: TIMEOUT })
|
||||
expect([systemMonitorBtn, settingsBtn].filter((e) => !e).length).toBe(0)
|
||||
// Chat section should be there
|
||||
const apiServer = await page.getByTestId('Local API Server').first()
|
||||
expect(apiServer).toBeVisible({
|
||||
await page.getByTestId('Local API Server').first().click({
|
||||
timeout: TIMEOUT,
|
||||
})
|
||||
const localServer = await page.getByTestId('local-server-testid').first()
|
||||
await expect(localServer).toBeVisible({
|
||||
timeout: TIMEOUT,
|
||||
})
|
||||
})
|
||||
|
||||
@ -38,7 +38,8 @@ test.afterAll(async () => {
|
||||
})
|
||||
|
||||
test('shows settings', async () => {
|
||||
test.setTimeout(TIMEOUT)
|
||||
await page.getByTestId('Settings').first().click({ timeout: TIMEOUT })
|
||||
const settingDescription = page.getByTestId('testid-setting-description')
|
||||
expect(settingDescription).toBeVisible({ timeout: TIMEOUT })
|
||||
await expect(settingDescription).toBeVisible({ timeout: TIMEOUT })
|
||||
})
|
||||
|
||||
@ -6,8 +6,6 @@ import { Toaster } from 'react-hot-toast'
|
||||
|
||||
import { TooltipProvider } from '@janhq/uikit'
|
||||
|
||||
import { PostHogProvider } from 'posthog-js/react'
|
||||
|
||||
import GPUDriverPrompt from '@/containers/GPUDriverPromptModal'
|
||||
import EventListenerWrapper from '@/containers/Providers/EventListener'
|
||||
import JotaiWrapper from '@/containers/Providers/Jotai'
|
||||
@ -21,7 +19,7 @@ import {
|
||||
setupBaseExtensions,
|
||||
} from '@/services/extensionService'
|
||||
|
||||
import { instance } from '@/utils/posthog'
|
||||
import Umami from '@/utils/umami'
|
||||
|
||||
import KeyListener from './KeyListener'
|
||||
|
||||
@ -70,16 +68,14 @@ const Providers = (props: PropsWithChildren) => {
|
||||
}, [setupCore])
|
||||
|
||||
return (
|
||||
<PostHogProvider client={instance}>
|
||||
<JotaiWrapper>
|
||||
<ThemeWrapper>
|
||||
<Umami />
|
||||
{setupCore && activated && (
|
||||
<KeyListener>
|
||||
<FeatureToggleWrapper>
|
||||
<EventListenerWrapper>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
{children}
|
||||
</TooltipProvider>
|
||||
<TooltipProvider delayDuration={0}>{children}</TooltipProvider>
|
||||
{!isMac && <GPUDriverPrompt />}
|
||||
</EventListenerWrapper>
|
||||
<Toaster />
|
||||
@ -88,7 +84,6 @@ const Providers = (props: PropsWithChildren) => {
|
||||
)}
|
||||
</ThemeWrapper>
|
||||
</JotaiWrapper>
|
||||
</PostHogProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -25,10 +25,8 @@ const nextConfig = {
|
||||
...config.plugins,
|
||||
new webpack.DefinePlugin({
|
||||
VERSION: JSON.stringify(packageJson.version),
|
||||
ANALYTICS_ID:
|
||||
JSON.stringify(process.env.ANALYTICS_ID) ?? JSON.stringify('xxx'),
|
||||
ANALYTICS_HOST:
|
||||
JSON.stringify(process.env.ANALYTICS_HOST) ?? JSON.stringify('xxx'),
|
||||
ANALYTICS_ID: JSON.stringify(process.env.ANALYTICS_ID),
|
||||
ANALYTICS_HOST: JSON.stringify(process.env.ANALYTICS_HOST),
|
||||
API_BASE_URL: JSON.stringify('http://localhost:1337'),
|
||||
isMac: process.platform === 'darwin',
|
||||
isWindows: process.platform === 'win32',
|
||||
|
||||
@ -103,7 +103,7 @@ const LocalServerScreen = () => {
|
||||
}, [handleChangePort, port])
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full">
|
||||
<div className="flex h-full w-full" data-testid="local-server-testid">
|
||||
{/* Left SideBar */}
|
||||
<div className="flex h-full w-60 flex-shrink-0 flex-col overflow-y-auto border-r border-border">
|
||||
<div className="p-4">
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
import posthog, { Properties } from 'posthog-js'
|
||||
|
||||
// Initialize PostHog
|
||||
posthog.init(ANALYTICS_ID, {
|
||||
api_host: ANALYTICS_HOST,
|
||||
autocapture: false,
|
||||
capture_pageview: false,
|
||||
capture_pageleave: false,
|
||||
rageclick: false,
|
||||
})
|
||||
// Export the PostHog instance
|
||||
export const instance = posthog
|
||||
|
||||
// Enum for Analytics Events
|
||||
export enum AnalyticsEvent {
|
||||
Ping = 'Ping',
|
||||
}
|
||||
|
||||
// Function to determine the operating system
|
||||
function getOperatingSystem(): string {
|
||||
if (isMac) return 'MacOS'
|
||||
if (isWindows) return 'Windows'
|
||||
if (isLinux) return 'Linux'
|
||||
return 'Unknown'
|
||||
}
|
||||
|
||||
function captureAppVersionAndOS() {
|
||||
const properties: Properties = {
|
||||
$appVersion: VERSION,
|
||||
$userOperatingSystem: getOperatingSystem(),
|
||||
// Set the following Posthog default properties to empty strings
|
||||
$initial_browser: '',
|
||||
$browser: '',
|
||||
$initial_browser_version: '',
|
||||
$browser_version: '',
|
||||
$initial_current_url: '',
|
||||
$current_url: '',
|
||||
$initial_device_type: '',
|
||||
$device_type: '',
|
||||
$initial_pathname: '',
|
||||
$pathname: '',
|
||||
$initial_referrer: '',
|
||||
$referrer: '',
|
||||
$initial_referring_domain: '',
|
||||
$referring_domain: '',
|
||||
}
|
||||
posthog.capture(AnalyticsEvent.Ping, properties)
|
||||
}
|
||||
|
||||
captureAppVersionAndOS()
|
||||
65
web/utils/umami.tsx
Normal file
65
web/utils/umami.tsx
Normal file
@ -0,0 +1,65 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
import Script from 'next/script'
|
||||
|
||||
// Define the type for the umami data object
|
||||
interface UmamiData {
|
||||
version: string
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
umami:
|
||||
| {
|
||||
track: (event: string, data?: UmamiData) => void
|
||||
}
|
||||
| undefined
|
||||
}
|
||||
}
|
||||
|
||||
const Umami = () => {
|
||||
const appVersion = VERSION
|
||||
const analyticsHost = ANALYTICS_HOST
|
||||
const analyticsId = ANALYTICS_ID
|
||||
|
||||
useEffect(() => {
|
||||
if (!appVersion || !analyticsHost || !analyticsId) return
|
||||
const ping = () => {
|
||||
// Check if umami is defined before ping
|
||||
if (window.umami !== null && typeof window.umami !== 'undefined') {
|
||||
window.umami.track(appVersion, {
|
||||
version: appVersion,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for umami to be defined before ping
|
||||
if (window.umami !== null && typeof window.umami !== 'undefined') {
|
||||
ping()
|
||||
} else {
|
||||
// Listen for umami script load event
|
||||
document.addEventListener('umami:loaded', ping)
|
||||
}
|
||||
|
||||
// Cleanup function to remove event listener if the component unmounts
|
||||
return () => {
|
||||
document.removeEventListener('umami:loaded', ping)
|
||||
}
|
||||
}, [appVersion, analyticsHost, analyticsId])
|
||||
|
||||
return (
|
||||
<>
|
||||
{appVersion && analyticsHost && analyticsId && (
|
||||
<Script
|
||||
src={analyticsHost}
|
||||
data-website-id={analyticsId}
|
||||
data-cache="true"
|
||||
defer
|
||||
onLoad={() => document.dispatchEvent(new Event('umami:loaded'))}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Umami
|
||||
Loading…
x
Reference in New Issue
Block a user