1- import { useQuery , useZero , useZeroVirtualizer } from '@rocicorp/zero/react' ;
1+ import { useQuery , useZero } from '@rocicorp/zero/react' ;
22import classNames from 'classnames' ;
33import Cookies from 'js-cookie' ;
44import React , {
@@ -13,6 +13,7 @@ import React, {
1313import { toast } from 'react-toastify' ;
1414import { useDebouncedCallback } from 'use-debounce' ;
1515import { useParams , useSearch } from 'wouter' ;
16+ import { useHistoryState } from 'wouter/use-browser-location' ;
1617import { must } from '../../../../../packages/shared/src/must.ts' ;
1718import {
1819 queries ,
@@ -26,16 +27,18 @@ import {IssueLink} from '../../components/issue-link.tsx';
2627import { Link } from '../../components/link.tsx' ;
2728import { OnboardingModal } from '../../components/onboarding-modal.tsx' ;
2829import { RelativeTime } from '../../components/relative-time.tsx' ;
30+ import {
31+ useArrayVirtualizer ,
32+ type ScrollRestorationState ,
33+ } from '../../hooks/use-array-virtualizer.ts' ;
2934import { useClickOutside } from '../../hooks/use-click-outside.ts' ;
3035import { useElementSize } from '../../hooks/use-element-size.ts' ;
3136import { useHash } from '../../hooks/use-hash.ts' ;
3237import { useKeypress } from '../../hooks/use-keypress.ts' ;
3338import { useLogin } from '../../hooks/use-login.tsx' ;
34- import { useWouterPermalinkState } from '../../hooks/use-wouter-permalink-state.ts' ;
3539import { appendParam , navigate , removeParam , setParam } from '../../navigate.ts' ;
3640import { recordPageLoad } from '../../page-load-stats.ts' ;
3741import { mark } from '../../perf-log.ts' ;
38- import { CACHE_NAV , CACHE_NONE } from '../../query-cache-policy.ts' ;
3942import { isGigabugs , links , useListContext } from '../../routes.tsx' ;
4043import { preload } from '../../zero-preload.ts' ;
4144import { getIDFromString } from '../issue/get-id.tsx' ;
@@ -109,7 +112,6 @@ export function ListPage({onReady}: {onReady: () => void}) {
109112 labels,
110113 open,
111114 textFilter,
112- permalinkID,
113115 } ) as const ,
114116 [
115117 projectName ,
@@ -120,7 +122,6 @@ export function ListPage({onReady}: {onReady: () => void}) {
120122 open ,
121123 textFilter ,
122124 labels ,
123- permalinkID ,
124125 ] ,
125126 ) ;
126127
@@ -162,30 +163,31 @@ export function ListPage({onReady}: {onReady: () => void}) {
162163 navigate ( `#issue-${ id } ` ) ;
163164 } ;
164165
165- const [ permalinkState , setPermalinkState ] =
166- useWouterPermalinkState < IssueRowSort > ( ) ;
166+ const scrollStateFromHistory =
167+ useHistoryState < ScrollRestorationState | null > ( ) ;
167168
168- const {
169- virtualizer,
170- rowAt,
171- complete,
172- rowsEmpty,
173- permalinkNotFound,
174- estimatedTotal,
175- total,
176- } = useZeroVirtualizer ( {
177- estimateSize : ( ) => ITEM_SIZE ,
178- getScrollElement : ( ) => listRef . current ,
179- getRowKey : row => row . id ,
169+ const scrollState = useMemo (
170+ ( ) => scrollStateFromHistory ,
171+ [
172+ scrollStateFromHistory ?. scrollAnchorID ,
173+ scrollStateFromHistory ?. index ,
174+ scrollStateFromHistory ?. scrollOffset ,
175+ ] ,
176+ ) ;
180177
181- listContextParams,
182- permalinkID,
178+ const handleScrollStateChange = useCallback (
179+ ( state : ScrollRestorationState ) => {
180+ window . history . replaceState ( state , '' , window . location . href ) ;
181+ } ,
182+ [ ] ,
183+ ) ;
183184
184- getPageQuery : (
185- limit : number ,
186- start : IssueRowSort | null ,
187- dir : 'forward' | 'backward' ,
188- ) =>
185+ const estimateRowSize = useCallback ( ( ) => ITEM_SIZE , [ ] ) ;
186+
187+ const getScrollElement = useCallback ( ( ) => listRef . current , [ ] ) ;
188+
189+ const getPageQuery = useCallback (
190+ ( limit : number , start : IssueRowSort | null , dir : 'forward' | 'backward' ) =>
189191 queries . issueListV2 ( {
190192 listContext : listContextParams ,
191193 userID : z . userID ,
@@ -194,8 +196,11 @@ export function ListPage({onReady}: {onReady: () => void}) {
194196 dir,
195197 inclusive : start === null ,
196198 } ) ,
199+ [ listContextParams , z . userID ] ,
200+ ) ;
197201
198- getSingleQuery : ( id : string ) => {
202+ const getSingleQuery = useCallback (
203+ ( id : string ) => {
199204 // Allow short ID too.
200205 const { idField, idValue} = getIDFromString ( id ) ;
201206 return queries . listIssueByID ( {
@@ -204,17 +209,36 @@ export function ListPage({onReady}: {onReady: () => void}) {
204209 listContext : listContextParams ,
205210 } ) ;
206211 } ,
212+ [ listContextParams ] ,
213+ ) ;
207214
208- toStartRow : row => ( {
215+ const toStartRow = useCallback (
216+ ( row : { id : string ; modified : number ; created : number } ) => ( {
209217 id : row . id ,
210218 modified : row . modified ,
211219 created : row . created ,
212220 } ) ,
221+ [ ] ,
222+ ) ;
213223
214- options : textFilterQuery === textFilter ? CACHE_NAV : CACHE_NONE ,
215-
216- permalinkState,
217- onPermalinkStateChange : setPermalinkState ,
224+ const {
225+ virtualizer,
226+ rowAt,
227+ complete,
228+ rowsEmpty,
229+ permalinkNotFound,
230+ estimatedTotal,
231+ total,
232+ } = useArrayVirtualizer ( {
233+ estimateRowSize,
234+ getScrollElement,
235+ permalinkID : permalinkID ?? undefined ,
236+ listContextParams,
237+ getPageQuery,
238+ getSingleQuery,
239+ toStartRow,
240+ scrollState : scrollState ?? undefined ,
241+ onScrollStateChange : handleScrollStateChange ,
218242 } ) ;
219243
220244 useEffect ( ( ) => {
0 commit comments