diff --git a/ui/desktop/src/components/recipes/CreateEditRecipeModal.tsx b/ui/desktop/src/components/recipes/CreateEditRecipeModal.tsx index 2e0eadb592d9..dc58446ba413 100644 --- a/ui/desktop/src/components/recipes/CreateEditRecipeModal.tsx +++ b/ui/desktop/src/components/recipes/CreateEditRecipeModal.tsx @@ -7,6 +7,7 @@ import { Check, Save, Calendar, X, Play } from 'lucide-react'; import { ExtensionConfig } from '../ConfigContext'; import { ScheduleFromRecipeModal } from '../schedule/ScheduleFromRecipeModal'; import { Button } from '../ui/button'; +import { useNavigation } from '../../hooks/useNavigation'; import { RecipeFormFields } from './shared/RecipeFormFields'; import { RecipeFormData } from './shared/recipeFormSchema'; @@ -28,6 +29,7 @@ export default function CreateEditRecipeModal({ isCreateMode = false, recipeId, }: CreateEditRecipeModalProps) { + const setView = useNavigation(); const getInitialValues = React.useCallback((): RecipeFormData => { if (recipe) { return { @@ -452,18 +454,9 @@ export default function CreateEditRecipeModal({ onClose={() => setIsScheduleModalOpen(false)} recipe={getCurrentRecipe()} onCreateSchedule={(deepLink) => { - // Open the schedules view with the deep link pre-filled - window.electron.createChatWindow( - undefined, - undefined, - undefined, - undefined, - undefined, - 'schedules', - undefined - ); - // Store the deep link in localStorage for the schedules view to pick up - localStorage.setItem('pendingScheduleDeepLink', deepLink); + // Navigate to schedules view with the deep link in state + setView('schedules', { pendingScheduleDeepLink: deepLink }); + setIsScheduleModalOpen(false); }} /> diff --git a/ui/desktop/src/components/recipes/RecipesView.tsx b/ui/desktop/src/components/recipes/RecipesView.tsx index 05544ada540b..a508b39a8fc0 100644 --- a/ui/desktop/src/components/recipes/RecipesView.tsx +++ b/ui/desktop/src/components/recipes/RecipesView.tsx @@ -13,8 +13,10 @@ import ImportRecipeForm, { ImportRecipeButton } from './ImportRecipeForm'; import CreateEditRecipeModal from './CreateEditRecipeModal'; import { generateDeepLink, Recipe } from '../../recipe'; import { ScheduleFromRecipeModal } from '../schedule/ScheduleFromRecipeModal'; +import { useNavigation } from '../../hooks/useNavigation'; export default function RecipesView() { + const setView = useNavigation(); const [savedRecipes, setSavedRecipes] = useState([]); const [loading, setLoading] = useState(true); const [showSkeleton, setShowSkeleton] = useState(true); @@ -160,11 +162,8 @@ export default function RecipesView() { }; const handleCreateScheduleFromRecipe = async (deepLink: string) => { - // Store the deeplink for the schedule modal to pick up - localStorage.setItem('pendingScheduleDeepLink', deepLink); - - // Navigate to schedules view and open create modal - window.location.hash = '#/schedules'; + // Navigate to schedules view with the deep link in state + setView('schedules', { pendingScheduleDeepLink: deepLink }); setShowScheduleModal(false); setSelectedRecipeForSchedule(null); diff --git a/ui/desktop/src/components/schedule/CreateScheduleModal.tsx b/ui/desktop/src/components/schedule/CreateScheduleModal.tsx index f0edaa42cf88..ce5597afd2b1 100644 --- a/ui/desktop/src/components/schedule/CreateScheduleModal.tsx +++ b/ui/desktop/src/components/schedule/CreateScheduleModal.tsx @@ -31,6 +31,7 @@ interface CreateScheduleModalProps { onSubmit: (payload: NewSchedulePayload) => Promise; isLoadingExternally: boolean; apiErrorExternally: string | null; + initialDeepLink?: string | null; } // Interface for clean extension in YAML @@ -272,6 +273,7 @@ export const CreateScheduleModal: React.FC = ({ onSubmit, isLoadingExternally, apiErrorExternally, + initialDeepLink, }) => { const [scheduleId, setScheduleId] = useState(''); const [sourceType, setSourceType] = useState('file'); @@ -331,16 +333,12 @@ export const CreateScheduleModal: React.FC = ({ ); useEffect(() => { - // Check for pending deep link when modal opens - if (isOpen) { - const pendingDeepLink = localStorage.getItem('pendingScheduleDeepLink'); - if (pendingDeepLink) { - localStorage.removeItem('pendingScheduleDeepLink'); - setSourceType('deeplink'); - handleDeepLinkChange(pendingDeepLink); - } + // Check for initial deep link from props when modal opens + if (isOpen && initialDeepLink) { + setSourceType('deeplink'); + handleDeepLinkChange(initialDeepLink); } - }, [isOpen, handleDeepLinkChange]); + }, [isOpen, initialDeepLink, handleDeepLinkChange]); const resetForm = () => { setScheduleId(''); diff --git a/ui/desktop/src/components/schedule/SchedulesView.tsx b/ui/desktop/src/components/schedule/SchedulesView.tsx index 2ada60102083..885bf1ff4cc5 100644 --- a/ui/desktop/src/components/schedule/SchedulesView.tsx +++ b/ui/desktop/src/components/schedule/SchedulesView.tsx @@ -1,4 +1,5 @@ import React, { useState, useEffect, useCallback, useMemo } from 'react'; +import { useLocation } from 'react-router-dom'; import { listSchedules, createSchedule, @@ -22,6 +23,7 @@ import { toastError, toastSuccess } from '../../toasts'; import cronstrue from 'cronstrue'; import { formatToLocalDateWithTimezone } from '../../utils/date'; import { MainPanelLayout } from '../Layout/MainPanelLayout'; +import { ViewOptions } from '../../utils/navigationUtils'; interface SchedulesViewProps { onClose?: () => void; @@ -210,6 +212,7 @@ const ScheduleCard = React.memo<{ ScheduleCard.displayName = 'ScheduleCard'; const SchedulesView: React.FC = ({ onClose: _onClose }) => { + const location = useLocation(); const [schedules, setSchedules] = useState([]); const [isLoading, setIsLoading] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); @@ -219,6 +222,7 @@ const SchedulesView: React.FC = ({ onClose: _onClose }) => { const [isEditModalOpen, setIsEditModalOpen] = useState(false); const [editingSchedule, setEditingSchedule] = useState(null); const [isRefreshing, setIsRefreshing] = useState(false); + const [pendingDeepLink, setPendingDeepLink] = useState(null); // Individual loading states for each action to prevent double-clicks const [pausingScheduleIds, setPausingScheduleIds] = useState>(new Set()); @@ -257,15 +261,16 @@ const SchedulesView: React.FC = ({ onClose: _onClose }) => { if (viewingScheduleId === null) { fetchSchedules(); - // Check for pending deep link from recipe editor - const pendingDeepLink = localStorage.getItem('pendingScheduleDeepLink'); - if (pendingDeepLink) { - localStorage.removeItem('pendingScheduleDeepLink'); + // Check for pending deep link from navigation state + const locationState = location.state as ViewOptions | null; + if (locationState?.pendingScheduleDeepLink) { + setPendingDeepLink(locationState.pendingScheduleDeepLink); setIsCreateModalOpen(true); - // The CreateScheduleModal will handle the deep link + // Clear the state after reading it + window.history.replaceState({}, document.title); } } - }, [viewingScheduleId, fetchSchedules]); + }, [viewingScheduleId, fetchSchedules, location.state]); // Optimized periodic refresh - only refresh if not actively doing something useEffect(() => { @@ -320,6 +325,7 @@ const SchedulesView: React.FC = ({ onClose: _onClose }) => { const handleCloseCreateModal = () => { setIsCreateModalOpen(false); setSubmitApiError(null); + setPendingDeepLink(null); }; const handleOpenEditModal = (schedule: ScheduledJob) => { @@ -648,6 +654,7 @@ const SchedulesView: React.FC = ({ onClose: _onClose }) => { onSubmit={handleCreateScheduleSubmit} isLoadingExternally={isSubmitting} apiErrorExternally={submitApiError} + initialDeepLink={pendingDeepLink} /> { log.info('No pending deep link to process'); } - // We don't need to handle pending deep links here anymore - // since we're handling them in the window creation flow log.info('React ready - window is prepared for deep links'); }); @@ -1937,7 +1935,6 @@ async function appMain() { // Log the recipe for debugging console.log('Creating chat window with recipe:', recipe); - // Pass recipe as part of viewOptions when viewType is recipeEditor createChat( app, query, diff --git a/ui/desktop/src/utils/navigationUtils.ts b/ui/desktop/src/utils/navigationUtils.ts index 96b9329447ef..d9bbe36e7263 100644 --- a/ui/desktop/src/utils/navigationUtils.ts +++ b/ui/desktop/src/utils/navigationUtils.ts @@ -35,6 +35,7 @@ export type ViewOptions = { resetChat?: boolean; shareToken?: string; resumeSessionId?: string; + pendingScheduleDeepLink?: string; }; export const createNavigationHandler = (navigate: NavigateFunction) => {