diff --git a/src/renderer/components/custom/control-panel/index.tsx b/src/renderer/components/custom/control-panel/index.tsx
index 45fcdcb..929bf10 100644
--- a/src/renderer/components/custom/control-panel/index.tsx
+++ b/src/renderer/components/custom/control-panel/index.tsx
@@ -6,6 +6,7 @@ import { useAppState } from '@/hooks/use-app-state';
import { useAssistantService } from '@/hooks/use-assistant-service';
import { useAudioInputDevices } from '@/hooks/use-audio-devices';
import { useConfigStore } from '@/hooks/use-config-store';
+import { useConfigurationDialog } from '@/hooks/use-configuration-dialog';
import useIsStealthMode from '@/hooks/use-is-stealth-mode';
import { isMac } from '@/lib/consts';
import { getElectron } from '@/lib/utils';
@@ -30,6 +31,7 @@ export default function ControlPanel() {
const { startAssistant, stopAssistant } = useAssistantService();
const { runningState, appState } = useAppState();
const { config } = useConfigStore();
+ const { openConfigurationDialog } = useConfigurationDialog();
const [permGateOpen, setPermGateOpen] = useState(false);
const audioInputDevices = useAudioInputDevices();
@@ -47,10 +49,15 @@ export default function ControlPanel() {
// retry: without this the same toast repeats forever however often Start is pressed.
onFail: () => void getElectron()?.account?.refresh(),
},
- { ok: !!appState?.interviewConfig?.fullName, message: 'Full name is not set' },
+ {
+ ok: !!appState?.interviewConfig?.fullName,
+ message: 'Full name is not set',
+ onFail: openConfigurationDialog,
+ },
{
ok: appState?.interviewConfig?.hasProfileData ?? false,
message: 'Profile data is not set',
+ onFail: openConfigurationDialog,
},
{
ok: !audioInputDeviceNotFound,
diff --git a/src/renderer/components/custom/main-frame.tsx b/src/renderer/components/custom/main-frame.tsx
index 86d9872..b9b9fc7 100644
--- a/src/renderer/components/custom/main-frame.tsx
+++ b/src/renderer/components/custom/main-frame.tsx
@@ -1,10 +1,12 @@
import React, { useEffect } from 'react';
import { toast } from 'sonner';
+import { ConfigurationDialogContext } from '@/hooks/use-configuration-dialog';
import { MainContainerContext } from '@/hooks/use-main-container';
import usePointerLockGuard from '@/hooks/use-pointer-lock-guard';
import type { PushNotification } from '@/types/push-notification';
+import ConfigurationDialog from './configuration-dialog';
import Titlebar from './titlebar';
import { UpdateNotification } from './update-notification';
@@ -16,6 +18,14 @@ export default function MainFrame({ children }: { children: React.ReactNode }) {
setContainer(el);
}, []);
+ // Owned here (rather than by the menu that used to be its only opener) so the start-checks
+ // in ControlPanel can also open it when username/profile turn out to be unconfigured.
+ const [isConfigOpen, setIsConfigOpen] = React.useState(false);
+ const configurationDialogValue = React.useMemo(
+ () => ({ openConfigurationDialog: () => setIsConfigOpen(true) }),
+ []
+ );
+
useEffect(() => {
const api = window.electronAPI;
if (!api?.onPushNotification) return;
@@ -39,14 +49,18 @@ export default function MainFrame({ children }: { children: React.ReactNode }) {
}, []);
return (
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/src/renderer/components/custom/titlebar-menu.tsx b/src/renderer/components/custom/titlebar-menu.tsx
index f6e572d..683e56d 100644
--- a/src/renderer/components/custom/titlebar-menu.tsx
+++ b/src/renderer/components/custom/titlebar-menu.tsx
@@ -14,7 +14,6 @@ import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { toast } from 'sonner';
-import ConfigurationDialog from '@/components/custom/configuration-dialog';
import DocumentationDialog from '@/components/custom/documentation-dialog';
import {
DropdownMenu,
@@ -28,6 +27,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip
import { useAppState } from '@/hooks/use-app-state';
import useAuth from '@/hooks/use-auth';
import { useConfigStore } from '@/hooks/use-config-store';
+import { useConfigurationDialog } from '@/hooks/use-configuration-dialog';
import { useThemeStore } from '@/hooks/use-theme-store';
import { Hotkey, HOTKEYS } from '@/lib/hotkeys';
import { getElectron } from '@/lib/utils';
@@ -41,8 +41,8 @@ export default function TitlebarMenu({ style }: { style?: React.CSSProperties })
const { config } = useConfigStore();
const { isDark, toggleTheme } = useThemeStore();
const { logout, changePassword, loading, error, setError } = useAuth();
+ const { openConfigurationDialog } = useConfigurationDialog();
const [isDocsOpen, setIsDocsOpen] = useState(false);
- const [isConfigOpen, setIsConfigOpen] = useState(false);
const [isChangePasswordOpen, setIsChangePasswordOpen] = useState(false);
const isLoggedIn = appState?.isLoggedIn ?? false;
@@ -116,7 +116,7 @@ export default function TitlebarMenu({ style }: { style?: React.CSSProperties })
!disabled && setIsConfigOpen(true)}
+ onClick={() => !disabled && openConfigurationDialog()}
disabled={disabled}
>
@@ -173,8 +173,6 @@ export default function TitlebarMenu({ style }: { style?: React.CSSProperties })
moment a session ends, stranding the pointer-events lock it holds. */}
-
-
void;
+}
+
+export const ConfigurationDialogContext =
+ React.createContext(null);
+
+export function useConfigurationDialog(): ConfigurationDialogContextValue {
+ const ctx = React.useContext(ConfigurationDialogContext);
+ if (!ctx) {
+ throw new Error('useConfigurationDialog must be used within MainFrame');
+ }
+ return ctx;
+}