Skip to content

Commit 634b2a2

Browse files
committed
implement generates notes and save notes
implemention to show toast components for messages
1 parent 7ae1194 commit 634b2a2

File tree

2 files changed

+215
-0
lines changed

2 files changed

+215
-0
lines changed

ts/image-occlusion/Toast.svelte

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<!--
2+
Copyright: Ankitects Pty Ltd and contributors
3+
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
4+
-->
5+
<script lang="ts">
6+
import IconButton from "../components/IconButton.svelte";
7+
import { mdiClose } from "./icons";
8+
9+
export let type: "success" | "error" = "success";
10+
export let message;
11+
export let showToast = false;
12+
const closeToast = () => {
13+
showToast = false;
14+
};
15+
</script>
16+
17+
{#if showToast}
18+
<div class="toast-container">
19+
<div class="toast {type === 'success' ? 'success' : 'error'}">
20+
{message}
21+
<IconButton iconSize={96} on:click={closeToast} class="toast-icon">
22+
{@html mdiClose}</IconButton
23+
>
24+
</div>
25+
</div>
26+
{/if}
27+
28+
<style>
29+
.toast-container {
30+
position: fixed;
31+
bottom: 3rem;
32+
z-index: 100;
33+
width: 100%;
34+
text-align: center;
35+
display: flex;
36+
justify-content: center;
37+
}
38+
.toast {
39+
display: flex;
40+
align-items: center;
41+
padding: 1rem;
42+
background-color: #fff;
43+
border-radius: 0.5rem;
44+
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.1);
45+
width: 60%;
46+
justify-content: space-between;
47+
}
48+
.success {
49+
background: #66bb6a;
50+
color: white;
51+
}
52+
.error {
53+
background: #ef5350;
54+
color: white;
55+
}
56+
:global(.toast-icon) {
57+
background: unset !important;
58+
color: white !important;
59+
border: none !important;
60+
}
61+
</style>

ts/image-occlusion/generate.ts

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
// Copyright: Ankitects Pty Ltd and contributors
2+
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
3+
4+
import * as tr from "@tslib/ftl";
5+
import { get } from "svelte/store";
6+
7+
import type { Collection } from "../lib/proto";
8+
import { addImageOcclusionNotes } from "./lib";
9+
import { noteFieldsData, tagsWritable } from "./store";
10+
import Toast from "./Toast.svelte";
11+
import { getQuestionMaskColor } from "./tools/lib";
12+
13+
const divData = [
14+
"angle",
15+
"fill",
16+
"height",
17+
"left",
18+
"points",
19+
"rx",
20+
"ry",
21+
"top",
22+
"type",
23+
"width",
24+
];
25+
26+
export function generate(): { occlusionCloze: string; noteCount: number } {
27+
const canvas = globalThis.canvas;
28+
const canvasObjects = canvas.getObjects();
29+
if (canvasObjects.length < 1) {
30+
return { occlusionCloze: "", noteCount: 0 };
31+
}
32+
33+
let occlusionCloze = "";
34+
let clozeData = "";
35+
let noteCount = 0;
36+
37+
canvasObjects.forEach((object, index) => {
38+
const obJson = object.toJSON();
39+
noteCount++;
40+
if (obJson.type === "group") {
41+
clozeData += getGroupCloze(object, index);
42+
} else {
43+
clozeData += getCloze(object, index, null);
44+
}
45+
});
46+
47+
occlusionCloze += clozeData;
48+
console.log(occlusionCloze);
49+
return { occlusionCloze, noteCount };
50+
}
51+
52+
const getCloze = (object, index, relativePos): string => {
53+
const obJson = object.toJSON();
54+
let clozeData = "";
55+
56+
// generate cloze data in form of
57+
// {{c1::image-occlusion:rect:top=100:left=100:width=100:height=100}}
58+
Object.keys(obJson).forEach(function(key) {
59+
if (divData.includes(key)) {
60+
if (key === "type") {
61+
clozeData += `:${obJson[key]}`;
62+
} else if (key === "points") {
63+
const points = obJson[key];
64+
let pnts = "";
65+
points.forEach((point: { x: number; y: number }) => {
66+
pnts += point.x.toFixed(2) + "," + point.y.toFixed(2) + " ";
67+
});
68+
clozeData += `:${key}=${pnts.trim()}`;
69+
} else if (relativePos && key === "top") {
70+
clozeData += `:top=${relativePos.top}`;
71+
} else if (relativePos && key === "left") {
72+
clozeData += `:left=${relativePos.left}`;
73+
} else {
74+
clozeData += `:${key}=${obJson[key]}`;
75+
}
76+
}
77+
});
78+
79+
// question mask color, on front side asking for cloze
80+
clozeData += `:quesmaskcolor=${getQuestionMaskColor()}`;
81+
clozeData = `{{c${index + 1}::image-occlusion${clozeData}}}\n`;
82+
return clozeData;
83+
};
84+
85+
const getGroupCloze = (group, index): string => {
86+
let clozeData = "";
87+
const objects = group._objects;
88+
89+
objects.forEach((object) => {
90+
const { top, left } = getObjectPositionInGroup(group, object);
91+
clozeData += getCloze(object, index, { top, left });
92+
});
93+
94+
return clozeData;
95+
};
96+
97+
const getObjectPositionInGroup = (group, object): { top: number; left: number } => {
98+
let left = object.left + group.left + group.width / 2;
99+
let top = object.top + group.top + group.height / 2;
100+
left = left.toFixed(2);
101+
top = top.toFixed(2);
102+
return { top, left };
103+
};
104+
105+
export const saveImageNotes = async function(
106+
imagePath: string,
107+
deckId: number,
108+
hideAll: boolean,
109+
): Promise<void> {
110+
const { occlusionCloze, noteCount } = generate();
111+
if (noteCount === 0) {
112+
return;
113+
}
114+
115+
const fieldsData = get(noteFieldsData);
116+
const tags = get(tagsWritable);
117+
let header = fieldsData["header"];
118+
let notes = fieldsData["back-extra"];
119+
120+
if (header === undefined) {
121+
const textArea = document.getElementById("img-occ-html-header")! as HTMLTextAreaElement;
122+
header = textArea.value;
123+
}
124+
125+
if (notes === undefined) {
126+
const textArea = document.getElementById("img-occ-html-back-extra")! as HTMLTextAreaElement;
127+
notes = textArea.value;
128+
}
129+
130+
header = `<div>${header}</div>`;
131+
notes = `<div">${notes}</div>`;
132+
133+
const result = await addImageOcclusionNotes(imagePath, deckId, occlusionCloze, header, notes, tags, hideAll);
134+
showResult(result, noteCount);
135+
};
136+
137+
// show toast message
138+
const showResult = (result: Collection.OpChanges, count: number) => {
139+
const toastComponent = new Toast({
140+
target: document.body,
141+
props: {
142+
message: "",
143+
type: "error",
144+
},
145+
});
146+
147+
if (result.note) {
148+
const msg = tr.importingNoteAdded({ count: count });
149+
toastComponent.$set({ message: msg, type: "success", showToast: true });
150+
} else {
151+
const msg = tr.notetypesErrorGeneratingCloze();
152+
toastComponent.$set({ message: msg, showToast: true });
153+
}
154+
};

0 commit comments

Comments
 (0)