mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-07 06:12:44 +02:00
identify signed-in users on every app startup
Previously identify() only fired during the OAuth completion flow, so existing installs (signed in before analytics shipped) and every cold start of v0.3.4+ would emit main-process events under the anonymous installation_id until the user happened to re-sign-in. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
43c1ba719f
commit
de176ec458
3 changed files with 32 additions and 0 deletions
|
|
@ -10,6 +10,7 @@
|
||||||
- Main does it from `apps/main/src/oauth-handler.ts:285` (after `getBillingInfo()` resolves) — this is the load-bearing call, since main always runs.
|
- Main does it from `apps/main/src/oauth-handler.ts:285` (after `getBillingInfo()` resolves) — this is the load-bearing call, since main always runs.
|
||||||
- Renderer mirrors via `apps/renderer/src/hooks/useAnalyticsIdentity.ts` listening on the `oauth:didConnect` IPC event.
|
- Renderer mirrors via `apps/renderer/src/hooks/useAnalyticsIdentity.ts` listening on the `oauth:didConnect` IPC event.
|
||||||
- Main also calls `alias()` so events emitted under the anonymous installation_id are linked to the identified user retroactively.
|
- Main also calls `alias()` so events emitted under the anonymous installation_id are linked to the identified user retroactively.
|
||||||
|
- **On every app startup**: main re-identifies if rowboat tokens exist (`packages/core/src/analytics/identify.ts`, called from `apps/main/src/main.ts` whenReady). Idempotent — PostHog merges person properties on duplicate identifies. This catches users who installed before analytics existed, and refreshes person properties (plan/status) on every launch.
|
||||||
- **On rowboat sign-out**: `posthog.reset()` in both processes; future events resolve to the installation_id again.
|
- **On rowboat sign-out**: `posthog.reset()` in both processes; future events resolve to the installation_id again.
|
||||||
- **`email`** is set on `identify` from main only (sourced from `/v1/me`). Person properties are server-side, so the renderer's events resolve to the same record without redundantly setting it.
|
- **`email`** is set on `identify` from main only (sourced from `/v1/me`). Person properties are server-side, so the renderer's events resolve to the same record without redundantly setting it.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import { init as initTrackScheduler } from "@x/core/dist/knowledge/track/schedul
|
||||||
import { init as initTrackEventProcessor } from "@x/core/dist/knowledge/track/events.js";
|
import { init as initTrackEventProcessor } from "@x/core/dist/knowledge/track/events.js";
|
||||||
import { init as initLocalSites, shutdown as shutdownLocalSites } from "@x/core/dist/local-sites/server.js";
|
import { init as initLocalSites, shutdown as shutdownLocalSites } from "@x/core/dist/local-sites/server.js";
|
||||||
import { shutdown as shutdownAnalytics } from "@x/core/dist/analytics/posthog.js";
|
import { shutdown as shutdownAnalytics } from "@x/core/dist/analytics/posthog.js";
|
||||||
|
import { identifyIfSignedIn } from "@x/core/dist/analytics/identify.js";
|
||||||
|
|
||||||
import { initConfigs } from "@x/core/dist/config/initConfigs.js";
|
import { initConfigs } from "@x/core/dist/config/initConfigs.js";
|
||||||
import started from "electron-squirrel-startup";
|
import started from "electron-squirrel-startup";
|
||||||
|
|
@ -231,6 +232,13 @@ app.whenReady().then(async () => {
|
||||||
// Initialize all config files before UI can access them
|
// Initialize all config files before UI can access them
|
||||||
await initConfigs();
|
await initConfigs();
|
||||||
|
|
||||||
|
// PostHog identify() is idempotent — call it on every startup so existing
|
||||||
|
// signed-in installs (and every cold start of v0.3.4+) get re-identified.
|
||||||
|
// Otherwise main-process events stay anonymous until the user re-signs-in.
|
||||||
|
identifyIfSignedIn().catch((error) => {
|
||||||
|
console.error('[Analytics] Failed to identify on startup:', error);
|
||||||
|
});
|
||||||
|
|
||||||
registerBrowserControlService(new ElectronBrowserControlService());
|
registerBrowserControlService(new ElectronBrowserControlService());
|
||||||
|
|
||||||
setupIpcHandlers();
|
setupIpcHandlers();
|
||||||
|
|
|
||||||
23
apps/x/packages/core/src/analytics/identify.ts
Normal file
23
apps/x/packages/core/src/analytics/identify.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { isSignedIn } from '../account/account.js';
|
||||||
|
import { getBillingInfo } from '../billing/billing.js';
|
||||||
|
import { identify } from './posthog.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the user has rowboat OAuth tokens, fetch their billing info and
|
||||||
|
* call posthog.identify(). Idempotent — safe to call on every app start.
|
||||||
|
* Catches all errors so analytics never blocks app launch.
|
||||||
|
*/
|
||||||
|
export async function identifyIfSignedIn(): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (!(await isSignedIn())) return;
|
||||||
|
const billing = await getBillingInfo();
|
||||||
|
if (!billing.userId) return;
|
||||||
|
identify(billing.userId, {
|
||||||
|
...(billing.userEmail ? { email: billing.userEmail } : {}),
|
||||||
|
plan: billing.subscriptionPlan,
|
||||||
|
status: billing.subscriptionStatus,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[Analytics] startup identify failed:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue