diff --git a/apps/memoro/apps/web/src/lib/components/AudioPlayer.svelte b/apps/memoro/apps/web/src/lib/components/AudioPlayer.svelte index e65ba874d..7673b290a 100644 --- a/apps/memoro/apps/web/src/lib/components/AudioPlayer.svelte +++ b/apps/memoro/apps/web/src/lib/components/AudioPlayer.svelte @@ -2,6 +2,7 @@ import { onMount } from 'svelte'; import Icon from '$lib/components/Icon.svelte'; import Text from '$lib/components/atoms/Text.svelte'; + import { MemoroEvents } from '@manacore/shared-utils/analytics'; let { src, duration }: { src: string; duration?: number } = $props(); @@ -44,6 +45,7 @@ audio.pause(); } else { audio.play(); + MemoroEvents.playbackStarted(); } isPlaying = !isPlaying; } diff --git a/apps/memoro/apps/web/src/lib/services/memoService.ts b/apps/memoro/apps/web/src/lib/services/memoService.ts index dec412823..39d479b0a 100644 --- a/apps/memoro/apps/web/src/lib/services/memoService.ts +++ b/apps/memoro/apps/web/src/lib/services/memoService.ts @@ -16,6 +16,7 @@ import { } from '$lib/data/local-store'; import type { Memo, Tag, Memory } from '$lib/types/memo.types'; import { getCachedUrl, setCachedUrl, cleanupExpiredUrls } from '$lib/utils/indexedDBCache'; +import { MemoroEvents } from '@manacore/shared-utils/analytics'; // ─── Type mappers ────────────────────────────────────────────── @@ -269,6 +270,7 @@ export class MemoService { async deleteMemo(memoId: string): Promise { await memoCollection.delete(memoId); + MemoroEvents.memoDeleted(); } async addTagToMemo(memoId: string, tagId: string): Promise { diff --git a/apps/memoro/apps/web/src/lib/services/spaceService.ts b/apps/memoro/apps/web/src/lib/services/spaceService.ts index b072d0f73..fe5b3b97b 100644 --- a/apps/memoro/apps/web/src/lib/services/spaceService.ts +++ b/apps/memoro/apps/web/src/lib/services/spaceService.ts @@ -4,6 +4,7 @@ */ import { env } from '$lib/config/env'; +import { MemoroEvents } from '@manacore/shared-utils/analytics'; const API = () => env.server.memoroUrl.replace(/\/$/, ''); @@ -137,6 +138,7 @@ class SpaceService { }); await throwOnError(r, 'Error creating space'); const d = await r.json(); + MemoroEvents.spaceCreated(); return mapSpace(d.space); } @@ -156,6 +158,7 @@ class SpaceService { headers: headers(token), }); await throwOnError(r, 'Error deleting space'); + MemoroEvents.spaceDeleted(); return true; } @@ -165,6 +168,7 @@ class SpaceService { headers: headers(token), }); await throwOnError(r, 'Error leaving space'); + MemoroEvents.spaceLeft(); return true; } @@ -184,6 +188,7 @@ class SpaceService { body: JSON.stringify({ memoId }), }); await throwOnError(r, 'Error linking memo to space'); + MemoroEvents.memoLinkedToSpace(); return true; } @@ -194,6 +199,7 @@ class SpaceService { body: JSON.stringify({ memoId }), }); await throwOnError(r, 'Error unlinking memo from space'); + MemoroEvents.memoUnlinkedFromSpace(); return true; } @@ -219,6 +225,7 @@ class SpaceService { }); await throwOnError(r, 'Error inviting user to space'); const d = await r.json(); + MemoroEvents.inviteSent(); return d.invite?.id ?? d.inviteId; } @@ -247,6 +254,7 @@ class SpaceService { body: JSON.stringify({ inviteId }), }); await throwOnError(r, 'Error accepting invite'); + MemoroEvents.inviteAccepted(); return true; } @@ -257,6 +265,7 @@ class SpaceService { body: JSON.stringify({ inviteId }), }); await throwOnError(r, 'Error declining invite'); + MemoroEvents.inviteDeclined(); return true; } diff --git a/apps/memoro/apps/web/src/lib/services/transcriptionService.ts b/apps/memoro/apps/web/src/lib/services/transcriptionService.ts index 9004cbd2d..de984e7d6 100644 --- a/apps/memoro/apps/web/src/lib/services/transcriptionService.ts +++ b/apps/memoro/apps/web/src/lib/services/transcriptionService.ts @@ -4,6 +4,7 @@ */ import { env } from '$lib/config/env'; +import { MemoroEvents } from '@manacore/shared-utils/analytics'; const SERVER_URL = env.server.memoroUrl.replace(/\/$/, ''); @@ -75,6 +76,7 @@ export async function triggerTranscription({ }; } + MemoroEvents.memoCreated(mediaType); return { success: true }; } catch (error) { return { diff --git a/apps/memoro/apps/web/src/lib/stores/theme.ts b/apps/memoro/apps/web/src/lib/stores/theme.ts index b15b3f5c4..5c41a3e15 100644 --- a/apps/memoro/apps/web/src/lib/stores/theme.ts +++ b/apps/memoro/apps/web/src/lib/stores/theme.ts @@ -1,5 +1,6 @@ import { writable } from 'svelte/store'; import { browser } from '$app/environment'; +import { MemoroEvents } from '@manacore/shared-utils/analytics'; export type ThemeMode = 'light' | 'dark' | 'system'; export type ThemeVariant = 'lume' | 'nature' | 'ocean' | 'stone'; @@ -63,6 +64,7 @@ function createThemeStore() { if (browser) { localStorage.setItem(THEME_STORAGE_KEY, JSON.stringify(newState)); applyTheme(newState); + MemoroEvents.themeChanged(mode); } return newState; }); @@ -73,6 +75,7 @@ function createThemeStore() { if (browser) { localStorage.setItem(THEME_STORAGE_KEY, JSON.stringify(newState)); applyTheme(newState); + MemoroEvents.themeChanged(variant); } return newState; }); diff --git a/apps/memoro/apps/web/src/routes/(protected)/record/+page.svelte b/apps/memoro/apps/web/src/routes/(protected)/record/+page.svelte index f0bb31254..a24460652 100644 --- a/apps/memoro/apps/web/src/routes/(protected)/record/+page.svelte +++ b/apps/memoro/apps/web/src/routes/(protected)/record/+page.svelte @@ -6,6 +6,7 @@ import RecordingButton from '$lib/components/RecordingButton.svelte'; import BlueprintSelector from '$lib/components/BlueprintSelector.svelte'; import AdviceCarousel from '$lib/components/AdviceCarousel.svelte'; + import { MemoroEvents } from '@manacore/shared-utils/analytics'; // Standard blueprint ID - matches the mobile app constant const STANDARD_BLUEPRINT_ID = '11111111-2222-3333-4444-555555555555'; @@ -14,6 +15,7 @@ function handleSelectBlueprint(blueprintId: string | null) { selectedBlueprintId = blueprintId; + if (blueprintId) MemoroEvents.blueprintSelected(blueprintId); } async function handleRecordingComplete(audioBlob: Blob) { @@ -25,6 +27,7 @@ try { recording.setStatus('uploading'); + MemoroEvents.recordingCompleted($recording.duration); const audioDuration = $recording.duration; diff --git a/packages/shared-utils/src/analytics.ts b/packages/shared-utils/src/analytics.ts index 4df0d7f5c..278462961 100644 --- a/packages/shared-utils/src/analytics.ts +++ b/packages/shared-utils/src/analytics.ts @@ -441,6 +441,39 @@ export const SubscriptionEvents = { trialEnded: (converted: boolean) => trackEvent('trial_ended', { converted }), }; +/** + * Memoro App Events + */ +export const MemoroEvents = { + memoCreated: (mediaType?: string) => + trackEvent('memo_created', mediaType ? { media_type: mediaType } : undefined), + memoDeleted: () => trackEvent('memo_deleted'), + memoCombined: (count: number) => trackEvent('memo_combined', { memo_count: count }), + memoQuestioned: () => trackEvent('memo_questioned'), + recordingStarted: () => trackEvent('recording_started'), + recordingCompleted: (durationSeconds: number) => + trackEvent('recording_completed', { duration_seconds: durationSeconds }), + recordingAppended: () => trackEvent('recording_appended'), + transcriptionRetried: () => trackEvent('transcription_retried'), + headlineRetried: () => trackEvent('headline_retried'), + spaceCreated: () => trackEvent('space_created'), + spaceDeleted: () => trackEvent('space_deleted'), + spaceLeft: () => trackEvent('space_left'), + memoLinkedToSpace: () => trackEvent('memo_linked_to_space'), + memoUnlinkedFromSpace: () => trackEvent('memo_unlinked_from_space'), + inviteSent: () => trackEvent('invite_sent'), + inviteAccepted: () => trackEvent('invite_accepted'), + inviteDeclined: () => trackEvent('invite_declined'), + meetingBotCreated: (platform: string) => trackEvent('meeting_bot_created', { platform }), + meetingBotStopped: () => trackEvent('meeting_bot_stopped'), + recordingToMemo: () => trackEvent('recording_to_memo'), + blueprintSelected: (blueprintId: string) => + trackEvent('blueprint_selected', { blueprint_id: blueprintId }), + playbackStarted: () => trackEvent('playback_started'), + settingsUpdated: (setting: string) => trackEvent('settings_updated', { setting }), + themeChanged: (theme: string) => trackEvent('theme_changed', { theme }), +}; + /** * General App Events */