Skip to content

Commit ff7cf3b

Browse files
committed
improvement(tour): remove auto-start, only trigger on explicit user action
1 parent d2c3c1c commit ff7cf3b

File tree

3 files changed

+8
-86
lines changed

3 files changed

+8
-86
lines changed

apps/sim/app/workspace/[workspaceId]/components/product-tour/product-tour.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ const Joyride = dynamic(() => import('react-joyride'), {
1616
ssr: false,
1717
})
1818

19-
const NAV_TOUR_STORAGE_KEY = 'sim-nav-tour-completed-v1'
2019
export const START_NAV_TOUR_EVENT = 'start-nav-tour'
2120

2221
export function NavTour() {
@@ -25,9 +24,6 @@ export function NavTour() {
2524

2625
const { run, stepIndex, tourKey, isTooltipVisible, isEntrance, handleCallback } = useTour({
2726
steps: navTourSteps,
28-
storageKey: NAV_TOUR_STORAGE_KEY,
29-
autoStartDelay: 1200,
30-
resettable: true,
3127
triggerEvent: START_NAV_TOUR_EVENT,
3228
tourName: 'Navigation tour',
3329
disabled: isWorkflowPage,

apps/sim/app/workspace/[workspaceId]/components/product-tour/use-tour.ts

Lines changed: 7 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,11 @@ const FADE_OUT_MS = 80
1212
interface UseTourOptions {
1313
/** Tour step definitions */
1414
steps: Step[]
15-
/** localStorage key for completion persistence */
16-
storageKey: string
17-
/** Delay before auto-starting the tour (ms) */
18-
autoStartDelay?: number
19-
/** Whether this tour can be reset/retriggered */
20-
resettable?: boolean
2115
/** Custom event name to listen for manual triggers */
2216
triggerEvent?: string
2317
/** Identifier for logging */
2418
tourName?: string
25-
/** When true, suppresses auto-start (e.g. to avoid overlapping with another active tour) */
19+
/** When true, stops a running tour (e.g. navigating away from the relevant page) */
2620
disabled?: boolean
2721
}
2822

@@ -41,49 +35,14 @@ interface UseTourReturn {
4135
handleCallback: (data: CallBackProps) => void
4236
}
4337

44-
function isTourCompleted(storageKey: string): boolean {
45-
try {
46-
return localStorage.getItem(storageKey) === 'true'
47-
} catch {
48-
return false
49-
}
50-
}
51-
52-
function markTourCompleted(storageKey: string): void {
53-
try {
54-
localStorage.setItem(storageKey, 'true')
55-
} catch {
56-
logger.warn('Failed to persist tour completion', { storageKey })
57-
}
58-
}
59-
60-
function clearTourCompletion(storageKey: string): void {
61-
try {
62-
localStorage.removeItem(storageKey)
63-
} catch {
64-
logger.warn('Failed to clear tour completion', { storageKey })
65-
}
66-
}
67-
68-
/**
69-
* Tracks which tours have already attempted auto-start in this page session.
70-
* Module-level so it survives component remounts (e.g. navigating between
71-
* workflows remounts WorkflowTour), while still resetting on full page reload.
72-
*/
73-
const autoStartAttempted = new Set<string>()
74-
7538
/**
7639
* Shared hook for managing product tour state with smooth transitions.
7740
*
78-
* Handles auto-start on first visit, localStorage persistence,
79-
* manual triggering via custom events, and coordinated fade
41+
* Handles manual triggering via custom events and coordinated fade
8042
* transitions between steps to prevent layout shift.
8143
*/
8244
export function useTour({
8345
steps,
84-
storageKey,
85-
autoStartDelay = 1200,
86-
resettable = false,
8746
triggerEvent,
8847
tourName = 'tour',
8948
disabled = false,
@@ -94,15 +53,10 @@ export function useTour({
9453
const [isTooltipVisible, setIsTooltipVisible] = useState(true)
9554
const [isEntrance, setIsEntrance] = useState(true)
9655

97-
const disabledRef = useRef(disabled)
9856
const retriggerTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
9957
const transitionTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
10058
const rafRef = useRef<number | null>(null)
10159

102-
useEffect(() => {
103-
disabledRef.current = disabled
104-
}, [disabled])
105-
10660
/**
10761
* Schedules a two-frame rAF to reveal the tooltip after the browser
10862
* finishes repositioning. Stores the outer frame ID in `rafRef` so
@@ -137,8 +91,7 @@ export function useTour({
13791
setRun(false)
13892
setIsTooltipVisible(true)
13993
setIsEntrance(true)
140-
markTourCompleted(storageKey)
141-
}, [storageKey, cancelPendingTransitions])
94+
}, [cancelPendingTransitions])
14295

14396
/** Transition to a new step with a coordinated fade-out/fade-in */
14497
const transitionToStep = useCallback(
@@ -164,40 +117,17 @@ export function useTour({
164117
/** Stop the tour when disabled becomes true (e.g. navigating away from the relevant page) */
165118
useEffect(() => {
166119
if (disabled && run) {
167-
cancelPendingTransitions()
168-
setRun(false)
169-
setIsTooltipVisible(true)
170-
setIsEntrance(true)
120+
stopTour()
171121
logger.info(`${tourName} paused — disabled became true`)
172122
}
173-
}, [disabled, run, tourName, cancelPendingTransitions])
174-
175-
/** Auto-start on first visit (once per page session per tour) */
176-
useEffect(() => {
177-
if (disabled || autoStartAttempted.has(storageKey) || isTourCompleted(storageKey)) return
178-
179-
const timer = setTimeout(() => {
180-
if (disabledRef.current) return
181-
182-
autoStartAttempted.add(storageKey)
183-
setStepIndex(0)
184-
setIsEntrance(true)
185-
setIsTooltipVisible(false)
186-
setRun(true)
187-
logger.info(`Auto-starting ${tourName}`)
188-
scheduleReveal()
189-
}, autoStartDelay)
190-
191-
return () => clearTimeout(timer)
192-
}, [disabled, storageKey, autoStartDelay, tourName, scheduleReveal])
123+
}, [disabled, run, tourName, stopTour])
193124

194125
/** Listen for manual trigger events */
195126
useEffect(() => {
196-
if (!triggerEvent || !resettable) return
127+
if (!triggerEvent) return
197128

198129
const handleTrigger = () => {
199130
setRun(false)
200-
clearTourCompletion(storageKey)
201131
setTourKey((k) => k + 1)
202132

203133
if (retriggerTimerRef.current) {
@@ -222,7 +152,7 @@ export function useTour({
222152
clearTimeout(retriggerTimerRef.current)
223153
}
224154
}
225-
}, [triggerEvent, resettable, storageKey, tourName, scheduleReveal])
155+
}, [triggerEvent, tourName, scheduleReveal])
226156

227157
/** Clean up all pending async work on unmount */
228158
useEffect(() => {

apps/sim/app/workspace/[workspaceId]/components/product-tour/workflow-tour.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,15 @@ const Joyride = dynamic(() => import('react-joyride'), {
1515
ssr: false,
1616
})
1717

18-
const WORKFLOW_TOUR_STORAGE_KEY = 'sim-workflow-tour-completed-v1'
1918
export const START_WORKFLOW_TOUR_EVENT = 'start-workflow-tour'
2019

2120
/**
2221
* Workflow tour that covers the canvas, blocks, copilot, and deployment.
23-
* Runs on first workflow visit and can be retriggered via "Take a tour".
22+
* Triggered via "Take a tour" in the sidebar menu.
2423
*/
2524
export function WorkflowTour() {
2625
const { run, stepIndex, tourKey, isTooltipVisible, isEntrance, handleCallback } = useTour({
2726
steps: workflowTourSteps,
28-
storageKey: WORKFLOW_TOUR_STORAGE_KEY,
29-
autoStartDelay: 800,
30-
resettable: true,
3127
triggerEvent: START_WORKFLOW_TOUR_EVENT,
3228
tourName: 'Workflow tour',
3329
})

0 commit comments

Comments
 (0)