• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

teableio / teable / 8536869866

03 Apr 2024 10:05AM CUT coverage: 21.234% (-0.3%) from 21.535%
8536869866

Pull #514

github

web-flow
Merge 91a25d710 into 45ee7ebb3
Pull Request #514: refactor: user and link selector

1394 of 2532 branches covered (55.06%)

27 of 1620 new or added lines in 60 files covered. (1.67%)

4 existing lines in 2 files now uncovered.

14588 of 68702 relevant lines covered (21.23%)

2.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/packages/sdk/src/hooks/use-infinite-records.ts
NEW
1
import type { IRecord } from '@teable/core';
×
NEW
2
import type { IGetRecordsRo } from '@teable/openapi';
×
NEW
3
import { inRange, debounce } from 'lodash';
×
NEW
4
import { useCallback, useEffect, useRef, useState, useMemo } from 'react';
×
NEW
5
import type { Record } from '../model';
×
NEW
6
import { useRecords } from './use-records';
×
NEW
7

×
NEW
8
// eslint-disable-next-line
×
NEW
9
export const LOAD_PAGE_SIZE = 300;
×
NEW
10
const defaultVisiblePages = { y: 0, height: 0 };
×
NEW
11

×
NEW
12
interface IVisiblePages {
×
NEW
13
  y: number;
×
NEW
14
  height: number;
×
NEW
15
}
×
NEW
16

×
NEW
17
type IRes = {
×
NEW
18
  recordMap: IRecordIndexMap;
×
NEW
19
  onReset: () => void;
×
NEW
20
  onForceUpdate: () => void;
×
NEW
21
  onVisibleRegionChanged: (r: IVisiblePages) => void;
×
NEW
22
};
×
NEW
23

×
NEW
24
export type IRecordIndexMap = { [i: number | string]: Record };
×
NEW
25

×
NEW
26
export const useInfiniteRecords = (
×
NEW
27
  recordsQuery?: Omit<IGetRecordsRo, 'take' | 'skip'>,
×
NEW
28
  initRecords?: IRecord[]
×
NEW
29
): IRes => {
×
NEW
30
  const [query, setQuery] = useState<IGetRecordsRo>({
×
NEW
31
    skip: 0,
×
NEW
32
    take: LOAD_PAGE_SIZE,
×
NEW
33
    ...recordsQuery,
×
NEW
34
  });
×
NEW
35
  const queryRef = useRef(query);
×
NEW
36
  queryRef.current = query;
×
NEW
37
  const records = useRecords(query, initRecords);
×
NEW
38
  const [loadedRecordMap, setLoadedRecordMap] = useState<IRecordIndexMap>(() =>
×
NEW
39
    records.reduce((acc, record, i) => {
×
NEW
40
      acc[i] = record;
×
NEW
41
      return acc;
×
NEW
42
    }, {} as IRecordIndexMap)
×
NEW
43
  );
×
NEW
44
  const [visiblePages, setVisiblePages] = useState<IVisiblePages>(defaultVisiblePages);
×
NEW
45
  const visiblePagesRef = useRef(visiblePages);
×
NEW
46
  visiblePagesRef.current = visiblePages;
×
NEW
47

×
NEW
48
  const onForceUpdate = useCallback(() => {
×
NEW
49
    const startIndex = queryRef.current.skip ?? 0;
×
NEW
50
    const take = queryRef.current.take ?? LOAD_PAGE_SIZE;
×
NEW
51
    setLoadedRecordMap((preLoadedRecords) => {
×
NEW
52
      const cacheLen = take * 2;
×
NEW
53
      const [cacheStartIndex, cacheEndIndex] = [
×
NEW
54
        Math.max(startIndex - cacheLen / 2, 0),
×
NEW
55
        startIndex + records.length + cacheLen / 2,
×
NEW
56
      ];
×
NEW
57
      const newRecordsState: IRecordIndexMap = {};
×
NEW
58
      for (let i = cacheStartIndex; i < cacheEndIndex; i++) {
×
NEW
59
        if (startIndex <= i && i < startIndex + records.length) {
×
NEW
60
          newRecordsState[i] = records[i - startIndex];
×
NEW
61
          continue;
×
NEW
62
        }
×
NEW
63
        newRecordsState[i] = preLoadedRecords[i];
×
NEW
64
      }
×
NEW
65
      return newRecordsState;
×
NEW
66
    });
×
NEW
67
  }, [records]);
×
NEW
68

×
NEW
69
  useEffect(() => onForceUpdate(), [onForceUpdate]);
×
NEW
70

×
NEW
71
  useEffect(() => {
×
NEW
72
    const { y, height } = visiblePages;
×
NEW
73
    setQuery((cv) => {
×
NEW
74
      if (cv.skip === undefined) {
×
NEW
75
        return cv;
×
NEW
76
      }
×
NEW
77

×
NEW
78
      const take = cv.take ?? LOAD_PAGE_SIZE;
×
NEW
79

×
NEW
80
      const pageOffsetSize = take / 3;
×
NEW
81
      const pageGap = take / 3;
×
NEW
82

×
NEW
83
      const visibleStartIndex = cv.skip <= y ? cv.skip - pageOffsetSize : cv.skip + pageOffsetSize;
×
NEW
84
      const visibleEndIndex = visibleStartIndex + take;
×
NEW
85
      const viewInRange =
×
NEW
86
        inRange(y, visibleStartIndex, visibleEndIndex) &&
×
NEW
87
        inRange(y + height, visibleStartIndex, visibleEndIndex);
×
NEW
88
      if (!viewInRange) {
×
NEW
89
        const skip = Math.floor(y / pageGap) * pageGap - pageGap;
×
NEW
90
        return {
×
NEW
91
          take: cv.take,
×
NEW
92
          ...recordsQuery,
×
NEW
93
          skip: Math.max(0, skip),
×
NEW
94
        };
×
NEW
95
      }
×
NEW
96
      return {
×
NEW
97
        take: cv.take,
×
NEW
98
        ...recordsQuery,
×
NEW
99
        skip: cv.skip,
×
NEW
100
      };
×
NEW
101
    });
×
NEW
102
  }, [visiblePages, recordsQuery]);
×
NEW
103

×
NEW
104
  const updateVisiblePages = useMemo(() => {
×
NEW
105
    return debounce(setVisiblePages, 30, { maxWait: 500 });
×
NEW
106
  }, []);
×
NEW
107

×
NEW
108
  const onVisibleRegionChanged = useCallback(
×
NEW
109
    (r: IVisiblePages) => {
×
NEW
110
      const { y, height } = visiblePagesRef.current;
×
NEW
111
      if (r.y === y && r.height === height) return;
×
NEW
112
      updateVisiblePages(r);
×
NEW
113
    },
×
NEW
114
    [updateVisiblePages]
×
NEW
115
  );
×
NEW
116

×
NEW
117
  const onReset = useCallback(() => {
×
NEW
118
    setLoadedRecordMap({});
×
NEW
119
    setVisiblePages(defaultVisiblePages);
×
NEW
120
  }, []);
×
NEW
121

×
NEW
122
  return {
×
NEW
123
    recordMap: loadedRecordMap,
×
NEW
124
    onVisibleRegionChanged,
×
NEW
125
    onForceUpdate,
×
NEW
126
    onReset,
×
NEW
127
  };
×
NEW
128
};
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc