1 import type { MutableRefObject } from 'react';
2 import { useEffect, useRef } from 'react';
4 import { useLinksListing } from './useLinksListing';
5 import useLinksState from './useLinksState';
9 * Whether or not to load thumbnails with the links.
11 loadThumbnails?: boolean;
14 export const useLinksQueue = ({ loadThumbnails }: Props = {}) => {
15 const { loadLinksMeta } = useLinksListing();
16 const linksState = useLinksState();
18 const queue = useRef(new Set<string>());
19 const domRefMap = useRef(new Map<string, MutableRefObject<unknown>>());
20 const controller = useRef<AbortController | null>(null);
21 const promise = useRef<Promise<unknown> | null>(null);
25 controller.current?.abort();
29 const processQueue = (shareId: string) =>
30 new Promise(async (resolve) => {
31 controller.current = new AbortController();
33 while (queue.current.size > 0 && !controller.current.signal.aborted) {
34 // Remove items from the queue which are no longer visible
35 queue.current.forEach((item) => {
36 let ref = domRefMap.current.get(item);
38 if (ref && !ref.current) {
39 queue.current.delete(item);
40 domRefMap.current.delete(item);
44 if (queue.current.size === 0) {
48 const linkIds = Array.from(queue.current);
51 await loadLinksMeta(controller.current.signal, `links-${shareId}`, shareId, linkIds, {
58 linkIds.forEach((linkId) => {
59 queue.current.delete(linkId);
60 domRefMap.current.delete(linkId);
64 controller.current = null;
68 const addToQueue = (shareId: string, linkId: string, domRef?: React.MutableRefObject<unknown>) => {
69 if (linksState.getLink(shareId, linkId) || queue.current.has(linkId)) {
73 queue.current.add(linkId);
75 domRefMap.current.set(linkId, domRef);
78 // We'll debounce starting the queue for a bit, to collect items to batch
80 if (!promise.current) {
81 promise.current = processQueue(shareId).then(() => {
82 promise.current = null;