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: 6 additions & 0 deletions apps/desktop/electron.vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ export default defineConfig({
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
"process.platform": JSON.stringify(process.platform),
"import.meta.env.DEV_SERVER_PORT": JSON.stringify(DEV_SERVER_PORT),
"import.meta.env.NEXT_PUBLIC_POSTHOG_KEY": JSON.stringify(
process.env.NEXT_PUBLIC_POSTHOG_KEY,
),
"import.meta.env.NEXT_PUBLIC_POSTHOG_HOST": JSON.stringify(
process.env.NEXT_PUBLIC_POSTHOG_HOST,
),
},

server: {
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
"nanoid": "^5.1.6",
"node-pty": "1.1.0-beta30",
"os-locale": "^6.0.2",
"posthog-js": "^1.306.1",
"react": "^19.2.3",
"react-arborist": "^3.4.3",
"react-dnd": "^16.0.1",
Expand Down
5 changes: 5 additions & 0 deletions apps/desktop/src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import { initDb } from "./lib/db";
import { terminalManager } from "./lib/terminal";
import { MainWindow } from "./windows/main";

// Set different app name for dev to avoid singleton lock conflicts with production
if (process.env.NODE_ENV === "development") {
app.setName("Superset Dev");
}

// Register protocol handler for deep linking
// In development, we need to provide the execPath and args
if (process.defaultApp) {
Expand Down
9 changes: 6 additions & 3 deletions apps/desktop/src/renderer/contexts/AppProviders.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type React from "react";
import { MonacoProvider } from "./MonacoProvider";
import { PostHogProvider } from "./PostHogProvider";
import { TRPCProvider } from "./TRPCProvider";

interface AppProvidersProps {
Expand All @@ -8,8 +9,10 @@ interface AppProvidersProps {

export function AppProviders({ children }: AppProvidersProps) {
return (
<TRPCProvider>
<MonacoProvider>{children}</MonacoProvider>
</TRPCProvider>
<PostHogProvider>
<TRPCProvider>
<MonacoProvider>{children}</MonacoProvider>
</TRPCProvider>
</PostHogProvider>
);
}
56 changes: 56 additions & 0 deletions apps/desktop/src/renderer/contexts/PostHogProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import posthog from "posthog-js";
import { PostHogProvider as PHProvider } from "posthog-js/react";
import type React from "react";
import { useEffect, useState } from "react";

const POSTHOG_KEY = import.meta.env.NEXT_PUBLIC_POSTHOG_KEY as
| string
| undefined;
const POSTHOG_HOST =
(import.meta.env.NEXT_PUBLIC_POSTHOG_HOST as string | undefined) ||
"https://us.i.posthog.com";

interface PostHogProviderProps {
children: React.ReactNode;
}

export function PostHogProvider({ children }: PostHogProviderProps) {
const [isInitialized, setIsInitialized] = useState(false);

useEffect(() => {
if (!POSTHOG_KEY) {
console.log("[posthog] No PostHog key configured, skipping init");
setIsInitialized(true);
return;
}

posthog.init(POSTHOG_KEY, {
api_host: POSTHOG_HOST,
// Electron apps don't have traditional page views
capture_pageview: false,
// Disable session recording for now
disable_session_recording: true,
// Persist across sessions
persistence: "localStorage",
// Load feature flags on init
bootstrap: {
featureFlags: {},
},
});

setIsInitialized(true);
console.log("[posthog] Initialized");
}, []);

// Don't render children until PostHog is initialized (or skipped)
if (!isInitialized) {
return null;
}
Comment on lines +46 to +48
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Brief render blocking during initialization.

Returning null while PostHog initializes will briefly block all content from rendering. While PostHog initialization is typically fast, consider showing a loading state or allowing children to render immediately to avoid any perceived delay, especially on slower connections.

Apply this diff to render children immediately:

-	// Don't render children until PostHog is initialized (or skipped)
-	if (!isInitialized) {
-		return null;
-	}
-
 	// If no PostHog key, just render children without the provider
-	if (!POSTHOG_KEY) {
+	if (!isInitialized || !POSTHOG_KEY) {
 		return <>{children}</>;
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!isInitialized) {
return null;
}
// If no PostHog key, just render children without the provider
if (!isInitialized || !POSTHOG_KEY) {
return <>{children}</>;
}
return <PHProvider client={posthogClient}>{children}</PHProvider>;
🤖 Prompt for AI Agents
In apps/desktop/src/renderer/contexts/PostHogProvider.tsx around lines 46-48,
the provider currently returns null while PostHog initializes which blocks
rendering; change the component to render its children immediately (or render a
lightweight loading UI) instead of returning null, and move/guard any
PostHog-dependent logic so initialization continues in the background (e.g.,
kick off init in useEffect and only gate calls that require
posthog.isInitialized rather than the whole render).


// If no PostHog key, just render children without the provider
if (!POSTHOG_KEY) {
return <>{children}</>;
}

return <PHProvider client={posthog}>{children}</PHProvider>;
}
1 change: 1 addition & 0 deletions apps/desktop/src/renderer/contexts/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { AppProviders } from "./AppProviders";
export { MonacoProvider, SUPERSET_THEME } from "./MonacoProvider";
export { PostHogProvider } from "./PostHogProvider";
export { TRPCProvider } from "./TRPCProvider";
6 changes: 3 additions & 3 deletions apps/desktop/src/renderer/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP

- default-src 'self': Only allow resources from same origin
- script-src 'self': Only allow scripts from same origin (no inline)
- script-src 'self' https://*.posthog.com: Allow scripts from same origin + PostHog
- style-src 'self' 'unsafe-inline': Allow styles from same origin + inline (needed for CSS-in-JS)
- connect-src 'self' ws: wss:: Allow WebSocket connections for HMR in dev
- connect-src 'self' ws: wss: https://*.posthog.com: Allow WebSocket connections for HMR + PostHog analytics
- img-src 'self' data:: Allow images from same origin + data URIs
- font-src 'self': Allow fonts from same origin
-->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' ws: wss:; img-src 'self' data:; font-src 'self';" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://*.posthog.com; style-src 'self' 'unsafe-inline'; connect-src 'self' ws: wss: https://*.posthog.com; img-src 'self' data:; font-src 'self';" />
</head>

<body>
Expand Down
40 changes: 36 additions & 4 deletions apps/desktop/src/renderer/screens/main/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { FEATURE_FLAGS } from "@superset/shared/constants";
import { Button } from "@superset/ui/button";
import { useCallback, useState } from "react";
import { useFeatureFlagEnabled, usePostHog } from "posthog-js/react";
import { useCallback, useEffect, useState } from "react";
import { DndProvider } from "react-dnd";
import { useHotkeys } from "react-hotkeys-hook";
import { HiArrowPath } from "react-icons/hi2";
Expand Down Expand Up @@ -30,10 +32,24 @@ function LoadingSpinner() {

export function MainScreen() {
const utils = trpc.useUtils();
const posthog = usePostHog();
const { data: authState } = trpc.auth.getState.useQuery();
const isSignedIn = authState?.isSignedIn ?? false;
const isAuthLoading = !authState;

// Feature flag to control auth requirement
const requireAuth = useFeatureFlagEnabled(FEATURE_FLAGS.REQUIRE_DESKTOP_AUTH);
const [flagsLoaded, setFlagsLoaded] = useState(false);

// Track when feature flags are loaded
useEffect(() => {
if (posthog) {
posthog.onFeatureFlags(() => {
setFlagsLoaded(true);
});
}
}, [posthog]);

// Subscribe to auth state changes
trpc.auth.onStateChange.useSubscription(undefined, {
onData: () => utils.auth.getState.invalidate(),
Expand Down Expand Up @@ -157,8 +173,23 @@ export function MainScreen() {
const showStartView =
!isLoading && !activeWorkspace && currentView !== "settings";

// Show sign-in screen if not authenticated
if (isAuthLoading) {
// Wait for feature flags to load before deciding on auth
const shouldRequireAuth = flagsLoaded && requireAuth === true;

// Show empty screen while feature flags are loading
if (!flagsLoaded) {
return (
<>
<Background />
<AppFrame>
<div className="h-full w-full bg-background" />
</AppFrame>
</>
);
}

// Show loading while auth state is being determined (only if auth is required)
if (shouldRequireAuth && isAuthLoading) {
return (
<>
<Background />
Expand All @@ -171,7 +202,8 @@ export function MainScreen() {
);
}

if (!isSignedIn) {
// Show sign-in screen if auth is required and user is not signed in
if (shouldRequireAuth && !isSignedIn) {
return (
<>
<Background />
Expand Down
17 changes: 15 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions packages/shared/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,9 @@ export const TOKEN_CONFIG = {
/** Refresh access token when this many seconds remain (5 minutes) */
REFRESH_THRESHOLD: 5 * 60,
} as const;

// PostHog Feature Flags
export const FEATURE_FLAGS = {
/** When enabled, users must sign in to use the desktop app */
REQUIRE_DESKTOP_AUTH: "require-desktop-auth",
} as const;