Skip to content

Commit 1fe437f

Browse files
committed
Ability to create cloze cards
- At the moment using syntax similar to anki {{c1::Cloze text goes here}} etc. - no UI integration - no option to review cards - #27
1 parent 73cc904 commit 1fe437f

File tree

3 files changed

+141
-94
lines changed

3 files changed

+141
-94
lines changed

src/components/editcard/CardEditor/ClozeCardEditor.tsx

+98-78
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,29 @@ import { useHotkeys } from "@mantine/hooks";
22
import { RichTextEditor } from "@mantine/tiptap";
33
import { IconBracketsContain } from "@tabler/icons-react";
44
import { EditMode } from "../../../logic/CardTypeManager";
5-
import { Card, CardType } from "../../../logic/card";
5+
import { Card, CardType, newCards } from "../../../logic/card";
66
import { Deck } from "../../../logic/deck";
77
import CardEditor, { useCardEditor } from "./CardEditor";
88
import classes from "./ClozeCardEditor.module.css";
99

10-
import { useMantineTheme } from "@mantine/core";
10+
import { Button, Stack, useMantineTheme } from "@mantine/core";
1111
import { Mark } from "@tiptap/core";
12-
import { mergeAttributes } from "@tiptap/react";
12+
import { Editor, mergeAttributes } from "@tiptap/react";
13+
import { useCallback, useState } from "react";
14+
import CardEditorFooter from "../CardEditorFooter";
15+
import {
16+
addFailed,
17+
saveFailed,
18+
successfullyAdded,
19+
successfullySaved,
20+
} from "../../custom/Notification/Notification";
21+
import {
22+
ClozeCardUtils,
23+
createClozeCardSet,
24+
} from "../../../logic/CardTypeImplementations/ClozeCard";
25+
import { useSharedValue } from "../../../logic/sharedvalue";
1326

