mirror of
https://github.com/ankitects/anki.git
synced 2025-09-25 01:06:35 -04:00
Move note saving to TS
This commit is contained in:
parent
75bc67608a
commit
1809e793e6
4 changed files with 64 additions and 55 deletions
|
@ -10,6 +10,7 @@ package anki.frontend;
|
||||||
import "anki/scheduler.proto";
|
import "anki/scheduler.proto";
|
||||||
import "anki/generic.proto";
|
import "anki/generic.proto";
|
||||||
import "anki/search.proto";
|
import "anki/search.proto";
|
||||||
|
import "anki/notes.proto";
|
||||||
|
|
||||||
service FrontendService {
|
service FrontendService {
|
||||||
// Returns values from the reviewer
|
// Returns values from the reviewer
|
||||||
|
@ -28,7 +29,9 @@ service FrontendService {
|
||||||
// Warns python that the deck option web view is ready to receive requests.
|
// Warns python that the deck option web view is ready to receive requests.
|
||||||
rpc deckOptionsReady(generic.Empty) returns (generic.Empty);
|
rpc deckOptionsReady(generic.Empty) returns (generic.Empty);
|
||||||
|
|
||||||
|
// Editor
|
||||||
rpc editorReady(generic.Empty) returns (generic.Empty);
|
rpc editorReady(generic.Empty) returns (generic.Empty);
|
||||||
|
rpc editorUpdateNote(notes.UpdateNotesRequest) returns (generic.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
service BackendFrontendService {}
|
service BackendFrontendService {}
|
||||||
|
|
|
@ -402,24 +402,8 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
|
||||||
|
|
||||||
# focus lost or key/button pressed?
|
# focus lost or key/button pressed?
|
||||||
if cmd.startswith("blur") or cmd.startswith("key"):
|
if cmd.startswith("blur") or cmd.startswith("key"):
|
||||||
(type, ord_str, nid_str, txt) = cmd.split(":", 3)
|
(type, ord_str) = cmd.split(":", 1)
|
||||||
ord = int(ord_str)
|
ord = int(ord_str)
|
||||||
try:
|
|
||||||
nid = int(nid_str)
|
|
||||||
except ValueError:
|
|
||||||
nid = 0
|
|
||||||
if nid != self.note.id:
|
|
||||||
print("ignored late blur")
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.note.fields[ord] = self.mungeHTML(txt)
|
|
||||||
except IndexError:
|
|
||||||
print("ignored late blur after notetype change")
|
|
||||||
return
|
|
||||||
|
|
||||||
if not self.addMode:
|
|
||||||
self._save_current_note()
|
|
||||||
if type == "blur":
|
if type == "blur":
|
||||||
self.currentField = None
|
self.currentField = None
|
||||||
# run any filters
|
# run any filters
|
||||||
|
|
|
@ -608,13 +608,30 @@ def editor_ready() -> bytes:
|
||||||
|
|
||||||
def handle_on_main() -> None:
|
def handle_on_main() -> None:
|
||||||
window = aqt.mw.app.activeWindow()
|
window = aqt.mw.app.activeWindow()
|
||||||
if isinstance(getattr(window, "editor"), Editor):
|
if window and isinstance(getattr(window, "editor"), Editor):
|
||||||
window.editor._set_ready() # type: ignore
|
window.editor._set_ready() # type: ignore
|
||||||
|
|
||||||
aqt.mw.taskman.run_on_main(handle_on_main)
|
aqt.mw.taskman.run_on_main(handle_on_main)
|
||||||
return b""
|
return b""
|
||||||
|
|
||||||
|
|
||||||
|
def editor_update_note() -> bytes:
|
||||||
|
from aqt.editor import Editor
|
||||||
|
|
||||||
|
output = raw_backend_request("update_notes")()
|
||||||
|
response = OpChanges()
|
||||||
|
response.ParseFromString(output)
|
||||||
|
|
||||||
|
def handle_on_main() -> None:
|
||||||
|
window = aqt.mw.app.activeWindow()
|
||||||
|
if window and isinstance(getattr(window, "editor"), Editor):
|
||||||
|
on_op_finished(aqt.mw, response, window.editor) # type: ignore
|
||||||
|
|
||||||
|
aqt.mw.taskman.run_on_main(handle_on_main)
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
post_handler_list = [
|
post_handler_list = [
|
||||||
congrats_info,
|
congrats_info,
|
||||||
get_deck_configs_for_update,
|
get_deck_configs_for_update,
|
||||||
|
@ -631,6 +648,7 @@ post_handler_list = [
|
||||||
deck_options_require_close,
|
deck_options_require_close,
|
||||||
deck_options_ready,
|
deck_options_ready,
|
||||||
editor_ready,
|
editor_ready,
|
||||||
|
editor_update_note,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -647,6 +665,7 @@ exposed_backend_list = [
|
||||||
# NotesService
|
# NotesService
|
||||||
"get_field_names",
|
"get_field_names",
|
||||||
"get_note",
|
"get_note",
|
||||||
|
"new_note",
|
||||||
# NotetypesService
|
# NotetypesService
|
||||||
"get_notetype",
|
"get_notetype",
|
||||||
"get_notetype_names",
|
"get_notetype_names",
|
||||||
|
|
|
@ -224,14 +224,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
bridgeCommand(`setTagsCollapsed:${$tagsCollapsed}`);
|
bridgeCommand(`setTagsCollapsed:${$tagsCollapsed}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let noteId: bigint | null = null;
|
let note: Note | null = null;
|
||||||
export function setNoteId(ntid: bigint | null): void {
|
export function setNote(n: Note): void {
|
||||||
|
note = n;
|
||||||
// TODO this is a hack, because it requires the NoteEditor to know implementation details of the PlainTextInput.
|
// TODO this is a hack, because it requires the NoteEditor to know implementation details of the PlainTextInput.
|
||||||
// It should be refactored once we work on our own Undo stack
|
// It should be refactored once we work on our own Undo stack
|
||||||
for (const pi of plainTextInputs) {
|
for (const pi of plainTextInputs) {
|
||||||
pi.api.codeMirror.editor.then((editor) => editor.clearHistory());
|
pi.api.codeMirror.editor.then((editor) => editor.clearHistory());
|
||||||
}
|
}
|
||||||
noteId = ntid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let notetypeMeta: NotetypeIdAndModTime;
|
let notetypeMeta: NotetypeIdAndModTime;
|
||||||
|
@ -251,10 +251,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNoteId(): string | null {
|
|
||||||
return noteId?.toString() ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let isImageOcclusion = false;
|
let isImageOcclusion = false;
|
||||||
function setIsImageOcclusion(val: boolean) {
|
function setIsImageOcclusion(val: boolean) {
|
||||||
isImageOcclusion = val;
|
isImageOcclusion = val;
|
||||||
|
@ -294,18 +290,24 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
function transformContentBeforeSave(content: string): string {
|
function transformContentBeforeSave(content: string): string {
|
||||||
return content.replace(/ data-editor-shrink="(true|false)"/g, "");
|
return content.replace(/ data-editor-shrink="(true|false)"/g, "");
|
||||||
|
// TODO: mungeHTML()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateCurrentNote() {
|
||||||
|
if (mode !== "add") {
|
||||||
|
await editorUpdateNote({
|
||||||
|
notes: [note!],
|
||||||
|
skipUndoEntry: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateField(index: number, content: string): void {
|
function updateField(index: number, content: string): void {
|
||||||
fieldSave.schedule(
|
fieldSave.schedule(() => {
|
||||||
() =>
|
bridgeCommand(`key:${index}`);
|
||||||
bridgeCommand(
|
note!.fields[index] = transformContentBeforeSave(content);
|
||||||
`key:${index}:${getNoteId()}:${transformContentBeforeSave(
|
updateCurrentNote();
|
||||||
content,
|
}, 600);
|
||||||
)}`,
|
|
||||||
),
|
|
||||||
600,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveFieldNow(): void {
|
function saveFieldNow(): void {
|
||||||
|
@ -422,6 +424,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
getNote,
|
getNote,
|
||||||
getNotetype,
|
getNotetype,
|
||||||
encodeIriPaths,
|
encodeIriPaths,
|
||||||
|
newNote,
|
||||||
|
editorUpdateNote,
|
||||||
} from "@generated/backend";
|
} from "@generated/backend";
|
||||||
import { wrapInternal } from "@tslib/wrap";
|
import { wrapInternal } from "@tslib/wrap";
|
||||||
|
|
||||||
|
@ -442,6 +446,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
import StickyBadge from "./StickyBadge.svelte";
|
import StickyBadge from "./StickyBadge.svelte";
|
||||||
import ButtonGroupItem from "$lib/components/ButtonGroupItem.svelte";
|
import ButtonGroupItem from "$lib/components/ButtonGroupItem.svelte";
|
||||||
import PreviewButton from "./PreviewButton.svelte";
|
import PreviewButton from "./PreviewButton.svelte";
|
||||||
|
import type { Note } from "@generated/anki/notes_pb";
|
||||||
|
|
||||||
$: isIOImageLoaded = false;
|
$: isIOImageLoaded = false;
|
||||||
$: ioImageLoadedStore.set(isIOImageLoaded);
|
$: ioImageLoadedStore.set(isIOImageLoaded);
|
||||||
|
@ -562,11 +567,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadNote(nid: bigint | null, notetypeId: bigint, focusTo: number) {
|
async function loadNote(nid: bigint, notetypeId: bigint, focusTo: number) {
|
||||||
const notetype = await getNotetype({
|
const notetype = await getNotetype({
|
||||||
ntid: notetypeId,
|
ntid: notetypeId,
|
||||||
});
|
});
|
||||||
let fieldValues: string[] = notetype.fields.map(() => "");
|
|
||||||
const fieldNames = (
|
const fieldNames = (
|
||||||
await getFieldNames({
|
await getFieldNames({
|
||||||
ntid: notetype.id,
|
ntid: notetype.id,
|
||||||
|
@ -576,18 +580,22 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
const clozeFields = fieldNames.map((name, index) =>
|
const clozeFields = fieldNames.map((name, index) =>
|
||||||
clozeFieldOrds.includes(index),
|
clozeFieldOrds.includes(index),
|
||||||
);
|
);
|
||||||
let tags: string[] = [];
|
if (mode === "add") {
|
||||||
if (nid) {
|
setNote(await newNote({ ntid: notetype.id }));
|
||||||
const note = await getNote({
|
} else {
|
||||||
nid,
|
setNote(
|
||||||
});
|
await getNote({
|
||||||
fieldValues = (
|
nid,
|
||||||
await Promise.all(
|
}),
|
||||||
note.fields.map((field) => encodeIriPaths({ val: field })),
|
);
|
||||||
)
|
|
||||||
).map((field) => field.val);
|
|
||||||
tags = note.tags;
|
|
||||||
}
|
}
|
||||||
|
const fieldValues = (
|
||||||
|
await Promise.all(
|
||||||
|
note!.fields.map((field) => encodeIriPaths({ val: field })),
|
||||||
|
)
|
||||||
|
).map((field) => field.val);
|
||||||
|
const tags = note!.tags;
|
||||||
|
|
||||||
saveSession();
|
saveSession();
|
||||||
setFields(fieldNames, fieldValues);
|
setFields(fieldNames, fieldValues);
|
||||||
setIsImageOcclusion(
|
setIsImageOcclusion(
|
||||||
|
@ -610,7 +618,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
focusField(focusTo);
|
focusField(focusTo);
|
||||||
setNoteId(nid);
|
|
||||||
// TODO: lastTextColor/lastHighlightColor profile config
|
// TODO: lastTextColor/lastHighlightColor profile config
|
||||||
// setColorButtons(["#0000ff", "#0000ff"]);
|
// setColorButtons(["#0000ff", "#0000ff"]);
|
||||||
setTags(tags);
|
setTags(tags);
|
||||||
|
@ -666,8 +673,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
setClozeHint,
|
setClozeHint,
|
||||||
saveNow,
|
saveNow,
|
||||||
focusIfField,
|
focusIfField,
|
||||||
getNoteId,
|
|
||||||
setNoteId,
|
|
||||||
setNotetypeMeta,
|
setNotetypeMeta,
|
||||||
wrap,
|
wrap,
|
||||||
setMathjaxEnabled,
|
setMathjaxEnabled,
|
||||||
|
@ -786,11 +791,9 @@ components and functionality for general note editing.
|
||||||
on:focusout={() => {
|
on:focusout={() => {
|
||||||
$focusedField = null;
|
$focusedField = null;
|
||||||
setAddonButtonsDisabled(true);
|
setAddonButtonsDisabled(true);
|
||||||
bridgeCommand(
|
bridgeCommand(`blur:${index}`);
|
||||||
`blur:${index}:${getNoteId()}:${transformContentBeforeSave(
|
note!.fields[index] = transformContentBeforeSave(get(content));
|
||||||
get(content),
|
updateCurrentNote();
|
||||||
)}`,
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
on:mouseenter={() => {
|
on:mouseenter={() => {
|
||||||
$hoveredField = fields[index];
|
$hoveredField = fields[index];
|
||||||
|
|
Loading…
Reference in a new issue