a tool for shared writing and social publishing

fix footnote indexing

+37 -9
+37 -9
components/Blocks/TextBlock/insertFootnote.ts
··· 18 18 let scan = scanIndex(tx); 19 19 return scan.eav(blockID, "block/footnote"); 20 20 }); 21 - let lastPosition = 22 - existingFootnotes.length > 0 23 - ? existingFootnotes 24 - .map((f) => f.data.position) 25 - .sort() 26 - .at(-1)! 27 - : null; 28 - let position = generateKeyBetween(lastPosition, null); 21 + 22 + // Build a map from footnoteEntityID to its fractional-index position 23 + let positionByEntityID: Record<string, string> = {}; 24 + for (let f of existingFootnotes) { 25 + positionByEntityID[f.data.value] = f.data.position; 26 + } 27 + 28 + // Find footnotes that appear before and after the insertion point in the text 29 + let { from } = view.state.selection; 30 + let beforePosition: string | null = null; 31 + let afterPosition: string | null = null; 32 + let foundInsertionPoint = false; 33 + 34 + view.state.doc.descendants((node, pos) => { 35 + if (node.type.name === "footnote") { 36 + let entityID = node.attrs.footnoteEntityID; 37 + let p = positionByEntityID[entityID]; 38 + if (p !== undefined) { 39 + if (pos < from) { 40 + // This footnote is before the insertion point 41 + if (beforePosition === null || p > beforePosition) { 42 + beforePosition = p; 43 + } 44 + } else { 45 + // This footnote is at or after the insertion point 46 + if (!foundInsertionPoint) { 47 + afterPosition = p; 48 + foundInsertionPoint = true; 49 + } else if (afterPosition !== null && p < afterPosition) { 50 + afterPosition = p; 51 + } 52 + } 53 + } 54 + } 55 + }); 56 + 57 + let position = generateKeyBetween(beforePosition, afterPosition); 29 58 30 59 await rep.mutate.createFootnote({ 31 60 footnoteEntityID, ··· 35 64 }); 36 65 37 66 let node = schema.nodes.footnote.create({ footnoteEntityID }); 38 - let { from } = view.state.selection; 39 67 let tr = view.state.tr.insert(from, node); 40 68 view.dispatch(tr); 41 69