PageSourceSearch

https://jitty.com/assets/assistant/agent_welcome_component/controller-df5e3e65.js

js jitty.com collected 2026-10-06 21:56:26 UTC 3,645 bytes, 118 lines download raw bytes

1import { Controller } from '@hotwired/stimulus'
2
3// Opens once for a visitor who has just clicked through from the property agent
4// promotion. The promotion sets the key below immediately before navigating; this
5// controller spends it on arrival, so the modal cannot reappear later in the session.
6// Counterpart: app/components/assistant/property_agent_promotion_component/controller.js
7const ARRIVED_KEY = 'jitty.assistantPromotion.welcomePending'
8const DISPLAY_DELAY = 500
9const CLOSE_TRANSITION_DURATION = 150
10
11export default class extends Controller {
12  static targets = ['dialog']
13
14  connect() {
15    this.seenFired = false
16    this.dismissedFired = false
17    this.resetRestoredDialog()
18
19    // Turbo connects controllers on the cached preview shown before the real page.
20    if (document.documentElement.hasAttribute('data-turbo-preview')) return
21    if (!this.spendArrival()) return
22
23    this.timerId = window.setTimeout(() => this.open(), DISPLAY_DELAY)
24  }
25
26  disconnect() {
27    if (this.timerId) window.clearTimeout(this.timerId)
28    this.timerId = null
29    document.body.classList.remove('overflow-hidden')
30    if (this.dialogTarget.open) this.dialogTarget.close()
31  }
32
33  // Returns true only for the arrival that owns the key, and removes it so a later
34  // visit in the same session does not open the modal again.
35  spendArrival() {
36    try {
37      const storage = window.sessionStorage
38      if (storage.getItem(ARRIVED_KEY) !== '1') return false
39
40      storage.removeItem(ARRIVED_KEY)
41      return true
42    } catch {
43      return false
44    }
45  }
46
47  open() {
48    if (this.dialogTarget.open) return
49
50    this.previouslyFocusedElement = document.activeElement
51    this.dialogTarget.showModal()
52    document.body.classList.add('overflow-hidden')
53
54    requestAnimationFrame(() => {
55      this.dialogTarget.classList.remove('opacity-0')
56      this.dialogTarget.classList.add('opacity-100')
57
58      if (!this.seenFired) {
59        this.seenFired = true
60        this.emitAnalytics('assistant.welcome.seen')
61      }
62    })
63  }
64
65  dismiss(event) {
66    this.dismissWithMethod(event.params.method)
67  }
68
69  backdropClicked(event) {
70    if (event.target === this.dialogTarget) this.dismissWithMethod('backdrop')
71  }
72
73  escapePressed(event) {
74    event.preventDefault()
75    this.dismissWithMethod('escape')
76  }
77
78  dismissWithMethod(method) {
79    if (!this.dialogTarget.open || this.dismissedFired) return
80
81    this.dismissedFired = true
82    this.emitAnalytics('assistant.welcome.dismissed', { dismissal_method: method })
83    this.close()
84  }
85
86  close() {
87    this.dialogTarget.classList.remove('opacity-100')
88    this.dialogTarget.classList.add('opacity-0')
89
90    const finish = () => {
91      if (this.dialogTarget.open) this.dialogTarget.close()
92      document.body.classList.remove('overflow-hidden')
93      if (this.previouslyFocusedElement?.isConnected) this.previouslyFocusedElement.focus()
94    }
95
96    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
97      finish()
98    } else {
99      window.setTimeout(finish, CLOSE_TRANSITION_DURATION)
100    }
101  }
102
103  // Turbo caches the page before Stimulus disconnects, so a snapshot taken while the
104  // dialog was open restores it open but no longer modal, on a scroll locked page.
105  resetRestoredDialog() {
106    if (this.dialogTarget.open) this.dialogTarget.close()
107    this.dialogTarget.classList.add('opacity-0')
108    this.dialogTarget.classList.remove('opacity-100')
109    document.body.classList.remove('overflow-hidden')
110  }
111
112  emitAnalytics(name, detail = {}) {
113    this.dispatch('analytics', {
114      prefix: 'assistant-agent-welcome',
115      detail: { name, ...detail }
116    })
117  }
118}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.