1- import { FC , useCallback , useEffect , useMemo , useState } from 'react'
1+ import { FC , useCallback , useMemo } from 'react'
22import moment from 'moment'
33import { LineChart } from 'components/charts'
44import ContentCard from 'components/base/grid/ContentCard'
55import Button from 'components/base/forms/Button'
66import Icon from 'components/icons/Icon'
7- import useCountdown from 'common/hooks/useCountdown'
87import { colorIconDanger } from 'common/theme/tokens'
98import {
109 useGetExperimentExposuresQuery ,
@@ -19,14 +18,9 @@ import {
1918} from './derive'
2019import type { VariantTotal } from './derive'
2120import {
22- DEFAULT_RETRY_AFTER_S ,
23- POLL_TIMEOUT_MS ,
24- REFRESH_POLL_INTERVAL_MS ,
2521 canRefreshExposures ,
2622 deriveExposuresViewState ,
27- getExposuresRefreshLabel ,
2823} from './exposuresViewState'
29- import RefreshControl from './RefreshControl'
3024import './results.scss'
3125
3226const AsOfLabel : FC < { asOf : string | null } > = ( { asOf } ) => (
@@ -45,75 +39,31 @@ const buildLegendLabels = (totals: VariantTotal[]): Record<string, string> => {
4539 return labels
4640}
4741
48- const parseRetryAfter = ( err : unknown ) : number | null => {
49- const fetchErr = err as {
50- status ?: number
51- retryAfter ?: number | null
52- }
53- if ( fetchErr . status !== 429 ) return null
54- if ( fetchErr . retryAfter ) return fetchErr . retryAfter
55- return DEFAULT_RETRY_AFTER_S
56- }
57-
5842type ExperimentExposuresPanelProps = {
5943 experiment : Experiment
6044 environmentId : string
6145 exposuresOverride ?: ExperimentExposures
6246}
6347
64- const REFRESH_DISABLED_COPY : Record < string , string > = {
65- final : 'Refresh is disabled because the experiment is complete.' ,
66- not_started : 'Start the experiment to compute exposures.' ,
67- }
68-
6948const ExperimentExposuresPanel : FC < ExperimentExposuresPanelProps > = ( {
7049 environmentId,
7150 experiment,
7251 exposuresOverride,
7352} ) => {
74- const [ pollInterval , setPollInterval ] = useState ( 0 )
75- const [ refreshRequested , setRefreshRequested ] = useState ( false )
76- const [ pollStartedAt , setPollStartedAt ] = useState < number | null > ( null )
77- const [ retryAfter , startRetryCountdown ] = useCountdown ( )
7853 const { data : fetched } = useGetExperimentExposuresQuery (
7954 { environmentId, experimentId : experiment . id } ,
8055 {
81- pollingInterval : pollInterval ,
8256 refetchOnMountOrArgChange : true ,
8357 skip : ! ! exposuresOverride ,
8458 } ,
8559 )
8660 const exposures = exposuresOverride ?? fetched
87- const [ refresh , { isLoading : isSubmitting } ] =
88- useRefreshExperimentExposuresMutation ( )
61+ const [ refreshExposures ] = useRefreshExperimentExposuresMutation ( )
8962
9063 const viewState = deriveExposuresViewState ( exposures )
9164 const availability = canRefreshExposures ( experiment . status , exposures )
9265 const payload = exposures ?. payload ?? null
9366
94- const pollTimedOut =
95- pollStartedAt !== null && Date . now ( ) - pollStartedAt > POLL_TIMEOUT_MS
96- const shouldPoll =
97- ! pollTimedOut && ( viewState . kind === 'refreshing' || refreshRequested )
98- const nextPollInterval = shouldPoll ? REFRESH_POLL_INTERVAL_MS : 0
99- useEffect ( ( ) => {
100- setPollInterval ( nextPollInterval )
101- } , [ nextPollInterval ] )
102-
103- useEffect ( ( ) => {
104- if ( viewState . kind === 'loaded' || viewState . kind === 'error' ) {
105- setRefreshRequested ( false )
106- setPollStartedAt ( null )
107- }
108- } , [ viewState . kind ] )
109-
110- useEffect ( ( ) => {
111- if ( pollTimedOut ) {
112- setRefreshRequested ( false )
113- setPollStartedAt ( null )
114- }
115- } , [ pollTimedOut ] )
116-
11767 const identities = useMemo (
11868 ( ) => getVariantIdentities ( experiment . feature ) ,
11969 [ experiment . feature ] ,
@@ -127,61 +77,24 @@ const ExperimentExposuresPanel: FC<ExperimentExposuresPanelProps> = ({
12777 [ payload , identities ] ,
12878 )
12979
130- const isRefreshing =
131- refreshRequested || viewState . kind === 'refreshing' || isSubmitting
80+ const isRefreshing = viewState . kind === 'refreshing'
13281 const headline = payload ? getHeadlineTotal ( payload ) : 0
13382 const hasData = ! ! payload && headline > 0
13483
135- const handleRefresh = useCallback ( async ( ) => {
136- setRefreshRequested ( true )
137- setPollStartedAt ( Date . now ( ) )
138- const result = await refresh ( {
84+ const handleComputeNow = useCallback ( async ( ) => {
85+ const result = await refreshExposures ( {
13986 environmentId,
14087 experimentId : experiment . id ,
14188 } )
14289 if ( 'error' in result && result . error ) {
143- setRefreshRequested ( false )
144- setPollStartedAt ( null )
145- const seconds = parseRetryAfter ( result . error )
146- if ( seconds !== null ) {
147- startRetryCountdown ( seconds )
148- } else {
149- toast ( 'Failed to refresh exposures' , 'danger' )
150- }
90+ toast ( 'Failed to compute exposures' , 'danger' )
15191 }
152- } , [ refresh , environmentId , experiment . id , startRetryCountdown ] )
153-
154- const refreshLabel = getExposuresRefreshLabel ( retryAfter , isRefreshing )
155-
156- const action = (
157- < RefreshControl
158- disabled = { ! availability . canRefresh || retryAfter !== null }
159- disabledReason = {
160- availability . reason
161- ? REFRESH_DISABLED_COPY [ availability . reason ]
162- : undefined
163- }
164- isRefreshing = { isRefreshing }
165- label = {
166- refreshLabel && (
167- < span
168- className = {
169- refreshLabel . tone === 'danger' ? 'text-danger' : undefined
170- }
171- >
172- { refreshLabel . message }
173- </ span >
174- )
175- }
176- onRefresh = { handleRefresh }
177- />
178- )
92+ } , [ refreshExposures , environmentId , experiment . id ] )
17993
18094 const asOf = exposures ?. as_of ?? null
18195
18296 return (
18397 < ContentCard
184- action = { action }
18598 className = 'experiment-results__exposures-card'
18699 title = 'Enrollment over time'
187100 >
@@ -262,7 +175,7 @@ const ExperimentExposuresPanel: FC<ExperimentExposuresPanelProps> = ({
262175 : 'No exposure data computed yet.' }
263176 { ! isRefreshing && availability . canRefresh && (
264177 < div className = 'mt-2' >
265- < Button onClick = { handleRefresh } size = 'small' theme = 'secondary' >
178+ < Button onClick = { handleComputeNow } size = 'small' theme = 'secondary' >
266179 Compute now
267180 </ Button >
268181 </ div >
0 commit comments