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 listStyle: listStyle?.data.value,
82 listStart: listNumber?.data.value,
83 },
84 },
85 ...childBlocks.flat(),
86 ];
87 };
88 return getChildren(b, b.entity, 1, []);
89 }
90 return [
91 {
92 ...b.data,
93 factID: b.id,
94 type: type.data.value,
95 parent: b.entity,
96 },
97 ] as Block[];
98 }),
99 )
100 )
101 .flat()
102 .filter((f) => f !== null);
103
104 computeDisplayNumbers(result);
105 return result;
106};
107
108export const getBlocksWithTypeLocal = (
109 initialFacts: Fact<any>[],
110 entityID: string,
111) => {
112 let scan = scanIndexLocal(initialFacts);
113 let blocks = scan.eav(entityID, "card/block");
114 let result = blocks
115 .sort((a, b) => {
116 if (a.data.position === b.data.position) return a.id > b.id ? 1 : -1;
117 return a.data.position > b.data.position ? 1 : -1;
118 })
119 .map((b) => {
120 let type = scan.eav(b.data.value, "block/type")[0];
121 let isList = scan.eav(b.data.value, "block/is-list");
122 if (!type) return null;
123 // All lists use recursive structure
124 if (isList[0]?.data.value) {
125 const getChildren = (
126 root: Fact<"card/block">,
127 parent: string,
128 depth: number,
129 path: { depth: number; entity: string }[],
130 ): Block[] => {
131 let children = scan
132 .eav(root.data.value, "card/block")
133 .sort((a, b) => (a.data.position > b.data.position ? 1 : -1));
134 let type = scan.eav(root.data.value, "block/type")[0];
135 let listStyle = scan.eav(root.data.value, "block/list-style")[0];
136 let listNumber = scan.eav(root.data.value, "block/list-number")[0];
137 if (!type) return [];
138 let newPath = [...path, { entity: root.data.value, depth }];
139 let childBlocks = children.map((c) =>
140 getChildren(c, root.data.value, depth + 1, newPath),
141 );
142 return [
143 {
144 ...root.data,
145 factID: root.id,
146 type: type.data.value,
147 parent: b.entity,
148 listData: {
149 depth: depth,
150 parent,
151 path: newPath,
152 listStyle: listStyle?.data.value,
153 listStart: listNumber?.data.value,
154 },
155 },
156 ...childBlocks.flat(),
157 ];
158 };
159 return getChildren(b, b.entity, 1, []);
160 }
161 return [
162 {
163 ...b.data,
164 factID: b.id,
165 type: type.data.value,
166 parent: b.entity,
167 },
168 ] as Block[];
169 })
170 .flat()
171 .filter((f) => f !== null);
172
173 computeDisplayNumbers(result);
174 return result;
175};