Projects included: - maerchenzauber (NestJS backend + Expo mobile + SvelteKit web + Astro landing) - manacore (Expo mobile + SvelteKit web + Astro landing) - manadeck (NestJS backend + Expo mobile + SvelteKit web) - memoro (Expo mobile + SvelteKit web + Astro landing) This commit preserves the current state before monorepo restructuring. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
13 KiB
Analytics Integration Documentation
Overview
The Memoro app uses a multi-platform analytics system with PostHog for mobile (iOS/Android) and Umami for web. This document describes all tracked events, implementation details, and best practices.
Architecture
Service Layer
- MultiPlatformAnalyticsService (
/features/analytics/services/multiPlatformAnalytics.ts)- Routes analytics calls to the appropriate service based on platform
- PostHog for mobile (iOS/Android)
- Umami for web
Key Components
- AnalyticsProvider - Initializes analytics on app start
- AnalyticsNavigationTracker - Automatic screen tracking via Expo Router
- useAnalytics - Hook for tracking events
- useScreenTracking - Hook for manual screen tracking
- useFeatureFlag - Hook for PostHog feature flags (mobile only)
Configuration
PostHog (Mobile) - v3.x
- API Key: Set in
EXPO_PUBLIC_POSTHOG_KEY - Host:
https://eu.i.posthog.com(EU datacenter) - Auto-capture: Enabled for lifecycle events
- Debug Mode: Enabled in development
- Initialization: Automatic in constructor (no
initAsync()needed) - Methods: Synchronous (no
async/awaitrequired)
Umami (Web)
- Website ID: Set in
EXPO_PUBLIC_UMAMI_WEBSITE_ID - Script URL:
https://umami.manacore.ai/script.js
Tracked Events
🎙️ Recording Events
recording_started
Fired when user starts a recording
{
blueprint_id: string,
space_id: string | null,
languages: string[],
language_count: number,
is_append: boolean,
theme: 'light' | 'dark'
}
recording_stopped
Fired when recording is stopped (manual or automatic)
{
duration_seconds: number,
reason: 'manual' | 'auto' | 'error',
blueprint_id: string | null,
space_id: string | null,
has_result: boolean
}
recording_failed
Fired when recording fails
{
error_type: 'insufficient_credits' | 'permission_denied' | 'network_error',
blueprint_id: string | null,
space_id: string | null,
can_ask_again?: boolean // For permission errors
}
recording_permission_retry
Fired when user retries after permission denial
{
blueprint_id: string | null,
space_id: string | null
}
📝 Memo Events
memo_viewed
Fired when a memo is opened
{
memo_id: string,
source: 'direct_navigation' | 'list' | 'search',
is_translated: boolean
}
memo_shared
Fired when user shares a memo
{
memo_id: string,
has_transcript: boolean,
has_memories: boolean
}
memo_deleted
Fired when a memo is deleted
{
memo_id: string,
has_transcript: boolean,
duration_seconds: number | null
}
memo_pinned / memo_unpinned
Fired when pin status changes
{
memo_id: string
}
transcript_copied
Fired when transcript is copied to clipboard
{
memo_id: string,
transcript_length: number
}
📋 List & Search Events
memo_list_filtered
Fired when filters are applied to memo list
{
filter_type: 'tags' | 'space' | 'date',
tag_count?: number,
tag_ids?: string[]
}
memo_searched
Fired when user performs a search
{
query_length: number,
has_results: boolean
}
memo_bulk_action
Fired for bulk operations on multiple memos
{
action: 'delete' | 'add_tag' | 'remove_tag' | 'move_space',
memo_count: number,
tag_id?: string,
space_id?: string
}
🤖 AI Processing Events
blueprint_selected
Fired when user selects a blueprint
{
blueprint_id: string | null,
is_standard: boolean,
space_id: string | null
}
blueprint_applied
Fired when blueprint processing completes
{
memo_id: string,
blueprint_id: string,
mana_cost: number,
processing_time_ms: number
}
question_asked
Fired when user asks a question about a memo
{
memo_id: string,
question_length: number,
mana_cost: 5 // Fixed cost
}
memos_combined
Fired when multiple memos are combined
{
memo_count: number,
blueprint_id: string,
has_prompt: boolean,
mana_cost: number // 5 per memo
}
memo_reprocessed
Fired when memo is reprocessed with new settings
{
memo_id: string,
language: string,
has_blueprint: boolean,
blueprint_id?: string
}
memo_translated
Fired when memo is translated
{
memo_id: string,
target_language: string
}
💳 Subscription & Credits Events
subscription_page
Screen view for subscription page
{
is_b2b: boolean
}
subscription_purchase_attempted
Fired when user initiates purchase
{
plan_id: string,
plan_type: 'individual' | 'team' | 'enterprise',
billing_cycle: 'monthly' | 'yearly',
credits: number,
price: number
}
subscription_purchased
Fired on successful purchase
{
plan_id: string,
plan_type: 'individual' | 'team' | 'enterprise',
billing_cycle: 'monthly' | 'yearly',
credits: number,
price: number
}
subscription_restore_attempted / subscription_restore_completed
Fired during purchase restoration flow
📱 Screen Tracking
Automatic screen tracking via AnalyticsNavigationTracker:
recording_screen- Home/Recording tabmemos_list- Memos tabmemo_detail- Individual memo viewsubscription_page- Subscription/credits pagesettings- Settings pageprofile- User profile
Each screen event includes:
{
path: string, // URL path
timestamp: string,
focused_at?: string // For manual tracking
}
⚡ Performance Events
memo_load_time
Tracks memo loading performance
{
memo_id: string,
duration_ms: number
}
🔥 Error Tracking
error_occurred
Generic error tracking
{
error_name: string,
error_message: string,
error_stack?: string, // Only in dev
screen?: string,
action?: string,
user_id?: string
}
Implementation Examples
Basic Event Tracking
import { useAnalytics } from '~/features/analytics';
function MyComponent() {
const { track } = useAnalytics();
const handleAction = () => {
// Note: track() is synchronous in PostHog v3
track('custom_event', {
property1: 'value',
property2: 123
});
};
}
Screen Tracking
import { useScreenTracking } from '~/features/analytics';
function MyScreen() {
// Automatic screen tracking
useScreenTracking('my_screen', {
tab: 'main',
source: 'navigation'
});
}
Error Tracking
import { trackError } from '~/features/analytics';
try {
// Some operation
} catch (error) {
trackError(track, error, {
screen: 'memo_detail',
action: 'load_data',
memo_id: id
});
}
Performance Tracking
import { trackPerformance } from '~/features/analytics';
const startTime = Date.now();
// ... operation ...
trackPerformance(track, 'operation_name', Date.now() - startTime, {
additional_context: 'value'
});
Feature Flags (Mobile Only)
import { useFeatureFlag } from '~/features/analytics';
function MyComponent() {
const showNewFeature = useFeatureFlag('new-feature-flag');
if (showNewFeature) {
return <NewFeature />;
}
return <OldFeature />;
}
PostHog Dashboard Setup
Recommended Dashboards
1. User Journey Funnel
Create a funnel visualization:
recording_startedrecording_stoppedmemo_viewedmemo_shared
2. Feature Adoption
Track usage of key features:
- Blueprint usage (non-standard selections)
- AI features (questions, reprocessing, translation)
- Collaboration (space usage, sharing)
3. Error Monitoring
- Group
recording_failedbyerror_type - Track
error_occurredby screen and action - Monitor permission denial rates
4. Performance Metrics
memo_load_timepercentiles (P50, P95, P99)- Track by device type and network conditions
5. Engagement Metrics
- Daily/Weekly/Monthly Active Users
- Session duration
- Actions per session
- Retention cohorts
Key Metrics to Monitor
-
Recording Success Rate
recording_stopped/recording_started- Identify drop-off points
-
AI Feature Usage
- Questions per user
- Blueprint adoption rate
- Mana consumption patterns
-
User Retention
- Day 1, 7, 30 retention
- Feature-specific retention
-
Error Rate
- Errors per user
- Error types distribution
- Recovery success rate
Privacy & Compliance
Data Collected
- No PII in event properties by default
- User IDs are anonymized
- Device info for debugging
- Usage patterns and feature adoption
GDPR Compliance
- PostHog hosted in EU (eu.i.posthog.com)
- User consent required before tracking
- Option to opt-out in settings
- Data retention policies applied
Testing Analytics
Development Testing
- Enable debug mode: Events logged to console
- Use PostHog Live Events view for real-time monitoring
- Test event in dev settings:
/app/(protected)/settings.tsx - PostHog v3: Methods are synchronous, no need for await
Production Validation
- Check PostHog dashboard for event flow
- Verify event properties are correct
- Monitor for any tracking errors
- Validate funnel completion rates
Troubleshooting
Events Not Appearing
- Check API key configuration
- Verify PostHog instance is created successfully
- Check network connectivity
- Look for console errors
- Ensure
flush()is called in dev (synchronous)
PostHog v3 Specific Issues
initAsync is not a function: PostHog v3 doesn't haveinitAsync(). Initialization happens in the constructor.- Async methods: All PostHog v3 methods are synchronous. Don't use
await. - TypeScript errors: Ensure types match synchronous signatures (return
voidnotPromise<void>)
Missing Properties
- Verify property names match schema
- Check for undefined values
- Ensure proper async/await usage
Performance Issues
- Batch events when possible
- Use sampling for high-frequency events
- Optimize property payload size
Future Enhancements
Planned Additions
- Session replay integration
- Heatmaps for UI interactions
- Custom user properties
- Revenue tracking
- A/B testing framework
- Crash reporting integration
- Network performance tracking
- Battery usage monitoring
Potential Events
- Audio playback events (play, pause, seek)
- Memory creation/edit events
- Tag management events
- Space collaboration events
- Network status changes
- App background/foreground transitions
Contact & Support
For analytics questions or to request new events:
- Create an issue in the repository
- Contact the development team
- Check PostHog documentation: https://posthog.com/docs
Technical Implementation Details
PostHog v3 React Native SDK
Initialization
// PostHog v3 - Initialization happens in constructor
this.posthog = new PostHog(apiKey, {
host: 'https://eu.i.posthog.com',
debug: __DEV__,
captureApplicationLifecycleEvents: true,
captureDeepLinks: true,
recordScreenViews: true,
flushInterval: 30,
flushAt: 20,
maxQueueSize: 1000,
disableAutoCapture: false,
enableLogs: __DEV__,
});
// No initAsync() needed - ready to use immediately
Method Signatures (Synchronous)
// All methods are synchronous in v3
posthog.identify(userId: string, properties?: object): void
posthog.capture(event: string, properties?: object): void
posthog.screen(name: string, properties?: object): void
posthog.reset(): void
posthog.flush(): void
Common Pitfalls
- ❌ Don't use
await posthog.initAsync()- doesn't exist - ❌ Don't use
await posthog.capture()- synchronous - ✅ Do use
posthog.flush()in dev for immediate sending - ✅ Do handle errors with try-catch around constructor
Service Architecture
// analyticsService.ts
class PostHogAnalyticsService implements AnalyticsService {
private posthog: any = null;
private initialized = false;
async initialize() {
// Create instance (initialization happens here)
this.posthog = new PostHog(key, options);
this.initialized = true;
// Test event with small delay
setTimeout(() => {
this.posthog.capture('posthog_initialized', {...});
}, 100);
}
// All methods are synchronous wrappers
track(event: string, properties?: object): void {
if (!this.initialized) return;
this.posthog.capture(event, properties);
if (__DEV__) this.posthog.flush();
}
}
Last updated: 2025-01-09 Version: 1.1.0 PostHog SDK: v3.x