-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implement generates notes and save notes
implemention to show toast components for messages
- Loading branch information
Showing
2 changed files
with
215 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
<!-- | ||
Copyright: Ankitects Pty Ltd and contributors | ||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html | ||
--> | ||
<script lang="ts"> | ||
import IconButton from "../components/IconButton.svelte"; | ||
import { mdiClose } from "./icons"; | ||
export let type: "success" | "error" = "success"; | ||
export let message; | ||
export let showToast = false; | ||
const closeToast = () => { | ||
showToast = false; | ||
}; | ||
</script> | ||
|
||
{#if showToast} | ||
<div class="toast-container"> | ||
<div class="toast {type === 'success' ? 'success' : 'error'}"> | ||
{message} | ||
<IconButton iconSize={96} on:click={closeToast} class="toast-icon"> | ||
{@html mdiClose}</IconButton | ||
> | ||
</div> | ||
</div> | ||
{/if} | ||
|
||
<style> | ||
.toast-container { | ||
position: fixed; | ||
bottom: 3rem; | ||
z-index: 100; | ||
width: 100%; | ||
text-align: center; | ||
display: flex; | ||
justify-content: center; | ||
} | ||
.toast { | ||
display: flex; | ||
align-items: center; | ||
padding: 1rem; | ||
background-color: #fff; | ||
border-radius: 0.5rem; | ||
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.1); | ||
width: 60%; | ||
justify-content: space-between; | ||
} | ||
.success { | ||
background: #66bb6a; | ||
color: white; | ||
} | ||
.error { | ||
background: #ef5350; | ||
color: white; | ||
} | ||
:global(.toast-icon) { | ||
background: unset !important; | ||
color: white !important; | ||
border: none !important; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
// Copyright: Ankitects Pty Ltd and contributors | ||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html | ||
|
||
import * as tr from "@tslib/ftl"; | ||
import { get } from "svelte/store"; | ||
|
||
import type { Collection } from "../lib/proto"; | ||
import { addImageOcclusionNotes } from "./lib"; | ||
import { noteFieldsData, tagsWritable } from "./store"; | ||
import Toast from "./Toast.svelte"; | ||
import { getQuestionMaskColor } from "./tools/lib"; | ||
|
||
const divData = [ | ||
"angle", | ||
"fill", | ||
"height", | ||
"left", | ||
"points", | ||
"rx", | ||
"ry", | ||
"top", | ||
"type", | ||
"width", | ||
]; | ||
|
||
export function generate(): { occlusionCloze: string; noteCount: number } { | ||
const canvas = globalThis.canvas; | ||
const canvasObjects = canvas.getObjects(); | ||
if (canvasObjects.length < 1) { | ||
return { occlusionCloze: "", noteCount: 0 }; | ||
} | ||
|
||
let occlusionCloze = ""; | ||
let clozeData = ""; | ||
let noteCount = 0; | ||
|
||
canvasObjects.forEach((object, index) => { | ||
const obJson = object.toJSON(); | ||
noteCount++; | ||
if (obJson.type === "group") { | ||
clozeData += getGroupCloze(object, index); | ||
} else { | ||
clozeData += getCloze(object, index, null); | ||
} | ||
}); | ||
|
||
occlusionCloze += clozeData; | ||
console.log(occlusionCloze); | ||
return { occlusionCloze, noteCount }; | ||
} | ||
|
||
const getCloze = (object, index, relativePos): string => { | ||
const obJson = object.toJSON(); | ||
let clozeData = ""; | ||
|
||
// generate cloze data in form of | ||
// {{c1::image-occlusion:rect:top=100:left=100:width=100:height=100}} | ||
Object.keys(obJson).forEach(function(key) { | ||
if (divData.includes(key)) { | ||
if (key === "type") { | ||
clozeData += `:${obJson[key]}`; | ||
} else if (key === "points") { | ||
const points = obJson[key]; | ||
let pnts = ""; | ||
points.forEach((point: { x: number; y: number }) => { | ||
pnts += point.x.toFixed(2) + "," + point.y.toFixed(2) + " "; | ||
}); | ||
clozeData += `:${key}=${pnts.trim()}`; | ||
} else if (relativePos && key === "top") { | ||
clozeData += `:top=${relativePos.top}`; | ||
} else if (relativePos && key === "left") { | ||
clozeData += `:left=${relativePos.left}`; | ||
} else { | ||
clozeData += `:${key}=${obJson[key]}`; | ||
} | ||
} | ||
}); | ||
|
||
// question mask color, on front side asking for cloze | ||
clozeData += `:quesmaskcolor=${getQuestionMaskColor()}`; | ||
clozeData = `{{c${index + 1}::image-occlusion${clozeData}}}\n`; | ||
return clozeData; | ||
}; | ||
|
||
const getGroupCloze = (group, index): string => { | ||
let clozeData = ""; | ||
const objects = group._objects; | ||
|
||
objects.forEach((object) => { | ||
const { top, left } = getObjectPositionInGroup(group, object); | ||
clozeData += getCloze(object, index, { top, left }); | ||
}); | ||
|
||
return clozeData; | ||
}; | ||
|
||
const getObjectPositionInGroup = (group, object): { top: number; left: number } => { | ||
let left = object.left + group.left + group.width / 2; | ||
let top = object.top + group.top + group.height / 2; | ||
left = left.toFixed(2); | ||
top = top.toFixed(2); | ||
return { top, left }; | ||
}; | ||
|
||
export const saveImageNotes = async function( | ||
imagePath: string, | ||
deckId: number, | ||
hideAll: boolean, | ||
): Promise<void> { | ||
const { occlusionCloze, noteCount } = generate(); | ||
if (noteCount === 0) { | ||
return; | ||
} | ||
|
||
const fieldsData = get(noteFieldsData); | ||
const tags = get(tagsWritable); | ||
let header = fieldsData["header"]; | ||
let notes = fieldsData["back-extra"]; | ||
|
||
if (header === undefined) { | ||
const textArea = document.getElementById("img-occ-html-header")! as HTMLTextAreaElement; | ||
header = textArea.value; | ||
} | ||
|
||
if (notes === undefined) { | ||
const textArea = document.getElementById("img-occ-html-back-extra")! as HTMLTextAreaElement; | ||
notes = textArea.value; | ||
} | ||
|
||
header = `<div>${header}</div>`; | ||
notes = `<div">${notes}</div>`; | ||
|
||
const result = await addImageOcclusionNotes(imagePath, deckId, occlusionCloze, header, notes, tags, hideAll); | ||
showResult(result, noteCount); | ||
}; | ||
|
||
// show toast message | ||
const showResult = (result: Collection.OpChanges, count: number) => { | ||
const toastComponent = new Toast({ | ||
target: document.body, | ||
props: { | ||
message: "", | ||
type: "error", | ||
}, | ||
}); | ||
|
||
if (result.note) { | ||
const msg = tr.importingNoteAdded({ count: count }); | ||
toastComponent.$set({ message: msg, type: "success", showToast: true }); | ||
} else { | ||
const msg = tr.notetypesErrorGeneratingCloze(); | ||
toastComponent.$set({ message: msg, showToast: true }); | ||
} | ||
}; |