Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .DS_Store
Binary file not shown.
2 changes: 2 additions & 0 deletions .github/workflows/react-native-cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ env:
EXPO_ASC_ISSUER_ID: ${{ secrets.EXPO_ASC_ISSUER_ID }}
EXPO_APPLE_TEAM_ID: ${{ secrets.EXPO_TEAM_ID }}
EXPO_APPLE_TEAM_TYPE: ${{ secrets.EXPO_APPLE_TEAM_TYPE }}
UNIT_APTABASE_APP_KEY: ${{ secrets.UNIT_APTABASE_APP_KEY }}
UNIT_APTABASE_URL: ${{ secrets.UNIT_APTABASE_URL }}
NODE_OPTIONS: --openssl-legacy-provider

jobs:
Expand Down
5 changes: 5 additions & 0 deletions __mocks__/@aptabase/react-native.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const trackEvent = jest.fn();

export default {
trackEvent,
};
2 changes: 1 addition & 1 deletion app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
[
'expo-notifications',
{
icon: './assets/adaptive-icon.png',
icon: './assets/notification-icon.png',
color: '#2E3C4B',
permissions: {
ios: {
Expand Down
Binary file modified assets/audio/callemergency.wav
Binary file not shown.
Binary file modified assets/audio/callhigh.wav
Binary file not shown.
Binary file modified assets/audio/calllow.wav
Binary file not shown.
Binary file modified assets/audio/callmedium.wav
Binary file not shown.
Binary file modified assets/audio/callupdated.wav
Binary file not shown.
Binary file modified assets/audio/custom/c1.wav
Binary file not shown.
Binary file modified assets/audio/custom/c10.wav
Binary file not shown.
Binary file modified assets/audio/custom/c11.wav
Binary file not shown.
Binary file modified assets/audio/custom/c12.wav
Binary file not shown.
Binary file modified assets/audio/custom/c13.wav
Binary file not shown.
Binary file modified assets/audio/custom/c14.wav
Binary file not shown.
Binary file modified assets/audio/custom/c15.wav
Binary file not shown.
Binary file modified assets/audio/custom/c16.wav
Binary file not shown.
Binary file modified assets/audio/custom/c17.wav
Binary file not shown.
Binary file modified assets/audio/custom/c18.wav
Binary file not shown.
Binary file modified assets/audio/custom/c19.wav
Binary file not shown.
Binary file modified assets/audio/custom/c2.wav
Binary file not shown.
Binary file modified assets/audio/custom/c20.wav
Binary file not shown.
Binary file modified assets/audio/custom/c21.wav
Binary file not shown.
Binary file modified assets/audio/custom/c22.wav
Binary file not shown.
Binary file modified assets/audio/custom/c23.wav
Binary file not shown.
Binary file modified assets/audio/custom/c24.wav
Binary file not shown.
Binary file modified assets/audio/custom/c25.wav
Binary file not shown.
Binary file modified assets/audio/custom/c3.wav
Binary file not shown.
Binary file modified assets/audio/custom/c4.wav
Binary file not shown.
Binary file modified assets/audio/custom/c5.wav
Binary file not shown.
Binary file modified assets/audio/custom/c6.wav
Binary file not shown.
Binary file modified assets/audio/custom/c7.wav
Binary file not shown.
Binary file modified assets/audio/custom/c8.wav
Binary file not shown.
Binary file modified assets/audio/custom/c9.wav
Binary file not shown.
Binary file modified assets/audio/newcall.wav
Binary file not shown.
Binary file modified assets/audio/troublealert.wav
Binary file not shown.
Binary file added assets/notification-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 7 additions & 1 deletion babel.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
module.exports = function (api) {
api.cache(true);

// Check if we're in test environment
const isTest = process.env.NODE_ENV === 'test';

return {
presets: [['babel-preset-expo', { jsxImportSource: 'nativewind' }], 'nativewind/babel'],
presets: isTest
? ['babel-preset-expo'] // No nativewind in test environment
: [['babel-preset-expo', { jsxImportSource: 'nativewind' }], 'nativewind/babel'],
plugins: [
[
'module-resolver',
Expand Down
152 changes: 152 additions & 0 deletions docs/analytics-migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Analytics Migration: PostHog to Aptabase

## Migration Completed

This project has been migrated from PostHog to Aptabase for analytics tracking.

## Previous PostHog Implementation

Previously, the application used PostHog for analytics with comprehensive error handling:

- **PostHog Service**: Centralized error handling and retry logic
- **PostHog Provider Wrapper**: Error-safe provider with fallback rendering
- **Conservative Configuration**: Reduced network calls and optimized settings

## Current Aptabase Implementation

### 1. Aptabase Service (`src/services/aptabase.service.ts`)

- **Purpose**: Centralized analytics tracking with error handling
- **Features**:
- Simple event tracking interface
- Graceful error handling with retry logic
- Automatic service disable/enable after failures
- Comprehensive logging of events and errors

### 2. Aptabase Provider Wrapper (`src/components/common/aptabase-provider.tsx`)

- **Purpose**: Initializes Aptabase SDK with error handling
- **Features**:
- Safe initialization with error recovery
- Uses the Aptabase service for error management
- Always renders children (no provider wrapper required)
- Simple configuration with just app key

### 3. Updated Layout (`src/app/_layout.tsx`)

- **Purpose**: Uses the new Aptabase wrapper instead of PostHog provider
- **Change**: Replaced `PostHogProviderWrapper` with `AptabaseProviderWrapper`

## Key Benefits of Migration

### Simplified Configuration

- Single app key instead of API key + host
- No complex provider configuration needed
- Reduced bundle size and dependencies

### Improved Performance

- Lighter SDK with smaller footprint
- Better network efficiency
- Faster initialization

### Better Error Handling

- Simplified error recovery
- Cleaner service architecture
- Focused analytics interface

## Configuration

The system uses environment variables for Aptabase configuration:

- `APTABASE_APP_KEY`: Aptabase application key

When no app key is provided, the app runs without analytics entirely.

## Usage

To track analytics events:

```typescript
import { aptabaseService } from '@/services/aptabase.service';

// Track a simple event
aptabaseService.trackEvent('user_login');

// Track an event with properties
aptabaseService.trackEvent('button_clicked', {
button_name: 'submit',
screen: 'login',
user_type: 'premium'
});
```

## Testing

The implementation includes comprehensive unit tests:

### Aptabase Service Tests (`src/services/__tests__/aptabase.service.test.ts`)

- Event tracking functionality
- Error handling logic
- Retry mechanism
- Disable/enable functionality
- Status tracking
- Timer-based recovery

### Aptabase Provider Tests (`src/components/common/__tests__/aptabase-provider.test.tsx`)

- Component rendering with Aptabase enabled/disabled
- Error handling integration
- Configuration validation
- Service integration

All tests pass successfully and provide good coverage of the analytics functionality.

## Migration Notes

- All PostHog-specific code has been removed
- Environment variables changed from `POSTHOG_*` to `APTABASE_*`
- Service interface simplified but maintains error handling patterns
- No breaking changes to application functionality

## Example Analytics Events

Here are some common analytics events you might want to track:

```typescript
// User authentication
aptabaseService.trackEvent('user_login', { method: 'email' });
aptabaseService.trackEvent('user_logout');

// Navigation
aptabaseService.trackEvent('screen_view', { screen_name: 'dashboard' });

// User actions
aptabaseService.trackEvent('button_clicked', {
button_name: 'emergency_call',
screen: 'home'
});

// Feature usage
aptabaseService.trackEvent('feature_used', {
feature_name: 'gps_tracking',
enabled: true
});

// Error tracking (in addition to Sentry)
aptabaseService.trackEvent('error_occurred', {
error_type: 'network',
component: 'api_client'
});
```

## Best Practices

1. **Keep event names consistent**: Use snake_case for event names
2. **Include relevant context**: Add properties that help understand user behavior
3. **Don't track sensitive data**: Avoid PII or sensitive information
4. **Use descriptive property names**: Make properties self-explanatory
5. **Track both success and failure**: Include error states for complete picture
96 changes: 0 additions & 96 deletions docs/posthog-error-handling.md

This file was deleted.

8 changes: 4 additions & 4 deletions env.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ const client = z.object({
UNIT_MAPBOX_DLKEY: z.string(),
IS_MOBILE_APP: z.boolean(),
SENTRY_DSN: z.string(),
POSTHOG_API_KEY: z.string(),
POSTHOG_HOST: z.string(),
APTABASE_URL: z.string(),
APTABASE_APP_KEY: z.string(),
});

const buildTime = z.object({
Expand Down Expand Up @@ -127,8 +127,8 @@ const _clientEnv = {
UNIT_MAPBOX_PUBKEY: process.env.UNIT_MAPBOX_PUBKEY || '',
UNIT_MAPBOX_DLKEY: process.env.UNIT_MAPBOX_DLKEY || '',
SENTRY_DSN: process.env.UNIT_SENTRY_DSN || '',
POSTHOG_API_KEY: process.env.POSTHOG_API_KEY || '',
POSTHOG_HOST: process.env.POSTHOG_HOST || 'https://us.i.posthog.com',
APTABASE_APP_KEY: process.env.UNIT_APTABASE_APP_KEY || '',
APTABASE_URL: process.env.UNIT_APTABASE_URL || '',
};

/**
Expand Down
10 changes: 10 additions & 0 deletions jest-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,13 @@ jest.mock('react-native-permissions', () => ({
settings: {},
}),
}));

// Mock nativewind to avoid react-native-css-interop issues
jest.mock('nativewind', () => ({
cssInterop: jest.fn((Component: any) => Component),
useColorScheme: jest.fn(() => ({
colorScheme: 'light',
get: jest.fn(() => 'light'),
})),
__esModule: true,
}));
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module.exports = {
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
moduleDirectories: ['node_modules', '<rootDir>/'],
transformIgnorePatterns: [
'node_modules/(?!((jest-)?react-native|@react-native(-community)?|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@sentry/react-native|native-base|react-native-svg|@legendapp/motion|@gluestack-ui|expo-audio/.*))',
'node_modules/(?!((jest-)?react-native|@react-native(-community)?|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@sentry/react-native|native-base|react-native-svg|@legendapp/motion|@gluestack-ui|expo-audio|@aptabase/.*))',
],
coverageReporters: ['json-summary', ['text', { file: 'coverage.txt' }], 'cobertura'],
reporters: [
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"e2e-test": "maestro test .maestro/ -e APP_ID=com.obytes.development"
},
"dependencies": {
"@aptabase/react-native": "^0.3.10",
"@config-plugins/react-native-webrtc": "~12.0.0",
"@dev-plugins/react-query": "~0.2.0",
"@expo/config-plugins": "~9.0.0",
Expand Down Expand Up @@ -128,7 +129,6 @@
"lucide-react-native": "~0.475.0",
"moti": "~0.29.0",
"nativewind": "~4.1.21",
"posthog-react-native": "^4.1.4",
"react": "18.3.1",
"react-dom": "^19.1.0",
"react-error-boundary": "~4.0.13",
Expand Down
6 changes: 6 additions & 0 deletions src/app/(app)/__tests__/contacts.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ jest.mock('react-i18next', () => ({
}),
}));

jest.mock('@/hooks/use-analytics', () => ({
useAnalytics: () => ({
trackEvent: jest.fn(),
}),
}));

jest.mock('@/stores/contacts/store', () => ({
useContactsStore: jest.fn(),
}));
Expand Down
Loading
Loading