Skip to content

Commit ceffa97

Browse files
committed
Guard tour advance with expected tour and step IDs
1 parent 7ec7f30 commit ceffa97

File tree

5 files changed

+40
-13
lines changed

5 files changed

+40
-13
lines changed

apps/code/src/renderer/components/MainLayout.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { TaskInput } from "@features/task-detail/components/TaskInput";
1818
import { useTasks } from "@features/tasks/hooks/useTasks";
1919
import { TourOverlay } from "@features/tour/components/TourOverlay";
2020
import { useTourStore } from "@features/tour/stores/tourStore";
21+
import { createFirstTaskTour } from "@features/tour/tours/createFirstTaskTour";
2122
import { useConnectivity } from "@hooks/useConnectivity";
2223
import { useIntegrations } from "@hooks/useIntegrations";
2324
import { Box, Flex } from "@radix-ui/themes";
@@ -45,7 +46,7 @@ export function MainLayout() {
4546

4647
const startTour = useTourStore((s) => s.startTour);
4748
const isFirstTaskTourDone = useTourStore((s) =>
48-
s.completedTourIds.includes("create-first-task"),
49+
s.completedTourIds.includes(createFirstTaskTour.id),
4950
);
5051

5152
useIntegrations();
@@ -67,7 +68,7 @@ export function MainLayout() {
6768

6869
useEffect(() => {
6970
if (isFirstTaskTourDone || settingsOpen) return;
70-
const timer = setTimeout(() => startTour("create-first-task"), 600);
71+
const timer = setTimeout(() => startTour(createFirstTaskTour.id), 600);
7172
return () => clearTimeout(timer);
7273
}, [isFirstTaskTourDone, settingsOpen, startTour]);
7374

apps/code/src/renderer/features/settings/components/sections/AdvancedSettings.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useOnboardingStore } from "@features/onboarding/stores/onboardingStore";
22
import { SettingRow } from "@features/settings/components/SettingRow";
3+
import { useSettingsDialogStore } from "@features/settings/stores/settingsDialogStore";
34
import { useSettingsStore } from "@features/settings/stores/settingsStore";
45
import { useTourStore } from "@features/tour/stores/tourStore";
56
import { useFeatureFlag } from "@hooks/useFeatureFlag";
@@ -23,7 +24,10 @@ export function AdvancedSettings() {
2324
<Button
2425
variant="soft"
2526
size="1"
26-
onClick={() => useOnboardingStore.getState().resetOnboarding()}
27+
onClick={() => {
28+
useOnboardingStore.getState().resetOnboarding();
29+
useSettingsDialogStore.getState().close();
30+
}}
2731
>
2832
Reset
2933
</Button>

apps/code/src/renderer/features/task-detail/hooks/useTaskCreation.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
import { useSettingsStore } from "@features/settings/stores/settingsStore";
1010
import { useCreateTask } from "@features/tasks/hooks/useTasks";
1111
import { useTourStore } from "@features/tour/stores/tourStore";
12+
import { createFirstTaskTour } from "@features/tour/tours/createFirstTaskTour";
1213
import { useConnectivity } from "@hooks/useConnectivity";
1314
import type { WorkspaceMode } from "@main/services/workspace/schemas";
1415
import { get } from "@renderer/di/container";
@@ -173,7 +174,7 @@ export function useTaskCreation({
173174
} else {
174175
navigateToTask(output.task);
175176
}
176-
useTourStore.getState().advance();
177+
useTourStore.getState().completeTour(createFirstTaskTour.id);
177178
editor.clear();
178179
log.info("Task ready, navigated early", { taskId: output.task.id });
179180
});

apps/code/src/renderer/features/tour/components/TourOverlay.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,25 +61,31 @@ export function TourOverlay() {
6161
}, [activeStepIndex]);
6262

6363
useEffect(() => {
64-
if (!step || step.advanceOn.type !== "click" || !selector) return;
64+
if (!step || !activeTourId || step.advanceOn.type !== "click" || !selector)
65+
return;
6566

6667
const el = document.querySelector(selector);
6768
if (!el) return;
6869

70+
const tourId = activeTourId;
71+
const stepId = step.id;
6972
const handler = () => {
7073
if (!advancedRef.current) {
7174
advancedRef.current = true;
72-
setTimeout(advance, 0);
75+
setTimeout(() => advance(tourId, stepId), 0);
7376
}
7477
};
7578

7679
el.addEventListener("click", handler, { capture: true });
7780
return () => el.removeEventListener("click", handler, { capture: true });
78-
}, [step, selector, advance]);
81+
}, [step, selector, advance, activeTourId]);
7982

8083
useEffect(() => {
81-
if (!step || step.advanceOn.type !== "action" || !selector) return;
84+
if (!step || !activeTourId || step.advanceOn.type !== "action" || !selector)
85+
return;
8286

87+
const tourId = activeTourId;
88+
const stepId = step.id;
8389
const SETTLE_MS = 2000;
8490
let settleTimer: ReturnType<typeof setTimeout> | null = null;
8591

@@ -90,7 +96,7 @@ export function TourOverlay() {
9096
!advancedRef.current
9197
) {
9298
advancedRef.current = true;
93-
advance();
99+
advance(tourId, stepId);
94100
}
95101
};
96102

@@ -119,7 +125,7 @@ export function TourOverlay() {
119125
observer.disconnect();
120126
if (settleTimer) clearTimeout(settleTimer);
121127
};
122-
}, [step, selector, advance]);
128+
}, [step, selector, advance, activeTourId]);
123129

124130
const settingsOpen = useSettingsDialogStore((s) => s.isOpen);
125131
const commandMenuOpen = useCommandMenuStore((s) => s.isOpen);

apps/code/src/renderer/features/tour/stores/tourStore.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ interface TourStoreState {
1010

1111
interface TourStoreActions {
1212
startTour: (tourId: string) => void;
13-
advance: () => void;
13+
advance: (tourId: string, stepId: string) => void;
14+
completeTour: (tourId: string) => void;
1415
dismiss: () => void;
1516
resetTours: () => void;
1617
}
@@ -29,13 +30,16 @@ export const useTourStore = create<TourStore>()(
2930
set({ activeTourId: tourId, activeStepIndex: 0 });
3031
},
3132

32-
advance: () => {
33+
advance: (tourId, stepId) => {
3334
const { activeTourId, activeStepIndex } = get();
34-
if (!activeTourId) return;
35+
if (activeTourId !== tourId) return;
3536

3637
const tour = TOUR_REGISTRY[activeTourId];
3738
if (!tour) return;
3839

40+
const currentStep = tour.steps[activeStepIndex];
41+
if (!currentStep || currentStep.id !== stepId) return;
42+
3943
if (activeStepIndex >= tour.steps.length - 1) {
4044
set((state) => ({
4145
completedTourIds: [...state.completedTourIds, activeTourId],
@@ -47,6 +51,17 @@ export const useTourStore = create<TourStore>()(
4751
}
4852
},
4953

54+
completeTour: (tourId) => {
55+
const { activeTourId, completedTourIds } = get();
56+
if (activeTourId !== tourId) return;
57+
if (completedTourIds.includes(tourId)) return;
58+
set({
59+
completedTourIds: [...completedTourIds, tourId],
60+
activeTourId: null,
61+
activeStepIndex: 0,
62+
});
63+
},
64+
5065
dismiss: () => {
5166
const { activeTourId } = get();
5267
if (!activeTourId) return;

0 commit comments

Comments
 (0)