Skip to content

Commit fb740da

Browse files
authored
Fix: Avoid duplicate subscription on component reuse (#981)
1 parent 260ee6d commit fb740da

File tree

3 files changed

+74
-90
lines changed

3 files changed

+74
-90
lines changed

web/client/src/library/components/report/ReportErrors.tsx

Lines changed: 65 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,88 @@
1-
import { useChannelEvents } from '@api/channels'
21
import { Disclosure, Popover, Transition } from '@headlessui/react'
32
import pluralize from 'pluralize'
43
import clsx from 'clsx'
5-
import { useState, useEffect, Fragment } from 'react'
6-
import {
7-
useIDE,
8-
EnumErrorKey,
9-
type ErrorIDE,
10-
type ErrorKey,
11-
} from '../../pages/ide/context'
4+
import { useState, Fragment } from 'react'
5+
import { useIDE, type ErrorIDE, type ErrorKey } from '../../pages/ide/context'
126
import { toDate, toDateFormat } from '@utils/index'
137
import { MinusCircleIcon, PlusCircleIcon } from '@heroicons/react/24/solid'
148
import { Divider } from '@components/divider/Divider'
159

1610
export default function ReportErrors(): JSX.Element {
17-
const subscribe = useChannelEvents()
18-
19-
const { errors, addError } = useIDE()
11+
const { errors } = useIDE()
2012

2113
const [isShow, setIsShow] = useState(false)
2214

23-
useEffect(() => {
24-
const unsubscribeErrors = subscribe<ErrorIDE>('errors', displayErrors)
25-
26-
return () => {
27-
unsubscribeErrors?.()
28-
}
29-
}, [])
30-
31-
function displayErrors(data: ErrorIDE): void {
32-
addError(EnumErrorKey.General, data)
33-
}
34-
3515
const hasError = errors.size > 0
3616

3717
return (
38-
<>
39-
<Popover
40-
onMouseEnter={() => {
41-
setIsShow(true)
42-
}}
43-
onMouseLeave={() => {
44-
setIsShow(false)
45-
}}
46-
className="flex"
47-
>
48-
{() => (
49-
<>
50-
<span
18+
<Popover
19+
onMouseEnter={() => {
20+
setIsShow(true)
21+
}}
22+
onMouseLeave={() => {
23+
setIsShow(false)
24+
}}
25+
className="flex"
26+
>
27+
{() => (
28+
<>
29+
<span
30+
className={clsx(
31+
'block ml-1 px-2 first-child:ml-0 rounded-full border whitespace-nowrap dark:text-neutral-100 text-xs text-center',
32+
hasError
33+
? 'border-danger-500 text-danger-100 bg-danger-500 cursor-pointer'
34+
: 'border-neutral-500 cursor-default text-neutral-700',
35+
)}
36+
>
37+
{hasError ? (
38+
<span>
39+
{errors.size} {pluralize('Error', errors.size)}
40+
</span>
41+
) : (
42+
<span>No Errors</span>
43+
)}
44+
</span>
45+
<Transition
46+
show={isShow && hasError}
47+
as={Fragment}
48+
enter="transition ease-out duration-200"
49+
enterFrom="opacity-0 translate-y-1"
50+
enterTo="opacity-100 translate-y-0"
51+
leave="transition ease-in duration-150"
52+
leaveFrom="opacity-100 translate-y-0"
53+
leaveTo="opacity-0 translate-y-1"
54+
>
55+
<Popover.Panel
5156
className={clsx(
52-
'block ml-1 px-2 first-child:ml-0 rounded-full border whitespace-nowrap dark:text-neutral-100 text-xs text-center',
53-
hasError
54-
? 'border-danger-500 text-danger-100 bg-danger-500 cursor-pointer'
55-
: 'border-neutral-500 cursor-default text-neutral-700',
57+
'absolute rounded-md top-20 right-2 z-[1000] bg-light p-2 transform overflow-hidden text-danger-700 shadow-2xl',
58+
'w-[90vw] max-h-[80vh]',
5659
)}
5760
>
58-
{hasError ? (
59-
<span>
60-
{errors.size} {pluralize('Error', errors.size)}
61-
</span>
62-
) : (
63-
<span>No Errors</span>
64-
)}
65-
</span>
66-
<Transition
67-
show={isShow && hasError}
68-
as={Fragment}
69-
enter="transition ease-out duration-200"
70-
enterFrom="opacity-0 translate-y-1"
71-
enterTo="opacity-100 translate-y-0"
72-
leave="transition ease-in duration-150"
73-
leaveFrom="opacity-100 translate-y-0"
74-
leaveTo="opacity-0 translate-y-1"
75-
>
76-
<Popover.Panel
61+
<ul
7762
className={clsx(
78-
'absolute rounded-md top-20 right-2 z-[1000] bg-light p-2 transform overflow-hidden text-danger-700 shadow-2xl',
79-
'w-[90vw] max-h-[80vh]',
63+
'w-full bg-danger-10 py-4 pl-3 pr-1 rounded-md overflow-auto scrollbar scrollbar--vertical scrollbar--horizontal',
64+
'max-w-[90vw] max-h-[80vh]',
8065
)}
8166
>
82-
<ul
83-
className={clsx(
84-
'w-full bg-danger-10 py-4 pl-3 pr-1 rounded-md overflow-auto scrollbar scrollbar--vertical scrollbar--horizontal',
85-
'max-w-[90vw] max-h-[80vh]',
86-
)}
87-
>
88-
{Array.from(errors.entries())
89-
.reverse()
90-
.map(([scope, error]) => (
91-
<li
92-
key={`${scope}-${error.message}`}
93-
className="mb-10"
94-
>
95-
<DisplayError
96-
scope={scope}
97-
error={error}
98-
/>
99-
</li>
100-
))}
101-
</ul>
102-
</Popover.Panel>
103-
</Transition>
104-
</>
105-
)}
106-
</Popover>
107-
</>
67+
{Array.from(errors.entries())
68+
.reverse()
69+
.map(([scope, error]) => (
70+
<li
71+
key={`${scope}-${error.message}`}
72+
className="mb-10"
73+
>
74+
<DisplayError
75+
scope={scope}
76+
error={error}
77+
/>
78+
</li>
79+
))}
80+
</ul>
81+
</Popover.Panel>
82+
</Transition>
83+
</>
84+
)}
85+
</Popover>
10886
)
10987
}
11088

web/client/src/library/components/report/ReportTestsErrors.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export default function ReportTestsErrors({
2121
className="flex mb-1"
2222
>
2323
<span className="inline-block mr-4">&mdash;</span>
24-
<div className="mr-4">
24+
<div className="overflow-hidden">
2525
<span className="inline-block mb-2">{item.message}</span>
2626
<code className="inline-block max-h-[50vh] bg-theme py-2 px-4 rounded-lg w-full overflow-auto scrollbar scrollbar--vertical scrollbar--horizontal">
2727
<pre>{item.details}</pre>

web/client/src/library/pages/ide/IDE.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { EnumSize, EnumVariant } from '~/types/enum'
2424
import { Link, Outlet, useLocation, useNavigate } from 'react-router-dom'
2525
import { EnumRoutes } from '~/routes'
2626
import { useStoreFileTree } from '@context/fileTree'
27-
import { EnumErrorKey, useIDE } from './context'
27+
import { EnumErrorKey, ErrorIDE, useIDE } from './context'
2828
import { type Model } from '@api/client'
2929
import { Button } from '@components/button/Button'
3030
import { Divider } from '@components/divider/Divider'
@@ -43,7 +43,7 @@ export default function PageIDE(): JSX.Element {
4343

4444
const client = useQueryClient()
4545

46-
const { removeError } = useIDE()
46+
const { removeError, addError } = useIDE()
4747

4848
const models = useStoreContext(s => s.models)
4949
const environment = useStoreContext(s => s.environment)
@@ -98,6 +98,7 @@ export default function PageIDE(): JSX.Element {
9898
useEffect(() => {
9999
const unsubscribeTasks = subscribe<PlanProgress>('tasks', updateTasks)
100100
const unsubscribeModels = subscribe<Model[]>('models', updateModels)
101+
const unsubscribeErrors = subscribe<ErrorIDE>('errors', displayErrors)
101102
const unsubscribePromote = subscribe<any>(
102103
'promote-environment',
103104
handlePromote,
@@ -125,6 +126,7 @@ export default function PageIDE(): JSX.Element {
125126
unsubscribeTasks?.()
126127
unsubscribeModels?.()
127128
unsubscribePromote?.()
129+
unsubscribeErrors?.()
128130
}
129131
}, [])
130132

@@ -192,6 +194,10 @@ export default function PageIDE(): JSX.Element {
192194
}
193195
}
194196

197+
function displayErrors(data: ErrorIDE): void {
198+
addError(EnumErrorKey.General, data)
199+
}
200+
195201
function handlePromote(): void {
196202
setActivePlan(undefined)
197203

0 commit comments

Comments
 (0)