27+
//not used right now, use ui control or remove later
1428
const Gap = Mark.create({
1529
name: "gap",
1630
inline: true,
@@ -53,89 +67,95 @@ interface ClozeCardEditorProps {
5367
mode: EditMode;
5468
}
5569

56-
/*async function finish(
57-
mode: EditMode,
58-
clear: Function,
59-
deck: Deck,
60-
card: Card<CardType.Cloze> | null,
61-
editor: Editor | null,
62-
) {
63-
const cardInstance = createCardInstance(card, editor);
64-
if (cardInstance !== null) {
65-
if (mode === "edit") {
66-
//SAVE
67-
try {
68-
console.log(cardInstance.id);
69-
const numberOfUpdatedRecords = await updateCard(
70-
cardInstance.id,
71-
cardInstance
72-
);
73-
if (numberOfUpdatedRecords === 0) {
74-
saveFailed();
75-
return;
76-
}
77-
successfullySaved();
78-
} catch (error) {
79-
saveFailed();
80-
}
81-
} else {
82-
//NEW
83-
try {
84-
await newCard(cardInstance, deck);
85-
clear && clear();
86-
successfullyAdded();
87-
} catch (error) {
88-
addFailed();
89-
}
90-
}
91-
}
92-
}*/
93-
94-
/*function createCardInstance(
95-
card: Card<CardType.Cloze> | null,
96-
editor: Editor | null,
97-
) {
98-
return card
99-
? ClozeCardUtils.update(
100-
{
101-
frame: ClozeFrame,
102-
back: backEditor?.getHTML() ?? "",
103-
},
104-
card
105-
)
106-
: ClozeCardUtils.create({
107-
front: frontEditor?.getHTML() ?? "",
108-
back: backEditor?.getHTML() ?? "",
109-
});
110-
}*/
111-
11270
export default function ClozeCardEditor({
11371
card,
11472
deck,
11573
mode,
11674
}: ClozeCardEditorProps) {
117-
const theme = useMantineTheme();
118-
11975
useHotkeys([["mod+Enter", () => {}]]);
12076
//fix sometime
121-
const editor = useCardEditor("", Gap);
77+
const editor = useCardEditor(
78+
useSharedValue(card?.content.textReferenceId ?? "")?.value ?? "",
79+
Gap
80+
);
81+
82+
const clear = useCallback(() => {
83+
editor?.commands.setContent("");
84+
editor?.commands.focus();
85+
}, [editor]);
12286

12387
return (
124-
<CardEditor
125-
editor={editor}
126-
//TODO
127-
className={classes}
128-
controls={
129-
<RichTextEditor.Control
130-
tabIndex={-1}
131-
onClick={() => {
132-
editor?.commands.toggleMark("gap", { group: Math.random() });
133-
console.log(editor?.getHTML());
134-
}}
135-
>
136-
<IconBracketsContain />
137-
</RichTextEditor.Control>
138-
}
139-
/>
88+
<Stack gap="2rem">
89+
<CardEditor
90+
editor={editor}
91+
className={classes}
92+
controls={
93+
//not used right now, use ui control or remove later
94+
<RichTextEditor.Control
95+
tabIndex={-1}
96+
onClick={() => {
97+
editor?.commands.toggleMark("gap", { group: Math.random() });
98+
console.log(editor?.getHTML());
99+
}}
100+
>
101+
<IconBracketsContain />
102+
</RichTextEditor.Control>
103+
}
104+
/>
105+
<CardEditorFooter
106+
finish={() => finish(mode, clear, deck, card, editor)}
107+
mode={mode}
108+
/>
109+
</Stack>
140110
);
141111
}
112+
113+
function finish(
114+
mode: EditMode,
115+
clear: Function,
116+
deck: Deck,
117+
card: Card<CardType.Cloze> | null,
118+
editor: Editor | null
119+
) {
120+
if (mode === "edit") {
121+
//ISSUE newly introduced cards through edit are not recognized
122+
try {
123+
if (card) {
124+
ClozeCardUtils.update(
125+
{
126+
text: editor?.getHTML() ?? "",
127+
},
128+
card
129+
);
130+
}
131+
successfullySaved();
132+
} catch (error) {
133+
saveFailed();
134+
}
135+
} else {
136+
const occlusionNumberSet: number[] = getOcclusionNumberSet(
137+
editor?.getHTML() ?? ""
138+
);
139+
try {
140+
createClozeCardSet({
141+
text: editor?.getHTML() ?? "",
142+
occlusionNumberSet,
143+
}).then((cards) => newCards(cards, deck));
144+
clear();
145+
successfullyAdded();
146+
} catch (error) {
147+
addFailed();
148+
}
149+
}
150+
}
151+
152+
function getOcclusionNumberSet(text: string) {
153+
const regex = /\{\{c\d::((?!\{\{|}}).)*\}\}/g;
154+
const matches = text.match(regex);
155+
const cardDigits = new Set<number>();
156+
matches?.forEach((match) => {
157+
const digit = parseInt(match[3]);
158+
cardDigits.add(digit);
159+
});
160+
return Array.from(cardDigits);
161+
}

src/components/editcard/CardEditor/DoubleSidedCardEditor.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import classes from "./DoublieSidedCardEditor.module.css";
1+
import classes from "./DoubleSidedCardEditor.module.css";
22
import React, { useCallback } from "react";
33
import { Stack, Text, useMantineTheme } from "@mantine/core";
44
import CardEditor, { useCardEditor } from "./CardEditor";
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,72 @@
1-
import { Card, CardType, createCardSkeleton } from "../card";
1+
import { Card, CardType, createCardSkeleton, updateCard } from "../card";
22
import { Deck } from "../deck";
33
import ClozeCardEditor from "../../components/editcard/CardEditor/ClozeCardEditor";
44
import React from "react";
55
import { CardTypeManager, EditMode } from "../CardTypeManager";
6+
import {
7+
createSharedValue,
8+
getSharedValue,
9+
setSharedValue,
10+
} from "../sharedvalue";
611

712
export type ClozeContent = {
8-
occlusionId: string;
13+
occlusionNumber: number;
14+
textReferenceId: string;
915
};
1016

1117
export const ClozeCardUtils: CardTypeManager<CardType.Cloze> = {
12-
update(params: {}, existingCard: Card<CardType.Cloze>) {
13-
return {
14-
...existingCard,
15-
content: {
16-
type: existingCard.content.type,
17-
occlusionId: " params.occlusionId",
18-
},
19-
};
18+
update(params: { text: string }, existingCard: Card<CardType.Cloze>) {
19+
setSharedValue(existingCard.content.textReferenceId, params.text);
20+
updateCard(existingCard.id, existingCard);
21+
return existingCard;
2022
},
2123

22-
create(params: { front: string; occlusionId: string }): Card<CardType.Cloze> {
24+
create(params: {
25+
occlusionNumber: number;
26+
textReferenceId: string;
27+
}): Card<CardType.Cloze> {
2328
return {
2429
...createCardSkeleton(),
2530
content: {
2631
type: CardType.Cloze,
27-
occlusionId: "params.occlusionId",
32+
occlusionNumber: params.occlusionNumber,
33+
textReferenceId: params.textReferenceId,
2834
},
2935
};
3036
},
3137

3238
displayPreview(card: Card<CardType.Cloze>) {
33-
return "Cloze Card";
39+
return getSharedValue(card.content.textReferenceId)
40+
.then(
41+
(sharedValue) =>
42+
"[" +
43+
card.content.occlusionNumber +
44+
"] " +
45+
sharedValue?.value.replace(/<[^>]*>/g, "") ?? "error"
46+
)
47+
.catch(() => "error");
3448
},
3549

3650
displayQuestion(card: Card<CardType.Cloze>) {
37-
return "card.content.front;";
51+
return "Cloze Card Occluded";
3852
},
3953
displayAnswer(card: Card<CardType.Cloze>) {
40-
return "card.content.front;";
54+
return "Cloze Card Answer";
4155
},
4256
editor(card: Card<CardType.Cloze> | null, deck: Deck, mode: EditMode) {
4357
return <ClozeCardEditor card={card} deck={deck} mode={mode} />;
4458
},
4559
};
60+
61+
export async function createClozeCardSet(params: {
62+
text: string;
63+
occlusionNumberSet: number[];
64+
}): Promise<Card<CardType.Cloze>[]> {
65+
const textReferenceId = await createSharedValue(params.text);
66+
return params.occlusionNumberSet.map((occlusionNumber) =>
67+
ClozeCardUtils.create({
68+
occlusionNumber: occlusionNumber,
69+
textReferenceId: textReferenceId,
70+
})
71+
);
72+
}

0 commit comments

Comments
 (0)