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
9 changes: 8 additions & 1 deletion src/renderer/components/custom/control-panel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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();
Expand All @@ -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,
Expand Down
32 changes: 23 additions & 9 deletions src/renderer/components/custom/main-frame.tsx
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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;
Expand All @@ -39,14 +49,18 @@ export default function MainFrame({ children }: { children: React.ReactNode }) {
}, []);

return (
<MainContainerContext.Provider value={container}>
<main ref={mainRef} className="relative overflow-hidden bg-background">
<div className="flex flex-col h-dvh">
<Titlebar />
<div className="flex-1 flex flex-col overflow-auto hide-scrollbar">{children}</div>
</div>
<UpdateNotification />
</main>
</MainContainerContext.Provider>
<ConfigurationDialogContext.Provider value={configurationDialogValue}>
<MainContainerContext.Provider value={container}>
<main ref={mainRef} className="relative overflow-hidden bg-background">
<div className="flex flex-col h-dvh">
<Titlebar />
<div className="flex-1 flex flex-col overflow-auto hide-scrollbar">{children}</div>
</div>
<UpdateNotification />
</main>

<ConfigurationDialog isOpen={isConfigOpen} onOpenChange={setIsConfigOpen} />
</MainContainerContext.Provider>
</ConfigurationDialogContext.Provider>
);
}
8 changes: 3 additions & 5 deletions src/renderer/components/custom/titlebar-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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';
Expand All @@ -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;
Expand Down Expand Up @@ -116,7 +116,7 @@ export default function TitlebarMenu({ style }: { style?: React.CSSProperties })
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() => !disabled && setIsConfigOpen(true)}
onClick={() => !disabled && openConfigurationDialog()}
disabled={disabled}
>
<SettingsIcon className="mr-2 h-4 w-4" />
Expand Down Expand Up @@ -173,8 +173,6 @@ export default function TitlebarMenu({ style }: { style?: React.CSSProperties })
moment a session ends, stranding the pointer-events lock it holds. */}
<DocumentationDialog open={isDocsOpen} onOpenChange={setIsDocsOpen} />

<ConfigurationDialog isOpen={isConfigOpen} onOpenChange={setIsConfigOpen} />

<ChangePasswordDialog
open={isChangePasswordOpen}
onOpenChange={setIsChangePasswordOpen}
Expand Down
18 changes: 18 additions & 0 deletions src/renderer/hooks/use-configuration-dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as React from 'react';

// Lets any descendant (menu item, start-checks) open the configuration dialog without each
// owning its own dialog instance - the dialog itself is mounted once in MainFrame.
interface ConfigurationDialogContextValue {
openConfigurationDialog: () => void;
}

export const ConfigurationDialogContext =
React.createContext<ConfigurationDialogContextValue | null>(null);

export function useConfigurationDialog(): ConfigurationDialogContextValue {
const ctx = React.useContext(ConfigurationDialogContext);
if (!ctx) {
throw new Error('useConfigurationDialog must be used within MainFrame');
}
return ctx;
}