a tool for shared writing and social publishing
1import { Replicache } from "replicache";
2import { ReplicacheMutators } from "src/replicache";
3import { useUIState } from "src/useUIState";
4import { scanIndex } from "src/replicache/utils";
5import { getBlocksWithType } from "src/replicache/getBlocks";
6import { focusBlock } from "src/utils/focusBlock";
7import { UndoManager } from "src/undoManager";
8
9export async function deleteBlock(
10 entities: string[],
11 rep: Replicache<ReplicacheMutators>,
12 undoManager?: UndoManager,
13) {
14 // get what pagess we need to close as a result of deleting this block
15 let pagesToClose = [] as string[];
16
17 for (let entity of entities) {
18 let [type] = await rep.query((tx) =>
19 scanIndex(tx).eav(entity, "block/type"),
20 );
21 if (type.data.value === "card") {
22 let [childPages] = await rep?.query(
23 (tx) => scanIndex(tx).eav(entity, "block/card") || [],
24 );
25 pagesToClose = [childPages?.data.value];
26 }
27 if (type.data.value === "mailbox") {
28 let [archive] = await rep?.query(
29 (tx) => scanIndex(tx).eav(entity, "mailbox/archive") || [],
30 );
31 let [draft] = await rep?.query(
32 (tx) => scanIndex(tx).eav(entity, "mailbox/draft") || [],
33 );
34 pagesToClose = [archive?.data.value, draft?.data.value];
35 }
36 }
37
38 // the next and previous blocks in the block list
39 // if the focused thing is a page and not a block, return
40 let focusedBlock = useUIState.getState().focusedEntity;
41 let parent =
42 focusedBlock?.entityType === "page"
43 ? focusedBlock.entityID
44 : focusedBlock?.parent;
45
46 if (parent) {
47 let parentType = await rep?.query((tx) =>
48 scanIndex(tx).eav(parent, "page/type"),
49 );
50 // if the page is a canvas, focus the page
51 if (parentType[0]?.data.value === "canvas") {
52 useUIState
53 .getState()
54 .setFocusedBlock({ entityType: "page", entityID: parent });
55 useUIState.getState().setSelectedBlocks([]);
56 } else {
57 // if the page is a doc, focus the previous block (or if there isn't a prev block, focus the next block)
58 let siblings =
59 (await rep?.query((tx) => getBlocksWithType(tx, parent))) || [];
60
61 let selectedBlocks = useUIState.getState().selectedBlocks;
62 let firstSelected = selectedBlocks[0];
63 let lastSelected = selectedBlocks[entities.length - 1];
64
65 let prevBlock =
66 siblings?.[
67 siblings.findIndex((s) => s.value === firstSelected?.value) - 1
68 ];
69 let prevBlockType = await rep?.query((tx) =>
70 scanIndex(tx).eav(prevBlock?.value, "block/type"),
71 );
72
73 let nextBlock =
74 siblings?.[
75 siblings.findIndex((s) => s.value === lastSelected.value) + 1
76 ];
77 let nextBlockType = await rep?.query((tx) =>
78 scanIndex(tx).eav(nextBlock?.value, "block/type"),
79 );
80
81 if (prevBlock) {
82 useUIState.getState().setSelectedBlock({
83 value: prevBlock.value,
84 parent: prevBlock.parent,
85 });
86
87 focusBlock(
88 {
89 value: prevBlock.value,
90 type: prevBlockType?.[0].data.value,
91 parent: prevBlock.parent,
92 },
93 { type: "end" },
94 );
95 } else {
96 useUIState.getState().setSelectedBlock({
97 value: nextBlock.value,
98 parent: nextBlock.parent,
99 });
100
101 focusBlock(
102 {
103 value: nextBlock.value,
104 type: nextBlockType?.[0]?.data.value,
105 parent: nextBlock.parent,
106 },
107 { type: "start" },
108 );
109 }
110 }
111 }
112
113 // close the pages
114 pagesToClose.forEach((page) => page && useUIState.getState().closePage(page));
115
116 // Clean up footnotes from blocks being deleted
117 for (let entity of entities) {
118 let footnotes = await rep.query((tx) =>
119 scanIndex(tx).eav(entity, "block/footnote"),
120 );
121 for (let fn of footnotes) {
122 await rep.mutate.deleteFootnote({
123 footnoteEntityID: fn.data.value,
124 blockID: entity,
125 });
126 }
127 }
128
129 await Promise.all(
130 entities.map((entity) =>
131 rep?.mutate.removeBlock({
132 blockEntity: entity,
133 }),
134 ),
135 );
136
137 undoManager && undoManager.endGroup();
138}