mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-10-31 18:19:43 +00:00
Switch analytics from PostHog to Firebase
This commit is contained in:
1 parent
23e24aa4c2
commit
1911f8f058
8 files changed
+980
-274
No files matched your search
@@ -17,7 +17,7 @@
|
||||
<key>IS_ADS_ENABLED</key>
|
||||
<false></false>
|
||||
<key>IS_ANALYTICS_ENABLED</key>
|
||||
<false></false>
|
||||
<true></true>
|
||||
<key>IS_APPINVITE_ENABLED</key>
|
||||
<true></true>
|
||||
<key>IS_GCM_ENABLED</key>
|
||||
|
||||
@@ -93,8 +93,8 @@ android {
|
||||
applicationId 'com.sauci.app'
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 22
|
||||
versionName "1.0.1"
|
||||
versionCode 24
|
||||
versionName "1.0.2"
|
||||
}
|
||||
signingConfigs {
|
||||
debug {
|
||||
|
||||
@@ -57,3 +57,6 @@ expo.useLegacyPackaging=false
|
||||
|
||||
android.targetSdkVersion=35
|
||||
android.extraMavenRepos=[]
|
||||
|
||||
# Enable 16 KB page size support (required by Google Play for API 35+)
|
||||
android.bundle.pageAlignment=16kb
|
||||
@@ -2,7 +2,7 @@
|
||||
"expo": {
|
||||
"name": "Sauci",
|
||||
"slug": "sauci",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.2",
|
||||
"orientation": "portrait",
|
||||
"icon": "./assets/icon.png",
|
||||
"scheme": "app.sauci",
|
||||
@@ -18,6 +18,7 @@
|
||||
"bundleIdentifier": "com.sauci.app",
|
||||
"buildNumber": "30",
|
||||
"usesAppleSignIn": true,
|
||||
"googleServicesFile": "./GoogleService-Info.plist",
|
||||
"infoPlist": {
|
||||
"UIBackgroundModes": [
|
||||
"remote-notification"
|
||||
@@ -176,7 +177,7 @@
|
||||
"backgroundColor": "#1a1a2e"
|
||||
},
|
||||
"package": "com.sauci.app",
|
||||
"versionCode": 22,
|
||||
"versionCode": 24,
|
||||
"googleServicesFile": "./google-services.json",
|
||||
"permissions": [
|
||||
"RECEIVE_BOOT_COMPLETED",
|
||||
@@ -197,6 +198,9 @@
|
||||
{
|
||||
"android": {
|
||||
"targetSdkVersion": 35
|
||||
},
|
||||
"ios": {
|
||||
"useFrameworks": "static"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
"@expo/ngrok": "^4.1.0",
|
||||
"@expo/vector-icons": "~14.0.4",
|
||||
"@react-native-async-storage/async-storage": "1.23.1",
|
||||
"@react-native-firebase/analytics": "^23.8.2",
|
||||
"@react-native-firebase/app": "^23.8.2",
|
||||
"@react-native-google-signin/google-signin": "^16.1.1",
|
||||
"@react-navigation/bottom-tabs": "^7.0.0",
|
||||
"@react-navigation/native": "^7.0.0",
|
||||
@@ -53,7 +55,6 @@
|
||||
"expo-router": "~4.0.0",
|
||||
"expo-secure-store": "~14.0.0",
|
||||
"expo-status-bar": "~2.0.0",
|
||||
"posthog-react-native": "^4.17.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-native": "0.76.9",
|
||||
|
||||
@@ -1,7 +1,31 @@
|
||||
import PostHog from "posthog-react-native";
|
||||
import { getApp } from "@react-native-firebase/app";
|
||||
import {
|
||||
getAnalytics,
|
||||
logEvent as firebaseLogEvent,
|
||||
setUserId as firebaseSetUserId,
|
||||
setUserProperty,
|
||||
setAnalyticsCollectionEnabled,
|
||||
resetAnalyticsData,
|
||||
FirebaseAnalyticsTypes,
|
||||
} from "@react-native-firebase/analytics";
|
||||
|
||||
let posthog: PostHog | null = null;
|
||||
let isInitialized = false;
|
||||
let analyticsInstance: FirebaseAnalyticsTypes.Module | null = null;
|
||||
|
||||
function getAnalyticsInstance(): FirebaseAnalyticsTypes.Module | null {
|
||||
if (analyticsInstance) {
|
||||
return analyticsInstance;
|
||||
}
|
||||
|
||||
try {
|
||||
const app = getApp();
|
||||
analyticsInstance = getAnalytics(app);
|
||||
return analyticsInstance;
|
||||
} catch (error) {
|
||||
console.warn("[Analytics] Failed to get analytics instance:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function initAnalytics() {
|
||||
if (isInitialized) {
|
||||
@@ -9,30 +33,19 @@ export async function initAnalytics() {
|
||||
}
|
||||
|
||||
// Skip initialization in non-RN environments (e.g., during tsc/Node.js)
|
||||
// AsyncStorage requires window to be defined
|
||||
if (typeof window === "undefined" && typeof global !== "undefined" && !("__BUNDLE_START_TIME__" in global)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const apiKey = process.env.EXPO_PUBLIC_POSTHOG_API_KEY;
|
||||
const host = process.env.EXPO_PUBLIC_POSTHOG_HOST || "https://us.i.posthog.com";
|
||||
|
||||
if (!apiKey) {
|
||||
console.warn("[Analytics] PostHog API key not configured");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
posthog = new PostHog(apiKey, {
|
||||
host,
|
||||
// Disable automatic capture in production for privacy
|
||||
captureAppLifecycleEvents: true,
|
||||
});
|
||||
|
||||
const analytics = getAnalyticsInstance();
|
||||
if (analytics) {
|
||||
await setAnalyticsCollectionEnabled(analytics, true);
|
||||
isInitialized = true;
|
||||
console.log("[Analytics] PostHog initialized");
|
||||
console.log("[Analytics] Firebase Analytics initialized");
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("[Analytics] Failed to initialize PostHog:", error);
|
||||
console.warn("[Analytics] Failed to initialize Firebase Analytics:", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +56,10 @@ export function logEvent(
|
||||
) {
|
||||
try {
|
||||
if (__DEV__) console.log("[Analytics] Event:", name, params);
|
||||
posthog?.capture(name, params);
|
||||
const analytics = getAnalyticsInstance();
|
||||
if (analytics) {
|
||||
firebaseLogEvent(analytics, name, params);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("[Analytics] Failed to log event:", error);
|
||||
}
|
||||
@@ -53,7 +69,10 @@ export function logEvent(
|
||||
export function setUserId(userId: string) {
|
||||
try {
|
||||
if (__DEV__) console.log("[Analytics] Set user ID:", userId);
|
||||
posthog?.identify(userId);
|
||||
const analytics = getAnalyticsInstance();
|
||||
if (analytics) {
|
||||
firebaseSetUserId(analytics, userId);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("[Analytics] Failed to set user ID:", error);
|
||||
}
|
||||
@@ -63,7 +82,11 @@ export function setUserId(userId: string) {
|
||||
export function clearUserId() {
|
||||
try {
|
||||
if (__DEV__) console.log("[Analytics] Cleared user ID");
|
||||
posthog?.reset();
|
||||
const analytics = getAnalyticsInstance();
|
||||
if (analytics) {
|
||||
firebaseSetUserId(analytics, null);
|
||||
resetAnalyticsData(analytics);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("[Analytics] Failed to clear user ID:", error);
|
||||
}
|
||||
@@ -75,14 +98,13 @@ export function setUserProperties(
|
||||
) {
|
||||
try {
|
||||
if (__DEV__) console.log("[Analytics] Set user properties:", properties);
|
||||
// Filter out null values for PostHog
|
||||
const filteredProps: Record<string, string> = {};
|
||||
const analytics = getAnalyticsInstance();
|
||||
if (analytics) {
|
||||
// Firebase Analytics requires setting user properties one at a time
|
||||
for (const [key, value] of Object.entries(properties)) {
|
||||
if (value !== null) {
|
||||
filteredProps[key] = value;
|
||||
setUserProperty(analytics, key, value);
|
||||
}
|
||||
}
|
||||
posthog?.capture("$set", { $set: filteredProps });
|
||||
} catch (error) {
|
||||
console.warn("[Analytics] Failed to set user properties:", error);
|
||||
}
|
||||
@@ -92,7 +114,13 @@ export function setUserProperties(
|
||||
export function logScreenView(screenName: string, screenClass?: string) {
|
||||
try {
|
||||
if (__DEV__) console.log("[Analytics] Screen view:", screenName);
|
||||
posthog?.screen(screenName, { screen_class: screenClass ?? screenName });
|
||||
const analytics = getAnalyticsInstance();
|
||||
if (analytics) {
|
||||
firebaseLogEvent(analytics, "screen_view", {
|
||||
screen_name: screenName,
|
||||
screen_class: screenClass ?? screenName,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("[Analytics] Failed to log screen view:", error);
|
||||
}
|
||||
@@ -100,11 +128,9 @@ export function logScreenView(screenName: string, screenClass?: string) {
|
||||
|
||||
// Flush events (useful before app backgrounding)
|
||||
export function flush() {
|
||||
try {
|
||||
posthog?.flush();
|
||||
} catch (error) {
|
||||
console.warn("[Analytics] Failed to flush:", error);
|
||||
}
|
||||
// Firebase Analytics batches and sends events automatically
|
||||
// No manual flush needed, but we keep the function for API compatibility
|
||||
if (__DEV__) console.log("[Analytics] Flush called (no-op for Firebase)");
|
||||
}
|
||||
|
||||
// Pre-defined event helpers for common actions
|
||||
|
||||
@@ -120,16 +120,15 @@ jest.mock('base64-arraybuffer', () => ({
|
||||
decode: jest.fn(() => new ArrayBuffer(0)),
|
||||
}));
|
||||
|
||||
jest.mock('posthog-react-native', () => {
|
||||
return class PostHog {
|
||||
capture = jest.fn();
|
||||
identify = jest.fn();
|
||||
reset = jest.fn();
|
||||
screen = jest.fn();
|
||||
flush = jest.fn();
|
||||
jest.mock('@react-native-firebase/app', () => ({
|
||||
getApp: jest.fn(() => ({})),
|
||||
}));
|
||||
|
||||
constructor() {
|
||||
// no-op
|
||||
}
|
||||
};
|
||||
});
|
||||
jest.mock('@react-native-firebase/analytics', () => ({
|
||||
getAnalytics: jest.fn(() => ({})),
|
||||
logEvent: jest.fn(),
|
||||
setUserId: jest.fn(),
|
||||
setUserProperty: jest.fn(),
|
||||
setAnalyticsCollectionEnabled: jest.fn(() => Promise.resolve()),
|
||||
resetAnalyticsData: jest.fn(),
|
||||
}));
|
||||
Generated
+896
-223
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user