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
6 changes: 4 additions & 2 deletions docs/analytics.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,16 +128,18 @@ Entitlement is synced as a person property (`entitlement: 'free' | 'premium'`) i
|---|---|---|---|
| `review_prompted` | In-app review dialog triggered | `trigger`: `'place_visited' \| 'places_saved_milestone' \| 'directions_after_filter'` | `hooks/use-app-review.ts` |

### Payments (4 events)
### Payments (6 events)

| Event | When | Properties | File |
|---|---|---|---|
| `paywall_viewed` | Paywall screen mounts | `trigger`: `'map_limit' \| 'place_limit' \| 'invite_limit' \| 'profile_tap' \| 'profile_cta'` | `app/(tabs)/profile/paywall.tsx` |
| `purchase_started` | User taps Subscribe | _(none)_ | `app/(tabs)/profile/paywall.tsx` |
| `purchase_completed` | Purchase succeeds | _(none)_ | `app/(tabs)/profile/paywall.tsx` |
| `purchase_failed` | Purchase fails or is cancelled | `reason`: `'cancelled' \| 'error'` | `app/(tabs)/profile/paywall.tsx` |
| `paywall_offerings_load_failed` | RC offerings query fails or returns empty | `reason`: `'error' \| 'empty' \| 'not_configured' \| 'network'` | `hooks/use-revenuecat.ts` |
| `revenuecat_login_network_error` | RC `logIn()` fails with a network/connectivity error | _(none)_ | `hooks/use-revenuecat.ts` |

**Total: 30 events**
**Total: 32 events**

## Rules for Adding New Events

Expand Down
15 changes: 12 additions & 3 deletions hooks/use-revenuecat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
purchasePackage,
restorePurchases,
isPremium,
isRevenueCatNetworkError,
} from '@/lib/revenuecat';
import type { Profile } from '@/types';

Expand Down Expand Up @@ -69,7 +70,14 @@ export function useRevenueCat() {
.catch((error: unknown) => {
const message = error instanceof Error ? error.message : String(error);
if (message.toLowerCase().includes('already in progress')) return; // known Android race
Sentry.captureException(error, { tags: { context: 'revenuecat_login' } });
if (isRevenueCatNetworkError(error)) {
track('revenuecat_login_network_error', {}); // transient device connectivity — not actionable
return;
}
Sentry.captureException(error, {
tags: { context: 'revenuecat_login' },
extra: { userId },
});
})
.finally(() => {
isIdentifyingRef.current = false;
Expand Down Expand Up @@ -118,13 +126,14 @@ export function useRevenueCat() {
const key = `error:${offerings.errorUpdatedAt}`;
if (reportedKeyRef.current === key) return;
reportedKeyRef.current = key;
if (IS_PRODUCTION) {
const isNetworkError = isRevenueCatNetworkError(offerings.error);
if (IS_PRODUCTION && !isNetworkError) {
Sentry.captureException(offerings.error, {
tags: { context: 'rc_offerings', platform: Platform.OS },
extra: { userId, revenueCatReady },
});
}
track('paywall_offerings_load_failed', { reason: 'error' });
track('paywall_offerings_load_failed', { reason: isNetworkError ? 'network' : 'error' });
return;
}
const data = offerings.data;
Expand Down
3 changes: 2 additions & 1 deletion lib/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ type AnalyticsEvents = {
purchase_started: Record<string, never>;
purchase_completed: Record<string, never>;
purchase_failed: { reason: 'cancelled' | 'error' };
paywall_offerings_load_failed: { reason: 'error' | 'empty' | 'not_configured' };
paywall_offerings_load_failed: { reason: 'error' | 'empty' | 'not_configured' | 'network' };
revenuecat_login_network_error: Record<string, never>;
review_prompted: { trigger: 'place_visited' | 'places_saved_milestone' | 'directions_after_filter' };
settings_viewed: Record<string, never>;
manage_maps_viewed: Record<string, never>;
Expand Down
9 changes: 9 additions & 0 deletions lib/revenuecat.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Platform } from 'react-native';
import Purchases, {
LOG_LEVEL,
PURCHASES_ERROR_CODE,
type CustomerInfo,
type PurchasesOfferings,
type PurchasesPackage,
Expand Down Expand Up @@ -90,3 +91,11 @@ export async function getCustomerInfo(): Promise<CustomerInfo | null> {
export function isPremium(customerInfo: CustomerInfo): boolean {
return !!customerInfo.entitlements.active['premium'];
}

export function isRevenueCatNetworkError(error: unknown): boolean {
const code = (error as { code?: string })?.code;
return (
code === PURCHASES_ERROR_CODE.NETWORK_ERROR ||
code === PURCHASES_ERROR_CODE.OFFLINE_CONNECTION_ERROR
);
}
Loading