a tool for shared writing and social publishing
at feature/footnotes 61 lines 1.9 kB view raw
1import { useReplicache } from "src/replicache"; 2import { useSubscribe } from "src/replicache/useSubscribe"; 3import { scanIndex } from "src/replicache/utils"; 4 5export type FootnoteInfo = { 6 footnoteEntityID: string; 7 blockID: string; 8 index: number; 9}; 10 11export function usePageFootnotes(pageID: string) { 12 let rep = useReplicache(); 13 let data = useSubscribe( 14 rep?.rep, 15 async (tx) => { 16 let scan = scanIndex(tx); 17 let cardBlocks = await scan.eav(pageID, "card/block"); 18 let canvasBlocks = await scan.eav(pageID, "canvas/block"); 19 20 let sortedCardBlocks = cardBlocks 21 .map((b) => ({ value: b.data.value, position: b.data.position })) 22 .toSorted((a, b) => (a.position > b.position ? 1 : -1)); 23 24 let sortedCanvasBlocks = canvasBlocks 25 .map((b) => ({ value: b.data.value, position: b.data.position })) 26 .toSorted((a, b) => { 27 if (a.position.y === b.position.y) return a.position.x - b.position.x; 28 return a.position.y - b.position.y; 29 }); 30 31 let sorted = [...sortedCardBlocks, ...sortedCanvasBlocks]; 32 33 let footnotes: FootnoteInfo[] = []; 34 let indexMap: Record<string, number> = {}; 35 let idx = 1; 36 37 for (let block of sorted) { 38 let blockFootnotes = await scan.eav(block.value, "block/footnote"); 39 let sortedFootnotes = blockFootnotes.toSorted((a, b) => 40 a.data.position > b.data.position ? 1 : -1, 41 ); 42 for (let fn of sortedFootnotes) { 43 footnotes.push({ 44 footnoteEntityID: fn.data.value, 45 blockID: block.value, 46 index: idx, 47 }); 48 indexMap[fn.data.value] = idx; 49 idx++; 50 } 51 } 52 53 return { pageID, footnotes, indexMap }; 54 }, 55 { dependencies: [pageID] }, 56 ); 57 58 return ( 59 data || { pageID, footnotes: [], indexMap: {} as Record<string, number> } 60 ); 61}