diff --git a/.env.example b/.env.example index 30973b2c1e..14a080834d 100644 --- a/.env.example +++ b/.env.example @@ -5,4 +5,6 @@ PUBLIC_APPWRITE_ENDPOINT=http://localhost/v1 PUBLIC_STRIPE_KEY= PUBLIC_GROWTH_ENDPOINT= PUBLIC_CONSOLE_EMAIL_VERIFICATION=false -PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=true \ No newline at end of file +PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=true +PUBLIC_POSTHOG_API_KEY= +PUBLIC_POSTHOG_HOST= \ No newline at end of file diff --git a/package.json b/package.json index a2e1778fe3..0aa4a0efa7 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "nanoid": "^5.1.5", "nanotar": "^0.1.1", "plausible-tracker": "^0.3.9", + "posthog-js": "^1.312.0", "pretty-bytes": "^6.1.1", "prismjs": "^1.30.0", "remarkable": "^2.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9a968c01e9..572d2d583b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,6 +74,9 @@ importers: plausible-tracker: specifier: ^0.3.9 version: 0.3.9 + posthog-js: + specifier: ^1.312.0 + version: 1.312.0 pretty-bytes: specifier: ^6.1.1 version: 6.1.1 @@ -1019,6 +1022,9 @@ packages: '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + '@posthog/core@1.9.0': + resolution: {integrity: sha512-j7KSWxJTUtNyKynLt/p0hfip/3I46dWU2dk+pt7dKRoz2l5CYueHuHK4EO7Wlgno5yo1HO4sc4s30MXMTICHJw==} + '@prisma/instrumentation@5.22.0': resolution: {integrity: sha512-LxccF392NN37ISGxIurUljZSh1YWnphO34V5a0+T7FVQG2u9bhAXRTJpgmQ3483woVhkraQZFF7cbRrpbw/F4Q==} @@ -1855,6 +1861,9 @@ packages: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} + core-js@3.47.0: + resolution: {integrity: sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==} + cron-parser@4.9.0: resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} engines: {node: '>=12.0.0'} @@ -2282,6 +2291,9 @@ packages: picomatch: optional: true + fflate@0.4.8: + resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==} + fflate@0.8.2: resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} @@ -2973,6 +2985,12 @@ packages: resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} engines: {node: '>=0.10.0'} + posthog-js@1.312.0: + resolution: {integrity: sha512-rdXprhuRzhutU8powMJpIfC0uRtI3OyuYktmLhZRMsD4DQaO3fnudKNq4zxtNmqMPFCSTfmlBH8ByLNOppm2tg==} + + preact@10.28.1: + resolution: {integrity: sha512-u1/ixq/lVQI0CakKNvLDEcW5zfCjUQfZdK9qqWuIJtsezuyG6pk9TWj75GMuI/EzRSZB/VAE43sNWWZfiy8psw==} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -3641,6 +3659,9 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} + web-vitals@4.2.4: + resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} + webgl-sdf-generator@1.1.1: resolution: {integrity: sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==} @@ -4583,6 +4604,10 @@ snapshots: '@popperjs/core@2.11.8': {} + '@posthog/core@1.9.0': + dependencies: + cross-spawn: 7.0.6 + '@prisma/instrumentation@5.22.0': dependencies: '@opentelemetry/api': 1.9.0 @@ -5593,6 +5618,8 @@ snapshots: cookie@0.6.0: {} + core-js@3.47.0: {} + cron-parser@4.9.0: dependencies: luxon: 3.6.0 @@ -6082,6 +6109,8 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fflate@0.4.8: {} + fflate@0.8.2: {} file-entry-cache@8.0.0: @@ -6736,6 +6765,16 @@ snapshots: dependencies: xtend: 4.0.2 + posthog-js@1.312.0: + dependencies: + '@posthog/core': 1.9.0 + core-js: 3.47.0 + fflate: 0.4.8 + preact: 10.28.1 + web-vitals: 4.2.4 + + preact@10.28.1: {} + prelude-ls@1.2.1: {} prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.25.3): @@ -7431,6 +7470,8 @@ snapshots: dependencies: xml-name-validator: 5.0.0 + web-vitals@4.2.4: {} + webgl-sdf-generator@1.1.1: {} webidl-conversions@3.0.1: {} diff --git a/src/lib/actions/analytics.ts b/src/lib/actions/analytics.ts index 6880f1c2f4..85878b6021 100644 --- a/src/lib/actions/analytics.ts +++ b/src/lib/actions/analytics.ts @@ -7,6 +7,7 @@ import { ENV, MODE, VARS, isCloud } from '$lib/system'; import { AppwriteException } from '@appwrite.io/console'; import { browser } from '$app/environment'; import { getReferrerAndUtmSource, getTrackedQueryParams } from '$lib/helpers/utm'; +// import { posthog } from './posthog'; function plausible(domain: string): AnalyticsPlugin { if (!browser) return { name: 'analytics-plugin-plausible' }; @@ -72,6 +73,9 @@ export function trackEvent(name: string, data: object = null): void { } else { analytics.track(name, { ...data, path }); sendEventToGrowth(name, path, data); + /*if (posthog) { + posthog.capture(name, { ...data, path }); + }*/ } } diff --git a/src/lib/actions/posthog.ts b/src/lib/actions/posthog.ts new file mode 100644 index 0000000000..ae81db1e63 --- /dev/null +++ b/src/lib/actions/posthog.ts @@ -0,0 +1,40 @@ +import posthog from 'posthog-js'; +import { browser } from '$app/environment'; +import { VARS, ENV } from '$lib/system'; +import type { Models } from '@appwrite.io/console'; +import { preferences } from '$lib/stores/preferences'; + +const POSTHOG_PREFS_KEY = 'posthog_session_recorded'; + +if (browser && VARS.POSTHOG_API_KEY && !ENV.DEV && !ENV.TEST) { + posthog.init(VARS.POSTHOG_API_KEY, { + api_host: VARS.POSTHOG_HOST, + person_profiles: 'identified_only', + capture_pageview: true, + capture_pageleave: true, + session_recording: { + recordCrossOriginIframes: false, + /*maskTextSelector: '.ph-no-capture'*/ + }, + autocapture: true, + disable_session_recording: !ENV.PROD + }); +} + +export async function initializeSessionRecording(user: Models.User): Promise { + if (!posthog) return; + + const hasRecordedFirstSession = preferences.getKey(POSTHOG_PREFS_KEY, false); + + if (!hasRecordedFirstSession) { + posthog.identify(user.$id, { + account_created: user.$createdAt + }); + posthog.startSessionRecording(); + await preferences.setKey(POSTHOG_PREFS_KEY, true); + } else { + posthog.stopSessionRecording(); + } +} + +export { posthog }; diff --git a/src/lib/system.ts b/src/lib/system.ts index 4532609925..c6b1904ad7 100644 --- a/src/lib/system.ts +++ b/src/lib/system.ts @@ -12,7 +12,9 @@ export const VARS = { GROWTH_ENDPOINT: env.PUBLIC_GROWTH_ENDPOINT ?? undefined, PUBLIC_STRIPE_KEY: env.PUBLIC_STRIPE_KEY ?? undefined, EMAIL_VERIFICATION: env.PUBLIC_CONSOLE_EMAIL_VERIFICATION === 'true', - MOCK_AI_SUGGESTIONS: (env.PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS ?? 'true') === 'true' + MOCK_AI_SUGGESTIONS: (env.PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS ?? 'true') === 'true', + POSTHOG_API_KEY: env.PUBLIC_POSTHOG_API_KEY ?? undefined, + POSTHOG_HOST: env.PUBLIC_POSTHOG_HOST ?? undefined }; export const ENV = { diff --git a/src/routes/(console)/+layout.svelte b/src/routes/(console)/+layout.svelte index 109bdabb3a..cdf27d0c0e 100644 --- a/src/routes/(console)/+layout.svelte +++ b/src/routes/(console)/+layout.svelte @@ -40,6 +40,8 @@ import MobileSupportModal from './wizard/support/mobileSupportModal.svelte'; import { showSupportModal } from './wizard/support/store'; import { activeHeaderAlert, consoleVariables } from './store'; + import { user } from '$lib/stores/user'; + import { initializeSessionRecording } from '$lib/actions/posthog'; import { base } from '$app/paths'; import { headerAlert } from '$lib/stores/headerAlert'; @@ -270,6 +272,10 @@ $stripe = await loadStripe(VARS.PUBLIC_STRIPE_KEY); await checkForMissingPaymentMethod(); } + + if ($user && isCloud) { + await initializeSessionRecording($user); + } }); function checkForFeedback(interval: number) { diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 571542d06f..fd8368eb70 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -110,7 +110,7 @@ } }); - $: { + $effect(() => { if (browser) { const isCloudClass = isCloud ? 'is-cloud' : ''; @@ -137,7 +137,7 @@ document.body.classList.remove('no-transition'); }); } - } + }); const preloadFonts = [ base + '/fonts/inter/inter-v8-latin-600.woff2',