a tool for shared writing and social publishing
1import { Block } from "components/Blocks/Block";
2import { ReadTransaction } from "replicache";
3import { Fact } from "src/replicache";
4import { scanIndex, scanIndexLocal } from "src/replicache/utils";
5
6function computeDisplayNumbers(blocks: Block[]): void {
7 let counters = new Map<string, number>();
8 for (let block of blocks) {
9 if (!block.listData) {
10 counters.clear();
11 continue;
12 }
13 if (block.listData.listStyle !== "ordered") continue;
14 let parent = block.listData.parent;
15 if (block.listData.listStart !== undefined) {
16 counters.set(parent, block.listData.listStart);
17 } else if (!counters.has(parent)) {
18 counters.set(parent, 1);
19 }
20 block.listData.displayNumber = counters.get(parent)!;
21 counters.set(parent, counters.get(parent)! + 1);
22 }
23}
24
25export const getBlocksWithType = async (
26 tx: ReadTransaction,
27 entityID: string,
28) => {
29 let initialized = await tx.get("initialized");
30 if (!initialized) return null;
31 let scan = scanIndex(tx);
32 let blocks = await scan.eav(entityID, "card/block");
33
34 let result = (
35 await Promise.all(
36 blocks
37 .sort((a, b) => {
38 if (a.data.position === b.data.position) return a.id > b.id ? 1 : -1;
39 return a.data.position > b.data.position ? 1 : -1;
40 })
41 .map(async (b) => {
42 let type = (await scan.eav(b.data.value, "block/type"))[0];
43 let isList = await scan.eav(b.data.value, "block/is-list");
44 if (!type) return null;
45 // All lists use recursive structure
46 if (isList[0]?.data.value) {
47 const getChildren = async (
48 root: Fact<"card/block">,
49 parent: string,
50 depth: number,
51 path: { depth: number; entity: string }[],
52 ): Promise<Block[]> => {
53 let children = (
54 await scan.eav(root.data.value, "card/block")
55 ).sort((a, b) => (a.data.position > b.data.position ? 1 : -1));
56 let type = (await scan.eav(root.data.value, "block/type"))[0];
57 let checklist = await scan.eav(
58 root.data.value,
59 "block/check-list",
60 );
61 let listStyle = (await scan.eav(root.data.value, "block/list-style"))[0];
62 let listNumber = (await scan.eav(root.data.value, "block/list-number"))[0];
63 if (!type) return [];
64 let newPath = [...path, { entity: root.data.value, depth }];
65 let childBlocks = await Promise.all(
66 children.map((c) =>
67 getChildren(c, root.data.value, depth + 1, newPath),
68 ),
69 );
70 return [
71 {
72 ...root.data,
73 factID: root.id,
74 type: type.data.value,
75 parent: b.entity,
76 listData: {
77 depth: depth,
78 parent,
79 path: newPath,
80 checklist: !!checklist[0],
81 checked: checklist[0]?.data.value,
82 listStyle: listStyle?.data.value,
83 listStart: listNumber?.data.value,
84 },
85 },
86 ...childBlocks.flat(),
87 ];
88 };
89 return getChildren(b, b.entity, 1, []);
90 }
91 return [
92 {
93 ...b.data,
94 factID: b.id,
95 type: type.data.value,
96 parent: b.entity,
97 },
98 ] as Block[];
99 }),
100 )
101 )
102 .flat()
103 .filter((f) => f !== null);
104
105 computeDisplayNumbers(result);
106 return result;
107};
108
109export const getBlocksWithTypeLocal = (
110 initialFacts: Fact<any>[],
111 entityID: string,
112) => {
113 let scan = scanIndexLocal(initialFacts);
114 let blocks = scan.eav(entityID, "card/block");
115 let result = blocks
116 .sort((a, b) => {
117 if (a.data.position === b.data.position) return a.id > b.id ? 1 : -1;
118 return a.data.position > b.data.position ? 1 : -1;
119 })
120 .map((b) => {
121 let type = scan.eav(b.data.value, "block/type")[0];
122 let isList = scan.eav(b.data.value, "block/is-list");
123 if (!type) return null;
124 // All lists use recursive structure
125 if (isList[0]?.data.value) {
126 const getChildren = (
127 root: Fact<"card/block">,
128 parent: string,
129 depth: number,
130 path: { depth: number; entity: string }[],
131 ): Block[] => {
132 let children = scan
133 .eav(root.data.value, "card/block")
134 .sort((a, b) => (a.data.position > b.data.position ? 1 : -1));
135 let type = scan.eav(root.data.value, "block/type")[0];
136 let listStyle = scan.eav(root.data.value, "block/list-style")[0];
137 let listNumber = scan.eav(root.data.value, "block/list-number")[0];
138 if (!type) return [];
139 let newPath = [...path, { entity: root.data.value, depth }];
140 let childBlocks = children.map((c) =>
141 getChildren(c, root.data.value, depth + 1, newPath),
142 );
143 return [
144 {
145 ...root.data,
146 factID: root.id,
147 type: type.data.value,
148 parent: b.entity,
149 listData: {
150 depth: depth,
151 parent,
152 path: newPath,
153 listStyle: listStyle?.data.value,
154 listStart: listNumber?.data.value,
155 },
156 },
157 ...childBlocks.flat(),
158 ];
159 };
160 return getChildren(b, b.entity, 1, []);
161 }
162 return [
163 {
164 ...b.data,
165 factID: b.id,
166 type: type.data.value,
167 parent: b.entity,
168 },
169 ] as Block[];
170 })
171 .flat()
172 .filter((f) => f !== null);
173
174 computeDisplayNumbers(result);
175 return result;
176};