Skip to content
Draft
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
73 changes: 73 additions & 0 deletions apps/web/src/app/(authenticated)/home/Home.client.test.tsx

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

68 changes: 64 additions & 4 deletions apps/web/src/app/(authenticated)/home/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import {

const FALLBACK_PROMPT_PLACEHOLDER = 'What do you want to do?';
const FEEDBACK_DISMISSED_STORAGE_KEY = 'roomote-home-feedback-dismissed';
const PROMPT_DRAFT_STORAGE_KEY_PREFIX = 'roomote-home-prompt-draft:v1';
const FEEDBACK_CALENDLY_URL =
'https://calendly.com/d/ctx9-f7q-6vr/roomote-feedback';
const FEEDBACK_EMAIL_URL =
Expand All @@ -95,6 +96,38 @@ function persistFeedbackPromptDismissal(): void {
}
}

function getPromptDraftStorageKey(userId: string | undefined): string | null {
return userId ? `${PROMPT_DRAFT_STORAGE_KEY_PREFIX}:${userId}` : null;
}

function readPromptDraft(storageKey: string | null): string {
if (!storageKey) {
return '';
}

try {
return window.localStorage.getItem(storageKey) ?? '';
} catch {
return '';
}
}

function persistPromptDraft(storageKey: string | null, prompt: string): void {
if (!storageKey) {
return;
}

try {
if (prompt) {
window.localStorage.setItem(storageKey, prompt);
} else {
window.localStorage.removeItem(storageKey);
}
} catch {
// Draft recovery is best-effort when browser storage is unavailable.
}
}

type RoutingFlowState = 'idle' | 'routing_pending' | 'launching';

type SubmissionSnapshot = {
Expand Down Expand Up @@ -141,6 +174,7 @@ export function Home({
const router = useRouter();
const environments = useEnvironments();
const {
userId,
cloudEnabled,
isAdmin,
managedAccess = DEFAULT_MANAGED_DEPLOYMENT_ACCESS,
Expand Down Expand Up @@ -172,8 +206,11 @@ export function Home({
const searchParams = useSearchParams();
const promptParam = searchParams.get('prompt') ?? '';
const environmentIdParam = searchParams.get('environmentId')?.trim() ?? '';
const promptDraftStorageKey = getPromptDraftStorageKey(userId);

const [promptText, setPromptText] = useState(promptParam);
const [promptText, setPromptText] = useState(
() => promptParam || readPromptDraft(promptDraftStorageKey),

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.

Home is server-rendered by the dynamic authenticated route, so this lazy initializer also runs without a browser. readPromptDraft then dereferences window.localStorage, causing ReferenceError: window is not defined and preventing the Home page from rendering. Read storage only after mount or guard the server path before touching window.

);
const [isExiting, setIsExiting] = useState(false);
const [routingState, setRoutingState] = useState<RoutingFlowState>('idle');
const [selectedComputeProvider, setSelectedComputeProvider] =
Expand Down Expand Up @@ -201,7 +238,21 @@ export function Home({

const routingRequestIdRef = useRef(0);

useEffect(() => setPromptText(promptParam), [promptParam]);
useEffect(() => {
setPromptText(promptParam || readPromptDraft(promptDraftStorageKey));
}, [promptDraftStorageKey, promptParam]);

const handlePromptTextChange = useCallback(
(prompt: string) => {
setPromptText(prompt);
persistPromptDraft(promptDraftStorageKey, prompt);
},
[promptDraftStorageKey],
);

const clearPromptDraft = useCallback(() => {
persistPromptDraft(promptDraftStorageKey, '');
}, [promptDraftStorageKey]);

useEffect(() => {
setIsFeedbackPromptVisible(!isFeedbackPromptDismissed());
Expand Down Expand Up @@ -437,12 +488,21 @@ export function Home({
payload,
});

if (result.success) {
clearPromptDraft();
}

return result.success;
} catch {
return false;
}
},
[createStandardTaskRun, selectedComputeProvider, selectedModelId],
[
clearPromptDraft,
createStandardTaskRun,
selectedComputeProvider,
selectedModelId,
],
);

useEffect(() => {
Expand Down Expand Up @@ -725,7 +785,7 @@ export function Home({
promptKey={promptParam}
isBusy={isBusy}
promptText={promptText}
onPromptTextChange={setPromptText}
onPromptTextChange={handlePromptTextChange}
onSubmit={handleSubmit}
placeholder={activePromptPlaceholder}
autoFocus
Expand Down
Loading