Skip to content

Commit e76a872

Browse files
committed
Improve user experience
1 parent 68b7142 commit e76a872

File tree

4 files changed

+97
-12
lines changed

4 files changed

+97
-12
lines changed

extension/shared/pairing.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
;(function () {
22
const REQUEST_TYPE = 'EASYRELOCATE_PAIR_REQUEST'
33
const RESULT_TYPE = 'EASYRELOCATE_PAIR_RESULT'
4+
const PING_TYPE = 'EASYRELOCATE_PING_REQUEST'
5+
const PING_RESULT = 'EASYRELOCATE_PING_RESULT'
46
const PAIR_HASH = '/onboarding/token'
57

68
function shouldHandle() {
@@ -13,6 +15,10 @@
1315
if (event.origin !== window.location.origin) return
1416

1517
const data = event.data || {}
18+
if (data.type === PING_TYPE) {
19+
window.postMessage({ type: PING_RESULT, ok: true }, window.location.origin)
20+
return
21+
}
1622
if (data.type !== REQUEST_TYPE) return
1723

1824
const token = String(data.token || '').trim()

frontend/src/App.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,14 @@
670670
color: #0f172a;
671671
}
672672

673+
.emptyState {
674+
display: grid;
675+
gap: 10px;
676+
padding: 10px 6px;
677+
color: #475569;
678+
font-size: 13px;
679+
}
680+
673681
@media (max-width: 960px) {
674682
.content {
675683
grid-template-columns: 1fr;

frontend/src/pages/ComparePage.tsx

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -337,10 +337,28 @@ function App() {
337337
localStorage.setItem('easyrelocate_workspace_token', t)
338338
window.postMessage({ type: 'EASYRELOCATE_PAIR_REQUEST', token: t }, window.location.origin)
339339
setError(null)
340-
setWorkspaceNote('Saved.')
340+
setWorkspaceNote('Saved. Pairing request sent to extension.')
341341
if (targetId) await refresh({ nextTargetId: targetId })
342342
}, [refresh, targetId, workspaceToken])
343343

344+
useEffect(() => {
345+
const handler = (event: MessageEvent) => {
346+
if (event.source !== window) return
347+
if (event.origin !== window.location.origin) return
348+
const data = event.data as { type?: string; ok?: boolean; error?: string } | null
349+
if (!data || data.type !== 'EASYRELOCATE_PAIR_RESULT') return
350+
if (data.ok) {
351+
setWorkspaceNote('Extension paired successfully.')
352+
} else if (data.error) {
353+
setWorkspaceNote(`Pairing failed: ${data.error}`)
354+
} else {
355+
setWorkspaceNote('Pairing failed. Please retry.')
356+
}
357+
}
358+
window.addEventListener('message', handler)
359+
return () => window.removeEventListener('message', handler)
360+
}, [])
361+
344362
useEffect(() => {
345363
if (!targetId) return
346364
void refresh()
@@ -889,10 +907,20 @@ function App() {
889907

890908
<section className="list" aria-busy={loading}>
891909
{filteredAndSorted.length === 0 ? (
892-
<div style={{ color: '#475569', fontSize: 13, padding: '4px 4px' }}>
893-
{targetId
894-
? 'No listings yet (or filtered out). Add some via the extension.'
895-
: 'Set a target first, then add listings via the extension.'}
910+
<div className="emptyState">
911+
<p>
912+
{targetId
913+
? 'No listings yet (or filtered out). Add some via the extension.'
914+
: 'Set a target first, then add listings via the extension.'}
915+
</p>
916+
<a
917+
className="button secondary"
918+
href="https://chromewebstore.google.com/detail/easyrelocate/mogfgembdgeckjlklmoacakiaegnhfgj?hl=en-US&utm_source=ext_sidebar"
919+
target="_blank"
920+
rel="noreferrer"
921+
>
922+
Get Chrome Extension
923+
</a>
896924
</div>
897925
) : null}
898926
{filteredAndSorted.map((it) => {

frontend/src/pages/OnboardingTokenPage.tsx

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ export default function OnboardingTokenPage() {
3232
const [error, setError] = useState<string | null>(null)
3333
const [issued, setIssued] = useState<WorkspaceIssue | null>(null)
3434
const [pairStatus, setPairStatus] = useState<string | null>(null)
35+
const [extensionStatus, setExtensionStatus] = useState<string | null>(null)
3536
const pairTimeoutRef = useRef<number | null>(null)
37+
const pingTimeoutRef = useRef<number | null>(null)
3638

3739
const token = issued?.workspace_token ?? ''
3840
const expiresAt = issued?.expires_at ?? ''
@@ -119,8 +121,28 @@ export default function OnboardingTokenPage() {
119121
window.postMessage({ type: 'EASYRELOCATE_PAIR_REQUEST', token: t }, window.location.origin)
120122
}
121123

122-
const onPairAndContinue = () => {
124+
const waitForPairResult = (): Promise<boolean> =>
125+
new Promise((resolve) => {
126+
const timeout = window.setTimeout(() => {
127+
window.removeEventListener('message', onResult)
128+
resolve(false)
129+
}, 3500)
130+
const onResult = (event: MessageEvent) => {
131+
if (event.source !== window) return
132+
if (event.origin !== window.location.origin) return
133+
const data = event.data as { type?: string; ok?: boolean } | null
134+
if (!data || data.type !== 'EASYRELOCATE_PAIR_RESULT') return
135+
window.clearTimeout(timeout)
136+
window.removeEventListener('message', onResult)
137+
resolve(!!data.ok)
138+
}
139+
window.addEventListener('message', onResult)
140+
})
141+
142+
const onPairAndContinue = async () => {
123143
onPairExtension()
144+
const ok = await waitForPairResult()
145+
if (!ok) return
124146
navigate('/compare')
125147
}
126148

@@ -129,6 +151,14 @@ export default function OnboardingTokenPage() {
129151
if (event.source !== window) return
130152
if (event.origin !== window.location.origin) return
131153
const data = event.data as { type?: string; ok?: boolean; error?: string } | null
154+
if (data?.type === 'EASYRELOCATE_PING_RESULT') {
155+
if (pingTimeoutRef.current) {
156+
window.clearTimeout(pingTimeoutRef.current)
157+
pingTimeoutRef.current = null
158+
}
159+
setExtensionStatus('Extension detected.')
160+
return
161+
}
132162
if (!data || data.type !== 'EASYRELOCATE_PAIR_RESULT') return
133163
if (pairTimeoutRef.current) {
134164
window.clearTimeout(pairTimeoutRef.current)
@@ -141,12 +171,20 @@ export default function OnboardingTokenPage() {
141171
}
142172
}
143173
window.addEventListener('message', handler)
174+
pingTimeoutRef.current = window.setTimeout(() => {
175+
setExtensionStatus('Extension not detected. Install or reload it, then refresh this page.')
176+
}, 1200)
177+
window.postMessage({ type: 'EASYRELOCATE_PING_REQUEST' }, window.location.origin)
144178
return () => {
145179
window.removeEventListener('message', handler)
146180
if (pairTimeoutRef.current) {
147181
window.clearTimeout(pairTimeoutRef.current)
148182
pairTimeoutRef.current = null
149183
}
184+
if (pingTimeoutRef.current) {
185+
window.clearTimeout(pingTimeoutRef.current)
186+
pingTimeoutRef.current = null
187+
}
150188
}
151189
}, [])
152190

@@ -231,12 +269,12 @@ export default function OnboardingTokenPage() {
231269
<button className="button secondary" onClick={() => void onCopy()}>
232270
Copy
233271
</button>
234-
<button className="button" onClick={onPairAndContinue}>
235-
Pair & Continue
236-
</button>
237272
<button className="button secondary" onClick={onContinue}>
238273
Continue to map
239274
</button>
275+
<button className="button" onClick={onPairAndContinue}>
276+
Pair & Continue
277+
</button>
240278
</div>
241279
</>
242280
) : null}
@@ -246,18 +284,23 @@ export default function OnboardingTokenPage() {
246284
<button className="button secondary" onClick={() => void doIssue()}>
247285
Generate token
248286
</button>
249-
<button className="button" onClick={onPairAndContinue}>
250-
Pair & Continue
251-
</button>
252287
<button className="button secondary" onClick={onContinue}>
253288
Continue to map
254289
</button>
290+
<button className="button" onClick={onPairAndContinue}>
291+
Pair & Continue
292+
</button>
255293
</div>
256294
) : null}
257295

258296
{pairStatus ? (
259297
<div style={{ marginTop: 12, color: '#475569', fontSize: 13 }}>{pairStatus}</div>
260298
) : null}
299+
{extensionStatus ? (
300+
<div style={{ marginTop: 6, color: '#475569', fontSize: 13 }}>
301+
{extensionStatus}
302+
</div>
303+
) : null}
261304
</div>
262305
</section>
263306
</main>

0 commit comments

Comments
 (0)