1
- import { Fragment , useMemo , useRef } from 'react' ;
2
- import type { ListRowProps } from 'react-virtualized' ;
3
- import {
4
- AutoSizer ,
5
- CellMeasurer ,
6
- InfiniteLoader ,
7
- List as ReactVirtualizedList ,
8
- } from 'react-virtualized' ;
1
+ import { Fragment , useMemo } from 'react' ;
9
2
import styled from '@emotion/styled' ;
10
3
11
4
import waitingForEventImg from 'sentry-images/spot/waiting-for-event.svg' ;
@@ -15,157 +8,94 @@ import ErrorBoundary from 'sentry/components/errorBoundary';
15
8
import FeedbackListHeader from 'sentry/components/feedback/list/feedbackListHeader' ;
16
9
import FeedbackListItem from 'sentry/components/feedback/list/feedbackListItem' ;
17
10
import useFeedbackQueryKeys from 'sentry/components/feedback/useFeedbackQueryKeys' ;
11
+ import InfiniteListItems from 'sentry/components/infiniteList/infiniteListItems' ;
12
+ import InfiniteListState from 'sentry/components/infiniteList/infiniteListState' ;
18
13
import LoadingIndicator from 'sentry/components/loadingIndicator' ;
19
14
import { t } from 'sentry/locale' ;
20
15
import { space } from 'sentry/styles/space' ;
21
- import useFetchInfiniteListData from 'sentry/utils/api/useFetchInfiniteListData ' ;
16
+ import uniqueBy from 'sentry/utils/array/uniqBy ' ;
22
17
import type { FeedbackIssueListItem } from 'sentry/utils/feedback/types' ;
23
18
import { useListItemCheckboxContext } from 'sentry/utils/list/useListItemCheckboxState' ;
24
- import useVirtualizedList from 'sentry/views/replays/detail/useVirtualizedList ' ;
19
+ import { useInfiniteApiQuery } from 'sentry/utils/queryClient ' ;
25
20
26
- // Ensure this object is created once as it is an input to
27
- // `useVirtualizedList`'s memoization
28
- const cellMeasurer = {
29
- fixedWidth : true ,
30
- minHeight : 24 ,
31
- } ;
32
-
33
- function NoFeedback ( { title, subtitle} : { subtitle : string ; title : string } ) {
21
+ function NoFeedback ( ) {
34
22
return (
35
- < Wrapper >
36
- < img src = { waitingForEventImg } alt = "No feedback found spot illustration" />
37
- < EmptyMessage > { title } </ EmptyMessage >
38
- < p > { subtitle } </ p >
39
- </ Wrapper >
23
+ < NoFeedbackWrapper >
24
+ < img src = { waitingForEventImg } alt = { t ( 'A person waiting for a phone to ring' ) } />
25
+ < NoFeedbackMessage > { t ( 'Inbox Zero' ) } </ NoFeedbackMessage >
26
+ < p > { t ( 'You have two options: take a nap or be productive.' ) } </ p >
27
+ </ NoFeedbackWrapper >
40
28
) ;
41
29
}
42
30
43
31
export default function FeedbackList ( ) {
44
32
const { listQueryKey} = useFeedbackQueryKeys ( ) ;
45
- const {
46
- isFetchingNextPage,
47
- isFetchingPreviousPage,
48
- isLoading, // If anything is loaded yet
49
- getRow,
50
- isRowLoaded,
51
- issues,
52
- loadMoreRows,
53
- hits,
54
- } = useFetchInfiniteListData < FeedbackIssueListItem > ( {
33
+ const queryResult = useInfiniteApiQuery < FeedbackIssueListItem [ ] > ( {
55
34
queryKey : listQueryKey ?? [ 'infinite' , '' ] ,
56
- uniqueField : 'id' ,
57
35
enabled : Boolean ( listQueryKey ) ,
58
36
} ) ;
59
37
38
+ const issues = useMemo (
39
+ ( ) => uniqueBy ( queryResult . data ?. pages . flatMap ( ( [ pageData ] ) => pageData ) ?? [ ] , 'id' ) ,
40
+ [ queryResult . data ?. pages ]
41
+ ) ;
60
42
const checkboxState = useListItemCheckboxContext ( {
61
- hits,
43
+ hits : issues . length ,
62
44
knownIds : issues . map ( issue => issue . id ) ,
63
45
queryKey : listQueryKey ,
64
46
} ) ;
65
47
66
- const listRef = useRef < ReactVirtualizedList > ( null ) ;
67
-
68
- const deps = useMemo ( ( ) => [ isLoading , issues . length ] , [ isLoading , issues . length ] ) ;
69
- const { cache, updateList} = useVirtualizedList ( {
70
- cellMeasurer,
71
- ref : listRef ,
72
- deps,
73
- } ) ;
74
-
75
- const renderRow = ( { index, key, style, parent} : ListRowProps ) => {
76
- const item = getRow ( { index} ) ;
77
- if ( ! item ) {
78
- return null ;
79
- }
80
-
81
- return (
82
- < ErrorBoundary mini key = { key } >
83
- < CellMeasurer cache = { cache } columnIndex = { 0 } parent = { parent } rowIndex = { index } >
84
- < FeedbackListItem
85
- feedbackItem = { item }
86
- isSelected = { checkboxState . isSelected ( item . id ) }
87
- onSelect = { ( ) => {
88
- checkboxState . toggleSelected ( item . id ) ;
89
- } }
90
- style = { style }
91
- />
92
- </ CellMeasurer >
93
- </ ErrorBoundary >
94
- ) ;
95
- } ;
96
-
97
48
return (
98
49
< Fragment >
99
50
< FeedbackListHeader { ...checkboxState } />
100
51
< FeedbackListItems >
101
- < InfiniteLoader
102
- isRowLoaded = { isRowLoaded }
103
- loadMoreRows = { loadMoreRows }
104
- rowCount = { hits }
52
+ < InfiniteListState
53
+ queryResult = { queryResult }
54
+ backgroundUpdatingMessage = { ( ) => null }
55
+ loadingMessage = { ( ) => < LoadingIndicator /> }
105
56
>
106
- { ( { onRowsRendered, registerChild} ) => (
107
- < AutoSizer onResize = { updateList } >
108
- { ( { width, height} ) => (
109
- < ReactVirtualizedList
110
- deferredMeasurementCache = { cache }
111
- height = { height }
112
- noRowsRenderer = { ( ) =>
113
- isLoading ? (
114
- < LoadingIndicator />
115
- ) : (
116
- < NoFeedback
117
- title = { t ( 'Inbox Zero' ) }
118
- subtitle = { t ( 'You have two options: take a nap or be productive.' ) }
119
- />
120
- )
121
- }
122
- onRowsRendered = { onRowsRendered }
123
- overscanRowCount = { 5 }
124
- ref = { e => {
125
- listRef . current = e ;
126
- registerChild ( e ) ;
57
+ < InfiniteListItems < FeedbackIssueListItem >
58
+ estimateSize = { ( ) => 24 }
59
+ queryResult = { queryResult }
60
+ itemRenderer = { ( { item} ) => (
61
+ < ErrorBoundary mini >
62
+ < FeedbackListItem
63
+ feedbackItem = { item }
64
+ isSelected = { checkboxState . isSelected ( item . id ) }
65
+ onSelect = { ( ) => {
66
+ checkboxState . toggleSelected ( item . id ) ;
127
67
} }
128
- rowCount = { issues . length }
129
- rowHeight = { cache . rowHeight }
130
- rowRenderer = { renderRow }
131
- width = { width }
132
68
/>
133
- ) }
134
- </ AutoSizer >
135
- ) }
136
- </ InfiniteLoader >
137
- < FloatingContainer style = { { top : '2px' } } >
138
- { isFetchingPreviousPage ? (
139
- < Tooltip title = { t ( 'Loading more feedback...' ) } >
140
- < LoadingIndicator mini />
141
- </ Tooltip >
142
- ) : null }
143
- </ FloatingContainer >
144
- < FloatingContainer style = { { bottom : '2px' } } >
145
- { isFetchingNextPage ? (
146
- < Tooltip title = { t ( 'Loading more feedback...' ) } >
147
- < LoadingIndicator mini />
148
- </ Tooltip >
149
- ) : null }
150
- </ FloatingContainer >
69
+ </ ErrorBoundary >
70
+ ) }
71
+ emptyMessage = { ( ) => < NoFeedback /> }
72
+ loadingMoreMessage = { ( ) => (
73
+ < Centered >
74
+ < Tooltip title = { t ( 'Loading more feedback...' ) } >
75
+ < LoadingIndicator mini />
76
+ </ Tooltip >
77
+ </ Centered >
78
+ ) }
79
+ loadingCompleteMessage = { ( ) => null }
80
+ />
81
+ </ InfiniteListState >
151
82
</ FeedbackListItems >
152
83
</ Fragment >
153
84
) ;
154
85
}
155
86
156
87
const FeedbackListItems = styled ( 'div' ) `
157
- display: grid;
88
+ display: flex;
89
+ flex-direction: column;
158
90
flex-grow: 1;
159
- min-height: 300px ;
91
+ padding-bottom: ${ space ( 0.5 ) } ;
160
92
` ;
161
93
162
- const FloatingContainer = styled ( 'div' ) `
163
- position: absolute;
94
+ const Centered = styled ( 'div' ) `
164
95
justify-self: center;
165
96
` ;
166
97
167
- const Wrapper = styled ( 'div' ) `
168
- display: flex;
98
+ const NoFeedbackWrapper = styled ( 'div' ) `
169
99
padding: ${ space ( 4 ) } ${ space ( 4 ) } ;
170
100
flex-direction: column;
171
101
align-items: center;
@@ -175,12 +105,9 @@ const Wrapper = styled('div')`
175
105
@media (max-width: ${ p => p . theme . breakpoints . sm } ) {
176
106
font-size: ${ p => p . theme . fontSize . md } ;
177
107
}
178
- position: relative;
179
- top: 50%;
180
- transform: translateY(-50%);
181
108
` ;
182
109
183
- const EmptyMessage = styled ( 'div' ) `
110
+ const NoFeedbackMessage = styled ( 'div' ) `
184
111
font-weight: ${ p => p . theme . fontWeight . bold } ;
185
112
color: ${ p => p . theme . gray400 } ;
186
113
0 commit comments